Skip to content

Commit

Permalink
GH-2161 Add support for transition to savanna in irreversible mode
Browse files Browse the repository at this point in the history
  • Loading branch information
heifner committed Mar 22, 2024
1 parent 988261e commit 32258f4
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 14 deletions.
68 changes: 54 additions & 14 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1246,14 +1246,49 @@ struct controller_impl {
}
}

template<typename ForkDB, typename BSP>
void apply_irreversible_block(ForkDB& forkdb, const BSP& bsp) {
if( read_mode == db_read_mode::IRREVERSIBLE ) {
controller::block_report br;
if constexpr (std::is_same_v<block_state_legacy_ptr, std::decay_t<decltype(bsp)>>) {
// before transition to savanna
apply_block(br, bsp, controller::block_status::complete, trx_meta_cache_lookup{});
} else {
if (bsp->block->is_proper_svnn_block()) {
// if chain_head is legacy, update to non-legacy chain_head, this is needed so that the correct block_state is created in apply_block
if (std::holds_alternative<block_state_legacy_ptr>(chain_head.internal())) {
chain_head = block_handle{forkdb.get_block(bsp->previous(), include_root_t::yes)};
}
apply_block(br, bsp, controller::block_status::complete, trx_meta_cache_lookup{});
} else {
// only called during transition when not a proper savanna block
fork_db.apply_l<void>([&](const auto& forkdb_l) {
block_state_legacy_ptr legacy = forkdb_l.get_block(bsp->id());
fork_db.switch_to_legacy(); // apply block uses to know what types to create
fc::scoped_exit<std::function<void()>> e([&]{fork_db.switch_to_both();});
apply_block(br, legacy, controller::block_status::complete, trx_meta_cache_lookup{});
// irreversible apply was just done, calculate new_valid here instead of in transition_to_savanna()
assert(legacy->action_receipt_digests);
auto action_mroot = calculate_merkle(*legacy->action_receipt_digests);
// Create the valid structure for producing
auto prev = forkdb.get_block(legacy->previous(), include_root_t::yes);
assert(prev);
bsp->valid = prev->new_valid(*bsp, action_mroot);
forkdb.add(bsp, bsp->is_valid() ? mark_valid_t::yes : mark_valid_t::no, ignore_duplicate_t::yes);
});
}
}
}
}

