camphortree avatar

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

camphortree

Published: 14 Aug 2018 › Updated: 14 Aug 2018eos源码解析(三):dpos共识源码

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的关键
C++
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
JSON
{
"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。
C++
........
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:已激活的生产者列表
有了对这几个定义的解释,代码变得很清晰
C++
//计算不可逆区块,将不可逆区块候选名单中从小到大排,选出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
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。
C++
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为:
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"
},
{
"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": [
1,
1,
1,
1, 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的关键
C++
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
JSON
{
"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。
C++
........
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:已激活的生产者列表
有了对这几个定义的解释,代码变得很清晰
C++
//计算不可逆区块,将不可逆区块候选名单中从小到大排,选出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
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。
C++
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为:
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
}
综上,实际上dops在代码里面使用是分作两步的。
1,2/3共识,符合2/3认可的最高区块可以进入”由生产者确定的不可逆区块候选名单”。由于出块轮转,每个生产者所能见证的高度是不一样的。
2,1/3共识,从“由生产者确定的不可逆区块候选名单”中选择1/3高度的地方得到不可逆区块。
仔细一想,2还是加强了不可逆区块的共识。

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

Written by

Read more #eos posts


Best Posts From camphortree

We have not curated any of camphortree's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.

More Posts From camphortree