EOS智能合约开发(二十五)EOS项目中实现交易确认问题,及解决办法

我们在开发EOS项目中,在高TPS下可能会出现交易从可逆的block无法打包到不可以的block中,造成交易回滚,就此问题。我们提出解决方案。
在交易过程中,我们需要确认这笔交易,从可逆的状态到不可逆的状态。才确认这笔交易完成。
一、问题提出:
1、如何确认交易;
2、如果发现,可逆block与不可逆的差距很大,如何解决。
二、实现办法:
针对如何确认交易问题,我们通过以下方法解决。
1、同步一个mongodb。
2、在一笔交易完成后,检测mongodb中,transactions document ,中一个字段irreversible为true,才能确认这笔交易完成。

{
    "_id" : ObjectId("5d8da6b63f6b7ad62a74534c"),
    "trx_id" : "10d9841055e4e8517df6519e3e02d02bfa0ec209b121b551e6e875c85b238adb",
    "accepted" : true,
    "actions" : [ 
        {
            "account" : "token.abcd",
            "name" : "transfer",
            "authorization" : [ 
                {
                    "actor" : "antreceipt",
                    "permission" : "active"
                }
            ],
            "data" : {
                "from" : "antreceipt",
                "to" : "tyre",
                "quantity" : "1.9600 ABCD",
                "memo" : "第三方调用转账"
            },
            "hex_data" : "0040ae4e2175f3340000000000a0aecf904c00000000000004494b454300000015e7acace4b889e696b9e8b083e794a8e8bdace8b4a6"
        }
    ],
    "context_free_actions" : [],
    "context_free_data" : [],
    "createdAt" : ISODate("2019-09-27T06:05:43.048Z"),
    "delay_sec" : 0,
    "expiration" : "2019-09-27T06:06:12",
    "implicit" : false,
    "max_cpu_usage_ms" : 0,
    "max_net_usage_words" : 0,
    "ref_block_num" : 26827,
    "ref_block_prefix" : NumberLong(2692015748),
    "scheduled" : false,
    "signatures" : [ 
        "SIG_K1_K1AaPDVagBgdAxqwYCGQempDJ5FJn1RRAaZEMfqJpeXLAJebAUFWaguai4v2H1cnAqm5aHiwxdRQWk5kU9ycxUQGK6Bm4V"
    ],
    "signing_keys" : {
        "0" : "EOS6Kv7BREsnXjzHGkjtjDR5FDbTDTNvkdExcsjumufUEvR5UvQFC"
    },
    "transaction_extensions" : [],
    "block_id" : "012168cd8439207b3a1105b1b5dd3f2d95f883210b705e213c8a0fa2395f03b9",
    "block_num" : 18966733,
    "irreversible" : true,
    "updatedAt" : ISODate("2019-09-27T06:07:21.532Z")
}

确认这个字段 ,“irreversible” : true,

如果,这个同步的mongodb出现问题,我们需要重新同步。就会导致新的问题产生。

可逆block与不可逆的差距很大,我们通过以下办法解决

可逆节点位置 不可逆节点位置 备注
2000 1000 启动节点
2000+1 1000 +1 收到一个新包
差距一直存在
问题 如何解决
解决办法 加速执行不可逆节点速度

解决这个问题,我们需要分析源代码
文件路径
eos\libraries\chain\controller.cpp

	...
   void push_block( std::future<block_state_ptr>& block_state_future ) {
      controller::block_status s = controller::block_status::complete;
      EOS_ASSERT(!pending, block_validate_exception, "it is not valid to push a block when there is a pending block");

      auto reset_prod_light_validation = fc::make_scoped_exit([old_value=trusted_producer_light_validation, this]() {
         trusted_producer_light_validation = old_value;
      });
      try {
         block_state_ptr new_header_state = block_state_future.get();
         auto& b = new_header_state->block;
         emit( self.pre_accepted_block, b );

         fork_db.add( new_header_state, false );

         if (conf.trusted_producers.count(b->producer)) {
            trusted_producer_light_validation = true;
         };
         emit( self.accepted_block_header, new_header_state );

         if ( read_mode != db_read_mode::IRREVERSIBLE ) {
            maybe_switch_forks( s );
         }

      } FC_LOG_AND_RETHROW( )
   }
   ...

