EOS deferred action与inline action

龚鸿羽
2023-12-01

 

action

EOS体系是以通讯为基本的,action就是EOS上通讯的载体。若是你想挪用某个智能合约,那末就要给它发action。若是你的智能合约要供应某种效劳,供他人挪用,那该智能合约就要供应action的处置惩罚器(handler)函数,这些handler函数就是你对外界发来的action做出响应中央,也可以或许说是,你给他人供应效劳的中央。

在没有严加辨别的时刻,我说的action函数,实际上就是action的handler函数;说成action函数是为了轻易。

这类经由过程音讯挪用其他智能合约接口的体式格局,有点相似于近程挪用(RPC),实际上,它们的道理也是很相似的,都是提议方把本身的挪用参数和相干数据序列化,经由过程某种音讯通道,发送给接收方,接收方反序列化音讯并解析出相干请求,然后依照请求执行操纵,最初把操纵效果以相似的体式格局前往给提议方。

RPC有同步挪用和异步挪用之分,同步挪用是指,提议方在发送挪用音讯今后,会守候效劳方的前往,直到挪用方前往数据后才执行前面的语句。异步挪用是指,提议方在发送挪用音讯今后,不会傻傻守候效劳方的前往,而是继承执行上面的语句;当收到效劳方前往数据的时刻,提议方也会作出响应的处置惩罚。

与RPC挪用相似,action音讯也有相似的两种情势:离别对应于deferred actioninline action。不外,这两种action音讯都是异步的,没有同步的action音讯。

那末deferred actioninline 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,能一样平常胜利

deferred action

有人翻译成提早的action,我以为可以或许,我们今后也如许翻译吧。既然是提早,那肯定是异步了,那这里的异步与inline action的异步有何区分呢?区分有3:

  1. inline action与本来的action是统一个事件,若是inline action失利了,全部事件会回滚;deferred action 与本来的action不属于统一个事件,而且不包管deferred action 可以或许胜利执行,若是失利了,也不会引发原有action的事件回滚。
  2. 由于inline action与本来的action是统一个事件,以是他们肯定会被打包在统一个块中;而deferred action不包管这一点,实际上,一样平常状况都不会在统一个区块中。
  3. 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_idreplace_existing这两个参数。

sender_id详细的值,实际上是你本身定的,可以或许作废还没有发作的提早生意业务。cancel的要领署名以下:

 int cancel_deferred(const uint128_t& sender_id);

replace_existing若是为true,代表想要替代掉之前统一sender_id对应的提早action;若是为false,代表不替代之前的,也就是新增一个以后参数指定的提早action

明天就如许吧,明天见。我建立了一个智能合约手艺议论群,若是有兴致的,可以或许搜刮微信sgeng3shi21(三生三世),加挚友,并复兴”智能合约”,可拉你进群。

 类似资料: