美文网首页EOS区块链应用
eos源码解析(三):dpos共识源码

eos源码解析(三):dpos共识源码

作者: 荒原葱郁 | 来源:发表于2018-08-14 16:27 被阅读479次

    原创不易,转载请注明出处https://steemit.com/eos/@camphortree/eos-dpos

    eos的出块流程大致如下:

    Plain Text
    ........
    //启动生产插件
    producer_plugin::plugin_startup();
    ........
    //出块循环
    my->schedule_production_loop();
    .........
    //出块
    auto result = start_block();
    ..........
    //签名和提交
    auto res = self->maybe_produce_block();
    ..........
    

    以上步骤的核心在于:auto result = start_block();
    这个函数更跟下去会发现,重点在于:
    Plain Text
    chain.start_block(block_time, blocks_to_confirm);
    这个函数才是dops的关键

    void start_block( block_timestamp_type when, uint16_t confirm_block_count, controller::block_status s ) {
          FC_ASSERT( !pending );
    ​
          FC_ASSERT( db.revision() == head->block_num, "",
                    ("db.revision()", db.revision())("controller_head_block", head->block_num)("fork_db_head_block", fork_db.head()->block_num) );
    ​
          auto guard_pending = fc::make_scoped_exit([this](){
             pending.reset();
          });
    ​
          pending = db.start_undo_session(true);
    ​
          pending->_block_status = s;
          //由当前区块头创建一个新的block_state ---->>>>> 11111
          pending->_pending_block_state = std::make_shared<block_state>( *head, when ); // promotes pending schedule (if any) to active
          pending->_pending_block_state->in_current_chain = true;
          //确认链上的区块  ----->>>>> 22222
          pending->_pending_block_state->set_confirmed(confirm_block_count);
          //更新激活的生产者 ----->>>> 33333
          auto was_pending_promoted = pending->_pending_block_state->maybe_promote_pending();
           const auto& gpo = db.get<global_property_object>();
          if( gpo.proposed_schedule_block_num.valid() && // if there is a proposed schedule that was proposed in a block ...
              ( *gpo.proposed_schedule_block_num <= pending->_pending_block_state->dpos_irreversible_blocknum ) && // ... that has now become irreversible ...
              pending->_pending_block_state->pending_schedule.producers.size() == 0 && // ... and there is room for a new pending schedule ...
              !was_pending_promoted // ... and not just because it was promoted to active at the start of this block, then:
            )
          {
             // Promote proposed schedule to pending schedule.
             if( !replaying ) {
                ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ",
                      ("proposed_num", *gpo.proposed_schedule_block_num)("n", pending->_pending_block_state->block_num)
                      ("lib", pending->_pending_block_state->dpos_irreversible_blocknum)
                      ("schedule", static_cast<producer_schedule_type>(gpo.proposed_schedule) ) );
             }
             pending->_pending_block_state->set_new_producers( gpo.proposed_schedule );
             db.modify( gpo, [&]( auto& gp ) {
                gp.proposed_schedule_block_num = optional<block_num_type>();
                gp.proposed_schedule.clear();
             });
    ​
              std::cout<<"\n========555==========\n";
              std::cout<< fc::json::to_string(*pending->_pending_block_state)<<"\n";
              std::cout<<"==========555========\n";
          }
    

    11111,22222,33333这三步涉及到的数据结构是block_state

    {
        "id": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", 
        "block_num": 1418, 
        "header": {
            "timestamp": "2018-08-04T07:31:47.500", 
            "producer": "accountnum33", 
            "confirmed": 0, 
            "previous": "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", 
            "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "action_mroot": "eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9", 
            "schedule_version": 1, 
            "header_extensions": [ ], 
            "producer_signature": "SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ"
        }, 
        "dpos_proposed_irreversible_blocknum": 1370, 
        "dpos_irreversible_blocknum": 1370, 
        "bft_irreversible_blocknum": 0, 
        "pending_schedule_lib_num": 1369, 
        "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", 
        "pending_schedule": {
            "version": 1, 
            "producers": [ ]
        }, 
        "active_schedule": {
            "version": 1, 
            "producers": [
                {
                    "producer_name": "accountnum11", 
                    "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt"
                }, 
                {
                    "producer_name": "accountnum22", 
                    "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt"
                }, 
                {
                    "producer_name": "accountnum33", 
                    "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt"
                }
            ]
        }, 
        "blockroot_merkle": {
            "_active_nodes": [
                "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", 
                "ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544", 
                "0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18", 
                "af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58", 
                "e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434", 
                "5a36ca2325d5f7b2de22b98de7cf70c0a06cf391fc17f1d242b964d16146dee9"
            ], 
            "_node_count": 1417
        }, 
        "producer_to_last_produced": [
            [
                "accountnum11", 
                1394
            ], 
            [
                "accountnum22", 
                1406
            ], 
            [
                "accountnum33", 
                1418
            ], 
            [
                "eosio", 
                1370
            ]
        ], 
        "producer_to_last_implied_irb": [
            [
                "accountnum11", 
                1370
            ], 
            [
                "accountnum22", 
                1370
            ], 
            [
                "accountnum33", 
                1370
            ]
        ], 
        "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt", 
        "confirm_count": [
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2
        ], 
        "confirmations": [ ], 
        "block": {
            "timestamp": "2018-08-04T07:31:47.500", 
            "producer": "accountnum33", 
            "confirmed": 0, 
            "previous": "0000058980ac0382075af4216ddbd7be1eae1690a278d7cc04a61570f307b39d", 
            "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "action_mroot": "eaadc508183ba966551e48dba3a44505a95e6a42d7a53f435db39c3dec3584c9", 
            "schedule_version": 1, 
            "header_extensions": [ ], 
            "producer_signature": "SIG_K1_KVyKdpLpEXPQUvRR5jZL4FMYvFDNic6tyvX6icLXFKhoriskCg21esduAceX2cZDv2GwcB12TxdneeKMwfzWB2TckZpZvQ", 
            "transactions": [ ], 
            "block_extensions": [ ]
        }, 
        "validated": true, 
        "in_current_chain": true
    }
    

    第一步:从当前块头出发,生成一个新的block_state。

    ........
    result.producer_to_last_implied_irb[prokey.producer_name] = result.dpos_proposed_irreversible_blocknum;
    result.dpos_irreversible_blocknum                         = result.calc_dpos_last_irreversible(); 
    ​
    /// grow the confirmed count
    static_assert(std::numeric_limits<uint8_t>::max() >= (config::max_producers * 2 / 3) + 1, "8bit confirmations may not be able to hold all of the needed confirmations");
    ​
    // This uses the previous block active_schedule because thats the "schedule" that signs and therefore confirms _this_ block
    auto num_active_producers = active_schedule.producers.size();
    uint32_t required_confs = (uint32_t)(num_active_producers * 2 / 3) + 1;
    ​
    if( confirm_count.size() < config::maximum_tracked_dpos_confirmations ) {
       result.confirm_count.reserve( confirm_count.size() + 1 );
       result.confirm_count  = confirm_count;
       result.confirm_count.resize( confirm_count.size() + 1 );
       result.confirm_count.back() = (uint8_t)required_confs;
    } else {
       result.confirm_count.resize( confirm_count.size() );
       memcpy( &result.confirm_count[0], &confirm_count[1], confirm_count.size() - 1 );
       result.confirm_count.back() = (uint8_t)required_confs;
    }
    .......
    

    当前3个生产节点,required_confs为3,所以11111结束后,confirm_count中会压入3,即当前出产块所需的确认个数。
    下面直接给出几个解释:
    confirm_count:区块所需确认个数表,从当前出产块往前推,最后的元素为当前出产块所需的确认个数
    producer_to_last_implied_irb:由生产者确定的不可逆区块候选名单
    dpos_irreversible_blocknum:不可逆区块
    dpos_proposed_irreversible_blocknum:候选不可逆区块
    active_schedule:已激活的生产者列表
    有了对这几个定义的解释,代码变得很清晰

    //计算不可逆区块,将不可逆区块候选名单中从小到大排,选出1/3处的区块号作为不可逆区块
    uint32_t block_header_state::calc_dpos_last_irreversible()const {
       vector<uint32_t> blocknums; blocknums.reserve( producer_to_last_implied_irb.size() );
       for( auto& i : producer_to_last_implied_irb ) {
          blocknums.push_back(i.second);
       }
       /// 2/3 must be greater, so if I go 1/3 into the list sorted from low to high, then 2/3 are greater
    ​
       if( blocknums.size() == 0 ) return 0;
       /// TODO: update to nth_element 
       std::sort( blocknums.begin(), blocknums.end() );
       return blocknums[ (blocknums.size()-1) / 3 ];
    }
    

    上面的程序中出现两次3分之几的算法,第一次是required_confs,没毛病,一个区块要得到2/3个生产者的确认。但是得到2/3的确认与不可逆区块的联系不仅仅是这个,严格来说,不可逆区块是从不可逆区块候选名单中选择出来的。
    第11111步之后的json

    {
        "id": "0000000000000000000000000000000000000000000000000000000000000000", 
        "block_num": 1419, 
        "header": {
            "timestamp": "2018-08-04T07:31:48.000", 
            "producer": "accountnum11", 
            "confirmed": 1, 
            "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", 
            "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "schedule_version": 1, 
            "header_extensions": [ ], 
            "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne"
        }, 
        "dpos_proposed_irreversible_blocknum": 1370, 
        "dpos_irreversible_blocknum": 1370, 
        "bft_irreversible_blocknum": 0, 
        "pending_schedule_lib_num": 1369, 
        "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", 
        "pending_schedule": {
            "version": 1, 
            "producers": [ ]
        }, 
        "active_schedule": {
            "version": 1, 
            "producers": [
                {
                    "producer_name": "accountnum11", 
                    "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt"
                }, 
                {
                    "producer_name": "accountnum22", 
                    "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt"
                }, 
                {
                    "producer_name": "accountnum33", 
                    "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt"
                }
            ]
        }, 
        "blockroot_merkle": {
            "_active_nodes": [
                "f8a83438724a996caaa42231c39b757bec3d1b6fb0bd2c982812eb2f7fadc377", 
                "ab53f4807abffafbcf15fa6edd6ffe72cf73d18689403e389127015b4fd1e544", 
                "0275481b63dcb217603558835292d4057d39f9a5d41c9d5302230d379a992b18", 
                "af944001e02b80671b6ea50e5f83969fc2db5e56671ff83700a9018336d0fc58", 
                "e1ed7b7a82f5507110497fd41f2098a269ffbe8202de0f57393b4aeb27792434", 
                "2553b9e4b8225b0f311c2a382fccd027e90f909319616b75933b56f19d20c440"
            ], 
            "_node_count": 1418
        }, 
        "producer_to_last_produced": [
            [
                "accountnum11", 
                1419
            ], 
            [
                "accountnum22", 
                1406
            ], 
            [
                "accountnum33", 
                1418
            ], 
            [
                "eosio", 
                1370
            ]
        ], 
        "producer_to_last_implied_irb": [
            [
                "accountnum11", 
                1370
            ], 
            [
                "accountnum22", 
                1370
            ], 
            [
                "accountnum33", 
                1370
            ]
        ], 
        "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt", 
        "confirm_count": [
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            2, 
            3  
        ], 
        "confirmations": [ ], 
        "block": {
            "timestamp": "2018-08-04T07:31:48.000", 
            "producer": "accountnum11", 
            "confirmed": 1, 
            "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", 
            "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "schedule_version": 1, 
            "header_extensions": [ ], 
            "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne", 
            "transactions": [ ], 
            "block_extensions": [ ]
        }, 
        "validated": false, 
        "in_current_chain": false
    }
    

    到第22222步,这个函数确定了候选不可逆区与确认列表的关系
    num_prev_blocks是指当前节点最后一次出块到现在的间隔,本文使用3个节点,故间隔为24
    比如出块顺序为accountnum33--》accountnum11--》accountnum22--》accountnum33
    accountnum33会把自己的块确认一次
    accountnum11的时候会把33出过的块和自己确认一次。。。。
    确认就是将confirmed数组的相应元素减去1,第一步得到的json中:
    "confirm_count":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3]
    此时num_prev_blocks = 24
    所以以下程序的工作是先将confirm_count元素从尾到头减去1,最多遍历num_prev_blocks和confirm_count大小中最小的。
    "confirm_count":[2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,2]
    由于出现了“0”元素,将0元素的区块号(1406)记录在dpos_proposed_irreversible_blocknum中,然后在更新数组,将0之前的元素去掉。
    "confirm_count":[1,1,1,1,1,1,1,1,1,1,1,1,2]
    所以这样看来,dpos_proposed_irreversible_blocknum就是本生产节点在本块确定的候选不可逆区块。而这个参数会在下一次出块时打包进producer_to_last_implied_irb。

    void block_header_state::set_confirmed( uint16_t num_prev_blocks ) {
       /*
       idump((num_prev_blocks)(confirm_count.size()));
    ​
       for( uint32_t i = 0; i < confirm_count.size(); ++i ) {
          std::cerr << "confirm_count["<<i<<"] = " << int(confirm_count[i]) << "\n";
       }
       */
       header.confirmed = num_prev_blocks;
    ​
       int32_t i = (int32_t)(confirm_count.size() - 1);
       uint32_t blocks_to_confirm = num_prev_blocks + 1; /// confirm the head block too
       while( i >= 0 && blocks_to_confirm ) {
          --confirm_count[i];
          //idump((confirm_count[i]));
          if( confirm_count[i] == 0 )
          {
             uint32_t block_num_for_i = block_num - (uint32_t)(confirm_count.size() - 1 - i);
             dpos_proposed_irreversible_blocknum = block_num_for_i;
             //idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum));
    ​
             if (i == confirm_count.size() - 1) {
                confirm_count.resize(0);
             } else {
                memmove( &confirm_count[0], &confirm_count[i + 1], confirm_count.size() - i  - 1);
                confirm_count.resize( confirm_count.size() - i - 1 );
             }
    ​
             return;
          }
          --i;
          --blocks_to_confirm;
       }
    }
    

    第22222步之后的输出json为:

    {
        "id": "0000000000000000000000000000000000000000000000000000000000000000", 
        "block_num": 1419, 
        "header": {
            "timestamp": "2018-08-04T07:31:48.000", 
            "producer": "accountnum11", 
            "confirmed": 24, 
            "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", 
            "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "schedule_version": 1, 
            "header_extensions": [ ], 
            "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne"
        }, 
        "dpos_proposed_irreversible_blocknum": 1406, 
        "dpos_irreversible_blocknum": 1370, 
        "bft_irreversible_blocknum": 0, 
        "pending_schedule_lib_num": 1369, 
        "pending_schedule_hash": "b4ea72a3e20628a54028e681258cd1e6a46b20ae07210836f29087f9f8f37346", 
        "pending_schedule": {
            "version": 1, 
            "producers": [ ]
        }, 
        "active_schedule": {
            "version": 1, 
            "producers": [
                {
                    "producer_name": "accountnum11", 
                    "block_signing_key": "EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt"
                }, 
                {
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            1, 
            2
        ], 
        "confirmations": [ ], 
        "block": {
            "timestamp": "2018-08-04T07:31:48.000", 
            "producer": "accountnum11", 
            "confirmed": 1, 
            "previous": "0000058af1b33d45e4ec927e52b74e8568422f45245dc0399c144bdff6dc16d8", 
            "transaction_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "action_mroot": "0000000000000000000000000000000000000000000000000000000000000000", 
            "schedule_version": 1, 
            "header_extensions": [ ], 
            "producer_signature": "SIG_K1_111111111111111111111111111111111111111111111111111111111111111116uk5ne", 
            "transactions": [ ], 
            "block_extensions": [ ]
        }, 
        "validated": false, 
        "in_current_chain": true
    }
    

    综上,实际上dpos在代码里面使用是分作两步的。
    1,2/3共识,符合2/3认可的最高区块可以进入”由生产者确定的不可逆区块候选名单”。由于出块轮转,每个生产者所能见证的高度是不一样的。
    2,1/3共识,从“由生产者确定的不可逆区块候选名单”中选择1/3高度的地方得到不可逆区块。
    仔细一想,2还是加强了不可逆区块的共识。

    相关文章

      网友评论

      本文标题:eos源码解析(三):dpos共识源码

      本文链接:https://www.haomeiwen.com/subject/qxtxuftx.html