Skip to content

Commit f00b6d0

Browse files
committed
Implement BFT Double Confirmation #3088
- this issue requires 2/3+1 of producers to implicitly agree on an irreversible block before the block is marked as irreversible.
1 parent 1b16843 commit f00b6d0

File tree

4 files changed

+59
-27
lines changed

4 files changed

+59
-27
lines changed

libraries/chain/block_header_state.cpp

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ namespace eosio { namespace chain {
1515
return active_schedule.producers[index];
1616
}
1717

18+
uint32_t block_header_state::calc_dpos_last_irreversible()const {
19+
vector<uint32_t> blocknums; blocknums.reserve( producer_to_last_implied_irb.size() );
20+
for( auto& i : producer_to_last_implied_irb ) {
21+
blocknums.push_back(i.second);
22+
}
23+
/// 2/3 must be greater, so if I go 1/3 into the list sorted from low to high, then 2/3 are greater
24+
25+
if( blocknums.size() == 0 ) return 0;
26+
/// TODO: update to nth_element
27+
std::sort( blocknums.begin(), blocknums.end() );
28+
return blocknums[ (blocknums.size()-1) / 3 ];
29+
}
1830

1931
/**
2032
* Generate a template block header state for a given block time, it will not
@@ -29,28 +41,32 @@ namespace eosio { namespace chain {
2941
} else {
3042
(when = header.timestamp).slot++;
3143
}
32-
result.header.timestamp = when;
33-
result.header.previous = id;
34-
result.header.schedule_version = active_schedule.version;
35-
36-
auto prokey = get_scheduled_producer(when);
37-
result.block_signing_key = prokey.block_signing_key;
38-
result.header.producer = prokey.producer_name;
39-
40-
result.pending_schedule_lib_num = pending_schedule_lib_num;
41-
result.pending_schedule_hash = pending_schedule_hash;
42-
result.block_num = block_num + 1;
43-
result.producer_to_last_produced = producer_to_last_produced;
44+
result.header.timestamp = when;
45+
result.header.previous = id;
46+
result.header.schedule_version = active_schedule.version;
47+
48+
auto prokey = get_scheduled_producer(when);
49+
result.block_signing_key = prokey.block_signing_key;
50+
result.header.producer = prokey.producer_name;
51+
52+
result.pending_schedule_lib_num = pending_schedule_lib_num;
53+
result.pending_schedule_hash = pending_schedule_hash;
54+
result.block_num = block_num + 1;
55+
result.producer_to_last_produced = producer_to_last_produced;
56+
result.producer_to_last_implied_irb = producer_to_last_implied_irb;
4457
result.producer_to_last_produced[prokey.producer_name] = result.block_num;
4558
result.blockroot_merkle = blockroot_merkle;
4659
result.blockroot_merkle.append( id );
4760

4861
auto block_mroot = result.blockroot_merkle.get_root();
4962

50-
result.active_schedule = active_schedule;
51-
result.pending_schedule = pending_schedule;
52-
result.dpos_irreversible_blocknum = dpos_irreversible_blocknum;
53-
result.bft_irreversible_blocknum = bft_irreversible_blocknum;
63+
result.active_schedule = active_schedule;
64+
result.pending_schedule = pending_schedule;
65+
result.dpos_proposed_irreversible_blocknum = dpos_proposed_irreversible_blocknum;
66+
result.bft_irreversible_blocknum = bft_irreversible_blocknum;
67+
68+
result.producer_to_last_implied_irb[prokey.producer_name] = result.dpos_proposed_irreversible_blocknum;
69+
result.dpos_irreversible_blocknum = result.calc_dpos_last_irreversible();
5470

5571
/// grow the confirmed count
5672
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");
@@ -88,7 +104,19 @@ namespace eosio { namespace chain {
88104
new_producer_to_last_produced[pro.producer_name] = dpos_irreversible_blocknum;
89105
}
90106
}
107+
108+
flat_map<account_name,uint32_t> new_producer_to_last_implied_irb;
109+
for( const auto& pro : active_schedule.producers ) {
110+
auto existing = producer_to_last_implied_irb.find( pro.producer_name );
111+
if( existing != producer_to_last_implied_irb.end() ) {
112+
new_producer_to_last_implied_irb[pro.producer_name] = existing->second;
113+
} else {
114+
new_producer_to_last_implied_irb[pro.producer_name] = dpos_irreversible_blocknum;
115+
}
116+
}
117+
91118
producer_to_last_produced = move( new_producer_to_last_produced );
119+
producer_to_last_implied_irb = move( new_producer_to_last_implied_irb);
92120
producer_to_last_produced[header.producer] = block_num;
93121

94122
return true;
@@ -175,7 +203,7 @@ namespace eosio { namespace chain {
175203
if( confirm_count[i] == 0 )
176204
{
177205
uint32_t block_num_for_i = block_num - (uint32_t)(confirm_count.size() - 1 - i);
178-
dpos_irreversible_blocknum = block_num_for_i;
206+
dpos_proposed_irreversible_blocknum = block_num_for_i;
179207
//idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum));
180208

181209
if (i == confirm_count.size() - 1) {

libraries/chain/include/eosio/chain/block_header_state.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct block_header_state {
1212
block_id_type id;
1313
uint32_t block_num = 0;
1414
signed_block_header header;
15+
uint32_t dpos_proposed_irreversible_blocknum = 0;
1516
uint32_t dpos_irreversible_blocknum = 0;
1617
uint32_t bft_irreversible_blocknum = 0;
1718
uint32_t pending_schedule_lib_num = 0; /// last irr block num
@@ -20,6 +21,7 @@ struct block_header_state {
2021
producer_schedule_type active_schedule;
2122
incremental_merkle blockroot_merkle;
2223
flat_map<account_name,uint32_t> producer_to_last_produced;
24+
flat_map<account_name,uint32_t> producer_to_last_implied_irb;
2325
public_key_type block_signing_key;
2426
vector<uint8_t> confirm_count;
2527
vector<header_confirmation> confirmations;
@@ -34,7 +36,7 @@ struct block_header_state {
3436

3537

3638
bool has_pending_producers()const { return pending_schedule.producers.size(); }
37-
//uint32_t calc_dpos_last_irreversible()const;
39+
uint32_t calc_dpos_last_irreversible()const;
3840
bool is_active_producer( account_name n )const;
3941

4042
/*
@@ -56,8 +58,8 @@ struct block_header_state {
5658
} } /// namespace eosio::chain
5759

5860
FC_REFLECT( eosio::chain::block_header_state,
59-
(id)(block_num)(header)(dpos_irreversible_blocknum)(bft_irreversible_blocknum)
61+
(id)(block_num)(header)(dpos_proposed_irreversible_blocknum)(dpos_irreversible_blocknum)(bft_irreversible_blocknum)
6062
(pending_schedule_lib_num)(pending_schedule_hash)
6163
(pending_schedule)(active_schedule)(blockroot_merkle)
62-
(producer_to_last_produced)(block_signing_key)
64+
(producer_to_last_produced)(producer_to_last_implied_irb)(block_signing_key)
6365
(confirm_count)(confirmations) )

unittests/database_tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ BOOST_AUTO_TEST_SUITE(database_tests)
7777
};
7878

7979
// Check the last irreversible block number is set correctly
80-
const auto expected_last_irreversible_block_number = calc_exp_last_irr_block_num(num_of_blocks_to_prod) + 1;
80+
const auto expected_last_irreversible_block_number = calc_exp_last_irr_block_num(num_of_blocks_to_prod);
8181
BOOST_TEST(test.control->head_block_state()->dpos_irreversible_blocknum == expected_last_irreversible_block_number);
8282
// Check that block 201 cannot be found (only 20 blocks exist)
8383
BOOST_TEST(test.control->fetch_block_by_number(num_of_blocks_to_prod + 1 + 1) == nullptr);
@@ -87,7 +87,7 @@ BOOST_AUTO_TEST_SUITE(database_tests)
8787
test.produce_blocks(next_num_of_blocks_to_prod);
8888

8989
const auto next_expected_last_irreversible_block_number = calc_exp_last_irr_block_num(
90-
num_of_blocks_to_prod + next_num_of_blocks_to_prod) + 1;
90+
num_of_blocks_to_prod + next_num_of_blocks_to_prod);
9191
// Check the last irreversible block number is updated correctly
9292
BOOST_TEST(test.control->head_block_state()->dpos_irreversible_blocknum == next_expected_last_irreversible_block_number);
9393
// Check that block 201 can now be found

unittests/producer_schedule_tests.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, TESTER ) try {
219219
BOOST_CHECK_EQUAL( control->pending_producers().version, 1 );
220220
BOOST_CHECK_EQUAL( true, compare_schedules( sch1, control->pending_producers() ) );
221221
BOOST_CHECK_EQUAL( control->active_producers().version, 0 );
222+
produce_block();
222223
produce_block(); // Starts new block which promotes the pending schedule to active
223224
BOOST_CHECK_EQUAL( control->active_producers().version, 1 );
224225
BOOST_CHECK_EQUAL( true, compare_schedules( sch1, control->active_producers() ) );
@@ -234,18 +235,19 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, TESTER ) try {
234235
BOOST_REQUIRE_EQUAL( true, control->proposed_producers().valid() );
235236
BOOST_CHECK_EQUAL( true, compare_schedules( sch2, *control->proposed_producers() ) );
236237

237-
produce_block(); // Alice produces the last block of her first round.
238+
produce_block();
239+
produce_blocks(23); // Alice produces the last block of her first round.
238240
// Bob's first block (which advances LIB to Alice's last block) is started but not finalized.
239241
BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(alice) );
240242
BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(bob) );
241243
BOOST_CHECK_EQUAL( control->pending_producers().version, 2 );
242244

243-
produce_blocks(11); // Bob produces his first 11 blocks
245+
produce_blocks(12); // Bob produces his first 11 blocks
244246
BOOST_CHECK_EQUAL( control->active_producers().version, 1 );
245-
produce_block(); // Bob produces his 12th block.
247+
produce_blocks(12); // Bob produces his 12th block.
246248
// Alice's first block of the second round is started but not finalized (which advances LIB to Bob's last block).
247-
BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(bob) );
248-
BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(alice) );
249+
BOOST_REQUIRE_EQUAL( control->head_block_producer(), N(alice) );
250+
BOOST_REQUIRE_EQUAL( control->pending_block_state()->header.producer, N(bob) );
249251
BOOST_CHECK_EQUAL( control->active_producers().version, 2 );
250252
BOOST_CHECK_EQUAL( true, compare_schedules( sch2, control->active_producers() ) );
251253

0 commit comments

Comments
 (0)