Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IF: Activate hotstuff consensus #1640

Merged
merged 8 commits into from
Nov 17, 2023
206 changes: 117 additions & 89 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; // current index in current round
// (increment to the end of this round ) + 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 @@ -37,7 +43,11 @@ namespace eosio { namespace chain {
return blocknums[ index ];
}

// create pending_block_header_state from this for `when`
// If hotstuff_activated then use new consensus values and simpler active schedule update.
// If notstuff is not activated then use previous pre-hotstuff consensus logic.
pending_block_header_state block_header_state::next( block_timestamp_type when,
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
bool hotstuff_activated,
uint16_t num_prev_blocks_to_confirm )const
{
pending_block_header_state result;
Expand All @@ -48,127 +58,144 @@ namespace eosio { namespace chain {
(when = header.timestamp).slot++;
}

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;

auto proauth = get_scheduled_producer(when);

result.valid_block_signing_authority = proauth.authority;
result.producer = proauth.producer_name;
result.last_proposed_finalizer_set_generation = last_proposed_finalizer_set_generation;

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");

result.dpos_proposed_irreversible_blocknum = new_dpos_proposed_irreversible_blocknum;
result.dpos_irreversible_blocknum = calc_dpos_last_irreversible( proauth.producer_name );
// 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( 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;
}

if( pending_schedule.schedule.producers.size() &&
result.dpos_irreversible_blocknum >= pending_schedule.schedule_lib_num )
{
result.active_schedule = pending_schedule.schedule;
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 );
}

break;
}
--i;
--blocks_to_confirm;
}

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

flat_map<account_name,uint32_t> new_producer_to_last_produced;
if( pending_schedule.schedule.producers.size() &&
result.dpos_irreversible_blocknum >= pending_schedule.schedule_lib_num )
{
result.active_schedule = pending_schedule.schedule;

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;
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 {
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 +412,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
Loading