From a0a3528e29805bd0df114ab8ab0be441aa5e7dac Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 19 Jan 2024 15:14:31 -0600 Subject: [PATCH 1/8] GH-2048 Support starting with fork database saved after enabling instant-finality. GH-2083 Moved mutex out of fork database impl so it can protect transition from legacy to instant-finality. --- libraries/chain/block_header_state.cpp | 5 + libraries/chain/controller.cpp | 746 ++++++++---------- libraries/chain/fork_database.cpp | 223 +++--- .../eosio/chain/block_header_state.hpp | 1 - .../chain/include/eosio/chain/controller.hpp | 4 +- .../include/eosio/chain/fork_database.hpp | 97 ++- programs/leap-util/actions/blocklog.cpp | 9 +- tests/CMakeLists.txt | 3 + tests/nodeos_startup_catchup.py | 5 +- .../unapplied_transaction_queue_tests.cpp | 2 +- 10 files changed, 556 insertions(+), 539 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 9359b8271c..a2d9144f79 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -7,6 +7,11 @@ namespace eosio::chain { +// moved this warning out of header so it only uses once +#warning TDDO https://github.com/AntelopeIO/leap/issues/2080 +// digest_type compute_finalizer_digest() const { return id; }; + + producer_authority block_header_state::get_scheduled_producer(block_timestamp_type t) const { return detail::get_scheduled_producer(active_proposer_policy->proposer_schedule.producers, t); } diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 283a4ff66f..52e61904f8 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -117,246 +117,6 @@ class maybe_session { std::optional _session; }; -template -struct block_data_gen_t { -public: - using bs = bsp::element_type; - using fork_db_t = fork_database; - - bsp head; - fork_db_t fork_db; - - explicit block_data_gen_t(const std::filesystem::path& path) : fork_db(path) {} - - bsp fork_db_head(bool irreversible_mode) const { - if (irreversible_mode) { - // When in IRREVERSIBLE mode fork_db blocks are marked valid when they become irreversible so that - // fork_db.head() returns irreversible block - // Use pending_head since this method should return the chain head and not last irreversible. - return fork_db.pending_head(); - } else { - return fork_db.head(); - } - } - - bsp fork_db_root() const { return fork_db.root(); } - - bsp fork_db_head() const { return fork_db.head(); } - - void fork_db_open(validator_t& validator) { return fork_db.open(validator); } - - void fork_db_reset_to_head() { return fork_db.reset(*head); } - - template - R apply(F &f) { if constexpr (std::is_same_v) f(fork_db, head); else return f(fork_db, head); } - - uint32_t pop_block() { - auto prev = fork_db.get_block( head->previous() ); - - if( !prev ) { - EOS_ASSERT( fork_db.root()->id() == head->previous(), block_validate_exception, "attempt to pop beyond last irreversible block" ); - prev = fork_db.root(); - } - - EOS_ASSERT( head->block, block_validate_exception, "attempting to pop a block that was sparsely loaded from a snapshot"); - head = prev; - - return prev->block_num(); - } -}; - -using block_data_legacy_t = block_data_gen_t; -using block_data_new_t = block_data_gen_t; - -struct block_data_t { - using block_data_variant = std::variant; - - block_data_variant v; - - uint32_t head_block_num() const { return std::visit([](const auto& bd) { return bd.head->block_num(); }, v); } - block_timestamp_type head_block_time() const { return std::visit([](const auto& bd) { return bd.head->timestamp(); }, v); } - account_name head_block_producer() const { return std::visit([](const auto& bd) { return bd.head->producer(); }, v); } - - void transition_fork_db_to_if(const auto& vbsp) { - // no need to close fork_db because we don't want to write anything out, file is removed on open - auto bsp = std::make_shared(*std::get(vbsp)); - v.emplace(std::visit([](const auto& bd) { return bd.fork_db.get_data_dir(); }, v)); - std::visit(overloaded{ - [](block_data_legacy_t&) {}, - [&](block_data_new_t& bd) { - bd.head = bsp; - bd.fork_db.reset(*bd.head); - } - }, v); - } - - protocol_feature_activation_set_ptr head_activated_protocol_features() const { return std::visit([](const auto& bd) { - return bd.head->get_activated_protocol_features(); }, v); - } - - const producer_authority_schedule& head_active_schedule_auth() { - return std::visit([](const auto& bd) -> const producer_authority_schedule& { return bd.head->active_schedule_auth(); }, v); - } - - const producer_authority_schedule* head_pending_schedule_auth_legacy() { - return std::visit(overloaded{ - [](const block_data_legacy_t& bd) -> const producer_authority_schedule* { - return bd.head->pending_schedule_auth(); - }, - [](const block_data_new_t&) -> const producer_authority_schedule* { return nullptr; } - }, v); - } - - const producer_authority_schedule* next_producers() { - return std::visit(overloaded{ - [](const block_data_legacy_t& bd) -> const producer_authority_schedule* { - return bd.head->pending_schedule_auth(); - }, - [](const block_data_new_t& bd) -> const producer_authority_schedule* { - return bd.head->proposer_policies.empty() ? nullptr : &bd.head->proposer_policies.begin()->second->proposer_schedule; - } - }, v); - } - - const block_id_type& head_block_id() const { - return std::visit([](const auto& bd) -> const block_id_type& { return bd.head->id(); }, v); - } - - const block_header& head_block_header() const { - return std::visit([](const auto& bd) -> const block_header& { return bd.head->header; }, v); - } - - const signed_block_ptr& head_block() const { - return std::visit([](const auto& bd) -> const signed_block_ptr& { return bd.head->block; }, v); - } - - void replace_producer_keys( const public_key_type& key ) { - ilog("Replace producer keys with ${k}", ("k", key)); - - std::visit( - overloaded{ - [&](const block_data_legacy_t& bd) { - auto version = bd.head->pending_schedule.schedule.version; - bd.head->pending_schedule = {}; - bd.head->pending_schedule.schedule.version = version; - for (auto& prod: bd.head->active_schedule.producers ) { - ilog("${n}", ("n", prod.producer_name)); - std::visit([&](auto &auth) { - auth.threshold = 1; - auth.keys = {key_weight{key, 1}}; - }, prod.authority); - } - }, - [](const block_data_new_t&) { - // TODO IF: add instant-finality implementation, will need to replace finalizers as well - } - }, v); - } - - // --------------- access fork_db head ---------------------------------------------------------------------- - bool fork_db_has_head() const { - return std::visit([&](const auto& bd) { return !!bd.fork_db_head(); }, v); - } - - uint32_t fork_db_head_block_num(bool irreversible_mode) const { - return std::visit([&](const auto& bd) { return bd.fork_db_head(irreversible_mode)->block_num(); }, v); - } - - const block_id_type& fork_db_head_block_id(bool irreversible_mode) const { - return std::visit([&](const auto& bd) -> const block_id_type& { return bd.fork_db_head(irreversible_mode)->id(); }, v); - } - - uint32_t fork_db_head_irreversible_blocknum(bool irreversible_mode) const { - return std::visit([&](const auto& bd) { return bd.fork_db_head(irreversible_mode)->irreversible_blocknum(); }, v); - } - - // --------------- access fork_db root ---------------------------------------------------------------------- - bool fork_db_has_root() const { - return std::visit([&](const auto& bd) { return !!bd.fork_db_root(); }, v); - } - - const block_id_type& fork_db_root_block_id() const { - return std::visit([&](const auto& bd) -> const block_id_type& { return bd.fork_db_root()->id(); }, v); - } - - uint32_t fork_db_root_block_num() const { - return std::visit([&](const auto& bd) { return bd.fork_db_root()->block_num(); }, v); - } - - block_timestamp_type fork_db_root_timestamp() const { - return std::visit([&](const auto& bd) { return bd.fork_db_root()->timestamp(); }, v); - } - - // --------------- fork_db APIs ---------------------------------------------------------------------- - uint32_t pop_block() { return std::visit([](auto& bd) { return bd.pop_block(); }, v); } - - void fork_db_open(validator_t& validator) { return std::visit([&](auto& bd) { bd.fork_db_open(validator); }, v); } - - void fork_db_reset_to_head() { return std::visit([&](auto& bd) { bd.fork_db_reset_to_head(); }, v); } - - signed_block_ptr fork_db_fetch_block_by_id( const block_id_type& id ) const { - return std::visit([&](const auto& bd) -> signed_block_ptr { - auto bsp = bd.fork_db.get_block(id); - return bsp ? bsp->block : nullptr; - }, v); - } - - signed_block_ptr fork_db_fetch_block_by_num(uint32_t block_num) const { - return std::visit([&](const auto& bd) -> signed_block_ptr { - auto bsp = bd.fork_db.search_on_branch(bd.fork_db.head()->id(), block_num); - if (bsp) return bsp->block; - return {}; - }, v); - } - - std::optional fork_db_fetch_block_id_by_num(uint32_t block_num) const { - return std::visit([&](const auto& bd) -> std::optional { - auto bsp = bd.fork_db.search_on_branch(bd.fork_db.head()->id(), block_num); - if (bsp) return bsp->id(); - return {}; - }, v); - } - - block_state_ptr fork_db_fetch_bsp_by_num(uint32_t block_num) const { - return std::visit(overloaded{ - [](const block_data_legacy_t&) -> block_state_ptr { return nullptr; }, - [&](const block_data_new_t& bd) -> block_state_ptr { - auto bsp = bd.fork_db.search_on_branch(bd.fork_db.head()->id(), block_num); - return bsp; } - }, v); - } - - template - R apply(const F& f) { - if constexpr (std::is_same_v) - std::visit([&](auto& bd) { bd.template apply(f); }, v); - else - return std::visit([&](auto& bd) -> R { return bd.template apply(f); }, v); - } - - template - R apply_dpos(const F& f) { - if constexpr (std::is_same_v) - std::visit(overloaded{[&](block_data_legacy_t& bd) { bd.template apply(f); }, [&](block_data_new_t& bd) {}}, - v); - else - return std::visit(overloaded{[&](block_data_legacy_t& bd) -> R { return bd.template apply(f); }, - [&](block_data_new_t& bd) -> R { return {}; }}, - v); - } - - template - R apply_if(const F& f) { - if constexpr (std::is_same_v) - std::visit(overloaded{[&](block_data_legacy_t& bd) {}, [&](block_data_new_t& bd) { bd.template apply(f); }}, - v); - else - return std::visit(overloaded{[&](block_data_legacy_t& bd) -> R { return {}; }, - [&](block_data_new_t& bd) -> R { return bd.template apply(f); }}, - v); - } -}; - struct completed_block { std::variant bsp; @@ -854,7 +614,7 @@ struct building_block { assembled_block assemble_block(boost::asio::io_context& ioc, const protocol_feature_set& pfs, - const block_data_t& block_data, + fork_database& fork_db, bool validating, std::optional validating_qc_info) { digests_t& action_receipts = action_receipt_digests(); @@ -904,26 +664,24 @@ struct building_block { }}, trx_mroot_or_receipt_digests()); - // get fork_database so that we can search for the best qc to include in this block. - assert(std::holds_alternative(block_data.v)); - const block_data_new_t& bd = std::get(block_data.v); - const auto& fork_db = bd.fork_db; // fork_db - // find most recent ancestor block that has a QC by traversing fork db // branch from parent std::optional qc_data; if (!validating) { - auto branch = fork_db.fetch_branch(parent_id()); - for( auto it = branch.begin(); it != branch.end(); ++it ) { - auto qc = (*it)->get_best_qc(); - if( qc ) { - EOS_ASSERT( qc->block_height <= block_header::num_from_id(parent_id()), block_validate_exception, - "most recent ancestor QC block number (${a}) cannot be greater than parent's block number (${p})", - ("a", qc->block_height)("p", block_header::num_from_id(parent_id())) ); - qc_data = qc_data_t{ *qc, qc_info_t{ qc->block_height, qc->qc.is_strong() }}; - break; + // get fork_database so that we can search for the best qc to include in this block. + fork_db.apply_if([&](const auto& forkdb) { + auto branch = forkdb.fetch_branch(parent_id()); + for( auto it = branch.begin(); it != branch.end(); ++it ) { + auto qc = (*it)->get_best_qc(); + if( qc ) { + EOS_ASSERT( qc->block_height <= block_header::num_from_id(parent_id()), block_validate_exception, + "most recent ancestor QC block number (${a}) cannot be greater than parent's block number (${p})", + ("a", qc->block_height)("p", block_header::num_from_id(parent_id())) ); + qc_data = qc_data_t{ *qc, qc_info_t{ qc->block_height, qc->qc.is_strong() }}; + break; + } } - } + }); } building_block_input bb_input { @@ -1053,7 +811,7 @@ struct controller_impl { chainbase::database db; block_log blog; std::optional pending; - block_data_t block_data; // includes `head` aand `fork_db` + mutable fork_database fork_db; std::optional pacemaker; std::atomic if_irreversible_block_num{0}; resource_limits_manager resource_limits; @@ -1089,12 +847,183 @@ struct controller_impl { int64_t set_proposed_producers( vector producers ); int64_t set_proposed_producers_legacy( vector producers ); + uint32_t head_block_num() const { return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->block_num(); }); } + block_timestamp_type head_block_time() const { return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->timestamp(); }); } + account_name head_block_producer() const { return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->producer(); }); } + const block_id_type& head_block_id() const { return fork_db.apply([](const auto& forkdb) -> const block_id_type& { return forkdb.chain_head->id(); }); } + const block_header& head_block_header() const { return fork_db.apply([](const auto& forkdb) -> const block_header& { return forkdb.chain_head->header; }); } + const signed_block_ptr& head_block() const { return fork_db.apply([](const auto& forkdb) -> const signed_block_ptr& { return forkdb.chain_head->block; }); } + + protocol_feature_activation_set_ptr head_activated_protocol_features() const { + return fork_db.apply([](const auto& forkdb) { + return forkdb.chain_head->get_activated_protocol_features(); + }); + } + + const producer_authority_schedule& head_active_schedule_auth() const { + return fork_db.apply([](const auto& forkdb) -> const producer_authority_schedule& { + return forkdb.chain_head->active_schedule_auth(); + }); + } + + const producer_authority_schedule* head_pending_schedule_auth_legacy() { + return fork_db.apply(overloaded{ + [](const fork_database_legacy_t& forkdb) -> const producer_authority_schedule* { + return forkdb.chain_head->pending_schedule_auth(); + }, + [](const fork_database_if_t&) -> const producer_authority_schedule* { return nullptr; } + }); + } + + const producer_authority_schedule* next_producers() { + return fork_db.apply(overloaded{ + [](const fork_database_legacy_t& forkdb) -> const producer_authority_schedule* { + return forkdb.chain_head->pending_schedule_auth(); + }, + [](const fork_database_if_t& forkdb) -> const producer_authority_schedule* { + return forkdb.chain_head->proposer_policies.empty() + ? nullptr + : &forkdb.chain_head->proposer_policies.begin()->second->proposer_schedule; + } + }); + } + + void replace_producer_keys( const public_key_type& key ) { + ilog("Replace producer keys with ${k}", ("k", key)); + + fork_db.apply( + overloaded{ + [&](const fork_database_legacy_t& forkdb) { + auto version = forkdb.chain_head->pending_schedule.schedule.version; + forkdb.chain_head->pending_schedule = {}; + forkdb.chain_head->pending_schedule.schedule.version = version; + for (auto& prod: forkdb.chain_head->active_schedule.producers ) { + ilog("${n}", ("n", prod.producer_name)); + std::visit([&](auto &auth) { + auth.threshold = 1; + auth.keys = {key_weight{key, 1}}; + }, prod.authority); + } + }, + [](const fork_database_if_t&) { + // TODO IF: add instant-finality implementation, will need to replace finalizers as well + } + }); + } + + // --------------- access fork_db head ---------------------------------------------------------------------- + bool fork_db_has_head() const { + return fork_db.apply([&](const auto& forkdb) { return !!forkdb.head(); }); + } + + template + typename ForkDB::bsp_t fork_db_head(const ForkDB& fork_db, bool irreversible_mode) const { + if (irreversible_mode) { + // When in IRREVERSIBLE mode fork_db blocks are marked valid when they become irreversible so that + // fork_db.head() returns irreversible block + // Use pending_head since this method should return the chain head and not last irreversible. + return fork_db.pending_head(); + } else { + return fork_db.head(); + } + } + + uint32_t fork_db_head_block_num(bool irreversible_mode) const { + return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode)->block_num(); } ); + } + + block_id_type fork_db_head_block_id(bool irreversible_mode) const { + return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode)->id(); } ); + } + + uint32_t fork_db_head_irreversible_blocknum(bool irreversible_mode) const { + return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode)->irreversible_blocknum(); }); + } + + // --------------- access fork_db root ---------------------------------------------------------------------- + bool fork_db_has_root() const { + return fork_db.apply([&](const auto& forkdb) { return !!forkdb.root(); }); + } + + block_id_type fork_db_root_block_id() const { + return fork_db.apply([&](const auto& forkdb) { return forkdb.root()->id(); }); + } + + uint32_t fork_db_root_block_num() const { + return fork_db.apply([&](const auto& forkdb) { return forkdb.root()->block_num(); }); + } + + block_timestamp_type fork_db_root_timestamp() const { + return fork_db.apply([&](const auto& forkdb) { return forkdb.root()->timestamp(); }); + } + + // --------------- fork_db APIs ---------------------------------------------------------------------- + template + uint32_t pop_block(ForkDB& forkdb) { + typename ForkDB::bsp_t prev = forkdb.get_block( forkdb.chain_head->previous() ); + + if( !prev ) { + EOS_ASSERT( forkdb.root()->id() == forkdb.chain_head->previous(), block_validate_exception, "attempt to pop beyond last irreversible block" ); + prev = forkdb.root(); + } + + EOS_ASSERT( forkdb.chain_head->block, block_validate_exception, "attempting to pop a block that was sparsely loaded from a snapshot"); + forkdb.chain_head = prev; + + return prev->block_num(); + } + + void fork_db_reset_to_head() { + return fork_db.apply([&](auto& forkdb) { + forkdb.reset(*forkdb.chain_head); + }); + } + + signed_block_ptr fork_db_fetch_block_by_id( const block_id_type& id ) const { + return fork_db.apply([&](const auto& forkdb) { + auto bsp = forkdb.get_block(id); + return bsp ? bsp->block : nullptr; + }); + } + + signed_block_ptr fork_db_fetch_block_by_num(uint32_t block_num) const { + return fork_db.apply([&](const auto& forkdb) { + auto bsp = forkdb.search_on_branch(forkdb.head()->id(), block_num); + if (bsp) return bsp->block; + return signed_block_ptr{}; + }); + } + + std::optional fork_db_fetch_block_id_by_num(uint32_t block_num) const { + return fork_db.apply>([&](const auto& forkdb) -> std::optional { + auto bsp = forkdb.search_on_branch(forkdb.head()->id(), block_num); + if (bsp) return bsp->id(); + return {}; + }); + } + + block_state_ptr fork_db_fetch_bsp_by_num(uint32_t block_num) const { + return fork_db.apply( + overloaded{ + [](const fork_database_legacy_t&) -> block_state_ptr { return nullptr; }, + [&](const fork_database_if_t&forkdb) -> block_state_ptr { + auto bsp = forkdb.search_on_branch(forkdb.head()->id(), block_num); + return bsp; + } + } + ); + } + void pop_block() { - uint32_t prev_block_num = block_data.pop_block(); + uint32_t prev_block_num = fork_db.apply([&](auto& forkdb) { + return pop_block(forkdb); + }); db.undo(); protocol_features.popped_blocks_to(prev_block_num); } + // ------------------------------------------- + template void on_activation(); @@ -1121,9 +1050,7 @@ struct controller_impl { cfg.read_only ? database::read_only : database::read_write, cfg.state_size, false, cfg.db_map_mode ), blog( cfg.blocks_dir, cfg.blog ), - block_data(block_data_t::block_data_variant{ - std::in_place_type, // initial state is always dpos - std::filesystem::path{cfg.blocks_dir / config::reversible_blocks_dir_name}}), + fork_db(cfg.blocks_dir / config::reversible_blocks_dir_name), resource_limits( db, [&s](bool is_trx_transient) { return s.get_deep_mind_logger(is_trx_transient); }), authorization( s, db ), protocol_features( std::move(pfs), [&s](bool is_trx_transient) { return s.get_deep_mind_logger(is_trx_transient); } ), @@ -1133,8 +1060,8 @@ struct controller_impl { thread_pool(), wasmif( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() ) { - block_data.fork_db_open([this](block_timestamp_type timestamp, const flat_set& cur_features, - const vector& new_features) { + fork_db.open([this](block_timestamp_type timestamp, const flat_set& cur_features, + const vector& new_features) { check_protocol_features(timestamp, cur_features, new_features); }); @@ -1245,7 +1172,7 @@ struct controller_impl { if( new_lib <= lib_num ) return; - auto mark_branch_irreversible = [&](auto& fork_db, auto& head) { + auto mark_branch_irreversible = [&](auto& fork_db) { auto branch = fork_db.fetch_branch( fork_db_head_block_id(), new_lib ); try { std::vector>> v; @@ -1289,7 +1216,7 @@ struct controller_impl { boost::asio::post( thread_pool.get_executor(), [branch{std::move(branch)}]() {} ); }; - block_data.apply(mark_branch_irreversible); + fork_db.apply(mark_branch_irreversible); } /** @@ -1298,7 +1225,7 @@ struct controller_impl { void initialize_blockchain_state(const genesis_state& genesis) { wlog( "Initializing new blockchain with genesis state" ); - auto init_blockchain = [&genesis](auto& fork_db, auto& head) { + auto init_blockchain = [&genesis](auto& forkdb) { producer_authority_schedule initial_schedule = { 0, { producer_authority{config::system_account_name, block_signing_authority_v0{ 1, {{genesis.initial_key, 1}} } } } }; legacy::producer_schedule_type initial_legacy_schedule{ 0, {{config::system_account_name, genesis.initial_key}} }; @@ -1312,13 +1239,13 @@ struct controller_impl { genheader.id = genheader.header.calculate_id(); genheader.block_num = genheader.header.block_num(); - head = std::make_shared(); - static_cast(*head) = genheader; - head->activated_protocol_features = std::make_shared(); - head->block = std::make_shared(genheader.header); + forkdb.chain_head = std::make_shared(); + static_cast(*forkdb.chain_head) = genheader; + forkdb.chain_head->activated_protocol_features = std::make_shared(); + forkdb.chain_head->block = std::make_shared(genheader.header); }; - block_data.apply_dpos(init_blockchain); // assuming here that genesis_state is always dpos + fork_db.apply_dpos(init_blockchain); // assuming here that genesis_state is always dpos db.set_revision( head_block_num() ); initialize_database(genesis); @@ -1327,7 +1254,7 @@ struct controller_impl { void replay(std::function check_shutdown) { auto blog_head = blog.head(); if( !fork_db_has_root() ) { - block_data.fork_db_reset_to_head(); + fork_db_reset_to_head(); if (!blog_head) return; } @@ -1338,8 +1265,9 @@ struct controller_impl { std::exception_ptr except_ptr; - auto replay_blog = [&](auto& fork_db, auto& head) { - using BSP = std::decay_t; + auto replay_blog = [&](auto& forkdb) { + using BSP = std::decay_t; + auto& head = forkdb.chain_head; if( blog_head && start_block_num <= blog_head->block_num() ) { ilog( "existing block log, attempting to replay from ${s} to ${n} blocks", ("s", start_block_num)("n", blog_head->block_num()) ); @@ -1356,20 +1284,20 @@ struct controller_impl { } ilog( "${n} irreversible blocks replayed", ("n", 1 + head->block_num() - start_block_num) ); - auto pending_head = fork_db.pending_head(); + auto pending_head = forkdb.pending_head(); if( pending_head ) { - ilog( "fork database head ${h}, root ${r}", ("h", pending_head->block_num())( "r", fork_db.root()->block_num() ) ); - if( pending_head->block_num() < head->block_num() || head->block_num() < fork_db.root()->block_num() ) { + ilog( "fork database head ${h}, root ${r}", ("h", pending_head->block_num())( "r", forkdb.root()->block_num() ) ); + if( pending_head->block_num() < head->block_num() || head->block_num() < forkdb.root()->block_num() ) { ilog( "resetting fork database with new last irreversible block as the new root: ${id}", ("id", head->id()) ); - fork_db.reset( *head ); - } else if( head->block_num() != fork_db.root()->block_num() ) { - auto new_root = fork_db.search_on_branch( pending_head->id(), head->block_num() ); + forkdb.reset( *head ); + } else if( head->block_num() != forkdb.root()->block_num() ) { + auto new_root = forkdb.search_on_branch( pending_head->id(), head->block_num() ); EOS_ASSERT( new_root, fork_database_exception, "unexpected error: could not find new LIB in fork database" ); ilog( "advancing fork database root to new last irreversible block within existing fork database: ${id}", ("id", new_root->id()) ); - fork_db.mark_valid( new_root ); - fork_db.advance_root( new_root->id() ); + forkdb.mark_valid( new_root ); + forkdb.advance_root( new_root->id() ); } } @@ -1383,10 +1311,10 @@ struct controller_impl { if (snapshot_head_block != 0 && !blog_head) { // loading from snapshot without a block log so fork_db can't be considered valid - fork_db.reset( *head ); - } else if( !except_ptr && !check_shutdown() && fork_db.head() ) { + forkdb.reset( *head ); + } else if( !except_ptr && !check_shutdown() && forkdb.head() ) { auto head_block_num = head->block_num(); - auto branch = fork_db.fetch_branch( fork_db.head()->id() ); + auto branch = forkdb.fetch_branch( forkdb.head()->id() ); int rev = 0; for( auto i = branch.rbegin(); i != branch.rend(); ++i ) { if( check_shutdown() ) break; @@ -1397,8 +1325,8 @@ struct controller_impl { ilog( "${n} reversible blocks replayed", ("n",rev) ); } - if( !fork_db.head() ) { - fork_db.reset( *head ); + if( !forkdb.head() ) { + forkdb.reset( *head ); } auto end = fc::time_point::now(); @@ -1408,7 +1336,7 @@ struct controller_impl { replaying = false; }; - block_data.apply(replay_blog); + fork_db.apply(replay_blog); if( except_ptr ) { std::rethrow_exception( except_ptr ); @@ -1454,10 +1382,10 @@ struct controller_impl { this->shutdown = std::move(shutdown); - auto do_startup = [&](auto& fork_db, auto& head) { - if( fork_db.head() ) { - if( read_mode == db_read_mode::IRREVERSIBLE && fork_db.head()->id() != fork_db.root()->id() ) { - fork_db.rollback_head_to_root(); + auto do_startup = [&](auto& forkdb) { + if( forkdb.head() ) { + if( read_mode == db_read_mode::IRREVERSIBLE && forkdb.head()->id() != forkdb.root()->id() ) { + forkdb.rollback_head_to_root(); } wlog( "No existing chain state. Initializing fresh blockchain state." ); } else { @@ -1465,12 +1393,12 @@ struct controller_impl { } initialize_blockchain_state(genesis); // sets head to genesis state - if( !fork_db.head() ) { - fork_db.reset( *head ); + if( !forkdb.head() ) { + forkdb.reset( *forkdb.chain_head ); } }; - block_data.apply(do_startup); + fork_db.apply(do_startup); if( blog.head() ) { EOS_ASSERT( blog.first_block_num() == 1, block_log_exception, @@ -1484,7 +1412,7 @@ struct controller_impl { void startup(std::function shutdown, std::function check_shutdown) { EOS_ASSERT( db.revision() >= 1, database_exception, "This version of controller::startup does not work with a fresh state database." ); - EOS_ASSERT( block_data.fork_db_has_head(), fork_database_exception, "No existing fork database despite existing chain state. Replay required." ); + EOS_ASSERT( fork_db_has_head(), fork_database_exception, "No existing fork database despite existing chain state. Replay required." ); this->shutdown = std::move(shutdown); uint32_t lib_num = fork_db_root_block_num(); @@ -1504,14 +1432,14 @@ struct controller_impl { } } - auto do_startup = [&](auto& fork_db, auto& head) { - if( read_mode == db_read_mode::IRREVERSIBLE && fork_db.head()->id() != fork_db.root()->id() ) { - fork_db.rollback_head_to_root(); + auto do_startup = [&](auto& forkdb) { + if( read_mode == db_read_mode::IRREVERSIBLE && forkdb.head()->id() != forkdb.root()->id() ) { + forkdb.rollback_head_to_root(); } - head = fork_db.head(); + forkdb.chain_head = forkdb.head(); }; - block_data.apply(do_startup); + fork_db.apply(do_startup); init(std::move(check_shutdown)); } @@ -1581,16 +1509,16 @@ struct controller_impl { // Furthermore, fork_db.root()->block_num() <= lib_num. // Also, even though blog.head() may still be nullptr, blog.first_block_num() is guaranteed to be lib_num + 1. - auto finish_init = [&](auto& fork_db, auto& head) { + auto finish_init = [&](auto& forkdb) { if( read_mode != db_read_mode::IRREVERSIBLE - && fork_db.pending_head()->id() != fork_db.head()->id() - && fork_db.head()->id() == fork_db.root()->id() + && forkdb.pending_head()->id() != forkdb.head()->id() + && forkdb.head()->id() == forkdb.root()->id() ) { wlog( "read_mode has changed from irreversible: applying best branch from fork database" ); - for( auto pending_head = fork_db.pending_head(); - pending_head->id() != fork_db.head()->id(); - pending_head = fork_db.pending_head() + for( auto pending_head = forkdb.pending_head(); + pending_head->id() != forkdb.head()->id(); + pending_head = forkdb.pending_head() ) { wlog( "applying branch from fork database ending with block: ${id}", ("id", pending_head->id()) ); controller::block_report br; @@ -1599,7 +1527,7 @@ struct controller_impl { } }; - block_data.apply(finish_init); + fork_db.apply(finish_init); } ~controller_impl() { @@ -1692,12 +1620,12 @@ struct controller_impl { }); #warning todo: add snapshot support for new (IF) block_state section - auto write_block_state_section = [&](auto& fork_db, auto& head) { + auto write_block_state_section = [&](auto& forkdb) { snapshot->write_section("eosio::chain::block_state", [&]( auto §ion ) { - section.template add_row(*head, db); + section.template add_row(*forkdb.chain_head, db); }); }; - block_data.apply_dpos(write_block_state_section); + fork_db.apply_dpos(write_block_state_section); controller_index_set::walk_indices([this, &snapshot]( auto utils ){ using value_t = typename decltype(utils)::index_t::value_type; @@ -1746,7 +1674,7 @@ struct controller_impl { }); #warning todo: add snapshot support for new (IF) block_state section - auto read_block_state_section = [&](auto& fork_db, auto& head) { /// load and upgrade the block header state + auto read_block_state_section = [&](auto& forkdb) { /// load and upgrade the block header state block_header_state_legacy head_header_state; using v2 = legacy::snapshot_block_header_state_v2; @@ -1771,10 +1699,10 @@ struct controller_impl { ("block_log_last_num", blog_end) ); - head = std::make_shared(); - static_cast(*head) = head_header_state; + forkdb.chain_head = std::make_shared(); + static_cast(*forkdb.chain_head) = head_header_state; }; - block_data.apply_dpos(read_block_state_section); + fork_db.apply_dpos(read_block_state_section); controller_index_set::walk_indices([this, &snapshot, &header]( auto utils ){ using value_t = typename decltype(utils)::index_t::value_type; @@ -1923,7 +1851,7 @@ struct controller_impl { const auto& tapos_block_summary = db.get(1); db.modify( tapos_block_summary, [&]( auto& bs ) { - bs.block_id = block_data.head_block_id(); + bs.block_id = head_block_id(); }); genesis.initial_configuration.validate(); @@ -2538,21 +2466,20 @@ struct controller_impl { pending.reset(); }); - auto update_pending = [&](fork_db_t& fork_db, bsp& head) { - EOS_ASSERT( self.skip_db_sessions(s) || - db.revision() == head_block_num(), database_exception, "db revision is not on par with head block", - ("db.revision()", db.revision())("controller_head_block", head_block_num())("fork_db_head_block", fork_db_head_block_num()) ); - maybe_session session = self.skip_db_sessions(s) ? maybe_session() : maybe_session(db); - if constexpr (std::is_same_v) - pending.emplace(std::move(session), *head, when, confirm_block_count, new_protocol_feature_activations); - else { - building_block_input bbi{ head->id(), when, head->get_scheduled_producer(when).producer_name, - new_protocol_feature_activations }; - pending.emplace(std::move(session), *head, bbi); - } - }; + EOS_ASSERT( self.skip_db_sessions(s) || db.revision() == head_block_num(), database_exception, + "db revision is not on par with head block", + ("db.revision()", db.revision())("controller_head_block", head_block_num())("fork_db_head_block", fork_db_head_block_num()) ); - block_data.apply(update_pending); + fork_db.apply_dpos([&](auto& forkdb) { + maybe_session session = self.skip_db_sessions(s) ? maybe_session() : maybe_session(db); + pending.emplace(std::move(session), *forkdb.chain_head, when, confirm_block_count, new_protocol_feature_activations); + }); + fork_db.apply_if([&](auto& forkdb) { + maybe_session session = self.skip_db_sessions(s) ? maybe_session() : maybe_session(db); + building_block_input bbi{ forkdb.chain_head->id(), when, forkdb.chain_head->get_scheduled_producer(when).producer_name, + new_protocol_feature_activations }; + pending.emplace(std::move(session), *forkdb.chain_head, bbi); + }); pending->_block_status = s; pending->_producer_block_id = producer_block_id; @@ -2717,7 +2644,7 @@ struct controller_impl { resource_limits.process_block_usage(bb.block_num()); auto assembled_block = bb.assemble_block(thread_pool.get_executor(), - protocol_features.get_protocol_feature_set(), block_data, validating, validating_qc_info); + protocol_features.get_protocol_feature_set(), fork_db, validating, validating_qc_info); // Update TaPoS table: create_block_summary( assembled_block.id() ); @@ -2741,46 +2668,45 @@ struct controller_impl { const auto& cb = std::get(pending->_block_stage); - auto add_completed_block = [&](auto& fork_db, auto& head) { - const auto& bsp = std::get>(cb.bsp); + auto add_completed_block = [&](auto& forkdb) { + const auto& bsp = std::get>(cb.bsp); if( s == controller::block_status::incomplete ) { - fork_db.add( bsp ); - fork_db.mark_valid( bsp ); + forkdb.add( bsp ); + forkdb.mark_valid( bsp ); emit( self.accepted_block_header, std::tie(bsp->block, bsp->id()) ); - EOS_ASSERT( bsp == fork_db.head(), fork_database_exception, "committed block did not become the new head in fork database"); + EOS_ASSERT( bsp == forkdb.head(), fork_database_exception, "committed block did not become the new head in fork database"); } else if (s != controller::block_status::irreversible) { - fork_db.mark_valid( bsp ); + forkdb.mark_valid( bsp ); } - head = bsp; + forkdb.chain_head = bsp; emit( self.accepted_block, std::tie(bsp->block, bsp->id()) ); }; - block_data.apply(add_completed_block); + fork_db.apply(add_completed_block); - block_data.apply_dpos([this](auto& fork_db, auto& head) { + fork_db.apply_dpos([this](auto& forkdb) { #warning todo: support deep_mind_logger even when in IF mode (use apply instead of apply_dpos) // at block level, no transaction specific logging is possible if (auto* dm_logger = get_deep_mind_logger(false)) { - dm_logger->on_accepted_block(head); + dm_logger->on_accepted_block(forkdb.chain_head); }}); if( s == controller::block_status::incomplete ) { log_irreversible(); } - block_data.apply_if([&](auto& fork_db, auto& head) { create_and_send_vote_msg(head); }); + fork_db.apply_if([&](auto& forkdb) { create_and_send_vote_msg(forkdb.chain_head); }); // TODO: temp transition to instant-finality, happens immediately after block with new_finalizer_policy - auto transition = [&](auto& fork_db, auto& head) -> bool { - const auto& bsp = std::get>(cb.bsp); - std::optional ext = bsp->block->extract_header_extension(instant_finality_extension::extension_id()); + auto transition = [&](auto& forkdb) -> bool { + std::optional ext = forkdb.chain_head->block->extract_header_extension(instant_finality_extension::extension_id()); if (ext) { const auto& if_extension = std::get(*ext); if (if_extension.new_finalizer_policy) { - ilog("Transition to instant finality happening after block ${b}", ("b", bsp->block_num())); - if_irreversible_block_num = bsp->block_num(); + ilog("Transition to instant finality happening after block ${b}", ("b", forkdb.chain_head->block_num())); + if_irreversible_block_num = forkdb.chain_head->block_num(); log_irreversible(); return true; @@ -2788,8 +2714,8 @@ struct controller_impl { } return false; }; - if (block_data.apply_dpos(transition)) { - block_data.transition_fork_db_to_if(cb.bsp); + if (fork_db.apply_dpos(transition)) { + fork_db.switch_from_legacy(); } } catch (...) { @@ -2912,7 +2838,7 @@ struct controller_impl { const trx_meta_cache_lookup& trx_lookup ) { try { try { - auto do_the_work = [&](auto& fork_db, auto& head) { + auto do_the_work = [&](auto&) { auto start = fc::time_point::now(); const signed_block_ptr& b = bsp->block; const auto& new_protocol_feature_activations = bsp->get_new_protocol_feature_activations(); @@ -3027,7 +2953,7 @@ struct controller_impl { commit_block(s); br.total_time = fc::time_point::now() - start; }; - block_data.apply(do_the_work); + fork_db.apply(do_the_work); return; } catch ( const std::bad_alloc& ) { throw; @@ -3102,7 +3028,7 @@ struct controller_impl { auto block_exts = b->validate_and_extract_extensions(); if( block_exts.count(quorum_certificate_extension::extension_id()) > 0 ) { const auto& qc_ext = std::get(block_exts. lower_bound(quorum_certificate_extension::extension_id())->second); - auto last_qc_block_bsp = block_data.fork_db_fetch_bsp_by_num( qc_ext.qc.block_height ); + auto last_qc_block_bsp = fork_db_fetch_bsp_by_num( qc_ext.qc.block_height ); #warning a mutex might be needed for updating valid_pc last_qc_block_bsp->valid_qc = qc_ext.qc.qc; } @@ -3138,13 +3064,13 @@ struct controller_impl { std::future create_block_token_future( const block_id_type& id, const signed_block_ptr& b ) { EOS_ASSERT( b, block_validate_exception, "null block" ); - auto f = [&](auto& fork_db, auto& head) -> std::future { - return post_async_task( thread_pool.get_executor(), [b, id, &fork_db, control=this]() { + auto f = [&](auto& forkdb) -> std::future { + return post_async_task( thread_pool.get_executor(), [b, id, &forkdb, control=this]() { // no reason for a block_state if fork_db already knows about block - auto existing = fork_db.get_block( id ); + auto existing = forkdb.get_block( id ); EOS_ASSERT( !existing, fork_database_exception, "we already know about this block: ${id}", ("id", id) ); - auto prev = fork_db.get_block_header( b->previous ); + auto prev = forkdb.get_block_header( b->previous ); EOS_ASSERT( prev, unlinkable_block_exception, "unlinkable block ${id}", ("id", id)("previous", b->previous) ); @@ -3152,26 +3078,26 @@ struct controller_impl { } ); }; - return block_data.apply>(f); + return fork_db.apply>(f); } // thread safe, expected to be called from thread other than the main thread std::optional create_block_token( const block_id_type& id, const signed_block_ptr& b ) { EOS_ASSERT( b, block_validate_exception, "null block" ); - auto f = [&](auto& fork_db, auto& head) -> std::optional { + auto f = [&](auto& forkdb) -> std::optional { // no reason for a block_state if fork_db already knows about block - auto existing = fork_db.get_block( id ); + auto existing = forkdb.get_block( id ); EOS_ASSERT( !existing, fork_database_exception, "we already know about this block: ${id}", ("id", id) ); // previous not found could mean that previous block not applied yet - auto prev = fork_db.get_block_header( b->previous ); + auto prev = forkdb.get_block_header( b->previous ); if( !prev ) return {}; return create_block_state_i( id, b, *prev ); }; - return block_data.apply>(f); + return fork_db.apply>(f); } template @@ -3196,9 +3122,9 @@ struct controller_impl { return; } - auto do_push = [&](auto& fork_db, auto& head) { - if constexpr (std::is_same_v>) - fork_db.add( bsp ); + auto do_push = [&](auto& forkdb) { + if constexpr (std::is_same_v>) + forkdb.add( bsp ); if (self.is_trusted_producer(b->producer)) { trusted_producer_light_validation = true; @@ -3207,14 +3133,14 @@ struct controller_impl { emit( self.accepted_block_header, std::tie(bsp->block, bsp->id()) ); if( read_mode != db_read_mode::IRREVERSIBLE ) { - if constexpr (std::is_same_v>) - maybe_switch_forks( br, fork_db.pending_head(), s, forked_branch_cb, trx_lookup ); + if constexpr (std::is_same_v>) + maybe_switch_forks( br, forkdb.pending_head(), s, forked_branch_cb, trx_lookup ); } else { log_irreversible(); } }; - block_data.apply(do_push); + fork_db.apply(do_push); } FC_LOG_AND_RETHROW( ) } @@ -3242,13 +3168,13 @@ struct controller_impl { check_protocol_features(timestamp, cur_features, new_features); }; - auto do_push = [&](auto& fork_db, auto& head) { - if constexpr (std::is_same_v>) { + auto do_push = [&](auto& forkdb) { + if constexpr (std::is_same_v>) { auto bsp = std::make_shared( - *head, b, protocol_features.get_protocol_feature_set(), validator, skip_validate_signee); + *forkdb.chain_head, b, protocol_features.get_protocol_feature_set(), validator, skip_validate_signee); if (s != controller::block_status::irreversible) { - fork_db.add(bsp, true); + forkdb.add(bsp, true); } emit(self.accepted_block_header, std::tie(bsp->block, bsp->id())); @@ -3272,7 +3198,7 @@ struct controller_impl { } }; - block_data.apply(do_push); + fork_db.apply(do_push); } FC_LOG_AND_RETHROW( ) } @@ -3281,13 +3207,14 @@ struct controller_impl { void maybe_switch_forks( controller::block_report& br, const BSP& new_head, controller::block_status s, const forked_callback_t& forked_cb, const trx_meta_cache_lookup& trx_lookup ) { - auto do_maybe_switch_forks = [&](auto& fork_db, auto& head) { + auto do_maybe_switch_forks = [&](auto& forkdb) { bool head_changed = true; + auto& head = forkdb.chain_head; if( new_head->header.previous == head->id() ) { try { apply_block( br, new_head, s, trx_lookup ); } catch ( const std::exception& e ) { - fork_db.remove( new_head->id() ); + forkdb.remove( new_head->id() ); throw; } } else if( new_head->id() != head->id() ) { @@ -3299,7 +3226,7 @@ struct controller_impl { dm_logger->on_switch_forks(head->id(), new_head->id()); } - auto branches = fork_db.fetch_branch_from( new_head->id(), head->id() ); + auto branches = forkdb.fetch_branch_from( new_head->id(), head->id() ); if( branches.second.size() > 0 ) { for( auto itr = branches.second.begin(); itr != branches.second.end(); ++itr ) { @@ -3340,7 +3267,7 @@ struct controller_impl { if( except ) { // ritr currently points to the block that threw // Remove the block that threw and all forks built off it. - fork_db.remove( (*ritr)->id() ); + forkdb.remove( (*ritr)->id() ); // pop all blocks from the bad fork, discarding their transactions // ritr base is a forward itr to the last block successfully applied @@ -3380,7 +3307,7 @@ struct controller_impl { log_irreversible(); }; - block_data.apply(do_maybe_switch_forks); + fork_db.apply(do_maybe_switch_forks); } /// push_block @@ -3700,16 +3627,9 @@ struct controller_impl { } bool irreversible_mode() const { return read_mode == db_read_mode::IRREVERSIBLE; } - const block_id_type& fork_db_head_block_id() const { return block_data.fork_db_head_block_id(irreversible_mode()); } - uint32_t fork_db_head_block_num() const { return block_data.fork_db_head_block_num(irreversible_mode()); } - uint32_t fork_db_head_irreversible_blocknum() const { return block_data.fork_db_head_irreversible_blocknum(irreversible_mode()); } - bool fork_db_has_root() const { return block_data.fork_db_has_root(); } - uint32_t fork_db_root_block_num() const { return block_data.fork_db_root_block_num(); } - const block_id_type& fork_db_root_block_id() const { return block_data.fork_db_root_block_id(); } - block_timestamp_type fork_db_root_timestamp() const { return block_data.fork_db_root_timestamp(); } - - uint32_t head_block_num() const { return block_data.head_block_num(); } - const signed_block_ptr& head_block() const { return block_data.head_block(); } + block_id_type fork_db_head_block_id() const { return fork_db_head_block_id(irreversible_mode()); } + uint32_t fork_db_head_block_num() const { return fork_db_head_block_num(irreversible_mode()); } + uint32_t fork_db_head_irreversible_blocknum() const { return fork_db_head_irreversible_blocknum(irreversible_mode()); } }; /// controller_impl thread_local platform_timer controller_impl::timer; @@ -3912,8 +3832,8 @@ vector controller::get_preactivated_protocol_features()const { } void controller::validate_protocol_features( const vector& features_to_activate )const { - my->check_protocol_features( my->block_data.head_block_time(), - my->block_data.head_activated_protocol_features()->protocol_features, + my->check_protocol_features( my->head_block_time(), + my->head_activated_protocol_features()->protocol_features, features_to_activate ); } @@ -4048,27 +3968,27 @@ uint32_t controller::head_block_num()const { return my->head_block_num(); } block_timestamp_type controller::head_block_timestamp()const { - return my->block_data.head_block_time(); + return my->head_block_time(); } time_point controller::head_block_time()const { - return my->block_data.head_block_time(); + return my->head_block_time(); } block_id_type controller::head_block_id()const { - return my->block_data.head_block_id(); + return my->head_block_id(); } account_name controller::head_block_producer()const { - return my->block_data.head_block_producer(); + return my->head_block_producer(); } const block_header& controller::head_block_header()const { - return my->block_data.head_block_header(); + return my->head_block_header(); } block_state_legacy_ptr controller::head_block_state_legacy()const { // returns null after instant finality activated - auto dpos_head = [](auto& fork_db, auto& head) -> block_state_legacy_ptr { return head; }; - return my->block_data.apply_dpos(dpos_head); + return my->fork_db.apply_dpos( + [](auto& forkdb) -> block_state_legacy_ptr { return forkdb.chain_head; }); } const signed_block_ptr& controller::head_block()const { @@ -4076,10 +3996,10 @@ const signed_block_ptr& controller::head_block()const { } uint32_t controller::fork_db_head_block_num()const { - return my->block_data.fork_db_head_block_num(my->read_mode == db_read_mode::IRREVERSIBLE); + return my->fork_db_head_block_num(my->read_mode == db_read_mode::IRREVERSIBLE); } -const block_id_type& controller::fork_db_head_block_id()const { +block_id_type controller::fork_db_head_block_id()const { return my->fork_db_head_block_id(); } @@ -4140,7 +4060,7 @@ const global_property_object& controller::get_global_properties()const { } signed_block_ptr controller::fetch_block_by_id( const block_id_type& id )const { - auto sb_ptr = my->block_data.fork_db_fetch_block_by_id(id); + auto sb_ptr = my->fork_db_fetch_block_by_id(id); if( sb_ptr ) return sb_ptr; auto bptr = my->blog.read_block_by_num( block_header::num_from_id(id) ); if( bptr && bptr->calculate_id() == id ) return bptr; @@ -4148,7 +4068,7 @@ signed_block_ptr controller::fetch_block_by_id( const block_id_type& id )const { } bool controller::block_exists(const block_id_type&id) const { - signed_block_ptr sb_ptr = my->block_data.fork_db_fetch_block_by_id(id); + signed_block_ptr sb_ptr = my->fork_db_fetch_block_by_id(id); if( sb_ptr ) return true; std::optional sbh = my->blog.read_block_header_by_num( block_header::num_from_id(id) ); if( sbh && sbh->calculate_id() == id ) return true; @@ -4156,7 +4076,7 @@ bool controller::block_exists(const block_id_type&id) const { } std::optional controller::fetch_block_header_by_id( const block_id_type& id )const { - auto sb_ptr = my->block_data.fork_db_fetch_block_by_id(id); + auto sb_ptr = my->fork_db_fetch_block_by_id(id); if( sb_ptr ) return *static_cast(sb_ptr.get()); auto result = my->blog.read_block_header_by_num( block_header::num_from_id(id) ); if( result && result->calculate_id() == id ) return result; @@ -4164,7 +4084,7 @@ std::optional controller::fetch_block_header_by_id( const b } signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { try { - auto b = my->block_data.fork_db_fetch_block_by_num( block_num ); + auto b = my->fork_db_fetch_block_by_num( block_num ); if (b) return b; @@ -4172,7 +4092,7 @@ signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { } FC_CAPTURE_AND_RETHROW( (block_num) ) } std::optional controller::fetch_block_header_by_number( uint32_t block_num )const { try { - auto b = my->block_data.fork_db_fetch_block_by_num(block_num); + auto b = my->fork_db_fetch_block_by_num(block_num); if (b) return *b; @@ -4186,7 +4106,7 @@ block_id_type controller::get_block_id_for_num( uint32_t block_num )const { try bool find_in_blog = (blog_head && block_num <= blog_head->block_num()); if( !find_in_blog ) { - std::optional id = my->block_data.fork_db_fetch_block_id_by_num(block_num); + std::optional id = my->fork_db_fetch_block_id_by_num(block_num); if (id) return *id; } @@ -4303,13 +4223,13 @@ void controller::get_finalizer_state( finalizer_state& fs ) const { // called from net threads bool controller::process_vote_message( const hs_vote_message& vote ) { - auto do_vote = [&vote](auto& fork_db, auto& head) -> std::pair> { - auto bsp = fork_db.get_block(vote.proposal_id); + auto do_vote = [&vote](auto& forkdb) -> std::pair> { + auto bsp = forkdb.get_block(vote.proposal_id); if (bsp) return bsp->aggregate_vote(vote); return {false, {}}; }; - auto [valid, new_lib] = my->block_data.apply_if>>(do_vote); + auto [valid, new_lib] = my->fork_db.apply_if>>(do_vote); if (new_lib) { my->if_irreversible_block_num = *new_lib; } @@ -4318,18 +4238,18 @@ bool controller::process_vote_message( const hs_vote_message& vote ) { const producer_authority_schedule& controller::active_producers()const { if( !(my->pending) ) - return my->block_data.head_active_schedule_auth(); + return my->head_active_schedule_auth(); return my->pending->active_producers(); } const producer_authority_schedule& controller::head_active_producers()const { - return my->block_data.head_active_schedule_auth(); + return my->head_active_schedule_auth(); } const producer_authority_schedule* controller::pending_producers_legacy()const { if( !(my->pending) ) - return my->block_data.head_pending_schedule_auth_legacy(); + return my->head_pending_schedule_auth_legacy(); return my->pending->pending_producers_legacy(); } @@ -4344,7 +4264,7 @@ std::optional controller::proposed_producers_legacy const producer_authority_schedule* controller::next_producers()const { if( !(my->pending) ) - return my->block_data.next_producers(); + return my->next_producers(); return my->pending->next_producers(); } @@ -4518,7 +4438,7 @@ bool controller::is_protocol_feature_activated( const digest_type& feature_diges if( my->pending ) return my->pending->is_protocol_feature_activated( feature_digest ); - const auto& activated_features = my->block_data.head_activated_protocol_features()->protocol_features; + const auto& activated_features = my->head_activated_protocol_features()->protocol_features; return (activated_features.find( feature_digest ) != activated_features.end()); } @@ -4699,7 +4619,7 @@ void controller::replace_producer_keys( const public_key_type& key ) { gp.proposed_schedule.producers.clear(); }); - my->block_data.replace_producer_keys(key); + my->replace_producer_keys(key); } void controller::replace_account_keys( name account, name permission, const public_key_type& key ) { diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index c98a48c3d1..f1aadfe01a 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -8,25 +8,16 @@ #include #include #include +#include #include -#include namespace eosio::chain { using boost::multi_index_container; using namespace boost::multi_index; - template - const uint32_t fork_database::magic_number = 0x30510FDB; - - template - const uint32_t fork_database::min_supported_version = 2; - template - const uint32_t fork_database::max_supported_version = 2; - /** * History: * Version 1: initial version of the new refactored fork database portable format - * Version 2: New format for block_state for hotstuff/instant-finality */ struct by_block_id; @@ -48,9 +39,9 @@ namespace eosio::chain { using bhsp = bs::bhsp_t; using bhs = bhsp::element_type; - using fork_database_t = fork_database; - using branch_type = fork_database_t::branch_type; - using branch_type_pair = fork_database_t::branch_type_pair; + using fork_db_t = fork_database_t; + using branch_type = fork_db_t::branch_type; + using branch_type_pair = fork_db_t::branch_type_pair; using fork_multi_index_type = multi_index_container< bsp, @@ -65,16 +56,15 @@ namespace eosio::chain { BOOST_MULTI_INDEX_CONST_MEM_FUN(bs, const block_id_type&, id)>, composite_key_compare, std::greater, std::greater, sha256_less>>>>; - std::shared_mutex mtx; fork_multi_index_type index; bsp root; // Only uses the block_header_state_legacy portion bsp head; - const std::filesystem::path datadir; + const uint32_t magic_number; - explicit fork_database_impl( const std::filesystem::path& data_dir ) : datadir(data_dir) {} + explicit fork_database_impl(uint32_t magic_number) : magic_number(magic_number) {} - void open_impl( validator_t& validator ); - void close_impl(); + void open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ); + void close_impl( const std::filesystem::path& fork_db_file ); void add_impl( const bsp& n, bool ignore_duplicate, bool validate, validator_t& validator ); bhsp get_block_header_impl( const block_id_type& id ) const; @@ -91,56 +81,44 @@ namespace eosio::chain { }; template - fork_database::fork_database( const std::filesystem::path& data_dir ) - :my( new fork_database_impl( data_dir ) ) + fork_database_t::fork_database_t(uint32_t magic_number) + :my( new fork_database_impl(magic_number) ) {} template - void fork_database::open( validator_t& validator ) { - std::lock_guard g( my->mtx ); - my->open_impl( validator ); + void fork_database_t::open( const std::filesystem::path& fork_db_file, validator_t& validator ) { + my->open_impl( fork_db_file, validator ); } template - std::filesystem::path fork_database::get_data_dir() const { - return my->datadir; - } - - template - void fork_database_impl::open_impl( validator_t& validator ) { - if (!std::filesystem::is_directory(datadir)) - std::filesystem::create_directories(datadir); - - auto fork_db_dat = datadir / config::forkdb_filename; - if( std::filesystem::exists( fork_db_dat ) ) { + void fork_database_impl::open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ) { + if( std::filesystem::exists( fork_db_file ) ) { try { string content; - fc::read_file_contents( fork_db_dat, content ); + fc::read_file_contents( fork_db_file, content ); fc::datastream ds( content.data(), content.size() ); // validate totem uint32_t totem = 0; fc::raw::unpack( ds, totem ); - EOS_ASSERT( totem == fork_database_t::magic_number, fork_database_exception, + EOS_ASSERT( totem == magic_number, fork_database_exception, "Fork database file '${filename}' has unexpected magic number: ${actual_totem}. Expected ${expected_totem}", - ("filename", fork_db_dat) - ("actual_totem", totem) - ("expected_totem", fork_database_t::magic_number) + ("filename", fork_db_file)("actual_totem", totem)("expected_totem", magic_number) ); // validate version uint32_t version = 0; fc::raw::unpack( ds, version ); - EOS_ASSERT( version >= fork_database_t::min_supported_version && version <= fork_database_t::max_supported_version, + EOS_ASSERT( version >= fork_database::min_supported_version && version <= fork_database::max_supported_version, fork_database_exception, "Unsupported version of fork database file '${filename}'. " "Fork database version is ${version} while code supports version(s) [${min},${max}]", - ("filename", fork_db_dat) + ("filename", fork_db_file) ("version", version) - ("min", fork_database_t::min_supported_version) - ("max", fork_database_t::max_supported_version) + ("min", fork_database::min_supported_version) + ("max", fork_database::max_supported_version) ); bhs state; @@ -164,39 +142,36 @@ namespace eosio::chain { head = get_block_impl( head_id ); EOS_ASSERT( head, fork_database_exception, "could not find head while reconstructing fork database from file; '${filename}' is likely corrupted", - ("filename", fork_db_dat) ); + ("filename", fork_db_file) ); } auto candidate = index.template get().begin(); if( candidate == index.template get().end() || !(*candidate)->is_valid() ) { EOS_ASSERT( head->id() == root->id(), fork_database_exception, "head not set to root despite no better option available; '${filename}' is likely corrupted", - ("filename", fork_db_dat) ); + ("filename", fork_db_file) ); } else { EOS_ASSERT( !first_preferred( **candidate, *head ), fork_database_exception, "head not set to best available option available; '${filename}' is likely corrupted", - ("filename", fork_db_dat) ); + ("filename", fork_db_file) ); } - } FC_CAPTURE_AND_RETHROW( (fork_db_dat) ) + } FC_CAPTURE_AND_RETHROW( (fork_db_file) ) - std::filesystem::remove( fork_db_dat ); + std::filesystem::remove( fork_db_file ); } } template - void fork_database::close() { - std::lock_guard g( my->mtx ); - my->close_impl(); + void fork_database_t::close(const std::filesystem::path& fork_db_file) { + my->close_impl(fork_db_file); } template - void fork_database_impl::close_impl() { - auto fork_db_dat = datadir / config::forkdb_filename; - + void fork_database_impl::close_impl(const std::filesystem::path& fork_db_file) { if( !root ) { if( index.size() > 0 ) { elog( "fork_database is in a bad state when closing; not writing out '${filename}'", - ("filename", fork_db_dat) ); + ("filename", fork_db_file) ); } return; } @@ -205,9 +180,9 @@ namespace eosio::chain { // I think it would be easier to have a different magic number for the new format (rather than a different // version), since we do not need to be able to load a fork_db which is meant for a different // consensus (dpos vs if). - std::ofstream out( fork_db_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc ); - fc::raw::pack( out, fork_database_t::magic_number ); - fc::raw::pack( out, fork_database_t::max_supported_version ); // write out current version which is always max_supported_version + std::ofstream out( fork_db_file.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc ); + fc::raw::pack( out, magic_number ); + fc::raw::pack( out, fork_database::max_supported_version ); // write out current version which is always max_supported_version fc::raw::pack( out, *static_cast(&*root) ); // [greg todo] enought to write only bhs for IF? uint32_t num_blocks_in_fork_db = index.size(); fc::raw::pack( out, unsigned_int{num_blocks_in_fork_db} ); @@ -251,20 +226,14 @@ namespace eosio::chain { fc::raw::pack( out, head->id() ); } else { elog( "head not set in fork database; '${filename}' will be corrupted", - ("filename", fork_db_dat) ); + ("filename", fork_db_file) ); } index.clear(); } template - fork_database::~fork_database() { - my->close_impl(); - } - - template - void fork_database::reset( const bhs& root_bhs ) { - std::lock_guard g( my->mtx ); + void fork_database_t::reset( const bhs& root_bhs ) { my->reset_impl(root_bhs); } @@ -278,8 +247,7 @@ namespace eosio::chain { } template - void fork_database::rollback_head_to_root() { - std::lock_guard g( my->mtx ); + void fork_database_t::rollback_head_to_root() { my->rollback_head_to_root_impl(); } @@ -297,8 +265,7 @@ namespace eosio::chain { } template - void fork_database::advance_root( const block_id_type& id ) { - std::lock_guard g( my->mtx ); + void fork_database_t::advance_root( const block_id_type& id ) { my->advance_root_impl( id ); } @@ -337,8 +304,7 @@ namespace eosio::chain { } template - fork_database::bhsp fork_database::get_block_header( const block_id_type& id ) const { - std::shared_lock g( my->mtx ); + fork_database_t::bhsp fork_database_t::get_block_header( const block_id_type& id ) const { return my->get_block_header_impl( id ); } @@ -389,8 +355,7 @@ namespace eosio::chain { } template - void fork_database::add( const bsp& n, bool ignore_duplicate ) { - std::lock_guard g( my->mtx ); + void fork_database_t::add( const bsp& n, bool ignore_duplicate ) { my->add_impl( n, ignore_duplicate, false, []( block_timestamp_type timestamp, const flat_set& cur_features, @@ -400,20 +365,17 @@ namespace eosio::chain { } template - bsp fork_database::root() const { - std::shared_lock g( my->mtx ); + bsp fork_database_t::root() const { return my->root; } template - bsp fork_database::head() const { - std::shared_lock g( my->mtx ); + bsp fork_database_t::head() const { return my->head; } template - bsp fork_database::pending_head() const { - std::shared_lock g( my->mtx ); + bsp fork_database_t::pending_head() const { const auto& indx = my->index.template get(); auto itr = indx.lower_bound( false ); @@ -426,15 +388,14 @@ namespace eosio::chain { } template - fork_database::branch_type - fork_database::fetch_branch(const block_id_type& h, + fork_database_t::branch_type + fork_database_t::fetch_branch(const block_id_type& h, uint32_t trim_after_block_num) const { - std::shared_lock g(my->mtx); return my->fetch_branch_impl(h, trim_after_block_num); } template - fork_database::branch_type + fork_database_t::branch_type fork_database_impl::fetch_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { branch_type result; for (auto s = get_block_impl(h); s; s = get_block_impl(s->previous())) { @@ -446,8 +407,7 @@ namespace eosio::chain { } template - bsp fork_database::search_on_branch( const block_id_type& h, uint32_t block_num ) const { - std::shared_lock g( my->mtx ); + bsp fork_database_t::search_on_branch( const block_id_type& h, uint32_t block_num ) const { return my->search_on_branch_impl( h, block_num ); } @@ -466,14 +426,13 @@ namespace eosio::chain { * end with a common ancestor (same prior block) */ template - fork_database::branch_type_pair - fork_database::fetch_branch_from(const block_id_type& first, const block_id_type& second) const { - std::shared_lock g(my->mtx); + fork_database_t::branch_type_pair + fork_database_t::fetch_branch_from(const block_id_type& first, const block_id_type& second) const { return my->fetch_branch_from_impl(first, second); } template - fork_database::branch_type_pair + fork_database_t::branch_type_pair fork_database_impl::fetch_branch_from_impl(const block_id_type& first, const block_id_type& second) const { pair result; auto first_branch = (first == root->id()) ? root : get_block_impl(first); @@ -534,8 +493,7 @@ namespace eosio::chain { /// remove all of the invalid forks built off of this id including this id template - void fork_database::remove( const block_id_type& id ) { - std::lock_guard g( my->mtx ); + void fork_database_t::remove( const block_id_type& id ) { return my->remove_impl( id ); } @@ -562,8 +520,7 @@ namespace eosio::chain { } template - void fork_database::mark_valid( const bsp& h ) { - std::lock_guard g( my->mtx ); + void fork_database_t::mark_valid( const bsp& h ) { my->mark_valid_impl( h ); } @@ -589,8 +546,7 @@ namespace eosio::chain { } template - bsp fork_database::get_block(const block_id_type& id) const { - std::shared_lock g( my->mtx ); + bsp fork_database_t::get_block(const block_id_type& id) const { return my->get_block_impl(id); } @@ -602,9 +558,80 @@ namespace eosio::chain { return bsp(); } + // ------------------ fork_database ------------------------- + + fork_database::fork_database(const std::filesystem::path& data_dir) + : data_dir(data_dir) { + } + + fork_database::~fork_database() { + apply([&](auto& forkdb) { forkdb.close(data_dir / config::forkdb_filename); }); + } + + void fork_database::open( validator_t& validator ) { + std::lock_guard g(m); + if (!std::filesystem::is_directory(data_dir)) + std::filesystem::create_directories(data_dir); + + auto fork_db_file = data_dir / config::forkdb_filename; + if( std::filesystem::exists( fork_db_file ) ) { + try { + fc::cfile f; + f.set_file_path(fork_db_file); + f.open("rb"); + + fc::cfile_datastream ds(f); + + // determine file type, validate totem + uint32_t totem = 0; + fc::raw::unpack( ds, totem ); + EOS_ASSERT( totem == fork_database_legacy_t::legacy_magic_number || totem == fork_database_if_t::magic_number, fork_database_exception, + "Fork database file '${filename}' has unexpected magic number: ${actual_totem}. Expected ${t1} or ${t2}", + ("filename", fork_db_file) + ("actual_totem", totem)("t1", fork_database_legacy_t::legacy_magic_number)("t2", fork_database_if_t::magic_number) + ); + + if (totem == fork_database_legacy_t::legacy_magic_number) { + apply_dpos([&](auto& forkdb) { + forkdb.open(fork_db_file, validator); + }); + } else { + // file is instant-finality data, so switch to fork_database_if_t + v.emplace(fork_database_if_t::magic_number); + apply_if([&](auto& forkdb) { + forkdb.open(fork_db_file, validator); + }); + } + } FC_CAPTURE_AND_RETHROW( (fork_db_file) ) + } + } + + void fork_database::switch_from_legacy() { + // no need to close fork_db because we don't want to write anything out, file is removed on open + block_state_legacy_ptr head = std::get(v).chain_head; // will throw if called after transistion + auto new_head = std::make_shared(*head); + std::lock_guard g(m); + v.emplace(fork_database_if_t::magic_number); + apply_if([&](auto& forkdb) { + forkdb.chain_head = new_head; + forkdb.reset(*new_head); + }); + } + + std::vector fork_database::fetch_branch_from_head() { + std::vector r; + apply([&](auto& forkdb) { + auto branch = forkdb.fetch_branch(forkdb.head()->id()); + r.reserve(branch.size()); + for (auto& b : branch) + r.push_back(b->block); + }); + return r; + } + // do class instantiations - template class fork_database; - template class fork_database; + template class fork_database_t; + template class fork_database_t; template struct fork_database_impl; template struct fork_database_impl; diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 48253149bc..39b4bdb411 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -68,7 +68,6 @@ struct block_header_state { // ------ functions ----------------------------------------------------------------- -#warning TDDO https://github.com/AntelopeIO/leap/issues/2080 digest_type compute_finalizer_digest() const { return id; }; block_timestamp_type timestamp() const { return header.timestamp; } account_name producer() const { return header.producer; } diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 402209b563..fb70b3451d 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -209,8 +209,6 @@ namespace eosio::chain { const chainbase::database& db()const; - const fork_database_legacy& fork_db()const; - const account_object& get_account( account_name n )const; const global_property_object& get_global_properties()const; const dynamic_global_property_object& get_dynamic_global_properties()const; @@ -249,7 +247,7 @@ namespace eosio::chain { block_state_legacy_ptr head_block_state_legacy()const; uint32_t fork_db_head_block_num()const; - const block_id_type& fork_db_head_block_id()const; + block_id_type fork_db_head_block_id()const; time_point pending_block_time()const; block_timestamp_type pending_block_timestamp()const; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index d43031cf98..654b583095 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -11,7 +11,7 @@ namespace eosio::chain { struct fork_database_impl; /** - * @class fork_database + * @class fork_database_t * @brief manages light-weight state for all potential unconfirmed forks * * As new blocks are received, they are pushed into the fork database. The fork @@ -19,11 +19,14 @@ namespace eosio::chain { * blocks older than the last irreversible block are freed after emitting the * irreversible signal. * - * An internal mutex is used to provide thread-safety. + * Not thread safe, thread safety provided by fork_database below. */ template // either block_state_legacy_ptr or block_state_ptr - class fork_database { + class fork_database_t { public: + static constexpr uint32_t legacy_magic_number = 0x30510FDB; + static constexpr uint32_t magic_number = 0x4242FDB; + using bs = bsp::element_type; using bhsp = bs::bhsp_t; using bhs = bhsp::element_type; @@ -31,13 +34,10 @@ namespace eosio::chain { using branch_type = deque; using branch_type_pair = pair; - explicit fork_database( const std::filesystem::path& data_dir ); - ~fork_database(); + explicit fork_database_t(uint32_t magic_number = legacy_magic_number); - std::filesystem::path get_data_dir() const; - - void open( validator_t& validator ); - void close(); + void open( const std::filesystem::path& fork_db_file, validator_t& validator ); + void close( const std::filesystem::path& fork_db_file ); bhsp get_block_header( const block_id_type& id ) const; bsp get_block( const block_id_type& id ) const; @@ -70,6 +70,9 @@ namespace eosio::chain { bsp head() const; bsp pending_head() const; + // only accessed by main thread, no mutex protection + bsp chain_head; + /** * Returns the sequence of block states resulting from trimming the branch from the * root block (exclusive) to the block with an id of `h` (inclusive) by removing any @@ -95,15 +98,77 @@ namespace eosio::chain { void mark_valid( const bsp& h ); - static const uint32_t magic_number; - - static const uint32_t min_supported_version; - static const uint32_t max_supported_version; - private: unique_ptr> my; }; - using fork_database_legacy = fork_database; - + using fork_database_legacy_t = fork_database_t; + using fork_database_if_t = fork_database_t; + + /** + * Provides thread safety on fork_database_t and provide mechanism for opening the correct type + * as well as switching from legacy to instant-finality. + */ + class fork_database { + mutable std::recursive_mutex m; + const std::filesystem::path data_dir; + std::variant, fork_database_t> v; + public: + explicit fork_database(const std::filesystem::path& data_dir); + ~fork_database(); // close on destruction + + void open( validator_t& validator ); + + void switch_from_legacy(); + + // see fork_database_t::fetch_branch(forkdb->head()->id()) + std::vector fetch_branch_from_head(); + + template + R apply(const F& f) { + std::lock_guard g(m); + if constexpr (std::is_same_v) + std::visit([&](auto& forkdb) { f(forkdb); }, v); + else + return std::visit([&](auto& forkdb) -> R { return f(forkdb); }, v); + } + + template + R apply_if(const F& f) { + if constexpr (std::is_same_v) + std::visit(overloaded{[&](fork_database_legacy_t&) {}, + [&](fork_database_if_t& forkdb) { + std::lock_guard g(m); + f(forkdb); + }}, v); + else + return std::visit(overloaded{[&](fork_database_legacy_t&) -> R { return {}; }, + [&](fork_database_if_t& forkdb) -> R { + std::lock_guard g(m); + return f(forkdb); + }}, v); + } + + template + R apply_dpos(const F& f) { + if constexpr (std::is_same_v) + std::visit(overloaded{[&](fork_database_legacy_t& forkdb) { + std::lock_guard g(m); + f(forkdb); + }, + [&](fork_database_if_t&) {}}, v); + else + return std::visit(overloaded{[&](fork_database_legacy_t& forkdb) -> R { + std::lock_guard g(m); + return f(forkdb); + }, + [&](fork_database_if_t&) -> R { + return {}; + }}, v); + } + + // if we every support more than one version then need to save min/max in fork_database_t + static constexpr uint32_t min_supported_version = 1; + static constexpr uint32_t max_supported_version = 1; + }; } /// eosio::chain diff --git a/programs/leap-util/actions/blocklog.cpp b/programs/leap-util/actions/blocklog.cpp index 88a933f81c..1807081b59 100644 --- a/programs/leap-util/actions/blocklog.cpp +++ b/programs/leap-util/actions/blocklog.cpp @@ -266,18 +266,17 @@ int blocklog_actions::read_log() { opt->first_block = block_logger.first_block_num(); } - using fork_database_t = fork_database_legacy; // [greg todo] what is it is not a legacy fork_db? - fork_database_t::branch_type fork_db_branch; + std::vector fork_db_branch; if(std::filesystem::exists(std::filesystem::path(opt->blocks_dir) / config::reversible_blocks_dir_name / config::forkdb_filename)) { ilog("opening fork_db"); - fork_database_t fork_db(std::filesystem::path(opt->blocks_dir) / config::reversible_blocks_dir_name); + fork_database fork_db(std::filesystem::path(opt->blocks_dir) / config::reversible_blocks_dir_name); fork_db.open([](block_timestamp_type timestamp, const flat_set& cur_features, const vector& new_features) {}); - fork_db_branch = fork_db.fetch_branch(fork_db.head()->id()); + fork_db_branch = fork_db.fetch_branch_from_head(); if(fork_db_branch.empty()) { elog("no blocks available in reversible block database: only block_log blocks are available"); } else { @@ -336,7 +335,7 @@ int blocklog_actions::read_log() { for(auto bitr = fork_db_branch.rbegin(); bitr != fork_db_branch.rend() && block_num <= opt->last_block; ++bitr) { if(opt->as_json_array && contains_obj) *out << ","; - auto next = (*bitr)->block; + auto& next = *bitr; print_block(next); ++block_num; contains_obj = true; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5178c83812..8c79346a00 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -229,6 +229,9 @@ set_property(TEST nodeos_chainbase_allocation_test PROPERTY LABELS nonparalleliz add_test(NAME nodeos_startup_catchup_lr_test COMMAND tests/nodeos_startup_catchup.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST nodeos_startup_catchup_lr_test PROPERTY LABELS long_running_tests) +add_test(NAME nodeos_startup_catchup_if_lr_test COMMAND tests/nodeos_startup_catchup.py -p3 --activate-if -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +set_property(TEST nodeos_startup_catchup_if_lr_test PROPERTY LABELS long_running_tests) + add_test(NAME nodeos_short_fork_take_over_test COMMAND tests/nodeos_short_fork_take_over_test.py -v --wallet-port 9905 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST nodeos_short_fork_take_over_test PROPERTY LABELS nonparallelizable_tests) diff --git a/tests/nodeos_startup_catchup.py b/tests/nodeos_startup_catchup.py index 28364fe6c2..0449f21242 100755 --- a/tests/nodeos_startup_catchup.py +++ b/tests/nodeos_startup_catchup.py @@ -32,7 +32,7 @@ extraArgs = appArgs.add(flag="--catchup-count", type=int, help="How many catchup-nodes to launch", default=10) extraArgs = appArgs.add(flag="--txn-gen-nodes", type=int, help="How many transaction generator nodes", default=2) args = TestHelper.parse_args({"--dump-error-details","--keep-logs","-v","--leave-running", - "-p","--wallet-port","--unshared"}, applicationSpecificArgs=appArgs) + "--activate-if","-p","--wallet-port","--unshared"}, applicationSpecificArgs=appArgs) Utils.Debug=args.v pnodes=args.p if args.p > 0 else 1 startedNonProdNodes = args.txn_gen_nodes if args.txn_gen_nodes >= 2 else 2 @@ -43,6 +43,7 @@ walletPort=args.wallet_port catchupCount=args.catchup_count if args.catchup_count > 0 else 1 totalNodes=startedNonProdNodes+pnodes+catchupCount +activateIF=args.activate_if walletMgr=WalletMgr(True, port=walletPort) testSuccessful=False @@ -56,7 +57,7 @@ cluster.setWalletMgr(walletMgr) Print("Stand up cluster") - if cluster.launch(prodCount=prodCount, onlyBios=False, pnodes=pnodes, totalNodes=totalNodes, totalProducers=pnodes*prodCount, + if cluster.launch(prodCount=prodCount, activateIF=activateIF, onlyBios=False, pnodes=pnodes, totalNodes=totalNodes, totalProducers=pnodes*prodCount, unstartedNodes=catchupCount, loadSystemContract=True, maximumP2pPerHost=totalNodes+trxGeneratorCnt) is False: Utils.errorExit("Failed to stand up eos cluster.") diff --git a/unittests/unapplied_transaction_queue_tests.cpp b/unittests/unapplied_transaction_queue_tests.cpp index 53dd773647..f8c76fe63c 100644 --- a/unittests/unapplied_transaction_queue_tests.cpp +++ b/unittests/unapplied_transaction_queue_tests.cpp @@ -78,7 +78,7 @@ auto create_test_block_state( deque trx_metas ) { return bsp; } -using branch_type_legacy = fork_database::branch_type; +using branch_type_legacy = fork_database_t::branch_type; template void add_forked( unapplied_transaction_queue& queue, const BRANCH_TYPE& forked_branch ) { From 4f634cde7aa3e405c6f360dae10f5430d6ffe74a Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 19 Jan 2024 16:19:21 -0600 Subject: [PATCH 2/8] GH-2048 Minor cleanup --- libraries/chain/controller.cpp | 242 ++++++++++++++++----------------- 1 file changed, 118 insertions(+), 124 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 52e61904f8..75aae0eabe 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -917,27 +917,27 @@ struct controller_impl { } template - typename ForkDB::bsp_t fork_db_head(const ForkDB& fork_db, bool irreversible_mode) const { + typename ForkDB::bsp_t fork_db_head(const ForkDB& forkdb, bool irreversible_mode) const { if (irreversible_mode) { // When in IRREVERSIBLE mode fork_db blocks are marked valid when they become irreversible so that // fork_db.head() returns irreversible block // Use pending_head since this method should return the chain head and not last irreversible. - return fork_db.pending_head(); + return forkdb.pending_head(); } else { - return fork_db.head(); + return forkdb.head(); } } - uint32_t fork_db_head_block_num(bool irreversible_mode) const { - return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode)->block_num(); } ); + uint32_t fork_db_head_block_num() const { + return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->block_num(); } ); } - block_id_type fork_db_head_block_id(bool irreversible_mode) const { - return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode)->id(); } ); + block_id_type fork_db_head_block_id() const { + return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->id(); } ); } - uint32_t fork_db_head_irreversible_blocknum(bool irreversible_mode) const { - return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode)->irreversible_blocknum(); }); + uint32_t fork_db_head_irreversible_blocknum() const { + return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->irreversible_blocknum(); }); } // --------------- access fork_db root ---------------------------------------------------------------------- @@ -1172,8 +1172,8 @@ struct controller_impl { if( new_lib <= lib_num ) return; - auto mark_branch_irreversible = [&](auto& fork_db) { - auto branch = fork_db.fetch_branch( fork_db_head_block_id(), new_lib ); + auto mark_branch_irreversible = [&, this](auto& forkdb) { + auto branch = forkdb.fetch_branch( fork_db_head(forkdb, irreversible_mode())->id(), new_lib ); try { std::vector>> v; v.reserve( branch.size() ); @@ -1199,17 +1199,17 @@ struct controller_impl { root_id = (*bitr)->id(); } } catch( std::exception& ) { - if( root_id != fork_db.root()->id() ) { - fork_db.advance_root( root_id ); + if( root_id != forkdb.root()->id() ) { + forkdb.advance_root( root_id ); } throw; } //db.commit( new_lib ); // redundant - if( root_id != fork_db.root()->id() ) { - branch.emplace_back(fork_db.root()); - fork_db.advance_root( root_id ); + if( root_id != forkdb.root()->id() ) { + branch.emplace_back(forkdb.root()); + forkdb.advance_root( root_id ); } // delete branch in thread pool @@ -2838,123 +2838,120 @@ struct controller_impl { const trx_meta_cache_lookup& trx_lookup ) { try { try { - auto do_the_work = [&](auto&) { - auto start = fc::time_point::now(); - const signed_block_ptr& b = bsp->block; - const auto& new_protocol_feature_activations = bsp->get_new_protocol_feature_activations(); - - auto producer_block_id = bsp->id(); - start_block( b->timestamp, b->confirmed, new_protocol_feature_activations, s, producer_block_id, fc::time_point::maximum() ); - - // validated in create_block_token() - std::get(pending->_block_stage).trx_mroot_or_receipt_digests() = b->transaction_mroot; - - const bool existing_trxs_metas = !bsp->trxs_metas().empty(); - const bool pub_keys_recovered = bsp->is_pub_keys_recovered(); - const bool skip_auth_checks = self.skip_auth_check(); - std::vector> trx_metas; - bool use_bsp_cached = false; - if( pub_keys_recovered || (skip_auth_checks && existing_trxs_metas) ) { - use_bsp_cached = true; - } else { - trx_metas.reserve( b->transactions.size() ); - for( const auto& receipt : b->transactions ) { - if( std::holds_alternative(receipt.trx)) { - const auto& pt = std::get(receipt.trx); - transaction_metadata_ptr trx_meta_ptr = trx_lookup ? trx_lookup( pt.id() ) : transaction_metadata_ptr{}; - if( trx_meta_ptr && *trx_meta_ptr->packed_trx() != pt ) trx_meta_ptr = nullptr; - if( trx_meta_ptr && ( skip_auth_checks || !trx_meta_ptr->recovered_keys().empty() ) ) { - trx_metas.emplace_back( std::move( trx_meta_ptr ), recover_keys_future{} ); - } else if( skip_auth_checks ) { - packed_transaction_ptr ptrx( b, &pt ); // alias signed_block_ptr - trx_metas.emplace_back( - transaction_metadata::create_no_recover_keys( std::move(ptrx), transaction_metadata::trx_type::input ), - recover_keys_future{} ); - } else { - packed_transaction_ptr ptrx( b, &pt ); // alias signed_block_ptr - auto fut = transaction_metadata::start_recover_keys( - std::move( ptrx ), thread_pool.get_executor(), chain_id, fc::microseconds::maximum(), - transaction_metadata::trx_type::input ); - trx_metas.emplace_back( transaction_metadata_ptr{}, std::move( fut ) ); - } + auto start = fc::time_point::now(); + const signed_block_ptr& b = bsp->block; + const auto& new_protocol_feature_activations = bsp->get_new_protocol_feature_activations(); + + auto producer_block_id = bsp->id(); + start_block( b->timestamp, b->confirmed, new_protocol_feature_activations, s, producer_block_id, fc::time_point::maximum() ); + + // validated in create_block_token() + std::get(pending->_block_stage).trx_mroot_or_receipt_digests() = b->transaction_mroot; + + const bool existing_trxs_metas = !bsp->trxs_metas().empty(); + const bool pub_keys_recovered = bsp->is_pub_keys_recovered(); + const bool skip_auth_checks = self.skip_auth_check(); + std::vector> trx_metas; + bool use_bsp_cached = false; + if( pub_keys_recovered || (skip_auth_checks && existing_trxs_metas) ) { + use_bsp_cached = true; + } else { + trx_metas.reserve( b->transactions.size() ); + for( const auto& receipt : b->transactions ) { + if( std::holds_alternative(receipt.trx)) { + const auto& pt = std::get(receipt.trx); + transaction_metadata_ptr trx_meta_ptr = trx_lookup ? trx_lookup( pt.id() ) : transaction_metadata_ptr{}; + if( trx_meta_ptr && *trx_meta_ptr->packed_trx() != pt ) trx_meta_ptr = nullptr; + if( trx_meta_ptr && ( skip_auth_checks || !trx_meta_ptr->recovered_keys().empty() ) ) { + trx_metas.emplace_back( std::move( trx_meta_ptr ), recover_keys_future{} ); + } else if( skip_auth_checks ) { + packed_transaction_ptr ptrx( b, &pt ); // alias signed_block_ptr + trx_metas.emplace_back( + transaction_metadata::create_no_recover_keys( std::move(ptrx), transaction_metadata::trx_type::input ), + recover_keys_future{} ); + } else { + packed_transaction_ptr ptrx( b, &pt ); // alias signed_block_ptr + auto fut = transaction_metadata::start_recover_keys( + std::move( ptrx ), thread_pool.get_executor(), chain_id, fc::microseconds::maximum(), + transaction_metadata::trx_type::input ); + trx_metas.emplace_back( transaction_metadata_ptr{}, std::move( fut ) ); } } } + } - transaction_trace_ptr trace; + transaction_trace_ptr trace; + + size_t packed_idx = 0; + const auto& trx_receipts = std::get(pending->_block_stage).pending_trx_receipts(); + for( const auto& receipt : b->transactions ) { + auto num_pending_receipts = trx_receipts.size(); + if( std::holds_alternative(receipt.trx) ) { + const auto& trx_meta = (use_bsp_cached ? bsp->trxs_metas().at(packed_idx) + : (!!std::get<0>(trx_metas.at(packed_idx)) + ? std::get<0>(trx_metas.at(packed_idx)) + : std::get<1>(trx_metas.at(packed_idx)).get())); + trace = push_transaction(trx_meta, fc::time_point::maximum(), fc::microseconds::maximum(), + receipt.cpu_usage_us, true, 0); + ++packed_idx; + } else if( std::holds_alternative(receipt.trx) ) { + trace = push_scheduled_transaction(std::get(receipt.trx), fc::time_point::maximum(), + fc::microseconds::maximum(), receipt.cpu_usage_us, true); + } else { + EOS_ASSERT( false, block_validate_exception, "encountered unexpected receipt type" ); + } - size_t packed_idx = 0; - const auto& trx_receipts = std::get(pending->_block_stage).pending_trx_receipts(); - for( const auto& receipt : b->transactions ) { - auto num_pending_receipts = trx_receipts.size(); - if( std::holds_alternative(receipt.trx) ) { - const auto& trx_meta = (use_bsp_cached ? bsp->trxs_metas().at(packed_idx) - : (!!std::get<0>(trx_metas.at(packed_idx)) - ? std::get<0>(trx_metas.at(packed_idx)) - : std::get<1>(trx_metas.at(packed_idx)).get())); - trace = push_transaction(trx_meta, fc::time_point::maximum(), fc::microseconds::maximum(), - receipt.cpu_usage_us, true, 0); - ++packed_idx; - } else if( std::holds_alternative(receipt.trx) ) { - trace = push_scheduled_transaction(std::get(receipt.trx), fc::time_point::maximum(), - fc::microseconds::maximum(), receipt.cpu_usage_us, true); - } else { - EOS_ASSERT( false, block_validate_exception, "encountered unexpected receipt type" ); - } + bool transaction_failed = trace && trace->except; + bool transaction_can_fail = receipt.status == transaction_receipt_header::hard_fail && + std::holds_alternative(receipt.trx); - bool transaction_failed = trace && trace->except; - bool transaction_can_fail = receipt.status == transaction_receipt_header::hard_fail && - std::holds_alternative(receipt.trx); + if( transaction_failed && !transaction_can_fail) { + edump((*trace)); + throw *trace->except; + } - if( transaction_failed && !transaction_can_fail) { - edump((*trace)); - throw *trace->except; - } + EOS_ASSERT(trx_receipts.size() > 0, block_validate_exception, + "expected a receipt, block_num ${bn}, block_id ${id}, receipt ${e}", + ("bn", b->block_num())("id", producer_block_id)("e", receipt)); + EOS_ASSERT(trx_receipts.size() == num_pending_receipts + 1, block_validate_exception, + "expected receipt was not added, block_num ${bn}, block_id ${id}, receipt ${e}", + ("bn", b->block_num())("id", producer_block_id)("e", receipt)); + const transaction_receipt_header& r = trx_receipts.back(); + EOS_ASSERT(r == static_cast(receipt), block_validate_exception, + "receipt does not match, ${lhs} != ${rhs}", + ("lhs", r)("rhs", static_cast(receipt))); + } - EOS_ASSERT(trx_receipts.size() > 0, block_validate_exception, - "expected a receipt, block_num ${bn}, block_id ${id}, receipt ${e}", - ("bn", b->block_num())("id", producer_block_id)("e", receipt)); - EOS_ASSERT(trx_receipts.size() == num_pending_receipts + 1, block_validate_exception, - "expected receipt was not added, block_num ${bn}, block_id ${id}, receipt ${e}", - ("bn", b->block_num())("id", producer_block_id)("e", receipt)); - const transaction_receipt_header& r = trx_receipts.back(); - EOS_ASSERT(r == static_cast(receipt), block_validate_exception, - "receipt does not match, ${lhs} != ${rhs}", - ("lhs", r)("rhs", static_cast(receipt))); - } + std::optional qc_info; + auto exts = b->validate_and_extract_header_extensions(); + if (auto if_entry = exts.lower_bound(instant_finality_extension::extension_id()); if_entry != exts.end()) { + auto& if_ext = std::get(if_entry->second); + qc_info = if_ext.qc_info; + } + finish_block(true, qc_info); - std::optional qc_info; - auto exts = b->validate_and_extract_header_extensions(); - if (auto if_entry = exts.lower_bound(instant_finality_extension::extension_id()); if_entry != exts.end()) { - auto& if_ext = std::get(if_entry->second); - qc_info = if_ext.qc_info; - } - finish_block(true, qc_info); + auto& ab = std::get(pending->_block_stage); - auto& ab = std::get(pending->_block_stage); + if( producer_block_id != ab.id() ) { + elog( "Validation block id does not match producer block id" ); - if( producer_block_id != ab.id() ) { - elog( "Validation block id does not match producer block id" ); + report_block_header_diff(*b, ab.header()); - report_block_header_diff(*b, ab.header()); - - // this implicitly asserts that all header fields (less the signature) are identical - EOS_ASSERT(producer_block_id == ab.id(), block_validate_exception, "Block ID does not match", - ("producer_block_id", producer_block_id)("validator_block_id", ab.id())); - } + // this implicitly asserts that all header fields (less the signature) are identical + EOS_ASSERT(producer_block_id == ab.id(), block_validate_exception, "Block ID does not match", + ("producer_block_id", producer_block_id)("validator_block_id", ab.id())); + } - if( !use_bsp_cached ) { - bsp->set_trxs_metas( ab.extract_trx_metas(), !skip_auth_checks ); - } - // create completed_block with the existing block_state as we just verified it is the same as assembled_block - pending->_block_stage = completed_block{ bsp }; + if( !use_bsp_cached ) { + bsp->set_trxs_metas( ab.extract_trx_metas(), !skip_auth_checks ); + } + // create completed_block with the existing block_state as we just verified it is the same as assembled_block + pending->_block_stage = completed_block{ bsp }; + + br = pending->_block_report; // copy before commit block destroys pending + commit_block(s); + br.total_time = fc::time_point::now() - start; - br = pending->_block_report; // copy before commit block destroys pending - commit_block(s); - br.total_time = fc::time_point::now() - start; - }; - fork_db.apply(do_the_work); - return; } catch ( const std::bad_alloc& ) { throw; } catch ( const boost::interprocess::bad_alloc& ) { @@ -3626,10 +3623,7 @@ struct controller_impl { } } - bool irreversible_mode() const { return read_mode == db_read_mode::IRREVERSIBLE; } - block_id_type fork_db_head_block_id() const { return fork_db_head_block_id(irreversible_mode()); } - uint32_t fork_db_head_block_num() const { return fork_db_head_block_num(irreversible_mode()); } - uint32_t fork_db_head_irreversible_blocknum() const { return fork_db_head_irreversible_blocknum(irreversible_mode()); } + bool irreversible_mode() const { return read_mode == db_read_mode::IRREVERSIBLE; } }; /// controller_impl thread_local platform_timer controller_impl::timer; @@ -3996,7 +3990,7 @@ const signed_block_ptr& controller::head_block()const { } uint32_t controller::fork_db_head_block_num()const { - return my->fork_db_head_block_num(my->read_mode == db_read_mode::IRREVERSIBLE); + return my->fork_db_head_block_num(); } block_id_type controller::fork_db_head_block_id()const { From 7ec302e0e48c28207662a0d4417ff3260e7ed771 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 19 Jan 2024 16:48:41 -0600 Subject: [PATCH 3/8] GH-2048 Support const --- libraries/chain/controller.cpp | 2 +- libraries/chain/include/eosio/chain/fork_database.hpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 75aae0eabe..600251aaa1 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -811,7 +811,7 @@ struct controller_impl { chainbase::database db; block_log blog; std::optional pending; - mutable fork_database fork_db; + fork_database fork_db; std::optional pacemaker; std::atomic if_irreversible_block_num{0}; resource_limits_manager resource_limits; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 654b583095..9e90c6e310 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -133,6 +133,15 @@ namespace eosio::chain { return std::visit([&](auto& forkdb) -> R { return f(forkdb); }, v); } + template + R apply(const F& f) const { + std::lock_guard g(m); + if constexpr (std::is_same_v) + std::visit([&](const auto& forkdb) { f(forkdb); }, v); + else + return std::visit([&](const auto& forkdb) -> R { return f(forkdb); }, v); + } + template R apply_if(const F& f) { if constexpr (std::is_same_v) From a0a5573680d190e2bbd699c407f48f742aa88366 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 19 Jan 2024 19:08:40 -0500 Subject: [PATCH 4/8] Whitespace change - avoid very long lines. --- libraries/chain/controller.cpp | 109 +++++++++++------- libraries/chain/fork_database.cpp | 25 ++-- .../include/eosio/chain/fork_database.hpp | 8 +- 3 files changed, 89 insertions(+), 53 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 600251aaa1..8b5f1896bb 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -150,19 +150,24 @@ struct completed_block { } const producer_authority_schedule* next_producers() const { - return std::visit(overloaded{ - [](const block_state_legacy_ptr& bsp) -> const producer_authority_schedule* { return bsp->pending_schedule_auth();}, - [](const block_state_ptr& bsp) -> const producer_authority_schedule* { - return bsp->proposer_policies.empty() ? nullptr : &bsp->proposer_policies.begin()->second->proposer_schedule; - } - }, bsp); + return std::visit(overloaded{[](const block_state_legacy_ptr& bsp) -> const producer_authority_schedule* { + return bsp->pending_schedule_auth(); + }, + [](const block_state_ptr& bsp) -> const producer_authority_schedule* { + return bsp->proposer_policies.empty() + ? nullptr + : &bsp->proposer_policies.begin()->second->proposer_schedule; + }}, + bsp); } const producer_authority_schedule* pending_producers_legacy() const { - return std::visit(overloaded{ - [](const block_state_legacy_ptr& bsp) -> const producer_authority_schedule* { return &bsp->pending_schedule.schedule; }, - [](const block_state_ptr&) -> const producer_authority_schedule* { return nullptr; } - }, bsp); + return std::visit( + overloaded{[](const block_state_legacy_ptr& bsp) -> const producer_authority_schedule* { + return &bsp->pending_schedule.schedule; + }, + [](const block_state_ptr&) -> const producer_authority_schedule* { return nullptr; }}, + bsp); } bool is_protocol_feature_activated(const digest_type& digest) const { @@ -285,27 +290,27 @@ struct assembled_block { } const producer_authority_schedule* next_producers() const { - return std::visit(overloaded{ - [](const assembled_block_dpos& ab) -> const producer_authority_schedule* { - return ab.new_producer_authority_cache.has_value() ? &ab.new_producer_authority_cache.value() : nullptr; - }, - [](const assembled_block_if& ab) -> const producer_authority_schedule* { - return ab.bhs.proposer_policies.empty() ? nullptr : &ab.bhs.proposer_policies.begin()->second->proposer_schedule; - } - }, + return std::visit(overloaded{[](const assembled_block_dpos& ab) -> const producer_authority_schedule* { + return ab.new_producer_authority_cache.has_value() + ? &ab.new_producer_authority_cache.value() + : nullptr; + }, + [](const assembled_block_if& ab) -> const producer_authority_schedule* { + return ab.bhs.proposer_policies.empty() + ? nullptr + : &ab.bhs.proposer_policies.begin()->second->proposer_schedule; + }}, v); } const producer_authority_schedule* pending_producers_legacy() const { - return std::visit(overloaded{ - [](const assembled_block_dpos& ab) -> const producer_authority_schedule* { - return ab.new_producer_authority_cache.has_value() ? &ab.new_producer_authority_cache.value() : nullptr; - }, - [](const assembled_block_if&) -> const producer_authority_schedule* { - return nullptr; - } - }, - v); + return std::visit( + overloaded{[](const assembled_block_dpos& ab) -> const producer_authority_schedule* { + return ab.new_producer_authority_cache.has_value() ? &ab.new_producer_authority_cache.value() + : nullptr; + }, + [](const assembled_block_if&) -> const producer_authority_schedule* { return nullptr; }}, + v); } const block_signing_authority& pending_block_signing_authority() const { @@ -668,7 +673,7 @@ struct building_block { // branch from parent std::optional qc_data; if (!validating) { - // get fork_database so that we can search for the best qc to include in this block. + // get fork_database so that we can search for the best qc to include in this block. fork_db.apply_if([&](const auto& forkdb) { auto branch = forkdb.fetch_branch(parent_id()); for( auto it = branch.begin(); it != branch.end(); ++it ) { @@ -847,12 +852,27 @@ struct controller_impl { int64_t set_proposed_producers( vector producers ); int64_t set_proposed_producers_legacy( vector producers ); - uint32_t head_block_num() const { return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->block_num(); }); } - block_timestamp_type head_block_time() const { return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->timestamp(); }); } - account_name head_block_producer() const { return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->producer(); }); } - const block_id_type& head_block_id() const { return fork_db.apply([](const auto& forkdb) -> const block_id_type& { return forkdb.chain_head->id(); }); } - const block_header& head_block_header() const { return fork_db.apply([](const auto& forkdb) -> const block_header& { return forkdb.chain_head->header; }); } - const signed_block_ptr& head_block() const { return fork_db.apply([](const auto& forkdb) -> const signed_block_ptr& { return forkdb.chain_head->block; }); } + uint32_t head_block_num() const { + return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->block_num(); }); + } + block_timestamp_type head_block_time() const { + return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->timestamp(); }); + } + account_name head_block_producer() const { + return fork_db.apply([](const auto& forkdb) { return forkdb.chain_head->producer(); }); + } + const block_id_type& head_block_id() const { + return fork_db.apply( + [](const auto& forkdb) -> const block_id_type& { return forkdb.chain_head->id(); }); + } + const block_header& head_block_header() const { + return fork_db.apply( + [](const auto& forkdb) -> const block_header& { return forkdb.chain_head->header; }); + } + const signed_block_ptr& head_block() const { + return fork_db.apply( + [](const auto& forkdb) -> const signed_block_ptr& { return forkdb.chain_head->block; }); + } protocol_feature_activation_set_ptr head_activated_protocol_features() const { return fork_db.apply([](const auto& forkdb) { @@ -929,15 +949,18 @@ struct controller_impl { } uint32_t fork_db_head_block_num() const { - return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->block_num(); } ); + return fork_db.apply( + [&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->block_num(); }); } block_id_type fork_db_head_block_id() const { - return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->id(); } ); + return fork_db.apply( + [&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->id(); }); } uint32_t fork_db_head_irreversible_blocknum() const { - return fork_db.apply([&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->irreversible_blocknum(); }); + return fork_db.apply( + [&](const auto& forkdb) { return fork_db_head(forkdb, irreversible_mode())->irreversible_blocknum(); }); } // --------------- access fork_db root ---------------------------------------------------------------------- @@ -963,11 +986,13 @@ struct controller_impl { typename ForkDB::bsp_t prev = forkdb.get_block( forkdb.chain_head->previous() ); if( !prev ) { - EOS_ASSERT( forkdb.root()->id() == forkdb.chain_head->previous(), block_validate_exception, "attempt to pop beyond last irreversible block" ); + EOS_ASSERT( forkdb.root()->id() == forkdb.chain_head->previous(), block_validate_exception, + "attempt to pop beyond last irreversible block" ); prev = forkdb.root(); } - EOS_ASSERT( forkdb.chain_head->block, block_validate_exception, "attempting to pop a block that was sparsely loaded from a snapshot"); + EOS_ASSERT( forkdb.chain_head->block, block_validate_exception, + "attempting to pop a block that was sparsely loaded from a snapshot"); forkdb.chain_head = prev; return prev->block_num(); @@ -1411,8 +1436,10 @@ struct controller_impl { } void startup(std::function shutdown, std::function check_shutdown) { - EOS_ASSERT( db.revision() >= 1, database_exception, "This version of controller::startup does not work with a fresh state database." ); - EOS_ASSERT( fork_db_has_head(), fork_database_exception, "No existing fork database despite existing chain state. Replay required." ); + EOS_ASSERT( db.revision() >= 1, database_exception, + "This version of controller::startup does not work with a fresh state database." ); + EOS_ASSERT( fork_db_has_head(), fork_database_exception, + "No existing fork database despite existing chain state. Replay required." ); this->shutdown = std::move(shutdown); uint32_t lib_num = fork_db_root_block_num(); diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index f1aadfe01a..304856c1e5 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -39,7 +39,7 @@ namespace eosio::chain { using bhsp = bs::bhsp_t; using bhs = bhsp::element_type; - using fork_db_t = fork_database_t; + using fork_db_t = fork_database_t; using branch_type = fork_db_t::branch_type; using branch_type_pair = fork_db_t::branch_type_pair; @@ -284,7 +284,8 @@ namespace eosio::chain { for( auto b = new_root; b; ) { blocks_to_remove.emplace_back( b->previous() ); b = get_block_impl( blocks_to_remove.back() ); - EOS_ASSERT( b || blocks_to_remove.back() == root->id(), fork_database_exception, "invariant violation: orphaned branch was present in forked database" ); + EOS_ASSERT( b || blocks_to_remove.back() == root->id(), fork_database_exception, + "invariant violation: orphaned branch was present in forked database" ); } // The new root block should be erased from the fork database index individually rather than with the remove method, @@ -331,15 +332,22 @@ namespace eosio::chain { EOS_ASSERT( prev_bh, unlinkable_block_exception, "unlinkable block", ("id", n->id())("previous", n->previous()) ); - if( validate ) { + if (validate) { try { const auto& exts = n->header_exts; - if( exts.count(protocol_feature_activation::extension_id()) > 0 ) { - const auto& new_protocol_features = std::get(exts.lower_bound(protocol_feature_activation::extension_id())->second).protocol_features; - validator( n->timestamp(), static_cast(prev_bh.get())->get_activated_protocol_features()->protocol_features, new_protocol_features ); + if (exts.count(protocol_feature_activation::extension_id()) > 0) { + const auto& new_protocol_features = + std::get( + exts.lower_bound(protocol_feature_activation::extension_id())->second) + .protocol_features; + validator(n->timestamp(), + static_cast(prev_bh.get())->get_activated_protocol_features()->protocol_features, + new_protocol_features); } - } EOS_RETHROW_EXCEPTIONS( fork_database_exception, "serialized fork database is incompatible with configured protocol features" ) + } + EOS_RETHROW_EXCEPTIONS(fork_database_exception, + "serialized fork database is incompatible with configured protocol features") } auto inserted = index.insert(n); @@ -585,7 +593,8 @@ namespace eosio::chain { // determine file type, validate totem uint32_t totem = 0; fc::raw::unpack( ds, totem ); - EOS_ASSERT( totem == fork_database_legacy_t::legacy_magic_number || totem == fork_database_if_t::magic_number, fork_database_exception, + EOS_ASSERT( totem == fork_database_legacy_t::legacy_magic_number || + totem == fork_database_if_t::magic_number, fork_database_exception, "Fork database file '${filename}' has unexpected magic number: ${actual_totem}. Expected ${t1} or ${t2}", ("filename", fork_db_file) ("actual_totem", totem)("t1", fork_database_legacy_t::legacy_magic_number)("t2", fork_database_if_t::magic_number) diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 9e90c6e310..e25c8140d2 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -165,15 +165,15 @@ namespace eosio::chain { std::lock_guard g(m); f(forkdb); }, - [&](fork_database_if_t&) {}}, v); + [&](fork_database_if_t&) {}}, + v); else return std::visit(overloaded{[&](fork_database_legacy_t& forkdb) -> R { std::lock_guard g(m); return f(forkdb); }, - [&](fork_database_if_t&) -> R { - return {}; - }}, v); + [&](fork_database_if_t&) -> R { return {}; }}, + v); } // if we every support more than one version then need to save min/max in fork_database_t From 5cae9d9d9086482bc904078498feaedf29c8014b Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Sat, 20 Jan 2024 10:18:20 -0500 Subject: [PATCH 5/8] Fix indentation --- libraries/chain/fork_database.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 304856c1e5..352bcec54a 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -337,10 +337,8 @@ namespace eosio::chain { const auto& exts = n->header_exts; if (exts.count(protocol_feature_activation::extension_id()) > 0) { - const auto& new_protocol_features = - std::get( - exts.lower_bound(protocol_feature_activation::extension_id())->second) - .protocol_features; + const auto& pfa = exts.lower_bound(protocol_feature_activation::extension_id())->second; + const auto& new_protocol_features = std::get(pfa).protocol_features; validator(n->timestamp(), static_cast(prev_bh.get())->get_activated_protocol_features()->protocol_features, new_protocol_features); From 12075229f185ea6f3998d9a66a83a945bad4ae52 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 20 Jan 2024 09:43:09 -0600 Subject: [PATCH 6/8] GH-2048 Misc cleanup --- libraries/chain/controller.cpp | 35 +++++----- libraries/chain/fork_database.cpp | 19 +++-- .../include/eosio/chain/fork_database.hpp | 70 +++++++++++-------- 3 files changed, 73 insertions(+), 51 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 600251aaa1..5dbcecd145 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -668,7 +668,7 @@ struct building_block { // branch from parent std::optional qc_data; if (!validating) { - // get fork_database so that we can search for the best qc to include in this block. + // get fork_database so that we can search for the best qc to include in this block. fork_db.apply_if([&](const auto& forkdb) { auto branch = forkdb.fetch_branch(parent_id()); for( auto it = branch.begin(); it != branch.end(); ++it ) { @@ -1245,7 +1245,7 @@ struct controller_impl { forkdb.chain_head->block = std::make_shared(genheader.header); }; - fork_db.apply_dpos(init_blockchain); // assuming here that genesis_state is always dpos + fork_db.apply_legacy(init_blockchain); // assuming here that genesis_state is always dpos db.set_revision( head_block_num() ); initialize_database(genesis); @@ -1625,7 +1625,7 @@ struct controller_impl { section.template add_row(*forkdb.chain_head, db); }); }; - fork_db.apply_dpos(write_block_state_section); + fork_db.apply_legacy(write_block_state_section); controller_index_set::walk_indices([this, &snapshot]( auto utils ){ using value_t = typename decltype(utils)::index_t::value_type; @@ -1702,7 +1702,7 @@ struct controller_impl { forkdb.chain_head = std::make_shared(); static_cast(*forkdb.chain_head) = head_header_state; }; - fork_db.apply_dpos(read_block_state_section); + fork_db.apply_legacy(read_block_state_section); controller_index_set::walk_indices([this, &snapshot, &header]( auto utils ){ using value_t = typename decltype(utils)::index_t::value_type; @@ -2470,16 +2470,17 @@ struct controller_impl { "db revision is not on par with head block", ("db.revision()", db.revision())("controller_head_block", head_block_num())("fork_db_head_block", fork_db_head_block_num()) ); - fork_db.apply_dpos([&](auto& forkdb) { - maybe_session session = self.skip_db_sessions(s) ? maybe_session() : maybe_session(db); - pending.emplace(std::move(session), *forkdb.chain_head, when, confirm_block_count, new_protocol_feature_activations); - }); - fork_db.apply_if([&](auto& forkdb) { - maybe_session session = self.skip_db_sessions(s) ? maybe_session() : maybe_session(db); - building_block_input bbi{ forkdb.chain_head->id(), when, forkdb.chain_head->get_scheduled_producer(when).producer_name, - new_protocol_feature_activations }; - pending.emplace(std::move(session), *forkdb.chain_head, bbi); - }); + fork_db.apply( + [&](auto& forkdb) { // legacy + maybe_session session = self.skip_db_sessions(s) ? maybe_session() : maybe_session(db); + pending.emplace(std::move(session), *forkdb.chain_head, when, confirm_block_count, new_protocol_feature_activations); + }, + [&](auto& forkdb) { // instant-finality + maybe_session session = self.skip_db_sessions(s) ? maybe_session() : maybe_session(db); + building_block_input bbi{forkdb.chain_head->id(), when, forkdb.chain_head->get_scheduled_producer(when).producer_name, + new_protocol_feature_activations}; + pending.emplace(std::move(session), *forkdb.chain_head, bbi); + }); pending->_block_status = s; pending->_producer_block_id = producer_block_id; @@ -2686,7 +2687,7 @@ struct controller_impl { fork_db.apply(add_completed_block); - fork_db.apply_dpos([this](auto& forkdb) { + fork_db.apply_legacy([this](auto& forkdb) { #warning todo: support deep_mind_logger even when in IF mode (use apply instead of apply_dpos) // at block level, no transaction specific logging is possible if (auto* dm_logger = get_deep_mind_logger(false)) { @@ -2714,7 +2715,7 @@ struct controller_impl { } return false; }; - if (fork_db.apply_dpos(transition)) { + if (fork_db.apply_legacy(transition)) { fork_db.switch_from_legacy(); } @@ -3981,7 +3982,7 @@ const block_header& controller::head_block_header()const { block_state_legacy_ptr controller::head_block_state_legacy()const { // returns null after instant finality activated - return my->fork_db.apply_dpos( + return my->fork_db.apply_legacy( [](auto& forkdb) -> block_state_legacy_ptr { return forkdb.chain_head; }); } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index f1aadfe01a..552298a025 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -39,7 +39,7 @@ namespace eosio::chain { using bhsp = bs::bhsp_t; using bhs = bhsp::element_type; - using fork_db_t = fork_database_t; + using fork_db_t = fork_database_t; using branch_type = fork_db_t::branch_type; using branch_type_pair = fork_db_t::branch_type_pair; @@ -565,6 +565,10 @@ namespace eosio::chain { } fork_database::~fork_database() { + close(); + } + + void fork_database::close() { apply([&](auto& forkdb) { forkdb.close(data_dir / config::forkdb_filename); }); } @@ -592,26 +596,29 @@ namespace eosio::chain { ); if (totem == fork_database_legacy_t::legacy_magic_number) { - apply_dpos([&](auto& forkdb) { + apply_legacy([&](auto& forkdb) { forkdb.open(fork_db_file, validator); }); } else { // file is instant-finality data, so switch to fork_database_if_t - v.emplace(fork_database_if_t::magic_number); + vforkdb.emplace(fork_database_if_t::magic_number); apply_if([&](auto& forkdb) { forkdb.open(fork_db_file, validator); }); } + apply([&](auto& forkdb) { + forkdb.open(fork_db_file, validator); + }); } FC_CAPTURE_AND_RETHROW( (fork_db_file) ) } } void fork_database::switch_from_legacy() { + std::lock_guard g(m); // no need to close fork_db because we don't want to write anything out, file is removed on open - block_state_legacy_ptr head = std::get(v).chain_head; // will throw if called after transistion + block_state_legacy_ptr head = std::get(vforkdb).chain_head; // will throw if called after transistion auto new_head = std::make_shared(*head); - std::lock_guard g(m); - v.emplace(fork_database_if_t::magic_number); + vforkdb.emplace(fork_database_if_t::magic_number); apply_if([&](auto& forkdb) { forkdb.chain_head = new_head; forkdb.reset(*new_head); diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 9e90c6e310..a2f4f7f868 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -20,6 +20,8 @@ namespace eosio::chain { * irreversible signal. * * Not thread safe, thread safety provided by fork_database below. + * fork_database should be used instead of fork_database_t directly as it manages + * the different supported types. */ template // either block_state_legacy_ptr or block_state_ptr class fork_database_t { @@ -107,17 +109,20 @@ namespace eosio::chain { /** * Provides thread safety on fork_database_t and provide mechanism for opening the correct type - * as well as switching from legacy to instant-finality. + * as well as switching from legacy (old dpos) to instant-finality. + * + * All methods assert until open() is closed. */ class fork_database { mutable std::recursive_mutex m; const std::filesystem::path data_dir; - std::variant, fork_database_t> v; + std::variant, fork_database_t> vforkdb; public: explicit fork_database(const std::filesystem::path& data_dir); ~fork_database(); // close on destruction void open( validator_t& validator ); + void close(); void switch_from_legacy(); @@ -128,55 +133,64 @@ namespace eosio::chain { R apply(const F& f) { std::lock_guard g(m); if constexpr (std::is_same_v) - std::visit([&](auto& forkdb) { f(forkdb); }, v); + std::visit([&](auto& forkdb) { f(forkdb); }, vforkdb); else - return std::visit([&](auto& forkdb) -> R { return f(forkdb); }, v); + return std::visit([&](auto& forkdb) -> R { return f(forkdb); }, vforkdb); } template R apply(const F& f) const { std::lock_guard g(m); if constexpr (std::is_same_v) - std::visit([&](const auto& forkdb) { f(forkdb); }, v); + std::visit([&](const auto& forkdb) { f(forkdb); }, vforkdb); else - return std::visit([&](const auto& forkdb) -> R { return f(forkdb); }, v); + return std::visit([&](const auto& forkdb) -> R { return f(forkdb); }, vforkdb); } + /// Apply for when only need lambda executed when in instant-finality mode template R apply_if(const F& f) { + std::lock_guard g(m); if constexpr (std::is_same_v) std::visit(overloaded{[&](fork_database_legacy_t&) {}, - [&](fork_database_if_t& forkdb) { - std::lock_guard g(m); - f(forkdb); - }}, v); + [&](fork_database_if_t& forkdb) { f(forkdb); }}, + vforkdb); else return std::visit(overloaded{[&](fork_database_legacy_t&) -> R { return {}; }, - [&](fork_database_if_t& forkdb) -> R { - std::lock_guard g(m); - return f(forkdb); - }}, v); + [&](fork_database_if_t& forkdb) -> R { return f(forkdb); }}, + vforkdb); } + /// Apply for when only need lambda executed when in legacy mode template - R apply_dpos(const F& f) { + R apply_legacy(const F& f) { + std::lock_guard g(m); + if constexpr (std::is_same_v) + std::visit(overloaded{[&](fork_database_legacy_t& forkdb) { f(forkdb); }, + [&](fork_database_if_t&) {}}, + vforkdb); + else + return std::visit(overloaded{[&](fork_database_legacy_t& forkdb) -> R { return f(forkdb); }, + [&](fork_database_if_t&) -> R { return {}; }}, + vforkdb); + } + + /// @param legacy_f the lambda to execute if in legacy mode + /// @param if_f the lambda to execute if in instant-finality mode + template + R apply(const LegacyF& legacy_f, const IfF& if_f) { + std::lock_guard g(m); if constexpr (std::is_same_v) - std::visit(overloaded{[&](fork_database_legacy_t& forkdb) { - std::lock_guard g(m); - f(forkdb); - }, - [&](fork_database_if_t&) {}}, v); + std::visit(overloaded{[&](fork_database_legacy_t& forkdb) { legacy_f(forkdb); }, + [&](fork_database_if_t& forkdb) { if_f(forkdb); }}, + vforkdb); else - return std::visit(overloaded{[&](fork_database_legacy_t& forkdb) -> R { - std::lock_guard g(m); - return f(forkdb); - }, - [&](fork_database_if_t&) -> R { - return {}; - }}, v); + return std::visit(overloaded{[&](fork_database_legacy_t& forkdb) -> R { return legacy_f(forkdb); }, + [&](fork_database_if_t& forkdb) -> R { return if_f(forkdb); }}, + vforkdb); } - // if we every support more than one version then need to save min/max in fork_database_t + // if we ever support more than one version then need to save min/max in fork_database_t static constexpr uint32_t min_supported_version = 1; static constexpr uint32_t max_supported_version = 1; }; From 175af8f89a52a23c2af43179b2b01f6dd29cc6a5 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 20 Jan 2024 11:22:32 -0600 Subject: [PATCH 7/8] GH-2048 Remove extra open --- libraries/chain/fork_database.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 552298a025..c729ce91e8 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -606,9 +606,6 @@ namespace eosio::chain { forkdb.open(fork_db_file, validator); }); } - apply([&](auto& forkdb) { - forkdb.open(fork_db_file, validator); - }); } FC_CAPTURE_AND_RETHROW( (fork_db_file) ) } } From e07d93705fbbbdc0de84b5966e70695cb247699e Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Sat, 20 Jan 2024 11:25:52 -0600 Subject: [PATCH 8/8] GH-2048 Fix merge issue --- libraries/chain/controller.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 16846bb345..1b45bcfa76 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2934,13 +2934,6 @@ struct controller_impl { ("lhs", r)("rhs", static_cast(receipt))); } - std::optional qc_info; - auto exts = b->validate_and_extract_header_extensions(); - if (auto if_entry = exts.lower_bound(instant_finality_extension::extension_id()); if_entry != exts.end()) { - auto& if_ext = std::get(if_entry->second); - qc_info = if_ext.qc_info; - } - assemble_block(true, extract_qc_data(b)); auto& ab = std::get(pending->_block_stage);