From a3ce90c0947c6a8168a1988f6ce1afded3bb900e Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 10 Apr 2024 15:13:58 -0400 Subject: [PATCH 01/60] Implement finalizer policy change at appropriate time. --- libraries/chain/block_header_state.cpp | 72 +++++++++++++++---- .../eosio/chain/block_header_state.hpp | 12 +++- .../include/eosio/chain/snapshot_detail.hpp | 2 +- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 7097e567f8..1f747a5d51 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -29,8 +29,10 @@ digest_type block_header_state::compute_base_digest() const { for (const auto& fp_pair : finalizer_policies) { fc::raw::pack( enc, fp_pair.first ); - assert(fp_pair.second); - fc::raw::pack( enc, *fp_pair.second ); + const finalizer_policy_tracker& tracker = fp_pair.second; + fc::raw::pack( enc, tracker.state ); + assert(tracker.policy); + fc::raw::pack( enc, *tracker.policy ); } assert(active_proposer_policy); @@ -79,6 +81,7 @@ void finish_next(const block_header_state& prev, block_header_state& next_header_state, vector new_protocol_feature_activations, std::shared_ptr new_proposer_policy, + std::optional new_finalizer_policy, qc_claim_t qc_claim) { // activated protocol features @@ -110,10 +113,6 @@ void finish_next(const block_header_state& prev, next_header_state.proposer_policies[new_proposer_policy->active_time] = std::move(new_proposer_policy); } - // finalizer policy - // ---------------- - next_header_state.active_finalizer_policy = prev.active_finalizer_policy; - // finality_core // ------------- block_ref parent_block { @@ -122,6 +121,41 @@ void finish_next(const block_header_state& prev, }; next_header_state.core = prev.core.next(parent_block, qc_claim); + // finalizer policy + // ---------------- + next_header_state.active_finalizer_policy = prev.active_finalizer_policy; + + if(!prev.finalizer_policies.empty()) { + auto lib = next_header_state.core.last_final_block_num(); + auto it = prev.finalizer_policies.begin(); + if (it->first > lib) { + next_header_state.finalizer_policies = prev.finalizer_policies; + } else { + while (it->first <= lib && it != prev.finalizer_policies.end()) { + const finalizer_policy_tracker& tracker = it->second; + if (tracker.state == finalizer_policy_tracker::state_t::pending) { + // new finalizer_policy becones active + next_header_state.active_finalizer_policy = tracker.policy; + } else { + assert(tracker.state == finalizer_policy_tracker::state_t::proposed); + // block where finalizer_policy was proposed became final. The finalizer policy will + // become active when next block becomes final. + finalizer_policy_tracker t { finalizer_policy_tracker::state_t::pending, tracker.policy }; + next_header_state.finalizer_policies.emplace(next_header_state.block_num(), std::move(t)); + } + ++it; + } + next_header_state.finalizer_policies.insert(boost::container::ordered_unique_range_t(), + it, prev.finalizer_policies.end()); + } + } + + if (new_finalizer_policy) { + next_header_state.finalizer_policies[next_header_state.block_num()] = + finalizer_policy_tracker{finalizer_policy_tracker::state_t::proposed, + std::make_shared(std::move(*new_finalizer_policy)) }; + } + // Finally update block id from header // ----------------------------------- next_header_state.block_id = next_header_state.header.calculate_id(); @@ -162,7 +196,9 @@ block_header_state block_header_state::next(block_header_state_input& input) con next_header_state.header_exts.emplace(ext_id, std::move(pfa_ext)); } - finish_next(*this, next_header_state, std::move(input.new_protocol_feature_activations), std::move(input.new_proposer_policy), input.most_recent_ancestor_with_qc); + finish_next(*this, next_header_state, std::move(input.new_protocol_feature_activations), + std::move(input.new_proposer_policy), std::move(input.new_finalizer_policy), + input.most_recent_ancestor_with_qc); return next_header_state; } @@ -176,14 +212,16 @@ block_header_state block_header_state::next(block_header_state_input& input) con block_header_state block_header_state::next(const signed_block_header& h, validator_t& validator) const { auto producer = detail::get_scheduled_producer(active_proposer_policy->proposer_schedule.producers, h.timestamp).producer_name; - EOS_ASSERT( h.previous == block_id, unlinkable_block_exception, "previous mismatch ${p} != ${id}", ("p", h.previous)("id", block_id) ); + EOS_ASSERT( h.previous == block_id, unlinkable_block_exception, + "previous mismatch ${p} != ${id}", ("p", h.previous)("id", block_id) ); EOS_ASSERT( h.producer == producer, wrong_producer, "wrong producer specified" ); - EOS_ASSERT( !h.new_producers, producer_schedule_exception, "Block header contains legacy producer schedule outdated by activation of WTMsig Block Signatures" ); + EOS_ASSERT( !h.new_producers, producer_schedule_exception, + "Block header contains legacy producer schedule outdated by activation of WTMsig Block Signatures" ); block_header_state next_header_state; next_header_state.header = static_cast(h); next_header_state.header_exts = h.validate_and_extract_header_extensions(); - auto& exts = next_header_state.header_exts; + const auto& exts = next_header_state.header_exts; // retrieve protocol_feature_activation from incoming block header extension // ------------------------------------------------------------------------- @@ -199,8 +237,8 @@ block_header_state block_header_state::next(const signed_block_header& h, valida // -------------------------------------------------------------------- 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); + auto if_entry = exts.lower_bound(instant_finality_extension::extension_id()); + const auto& if_ext = std::get(if_entry->second); if (h.is_proper_svnn_block()) { // if there is no Finality Tree Root associated with the block, @@ -211,14 +249,18 @@ block_header_state block_header_state::next(const signed_block_header& h, valida EOS_ASSERT(no_finality_tree_associated == h.action_mroot.empty(), block_validate_exception, "No Finality Tree Root associated with the block, does not match with empty action_mroot: " "(${n}), action_mroot empty (${e}), final_on_strong_qc_block_num (${f})", - ("n", no_finality_tree_associated)("e", h.action_mroot.empty())("f", next_core_metadata.final_on_strong_qc_block_num)); + ("n", no_finality_tree_associated)("e", h.action_mroot.empty()) + ("f", next_core_metadata.final_on_strong_qc_block_num)); }; - finish_next(*this, next_header_state, std::move(new_protocol_feature_activations), if_ext.new_proposer_policy, if_ext.qc_claim); + finish_next(*this, next_header_state, std::move(new_protocol_feature_activations), if_ext.new_proposer_policy, + if_ext.new_finalizer_policy, if_ext.qc_claim); return next_header_state; } } // namespace eosio::chain -FC_REFLECT( eosio::chain::finality_digest_data_v1, (major_version)(minor_version)(active_finalizer_policy_generation)(finality_tree_digest)(active_finalizer_policy_and_base_digest) ) +FC_REFLECT( eosio::chain::finality_digest_data_v1, + (major_version)(minor_version)(active_finalizer_policy_generation) + (finality_tree_digest)(active_finalizer_policy_and_base_digest) ) diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 02291c3547..c073b83d76 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -20,6 +20,12 @@ namespace detail { struct schedule_info; }; constexpr uint32_t light_header_protocol_version_major = 1; constexpr uint32_t light_header_protocol_version_minor = 0; +struct finalizer_policy_tracker { + enum class state_t { proposed = 0, pending }; + state_t state; + finalizer_policy_ptr policy; +}; + struct building_block_input { block_id_type parent_id; block_timestamp_type parent_timestamp; @@ -50,7 +56,7 @@ struct block_header_state { // block time when proposer_policy will become active flat_map proposer_policies; - flat_map finalizer_policies; + flat_map finalizer_policies; // ------ data members caching information available elsewhere ---------------------- @@ -93,6 +99,10 @@ using block_header_state_ptr = std::shared_ptr; } +FC_REFLECT_ENUM( eosio::chain::finalizer_policy_tracker::state_t, (proposed)(pending)) + +FC_REFLECT( eosio::chain::finalizer_policy_tracker, (state)(policy)) + FC_REFLECT( eosio::chain::block_header_state, (block_id)(header) (activated_protocol_features)(core)(active_finalizer_policy) (active_proposer_policy)(proposer_policies)(finalizer_policies)(header_exts)) diff --git a/libraries/chain/include/eosio/chain/snapshot_detail.hpp b/libraries/chain/include/eosio/chain/snapshot_detail.hpp index 6bf0a8b7a9..5373aa6a7e 100644 --- a/libraries/chain/include/eosio/chain/snapshot_detail.hpp +++ b/libraries/chain/include/eosio/chain/snapshot_detail.hpp @@ -117,7 +117,7 @@ namespace eosio::chain::snapshot_detail { finalizer_policy_ptr active_finalizer_policy; proposer_policy_ptr active_proposer_policy; flat_map proposer_policies; - flat_map finalizer_policies; + flat_map finalizer_policies; // from block_state std::optional valid; From 64616c35964ba736bf77cbd115d7d1d9f32b1787 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 10 Apr 2024 16:29:15 -0400 Subject: [PATCH 02/60] Add missing serialization of `proposer_policies`'s timestamp in `base_digest` --- libraries/chain/block_header_state.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 1f747a5d51..f00ac728b6 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -39,6 +39,7 @@ digest_type block_header_state::compute_base_digest() const { fc::raw::pack( enc, *active_proposer_policy ); for (const auto& pp_pair : proposer_policies) { + fc::raw::pack( enc, pp_pair.first ); assert(pp_pair.second); fc::raw::pack( enc, *pp_pair.second ); } From 7f307959feb351e5b271eff91d3b24586133804d Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 12 Apr 2024 10:38:18 -0400 Subject: [PATCH 03/60] Add test for `set_finalizers`, wip --- libraries/chain/controller.cpp | 4 + .../testing/include/eosio/testing/tester.hpp | 12 ++- libraries/testing/tester.cpp | 27 +++++-- unittests/api_tests.cpp | 80 ++++++++++++++++++- 4 files changed, 113 insertions(+), 10 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 613a7fb16b..9b25ee8334 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3575,6 +3575,10 @@ struct controller_impl { auto bsp = forkdb.get_block(id); if (bsp) { return my_finalizers.all_of_public_keys([&bsp](const auto& k) { + const finalizer_policy_ptr& fp { bsp->active_finalizer_policy }; + assert(fp); + if (!std::ranges::any_of(fp->finalizers, [&](const auto& auth) { return auth.public_key == k; })) + return true; // we only care about keys from the active finalizer_policy return bsp->has_voted(k); }); } diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index 6ca6dbf922..9cbf1d81db 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -233,7 +233,7 @@ namespace eosio { namespace testing { uint32_t expiration = DEFAULT_EXPIRATION_DELTA, uint32_t delay_sec = 0 )const; - vector create_accounts( vector names, + vector create_accounts( const vector& names, bool multisig = false, bool include_code = true ) @@ -255,7 +255,15 @@ namespace eosio { namespace testing { // libtester uses 1 as weight of each of the finalizer, sets (2/3 finalizers + 1) // as threshold, and makes all finalizers vote QC - std::pair> set_finalizers(const vector& finalizer_names); + std::pair> + set_finalizers(std::span finalizer_names); + + std::pair> + set_finalizers(const std::vector& names) { + return set_finalizers(std::span{names.begin(), names.end()}); + } + + void set_node_finalizers(std::span finalizer_names); // Finalizer policy input to set up a test: weights, threshold and local finalizers // which participate voting. diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index 719851a767..aa86fe9678 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -20,7 +20,9 @@ eosio::chain::asset core_from_string(const std::string& s) { return eosio::chain::asset::from_string(s + " " CORE_SYMBOL_NAME); } -namespace eosio { namespace testing { +using bls_private_key = fc::crypto::blslib::bls_private_key; + +namespace eosio::testing { fc::logger test_logger = fc::logger::get(); @@ -1189,7 +1191,7 @@ namespace eosio { namespace testing { } - std::pair> base_tester::set_finalizers(const vector& finalizer_names) { + std::pair> base_tester::set_finalizers(std::span finalizer_names) { auto num_finalizers = finalizer_names.size(); std::vector finalizers_info; finalizers_info.reserve(num_finalizers); @@ -1200,22 +1202,23 @@ namespace eosio { namespace testing { finalizer_policy_input policy_input = { .finalizers = finalizers_info, .threshold = num_finalizers * 2 / 3 + 1, - .local_finalizers = finalizer_names + .local_finalizers = std::vector{finalizer_names.begin(), finalizer_names.end()} }; return set_finalizers(policy_input); } - std::pair> base_tester::set_finalizers(const finalizer_policy_input& input) { + std::pair> base_tester::set_finalizers(const finalizer_policy_input& input) { chain::bls_pub_priv_key_map_t local_finalizer_keys; fc::variants finalizer_auths; - std::vector priv_keys; + std::vector priv_keys; for (const auto& f: input.finalizers) { auto [privkey, pubkey, pop] = get_bls_key( f.name ); // if it is a local finalizer, set up public to private key mapping for voting - if( auto it = std::ranges::find_if(input.local_finalizers, [&](const auto& name) { return name == f.name; }); it != input.local_finalizers.end()) { + if( auto it = std::ranges::find_if(input.local_finalizers, [&](const auto& name) { return name == f.name; }); + it != input.local_finalizers.end()) { local_finalizer_keys[pubkey.to_string()] = privkey.to_string(); priv_keys.emplace_back(privkey); }; @@ -1239,6 +1242,16 @@ namespace eosio { namespace testing { priv_keys }; } + void base_tester::set_node_finalizers(std::span names) { + + chain::bls_pub_priv_key_map_t local_finalizer_keys; + for (auto name: names) { + auto [privkey, pubkey, pop] = get_bls_key(name); + local_finalizer_keys[pubkey.to_string()] = privkey.to_string(); + } + control->set_node_finalizer_keys(local_finalizer_keys); + } + const table_id_object* base_tester::find_table( name code, name scope, name table ) { auto tid = control->db().find(boost::make_tuple(code, scope, table)); return tid; @@ -1460,7 +1473,7 @@ namespace eosio { namespace testing { const std::string mock::webauthn_private_key::_origin = "mock.webauthn.invalid"; const sha256 mock::webauthn_private_key::_origin_hash = fc::sha256::hash(mock::webauthn_private_key::_origin); -} } /// eosio::testing +} /// eosio::testing std::ostream& operator<<( std::ostream& osm, const fc::variant& v ) { //fc::json::to_stream( osm, v ); diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index f96fe14ec3..25d94870c2 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3860,7 +3860,7 @@ BOOST_AUTO_TEST_CASE(get_code_hash_tests) { try { } FC_LOG_AND_RETHROW() } // test set_finalizer host function serialization and tester set_finalizers -BOOST_AUTO_TEST_CASE(set_finalizer_test) { try { +BOOST_AUTO_TEST_CASE(initial_set_finalizer_test) { try { validating_tester t; uint32_t lib = 0; @@ -3919,6 +3919,84 @@ BOOST_AUTO_TEST_CASE(set_finalizer_test) { try { BOOST_CHECK_GT(lib, lib_after_transition); } FC_LOG_AND_RETHROW() } +// verify that finalizers changes via set_finalizer take 2 3-cchain to take effect +// and that multiple ones can be in flight at the same time. +// ------------------------------------------------------------------------------- +BOOST_AUTO_TEST_CASE(savanna_set_finalizer_test) { try { + validating_tester t; + + auto set_finalizers = [&](std::span names) { + base_tester::finalizer_policy_input input; + for (auto n : names) input.finalizers.emplace_back(n, 1); + input.threshold = names.size() * 2 / 3 + 1; + t.set_finalizers(input); + }; + + uint32_t lib = 0; + signed_block_ptr lib_block; + t.control->irreversible_block().connect([&](const block_signal_params& t) { + const auto& [ block, id ] = t; + lib = block->block_num(); + lib_block = block; + }); + + t.produce_block(); + + // Create finalizer accounts + vector finalizers; + finalizers.reserve(50); + for (size_t i=0; i<50; ++i) + finalizers.emplace_back(std::string("init") + (char)('a' + i/26) + (char)('a' + i%26)); + + t.create_accounts(finalizers); + t.produce_block(); + + // activate savanna' + t.set_node_finalizers({&finalizers[0], finalizers.size()}); + + constexpr size_t finset_size = 21; + set_finalizers({&finalizers[0], finset_size}); + + // `genesis_block` is the first block where set_finalizers() was executed. + // It is the genesis block. + // It will include the first header extension for the instant finality. + // ----------------------------------------------------------------------- + auto genesis_block = t.produce_block(); + + // wait till the genesis_block becomes irreversible. + // The critical block is the block that makes the genesis_block irreversible + // ------------------------------------------------------------------------- + signed_block_ptr critical_block = nullptr; + while(genesis_block->block_num() > lib) + critical_block = t.produce_block(); + + // Blocks after the critical block are proper IF blocks. + // ----------------------------------------------------- + auto first_proper_block = t.produce_block(); + BOOST_REQUIRE(first_proper_block->is_proper_svnn_block()); + + // wait till the first proper block becomes irreversible. Transition will be done then + // ----------------------------------------------------------------------------------- + signed_block_ptr pt_block = nullptr; + while(first_proper_block->block_num() > lib) { + pt_block = t.produce_block(); + BOOST_REQUIRE(pt_block->is_proper_svnn_block()); + } + + // lib must advance after 3 blocks + // ------------------------------- + t.produce_blocks(3); + BOOST_CHECK_EQUAL(lib, pt_block->block_num()); + + // run set_finalizers(), verify it becomes active after exactly two 3-chains + // ------------------------------------------------------------------------- + set_finalizers({&finalizers[1], finset_size}); + auto sf1_block = t.produce_block(); + +} FC_LOG_AND_RETHROW() } + + + void test_finality_transition(const vector& accounts, const base_tester::finalizer_policy_input& input, bool lib_advancing_expected) { validating_tester t; From 06e6109bc9bb0afe2ccbd4e67a8524c362674bdb Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 12 Apr 2024 13:06:51 -0500 Subject: [PATCH 04/60] Delay sending handshakes on becoming in_sync until controller state is caught up to head --- plugins/net_plugin/net_plugin.cpp | 40 ++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index 4f04dae7ca..60d8c579f2 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -234,6 +234,9 @@ namespace eosio { alignas(hardware_destructive_interference_size) std::atomic sync_state{in_sync}; std::atomic sync_ordinal{0}; + // indicate that we have received blocks to catch us up to head, delay sending out handshakes until we have + // applied the blocks and our controller head is updated + std::atomic send_handshakes_when_synced{false}; // Instant finality makes it likely peers think their lib and head are // not in sync but in reality they are only within small difference. @@ -262,7 +265,8 @@ namespace eosio { void sync_reset_lib_num( const connection_ptr& conn, bool closing ); void sync_reassign_fetch( const connection_ptr& c, go_away_reason reason ); void rejected_block( const connection_ptr& c, uint32_t blk_num, closing_mode mode ); - void sync_recv_block( const connection_ptr& c, const block_id_type& blk_id, uint32_t blk_num, bool blk_applied ); + void sync_recv_block( const connection_ptr& c, const block_id_type& blk_id, uint32_t blk_num, bool blk_applied, + const fc::microseconds& blk_latency ); void recv_handshake( const connection_ptr& c, const handshake_message& msg, uint32_t nblk_combined_latency ); void sync_recv_notice( const connection_ptr& c, const notice_message& msg ); }; @@ -2468,8 +2472,11 @@ namespace eosio { } // called from c's connection strand - void sync_manager::sync_recv_block(const connection_ptr& c, const block_id_type& blk_id, uint32_t blk_num, bool blk_applied) { - peer_dlog( c, "${d} block ${bn}", ("d", blk_applied ? "applied" : "got")("bn", blk_num) ); + void sync_manager::sync_recv_block(const connection_ptr& c, const block_id_type& blk_id, uint32_t blk_num, + bool blk_applied, const fc::microseconds& blk_latency) { + peer_dlog(c, "${d} block ${bn}:${id}.. latency ${l}ms", + ("d", blk_applied ? "applied" : "got")("bn", blk_num)("id", blk_id.str().substr(8,16)) + ("l", blk_latency == fc::microseconds::maximum() ? 0 : blk_latency.count()/1000) ); if( app().is_quiting() ) { c->close( false, true ); return; @@ -2509,14 +2516,14 @@ namespace eosio { } } else { set_state( in_sync ); - peer_dlog( c, "Switching to in_sync, sending handshakes" ); - send_handshakes(); + peer_dlog( c, "Switching to in_sync, will send handshakes when caught up" ); + send_handshakes_when_synced = true; } } else if( state == lib_catchup ) { fc::unique_lock g_sync( sync_mtx ); if( blk_applied && blk_num >= sync_known_lib_num ) { peer_dlog( c, "All caught up with last known last irreversible block resending handshake" ); - set_state( in_sync ); + set_state( head_catchup ); g_sync.unlock(); send_handshakes(); } else { @@ -2550,6 +2557,9 @@ namespace eosio { } } + } else if ( blk_latency.count() < config::block_interval_us && send_handshakes_when_synced ) { + send_handshakes(); + send_handshakes_when_synced = false; } } @@ -3108,7 +3118,7 @@ namespace eosio { if( my_impl->dispatcher.have_block( blk_id ) ) { peer_dlog( this, "canceling wait, already received block ${num}, id ${id}...", ("num", blk_num)("id", blk_id.str().substr(8,16)) ); - my_impl->sync_master->sync_recv_block( shared_from_this(), blk_id, blk_num, true ); + my_impl->sync_master->sync_recv_block( shared_from_this(), blk_id, blk_num, true, fc::microseconds::maximum() ); cancel_wait(); pending_message_buffer.advance_read_ptr( message_length ); @@ -3143,7 +3153,7 @@ namespace eosio { pending_message_buffer.advance_read_ptr( message_length ); return true; } - my_impl->sync_master->sync_recv_block(shared_from_this(), blk_id, blk_num, false); + my_impl->sync_master->sync_recv_block(shared_from_this(), blk_id, blk_num, false, fc::microseconds::maximum()); } auto ds = pending_message_buffer.create_datastream(); @@ -3778,7 +3788,7 @@ namespace eosio { 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 ); + my_impl->sync_master->sync_recv_block( c, id, block_header::num_from_id(id), false, fc::microseconds::maximum() ); }); return; } @@ -3842,12 +3852,13 @@ namespace eosio { connection_ptr c = shared_from_this(); uint32_t lib = cc.last_irreversible_block_num(); + fc::microseconds age(fc::time_point::now() - block->timestamp); try { if( blk_num <= lib || cc.validated_block_exists(blk_id) ) { c->strand.post( [sync_master = my_impl->sync_master.get(), - &dispatcher = my_impl->dispatcher, c, blk_id, blk_num]() { + &dispatcher = my_impl->dispatcher, c, blk_id, blk_num, latency = age]() { dispatcher.add_peer_block( blk_id, c->connection_id ); - sync_master->sync_recv_block( c, blk_id, blk_num, true ); + sync_master->sync_recv_block( c, blk_id, blk_num, true, latency ); }); return; } @@ -3858,7 +3869,6 @@ namespace eosio { fc_elog( logger, "caught an unknown exception trying to fetch block ${id}, conn ${c}", ("id", blk_id)("c", connection_id) ); } - fc::microseconds age( fc::time_point::now() - block->timestamp); fc_dlog( logger, "received signed_block: #${n} block age in secs = ${age}, connection - ${cid}, ${v}, lib #${lib}", ("n", blk_num)("age", age.to_seconds())("cid", c->connection_id)("v", obt ? "header validated" : "header validation pending")("lib", lib) ); @@ -3907,9 +3917,11 @@ namespace eosio { }); } }); - c->strand.post( [sync_master = my_impl->sync_master.get(), &dispatcher = my_impl->dispatcher, c, blk_id, blk_num]() { + c->strand.post( [sync_master = my_impl->sync_master.get(), + &dispatcher = my_impl->dispatcher, + c, blk_id, blk_num, latency = age]() { dispatcher.recv_block( c, blk_id, blk_num ); - sync_master->sync_recv_block( c, blk_id, blk_num, true ); + sync_master->sync_recv_block( c, blk_id, blk_num, true, latency ); }); } else { c->strand.post( [sync_master = my_impl->sync_master.get(), &dispatcher = my_impl->dispatcher, c, From 919e923984e828fcc93627a2c159a7f244683454 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Mon, 15 Apr 2024 09:30:24 -0400 Subject: [PATCH 05/60] First test of correct finalizer set change passes. --- libraries/chain/block_header_state.cpp | 2 +- libraries/chain/block_state.cpp | 12 ++++-- libraries/chain/controller.cpp | 13 ++++++ libraries/chain/hotstuff/finalizer.cpp | 2 +- .../chain/include/eosio/chain/controller.hpp | 3 ++ .../include/fc/crypto/bls_public_key.hpp | 8 ++++ .../testing/include/eosio/testing/tester.hpp | 6 +++ libraries/testing/tester.cpp | 14 +++++++ unittests/api_tests.cpp | 41 +++++++++++++++---- 9 files changed, 88 insertions(+), 13 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index f00ac728b6..364dde4c22 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -180,7 +180,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con // finality extension // ------------------ instant_finality_extension new_if_ext {input.most_recent_ancestor_with_qc, - std::move(input.new_finalizer_policy), + input.new_finalizer_policy, input.new_proposer_policy}; uint16_t if_ext_id = instant_finality_extension::extension_id(); diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 4735e81de4..e43e2cf434 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -15,7 +15,9 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con , block(std::move(b)) , strong_digest(compute_finality_digest()) , weak_digest(create_weak_digest(strong_digest)) - , pending_qc(prev.active_finalizer_policy->finalizers.size(), prev.active_finalizer_policy->threshold, prev.active_finalizer_policy->max_weak_sum_before_weak_final()) + , pending_qc(active_finalizer_policy->finalizers.size(), + active_finalizer_policy->threshold, + active_finalizer_policy->max_weak_sum_before_weak_final()) { // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here if( !skip_validate_signee ) { @@ -37,7 +39,9 @@ block_state::block_state(const block_header_state& bhs, , block(std::make_shared(signed_block_header{bhs.header})) , strong_digest(compute_finality_digest()) , weak_digest(create_weak_digest(strong_digest)) - , pending_qc(bhs.active_finalizer_policy->finalizers.size(), bhs.active_finalizer_policy->threshold, bhs.active_finalizer_policy->max_weak_sum_before_weak_final()) + , pending_qc(active_finalizer_policy->finalizers.size(), + active_finalizer_policy->threshold, + active_finalizer_policy->max_weak_sum_before_weak_final()) , valid(valid) , pub_keys_recovered(true) // called by produce_block so signature recovery of trxs must have been done , cached_trxs(std::move(trx_metas)) @@ -84,7 +88,9 @@ block_state_ptr block_state::create_if_genesis_block(const block_state_legacy& b // TODO: https://github.com/AntelopeIO/leap/issues/2057 // TODO: Do not aggregate votes on blocks created from block_state_legacy. This can be removed when #2057 complete. - result.pending_qc = pending_quorum_certificate{result.active_finalizer_policy->finalizers.size(), result.active_finalizer_policy->threshold, result.active_finalizer_policy->max_weak_sum_before_weak_final()}; + result.pending_qc = pending_quorum_certificate{result.active_finalizer_policy->finalizers.size(), + result.active_finalizer_policy->threshold, + result.active_finalizer_policy->max_weak_sum_before_weak_final()}; // build leaf_node and validation_tree valid_t::finality_leaf_node_t leaf_node { diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 9b25ee8334..edc00b099a 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3588,6 +3588,15 @@ struct controller_impl { return !voted || *voted; } + std::optional active_finalizer_policy(const block_id_type& id) const { + return fork_db.apply_s>([&](auto& forkdb) -> std::optional { + auto bsp = forkdb.get_block(id); + if (bsp) + return *bsp->active_finalizer_policy; + return {}; + }); + } + // thread safe void create_and_send_vote_msg(const block_state_ptr& bsp) { if (!bsp->block->is_proper_svnn_block()) @@ -5266,6 +5275,10 @@ bool controller::node_has_voted_if_finalizer(const block_id_type& id) const { return my->node_has_voted_if_finalizer(id); } +std::optional controller::active_finalizer_policy(const block_id_type& id) const { + return my->active_finalizer_policy(id); +} + const producer_authority_schedule& controller::active_producers()const { return my->active_producers(); } diff --git a/libraries/chain/hotstuff/finalizer.cpp b/libraries/chain/hotstuff/finalizer.cpp index af0d3028c2..aa34f01f2c 100644 --- a/libraries/chain/hotstuff/finalizer.cpp +++ b/libraries/chain/hotstuff/finalizer.cpp @@ -201,10 +201,10 @@ my_finalizers_t::fsi_map my_finalizers_t::load_finalizer_safety_info() { // ---------------------------------------------------------------------------------------- void my_finalizers_t::set_keys(const std::map& finalizer_keys) { - assert(finalizers.empty()); // set_keys should be called only once at startup if (finalizer_keys.empty()) return; + assert(finalizers.empty()); // set_keys should be called only once at startup fsi_map safety_info = load_finalizer_safety_info(); for (const auto& [pub_key_str, priv_key_str] : finalizer_keys) { auto public_key {bls_public_key{pub_key_str}}; diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index d2ac81dc32..c92799825e 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -330,6 +330,9 @@ namespace eosio::chain { // thread safe, for testing bool node_has_voted_if_finalizer(const block_id_type& id) const; + // thread safe, for testing + std::optional active_finalizer_policy(const block_id_type& id) const; + bool light_validation_allowed() const; bool skip_auth_check()const; bool skip_trx_checks()const; diff --git a/libraries/libfc/include/fc/crypto/bls_public_key.hpp b/libraries/libfc/include/fc/crypto/bls_public_key.hpp index 81a6538fe1..e80f44fd21 100644 --- a/libraries/libfc/include/fc/crypto/bls_public_key.hpp +++ b/libraries/libfc/include/fc/crypto/bls_public_key.hpp @@ -61,6 +61,14 @@ namespace fc::crypto::blslib { return ds; } + friend std::ostream& operator<<(std::ostream& os, const fc::crypto::blslib::bls_public_key& k) { + os << "bls_public_key(" << std::hex; + for (auto c : k.affine_non_montgomery_le()) + os << std::setfill('0') << std::setw(2) << c; + os << std::dec << ")"; + return os; + } + template friend T& operator>>(T& ds, bls_public_key& sig) { // Serialization as variable length array when it is stored as a fixed length array. This makes for easier deserialization by external tools diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index 9cbf1d81db..537e9c06b6 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -265,6 +265,8 @@ namespace eosio { namespace testing { void set_node_finalizers(std::span finalizer_names); + std::vector set_active_finalizers(std::span finalizer_names); + // Finalizer policy input to set up a test: weights, threshold and local finalizers // which participate voting. struct finalizer_policy_input { @@ -279,6 +281,10 @@ namespace eosio { namespace testing { }; std::pair> set_finalizers(const finalizer_policy_input& input); + std::optional active_finalizer_policy(const block_id_type& id) const { + return control->active_finalizer_policy(id); + } + void link_authority( account_name account, account_name code, permission_name req, action_name type = {} ); void unlink_authority( account_name account, account_name code, action_name type = {} ); void set_authority( account_name account, permission_name perm, authority auth, diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index aa86fe9678..fb617d99d9 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -21,6 +21,7 @@ eosio::chain::asset core_from_string(const std::string& s) { } using bls_private_key = fc::crypto::blslib::bls_private_key; +using bls_public_key = fc::crypto::blslib::bls_public_key; namespace eosio::testing { @@ -1252,6 +1253,19 @@ namespace eosio::testing { control->set_node_finalizer_keys(local_finalizer_keys); } + std::vector base_tester::set_active_finalizers(std::span names) { + std::vector pubkeys; + finalizer_policy_input input; + for (auto name : names) { + auto [privkey, pubkey, pop] = get_bls_key(name); + pubkeys.push_back(pubkey); + input.finalizers.emplace_back(name, 1); + } + input.threshold = names.size() * 2 / 3 + 1; + set_finalizers(input); + return pubkeys; + } + const table_id_object* base_tester::find_table( name code, name scope, name table ) { auto tid = control->db().find(boost::make_tuple(code, scope, table)); return tid; diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 25d94870c2..69c2718eca 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3923,13 +3923,26 @@ BOOST_AUTO_TEST_CASE(initial_set_finalizer_test) { try { // and that multiple ones can be in flight at the same time. // ------------------------------------------------------------------------------- BOOST_AUTO_TEST_CASE(savanna_set_finalizer_test) { try { + using bls_public_key = fc::crypto::blslib::bls_public_key; validating_tester t; - auto set_finalizers = [&](std::span names) { - base_tester::finalizer_policy_input input; - for (auto n : names) input.finalizers.emplace_back(n, 1); - input.threshold = names.size() * 2 / 3 + 1; - t.set_finalizers(input); + auto check_finalizer_policy = [&](const signed_block_ptr& block, + uint32_t generation, + std::span keys_span) { + auto finpol = t.active_finalizer_policy(block->calculate_id()); + BOOST_REQUIRE(!!finpol); + BOOST_CHECK_EQUAL(finpol->generation, generation); // new policy should not be active + // until after two 3-chains + BOOST_CHECK_EQUAL(keys_span.size(), finpol->finalizers.size()); + std::vector keys {keys_span.begin(), keys_span.end() }; + std::sort(keys.begin(), keys.end()); + + std::vector active_keys; + for (const auto& auth : finpol->finalizers) + active_keys.push_back(auth.public_key); + std::sort(active_keys.begin(), active_keys.end()); + for (size_t i=0; i Date: Mon, 15 Apr 2024 09:21:53 -0500 Subject: [PATCH 06/60] GH-16 Add deep-mind ACCEPTED_BLOCK_V2 --- libraries/chain/controller.cpp | 21 ++++++++++--------- libraries/chain/deep_mind.cpp | 14 +++++++++++++ .../chain/include/eosio/chain/block_state.hpp | 2 +- .../chain/include/eosio/chain/deep_mind.hpp | 3 +++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 613a7fb16b..4b73cfb954 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3167,16 +3167,6 @@ struct controller_impl { chain_head = block_handle{cb.bsp}; emit( accepted_block, std::tie(chain_head.block(), chain_head.id()) ); - apply(chain_head, [&](const auto& head) { -#warning todo: support deep_mind_logger even when in IF mode - if constexpr (std::is_same_v>) { - // at block level, no transaction specific logging is possible - if (auto* dm_logger = get_deep_mind_logger(false)) { - dm_logger->on_accepted_block(head); - } - } - }); - if( s == controller::block_status::incomplete ) { fork_db.apply_s([&](auto& forkdb) { assert(std::holds_alternative>(cb.bsp.internal())); @@ -3202,6 +3192,17 @@ struct controller_impl { apply_s(chain_head, [&](const auto& head) { create_and_send_vote_msg(head); }); } + apply(chain_head, [&](const auto& head) { + if (auto* dm_logger = get_deep_mind_logger(false)) { + auto fd = head_finality_data(); + if constexpr (std::is_same_v>) { + dm_logger->on_accepted_block(head); + } else { + assert(fd); + dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); + } + } + }); if (s == controller::block_status::incomplete) { const auto& id = chain_head.id(); diff --git a/libraries/chain/deep_mind.cpp b/libraries/chain/deep_mind.cpp index 3714adf6ce..308b047004 100644 --- a/libraries/chain/deep_mind.cpp +++ b/libraries/chain/deep_mind.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -82,6 +83,19 @@ namespace eosio::chain { ); } + void deep_mind_handler::on_accepted_block_v2(block_num_type lib, const signed_block_ptr& b, const finality_data_t& fd) + { + auto packed_blk = fc::raw::pack(*b); + auto finality_data = fc::raw::pack(fd); + + fc_dlog(_logger, "ACCEPTED_BLOCK_V2 ${num} ${lib} ${blk} ${fd}", + ("num", b->block_num()) + ("lib", lib) + ("blk", fc::to_hex(packed_blk)) + ("fd", fc::to_hex(finality_data)) + ); + } + void deep_mind_handler::on_switch_forks(const block_id_type& old_head, const block_id_type& new_head) { fc_dlog(_logger, "SWITCH_FORK ${from_id} ${to_id}", diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index a212f1655f..4b51da8443 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -55,7 +55,7 @@ struct valid_t { std::vector validation_mroots; }; -// This is mostly used by SHiP to stream finality_data +// This is mostly used by SHiP & deep-mind to stream finality_data struct finality_data_t { uint32_t major_version{light_header_protocol_version_major}; uint32_t minor_version{light_header_protocol_version_minor}; diff --git a/libraries/chain/include/eosio/chain/deep_mind.hpp b/libraries/chain/include/eosio/chain/deep_mind.hpp index ba630440a4..08bffa129e 100644 --- a/libraries/chain/include/eosio/chain/deep_mind.hpp +++ b/libraries/chain/include/eosio/chain/deep_mind.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace eosio::chain { @@ -17,6 +18,7 @@ struct signed_transaction; struct packed_transaction; struct transaction_trace; struct ram_trace; +struct finality_data_t; namespace resource_limits { class resource_limits_config_object; class resource_limits_state_object; @@ -57,6 +59,7 @@ class deep_mind_handler void on_startup(chainbase::database& db, uint32_t head_block_num); void on_start_block(uint32_t block_num); void on_accepted_block(const std::shared_ptr& bsp); + void on_accepted_block_v2(block_num_type lib, const signed_block_ptr& b, const finality_data_t& fd); void on_switch_forks(const block_id_type& old_head, const block_id_type& new_head); void on_onerror(const signed_transaction& etrx); void on_onblock(const signed_transaction& trx); From fc4b48ad14040c2eb54b9591638422b488d2f9e7 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 Apr 2024 09:34:14 -0500 Subject: [PATCH 07/60] GH-16 Call deep-mind ACCEPTED_BLOCK_V2 during transition --- libraries/chain/controller.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 4b73cfb954..8fd64ed763 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3196,7 +3196,12 @@ struct controller_impl { if (auto* dm_logger = get_deep_mind_logger(false)) { auto fd = head_finality_data(); if constexpr (std::is_same_v>) { - dm_logger->on_accepted_block(head); + if (head->block->contains_header_extension(instant_finality_extension::extension_id())) { + assert(fd); + dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); + } else { + dm_logger->on_accepted_block(head); + } } else { assert(fd); dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); From 5147914cf3b2175fa0757d3e06080bba4f79d1ae Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Mon, 15 Apr 2024 10:53:44 -0400 Subject: [PATCH 08/60] Add new testcase validating that latest `set_finalizer()` call in a block wins. --- unittests/api_tests.cpp | 126 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 6 deletions(-) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 69c2718eca..86c8477099 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3919,10 +3919,11 @@ BOOST_AUTO_TEST_CASE(initial_set_finalizer_test) { try { BOOST_CHECK_GT(lib, lib_after_transition); } FC_LOG_AND_RETHROW() } -// verify that finalizers changes via set_finalizer take 2 3-cchain to take effect -// and that multiple ones can be in flight at the same time. -// ------------------------------------------------------------------------------- -BOOST_AUTO_TEST_CASE(savanna_set_finalizer_test) { try { +// --------------------------------------------------------------------- +// verify that finalizer policy change via set_finalizer take 2 3-chains +// to take effect. +// --------------------------------------------------------------------- +BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { using bls_public_key = fc::crypto::blslib::bls_public_key; validating_tester t; @@ -3979,7 +3980,7 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_test) { try { // wait till the genesis_block becomes irreversible. // The critical block is the block that makes the genesis_block irreversible // ------------------------------------------------------------------------- - signed_block_ptr critical_block = nullptr; + signed_block_ptr critical_block = nullptr; // last value of this var is the critical block while(genesis_block->block_num() > lib) critical_block = t.produce_block(); @@ -3990,7 +3991,7 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_test) { try { // wait till the first proper block becomes irreversible. Transition will be done then // ----------------------------------------------------------------------------------- - signed_block_ptr pt_block = nullptr; + signed_block_ptr pt_block = nullptr; // last value of this var is the first post-transition block while(first_proper_block->block_num() > lib) { pt_block = t.produce_block(); BOOST_REQUIRE(pt_block->is_proper_svnn_block()); @@ -4020,7 +4021,120 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_test) { try { } FC_LOG_AND_RETHROW() } +// --------------------------------------------------------------------------- +// Test correct behavior when multiple finalizer policy changes are in-flight +// at the same time. +// --------------------------------------------------------------------------- +BOOST_AUTO_TEST_CASE(savanna_set_finalizer_multiple_test) { try { + using bls_public_key = fc::crypto::blslib::bls_public_key; + validating_tester t; + + auto check_finalizer_policy = [&](const signed_block_ptr& block, + uint32_t generation, + std::span keys_span) { + auto finpol = t.active_finalizer_policy(block->calculate_id()); + BOOST_REQUIRE(!!finpol); + BOOST_CHECK_EQUAL(finpol->generation, generation); // new policy should not be active + // until after two 3-chains + BOOST_CHECK_EQUAL(keys_span.size(), finpol->finalizers.size()); + std::vector keys {keys_span.begin(), keys_span.end() }; + std::sort(keys.begin(), keys.end()); + + std::vector active_keys; + for (const auto& auth : finpol->finalizers) + active_keys.push_back(auth.public_key); + std::sort(active_keys.begin(), active_keys.end()); + for (size_t i=0; iirreversible_block().connect([&](const block_signal_params& t) { + const auto& [ block, id ] = t; + lib = block->block_num(); + lib_block = block; + }); + + t.produce_block(); + + // Create finalizer accounts + vector finalizers; + finalizers.reserve(50); + for (size_t i=0; i<50; ++i) + finalizers.emplace_back(std::string("init") + (char)('a' + i/26) + (char)('a' + i%26)); + + t.create_accounts(finalizers); + t.produce_block(); + + // activate savanna' + t.set_node_finalizers({&finalizers[0], finalizers.size()}); + + constexpr size_t finset_size = 21; + auto pubkeys0 = t.set_active_finalizers({&finalizers[0], finset_size}); + + // `genesis_block` is the first block where set_finalizers() was executed. + // It is the genesis block. + // It will include the first header extension for the instant finality. + // ----------------------------------------------------------------------- + auto genesis_block = t.produce_block(); + + // wait till the genesis_block becomes irreversible. + // The critical block is the block that makes the genesis_block irreversible + // ------------------------------------------------------------------------- + signed_block_ptr critical_block = nullptr; // last value of this var is the critical block + while(genesis_block->block_num() > lib) + critical_block = t.produce_block(); + + // Blocks after the critical block are proper IF blocks. + // ----------------------------------------------------- + auto first_proper_block = t.produce_block(); + BOOST_REQUIRE(first_proper_block->is_proper_svnn_block()); + + // wait till the first proper block becomes irreversible. Transition will be done then + // ----------------------------------------------------------------------------------- + signed_block_ptr pt_block = nullptr; // last value of this var is the first post-transition block + while(first_proper_block->block_num() > lib) { + pt_block = t.produce_block(); + BOOST_REQUIRE(pt_block->is_proper_svnn_block()); + } + + // lib must advance after 3 blocks + // ------------------------------- + t.produce_blocks(3); + BOOST_CHECK_EQUAL(lib, pt_block->block_num()); + // run set_finalizers() twice in same block, verify only latest one becomes active + // ------------------------------------------------------------------------------- + auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); + auto pubkeys2 = t.set_active_finalizers({&finalizers[2], finset_size}); + auto b0 = t.produce_block(); + check_finalizer_policy(b0, 1, pubkeys0); // new policy should only be active until after two 3-chains + t.produce_blocks(4); + auto b5 = t.produce_block(); + check_finalizer_policy(b5, 1, pubkeys0); // new policy should only be active until after two 3-chains + auto b6 = t.produce_block(); + check_finalizer_policy(b6, 2, pubkeys2); // two 3-chain - new policy pubkeys2 *should* be active + +#if 0 + // run set_finalizers(), verify it becomes active after exactly two 3-chains + // ------------------------------------------------------------------------- + auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); + auto b0 = t.produce_block(); + check_finalizer_policy(b0, 1, pubkeys0); // new policy should only be active until after two 3-chains + + t.produce_blocks(2); + auto b3 = t.produce_block(); + check_finalizer_policy(b3, 1, pubkeys0); // one 3-chain - new policy still should not be active + + t.produce_blocks(1); + auto b5 = t.produce_block(); + check_finalizer_policy(b5, 1, pubkeys0); // one 3-chain + 2 blocks - new policy still should not be active + + auto b6 = t.produce_block(); + check_finalizer_policy(b6, 2, pubkeys1); // two 3-chain - new policy *should* be active +#endif +} FC_LOG_AND_RETHROW() } void test_finality_transition(const vector& accounts, const base_tester::finalizer_policy_input& input, bool lib_advancing_expected) { validating_tester t; From 4b4eca60d96cbad631f6b2d15fbc0f86d7d76b80 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 Apr 2024 15:00:08 -0500 Subject: [PATCH 09/60] GH-3 Change default --vote-threads to 0 to be disabled by default. EOS_ASSERT if configured as a producer with vote processing disabled. --- libraries/chain/include/eosio/chain/config.hpp | 1 - libraries/chain/include/eosio/chain/controller.hpp | 2 +- plugins/chain_plugin/chain_plugin.cpp | 8 +++++++- .../include/eosio/chain_plugin/chain_plugin.hpp | 2 ++ plugins/producer_plugin/producer_plugin.cpp | 3 +++ tests/TestHarness/Cluster.py | 2 ++ 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/eosio/chain/config.hpp b/libraries/chain/include/eosio/chain/config.hpp index 74af7b59ca..9dd10a1b85 100644 --- a/libraries/chain/include/eosio/chain/config.hpp +++ b/libraries/chain/include/eosio/chain/config.hpp @@ -80,7 +80,6 @@ const static uint16_t default_max_auth_depth = 6; const static uint32_t default_sig_cpu_bill_pct = 50 * percent_1; // billable percentage of signature recovery const static uint32_t default_produce_block_offset_ms = 450; const static uint16_t default_controller_thread_pool_size = 2; -const static uint16_t default_vote_thread_pool_size = 4; const static uint32_t default_max_variable_signature_length = 16384u; const static uint32_t default_max_action_return_value_size = 256; diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index dcd1a18303..1978b1c21b 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -89,7 +89,7 @@ namespace eosio::chain { uint64_t state_guard_size = chain::config::default_state_guard_size; uint32_t sig_cpu_bill_pct = chain::config::default_sig_cpu_bill_pct; uint16_t chain_thread_pool_size = chain::config::default_controller_thread_pool_size; - uint16_t vote_thread_pool_size = chain::config::default_vote_thread_pool_size; + uint16_t vote_thread_pool_size = 0; bool read_only = false; bool force_all_checks = false; bool disable_replay_opts = false; diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 526bd18104..324e861ea4 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -158,6 +158,7 @@ class chain_plugin_impl { std::filesystem::path state_dir; bool readonly = false; flat_map loaded_checkpoints; + bool accept_votes = false; bool accept_transactions = false; bool api_accept_transactions = true; bool account_queries_enabled = false; @@ -291,7 +292,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip "Percentage of actual signature recovery cpu to bill. Whole number percentages, e.g. 50 for 50%") ("chain-threads", bpo::value()->default_value(config::default_controller_thread_pool_size), "Number of worker threads in controller thread pool") - ("vote-threads", bpo::value()->default_value(config::default_vote_thread_pool_size), + ("vote-threads", bpo::value()->default_value(0), "Number of worker threads in vote processor thread pool. Voting disabled if set to 0 (votes are not propagatged on P2P network).") ("contracts-console", bpo::bool_switch()->default_value(false), "print contract's output to console") @@ -645,6 +646,7 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) { "vote-threads ${num} must be greater than 1 or 0. " "Voting disabled if set to 0 (votes are not propagatged on P2P network).", ("num", chain_config->vote_thread_pool_size) ); + accept_votes = chain_config->vote_thread_pool_size > 0; } chain_config->sig_cpu_bill_pct = options.at("signature-cpu-billable-pct").as(); @@ -1232,6 +1234,10 @@ void chain_plugin::enable_accept_transactions() { my->enable_accept_transactions(); } +bool chain_plugin::accept_votes() const { + return my->accept_votes; +} + void chain_plugin_impl::log_guard_exception(const chain::guard_exception&e ) { if (e.code() == chain::database_guard_exception::code_value) { 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 45576abe0e..33441f652c 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -987,6 +987,8 @@ class chain_plugin : public plugin { // set true by other plugins if any plugin allows transactions bool accept_transactions() const; void enable_accept_transactions(); + // true if vote processing is enabled + bool accept_votes() const; static void handle_guard_exception(const chain::guard_exception& e); diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 91f23c1798..2a339b73ad 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1338,6 +1338,9 @@ void producer_plugin_impl::plugin_startup() { EOS_ASSERT(_producers.empty() || chain_plug->accept_transactions(), plugin_config_exception, "node cannot have any producer-name configured because no block production is possible with no [api|p2p]-accepted-transactions"); + EOS_ASSERT(_producers.empty() || chain_plug->accept_votes(), plugin_config_exception, + "node cannot have any producer-name configured because --vote-threads is defaulted to 0. Enable vote processing for block production."); + chain.set_node_finalizer_keys(_finalizer_keys); _accepted_block_connection.emplace(chain.accepted_block().connect([this](const block_signal_params& t) { diff --git a/tests/TestHarness/Cluster.py b/tests/TestHarness/Cluster.py index 292adaae21..1f4a11b7e0 100644 --- a/tests/TestHarness/Cluster.py +++ b/tests/TestHarness/Cluster.py @@ -256,6 +256,8 @@ def launch(self, pnodes=1, unstartedNodes=0, totalNodes=1, prodCount=21, topo="m if self.staging: argsArr.append("--nogen") nodeosArgs="" + if "--vote-threads" not in extraNodeosArgs: + nodeosArgs += " --vote-threads 3" if "--max-transaction-time" not in extraNodeosArgs: nodeosArgs += " --max-transaction-time -1" if "--abi-serializer-max-time-ms" not in extraNodeosArgs: From 895cef74edcb2ad6ca8ffd0399d52425fb5c7b00 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 15 Apr 2024 16:47:34 -0500 Subject: [PATCH 10/60] GH-3 --vote-threads needed for all producers --- docs/01_nodeos/02_usage/01_nodeos-configuration.md | 1 + .../03_development-environment/00_local-single-node-testnet.md | 2 +- tests/cli_test.py | 2 +- tests/gelf_test.py | 2 +- tests/p2p_no_listen_test.py | 2 ++ tests/plugin_http_api_test.py | 2 +- tests/resource_monitor_plugin_test.py | 2 +- tests/split_blocklog_replay_test.py | 2 +- tutorials/bios-boot-tutorial/bios-boot-tutorial.py | 1 + 9 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/01_nodeos/02_usage/01_nodeos-configuration.md b/docs/01_nodeos/02_usage/01_nodeos-configuration.md index 4db966a433..68ec2db596 100644 --- a/docs/01_nodeos/02_usage/01_nodeos-configuration.md +++ b/docs/01_nodeos/02_usage/01_nodeos-configuration.md @@ -22,6 +22,7 @@ The example below shows a typical usage of `nodeos` when starting a block produc ```sh nodeos \ -e -p eosio \ + --vote-threads 3 \ --data-dir /users/mydir/eosio/data \ --config-dir /users/mydir/eosio/config \ --plugin eosio::producer_plugin \ diff --git a/docs/01_nodeos/02_usage/03_development-environment/00_local-single-node-testnet.md b/docs/01_nodeos/02_usage/03_development-environment/00_local-single-node-testnet.md index e94c037d02..b326868b0f 100644 --- a/docs/01_nodeos/02_usage/03_development-environment/00_local-single-node-testnet.md +++ b/docs/01_nodeos/02_usage/03_development-environment/00_local-single-node-testnet.md @@ -32,7 +32,7 @@ Open one "terminal" window and perform the following steps: Start your own single-node blockchain with this single command: ```sh -nodeos -e -p eosio --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin +nodeos -e -p eosio --vote-threads 3 --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin ``` [[info | Nodeos Minimal Options]] diff --git a/tests/cli_test.py b/tests/cli_test.py index 74e730e60c..4505e0dad2 100755 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -355,7 +355,7 @@ def abi_file_with_nodeos_test(): os.makedirs(data_dir, exist_ok=True) walletMgr = WalletMgr(True) walletMgr.launch() - cmd = "./programs/nodeos/nodeos -e -p eosio --plugin eosio::trace_api_plugin --trace-no-abis --plugin eosio::producer_plugin --plugin eosio::producer_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::chain_plugin --plugin eosio::http_plugin --access-control-allow-origin=* --http-validate-host=false --max-transaction-time=-1 --resource-monitor-not-shutdown-on-threshold-exceeded " + "--data-dir " + data_dir + " --config-dir " + data_dir + cmd = "./programs/nodeos/nodeos -e -p eosio --vote-threads 2 --plugin eosio::trace_api_plugin --trace-no-abis --plugin eosio::producer_plugin --plugin eosio::producer_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::chain_plugin --plugin eosio::http_plugin --access-control-allow-origin=* --http-validate-host=false --max-transaction-time=-1 --resource-monitor-not-shutdown-on-threshold-exceeded " + "--data-dir " + data_dir + " --config-dir " + data_dir node = Node('localhost', 8888, nodeId, data_dir=Path(data_dir), config_dir=Path(data_dir), cmd=shlex.split(cmd), launch_time=datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S'), walletMgr=walletMgr) time.sleep(5) node.waitForBlock(1) diff --git a/tests/gelf_test.py b/tests/gelf_test.py index d0ed7f1888..21746c63cb 100755 --- a/tests/gelf_test.py +++ b/tests/gelf_test.py @@ -86,7 +86,7 @@ def gelfServer(stop): data_dir = Path(Utils.getNodeDataDir(node_id)) config_dir = Path(Utils.getNodeConfigDir(node_id)) -start_nodeos_cmd = shlex.split(f"{Utils.EosServerPath} -e -p eosio --data-dir={data_dir} --config-dir={config_dir}") +start_nodeos_cmd = shlex.split(f"{Utils.EosServerPath} -e -p eosio --vote-threads 2 --data-dir={data_dir} --config-dir={config_dir}") if os.path.exists(data_dir): shutil.rmtree(data_dir) os.makedirs(data_dir) diff --git a/tests/p2p_no_listen_test.py b/tests/p2p_no_listen_test.py index 76b3c76886..d669fcce1b 100755 --- a/tests/p2p_no_listen_test.py +++ b/tests/p2p_no_listen_test.py @@ -33,6 +33,8 @@ '-e', '-p', 'eosio', + '--vote-threads', + '3', '--p2p-listen-endpoint', '', '--plugin', diff --git a/tests/plugin_http_api_test.py b/tests/plugin_http_api_test.py index f9628847cc..687788f76c 100755 --- a/tests/plugin_http_api_test.py +++ b/tests/plugin_http_api_test.py @@ -97,7 +97,7 @@ def startEnv(self) : "--p2p-peer-address localhost:9011 --resource-monitor-not-shutdown-on-threshold-exceeded ") % (self.data_dir, self.config_dir, self.data_dir, "\'*\'", "false") nodeos_flags += category_config.nodeosArgs() - start_nodeos_cmd = ("%s -e -p eosio %s %s ") % (Utils.EosServerPath, nodeos_plugins, nodeos_flags) + start_nodeos_cmd = ("%s -e -p eosio --vote-threads 2 %s %s ") % (Utils.EosServerPath, nodeos_plugins, nodeos_flags) self.nodeos = Node(TestHelper.LOCAL_HOST, TestHelper.DEFAULT_PORT, self.node_id, self.data_dir, self.config_dir, shlex.split(start_nodeos_cmd), walletMgr=self.keosd) time.sleep(self.sleep_s*2) self.nodeos.waitForBlock(1, timeout=30) diff --git a/tests/resource_monitor_plugin_test.py b/tests/resource_monitor_plugin_test.py index 4370d529dc..0036bf188c 100755 --- a/tests/resource_monitor_plugin_test.py +++ b/tests/resource_monitor_plugin_test.py @@ -77,7 +77,7 @@ def prepareDirectories(): def runNodeos(extraNodeosArgs, myTimeout): """Startup nodeos, wait for timeout (before forced shutdown) and collect output.""" if debug: Print("Launching nodeos process.") - cmd="programs/nodeos/nodeos --config-dir rsmStaging/etc -e -p eosio --plugin eosio::chain_api_plugin --data-dir " + dataDir + " " + cmd="programs/nodeos/nodeos --config-dir rsmStaging/etc -e -p eosio --vote-threads 2 --plugin eosio::chain_api_plugin --data-dir " + dataDir + " " cmd=cmd + extraNodeosArgs if debug: Print("cmd: %s" % (cmd)) diff --git a/tests/split_blocklog_replay_test.py b/tests/split_blocklog_replay_test.py index ae7c24ffd8..dc9c8d178e 100755 --- a/tests/split_blocklog_replay_test.py +++ b/tests/split_blocklog_replay_test.py @@ -17,7 +17,7 @@ os.makedirs(config_dir) try: - start_nodeos_cmd = f"{Utils.EosServerPath} -e -p eosio --data-dir={data_dir} --config-dir={config_dir} --blocks-log-stride 10" \ + start_nodeos_cmd = f"{Utils.EosServerPath} -e -p eosio --vote-threads 2 --data-dir={data_dir} --config-dir={config_dir} --blocks-log-stride 10" \ " --plugin=eosio::http_plugin --plugin=eosio::chain_api_plugin --http-server-address=localhost:8888" nodeos.launchCmd(start_nodeos_cmd, node_id) diff --git a/tutorials/bios-boot-tutorial/bios-boot-tutorial.py b/tutorials/bios-boot-tutorial/bios-boot-tutorial.py index 8822546307..474d34c52a 100755 --- a/tutorials/bios-boot-tutorial/bios-boot-tutorial.py +++ b/tutorials/bios-boot-tutorial/bios-boot-tutorial.py @@ -118,6 +118,7 @@ def startNode(nodeIndex, account): ' --p2p-max-nodes-per-host ' + str(maxClients) + ' --enable-stale-production' ' --producer-name ' + account['name'] + + ' --vote-threads 3' ' --signature-provider ' + account['pub'] + '=KEY:' + account['pvt'] + ' --plugin eosio::http_plugin' ' --plugin eosio::chain_api_plugin' From b0ee7411dd84ae9cdcbdfb5db136ad559889ccb4 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Mon, 15 Apr 2024 17:48:24 -0400 Subject: [PATCH 11/60] Fix empty action_mroot in Transition blocks --- libraries/chain/block_state.cpp | 10 ++++++++++ libraries/chain/controller.cpp | 17 ++++++++--------- .../chain/include/eosio/chain/block_state.hpp | 9 +++++++++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 4735e81de4..ce64d661e1 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -25,6 +25,14 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con } } +block_state::block_state(const block_header_state& prev, signed_block_ptr b, const protocol_feature_set& pfs, + const validator_t& validator, bool skip_validate_signee, + const digest_type& action_mroot_savanna) + : block_state(prev, b, pfs, validator, skip_validate_signee) +{ + action_mroot = action_mroot_savanna; +} + block_state::block_state(const block_header_state& bhs, deque&& trx_metas, deque&& trx_receipts, @@ -250,6 +258,8 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const { valid_t block_state::new_valid(const block_header_state& next_bhs, const digest_type& action_mroot, const digest_type& strong_digest) const { assert(valid); assert(next_bhs.core.last_final_block_num() >= core.last_final_block_num()); + assert(action_mroot != digest_type() ); + assert(strong_digest != digest_type() ); // Copy parent's validation_tree and validation_mroots. auto start = next_bhs.core.last_final_block_num() - core.last_final_block_num(); diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 613a7fb16b..42e4bdfd84 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1348,12 +1348,14 @@ struct controller_impl { block_state_ptr prev = forkdb.root(); assert(prev); for (auto bitr = legacy_branch.rbegin(); bitr != legacy_branch.rend(); ++bitr) { + assert((*bitr)->action_mroot_savanna.has_value()); const bool skip_validate_signee = true; // validated already auto new_bsp = std::make_shared( *prev, (*bitr)->block, protocol_features.get_protocol_feature_set(), - validator_t{}, skip_validate_signee); + validator_t{}, skip_validate_signee, + *((*bitr)->action_mroot_savanna)); transition_add_to_savanna_fork_db(forkdb, *bitr, new_bsp, prev); prev = new_bsp; } @@ -1529,11 +1531,13 @@ struct controller_impl { prev = block_state::create_if_genesis_block(*legacy_branch[0]); } else { const auto& bspl = legacy_branch[i]; + assert(bspl->action_mroot_savanna.has_value()); auto new_bsp = std::make_shared( *prev, bspl->block, protocol_features.get_protocol_feature_set(), - validator_t{}, skip_validate_signee); + validator_t{}, skip_validate_signee, + *(bspl->action_mroot_savanna)); // legacy_branch is from head, all should be validated assert(bspl->action_mroot_savanna); // Create the valid structure for producing @@ -4439,17 +4443,12 @@ struct controller_impl { const bool skip_validate_signee = true; // validated already for (; bitr != legacy_branch.rend(); ++bitr) { + assert((*bitr)->action_mroot_savanna.has_value()); auto new_bsp = std::make_shared( *prev, (*bitr)->block, protocol_features.get_protocol_feature_set(), - validator_t{}, skip_validate_signee); - - // We only need action_mroot of the last block for finality_data - if ((bitr + 1) == legacy_branch.rend()) { - assert((*bitr)->action_mroot_savanna); - new_bsp->action_mroot = *((*bitr)->action_mroot_savanna); - } + validator_t{}, skip_validate_signee, *((*bitr)->action_mroot_savanna)); prev = new_bsp; } diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index a212f1655f..a118becf96 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -149,6 +149,15 @@ struct block_state : public block_header_state { // block_header_state provi const block_signing_authority& valid_block_signing_authority, const digest_type& action_mroot); + // This is used during transition to Savanna to construct a Savanna block state from + // a Legacy block state, specifically for building action_mroot from action_mroot_savanna. + block_state(const block_header_state& prev, + signed_block_ptr b, + const protocol_feature_set& pfs, + const validator_t& validator, + bool skip_validate_signee, + const digest_type& action_mroot_savanna); + static std::shared_ptr create_if_genesis_block(const block_state_legacy& bsp); explicit block_state(snapshot_detail::snapshot_block_state_v7&& sbs); From fc2d50cfcaa47526ed6a2d0315464278a750fb40 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Mon, 15 Apr 2024 21:56:20 -0400 Subject: [PATCH 12/60] Use empty() instead of digest_type() to check whether a digest is empty --- libraries/chain/block_state.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index ce64d661e1..6bfdae2137 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -258,8 +258,8 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const { valid_t block_state::new_valid(const block_header_state& next_bhs, const digest_type& action_mroot, const digest_type& strong_digest) const { assert(valid); assert(next_bhs.core.last_final_block_num() >= core.last_final_block_num()); - assert(action_mroot != digest_type() ); - assert(strong_digest != digest_type() ); + assert(!action_mroot.empty()); + assert(!strong_digest.empty()); // Copy parent's validation_tree and validation_mroots. auto start = next_bhs.core.last_final_block_num() - core.last_final_block_num(); From 68d4e8d18c0ff474d151af973ffc773c645c9502 Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Mon, 15 Apr 2024 22:23:18 -0400 Subject: [PATCH 13/60] fix a couple compile warnings --- .../chain_plugin/test/test_trx_finality_status_processing.cpp | 2 -- unittests/producer_schedule_if_tests.cpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/chain_plugin/test/test_trx_finality_status_processing.cpp b/plugins/chain_plugin/test/test_trx_finality_status_processing.cpp index 5bff8f075c..a814060303 100644 --- a/plugins/chain_plugin/test/test_trx_finality_status_processing.cpp +++ b/plugins/chain_plugin/test/test_trx_finality_status_processing.cpp @@ -101,8 +101,6 @@ chain::transaction_trace_ptr make_transaction_trace( const packed_transaction_pt auto make_block( uint32_t block_num ) { static uint64_t unique_num = 0; ++unique_num; - chain::block_id_type block_id = make_block_id(block_num); - block_id._hash[3] = unique_num; name producer = "brianj"_n; chain::signed_block_ptr block = std::make_shared(); block->producer = producer; diff --git a/unittests/producer_schedule_if_tests.cpp b/unittests/producer_schedule_if_tests.cpp index dd24d501dd..60833bb359 100644 --- a/unittests/producer_schedule_if_tests.cpp +++ b/unittests/producer_schedule_if_tests.cpp @@ -128,7 +128,7 @@ BOOST_FIXTURE_TEST_CASE( proposer_policy_progression_test, validating_tester ) t vector prev_sch = { producer_authority{"eosio"_n, block_signing_authority_v0{1, {{get_public_key("eosio"_n, "active"), 1}}}}}; BOOST_CHECK_EQUAL( true, compare_schedules( prev_sch, control->active_producers() ) ); - BOOST_CHECK_EQUAL( 0, control->active_producers().version ); + BOOST_CHECK_EQUAL( 0u, control->active_producers().version ); // set a new proposer policy sch1 set_producers( {"alice"_n} ); @@ -140,7 +140,7 @@ BOOST_FIXTURE_TEST_CASE( proposer_policy_progression_test, validating_tester ) t produce_blocks(config::producer_repetitions); // sch1 cannot become active before one round of production - BOOST_CHECK_EQUAL( 0, control->active_producers().version ); + BOOST_CHECK_EQUAL( 0u, control->active_producers().version ); BOOST_CHECK_EQUAL( true, compare_schedules( prev_sch, control->active_producers() ) ); // set another ploicy to have multiple pending different active time policies From c3bdd8aef515c4eb83825d3e503124ee6aef0df1 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 07:23:45 -0500 Subject: [PATCH 14/60] GH-12 Add vote threads needed for unittests --- libraries/testing/include/eosio/testing/tester.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/testing/include/eosio/testing/tester.hpp b/libraries/testing/include/eosio/testing/tester.hpp index 6ca6dbf922..210ff4b67a 100644 --- a/libraries/testing/include/eosio/testing/tester.hpp +++ b/libraries/testing/include/eosio/testing/tester.hpp @@ -426,6 +426,7 @@ namespace eosio { namespace testing { cfg.state_guard_size = 0; cfg.contracts_console = true; cfg.eosvmoc_config.cache_size = 1024*1024*8; + cfg.vote_thread_pool_size = 3; // don't enforce OC compilation subject limits for tests, // particularly EOS EVM tests may run over those limits From e4010d212f9f1352d9d92518c2d3e4e2260cad6b Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 07:24:01 -0500 Subject: [PATCH 15/60] GH-12 Add vote threads --- tests/test_read_only_trx.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_read_only_trx.cpp b/tests/test_read_only_trx.cpp index 56b684da86..a9ee294106 100644 --- a/tests/test_read_only_trx.cpp +++ b/tests/test_read_only_trx.cpp @@ -108,7 +108,7 @@ void test_trxs_common(std::vector& specific_args, bool test_disable fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); std::vector argv = { "test", // dummy executible name - "-p", "eosio", "-e", // actual arguments follow + "-p", "eosio", "--vote-threads", "3", "-e", // actual arguments follow "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(), "--max-transaction-time=100", From bf8fe18aa2e7c15c1839ebcadddd8ccb629d2498 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 07:33:26 -0500 Subject: [PATCH 16/60] GH-12 Add vote threads to tests --- plugins/producer_plugin/test/test_disallow_delayed_trx.cpp | 2 +- plugins/producer_plugin/test/test_options.cpp | 2 +- plugins/producer_plugin/test/test_trx_full.cpp | 2 +- tests/db_modes_test.sh | 2 +- tests/test_snapshot_scheduler.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/producer_plugin/test/test_disallow_delayed_trx.cpp b/plugins/producer_plugin/test/test_disallow_delayed_trx.cpp index 3723970ffc..df850afd48 100644 --- a/plugins/producer_plugin/test/test_disallow_delayed_trx.cpp +++ b/plugins/producer_plugin/test/test_disallow_delayed_trx.cpp @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(delayed_trx) { fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); std::vector argv = {"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(), - "-p", "eosio", "-e", "--disable-subjective-p2p-billing=true" }; + "-p", "eosio", "-e", "--vote-threads", "3", "--disable-subjective-p2p-billing=true" }; app->initialize( argv.size(), (char**) &argv[0] ); app->startup(); plugin_promise.set_value( diff --git a/plugins/producer_plugin/test/test_options.cpp b/plugins/producer_plugin/test/test_options.cpp index 3fe429b6a9..072cc27ec5 100644 --- a/plugins/producer_plugin/test/test_options.cpp +++ b/plugins/producer_plugin/test/test_options.cpp @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(state_dir) { "--data-dir", temp_dir_str.c_str(), "--state-dir", custom_state_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(), - "-p", "eosio", "-e" }; + "-p", "eosio", "--vote-threads", "3", "-e" }; app->initialize( argv.size(), (char**) &argv[0] ); app->startup(); plugin_promise.set_value( {app->find_plugin(), app->find_plugin()} ); diff --git a/plugins/producer_plugin/test/test_trx_full.cpp b/plugins/producer_plugin/test/test_trx_full.cpp index 1a51570515..e827f44d6e 100644 --- a/plugins/producer_plugin/test/test_trx_full.cpp +++ b/plugins/producer_plugin/test/test_trx_full.cpp @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(producer) { fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); std::vector argv = {"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(), - "-p", "eosio", "-e", "--disable-subjective-p2p-billing=true" }; + "-p", "eosio", "-e", "--vote-threads", "3", "--disable-subjective-p2p-billing=true" }; app->initialize( argv.size(), (char**) &argv[0] ); app->startup(); plugin_promise.set_value( diff --git a/tests/db_modes_test.sh b/tests/db_modes_test.sh index 6b5fd20fe3..a105ad1634 100755 --- a/tests/db_modes_test.sh +++ b/tests/db_modes_test.sh @@ -32,7 +32,7 @@ EOSIO_STUFF_DIR=$(mktemp -d) trap "rm -rf $EOSIO_STUFF_DIR" EXIT NODEOS_LAUNCH_PARAMS="./programs/nodeos/nodeos --resource-monitor-not-shutdown-on-threshold-exceeded -d $EOSIO_STUFF_DIR --config-dir $EOSIO_STUFF_DIR \ --chain-state-db-size-mb 8 --chain-state-db-guard-size-mb 0 \ --e -peosio" +-e -peosio --vote-threads 3" run_nodeos() { if (( $VERBOSE == 0 )); then diff --git a/tests/test_snapshot_scheduler.cpp b/tests/test_snapshot_scheduler.cpp index a03add72bb..2ebf170dc1 100644 --- a/tests/test_snapshot_scheduler.cpp +++ b/tests/test_snapshot_scheduler.cpp @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(snapshot_scheduler_test) { fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); std::vector argv = {"test", "--data-dir", temp.c_str(), "--config-dir", temp.c_str(), - "-p", "eosio", "-e"}; + "-p", "eosio", "--vote-threads", "3", "-e"}; app->initialize(argv.size(), (char**) &argv[0]); app->startup(); From 57f8fde94cf0565b995ea3bd3c18e0cda51e5ac3 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 07:45:26 -0500 Subject: [PATCH 17/60] GH-16 Refactor to remove literal constexpr --- libraries/chain/controller.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 8fd64ed763..062241ac2a 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3192,22 +3192,21 @@ struct controller_impl { apply_s(chain_head, [&](const auto& head) { create_and_send_vote_msg(head); }); } - apply(chain_head, [&](const auto& head) { - if (auto* dm_logger = get_deep_mind_logger(false)) { - auto fd = head_finality_data(); - if constexpr (std::is_same_v>) { - if (head->block->contains_header_extension(instant_finality_extension::extension_id())) { - assert(fd); - dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); - } else { - dm_logger->on_accepted_block(head); - } - } else { - assert(fd); - dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); - } + if (auto* dm_logger = get_deep_mind_logger(false)) { + auto fd = head_finality_data(); + apply_l(chain_head, [&](const auto& head) { + if (head->block->contains_header_extension(instant_finality_extension::extension_id())) { + assert(fd); + dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); + } else { + dm_logger->on_accepted_block(head); } - }); + }); + apply_s(chain_head, [&](const auto& head) { + assert(fd); + dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); + }); + } if (s == controller::block_status::incomplete) { const auto& id = chain_head.id(); From 0a28839e23010d938c16884aefe871395a218b2e Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 16 Apr 2024 09:23:42 -0400 Subject: [PATCH 18/60] Rename block_state constructor with action_mroot parameter to create_transition_block --- libraries/chain/block_state.cpp | 20 +++++++++++-------- libraries/chain/controller.cpp | 6 +++--- .../chain/include/eosio/chain/block_state.hpp | 18 ++++++++--------- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 6bfdae2137..c1263a91b5 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -25,14 +25,6 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con } } -block_state::block_state(const block_header_state& prev, signed_block_ptr b, const protocol_feature_set& pfs, - const validator_t& validator, bool skip_validate_signee, - const digest_type& action_mroot_savanna) - : block_state(prev, b, pfs, validator, skip_validate_signee) -{ - action_mroot = action_mroot_savanna; -} - block_state::block_state(const block_header_state& bhs, deque&& trx_metas, deque&& trx_receipts, @@ -117,6 +109,18 @@ block_state_ptr block_state::create_if_genesis_block(const block_state_legacy& b return result_ptr; } +block_state_ptr block_state::create_transition_block( + const block_header_state& prev, + signed_block_ptr b, + const protocol_feature_set& pfs, + const validator_t& validator, + bool skip_validate_signee, + const std::optional& action_mroot_savanna) { + auto result_ptr = std::make_shared(prev, b, pfs, validator, skip_validate_signee); + result_ptr->action_mroot = action_mroot_savanna.has_value() ? *action_mroot_savanna : digest_type(); + return result_ptr; +} + block_state::block_state(snapshot_detail::snapshot_block_state_v7&& sbs) : block_header_state { .block_id = sbs.block_id, diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 42e4bdfd84..caf08d0240 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1350,7 +1350,7 @@ struct controller_impl { for (auto bitr = legacy_branch.rbegin(); bitr != legacy_branch.rend(); ++bitr) { assert((*bitr)->action_mroot_savanna.has_value()); const bool skip_validate_signee = true; // validated already - auto new_bsp = std::make_shared( + auto new_bsp = block_state::create_transition_block( *prev, (*bitr)->block, protocol_features.get_protocol_feature_set(), @@ -1532,7 +1532,7 @@ struct controller_impl { } else { const auto& bspl = legacy_branch[i]; assert(bspl->action_mroot_savanna.has_value()); - auto new_bsp = std::make_shared( + auto new_bsp = block_state::create_transition_block( *prev, bspl->block, protocol_features.get_protocol_feature_set(), @@ -4444,7 +4444,7 @@ struct controller_impl { for (; bitr != legacy_branch.rend(); ++bitr) { assert((*bitr)->action_mroot_savanna.has_value()); - auto new_bsp = std::make_shared( + auto new_bsp = block_state::create_transition_block( *prev, (*bitr)->block, protocol_features.get_protocol_feature_set(), diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index a118becf96..69ab1c7e3f 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -149,17 +149,17 @@ struct block_state : public block_header_state { // block_header_state provi const block_signing_authority& valid_block_signing_authority, const digest_type& action_mroot); - // This is used during transition to Savanna to construct a Savanna block state from - // a Legacy block state, specifically for building action_mroot from action_mroot_savanna. - block_state(const block_header_state& prev, - signed_block_ptr b, - const protocol_feature_set& pfs, - const validator_t& validator, - bool skip_validate_signee, - const digest_type& action_mroot_savanna); - static std::shared_ptr create_if_genesis_block(const block_state_legacy& bsp); + // Constructs a Transition Savanna block state from a Legacy block state. + static std::shared_ptr create_transition_block( + const block_header_state& prev, + signed_block_ptr b, + const protocol_feature_set& pfs, + const validator_t& validator, + bool skip_validate_signee, + const std::optional& action_mroot_savanna); + explicit block_state(snapshot_detail::snapshot_block_state_v7&& sbs); void sign(const signer_callback_type& signer, const block_signing_authority& valid_block_signing_authority); From ded357d4c663e5c17717863b2ad8d884670f2969 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 16 Apr 2024 09:36:35 -0400 Subject: [PATCH 19/60] adjust the assert for action_mroot_savanna --- libraries/chain/controller.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index caf08d0240..2540e65318 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1348,14 +1348,14 @@ struct controller_impl { block_state_ptr prev = forkdb.root(); assert(prev); for (auto bitr = legacy_branch.rbegin(); bitr != legacy_branch.rend(); ++bitr) { - assert((*bitr)->action_mroot_savanna.has_value()); + assert(read_mode == db_read_mode::IRREVERSIBLE || (*bitr)->action_mroot_savanna.has_value()); const bool skip_validate_signee = true; // validated already auto new_bsp = block_state::create_transition_block( *prev, (*bitr)->block, protocol_features.get_protocol_feature_set(), validator_t{}, skip_validate_signee, - *((*bitr)->action_mroot_savanna)); + (*bitr)->action_mroot_savanna); transition_add_to_savanna_fork_db(forkdb, *bitr, new_bsp, prev); prev = new_bsp; } @@ -1532,12 +1532,13 @@ struct controller_impl { } else { const auto& bspl = legacy_branch[i]; assert(bspl->action_mroot_savanna.has_value()); + assert(read_mode == db_read_mode::IRREVERSIBLE || bspl->action_mroot_savanna.has_value()); auto new_bsp = block_state::create_transition_block( *prev, bspl->block, protocol_features.get_protocol_feature_set(), validator_t{}, skip_validate_signee, - *(bspl->action_mroot_savanna)); + bspl->action_mroot_savanna); // legacy_branch is from head, all should be validated assert(bspl->action_mroot_savanna); // Create the valid structure for producing @@ -4444,11 +4445,12 @@ struct controller_impl { for (; bitr != legacy_branch.rend(); ++bitr) { assert((*bitr)->action_mroot_savanna.has_value()); + assert(read_mode == db_read_mode::IRREVERSIBLE || (*bitr)->action_mroot_savanna.has_value()); auto new_bsp = block_state::create_transition_block( *prev, (*bitr)->block, protocol_features.get_protocol_feature_set(), - validator_t{}, skip_validate_signee, *((*bitr)->action_mroot_savanna)); + validator_t{}, skip_validate_signee, (*bitr)->action_mroot_savanna); prev = new_bsp; } From ee2ca707bacdab63c1bbc130212e3491e00cf903 Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:11:04 -0400 Subject: [PATCH 20/60] remove another unused variable --- .../chain_plugin/test/test_trx_finality_status_processing.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/chain_plugin/test/test_trx_finality_status_processing.cpp b/plugins/chain_plugin/test/test_trx_finality_status_processing.cpp index a814060303..39ef3c8419 100644 --- a/plugins/chain_plugin/test/test_trx_finality_status_processing.cpp +++ b/plugins/chain_plugin/test/test_trx_finality_status_processing.cpp @@ -99,8 +99,6 @@ chain::transaction_trace_ptr make_transaction_trace( const packed_transaction_pt } auto make_block( uint32_t block_num ) { - static uint64_t unique_num = 0; - ++unique_num; name producer = "brianj"_n; chain::signed_block_ptr block = std::make_shared(); block->producer = producer; From 8bfbe0a355b43de8f656054af0df3a7efd99f45b Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 11:40:42 -0500 Subject: [PATCH 21/60] GH-3 Modify named_thread_pool to be a no-op if given 0 for num_threads. --- libraries/chain/controller.cpp | 11 +++++------ .../chain/include/eosio/chain/thread_utils.hpp | 16 ++++++++++------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index e79fccfcd8..9e74d85e63 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1199,16 +1199,15 @@ struct controller_impl { my_finalizers(fc::time_point::now(), cfg.finalizers_dir / "safety.dat"), wasmif( conf.wasm_runtime, conf.eosvmoc_tierup, db, conf.state_dir, conf.eosvmoc_config, !conf.profile_accounts.empty() ) { + assert(cfg.chain_thread_pool_size > 0); thread_pool.start( cfg.chain_thread_pool_size, [this]( const fc::exception& e ) { elog( "Exception in chain thread pool, exiting: ${e}", ("e", e.to_detail_string()) ); if( shutdown ) shutdown(); } ); - if (cfg.vote_thread_pool_size > 0) { - vote_processor.start(cfg.vote_thread_pool_size, [this]( const fc::exception& e ) { - elog( "Exception in vote thread pool, exiting: ${e}", ("e", e.to_detail_string()) ); - if( shutdown ) shutdown(); - } ); - } + vote_processor.start(cfg.vote_thread_pool_size, [this]( const fc::exception& e ) { + elog( "Exception in vote thread pool, exiting: ${e}", ("e", e.to_detail_string()) ); + if( shutdown ) shutdown(); + } ); set_activation_handler(); set_activation_handler(); diff --git a/libraries/chain/include/eosio/chain/thread_utils.hpp b/libraries/chain/include/eosio/chain/thread_utils.hpp index b4e4d5a673..3838b380e4 100644 --- a/libraries/chain/include/eosio/chain/thread_utils.hpp +++ b/libraries/chain/include/eosio/chain/thread_utils.hpp @@ -108,7 +108,7 @@ namespace eosio { namespace chain { /// Blocks until all threads are created and completed their init function, or an exception is thrown /// during thread startup or an init function. Exceptions thrown during these stages are rethrown from start() /// but some threads might still have been started. Calling stop() after such a failure is safe. - /// @param num_threads is number of threads spawned + /// @param num_threads is number of threads spawned, if 0 then no threads are spawned and stop() is a no-op. /// @param on_except is the function to call if io_context throws an exception, is called from thread pool thread. /// if an empty function then logs and rethrows exception on thread which will terminate. Not called /// for exceptions during the init function (such exceptions are rethrown from start()) @@ -116,6 +116,8 @@ namespace eosio { namespace chain { /// @throw assert_exception if already started and not stopped. void start( size_t num_threads, on_except_t on_except, init_t init = {} ) { FC_ASSERT( !_ioc_work, "Thread pool already started" ); + if (num_threads == 0) + return; _ioc_work.emplace( boost::asio::make_work_guard( _ioc ) ); _ioc.restart(); _thread_pool.reserve( num_threads ); @@ -142,12 +144,14 @@ namespace eosio { namespace chain { /// destroy work guard, stop io_context, join thread_pool void stop() { - _ioc_work.reset(); - _ioc.stop(); - for( auto& t : _thread_pool ) { - t.join(); + if (_thread_pool.size() > 0) { + _ioc_work.reset(); + _ioc.stop(); + for( auto& t : _thread_pool ) { + t.join(); + } + _thread_pool.clear(); } - _thread_pool.clear(); } private: From 117f02d205ce5f114b150812d6319f2951f74cb2 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 11:57:29 -0500 Subject: [PATCH 22/60] GH-3 Check for stopped in inner loop --- libraries/chain/include/eosio/chain/vote_processor.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 793705acbc..68f599ab0a 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -161,6 +161,8 @@ class vote_processor_t { g.lock(); } for (auto i = idx.begin(); i != idx.end();) { + if (stopped) + break; auto& vt = *i; block_state_ptr bsp = fetch_block_func(vt.id()); if (bsp) { From 027d2780b4979cdba2f5a56ef1e8e9a8d4211901 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 12:04:46 -0500 Subject: [PATCH 23/60] GH-3 Add better descriptions --- libraries/chain/controller.cpp | 6 +++--- libraries/chain/include/eosio/chain/controller.hpp | 1 + libraries/chain/include/eosio/chain/vote_processor.hpp | 5 ++++- plugins/chain_plugin/chain_plugin.cpp | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 9e74d85e63..c172e1dc08 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1393,8 +1393,8 @@ struct controller_impl { block_num_type latest_known_lib_num() const { block_id_type irreversible_block_id = if_irreversible_block_id.load(); - block_num_type if_lib_num = block_header::num_from_id(irreversible_block_id); - return if_lib_num > 0 ? if_lib_num : fork_db_head_irreversible_blocknum(); + block_num_type savanna_lib_num = block_header::num_from_id(irreversible_block_id); + return savanna_lib_num > 0 ? savanna_lib_num : fork_db_head_irreversible_blocknum(); } void log_irreversible() { @@ -3606,7 +3606,7 @@ struct controller_impl { // net plugin subscribed to this signal. it will broadcast the vote message on receiving the signal emit(voted_block, std::tuple{uint32_t{0}, vote_status::success, std::cref(vote)}); - // also aggregate our own vote into the pending_qc for this block. + // also aggregate our own vote into the pending_qc for this block, 0 connection_id indicates our own vote process_vote_message(0, vote); }); } diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 1978b1c21b..490b69b540 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -58,6 +58,7 @@ namespace eosio::chain { using trx_meta_cache_lookup = std::function; using block_signal_params = std::tuple; + // connection_id, vote result status, vote_message processed using vote_signal_params = std::tuple; enum class db_read_mode { diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 68f599ab0a..47489b207e 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -15,7 +15,9 @@ namespace eosio { namespace chain { * Process votes in a dedicated thread pool. */ class vote_processor_t { - static constexpr size_t max_votes_per_connection = 2500; // 3000 is less than 1MB per connection + // Even 3000 vote structs are less than 1MB per connection. + // 2500 is should never be reached unless a specific connection is sending garbage. + static constexpr size_t max_votes_per_connection = 2500; static constexpr std::chrono::milliseconds block_wait_time{10}; struct by_block_num; @@ -209,6 +211,7 @@ class vote_processor_t { } void process_vote_message(uint32_t connection_id, const vote_message_ptr& msg) { + assert(msg); boost::asio::post(thread_pool.get_executor(), [this, connection_id, msg] { std::unique_lock g(mtx); if (++num_messages[connection_id] > max_votes_per_connection) { diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 324e861ea4..fcc202ac6b 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -293,7 +293,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip ("chain-threads", bpo::value()->default_value(config::default_controller_thread_pool_size), "Number of worker threads in controller thread pool") ("vote-threads", bpo::value()->default_value(0), - "Number of worker threads in vote processor thread pool. Voting disabled if set to 0 (votes are not propagatged on P2P network).") + "Number of worker threads in vote processor thread pool. If set to 0, voting disabled, votes are not propagatged on P2P network.") ("contracts-console", bpo::bool_switch()->default_value(false), "print contract's output to console") ("deep-mind", bpo::bool_switch()->default_value(false), @@ -643,7 +643,7 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) { if( options.count( "vote-threads" )) { chain_config->vote_thread_pool_size = options.at( "vote-threads" ).as(); EOS_ASSERT( chain_config->vote_thread_pool_size > 1 || chain_config->vote_thread_pool_size == 0, plugin_config_exception, - "vote-threads ${num} must be greater than 1 or 0. " + "vote-threads ${num} must be greater than 1, or equal to 0 to disable. " "Voting disabled if set to 0 (votes are not propagatged on P2P network).", ("num", chain_config->vote_thread_pool_size) ); accept_votes = chain_config->vote_thread_pool_size > 0; From 8f26a50e4f3864dd5ac348ef5da45b27e410894b Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 13:22:24 -0500 Subject: [PATCH 24/60] GH-3 Default vote-threads to 4 when a block producer --- docs/01_nodeos/02_usage/01_nodeos-configuration.md | 1 - .../00_local-single-node-testnet.md | 2 +- libraries/chain/include/eosio/chain/config.hpp | 1 + libraries/chain/include/eosio/chain/thread_utils.hpp | 1 + .../chain/include/eosio/chain/vote_processor.hpp | 11 +++++++++-- plugins/chain_plugin/chain_plugin.cpp | 11 +++++++---- plugins/producer_plugin/producer_plugin.cpp | 3 --- .../test/test_disallow_delayed_trx.cpp | 2 +- plugins/producer_plugin/test/test_options.cpp | 2 +- plugins/producer_plugin/test/test_trx_full.cpp | 2 +- tests/TestHarness/Cluster.py | 2 -- tests/cli_test.py | 2 +- tests/db_modes_test.sh | 2 +- tests/gelf_test.py | 2 +- tests/p2p_no_listen_test.py | 2 -- tests/plugin_http_api_test.py | 2 +- tests/resource_monitor_plugin_test.py | 2 +- tests/split_blocklog_replay_test.py | 2 +- tests/test_read_only_trx.cpp | 2 +- tests/test_snapshot_scheduler.cpp | 2 +- tutorials/bios-boot-tutorial/bios-boot-tutorial.py | 1 - 21 files changed, 30 insertions(+), 27 deletions(-) diff --git a/docs/01_nodeos/02_usage/01_nodeos-configuration.md b/docs/01_nodeos/02_usage/01_nodeos-configuration.md index 68ec2db596..4db966a433 100644 --- a/docs/01_nodeos/02_usage/01_nodeos-configuration.md +++ b/docs/01_nodeos/02_usage/01_nodeos-configuration.md @@ -22,7 +22,6 @@ The example below shows a typical usage of `nodeos` when starting a block produc ```sh nodeos \ -e -p eosio \ - --vote-threads 3 \ --data-dir /users/mydir/eosio/data \ --config-dir /users/mydir/eosio/config \ --plugin eosio::producer_plugin \ diff --git a/docs/01_nodeos/02_usage/03_development-environment/00_local-single-node-testnet.md b/docs/01_nodeos/02_usage/03_development-environment/00_local-single-node-testnet.md index b326868b0f..e94c037d02 100644 --- a/docs/01_nodeos/02_usage/03_development-environment/00_local-single-node-testnet.md +++ b/docs/01_nodeos/02_usage/03_development-environment/00_local-single-node-testnet.md @@ -32,7 +32,7 @@ Open one "terminal" window and perform the following steps: Start your own single-node blockchain with this single command: ```sh -nodeos -e -p eosio --vote-threads 3 --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin +nodeos -e -p eosio --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin ``` [[info | Nodeos Minimal Options]] diff --git a/libraries/chain/include/eosio/chain/config.hpp b/libraries/chain/include/eosio/chain/config.hpp index 9dd10a1b85..74af7b59ca 100644 --- a/libraries/chain/include/eosio/chain/config.hpp +++ b/libraries/chain/include/eosio/chain/config.hpp @@ -80,6 +80,7 @@ const static uint16_t default_max_auth_depth = 6; const static uint32_t default_sig_cpu_bill_pct = 50 * percent_1; // billable percentage of signature recovery const static uint32_t default_produce_block_offset_ms = 450; const static uint16_t default_controller_thread_pool_size = 2; +const static uint16_t default_vote_thread_pool_size = 4; const static uint32_t default_max_variable_signature_length = 16384u; const static uint32_t default_max_action_return_value_size = 256; diff --git a/libraries/chain/include/eosio/chain/thread_utils.hpp b/libraries/chain/include/eosio/chain/thread_utils.hpp index 3838b380e4..81dbb24dbe 100644 --- a/libraries/chain/include/eosio/chain/thread_utils.hpp +++ b/libraries/chain/include/eosio/chain/thread_utils.hpp @@ -143,6 +143,7 @@ namespace eosio { namespace chain { } /// destroy work guard, stop io_context, join thread_pool + /// not thread safe, expected to only be called from thread that called start() void stop() { if (_thread_pool.size() > 0) { _ioc_work.reset(); diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 47489b207e..8d8417cc16 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -58,7 +58,7 @@ class vote_processor_t { std::map num_messages; std::atomic lib{0}; - std::atomic stopped{false}; + std::atomic stopped{true}; named_thread_pool thread_pool; private: @@ -136,7 +136,10 @@ class vote_processor_t { } void start(size_t num_threads, decltype(thread_pool)::on_except_t&& on_except) { - assert(num_threads > 1); // need at least two as one is used for coordinatation + if (num_threads == 0) + return; + + stopped = false; thread_pool.start( num_threads, std::move(on_except)); // one coordinator thread @@ -210,7 +213,11 @@ class vote_processor_t { lib = block_num; } + /// called from net threads and controller's thread pool + /// msg is ignored vote_processor not start()ed void process_vote_message(uint32_t connection_id, const vote_message_ptr& msg) { + if (stopped) + return; assert(msg); boost::asio::post(thread_pool.get_executor(), [this, connection_id, msg] { std::unique_lock g(mtx); diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index fcc202ac6b..b34039e096 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -292,8 +292,8 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip "Percentage of actual signature recovery cpu to bill. Whole number percentages, e.g. 50 for 50%") ("chain-threads", bpo::value()->default_value(config::default_controller_thread_pool_size), "Number of worker threads in controller thread pool") - ("vote-threads", bpo::value()->default_value(0), - "Number of worker threads in vote processor thread pool. If set to 0, voting disabled, votes are not propagatged on P2P network.") + ("vote-threads", bpo::value(), + "Number of worker threads in vote processor thread pool. If set to 0, voting disabled, votes are not propagatged on P2P network. Defaults to 4 on producer nodes.") ("contracts-console", bpo::bool_switch()->default_value(false), "print contract's output to console") ("deep-mind", bpo::bool_switch()->default_value(false), @@ -640,8 +640,11 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) { "chain-threads ${num} must be greater than 0", ("num", chain_config->chain_thread_pool_size) ); } - if( options.count( "vote-threads" )) { - chain_config->vote_thread_pool_size = options.at( "vote-threads" ).as(); + if (options.count("producer-name") || options.count("vote-threads")) { + chain_config->vote_thread_pool_size = options.count("vote-threads") ? options.at("vote-threads").as() : 0; + if (chain_config->vote_thread_pool_size == 0 && options.count("producer-name")) { + chain_config->vote_thread_pool_size = config::default_vote_thread_pool_size; + } EOS_ASSERT( chain_config->vote_thread_pool_size > 1 || chain_config->vote_thread_pool_size == 0, plugin_config_exception, "vote-threads ${num} must be greater than 1, or equal to 0 to disable. " "Voting disabled if set to 0 (votes are not propagatged on P2P network).", diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index 2a339b73ad..91f23c1798 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1338,9 +1338,6 @@ void producer_plugin_impl::plugin_startup() { EOS_ASSERT(_producers.empty() || chain_plug->accept_transactions(), plugin_config_exception, "node cannot have any producer-name configured because no block production is possible with no [api|p2p]-accepted-transactions"); - EOS_ASSERT(_producers.empty() || chain_plug->accept_votes(), plugin_config_exception, - "node cannot have any producer-name configured because --vote-threads is defaulted to 0. Enable vote processing for block production."); - chain.set_node_finalizer_keys(_finalizer_keys); _accepted_block_connection.emplace(chain.accepted_block().connect([this](const block_signal_params& t) { diff --git a/plugins/producer_plugin/test/test_disallow_delayed_trx.cpp b/plugins/producer_plugin/test/test_disallow_delayed_trx.cpp index df850afd48..3723970ffc 100644 --- a/plugins/producer_plugin/test/test_disallow_delayed_trx.cpp +++ b/plugins/producer_plugin/test/test_disallow_delayed_trx.cpp @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(delayed_trx) { fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); std::vector argv = {"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(), - "-p", "eosio", "-e", "--vote-threads", "3", "--disable-subjective-p2p-billing=true" }; + "-p", "eosio", "-e", "--disable-subjective-p2p-billing=true" }; app->initialize( argv.size(), (char**) &argv[0] ); app->startup(); plugin_promise.set_value( diff --git a/plugins/producer_plugin/test/test_options.cpp b/plugins/producer_plugin/test/test_options.cpp index 072cc27ec5..3fe429b6a9 100644 --- a/plugins/producer_plugin/test/test_options.cpp +++ b/plugins/producer_plugin/test/test_options.cpp @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(state_dir) { "--data-dir", temp_dir_str.c_str(), "--state-dir", custom_state_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(), - "-p", "eosio", "--vote-threads", "3", "-e" }; + "-p", "eosio", "-e" }; app->initialize( argv.size(), (char**) &argv[0] ); app->startup(); plugin_promise.set_value( {app->find_plugin(), app->find_plugin()} ); diff --git a/plugins/producer_plugin/test/test_trx_full.cpp b/plugins/producer_plugin/test/test_trx_full.cpp index e827f44d6e..1a51570515 100644 --- a/plugins/producer_plugin/test/test_trx_full.cpp +++ b/plugins/producer_plugin/test/test_trx_full.cpp @@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(producer) { fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); std::vector argv = {"test", "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(), - "-p", "eosio", "-e", "--vote-threads", "3", "--disable-subjective-p2p-billing=true" }; + "-p", "eosio", "-e", "--disable-subjective-p2p-billing=true" }; app->initialize( argv.size(), (char**) &argv[0] ); app->startup(); plugin_promise.set_value( diff --git a/tests/TestHarness/Cluster.py b/tests/TestHarness/Cluster.py index 1f4a11b7e0..292adaae21 100644 --- a/tests/TestHarness/Cluster.py +++ b/tests/TestHarness/Cluster.py @@ -256,8 +256,6 @@ def launch(self, pnodes=1, unstartedNodes=0, totalNodes=1, prodCount=21, topo="m if self.staging: argsArr.append("--nogen") nodeosArgs="" - if "--vote-threads" not in extraNodeosArgs: - nodeosArgs += " --vote-threads 3" if "--max-transaction-time" not in extraNodeosArgs: nodeosArgs += " --max-transaction-time -1" if "--abi-serializer-max-time-ms" not in extraNodeosArgs: diff --git a/tests/cli_test.py b/tests/cli_test.py index 4505e0dad2..74e730e60c 100755 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -355,7 +355,7 @@ def abi_file_with_nodeos_test(): os.makedirs(data_dir, exist_ok=True) walletMgr = WalletMgr(True) walletMgr.launch() - cmd = "./programs/nodeos/nodeos -e -p eosio --vote-threads 2 --plugin eosio::trace_api_plugin --trace-no-abis --plugin eosio::producer_plugin --plugin eosio::producer_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::chain_plugin --plugin eosio::http_plugin --access-control-allow-origin=* --http-validate-host=false --max-transaction-time=-1 --resource-monitor-not-shutdown-on-threshold-exceeded " + "--data-dir " + data_dir + " --config-dir " + data_dir + cmd = "./programs/nodeos/nodeos -e -p eosio --plugin eosio::trace_api_plugin --trace-no-abis --plugin eosio::producer_plugin --plugin eosio::producer_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::chain_plugin --plugin eosio::http_plugin --access-control-allow-origin=* --http-validate-host=false --max-transaction-time=-1 --resource-monitor-not-shutdown-on-threshold-exceeded " + "--data-dir " + data_dir + " --config-dir " + data_dir node = Node('localhost', 8888, nodeId, data_dir=Path(data_dir), config_dir=Path(data_dir), cmd=shlex.split(cmd), launch_time=datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S'), walletMgr=walletMgr) time.sleep(5) node.waitForBlock(1) diff --git a/tests/db_modes_test.sh b/tests/db_modes_test.sh index a105ad1634..6b5fd20fe3 100755 --- a/tests/db_modes_test.sh +++ b/tests/db_modes_test.sh @@ -32,7 +32,7 @@ EOSIO_STUFF_DIR=$(mktemp -d) trap "rm -rf $EOSIO_STUFF_DIR" EXIT NODEOS_LAUNCH_PARAMS="./programs/nodeos/nodeos --resource-monitor-not-shutdown-on-threshold-exceeded -d $EOSIO_STUFF_DIR --config-dir $EOSIO_STUFF_DIR \ --chain-state-db-size-mb 8 --chain-state-db-guard-size-mb 0 \ --e -peosio --vote-threads 3" +-e -peosio" run_nodeos() { if (( $VERBOSE == 0 )); then diff --git a/tests/gelf_test.py b/tests/gelf_test.py index 21746c63cb..d0ed7f1888 100755 --- a/tests/gelf_test.py +++ b/tests/gelf_test.py @@ -86,7 +86,7 @@ def gelfServer(stop): data_dir = Path(Utils.getNodeDataDir(node_id)) config_dir = Path(Utils.getNodeConfigDir(node_id)) -start_nodeos_cmd = shlex.split(f"{Utils.EosServerPath} -e -p eosio --vote-threads 2 --data-dir={data_dir} --config-dir={config_dir}") +start_nodeos_cmd = shlex.split(f"{Utils.EosServerPath} -e -p eosio --data-dir={data_dir} --config-dir={config_dir}") if os.path.exists(data_dir): shutil.rmtree(data_dir) os.makedirs(data_dir) diff --git a/tests/p2p_no_listen_test.py b/tests/p2p_no_listen_test.py index d669fcce1b..76b3c76886 100755 --- a/tests/p2p_no_listen_test.py +++ b/tests/p2p_no_listen_test.py @@ -33,8 +33,6 @@ '-e', '-p', 'eosio', - '--vote-threads', - '3', '--p2p-listen-endpoint', '', '--plugin', diff --git a/tests/plugin_http_api_test.py b/tests/plugin_http_api_test.py index 687788f76c..f9628847cc 100755 --- a/tests/plugin_http_api_test.py +++ b/tests/plugin_http_api_test.py @@ -97,7 +97,7 @@ def startEnv(self) : "--p2p-peer-address localhost:9011 --resource-monitor-not-shutdown-on-threshold-exceeded ") % (self.data_dir, self.config_dir, self.data_dir, "\'*\'", "false") nodeos_flags += category_config.nodeosArgs() - start_nodeos_cmd = ("%s -e -p eosio --vote-threads 2 %s %s ") % (Utils.EosServerPath, nodeos_plugins, nodeos_flags) + start_nodeos_cmd = ("%s -e -p eosio %s %s ") % (Utils.EosServerPath, nodeos_plugins, nodeos_flags) self.nodeos = Node(TestHelper.LOCAL_HOST, TestHelper.DEFAULT_PORT, self.node_id, self.data_dir, self.config_dir, shlex.split(start_nodeos_cmd), walletMgr=self.keosd) time.sleep(self.sleep_s*2) self.nodeos.waitForBlock(1, timeout=30) diff --git a/tests/resource_monitor_plugin_test.py b/tests/resource_monitor_plugin_test.py index 0036bf188c..4370d529dc 100755 --- a/tests/resource_monitor_plugin_test.py +++ b/tests/resource_monitor_plugin_test.py @@ -77,7 +77,7 @@ def prepareDirectories(): def runNodeos(extraNodeosArgs, myTimeout): """Startup nodeos, wait for timeout (before forced shutdown) and collect output.""" if debug: Print("Launching nodeos process.") - cmd="programs/nodeos/nodeos --config-dir rsmStaging/etc -e -p eosio --vote-threads 2 --plugin eosio::chain_api_plugin --data-dir " + dataDir + " " + cmd="programs/nodeos/nodeos --config-dir rsmStaging/etc -e -p eosio --plugin eosio::chain_api_plugin --data-dir " + dataDir + " " cmd=cmd + extraNodeosArgs if debug: Print("cmd: %s" % (cmd)) diff --git a/tests/split_blocklog_replay_test.py b/tests/split_blocklog_replay_test.py index dc9c8d178e..ae7c24ffd8 100755 --- a/tests/split_blocklog_replay_test.py +++ b/tests/split_blocklog_replay_test.py @@ -17,7 +17,7 @@ os.makedirs(config_dir) try: - start_nodeos_cmd = f"{Utils.EosServerPath} -e -p eosio --vote-threads 2 --data-dir={data_dir} --config-dir={config_dir} --blocks-log-stride 10" \ + start_nodeos_cmd = f"{Utils.EosServerPath} -e -p eosio --data-dir={data_dir} --config-dir={config_dir} --blocks-log-stride 10" \ " --plugin=eosio::http_plugin --plugin=eosio::chain_api_plugin --http-server-address=localhost:8888" nodeos.launchCmd(start_nodeos_cmd, node_id) diff --git a/tests/test_read_only_trx.cpp b/tests/test_read_only_trx.cpp index a9ee294106..56b684da86 100644 --- a/tests/test_read_only_trx.cpp +++ b/tests/test_read_only_trx.cpp @@ -108,7 +108,7 @@ void test_trxs_common(std::vector& specific_args, bool test_disable fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); std::vector argv = { "test", // dummy executible name - "-p", "eosio", "--vote-threads", "3", "-e", // actual arguments follow + "-p", "eosio", "-e", // actual arguments follow "--data-dir", temp_dir_str.c_str(), "--config-dir", temp_dir_str.c_str(), "--max-transaction-time=100", diff --git a/tests/test_snapshot_scheduler.cpp b/tests/test_snapshot_scheduler.cpp index 2ebf170dc1..a03add72bb 100644 --- a/tests/test_snapshot_scheduler.cpp +++ b/tests/test_snapshot_scheduler.cpp @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(snapshot_scheduler_test) { fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug); std::vector argv = {"test", "--data-dir", temp.c_str(), "--config-dir", temp.c_str(), - "-p", "eosio", "--vote-threads", "3", "-e"}; + "-p", "eosio", "-e"}; app->initialize(argv.size(), (char**) &argv[0]); app->startup(); diff --git a/tutorials/bios-boot-tutorial/bios-boot-tutorial.py b/tutorials/bios-boot-tutorial/bios-boot-tutorial.py index 474d34c52a..8822546307 100755 --- a/tutorials/bios-boot-tutorial/bios-boot-tutorial.py +++ b/tutorials/bios-boot-tutorial/bios-boot-tutorial.py @@ -118,7 +118,6 @@ def startNode(nodeIndex, account): ' --p2p-max-nodes-per-host ' + str(maxClients) + ' --enable-stale-production' ' --producer-name ' + account['name'] + - ' --vote-threads 3' ' --signature-provider ' + account['pub'] + '=KEY:' + account['pvt'] + ' --plugin eosio::http_plugin' ' --plugin eosio::chain_api_plugin' From 6cc68200e69600688881241626c6b47ae88d4c31 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 13:50:02 -0500 Subject: [PATCH 25/60] GH-3 Simplify by adding a vote_signal_t type --- libraries/chain/controller.cpp | 16 ++++++++-------- .../chain/include/eosio/chain/controller.hpp | 3 ++- .../chain/include/eosio/chain/vote_processor.hpp | 15 ++++++--------- unittests/vote_processor_tests.cpp | 2 +- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index c172e1dc08..643ed231ec 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -947,14 +947,14 @@ struct controller_impl { signal accepted_block; signal irreversible_block; signal)> applied_transaction; - signal voted_block; + vote_signal_t voted_block; - vote_processor_t vote_processor{voted_block, - [this](const block_id_type& id) -> block_state_ptr { - return fork_db.apply_s([&](const auto& forkdb) { - return forkdb.get_block(id); - }); - }}; + vote_processor_t vote_processor{voted_block, + [this](const block_id_type& id) -> block_state_ptr { + return fork_db.apply_s([&](const auto& forkdb) { + return forkdb.get_block(id); + }); + }}; int64_t set_proposed_producers( vector producers ); int64_t set_proposed_producers_legacy( vector producers ); @@ -5548,7 +5548,7 @@ signal& controller::accepted_block_header() { signal& controller::accepted_block() { return my->accepted_block; } signal& controller::irreversible_block() { return my->irreversible_block; } signal)>& controller::applied_transaction() { return my->applied_transaction; } -signal& controller::voted_block() { return my->voted_block; } +vote_signal_t& controller::voted_block() { return my->voted_block; } chain_id_type controller::extract_chain_id(snapshot_reader& snapshot) { chain_snapshot_header header; diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 490b69b540..e8d1a5d1e3 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -60,6 +60,7 @@ namespace eosio::chain { using block_signal_params = std::tuple; // connection_id, vote result status, vote_message processed using vote_signal_params = std::tuple; + using vote_signal_t = signal; enum class db_read_mode { HEAD, @@ -377,7 +378,7 @@ namespace eosio::chain { signal& irreversible_block(); signal)>& applied_transaction(); // Unlike other signals, voted_block is signaled from other threads than the main thread. - signal& voted_block(); + vote_signal_t& voted_block(); const apply_handler* find_apply_handler( account_name contract, scope_name scope, action_name act )const; wasm_interface& get_wasm_interface(); diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 8d8417cc16..1f53ca6212 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -9,7 +10,7 @@ #include #include -namespace eosio { namespace chain { +namespace eosio::chain { /** * Process votes in a dedicated thread pool. @@ -32,8 +33,6 @@ class vote_processor_t { block_num_type block_num() const { return block_header::num_from_id(msg->block_id); } }; - using vote_signal_type = decltype(controller({},chain_id_type::empty_chain_id()).voted_block()); - using vote_index_type = boost::multi_index_container< vote, indexed_by< ordered_non_unique, @@ -48,7 +47,7 @@ class vote_processor_t { using fetch_block_func_t = std::function; - vote_signal_type& vote_signal; + vote_signal_t& vote_signal; fetch_block_func_t fetch_block_func; std::mutex mtx; @@ -119,7 +118,7 @@ class vote_processor_t { } public: - explicit vote_processor_t(vote_signal_type& vote_signal, fetch_block_func_t&& get_block) + explicit vote_processor_t(vote_signal_t& vote_signal, fetch_block_func_t&& get_block) : vote_signal(vote_signal) , fetch_block_func(get_block) {} @@ -148,9 +147,7 @@ class vote_processor_t { while (!stopped) { std::unique_lock g(mtx); cv.wait(g, [&]() { - if (!index.empty() || stopped) - return true; - return false; + return !index.empty() || stopped; }); if (stopped) break; @@ -240,4 +237,4 @@ class vote_processor_t { }; -} } //eosio::chain +} // namespace eosio::chain diff --git a/unittests/vote_processor_tests.cpp b/unittests/vote_processor_tests.cpp index 60f3964cd9..223e89de83 100644 --- a/unittests/vote_processor_tests.cpp +++ b/unittests/vote_processor_tests.cpp @@ -123,7 +123,7 @@ vote_message_ptr make_vote_message(const block_state_ptr& bsp) { BOOST_AUTO_TEST_SUITE(vote_processor_tests) BOOST_AUTO_TEST_CASE( vote_processor_test ) { - boost::signals2::signal voted_block; + vote_signal_t voted_block; uint32_t received_connection_id = 0; vote_status received_vote_status = vote_status::unknown_block; From 166a5809704dae0597d45b62d343bff72cf38abd Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 14:02:02 -0500 Subject: [PATCH 26/60] GH-3 Use chain pluging accept_votes to init p2p_accept_votes --- plugins/net_plugin/net_plugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index 2050139595..cb67c5c56a 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -4278,7 +4278,6 @@ namespace eosio { resp_expected_period = def_resp_expected_wait; max_nodes_per_host = options.at( "p2p-max-nodes-per-host" ).as(); p2p_accept_transactions = options.at( "p2p-accept-transactions" ).as(); - p2p_accept_votes = options.at("vote-threads").as() != 0; use_socket_read_watermark = options.at( "use-socket-read-watermark" ).as(); keepalive_interval = std::chrono::milliseconds( options.at( "p2p-keepalive-interval-ms" ).as() ); @@ -4427,6 +4426,8 @@ namespace eosio { "***********************************\n" ); } + p2p_accept_votes = chain_plug->accept_votes(); + std::vector listen_addresses = p2p_addresses; EOS_ASSERT( p2p_addresses.size() == p2p_server_addresses.size(), chain::plugin_config_exception, "" ); From 83434d13d502d1f1012f74b3ec00247594d75c7c Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 16 Apr 2024 14:36:55 -0500 Subject: [PATCH 27/60] GH-3 Add vote-threads to all nodes so bridge nodes process votes --- tests/TestHarness/Cluster.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/TestHarness/Cluster.py b/tests/TestHarness/Cluster.py index 292adaae21..1f4a11b7e0 100644 --- a/tests/TestHarness/Cluster.py +++ b/tests/TestHarness/Cluster.py @@ -256,6 +256,8 @@ def launch(self, pnodes=1, unstartedNodes=0, totalNodes=1, prodCount=21, topo="m if self.staging: argsArr.append("--nogen") nodeosArgs="" + if "--vote-threads" not in extraNodeosArgs: + nodeosArgs += " --vote-threads 3" if "--max-transaction-time" not in extraNodeosArgs: nodeosArgs += " --max-transaction-time -1" if "--abi-serializer-max-time-ms" not in extraNodeosArgs: From 917ca159dfef12ff44aa3d0d2f6411afcffa14bd Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Mon, 4 Mar 2024 20:41:22 -0500 Subject: [PATCH 28/60] restore get_block_header_state endpoint --- plugins/chain_api_plugin/chain.swagger.yaml | 24 +++++++++++ plugins/chain_api_plugin/chain_api_plugin.cpp | 1 + plugins/chain_plugin/chain_plugin.cpp | 30 ++++++++++++++ .../eosio/chain_plugin/chain_plugin.hpp | 7 ++++ tests/plugin_http_api_test.py | 40 +++++++++++++++++++ 5 files changed, 102 insertions(+) diff --git a/plugins/chain_api_plugin/chain.swagger.yaml b/plugins/chain_api_plugin/chain.swagger.yaml index 5bef6ef0d0..339568989b 100644 --- a/plugins/chain_api_plugin/chain.swagger.yaml +++ b/plugins/chain_api_plugin/chain.swagger.yaml @@ -186,6 +186,30 @@ paths: schema: description: Returns Nothing + /get_block_header_state: + post: + description: Retrieves a block header state object with only block_num, id, header, and additional_signatures filled. Other fields are left empty or defaulted to a static value. + operationId: get_block_header_state + requestBody: + content: + application/json: + schema: + type: object + required: + - block_num_or_id + properties: + block_num_or_id: + type: string + description: Provide a block_number or a block_id + + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: "https://docs.eosnetwork.com/openapi/v2.0/BlockHeaderState.yaml" + /get_abi: post: description: Retrieves the ABI for a contract based on its account name diff --git a/plugins/chain_api_plugin/chain_api_plugin.cpp b/plugins/chain_api_plugin/chain_api_plugin.cpp index fc558536f5..bd1a05611a 100644 --- a/plugins/chain_api_plugin/chain_api_plugin.cpp +++ b/plugins/chain_api_plugin/chain_api_plugin.cpp @@ -132,6 +132,7 @@ void chain_api_plugin::plugin_startup() { CHAIN_RO_CALL(get_activated_protocol_features, 200, http_params_types::possible_no_params), CHAIN_RO_CALL_POST(get_block, fc::variant, 200, http_params_types::params_required), // _POST because get_block() returns a lambda to be executed on the http thread pool CHAIN_RO_CALL(get_block_info, 200, http_params_types::params_required), + CHAIN_RO_CALL(get_block_header_state, 200, http_params_types::params_required), CHAIN_RO_CALL_POST(get_account, chain_apis::read_only::get_account_results, 200, http_params_types::params_required), CHAIN_RO_CALL(get_code, 200, http_params_types::params_required), CHAIN_RO_CALL(get_code_hash, 200, http_params_types::params_required), diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index c98d503664..beca74a587 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -2015,6 +2016,35 @@ fc::variant read_only::get_block_info(const read_only::get_block_info_params& pa ("ref_block_prefix", ref_block_prefix); } +fc::variant read_only::get_block_header_state(const get_block_header_state_params& params, const fc::time_point&) const { + signed_block_ptr sbp; + std::optional block_num; + + try { + block_num = fc::to_uint64(params.block_num_or_id); + } catch( ... ) {} + + if( block_num ) { + sbp = db.fetch_block_by_number(*block_num); + } else { + try { + sbp = db.fetch_block_by_id(fc::variant(params.block_num_or_id).as()); + } EOS_RETHROW_EXCEPTIONS(chain::block_id_type_exception, "Invalid block ID: ${block_num_or_id}", ("block_num_or_id", params.block_num_or_id)) + } + + EOS_ASSERT( sbp, unknown_block_exception, "Could not find block: ${block}", ("block", params.block_num_or_id)); + + block_header_state_legacy ret; + ret.block_num = sbp->block_num(); + ret.id = sbp->calculate_id(); + ret.header = *sbp; + ret.additional_signatures = detail::extract_additional_signatures(sbp); + + fc::variant vo; + fc::to_variant( ret, vo ); + return vo; +} + void read_write::push_block(read_write::push_block_params&& params, next_function next) { try { auto b = std::make_shared( std::move(params) ); 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 45576abe0e..6798d7e44c 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -409,6 +409,12 @@ class read_only : public api_base { fc::variant get_block_info(const get_block_info_params& params, const fc::time_point& deadline) const; + struct get_block_header_state_params { + string block_num_or_id; + }; + + fc::variant get_block_header_state(const get_block_header_state_params& params, const fc::time_point& deadline) const; + struct get_table_rows_params { bool json = false; name code; @@ -1022,6 +1028,7 @@ FC_REFLECT(eosio::chain_apis::read_only::get_activated_protocol_features_params, FC_REFLECT(eosio::chain_apis::read_only::get_activated_protocol_features_results, (activated_protocol_features)(more) ) FC_REFLECT(eosio::chain_apis::read_only::get_raw_block_params, (block_num_or_id)) FC_REFLECT(eosio::chain_apis::read_only::get_block_info_params, (block_num)) +FC_REFLECT(eosio::chain_apis::read_only::get_block_header_state_params, (block_num_or_id)) FC_REFLECT(eosio::chain_apis::read_only::get_block_header_params, (block_num_or_id)(include_extensions)) FC_REFLECT(eosio::chain_apis::read_only::get_block_header_result, (id)(signed_block_header)(block_extensions)) diff --git a/tests/plugin_http_api_test.py b/tests/plugin_http_api_test.py index f9628847cc..ba9e964f24 100755 --- a/tests/plugin_http_api_test.py +++ b/tests/plugin_http_api_test.py @@ -336,6 +336,46 @@ def test_ChainApi(self) : ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(ret_json["payload"]["block_num"], 1) + # get_block_header_state with empty parameter + command = "get_block_header_state" + ret_json = self.nodeos.processUrllibRequest(resource, command, endpoint=endpoint) + self.assertEqual(ret_json["code"], 400) + self.assertEqual(ret_json["error"]["code"], 3200006) + # get_block_header_state with empty content parameter + ret_json = self.nodeos.processUrllibRequest(resource, command, self.empty_content_dict, endpoint=endpoint) + self.assertEqual(ret_json["code"], 400) + self.assertEqual(ret_json["error"]["code"], 3200006) + # get_block_header_state with invalid parameter + ret_json = self.nodeos.processUrllibRequest(resource, command, self.http_post_invalid_param, endpoint=endpoint) + self.assertEqual(ret_json["code"], 400) + self.assertEqual(ret_json["error"]["code"], 3200006) + self.nodeos.waitForNextBlock() + # get_block_header_state with reversible block + head_block_num = self.nodeos.getHeadBlockNum() + payload = {"block_num_or_id":head_block_num} + ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) + self.assertEqual(ret_json["payload"]["block_num"], head_block_num) + self.assertEqual(ret_json["payload"]["header"]["producer"], "eosio") + # and by id + ret_json = self.nodeos.processUrllibRequest(resource, command, {"block_num_or_id":ret_json["payload"]["id"]}, endpoint=endpoint) + self.assertEqual(ret_json["payload"]["block_num"], head_block_num) + self.assertEqual(ret_json["payload"]["header"]["producer"], "eosio") + # get_block_header_state with irreversible block + lib_block_num = self.nodeos.getIrreversibleBlockNum() + payload = {"block_num_or_id":lib_block_num} + ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) + self.assertEqual(ret_json["payload"]["block_num"], lib_block_num) + self.assertEqual(ret_json["payload"]["header"]["producer"], "eosio") + # and by id + ret_json = self.nodeos.processUrllibRequest(resource, command, {"block_num_or_id":ret_json["payload"]["id"]}, endpoint=endpoint) + self.assertEqual(ret_json["payload"]["block_num"], lib_block_num) + self.assertEqual(ret_json["payload"]["header"]["producer"], "eosio") + # get_block_header_state with block far in future + payload = {"block_num_or_id":head_block_num+2000000} + ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) + self.assertEqual(ret_json["code"], 400) + self.assertEqual(ret_json["error"]["code"], 3100002) + # get_account with empty parameter command = "get_account" ret_json = self.nodeos.processUrllibRequest(resource, command, endpoint=endpoint) From eddbff89c056425ab4b4e06f5aa43f001239b37e Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:48:59 -0400 Subject: [PATCH 29/60] remove hardware_destructive_interference_size to silence warn --- libraries/chain/include/eosio/chain/thread_utils.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/thread_utils.hpp b/libraries/chain/include/eosio/chain/thread_utils.hpp index b4e4d5a673..7164beaf41 100644 --- a/libraries/chain/include/eosio/chain/thread_utils.hpp +++ b/libraries/chain/include/eosio/chain/thread_utils.hpp @@ -25,7 +25,6 @@ namespace eosio { namespace chain { // Use instead of std::atomic when std::atomic does not support type template class large_atomic { - alignas(hardware_destructive_interference_size) mutable std::mutex mtx; T value{}; public: From b89af8317f917ef61c3273beb66d7f07555c1c14 Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:47:21 -0400 Subject: [PATCH 30/60] avoid a copy; add test for invalid block id --- plugins/chain_plugin/chain_plugin.cpp | 2 +- tests/plugin_http_api_test.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index beca74a587..11608e7327 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -2028,7 +2028,7 @@ fc::variant read_only::get_block_header_state(const get_block_header_state_param sbp = db.fetch_block_by_number(*block_num); } else { try { - sbp = db.fetch_block_by_id(fc::variant(params.block_num_or_id).as()); + sbp = db.fetch_block_by_id(block_id_type(params.block_num_or_id)); } EOS_RETHROW_EXCEPTIONS(chain::block_id_type_exception, "Invalid block ID: ${block_num_or_id}", ("block_num_or_id", params.block_num_or_id)) } diff --git a/tests/plugin_http_api_test.py b/tests/plugin_http_api_test.py index ba9e964f24..de4791ba47 100755 --- a/tests/plugin_http_api_test.py +++ b/tests/plugin_http_api_test.py @@ -375,6 +375,11 @@ def test_ChainApi(self) : ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) self.assertEqual(ret_json["code"], 400) self.assertEqual(ret_json["error"]["code"], 3100002) + #invalid num and invalid sha256 + payload = {"block_num_or_id":"spoon was here"} + ret_json = self.nodeos.processUrllibRequest(resource, command, payload, endpoint=endpoint) + self.assertEqual(ret_json["code"], 500) + self.assertEqual(ret_json["error"]["code"], 3010008) # get_account with empty parameter command = "get_account" From 3e0d4c3daf6b52e5a40eb6e703615fc438dcfe30 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 10:30:04 -0400 Subject: [PATCH 31/60] Increment finalizer_policy generation correctly --- libraries/chain/block_header_state.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 364dde4c22..fd41441b1e 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -137,6 +137,7 @@ void finish_next(const block_header_state& prev, if (tracker.state == finalizer_policy_tracker::state_t::pending) { // new finalizer_policy becones active next_header_state.active_finalizer_policy = tracker.policy; + next_header_state.active_finalizer_policy->generation = prev.active_finalizer_policy->generation + 1; } else { assert(tracker.state == finalizer_policy_tracker::state_t::proposed); // block where finalizer_policy was proposed became final. The finalizer policy will From eb74ba0613373bc5347f3bc8280e0f452f3142a3 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 10:30:58 -0400 Subject: [PATCH 32/60] `finalizer_policies` should be a `multimap`. --- libraries/chain/block_header_state.cpp | 5 +++-- libraries/chain/include/eosio/chain/block_header_state.hpp | 2 +- libraries/chain/include/eosio/chain/snapshot_detail.hpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index fd41441b1e..f981e14b2c 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -153,9 +153,10 @@ void finish_next(const block_header_state& prev, } if (new_finalizer_policy) { - next_header_state.finalizer_policies[next_header_state.block_num()] = + next_header_state.finalizer_policies.emplace( + next_header_state.block_num(), finalizer_policy_tracker{finalizer_policy_tracker::state_t::proposed, - std::make_shared(std::move(*new_finalizer_policy)) }; + std::make_shared(std::move(*new_finalizer_policy))}); } // Finally update block id from header diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index c073b83d76..b621e6f13c 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -56,7 +56,7 @@ struct block_header_state { // block time when proposer_policy will become active flat_map proposer_policies; - flat_map finalizer_policies; + flat_multimap finalizer_policies; // ------ data members caching information available elsewhere ---------------------- diff --git a/libraries/chain/include/eosio/chain/snapshot_detail.hpp b/libraries/chain/include/eosio/chain/snapshot_detail.hpp index 5373aa6a7e..e477e5d9a0 100644 --- a/libraries/chain/include/eosio/chain/snapshot_detail.hpp +++ b/libraries/chain/include/eosio/chain/snapshot_detail.hpp @@ -117,7 +117,7 @@ namespace eosio::chain::snapshot_detail { finalizer_policy_ptr active_finalizer_policy; proposer_policy_ptr active_proposer_policy; flat_map proposer_policies; - flat_map finalizer_policies; + flat_multimap finalizer_policies; // from block_state std::optional valid; From 22ef766c45a202f9b73d29c621900134bd3bce36 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 10:31:32 -0400 Subject: [PATCH 33/60] Fix debugging output for `bls_public_key` --- libraries/libfc/include/fc/crypto/bls_public_key.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/libfc/include/fc/crypto/bls_public_key.hpp b/libraries/libfc/include/fc/crypto/bls_public_key.hpp index e80f44fd21..70e031fdd7 100644 --- a/libraries/libfc/include/fc/crypto/bls_public_key.hpp +++ b/libraries/libfc/include/fc/crypto/bls_public_key.hpp @@ -61,10 +61,10 @@ namespace fc::crypto::blslib { return ds; } - friend std::ostream& operator<<(std::ostream& os, const fc::crypto::blslib::bls_public_key& k) { - os << "bls_public_key(" << std::hex; + friend std::ostream& operator<<(std::ostream& os, const bls_public_key& k) { + os << "bls_public_key(0x" << std::hex; for (auto c : k.affine_non_montgomery_le()) - os << std::setfill('0') << std::setw(2) << c; + os << std::setfill('0') << std::setw(2) << (int)c; os << std::dec << ")"; return os; } From 00e59ef5b36549bbe2f90cb6aa33667a40a4e21a Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 10:32:17 -0400 Subject: [PATCH 34/60] Complete basic unittests. --- unittests/api_tests.cpp | 57 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 86c8477099..1d429078a1 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3932,9 +3932,9 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { std::span keys_span) { auto finpol = t.active_finalizer_policy(block->calculate_id()); BOOST_REQUIRE(!!finpol); - BOOST_CHECK_EQUAL(finpol->generation, generation); // new policy should not be active - // until after two 3-chains - BOOST_CHECK_EQUAL(keys_span.size(), finpol->finalizers.size()); + BOOST_REQUIRE_EQUAL(finpol->generation, generation); // new policy should not be active + // until after two 3-chains + BOOST_REQUIRE_EQUAL(keys_span.size(), finpol->finalizers.size()); std::vector keys {keys_span.begin(), keys_span.end() }; std::sort(keys.begin(), keys.end()); @@ -3943,7 +3943,7 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { active_keys.push_back(auth.public_key); std::sort(active_keys.begin(), active_keys.end()); for (size_t i=0; iblock_num()); + BOOST_REQUIRE_EQUAL(lib, pt_block->block_num()); // run set_finalizers(), verify it becomes active after exactly two 3-chains // ------------------------------------------------------------------------- @@ -4034,9 +4034,9 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_multiple_test) { try { std::span keys_span) { auto finpol = t.active_finalizer_policy(block->calculate_id()); BOOST_REQUIRE(!!finpol); - BOOST_CHECK_EQUAL(finpol->generation, generation); // new policy should not be active - // until after two 3-chains - BOOST_CHECK_EQUAL(keys_span.size(), finpol->finalizers.size()); + BOOST_REQUIRE_EQUAL(finpol->generation, generation); // new policy should not be active + // until after two 3-chains + BOOST_REQUIRE_EQUAL(keys_span.size(), finpol->finalizers.size()); std::vector keys {keys_span.begin(), keys_span.end() }; std::sort(keys.begin(), keys.end()); @@ -4045,7 +4045,7 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_multiple_test) { try { active_keys.push_back(auth.public_key); std::sort(active_keys.begin(), active_keys.end()); for (size_t i=0; iblock_num()); + BOOST_REQUIRE_EQUAL(lib, pt_block->block_num()); // run set_finalizers() twice in same block, verify only latest one becomes active // ------------------------------------------------------------------------------- @@ -4116,24 +4116,27 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_multiple_test) { try { auto b6 = t.produce_block(); check_finalizer_policy(b6, 2, pubkeys2); // two 3-chain - new policy pubkeys2 *should* be active -#if 0 - // run set_finalizers(), verify it becomes active after exactly two 3-chains - // ------------------------------------------------------------------------- - auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); - auto b0 = t.produce_block(); - check_finalizer_policy(b0, 1, pubkeys0); // new policy should only be active until after two 3-chains - + // run a test with multiple set_finlizers in-flight during the two 3-chains they + // take to become active + // ----------------------------------------------------------------------------- + auto pubkeys3 = t.set_active_finalizers({&finalizers[3], finset_size}); + b0 = t.produce_block(); + auto pubkeys4 = t.set_active_finalizers({&finalizers[4], finset_size}); + auto b1 = t.produce_block(); + auto b2 = t.produce_block(); + auto pubkeys5 = t.set_active_finalizers({&finalizers[5], finset_size}); t.produce_blocks(2); - auto b3 = t.produce_block(); - check_finalizer_policy(b3, 1, pubkeys0); // one 3-chain - new policy still should not be active - - t.produce_blocks(1); - auto b5 = t.produce_block(); - check_finalizer_policy(b5, 1, pubkeys0); // one 3-chain + 2 blocks - new policy still should not be active - - auto b6 = t.produce_block(); - check_finalizer_policy(b6, 2, pubkeys1); // two 3-chain - new policy *should* be active -#endif + b5 = t.produce_block(); + check_finalizer_policy(b5, 2, pubkeys2); // 5 blocks after pubkeys3 (b5 - b0), pubkeys2 should still be active + b6 = t.produce_block(); + check_finalizer_policy(b6, 3, pubkeys3); // 6 blocks after pubkeys3 (b6 - b0), pubkeys3 should be active + auto b7 = t.produce_block(); + check_finalizer_policy(b7, 4, pubkeys4); // 6 blocks after pubkeys4 (b7 - b1), pubkeys4 should be active + + auto b8 = t.produce_block(); + check_finalizer_policy(b8, 4, pubkeys4); // 7 blocks after pubkeys4, pubkeys4 should still be active + auto b9 = t.produce_block(); + check_finalizer_policy(b9, 5, pubkeys5); // 6 blocks after pubkeys5 (b9 - b3), pubkeys5 should be active } FC_LOG_AND_RETHROW() } void test_finality_transition(const vector& accounts, const base_tester::finalizer_policy_input& input, bool lib_advancing_expected) { From 60bda6dfc8b6991666bed6a1f8802cea974f9a4a Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 10:35:22 -0400 Subject: [PATCH 35/60] Check that range not empty before copying remainder of pending finalizer_policy changes. --- libraries/chain/block_header_state.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index f981e14b2c..ba0f2eb097 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -147,8 +147,11 @@ void finish_next(const block_header_state& prev, } ++it; } - next_header_state.finalizer_policies.insert(boost::container::ordered_unique_range_t(), - it, prev.finalizer_policies.end()); + if (it != prev.finalizer_policies.end()) { + // copy remainder of pending finalizer_policy changes + next_header_state.finalizer_policies.insert(boost::container::ordered_unique_range_t(), + it, prev.finalizer_policies.end()); + } } } From f894cdda4998a686dacdcc8cd0f21900daa98bae Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 11:33:42 -0400 Subject: [PATCH 36/60] Remove code duplication in `set_finalizers` tests. --- unittests/api_tests.cpp | 197 +++++++++++++++++----------------------- 1 file changed, 81 insertions(+), 116 deletions(-) diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 1d429078a1..138e203c83 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3920,35 +3920,14 @@ BOOST_AUTO_TEST_CASE(initial_set_finalizer_test) { try { } FC_LOG_AND_RETHROW() } // --------------------------------------------------------------------- -// verify that finalizer policy change via set_finalizer take 2 3-chains -// to take effect. +// Given a newly created `validating_tester`, trigger the transition to +// Savanna, and produce blocks until the transition is completed. // --------------------------------------------------------------------- -BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { - using bls_public_key = fc::crypto::blslib::bls_public_key; - validating_tester t; - - auto check_finalizer_policy = [&](const signed_block_ptr& block, - uint32_t generation, - std::span keys_span) { - auto finpol = t.active_finalizer_policy(block->calculate_id()); - BOOST_REQUIRE(!!finpol); - BOOST_REQUIRE_EQUAL(finpol->generation, generation); // new policy should not be active - // until after two 3-chains - BOOST_REQUIRE_EQUAL(keys_span.size(), finpol->finalizers.size()); - std::vector keys {keys_span.begin(), keys_span.end() }; - std::sort(keys.begin(), keys.end()); - - std::vector active_keys; - for (const auto& auth : finpol->finalizers) - active_keys.push_back(auth.public_key); - std::sort(active_keys.begin(), active_keys.end()); - for (size_t i=0; i, std::vector> +transition_to_Savanna(validating_tester& t, size_t num_local_finalizers, size_t finset_size) { uint32_t lib = 0; signed_block_ptr lib_block; - t.control->irreversible_block().connect([&](const block_signal_params& t) { + auto c = t.control->irreversible_block().connect([&](const block_signal_params& t) { const auto& [ block, id ] = t; lib = block->block_num(); lib_block = block; @@ -3958,8 +3937,8 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { // Create finalizer accounts vector finalizers; - finalizers.reserve(50); - for (size_t i=0; i<50; ++i) + finalizers.reserve(num_local_finalizers); + for (size_t i=0; iblock_num()); + c.disconnect(); + return { finalizers, pubkeys0 }; +} + +// --------------------------------------------------------------------- +// checks that the active finalizer_policy for `block` matches the +// passed `generation` and `keys_span`. +// --------------------------------------------------------------------- +static void check_finalizer_policy(validating_tester& t, + const signed_block_ptr& block, + uint32_t generation, + std::span keys_span) { + auto finpol = t.active_finalizer_policy(block->calculate_id()); + BOOST_REQUIRE(!!finpol); + BOOST_REQUIRE_EQUAL(finpol->generation, generation); // new policy should not be active + // until after two 3-chains + BOOST_REQUIRE_EQUAL(keys_span.size(), finpol->finalizers.size()); + std::vector keys {keys_span.begin(), keys_span.end() }; + std::sort(keys.begin(), keys.end()); + + std::vector active_keys; + for (const auto& auth : finpol->finalizers) + active_keys.push_back(auth.public_key); + std::sort(active_keys.begin(), active_keys.end()); + for (size_t i=0; i keys_span) { + auto b = t.produce_block(); + check_finalizer_policy(t, b, generation, keys_span); +} + +// --------------------------------------------------------------------- +// verify that finalizer policy change via set_finalizer take 2 3-chains +// to take effect. +// --------------------------------------------------------------------- +BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { + validating_tester t; + size_t num_local_finalizers = 50; + size_t finset_size = 21; + + auto [finalizers, pubkeys0] = transition_to_Savanna(t, num_local_finalizers, finset_size); + assert(finalizers.size() == num_local_finalizers && pubkeys0.size() == finset_size); + // run set_finalizers(), verify it becomes active after exactly two 3-chains // ------------------------------------------------------------------------- auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); auto b0 = t.produce_block(); - check_finalizer_policy(b0, 1, pubkeys0); // new policy should only be active until after two 3-chains + check_finalizer_policy(t, b0, 1, pubkeys0); // new policy should only be active until after two 3-chains t.produce_blocks(2); auto b3 = t.produce_block(); - check_finalizer_policy(b3, 1, pubkeys0); // one 3-chain - new policy still should not be active + check_finalizer_policy(t, b3, 1, pubkeys0); // one 3-chain - new policy still should not be active t.produce_blocks(1); auto b5 = t.produce_block(); - check_finalizer_policy(b5, 1, pubkeys0); // one 3-chain + 2 blocks - new policy still should not be active + check_finalizer_policy(t, b5, 1, pubkeys0); // one 3-chain + 2 blocks - new policy still should not be active auto b6 = t.produce_block(); - check_finalizer_policy(b6, 2, pubkeys1); // two 3-chain - new policy *should* be active + check_finalizer_policy(t, b6, 2, pubkeys1); // two 3-chain - new policy *should* be active } FC_LOG_AND_RETHROW() } @@ -4026,95 +4056,23 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { // at the same time. // --------------------------------------------------------------------------- BOOST_AUTO_TEST_CASE(savanna_set_finalizer_multiple_test) { try { - using bls_public_key = fc::crypto::blslib::bls_public_key; validating_tester t; + size_t num_local_finalizers = 50; + size_t finset_size = 21; - auto check_finalizer_policy = [&](const signed_block_ptr& block, - uint32_t generation, - std::span keys_span) { - auto finpol = t.active_finalizer_policy(block->calculate_id()); - BOOST_REQUIRE(!!finpol); - BOOST_REQUIRE_EQUAL(finpol->generation, generation); // new policy should not be active - // until after two 3-chains - BOOST_REQUIRE_EQUAL(keys_span.size(), finpol->finalizers.size()); - std::vector keys {keys_span.begin(), keys_span.end() }; - std::sort(keys.begin(), keys.end()); - - std::vector active_keys; - for (const auto& auth : finpol->finalizers) - active_keys.push_back(auth.public_key); - std::sort(active_keys.begin(), active_keys.end()); - for (size_t i=0; iirreversible_block().connect([&](const block_signal_params& t) { - const auto& [ block, id ] = t; - lib = block->block_num(); - lib_block = block; - }); - - t.produce_block(); - - // Create finalizer accounts - vector finalizers; - finalizers.reserve(50); - for (size_t i=0; i<50; ++i) - finalizers.emplace_back(std::string("init") + (char)('a' + i/26) + (char)('a' + i%26)); - - t.create_accounts(finalizers); - t.produce_block(); - - // activate savanna' - t.set_node_finalizers({&finalizers[0], finalizers.size()}); - - constexpr size_t finset_size = 21; - auto pubkeys0 = t.set_active_finalizers({&finalizers[0], finset_size}); - - // `genesis_block` is the first block where set_finalizers() was executed. - // It is the genesis block. - // It will include the first header extension for the instant finality. - // ----------------------------------------------------------------------- - auto genesis_block = t.produce_block(); - - // wait till the genesis_block becomes irreversible. - // The critical block is the block that makes the genesis_block irreversible - // ------------------------------------------------------------------------- - signed_block_ptr critical_block = nullptr; // last value of this var is the critical block - while(genesis_block->block_num() > lib) - critical_block = t.produce_block(); - - // Blocks after the critical block are proper IF blocks. - // ----------------------------------------------------- - auto first_proper_block = t.produce_block(); - BOOST_REQUIRE(first_proper_block->is_proper_svnn_block()); - - // wait till the first proper block becomes irreversible. Transition will be done then - // ----------------------------------------------------------------------------------- - signed_block_ptr pt_block = nullptr; // last value of this var is the first post-transition block - while(first_proper_block->block_num() > lib) { - pt_block = t.produce_block(); - BOOST_REQUIRE(pt_block->is_proper_svnn_block()); - } - - // lib must advance after 3 blocks - // ------------------------------- - t.produce_blocks(3); - BOOST_REQUIRE_EQUAL(lib, pt_block->block_num()); + auto [finalizers, pubkeys0] = transition_to_Savanna(t, num_local_finalizers, finset_size); // run set_finalizers() twice in same block, verify only latest one becomes active // ------------------------------------------------------------------------------- auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); auto pubkeys2 = t.set_active_finalizers({&finalizers[2], finset_size}); auto b0 = t.produce_block(); - check_finalizer_policy(b0, 1, pubkeys0); // new policy should only be active until after two 3-chains + check_finalizer_policy(t, b0, 1, pubkeys0); // new policy should only be active until after two 3-chains t.produce_blocks(4); auto b5 = t.produce_block(); - check_finalizer_policy(b5, 1, pubkeys0); // new policy should only be active until after two 3-chains + check_finalizer_policy(t, b5, 1, pubkeys0); // new policy should only be active until after two 3-chains auto b6 = t.produce_block(); - check_finalizer_policy(b6, 2, pubkeys2); // two 3-chain - new policy pubkeys2 *should* be active + check_finalizer_policy(t, b6, 2, pubkeys2); // two 3-chain - new policy pubkeys2 *should* be active // run a test with multiple set_finlizers in-flight during the two 3-chains they // take to become active @@ -4127,16 +4085,23 @@ BOOST_AUTO_TEST_CASE(savanna_set_finalizer_multiple_test) { try { auto pubkeys5 = t.set_active_finalizers({&finalizers[5], finset_size}); t.produce_blocks(2); b5 = t.produce_block(); - check_finalizer_policy(b5, 2, pubkeys2); // 5 blocks after pubkeys3 (b5 - b0), pubkeys2 should still be active + check_finalizer_policy(t, b5, 2, pubkeys2); // 5 blocks after pubkeys3 (b5 - b0), pubkeys2 should still be active b6 = t.produce_block(); - check_finalizer_policy(b6, 3, pubkeys3); // 6 blocks after pubkeys3 (b6 - b0), pubkeys3 should be active + check_finalizer_policy(t, b6, 3, pubkeys3); // 6 blocks after pubkeys3 (b6 - b0), pubkeys3 should be active auto b7 = t.produce_block(); - check_finalizer_policy(b7, 4, pubkeys4); // 6 blocks after pubkeys4 (b7 - b1), pubkeys4 should be active + check_finalizer_policy(t, b7, 4, pubkeys4); // 6 blocks after pubkeys4 (b7 - b1), pubkeys4 should be active auto b8 = t.produce_block(); - check_finalizer_policy(b8, 4, pubkeys4); // 7 blocks after pubkeys4, pubkeys4 should still be active + check_finalizer_policy(t, b8, 4, pubkeys4); // 7 blocks after pubkeys4, pubkeys4 should still be active auto b9 = t.produce_block(); - check_finalizer_policy(b9, 5, pubkeys5); // 6 blocks after pubkeys5 (b9 - b3), pubkeys5 should be active + check_finalizer_policy(t, b9, 5, pubkeys5); // 6 blocks after pubkeys5 (b9 - b3), pubkeys5 should be active + + // and no further change + ensure_next_block_finalizer_policy(t, 5, pubkeys5); + ensure_next_block_finalizer_policy(t, 5, pubkeys5); + ensure_next_block_finalizer_policy(t, 5, pubkeys5); + ensure_next_block_finalizer_policy(t, 5, pubkeys5); + ensure_next_block_finalizer_policy(t, 5, pubkeys5); } FC_LOG_AND_RETHROW() } void test_finality_transition(const vector& accounts, const base_tester::finalizer_policy_input& input, bool lib_advancing_expected) { From f6ddcce64c22b36fa93a3d10cd2a87b3bcc97988 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Wed, 17 Apr 2024 11:36:18 -0400 Subject: [PATCH 37/60] Make getLatestSnapshot() and removeState() member functions of Node --- tests/TestHarness/Node.py | 16 ++++++++++++++++ tests/nodeos_irreversible_mode_test.py | 25 ++++++------------------- tests/nodeos_snapshot_diff_test.py | 24 +++++------------------- tests/ship_streamer_test.py | 9 +-------- 4 files changed, 28 insertions(+), 46 deletions(-) diff --git a/tests/TestHarness/Node.py b/tests/TestHarness/Node.py index d23f42d2f9..b9c5c9b8e8 100644 --- a/tests/TestHarness/Node.py +++ b/tests/TestHarness/Node.py @@ -8,6 +8,7 @@ import shlex import signal import sys +import shutil from pathlib import Path from typing import List from dataclasses import InitVar, dataclass, field, is_dataclass, asdict @@ -537,6 +538,21 @@ def scheduleSnapshotAt(self, sbn): param = { "start_block_num": sbn, "end_block_num": sbn } return self.processUrllibRequest("producer", "schedule_snapshot", param) + def getLatestSnapshot(self): + snapshotDir = os.path.join(Utils.getNodeDataDir(self.nodeId), "snapshots") + snapshotDirContents = os.listdir(snapshotDir) + assert len(snapshotDirContents) > 0 + # disregard snapshot schedule config in same folder + snapshotScheduleDB = "snapshot-schedule.json" + if snapshotScheduleDB in snapshotDirContents: snapshotDirContents.remove(snapshotScheduleDB) + snapshotDirContents.sort() + return os.path.join(snapshotDir, snapshotDirContents[-1]) + + def removeState(self): + dataDir = Utils.getNodeDataDir(self.nodeId) + state = os.path.join(dataDir, "state") + shutil.rmtree(state, ignore_errors=True) + @staticmethod def findStderrFiles(path): files=[] diff --git a/tests/nodeos_irreversible_mode_test.py b/tests/nodeos_irreversible_mode_test.py index 144e8793c6..10d72bd3bb 100755 --- a/tests/nodeos_irreversible_mode_test.py +++ b/tests/nodeos_irreversible_mode_test.py @@ -50,24 +50,11 @@ def recoverBackedupBlksDir(nodeId): shutil.rmtree(existingBlocksDir, ignore_errors=True) shutil.copytree(backedupBlocksDir, existingBlocksDir) -def getLatestSnapshot(nodeId): - snapshotDir = os.path.join(Utils.getNodeDataDir(nodeId), "snapshots") - snapshotDirContents = os.listdir(snapshotDir) - assert len(snapshotDirContents) > 0 - snapshotDirContents.sort() - return os.path.join(snapshotDir, snapshotDirContents[-1]) - - def removeReversibleBlks(nodeId): dataDir = Utils.getNodeDataDir(nodeId) reversibleBlks = os.path.join(dataDir, "blocks", "reversible") shutil.rmtree(reversibleBlks, ignore_errors=True) -def removeState(nodeId): - dataDir = Utils.getNodeDataDir(nodeId) - state = os.path.join(dataDir, "state") - shutil.rmtree(state, ignore_errors=True) - def getHeadLibAndForkDbHead(node: Node): info = node.getInfo() assert info is not None, "Fail to retrieve info from the node, the node is currently having a problem" @@ -375,9 +362,9 @@ def switchToSpecModeWithIrrModeSnapshot(nodeIdOfNodeToTest, nodeToTest): nodeToTest.kill(signal.SIGTERM) # Start from clean data dir, recover back up blocks, and then relaunch with irreversible snapshot - removeState(nodeIdOfNodeToTest) + nodeToTest.removeState() recoverBackedupBlksDir(nodeIdOfNodeToTest) # this function will delete the existing blocks dir first - relaunchNode(nodeToTest, chainArg=" --snapshot {}".format(getLatestSnapshot(nodeIdOfNodeToTest)), addSwapFlags={"--read-mode": speculativeReadMode}) + relaunchNode(nodeToTest, chainArg=" --snapshot {}".format(nodeToTest.getLatestSnapshot()), addSwapFlags={"--read-mode": speculativeReadMode}) confirmHeadLibAndForkDbHeadOfSpecMode(nodeToTest) # Ensure it automatically replays "reversible blocks", i.e. head lib and fork db should be the same headLibAndForkDbHeadAfterRelaunch = getHeadLibAndForkDbHead(nodeToTest) @@ -395,7 +382,7 @@ def switchToSpecModeWithIrrModeSnapshot(nodeIdOfNodeToTest, nodeToTest): # Relaunch the node again (using the same snapshot) # This time ensure it automatically replays both "irreversible blocks" and "reversible blocks", i.e. the end result should be the same as before shutdown - removeState(nodeIdOfNodeToTest) + nodeToTest.removeState() relaunchNode(nodeToTest) headLibAndForkDbHeadAfterRelaunch = getHeadLibAndForkDbHead(nodeToTest) assert headLibAndForkDbHeadBeforeShutdown == headLibAndForkDbHeadAfterRelaunch, \ @@ -419,8 +406,8 @@ def switchToNoBlockLogWithIrrModeSnapshot(nodeIdOfNodeToTest, nodeToTest): nodeToTest.kill(signal.SIGTERM) # Start from clean data dir and then relaunch with irreversible snapshot, no block log means that fork_db will be reset - removeState(nodeIdOfNodeToTest) - relaunchNode(nodeToTest, chainArg=" --snapshot {}".format(getLatestSnapshot(nodeIdOfNodeToTest)), addSwapFlags={"--read-mode": speculativeReadMode, "--block-log-retain-blocks":"0"}) + nodeToTest.removeState() + relaunchNode(nodeToTest, chainArg=" --snapshot {}".format(nodeToTest.getLatestSnapshot()), addSwapFlags={"--read-mode": speculativeReadMode, "--block-log-retain-blocks":"0"}) confirmHeadLibAndForkDbHeadOfSpecMode(nodeToTest) # Ensure it does not replay "reversible blocks", i.e. head and lib should be different headLibAndForkDbHeadAfterRelaunch = getHeadLibAndForkDbHead(nodeToTest) @@ -438,7 +425,7 @@ def switchToNoBlockLogWithIrrModeSnapshot(nodeIdOfNodeToTest, nodeToTest): # Relaunch the node again (using the same snapshot) # The end result should be the same as before shutdown - removeState(nodeIdOfNodeToTest) + nodeToTest.removeState() relaunchNode(nodeToTest) headLibAndForkDbHeadAfterRelaunch2 = getHeadLibAndForkDbHead(nodeToTest) assert headLibAndForkDbHeadAfterRelaunch == headLibAndForkDbHeadAfterRelaunch2, \ diff --git a/tests/nodeos_snapshot_diff_test.py b/tests/nodeos_snapshot_diff_test.py index 2185df1509..44fe778fba 100755 --- a/tests/nodeos_snapshot_diff_test.py +++ b/tests/nodeos_snapshot_diff_test.py @@ -58,20 +58,6 @@ snapshotScheduleDB = "snapshot-schedule.json" -def getLatestSnapshot(nodeId): - snapshotDir = os.path.join(Utils.getNodeDataDir(nodeId), "snapshots") - snapshotDirContents = os.listdir(snapshotDir) - assert len(snapshotDirContents) > 0 - # disregard snapshot schedule config in same folder - if snapshotScheduleDB in snapshotDirContents: snapshotDirContents.remove(snapshotScheduleDB) - snapshotDirContents.sort() - return os.path.join(snapshotDir, snapshotDirContents[-1]) - -def removeState(nodeId): - dataDir = Utils.getNodeDataDir(nodeId) - state = os.path.join(dataDir, "state") - shutil.rmtree(state, ignore_errors=True) - try: TestHelper.printSystemInfo("BEGIN") cluster.setWalletMgr(walletMgr) @@ -165,14 +151,14 @@ def waitForBlock(node, blockNum, blockType=BlockType.head, timeout=None, reportI nodeSnap.kill(signal.SIGTERM) Print("Convert snapshot to JSON") - snapshotFile = getLatestSnapshot(snapshotNodeId) + snapshotFile = nodeSnap.getLatestSnapshot() Utils.processLeapUtilCmd("snapshot to-json --input-file {}".format(snapshotFile), "snapshot to-json", silentErrors=False) snapshotFile = snapshotFile + ".json" Print("Trim programmable blocklog to snapshot head block num and relaunch programmable node") nodeProg.kill(signal.SIGTERM) output=cluster.getBlockLog(progNodeId, blockLogAction=BlockLogAction.trim, first=0, last=ret_head_block_num, throwException=True) - removeState(progNodeId) + nodeProg.removeState() nodeProg.rmFromCmd('--p2p-peer-address') isRelaunchSuccess = nodeProg.relaunch(chainArg="--replay", addSwapFlags={}, timeout=relaunchTimeout) assert isRelaunchSuccess, "Failed to relaunch programmable node" @@ -188,7 +174,7 @@ def waitForBlock(node, blockNum, blockType=BlockType.head, timeout=None, reportI nodeProg.kill(signal.SIGTERM) Print("Convert snapshot to JSON") - progSnapshotFile = getLatestSnapshot(progNodeId) + progSnapshotFile = nodeProg.getLatestSnapshot() Utils.processLeapUtilCmd("snapshot to-json --input-file {}".format(progSnapshotFile), "snapshot to-json", silentErrors=False) progSnapshotFile = progSnapshotFile + ".json" @@ -197,7 +183,7 @@ def waitForBlock(node, blockNum, blockType=BlockType.head, timeout=None, reportI output=cluster.getBlockLog(irrNodeId, blockLogAction=BlockLogAction.trim, first=0, last=ret_head_block_num, throwException=True) Print("Relaunch irreversible node in irreversible mode") - removeState(irrNodeId) + nodeIrr.removeState() nodeIrr.rmFromCmd('--p2p-peer-address') swapFlags = {"--read-mode":"irreversible", "--p2p-max-nodes-per-host":"0", "--max-clients":"0", "--allowed-connection":"none"} isRelaunchSuccess = nodeIrr.relaunch(chainArg="--replay", addSwapFlags=swapFlags, timeout=relaunchTimeout) @@ -214,7 +200,7 @@ def waitForBlock(node, blockNum, blockType=BlockType.head, timeout=None, reportI nodeIrr.kill(signal.SIGTERM) Print("Convert snapshot to JSON") - irrSnapshotFile = getLatestSnapshot(irrNodeId) + irrSnapshotFile = nodeIrr.getLatestSnapshot() Utils.processLeapUtilCmd("snapshot to-json --input-file {}".format(irrSnapshotFile), "snapshot to-json", silentErrors=False) irrSnapshotFile = irrSnapshotFile + ".json" diff --git a/tests/ship_streamer_test.py b/tests/ship_streamer_test.py index 8b92397ef7..685935cb22 100755 --- a/tests/ship_streamer_test.py +++ b/tests/ship_streamer_test.py @@ -52,13 +52,6 @@ WalletdName=Utils.EosWalletName shipTempDir=None -def getLatestSnapshot(nodeId): - snapshotDir = os.path.join(Utils.getNodeDataDir(nodeId), "snapshots") - snapshotDirContents = os.listdir(snapshotDir) - assert len(snapshotDirContents) > 0 - snapshotDirContents.sort() - return os.path.join(snapshotDir, snapshotDirContents[-1]) - try: TestHelper.printSystemInfo("BEGIN") @@ -230,7 +223,7 @@ def getLatestSnapshot(nodeId): Print("Test starting ship from snapshot") Utils.rmNodeDataDir(shipNodeNum) - isRelaunchSuccess = shipNode.relaunch(chainArg=" --snapshot {}".format(getLatestSnapshot(shipNodeNum))) + isRelaunchSuccess = shipNode.relaunch(chainArg=" --snapshot {}".format(shipNode.getLatestSnapshot())) assert isRelaunchSuccess, "relaunch from snapshot failed" afterSnapshotBlockNum = shipNode.getBlockNum() From bf724941a21bb187d0f177b158e01fb2cca5b954 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 13:43:48 -0400 Subject: [PATCH 38/60] Use `block_num_type` instead of `uint32_t` --- libraries/chain/include/eosio/chain/block_header_state.hpp | 4 ++-- libraries/chain/include/eosio/chain/snapshot_detail.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index b621e6f13c..829a610902 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -55,8 +55,8 @@ struct block_header_state { proposer_policy_ptr active_proposer_policy; // producer authority schedule, supports `digest()` // block time when proposer_policy will become active - flat_map proposer_policies; - flat_multimap finalizer_policies; + flat_map proposer_policies; + flat_multimap finalizer_policies; // ------ data members caching information available elsewhere ---------------------- diff --git a/libraries/chain/include/eosio/chain/snapshot_detail.hpp b/libraries/chain/include/eosio/chain/snapshot_detail.hpp index e477e5d9a0..98abde8a65 100644 --- a/libraries/chain/include/eosio/chain/snapshot_detail.hpp +++ b/libraries/chain/include/eosio/chain/snapshot_detail.hpp @@ -117,7 +117,7 @@ namespace eosio::chain::snapshot_detail { finalizer_policy_ptr active_finalizer_policy; proposer_policy_ptr active_proposer_policy; flat_map proposer_policies; - flat_multimap finalizer_policies; + flat_multimap finalizer_policies; // from block_state std::optional valid; From 3d1cddc4a7af9b2fed10c508a578e60651161cd5 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 13:44:29 -0400 Subject: [PATCH 39/60] Avoid modifying data from previous `block_header_state`. --- libraries/chain/block_header_state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index ba0f2eb097..5ddfd7de32 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -136,7 +136,7 @@ void finish_next(const block_header_state& prev, const finalizer_policy_tracker& tracker = it->second; if (tracker.state == finalizer_policy_tracker::state_t::pending) { // new finalizer_policy becones active - next_header_state.active_finalizer_policy = tracker.policy; + next_header_state.active_finalizer_policy.reset(new finalizer_policy(*tracker.policy)); next_header_state.active_finalizer_policy->generation = prev.active_finalizer_policy->generation + 1; } else { assert(tracker.state == finalizer_policy_tracker::state_t::proposed); From 7f6a622a64d008a31f4fa775bafc0c594b540fb1 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 13:45:01 -0400 Subject: [PATCH 40/60] Remove unnecessary setting of `finalizer_policy::generation` --- libraries/chain/controller.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index edc00b099a..8cd489a0f5 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -525,8 +525,8 @@ struct building_block { bb.new_finalizer_policy = std::move(fin_pol); }, [&](building_block_if& bb) { - fin_pol.generation = bb.parent.active_finalizer_policy->generation + 1; - bb.new_finalizer_policy = std::move(fin_pol); + bb.new_finalizer_policy = std::move(fin_pol); + // generation will be updated when activated } }, v); } From 25f93f811ba991ab4bc3f43809078e4377d4d141 Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 13:48:56 -0400 Subject: [PATCH 41/60] Fix asan issue. --- libraries/chain/block_header_state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 5ddfd7de32..13392e357e 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -132,7 +132,7 @@ void finish_next(const block_header_state& prev, if (it->first > lib) { next_header_state.finalizer_policies = prev.finalizer_policies; } else { - while (it->first <= lib && it != prev.finalizer_policies.end()) { + while (it != prev.finalizer_policies.end() && it->first <= lib) { const finalizer_policy_tracker& tracker = it->second; if (tracker.state == finalizer_policy_tracker::state_t::pending) { // new finalizer_policy becones active From 042ce3002eeb8169b2d17e4d238e30956b1dda63 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 17 Apr 2024 14:10:06 -0500 Subject: [PATCH 42/60] GH-3 Simplify vote_processor by processing votes on a first-come-first-serve basis. --- libraries/chain/controller.cpp | 2 + .../include/eosio/chain/vote_processor.hpp | 220 ++++++++++-------- plugins/chain_plugin/chain_plugin.cpp | 5 +- unittests/vote_processor_tests.cpp | 51 ++-- 4 files changed, 160 insertions(+), 118 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 643ed231ec..157f191ea8 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3765,6 +3765,8 @@ struct controller_impl { if (conf.terminate_at_block == 0 || bsp->block_num() <= conf.terminate_at_block) { forkdb.add(bsp, mark_valid_t::no, ignore_duplicate_t::yes); + if constexpr (savanna_mode) + vote_processor.notify_new_block(); } return block_handle{bsp}; diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 1f53ca6212..430faec8e2 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -19,14 +19,16 @@ class vote_processor_t { // Even 3000 vote structs are less than 1MB per connection. // 2500 is should never be reached unless a specific connection is sending garbage. static constexpr size_t max_votes_per_connection = 2500; - static constexpr std::chrono::milliseconds block_wait_time{10}; + // If we have not processed a vote in this amount of time, give up on it. + static constexpr fc::microseconds too_old = fc::seconds(5); struct by_block_num; struct by_connection; - struct by_vote; + struct by_last_received; struct vote { uint32_t connection_id; + fc::time_point received; vote_message_ptr msg; const block_id_type& id() const { return msg->block_id; } @@ -35,13 +37,9 @@ class vote_processor_t { using vote_index_type = boost::multi_index_container< vote, indexed_by< - ordered_non_unique, - composite_key, - const_mem_fun - >, composite_key_compare< std::greater<>, sha256_less > // greater for block_num - >, - ordered_non_unique< tag, member > + ordered_non_unique< tag, const_mem_fun, std::greater<> >, + ordered_non_unique< tag, member >, + ordered_non_unique< tag, member > > >; @@ -51,12 +49,14 @@ class vote_processor_t { fetch_block_func_t fetch_block_func; std::mutex mtx; - std::condition_variable cv; vote_index_type index; + block_state_ptr last_bsp; // connection, count of messages std::map num_messages; std::atomic lib{0}; + std::atomic largest_known_block_num{0}; + std::atomic queued_votes{0}; std::atomic stopped{true}; named_thread_pool thread_pool; @@ -83,53 +83,111 @@ class vote_processor_t { } } + // called with unlocked mtx void emit(uint32_t connection_id, vote_status status, const vote_message_ptr& msg) { if (connection_id != 0) { // this nodes vote was already signaled - emit( vote_signal, std::tuple{connection_id, status, std::cref(msg)} ); + if (status != vote_status::duplicate) { // don't bother emitting duplicates + emit( vote_signal, std::tuple{connection_id, status, std::cref(msg)} ); + } } } + // called with locked mtx void remove_connection(uint32_t connection_id) { auto& idx = index.get(); idx.erase(idx.lower_bound(connection_id), idx.upper_bound(connection_id)); } + // called with locked mtx void remove_before_lib() { auto& idx = index.get(); idx.erase(idx.lower_bound(lib.load()), idx.end()); // descending // don't decrement num_messages as too many before lib should be considered an error } - bool remove_all_for_block(auto& idx, auto& it, const block_id_type& id) { - while (it != idx.end() && it->id() == id) { - if (auto& num = num_messages[it->connection_id]; num != 0) - --num; + // called with locked mtx + void remove_too_old() { + auto& idx = index.get(); + fc::time_point vote_too_old = fc::time_point::now() - too_old; + idx.erase(idx.lower_bound(fc::time_point::min()), idx.upper_bound(vote_too_old)); + // don't decrement num_messages as too many that are too old should be considered an error + } - it = idx.erase(it); + // called with locked mtx + void queue_for_later(uint32_t connection_id, const vote_message_ptr& msg) { + fc::time_point now = fc::time_point::now(); + remove_before_lib(); + remove_too_old(); + index.insert(vote{.connection_id = connection_id, .received = now, .msg = msg}); + } + + // called with locked mtx + void process_any_queued_for_later(std::unique_lock& g) { + if (index.empty()) + return; + remove_too_old(); + remove_before_lib(); + auto& idx = index.get(); + std::vector unprocessed; + unprocessed.reserve(std::min(21u, idx.size())); // maybe increase if we increase # of finalizers from 21 + for (auto i = idx.begin(); i != idx.end();) { + if (stopped) + return; + vote v = *i; + idx.erase(i); + auto bsp = get_block(i->msg->block_id, g); + // g is unlocked + if (bsp) { + vote_status s = bsp->aggregate_vote(*v.msg); + emit(v.connection_id, s, v.msg); + + g.lock(); + if (auto& num = num_messages[v.connection_id]; num != 0) + --num; + } else { + unprocessed.push_back(std::move(v)); + g.lock(); + } + i = idx.begin(); // need to update since unlocked in loop + } + for (auto& v : unprocessed) { + index.insert(std::move(v)); } - return it == idx.end(); } - bool skip_all_for_block(auto& idx, auto& it, const block_id_type& id) { - while (it != idx.end() && it->id() == id) { - ++it; + // called with locked mtx, returns with unlocked mtx + block_state_ptr get_block(const block_id_type& id, std::unique_lock& g) { + block_state_ptr bsp; + if (last_bsp && last_bsp->id() == id) { + bsp = last_bsp; } - return it == idx.end(); + g.unlock(); + + if (!bsp) { + bsp = fetch_block_func(id); + if (bsp) { + g.lock(); + last_bsp = bsp; + largest_known_block_num = std::max(bsp->block_num(), largest_known_block_num.load()); + g.unlock(); + } + } + return bsp; } public: explicit vote_processor_t(vote_signal_t& vote_signal, fetch_block_func_t&& get_block) : vote_signal(vote_signal) , fetch_block_func(get_block) - {} + { + assert(get_block); + } ~vote_processor_t() { stopped = true; - std::lock_guard g(mtx); - cv.notify_one(); } - size_t size() { + size_t index_size() { std::lock_guard g(mtx); return index.size(); } @@ -140,98 +198,70 @@ class vote_processor_t { stopped = false; thread_pool.start( num_threads, std::move(on_except)); - - // one coordinator thread - boost::asio::post(thread_pool.get_executor(), [&]() { - block_id_type not_in_forkdb_id{}; - while (!stopped) { - std::unique_lock g(mtx); - cv.wait(g, [&]() { - return !index.empty() || stopped; - }); - if (stopped) - break; - remove_before_lib(); - if (index.empty()) { - num_messages.clear(); - continue; - } - auto& idx = index.get(); - if (auto i = idx.begin(); i != idx.end() && not_in_forkdb_id == i->id()) { // same block as last while loop - g.unlock(); - std::this_thread::sleep_for(block_wait_time); - g.lock(); - } - for (auto i = idx.begin(); i != idx.end();) { - if (stopped) - break; - auto& vt = *i; - block_state_ptr bsp = fetch_block_func(vt.id()); - if (bsp) { - if (!bsp->is_proper_svnn_block()) { - if (remove_all_for_block(idx, i, bsp->id())) - break; - continue; - } - auto iter_of_bsp = i; - std::vector to_process; - to_process.reserve(std::min(21u, idx.size())); // increase if we increase # of finalizers from 21 - for(; i != idx.end() && bsp->id() == i->id(); ++i) { - // although it is the highest contention on block state pending mutex posting all of the same bsp, - // the highest priority is processing votes for this block state. - to_process.push_back(*i); - } - bool should_break = remove_all_for_block(idx, iter_of_bsp, bsp->id()); - g.unlock(); // do not hold lock when posting - for (auto& v : to_process) { - boost::asio::post(thread_pool.get_executor(), [this, bsp, v=std::move(v)]() { - vote_status s = bsp->aggregate_vote(*v.msg); - if (s != vote_status::duplicate) { // don't bother emitting duplicates - emit(v.connection_id, s, v.msg); - } - }); - } - if (should_break) - break; - g.lock(); - i = idx.begin(); - } else { - not_in_forkdb_id = vt.id(); - if (skip_all_for_block(idx, i, i->id())) - break; - } - } - } - dlog("Exiting vote processor coordinator thread"); - }); } + // called from main thread void notify_lib(block_num_type block_num) { lib = block_num; } + // called from net threads + void notify_new_block() { + // would require a mtx lock to check if index is empty, post check to thread_pool + boost::asio::post(thread_pool.get_executor(), [this] { + std::unique_lock g(mtx); + process_any_queued_for_later(g); + }); + } + /// called from net threads and controller's thread pool /// msg is ignored vote_processor not start()ed void process_vote_message(uint32_t connection_id, const vote_message_ptr& msg) { if (stopped) return; assert(msg); + block_num_type msg_block_num = block_header::num_from_id(msg->block_id); + if (msg_block_num <= lib.load(std::memory_order_relaxed)) + return; + ++queued_votes; boost::asio::post(thread_pool.get_executor(), [this, connection_id, msg] { + if (stopped) + return; + auto num_queued_votes = --queued_votes; + bool reset_num_messages = num_queued_votes == 0; // caught up, so clear num_messages + if (block_header::num_from_id(msg->block_id) <= lib.load(std::memory_order_relaxed)) + return; // ignore any votes lower than lib std::unique_lock g(mtx); + if (reset_num_messages) + num_messages.clear(); if (++num_messages[connection_id] > max_votes_per_connection) { - // consider the connection invalid, remove all votes of connection - // don't clear num_messages[connection_id] so we keep reporting max_exceeded until index is drained remove_connection(connection_id); g.unlock(); + // drop, too many from this connection to process, consider connection invalid + // don't clear num_messages[connection_id] so we keep reporting max_exceeded until index is drained elog("Exceeded max votes per connection for ${c}", ("c", connection_id)); emit(connection_id, vote_status::max_exceeded, msg); - } else if (block_header::num_from_id(msg->block_id) < lib.load(std::memory_order_relaxed)) { - // ignore } else { - index.insert(vote{.connection_id = connection_id, .msg = msg}); - cv.notify_one(); + block_state_ptr bsp = get_block(msg->block_id, g); + // g is unlocked + + if (!bsp) { + // queue up for later processing + g.lock(); + queue_for_later(connection_id, msg); + } else { + vote_status s = bsp->aggregate_vote(*msg); + emit(connection_id, s, msg); + + g.lock(); + if (auto& num = num_messages[connection_id]; num != 0) + --num; + + process_any_queued_for_later(g); + } } + }); } diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index b34039e096..193691a051 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -644,11 +644,8 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) { chain_config->vote_thread_pool_size = options.count("vote-threads") ? options.at("vote-threads").as() : 0; if (chain_config->vote_thread_pool_size == 0 && options.count("producer-name")) { chain_config->vote_thread_pool_size = config::default_vote_thread_pool_size; + ilog("Setting vote-threads to ${n} on producing node", ("n", chain_config->vote_thread_pool_size)); } - EOS_ASSERT( chain_config->vote_thread_pool_size > 1 || chain_config->vote_thread_pool_size == 0, plugin_config_exception, - "vote-threads ${num} must be greater than 1, or equal to 0 to disable. " - "Voting disabled if set to 0 (votes are not propagatged on P2P network).", - ("num", chain_config->vote_thread_pool_size) ); accept_votes = chain_config->vote_thread_pool_size > 0; } diff --git a/unittests/vote_processor_tests.cpp b/unittests/vote_processor_tests.cpp index 223e89de83..7c45fab7fb 100644 --- a/unittests/vote_processor_tests.cpp +++ b/unittests/vote_processor_tests.cpp @@ -157,16 +157,17 @@ BOOST_AUTO_TEST_CASE( vote_processor_test ) { vote_message_ptr vm1 = make_empty_message(make_block_id(1)); signaled = 0; vp.process_vote_message(1, vm1); - for (size_t i = 0; i < 50 && vp.size() < 1; ++i) { + for (size_t i = 0; i < 50 && vp.index_size() < 1; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds{5}); } - BOOST_CHECK(vp.size() == 1); + BOOST_TEST(vp.index_size() == 1); // move lib past block vp.notify_lib(2); - for (size_t i = 0; i < 50 && vp.size() > 0; ++i) { + vp.notify_new_block(); + for (size_t i = 0; i < 50 && vp.index_size() > 0; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds{5}); } - BOOST_CHECK(vp.size() == 0); + BOOST_TEST(vp.index_size() == 0); } { // process a valid vote signaled = 0; @@ -181,10 +182,10 @@ BOOST_AUTO_TEST_CASE( vote_processor_test ) { for (size_t i = 0; i < 50 && signaled.load() < 1; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds{5}); } - BOOST_CHECK(signaled.load() == 1); - BOOST_CHECK(1 == received_connection_id); - BOOST_CHECK(vote_status::success == received_vote_status); - BOOST_CHECK(m1 == received_vote_message); + BOOST_TEST(signaled.load() == 1); + BOOST_TEST(1 == received_connection_id); + BOOST_TEST(vote_status::success == received_vote_status); + BOOST_TEST(m1 == received_vote_message); } { // process an invalid signature vote signaled = 0; @@ -198,10 +199,10 @@ BOOST_AUTO_TEST_CASE( vote_processor_test ) { for (size_t i = 0; i < 50 && signaled.load() < 1; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds{5}); } - BOOST_CHECK(signaled.load() == 1); - BOOST_CHECK(1 == received_connection_id); - BOOST_CHECK(vote_status::invalid_signature == received_vote_status); - BOOST_CHECK(m1 == received_vote_message); + BOOST_TEST(signaled.load() == 1); + BOOST_TEST(1 == received_connection_id); + BOOST_TEST(vote_status::invalid_signature == received_vote_status); + BOOST_TEST(m1 == received_vote_message); } { // process two diff block votes signaled = 0; @@ -211,21 +212,33 @@ BOOST_AUTO_TEST_CASE( vote_processor_test ) { vote_message_ptr m1 = make_vote_message(bsp); vote_message_ptr m2 = make_vote_message(bsp2); vp.process_vote_message(2, m1); - vp.process_vote_message(2, m2); + vp.process_vote_message(3, m2); for (size_t i = 0; i < 5; ++i) { - if (vp.size() == 2) break; + if (vp.index_size() == 2) break; std::this_thread::sleep_for(std::chrono::milliseconds{5}); } - BOOST_CHECK(vp.size() == 2); + BOOST_TEST(vp.index_size() == 2); + std::this_thread::sleep_for(std::chrono::milliseconds{5}); // no votes for awhile + BOOST_TEST(signaled.load() == 0); add_to_forkdb(bsp); + vp.notify_new_block(); + for (size_t i = 0; i < 50 && signaled.load() < 2; ++i) { + std::this_thread::sleep_for(std::chrono::milliseconds{5}); + } + BOOST_TEST(signaled.load() == 1); + BOOST_TEST(2 == received_connection_id); + BOOST_TEST(vote_status::success == received_vote_status); + BOOST_CHECK(m1 == received_vote_message); + add_to_forkdb(bsp2); + vp.notify_new_block(); for (size_t i = 0; i < 50 && signaled.load() < 2; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds{5}); } - BOOST_CHECK(signaled.load() == 2); - BOOST_CHECK(2 == received_connection_id); - BOOST_CHECK(vote_status::success == received_vote_status); - BOOST_CHECK(m1 == received_vote_message || m2 == received_vote_message); + BOOST_TEST(signaled.load() == 2); + BOOST_TEST(3 == received_connection_id); + BOOST_TEST(vote_status::success == received_vote_status); + BOOST_CHECK(m2 == received_vote_message); } } From d80def5d428f8272eb013b9d430b593ac9c8c958 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 17 Apr 2024 14:36:29 -0500 Subject: [PATCH 43/60] GH-3 Fix use of iterator after erase --- libraries/chain/include/eosio/chain/vote_processor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 430faec8e2..2287774fe9 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -135,7 +135,7 @@ class vote_processor_t { return; vote v = *i; idx.erase(i); - auto bsp = get_block(i->msg->block_id, g); + auto bsp = get_block(v.msg->block_id, g); // g is unlocked if (bsp) { vote_status s = bsp->aggregate_vote(*v.msg); From 9f8da9e40c94a7b9ab08ee395b66a16cddfef2fa Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 16:33:02 -0400 Subject: [PATCH 44/60] Split up new finalizer update tests into new file. More test cleanup will follow in a separate PR. --- unittests/api_tests.cpp | 185 ------------------------ unittests/finalizer_update_tests.cpp | 202 +++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 185 deletions(-) create mode 100644 unittests/finalizer_update_tests.cpp diff --git a/unittests/api_tests.cpp b/unittests/api_tests.cpp index 138e203c83..78438cf339 100644 --- a/unittests/api_tests.cpp +++ b/unittests/api_tests.cpp @@ -3919,191 +3919,6 @@ BOOST_AUTO_TEST_CASE(initial_set_finalizer_test) { try { BOOST_CHECK_GT(lib, lib_after_transition); } FC_LOG_AND_RETHROW() } -// --------------------------------------------------------------------- -// Given a newly created `validating_tester`, trigger the transition to -// Savanna, and produce blocks until the transition is completed. -// --------------------------------------------------------------------- -static std::pair, std::vector> -transition_to_Savanna(validating_tester& t, size_t num_local_finalizers, size_t finset_size) { - uint32_t lib = 0; - signed_block_ptr lib_block; - auto c = t.control->irreversible_block().connect([&](const block_signal_params& t) { - const auto& [ block, id ] = t; - lib = block->block_num(); - lib_block = block; - }); - - t.produce_block(); - - // Create finalizer accounts - vector finalizers; - finalizers.reserve(num_local_finalizers); - for (size_t i=0; iblock_num() > lib) - critical_block = t.produce_block(); - - // Blocks after the critical block are proper IF blocks. - // ----------------------------------------------------- - auto first_proper_block = t.produce_block(); - BOOST_REQUIRE(first_proper_block->is_proper_svnn_block()); - - // wait till the first proper block becomes irreversible. Transition will be done then - // ----------------------------------------------------------------------------------- - signed_block_ptr pt_block = nullptr; // last value of this var is the first post-transition block - while(first_proper_block->block_num() > lib) { - pt_block = t.produce_block(); - BOOST_REQUIRE(pt_block->is_proper_svnn_block()); - } - - // lib must advance after 3 blocks - // ------------------------------- - t.produce_blocks(3); - BOOST_REQUIRE_EQUAL(lib, pt_block->block_num()); - - c.disconnect(); - return { finalizers, pubkeys0 }; -} - -// --------------------------------------------------------------------- -// checks that the active finalizer_policy for `block` matches the -// passed `generation` and `keys_span`. -// --------------------------------------------------------------------- -static void check_finalizer_policy(validating_tester& t, - const signed_block_ptr& block, - uint32_t generation, - std::span keys_span) { - auto finpol = t.active_finalizer_policy(block->calculate_id()); - BOOST_REQUIRE(!!finpol); - BOOST_REQUIRE_EQUAL(finpol->generation, generation); // new policy should not be active - // until after two 3-chains - BOOST_REQUIRE_EQUAL(keys_span.size(), finpol->finalizers.size()); - std::vector keys {keys_span.begin(), keys_span.end() }; - std::sort(keys.begin(), keys.end()); - - std::vector active_keys; - for (const auto& auth : finpol->finalizers) - active_keys.push_back(auth.public_key); - std::sort(active_keys.begin(), active_keys.end()); - for (size_t i=0; i keys_span) { - auto b = t.produce_block(); - check_finalizer_policy(t, b, generation, keys_span); -} - -// --------------------------------------------------------------------- -// verify that finalizer policy change via set_finalizer take 2 3-chains -// to take effect. -// --------------------------------------------------------------------- -BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { - validating_tester t; - size_t num_local_finalizers = 50; - size_t finset_size = 21; - - auto [finalizers, pubkeys0] = transition_to_Savanna(t, num_local_finalizers, finset_size); - assert(finalizers.size() == num_local_finalizers && pubkeys0.size() == finset_size); - - // run set_finalizers(), verify it becomes active after exactly two 3-chains - // ------------------------------------------------------------------------- - auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); - auto b0 = t.produce_block(); - check_finalizer_policy(t, b0, 1, pubkeys0); // new policy should only be active until after two 3-chains - - t.produce_blocks(2); - auto b3 = t.produce_block(); - check_finalizer_policy(t, b3, 1, pubkeys0); // one 3-chain - new policy still should not be active - - t.produce_blocks(1); - auto b5 = t.produce_block(); - check_finalizer_policy(t, b5, 1, pubkeys0); // one 3-chain + 2 blocks - new policy still should not be active - - auto b6 = t.produce_block(); - check_finalizer_policy(t, b6, 2, pubkeys1); // two 3-chain - new policy *should* be active - -} FC_LOG_AND_RETHROW() } - -// --------------------------------------------------------------------------- -// Test correct behavior when multiple finalizer policy changes are in-flight -// at the same time. -// --------------------------------------------------------------------------- -BOOST_AUTO_TEST_CASE(savanna_set_finalizer_multiple_test) { try { - validating_tester t; - size_t num_local_finalizers = 50; - size_t finset_size = 21; - - auto [finalizers, pubkeys0] = transition_to_Savanna(t, num_local_finalizers, finset_size); - - // run set_finalizers() twice in same block, verify only latest one becomes active - // ------------------------------------------------------------------------------- - auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); - auto pubkeys2 = t.set_active_finalizers({&finalizers[2], finset_size}); - auto b0 = t.produce_block(); - check_finalizer_policy(t, b0, 1, pubkeys0); // new policy should only be active until after two 3-chains - t.produce_blocks(4); - auto b5 = t.produce_block(); - check_finalizer_policy(t, b5, 1, pubkeys0); // new policy should only be active until after two 3-chains - auto b6 = t.produce_block(); - check_finalizer_policy(t, b6, 2, pubkeys2); // two 3-chain - new policy pubkeys2 *should* be active - - // run a test with multiple set_finlizers in-flight during the two 3-chains they - // take to become active - // ----------------------------------------------------------------------------- - auto pubkeys3 = t.set_active_finalizers({&finalizers[3], finset_size}); - b0 = t.produce_block(); - auto pubkeys4 = t.set_active_finalizers({&finalizers[4], finset_size}); - auto b1 = t.produce_block(); - auto b2 = t.produce_block(); - auto pubkeys5 = t.set_active_finalizers({&finalizers[5], finset_size}); - t.produce_blocks(2); - b5 = t.produce_block(); - check_finalizer_policy(t, b5, 2, pubkeys2); // 5 blocks after pubkeys3 (b5 - b0), pubkeys2 should still be active - b6 = t.produce_block(); - check_finalizer_policy(t, b6, 3, pubkeys3); // 6 blocks after pubkeys3 (b6 - b0), pubkeys3 should be active - auto b7 = t.produce_block(); - check_finalizer_policy(t, b7, 4, pubkeys4); // 6 blocks after pubkeys4 (b7 - b1), pubkeys4 should be active - - auto b8 = t.produce_block(); - check_finalizer_policy(t, b8, 4, pubkeys4); // 7 blocks after pubkeys4, pubkeys4 should still be active - auto b9 = t.produce_block(); - check_finalizer_policy(t, b9, 5, pubkeys5); // 6 blocks after pubkeys5 (b9 - b3), pubkeys5 should be active - - // and no further change - ensure_next_block_finalizer_policy(t, 5, pubkeys5); - ensure_next_block_finalizer_policy(t, 5, pubkeys5); - ensure_next_block_finalizer_policy(t, 5, pubkeys5); - ensure_next_block_finalizer_policy(t, 5, pubkeys5); - ensure_next_block_finalizer_policy(t, 5, pubkeys5); -} FC_LOG_AND_RETHROW() } - void test_finality_transition(const vector& accounts, const base_tester::finalizer_policy_input& input, bool lib_advancing_expected) { validating_tester t; diff --git a/unittests/finalizer_update_tests.cpp b/unittests/finalizer_update_tests.cpp new file mode 100644 index 0000000000..17cdd1666c --- /dev/null +++ b/unittests/finalizer_update_tests.cpp @@ -0,0 +1,202 @@ +#pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wsign-compare" + #include +#pragma GCC diagnostic pop + +#include + +using namespace eosio::chain::literals; +using namespace eosio::testing; +using namespace eosio::chain; + +// --------------------------------------------------------------------- +// Given a newly created `validating_tester`, trigger the transition to +// Savanna, and produce blocks until the transition is completed. +// --------------------------------------------------------------------- +static std::pair, std::vector> +transition_to_Savanna(validating_tester& t, size_t num_local_finalizers, size_t finset_size) { + uint32_t lib = 0; + signed_block_ptr lib_block; + auto c = t.control->irreversible_block().connect([&](const block_signal_params& t) { + const auto& [ block, id ] = t; + lib = block->block_num(); + lib_block = block; + }); + + t.produce_block(); + + // Create finalizer accounts + vector finalizers; + finalizers.reserve(num_local_finalizers); + for (size_t i=0; iblock_num() > lib) + critical_block = t.produce_block(); + + // Blocks after the critical block are proper IF blocks. + // ----------------------------------------------------- + auto first_proper_block = t.produce_block(); + BOOST_REQUIRE(first_proper_block->is_proper_svnn_block()); + + // wait till the first proper block becomes irreversible. Transition will be done then + // ----------------------------------------------------------------------------------- + signed_block_ptr pt_block = nullptr; // last value of this var is the first post-transition block + while(first_proper_block->block_num() > lib) { + pt_block = t.produce_block(); + BOOST_REQUIRE(pt_block->is_proper_svnn_block()); + } + + // lib must advance after 3 blocks + // ------------------------------- + t.produce_blocks(3); + BOOST_REQUIRE_EQUAL(lib, pt_block->block_num()); + + c.disconnect(); + return { finalizers, pubkeys0 }; +} + +/* + * register test suite `finalizer_update_tests` + */ +BOOST_AUTO_TEST_SUITE(finalizer_update_tests) + +// --------------------------------------------------------------------- +// checks that the active finalizer_policy for `block` matches the +// passed `generation` and `keys_span`. +// --------------------------------------------------------------------- +static void check_finalizer_policy(validating_tester& t, + const signed_block_ptr& block, + uint32_t generation, + std::span keys_span) { + auto finpol = t.active_finalizer_policy(block->calculate_id()); + BOOST_REQUIRE(!!finpol); + BOOST_REQUIRE_EQUAL(finpol->generation, generation); // new policy should not be active + // until after two 3-chains + BOOST_REQUIRE_EQUAL(keys_span.size(), finpol->finalizers.size()); + std::vector keys {keys_span.begin(), keys_span.end() }; + std::sort(keys.begin(), keys.end()); + + std::vector active_keys; + for (const auto& auth : finpol->finalizers) + active_keys.push_back(auth.public_key); + std::sort(active_keys.begin(), active_keys.end()); + for (size_t i=0; i keys_span) { + auto b = t.produce_block(); + check_finalizer_policy(t, b, generation, keys_span); +} + +// --------------------------------------------------------------------- +// verify that finalizer policy change via set_finalizer take 2 3-chains +// to take effect. +// --------------------------------------------------------------------- +BOOST_AUTO_TEST_CASE(savanna_set_finalizer_single_test) { try { + validating_tester t; + size_t num_local_finalizers = 50; + size_t finset_size = 21; + + auto [finalizers, pubkeys0] = transition_to_Savanna(t, num_local_finalizers, finset_size); + assert(finalizers.size() == num_local_finalizers && pubkeys0.size() == finset_size); + + // run set_finalizers(), verify it becomes active after exactly two 3-chains + // ------------------------------------------------------------------------- + auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); + auto b0 = t.produce_block(); + check_finalizer_policy(t, b0, 1, pubkeys0); // new policy should only be active until after two 3-chains + + t.produce_blocks(2); + auto b3 = t.produce_block(); + check_finalizer_policy(t, b3, 1, pubkeys0); // one 3-chain - new policy still should not be active + + t.produce_blocks(1); + auto b5 = t.produce_block(); + check_finalizer_policy(t, b5, 1, pubkeys0); // one 3-chain + 2 blocks - new policy still should not be active + + auto b6 = t.produce_block(); + check_finalizer_policy(t, b6, 2, pubkeys1); // two 3-chain - new policy *should* be active + +} FC_LOG_AND_RETHROW() } + +// --------------------------------------------------------------------------- +// Test correct behavior when multiple finalizer policy changes are in-flight +// at the same time. +// --------------------------------------------------------------------------- +BOOST_AUTO_TEST_CASE(savanna_set_finalizer_multiple_test) { try { + validating_tester t; + size_t num_local_finalizers = 50; + size_t finset_size = 21; + + auto [finalizers, pubkeys0] = transition_to_Savanna(t, num_local_finalizers, finset_size); + + // run set_finalizers() twice in same block, verify only latest one becomes active + // ------------------------------------------------------------------------------- + auto pubkeys1 = t.set_active_finalizers({&finalizers[1], finset_size}); + auto pubkeys2 = t.set_active_finalizers({&finalizers[2], finset_size}); + auto b0 = t.produce_block(); + check_finalizer_policy(t, b0, 1, pubkeys0); // new policy should only be active until after two 3-chains + t.produce_blocks(4); + auto b5 = t.produce_block(); + check_finalizer_policy(t, b5, 1, pubkeys0); // new policy should only be active until after two 3-chains + auto b6 = t.produce_block(); + check_finalizer_policy(t, b6, 2, pubkeys2); // two 3-chain - new policy pubkeys2 *should* be active + + // run a test with multiple set_finlizers in-flight during the two 3-chains they + // take to become active + // ----------------------------------------------------------------------------- + auto pubkeys3 = t.set_active_finalizers({&finalizers[3], finset_size}); + b0 = t.produce_block(); + auto pubkeys4 = t.set_active_finalizers({&finalizers[4], finset_size}); + auto b1 = t.produce_block(); + auto b2 = t.produce_block(); + auto pubkeys5 = t.set_active_finalizers({&finalizers[5], finset_size}); + t.produce_blocks(2); + b5 = t.produce_block(); + check_finalizer_policy(t, b5, 2, pubkeys2); // 5 blocks after pubkeys3 (b5 - b0), pubkeys2 should still be active + b6 = t.produce_block(); + check_finalizer_policy(t, b6, 3, pubkeys3); // 6 blocks after pubkeys3 (b6 - b0), pubkeys3 should be active + auto b7 = t.produce_block(); + check_finalizer_policy(t, b7, 4, pubkeys4); // 6 blocks after pubkeys4 (b7 - b1), pubkeys4 should be active + + auto b8 = t.produce_block(); + check_finalizer_policy(t, b8, 4, pubkeys4); // 7 blocks after pubkeys4, pubkeys4 should still be active + auto b9 = t.produce_block(); + check_finalizer_policy(t, b9, 5, pubkeys5); // 6 blocks after pubkeys5 (b9 - b3), pubkeys5 should be active + + // and no further change + ensure_next_block_finalizer_policy(t, 5, pubkeys5); + ensure_next_block_finalizer_policy(t, 5, pubkeys5); + ensure_next_block_finalizer_policy(t, 5, pubkeys5); + ensure_next_block_finalizer_policy(t, 5, pubkeys5); + ensure_next_block_finalizer_policy(t, 5, pubkeys5); +} FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 5d226c4a22db4080d591d5b57eea82a68c174d3d Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Wed, 17 Apr 2024 17:37:32 -0400 Subject: [PATCH 45/60] Add comment for `finalizer_policies` multimap. --- libraries/chain/include/eosio/chain/block_header_state.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 829a610902..c9df92c57f 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -56,6 +56,10 @@ struct block_header_state { // block time when proposer_policy will become active flat_map proposer_policies; + + // track in-flight finalizer policies. This is a `multimap` because the same block number + // can hold a `proposed` and a `pending` finalizer_policy. When that block becomes final, the + // `pending` becomes active, and the `proposed` becomes `pending` (for a different block number). flat_multimap finalizer_policies; From f5b58d8701072b8ada69fa4d703b067d1e4d4621 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 12:07:01 -0500 Subject: [PATCH 46/60] GH-46 Do not advance LIB past HEAD. It is possible that HEAD is not on the branch that contains the LIB block. If on a fork, then only advance to our HEAD and only if on the same branch as LIB block. --- libraries/chain/controller.cpp | 11 ++++---- libraries/chain/fork_database.cpp | 27 ++++++++++++++++++- .../include/eosio/chain/fork_database.hpp | 5 ++++ unittests/fork_db_tests.cpp | 11 ++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 2540e65318..801269bad4 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1398,17 +1398,18 @@ struct controller_impl { ("lib_num", lib_num)("bn", fork_db_root_block_num()) ); } - block_id_type irreversible_block_id = if_irreversible_block_id.load(); - uint32_t if_lib_num = block_header::num_from_id(irreversible_block_id); - const uint32_t new_lib_num = if_lib_num > 0 ? if_lib_num : fork_db_head_irreversible_blocknum(); + const block_id_type irreversible_block_id = if_irreversible_block_id.load(); + const uint32_t savanna_lib_num = block_header::num_from_id(irreversible_block_id); + const bool savanna = savanna_lib_num > 0; + const uint32_t new_lib_num = savanna ? savanna_lib_num : fork_db_head_irreversible_blocknum(); if( new_lib_num <= lib_num ) return; bool savanna_transistion_required = false; auto mark_branch_irreversible = [&, this](auto& forkdb) { - auto branch = (if_lib_num > 0) ? forkdb.fetch_branch( irreversible_block_id, new_lib_num) - : forkdb.fetch_branch( fork_db_head_or_pending(forkdb)->id(), new_lib_num ); + auto branch = savanna ? forkdb.fetch_head_branch( irreversible_block_id, new_lib_num) + : forkdb.fetch_branch( fork_db_head_or_pending(forkdb)->id(), new_lib_num ); try { auto should_process = [&](auto& bsp) { // Only make irreversible blocks that have been validated. Blocks in the fork database may not be on our current best head diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 6ec9d9034d..259c05b893 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -135,6 +135,7 @@ namespace eosio::chain { void remove_impl( const block_id_type& id ); branch_t fetch_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; block_branch_t fetch_block_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; + branch_t fetch_head_branch_impl( const block_id_type& b, uint32_t trim_after_block_num ) const; full_branch_t fetch_full_branch_impl(const block_id_type& h) const; bsp_t search_on_branch_impl( const block_id_type& h, uint32_t block_num, include_root_t include_root ) const; bsp_t search_on_head_branch_impl( uint32_t block_num, include_root_t include_root ) const; @@ -438,6 +439,30 @@ namespace eosio::chain { return result; } + template + fork_database_t::branch_t + fork_database_t::fetch_head_branch(const block_id_type& b, uint32_t trim_after_block_num) const { + std::lock_guard g(my->mtx); + return my->fetch_head_branch_impl(b, trim_after_block_num); + } + + template + fork_database_t::branch_t + fork_database_impl::fetch_head_branch_impl(const block_id_type& b, uint32_t trim_after_block_num) const { + branch_t result; + if (!head) + return result; + result.reserve(index.size()); + bool found_branch = false; + for (auto i = index.find(head->id()); i != index.end(); i = index.find((*i)->previous())) { + if ((*i)->id() == b) + found_branch = true; + if (found_branch && (*i)->block_num() <= trim_after_block_num) + result.push_back(*i); + } + return result; + } + template block_branch_t fork_database_t::fetch_block_branch(const block_id_type& h, uint32_t trim_after_block_num) const { @@ -594,7 +619,7 @@ namespace eosio::chain { for( uint32_t i = 0; i < remove_queue.size(); ++i ) { EOS_ASSERT( remove_queue[i] != head_id, fork_database_exception, - "removing the block and its descendants would remove the current head block" ); + "removing the block and its descendants would remove the current head block ${id}", ("id", head_id) ); auto previtr = previdx.lower_bound( remove_queue[i] ); while( previtr != previdx.end() && (*previtr)->previous() == remove_queue[i] ) { diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 39d37cef00..d6273e6e9b 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -96,6 +96,11 @@ namespace eosio::chain { branch_t fetch_branch( const block_id_type& h, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; block_branch_t fetch_block_branch( const block_id_type& h, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; + /** + * Similar to fetch_branch but only returns up to head or empty if b not on head branch. + */ + branch_t fetch_head_branch( const block_id_type& b, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; + /** * Returns full branch of block_header_state pointers including the root. * The order of the sequence is in descending block number order. diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp index 05d5bc8ebe..38d0709881 100644 --- a/unittests/fork_db_tests.cpp +++ b/unittests/fork_db_tests.cpp @@ -117,6 +117,17 @@ BOOST_AUTO_TEST_CASE(add_remove_test) try { BOOST_TEST(branch[1] == bsp12bb); BOOST_TEST(branch[2] == bsp11b); + // test fetch head branch + BOOST_TEST(forkdb.head()->id() == root->id()); + forkdb.mark_valid(bsp13a); + BOOST_TEST(forkdb.head()->id() == bsp13a->id()); + branch = forkdb.fetch_head_branch(bsp11c->id()); + BOOST_TEST(branch.empty()); // bsp11c not on bsp13a branch + branch = forkdb.fetch_head_branch(bsp12a->id()); + BOOST_REQUIRE(branch.size() == 2); + BOOST_TEST(branch[0] == bsp12a); + BOOST_TEST(branch[1] == bsp11a); + } FC_LOG_AND_RETHROW(); BOOST_AUTO_TEST_SUITE_END() From 47cf8a2c58d835a0971e91e2f5659f9aefb9783e Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Thu, 18 Apr 2024 13:14:58 -0400 Subject: [PATCH 47/60] Add `reserve` --- libraries/testing/tester.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/testing/tester.cpp b/libraries/testing/tester.cpp index fb617d99d9..8afed28e64 100644 --- a/libraries/testing/tester.cpp +++ b/libraries/testing/tester.cpp @@ -1255,7 +1255,9 @@ namespace eosio::testing { std::vector base_tester::set_active_finalizers(std::span names) { std::vector pubkeys; + pubkeys.reserve(names.size()); finalizer_policy_input input; + input.finalizers.reserve(names.size()); for (auto name : names) { auto [privkey, pubkey, pop] = get_bls_key(name); pubkeys.push_back(pubkey); From c98b22115d1285c8df6092f402cfd8e79dbce88b Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Thu, 18 Apr 2024 13:15:22 -0400 Subject: [PATCH 48/60] Add comments. --- libraries/chain/block_header_state.cpp | 3 +++ .../include/eosio/chain/block_header_state.hpp | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 13392e357e..89d8e878f5 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -130,6 +130,9 @@ void finish_next(const block_header_state& prev, auto lib = next_header_state.core.last_final_block_num(); auto it = prev.finalizer_policies.begin(); if (it->first > lib) { + // we have at least one `finalizer_policy` in our map, but none of these is + // due to become active of this block because lib has not advanced enough, so + // we just copy the multimap and keep using the same `active_finalizer_policy` next_header_state.finalizer_policies = prev.finalizer_policies; } else { while (it != prev.finalizer_policies.end() && it->first <= lib) { diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index c9df92c57f..a864f1b79a 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -20,6 +20,20 @@ namespace detail { struct schedule_info; }; constexpr uint32_t light_header_protocol_version_major = 1; constexpr uint32_t light_header_protocol_version_minor = 0; +// ------------------------------------------------------------------------------------------ +// this is used for tracking in-flight `finalizer_policy` changes, which have been requested, +// but are not activated yet. This struct is associated to a block_number in the +// `finalizer_policies` flat_multimap: `block_num => state, finalizer_policy` +// +// When state == proposed, the block_num identifies the block in which the new policy was +// proposed via set_finalizers. +// +// When that block becomes final, according to the block_header_state's finality_core, +// 1. the policy becomes pending +// 2. its key `block_num,` in the proposer_policies multimap, is the current block +// +// When this current block itself becomes final, the policy becomes active. +// ------------------------------------------------------------------------------------------ struct finalizer_policy_tracker { enum class state_t { proposed = 0, pending }; state_t state; From 432da13b980ed9a20225e674ed7faaa150f50d81 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 13:18:57 -0500 Subject: [PATCH 49/60] GH-46 Fix irreversible mode and simplify fetch_head_branch. --- libraries/chain/controller.cpp | 2 +- libraries/chain/fork_database.cpp | 14 ++++++-------- .../chain/include/eosio/chain/fork_database.hpp | 6 ++++-- unittests/fork_db_tests.cpp | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 801269bad4..fc5f36c00d 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1408,7 +1408,7 @@ struct controller_impl { bool savanna_transistion_required = false; auto mark_branch_irreversible = [&, this](auto& forkdb) { - auto branch = savanna ? forkdb.fetch_head_branch( irreversible_block_id, new_lib_num) + auto branch = savanna ? forkdb.fetch_head_branch( fork_db_head_or_pending(forkdb)->id(), irreversible_block_id) : forkdb.fetch_branch( fork_db_head_or_pending(forkdb)->id(), new_lib_num ); try { auto should_process = [&](auto& bsp) { diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 259c05b893..689e3301d2 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -135,7 +135,7 @@ namespace eosio::chain { void remove_impl( const block_id_type& id ); branch_t fetch_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; block_branch_t fetch_block_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; - branch_t fetch_head_branch_impl( const block_id_type& b, uint32_t trim_after_block_num ) const; + branch_t fetch_head_branch_impl( const block_id_type& h, const block_id_type& b ) const; full_branch_t fetch_full_branch_impl(const block_id_type& h) const; bsp_t search_on_branch_impl( const block_id_type& h, uint32_t block_num, include_root_t include_root ) const; bsp_t search_on_head_branch_impl( uint32_t block_num, include_root_t include_root ) const; @@ -441,23 +441,21 @@ namespace eosio::chain { template fork_database_t::branch_t - fork_database_t::fetch_head_branch(const block_id_type& b, uint32_t trim_after_block_num) const { + fork_database_t::fetch_head_branch(const block_id_type& h, const block_id_type& b) const { std::lock_guard g(my->mtx); - return my->fetch_head_branch_impl(b, trim_after_block_num); + return my->fetch_head_branch_impl(h, b); } template fork_database_t::branch_t - fork_database_impl::fetch_head_branch_impl(const block_id_type& b, uint32_t trim_after_block_num) const { + fork_database_impl::fetch_head_branch_impl(const block_id_type& h, const block_id_type& b) const { branch_t result; - if (!head) - return result; result.reserve(index.size()); bool found_branch = false; - for (auto i = index.find(head->id()); i != index.end(); i = index.find((*i)->previous())) { + for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { if ((*i)->id() == b) found_branch = true; - if (found_branch && (*i)->block_num() <= trim_after_block_num) + if (found_branch) result.push_back(*i); } return result; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index d6273e6e9b..dba160ff8e 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -97,9 +97,11 @@ namespace eosio::chain { block_branch_t fetch_block_branch( const block_id_type& h, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; /** - * Similar to fetch_branch but only returns up to head or empty if b not on head branch. + * 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 + * block states that are after block `b`. Returns empty if `b` not found on `h` branch. */ - branch_t fetch_head_branch( const block_id_type& b, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; + branch_t fetch_head_branch( const block_id_type& h, const block_id_type& b ) const; /** * Returns full branch of block_header_state pointers including the root. diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp index 38d0709881..99069821db 100644 --- a/unittests/fork_db_tests.cpp +++ b/unittests/fork_db_tests.cpp @@ -121,9 +121,9 @@ BOOST_AUTO_TEST_CASE(add_remove_test) try { BOOST_TEST(forkdb.head()->id() == root->id()); forkdb.mark_valid(bsp13a); BOOST_TEST(forkdb.head()->id() == bsp13a->id()); - branch = forkdb.fetch_head_branch(bsp11c->id()); + branch = forkdb.fetch_head_branch(forkdb.head()->id(), bsp11c->id()); BOOST_TEST(branch.empty()); // bsp11c not on bsp13a branch - branch = forkdb.fetch_head_branch(bsp12a->id()); + branch = forkdb.fetch_head_branch(forkdb.head()->id(), bsp12a->id()); BOOST_REQUIRE(branch.size() == 2); BOOST_TEST(branch[0] == bsp12a); BOOST_TEST(branch[1] == bsp11a); From 9498355120e2b76b3fb62d82deca21b2e87f524a Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 13:55:51 -0500 Subject: [PATCH 50/60] GH-16 Update DEEP_MIND_VERSION to spring 1 0. --- libraries/chain/deep_mind.cpp | 2 +- unittests/deep-mind/deep-mind.log | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/deep_mind.cpp b/libraries/chain/deep_mind.cpp index 308b047004..c3d57bdf14 100644 --- a/libraries/chain/deep_mind.cpp +++ b/libraries/chain/deep_mind.cpp @@ -50,7 +50,7 @@ namespace eosio::chain { void deep_mind_handler::on_startup(chainbase::database& db, uint32_t head_block_num) { // FIXME: We should probably feed that from CMake directly somehow ... - fc_dlog(_logger, "DEEP_MIND_VERSION leap 13 0"); + fc_dlog(_logger, "DEEP_MIND_VERSION spring 1 0"); fc_dlog(_logger, "ABIDUMP START ${block_num} ${global_sequence_num}", ("block_num", head_block_num) diff --git a/unittests/deep-mind/deep-mind.log b/unittests/deep-mind/deep-mind.log index ae0819ecaa..c9bfbd2d8b 100644 --- a/unittests/deep-mind/deep-mind.log +++ b/unittests/deep-mind/deep-mind.log @@ -17,7 +17,7 @@ DMLOG RLIMIT_OP ACCOUNT_USAGE INS {"owner":"eosio.prods","net_usage":{"last_ordi DMLOG RAM_OP 0 eosio.prods account add newaccount eosio.prods 2656 2656 DMLOG PERM_OP INS 0 7 {"usage_id":6,"parent":6,"owner":"eosio.prods","name":"prod.major","last_updated":"2020-01-01T00:00:00.000","auth":{"threshold":1,"keys":[],"accounts":[{"permission":{"actor":"eosio","permission":"active"},"weight":1}],"waits":[]}} DMLOG PERM_OP INS 0 8 {"usage_id":7,"parent":7,"owner":"eosio.prods","name":"prod.minor","last_updated":"2020-01-01T00:00:00.000","auth":{"threshold":1,"keys":[],"accounts":[{"permission":{"actor":"eosio","permission":"active"},"weight":1}],"waits":[]}} -DMLOG DEEP_MIND_VERSION leap 13 0 +DMLOG DEEP_MIND_VERSION spring 1 0 DMLOG ABIDUMP START 1 0 DMLOG ABIDUMP ABI eosio DmVvc2lvOjphYmkvMS4wBwxhY2NvdW50X25hbWUEbmFtZQ9wZXJtaXNzaW9uX25hbWUEbmFtZQthY3Rpb25fbmFtZQRuYW1lCnRhYmxlX25hbWUEbmFtZRN0cmFuc2FjdGlvbl9pZF90eXBlC2NoZWNrc3VtMjU2DWJsb2NrX2lkX3R5cGULY2hlY2tzdW0yNTYLd2VpZ2h0X3R5cGUGdWludDE2FhBwZXJtaXNzaW9uX2xldmVsAAIFYWN0b3IMYWNjb3VudF9uYW1lCnBlcm1pc3Npb24PcGVybWlzc2lvbl9uYW1lBmFjdGlvbgAEB2FjY291bnQMYWNjb3VudF9uYW1lBG5hbWULYWN0aW9uX25hbWUNYXV0aG9yaXphdGlvbhJwZXJtaXNzaW9uX2xldmVsW10EZGF0YQVieXRlcwlleHRlbnNpb24AAgR0eXBlBnVpbnQxNgRkYXRhBWJ5dGVzEnRyYW5zYWN0aW9uX2hlYWRlcgAGCmV4cGlyYXRpb24OdGltZV9wb2ludF9zZWMNcmVmX2Jsb2NrX251bQZ1aW50MTYQcmVmX2Jsb2NrX3ByZWZpeAZ1aW50MzITbWF4X25ldF91c2FnZV93b3Jkcwl2YXJ1aW50MzIQbWF4X2NwdV91c2FnZV9tcwV1aW50OAlkZWxheV9zZWMJdmFydWludDMyC3RyYW5zYWN0aW9uEnRyYW5zYWN0aW9uX2hlYWRlcgMUY29udGV4dF9mcmVlX2FjdGlvbnMIYWN0aW9uW10HYWN0aW9ucwhhY3Rpb25bXRZ0cmFuc2FjdGlvbl9leHRlbnNpb25zC2V4dGVuc2lvbltdDHByb2R1Y2VyX2tleQACDXByb2R1Y2VyX25hbWUMYWNjb3VudF9uYW1lEWJsb2NrX3NpZ25pbmdfa2V5CnB1YmxpY19rZXkRcHJvZHVjZXJfc2NoZWR1bGUAAgd2ZXJzaW9uBnVpbnQzMglwcm9kdWNlcnMOcHJvZHVjZXJfa2V5W10MYmxvY2tfaGVhZGVyAAkJdGltZXN0YW1wBnVpbnQzMghwcm9kdWNlcgxhY2NvdW50X25hbWUJY29uZmlybWVkBnVpbnQxNghwcmV2aW91cw1ibG9ja19pZF90eXBlEXRyYW5zYWN0aW9uX21yb290C2NoZWNrc3VtMjU2DGFjdGlvbl9tcm9vdAtjaGVja3N1bTI1NhBzY2hlZHVsZV92ZXJzaW9uBnVpbnQzMg1uZXdfcHJvZHVjZXJzEnByb2R1Y2VyX3NjaGVkdWxlPxFoZWFkZXJfZXh0ZW5zaW9ucwtleHRlbnNpb25bXQprZXlfd2VpZ2h0AAIDa2V5CnB1YmxpY19rZXkGd2VpZ2h0C3dlaWdodF90eXBlF3Blcm1pc3Npb25fbGV2ZWxfd2VpZ2h0AAIKcGVybWlzc2lvbhBwZXJtaXNzaW9uX2xldmVsBndlaWdodAt3ZWlnaHRfdHlwZQt3YWl0X3dlaWdodAACCHdhaXRfc2VjBnVpbnQzMgZ3ZWlnaHQLd2VpZ2h0X3R5cGUJYXV0aG9yaXR5AAQJdGhyZXNob2xkBnVpbnQzMgRrZXlzDGtleV93ZWlnaHRbXQhhY2NvdW50cxlwZXJtaXNzaW9uX2xldmVsX3dlaWdodFtdBXdhaXRzDXdhaXRfd2VpZ2h0W10KbmV3YWNjb3VudAAEB2NyZWF0b3IMYWNjb3VudF9uYW1lBG5hbWUMYWNjb3VudF9uYW1lBW93bmVyCWF1dGhvcml0eQZhY3RpdmUJYXV0aG9yaXR5B3NldGNvZGUABAdhY2NvdW50DGFjY291bnRfbmFtZQZ2bXR5cGUFdWludDgJdm12ZXJzaW9uBXVpbnQ4BGNvZGUFYnl0ZXMGc2V0YWJpAAIHYWNjb3VudAxhY2NvdW50X25hbWUDYWJpBWJ5dGVzCnVwZGF0ZWF1dGgABAdhY2NvdW50DGFjY291bnRfbmFtZQpwZXJtaXNzaW9uD3Blcm1pc3Npb25fbmFtZQZwYXJlbnQPcGVybWlzc2lvbl9uYW1lBGF1dGgJYXV0aG9yaXR5CmRlbGV0ZWF1dGgAAgdhY2NvdW50DGFjY291bnRfbmFtZQpwZXJtaXNzaW9uD3Blcm1pc3Npb25fbmFtZQhsaW5rYXV0aAAEB2FjY291bnQMYWNjb3VudF9uYW1lBGNvZGUMYWNjb3VudF9uYW1lBHR5cGULYWN0aW9uX25hbWULcmVxdWlyZW1lbnQPcGVybWlzc2lvbl9uYW1lCnVubGlua2F1dGgAAwdhY2NvdW50DGFjY291bnRfbmFtZQRjb2RlDGFjY291bnRfbmFtZQR0eXBlC2FjdGlvbl9uYW1lC2NhbmNlbGRlbGF5AAIOY2FuY2VsaW5nX2F1dGgQcGVybWlzc2lvbl9sZXZlbAZ0cnhfaWQTdHJhbnNhY3Rpb25faWRfdHlwZQdvbmVycm9yAAIJc2VuZGVyX2lkB3VpbnQxMjgIc2VudF90cngFYnl0ZXMHb25ibG9jawABBmhlYWRlcgxibG9ja19oZWFkZXIKAECemiJkuJoKbmV3YWNjb3VudAAAAABAJYqywgdzZXRjb2RlAAAAAAC4Y7LCBnNldGFiaQAAQMvaqGxS1Qp1cGRhdGVhdXRoAABAy9qorKJKCmRlbGV0ZWF1dGgAAAAALWsDp4sIbGlua2F1dGgAAEDL2sDp4tQKdW5saW5rYXV0aAAAvIkqRYWmQQtjYW5jZWxkZWxheQAAAADg0nvVpAdvbmVycm9yAAAAAAAiGs+kB29uYmxvY2sAAAAAAAA= DMLOG ABIDUMP END From 41eedfeee49d022d41ada4f74ab7012e49e64976 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 13:56:29 -0500 Subject: [PATCH 51/60] GH-16 Add apply chain_head function that takes a legacy and a savanna lambda. --- libraries/chain/controller.cpp | 37 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 386d627fd5..eab98e7733 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -131,6 +131,18 @@ R apply(const block_handle& bh, F&& f) { }, bh.internal()); } +template +R apply(const block_handle& bh, F&& f, S&& s) { + if constexpr (std::is_same_v) + std::visit(overloaded{[&](const block_state_legacy_ptr& head) { std::forward(f)(head); }, + [&](const block_state_ptr& head) { std::forward(s)(head); } + }, bh.internal()); + else + return std::visit(overloaded{[&](const block_state_legacy_ptr& head) -> R { return std::forward(f)(head); }, + [&](const block_state_ptr& head) -> R { return std::forward(s)(head); } + }, bh.internal()); +} + // apply savanna block_state template R apply_s(const block_handle& bh, F&& f) { @@ -3199,18 +3211,19 @@ struct controller_impl { if (auto* dm_logger = get_deep_mind_logger(false)) { auto fd = head_finality_data(); - apply_l(chain_head, [&](const auto& head) { - if (head->block->contains_header_extension(instant_finality_extension::extension_id())) { - assert(fd); - dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); - } else { - dm_logger->on_accepted_block(head); - } - }); - apply_s(chain_head, [&](const auto& head) { - assert(fd); - dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); - }); + apply(chain_head, + [&](const block_state_legacy_ptr& head) { + if (head->block->contains_header_extension(instant_finality_extension::extension_id())) { + assert(fd); + dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); + } else { + dm_logger->on_accepted_block(head); + } + }, + [&](const block_state_ptr& head) { + assert(fd); + dm_logger->on_accepted_block_v2(fork_db_root_block_num(), head->block, *fd); + }); } if (s == controller::block_status::incomplete) { From 317dd61face4e7da7f06ab30ae744622dbdf179a Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 14:55:00 -0500 Subject: [PATCH 52/60] GH-3 Remove unneeded if --- libraries/chain/controller.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 157f191ea8..d7fd7fb948 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3573,9 +3573,7 @@ struct controller_impl { // called from net threads and controller's thread pool void process_vote_message( uint32_t connection_id, const vote_message_ptr& vote ) { - if (conf.vote_thread_pool_size > 0) { - vote_processor.process_vote_message(connection_id, vote); - } + vote_processor.process_vote_message(connection_id, vote); } bool node_has_voted_if_finalizer(const block_id_type& id) const { From 955a72ae358e56838bc6e9095b3679d78100e9a2 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 14:55:59 -0500 Subject: [PATCH 53/60] GH-3 Move emit to controller.hpp and use in vote_processor --- libraries/chain/controller.cpp | 31 ----------------- .../chain/include/eosio/chain/controller.hpp | 33 ++++++++++++++++++- .../include/eosio/chain/vote_processor.hpp | 24 +------------- 3 files changed, 33 insertions(+), 55 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index d7fd7fb948..696b94f491 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1253,37 +1253,6 @@ struct controller_impl { }); } - /** - * Plugins / observers listening to signals emited might trigger - * errors and throw exceptions. Unless those exceptions are caught it could impact consensus and/or - * cause a node to fork. - * - * If it is ever desirable to let a signal handler bubble an exception out of this method - * a full audit of its uses needs to be undertaken. - * - */ - template - void emit( const Signal& s, Arg&& a ) { - try { - s( std::forward( a )); - } catch (std::bad_alloc& e) { - wlog( "std::bad_alloc: ${w}", ("w", e.what()) ); - throw e; - } catch (boost::interprocess::bad_alloc& e) { - wlog( "boost::interprocess::bad alloc: ${w}", ("w", e.what()) ); - throw e; - } catch ( controller_emit_signal_exception& e ) { - wlog( "controller_emit_signal_exception: ${details}", ("details", e.to_detail_string()) ); - throw e; - } catch ( fc::exception& e ) { - wlog( "fc::exception: ${details}", ("details", e.to_detail_string()) ); - } catch ( std::exception& e ) { - wlog( "std::exception: ${details}", ("details", e.what()) ); - } catch ( ... ) { - wlog( "signal handler threw exception" ); - } - } - void dmlog_applied_transaction(const transaction_trace_ptr& t, const signed_transaction* trx = nullptr) { // dmlog_applied_transaction is called by push_scheduled_transaction // where transient transactions are not possible, and by push_transaction diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index e8d1a5d1e3..8d7c3981ef 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -409,6 +409,37 @@ namespace eosio::chain { chainbase::database& mutable_db()const; std::unique_ptr my; - }; + }; // controller + + /** + * Plugins / observers listening to signals emited might trigger + * errors and throw exceptions. Unless those exceptions are caught it could impact consensus and/or + * cause a node to fork. + * + * If it is ever desirable to let a signal handler bubble an exception out of this method + * a full audit of its uses needs to be undertaken. + * + */ + template + void emit( const Signal& s, Arg&& a, const char* location = "" ) { + try { + s( std::forward( a )); + } catch (std::bad_alloc& e) { + wlog( "${l}std::bad_alloc: ${w}", ("l", location)("w", e.what()) ); + throw e; + } catch (boost::interprocess::bad_alloc& e) { + wlog( "${l}boost::interprocess::bad alloc: ${w}", ("l", location)("w", e.what()) ); + throw e; + } catch ( controller_emit_signal_exception& e ) { + wlog( "${l}controller_emit_signal_exception: ${details}", ("l", location)("details", e.to_detail_string()) ); + throw e; + } catch ( fc::exception& e ) { + wlog( "${l}fc::exception: ${details}", ("l", location)("details", e.to_detail_string()) ); + } catch ( std::exception& e ) { + wlog( "std::exception: ${details}", ("l", location)("details", e.what()) ); + } catch ( ... ) { + wlog( "${l}signal handler threw exception", ("l", location) ); + } + } } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 2287774fe9..655e65bccf 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -61,33 +61,11 @@ class vote_processor_t { named_thread_pool thread_pool; private: - template - void emit( const Signal& s, Arg&& a ) { - try { - s(std::forward(a)); - } catch (std::bad_alloc& e) { - wlog( "std::bad_alloc: ${w}", ("w", e.what()) ); - throw e; - } catch (boost::interprocess::bad_alloc& e) { - wlog( "boost::interprocess::bad alloc: ${w}", ("w", e.what()) ); - throw e; - } catch ( controller_emit_signal_exception& e ) { - wlog( "controller_emit_signal_exception: ${details}", ("details", e.to_detail_string()) ); - throw e; - } catch ( fc::exception& e ) { - wlog( "fc::exception: ${details}", ("details", e.to_detail_string()) ); - } catch ( std::exception& e ) { - wlog( "std::exception: ${details}", ("details", e.what()) ); - } catch ( ... ) { - wlog( "signal handler threw exception" ); - } - } - // called with unlocked mtx void emit(uint32_t connection_id, vote_status status, const vote_message_ptr& msg) { if (connection_id != 0) { // this nodes vote was already signaled if (status != vote_status::duplicate) { // don't bother emitting duplicates - emit( vote_signal, std::tuple{connection_id, status, std::cref(msg)} ); + chain::emit( vote_signal, std::tuple{connection_id, status, std::cref(msg)}, "vote " ); } } } From 4a759b35fe2c0a58e8a692c74497afe7a967ffc2 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 14:56:25 -0500 Subject: [PATCH 54/60] GH-3 Use same value as default for producer --- tests/TestHarness/Cluster.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestHarness/Cluster.py b/tests/TestHarness/Cluster.py index 1f4a11b7e0..40f127368a 100644 --- a/tests/TestHarness/Cluster.py +++ b/tests/TestHarness/Cluster.py @@ -257,7 +257,7 @@ def launch(self, pnodes=1, unstartedNodes=0, totalNodes=1, prodCount=21, topo="m argsArr.append("--nogen") nodeosArgs="" if "--vote-threads" not in extraNodeosArgs: - nodeosArgs += " --vote-threads 3" + nodeosArgs += " --vote-threads 4" if "--max-transaction-time" not in extraNodeosArgs: nodeosArgs += " --max-transaction-time -1" if "--abi-serializer-max-time-ms" not in extraNodeosArgs: From 51503c404c275f94e762891fc2914ae794235c11 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 14:56:43 -0500 Subject: [PATCH 55/60] GH-3 Use unordered_map --- .../chain/include/eosio/chain/vote_processor.hpp | 13 +++++++------ plugins/net_plugin/net_plugin.cpp | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 655e65bccf..32141a2a2a 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -10,6 +10,8 @@ #include #include +#include + namespace eosio::chain { /** @@ -17,7 +19,7 @@ namespace eosio::chain { */ class vote_processor_t { // Even 3000 vote structs are less than 1MB per connection. - // 2500 is should never be reached unless a specific connection is sending garbage. + // 2500 should never be reached unless a specific connection is sending garbage. static constexpr size_t max_votes_per_connection = 2500; // If we have not processed a vote in this amount of time, give up on it. static constexpr fc::microseconds too_old = fc::seconds(5); @@ -51,8 +53,8 @@ class vote_processor_t { std::mutex mtx; vote_index_type index; block_state_ptr last_bsp; - // connection, count of messages - std::map num_messages; + // connection, count of messages + std::unordered_map num_messages; std::atomic lib{0}; std::atomic largest_known_block_num{0}; @@ -99,7 +101,7 @@ class vote_processor_t { index.insert(vote{.connection_id = connection_id, .received = now, .msg = msg}); } - // called with locked mtx + // called with locked mtx, returns with a locked mutex void process_any_queued_for_later(std::unique_lock& g) { if (index.empty()) return; @@ -107,11 +109,10 @@ class vote_processor_t { remove_before_lib(); auto& idx = index.get(); std::vector unprocessed; - unprocessed.reserve(std::min(21u, idx.size())); // maybe increase if we increase # of finalizers from 21 for (auto i = idx.begin(); i != idx.end();) { if (stopped) return; - vote v = *i; + vote v = std::move(*i); idx.erase(i); auto bsp = get_block(v.msg->block_id, g); // g is unlocked diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index cb67c5c56a..815b2815e9 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -535,7 +535,7 @@ namespace eosio { void on_accepted_block_header( const signed_block_ptr& block, const block_id_type& id ); void on_accepted_block(); - void on_voted_block ( uint32_t connection_id, vote_status stauts, const vote_message_ptr& vote ); + void on_voted_block( uint32_t connection_id, vote_status stauts, const vote_message_ptr& vote ); void transaction_ack(const std::pair&); void on_irreversible_block( const block_id_type& id, uint32_t block_num ); From b694aad3e04027355264271a4fa0c30f858a0a0d Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 15:05:05 -0500 Subject: [PATCH 56/60] GH-3 Add better log message --- libraries/chain/include/eosio/chain/vote_processor.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 32141a2a2a..9b9927d9b9 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -39,7 +39,7 @@ class vote_processor_t { using vote_index_type = boost::multi_index_container< vote, indexed_by< - ordered_non_unique< tag, const_mem_fun, std::greater<> >, + ordered_non_unique< tag, const_mem_fun, std::greater<> >, // decending ordered_non_unique< tag, member >, ordered_non_unique< tag, member > > @@ -213,13 +213,14 @@ class vote_processor_t { std::unique_lock g(mtx); if (reset_num_messages) num_messages.clear(); - if (++num_messages[connection_id] > max_votes_per_connection) { + if (auto& num_msgs = ++num_messages[connection_id]; num_msgs > max_votes_per_connection) { remove_connection(connection_id); g.unlock(); // drop, too many from this connection to process, consider connection invalid // don't clear num_messages[connection_id] so we keep reporting max_exceeded until index is drained - elog("Exceeded max votes per connection for ${c}", ("c", connection_id)); + ilog("Exceeded max votes per connection ${n} > ${max} for ${c}", + ("n", num_msgs)("max", max_votes_per_connection)("c", connection_id)); emit(connection_id, vote_status::max_exceeded, msg); } else { block_state_ptr bsp = get_block(msg->block_id, g); From 344b778f3d4bf21d35ece02bafe54934893efb6e Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 15:11:23 -0500 Subject: [PATCH 57/60] GH-3 Do not clear num_messages if there are votes in the index to be processed --- libraries/chain/include/eosio/chain/vote_processor.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 9b9927d9b9..6b6e41d2f2 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -207,11 +207,10 @@ class vote_processor_t { if (stopped) return; auto num_queued_votes = --queued_votes; - bool reset_num_messages = num_queued_votes == 0; // caught up, so clear num_messages if (block_header::num_from_id(msg->block_id) <= lib.load(std::memory_order_relaxed)) return; // ignore any votes lower than lib std::unique_lock g(mtx); - if (reset_num_messages) + if (num_queued_votes == 0 && index.empty()) // caught up, clear num_messages num_messages.clear(); if (auto& num_msgs = ++num_messages[connection_id]; num_msgs > max_votes_per_connection) { remove_connection(connection_id); From ecc8cd47dd09c2c9c702c7b6ed1bc13c802427ce Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 15:26:40 -0500 Subject: [PATCH 58/60] GH-3 More descriptive emit logs --- libraries/chain/controller.cpp | 32 +++++++++---------- .../chain/include/eosio/chain/controller.hpp | 14 ++++---- .../include/eosio/chain/vote_processor.hpp | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 696b94f491..a61bcc1413 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1417,7 +1417,7 @@ struct controller_impl { for( auto bitr = branch.rbegin(); bitr != branch.rend() && should_process(*bitr); ++bitr ) { apply_irreversible_block(forkdb, *bitr); - emit( irreversible_block, std::tie((*bitr)->block, (*bitr)->id()) ); + emit( irreversible_block, std::tie((*bitr)->block, (*bitr)->id()), __FILE__, __LINE__ ); // blog.append could fail due to failures like running out of space. // Do it before commit so that in case it throws, DB can be rolled back. @@ -2537,7 +2537,7 @@ struct controller_impl { pending->_block_report.total_elapsed_time += trace->elapsed; pending->_block_report.total_time += trace->elapsed; dmlog_applied_transaction(trace); - emit( applied_transaction, std::tie(trace, trx->packed_trx()) ); + emit( applied_transaction, std::tie(trace, trx->packed_trx()), __FILE__, __LINE__ ); undo_session.squash(); return trace; } @@ -2602,7 +2602,7 @@ struct controller_impl { trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); dmlog_applied_transaction(trace); - emit( applied_transaction, std::tie(trace, trx->packed_trx()) ); + emit( applied_transaction, std::tie(trace, trx->packed_trx()), __FILE__, __LINE__ ); trx_context.squash(); undo_session.squash(); @@ -2646,7 +2646,7 @@ struct controller_impl { trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); trace->elapsed = fc::time_point::now() - start; dmlog_applied_transaction(trace); - emit( applied_transaction, std::tie(trace, trx->packed_trx()) ); + emit( applied_transaction, std::tie(trace, trx->packed_trx()), __FILE__, __LINE__ ); undo_session.squash(); pending->_block_report.total_net_usage += trace->net_usage; if( trace->receipt ) pending->_block_report.total_cpu_usage_us += trace->receipt->cpu_usage_us; @@ -2690,12 +2690,12 @@ struct controller_impl { trace->account_ram_delta = account_delta( gtrx.payer, trx_removal_ram_delta ); dmlog_applied_transaction(trace); - emit( applied_transaction, std::tie(trace, trx->packed_trx()) ); + emit( applied_transaction, std::tie(trace, trx->packed_trx()), __FILE__, __LINE__ ); undo_session.squash(); } else { dmlog_applied_transaction(trace); - emit( applied_transaction, std::tie(trace, trx->packed_trx()) ); + emit( applied_transaction, std::tie(trace, trx->packed_trx()), __FILE__, __LINE__ ); } pending->_block_report.total_net_usage += trace->net_usage; @@ -2834,7 +2834,7 @@ struct controller_impl { } dmlog_applied_transaction(trace, &trn); - emit(applied_transaction, std::tie(trace, trx->packed_trx())); + emit( applied_transaction, std::tie(trace, trx->packed_trx()), __FILE__, __LINE__ ); } } @@ -2878,7 +2878,7 @@ struct controller_impl { if (!trx->is_transient()) { dmlog_applied_transaction(trace); - emit(applied_transaction, std::tie(trace, trx->packed_trx())); + emit( applied_transaction, std::tie(trace, trx->packed_trx()), __FILE__, __LINE__ ); pending->_block_report.total_net_usage += trace->net_usage; if( trace->receipt ) pending->_block_report.total_cpu_usage_us += trace->receipt->cpu_usage_us; @@ -2899,7 +2899,7 @@ struct controller_impl { { EOS_ASSERT( !pending, block_validate_exception, "pending block already exists" ); - emit( block_start, chain_head.block_num() + 1 ); + emit( block_start, chain_head.block_num() + 1, __FILE__, __LINE__ ); // at block level, no transaction specific logging is possible if (auto dm_logger = get_deep_mind_logger(false)) { @@ -3144,7 +3144,7 @@ struct controller_impl { const auto& bsp = std::get>(cb.bsp.internal()); if( s == controller::block_status::incomplete ) { forkdb.add( bsp, mark_valid_t::yes, ignore_duplicate_t::no ); - emit( accepted_block_header, std::tie(bsp->block, bsp->id()) ); + emit( accepted_block_header, std::tie(bsp->block, bsp->id()), __FILE__, __LINE__ ); } else { assert(s != controller::block_status::irreversible); forkdb.mark_valid( bsp ); @@ -3154,7 +3154,7 @@ struct controller_impl { } chain_head = block_handle{cb.bsp}; - emit( accepted_block, std::tie(chain_head.block(), chain_head.id()) ); + emit( accepted_block, std::tie(chain_head.block(), chain_head.id()), __FILE__, __LINE__ ); apply(chain_head, [&](const auto& head) { #warning todo: support deep_mind_logger even when in IF mode @@ -3571,7 +3571,7 @@ struct controller_impl { my_finalizers.maybe_vote( *bsp->active_finalizer_policy, bsp, bsp->strong_digest, [&](const vote_message_ptr& vote) { // net plugin subscribed to this signal. it will broadcast the vote message on receiving the signal - emit(voted_block, std::tuple{uint32_t{0}, vote_status::success, std::cref(vote)}); + emit(voted_block, std::tuple{uint32_t{0}, vote_status::success, std::cref(vote)}, __FILE__, __LINE__); // also aggregate our own vote into the pending_qc for this block, 0 connection_id indicates our own vote process_vote_message(0, vote); @@ -3862,7 +3862,7 @@ struct controller_impl { if constexpr (std::is_same_v>) forkdb.add( bsp, mark_valid_t::no, ignore_duplicate_t::yes ); - emit( accepted_block_header, std::tie(bsp->block, bsp->id()) ); + emit( accepted_block_header, std::tie(bsp->block, bsp->id()), __FILE__, __LINE__ ); }; fork_db.apply(do_accept_block); @@ -3900,7 +3900,7 @@ struct controller_impl { trusted_producer_light_validation = true; }; - emit( accepted_block_header, std::tie(bsp->block, bsp->id()) ); + emit( accepted_block_header, std::tie(bsp->block, bsp->id()), __FILE__, __LINE__ ); if( read_mode != db_read_mode::IRREVERSIBLE ) { if constexpr (std::is_same_v>) @@ -3949,7 +3949,7 @@ struct controller_impl { }); } - emit(accepted_block_header, std::tie(bsp->block, bsp->id())); + emit( accepted_block_header, std::tie(bsp->block, bsp->id()), __FILE__, __LINE__ ); controller::block_report br; if (s == controller::block_status::irreversible) { @@ -3957,7 +3957,7 @@ struct controller_impl { // On replay, log_irreversible is not called and so no irreversible_block signal is emitted. // So emit it explicitly here. - emit(irreversible_block, std::tie(bsp->block, bsp->id())); + emit( irreversible_block, std::tie(bsp->block, bsp->id()), __FILE__, __LINE__ ); if (!skip_db_sessions(s)) { db.commit(bsp->block_num()); diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 8d7c3981ef..6281e35fb7 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -421,24 +421,24 @@ namespace eosio::chain { * */ template - void emit( const Signal& s, Arg&& a, const char* location = "" ) { + void emit( const Signal& s, Arg&& a, const char* file, uint32_t line ) { try { s( std::forward( a )); } catch (std::bad_alloc& e) { - wlog( "${l}std::bad_alloc: ${w}", ("l", location)("w", e.what()) ); + wlog( "${f}:${l} std::bad_alloc: ${w}", ("f", file)("l", line)("w", e.what()) ); throw e; } catch (boost::interprocess::bad_alloc& e) { - wlog( "${l}boost::interprocess::bad alloc: ${w}", ("l", location)("w", e.what()) ); + wlog( "${f}:${l} boost::interprocess::bad alloc: ${w}", ("f", file)("l", line)("w", e.what()) ); throw e; } catch ( controller_emit_signal_exception& e ) { - wlog( "${l}controller_emit_signal_exception: ${details}", ("l", location)("details", e.to_detail_string()) ); + wlog( "${f}:${l} controller_emit_signal_exception: ${details}", ("f", file)("l", line)("details", e.to_detail_string()) ); throw e; } catch ( fc::exception& e ) { - wlog( "${l}fc::exception: ${details}", ("l", location)("details", e.to_detail_string()) ); + wlog( "${f}:${l} fc::exception: ${details}", ("f", file)("l", line)("details", e.to_detail_string()) ); } catch ( std::exception& e ) { - wlog( "std::exception: ${details}", ("l", location)("details", e.what()) ); + wlog( "std::exception: ${details}", ("f", file)("l", line)("details", e.what()) ); } catch ( ... ) { - wlog( "${l}signal handler threw exception", ("l", location) ); + wlog( "${f}:${l} signal handler threw exception", ("f", file)("l", line) ); } } diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index 6b6e41d2f2..d35cd1ccb3 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -67,7 +67,7 @@ class vote_processor_t { void emit(uint32_t connection_id, vote_status status, const vote_message_ptr& msg) { if (connection_id != 0) { // this nodes vote was already signaled if (status != vote_status::duplicate) { // don't bother emitting duplicates - chain::emit( vote_signal, std::tuple{connection_id, status, std::cref(msg)}, "vote " ); + chain::emit( vote_signal, std::tuple{connection_id, status, std::cref(msg)}, __FILE__, __LINE__ ); } } } From 4d09933af5637426e597ca1773e68cad94152b3c Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 15:32:32 -0500 Subject: [PATCH 59/60] GH-46 Rename fetch_head_branch to fetch_branch --- libraries/chain/controller.cpp | 2 +- libraries/chain/fork_database.cpp | 8 ++++---- libraries/chain/include/eosio/chain/fork_database.hpp | 2 +- unittests/fork_db_tests.cpp | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index fc5f36c00d..031691b64b 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1408,7 +1408,7 @@ struct controller_impl { bool savanna_transistion_required = false; auto mark_branch_irreversible = [&, this](auto& forkdb) { - auto branch = savanna ? forkdb.fetch_head_branch( fork_db_head_or_pending(forkdb)->id(), irreversible_block_id) + auto branch = savanna ? forkdb.fetch_branch( fork_db_head_or_pending(forkdb)->id(), irreversible_block_id) : forkdb.fetch_branch( fork_db_head_or_pending(forkdb)->id(), new_lib_num ); try { auto should_process = [&](auto& bsp) { diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 689e3301d2..ff8d81e253 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -135,7 +135,7 @@ namespace eosio::chain { void remove_impl( const block_id_type& id ); branch_t fetch_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; block_branch_t fetch_block_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; - branch_t fetch_head_branch_impl( const block_id_type& h, const block_id_type& b ) const; + branch_t fetch_branch_impl( const block_id_type& h, const block_id_type& b ) const; full_branch_t fetch_full_branch_impl(const block_id_type& h) const; bsp_t search_on_branch_impl( const block_id_type& h, uint32_t block_num, include_root_t include_root ) const; bsp_t search_on_head_branch_impl( uint32_t block_num, include_root_t include_root ) const; @@ -441,14 +441,14 @@ namespace eosio::chain { template fork_database_t::branch_t - fork_database_t::fetch_head_branch(const block_id_type& h, const block_id_type& b) const { + fork_database_t::fetch_branch(const block_id_type& h, const block_id_type& b) const { std::lock_guard g(my->mtx); - return my->fetch_head_branch_impl(h, b); + return my->fetch_branch_impl(h, b); } template fork_database_t::branch_t - fork_database_impl::fetch_head_branch_impl(const block_id_type& h, const block_id_type& b) const { + fork_database_impl::fetch_branch_impl(const block_id_type& h, const block_id_type& b) const { branch_t result; result.reserve(index.size()); bool found_branch = false; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index dba160ff8e..981967eb97 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -101,7 +101,7 @@ namespace eosio::chain { * root block (exclusive) to the block with an id of `h` (inclusive) by removing any * block states that are after block `b`. Returns empty if `b` not found on `h` branch. */ - branch_t fetch_head_branch( const block_id_type& h, const block_id_type& b ) const; + branch_t fetch_branch( const block_id_type& h, const block_id_type& b ) const; /** * Returns full branch of block_header_state pointers including the root. diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp index 99069821db..b9d024aa57 100644 --- a/unittests/fork_db_tests.cpp +++ b/unittests/fork_db_tests.cpp @@ -117,13 +117,13 @@ BOOST_AUTO_TEST_CASE(add_remove_test) try { BOOST_TEST(branch[1] == bsp12bb); BOOST_TEST(branch[2] == bsp11b); - // test fetch head branch + // test fetch branch providing head and lib BOOST_TEST(forkdb.head()->id() == root->id()); forkdb.mark_valid(bsp13a); BOOST_TEST(forkdb.head()->id() == bsp13a->id()); - branch = forkdb.fetch_head_branch(forkdb.head()->id(), bsp11c->id()); + branch = forkdb.fetch_branch(forkdb.head()->id(), bsp11c->id()); BOOST_TEST(branch.empty()); // bsp11c not on bsp13a branch - branch = forkdb.fetch_head_branch(forkdb.head()->id(), bsp12a->id()); + branch = forkdb.fetch_branch(forkdb.head()->id(), bsp12a->id()); BOOST_REQUIRE(branch.size() == 2); BOOST_TEST(branch[0] == bsp12a); BOOST_TEST(branch[1] == bsp11a); From 8d3a83821b9d3e6e0c2fa6a6b67a93f40746dac8 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 15:56:08 -0500 Subject: [PATCH 60/60] GH-3 Fix spelling --- libraries/chain/include/eosio/chain/vote_processor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/vote_processor.hpp b/libraries/chain/include/eosio/chain/vote_processor.hpp index d35cd1ccb3..6db78e286e 100644 --- a/libraries/chain/include/eosio/chain/vote_processor.hpp +++ b/libraries/chain/include/eosio/chain/vote_processor.hpp @@ -39,7 +39,7 @@ class vote_processor_t { using vote_index_type = boost::multi_index_container< vote, indexed_by< - ordered_non_unique< tag, const_mem_fun, std::greater<> >, // decending + ordered_non_unique< tag, const_mem_fun, std::greater<> >, // descending ordered_non_unique< tag, member >, ordered_non_unique< tag, member > >