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

news/2024/11/25 7:56:12/

本文所有实践都是基于EOS v1.0.1,请切到该分支然后对比源码

切换命令:git checkout v1.0.1

提到区块生产和同步,我们肯定有几个疑问?

  • 节点同步

             1)节点从哪里同步数据
                    节点如何知道哪些节点有最新的区块数据以同步数据
             2)genesis文件不同的节点互联会怎么样?
             3)节点什么时候同步数据

  • 区块生产

       节点什么时候生产?节点是一启动就开始生产?还是等同步好了才生产?任何节点都可以生产?
    接下来我们就一一解答

 

区块同步数据

节点信息获取

    节点要同步数据,必须得知道从哪里节点获取他们的区块信息。这个靠显式声明的种子节点解决,种子节点然后会发回更多节点信息。在config.ini文件里添加如下内容即可添加种子节点,这些种子节点信息一般是EOS链的创建者和运营者管理的,比如jungleTestNet测试网,我们要想加入这个测试网络就需要配置如下信息。

    

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

  •   initial_timestamp跟区块生产相关,后面区块生产分析时会用到这个值
  •   initial_key是创建这个genesis的公钥,系统将会以这个公钥创建eosio这个系统账号,而系统的智能合约的核心操作都需要系统账号授权,也就是说initial key控制着eosio.system等智能合约。eosio.system智能合约控制整个系统的。比如生产者注册就必须使用eosio.system智能合约。        

   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;

      }

      ….

}

 

区块生产

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

  •  chain-> _production_enabled==true

       _production_enabled=true有几种情况

            1. config.ini和或者启动时带有enable-stale-production

               这里看到enable-stale-production这个参数的作用了吧,它的意思是哪怕本地区块头是过时的区块,也继续生产。那什么情况下需要置位这个参数呢?私有链有其他生产者之前必须赋值这个变量

               创世块的时间戳(genesis文件中的initial_timestamp字段)是一个确定的值,节点nodeos第一次启动时当前时间肯定远大于这个创世块的时间戳,因而正常情况下,系统应该已经基于这个创世块生产很多后续区块,因而需要先同步到最新再生产新块的。但是由于这个链是你自己刚建立的,你确定没有其他节点基于你本地的区块(包括创世块)生产了其他区块,因此立即基于当前区块生产的新块是合法的且也是你应该做的。

           2.区块同步完成时

              这个很好理解,当我们已经同步下来所有区块时,我们自然可以基于最新的区块生产新的区块

    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;

    }

  •  节点被投票成了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();

}

    

生产流程图

|**************************************************
* 本文来自CSDN博主"爱踢门",喜欢请点关注
* 转载请标明出处:http://blog.csdn.net/itleaks
***************************************************|

如果你喜欢我的文章,请关注
如果你对EOS,ETH技术及开发感兴趣,请关注本公众号"区块链斜杠青年",一起探索区块链未来

有任何疑问或者遇到EOS,ETH的任何问题,请添加本人的微信号itleaks

 


http://www.ppmy.cn/news/196351.html

相关文章

EOS genesis 详解

genesis.json 不同genesis文件&#xff0c;就代表是不同的链&#xff0c;这样的节点其实是不能互联的 在整个开发过程中&#xff0c;测试网络/主网API测试都是一样&#xff0c;链接方式也是一样的&#xff0c; 只是需要修改 config.ini 文件和 genesis.json 文件&#xff0c;…

EOS系列 - 内存映射文件数据库 - chainbase

ChainBase-快速版本控制的事务性数据库 特征 支持具有多个索引的多个对象(表) (基于boost :: multi_index_container)状态在多个过程之间是持久且可共享的具有撤销更改功能的嵌套事务写入 并发访问 默认情况下&#xff0c;ChainBase不提供任何同步&#xff0c;并具有与任何…

EOS (4)账户

1. 定义 账户是存储在区块链中的人类可读标识符。 1.1 形式 EOS账户有12个字符的限制&#xff08;允许字符a&#xff5e;z、1&#xff5e;5&#xff09;&#xff0c;这12个字符是从64位整数的base-32编码派生而来的。 64位整数是本地机器字符的大小&#xff0c;而数据库索引…

佳能 RF800mm F5.6、RF1200mm F8 评测

F800mmF5.6 L IS USM 和 RF1200mm F8 L IS USM 的整体设计相对于同焦段的 EF 镜头更为紧凑、轻量。RF800mmF5.6 L IS USM 整体长度约为 432mm&#xff0c;直径约为 163mm&#xff0c;重量约为 3140g&#xff0c;和此前的 EF800mmF5.6 L IS USM 相比&#xff0c;长度减少约 29mm…

EOSIO概述

作者&#xff1a;michael比特天梯 架构概述 了解EOSIO我们从官方给出的以下架构图来入手&#xff0c;先看图如下&#xff1a; EOSIO主要包含了以下组件&#xff0c;nodeos、keosd和cleos nodeos&#xff1a; nodeeosnodeos&#xff0c;EOS系统核心进程&#xff0c;也就是所谓…

[EOS源码分析]6.EOS特殊智能合约eosio

这里说的eosio智能合约不是泛指eos的智能合约&#xff0c;它是一个特殊的具体的合约。它本事可大了&#xff0c;我们一起来看看它有哪些功能 负责智能合约部署 大家有注意到如下红色字体的log吗 $ cleos set contract hello.code ../eos-contract/hello -p hello.code Publish…

什么是EOS,它的作用及意义是什么

想了解更多区块链中的技术开发经验&#xff0c;请百度【链客区块链技术问答社区】 什么是EOSIO&#xff1f; EOS.IO软件引入了新的区块链架构&#xff0c;旨在实现分散式应用程序的垂直和水平缩放。这是通过创建一个可以构建应用程序的类似操作系统的构造来实现的。该软件提供…

EOS Contract 合约

EOS Contract 合约 在部署合约发行token&#xff0c;至少需要三类用户 类型Action账户职责合约账户createeosio.token合约托管账户&#xff0c;用来创建合约发行账户issuekevin发行Token交易账户transfercoco实现账户之间Token转移 部署合约 部署合约&#xff0c;需要创建一…