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節點。很快就會趕上,讓可逆節點與不可逆節點保持一定距離。

希望能否幫助到大家。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章