EOS延迟事务(deferred transaction)
2018-12-03 本文已影响291人
vergil6
EOS中的事务(transaction)中的动作(action)执行分2种,1)一般的事务里面的动作立即执行 2)延迟事务里面的动作在将来某个时刻执行,第二类事务成为延迟事务(deferred transaction)
延迟事务相关的表
在generated_transaction_object.hpp中定义了延迟事务相关的表跟数据结构,generated_transaction_multi_index表中保存的记录结构如下:
class generated_transaction_object : public chainbase::object<generated_transaction_object_type, generated_transaction_object>
{
OBJECT_CTOR(generated_transaction_object, (packed_trx) )
id_type id;
transaction_id_type trx_id; // packed_trx id
account_name sender; // 发送该trx的inline action合约名称 如果不是inline action产生 则为空
uint128_t sender_id = 0; // 由sender给出的唯一标号(inline action)
account_name payer; // 该结构的占用的ram由谁支付
time_point delay_until; // 事务开始执行的时间
time_point expiration; // 事务过期时间
time_point published; // 产生该延迟事务的时间
shared_string packed_trx; // packed_trx
uint32_t set( const transaction& trx ) {
auto trxsize = fc::raw::pack_size( trx );
packed_trx.resize( trxsize );
fc::datastream<char*> ds( packed_trx.data(), trxsize );
fc::raw::pack( ds, trx );
return trxsize;
}
};
延迟事务的产生
- 事务本身是延迟的
transaction中有一个字段delay_sec,如果该字段非0则表示延迟delay_sec秒后执行该事务,在transaction_context::exec() 函数中会判断
void transaction_context::exec() {
EOS_ASSERT( is_initialized, transaction_exception, "must first initialize" );
if( apply_context_free ) {
for( const auto& act : trx.context_free_actions ) {
trace->action_traces.emplace_back();
dispatch_action( trace->action_traces.back(), act, true );
}
}
if( delay == fc::microseconds() ) {
for( const auto& act : trx.actions ) {
trace->action_traces.emplace_back();
dispatch_action( trace->action_traces.back(), act );
}
// 如果delay 为非空,则是延迟事务
} else {
schedule_transaction();
}
}
schedule_transaction函数在generated_transaction_multi_index插入一条延迟事务记录
- 合约中发出的事务
合约 ===》send_deferred ===》apply_context::schedule_deferred_transaction
在执行合约中合约不仅能发出inline action还能发出延迟事务,事实上合约发出的事务都是延迟的,因为能发出事务的wasm接口目前只有一个
/**
* 发送一个延迟事务.
*
* @brief Sends a deferred transaction.
* @param sender_id - ID of sender
* @param payer - Account paying for RAM
* @param serialized_transaction - Pointer of serialized transaction to be deferred
* @param size - Size to reserve
* @param replace_existing - f this is `0` then if the provided sender_id is already in use by an in-flight transaction from this contract, which will be a failing assert. If `1` then transaction will atomically cancel/replace the inflight transaction
*/
void send_deferred(const uint128_t& sender_id, capi_name payer, const char *serialized_transaction, size_t size, uint32_t replace_existing = 0);
schedule_deferred_transaction函数主要是先做权限检查然后在generated_transaction_multi_index插入一条延迟事务记录
延迟事务的执行
start_block===>push_scheduled_transaction
延迟事务的执行发生在producer_plugin.cpp中的start_block中,通过get_scheduled_transactions函数获取可以执行的延迟事务然后调用push_scheduled_transaction执行该延迟事务