EOS体系是以通讯为基本的,action就是EOS上通讯的载体。若是你想挪用某个智能合约,那末就要给它发action。若是你的智能合约要供应某种效劳,供他人挪用,那该智能合约就要供应action的处置惩罚器(handler)函数,这些handler函数就是你对外界发来的action做出响应中央,也可以或许说是,你给他人供应效劳的中央。
在没有严加辨别的时刻,我说的action函数
,实际上就是action的handler函数;说成action函数是为了轻易。
这类经由过程音讯挪用其他智能合约接口的体式格局,有点相似于近程挪用(RPC),实际上,它们的道理也是很相似的,都是提议方把本身的挪用参数和相干数据序列化,经由过程某种音讯通道,发送给接收方,接收方反序列化音讯并解析出相干请求,然后依照请求执行操纵,最初把操纵效果以相似的体式格局前往给提议方。
RPC有同步挪用和异步挪用之分,同步挪用是指,提议方在发送挪用音讯今后,会守候效劳方的前往,直到挪用方前往数据后才执行前面的语句。异步挪用是指,提议方在发送挪用音讯今后,不会傻傻守候效劳方的前往,而是继承执行上面的语句;当收到效劳方前往数据的时刻,提议方也会作出响应的处置惩罚。
与RPC挪用相似,action音讯也有相似的两种情势:离别对应于deferred action
和inline action
。不外,这两种action音讯都是异步的,没有同步的action音讯。
那末deferred action
和inline action
有什么区分呢?
inline action
有人翻译成在线 action
,有人翻译成内联action
。我倾向于后者,后者更能注解它的寄义。inline action
相当于原有action的一部分,它和原有的action在统一个事件中执行;若是inline action
失利了,则全部事件也就失利了,并会回滚该事件曾经发作的任何效果。
我们修正一下hello
合约,演示一下这类状况,我们再建立一个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
");
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发送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”打印出来,这是由于命令行这里的打印效果有时刻会有缺失,没关系,我们可以或许从nodeos里看log,要如许启动nodeos
能力看到合约执行的log:
nodeos --contracts-console
然后我们再从新发送hi
action,我们可以或许nodeos
的output里看到绿色的执行log以下:
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 =====================
在这里,你会发明,先打印出end of hi
,后打印出Say, user
。而我们的代码,是先发送say
action再打印end of hi
的。这正申明,发送say
action是异步停止的。
我们这里发送inline action
的体式格局是组织一个action工具,然后挪用它的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
挪用本身的其他的action handler
,能一样平常胜利
有人翻译成提早的action
,我以为可以或许,我们今后也如许翻译吧。既然是提早
,那肯定是异步了,那这里的异步与inline action
的异步有何区分呢?区分有3:
inline action
与本来的action是统一个事件,若是inline action
失利了,全部事件会回滚;deferred action
与本来的action不属于统一个事件,而且不包管deferred action
可以或许胜利执行,若是失利了,也不会引发原有action的事件回滚。- 由于
inline action
与本来的action是统一个事件,以是他们肯定会被打包在统一个块中;而deferred action
不包管这一点,实际上,一样平常状况都不会在统一个区块中。deferred action
可以或许设置提早若干时候后执行;而inline action
,BP会包管他们在统一个事件中,因而会尽量快的执行它,以是不克不及设置提早时候。
我们看个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
");
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
设置为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不在统一个区块里。
我们发送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);
我们组织了一个transaction
工具, 然后挪用它的send
要领,这个send的实现是如许的:
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
这两个参数。
sender_id
详细的值,实际上是你本身定的,可以或许作废还没有发作的提早生意业务。cancel的要领署名以下:
int cancel_deferred(const uint128_t& sender_id);
replace_existing
若是为true,代表想要替代掉之前统一sender_id
对应的提早action
;若是为false,代表不替代之前的,也就是新增一个以后参数指定的提早action
。
明天就如许吧,明天见。我建立了一个智能合约手艺议论群,若是有兴致的,可以或许搜刮微信sgeng3shi21
(三生三世),加挚友,并复兴”智能合约”,可拉你进群。