void transition_to_savanna() {
assert(chain_head.header().contains_header_extension(instant_finality_extension::extension_id()));
// copy head branch branch from legacy forkdb legacy to savanna forkdb
fork_database_legacy_t::branch_t legacy_branch;
block_state_legacy_ptr legacy_root;
fork_db.apply_l<void>([&](const auto& forkdb) {
legacy_root = forkdb.root();
legacy_branch = forkdb.fetch_branch(forkdb.head()->id());
legacy_branch = forkdb.fetch_branch(fork_db_head(forkdb, irreversible_mode())->id());
});

assert(!!legacy_root);
Expand All @@ -1263,32 +1298,37 @@ struct controller_impl {
fork_db.switch_from_legacy(new_root);
fork_db.apply_s<void>([&](auto& forkdb) {
block_state_ptr prev = forkdb.root();
assert(prev);
for (auto bitr = legacy_branch.rbegin(); bitr != legacy_branch.rend(); ++bitr) {
const bool skip_validate_signee = true; // validated already
auto new_bsp = std::make_shared<block_state>(
*prev,
(*bitr)->block,
protocol_features.get_protocol_feature_set(),
validator_t{}, skip_validate_signee);
// legacy_branch is from head, all should be validated
assert((*bitr)->action_receipt_digests);
auto action_mroot = calculate_merkle(*(*bitr)->action_receipt_digests);
// Create the valid structure for producing
new_bsp->valid = prev->new_valid(*new_bsp, action_mroot);
// legacy_branch is from head, all will be validated unless irreversible_mode(),
// IRREVERSIBLE applies (validates) blocks when irreversible, new_valid will be done after apply in log_irreversible
assert(read_mode == db_read_mode::IRREVERSIBLE || (*bitr)->action_receipt_digests);
if ((*bitr)->action_receipt_digests) {
auto action_mroot = calculate_merkle(*(*bitr)->action_receipt_digests);
// Create the valid structure for producing
new_bsp->valid = prev->new_valid(*new_bsp, action_mroot);
}
forkdb.add(new_bsp, (*bitr)->is_valid() ? mark_valid_t::yes : mark_valid_t::no, ignore_duplicate_t::yes);
prev = new_bsp;
}
assert(read_mode == db_read_mode::IRREVERSIBLE || forkdb.head()->id() == legacy_branch.front()->id());
chain_head = block_handle{forkdb.head()};
if (read_mode != db_read_mode::IRREVERSIBLE)
chain_head = block_handle{forkdb.head()};
ilog("Transition to instant finality happening after block ${b}, First IF Proper Block ${pb}", ("b", prev->block_num())("pb", prev->block_num()+1));
});
ilog("Transition to instant finality happening after block ${b}, First IF Proper Block ${pb}", ("b", chain_head.block_num())("pb", chain_head.block_num()+1));

{
// If Leap started at a block prior to the IF transition, it needs to provide a default safety
// information for those finalizers that don't already have one. This typically should be done when
// we create the non-legacy fork_db, as from this point we may need to cast votes to participate
// to the IF consensus. See https://github.com/AntelopeIO/leap/issues/2070#issuecomment-1941901836
auto start_block = chain_head;
auto start_block = chain_head; // doesn't matter this is not updated for IRREVERSIBLE, can be in irreversible mode and be a finalizer
auto lib_block = chain_head;
my_finalizers.set_default_safety_information(
finalizer_safety_information{ .last_vote_range_start = block_timestamp_type(0),
Expand Down Expand Up @@ -1345,10 +1385,7 @@ struct controller_impl {
auto it = v.begin();

for( auto bitr = branch.rbegin(); bitr != branch.rend() && should_process(*bitr); ++bitr ) {
if( read_mode == db_read_mode::IRREVERSIBLE ) {
controller::block_report br;
apply_block( br, *bitr, controller::block_status::complete, trx_meta_cache_lookup{} );
}
apply_irreversible_block(forkdb, *bitr);

emit( irreversible_block, std::tie((*bitr)->block, (*bitr)->id()) );

Expand All @@ -1366,8 +1403,9 @@ struct controller_impl {
// Do not advance irreversible past IF Genesis Block
break;
}
} else if ((*bitr)->block->is_proper_svnn_block()) {
} else if ((*bitr)->block->is_proper_svnn_block() && fork_db.version_in_use() == fork_database::in_use_t::both) {
fork_db.switch_to_savanna();
break;
}
}
} catch( std::exception& ) {
Expand Down Expand Up @@ -3057,6 +3095,7 @@ struct controller_impl {

if (s != controller::block_status::irreversible) {
auto add_completed_block = [&](auto& forkdb) {
assert(std::holds_alternative<std::decay_t<decltype(forkdb.head())>>(cb.bsp.internal()));
const auto& bsp = std::get<std::decay_t<decltype(forkdb.head())>>(cb.bsp.internal());
if( s == controller::block_status::incomplete ) {
forkdb.add( bsp, mark_valid_t::yes, ignore_duplicate_t::no );
Expand Down Expand Up @@ -3084,6 +3123,7 @@ struct controller_impl {

if( s == controller::block_status::incomplete ) {
fork_db.apply_s<void>([&](auto& forkdb) {
assert(std::holds_alternative<std::decay_t<decltype(forkdb.head())>>(cb.bsp.internal()));
const auto& bsp = std::get<std::decay_t<decltype(forkdb.head())>>(cb.bsp.internal());

uint16_t if_ext_id = instant_finality_extension::extension_id();
Expand Down
10 changes: 10 additions & 0 deletions libraries/chain/fork_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,16 @@ namespace eosio::chain {
in_use = in_use_t::savanna;
}

// only called from the main thread
void fork_database::switch_to_legacy() {
in_use = in_use_t::legacy;
}

// only called from the main thread
void fork_database::switch_to_both() {
in_use = in_use_t::both;
}

block_branch_t fork_database::fetch_branch_from_head() const {
return apply<block_branch_t>([&](auto& forkdb) {
return forkdb.fetch_block_branch(forkdb.head()->id());
Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/include/eosio/chain/fork_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ namespace eosio::chain {
// switches to using both legacy and savanna during transition
void switch_from_legacy(const block_state_ptr& root);
void switch_to_savanna();
// used in irreversible mode
void switch_to_legacy();
// used in irreversible mode
void switch_to_both();

in_use_t version_in_use() const { return in_use.load(); }

Expand Down
3 changes: 3 additions & 0 deletions plugins/producer_plugin/producer_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,9 @@ void producer_plugin_impl::plugin_startup() {
EOS_ASSERT(_producers.empty() || chain.get_read_mode() != chain::db_read_mode::IRREVERSIBLE, plugin_config_exception,
"node cannot have any producer-name configured because block production is impossible when read_mode is \"irreversible\"");

EOS_ASSERT(_finalizer_keys.empty() || chain.get_read_mode() != chain::db_read_mode::IRREVERSIBLE, plugin_config_exception,
"node cannot have any finalizers configured because finalization is impossible when read_mode is \"irreversible\"");

EOS_ASSERT(_producers.empty() || chain.get_validation_mode() == chain::validation_mode::FULL, plugin_config_exception,
"node cannot have any producer-name configured because block production is not safe when validation_mode is not \"full\"");

Expand Down

0 comments on commit 32258f4

Please sign in to comment.