EOS開發教程(2.6) 添加行内action

in eos •  6 days ago 

簡介
在前面的教程中,我們已經簡單的展示了授權addressbook 成為多索引表的過程。在本教程中,我將學到如何構建action,如何從壹個合約發送這些action。
Step 1:把eosio.code添加到許可中
要想能從addressbook中發送行內action,需要把eosio.code許可添加到合約帳號的活躍許可(active permission)中去。打開妳的終端,運行下面的代碼:
cleos set account permission addressbook active --add-code
eosio.code授权是假授权,以此提高安全性,以及让合约能执行行内action。
Step 2:通知Action
打開上壹個教程中被授權的addressbook.cpp合約。寫壹個action,它能在壹筆交易發生時發出“交易憑證”。為了實現這點,在addressbook 類裏創建壹個輔助函數。
[[eosio::action]] void notify(name user, std::string msg) {}
這個函數非常簡單,接受壹個name類型的用戶帳號,壹個string類型的消息。用戶參數表明是哪個用戶接收了發出的消息。
Step3:使用require_recipient把action拷貝到發送者
需要把交易拷貝給用戶,這樣它才能被當作收據。為了實現這點,要使用require_recipient 方法。調用 require_recipient 添加壹個帳號到require_recipient,確保這些帳號接收到被執行的action發出的通知。這個通知類似於發送壹個該action的“carbon副本”到require_recipient中的帳號。
[[eosio::action]] void notify(name user, std::string msg) { require_recipient(user); }
這個action十分簡單,任何用戶都能調用這個函數,然後偽造壹個這個合約的收據。這可能會被用於惡意的行為,這是壹個缺陷。為了糾正這點,就要確保用於調用這個action的驗證是出自它本身的,在本例中,使用get_self
[[eosio::action]] void notify(name user, std::string msg) { require_auth(get_self()); require_recipient(user); }
如果用戶bob直接調用了這個函數,但是傳遞的參數卻是alice,那麽這個函數就會拋出壹個異常。
Step4:通知輔助(helper)方法發送行內交易
由於行內action會被多次調用所以我們可以寫壹個輔助函數,來最大化代碼重用。在合約的私有域內,定義壹個方法:
... private: void send_summary(name user, std::string message){}
在這個輔助方法內構建壹個action,然後把它發送出去。
Step 5: action構造器
修改addressbook 合約,在用戶每次執行該合約的action的時候,發送壹個收據給該用戶。
首先,要處理“創建記錄”這個問題。當在表中找不到記錄的時候會出現這個問題,也就是當iterator == addresses.end()為true的時候。
把這個對象保存到壹個叫做notification的action變量中去。
... private: void send_summary(name user, std::string message){ action( //permission_level, //code, //action, //data ); }
壹個action構造器需要幾個參數。
· 壹個permission_level 結構
· 調用的合約(使用eosio::name類型進行初始化)
· action(使用eosio::name類型初始化)
· 傳遞到action中的數據,與被調用的action相關的位置元組。
授權結構
在這個合約中,授權應該由該合約的活躍(active)授權使用進行get_self()授權。提醒壹下,要在行內使用“活躍授權”,妳需要把妳的合約的活躍授權發送給eosio.code的pseudo-authority
... private: void send_summary(name user, std::string message){ action( permission_level{get_self(),"active"_n}, ); }
“代碼(code)”,也就是“部署合約的帳號”
由於是在這個合約中使用get_self調用的action。”addressbook”_n 在這裏也能運行,但是如果這個合約是以別的帳號的名義調用的,那麽它就不能運行。正基於此,get_self() 的優先級更高。
... private: void send_summary(name user, std::string message){ action( permission_level{get_self(),"active"_n}, get_self(), //action //data ); }
action
我們在前面把notify action定義為從行內action被調用的。這裏我們使用_n 操作符。
... private: void send_summary(name user, std::string message){ action( permission_level{get_self(),"active"_n}, get_self(), "notify"_n, //data ); }
數據
最後,定義傳遞到這個action中的數據。通知函數接收兩個參數,壹個name,壹個string。Action構造器的數據的類型為bytes,所以我們使用make_tuple,通過std C++庫可以調用。在元組中傳遞的數據是關於位置的,由被調用的action所接收的參數的順序決定,
· 把user變量當作參數傳給upsert()action。
· 把用戶的名字,傳遞給通知(notify)action的消息拼接為壹個字符串。
... private: void send_summary(name user, std::string message){ action( permission_level{get_self(),"active"_n}, get_self(), "notify"_n, std::make_tuple(user, name{user}.to_string() + message) ); }
發送action
最後,使用action結構的send方法發送action
... private: void send_summary(name user, std::string message){ action( permission_level{get_self(),"active"_n}, get_self(), "notify"_n, std::make_tuple(user, name{user}.to_string() + message) ).send(); }
Step 6: 調用輔助函數並註入相關消息
現在我們定義了輔助函數,它會從相關的位置被調用。有三個地方可以調用notify的輔助函數:
· 在合約emplaces 後的壹條新記錄:send_summary(user, "successfully emplaced record to addressbook");
· 在合約modifies 後的壹條現有記錄:send_summary(user, "successfully modified record in addressbook.");
· 在合約erases 後的壹條已有記錄:send_summary(user, "successfully erased record from addressbook");
Step 7: 重新編譯,並重新生成ABI文件
壹切就緒,下面是當前addressbook 合約的完整內容:
#include <eosio/eosio.hpp> #include <eosio/print.hpp> using namespace eosio; class [[eosio::contract("addressbook")]] addressbook : public eosio::contract { public: addressbook(name receiver, name code, datastream<const char*> ds): contract(receiver, code, ds) {} [[eosio::action]] void upsert(name user, std::string first_name, std::string last_name, uint64_t age, std::string street, std::string city, std::string state) { require_auth(user); address_index addresses(get_first_receiver(), get_first_receiver().value); auto iterator = addresses.find(user.value); if( iterator == addresses.end() ) { addresses.emplace(user, [&]( auto& row ) { row.key = user; row.first_name = first_name; row.last_name = last_name; row.age = age; row.street = street; row.city = city; row.state = state; }); send_summary(user, " successfully emplaced record to addressbook"); } else { addresses.modify(iterator, user, [&]( auto& row ) { row.key = user; row.first_name = first_name; row.last_name = last_name; row.age = age; row.street = street; row.city = city; row.state = state; }); send_summary(user, " successfully modified record to addressbook"); } } [[eosio::action]] void erase(name user) { require_auth(user); address_index addresses(get_first_receiver(), get_first_receiver().value); auto iterator = addresses.find(user.value); check(iterator != addresses.end(), "Record does not exist"); addresses.erase(iterator); send_summary(user, " successfully erased record from addressbook"); } [[eosio::action]] void notify(name user, std::string msg) { require_auth(get_self()); require_recipient(user); } private: struct [[eosio::table]] person { name key; std::string first_name; std::string last_name; uint64_t age; std::string street; std::string city; std::string state; uint64_t primary_key() const { return key.value; } uint64_t get_secondary_1() const { return age;} }; void send_summary(name user, std::string message) { action( permission_level{get_self(),"active"_n}, get_self(), "notify"_n, std::make_tuple(user, name{user}.to_string() + message) ).send(); }; typedef eosio::multi_index<"people"_n, person, indexed_by<"byage"_n, const_mem_fun<person, uint64_t, &person::get_secondary_1>> > address_index; };
打开你的终端,进入 CONTRACTS_DIR/addressbook
cd CONTRACTS_DIR/addressbook
重新編譯合約,由於上面做的更改會影響到ABI文件,所以要使用 — abigen標識。如果上面的步驟都沒問題的話,這壹步也不會報錯。
eosio-cpp -o addressbook.wasm addressbook.cpp --abigen
EOSIO上的只能合約是可以升級的,所以這個合約可以被重新部署。
cleos set contract addressbook CONTRACTS_DIR/addressbook
Publishing contract... executed transaction: 1898d22d994c97824228b24a1741ca3bd5c7bc2eba9fea8e83446d78bfb264fd 7320 bytes 747 us # eosio <= eosio::setcode {"account":"addressbook","vmtype":0,"vmversion":0,"code":"0061736d0100000001a6011a60027f7e0060077f7e... # eosio <= eosio::setabi {"account":"addressbook","abi":"0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d65...
成功!
Step 8: 測試
現在合約已經被部署了,那麽就來測試壹下吧。在之前的教程中,alice的addressbook記錄在測試的時候被刪掉了,所以調用upsert將會觸發剛剛寫到“create”裏的行內action。
在終端運行以下命令
cleos push action addressbook upsert '["alice", "alice", "liddell", 21, "123 drink me way", "wonderland", "amsterdam"]' -p [email protected]
cleos會返回壹些數據,包括在交易中執行的所有action。
executed transaction: e9e30524186bb6501cf490ceb744fe50654eb393ce0dd733f3bb6c68ff4b5622 160 bytes 9810 us # addressbook <= addressbook::upsert {"user":"alice","first_name":"alice","last_name":"liddell","age":21,"street":"123 drink me way","cit... # addressbook <= addressbook::notify {"user":"alice","msg":"alicesuccessfully emplaced record to addressbook"} # alice <= addressbook::notify {"user":"alice","msg":"alicesuccessfully emplaced record to addressbook"}
上個日誌中的最後壹條是發送給alice的addressbook::notify action。使用cleos get actions 展示與alice相關的以及發送到alice的action。
cleos get actions alice

seq when contract::action => receiver trx id... args ================================================================================================================ # 62 2018-09-15T12:57:09.000 addressbook::notify => alice 685ecc09... {"user":"alice","msg":"alice successfully added record to ad...

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!