欢迎所有爱好区块链技术的人加入。

ARCHIVES / 154.HTML

[EOS源码分析]EOS区块同步及生产

Surou EOS-优秀转载

#####本文所有实践都是基于EOS v1.0.1,请切到该分支然后对比源码,切换命令git checkout v1.0.1
提到区块生产和同步,我们肯定有几个疑问?

  1. 节点从哪里同步数据,节点如何知道哪些节点有最新的区块数据以同步数据
  2. genesis文件不同的节点互联会怎么样?
  3. 节点什么时候同步数据
p2p-peer-address = jungle.cryptolions.io:19876
p2p-peer-address = jungle.cryptolions.io:29876
p2p-peer-address = dev.cryptolions.io:39876

#####不同genesis文件的节点互联
首先来说下genesis是什么东西
genesis文件是一个用来描述创世块信息的文件

最重要的是initial_timestamp和initial_key

void initialize_database() {
      authority system_auth(conf.genesis.initial_key);
      create_native_account( config::system_account_name, system_auth, system_auth, true );
}

不同genesis文件,就代表是不同的链,这样的节点其实是不能互联的。但是由于节点服务器信息(ip, 端口)是公开的,不排除有误加的情况。所以必须有机制拒绝这样的连接,这个是连接握手节点通过检测chain_id来实现的

void net_plugin_impl::handle_message( connection_ptr c, const handshake_message &msg) {
      if (msg.generation == 1) {
         //检测是否属于同一个链
         if( msg.chain_id != chain_id) {
            elog( "Peer on a different chain. Closing connection");
            c->enqueue( go_away_message(go_away_reason::wrong_chain) );
            return;
         }
      }
}

chain_id不一样时,你会发现如下错误输出

那chain_id具体是怎么生成的呢?

controller_impl( const controller::config& cfg, controller& s  )
   :chain_id( cfg.genesis.compute_chain_id() ){
}

chain::chain_id_type genesis_state::compute_chain_id() const {
   digest_type::encoder enc;
   fc::raw::pack( enc, *this );
   return chain_id_type{enc.result()};
}

其实就是将genesis文件的数据做一次类似hash的操作
#####同步时机
新连接建立时握手阶段就会互相检测各自链的状态,并开始同步区块数据

 void sync_manager::recv_handshake (connection_ptr c, const handshake_message &msg) {
      controller& cc = chain_plug->chain();
      //本地不可逆区块number
      uint32_t lib_num = cc.last_irreversible_block_num( );
      //remote peer不可逆区块number
      uint32_t peer_lib = msg.last_irreversible_block_num;
      reset_lib_num(c);
      c->syncing = false;
      
      //接下来就是比较本地区块高度和远端节点区块高度,有如下4情况
      //--------------------------------
      // sync need checks; (lib == last irreversible block)
      //
      // 0. my head block id == peer head id means we are all caugnt up block wise
      // 1. my head block num < peer lib - start sync locally
      // 2. my lib > peer head num - send an last_irr_catch_up notice if not the first generation
      //
      // 3  my head block num <= peer head block num - update sync state and send a catchup request
      // 4  my head block num > peer block num ssend a notice catchup if this is not the first generation
      //
      //-----------------------------

      uint32_t head = cc.head_block_num( );
      //本地节点的区块头num小于远端节点不可逆区块头num时,同步
      if (head < peer_lib) {
         fc_dlog(logger, "sync check state 1");
         // wait for receipt of a notice message before initiating sync
         if (c->protocol_version < proto_explicit_sync) {
            start_sync( c, peer_lib);
         }
         return;
      }
      ….
}

#####区块生产
一个节点要生成区块,必须满足两个条件

  1. chain-> _production_enabled==true
    _production_enabled=true有几种情况
 void on_incoming_block(const signed_block_ptr& block) {
         //如果下一个块的截止时间大于当前时间,意味着同步完成
         if( chain.head_block_state()->header.timestamp.next().to_time_point() >= fc::time_point::now() )
            _production_enabled = true;
    }
  1. 节点被投票成了21个代表中的一个,且到了生产区块的turn(21个代表节点是分时生产区块的)
    我们知道EOS采用的DPOS+BFT,一个节点要成为真正“生产者”,必须被系统其他节点投票出来成为21个超级节点中的一个。同时,被选择为超级节点后,也是和其他20个节点轮流生产。其实,这里存在一个生产者注册流程,也就说一个节点光配置为producer是不够的,还需要通过eosio.system智能合约注册生产者,这个操作权限只授予给了创世块的initial_key的持有人。
producer_plugin_impl::start_block_result producer_plugin_impl::start_block() {
   chain::controller& chain = app().get_plugin<chain_plugin>().chain();
   const auto& hbs = chain.head_block_state();
   _pending_block_mode = pending_block_mode::producing;

   // Not our turn
   //获取当前生产者
   const auto& scheduled_producer = hbs->get_scheduled_producer(block_time);
 
   // If the next block production opportunity is in the present or future, we're synced.
   if( !_production_enabled ) {
      //还在同步,不能生产
      _pending_block_mode = pending_block_mode::speculating;
   } else if( _producers.find(scheduled_producer.producer_name) == _producers.end()) {//检测当前生产者是否属于本节点的
      //不是自己的turn
      _pending_block_mode = pending_block_mode::speculating;
   } else if (signature_provider_itr == _signature_providers.end()) {
      //没有producer的签名,即没有producer的私钥
      elog("Not producing block because I don't have the private key for ${scheduled_key}", ("scheduled_key", scheduled_producer.block_signing_key));
      _pending_block_mode = pending_block_mode::speculating;
   } else if ( _pause_production ) {
      ...
   }
   try {
      uint16_t blocks_to_confirm = 0;
      chain.abort_block();
      //轮到该节点的producer生产了,真正生产区块
      chain.start_block(block_time, blocks_to_confirm);
   } FC_LOG_AND_DROP();
}

#####生产流程图

######转载自:http://blog.csdn.net/itleaks