eos源码解析(九):延时交易

寂寞如花落,窗前更无声,故园旧影迹难寻,倚看数点残红,已是梦中人。
今天,来讲讲eos中一个有关于约定的故事。
有两个问题:
1,transaction中的delay字段的含义是什么?
2,抵押交易的撤回,3天之后怎么就自动到账了?

其实,以上两个问题,本质上是一个问题,下面是系统合约中创建延时交易的部分:

            eosio::transaction out;
            out.actions.emplace_back( permission_level{ from, N(active) }, _self, N(refund), from );
            out.delay_sec = refund_delay;
            cancel_deferred( from ); // TODO: Remove this line when replacing deferred trxs is fixed
            out.send( from, from, true );

可以看到:out.delay_sec = refund_delay; refund_delay的值为三天。三天之后,交易自动进行。
那么这个交易是在合约中创建的,要不要签名什么的呢?
答案是,在合约中创建的交易不需要签名,但会验证权限:

   ......... 
  bool check_auth = false;
      for( const auto& act : trx.actions ) {
         if( act.account != receiver ) {
            check_auth = true;
            break;
         }
      }
      if( check_auth ) {
         control.get_authorization_manager()
                .check_authorization( trx.actions,
                                      {},
                                      {{receiver, config::eosio_code_name}},
                                      delay,
                                      std::bind(&transaction_context::checktime, &this->trx_context),
                                      false
                                    );
      }
   .........

上面的代码的意思是:
1,如果是延时交易的执行对象是自己,那么就不用验证权限了。
2,如果延时交易的的执行对象不是自己,那么就会提供自己的code权限。
3,上文中的"自己"是指的当前合约。

这里做一个延伸,“合约”与“账户”的区别。合约是一段代码,账户是eos上的身份。合约不可以单独存在与eos中,合约是一个账户对外提供的功能。多个账户可以部署同一份合约代码。

然后,将交易放入数据库里面:

   auto& d = control.db();
   if ( auto ptr = d.find(boost::make_tuple(receiver, sender_id)) ) {
      EOS_ASSERT( replace_existing, deferred_tx_duplicate, "deferred transaction with the same sender_id and payer already exists" );

      // TODO: Remove the following subjective check when the deferred trx replacement RAM bug has been fixed with a hard fork.
      EOS_ASSERT( !control.is_producing_block(), subjective_block_production_exception,
                  "Replacing a deferred transaction is temporarily disabled." );

      // TODO: The logic of the next line needs to be incorporated into the next hard fork.
      // trx_context.add_ram_usage( ptr->payer, -(config::billable_size_v + ptr->packed_trx.size()) );

      db.modify( *ptr, [&]( auto& gtx ) {
            gtx.sender      = receiver;
            gtx.sender_id   = sender_id;
            gtx.payer       = payer;
            gtx.published   = control.pending_block_time();
            gtx.delay_until = gtx.published + delay;
            gtx.expiration  = gtx.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);

            trx_size = gtx.set( trx );
         });
   } else {
      d.create( [&]( auto& gtx ) {
            gtx.trx_id      = trx.id();
            gtx.sender      = receiver;
            gtx.sender_id   = sender_id;
            gtx.payer       = payer;
            gtx.published   = control.pending_block_time();
            gtx.delay_until = gtx.published + delay;
            gtx.expiration  = gtx.delay_until + fc::seconds(control.get_global_properties().configuration.deferred_trx_expiration_window);

            trx_size = gtx.set( trx );
         });
   }

在producer_plugin.cpp中,生产完区块后
如果是生产节点,直接在producer_plugin.cpp中:

。。。。
//得到所有的延迟交易
auto scheduled_trxs = chain.get_scheduled_transactions();
。。。。
//提交延迟交易
auto trace = chain.push_scheduled_transaction(trx, deadline);
。。。。。
//    删除延时交易在内存里记录
       remove_scheduled_transaction(gto);
。。。。
// 执行transaction
      try {
         trx_context.init_for_deferred_trx( gtrx.published );
         trx_context.exec();
         trx_context.finalize(); // Automatically rounds up network and CPU usage in trace and bills payers if successful


         auto restore = make_block_restore_point();
// 回执
         trace->receipt = push_receipt( gtrx.trx_id,
                                        transaction_receipt::executed,
                                        trx_context.billed_cpu_time_us,
                                        trace->net_usage );
。。。。。。

区块中的交易是以receipt形式存在的

   const transaction_receipt& push_receipt( const T& trx, transaction_receipt_header::status_enum status,
                                            uint64_t cpu_usage_us, uint64_t net_usage ) {
      uint64_t net_usage_words = net_usage / 8;
      EOS_ASSERT( net_usage_words*8 == net_usage, transaction_exception, "net_usage is not divisible by 8" );
      pending->_pending_block_state->block->transactions.emplace_back( trx );
      transaction_receipt& r = pending->_pending_block_state->block->transactions.back();
      r.cpu_usage_us         = cpu_usage_us;
      r.net_usage_words      = net_usage_words;
      r.status               = status;
      return r;
   }

见证节点在接受一个区块的时候,apply_block的时候将会根据不同的回执执行不同的交易。

void apply_block( const signed_block_ptr& b, controller::block_status s ) { try {
      try {
         EOS_ASSERT( b->block_extensions.size() == 0, block_validate_exception, "no supported extensions" );
         start_block( b->timestamp, b->confirmed, s );

         transaction_trace_ptr trace;

         for( const auto& receipt : b->transactions ) {
            auto num_pending_receipts = pending->_pending_block_state->block->transactions.size();
            if( receipt.trx.contains() ) {
               auto& pt = receipt.trx.get();
               auto mtrx = std::make_shared(pt);
               trace = push_transaction( mtrx, fc::time_point::maximum(), receipt.cpu_usage_us, true );
            } else if( receipt.trx.contains() ) {
               trace = push_scheduled_transaction( receipt.trx.get(), fc::time_point::maximum(), receipt.cpu_usage_us, true );
            } else {
               EOS_ASSERT( false, block_validate_exception, "encountered unexpected receipt type" );
            }
.......
.......

综上,延迟交易发起后,会先判断权限,如果权限验证通过,则把交易存放在内存里面,如果不通过,则直接拒绝,此外,net资源的使用也是在schedule_deferred_transaction函数中进行的,使用的是当前调用合约对象的内存。

你可能感兴趣的:(eos源码解析(九):延时交易)