这个函数的作用,就是保存block,在这里fork_db.add( new_header_state, false );就是保存block的地方,我们需要找到这个方法。
文件位置
eos\libraries\chain\fork_database.cpp

   block_state_ptr fork_database::add( const block_state_ptr& n, bool skip_validate_previous ) {
      EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" );
      EOS_ASSERT( my->head, fork_db_block_not_found, "no head block set" );

      if( !skip_validate_previous ) {
         auto prior = my->index.find( n->block->previous );
         EOS_ASSERT( prior != my->index.end(), unlinkable_block_exception,
                     "unlinkable block", ("id", n->block->id())("previous", n->block->previous) );
      }

      auto inserted = my->index.insert(n);
      EOS_ASSERT( inserted.second, fork_database_exception, "duplicate block added?" );

      my->head = *my->index.get<by_lib_block_num>().begin();

      auto lib    = my->head->dpos_irreversible_blocknum;
      auto oldest = *my->index.get<by_block_num>().begin();

  if( oldest->block_num < lib ) {
       prune( oldest );
  }
      return n;
   }

我们分析上面代码,如果 oldest->block_num < lib 的时候,就确认这个块, prune( oldest );
这样就是我上面说的,2000+1 ,1000+1,这样,永远追不上。
我们需要再这里改造。

      if( oldest->block_num < lib ) {
           prune( oldest );
      }

修改如下:
在距离差距很远的情况下,我们加速确认块,每次确认12个blcok,在赶上后,按照既定规则确认。

      auto destsize = 168; //21个生产节点,2/3的节点确认 12 * 21*2/3 = 168
      if( ( oldest->block_num + destsize ) < lib )
      {
        uint8_t tmp = 12;  //每次加速确认12个block
        while(tmp)
        {
          auto lib    = my->head->dpos_irreversible_blocknum;
          auto oldest = *my->index.get<by_block_num>().begin();
          prune( oldest );
          tmp--;
        }
     }
     else{
          if( oldest->block_num < lib ) {
           prune( oldest );
          }
     }

这个函数完整的代码

   block_state_ptr fork_database::add( const block_state_ptr& n, bool skip_validate_previous ) {
      EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" );
      EOS_ASSERT( my->head, fork_db_block_not_found, "no head block set" );

      if( !skip_validate_previous ) {
         auto prior = my->index.find( n->block->previous );
         EOS_ASSERT( prior != my->index.end(), unlinkable_block_exception,
                     "unlinkable block", ("id", n->block->id())("previous", n->block->previous) );
      }

      auto inserted = my->index.insert(n);
      EOS_ASSERT( inserted.second, fork_database_exception, "duplicate block added?" );

      my->head = *my->index.get<by_lib_block_num>().begin();

      auto lib    = my->head->dpos_irreversible_blocknum;
      auto oldest = *my->index.get<by_block_num>().begin();
      //ilog("lib =${lib},oldest =${oldest}",("lib",lib)("oldest",oldest->block_num));
      // add by cjb 20190927 
      auto destsize = 168; //21个生产节点,2/3的节点确认 12 * 21*2/3 = 168
      if( ( oldest->block_num + destsize ) < lib )
      {
        uint8_t tmp = 12;  //每次加速确认12个block
        while(tmp)
        {
          auto lib    = my->head->dpos_irreversible_blocknum;
          auto oldest = *my->index.get<by_block_num>().begin();
          //ilog("lib =${lib},oldest =${oldest}",("lib",lib)("oldest",oldest->block_num));
          prune( oldest );
          tmp--;
        }
     }
     else{
          if( oldest->block_num < lib ) {
           prune( oldest );
          }
     }
  
      return n;
   }

执行

./eosio_install.sh

启动mongog节点。很快就会赶上,让可逆节点与不可逆节点保持一定距离。

希望能否帮助到大家。

你可能感兴趣的:(区块链开发,技术篇,区块链)