diff --git a/docs/block_production/lifecycle.md b/docs/block_production/lifecycle.md index cba10965f0..19478ffb2a 100644 --- a/docs/block_production/lifecycle.md +++ b/docs/block_production/lifecycle.md @@ -12,7 +12,7 @@ flowchart TD direction TB start -- "stage = Ø" --> sb sb("start_block()"):::fun -- "stage = building_block" --> et - et["execute transactions" ] -- "stage = building_block" --> fb("finalize_block()"):::fun + et["execute transactions" ] -- "stage = building_block" --> fb("finish_block()"):::fun fb -- "stage = assembled block" --> cb["add transaction metadata and create completed block"] cb -- "stage = completed block" --> commit("commit_block() (where we [maybe] add to fork_db and mark valid)"):::fun diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index b50c95dfab..8f2255e134 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -96,7 +96,8 @@ block_header_state block_header_state::next(block_header_state_input& input) con if(!proposer_policies.empty()) { auto it = proposer_policies.begin(); - if (it->first <= input.timestamp) { + // -1 since this is called after the block is built, this will be the active schedule for the next block + if (it->first.slot <= input.timestamp.slot - 1) { result.active_proposer_policy = it->second; result.header.schedule_version = header.schedule_version + 1; result.active_proposer_policy->proposer_schedule.version = result.header.schedule_version; @@ -108,7 +109,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con if (input.new_proposer_policy) { // called when assembling the block - result.proposer_policies[result.header.timestamp] = input.new_proposer_policy; + result.proposer_policies[input.new_proposer_policy->active_time] = input.new_proposer_policy; } // finalizer policy @@ -181,7 +182,7 @@ block_header_state block_header_state::next(const signed_block_header& h, const // retrieve instant_finality_extension data from block header extension // -------------------------------------------------------------------- - EOS_ASSERT(exts.count(instant_finality_extension::extension_id() > 0), misc_exception, + EOS_ASSERT(exts.count(instant_finality_extension::extension_id()) > 0, invalid_block_header_extension, "Instant Finality Extension is expected to be present in all block headers after switch to IF"); auto if_entry = exts.lower_bound(instant_finality_extension::extension_id()); auto& if_ext = std::get(if_entry->second); diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 67a40ebcc3..b750e68292 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -1,5 +1,6 @@ #include #include +#include #include namespace eosio::chain { @@ -16,7 +17,29 @@ block_state::block_state(const block_header_state& bhs, deque(signed_block_header{bhs.header})) // [greg todo] do we need signatures? , pub_keys_recovered(true) // probably not needed , cached_trxs(std::move(trx_metas)) -{} +{ + block->transactions = std::move(trx_receipts); +} + +// Used for transition from dpos to instant-finality +block_state::block_state(const block_state_legacy& bsp) { + block_header_state::id = bsp.id(); + header = bsp.header; + activated_protocol_features = bsp.activated_protocol_features; + std::optional ext = bsp.block->extract_header_extension(instant_finality_extension::extension_id()); + assert(ext); // required by current transition mechanism + const auto& if_extension = std::get(*ext); + assert(if_extension.new_finalizer_policy); // required by current transition mechanism + active_finalizer_policy = std::make_shared(*if_extension.new_finalizer_policy); + active_proposer_policy = std::make_shared(); + active_proposer_policy->active_time = bsp.timestamp(); + active_proposer_policy->proposer_schedule = bsp.active_schedule; + header_exts = bsp.header_exts; + block = bsp.block; + validated = bsp.is_valid(); + pub_keys_recovered = bsp._pub_keys_recovered; + cached_trxs = bsp._cached_trxs; +} deque block_state::extract_trxs_metas() { pub_keys_recovered = false; diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index eacb250315..00992e1172 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -127,7 +127,7 @@ struct block_data_gen_t { bsp head; fork_db_t fork_db; - block_data_gen_t(const std::filesystem::path& path) : fork_db(path) {} + explicit block_data_gen_t(const std::filesystem::path& path) : fork_db(path) {} bsp fork_db_head(bool irreversible_mode) const { if (irreversible_mode) { @@ -177,6 +177,19 @@ struct block_data_t { 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); @@ -194,6 +207,17 @@ struct block_data_t { [](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); @@ -278,6 +302,22 @@ struct block_data_t { }, 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); + } + template R apply(F& f) { if constexpr (std::is_same_v) @@ -863,8 +903,8 @@ struct building_block { bb_input, transaction_mroot, action_mroot, std::move(bb.new_proposer_policy), std::move(bb.new_finalizer_policy), qc_data ? qc_data->qc_info : std::optional{} }; - assembled_block::assembled_block_if ab{bb.active_producer_authority, bb.parent.next(bhs_input), - bb.pending_trx_metas, bb.pending_trx_receipts, + assembled_block::assembled_block_if ab{std::move(bb.active_producer_authority), bb.parent.next(bhs_input), + std::move(bb.pending_trx_metas), std::move(bb.pending_trx_receipts), qc_data ? std::move(qc_data->qc) : std::optional{}}; return assembled_block{.v = std::move(ab)}; @@ -973,7 +1013,7 @@ struct controller_impl { std::optional pending; block_data_t block_data; // includes `head` aand `fork_db` std::optional pacemaker; - std::atomic hs_irreversible_block_num{0}; + std::atomic if_irreversible_block_num{0}; resource_limits_manager resource_limits; subjective_billing subjective_bill; authorization_manager authorization; @@ -1156,8 +1196,8 @@ struct controller_impl { ("lib_num", lib_num)("bn", fork_db_root_block_num()) ); } - const uint32_t hs_lib = hs_irreversible_block_num; - const uint32_t new_lib = hs_lib > 0 ? hs_lib : fork_db_head_irreversible_blocknum(); + const uint32_t if_lib = if_irreversible_block_num; + const uint32_t new_lib = if_lib > 0 ? if_lib : fork_db_head_irreversible_blocknum(); if( new_lib <= lib_num ) return; @@ -2442,10 +2482,6 @@ struct controller_impl { { EOS_ASSERT( !pending, block_validate_exception, "pending block already exists" ); - // can change during start_block, so use same value throughout - uint32_t hs_lib = hs_irreversible_block_num.load(); - const bool hs_active = hs_lib > 0; // the transition from 0 to >0 cannot happen during start_block - emit( self.block_start, head_block_num() + 1 ); // at block level, no transaction specific logging is possible @@ -2548,37 +2584,35 @@ struct controller_impl { const auto& gpo = self.get_global_properties(); - if (!hs_active) { -#warning todo: how do we update the producer_schedule after the switch to IF? - bb.apply_dpos([&](building_block::building_block_dpos& bb_dpos) { - pending_block_header_state_legacy& pbhs = bb_dpos.pending_block_header_state; - - if( gpo.proposed_schedule_block_num && // if there is a proposed schedule that was proposed in a block ... - ( hs_active || *gpo.proposed_schedule_block_num <= pbhs.dpos_irreversible_blocknum ) && // ... that has now become irreversible or hotstuff activated... - pbhs.prev_pending_schedule.schedule.producers.size() == 0 // ... and there was room for a new pending schedule prior to any possible promotion - ) - { - // Promote proposed schedule to pending schedule; happens in next block after hotstuff activated - EOS_ASSERT( gpo.proposed_schedule.version == pbhs.active_schedule_version + 1, - producer_schedule_exception, "wrong producer schedule version specified" ); - - bb_dpos.new_pending_producer_schedule = producer_authority_schedule::from_shared(gpo.proposed_schedule); - - if( !replaying ) { - ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ", - ("proposed_num", *gpo.proposed_schedule_block_num)("n", pbhs.block_num) - ("lib", hs_active ? hs_lib : pbhs.dpos_irreversible_blocknum) - ("schedule", bb_dpos.new_pending_producer_schedule ) ); - } - - db.modify( gpo, [&]( auto& gp ) { - gp.proposed_schedule_block_num = std::optional(); - gp.proposed_schedule.version=0; - gp.proposed_schedule.producers.clear(); - }); + // instant finality uses alternative method for chaning producer schedule + bb.apply_dpos([&](building_block::building_block_dpos& bb_dpos) { + pending_block_header_state_legacy& pbhs = bb_dpos.pending_block_header_state; + + if( gpo.proposed_schedule_block_num && // if there is a proposed schedule that was proposed in a block ... + ( *gpo.proposed_schedule_block_num <= pbhs.dpos_irreversible_blocknum ) && // ... that has now become irreversible ... + pbhs.prev_pending_schedule.schedule.producers.size() == 0 // ... and there was room for a new pending schedule prior to any possible promotion + ) + { + // Promote proposed schedule to pending schedule; happens in next block after hotstuff activated + EOS_ASSERT( gpo.proposed_schedule.version == pbhs.active_schedule_version + 1, + producer_schedule_exception, "wrong producer schedule version specified" ); + + bb_dpos.new_pending_producer_schedule = producer_authority_schedule::from_shared(gpo.proposed_schedule); + + if( !replaying ) { + ilog( "promoting proposed schedule (set in block ${proposed_num}) to pending; current block: ${n} lib: ${lib} schedule: ${schedule} ", + ("proposed_num", *gpo.proposed_schedule_block_num)("n", pbhs.block_num) + ("lib", pbhs.dpos_irreversible_blocknum) + ("schedule", bb_dpos.new_pending_producer_schedule ) ); } - }); - } + + db.modify( gpo, [&]( auto& gp ) { + gp.proposed_schedule_block_num = std::optional(); + gp.proposed_schedule.version=0; + gp.proposed_schedule.producers.clear(); + }); + } + }); try { transaction_metadata_ptr onbtrx = @@ -2616,10 +2650,10 @@ struct controller_impl { guard_pending.cancel(); } /// start_block - void finalize_block() + void finish_block() { EOS_ASSERT( pending, block_validate_exception, "it is not valid to finalize when there is no pending block"); - EOS_ASSERT( std::holds_alternative(pending->_block_stage), block_validate_exception, "already called finalize_block"); + EOS_ASSERT( std::holds_alternative(pending->_block_stage), block_validate_exception, "already called finish_block"); try { auto& bb = std::get(pending->_block_stage); @@ -2646,24 +2680,9 @@ struct controller_impl { create_block_summary( assembled_block.id() ); pending->_block_stage = std::move(assembled_block); - - /* - ilog( "finalized block ${n} (${id}) at ${t} by ${p} (${signing_key}); schedule_version: ${v} lib: ${lib} #dtrxs: ${ndtrxs} ${np}", - ("n",pbhs.block_num()) - ("id",id) - ("t",pbhs.timestamp) - ("p",pbhs.producer) - ("signing_key", pbhs.block_signing_key) - ("v",pbhs.active_schedule_version) - ("lib",pbhs.dpos_irreversible_blocknum) - ("ndtrxs",db.get_index().size()) - ("np",block_ptr->new_producers) - ); - */ - } FC_CAPTURE_AND_RETHROW() - } /// finalize_block + } /** * @post regardless of the success of commit block there is no active pending block @@ -2707,8 +2726,28 @@ struct controller_impl { if( s == controller::block_status::incomplete ) { log_irreversible(); - pacemaker->beat(); } + + // 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()); + 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(); + + log_irreversible(); + return true; + } + } + return false; + }; + if (block_data.apply_dpos(transition)) { + block_data.transition_fork_db_to_if(cb.bsp); + } + } catch (...) { // dont bother resetting pending, instead abort the block reset_pending_on_exit.cancel(); @@ -2820,7 +2859,7 @@ struct controller_impl { 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_state_future() + // 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(); @@ -2897,7 +2936,7 @@ struct controller_impl { ("lhs", r)("rhs", static_cast(receipt))); } - finalize_block(); + finish_block(); auto& ab = std::get(pending->_block_stage); @@ -2941,14 +2980,8 @@ struct controller_impl { // thread safe, expected to be called from thread other than the main thread - block_state_legacy_ptr create_block_state_i( const block_id_type& id, const signed_block_ptr& b, const block_header_state_legacy& prev ) { - // There is a small race condition at time of activation where create_block_state_i - // is called right before hs_irreversible_block_num is set. If that happens, - // the block is considered invalid, and the node will attempt to sync the block - // in the future and succeed - uint32_t instant_finality_lib = hs_irreversible_block_num.load(); - const bool instant_finality_active = instant_finality_lib > 0; - auto trx_mroot = calculate_trx_merkle( b->transactions, instant_finality_active ); + block_token create_block_state_i( const block_id_type& id, const signed_block_ptr& b, const block_header_state_legacy& prev ) { + auto trx_mroot = calculate_trx_merkle( b->transactions, false ); EOS_ASSERT( b->transaction_mroot == trx_mroot, block_validate_exception, "invalid block transaction merkle root ${b} != ${c}", ("b", b->transaction_mroot)("c", trx_mroot) ); @@ -2966,13 +2999,36 @@ struct controller_impl { EOS_ASSERT( id == bsp->id(), block_validate_exception, "provided id ${id} does not match block id ${bid}", ("id", id)("bid", bsp->id()) ); - return bsp; + return block_token{bsp}; } - std::future create_block_state_future( const block_id_type& id, const signed_block_ptr& b ) { + // thread safe, expected to be called from thread other than the main thread + block_token create_block_state_i( const block_id_type& id, const signed_block_ptr& b, const block_header_state& prev ) { + auto trx_mroot = calculate_trx_merkle( b->transactions, true ); + EOS_ASSERT( b->transaction_mroot == trx_mroot, block_validate_exception, + "invalid block transaction merkle root ${b} != ${c}", ("b", b->transaction_mroot)("c", trx_mroot) ); + + const bool skip_validate_signee = false; + auto bsp = std::make_shared( + prev, + b, + protocol_features.get_protocol_feature_set(), + [this]( block_timestamp_type timestamp, + const flat_set& cur_features, + const vector& new_features ) + { check_protocol_features( timestamp, cur_features, new_features ); }, + skip_validate_signee + ); + + EOS_ASSERT( id == bsp->id(), block_validate_exception, + "provided id ${id} does not match block id ${bid}", ("id", id)("bid", bsp->id()) ); + return block_token{bsp}; + } + + 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 { + auto f = [&](auto& fork_db, auto& head) -> std::future { return post_async_task( thread_pool.get_executor(), [b, id, &fork_db, control=this]() { // no reason for a block_state if fork_db already knows about block auto existing = fork_db.get_block( id ); @@ -2982,18 +3038,18 @@ struct controller_impl { EOS_ASSERT( prev, unlinkable_block_exception, "unlinkable block ${id}", ("id", id)("previous", b->previous) ); - return control->create_block_state_i( id, b, *prev ); // [greg todo] make it work with apply() (if `create_block_state_future` needed) + return control->create_block_state_i( id, b, *prev ); } ); }; - return block_data.apply_dpos>(f); // [greg todo] make it work with apply() + return block_data.apply>(f); } // thread safe, expected to be called from thread other than the main thread - block_state_legacy_ptr create_block_state( const block_id_type& id, const signed_block_ptr& b ) { + 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) -> block_state_legacy_ptr { + auto f = [&](auto& fork_db, auto& head) -> std::optional { // no reason for a block_state if fork_db already knows about block auto existing = fork_db.get_block( id ); EOS_ASSERT( !existing, fork_database_exception, "we already know about this block: ${id}", ("id", id) ); @@ -3002,16 +3058,16 @@ struct controller_impl { auto prev = fork_db.get_block_header( b->previous ); if( !prev ) return {}; - return create_block_state_i( id, b, *prev ); // [greg todo] make it work with apply() - if `create_block_state` needed + return create_block_state_i( id, b, *prev ); }; - return block_data.apply_dpos(f); + return block_data.apply>(f); } template void push_block( controller::block_report& br, const BSP& bsp, - const forked_branch_callback_t& forked_branch_cb, + const forked_callback_t& forked_branch_cb, const trx_meta_cache_lookup& trx_lookup ) { controller::block_status s = controller::block_status::complete; @@ -3113,7 +3169,7 @@ struct controller_impl { template void maybe_switch_forks( controller::block_report& br, const BSP& new_head, controller::block_status s, - const forked_branch_callback_t& forked_branch_cb, const trx_meta_cache_lookup& trx_lookup ) + const forked_callback_t& forked_cb, const trx_meta_cache_lookup& trx_lookup ) { auto do_maybe_switch_forks = [&](auto& fork_db, auto& head) { bool head_changed = true; @@ -3142,9 +3198,15 @@ struct controller_impl { EOS_ASSERT( self.head_block_id() == branches.second.back()->header.previous, fork_database_exception, "loss of sync between fork_db and chainbase during fork switch" ); // _should_ never fail - if( forked_branch_cb ) - if constexpr (std::is_same_v>) - forked_branch_cb(branches.second); + if( forked_cb ) { + // forked_branch is in reverse order, maintain execution order + for( auto ritr = branches.second.rbegin(), rend = branches.second.rend(); ritr != rend; ++ritr ) { + const auto& bsptr = *ritr; + for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) { + forked_cb(*itr); + } + } + } } for( auto ritr = branches.first.rbegin(); ritr != branches.first.rend(); ++ritr ) { @@ -3757,10 +3819,10 @@ void controller::start_block( block_timestamp_type when, bs, std::optional(), deadline ); } -void controller::finalize_block( block_report& br, const signer_callback_type& signer_callback ) { +void controller::finish_block( block_report& br, const signer_callback_type& signer_callback ) { validate_db_available_size(); - my->finalize_block(); + my->finish_block(); auto& ab = std::get(my->pending->_block_stage); my->pending->_block_stage = ab.make_completed_block( @@ -3784,33 +3846,23 @@ boost::asio::io_context& controller::get_thread_pool() { return my->thread_pool.get_executor(); } -std::future controller::create_block_state_future( const block_id_type& id, const signed_block_ptr& b ) { - return my->create_block_state_future( id, b ); +std::future controller::create_block_token_future( const block_id_type& id, const signed_block_ptr& b ) { + return my->create_block_token_future( id, b ); } -block_state_legacy_ptr controller::create_block_state( const block_id_type& id, const signed_block_ptr& b ) const { - return my->create_block_state( id, b ); +std::optional controller::create_block_token( const block_id_type& id, const signed_block_ptr& b ) const { + return my->create_block_token( id, b ); } -void controller::push_block( controller::block_report& br, - const block_state_legacy_ptr& bsp, - const forked_branch_callback_legacy& forked_branch_cb, +void controller::push_block( block_report& br, + const block_token& bt, + const forked_callback_t& forked_cb, const trx_meta_cache_lookup& trx_lookup ) { validate_db_available_size(); - my->push_block( br, bsp, forked_branch_cb, trx_lookup ); + std::visit([&](const auto& bsp) { my->push_block( br, bsp, forked_cb, trx_lookup); }, bt.bsp); } -void controller::push_block( controller::block_report& br, - const block_state_ptr& bsp, - const forked_branch_callback& forked_branch_cb, - const trx_meta_cache_lookup& trx_lookup ) -{ - validate_db_available_size(); - my->push_block( br, bsp, forked_branch_cb, trx_lookup ); -} - - transaction_trace_ptr controller::push_transaction( const transaction_metadata_ptr& trx, fc::time_point block_deadline, fc::microseconds max_transaction_time, uint32_t billed_cpu_time_us, bool explicit_billed_cpu_time, @@ -3945,10 +3997,10 @@ std::optional controller::pending_producer_block_id()const { return my->pending->_producer_block_id; } -void controller::set_hs_irreversible_block_num(uint32_t block_num) { +void controller::set_if_irreversible_block_num(uint32_t block_num) { // needs to be set by qc_chain at startup and as irreversible changes assert(block_num > 0); - my->hs_irreversible_block_num = block_num; + my->if_irreversible_block_num = block_num; } uint32_t controller::last_irreversible_block_num() const { @@ -3979,63 +4031,47 @@ signed_block_ptr controller::fetch_block_by_id( const block_id_type& id )const { return signed_block_ptr(); } +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); + 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; + return false; +} + std::optional controller::fetch_block_header_by_id( const block_id_type& id )const { -#if 0 - // [greg todo] is the below code equivalent?? - auto state = my->fork_db.get_block(id); - if( state && state->block ) return state->header; -#else auto sb_ptr = my->block_data.fork_db_fetch_block_by_id(id); if( sb_ptr ) return *static_cast(sb_ptr.get()); -#endif auto result = my->blog.read_block_header_by_num( block_header::num_from_id(id) ); if( result && result->calculate_id() == id ) return result; return {}; } signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { try { - auto blk_state = fetch_block_state_by_number( block_num ); - if( blk_state ) { - return blk_state->block; - } + auto b = my->block_data.fork_db_fetch_block_by_num( block_num ); + if (b) + return b; return my->blog.read_block_by_num(block_num); } FC_CAPTURE_AND_RETHROW( (block_num) ) } std::optional controller::fetch_block_header_by_number( uint32_t block_num )const { try { - auto blk_state = fetch_block_state_by_number( block_num ); - if( blk_state ) { - return blk_state->header; - } + auto b = my->block_data.fork_db_fetch_block_by_num(block_num); + if (b) + return *b; return my->blog.read_block_header_by_num(block_num); } FC_CAPTURE_AND_RETHROW( (block_num) ) } -block_state_legacy_ptr controller::fetch_block_state_by_id( block_id_type id )const { - // returns nullptr when in IF mode - auto get_block_state = [&](auto& fork_db, auto& head) -> block_state_legacy_ptr { return fork_db.get_block(id); }; - return my->block_data.apply_dpos(get_block_state); -} - -block_state_legacy_ptr controller::fetch_block_state_by_number( uint32_t block_num )const { - try { - // returns nullptr when in IF mode - auto fetch_block_state = [&](auto& fork_db, auto& head) -> block_state_legacy_ptr { - return fork_db.search_on_branch( fork_db.head()->id(), block_num); - }; - return my->block_data.apply_dpos(fetch_block_state); - } FC_CAPTURE_AND_RETHROW( (block_num) ) -} - block_id_type controller::get_block_id_for_num( uint32_t block_num )const { try { const auto& blog_head = my->blog.head(); bool find_in_blog = (blog_head && block_num <= blog_head->block_num()); if( !find_in_blog ) { - auto bsp = fetch_block_state_by_number( block_num ); - if( bsp ) return bsp->id(); + std::optional id = my->block_data.fork_db_fetch_block_id_by_num(block_num); + if (id) return *id; } auto id = my->blog.read_block_id_by_num(block_num); @@ -4158,7 +4194,6 @@ const producer_authority_schedule& controller::active_producers()const { if( !(my->pending) ) return my->block_data.head_active_schedule_auth(); -#warning todo: support active/pending_producers correctly when in IF mode (see assembled_block and completed_block stages) return my->pending->active_producers(); } @@ -4182,6 +4217,9 @@ 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->pending->next_producers(); } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 6431c7d5f1..c98a48c3d1 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -69,7 +69,7 @@ namespace eosio::chain { fork_multi_index_type index; bsp root; // Only uses the block_header_state_legacy portion bsp head; - std::filesystem::path datadir; + const std::filesystem::path datadir; explicit fork_database_impl( const std::filesystem::path& data_dir ) : datadir(data_dir) {} @@ -102,6 +102,11 @@ namespace eosio::chain { my->open_impl( 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)) diff --git a/libraries/chain/hotstuff/chain_pacemaker.cpp b/libraries/chain/hotstuff/chain_pacemaker.cpp index 2d64185a53..ebe34117be 100644 --- a/libraries/chain/hotstuff/chain_pacemaker.cpp +++ b/libraries/chain/hotstuff/chain_pacemaker.cpp @@ -175,8 +175,8 @@ namespace eosio::chain { if (_active_finalizer_policy.generation == 0) { ilog("Switching to instant finality at block ${b}", ("b", block->block_num())); // switching from dpos to hotstuff, all nodes will switch at same block height - // block header extension is set in finalize_block to value set by host function set_finalizers - _chain->set_hs_irreversible_block_num(block->block_num()); // can be any value <= dpos lib + // block header extension is set in finish_block to value set by host function set_finalizers + _chain->set_if_irreversible_block_num(block->block_num()); // can be any value <= dpos lib } auto if_extension = std::get(*ext); #warning Revisit after finalizer policy change design is complete as this is not necessarily when we will change active finalizer policy. diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 2780d1b709..f18e3137a7 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -98,6 +98,8 @@ using block_header_state_ptr = std::shared_ptr; } -// [greg todo] which members need to be serialized to disk when saving fork_db -// obviously many are missing below. -FC_REFLECT( eosio::chain::block_header_state, (id)) +FC_REFLECT( eosio::chain::block_header_state_core, + (last_final_block_num)(final_on_strong_qc_block_num)(last_qc_block_num)(finalizer_policy_generation)) +FC_REFLECT( eosio::chain::block_header_state, + (id)(header)(activated_protocol_features)(core)(proposal_mtree)(finality_mtree) + (active_finalizer_policy)(active_proposer_policy)(proposer_policies)(finalizer_policies)(header_exts)) diff --git a/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp index 6f7a3153d9..95a3e71f69 100644 --- a/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp @@ -120,7 +120,7 @@ struct pending_block_header_state_legacy : public detail::block_header_state_leg * start_block -> (global_property_object.proposed_schedule_block_num == dpos_lib) * building_block._new_pending_producer_schedule = producers * - * finalize_block -> + * finish_block -> * block_header.extensions.wtmsig_block_signatures = producers * block_header.new_producers = producers * diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 3e853db596..ff48e4fb5e 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -7,10 +7,12 @@ namespace eosio::chain { +struct block_state_legacy; + struct block_state : public block_header_state { // block_header_state provides parent link // ------ data members ------------------------------------------------------------- signed_block_ptr block; - bool validated; // We have executed the block's trxs and verified that action merkle root (block id) matches. + bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches. digest_type strong_digest; // finalizer_digest (strong, cached so we can quickly validate votes) digest_type weak_digest; // finalizer_digest (weak, cached so we can quickly validate votes) pending_quorum_certificate pending_qc; // where we accumulate votes we receive @@ -47,11 +49,13 @@ struct block_state : public block_header_state { // block_header_state provi block_state(const block_header_state& bhs, deque&& trx_metas, deque&& trx_receipts); + + explicit block_state(const block_state_legacy& bsp); }; using block_state_ptr = std::shared_ptr; } // namespace eosio::chain -// [greg todo] which members need to be serialized to disk when saving fork_db -FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(validated) ) +// not exporting pending_qc or valid_qc +FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(validated)(strong_digest)(weak_digest) ) diff --git a/libraries/chain/include/eosio/chain/block_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_state_legacy.hpp index fc267d813c..72de14c279 100644 --- a/libraries/chain/include/eosio/chain/block_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_state_legacy.hpp @@ -52,6 +52,7 @@ namespace eosio::chain { friend struct fc::reflector; friend struct controller_impl; friend struct completed_block; + friend struct block_state; bool is_pub_keys_recovered()const { return _pub_keys_recovered; } diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 2941b55280..4b726a3a2d 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -51,23 +51,24 @@ namespace eosio::chain { using resource_limits::resource_limits_manager; using apply_handler = std::function; - template - using branch_type_t = fork_database::branch_type; - - using branch_type_legacy = branch_type_t; - using branch_type = branch_type_t; - - template - using forked_branch_callback_t = std::function&)>; - - using forked_branch_callback_legacy = forked_branch_callback_t; - using forked_branch_callback = forked_branch_callback_t; + using forked_callback_t = std::function; // lookup transaction_metadata via supplied function to avoid re-creation using trx_meta_cache_lookup = std::function; using block_signal_params = std::tuple; + // Created via create_block_token(const block_id_type& id, const signed_block_ptr& b) + // Valid to request id and signed_block_ptr it was created from. + // Avoid using internal block_state/block_state_legacy as those types are internal to controller. + struct block_token { + std::variant bsp; + + uint32_t block_num() const { return std::visit([](const auto& bsp) { return bsp->block_num(); }, bsp); } + block_id_type id() const { return std::visit([](const auto& bsp) { return bsp->id(); }, bsp); } + signed_block_ptr block() const { return std::visit([](const auto& bsp) { return bsp->block; }, bsp); } + }; + enum class db_read_mode { HEAD, IRREVERSIBLE, @@ -183,29 +184,25 @@ namespace eosio::chain { fc::microseconds total_time{}; }; - void finalize_block( block_report& br, const signer_callback_type& signer_callback ); + void finish_block( block_report& br, const signer_callback_type& signer_callback ); void sign_block( const signer_callback_type& signer_callback ); void commit_block(); - + // thread-safe - std::future create_block_state_future( const block_id_type& id, const signed_block_ptr& b ); + std::future create_block_token_future( const block_id_type& id, const signed_block_ptr& b ); // thread-safe - block_state_legacy_ptr create_block_state( const block_id_type& id, const signed_block_ptr& b ) const; + // returns empty optional if block b is not immediately ready to be processed + std::optional create_block_token( const block_id_type& id, const signed_block_ptr& b ) const; /** * @param br returns statistics for block - * @param bsp block to push + * @param bt block to push, created by create_block_token * @param cb calls cb with forked applied transactions for each forked block * @param trx_lookup user provided lookup function for externally cached transaction_metadata */ void push_block( block_report& br, - const block_state_legacy_ptr& bsp, - const forked_branch_callback_legacy& cb, - const trx_meta_cache_lookup& trx_lookup ); - - void push_block( block_report& br, - const block_state_ptr& bsp, - const forked_branch_callback& cb, + const block_token& bt, + const forked_callback_t& cb, const trx_meta_cache_lookup& trx_lookup ); boost::asio::io_context& get_thread_pool(); @@ -273,7 +270,7 @@ namespace eosio::chain { // Called by qc_chain to indicate the current irreversible block num // After hotstuff is activated, this should be called on startup by qc_chain - void set_hs_irreversible_block_num(uint32_t block_num); + void set_if_irreversible_block_num(uint32_t block_num); uint32_t last_irreversible_block_num() const; block_id_type last_irreversible_block_id() const; @@ -284,13 +281,11 @@ namespace eosio::chain { // thread-safe signed_block_ptr fetch_block_by_id( const block_id_type& id )const; // thread-safe + bool block_exists( const block_id_type& id)const; + // thread-safe std::optional fetch_block_header_by_number( uint32_t block_num )const; // thread-safe std::optional fetch_block_header_by_id( const block_id_type& id )const; - // return block_state_legacy from forkdb, thread-safe - block_state_legacy_ptr fetch_block_state_by_number( uint32_t block_num )const; - // return block_state_legacy from forkdb, thread-safe - block_state_legacy_ptr fetch_block_state_by_id( block_id_type id )const; // thread-safe block_id_type get_block_id_for_num( uint32_t block_num )const; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index cdd1d13fab..d43031cf98 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -34,6 +34,8 @@ namespace eosio::chain { explicit fork_database( const std::filesystem::path& data_dir ); ~fork_database(); + std::filesystem::path get_data_dir() const; + void open( validator_t& validator ); void close(); diff --git a/libraries/chain/include/eosio/chain/unapplied_transaction_queue.hpp b/libraries/chain/include/eosio/chain/unapplied_transaction_queue.hpp index 4a28f9eb51..e1231bedcb 100644 --- a/libraries/chain/include/eosio/chain/unapplied_transaction_queue.hpp +++ b/libraries/chain/include/eosio/chain/unapplied_transaction_queue.hpp @@ -133,17 +133,9 @@ class unapplied_transaction_queue { } } - template - void add_forked( const BRANCH_TYPE& forked_branch ) { - // forked_branch is in reverse order - for( auto ritr = forked_branch.rbegin(), rend = forked_branch.rend(); ritr != rend; ++ritr ) { - const auto& bsptr = *ritr; - for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) { - const auto& trx = *itr; - auto insert_itr = queue.insert( { trx, trx_enum_type::forked } ); - if( insert_itr.second ) added( insert_itr.first ); - } - } + void add_forked( const transaction_metadata_ptr& trx ) { + auto insert_itr = queue.insert( { trx, trx_enum_type::forked } ); + if( insert_itr.second ) added( insert_itr.first ); } void add_aborted( deque aborted_trxs ) { diff --git a/libraries/chain/resource_limits.cpp b/libraries/chain/resource_limits.cpp index b11eea1dba..4c5e4b6cb3 100644 --- a/libraries/chain/resource_limits.cpp +++ b/libraries/chain/resource_limits.cpp @@ -117,7 +117,7 @@ void resource_limits_manager::set_block_parameters(const elastic_limit_parameter c.cpu_limit_parameters = cpu_limit_parameters; c.net_limit_parameters = net_limit_parameters; - // set_block_parameters is called by controller::finalize_block, + // set_block_parameters is called by controller::finish_block, // where transaction specific logging is not possible if (auto dm_logger = _get_deep_mind_logger(false)) { dm_logger->on_update_resource_limits_config(c); @@ -359,7 +359,7 @@ void resource_limits_manager::process_account_limit_updates() { multi_index.remove(*itr); } - // process_account_limit_updates is called by controller::finalize_block, + // process_account_limit_updates is called by controller::finish_block, // where transaction specific logging is not possible if (auto dm_logger = _get_deep_mind_logger(false)) { dm_logger->on_update_resource_limits_state(state); @@ -381,7 +381,7 @@ void resource_limits_manager::process_block_usage(uint32_t block_num) { state.update_virtual_net_limit(config); state.pending_net_usage = 0; - // process_block_usage is called by controller::finalize_block, + // process_block_usage is called by controller::finish, // where transaction specific logging is not possible if (auto dm_logger = _get_deep_mind_logger(false)) { dm_logger->on_update_resource_limits_state(state); diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index eeabf71061..c359e761db 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -606,9 +606,9 @@ namespace eosio { namespace testing { signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override { auto sb = _produce_block(skip_time, false); - auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb ); + auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb ); controller::block_report br; - validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} ); + validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} ); return sb; } @@ -618,17 +618,17 @@ namespace eosio { namespace testing { } void validate_push_block(const signed_block_ptr& sb) { - auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb ); + auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb ); controller::block_report br; - validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} ); + validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} ); } signed_block_ptr produce_empty_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override { unapplied_transactions.add_aborted( control->abort_block() ); auto sb = _produce_block(skip_time, true); - auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb ); + auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb ); controller::block_report br; - validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} ); + validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} ); return sb; } diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index b5e35533cb..d4b97fc002 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -376,11 +376,11 @@ namespace eosio { namespace testing { } void base_tester::push_block(signed_block_ptr b) { - auto bsf = control->create_block_state_future(b->calculate_id(), b); + auto btf = control->create_block_token_future(b->calculate_id(), b); unapplied_transactions.add_aborted( control->abort_block() ); controller::block_report br; - control->push_block( br, bsf.get(), [this]( const branch_type_legacy& forked_branch ) { - unapplied_transactions.add_forked( forked_branch ); + control->push_block( br, btf.get(), [this]( const transaction_metadata_ptr& trx ) { + unapplied_transactions.add_forked( trx ); }, [this]( const transaction_id_type& id ) { return unapplied_transactions.get_trx( id ); } ); @@ -487,7 +487,7 @@ namespace eosio { namespace testing { }); controller::block_report br; - control->finalize_block( br, [&]( digest_type d ) { + control->finish_block( br, [&]( digest_type d ) { std::vector result; result.reserve(signing_keys.size()); for (const auto& k: signing_keys) @@ -1115,10 +1115,10 @@ namespace eosio { namespace testing { auto block = a.control->fetch_block_by_number(i); if( block ) { //&& !b.control->is_known_block(block->id()) ) { - auto bsf = b.control->create_block_state_future( block->calculate_id(), block ); + auto btf = b.control->create_block_token_future( block->calculate_id(), block ); b.control->abort_block(); controller::block_report br; - b.control->push_block(br, bsf.get(), {}, trx_meta_cache_lookup{}); //, eosio::chain::validation_steps::created_block); + b.control->push_block(br, btf.get(), {}, trx_meta_cache_lookup{}); //, eosio::chain::validation_steps::created_block); } } }; diff --git a/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp b/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp index 4fe1c247d4..2bd78ac800 100644 --- a/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp +++ b/plugins/chain_interface/include/eosio/chain/plugin_interface.hpp @@ -33,7 +33,7 @@ namespace eosio::chain::plugin_interface { namespace incoming { namespace methods { // synchronously push a block/trx to a single provider, block_state_legacy_ptr may be null - using block_sync = method_decl&, const block_state_legacy_ptr&), first_provider_policy>; + using block_sync = method_decl&, const std::optional&), first_provider_policy>; using transaction_async = method_decl), first_provider_policy>; } } diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 56761924a6..41f897b224 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1183,8 +1183,8 @@ chain_apis::read_only chain_plugin::get_read_only_api(const fc::microseconds& ht } -bool chain_plugin::accept_block(const signed_block_ptr& block, const block_id_type& id, const block_state_legacy_ptr& bsp ) { - return my->incoming_block_sync_method(block, id, bsp); +bool chain_plugin::accept_block(const signed_block_ptr& block, const block_id_type& id, const std::optional& obt ) { + return my->incoming_block_sync_method(block, id, obt); } void chain_plugin::accept_transaction(const chain::packed_transaction_ptr& trx, next_function next) { @@ -2013,7 +2013,7 @@ fc::variant read_only::get_block_info(const read_only::get_block_info_params& pa void read_write::push_block(read_write::push_block_params&& params, next_function next) { try { - app().get_method()(std::make_shared( std::move(params) ), std::optional{}, block_state_legacy_ptr{}); + app().get_method()(std::make_shared( std::move(params) ), std::optional{}, std::optional{}); } catch ( boost::interprocess::bad_alloc& ) { handle_db_exhaustion(); } catch ( const std::bad_alloc& ) { diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index cd1fd5b0aa..5e5d5b3701 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -1012,7 +1012,7 @@ class chain_plugin : public plugin { chain_apis::read_write get_read_write_api(const fc::microseconds& http_max_response_time); chain_apis::read_only get_read_only_api(const fc::microseconds& http_max_response_time) const; - bool accept_block( const chain::signed_block_ptr& block, const chain::block_id_type& id, const chain::block_state_legacy_ptr& bsp ); + bool accept_block( const chain::signed_block_ptr& block, const chain::block_id_type& id, const std::optional& obt ); void accept_transaction(const chain::packed_transaction_ptr& trx, chain::plugin_interface::next_function next); // Only call this after plugin_initialize()! diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index 967686ef4f..7452479497 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -1100,7 +1100,7 @@ namespace eosio { // returns calculated number of blocks combined latency uint32_t calc_block_latency(); - void process_signed_block( const block_id_type& id, signed_block_ptr block, block_state_legacy_ptr bsp ); + void process_signed_block( const block_id_type& id, signed_block_ptr block, const std::optional& obt ); fc::variant_object get_logger_variant() const { fc::mutable_variant_object mvo; @@ -3718,7 +3718,7 @@ namespace eosio { controller& cc = my_impl->chain_plug->chain(); // may have come in on a different connection and posted into dispatcher strand before this one - if( my_impl->dispatcher->have_block( id ) || cc.fetch_block_by_id( id ) ) { // thread-safe + if( my_impl->dispatcher->have_block( id ) || cc.block_exists( id ) ) { // thread-safe my_impl->dispatcher->add_peer_block( id, c->connection_id ); c->strand.post( [c, id]() { my_impl->sync_master->sync_recv_block( c, id, block_header::num_from_id(id), false ); @@ -3726,11 +3726,11 @@ namespace eosio { return; } - block_state_legacy_ptr bsp; + std::optional obt; bool exception = false; try { // this may return null if block is not immediately ready to be processed - bsp = cc.create_block_state( id, ptr ); + obt = cc.create_block_token( id, ptr ); } catch( const fc::exception& ex ) { exception = true; fc_ilog( logger, "bad block exception connection ${cid}: #${n} ${id}...: ${m}", @@ -3749,17 +3749,18 @@ namespace eosio { } - uint32_t block_num = bsp ? bsp->block_num() : 0; + uint32_t block_num = obt ? obt->block_num() : 0; if( block_num != 0 ) { + assert(obt); fc_dlog( logger, "validated block header, broadcasting immediately, connection ${cid}, blk num = ${num}, id = ${id}", - ("cid", cid)("num", block_num)("id", bsp->id()) ); - my_impl->dispatcher->add_peer_block( bsp->id(), cid ); // no need to send back to sender - my_impl->dispatcher->bcast_block( bsp->block, bsp->id() ); + ("cid", cid)("num", block_num)("id", obt->id()) ); + my_impl->dispatcher->add_peer_block( obt->id(), cid ); // no need to send back to sender + my_impl->dispatcher->bcast_block( obt->block(), obt->id() ); } - app().executor().post(priority::medium, exec_queue::read_write, [ptr{std::move(ptr)}, bsp{std::move(bsp)}, id, c{std::move(c)}]() mutable { - c->process_signed_block( id, std::move(ptr), std::move(bsp) ); + app().executor().post(priority::medium, exec_queue::read_write, [ptr{std::move(ptr)}, obt{std::move(obt)}, id, c{std::move(c)}]() mutable { + c->process_signed_block( id, std::move(ptr), obt ); }); if( block_num != 0 ) { @@ -3770,14 +3771,14 @@ namespace eosio { } // called from application thread - void connection::process_signed_block( const block_id_type& blk_id, signed_block_ptr block, block_state_legacy_ptr bsp ) { + void connection::process_signed_block( const block_id_type& blk_id, signed_block_ptr block, const std::optional& obt ) { controller& cc = my_impl->chain_plug->chain(); uint32_t blk_num = block_header::num_from_id(blk_id); // use c in this method instead of this to highlight that all methods called on c-> must be thread safe connection_ptr c = shared_from_this(); try { - if( blk_num <= cc.last_irreversible_block_num() || cc.fetch_block_by_id(blk_id) ) { + if( blk_num <= cc.last_irreversible_block_num() || cc.block_exists(blk_id) ) { c->strand.post( [sync_master = my_impl->sync_master.get(), dispatcher = my_impl->dispatcher.get(), c, blk_id, blk_num]() { dispatcher->add_peer_block( blk_id, c->connection_id ); @@ -3794,12 +3795,12 @@ namespace eosio { fc::microseconds age( fc::time_point::now() - block->timestamp); fc_dlog( logger, "received signed_block: #${n} block age in secs = ${age}, connection ${cid}, ${v}", - ("n", blk_num)("age", age.to_seconds())("cid", c->connection_id)("v", bsp ? "pre-validated" : "validation pending") ); + ("n", blk_num)("age", age.to_seconds())("cid", c->connection_id)("v", obt ? "pre-validated" : "validation pending") ); go_away_reason reason = no_reason; bool accepted = false; try { - accepted = my_impl->chain_plug->accept_block(block, blk_id, bsp); + accepted = my_impl->chain_plug->accept_block(block, blk_id, obt); my_impl->update_chain_info(); } catch( const unlinkable_block_exception &ex) { fc_ilog(logger, "unlinkable_block_exception connection ${cid}: #${n} ${id}...: ${m}", diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 182534b0f1..a65bafa471 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -666,7 +666,7 @@ class producer_plugin_impl : public std::enable_shared_from_this& block_id, const block_state_legacy_ptr& bsp) { + bool on_incoming_block(const signed_block_ptr& block, const std::optional& block_id, const std::optional& obt) { auto& chain = chain_plug->chain(); if (in_producing_mode()) { fc_wlog(_log, "dropped incoming block #${num} id: ${id}", ("num", block->block_num())("id", block_id ? (*block_id).str() : "UNKNOWN")); @@ -687,16 +687,15 @@ class producer_plugin_impl : public std::enable_shared_from_thistimestamp < (now + fc::seconds(7)), block_from_the_future, "received a block from the future, ignoring it: ${id}", ("id", id)); - /* de-dupe here... no point in aborting block if we already know the block */ - auto existing = chain.fetch_block_by_id(id); - if (existing) { + // de-dupe here... no point in aborting block if we already know the block + if (chain.block_exists(id)) { return true; // return true because the block is valid } // start processing of block - std::future bsf; - if (!bsp) { - bsf = chain.create_block_state_future(id, block); + std::future btf; + if (!obt) { + btf = chain.create_block_token_future(id, block); } // abort the pending block @@ -711,11 +710,11 @@ class producer_plugin_impl : public std::enable_shared_from_this().register_provider( - [this](const signed_block_ptr& block, const std::optional& block_id, const block_state_legacy_ptr& bsp) { - return on_incoming_block(block, block_id, bsp); + [this](const signed_block_ptr& block, const std::optional& block_id, const std::optional& obt) { + return on_incoming_block(block, block_id, obt); }); _incoming_transaction_async_provider = @@ -2646,7 +2645,7 @@ void producer_plugin_impl::produce_block() { // idump( (fc::time_point::now() - chain.pending_block_time()) ); controller::block_report br; - chain.finalize_block(br, [&](const digest_type& d) { + chain.finish_block(br, [&](const digest_type& d) { auto debug_logger = maybe_make_debug_time_logger(); vector sigs; sigs.reserve(relevant_providers.size()); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b288bc9a2d..2ccb07831b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -88,6 +88,8 @@ add_test(NAME nodeos_sanity_test COMMAND tests/nodeos_run_test.py -v --sanity-te set_property(TEST nodeos_sanity_test PROPERTY LABELS nonparallelizable_tests) add_test(NAME nodeos_run_test COMMAND tests/nodeos_run_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST nodeos_run_test PROPERTY LABELS nonparallelizable_tests) +add_test(NAME nodeos_if_test COMMAND tests/nodeos_run_test.py -v --activate-if ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +set_property(TEST nodeos_if_test PROPERTY LABELS nonparallelizable_tests) add_test(NAME block_log_util_test COMMAND tests/block_log_util_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST block_log_util_test PROPERTY LABELS nonparallelizable_tests) add_test(NAME block_log_retain_blocks_test COMMAND tests/block_log_retain_blocks_test.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/tests/TestHarness/Cluster.py b/tests/TestHarness/Cluster.py index ef846ff27b..8988f7f133 100644 --- a/tests/TestHarness/Cluster.py +++ b/tests/TestHarness/Cluster.py @@ -1187,6 +1187,10 @@ def createSystemAccount(accountName): if not biosNode.waitForTransactionsInBlock(transIds): Utils.Print('ERROR: Failed to validate creation of system accounts') return None + # + # Could activate instant finality here, but have to wait for finality which with all the producers takes a long time + # if activateIF: + # self.activateInstantFinality(launcher) eosioTokenAccount = copy.deepcopy(eosioAccount) eosioTokenAccount.name = 'eosio.token' diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index fd36f47c40..b11200c772 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3885,25 +3885,39 @@ BOOST_AUTO_TEST_CASE(set_finalizer_test) { try { BOOST_TEST(fin_policy->finalizers.size() == finalizers.size()); BOOST_TEST(fin_policy->generation == 1); BOOST_TEST(fin_policy->threshold == finalizers.size() / 3 * 2 + 1); -#if 0 // update after transition is complete: https://github.com/AntelopeIO/leap/issues/1911 - // old dpos still in affect until block is irreversible - BOOST_TEST(block->confirmed == 0); - block_state_legacy_ptr block_state = t.control->fetch_block_state_by_id(block->calculate_id()); - BOOST_REQUIRE(!!block_state); - BOOST_TEST(block_state->dpos_irreversible_blocknum != hs_dpos_irreversible_blocknum); - - block = t.produce_block(); // only one producer so now this block is irreversible, next block will be hotstuff - BOOST_TEST(block->confirmed == 0); - block_state = t.control->fetch_block_state_by_id(block->calculate_id()); - BOOST_REQUIRE(!!block_state); - BOOST_TEST(block_state->dpos_irreversible_blocknum != hs_dpos_irreversible_blocknum); + // currently transition happens immediately after set_finalizer block + + // TODO: update after transition is complete: https://github.com/AntelopeIO/leap/issues/1911 + + // // old dpos still in affect until block is irreversible + // BOOST_TEST(block->confirmed == 0); + // block_state_legacy_ptr block_state = t.control->fetch_block_state_by_id(block->calculate_id()); + // BOOST_REQUIRE(!!block_state); + // BOOST_TEST(block_state->dpos_irreversible_blocknum != hs_dpos_irreversible_blocknum); + + // block = t.produce_block(); // only one producer so now this block is irreversible, next block will be hotstuff + // BOOST_TEST(block->confirmed == 0); + // block_state = t.control->fetch_block_state_by_id(block->calculate_id()); + // BOOST_REQUIRE(!!block_state); + // BOOST_TEST(block_state->dpos_irreversible_blocknum != hs_dpos_irreversible_blocknum); block = t.produce_block(); // hotstuff now active BOOST_TEST(block->confirmed == std::numeric_limits::max()); - block_state = t.control->fetch_block_state_by_id(block->calculate_id()); - BOOST_REQUIRE(!!block_state); - BOOST_TEST(block_state->dpos_irreversible_blocknum == hs_dpos_irreversible_blocknum); -#endif + auto fb = t.control->fetch_block_by_id(block->calculate_id()); + BOOST_REQUIRE(!!fb); + BOOST_TEST(fb == block); + ext = fb->extract_header_extension(instant_finality_extension::extension_id()); + BOOST_REQUIRE(ext); + + // and another on top of a instant-finality block + block = t.produce_block(); + BOOST_TEST(block->confirmed == std::numeric_limits::max()); + fb = t.control->fetch_block_by_id(block->calculate_id()); + BOOST_REQUIRE(!!fb); + BOOST_TEST(fb == block); + ext = fb->extract_header_extension(instant_finality_extension::extension_id()); + BOOST_REQUIRE(ext); + } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/block_tests.cpp b/unittests/block_tests.cpp index 8e11f270e2..5ffb9d8408 100644 --- a/unittests/block_tests.cpp +++ b/unittests/block_tests.cpp @@ -44,10 +44,10 @@ BOOST_AUTO_TEST_CASE(block_with_invalid_tx_test) // Push block with invalid transaction to other chain tester validator; - auto bsf = validator.control->create_block_state_future( copy_b->calculate_id(), copy_b ); + auto btf = validator.control->create_block_token_future( copy_b->calculate_id(), copy_b ); validator.control->abort_block(); controller::block_report br; - BOOST_REQUIRE_EXCEPTION(validator.control->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} ), fc::exception , + BOOST_REQUIRE_EXCEPTION(validator.control->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} ), fc::exception , [] (const fc::exception &e)->bool { return e.code() == account_name_exists_exception::code_value ; }) ; @@ -83,10 +83,10 @@ BOOST_AUTO_TEST_CASE(block_with_invalid_tx_mroot_test) // Push block with invalid transaction to other chain tester validator; - auto bsf = validator.control->create_block_state_future( copy_b->calculate_id(), copy_b ); + auto btf = validator.control->create_block_token_future( copy_b->calculate_id(), copy_b ); validator.control->abort_block(); controller::block_report br; - BOOST_REQUIRE_EXCEPTION(validator.control->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} ), fc::exception, + BOOST_REQUIRE_EXCEPTION(validator.control->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} ), fc::exception, [] (const fc::exception &e)->bool { return e.code() == block_validate_exception::code_value && e.to_detail_string().find("invalid block transaction merkle root") != std::string::npos; diff --git a/unittests/chain_tests.cpp b/unittests/chain_tests.cpp index 45ad505115..bc2a973e12 100644 --- a/unittests/chain_tests.cpp +++ b/unittests/chain_tests.cpp @@ -161,6 +161,7 @@ BOOST_AUTO_TEST_CASE( signal_validated_blocks ) try { auto block_num = block->block_num(); BOOST_CHECK(block); BOOST_CHECK(chain.control->fetch_block_by_id(id) == block); + BOOST_CHECK(chain.control->block_exists(id)); BOOST_CHECK(chain.control->fetch_block_by_number(block_num) == block); BOOST_REQUIRE(chain.control->fetch_block_header_by_number(block_num)); BOOST_CHECK(chain.control->fetch_block_header_by_number(block_num)->calculate_id() == id); @@ -176,6 +177,7 @@ BOOST_AUTO_TEST_CASE( signal_validated_blocks ) try { auto block_num = block->block_num(); BOOST_CHECK(block); BOOST_CHECK(validator.control->fetch_block_by_id(id) == block); + BOOST_CHECK(validator.control->block_exists(id)); BOOST_CHECK(validator.control->fetch_block_by_number(block_num) == block); BOOST_REQUIRE(validator.control->fetch_block_header_by_number(block_num)); BOOST_CHECK(validator.control->fetch_block_header_by_number(block_num)->calculate_id() == id); diff --git a/unittests/forked_tests.cpp b/unittests/forked_tests.cpp index 2392663921..3800b090c6 100644 --- a/unittests/forked_tests.cpp +++ b/unittests/forked_tests.cpp @@ -266,10 +266,10 @@ BOOST_AUTO_TEST_CASE( forking ) try { signed_block bad_block = std::move(*b); bad_block.action_mroot = bad_block.previous; auto bad_id = bad_block.calculate_id(); - auto bad_block_bsf = c.control->create_block_state_future( bad_id, std::make_shared(std::move(bad_block)) ); + auto bad_block_btf = c.control->create_block_token_future( bad_id, std::make_shared(std::move(bad_block)) ); c.control->abort_block(); controller::block_report br; - BOOST_REQUIRE_EXCEPTION(c.control->push_block( br, bad_block_bsf.get(), {}, trx_meta_cache_lookup{} ), fc::exception, + BOOST_REQUIRE_EXCEPTION(c.control->push_block( br, bad_block_btf.get(), {}, trx_meta_cache_lookup{} ), fc::exception, [] (const fc::exception &ex)->bool { return ex.to_detail_string().find("block signed by unexpected key") != std::string::npos; }); @@ -496,6 +496,7 @@ BOOST_AUTO_TEST_CASE( irreversible_mode ) try { { auto b = irreversible.control->fetch_block_by_id( fork_first_block_id ); BOOST_REQUIRE( b && b->calculate_id() == fork_first_block_id ); + BOOST_TEST( irreversible.control->block_exists(fork_first_block_id) ); } main.produce_block(); @@ -509,6 +510,7 @@ BOOST_AUTO_TEST_CASE( irreversible_mode ) try { { auto b = irreversible.control->fetch_block_by_id( fork_first_block_id ); BOOST_REQUIRE( !b ); + BOOST_TEST( !irreversible.control->block_exists(fork_first_block_id) ); } } FC_LOG_AND_RETHROW() diff --git a/unittests/producer_schedule_hs_tests.cpp b/unittests/producer_schedule_hs_tests.cpp index b1188e9084..681627a42b 100644 --- a/unittests/producer_schedule_hs_tests.cpp +++ b/unittests/producer_schedule_hs_tests.cpp @@ -3,13 +3,11 @@ #include -#include "fork_test_utilities.hpp" - using namespace eosio::testing; using namespace eosio::chain; using mvo = fc::mutable_variant_object; -BOOST_AUTO_TEST_SUITE(producer_schedule_hs_tests) +BOOST_AUTO_TEST_SUITE(producer_schedule_if_tests) namespace { @@ -20,19 +18,16 @@ inline account_name get_expected_producer(const vector& sche }; } // anonymous namespace -#if 0 - -// [greg todo] Enable test when https://github.com/AntelopeIO/leap/issues/1980 is completed -BOOST_FIXTURE_TEST_CASE( verify_producer_schedule_after_hotstuff_activation, validating_tester ) try { +BOOST_FIXTURE_TEST_CASE( verify_producer_schedule_after_instant_finality_activation, validating_tester ) try { // Utility function to ensure that producer schedule work as expected const auto& confirm_schedule_correctness = [&](const vector& new_prod_schd, uint32_t expected_schd_ver, uint32_t expected_block_num = 0) { const uint32_t check_duration = 100; // number of blocks bool scheduled_changed_to_new = false; for (uint32_t i = 0; i < check_duration; ++i) { - const auto current_schedule = control->head_block_state()->active_schedule.producers; - if (new_prod_schd == current_schedule) { + const auto current_schedule = control->active_producers(); + if (new_prod_schd == current_schedule.producers) { scheduled_changed_to_new = true; if (expected_block_num != 0) BOOST_TEST(control->head_block_num() == expected_block_num); @@ -42,7 +37,7 @@ BOOST_FIXTURE_TEST_CASE( verify_producer_schedule_after_hotstuff_activation, val // Check if the producer is the same as what we expect const auto block_time = control->head_block_time(); - const auto& expected_producer = get_expected_producer(current_schedule, block_time); + const auto& expected_producer = get_expected_producer(current_schedule.producers, block_time); BOOST_TEST(control->head_block_producer() == expected_producer); if (scheduled_changed_to_new) @@ -69,17 +64,17 @@ BOOST_FIXTURE_TEST_CASE( verify_producer_schedule_after_hotstuff_activation, val }; create_accounts(producers); - // activate hotstuff + // activate instant_finality set_finalizers(producers); auto block = produce_block(); // this block contains the header extension of the finalizer set - BOOST_TEST(lib == 3); + BOOST_TEST(lib == 4); // TODO: currently lib advances immediately on set_finalizers // ---- Test first set of producers ---- // Send set prods action and confirm schedule correctness set_producers(producers); const auto first_prod_schd = get_producer_authorities(producers); - // TODO: update expected when lib for hotstuff is working, will change from 22 at that time - confirm_schedule_correctness(first_prod_schd, 1, 22); + // TODO: update expected when lib for instant_finality is working, will change from 26 at that time, 4+12+12 + confirm_schedule_correctness(first_prod_schd, 1, 26); // ---- Test second set of producers ---- vector second_set_of_producer = { @@ -88,8 +83,8 @@ BOOST_FIXTURE_TEST_CASE( verify_producer_schedule_after_hotstuff_activation, val // Send set prods action and confirm schedule correctness set_producers(second_set_of_producer); const auto second_prod_schd = get_producer_authorities(second_set_of_producer); - // TODO: update expected when lib for hotstuff is working, will change from 44 at that time - confirm_schedule_correctness(second_prod_schd, 2, 44); + // TODO: update expected when lib for instant_finality is working, will change from 50 at that time, 26+12+12 + confirm_schedule_correctness(second_prod_schd, 2, 50); // ---- Test deliberately miss some blocks ---- const int64_t num_of_missed_blocks = 5000; @@ -111,9 +106,7 @@ BOOST_FIXTURE_TEST_CASE( verify_producer_schedule_after_hotstuff_activation, val } FC_LOG_AND_RETHROW() -#endif - -/** TODO: Enable tests after hotstuff LIB is working +/** TODO: Enable tests after instant_finality LIB is working BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, validating_tester ) try { create_accounts( {"alice"_n,"bob"_n,"carol"_n} ); @@ -121,7 +114,7 @@ BOOST_FIXTURE_TEST_CASE( producer_schedule_promotion_test, validating_tester ) t produce_block(); } - // activate hotstuff + // activate instant_finality set_finalizers({"alice"_n,"bob"_n,"carol"_n}); auto block = produce_block(); // this block contains the header extension of the finalizer set @@ -190,7 +183,7 @@ BOOST_AUTO_TEST_CASE( producer_watermark_test ) try { c.create_accounts( {"alice"_n,"bob"_n,"carol"_n} ); c.produce_block(); - // activate hotstuff + // activate instant_finality c.set_finalizers({"alice"_n,"bob"_n,"carol"_n}); auto block = c.produce_block(); // this block contains the header extension of the finalizer set @@ -309,7 +302,7 @@ BOOST_FIXTURE_TEST_CASE( producer_one_of_n_test, validating_tester ) try { create_accounts( {"alice"_n,"bob"_n} ); produce_block(); - // activate hotstuff + // activate instant_finality set_finalizers({"alice"_n,"bob"_n}); auto block = produce_block(); // this block contains the header extension of the finalizer set @@ -331,7 +324,7 @@ BOOST_FIXTURE_TEST_CASE( producer_m_of_n_test, validating_tester ) try { create_accounts( {"alice"_n,"bob"_n} ); produce_block(); - // activate hotstuff + // activate instant_finality set_finalizers({"alice"_n,"bob"_n}); auto block = produce_block(); // this block contains the header extension of the finalizer set diff --git a/unittests/protocol_feature_tests.cpp b/unittests/protocol_feature_tests.cpp index 45c76ab339..38fbbe40a7 100644 --- a/unittests/protocol_feature_tests.cpp +++ b/unittests/protocol_feature_tests.cpp @@ -2252,12 +2252,12 @@ BOOST_AUTO_TEST_CASE( block_validation_after_stage_1_test ) { try { tester2.produce_block(); // Push the block with delayed transaction to the second chain - auto bsf = tester2.control->create_block_state_future( copy_b->calculate_id(), copy_b ); + auto btf = tester2.control->create_block_token_future( copy_b->calculate_id(), copy_b ); tester2.control->abort_block(); controller::block_report br; // The block is invalidated - BOOST_REQUIRE_EXCEPTION(tester2.control->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} ), + BOOST_REQUIRE_EXCEPTION(tester2.control->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} ), fc::exception, fc_exception_message_starts_with("transaction cannot be delayed") ); diff --git a/unittests/unapplied_transaction_queue_tests.cpp b/unittests/unapplied_transaction_queue_tests.cpp index d2f683dc32..53dd773647 100644 --- a/unittests/unapplied_transaction_queue_tests.cpp +++ b/unittests/unapplied_transaction_queue_tests.cpp @@ -78,6 +78,20 @@ auto create_test_block_state( deque trx_metas ) { return bsp; } +using branch_type_legacy = fork_database::branch_type; + +template +void add_forked( unapplied_transaction_queue& queue, const BRANCH_TYPE& forked_branch ) { + // forked_branch is in reverse order + for( auto ritr = forked_branch.rbegin(), rend = forked_branch.rend(); ritr != rend; ++ritr ) { + const auto& bsptr = *ritr; + for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) { + const auto& trx = *itr; + queue.add_forked(trx); + } + } +} + // given a current itr make sure expected number of items are iterated over void verify_order( unapplied_transaction_queue& q, unapplied_transaction_queue::iterator itr, size_t expected ) { size_t size = 0; @@ -136,7 +150,7 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { auto bs1 = create_test_block_state( { trx1, trx2 } ); auto bs2 = create_test_block_state( { trx3, trx4, trx5 } ); auto bs3 = create_test_block_state( { trx6 } ); - q.add_forked( branch_type_legacy{ bs3, bs2, bs1, bs1 } ); // bs1 duplicate ignored + add_forked( q, branch_type_legacy{ bs3, bs2, bs1, bs1 } ); // bs1 duplicate ignored BOOST_CHECK( q.size() == 6u ); BOOST_REQUIRE( next( q ) == trx1 ); BOOST_CHECK( q.size() == 5u ); @@ -155,9 +169,9 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { // fifo forked auto bs4 = create_test_block_state( { trx7 } ); - q.add_forked( branch_type_legacy{ bs1 } ); - q.add_forked( branch_type_legacy{ bs3, bs2 } ); - q.add_forked( branch_type_legacy{ bs4 } ); + add_forked( q, branch_type_legacy{ bs1 } ); + add_forked( q, branch_type_legacy{ bs3, bs2 } ); + add_forked( q, branch_type_legacy{ bs4 } ); BOOST_CHECK( q.size() == 7u ); BOOST_REQUIRE( next( q ) == trx1 ); BOOST_CHECK( q.size() == 6u ); @@ -189,10 +203,10 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { // fifo forked, multi forks auto bs5 = create_test_block_state( { trx11, trx12, trx13 } ); auto bs6 = create_test_block_state( { trx11, trx15 } ); - q.add_forked( branch_type_legacy{ bs3, bs2, bs1 } ); - q.add_forked( branch_type_legacy{ bs4 } ); - q.add_forked( branch_type_legacy{ bs3, bs2 } ); // dups ignored - q.add_forked( branch_type_legacy{ bs6, bs5 } ); + add_forked( q, branch_type_legacy{ bs3, bs2, bs1 } ); + add_forked( q, branch_type_legacy{ bs4 } ); + add_forked( q, branch_type_legacy{ bs3, bs2 } ); // dups ignored + add_forked( q, branch_type_legacy{ bs6, bs5 } ); BOOST_CHECK_EQUAL( q.size(), 11u ); BOOST_REQUIRE( next( q ) == trx1 ); BOOST_CHECK( q.size() == 10u ); @@ -220,10 +234,10 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { BOOST_CHECK( q.empty() ); // altogether, order fifo: forked, aborted - q.add_forked( branch_type_legacy{ bs3, bs2, bs1 } ); + add_forked( q, branch_type_legacy{ bs3, bs2, bs1 } ); q.add_aborted( { trx9, trx14 } ); q.add_aborted( { trx18, trx19 } ); - q.add_forked( branch_type_legacy{ bs6, bs5, bs4 } ); + add_forked( q, branch_type_legacy{ bs6, bs5, bs4 } ); // verify order verify_order( q, q.begin(), 15 ); // verify type order @@ -289,7 +303,7 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { BOOST_REQUIRE( next( q ) == trx22 ); BOOST_CHECK( q.empty() ); - q.add_forked( branch_type_legacy{ bs3, bs2, bs1 } ); + add_forked( q, branch_type_legacy{ bs3, bs2, bs1 } ); q.add_aborted( { trx9, trx11 } ); q.clear(); BOOST_CHECK( q.empty() );