前面兩篇文章我們分析了eosio.msig
合約勒奇,中間有些內(nèi)容因?yàn)槠鶝]有仔細(xì)講解,今天開始打算把一些知識點(diǎn)攻克一下巧骚,有些比較難的知識點(diǎn)赊颠,自然會詳細(xì)介紹;有些呢劈彪,則看起來比較簡單竣蹦,然而深入進(jìn)去之后,確可以加深對EOS系統(tǒng)的理解沧奴。今天介紹第三個知識點(diǎn):deferred action與inline action痘括。
action
EOS系統(tǒng)是以消息通信為基礎(chǔ)的,action就是EOS上消息的載體滔吠。如果你想調(diào)用某個智能合約纲菌,那么就要給它發(fā)action消息挠日。如果你的智能合約要提供某種服務(wù),供別人調(diào)用驰后,那該智能合約就要提供action的處理器(handler)函數(shù)肆资,這些handler函數(shù)就是你對外界發(fā)來的action做出響應(yīng)地方,也可以說是灶芝,你給別人提供服務(wù)的地方郑原。
在沒有嚴(yán)加區(qū)分的時候,我說的action函數(shù)
夜涕,實(shí)際上就是action的handler函數(shù)犯犁;說成action函數(shù)是為了方便。
這種通過消息調(diào)用其他智能合約接口的方式女器,有點(diǎn)類似于遠(yuǎn)程調(diào)用(RPC)酸役,實(shí)際上,它們的原理也是很類似的驾胆,都是發(fā)起方把自己的調(diào)用參數(shù)和相關(guān)數(shù)據(jù)序列化涣澡,通過某種消息通道,發(fā)送給接收方丧诺,接收方反序列化消息并解析出相關(guān)要求入桂,然后按照要求執(zhí)行操作,最后把操作結(jié)果以類似的方式返回給發(fā)起方驳阎。
RPC有同步調(diào)用和異步調(diào)用之分抗愁,同步調(diào)用是指,發(fā)起方在發(fā)送調(diào)用消息之后呵晚,會等待服務(wù)方的返回蜘腌,直到調(diào)用方返回數(shù)據(jù)后才執(zhí)行后面的語句。異步調(diào)用是指饵隙,發(fā)起方在發(fā)送調(diào)用消息之后撮珠,不會傻傻等待服務(wù)方的返回,而是繼續(xù)執(zhí)行下面的語句癞季;當(dāng)收到服務(wù)方返回數(shù)據(jù)的時候劫瞳,發(fā)起方也會作出相應(yīng)的處理。
與RPC調(diào)用類似绷柒,action消息也有類似的兩種形式:分別對應(yīng)于deferred action
和inline action
。不過涮因,這兩種action消息都是異步的废睦,沒有同步的action消息。
那么deferred action
和inline action
有什么區(qū)別呢养泡?
inline action
inline action
有人翻譯成在線 action
嗜湃,有人翻譯成內(nèi)聯(lián)action
奈应。我傾向于后者,后者更能表明它的含義购披。inline action
相當(dāng)于原有action的一部分杖挣,它和原有的action在同一個事務(wù)中執(zhí)行;如果inline action
失敗了刚陡,則整個事務(wù)也就失敗了惩妇,并會回滾該事務(wù)已經(jīng)產(chǎn)生的任何結(jié)果。
我們修改一下hello
合約筐乳,演示一下這種情況歌殃;再創(chuàng)建一個action處理器,命名為say:
#include <eosiolib/eosio.hpp>
#include <eosiolib/transaction.hpp>
using namespace eosio;
class hello : public eosio::contract {
public:
using contract::contract;
/// @abi action
void hi( account_name user ) {
require_auth( user);
print("before inline action\n");
action(
permission_level{user, N(active)},
N(hello.code), N(say),
user
).send();
print("end of hi");
}
void say( account_name user ) {
require_auth( user );
print( "Say, ", name{user});
}
};
EOSIO_ABI( hello, (hi)(say) )
與之前部署hello合約一樣蝙云,我們編譯并部署到hello.code
賬戶:
~ ./MakeContract hello
~ cleos set contract hello.code ./hello -p hello.code@active
Reading WAST/WASM from ./hello/hello.wasm...
Using already assembled WASM...
Publishing contract...
executed transaction: ec3e7517282b946c2c17e952936a1c22f89e68b88558790cd4545f1bf7f53629 2728 bytes 16026 us
# eosio <= eosio::setcode {"account":"hello.code","vmtype":0,"vmversion":0,"code":"0061736d01000000013b0c60027f7e0060000060000...
# eosio <= eosio::setabi {"account":"hello.code","abi":"0e656f73696f3a3a6162692f312e30000202686900010475736572046e616d6503736...
MakeContract
是之前我們寫的一個編譯腳本氓皱,還記得吧。
然后我們再向hello.code發(fā)送hi
action:
~ cleos push action hello.code hi '["user"]' -p user@active
executed transaction: 637e139bd4d332ed116c087f45605e61ec593225636ff35ee93b86096c749e39 104 bytes 1790 us
# hello.code <= hello.code::hi {"user":"user"}
>> before inline action
# hello.code <= hello.code::say {"user":"user"}
>> Say, user
可以看到print("end of hi");
沒有把"end of hi"打印出來勃刨,這是因?yàn)槊钚羞@里的打印結(jié)果有時候會有缺失波材,不要緊,我們可以從nodeos里看log身隐,要這樣啟動nodeos
才能看到合約執(zhí)行的log:
nodeos --contracts-console
然后我們再重新發(fā)送hi
action廷区,我們可以nodeos
的output里看到綠色的執(zhí)行l(wèi)og如下:
2018-08-27T08:53:04.692 thread-0 apply_context.cpp:28 print_debug ]
[(hello.code,hi)->hello.code]: CONSOLE OUTPUT BEGIN =====================
before inline action
end of hi
[(hello.code,hi)->hello.code]: CONSOLE OUTPUT END =====================
2018-08-27T08:53:04.692 thread-0 apply_context.cpp:28 print_debug ]
[(hello.code,say)->hello.code]: CONSOLE OUTPUT BEGIN =====================
Say, user
[(hello.code,say)->hello.code]: CONSOLE OUTPUT END =====================
在這里,你會發(fā)現(xiàn)抡医,先打印出end of hi
躲因,后打印出Say, user
。而我們的代碼忌傻,是先發(fā)送say
action再打印end of hi
的大脉。這正說明,發(fā)送say
action是異步進(jìn)行的水孩。
我們這里發(fā)送inline action
的方式是構(gòu)造一個action對象镰矿,然后調(diào)用它的send方法:
action(
permission_level{user, N(active)},
N(hello.code), N(say),
user
).send();
你也可以用這種方式:
INLINE_ACTION_SENDER(hello, say)(N(hello.code), {N(user), N(active)}, {user});
INLINE_ACTION_SENDER是一個宏,它的用法格式是這樣的:
INLINE_ACTION_SENDER(<class-name>, <action-handler-name>)(<receiver-contract>, {<account>, <permission>}, {<data>});
這里通過inline action
調(diào)用自己的其他的action handler
俘种,能正常成功秤标,但如果調(diào)用其他的合約呢?我們下一篇文章詳解其中玄機(jī)宙刘。
deferred action
有人翻譯成延遲的action
苍姜,我覺得不錯,我們以后也這樣翻譯吧悬包。既然是延遲
衙猪,那肯定是異步了,那這里的異步與inline action
的異步有何區(qū)別呢?區(qū)別有3:
inline action
與原來的action是同一個事務(wù)垫释。如果inline action
失敗了丝格,整個事務(wù)會回滾;deferred action
與原來的action不屬于同一個事務(wù)棵譬,并且不保證deferred action
能夠成功執(zhí)行显蝌,如果失敗了,也不會引起原有action的事務(wù)回滾订咸。- 因?yàn)?code>inline action與原來的action是同一個事務(wù)曼尊,所以他們肯定會被打包在同一個塊中;而
deferred action
不保證這一點(diǎn)算谈,實(shí)際上涩禀,一般情況都不會在同一個區(qū)塊中。deferred action
可以設(shè)置延遲多少時間后執(zhí)行然眼;而inline action
艾船,BP會保證他們在同一個事務(wù)中,因而會盡可能快的執(zhí)行它高每,所以不能設(shè)置延遲時間屿岂。
我們看個deferred action
的例子吧?
#include <eosiolib/eosio.hpp>
#include <eosiolib/transaction.hpp>
using namespace eosio;
class hello : public eosio::contract {
public:
using contract::contract;
/// @abi action
void hi( account_name user ) {
require_auth( user);
print("before deferred action\n");
eosio::transaction txn{};
const uint128_t sender_id = 100;
txn.actions.emplace_back(
action(eosio::permission_level(user, N(active)),
N(hello.code),
N(say),
std::make_tuple(user)));
txn.delay_sec = 0;
txn.send(sender_id, user);
print("end of hi");
}
void say( account_name user ) {
require_auth( user );
print( "Say, ", name{user});
}
};
EOSIO_ABI( hello, (hi)(say) )
這里故意把延遲時間delay_sec
設(shè)置為0鲸匿,可以從下面nodeos的output看到:
2018-08-27T09:32:04.002 thread-0 producer_plugin.cpp:1234 produce_block ] Produced block 000105488afa390b... #66888 @ 2018-08-27T09:32:04.000 signed by eosio [trxs: 0, lib: 66887, confirmed: 0]
2018-08-27T09:32:04.419 thread-0 apply_context.cpp:28 print_debug ]
[(hello.code,hi)->hello.code]: CONSOLE OUTPUT BEGIN =====================
before inline action
end of hi
[(hello.code,hi)->hello.code]: CONSOLE OUTPUT END =====================
2018-08-27T09:32:04.505 thread-0 producer_plugin.cpp:1234 produce_block ] Produced block 00010549e3d459b0... #66889 @ 2018-08-27T09:32:04.500 signed by eosio [trxs: 1, lib: 66888, confirmed: 0]
2018-08-27T09:32:04.510 thread-0 apply_context.cpp:28 print_debug ]
[(hello.code,say)->hello.code]: CONSOLE OUTPUT BEGIN =====================
Say, user
[(hello.code,say)->hello.code]: CONSOLE OUTPUT END =====================
可以看到爷怀,即便我們把delay_sec
置為0,deferred action
還是和原有的action不在同一個區(qū)塊里带欢。
我們發(fā)送deferred action
是這么做的:
eosio::transaction txn{};
const uint128_t sender_id = 100;
txn.actions.emplace_back(
action(eosio::permission_level(user, N(active)),
N(hello.code),
N(say),
std::make_tuple(user)));
txn.delay_sec = 0;
txn.send(sender_id, user);
我們構(gòu)造了一個transaction
對象运授, 然后調(diào)用它的send
方法,這個send的實(shí)現(xiàn)是這樣的:
void send(const uint128_t& sender_id, account_name payer, bool replace_existing = false) const {
auto serialize = pack(*this);
send_deferred(sender_id, payer, serialize.data(), serialize.size(), replace_existing);
}
你可能注意到了乔煞,sender_id
和replace_existing
這兩個參數(shù)吁朦。
sender_id
具體的值,實(shí)際上是你自己定的渡贾,可以取消還沒有發(fā)生的延遲交易逗宜。cancel的方法簽名如下:
int cancel_deferred(const uint128_t& sender_id);
replace_existing
如果為true,代表想要替換掉之前同一sender_id
對應(yīng)的延遲action
空骚;如果為false纺讲,代表不替換之前的,也就是新增一個當(dāng)前參數(shù)指定的延遲action
囤屹。
今天就這樣吧熬甚。
簡介:不羈,一名程序員肋坚;專研EOS技術(shù)则涯,玩轉(zhuǎn)EOS智能合約開發(fā)复局。
微信公眾號:know_it_well
知識星球地址:https://t.zsxq.com/QvbuzFM