Skip to content

Commit

Permalink
GH-1523 GH-1631 Transition from dpos to hotstuff
Browse files Browse the repository at this point in the history
Producer schedule changes under hotstuff in next,next producer round.
  • Loading branch information
heifner committed Sep 14, 2023
1 parent e127b56 commit bf3b284
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 112 deletions.
199 changes: 112 additions & 87 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ namespace eosio { namespace chain {
const auto& protocol_features = pfa->protocol_features;
return digest && protocol_features.find(*digest) != protocol_features.end();
}

uint32_t get_next_next_round_block_num( block_timestamp_type t, uint32_t block_num ) {
auto index = t.slot % config::producer_repetitions;
// remainder of current + next round
return block_num + config::producer_repetitions - index + config::producer_repetitions;
}
}

producer_authority block_header_state::get_scheduled_producer( block_timestamp_type t )const {
Expand All @@ -38,6 +44,7 @@ namespace eosio { namespace chain {
}

pending_block_header_state block_header_state::next( block_timestamp_type when,
bool hotstuff_activated,
uint16_t num_prev_blocks_to_confirm )const
{
pending_block_header_state result;
Expand All @@ -50,18 +57,9 @@ namespace eosio { namespace chain {

auto proauth = get_scheduled_producer(when);

auto itr = producer_to_last_produced.find( proauth.producer_name );
if( itr != producer_to_last_produced.end() ) {
EOS_ASSERT( itr->second < (block_num+1) - num_prev_blocks_to_confirm, producer_double_confirm,
"producer ${prod} double-confirming known range",
("prod", proauth.producer_name)("num", block_num+1)
("confirmed", num_prev_blocks_to_confirm)("last_produced", itr->second) );
}

result.block_num = block_num + 1;
result.previous = id;
result.timestamp = when;
result.confirmed = num_prev_blocks_to_confirm;
result.active_schedule_version = active_schedule.version;
result.prev_activated_protocol_features = activated_protocol_features;

Expand All @@ -72,103 +70,129 @@ namespace eosio { namespace chain {
result.blockroot_merkle = blockroot_merkle;
result.blockroot_merkle.append( id );

/// 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;
result.prev_pending_schedule = pending_schedule;

if (hotstuff_activated) {
result.confirmed = hs_block_confirmed;
result.dpos_proposed_irreversible_blocknum = 0;
// fork_database will prefer hotstuff blocks over dpos blocks
result.dpos_irreversible_blocknum = hs_dpos_irreversible_blocknum;
// Change to active on the next().next() producer block_num
if( pending_schedule.schedule.producers.size() &&
block_num >= detail::get_next_next_round_block_num(when, pending_schedule.schedule_lib_num)) {
result.active_schedule = pending_schedule.schedule;
result.was_pending_promoted = true;
} else {
result.active_schedule = active_schedule;
}

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;
}
auto itr = producer_to_last_produced.find( proauth.producer_name );
if( itr != producer_to_last_produced.end() ) {
EOS_ASSERT( itr->second < (block_num+1) - num_prev_blocks_to_confirm, producer_double_confirm,
"producer ${prod} double-confirming known range",
("prod", proauth.producer_name)("num", block_num+1)
("confirmed", num_prev_blocks_to_confirm)("last_produced", itr->second) );
}

auto new_dpos_proposed_irreversible_blocknum = dpos_proposed_irreversible_blocknum;

int32_t i = (int32_t)(result.confirm_count.size() - 1);
uint32_t blocks_to_confirm = num_prev_blocks_to_confirm + 1; /// confirm the head block too
while( i >= 0 && blocks_to_confirm ) {
--result.confirm_count[i];
//idump((confirm_count[i]));
if( result.confirm_count[i] == 0 )
{
uint32_t block_num_for_i = result.block_num - (uint32_t)(result.confirm_count.size() - 1 - i);
new_dpos_proposed_irreversible_blocknum = block_num_for_i;
//idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum));

if (i == static_cast<int32_t>(result.confirm_count.size() - 1)) {
result.confirm_count.resize(0);
} else {
memmove( &result.confirm_count[0], &result.confirm_count[i + 1], result.confirm_count.size() - i - 1);
result.confirm_count.resize( result.confirm_count.size() - i - 1 );
}
result.confirmed = num_prev_blocks_to_confirm;

break;
}
--i;
--blocks_to_confirm;
}
/// 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;
}

result.dpos_proposed_irreversible_blocknum = new_dpos_proposed_irreversible_blocknum;
result.dpos_irreversible_blocknum = calc_dpos_last_irreversible( proauth.producer_name );
auto new_dpos_proposed_irreversible_blocknum = dpos_proposed_irreversible_blocknum;

int32_t i = (int32_t)(result.confirm_count.size() - 1);
uint32_t blocks_to_confirm = num_prev_blocks_to_confirm + 1; /// confirm the head block too
while( i >= 0 && blocks_to_confirm ) {
--result.confirm_count[i];
//idump((confirm_count[i]));
if( result.confirm_count[i] == 0 )
{
uint32_t block_num_for_i = result.block_num - (uint32_t)(result.confirm_count.size() - 1 - i);
new_dpos_proposed_irreversible_blocknum = block_num_for_i;
//idump((dpos2_lib)(block_num)(dpos_irreversible_blocknum));

if (i == static_cast<int32_t>(result.confirm_count.size() - 1)) {
result.confirm_count.resize(0);
} else {
memmove( &result.confirm_count[0], &result.confirm_count[i + 1], result.confirm_count.size() - i - 1);
result.confirm_count.resize( result.confirm_count.size() - i - 1 );
}

result.prev_pending_schedule = pending_schedule;
break;
}
--i;
--blocks_to_confirm;
}

if( pending_schedule.schedule.producers.size() &&
result.dpos_irreversible_blocknum >= pending_schedule.schedule_lib_num )
{
result.active_schedule = pending_schedule.schedule;
result.dpos_proposed_irreversible_blocknum = new_dpos_proposed_irreversible_blocknum;
result.dpos_irreversible_blocknum = calc_dpos_last_irreversible( proauth.producer_name );

if( pending_schedule.schedule.producers.size() &&
result.dpos_irreversible_blocknum >= pending_schedule.schedule_lib_num )
{
result.active_schedule = pending_schedule.schedule;

flat_map<account_name,uint32_t> new_producer_to_last_produced;
flat_map<account_name,uint32_t> new_producer_to_last_produced;

for( const auto& pro : result.active_schedule.producers ) {
if( pro.producer_name == proauth.producer_name ) {
new_producer_to_last_produced[pro.producer_name] = result.block_num;
} else {
auto existing = producer_to_last_produced.find( pro.producer_name );
if( existing != producer_to_last_produced.end() ) {
new_producer_to_last_produced[pro.producer_name] = existing->second;
for( const auto& pro : result.active_schedule.producers ) {
if( pro.producer_name == proauth.producer_name ) {
new_producer_to_last_produced[pro.producer_name] = result.block_num;
} else {
new_producer_to_last_produced[pro.producer_name] = result.dpos_irreversible_blocknum;
auto existing = producer_to_last_produced.find( pro.producer_name );
if( existing != producer_to_last_produced.end() ) {
new_producer_to_last_produced[pro.producer_name] = existing->second;
} else {
new_producer_to_last_produced[pro.producer_name] = result.dpos_irreversible_blocknum;
}
}
}
}
new_producer_to_last_produced[proauth.producer_name] = result.block_num;
new_producer_to_last_produced[proauth.producer_name] = result.block_num;

result.producer_to_last_produced = std::move( new_producer_to_last_produced );
result.producer_to_last_produced = std::move( new_producer_to_last_produced );

flat_map<account_name,uint32_t> new_producer_to_last_implied_irb;
flat_map<account_name,uint32_t> new_producer_to_last_implied_irb;

for( const auto& pro : result.active_schedule.producers ) {
if( pro.producer_name == proauth.producer_name ) {
new_producer_to_last_implied_irb[pro.producer_name] = dpos_proposed_irreversible_blocknum;
} else {
auto existing = producer_to_last_implied_irb.find( pro.producer_name );
if( existing != producer_to_last_implied_irb.end() ) {
new_producer_to_last_implied_irb[pro.producer_name] = existing->second;
for( const auto& pro : result.active_schedule.producers ) {
if( pro.producer_name == proauth.producer_name ) {
new_producer_to_last_implied_irb[pro.producer_name] = dpos_proposed_irreversible_blocknum;
} else {
new_producer_to_last_implied_irb[pro.producer_name] = result.dpos_irreversible_blocknum;
auto existing = producer_to_last_implied_irb.find( pro.producer_name );
if( existing != producer_to_last_implied_irb.end() ) {
new_producer_to_last_implied_irb[pro.producer_name] = existing->second;
} else {
new_producer_to_last_implied_irb[pro.producer_name] = result.dpos_irreversible_blocknum;
}
}
}
}

result.producer_to_last_implied_irb = std::move( new_producer_to_last_implied_irb );
result.producer_to_last_implied_irb = std::move( new_producer_to_last_implied_irb );

result.was_pending_promoted = true;
} else {
result.active_schedule = active_schedule;
result.producer_to_last_produced = producer_to_last_produced;
result.producer_to_last_produced[proauth.producer_name] = result.block_num;
result.producer_to_last_implied_irb = producer_to_last_implied_irb;
result.producer_to_last_implied_irb[proauth.producer_name] = dpos_proposed_irreversible_blocknum;
}
result.was_pending_promoted = true;
} else {
result.active_schedule = active_schedule;
result.producer_to_last_produced = producer_to_last_produced;
result.producer_to_last_produced[proauth.producer_name] = result.block_num;
result.producer_to_last_implied_irb = producer_to_last_implied_irb;
result.producer_to_last_implied_irb[proauth.producer_name] = dpos_proposed_irreversible_blocknum;
}
} // !hotstuff_activated

return result;
}
Expand Down Expand Up @@ -385,12 +409,13 @@ namespace eosio { namespace chain {
const signed_block_header& h,
vector<signature_type>&& _additional_signatures,
const protocol_feature_set& pfs,
bool hotstuff_activated,
const std::function<void( block_timestamp_type,
const flat_set<digest_type>&,
const vector<digest_type>& )>& validator,
bool skip_validate_signee )const
{
return next( h.timestamp, h.confirmed ).finish_next( h, std::move(_additional_signatures), pfs, validator, skip_validate_signee );
return next( h.timestamp, hotstuff_activated, h.confirmed ).finish_next( h, std::move(_additional_signatures), pfs, validator, skip_validate_signee );
}

digest_type block_header_state::sig_digest()const {
Expand Down
3 changes: 2 additions & 1 deletion libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ namespace eosio { namespace chain {
block_state::block_state( const block_header_state& prev,
signed_block_ptr b,
const protocol_feature_set& pfs,
bool hotstuff_activated,
const std::function<void( block_timestamp_type,
const flat_set<digest_type>&,
const vector<digest_type>& )>& validator,
bool skip_validate_signee
)
:block_header_state( prev.next( *b, extract_additional_signatures(b, pfs, prev.activated_protocol_features), pfs, validator, skip_validate_signee ) )
:block_header_state( prev.next( *b, extract_additional_signatures(b, pfs, prev.activated_protocol_features), pfs, hotstuff_activated, validator, skip_validate_signee ) )
,block( std::move(b) )
{}

Expand Down
Loading

0 comments on commit bf3b284

Please sign in to comment.