From 472b1d4af527fb5e86ffd9b7eb9dba818d594299 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 8 Feb 2024 14:10:40 -0600 Subject: [PATCH 01/40] GH-2125 Optimize fetch_block_branch --- libraries/chain/controller.cpp | 4 +-- libraries/chain/fork_database.cpp | 31 ++++++++++++++----- .../include/eosio/chain/fork_database.hpp | 5 ++- programs/leap-util/actions/blocklog.cpp | 2 +- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index bb46a5058e..c679f8425b 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1339,13 +1339,13 @@ struct controller_impl { forkdb.reset( *head ); } else if( !except_ptr && !check_shutdown() && forkdb.head() ) { auto head_block_num = head->block_num(); - auto branch = forkdb.fetch_branch( forkdb.head()->id() ); + auto branch = fork_db.fetch_branch_from_head(); int rev = 0; for( auto i = branch.rbegin(); i != branch.rend(); ++i ) { if( check_shutdown() ) break; if( (*i)->block_num() <= head_block_num ) continue; ++rev; - replay_push_block( (*i)->block, controller::block_status::validated ); + replay_push_block( *i, controller::block_status::validated ); } ilog( "${n} reversible blocks replayed", ("n",rev) ); } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 1d6f920178..272b88957a 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -72,6 +72,7 @@ namespace eosio::chain { void advance_root_impl( const block_id_type& id ); void remove_impl( const block_id_type& id ); branch_type 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; bsp search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; void mark_valid_impl( const bsp& h ); branch_type_pair fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; @@ -420,6 +421,25 @@ namespace eosio::chain { return result; } + template + block_branch_t + fork_database_t::fetch_block_branch(const block_id_type& h, uint32_t trim_after_block_num) const { + std::lock_guard g(my->mtx); + return my->fetch_block_branch_impl(h, trim_after_block_num); + } + + template + block_branch_t + fork_database_impl::fetch_block_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { + block_branch_t result; + for (auto s = get_block_impl(h); s; s = get_block_impl(s->previous())) { + if (s->block_num() <= trim_after_block_num) + result.push_back(s->block); + } + + return result; + } + template bsp fork_database_t::search_on_branch( const block_id_type& h, uint32_t block_num ) const { std::lock_guard g( my->mtx ); @@ -648,15 +668,10 @@ namespace eosio::chain { }); } - std::vector fork_database::fetch_branch_from_head() { - std::vector r; - apply([&](auto& forkdb) { - auto branch = forkdb.fetch_branch(forkdb.head()->id()); - r.reserve(branch.size()); - for (auto& b : branch) - r.push_back(b->block); + block_branch_t fork_database::fetch_branch_from_head() const { + return apply([&](auto& forkdb) { + return forkdb.fetch_block_branch(forkdb.head()->id()); }); - return r; } // do class instantiations diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index c9ad269914..56b17744f9 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -10,6 +10,8 @@ namespace eosio::chain { template struct fork_database_impl; + using block_branch_t = deque; + /** * @class fork_database_t * @brief manages light-weight state for all potential unconfirmed forks @@ -85,6 +87,7 @@ namespace eosio::chain { * A block with an id of `h` must exist in the fork database otherwise this method will throw an exception. */ branch_type 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; /** @@ -131,7 +134,7 @@ namespace eosio::chain { void switch_from_legacy(); // see fork_database_t::fetch_branch(forkdb->head()->id()) - std::vector fetch_branch_from_head(); + block_branch_t fetch_branch_from_head() const; template R apply(const F& f) { diff --git a/programs/leap-util/actions/blocklog.cpp b/programs/leap-util/actions/blocklog.cpp index 1807081b59..869d25732f 100644 --- a/programs/leap-util/actions/blocklog.cpp +++ b/programs/leap-util/actions/blocklog.cpp @@ -266,7 +266,7 @@ int blocklog_actions::read_log() { opt->first_block = block_logger.first_block_num(); } - std::vector fork_db_branch; + block_branch_t fork_db_branch; if(std::filesystem::exists(std::filesystem::path(opt->blocks_dir) / config::reversible_blocks_dir_name / config::forkdb_filename)) { ilog("opening fork_db"); From 7c19670b022647f3ba5b72d84301d98324cbce60 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 13 Feb 2024 11:37:18 -0600 Subject: [PATCH 02/40] GH-2125 Update best fork logic of fork_database. --- libraries/chain/controller.cpp | 16 +- libraries/chain/fork_database.cpp | 312 ++++++++++-------- .../include/eosio/chain/fork_database.hpp | 120 ++++++- plugins/chain_plugin/chain_plugin.cpp | 1 - programs/leap-util/actions/snapshot.cpp | 1 - unittests/forked_tests.cpp | 1 - .../unapplied_transaction_queue_tests.cpp | 2 +- 7 files changed, 290 insertions(+), 163 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index c679f8425b..960c22599f 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -951,7 +951,7 @@ struct controller_impl { // --------------- access fork_db root ---------------------------------------------------------------------- bool fork_db_has_root() const { - return fork_db.apply([&](const auto& forkdb) { return !!forkdb.root(); }); + return fork_db.apply([&](const auto& forkdb) { return !!forkdb.has_root(); }); } block_id_type fork_db_root_block_id() const { @@ -984,9 +984,9 @@ struct controller_impl { return prev->block_num(); } - void fork_db_reset_to_head() { + void fork_db_reset_root_to_head() { return fork_db.apply([&](auto& forkdb) { - forkdb.reset(*forkdb.chain_head); + forkdb.reset_root(*forkdb.chain_head); }); } @@ -1279,7 +1279,7 @@ struct controller_impl { void replay(std::function check_shutdown) { auto blog_head = blog.head(); if( !fork_db_has_root() ) { - fork_db_reset_to_head(); + fork_db_reset_root_to_head(); if (!blog_head) return; } @@ -1314,7 +1314,7 @@ struct controller_impl { ilog( "fork database head ${h}, root ${r}", ("h", pending_head->block_num())( "r", forkdb.root()->block_num() ) ); if( pending_head->block_num() < head->block_num() || head->block_num() < forkdb.root()->block_num() ) { ilog( "resetting fork database with new last irreversible block as the new root: ${id}", ("id", head->id()) ); - forkdb.reset( *head ); + forkdb.reset_root( *head ); } else if( head->block_num() != forkdb.root()->block_num() ) { auto new_root = forkdb.search_on_branch( pending_head->id(), head->block_num() ); EOS_ASSERT( new_root, fork_database_exception, @@ -1336,7 +1336,7 @@ struct controller_impl { if (snapshot_head_block != 0 && !blog_head) { // loading from snapshot without a block log so fork_db can't be considered valid - forkdb.reset( *head ); + forkdb.reset_root( *head ); } else if( !except_ptr && !check_shutdown() && forkdb.head() ) { auto head_block_num = head->block_num(); auto branch = fork_db.fetch_branch_from_head(); @@ -1351,7 +1351,7 @@ struct controller_impl { } if( !forkdb.head() ) { - forkdb.reset( *head ); + forkdb.reset_root( *head ); } auto end = fc::time_point::now(); @@ -1419,7 +1419,7 @@ struct controller_impl { initialize_blockchain_state(genesis); // sets head to genesis state if( !forkdb.head() ) { - forkdb.reset( *forkdb.chain_head ); + forkdb.reset_root( *forkdb.chain_head ); } }; diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 272b88957a..bf37c60c7c 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -22,41 +22,47 @@ namespace eosio::chain { */ struct by_block_id; - struct by_lib_block_num; + struct by_best_branch; struct by_prev; - template - bool first_preferred( const bs& lhs, const bs& rhs ) { - return std::pair(lhs.irreversible_blocknum(), lhs.block_num()) > std::pair(rhs.irreversible_blocknum(), rhs.block_num()); + // match comparison of by_best_branch + template + bool first_preferred( const bsa& lhs, const bsa& rhs ) { + return std::make_tuple(lhs.last_final_block_num(), lhs.final_on_strong_qc_block_num(), lhs.last_qc_block_num(), lhs.block_height()) > + std::make_tuple(rhs.last_final_block_num(), rhs.final_on_strong_qc_block_num(), rhs.last_qc_block_num(), rhs.block_height()); } - template // either [block_state_legacy_ptr, block_state_ptr], same with block_header_state_ptr + template // either [block_state_legacy_forkdb_adaptor_ptr, block_state_forkdb_adaptor_ptr], same with block_header_state_ptr struct fork_database_impl { - using bs = bsp::element_type; + using bsa = BSAdaptorPtr::element_type; + using bs = bsa::bs; using bhsp = bs::bhsp_t; using bhs = bhsp::element_type; - - using fork_db_t = fork_database_t; + using bsp = BSAdaptorPtr::element_type::bsp_t; + + using fork_db_t = fork_database_t; using branch_type = fork_db_t::branch_type; using branch_type_pair = fork_db_t::branch_type_pair; using fork_multi_index_type = multi_index_container< - bsp, + BSAdaptorPtr, indexed_by< - hashed_unique, BOOST_MULTI_INDEX_CONST_MEM_FUN(bs, const block_id_type&, id), std::hash>, - ordered_non_unique, const_mem_fun>, - ordered_unique, - composite_key, BOOST_MULTI_INDEX_CONST_MEM_FUN(bsa, const block_id_type&, id), std::hash>, + ordered_non_unique, const_mem_fun>, + ordered_unique, + composite_key, - composite_key_compare, std::greater, std::greater, sha256_less>>>>; + BOOST_MULTI_INDEX_CONST_MEM_FUN(bsa, uint32_t, last_final_block_num), + BOOST_MULTI_INDEX_CONST_MEM_FUN(bsa, uint32_t, final_on_strong_qc_block_num), + BOOST_MULTI_INDEX_CONST_MEM_FUN(bsa, uint32_t, last_qc_block_num), + BOOST_MULTI_INDEX_CONST_MEM_FUN(bsa, uint32_t, block_height), + BOOST_MULTI_INDEX_CONST_MEM_FUN(bsa, const block_id_type&, id)>, + composite_key_compare, std::greater, std::greater, std::greater, std::greater, sha256_less>>>>; std::mutex mtx; fork_multi_index_type index; - bsp root; // Only uses the block_header_state_legacy portion - bsp head; + BSAdaptorPtr root; // Only uses the block_header_state portion of block_state + BSAdaptorPtr head; const uint32_t magic_number; explicit fork_database_impl(uint32_t magic_number) : magic_number(magic_number) {} @@ -66,8 +72,8 @@ namespace eosio::chain { void add_impl( const bsp& n, bool ignore_duplicate, bool validate, validator_t& validator ); bhsp get_block_header_impl( const block_id_type& id ) const; - bsp get_block_impl( const block_id_type& id ) const; - void reset_impl( const bhs& root_bhs ); + BSAdaptorPtr get_block_impl( const block_id_type& id ) const; + void reset_root_impl( const bhs& root_bhs ); void rollback_head_to_root_impl(); void advance_root_impl( const block_id_type& id ); void remove_impl( const block_id_type& id ); @@ -75,24 +81,25 @@ namespace eosio::chain { block_branch_t fetch_block_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; bsp search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; void mark_valid_impl( const bsp& h ); + void update_best_qc_strong_impl( const block_id_type& id ); branch_type_pair fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; }; - template - fork_database_t::fork_database_t(uint32_t magic_number) - :my( new fork_database_impl(magic_number) ) + template + fork_database_t::fork_database_t(uint32_t magic_number) + :my( new fork_database_impl(magic_number) ) {} - template - void fork_database_t::open( const std::filesystem::path& fork_db_file, validator_t& validator ) { + template + void fork_database_t::open( const std::filesystem::path& fork_db_file, validator_t& validator ) { std::lock_guard g( my->mtx ); my->open_impl( fork_db_file, validator ); } - template - void fork_database_impl::open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ) { + template + void fork_database_impl::open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ) { if( std::filesystem::exists( fork_db_file ) ) { try { string content; @@ -123,7 +130,7 @@ namespace eosio::chain { bhs state; fc::raw::unpack( ds, state ); - reset_impl( state ); + reset_root_impl( state ); unsigned_int size; fc::raw::unpack( ds, size ); for( uint32_t i = 0, n = size.value; i < n; ++i ) { @@ -145,8 +152,8 @@ namespace eosio::chain { ("filename", fork_db_file) ); } - auto candidate = index.template get().begin(); - if( candidate == index.template get().end() || !(*candidate)->is_valid() ) { + auto candidate = index.template get().begin(); + if( candidate == index.template get().end() || !(*candidate)->is_valid() ) { EOS_ASSERT( head->id() == root->id(), fork_database_exception, "head not set to root despite no better option available; '${filename}' is likely corrupted", ("filename", fork_db_file) ); @@ -161,14 +168,14 @@ namespace eosio::chain { } } - template - void fork_database_t::close(const std::filesystem::path& fork_db_file) { + template + void fork_database_t::close(const std::filesystem::path& fork_db_file) { std::lock_guard g( my->mtx ); my->close_impl(fork_db_file); } - template - void fork_database_impl::close_impl(const std::filesystem::path& fork_db_file) { + template + void fork_database_impl::close_impl(const std::filesystem::path& fork_db_file) { if( !root ) { if( index.size() > 0 ) { elog( "fork_database is in a bad state when closing; not writing out '${filename}'", @@ -177,18 +184,14 @@ namespace eosio::chain { return; } - // [greg todo] we need support for writing both the old and new format of fork_db to disk. - // I think it would be easier to have a different magic number for the new format (rather than a different - // version), since we do not need to be able to load a fork_db which is meant for a different - // consensus (dpos vs if). std::ofstream out( fork_db_file.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc ); fc::raw::pack( out, magic_number ); fc::raw::pack( out, fork_database::max_supported_version ); // write out current version which is always max_supported_version - fc::raw::pack( out, *static_cast(&*root) ); // [greg todo] enought to write only bhs for IF? + fc::raw::pack( out, *static_cast(&*root->get()) ); uint32_t num_blocks_in_fork_db = index.size(); fc::raw::pack( out, unsigned_int{num_blocks_in_fork_db} ); - const auto& indx = index.template get(); + const auto& indx = index.template get(); auto unvalidated_itr = indx.rbegin(); auto unvalidated_end = boost::make_reverse_iterator( indx.lower_bound( false ) ); @@ -220,7 +223,7 @@ namespace eosio::chain { ++validated_itr; } - fc::raw::pack( out, *(*itr) ); + fc::raw::pack( out, *(*itr)->get() ); } if( head ) { @@ -233,48 +236,49 @@ namespace eosio::chain { index.clear(); } - template - void fork_database_t::reset( const bhs& root_bhs ) { + template + void fork_database_t::reset_root( const bhs& root_bhs ) { std::lock_guard g( my->mtx ); - my->reset_impl(root_bhs); + my->reset_root_impl(root_bhs); } - template - void fork_database_impl::reset_impl( const bhs& root_bhs ) { + template + void fork_database_impl::reset_root_impl( const bhs& root_bhs ) { index.clear(); - root = std::make_shared(); - static_cast(*root) = root_bhs; + bsp root_bsp = std::make_shared(); + static_cast(*root_bsp) = root_bhs; + root = std::make_shared(std::move(root_bsp)); root->set_valid(true); head = root; } - template - void fork_database_t::rollback_head_to_root() { + template + void fork_database_t::rollback_head_to_root() { std::lock_guard g( my->mtx ); my->rollback_head_to_root_impl(); } - template - void fork_database_impl::rollback_head_to_root_impl() { + template + void fork_database_impl::rollback_head_to_root_impl() { auto& by_id_idx = index.template get(); auto itr = by_id_idx.begin(); while (itr != by_id_idx.end()) { - by_id_idx.modify( itr, []( bsp& _bsp ) { - _bsp->set_valid(false); + by_id_idx.modify( itr, []( auto& i ) { + i->set_valid(false); } ); ++itr; } head = root; } - template - void fork_database_t::advance_root( const block_id_type& id ) { + template + void fork_database_t::advance_root( const block_id_type& id ) { std::lock_guard g( my->mtx ); my->advance_root_impl( id ); } - template - void fork_database_impl::advance_root_impl( const block_id_type& id ) { + template + void fork_database_impl::advance_root_impl( const block_id_type& id ) { EOS_ASSERT( root, fork_database_exception, "root not yet set" ); auto new_root = get_block_impl( id ); @@ -308,27 +312,27 @@ namespace eosio::chain { root = new_root; } - template - fork_database_t::bhsp fork_database_t::get_block_header( const block_id_type& id ) const { + template + fork_database_t::bhsp fork_database_t::get_block_header( const block_id_type& id ) const { std::lock_guard g( my->mtx ); return my->get_block_header_impl( id ); } - template - fork_database_impl::bhsp fork_database_impl::get_block_header_impl( const block_id_type& id ) const { + template + fork_database_impl::bhsp fork_database_impl::get_block_header_impl( const block_id_type& id ) const { if( root->id() == id ) { - return root; + return root->get(); } auto itr = index.find( id ); if( itr != index.end() ) - return *itr; + return (*itr)->get(); return bhsp(); } - template - void fork_database_impl::add_impl(const bsp& n, bool ignore_duplicate, bool validate, validator_t& validator) { + template + void fork_database_impl::add_impl(const bsp& n, bool ignore_duplicate, bool validate, validator_t& validator) { EOS_ASSERT( root, fork_database_exception, "root not yet set" ); EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" ); @@ -353,20 +357,20 @@ namespace eosio::chain { "serialized fork database is incompatible with configured protocol features") } - auto inserted = index.insert(n); + auto inserted = index.insert(std::make_shared(n)); if( !inserted.second ) { if( ignore_duplicate ) return; EOS_THROW( fork_database_exception, "duplicate block added", ("id", n->id()) ); } - auto candidate = index.template get().begin(); + auto candidate = index.template get().begin(); if( (*candidate)->is_valid() ) { head = *candidate; } } - template - void fork_database_t::add( const bsp& n, bool ignore_duplicate ) { + template + void fork_database_t::add( const bsp& n, bool ignore_duplicate ) { std::lock_guard g( my->mtx ); my->add_impl( n, ignore_duplicate, false, []( block_timestamp_type timestamp, @@ -376,81 +380,86 @@ namespace eosio::chain { ); } - template - bsp fork_database_t::root() const { + template + bool fork_database_t::has_root() const { + return !!my->root; + } + + template + fork_database_t::bsp fork_database_t::root() const { std::lock_guard g( my->mtx ); - return my->root; + return my->root->get(); } - template - bsp fork_database_t::head() const { + template + fork_database_t::bsp fork_database_t::head() const { std::lock_guard g( my->mtx ); - return my->head; + return my->head ? my->head->get() : bsp(); } - template - bsp fork_database_t::pending_head() const { + template + fork_database_t::bsp fork_database_t::pending_head() const { std::lock_guard g( my->mtx ); - const auto& indx = my->index.template get(); + const auto& indx = my->index.template get(); auto itr = indx.lower_bound( false ); if( itr != indx.end() && !(*itr)->is_valid() ) { if( first_preferred( **itr, *my->head ) ) - return *itr; + return (*itr)->get(); } - return my->head; + return my->head ? my->head->get() : bsp(); } - template - fork_database_t::branch_type - fork_database_t::fetch_branch(const block_id_type& h, uint32_t trim_after_block_num) const { + template + fork_database_t::branch_type + fork_database_t::fetch_branch(const block_id_type& h, uint32_t trim_after_block_num) const { std::lock_guard g(my->mtx); return my->fetch_branch_impl(h, trim_after_block_num); } - template - fork_database_t::branch_type - fork_database_impl::fetch_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { + template + fork_database_t::branch_type + fork_database_impl::fetch_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { branch_type result; - for (auto s = get_block_impl(h); s; s = get_block_impl(s->previous())) { - if (s->block_num() <= trim_after_block_num) - result.push_back(s); + for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { + if ((*i)->block_num() <= trim_after_block_num) + result.push_back((*i)->get()); } return result; } - template + template block_branch_t - fork_database_t::fetch_block_branch(const block_id_type& h, uint32_t trim_after_block_num) const { + fork_database_t::fetch_block_branch(const block_id_type& h, uint32_t trim_after_block_num) const { std::lock_guard g(my->mtx); return my->fetch_block_branch_impl(h, trim_after_block_num); } - template + template block_branch_t - fork_database_impl::fetch_block_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { + fork_database_impl::fetch_block_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { block_branch_t result; - for (auto s = get_block_impl(h); s; s = get_block_impl(s->previous())) { - if (s->block_num() <= trim_after_block_num) - result.push_back(s->block); + for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { + if ((*i)->block_num() <= trim_after_block_num) + result.push_back((*i)->block()); } return result; } - template - bsp fork_database_t::search_on_branch( const block_id_type& h, uint32_t block_num ) const { + template + fork_database_t::bsp fork_database_t::search_on_branch( const block_id_type& h, uint32_t block_num ) const { std::lock_guard g( my->mtx ); return my->search_on_branch_impl( h, block_num ); } - template - bsp fork_database_impl::search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const { - for( auto s = get_block_impl(h); s; s = get_block_impl( s->previous() ) ) { - if( s->block_num() == block_num ) - return s; + template + fork_database_impl::bsp fork_database_impl::search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const { + for( auto i = index.find(h); i != index.end(); i = index.find( (*i)->previous() ) ) { + if ((*i)->block_num() == block_num) + return (*i)->get(); } return {}; @@ -460,16 +469,16 @@ namespace eosio::chain { * Given two head blocks, return two branches of the fork graph that * end with a common ancestor (same prior block) */ - template - fork_database_t::branch_type_pair - fork_database_t::fetch_branch_from(const block_id_type& first, const block_id_type& second) const { + template + fork_database_t::branch_type_pair + fork_database_t::fetch_branch_from(const block_id_type& first, const block_id_type& second) const { std::lock_guard g(my->mtx); return my->fetch_branch_from_impl(first, second); } - template - fork_database_t::branch_type_pair - fork_database_impl::fetch_branch_from_impl(const block_id_type& first, const block_id_type& second) const { + template + fork_database_t::branch_type_pair + fork_database_impl::fetch_branch_from_impl(const block_id_type& first, const block_id_type& second) const { pair result; auto first_branch = (first == root->id()) ? root : get_block_impl(first); auto second_branch = (second == root->id()) ? root : get_block_impl(second); @@ -479,7 +488,7 @@ namespace eosio::chain { while( first_branch->block_num() > second_branch->block_num() ) { - result.first.push_back(first_branch); + result.first.push_back(first_branch->get()); const auto& prev = first_branch->previous(); first_branch = (prev == root->id()) ? root : get_block_impl( prev ); EOS_ASSERT( first_branch, fork_db_block_not_found, @@ -490,7 +499,7 @@ namespace eosio::chain { while( second_branch->block_num() > first_branch->block_num() ) { - result.second.push_back( second_branch ); + result.second.push_back( second_branch->get() ); const auto& prev = second_branch->previous(); second_branch = (prev == root->id()) ? root : get_block_impl( prev ); EOS_ASSERT( second_branch, fork_db_block_not_found, @@ -503,8 +512,8 @@ namespace eosio::chain { while( first_branch->previous() != second_branch->previous() ) { - result.first.push_back(first_branch); - result.second.push_back(second_branch); + result.first.push_back(first_branch->get()); + result.second.push_back(second_branch->get()); const auto &first_prev = first_branch->previous(); first_branch = get_block_impl( first_prev ); const auto &second_prev = second_branch->previous(); @@ -521,21 +530,21 @@ namespace eosio::chain { if( first_branch && second_branch ) { - result.first.push_back(first_branch); - result.second.push_back(second_branch); + result.first.push_back(first_branch->get()); + result.second.push_back(second_branch->get()); } return result; } /// fetch_branch_from_impl /// remove all of the invalid forks built off of this id including this id - template - void fork_database_t::remove( const block_id_type& id ) { + template + void fork_database_t::remove( const block_id_type& id ) { std::lock_guard g( my->mtx ); return my->remove_impl( id ); } - template - void fork_database_impl::remove_impl( const block_id_type& id ) { + template + void fork_database_impl::remove_impl( const block_id_type& id ) { deque remove_queue{id}; const auto& previdx = index.template get(); const auto& head_id = head->id(); @@ -556,14 +565,14 @@ namespace eosio::chain { } } - template - void fork_database_t::mark_valid( const bsp& h ) { + template + void fork_database_t::mark_valid( const bsp& h ) { std::lock_guard g( my->mtx ); my->mark_valid_impl( h ); } - template - void fork_database_impl::mark_valid_impl( const bsp& h ) { + template + void fork_database_impl::mark_valid_impl( const bsp& h ) { if( h->is_valid() ) return; auto& by_id_idx = index.template get(); @@ -573,28 +582,53 @@ namespace eosio::chain { "block state not in fork database; cannot mark as valid", ("id", h->id()) ); - by_id_idx.modify( itr, []( bsp& _bsp ) { - _bsp->set_valid(true); + by_id_idx.modify( itr, []( auto& i ) { + i->set_valid(true); } ); - auto candidate = index.template get().begin(); + auto candidate = index.template get().begin(); if( first_preferred( **candidate, *head ) ) { head = *candidate; } } - template - bsp fork_database_t::get_block(const block_id_type& id) const { + template + void fork_database_t::update_best_qc_strong( const block_id_type& id ) { std::lock_guard g( my->mtx ); - return my->get_block_impl(id); + my->update_best_qc_strong_impl( id ); } - template - bsp fork_database_impl::get_block_impl(const block_id_type& id) const { + template + void fork_database_impl::update_best_qc_strong_impl( const block_id_type& id ) { + auto& by_id_idx = index.template get(); + + auto itr = by_id_idx.find( id ); + EOS_ASSERT( itr != by_id_idx.end(), fork_database_exception, + "block state not in fork database; cannot update", ("id", id) ); + + by_id_idx.modify( itr, []( auto& i ) { + i->update_best_qc_strong(); + } ); + + auto candidate = index.template get().begin(); + if( first_preferred( **candidate, *head ) ) { + head = *candidate; + } + } + + template + fork_database_t::bsp fork_database_t::get_block(const block_id_type& id) const { + std::lock_guard g( my->mtx ); + auto bsap = my->get_block_impl(id); + return bsap ? bsap->get() : bsp{}; + } + + template + BSAdaptorPtr fork_database_impl::get_block_impl(const block_id_type& id) const { auto itr = index.find( id ); if( itr != index.end() ) return *itr; - return bsp(); + return {}; } // ------------------ fork_database ------------------------- @@ -664,7 +698,7 @@ namespace eosio::chain { legacy = false; apply_if([&](auto& forkdb) { forkdb.chain_head = new_head; - forkdb.reset(*new_head); + forkdb.reset_root(*new_head); }); } @@ -675,10 +709,10 @@ namespace eosio::chain { } // do class instantiations - template class fork_database_t; - template class fork_database_t; + template class fork_database_t; + template class fork_database_t; - template struct fork_database_impl; - template struct fork_database_impl; + template struct fork_database_impl; + template struct fork_database_impl; } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 56b17744f9..f493b474c0 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -1,17 +1,106 @@ #pragma once #include #include -#include namespace eosio::chain { - using boost::signals2::signal; - template struct fork_database_impl; using block_branch_t = deque; + struct block_state_forkdb_adaptor { + private: + block_header_state_core current_core; // only modify/access while holding forkdb lock + block_state_ptr bsp; + + public: + using bs = block_state_ptr::element_type; + using bhsp = bs::bhsp_t; + using bhs = bhsp::element_type; + using bsp_t = block_state_ptr; + + explicit block_state_forkdb_adaptor(block_state_ptr bsp) + : current_core(bsp->core), bsp(std::move(bsp)) {} + + block_state_forkdb_adaptor(const block_state_forkdb_adaptor&) = delete; + block_state_forkdb_adaptor() = delete; + block_state_forkdb_adaptor& operator=(const block_state_forkdb_adaptor&) = delete; + block_state_forkdb_adaptor(block_state_forkdb_adaptor&&) = default; + + void update_best_qc_strong() { + if (current_core.last_qc_block_num != bsp->block_num()) + current_core = current_core.next(qc_claim_t{.last_qc_block_num = bsp->block_num(), .is_last_qc_strong = true}); + } + + // although valid is mutated and accessed, it should all be from the main thread or protected by forkdb mutex + void set_valid(bool v) { bsp->set_valid(v); } // not thread safe + bool is_valid() const { return bsp->is_valid(); } // not thread safe + + // only safe to call while holding fork_database lock + uint32_t last_final_block_num() const { + return current_core.last_final_block_num; + } + // only safe to call while holding fork_database lock + uint32_t final_on_strong_qc_block_num() const { + return current_core.final_on_strong_qc_block_num.value_or(last_final_block_num()); + } + // only safe to call while holding fork_database lock + uint32_t last_qc_block_num() const { + return current_core.last_qc_block_num.value_or(final_on_strong_qc_block_num()); + } + + // thread safe + uint32_t block_height() const { return bsp->timestamp().slot; } + uint32_t block_num() const { return bsp->block_num(); } + const block_id_type& id() const { return bsp->id(); } + const block_id_type& previous() const { return bsp->previous(); } + const block_state_ptr& get() const { return bsp; } + const signed_block_ptr& block() const { return bsp->block; } + explicit operator bool() const noexcept { return !!bsp; } + }; + + // thread safe + struct block_state_legacy_forkdb_adaptor { + private: + block_state_legacy_ptr bsp; + + public: + using bs = block_state_legacy_ptr::element_type; + using bhsp = bs::bhsp_t; + using bhs = bhsp::element_type; + using bsp_t = block_state_legacy_ptr; + + explicit block_state_legacy_forkdb_adaptor(block_state_legacy_ptr bsp) : bsp(std::move(bsp)) {} + + block_state_legacy_forkdb_adaptor(const block_state_legacy_forkdb_adaptor&) = delete; + block_state_legacy_forkdb_adaptor() = delete; + block_state_legacy_forkdb_adaptor& operator=(const block_state_legacy_forkdb_adaptor&) = delete; + block_state_legacy_forkdb_adaptor(block_state_legacy_forkdb_adaptor&&) = default; + + void update_best_qc_strong() {} // no-op for legacy + + // although valid is mutated and accessed, it should all be from the main thread or protected by forkdb mutex + void set_valid(bool v) { bsp->set_valid(v); } // not thread safe + bool is_valid() const { return bsp->is_valid(); } + + // maintains the equivalent of legacy behavior + uint32_t last_final_block_num() const { return bsp->irreversible_blocknum(); } + uint32_t final_on_strong_qc_block_num() const { return bsp->irreversible_blocknum(); } + uint32_t last_qc_block_num() const { return bsp->irreversible_blocknum(); } + + uint32_t block_height() const { return bsp->block_num(); } + uint32_t block_num() const { return bsp->block_num(); } + const block_id_type& id() const { return bsp->id(); } + const block_id_type& previous() const { return bsp->previous(); } + const block_state_legacy_ptr& get() const { return bsp; } + const signed_block_ptr& block() const { return bsp->block; } + explicit operator bool() const noexcept { return !!bsp; } + }; + + using block_state_legacy_forkdb_adaptor_ptr = std::shared_ptr; + using block_state_forkdb_adaptor_ptr = std::shared_ptr; + /** * @class fork_database_t * @brief manages light-weight state for all potential unconfirmed forks @@ -26,17 +115,18 @@ namespace eosio::chain { * fork_database should be used instead of fork_database_t directly as it manages * the different supported types. */ - template // either block_state_legacy_ptr or block_state_ptr + template // either block_state_legacy_forkdb_adaptor_ptr or block_state_forkdb_adaptor_ptr class fork_database_t { public: static constexpr uint32_t legacy_magic_number = 0x30510FDB; static constexpr uint32_t magic_number = 0x4242FDB; - using bs = bsp::element_type; + using bs = BSAdaptorPtr::element_type::bs; using bhsp = bs::bhsp_t; using bhs = bhsp::element_type; - using bsp_t = bsp; - using branch_type = deque; + using bsp_t = BSAdaptorPtr::element_type::bsp_t; + using bsp = bsp_t; + using branch_type = deque; using branch_type_pair = pair; explicit fork_database_t(uint32_t magic_number = legacy_magic_number); @@ -51,7 +141,7 @@ namespace eosio::chain { * Purges any existing blocks from the fork database and resets the root block_header_state to the provided value. * The head will also be reset to point to the root. */ - void reset( const bhs& root_bhs ); + void reset_root( const bhs& root_bhs ); /** * Removes validated flag from all blocks in fork database and resets head to point to the root. @@ -71,7 +161,8 @@ namespace eosio::chain { void remove( const block_id_type& id ); - bsp root() const; + bool has_root() const; + bsp root() const; // undefined if !has_root() bsp head() const; bsp pending_head() const; @@ -104,12 +195,17 @@ namespace eosio::chain { void mark_valid( const bsp& h ); + /** + * Update block_state_core for best qc strong + */ + void update_best_qc_strong( const block_id_type& id ); + private: - unique_ptr> my; + unique_ptr> my; }; - using fork_database_legacy_t = fork_database_t; - using fork_database_if_t = fork_database_t; + using fork_database_legacy_t = fork_database_t; + using fork_database_if_t = fork_database_t; /** * Provides mechanism for opening the correct type diff --git a/plugins/chain_plugin/chain_plugin.cpp b/plugins/chain_plugin/chain_plugin.cpp index 60e321da27..51300efdd2 100644 --- a/plugins/chain_plugin/chain_plugin.cpp +++ b/plugins/chain_plugin/chain_plugin.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff --git a/programs/leap-util/actions/snapshot.cpp b/programs/leap-util/actions/snapshot.cpp index 419158da51..ee750484e7 100644 --- a/programs/leap-util/actions/snapshot.cpp +++ b/programs/leap-util/actions/snapshot.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include diff --git a/unittests/forked_tests.cpp b/unittests/forked_tests.cpp index 8ac11285e0..9041a74baf 100644 --- a/unittests/forked_tests.cpp +++ b/unittests/forked_tests.cpp @@ -1,5 +1,4 @@ #include -#include #include #include diff --git a/unittests/unapplied_transaction_queue_tests.cpp b/unittests/unapplied_transaction_queue_tests.cpp index f8c76fe63c..eda8d01721 100644 --- a/unittests/unapplied_transaction_queue_tests.cpp +++ b/unittests/unapplied_transaction_queue_tests.cpp @@ -78,7 +78,7 @@ auto create_test_block_state( deque trx_metas ) { return bsp; } -using branch_type_legacy = fork_database_t::branch_type; +using branch_type_legacy = fork_database_legacy_t::branch_type; template void add_forked( unapplied_transaction_queue& queue, const BRANCH_TYPE& forked_branch ) { From 8101af08e944eb5dfcd9af35a7d7b67d59202dc9 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 13 Feb 2024 11:38:29 -0600 Subject: [PATCH 03/40] GH-2125 Update fork_database on strong vote --- libraries/chain/block_state.cpp | 36 +++++++++++++++---- libraries/chain/controller.cpp | 9 +++-- libraries/chain/hotstuff/hotstuff.cpp | 10 +++--- .../chain/include/eosio/chain/block_state.hpp | 7 ++-- .../include/eosio/chain/hotstuff/hotstuff.hpp | 9 ++--- unittests/block_state_tests.cpp | 12 +++---- 6 files changed, 56 insertions(+), 27 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index ca0d2e7419..e47207e78c 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -72,7 +72,8 @@ void block_state::set_trxs_metas( deque&& trxs_metas, } // Called from net threads -std::pair> block_state::aggregate_vote(const vote_message& vote) { +std::tuple> +block_state::aggregate_vote(const vote_message& vote) { const auto& finalizers = active_finalizer_policy->finalizers; auto it = std::find_if(finalizers.begin(), finalizers.end(), @@ -81,17 +82,20 @@ std::pair> block_state::aggregate_vote(cons if (it != finalizers.end()) { auto index = std::distance(finalizers.begin(), it); const digest_type& digest = vote.strong ? strong_digest : weak_digest; - auto [status, strong] = pending_qc.add_vote(vote.strong, + auto [status, state] = pending_qc.add_vote(vote.strong, #warning TODO change to use std::span if possible std::vector{digest.data(), digest.data() + digest.data_size()}, index, vote.finalizer_key, vote.sig, finalizers[index].weight); - return {status, strong ? core.final_on_strong_qc_block_num : std::optional{}}; + std::optional new_lib{}; + if (status == vote_status::success && state == pending_quorum_certificate::state_t::strong) + new_lib = core.final_on_strong_qc_block_num; + return {status, state, new_lib}; } else { wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) ); - return {vote_status::unknown_public_key, {}}; + return {vote_status::unknown_public_key, pending_quorum_certificate::state_t::unrestricted, {}}; } } @@ -169,7 +173,7 @@ std::optional block_state::get_best_qc() const { if( valid_qc ) { return quorum_certificate{ block_number, *valid_qc }; } else { - return std::nullopt;; + return std::nullopt; } } @@ -188,5 +192,25 @@ std::optional block_state::get_best_qc() const { *valid_qc : // tie broke by valid_qc valid_qc->is_strong() ? *valid_qc : valid_qc_from_pending; // strong beats weak return quorum_certificate{ block_number, best_qc }; -} +} + +bool block_state::is_best_qc_strong() const { + // if pending_qc does not have a valid QC, consider valid_qc only + if( !pending_qc.is_quorum_met() ) { + return valid_qc ? valid_qc->is_strong() : false; + } + + bool pending_is_strong = pending_qc.state() == pending_quorum_certificate::state_t::strong; + + // if valid_qc does not have value, consider pending only + if (!valid_qc) { + return pending_is_strong; + } + + // Both valid_qc and pending have value. Compare them and select a better one. + // Strong beats weak. Tie break by valid_qc. + return valid_qc->is_strong() == pending_is_strong ? valid_qc->is_strong() : + valid_qc->is_strong() ? valid_qc->is_strong() : pending_is_strong; +} + } /// eosio::chain diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 960c22599f..850cb25acd 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3002,8 +3002,13 @@ struct controller_impl { vote_status process_vote_message( const vote_message& vote ) { auto do_vote = [&vote](auto& forkdb) -> std::pair> { auto bsp = forkdb.get_block(vote.proposal_id); - if (bsp) - return bsp->aggregate_vote(vote); + if (bsp) { + auto [vote_state, state, block_num] = bsp->aggregate_vote(vote); + if (vote_state == vote_status::success && state == pending_quorum_certificate::state_t::strong) { // if block_num then strong vote + forkdb.update_best_qc_strong(bsp->id()); + } + return {vote_state, block_num}; + } return {vote_status::unknown_block, {}}; }; // TODO: https://github.com/AntelopeIO/leap/issues/2057 diff --git a/libraries/chain/hotstuff/hotstuff.cpp b/libraries/chain/hotstuff/hotstuff.cpp index ca43590ab9..e6f11ac273 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -124,14 +124,14 @@ vote_status pending_quorum_certificate::add_weak_vote(const std::vector return vote_status::success; } -// thread safe, -std::pair pending_quorum_certificate::add_vote(bool strong, const std::vector& proposal_digest, size_t index, - const bls_public_key& pubkey, const bls_signature& sig, - uint64_t weight) { +// thread safe +std::pair +pending_quorum_certificate::add_vote(bool strong, const std::vector& proposal_digest, size_t index, + const bls_public_key& pubkey, const bls_signature& sig, uint64_t weight) { std::lock_guard g(*_mtx); vote_status s = strong ? add_strong_vote(proposal_digest, index, pubkey, sig, weight) : add_weak_vote(proposal_digest, index, pubkey, sig, weight); - return {s, _state == state_t::strong}; + return {s, _state}; } // thread safe diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 35841b6312..3c7797e78b 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -32,14 +32,17 @@ struct block_state : public block_header_state { // block_header_state provi void set_valid(bool b) { validated = b; } uint32_t irreversible_blocknum() const { return core.last_final_block_num; } std::optional get_best_qc() const; + bool is_best_qc_strong() const; protocol_feature_activation_set_ptr get_activated_protocol_features() const { return block_header_state::activated_protocol_features; } bool is_pub_keys_recovered() const { return pub_keys_recovered; } deque extract_trxs_metas(); void set_trxs_metas(deque&& trxs_metas, bool keys_recovered); const deque& trxs_metas() const { return cached_trxs; } - - std::pair> aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc + + // vote_status, pending_qc state, last_final_block_num + std::tuple> + aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc void verify_qc(const valid_quorum_certificate& qc) const; // verify given qc is valid with respect block_state using bhs_t = block_header_state; diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index 2650260b9d..6595a4f745 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -162,12 +162,9 @@ namespace eosio::chain { bool is_quorum_met() const; // thread safe - std::pair add_vote(bool strong, - const std::vector& proposal_digest, - size_t index, - const bls_public_key& pubkey, - const bls_signature& sig, - uint64_t weight); + std::pair + add_vote(bool strong, const std::vector& proposal_digest, size_t index, + const bls_public_key& pubkey, const bls_signature& sig, uint64_t weight); state_t state() const { std::lock_guard g(*_mtx); return _state; }; valid_quorum_certificate to_valid_quorum_certificate() const; diff --git a/unittests/block_state_tests.cpp b/unittests/block_state_tests.cpp index 2092e63b78..fdda705db5 100644 --- a/unittests/block_state_tests.cpp +++ b/unittests/block_state_tests.cpp @@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bool strong = (i % 2 == 0); // alternate strong and weak auto sig = strong ? private_key[i].sign(strong_digest_data) : private_key[i].sign(weak_digest_data); vote_message vote{ block_id, strong, public_key[i], sig }; - BOOST_REQUIRE(bsp->aggregate_vote(vote).first == vote_status::success); + BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) == vote_status::success); } } @@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1, bsp->active_finalizer_policy->max_weak_sum_before_weak_final() }; vote_message vote {block_id, true, public_key[0], private_key[1].sign(strong_digest_data) }; - BOOST_REQUIRE(bsp->aggregate_vote(vote).first != vote_status::success); + BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) != vote_status::success); } { // duplicate votes @@ -71,8 +71,8 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { vote_message vote {block_id, true, public_key[0], private_key[0].sign(strong_digest_data) }; - BOOST_REQUIRE(bsp->aggregate_vote(vote).first == vote_status::success); // first time succeeds - BOOST_REQUIRE(bsp->aggregate_vote(vote).first != vote_status::success); // second time failed due to duplicate voting + BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) == vote_status::success); // first time succeeds + BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) != vote_status::success); // second time failed due to duplicate voting } { // public key does not exit in finalizer set @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bls_public_key new_public_key{ new_private_key.get_public_key() }; vote_message vote {block_id, true, new_public_key, private_key[0].sign(strong_digest_data) }; - BOOST_REQUIRE(bsp->aggregate_vote(vote).first != vote_status::success); + BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) != vote_status::success); } } FC_LOG_AND_RETHROW(); @@ -130,7 +130,7 @@ void do_quorum_test(const std::vector& weights, if( to_vote[i] ) { auto sig = strong ? private_key[i].sign(strong_digest_data) : private_key[i].sign(weak_digest_data); vote_message vote{ block_id, strong, public_key[i], sig }; - BOOST_REQUIRE(bsp->aggregate_vote(vote).first == vote_status::success); + BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) == vote_status::success); } } From 5d2a8390e321b56af1893b9490af8b02ce66faac Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 14 Feb 2024 07:15:37 -0600 Subject: [PATCH 04/40] GH-2125 Make last_qc_block_num non-optional --- libraries/chain/block_header_state.cpp | 4 +-- .../hotstuff/block_construction_data_flow.md | 4 +-- .../eosio/chain/block_header_state.hpp | 6 ++--- .../include/eosio/chain/fork_database.hpp | 2 +- .../hotstuff/instant_finality_extension.hpp | 4 +-- unittests/block_header_state_tests.cpp | 27 ++++++++++--------- 6 files changed, 23 insertions(+), 24 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 3c0da4bbfb..8b8147a795 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -47,9 +47,7 @@ block_header_state_core block_header_state_core::next(qc_claim_t incoming) const // next block which can become irreversible is the block with // old last_qc_block_num - if (old_last_qc_block_num.has_value()) { - result.final_on_strong_qc_block_num = *old_last_qc_block_num; - } + result.final_on_strong_qc_block_num = old_last_qc_block_num; } else { // new final_on_strong_qc_block_num should not be present result.final_on_strong_qc_block_num.reset(); diff --git a/libraries/chain/hotstuff/block_construction_data_flow.md b/libraries/chain/hotstuff/block_construction_data_flow.md index ca63780ccd..9169b50fc5 100644 --- a/libraries/chain/hotstuff/block_construction_data_flow.md +++ b/libraries/chain/hotstuff/block_construction_data_flow.md @@ -72,7 +72,7 @@ The new storage for IF is: struct block_header_state_core { uint32_t last_final_block_num = 0; // last irreversible (final) block. std::optional final_on_strong_qc_block_num; // will become final if this header achives a strong QC. - std::optional last_qc_block_num; // + uint32_t last_qc_block_num; // uint32_t finalizer_policy_generation; block_header_state_core next(uint32_t last_qc_block_num, bool is_last_qc_strong) const; @@ -110,7 +110,7 @@ struct block_header_state { // block descending from this need the provided qc in the block extension bool is_needed(const quorum_certificate& qc) const { - return !core.last_qc_block_num || qc.block_num > *core.last_qc_block_num; + return qc.block_num > core.last_qc_block_num; } block_header_state next(const block_header_state_input& data) const; diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index ee5f9cb118..0fd6b54e2f 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -38,8 +38,8 @@ struct block_header_state_input : public building_block_input { struct block_header_state_core { uint32_t last_final_block_num = 0; // last irreversible (final) block. std::optional final_on_strong_qc_block_num; // will become final if this header achives a strong QC. - std::optional last_qc_block_num; // - uint32_t finalizer_policy_generation; // + uint32_t last_qc_block_num = 0; + uint32_t finalizer_policy_generation = 0; block_header_state_core next(qc_claim_t incoming) const; }; @@ -81,7 +81,7 @@ struct block_header_state { // block descending from this need the provided qc in the block extension bool is_needed(const quorum_certificate& qc) const { - return !core.last_qc_block_num || qc.block_num > *core.last_qc_block_num; + return qc.block_num > core.last_qc_block_num; } flat_set get_activated_protocol_features() const { return activated_protocol_features->protocol_features; } diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index f493b474c0..ca81d60531 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -47,7 +47,7 @@ namespace eosio::chain { } // only safe to call while holding fork_database lock uint32_t last_qc_block_num() const { - return current_core.last_qc_block_num.value_or(final_on_strong_qc_block_num()); + return current_core.last_qc_block_num; } // thread safe diff --git a/libraries/chain/include/eosio/chain/hotstuff/instant_finality_extension.hpp b/libraries/chain/include/eosio/chain/hotstuff/instant_finality_extension.hpp index 449c98b4cc..b334c56220 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/instant_finality_extension.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/instant_finality_extension.hpp @@ -6,8 +6,8 @@ namespace eosio::chain { struct qc_claim_t { - uint32_t last_qc_block_num; // The block height of the most recent ancestor block that has a QC justification - bool is_last_qc_strong; // Whether the QC for the block referenced by last_qc_block_height is strong or weak. + uint32_t last_qc_block_num = 0; // The block height of the most recent ancestor block that has a QC justification + bool is_last_qc_strong = false; // Whether the QC for the block referenced by last_qc_block_height is strong or weak. }; struct instant_finality_extension : fc::reflect_init { diff --git a/unittests/block_header_state_tests.cpp b/unittests/block_header_state_tests.cpp index eddddf8f62..b024d6dea3 100644 --- a/unittests/block_header_state_tests.cpp +++ b/unittests/block_header_state_tests.cpp @@ -14,13 +14,13 @@ BOOST_AUTO_TEST_CASE(block_header_state_core_constructor_test) block_header_state_core bhs_core1(1, 2, 3); BOOST_REQUIRE_EQUAL(bhs_core1.last_final_block_num, 1u); BOOST_REQUIRE_EQUAL(*bhs_core1.final_on_strong_qc_block_num, 2u); - BOOST_REQUIRE_EQUAL(*bhs_core1.last_qc_block_num, 3u); + BOOST_REQUIRE_EQUAL(bhs_core1.last_qc_block_num, 3u); // verifies optional arguments work as expected block_header_state_core bhs_core2(10, std::nullopt, {}); BOOST_REQUIRE_EQUAL(bhs_core2.last_final_block_num, 10u); BOOST_REQUIRE(!bhs_core2.final_on_strong_qc_block_num.has_value()); - BOOST_REQUIRE(!bhs_core2.last_qc_block_num.has_value()); + BOOST_REQUIRE_EQUAL(bhs_core2.last_qc_block_num, 0); } // comprehensive state transition test @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(block_header_state_core_state_transition_test) auto new_bhs_core = old_bhs_core.next({old_last_qc_block_num, is_last_qc_strong}); BOOST_REQUIRE_EQUAL(new_bhs_core.last_final_block_num, old_bhs_core.last_final_block_num); BOOST_REQUIRE_EQUAL(*new_bhs_core.final_on_strong_qc_block_num, *old_bhs_core.final_on_strong_qc_block_num); - BOOST_REQUIRE_EQUAL(*new_bhs_core.last_qc_block_num, *old_bhs_core.last_qc_block_num); + BOOST_REQUIRE_EQUAL(new_bhs_core.last_qc_block_num, old_bhs_core.last_qc_block_num); } // verifies state cannot be transitioned to a smaller last_qc_block_num @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(block_header_state_core_state_transition_test) // old last_qc block became final_on_strong_qc block BOOST_REQUIRE_EQUAL(*new_bhs_core.final_on_strong_qc_block_num, old_last_qc_block_num); // new last_qc_block_num is the same as input - BOOST_REQUIRE_EQUAL(*new_bhs_core.last_qc_block_num, input_last_qc_block_num); + BOOST_REQUIRE_EQUAL(new_bhs_core.last_qc_block_num, input_last_qc_block_num); // verifies state transition works when is_last_qc_strong is false new_bhs_core = old_bhs_core.next({input_last_qc_block_num, false}); @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(block_header_state_core_state_transition_test) // new final_on_strong_qc_block_num should not be present BOOST_REQUIRE(!new_bhs_core.final_on_strong_qc_block_num.has_value()); // new last_qc_block_num is the same as input - BOOST_REQUIRE_EQUAL(*new_bhs_core.last_qc_block_num, input_last_qc_block_num); + BOOST_REQUIRE_EQUAL(new_bhs_core.last_qc_block_num, input_last_qc_block_num); } // A test to demonstrate 3-chain state transitions from the first @@ -71,18 +71,19 @@ BOOST_AUTO_TEST_CASE(block_header_state_core_3_chain_transition_test) { // block2: initial setup constexpr auto block2_last_final_block_num = 1u; - block_header_state_core block2_bhs_core(block2_last_final_block_num, {}, {}); + block_header_state_core block2_bhs_core(block2_last_final_block_num, {}, block2_last_final_block_num); // block2 --> block3 constexpr auto block3_input_last_qc_block_num = 2u; auto block3_bhs_core = block2_bhs_core.next({block3_input_last_qc_block_num, true}); // last_final_block_num should be the same as old one BOOST_REQUIRE_EQUAL(block3_bhs_core.last_final_block_num, block2_last_final_block_num); - // final_on_strong_qc_block_num should be same as old one - BOOST_REQUIRE(!block3_bhs_core.final_on_strong_qc_block_num.has_value()); + // final_on_strong_qc_block_num should be last_qc_block_num + BOOST_REQUIRE(block3_bhs_core.final_on_strong_qc_block_num.has_value()); + BOOST_REQUIRE_EQUAL(*block3_bhs_core.final_on_strong_qc_block_num, block2_last_final_block_num); // new last_qc_block_num is the same as input - BOOST_REQUIRE_EQUAL(*block3_bhs_core.last_qc_block_num, block3_input_last_qc_block_num); - auto block3_last_qc_block_num = *block3_bhs_core.last_qc_block_num; + BOOST_REQUIRE_EQUAL(block3_bhs_core.last_qc_block_num, block3_input_last_qc_block_num); + auto block3_last_qc_block_num = block3_bhs_core.last_qc_block_num; // block3 --> block4 constexpr auto block4_input_last_qc_block_num = 3u; @@ -92,9 +93,9 @@ BOOST_AUTO_TEST_CASE(block_header_state_core_3_chain_transition_test) // final_on_strong_qc_block_num should be block3's last_qc_block_num BOOST_REQUIRE_EQUAL(*block4_bhs_core.final_on_strong_qc_block_num, block3_last_qc_block_num); // new last_qc_block_num is the same as input - BOOST_REQUIRE_EQUAL(*block4_bhs_core.last_qc_block_num, block4_input_last_qc_block_num); + BOOST_REQUIRE_EQUAL(block4_bhs_core.last_qc_block_num, block4_input_last_qc_block_num); auto block4_final_on_strong_qc_block_num = *block4_bhs_core.final_on_strong_qc_block_num; - auto block4_last_qc_block_num = *block4_bhs_core.last_qc_block_num; + auto block4_last_qc_block_num = block4_bhs_core.last_qc_block_num; // block4 --> block5 constexpr auto block5_input_last_qc_block_num = 4u; @@ -104,7 +105,7 @@ BOOST_AUTO_TEST_CASE(block_header_state_core_3_chain_transition_test) // final_on_strong_qc_block_num should be block4's last_qc_block_num BOOST_REQUIRE_EQUAL(*block5_bhs_core.final_on_strong_qc_block_num, block4_last_qc_block_num); // new last_qc_block_num is the same as input - BOOST_REQUIRE_EQUAL(*block5_bhs_core.last_qc_block_num, block5_input_last_qc_block_num); + BOOST_REQUIRE_EQUAL(block5_bhs_core.last_qc_block_num, block5_input_last_qc_block_num); } BOOST_AUTO_TEST_SUITE_END() From 57371bd540d369e706a46d3476e084101686b6d2 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 14 Feb 2024 10:12:49 -0600 Subject: [PATCH 05/40] GH-2125 Move get_activated_protocol_features to block_header_state[_legacy] --- libraries/chain/fork_database.cpp | 2 +- libraries/chain/include/eosio/chain/block_header_state.hpp | 2 +- .../chain/include/eosio/chain/block_header_state_legacy.hpp | 1 + libraries/chain/include/eosio/chain/block_state.hpp | 1 - libraries/chain/include/eosio/chain/block_state_legacy.hpp | 1 - 5 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index bf37c60c7c..780dfbb6a8 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -349,7 +349,7 @@ namespace eosio::chain { const auto& pfa = exts.lower_bound(protocol_feature_activation::extension_id())->second; const auto& new_protocol_features = std::get(pfa).protocol_features; validator(n->timestamp(), - static_cast(prev_bh.get())->get_activated_protocol_features()->protocol_features, + prev_bh.get()->get_activated_protocol_features()->protocol_features, new_protocol_features); } } diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index 0fd6b54e2f..faa4f32d08 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -74,6 +74,7 @@ struct block_header_state { const block_id_type& previous() const { return header.previous; } uint32_t block_num() const { return block_header::num_from_id(previous()) + 1; } const producer_authority_schedule& active_schedule_auth() const { return active_proposer_policy->proposer_schedule; } + const protocol_feature_activation_set_ptr& get_activated_protocol_features() const { return activated_protocol_features; } block_header_state next(block_header_state_input& data) const; @@ -84,7 +85,6 @@ struct block_header_state { return qc.block_num > core.last_qc_block_num; } - flat_set get_activated_protocol_features() const { return activated_protocol_features->protocol_features; } const vector& get_new_protocol_feature_activations() const; producer_authority get_scheduled_producer(block_timestamp_type t) const; uint32_t active_schedule_version() const; diff --git a/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp index 95a3e71f69..204bb291d7 100644 --- a/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state_legacy.hpp @@ -170,6 +170,7 @@ struct block_header_state_legacy : public detail::block_header_state_legacy_comm uint32_t calc_dpos_last_irreversible( account_name producer_of_next_block )const; + const protocol_feature_activation_set_ptr& get_activated_protocol_features() const { return activated_protocol_features; } producer_authority get_scheduled_producer( block_timestamp_type t )const; const block_id_type& prev()const { return header.previous; } digest_type sig_digest()const; diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 3c7797e78b..5e7d669c29 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -34,7 +34,6 @@ struct block_state : public block_header_state { // block_header_state provi std::optional get_best_qc() const; bool is_best_qc_strong() const; - protocol_feature_activation_set_ptr get_activated_protocol_features() const { return block_header_state::activated_protocol_features; } bool is_pub_keys_recovered() const { return pub_keys_recovered; } deque extract_trxs_metas(); void set_trxs_metas(deque&& trxs_metas, bool keys_recovered); diff --git a/libraries/chain/include/eosio/chain/block_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_state_legacy.hpp index 72de14c279..5fc987b5d1 100644 --- a/libraries/chain/include/eosio/chain/block_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_state_legacy.hpp @@ -42,7 +42,6 @@ namespace eosio::chain { bool is_valid() const { return validated; } void set_valid(bool b) { validated = b; } - protocol_feature_activation_set_ptr get_activated_protocol_features() const { return activated_protocol_features; } const producer_authority_schedule& active_schedule_auth() const { return block_header_state_legacy_common::active_schedule; } const producer_authority_schedule* pending_schedule_auth() const { return &block_header_state_legacy::pending_schedule.schedule; } const deque& trxs_metas() const { return _cached_trxs; } From 6ab114689d701e59156a37d942200c55bf428fee Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 14 Feb 2024 13:32:23 -0600 Subject: [PATCH 06/40] GH-2125 Remove fork database adaptors and use fork database accessors instead. Move current_core to block_state. --- libraries/chain/fork_database.cpp | 363 ++++++++++-------- .../chain/include/eosio/chain/block_state.hpp | 29 +- .../eosio/chain/block_state_legacy.hpp | 9 +- .../include/eosio/chain/fork_database.hpp | 129 +------ 4 files changed, 254 insertions(+), 276 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 780dfbb6a8..e0492c137c 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -21,85 +21,145 @@ namespace eosio::chain { * Version 1: initial version of the new refactored fork database portable format */ + // call while holding fork database lock + struct block_state_accessor { + static bool is_valid(const block_state& bs) { + return bs.validated; + } + static void set_valid(block_state& bs, bool v) { + bs.validated = v; + } + static uint32_t last_final_block_num(const block_state& bs) { + return bs.current_core.last_final_block_num; + } + static uint32_t final_on_strong_qc_block_num(const block_state& bs) { + return bs.current_core.final_on_strong_qc_block_num.value_or(bs.current_core.last_final_block_num); + } + static uint32_t last_qc_block_num(const block_state& bs) { + return bs.current_core.last_qc_block_num; + } + static void update_best_qc_strong(block_state& bs, uint32_t block_num) { + if (bs.current_core.last_qc_block_num < block_num) + bs.current_core = bs.current_core.next(qc_claim_t{.last_qc_block_num = block_num, .is_last_qc_strong = true}); + } + + // thread safe + static uint32_t block_height(const block_state& bs) { + return bs.timestamp().slot; + } + }; + + struct block_state_legacy_accessor { + static bool is_valid(const block_state_legacy& bs) { + return bs.validated; + } + static void set_valid(block_state_legacy& bs, bool v) { + bs.validated = v; + } + static uint32_t last_final_block_num(const block_state_legacy& bs) { + return bs.irreversible_blocknum(); + } + static uint32_t final_on_strong_qc_block_num(const block_state_legacy& bs) { + return bs.irreversible_blocknum(); + } + static uint32_t last_qc_block_num(const block_state_legacy& bs) { + return bs.irreversible_blocknum(); + } + static void update_best_qc_strong(block_state_legacy&, uint32_t) {} // no-op + + // thread safe + static uint32_t block_height(const block_state_legacy& bs) { + return bs.block_num(); + } + }; + struct by_block_id; struct by_best_branch; struct by_prev; // match comparison of by_best_branch - template - bool first_preferred( const bsa& lhs, const bsa& rhs ) { - return std::make_tuple(lhs.last_final_block_num(), lhs.final_on_strong_qc_block_num(), lhs.last_qc_block_num(), lhs.block_height()) > - std::make_tuple(rhs.last_final_block_num(), rhs.final_on_strong_qc_block_num(), rhs.last_qc_block_num(), rhs.block_height()); + template + bool first_preferred( const BS& lhs, const BS& rhs ) { + using BSA = BS::fork_db_block_state_accessor; + return std::make_tuple(BSA::last_final_block_num(lhs), BSA::final_on_strong_qc_block_num(lhs), BSA::last_qc_block_num(lhs), BSA::block_height(lhs)) > + std::make_tuple(BSA::last_final_block_num(rhs), BSA::final_on_strong_qc_block_num(rhs), BSA::last_qc_block_num(rhs), BSA::block_height(rhs)); } - template // either [block_state_legacy_forkdb_adaptor_ptr, block_state_forkdb_adaptor_ptr], same with block_header_state_ptr + template // either [block_state_legacy_ptr, block_state_ptr], same with block_header_state_ptr struct fork_database_impl { - using bsa = BSAdaptorPtr::element_type; - using bs = bsa::bs; - using bhsp = bs::bhsp_t; - using bhs = bhsp::element_type; - using bsp = BSAdaptorPtr::element_type::bsp_t; + using BS = BSP::element_type; + using BSAccessor = BS::fork_db_block_state_accessor; + using BHSP = BS::bhsp_t; + using BHS = BHSP::element_type; - using fork_db_t = fork_database_t; + using fork_db_t = fork_database_t; using branch_type = fork_db_t::branch_type; using branch_type_pair = fork_db_t::branch_type_pair; using fork_multi_index_type = multi_index_container< - BSAdaptorPtr, + BSP, indexed_by< - hashed_unique, BOOST_MULTI_INDEX_CONST_MEM_FUN(bsa, const block_id_type&, id), std::hash>, - ordered_non_unique, const_mem_fun>, + hashed_unique, BOOST_MULTI_INDEX_CONST_MEM_FUN(BS, const block_id_type&, id), std::hash>, + ordered_non_unique, const_mem_fun>, ordered_unique, - composite_key, - composite_key_compare, std::greater, std::greater, std::greater, std::greater, sha256_less>>>>; + composite_key, + // see first_preferred comment + global_fun, + global_fun, + global_fun, + global_fun, + const_mem_fun + >, + composite_key_compare, + std::greater, std::greater, std::greater, std::greater, + sha256_less + > + > + > + >; std::mutex mtx; fork_multi_index_type index; - BSAdaptorPtr root; // Only uses the block_header_state portion of block_state - BSAdaptorPtr head; + BSP root; // Only uses the block_header_state portion of block_state + BSP head; const uint32_t magic_number; explicit fork_database_impl(uint32_t magic_number) : magic_number(magic_number) {} void open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ); void close_impl( const std::filesystem::path& fork_db_file ); - void add_impl( const bsp& n, bool ignore_duplicate, bool validate, validator_t& validator ); + void add_impl( const BSP& n, bool ignore_duplicate, bool validate, validator_t& validator ); - bhsp get_block_header_impl( const block_id_type& id ) const; - BSAdaptorPtr get_block_impl( const block_id_type& id ) const; - void reset_root_impl( const bhs& root_bhs ); + BHSP get_block_header_impl( const block_id_type& id ) const; + BSP get_block_impl( const block_id_type& id ) const; + void reset_root_impl( const BHS& root_bhs ); void rollback_head_to_root_impl(); void advance_root_impl( const block_id_type& id ); void remove_impl( const block_id_type& id ); branch_type 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; - bsp search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; - void mark_valid_impl( const bsp& h ); + BSP search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; + void mark_valid_impl( const BSP& h ); void update_best_qc_strong_impl( const block_id_type& id ); branch_type_pair fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; }; - template - fork_database_t::fork_database_t(uint32_t magic_number) - :my( new fork_database_impl(magic_number) ) + template + fork_database_t::fork_database_t(uint32_t magic_number) + :my( new fork_database_impl(magic_number) ) {} - template - void fork_database_t::open( const std::filesystem::path& fork_db_file, validator_t& validator ) { + template + void fork_database_t::open( const std::filesystem::path& fork_db_file, validator_t& validator ) { std::lock_guard g( my->mtx ); my->open_impl( fork_db_file, validator ); } - template - void fork_database_impl::open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ) { + template + void fork_database_impl::open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ) { if( std::filesystem::exists( fork_db_file ) ) { try { string content; @@ -128,17 +188,17 @@ namespace eosio::chain { ("max", fork_database::max_supported_version) ); - bhs state; + BHS state; fc::raw::unpack( ds, state ); reset_root_impl( state ); unsigned_int size; fc::raw::unpack( ds, size ); for( uint32_t i = 0, n = size.value; i < n; ++i ) { - bs s; + BS s; fc::raw::unpack( ds, s ); // do not populate transaction_metadatas, they will be created as needed in apply_block with appropriate key recovery s.header_exts = s.block->validate_and_extract_header_extensions(); - add_impl( std::make_shared( std::move( s ) ), false, true, validator ); + add_impl( std::make_shared( std::move( s ) ), false, true, validator ); } block_id_type head_id; fc::raw::unpack( ds, head_id ); @@ -153,7 +213,7 @@ namespace eosio::chain { } auto candidate = index.template get().begin(); - if( candidate == index.template get().end() || !(*candidate)->is_valid() ) { + if( candidate == index.template get().end() || !BSAccessor::is_valid(**candidate) ) { EOS_ASSERT( head->id() == root->id(), fork_database_exception, "head not set to root despite no better option available; '${filename}' is likely corrupted", ("filename", fork_db_file) ); @@ -168,14 +228,14 @@ namespace eosio::chain { } } - template - void fork_database_t::close(const std::filesystem::path& fork_db_file) { + template + void fork_database_t::close(const std::filesystem::path& fork_db_file) { std::lock_guard g( my->mtx ); my->close_impl(fork_db_file); } - template - void fork_database_impl::close_impl(const std::filesystem::path& fork_db_file) { + template + void fork_database_impl::close_impl(const std::filesystem::path& fork_db_file) { if( !root ) { if( index.size() > 0 ) { elog( "fork_database is in a bad state when closing; not writing out '${filename}'", @@ -187,7 +247,7 @@ namespace eosio::chain { std::ofstream out( fork_db_file.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc ); fc::raw::pack( out, magic_number ); fc::raw::pack( out, fork_database::max_supported_version ); // write out current version which is always max_supported_version - fc::raw::pack( out, *static_cast(&*root->get()) ); + fc::raw::pack( out, *static_cast(&*root) ); uint32_t num_blocks_in_fork_db = index.size(); fc::raw::pack( out, unsigned_int{num_blocks_in_fork_db} ); @@ -223,7 +283,7 @@ namespace eosio::chain { ++validated_itr; } - fc::raw::pack( out, *(*itr)->get() ); + fc::raw::pack( out, *(*itr) ); } if( head ) { @@ -236,55 +296,54 @@ namespace eosio::chain { index.clear(); } - template - void fork_database_t::reset_root( const bhs& root_bhs ) { + template + void fork_database_t::reset_root( const BHS& root_bhs ) { std::lock_guard g( my->mtx ); my->reset_root_impl(root_bhs); } - template - void fork_database_impl::reset_root_impl( const bhs& root_bhs ) { + template + void fork_database_impl::reset_root_impl( const BHS& root_bhs ) { index.clear(); - bsp root_bsp = std::make_shared(); - static_cast(*root_bsp) = root_bhs; - root = std::make_shared(std::move(root_bsp)); - root->set_valid(true); + root = std::make_shared(); + static_cast(*root) = root_bhs; + BSAccessor::set_valid(*root, true); head = root; } - template - void fork_database_t::rollback_head_to_root() { + template + void fork_database_t::rollback_head_to_root() { std::lock_guard g( my->mtx ); my->rollback_head_to_root_impl(); } - template - void fork_database_impl::rollback_head_to_root_impl() { + template + void fork_database_impl::rollback_head_to_root_impl() { auto& by_id_idx = index.template get(); auto itr = by_id_idx.begin(); while (itr != by_id_idx.end()) { by_id_idx.modify( itr, []( auto& i ) { - i->set_valid(false); + BSAccessor::set_valid(*i, false); } ); ++itr; } head = root; } - template - void fork_database_t::advance_root( const block_id_type& id ) { + template + void fork_database_t::advance_root( const block_id_type& id ) { std::lock_guard g( my->mtx ); my->advance_root_impl( id ); } - template - void fork_database_impl::advance_root_impl( const block_id_type& id ) { + template + void fork_database_impl::advance_root_impl( const block_id_type& id ) { EOS_ASSERT( root, fork_database_exception, "root not yet set" ); auto new_root = get_block_impl( id ); EOS_ASSERT( new_root, fork_database_exception, "cannot advance root to a block that does not exist in the fork database" ); - EOS_ASSERT( new_root->is_valid(), fork_database_exception, + EOS_ASSERT( BSAccessor::is_valid(*new_root), fork_database_exception, "cannot advance root to a block that has not yet been validated" ); @@ -312,65 +371,64 @@ namespace eosio::chain { root = new_root; } - template - fork_database_t::bhsp fork_database_t::get_block_header( const block_id_type& id ) const { + template + fork_database_t::BHSP fork_database_t::get_block_header( const block_id_type& id ) const { std::lock_guard g( my->mtx ); return my->get_block_header_impl( id ); } - template - fork_database_impl::bhsp fork_database_impl::get_block_header_impl( const block_id_type& id ) const { + template + fork_database_impl::BHSP fork_database_impl::get_block_header_impl( const block_id_type& id ) const { if( root->id() == id ) { - return root->get(); + return root; } auto itr = index.find( id ); if( itr != index.end() ) - return (*itr)->get(); + return *itr; - return bhsp(); + return BHSP(); } - template - void fork_database_impl::add_impl(const bsp& n, bool ignore_duplicate, bool validate, validator_t& validator) { + template + void fork_database_impl::add_impl(const BSP& n, bool ignore_duplicate, bool validate, validator_t& validator) { EOS_ASSERT( root, fork_database_exception, "root not yet set" ); EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" ); - auto prev_bh = get_block_header_impl( n->previous() ); - - EOS_ASSERT( prev_bh, unlinkable_block_exception, + auto itr = index.find( n->previous() ); + EOS_ASSERT( itr != index.end() || root->id() == n->previous(), unlinkable_block_exception, "unlinkable block", ("id", n->id())("previous", n->previous()) ); if (validate) { try { const auto& exts = n->header_exts; - if (exts.count(protocol_feature_activation::extension_id()) > 0) { - const auto& pfa = exts.lower_bound(protocol_feature_activation::extension_id())->second; + if (auto i = exts.lower_bound(protocol_feature_activation::extension_id()); i != exts.end() ) { + const auto& prev_protocol_features = itr != index.end() ? (*itr)->get_activated_protocol_features()->protocol_features + : root->get_activated_protocol_features()->protocol_features; + const auto& pfa = i->second; const auto& new_protocol_features = std::get(pfa).protocol_features; - validator(n->timestamp(), - prev_bh.get()->get_activated_protocol_features()->protocol_features, - new_protocol_features); + validator(n->timestamp(), prev_protocol_features, new_protocol_features); } } EOS_RETHROW_EXCEPTIONS(fork_database_exception, "serialized fork database is incompatible with configured protocol features") } - auto inserted = index.insert(std::make_shared(n)); + auto inserted = index.insert(n); if( !inserted.second ) { if( ignore_duplicate ) return; EOS_THROW( fork_database_exception, "duplicate block added", ("id", n->id()) ); } auto candidate = index.template get().begin(); - if( (*candidate)->is_valid() ) { + if( BSAccessor::is_valid(**candidate) ) { head = *candidate; } } - template - void fork_database_t::add( const bsp& n, bool ignore_duplicate ) { + template + void fork_database_t::add( const BSP& n, bool ignore_duplicate ) { std::lock_guard g( my->mtx ); my->add_impl( n, ignore_duplicate, false, []( block_timestamp_type timestamp, @@ -380,86 +438,86 @@ namespace eosio::chain { ); } - template - bool fork_database_t::has_root() const { + template + bool fork_database_t::has_root() const { return !!my->root; } - template - fork_database_t::bsp fork_database_t::root() const { + template + BSP fork_database_t::root() const { std::lock_guard g( my->mtx ); - return my->root->get(); + return my->root; } - template - fork_database_t::bsp fork_database_t::head() const { + template + BSP fork_database_t::head() const { std::lock_guard g( my->mtx ); - return my->head ? my->head->get() : bsp(); + return my->head; } - template - fork_database_t::bsp fork_database_t::pending_head() const { + template + BSP fork_database_t::pending_head() const { std::lock_guard g( my->mtx ); const auto& indx = my->index.template get(); auto itr = indx.lower_bound( false ); - if( itr != indx.end() && !(*itr)->is_valid() ) { + if( itr != indx.end() && !fork_database_impl::BSAccessor::is_valid(**itr) ) { if( first_preferred( **itr, *my->head ) ) - return (*itr)->get(); + return *itr; } - return my->head ? my->head->get() : bsp(); + return my->head; } - template - fork_database_t::branch_type - fork_database_t::fetch_branch(const block_id_type& h, uint32_t trim_after_block_num) const { + template + fork_database_t::branch_type + fork_database_t::fetch_branch(const block_id_type& h, uint32_t trim_after_block_num) const { std::lock_guard g(my->mtx); return my->fetch_branch_impl(h, trim_after_block_num); } - template - fork_database_t::branch_type - fork_database_impl::fetch_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { + template + fork_database_t::branch_type + fork_database_impl::fetch_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { branch_type result; for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { if ((*i)->block_num() <= trim_after_block_num) - result.push_back((*i)->get()); + result.push_back(*i); } return result; } - template + template block_branch_t - fork_database_t::fetch_block_branch(const block_id_type& h, uint32_t trim_after_block_num) const { + fork_database_t::fetch_block_branch(const block_id_type& h, uint32_t trim_after_block_num) const { std::lock_guard g(my->mtx); return my->fetch_block_branch_impl(h, trim_after_block_num); } - template + template block_branch_t - fork_database_impl::fetch_block_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { + fork_database_impl::fetch_block_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { block_branch_t result; for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { if ((*i)->block_num() <= trim_after_block_num) - result.push_back((*i)->block()); + result.push_back((*i)->block); } return result; } - template - fork_database_t::bsp fork_database_t::search_on_branch( const block_id_type& h, uint32_t block_num ) const { + template + BSP fork_database_t::search_on_branch( const block_id_type& h, uint32_t block_num ) const { std::lock_guard g( my->mtx ); return my->search_on_branch_impl( h, block_num ); } - template - fork_database_impl::bsp fork_database_impl::search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const { + template + BSP fork_database_impl::search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const { for( auto i = index.find(h); i != index.end(); i = index.find( (*i)->previous() ) ) { if ((*i)->block_num() == block_num) - return (*i)->get(); + return *i; } return {}; @@ -469,16 +527,16 @@ namespace eosio::chain { * Given two head blocks, return two branches of the fork graph that * end with a common ancestor (same prior block) */ - template - fork_database_t::branch_type_pair - fork_database_t::fetch_branch_from(const block_id_type& first, const block_id_type& second) const { + template + fork_database_t::branch_type_pair + fork_database_t::fetch_branch_from(const block_id_type& first, const block_id_type& second) const { std::lock_guard g(my->mtx); return my->fetch_branch_from_impl(first, second); } - template - fork_database_t::branch_type_pair - fork_database_impl::fetch_branch_from_impl(const block_id_type& first, const block_id_type& second) const { + template + fork_database_t::branch_type_pair + fork_database_impl::fetch_branch_from_impl(const block_id_type& first, const block_id_type& second) const { pair result; auto first_branch = (first == root->id()) ? root : get_block_impl(first); auto second_branch = (second == root->id()) ? root : get_block_impl(second); @@ -488,7 +546,7 @@ namespace eosio::chain { while( first_branch->block_num() > second_branch->block_num() ) { - result.first.push_back(first_branch->get()); + result.first.push_back(first_branch); const auto& prev = first_branch->previous(); first_branch = (prev == root->id()) ? root : get_block_impl( prev ); EOS_ASSERT( first_branch, fork_db_block_not_found, @@ -499,7 +557,7 @@ namespace eosio::chain { while( second_branch->block_num() > first_branch->block_num() ) { - result.second.push_back( second_branch->get() ); + result.second.push_back( second_branch ); const auto& prev = second_branch->previous(); second_branch = (prev == root->id()) ? root : get_block_impl( prev ); EOS_ASSERT( second_branch, fork_db_block_not_found, @@ -512,8 +570,8 @@ namespace eosio::chain { while( first_branch->previous() != second_branch->previous() ) { - result.first.push_back(first_branch->get()); - result.second.push_back(second_branch->get()); + result.first.push_back(first_branch); + result.second.push_back(second_branch); const auto &first_prev = first_branch->previous(); first_branch = get_block_impl( first_prev ); const auto &second_prev = second_branch->previous(); @@ -530,21 +588,21 @@ namespace eosio::chain { if( first_branch && second_branch ) { - result.first.push_back(first_branch->get()); - result.second.push_back(second_branch->get()); + result.first.push_back(first_branch); + result.second.push_back(second_branch); } return result; } /// fetch_branch_from_impl /// remove all of the invalid forks built off of this id including this id - template - void fork_database_t::remove( const block_id_type& id ) { + template + void fork_database_t::remove( const block_id_type& id ) { std::lock_guard g( my->mtx ); return my->remove_impl( id ); } - template - void fork_database_impl::remove_impl( const block_id_type& id ) { + template + void fork_database_impl::remove_impl( const block_id_type& id ) { deque remove_queue{id}; const auto& previdx = index.template get(); const auto& head_id = head->id(); @@ -565,15 +623,15 @@ namespace eosio::chain { } } - template - void fork_database_t::mark_valid( const bsp& h ) { + template + void fork_database_t::mark_valid( const BSP& h ) { std::lock_guard g( my->mtx ); my->mark_valid_impl( h ); } - template - void fork_database_impl::mark_valid_impl( const bsp& h ) { - if( h->is_valid() ) return; + template + void fork_database_impl::mark_valid_impl( const BSP& h ) { + if( BSAccessor::is_valid(*h) ) return; auto& by_id_idx = index.template get(); @@ -583,7 +641,7 @@ namespace eosio::chain { ("id", h->id()) ); by_id_idx.modify( itr, []( auto& i ) { - i->set_valid(true); + BSAccessor::set_valid(*i, true); } ); auto candidate = index.template get().begin(); @@ -592,14 +650,14 @@ namespace eosio::chain { } } - template - void fork_database_t::update_best_qc_strong( const block_id_type& id ) { + template + void fork_database_t::update_best_qc_strong( const block_id_type& id ) { std::lock_guard g( my->mtx ); my->update_best_qc_strong_impl( id ); } - template - void fork_database_impl::update_best_qc_strong_impl( const block_id_type& id ) { + template + void fork_database_impl::update_best_qc_strong_impl( const block_id_type& id ) { auto& by_id_idx = index.template get(); auto itr = by_id_idx.find( id ); @@ -607,7 +665,7 @@ namespace eosio::chain { "block state not in fork database; cannot update", ("id", id) ); by_id_idx.modify( itr, []( auto& i ) { - i->update_best_qc_strong(); + BSAccessor::update_best_qc_strong(*i, i->block_num()); } ); auto candidate = index.template get().begin(); @@ -616,15 +674,14 @@ namespace eosio::chain { } } - template - fork_database_t::bsp fork_database_t::get_block(const block_id_type& id) const { + template + BSP fork_database_t::get_block(const block_id_type& id) const { std::lock_guard g( my->mtx ); - auto bsap = my->get_block_impl(id); - return bsap ? bsap->get() : bsp{}; + return my->get_block_impl(id); } - template - BSAdaptorPtr fork_database_impl::get_block_impl(const block_id_type& id) const { + template + BSP fork_database_impl::get_block_impl(const block_id_type& id) const { auto itr = index.find( id ); if( itr != index.end() ) return *itr; @@ -709,10 +766,10 @@ namespace eosio::chain { } // do class instantiations - template class fork_database_t; - template class fork_database_t; + template class fork_database_t; + template class fork_database_t; - template struct fork_database_impl; - template struct fork_database_impl; + template struct fork_database_impl; + template struct fork_database_impl; } /// eosio::chain diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 5e7d669c29..2ad6ea4dbb 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -8,37 +8,47 @@ namespace eosio::chain { struct block_state_legacy; +struct block_state_accessor; struct block_state : public block_header_state { // block_header_state provides parent link // ------ data members ------------------------------------------------------------- signed_block_ptr block; - bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches. digest_type strong_digest; // finalizer_digest (strong, cached so we can quickly validate votes) digest_type weak_digest; // finalizer_digest (weak, cached so we can quickly validate votes) pending_quorum_certificate pending_qc; // where we accumulate votes we receive std::optional valid_qc; // best qc received from the network inside block extension + // ------ updated for votes, used for fork_db ordering ------------------------------ +private: + bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches. + block_header_state_core current_core; // only modify/access while holding forkdb lock + // ------ data members caching information available elsewhere ---------------------- bool pub_keys_recovered = false; deque cached_trxs; + // ------ private methods ----------------------------------------------------------- + bool is_valid() const { return validated; } + bool is_pub_keys_recovered() const { return pub_keys_recovered; } + deque extract_trxs_metas(); + void set_trxs_metas(deque&& trxs_metas, bool keys_recovered); + const deque& trxs_metas() const { return cached_trxs; } + + friend struct block_state_accessor; + friend struct fc::reflector; + friend struct controller_impl; + friend struct completed_block; +public: // ------ functions ----------------------------------------------------------------- const block_id_type& id() const { return block_header_state::id; } const block_id_type& previous() const { return block_header_state::previous(); } uint32_t block_num() const { return block_header_state::block_num(); } block_timestamp_type timestamp() const { return block_header_state::timestamp(); } const extensions_type& header_extensions() const { return block_header_state::header.header_extensions; } - bool is_valid() const { return validated; } - void set_valid(bool b) { validated = b; } uint32_t irreversible_blocknum() const { return core.last_final_block_num; } std::optional get_best_qc() const; bool is_best_qc_strong() const; - bool is_pub_keys_recovered() const { return pub_keys_recovered; } - deque extract_trxs_metas(); - void set_trxs_metas(deque&& trxs_metas, bool keys_recovered); - const deque& trxs_metas() const { return cached_trxs; } - // vote_status, pending_qc state, last_final_block_num std::tuple> aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc @@ -46,6 +56,7 @@ struct block_state : public block_header_state { // block_header_state provi using bhs_t = block_header_state; using bhsp_t = block_header_state_ptr; + using fork_db_block_state_accessor = block_state_accessor; block_state() = default; @@ -63,4 +74,4 @@ using block_state_ptr = std::shared_ptr; } // namespace eosio::chain // not exporting pending_qc or valid_qc -FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(validated)(strong_digest)(weak_digest)(pending_qc)(valid_qc) ) +FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(strong_digest)(weak_digest)(pending_qc)(valid_qc)(validated)(current_core) ) diff --git a/libraries/chain/include/eosio/chain/block_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_state_legacy.hpp index 5fc987b5d1..3d6d9cd707 100644 --- a/libraries/chain/include/eosio/chain/block_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_state_legacy.hpp @@ -7,6 +7,8 @@ namespace eosio::chain { + struct block_state_legacy_accessor; + struct block_state_legacy : public block_header_state_legacy { using bhs_t = block_header_state_legacy; using bhsp_t = block_header_state_legacy_ptr; @@ -39,20 +41,21 @@ namespace eosio::chain { block_timestamp_type timestamp() const { return header.timestamp; } account_name producer() const { return header.producer; } const extensions_type& header_extensions() const { return header.header_extensions; } - bool is_valid() const { return validated; } - void set_valid(bool b) { validated = b; } - + const producer_authority_schedule& active_schedule_auth() const { return block_header_state_legacy_common::active_schedule; } const producer_authority_schedule* pending_schedule_auth() const { return &block_header_state_legacy::pending_schedule.schedule; } const deque& trxs_metas() const { return _cached_trxs; } + using fork_db_block_state_accessor = block_state_legacy_accessor; private: // internal use only, not thread safe + friend struct block_state_legacy_accessor; friend struct fc::reflector; friend struct controller_impl; friend struct completed_block; friend struct block_state; + bool is_valid() const { return validated; } bool is_pub_keys_recovered()const { return _pub_keys_recovered; } deque extract_trxs_metas() { diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index ca81d60531..53fa6b09f0 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -9,98 +9,6 @@ namespace eosio::chain { using block_branch_t = deque; - struct block_state_forkdb_adaptor { - private: - block_header_state_core current_core; // only modify/access while holding forkdb lock - block_state_ptr bsp; - - public: - using bs = block_state_ptr::element_type; - using bhsp = bs::bhsp_t; - using bhs = bhsp::element_type; - using bsp_t = block_state_ptr; - - explicit block_state_forkdb_adaptor(block_state_ptr bsp) - : current_core(bsp->core), bsp(std::move(bsp)) {} - - block_state_forkdb_adaptor(const block_state_forkdb_adaptor&) = delete; - block_state_forkdb_adaptor() = delete; - block_state_forkdb_adaptor& operator=(const block_state_forkdb_adaptor&) = delete; - block_state_forkdb_adaptor(block_state_forkdb_adaptor&&) = default; - - void update_best_qc_strong() { - if (current_core.last_qc_block_num != bsp->block_num()) - current_core = current_core.next(qc_claim_t{.last_qc_block_num = bsp->block_num(), .is_last_qc_strong = true}); - } - - // although valid is mutated and accessed, it should all be from the main thread or protected by forkdb mutex - void set_valid(bool v) { bsp->set_valid(v); } // not thread safe - bool is_valid() const { return bsp->is_valid(); } // not thread safe - - // only safe to call while holding fork_database lock - uint32_t last_final_block_num() const { - return current_core.last_final_block_num; - } - // only safe to call while holding fork_database lock - uint32_t final_on_strong_qc_block_num() const { - return current_core.final_on_strong_qc_block_num.value_or(last_final_block_num()); - } - // only safe to call while holding fork_database lock - uint32_t last_qc_block_num() const { - return current_core.last_qc_block_num; - } - - // thread safe - uint32_t block_height() const { return bsp->timestamp().slot; } - uint32_t block_num() const { return bsp->block_num(); } - const block_id_type& id() const { return bsp->id(); } - const block_id_type& previous() const { return bsp->previous(); } - const block_state_ptr& get() const { return bsp; } - const signed_block_ptr& block() const { return bsp->block; } - explicit operator bool() const noexcept { return !!bsp; } - }; - - // thread safe - struct block_state_legacy_forkdb_adaptor { - private: - block_state_legacy_ptr bsp; - - public: - using bs = block_state_legacy_ptr::element_type; - using bhsp = bs::bhsp_t; - using bhs = bhsp::element_type; - using bsp_t = block_state_legacy_ptr; - - explicit block_state_legacy_forkdb_adaptor(block_state_legacy_ptr bsp) : bsp(std::move(bsp)) {} - - block_state_legacy_forkdb_adaptor(const block_state_legacy_forkdb_adaptor&) = delete; - block_state_legacy_forkdb_adaptor() = delete; - block_state_legacy_forkdb_adaptor& operator=(const block_state_legacy_forkdb_adaptor&) = delete; - block_state_legacy_forkdb_adaptor(block_state_legacy_forkdb_adaptor&&) = default; - - void update_best_qc_strong() {} // no-op for legacy - - // although valid is mutated and accessed, it should all be from the main thread or protected by forkdb mutex - void set_valid(bool v) { bsp->set_valid(v); } // not thread safe - bool is_valid() const { return bsp->is_valid(); } - - // maintains the equivalent of legacy behavior - uint32_t last_final_block_num() const { return bsp->irreversible_blocknum(); } - uint32_t final_on_strong_qc_block_num() const { return bsp->irreversible_blocknum(); } - uint32_t last_qc_block_num() const { return bsp->irreversible_blocknum(); } - - uint32_t block_height() const { return bsp->block_num(); } - uint32_t block_num() const { return bsp->block_num(); } - const block_id_type& id() const { return bsp->id(); } - const block_id_type& previous() const { return bsp->previous(); } - const block_state_legacy_ptr& get() const { return bsp; } - const signed_block_ptr& block() const { return bsp->block; } - explicit operator bool() const noexcept { return !!bsp; } - }; - - using block_state_legacy_forkdb_adaptor_ptr = std::shared_ptr; - using block_state_forkdb_adaptor_ptr = std::shared_ptr; - /** * @class fork_database_t * @brief manages light-weight state for all potential unconfirmed forks @@ -115,17 +23,16 @@ namespace eosio::chain { * fork_database should be used instead of fork_database_t directly as it manages * the different supported types. */ - template // either block_state_legacy_forkdb_adaptor_ptr or block_state_forkdb_adaptor_ptr + template // either block_state_legacy_ptr or block_state_ptr class fork_database_t { public: static constexpr uint32_t legacy_magic_number = 0x30510FDB; static constexpr uint32_t magic_number = 0x4242FDB; - using bs = BSAdaptorPtr::element_type::bs; - using bhsp = bs::bhsp_t; - using bhs = bhsp::element_type; - using bsp_t = BSAdaptorPtr::element_type::bsp_t; - using bsp = bsp_t; + using BS = BSP::element_type; + using BHSP = BS::bhsp_t; + using BHS = BHSP::element_type; + using bsp_t = BSP; using branch_type = deque; using branch_type_pair = pair; @@ -134,14 +41,14 @@ namespace eosio::chain { void open( const std::filesystem::path& fork_db_file, validator_t& validator ); void close( const std::filesystem::path& fork_db_file ); - bhsp get_block_header( const block_id_type& id ) const; - bsp get_block( const block_id_type& id ) const; + BHSP get_block_header( const block_id_type& id ) const; + BSP get_block( const block_id_type& id ) const; /** * Purges any existing blocks from the fork database and resets the root block_header_state to the provided value. * The head will also be reset to point to the root. */ - void reset_root( const bhs& root_bhs ); + void reset_root( const BHS& root_bhs ); /** * Removes validated flag from all blocks in fork database and resets head to point to the root. @@ -157,17 +64,17 @@ namespace eosio::chain { * Add block state to fork database. * Must link to existing block in fork database or the root. */ - void add( const bsp& next_block, bool ignore_duplicate = false ); + void add( const BSP& next_block, bool ignore_duplicate = false ); void remove( const block_id_type& id ); bool has_root() const; - bsp root() const; // undefined if !has_root() - bsp head() const; - bsp pending_head() const; + BSP root() const; // undefined if !has_root() + BSP head() const; + BSP pending_head() const; // only accessed by main thread, no mutex protection - bsp chain_head; + BSP chain_head; /** * Returns the sequence of block states resulting from trimming the branch from the @@ -185,7 +92,7 @@ namespace eosio::chain { * Returns the block state with a block number of `block_num` that is on the branch that * contains a block with an id of`h`, or the empty shared pointer if no such block can be found. */ - bsp search_on_branch( const block_id_type& h, uint32_t block_num ) const; + BSP search_on_branch( const block_id_type& h, uint32_t block_num ) const; /** * Given two head blocks, return two branches of the fork graph that @@ -193,7 +100,7 @@ namespace eosio::chain { */ branch_type_pair fetch_branch_from(const block_id_type& first, const block_id_type& second) const; - void mark_valid( const bsp& h ); + void mark_valid( const BSP& h ); /** * Update block_state_core for best qc strong @@ -201,11 +108,11 @@ namespace eosio::chain { void update_best_qc_strong( const block_id_type& id ); private: - unique_ptr> my; + unique_ptr> my; }; - using fork_database_legacy_t = fork_database_t; - using fork_database_if_t = fork_database_t; + using fork_database_legacy_t = fork_database_t; + using fork_database_if_t = fork_database_t; /** * Provides mechanism for opening the correct type From 970528c4bb0d3d13b94fef41d91db551e6a67fee Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 26 Feb 2024 16:37:31 -0600 Subject: [PATCH 07/40] GH-2125 Add updated_core and most_recent_ancestor_with_qc to block_state, use values for fork_database by_best_branch rule. --- libraries/chain/block_header_state.cpp | 2 +- libraries/chain/block_state.cpp | 19 ++- libraries/chain/controller.cpp | 31 ++-- libraries/chain/fork_database.cpp | 142 ++++++++++++------ libraries/chain/hotstuff/hotstuff.cpp | 9 +- .../hotstuff/test/finality_misc_tests.cpp | 4 +- .../chain/include/eosio/chain/block_state.hpp | 15 +- .../include/eosio/chain/finality_core.hpp | 7 +- .../include/eosio/chain/fork_database.hpp | 7 +- .../include/eosio/chain/hotstuff/hotstuff.hpp | 6 +- 10 files changed, 151 insertions(+), 91 deletions(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index aa7e57a26e..b0a83331d7 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -95,7 +95,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con uint16_t if_ext_id = instant_finality_extension::extension_id(); emplace_extension(result.header.header_extensions, if_ext_id, fc::raw::pack(new_if_ext)); - result.header_exts.emplace(if_ext_id, std::move(new_if_ext)); + result.header_exts.emplace(if_ext_id, std::move(new_if_ext)); // TODO: does not appear to be used // add protocol_feature_activation extension // ----------------------------------------- diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index b5ba8864a0..da3ce25310 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -15,6 +15,8 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con , strong_digest(compute_finalizer_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()) + , most_recent_ancestor_with_qc(core.latest_qc_claim()) + , updated_core(core.next_metadata(most_recent_ancestor_with_qc)) { // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here if( !skip_validate_signee ) { @@ -32,7 +34,9 @@ block_state::block_state(const block_header_state& bhs, dequefinalizers.size(), bhs.active_finalizer_policy->threshold, bhs.active_finalizer_policy->max_weak_sum_before_weak_final()) - , pub_keys_recovered(true) // probably not needed + , most_recent_ancestor_with_qc(core.latest_qc_claim()) + , updated_core(core.next_metadata(most_recent_ancestor_with_qc)) + , pub_keys_recovered(true) // called by produce_block so signature recovery of trxs must have been done , cached_trxs(std::move(trx_metas)) { block->transactions = std::move(trx_receipts); @@ -49,6 +53,8 @@ block_state::block_state(const block_state_legacy& bsp) { block_header_state::block_id = bsp.id(); header = bsp.header; core = finality_core::create_core_for_genesis_block(bsp.block_num()); // [if todo] instant transition is not acceptable + most_recent_ancestor_with_qc = core.latest_qc_claim(); + updated_core = core.next_metadata(most_recent_ancestor_with_qc); activated_protocol_features = bsp.activated_protocol_features; auto if_ext_id = instant_finality_extension::extension_id(); @@ -83,7 +89,7 @@ void block_state::set_trxs_metas( deque&& trxs_metas, } // Called from net threads -std::tuple> +std::tuple block_state::aggregate_vote(const vote_message& vote) { const auto& finalizers = active_finalizer_policy->finalizers; auto it = std::find_if(finalizers.begin(), @@ -93,19 +99,16 @@ block_state::aggregate_vote(const vote_message& vote) { if (it != finalizers.end()) { auto index = std::distance(finalizers.begin(), it); auto digest = vote.strong ? strong_digest.to_uint8_span() : std::span(weak_digest); - auto [status, state] = pending_qc.add_vote(vote.strong, + return pending_qc.add_vote(block_num(), + vote.strong, digest, index, vote.finalizer_key, vote.sig, finalizers[index].weight); - std::optional new_lib{}; - if (status == vote_status::success && state == pending_quorum_certificate::state_t::strong) - new_lib = core.final_on_strong_qc_block_num; - return {status, state, new_lib}; } else { wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) ); - return {vote_status::unknown_public_key, pending_quorum_certificate::state_t::unrestricted, {}}; + return {vote_status::unknown_public_key, pending_quorum_certificate::state_t::unrestricted}; } } diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index c106dd525b..c32c249e7f 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2780,8 +2780,7 @@ struct controller_impl { const auto& bsp = std::get>(cb.bsp); if( s == controller::block_status::incomplete ) { - forkdb.add( bsp ); - forkdb.mark_valid( bsp ); + forkdb.add( bsp, true, false ); emit( accepted_block_header, std::tie(bsp->block, bsp->id()) ); EOS_ASSERT( bsp == forkdb.head(), fork_database_exception, "committed block did not become the new head in fork database"); } else if (s != controller::block_status::irreversible) { @@ -3112,15 +3111,21 @@ struct controller_impl { // called from net threads and controller's thread pool vote_status process_vote_message( const vote_message& vote ) { auto aggregate_vote = [&vote](auto& forkdb) -> std::pair> { - auto bsp = forkdb.get_block(vote.proposal_id); - if (bsp) { - auto [vote_state, state, block_num] = bsp->aggregate_vote(vote); - if (vote_state == vote_status::success && state == pending_quorum_certificate::state_t::strong) { // if block_num then strong vote - forkdb.update_best_qc_strong(bsp->id()); - } - return {vote_state, block_num}; - } - return {vote_status::unknown_block, {}}; + auto bsp = forkdb.get_block(vote.proposal_id); + if (bsp) { + auto [status, state] = bsp->aggregate_vote(vote); + std::optional new_lib{}; + if (status == vote_status::success && pending_quorum_certificate::is_quorum_met(state)) { + if (state == pending_quorum_certificate::state_t::strong) { + new_lib = bsp->core.final_on_strong_qc_block_num; + forkdb.update_best_qc(bsp->id(), {.block_num = bsp->block_num(), .is_strong_qc = true}); + } else { + forkdb.update_best_qc(bsp->id(), {.block_num = bsp->block_num(), .is_strong_qc = false}); + } + } + return {status, new_lib}; + } + return {vote_status::unknown_block, {}}; }; // TODO: https://github.com/AntelopeIO/leap/issues/2057 // TODO: Do not aggregate votes on block_state if in legacy block fork_db @@ -3428,7 +3433,7 @@ struct controller_impl { auto do_push = [&](auto& forkdb) { if constexpr (std::is_same_v>) - forkdb.add( bsp ); + forkdb.add( bsp, false, false ); if (is_trusted_producer(b->producer)) { trusted_producer_light_validation = true; @@ -3478,7 +3483,7 @@ struct controller_impl { *forkdb.chain_head, b, protocol_features.get_protocol_feature_set(), validator, skip_validate_signee); if (s != controller::block_status::irreversible) { - forkdb.add(bsp, true); + forkdb.add(bsp, false, true); } emit(accepted_block_header, std::tie(bsp->block, bsp->id())); diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index b8a5139796..8851a790e3 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -24,23 +23,34 @@ namespace eosio::chain { // call while holding fork database lock struct block_state_accessor { static bool is_valid(const block_state& bs) { - return bs.validated; + return bs.is_valid(); } static void set_valid(block_state& bs, bool v) { bs.validated = v; } static uint32_t last_final_block_num(const block_state& bs) { - return bs.current_core.last_final_block_num; + return bs.updated_core.last_final_block_num; } static uint32_t final_on_strong_qc_block_num(const block_state& bs) { - return bs.current_core.final_on_strong_qc_block_num.value_or(bs.current_core.last_final_block_num); + return bs.updated_core.final_on_strong_qc_block_num; } - static uint32_t last_qc_block_num(const block_state& bs) { - return bs.current_core.last_qc_block_num; + static uint32_t lastest_qc_claim_block_num(const block_state& bs) { + return bs.updated_core.latest_qc_claim_block_num; } - static void update_best_qc_strong(block_state& bs, uint32_t block_num) { - if (bs.current_core.last_qc_block_num < block_num) - bs.current_core = bs.current_core.next(qc_claim_t{.last_qc_block_num = block_num, .is_last_qc_strong = true}); + static bool qc_claim_update_needed(block_state& bs, const qc_claim_t& most_recent_ancestor_with_qc) { + return bs.most_recent_ancestor_with_qc < most_recent_ancestor_with_qc; + } + static bool qc_claim_update_needed(block_state& bs, const block_state& prev) { + return bs.most_recent_ancestor_with_qc < prev.most_recent_ancestor_with_qc; + } + static void update_best_qc(block_state& bs, const qc_claim_t& most_recent_ancestor_with_qc) { + assert(bs.most_recent_ancestor_with_qc < most_recent_ancestor_with_qc); + bs.updated_core = bs.core.next_metadata(most_recent_ancestor_with_qc); + bs.most_recent_ancestor_with_qc = most_recent_ancestor_with_qc; + } + static void update_best_qc(block_state& bs, const block_state& prev) { + assert(bs.most_recent_ancestor_with_qc < prev.most_recent_ancestor_with_qc); + update_best_qc(bs, prev.most_recent_ancestor_with_qc); } // thread safe @@ -50,39 +60,37 @@ namespace eosio::chain { }; struct block_state_legacy_accessor { - static bool is_valid(const block_state_legacy& bs) { - return bs.validated; - } - static void set_valid(block_state_legacy& bs, bool v) { - bs.validated = v; - } - static uint32_t last_final_block_num(const block_state_legacy& bs) { - return bs.irreversible_blocknum(); - } - static uint32_t final_on_strong_qc_block_num(const block_state_legacy& bs) { - return bs.irreversible_blocknum(); - } - static uint32_t last_qc_block_num(const block_state_legacy& bs) { - return bs.irreversible_blocknum(); - } - static void update_best_qc_strong(block_state_legacy&, uint32_t) {} // no-op + static bool is_valid(const block_state_legacy& bs) { return bs.is_valid(); } + static void set_valid(block_state_legacy& bs, bool v) { bs.validated = v; } + static uint32_t last_final_block_num(const block_state_legacy& bs) { return bs.irreversible_blocknum(); } + static uint32_t final_on_strong_qc_block_num(const block_state_legacy& bs) { return bs.irreversible_blocknum(); } + static uint32_t lastest_qc_claim_block_num(const block_state_legacy& bs) { return bs.irreversible_blocknum(); } + static bool qc_claim_update_needed(block_state_legacy&, const qc_claim_t&) { return false; } + static bool qc_claim_update_needed(block_state_legacy&, const block_state_legacy&) { return false; } + static void update_best_qc(block_state_legacy&, const qc_claim_t&) { } + static void update_best_qc(block_state_legacy&, const block_state_legacy&) { } // thread safe - static uint32_t block_height(const block_state_legacy& bs) { - return bs.block_num(); - } + static uint32_t block_height(const block_state_legacy& bs) { return bs.block_num(); } }; struct by_block_id; struct by_best_branch; struct by_prev; + template + void log_bs(const char* desc, fork_database_impl& fork_db, const BS& lhs) { + using BSA = BS::fork_db_block_state_accessor; + dlog( "fork_db ${f}, ${d} ${bn}, last_final_block_num ${lfbn}, final_on_strong_qc_block_num ${fsbn}, lastest_qc_claim_block_num ${lbn}, block_height ${bh}, id ${id}", + ("f", (uint64_t)(&fork_db))("d", desc)("bn", lhs.block_num())("lfbn", BSA::last_final_block_num(lhs))("fsbn", BSA::final_on_strong_qc_block_num(lhs))("lbn", BSA::lastest_qc_claim_block_num(lhs))("bh", BSA::block_height(lhs))("id", lhs.id()) ); + } + // match comparison of by_best_branch template bool first_preferred( const BS& lhs, const BS& rhs ) { using BSA = BS::fork_db_block_state_accessor; - return std::make_tuple(BSA::last_final_block_num(lhs), BSA::final_on_strong_qc_block_num(lhs), BSA::last_qc_block_num(lhs), BSA::block_height(lhs)) > - std::make_tuple(BSA::last_final_block_num(rhs), BSA::final_on_strong_qc_block_num(rhs), BSA::last_qc_block_num(rhs), BSA::block_height(rhs)); + return std::make_tuple(BSA::last_final_block_num(lhs), BSA::lastest_qc_claim_block_num(lhs), BSA::block_height(lhs)) > + std::make_tuple(BSA::last_final_block_num(rhs), BSA::lastest_qc_claim_block_num(rhs), BSA::block_height(rhs)); } template // either [block_state_legacy_ptr, block_state_ptr], same with block_header_state_ptr @@ -105,15 +113,13 @@ namespace eosio::chain { ordered_unique, composite_key, - // see first_preferred comment global_fun, - global_fun, - global_fun, + global_fun, global_fun, const_mem_fun >, composite_key_compare, - std::greater, std::greater, std::greater, std::greater, + std::greater, std::greater, std::greater, sha256_less > > @@ -130,7 +136,7 @@ namespace eosio::chain { void open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ); void close_impl( const std::filesystem::path& fork_db_file ); - void add_impl( const BSP& n, bool ignore_duplicate, bool validate, validator_t& validator ); + void add_impl( const BSP& n, bool mark_valid, bool ignore_duplicate, bool validate, validator_t& validator ); BHSP get_block_header_impl( const block_id_type& id ) const; BSP get_block_impl( const block_id_type& id ) const; @@ -143,7 +149,7 @@ namespace eosio::chain { full_branch_type fetch_full_branch_impl(const block_id_type& h) const; BSP search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; void mark_valid_impl( const BSP& h ); - void update_best_qc_strong_impl( const block_id_type& id ); + void update_best_qc_impl( const block_id_type& id, const qc_claim_t& most_recent_ancestor_with_qc ); branch_type_pair fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; }; @@ -200,7 +206,7 @@ namespace eosio::chain { fc::raw::unpack( ds, s ); // do not populate transaction_metadatas, they will be created as needed in apply_block with appropriate key recovery s.header_exts = s.block->validate_and_extract_header_extensions(); - add_impl( std::make_shared( std::move( s ) ), false, true, validator ); + add_impl( std::make_shared( std::move( s ) ), false, false, true, validator ); } block_id_type head_id; fc::raw::unpack( ds, head_id ); @@ -393,12 +399,12 @@ namespace eosio::chain { } template - void fork_database_impl::add_impl(const BSP& n, bool ignore_duplicate, bool validate, validator_t& validator) { + void fork_database_impl::add_impl(const BSP& n, bool mark_valid, bool ignore_duplicate, bool validate, validator_t& validator) { EOS_ASSERT( root, fork_database_exception, "root not yet set" ); EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" ); - auto itr = index.find( n->previous() ); - EOS_ASSERT( itr != index.end() || root->id() == n->previous(), unlinkable_block_exception, + auto prev_bh = get_block_header_impl( n->previous() ); + EOS_ASSERT( prev_bh, unlinkable_block_exception, "unlinkable block", ("id", n->id())("previous", n->previous()) ); if (validate) { @@ -406,8 +412,7 @@ namespace eosio::chain { const auto& exts = n->header_exts; if (auto i = exts.lower_bound(protocol_feature_activation::extension_id()); i != exts.end() ) { - const auto& prev_protocol_features = itr != index.end() ? (*itr)->get_activated_protocol_features()->protocol_features - : root->get_activated_protocol_features()->protocol_features; + const auto& prev_protocol_features = prev_bh->get_activated_protocol_features()->protocol_features; const auto& pfa = i->second; const auto& new_protocol_features = std::get(pfa).protocol_features; validator(n->timestamp(), prev_protocol_features, new_protocol_features); @@ -417,12 +422,26 @@ namespace eosio::chain { "serialized fork database is incompatible with configured protocol features") } + if (mark_valid) + BSAccessor::set_valid(*n, true); + auto inserted = index.insert(n); if( !inserted.second ) { if( ignore_duplicate ) return; EOS_THROW( fork_database_exception, "duplicate block added", ("id", n->id()) ); } + // ancestor might have been updated since block_state was created + if (auto prev = index.find(n->previous()); prev != index.end()) { + if (BSAccessor::qc_claim_update_needed(*n, **prev)) { + auto& by_id_idx = index.template get(); + auto itr = by_id_idx.find(n->id()); + by_id_idx.modify( itr, [&]( auto& i ) { + BSAccessor::update_best_qc(*i, **prev); + } ); + } + } + auto candidate = index.template get().begin(); if( BSAccessor::is_valid(**candidate) ) { head = *candidate; @@ -430,9 +449,9 @@ namespace eosio::chain { } template - void fork_database_t::add( const BSP& n, bool ignore_duplicate ) { + void fork_database_t::add( const BSP& n, bool mark_valid, bool ignore_duplicate ) { std::lock_guard g( my->mtx ); - my->add_impl( n, ignore_duplicate, false, + my->add_impl( n, mark_valid, ignore_duplicate, false, []( block_timestamp_type timestamp, const flat_set& cur_features, const vector& new_features ) @@ -673,22 +692,45 @@ namespace eosio::chain { } template - void fork_database_t::update_best_qc_strong( const block_id_type& id ) { + void fork_database_t::update_best_qc( const block_id_type& id, const qc_claim_t& most_recent_ancestor_with_qc ) { std::lock_guard g( my->mtx ); - my->update_best_qc_strong_impl( id ); + my->update_best_qc_impl( id, most_recent_ancestor_with_qc ); } template - void fork_database_impl::update_best_qc_strong_impl( const block_id_type& id ) { + void fork_database_impl::update_best_qc_impl( const block_id_type& id, const qc_claim_t& most_recent_ancestor_with_qc ) { auto& by_id_idx = index.template get(); auto itr = by_id_idx.find( id ); EOS_ASSERT( itr != by_id_idx.end(), fork_database_exception, "block state not in fork database; cannot update", ("id", id) ); - by_id_idx.modify( itr, []( auto& i ) { - BSAccessor::update_best_qc_strong(*i, i->block_num()); - } ); + if (BSAccessor::qc_claim_update_needed(**itr, most_recent_ancestor_with_qc)) { + by_id_idx.modify( itr, [&]( auto& i ) { + BSAccessor::update_best_qc(*i, most_recent_ancestor_with_qc); + } ); + } + + // process descendants + vector descendants; + descendants.reserve(index.size()); + descendants.push_back(id); + auto& previdx = index.template get(); + for( uint32_t i = 0; i < descendants.size(); ++i ) { + auto previtr = previdx.lower_bound( descendants[i] ); + while( previtr != previdx.end() && (*previtr)->previous() == descendants[i] ) { + if (BSAccessor::qc_claim_update_needed(**previtr, most_recent_ancestor_with_qc)) { + previdx.modify( previtr, [&](auto& i) { + BSAccessor::update_best_qc(*i, most_recent_ancestor_with_qc); + }); + } else { + break; + } + + descendants.emplace_back( (*previtr)->id() ); + ++previtr; + } + } auto candidate = index.template get().begin(); if( first_preferred( **candidate, *head ) ) { diff --git a/libraries/chain/hotstuff/hotstuff.cpp b/libraries/chain/hotstuff/hotstuff.cpp index d01cdc55b8..0a57d16234 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -129,13 +129,14 @@ vote_status pending_quorum_certificate::add_weak_vote(std::span p // thread safe, std::pair -pending_quorum_certificate::add_vote(bool strong, std::span proposal_digest, size_t index, +pending_quorum_certificate::add_vote(block_num_type block_num, bool strong, std::span proposal_digest, size_t index, const bls_public_key& pubkey, const bls_signature& sig, uint64_t weight) { std::lock_guard g(*_mtx); vote_status s = strong ? add_strong_vote(proposal_digest, index, pubkey, sig, weight) : add_weak_vote(proposal_digest, index, pubkey, sig, weight); - dlog("status: ${s}, state: ${state}, quorum_met: ${q}", - ("s", s ==vote_status::success ? "success":"failure")("state", _state==state_t::strong ? "strong":"weak")("q", is_quorum_met_no_lock() ? "yes":"no")); + dlog("block_num: ${bn}, status: ${s}, state: ${state}, quorum_met: ${q}", + ("bn", block_num)("s", s ==vote_status::success ? "success":"failure")("state", _state==state_t::strong ? "strong":"weak") + ("q", is_quorum_met_no_lock() ? "yes":"no")); return {s, _state}; } @@ -159,7 +160,7 @@ valid_quorum_certificate pending_quorum_certificate::to_valid_quorum_certificate } bool pending_quorum_certificate::is_quorum_met_no_lock() const { - return _state == state_t::weak_achieved || _state == state_t::weak_final || _state == state_t::strong; + return is_quorum_met(_state); } valid_quorum_certificate::valid_quorum_certificate( diff --git a/libraries/chain/hotstuff/test/finality_misc_tests.cpp b/libraries/chain/hotstuff/test/finality_misc_tests.cpp index 418d82116c..93b89e1467 100644 --- a/libraries/chain/hotstuff/test/finality_misc_tests.cpp +++ b/libraries/chain/hotstuff/test/finality_misc_tests.cpp @@ -51,11 +51,11 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { pubkey.push_back(k.get_public_key()); auto weak_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index, uint64_t weight) { - return qc.add_vote(false, digest, index, pubkey[index], sk[index].sign(digest), weight).first; + return qc.add_vote(0, false, digest, index, pubkey[index], sk[index].sign(digest), weight).first; }; auto strong_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index, uint64_t weight) { - return qc.add_vote(true, digest, index, pubkey[index], sk[index].sign(digest), weight).first; + return qc.add_vote(0, true, digest, index, pubkey[index], sk[index].sign(digest), weight).first; }; constexpr uint64_t weight = 1; diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 94d4fa7640..e74f5f3030 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -32,16 +32,16 @@ struct block_state : public block_header_state { // block_header_state provi // ------ updated for votes, used for fork_db ordering ------------------------------ private: - bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches. - block_header_state_core current_core; // only modify/access while holding forkdb lock + bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches. + qc_claim_t most_recent_ancestor_with_qc; // only modify/access while holding forkdb lock + core_metadata updated_core; // only modify/access while holding forkdb lock // ------ data members caching information available elsewhere ---------------------- bool pub_keys_recovered = false; deque cached_trxs; // ------ private methods ----------------------------------------------------------- - bool is_valid() const { return validated; } - void set_valid(bool b) { validated = b; } + bool is_valid() const { return validated; } bool is_pub_keys_recovered() const { return pub_keys_recovered; } deque extract_trxs_metas(); void set_trxs_metas(deque&& trxs_metas, bool keys_recovered); @@ -65,7 +65,8 @@ struct block_state : public block_header_state { // block_header_state provi protocol_feature_activation_set_ptr get_activated_protocol_features() const { return block_header_state::activated_protocol_features; } - std::tuple> aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc + // vote_status, pending_qc state + std::tuple aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc void verify_qc(const valid_quorum_certificate& qc) const; // verify given qc is valid with respect block_state using bhs_t = block_header_state; @@ -73,6 +74,8 @@ struct block_state : public block_header_state { // block_header_state provi using fork_db_block_state_accessor = block_state_accessor; block_state() = default; + block_state(const block_state&) = delete; + block_state(block_state&&) = default; block_state(const block_header_state& prev, signed_block_ptr b, const protocol_feature_set& pfs, const validator_t& validator, bool skip_validate_signee); @@ -92,4 +95,4 @@ using block_state_ptr = std::shared_ptr; } // namespace eosio::chain // not exporting pending_qc or valid_qc -FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(strong_digest)(weak_digest)(pending_qc)(valid_qc)(validated)(current_core) ) +FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(strong_digest)(weak_digest)(pending_qc)(valid_qc)(validated)(most_recent_ancestor_with_qc)(updated_core) ) diff --git a/libraries/chain/include/eosio/chain/finality_core.hpp b/libraries/chain/include/eosio/chain/finality_core.hpp index 10d3268bdd..81e142c399 100644 --- a/libraries/chain/include/eosio/chain/finality_core.hpp +++ b/libraries/chain/include/eosio/chain/finality_core.hpp @@ -33,9 +33,9 @@ struct qc_claim_t struct core_metadata { - block_num_type last_final_block_num; - block_num_type final_on_strong_qc_block_num; - block_num_type latest_qc_claim_block_num; + block_num_type last_final_block_num {0}; + block_num_type final_on_strong_qc_block_num {0}; + block_num_type latest_qc_claim_block_num {0}; }; struct finality_core @@ -136,4 +136,5 @@ struct finality_core FC_REFLECT( eosio::chain::block_ref, (block_id)(timestamp) ) FC_REFLECT( eosio::chain::qc_link, (source_block_num)(target_block_num)(is_link_strong) ) FC_REFLECT( eosio::chain::qc_claim_t, (block_num)(is_strong_qc) ) +FC_REFLECT( eosio::chain::core_metadata, (last_final_block_num)(final_on_strong_qc_block_num)(latest_qc_claim_block_num)) FC_REFLECT( eosio::chain::finality_core, (links)(refs)(final_on_strong_qc_block_num)) diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index f65a99d7a0..5ef1fe7c4e 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -64,8 +64,9 @@ namespace eosio::chain { /** * Add block state to fork database. * Must link to existing block in fork database or the root. + * @param mark_valid if true also mark next_block valid */ - void add( const BSP& next_block, bool ignore_duplicate = false ); + void add( const BSP& next_block, bool mark_valid, bool ignore_duplicate ); void remove( const block_id_type& id ); @@ -110,9 +111,9 @@ namespace eosio::chain { void mark_valid( const BSP& h ); /** - * Update block_state_core for best qc strong + * Update finality_core for best qc */ - void update_best_qc_strong( const block_id_type& id ); + void update_best_qc( const block_id_type& id, const qc_claim_t& most_recent_ancestor_with_qc ); private: unique_ptr> my; diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index 56c6c6082a..55b813c605 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -93,9 +93,13 @@ namespace eosio::chain { // thread safe bool is_quorum_met() const; + static bool is_quorum_met(state_t s) { + return s == state_t::weak_achieved || s == state_t::weak_final || s == state_t::strong; + } // thread safe - std::pair add_vote(bool strong, + std::pair add_vote(block_num_type block_num, + bool strong, std::span proposal_digest, size_t index, const bls_public_key& pubkey, From 9e0a2260ca7e9d8d236caf9a30de6ef9de92a5ef Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 26 Feb 2024 17:33:04 -0600 Subject: [PATCH 08/40] GH-2125 Revert unneeded changes --- libraries/chain/fork_database.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 8851a790e3..2f98ea195b 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -411,15 +411,12 @@ namespace eosio::chain { try { const auto& exts = n->header_exts; - if (auto i = exts.lower_bound(protocol_feature_activation::extension_id()); i != exts.end() ) { - const auto& prev_protocol_features = prev_bh->get_activated_protocol_features()->protocol_features; - const auto& pfa = i->second; - const auto& new_protocol_features = std::get(pfa).protocol_features; - validator(n->timestamp(), prev_protocol_features, new_protocol_features); + if( exts.count(protocol_feature_activation::extension_id()) > 0 ) { + const auto& new_protocol_features = std::get(exts.lower_bound(protocol_feature_activation::extension_id())->second).protocol_features; + validator( n->header.timestamp, prev_bh->activated_protocol_features->protocol_features, new_protocol_features ); } } - EOS_RETHROW_EXCEPTIONS(fork_database_exception, - "serialized fork database is incompatible with configured protocol features") + EOS_RETHROW_EXCEPTIONS( fork_database_exception, "serialized fork database is incompatible with configured protocol features" ) } if (mark_valid) From 17274e25421a03c42c68a57c726f6f42e13aeef8 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Mon, 26 Feb 2024 18:56:01 -0600 Subject: [PATCH 09/40] GH-2125 Potentially switch forks before beginning to produce a block --- libraries/chain/controller.cpp | 22 ++++++++++++++++++- .../chain/include/eosio/chain/controller.hpp | 1 + plugins/producer_plugin/producer_plugin.cpp | 4 ++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index c32c249e7f..1ccfbfb135 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2782,7 +2782,6 @@ struct controller_impl { if( s == controller::block_status::incomplete ) { forkdb.add( bsp, true, false ); emit( accepted_block_header, std::tie(bsp->block, bsp->id()) ); - EOS_ASSERT( bsp == forkdb.head(), fork_database_exception, "committed block did not become the new head in fork database"); } else if (s != controller::block_status::irreversible) { forkdb.mark_valid( bsp ); } @@ -3512,6 +3511,21 @@ struct controller_impl { } FC_LOG_AND_RETHROW( ) } + void maybe_switch_forks(const forked_callback_t& cb, const trx_meta_cache_lookup& trx_lookup) { + auto maybe_switch = [&](auto& forkdb) { + if (read_mode != db_read_mode::IRREVERSIBLE) { + auto fork_head = forkdb.head(); + if (forkdb.chain_head->id() != fork_head->id()) { + controller::block_report br; + maybe_switch_forks(br, fork_head, fork_head->is_valid() ? controller::block_status::validated : controller::block_status::complete, + cb, trx_lookup); + } + } + }; + + fork_db.apply(maybe_switch); + } + template void maybe_switch_forks( controller::block_report& br, const BSP& new_head, controller::block_status s, const forked_callback_t& forked_cb, const trx_meta_cache_lookup& trx_lookup ) @@ -4277,6 +4291,12 @@ void controller::commit_block() { my->commit_block(block_status::incomplete); } +void controller::maybe_switch_forks(const forked_callback_t& cb, const trx_meta_cache_lookup& trx_lookup) { + validate_db_available_size(); + my->maybe_switch_forks(cb, trx_lookup); +} + + deque controller::abort_block() { return my->abort_block(); } diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 9a5dd18634..15467bea5f 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -189,6 +189,7 @@ namespace eosio::chain { void assemble_and_complete_block( block_report& br, const signer_callback_type& signer_callback ); void sign_block( const signer_callback_type& signer_callback ); void commit_block(); + void maybe_switch_forks(const forked_callback_t& cb, const trx_meta_cache_lookup& trx_lookup); // thread-safe std::future create_block_handle_future( const block_id_type& id, const signed_block_ptr& b ); diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index ebd3a6d27f..bc450e7157 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1779,6 +1779,10 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { if (!chain_plug->accept_transactions()) return start_block_result::waiting_for_block; + chain.maybe_switch_forks([this](const transaction_metadata_ptr& trx) { _unapplied_transactions.add_forked(trx); }, + [this](const transaction_id_type& id) { return _unapplied_transactions.get_trx(id); }); + + uint32_t head_block_num = chain.head_block_num(); if (chain.get_terminate_at_block() > 0 && chain.get_terminate_at_block() <= head_block_num) { From 842ed6c8a1bb2ad9d820f5b9c44cc7ce50741525 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 07:25:30 -0600 Subject: [PATCH 10/40] GH-2125 Can't have pending block when applying a block --- plugins/producer_plugin/producer_plugin.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/producer_plugin/producer_plugin.cpp b/plugins/producer_plugin/producer_plugin.cpp index bc450e7157..f798c584ea 100644 --- a/plugins/producer_plugin/producer_plugin.cpp +++ b/plugins/producer_plugin/producer_plugin.cpp @@ -1779,10 +1779,11 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { if (!chain_plug->accept_transactions()) return start_block_result::waiting_for_block; + abort_block(); + chain.maybe_switch_forks([this](const transaction_metadata_ptr& trx) { _unapplied_transactions.add_forked(trx); }, [this](const transaction_id_type& id) { return _unapplied_transactions.get_trx(id); }); - uint32_t head_block_num = chain.head_block_num(); if (chain.get_terminate_at_block() > 0 && chain.get_terminate_at_block() <= head_block_num) { @@ -1907,8 +1908,6 @@ producer_plugin_impl::start_block_result producer_plugin_impl::start_block() { blocks_to_confirm = (uint16_t)(std::min(blocks_to_confirm, (head_block_num - block_state->dpos_irreversible_blocknum))); } - abort_block(); - auto features_to_activate = chain.get_preactivated_protocol_features(); if (in_producing_mode() && _protocol_features_to_activate.size() > 0) { bool drop_features_to_activate = false; From 37a7a1705ae4983794b8b32dc24b65c09503db9a Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 08:46:33 -0600 Subject: [PATCH 11/40] GH-2125 Use vector instead of deque --- libraries/chain/fork_database.cpp | 1 + libraries/chain/include/eosio/chain/fork_database.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 2f98ea195b..afc65a8139 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -518,6 +518,7 @@ namespace eosio::chain { block_branch_t fork_database_impl::fetch_block_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { block_branch_t result; + result.reserve(index.size()); for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { if ((*i)->block_num() <= trim_after_block_num) result.push_back((*i)->block); diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 5ef1fe7c4e..8e8800aba0 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -7,7 +7,7 @@ namespace eosio::chain { template struct fork_database_impl; - using block_branch_t = deque; + using block_branch_t = std::vector; /** * @class fork_database_t From 042ffdece95c893cc2ef985ae97fc8ea962622ab Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 09:52:13 -0600 Subject: [PATCH 12/40] GH-2125 Return current state even on error --- libraries/chain/block_state.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index da3ce25310..ca697a1627 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -108,7 +108,7 @@ block_state::aggregate_vote(const vote_message& vote) { finalizers[index].weight); } else { wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) ); - return {vote_status::unknown_public_key, pending_quorum_certificate::state_t::unrestricted}; + return {vote_status::unknown_public_key, pending_qc.state()}; } } From 1eda923ac5e5d960f0eb921ee811e3cb4c5b2405 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 10:10:59 -0600 Subject: [PATCH 13/40] GH-2125 Rename method to make more clear --- 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 1ccfbfb135..a575c2a85b 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1004,7 +1004,7 @@ struct controller_impl { return prev->block_num(); } - void fork_db_reset_root_to_head() { + void fork_db_reset_root_to_chain_head() { return fork_db.apply([&](auto& forkdb) { forkdb.reset_root(*forkdb.chain_head); }); @@ -1300,7 +1300,7 @@ struct controller_impl { void replay(std::function check_shutdown) { auto blog_head = blog.head(); if( !fork_db_has_root() ) { - fork_db_reset_root_to_head(); + fork_db_reset_root_to_chain_head(); if (!blog_head) return; } From 2767821022edaa3820663c544c59ce2dc373799a Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 10:21:22 -0600 Subject: [PATCH 14/40] GH-2125 Use descriptive types --- libraries/chain/controller.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index a575c2a85b..453ddea338 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2780,7 +2780,9 @@ struct controller_impl { const auto& bsp = std::get>(cb.bsp); if( s == controller::block_status::incomplete ) { - forkdb.add( bsp, true, false ); + const bool mark_valid = true; + const bool ignore_duplicate = false; + forkdb.add( bsp, mark_valid, ignore_duplicate ); emit( accepted_block_header, std::tie(bsp->block, bsp->id()) ); } else if (s != controller::block_status::irreversible) { forkdb.mark_valid( bsp ); @@ -3431,8 +3433,11 @@ struct controller_impl { } auto do_push = [&](auto& forkdb) { - if constexpr (std::is_same_v>) - forkdb.add( bsp, false, false ); + if constexpr (std::is_same_v>) { + const bool mark_valid = false; + const bool ignore_duplicate = false; + forkdb.add( bsp, mark_valid, ignore_duplicate ); + } if (is_trusted_producer(b->producer)) { trusted_producer_light_validation = true; @@ -3482,7 +3487,9 @@ struct controller_impl { *forkdb.chain_head, b, protocol_features.get_protocol_feature_set(), validator, skip_validate_signee); if (s != controller::block_status::irreversible) { - forkdb.add(bsp, false, true); + const bool mark_valid = false; + const bool ignore_duplicate = true; + forkdb.add(bsp, mark_valid, ignore_duplicate); } emit(accepted_block_header, std::tie(bsp->block, bsp->id())); From bc5d464ff8fad1bb6cc7b062ddd157bb6a413a8d Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 11:26:53 -0600 Subject: [PATCH 15/40] GH-2125 Change order of eval --- libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index 55b813c605..19d7779b4f 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -94,7 +94,7 @@ namespace eosio::chain { // thread safe bool is_quorum_met() const; static bool is_quorum_met(state_t s) { - return s == state_t::weak_achieved || s == state_t::weak_final || s == state_t::strong; + return s == state_t::strong || s == state_t::weak_achieved || s == state_t::weak_final; } // thread safe From c3847eb9e9c7c3d157ae34a1f106a3555dfe9cf2 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 13:05:19 -0600 Subject: [PATCH 16/40] GH-2125 Improve logging --- libraries/chain/hotstuff/finalizer.cpp | 8 +++----- libraries/chain/hotstuff/hotstuff.cpp | 7 ++----- .../chain/include/eosio/chain/hotstuff/finalizer.hpp | 3 ++- libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp | 2 ++ plugins/net_plugin/net_plugin.cpp | 4 ++++ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/libraries/chain/hotstuff/finalizer.cpp b/libraries/chain/hotstuff/finalizer.cpp index 8145d9d1db..e0b80e7258 100644 --- a/libraries/chain/hotstuff/finalizer.cpp +++ b/libraries/chain/hotstuff/finalizer.cpp @@ -56,9 +56,6 @@ finalizer::vote_decision finalizer::decide_vote(const block_state_ptr& proposal, safety_check = false; } - dlog("liveness_check=${l}, safety_check=${s}, monotony_check=${m}, can vote = {can_vote}", - ("l",liveness_check)("s",safety_check)("m",monotony_check)("can_vote",(liveness_check || safety_check))); - // Figure out if we can vote and wether our vote will be strong or weak // If we vote, update `fsi.last_vote` and also `fsi.lock` if we have a newer commit qc // ----------------------------------------------------------------------------------- @@ -90,8 +87,9 @@ finalizer::vote_decision finalizer::decide_vote(const block_state_ptr& proposal, ("lqc",!!proposal->last_qc_block_num())("f",fork_db.root()->block_num())); dlog("last_qc_block_num=${lqc}", ("lqc", proposal->last_qc_block_num())); } - if (decision != vote_decision::no_vote) - dlog("Voting ${s}", ("s", decision == vote_decision::strong_vote ? "strong" : "weak")); + + dlog("liveness_check=${l}, safety_check=${s}, monotony_check=${m}, can vote=${can_vote}, voting=${v}", + ("l",liveness_check)("s",safety_check)("m",monotony_check)("can_vote",(liveness_check || safety_check))("v", decision)); return decision; } diff --git a/libraries/chain/hotstuff/hotstuff.cpp b/libraries/chain/hotstuff/hotstuff.cpp index 0a57d16234..be19be3c10 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -23,7 +23,6 @@ inline std::vector bitset_to_vector(const hs_bitset& bs) { vote_status pending_quorum_certificate::votes_t::add_vote(std::span proposal_digest, size_t index, const bls_public_key& pubkey, const bls_signature& new_sig) { if (_bitset[index]) { - dlog("duplicated vote"); return vote_status::duplicate; // shouldn't be already present } if (!fc::crypto::blslib::verify(pubkey, proposal_digest, new_sig)) { @@ -64,7 +63,6 @@ vote_status pending_quorum_certificate::add_strong_vote(std::span const bls_public_key& pubkey, const bls_signature& sig, uint64_t weight) { if (auto s = _strong_votes.add_vote(proposal_digest, index, pubkey, sig); s != vote_status::success) { - dlog("add_strong_vote returned failure"); return s; } _strong_sum += weight; @@ -134,9 +132,8 @@ pending_quorum_certificate::add_vote(block_num_type block_num, bool strong, std: std::lock_guard g(*_mtx); vote_status s = strong ? add_strong_vote(proposal_digest, index, pubkey, sig, weight) : add_weak_vote(proposal_digest, index, pubkey, sig, weight); - dlog("block_num: ${bn}, status: ${s}, state: ${state}, quorum_met: ${q}", - ("bn", block_num)("s", s ==vote_status::success ? "success":"failure")("state", _state==state_t::strong ? "strong":"weak") - ("q", is_quorum_met_no_lock() ? "yes":"no")); + dlog("block_num: ${bn}, vote strong: ${sv}, status: ${s}, state: ${state}, quorum_met: ${q}", + ("bn", block_num)("sv", strong)("s", s)("state", _state)("q", is_quorum_met_no_lock())); return {s, _state}; } diff --git a/libraries/chain/include/eosio/chain/hotstuff/finalizer.hpp b/libraries/chain/include/eosio/chain/hotstuff/finalizer.hpp index 01fee6e411..f730e004eb 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/finalizer.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/finalizer.hpp @@ -160,4 +160,5 @@ namespace std { } FC_REFLECT(eosio::chain::finalizer::proposal_ref, (id)(timestamp)) -FC_REFLECT(eosio::chain::finalizer::safety_information, (last_vote_range_start)(last_vote)(lock)) \ No newline at end of file +FC_REFLECT_ENUM(eosio::chain::finalizer::vote_decision, (strong_vote)(weak_vote)(no_vote)) +FC_REFLECT(eosio::chain::finalizer::safety_information, (last_vote_range_start)(last_vote)(lock)) diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index 19d7779b4f..a2b53ed9b8 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -141,7 +141,9 @@ namespace eosio::chain { FC_REFLECT(eosio::chain::vote_message, (proposal_id)(strong)(finalizer_key)(sig)); +FC_REFLECT_ENUM(eosio::chain::vote_status, (success)(duplicate)(unknown_public_key)(invalid_signature)(unknown_block)) FC_REFLECT(eosio::chain::valid_quorum_certificate, (_strong_votes)(_weak_votes)(_sig)); FC_REFLECT(eosio::chain::pending_quorum_certificate, (_quorum)(_max_weak_sum_before_weak_final)(_state)(_strong_sum)(_weak_sum)(_weak_votes)(_strong_votes)); +FC_REFLECT_ENUM(eosio::chain::pending_quorum_certificate::state_t, (unrestricted)(restricted)(weak_achieved)(weak_final)(strong)); FC_REFLECT(eosio::chain::pending_quorum_certificate::votes_t, (_bitset)(_sig)); FC_REFLECT(eosio::chain::quorum_certificate, (block_num)(qc)); diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index 5e8f0681e0..500094f9a8 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -4001,6 +4001,10 @@ namespace eosio { buffer_factory buff_factory; auto send_buffer = buff_factory.get_send_buffer( msg ); + fc_dlog(logger, "bcast vote: block #${bn}:${id}.., ${t}, key ${k}..", + ("bn", block_header::num_from_id(msg.proposal_id))("id", msg.proposal_id.str().substr(8,16)) + ("t", msg.strong ? "strong" : "weak")("k", msg.finalizer_key.to_string().substr(8,16))); + dispatcher->strand.post( [this, exclude_peer, msg{std::move(send_buffer)}]() mutable { dispatcher->bcast_vote_msg( exclude_peer, std::move(msg) ); }); From 6a45921037b59656364ac5f9e14028ffa1db1078 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 13:26:28 -0600 Subject: [PATCH 17/40] GH-2125 Use descriptive enums instead of bools --- libraries/chain/controller.cpp | 12 +++--------- libraries/chain/fork_database.cpp | 12 ++++++------ .../chain/include/eosio/chain/fork_database.hpp | 4 +++- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 453ddea338..88b905008a 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2780,9 +2780,7 @@ struct controller_impl { const auto& bsp = std::get>(cb.bsp); if( s == controller::block_status::incomplete ) { - const bool mark_valid = true; - const bool ignore_duplicate = false; - forkdb.add( bsp, mark_valid, ignore_duplicate ); + forkdb.add( bsp, mark_valid_t::yes, ignore_duplicate_t::no ); emit( accepted_block_header, std::tie(bsp->block, bsp->id()) ); } else if (s != controller::block_status::irreversible) { forkdb.mark_valid( bsp ); @@ -3434,9 +3432,7 @@ struct controller_impl { auto do_push = [&](auto& forkdb) { if constexpr (std::is_same_v>) { - const bool mark_valid = false; - const bool ignore_duplicate = false; - forkdb.add( bsp, mark_valid, ignore_duplicate ); + forkdb.add( bsp, mark_valid_t::no, ignore_duplicate_t::no ); } if (is_trusted_producer(b->producer)) { @@ -3487,9 +3483,7 @@ struct controller_impl { *forkdb.chain_head, b, protocol_features.get_protocol_feature_set(), validator, skip_validate_signee); if (s != controller::block_status::irreversible) { - const bool mark_valid = false; - const bool ignore_duplicate = true; - forkdb.add(bsp, mark_valid, ignore_duplicate); + forkdb.add(bsp, mark_valid_t::no, ignore_duplicate_t::yes); } emit(accepted_block_header, std::tie(bsp->block, bsp->id())); diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index afc65a8139..a33b8c7cb6 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -136,7 +136,7 @@ namespace eosio::chain { void open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ); void close_impl( const std::filesystem::path& fork_db_file ); - void add_impl( const BSP& n, bool mark_valid, bool ignore_duplicate, bool validate, validator_t& validator ); + void add_impl( const BSP& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate, bool validate, validator_t& validator ); BHSP get_block_header_impl( const block_id_type& id ) const; BSP get_block_impl( const block_id_type& id ) const; @@ -206,7 +206,7 @@ namespace eosio::chain { fc::raw::unpack( ds, s ); // do not populate transaction_metadatas, they will be created as needed in apply_block with appropriate key recovery s.header_exts = s.block->validate_and_extract_header_extensions(); - add_impl( std::make_shared( std::move( s ) ), false, false, true, validator ); + add_impl( std::make_shared( std::move( s ) ), mark_valid_t::no, ignore_duplicate_t::no, true, validator ); } block_id_type head_id; fc::raw::unpack( ds, head_id ); @@ -399,7 +399,7 @@ namespace eosio::chain { } template - void fork_database_impl::add_impl(const BSP& n, bool mark_valid, bool ignore_duplicate, bool validate, validator_t& validator) { + void fork_database_impl::add_impl(const BSP& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate, bool validate, validator_t& validator) { EOS_ASSERT( root, fork_database_exception, "root not yet set" ); EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" ); @@ -419,12 +419,12 @@ namespace eosio::chain { EOS_RETHROW_EXCEPTIONS( fork_database_exception, "serialized fork database is incompatible with configured protocol features" ) } - if (mark_valid) + if (mark_valid == mark_valid_t::yes) BSAccessor::set_valid(*n, true); auto inserted = index.insert(n); if( !inserted.second ) { - if( ignore_duplicate ) return; + if( ignore_duplicate == ignore_duplicate_t::yes ) return; EOS_THROW( fork_database_exception, "duplicate block added", ("id", n->id()) ); } @@ -446,7 +446,7 @@ namespace eosio::chain { } template - void fork_database_t::add( const BSP& n, bool mark_valid, bool ignore_duplicate ) { + void fork_database_t::add( const BSP& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate ) { std::lock_guard g( my->mtx ); my->add_impl( n, mark_valid, ignore_duplicate, false, []( block_timestamp_type timestamp, diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 8e8800aba0..63f25f9ef9 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -8,6 +8,8 @@ namespace eosio::chain { struct fork_database_impl; using block_branch_t = std::vector; + enum class mark_valid_t { no, yes }; + enum class ignore_duplicate_t { no, yes }; /** * @class fork_database_t @@ -66,7 +68,7 @@ namespace eosio::chain { * Must link to existing block in fork database or the root. * @param mark_valid if true also mark next_block valid */ - void add( const BSP& next_block, bool mark_valid, bool ignore_duplicate ); + void add( const BSP& next_block, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate ); void remove( const block_id_type& id ); From 69373f4a3b045dfe9132bbc2177e2ddd624c0a25 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 15:40:14 -0600 Subject: [PATCH 18/40] GH-2125 Revert unneeded change --- libraries/chain/fork_database.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index a33b8c7cb6..61431f5387 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -411,9 +411,10 @@ namespace eosio::chain { try { const auto& exts = n->header_exts; - if( exts.count(protocol_feature_activation::extension_id()) > 0 ) { - const auto& new_protocol_features = std::get(exts.lower_bound(protocol_feature_activation::extension_id())->second).protocol_features; - validator( n->header.timestamp, prev_bh->activated_protocol_features->protocol_features, new_protocol_features ); + if (exts.count(protocol_feature_activation::extension_id()) > 0) { + const auto& pfa = exts.lower_bound(protocol_feature_activation::extension_id())->second; + const auto& new_protocol_features = std::get(pfa).protocol_features; + validator(n->timestamp(), prev_bh->get_activated_protocol_features()->protocol_features, new_protocol_features); } } EOS_RETHROW_EXCEPTIONS( fork_database_exception, "serialized fork database is incompatible with configured protocol features" ) From f9dd4430cd03560b8215e5d49e6d827ae75ff8a2 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 15:52:48 -0600 Subject: [PATCH 19/40] GH-2125 Remove duplicate --- libraries/chain/include/eosio/chain/block_state.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index e74f5f3030..446f9ebc67 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -63,8 +63,6 @@ struct block_state : public block_header_state { // block_header_state provi uint32_t last_qc_block_num() const { return core.latest_qc_claim().block_num; } uint32_t final_on_strong_qc_block_num() const { return core.final_on_strong_qc_block_num; } - protocol_feature_activation_set_ptr get_activated_protocol_features() const { return block_header_state::activated_protocol_features; } - // vote_status, pending_qc state std::tuple aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc void verify_qc(const valid_quorum_certificate& qc) const; // verify given qc is valid with respect block_state From 772d74eae0af5e9a2076f89505b082144c934adc Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 16:38:18 -0600 Subject: [PATCH 20/40] GH-2125 Rename most_recent_ancestor_with_qc to best_qc_claim --- libraries/chain/block_state.cpp | 12 +++---- libraries/chain/fork_database.cpp | 32 +++++++++---------- .../chain/include/eosio/chain/block_state.hpp | 8 ++--- .../include/eosio/chain/fork_database.hpp | 2 +- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index ca697a1627..e824f30e7a 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -15,8 +15,8 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con , strong_digest(compute_finalizer_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()) - , most_recent_ancestor_with_qc(core.latest_qc_claim()) - , updated_core(core.next_metadata(most_recent_ancestor_with_qc)) + , best_qc_claim(core.latest_qc_claim()) + , updated_core(core.next_metadata(best_qc_claim)) { // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here if( !skip_validate_signee ) { @@ -34,8 +34,8 @@ block_state::block_state(const block_header_state& bhs, dequefinalizers.size(), bhs.active_finalizer_policy->threshold, bhs.active_finalizer_policy->max_weak_sum_before_weak_final()) - , most_recent_ancestor_with_qc(core.latest_qc_claim()) - , updated_core(core.next_metadata(most_recent_ancestor_with_qc)) + , best_qc_claim(core.latest_qc_claim()) + , updated_core(core.next_metadata(best_qc_claim)) , pub_keys_recovered(true) // called by produce_block so signature recovery of trxs must have been done , cached_trxs(std::move(trx_metas)) { @@ -53,8 +53,8 @@ block_state::block_state(const block_state_legacy& bsp) { block_header_state::block_id = bsp.id(); header = bsp.header; core = finality_core::create_core_for_genesis_block(bsp.block_num()); // [if todo] instant transition is not acceptable - most_recent_ancestor_with_qc = core.latest_qc_claim(); - updated_core = core.next_metadata(most_recent_ancestor_with_qc); + best_qc_claim = core.latest_qc_claim(); + updated_core = core.next_metadata(best_qc_claim); activated_protocol_features = bsp.activated_protocol_features; auto if_ext_id = instant_finality_extension::extension_id(); diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 61431f5387..4c03b394b0 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -37,20 +37,20 @@ namespace eosio::chain { static uint32_t lastest_qc_claim_block_num(const block_state& bs) { return bs.updated_core.latest_qc_claim_block_num; } - static bool qc_claim_update_needed(block_state& bs, const qc_claim_t& most_recent_ancestor_with_qc) { - return bs.most_recent_ancestor_with_qc < most_recent_ancestor_with_qc; + static bool qc_claim_update_needed(block_state& bs, const qc_claim_t& best_qc_claim) { + return bs.best_qc_claim < best_qc_claim; } static bool qc_claim_update_needed(block_state& bs, const block_state& prev) { - return bs.most_recent_ancestor_with_qc < prev.most_recent_ancestor_with_qc; + return bs.best_qc_claim < prev.best_qc_claim; } - static void update_best_qc(block_state& bs, const qc_claim_t& most_recent_ancestor_with_qc) { - assert(bs.most_recent_ancestor_with_qc < most_recent_ancestor_with_qc); - bs.updated_core = bs.core.next_metadata(most_recent_ancestor_with_qc); - bs.most_recent_ancestor_with_qc = most_recent_ancestor_with_qc; + static void update_best_qc(block_state& bs, const qc_claim_t& best_qc_claim) { + assert(bs.best_qc_claim < best_qc_claim); + bs.updated_core = bs.core.next_metadata(best_qc_claim); + bs.best_qc_claim = best_qc_claim; } static void update_best_qc(block_state& bs, const block_state& prev) { - assert(bs.most_recent_ancestor_with_qc < prev.most_recent_ancestor_with_qc); - update_best_qc(bs, prev.most_recent_ancestor_with_qc); + assert(bs.best_qc_claim < prev.best_qc_claim); + update_best_qc(bs, prev.best_qc_claim); } // thread safe @@ -691,22 +691,22 @@ namespace eosio::chain { } template - void fork_database_t::update_best_qc( const block_id_type& id, const qc_claim_t& most_recent_ancestor_with_qc ) { + void fork_database_t::update_best_qc( const block_id_type& id, const qc_claim_t& best_qc_claim ) { std::lock_guard g( my->mtx ); - my->update_best_qc_impl( id, most_recent_ancestor_with_qc ); + my->update_best_qc_impl( id, best_qc_claim ); } template - void fork_database_impl::update_best_qc_impl( const block_id_type& id, const qc_claim_t& most_recent_ancestor_with_qc ) { + void fork_database_impl::update_best_qc_impl( const block_id_type& id, const qc_claim_t& best_qc_claim ) { auto& by_id_idx = index.template get(); auto itr = by_id_idx.find( id ); EOS_ASSERT( itr != by_id_idx.end(), fork_database_exception, "block state not in fork database; cannot update", ("id", id) ); - if (BSAccessor::qc_claim_update_needed(**itr, most_recent_ancestor_with_qc)) { + if (BSAccessor::qc_claim_update_needed(**itr, best_qc_claim)) { by_id_idx.modify( itr, [&]( auto& i ) { - BSAccessor::update_best_qc(*i, most_recent_ancestor_with_qc); + BSAccessor::update_best_qc(*i, best_qc_claim); } ); } @@ -718,9 +718,9 @@ namespace eosio::chain { for( uint32_t i = 0; i < descendants.size(); ++i ) { auto previtr = previdx.lower_bound( descendants[i] ); while( previtr != previdx.end() && (*previtr)->previous() == descendants[i] ) { - if (BSAccessor::qc_claim_update_needed(**previtr, most_recent_ancestor_with_qc)) { + if (BSAccessor::qc_claim_update_needed(**previtr, best_qc_claim)) { previdx.modify( previtr, [&](auto& i) { - BSAccessor::update_best_qc(*i, most_recent_ancestor_with_qc); + BSAccessor::update_best_qc(*i, best_qc_claim); }); } else { break; diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 446f9ebc67..5f811e420e 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -32,9 +32,9 @@ struct block_state : public block_header_state { // block_header_state provi // ------ updated for votes, used for fork_db ordering ------------------------------ private: - bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches. - qc_claim_t most_recent_ancestor_with_qc; // only modify/access while holding forkdb lock - core_metadata updated_core; // only modify/access while holding forkdb lock + bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches. + qc_claim_t best_qc_claim; // only modify/access while holding forkdb lock + core_metadata updated_core; // only modify/access while holding forkdb lock, updated block_header_state::core by best_qc_claim // ------ data members caching information available elsewhere ---------------------- bool pub_keys_recovered = false; @@ -93,4 +93,4 @@ using block_state_ptr = std::shared_ptr; } // namespace eosio::chain // not exporting pending_qc or valid_qc -FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(strong_digest)(weak_digest)(pending_qc)(valid_qc)(validated)(most_recent_ancestor_with_qc)(updated_core) ) +FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(strong_digest)(weak_digest)(pending_qc)(valid_qc)(validated)(best_qc_claim)(updated_core) ) diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 63f25f9ef9..8c4afb1659 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -115,7 +115,7 @@ namespace eosio::chain { /** * Update finality_core for best qc */ - void update_best_qc( const block_id_type& id, const qc_claim_t& most_recent_ancestor_with_qc ); + void update_best_qc( const block_id_type& id, const qc_claim_t& best_qc_claim ); private: unique_ptr> my; From 39b3d3f276b757ea81752b5c11bb7e4afdd26b1e Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 27 Feb 2024 17:01:56 -0600 Subject: [PATCH 21/40] GH-2125 Only update best qc on state changes --- libraries/chain/block_state.cpp | 5 +++-- libraries/chain/controller.cpp | 6 +++--- libraries/chain/hotstuff/hotstuff.cpp | 11 ++++++----- .../chain/hotstuff/test/finality_misc_tests.cpp | 4 ++-- .../chain/include/eosio/chain/block_state.hpp | 5 +++-- .../include/eosio/chain/hotstuff/hotstuff.hpp | 16 ++++++++-------- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index e824f30e7a..66c5f4ba8c 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -89,7 +89,7 @@ void block_state::set_trxs_metas( deque&& trxs_metas, } // Called from net threads -std::tuple +std::tuple block_state::aggregate_vote(const vote_message& vote) { const auto& finalizers = active_finalizer_policy->finalizers; auto it = std::find_if(finalizers.begin(), @@ -108,7 +108,8 @@ block_state::aggregate_vote(const vote_message& vote) { finalizers[index].weight); } else { wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) ); - return {vote_status::unknown_public_key, pending_qc.state()}; + auto state = pending_qc.state(); + return {vote_status::unknown_public_key, state, state}; } } diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 88b905008a..89175e1701 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3112,10 +3112,10 @@ struct controller_impl { auto aggregate_vote = [&vote](auto& forkdb) -> std::pair> { auto bsp = forkdb.get_block(vote.proposal_id); if (bsp) { - auto [status, state] = bsp->aggregate_vote(vote); + auto [status, pre_state, post_state] = bsp->aggregate_vote(vote); std::optional new_lib{}; - if (status == vote_status::success && pending_quorum_certificate::is_quorum_met(state)) { - if (state == pending_quorum_certificate::state_t::strong) { + if (status == vote_status::success && pre_state != post_state && pending_quorum_certificate::is_quorum_met(post_state)) { + if (post_state == pending_quorum_certificate::state_t::strong) { new_lib = bsp->core.final_on_strong_qc_block_num; forkdb.update_best_qc(bsp->id(), {.block_num = bsp->block_num(), .is_strong_qc = true}); } else { diff --git a/libraries/chain/hotstuff/hotstuff.cpp b/libraries/chain/hotstuff/hotstuff.cpp index be19be3c10..b429d5eb5c 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -125,16 +125,17 @@ vote_status pending_quorum_certificate::add_weak_vote(std::span p return vote_status::success; } -// thread safe, -std::pair +// thread safe, status, pre state , post state +std::tuple pending_quorum_certificate::add_vote(block_num_type block_num, bool strong, std::span proposal_digest, size_t index, const bls_public_key& pubkey, const bls_signature& sig, uint64_t weight) { std::lock_guard g(*_mtx); + auto pre_state = _state; vote_status s = strong ? add_strong_vote(proposal_digest, index, pubkey, sig, weight) : add_weak_vote(proposal_digest, index, pubkey, sig, weight); - dlog("block_num: ${bn}, vote strong: ${sv}, status: ${s}, state: ${state}, quorum_met: ${q}", - ("bn", block_num)("sv", strong)("s", s)("state", _state)("q", is_quorum_met_no_lock())); - return {s, _state}; + dlog("block_num: ${bn}, vote strong: ${sv}, status: ${s}, pre-state: ${pre}, post-state: ${state}, quorum_met: ${q}", + ("bn", block_num)("sv", strong)("s", s)("pre", pre_state)("state", _state)("q", is_quorum_met_no_lock())); + return {s, pre_state, _state}; } // thread safe diff --git a/libraries/chain/hotstuff/test/finality_misc_tests.cpp b/libraries/chain/hotstuff/test/finality_misc_tests.cpp index 2ed9d09cf5..85bfdb72a8 100644 --- a/libraries/chain/hotstuff/test/finality_misc_tests.cpp +++ b/libraries/chain/hotstuff/test/finality_misc_tests.cpp @@ -51,11 +51,11 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { pubkey.push_back(k.get_public_key()); auto weak_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index, uint64_t weight) { - return qc.add_vote(0, false, digest, index, pubkey[index], sk[index].sign(digest), weight).first; + return std::get<0>(qc.add_vote(0, false, digest, index, pubkey[index], sk[index].sign(digest), weight)); }; auto strong_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index, uint64_t weight) { - return qc.add_vote(0, true, digest, index, pubkey[index], sk[index].sign(digest), weight).first; + return std::get<0>(qc.add_vote(0, true, digest, index, pubkey[index], sk[index].sign(digest), weight)); }; constexpr uint64_t weight = 1; diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 5f811e420e..dcedbefafa 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -63,8 +63,9 @@ struct block_state : public block_header_state { // block_header_state provi uint32_t last_qc_block_num() const { return core.latest_qc_claim().block_num; } uint32_t final_on_strong_qc_block_num() const { return core.final_on_strong_qc_block_num; } - // vote_status, pending_qc state - std::tuple aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc + // vote_status, pending_qc pre state, pending_qc post state + std::tuple + aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc void verify_qc(const valid_quorum_certificate& qc) const; // verify given qc is valid with respect block_state using bhs_t = block_header_state; diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index a2b53ed9b8..b4e4b49b80 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -97,14 +97,14 @@ namespace eosio::chain { return s == state_t::strong || s == state_t::weak_achieved || s == state_t::weak_final; } - // thread safe - std::pair add_vote(block_num_type block_num, - bool strong, - std::span proposal_digest, - size_t index, - const bls_public_key& pubkey, - const bls_signature& sig, - uint64_t weight); + // thread safe, status, pre , post + std::tuple add_vote(block_num_type block_num, + bool strong, + std::span proposal_digest, + size_t index, + const bls_public_key& pubkey, + const bls_signature& sig, + uint64_t weight); state_t state() const { std::lock_guard g(*_mtx); return _state; }; valid_quorum_certificate to_valid_quorum_certificate() const; From 3ec3d79323f8236a012cfba80fb7c481e929a22a Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 28 Feb 2024 08:06:27 -0600 Subject: [PATCH 22/40] GH-2125 Fix update_best_qc logic for processing descendants --- libraries/chain/fork_database.cpp | 7 +- .../chain/include/eosio/chain/controller.hpp | 1 - .../include/eosio/chain/fork_database.hpp | 1 + unittests/fork_db_tests.cpp | 131 ++++++++++++++++++ .../unapplied_transaction_queue_tests.cpp | 1 + 5 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 unittests/fork_db_tests.cpp diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 4c03b394b0..d529c23987 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -159,6 +159,8 @@ namespace eosio::chain { :my( new fork_database_impl(magic_number) ) {} + template + fork_database_t::~fork_database_t() = default; template void fork_database_t::open( const std::filesystem::path& fork_db_file, validator_t& validator ) { @@ -722,11 +724,8 @@ namespace eosio::chain { previdx.modify( previtr, [&](auto& i) { BSAccessor::update_best_qc(*i, best_qc_claim); }); - } else { - break; + descendants.emplace_back( (*previtr)->id() ); } - - descendants.emplace_back( (*previtr)->id() ); ++previtr; } } diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 15467bea5f..937268a70d 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 8c4afb1659..fe4398dfe8 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -40,6 +40,7 @@ namespace eosio::chain { using branch_type_pair = pair; explicit fork_database_t(uint32_t magic_number = legacy_magic_number); + ~fork_database_t(); void open( const std::filesystem::path& fork_db_file, validator_t& validator ); void close( const std::filesystem::path& fork_db_file ); diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp new file mode 100644 index 0000000000..e8bfae05c6 --- /dev/null +++ b/unittests/fork_db_tests.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include + + +namespace eosio::chain { + +inline block_id_type make_block_id(block_num_type block_num) { + block_id_type id = fc::sha256::hash(std::to_string(block_num) + std::to_string(fc::time_point::now().time_since_epoch().count())); + id._hash[0] &= 0xffffffff00000000; + id._hash[0] += fc::endian_reverse_u32(block_num); // store the block num in the ID, 160 bits is plenty for the hash + return id; +} + +struct block_state_accessor { + + static auto make_genesis_block_state() { + block_state_ptr root = std::make_shared(); + block_id_type genesis_id = make_block_id(10); + root->block_id = genesis_id; + root->header.timestamp = block_timestamp_type{10}; + root->core = finality_core::create_core_for_genesis_block(10); + root->best_qc_claim = {.block_num = 10, .is_strong_qc = false}; + return root; + } + + // use block_num > 10 + static auto make_unique_block_state(block_num_type block_num, const block_state_ptr& prev) { + block_state_ptr bsp = std::make_shared(); + bsp->block_id = make_block_id(block_num); + bsp->header.timestamp.slot = prev->header.timestamp.slot + 1; + bsp->header.previous = prev->id(); + block_ref parent_block { + .block_id = prev->id(), + .timestamp = prev->timestamp() + }; + bsp->core = prev->core.next(parent_block, prev->best_qc_claim); + bsp->best_qc_claim = bsp->core.latest_qc_claim(); + bsp->updated_core = bsp->core.next_metadata(bsp->best_qc_claim); + return bsp; + } + + static auto get_best_qc_claim(const block_state_ptr& bs) { + return bs->best_qc_claim; + } +}; + +} // namespace eosio::chain + +using namespace eosio::chain; + +BOOST_AUTO_TEST_SUITE(fork_database_tests) + +BOOST_AUTO_TEST_CASE(update_best_qc) try { + + fc::temp_directory temp_dir; + const auto& temp = temp_dir.path(); + fork_database fdb(temp); + fork_database_if_t forkdb; + auto root = block_state_accessor::make_genesis_block_state(); + auto bsp11a = block_state_accessor::make_unique_block_state(11, root); + auto bsp12a = block_state_accessor::make_unique_block_state(12, bsp11a); + auto bsp13a = block_state_accessor::make_unique_block_state(13, bsp12a); + auto bsp11b = block_state_accessor::make_unique_block_state(11, root); + auto bsp12b = block_state_accessor::make_unique_block_state(12, bsp11b); + auto bsp13b = block_state_accessor::make_unique_block_state(13, bsp12b); + auto bsp14b = block_state_accessor::make_unique_block_state(14, bsp13b); + auto bsp12bb = block_state_accessor::make_unique_block_state(12, bsp11b); + auto bsp13bb = block_state_accessor::make_unique_block_state(13, bsp12bb); + auto bsp13bbb = block_state_accessor::make_unique_block_state(13, bsp12bb); + auto bsp12bbb = block_state_accessor::make_unique_block_state(12, bsp11b); + auto bsp11c = block_state_accessor::make_unique_block_state(11, root); + auto bsp12c = block_state_accessor::make_unique_block_state(12, bsp11c); + auto bsp13c = block_state_accessor::make_unique_block_state(13, bsp12c); + + std::vector all { bsp11a, bsp12a, bsp13a, bsp11b, bsp12b, bsp12bb, bsp12bbb, bsp13b, bsp13bb, bsp13bbb, bsp14b, bsp11c, bsp12c, bsp13c }; + + forkdb.reset_root(*root); + forkdb.add(bsp11a, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp11b, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp11c, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp12a, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp13a, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp12b, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp12bb, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp12bbb, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp12c, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp13b, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp13bb, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp13bbb, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp14b, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp13c, mark_valid_t::no, ignore_duplicate_t::no); + + // test get_block + for (auto& i : all) { + BOOST_TEST(forkdb.get_block(i->id()) == i); + } + + // test remove + forkdb.remove(bsp12b->id()); + BOOST_TEST(!forkdb.get_block(bsp12b->id())); + BOOST_TEST(!forkdb.get_block(bsp13b->id())); + BOOST_TEST(!forkdb.get_block(bsp14b->id())); + forkdb.add(bsp12b, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp13b, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp14b, mark_valid_t::no, ignore_duplicate_t::no); + + // test update_best_qc + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp11b).block_num == 10); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).block_num == 10); + forkdb.update_best_qc(bsp11b->id(), {.block_num = 11, .is_strong_qc = false}); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).block_num == 11); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).is_strong_qc == false); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13b).block_num == 11); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp14b).block_num == 11); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12bb).block_num == 11); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bb).block_num == 11); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bbb).block_num == 11); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12bbb).block_num == 11); + forkdb.update_best_qc(bsp13bb->id(), {.block_num = 11, .is_strong_qc = true}); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bb).block_num == 11); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bb).is_strong_qc == true); + forkdb.update_best_qc(bsp11b->id(), {.block_num = 11, .is_strong_qc = true}); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).is_strong_qc == true); + BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bbb).is_strong_qc == true); + +} FC_LOG_AND_RETHROW(); + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unittests/unapplied_transaction_queue_tests.cpp b/unittests/unapplied_transaction_queue_tests.cpp index eda8d01721..6ab4d14dc2 100644 --- a/unittests/unapplied_transaction_queue_tests.cpp +++ b/unittests/unapplied_transaction_queue_tests.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include From d9c1b87014c0021f98d5984af2d3566bd762f3ff Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 28 Feb 2024 08:27:01 -0600 Subject: [PATCH 23/40] GH-2125 use a counter instead of time to ensure unique --- unittests/fork_db_tests.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp index e8bfae05c6..3fbc877c8a 100644 --- a/unittests/fork_db_tests.cpp +++ b/unittests/fork_db_tests.cpp @@ -8,7 +8,9 @@ namespace eosio::chain { inline block_id_type make_block_id(block_num_type block_num) { - block_id_type id = fc::sha256::hash(std::to_string(block_num) + std::to_string(fc::time_point::now().time_since_epoch().count())); + static uint32_t nonce = 0; + ++nonce; + block_id_type id = fc::sha256::hash(std::to_string(block_num) + "-" + std::to_string(nonce)); id._hash[0] &= 0xffffffff00000000; id._hash[0] += fc::endian_reverse_u32(block_num); // store the block num in the ID, 160 bits is plenty for the hash return id; From e2f0d4d96d7a171220fcc78ce977d7b708fa6579 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Wed, 28 Feb 2024 08:35:38 -0600 Subject: [PATCH 24/40] GH-2125 Add some comments --- unittests/fork_db_tests.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp index 3fbc877c8a..11ed680a48 100644 --- a/unittests/fork_db_tests.cpp +++ b/unittests/fork_db_tests.cpp @@ -16,8 +16,8 @@ inline block_id_type make_block_id(block_num_type block_num) { return id; } +// Used to access privates of block_state struct block_state_accessor { - static auto make_genesis_block_state() { block_state_ptr root = std::make_shared(); block_id_type genesis_id = make_block_id(10); @@ -56,11 +56,10 @@ using namespace eosio::chain; BOOST_AUTO_TEST_SUITE(fork_database_tests) BOOST_AUTO_TEST_CASE(update_best_qc) try { - - fc::temp_directory temp_dir; - const auto& temp = temp_dir.path(); - fork_database fdb(temp); fork_database_if_t forkdb; + + // Setup fork database with blocks based on a root of block 10 + // Add a number of forks in the fork database auto root = block_state_accessor::make_genesis_block_state(); auto bsp11a = block_state_accessor::make_unique_block_state(11, root); auto bsp12a = block_state_accessor::make_unique_block_state(12, bsp11a); @@ -77,6 +76,7 @@ BOOST_AUTO_TEST_CASE(update_best_qc) try { auto bsp12c = block_state_accessor::make_unique_block_state(12, bsp11c); auto bsp13c = block_state_accessor::make_unique_block_state(13, bsp12c); + // keep track of all those added for easy verification std::vector all { bsp11a, bsp12a, bsp13a, bsp11b, bsp12b, bsp12bb, bsp12bbb, bsp13b, bsp13bb, bsp13bbb, bsp14b, bsp11c, bsp12c, bsp13c }; forkdb.reset_root(*root); @@ -100,16 +100,16 @@ BOOST_AUTO_TEST_CASE(update_best_qc) try { BOOST_TEST(forkdb.get_block(i->id()) == i); } - // test remove + // test remove, should remove descendants forkdb.remove(bsp12b->id()); BOOST_TEST(!forkdb.get_block(bsp12b->id())); BOOST_TEST(!forkdb.get_block(bsp13b->id())); BOOST_TEST(!forkdb.get_block(bsp14b->id())); - forkdb.add(bsp12b, mark_valid_t::no, ignore_duplicate_t::no); - forkdb.add(bsp13b, mark_valid_t::no, ignore_duplicate_t::no); - forkdb.add(bsp14b, mark_valid_t::no, ignore_duplicate_t::no); + forkdb.add(bsp12b, mark_valid_t::no, ignore_duplicate_t::no); // will throw if already exists + forkdb.add(bsp13b, mark_valid_t::no, ignore_duplicate_t::no); // will throw if already exists + forkdb.add(bsp14b, mark_valid_t::no, ignore_duplicate_t::no); // will throw if already exists - // test update_best_qc + // test update_best_qc, should update descendants BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp11b).block_num == 10); BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).block_num == 10); forkdb.update_best_qc(bsp11b->id(), {.block_num = 11, .is_strong_qc = false}); From 1471adc758bcd9b0511d3083ba9392f1ea518520 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 29 Feb 2024 07:50:43 -0600 Subject: [PATCH 25/40] GH-2125 Use simpler fork-choice rule that doesn't change with votes. Only update LIB on block qc not on votes. --- libraries/chain/block_state.cpp | 12 +- libraries/chain/controller.cpp | 26 +---- libraries/chain/fork_database.cpp | 104 +----------------- libraries/chain/hotstuff/hotstuff.cpp | 7 +- .../hotstuff/test/finality_misc_tests.cpp | 4 +- .../eosio/chain/block_header_state.hpp | 2 +- .../chain/include/eosio/chain/block_state.hpp | 9 +- .../include/eosio/chain/fork_database.hpp | 5 - .../include/eosio/chain/hotstuff/hotstuff.hpp | 16 +-- unittests/block_state_tests.cpp | 12 +- unittests/fork_db_tests.cpp | 30 +---- 11 files changed, 38 insertions(+), 189 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 66c5f4ba8c..bdf68f0cab 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -15,8 +15,6 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con , strong_digest(compute_finalizer_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()) - , best_qc_claim(core.latest_qc_claim()) - , updated_core(core.next_metadata(best_qc_claim)) { // ASSUMPTION FROM controller_impl::apply_block = all untrusted blocks will have their signatures pre-validated here if( !skip_validate_signee ) { @@ -34,8 +32,6 @@ block_state::block_state(const block_header_state& bhs, dequefinalizers.size(), bhs.active_finalizer_policy->threshold, bhs.active_finalizer_policy->max_weak_sum_before_weak_final()) - , best_qc_claim(core.latest_qc_claim()) - , updated_core(core.next_metadata(best_qc_claim)) , pub_keys_recovered(true) // called by produce_block so signature recovery of trxs must have been done , cached_trxs(std::move(trx_metas)) { @@ -53,8 +49,6 @@ block_state::block_state(const block_state_legacy& bsp) { block_header_state::block_id = bsp.id(); header = bsp.header; core = finality_core::create_core_for_genesis_block(bsp.block_num()); // [if todo] instant transition is not acceptable - best_qc_claim = core.latest_qc_claim(); - updated_core = core.next_metadata(best_qc_claim); activated_protocol_features = bsp.activated_protocol_features; auto if_ext_id = instant_finality_extension::extension_id(); @@ -89,8 +83,7 @@ void block_state::set_trxs_metas( deque&& trxs_metas, } // Called from net threads -std::tuple -block_state::aggregate_vote(const vote_message& vote) { +vote_status block_state::aggregate_vote(const vote_message& vote) { const auto& finalizers = active_finalizer_policy->finalizers; auto it = std::find_if(finalizers.begin(), finalizers.end(), @@ -108,8 +101,7 @@ block_state::aggregate_vote(const vote_message& vote) { finalizers[index].weight); } else { wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) ); - auto state = pending_qc.state(); - return {vote_status::unknown_public_key, state, state}; + return vote_status::unknown_public_key; } } diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 89175e1701..309f0f591f 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3109,33 +3109,19 @@ struct controller_impl { // called from net threads and controller's thread pool vote_status process_vote_message( const vote_message& vote ) { - auto aggregate_vote = [&vote](auto& forkdb) -> std::pair> { + auto aggregate_vote = [&vote](auto& forkdb) -> vote_status { auto bsp = forkdb.get_block(vote.proposal_id); if (bsp) { - auto [status, pre_state, post_state] = bsp->aggregate_vote(vote); - std::optional new_lib{}; - if (status == vote_status::success && pre_state != post_state && pending_quorum_certificate::is_quorum_met(post_state)) { - if (post_state == pending_quorum_certificate::state_t::strong) { - new_lib = bsp->core.final_on_strong_qc_block_num; - forkdb.update_best_qc(bsp->id(), {.block_num = bsp->block_num(), .is_strong_qc = true}); - } else { - forkdb.update_best_qc(bsp->id(), {.block_num = bsp->block_num(), .is_strong_qc = false}); - } - } - return {status, new_lib}; + return bsp->aggregate_vote(vote); } - return {vote_status::unknown_block, {}}; + return vote_status::unknown_block; }; // TODO: https://github.com/AntelopeIO/leap/issues/2057 // TODO: Do not aggregate votes on block_state if in legacy block fork_db - auto aggregate_vote_legacy = [](auto&) -> std::pair> { - return {vote_status::unknown_block, {}}; + auto aggregate_vote_legacy = [](auto&) -> vote_status { + return vote_status::unknown_block; }; - auto [status, new_lib] = fork_db.apply>>(aggregate_vote_legacy, aggregate_vote); - if (new_lib) { - set_if_irreversible_block_num(*new_lib); - } - return status; + return fork_db.apply(aggregate_vote_legacy, aggregate_vote); } void create_and_send_vote_msg(const block_state_ptr& bsp, const fork_database_if_t& fork_db) { diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index d529c23987..ae363d9b76 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -20,57 +20,19 @@ namespace eosio::chain { * Version 1: initial version of the new refactored fork database portable format */ - // call while holding fork database lock struct block_state_accessor { - static bool is_valid(const block_state& bs) { - return bs.is_valid(); - } - static void set_valid(block_state& bs, bool v) { - bs.validated = v; - } - static uint32_t last_final_block_num(const block_state& bs) { - return bs.updated_core.last_final_block_num; - } - static uint32_t final_on_strong_qc_block_num(const block_state& bs) { - return bs.updated_core.final_on_strong_qc_block_num; - } - static uint32_t lastest_qc_claim_block_num(const block_state& bs) { - return bs.updated_core.latest_qc_claim_block_num; - } - static bool qc_claim_update_needed(block_state& bs, const qc_claim_t& best_qc_claim) { - return bs.best_qc_claim < best_qc_claim; - } - static bool qc_claim_update_needed(block_state& bs, const block_state& prev) { - return bs.best_qc_claim < prev.best_qc_claim; - } - static void update_best_qc(block_state& bs, const qc_claim_t& best_qc_claim) { - assert(bs.best_qc_claim < best_qc_claim); - bs.updated_core = bs.core.next_metadata(best_qc_claim); - bs.best_qc_claim = best_qc_claim; - } - static void update_best_qc(block_state& bs, const block_state& prev) { - assert(bs.best_qc_claim < prev.best_qc_claim); - update_best_qc(bs, prev.best_qc_claim); - } - - // thread safe - static uint32_t block_height(const block_state& bs) { - return bs.timestamp().slot; - } + static bool is_valid(const block_state& bs) { return bs.is_valid(); } + static void set_valid(block_state& bs, bool v) { bs.validated = v; } + static uint32_t last_final_block_num(const block_state& bs) { return bs.core.last_final_block_num(); } + static uint32_t lastest_qc_claim_block_num(const block_state& bs) { return bs.core.latest_qc_claim().block_num; } + static uint32_t block_height(const block_state& bs) { return bs.timestamp().slot; } }; struct block_state_legacy_accessor { static bool is_valid(const block_state_legacy& bs) { return bs.is_valid(); } static void set_valid(block_state_legacy& bs, bool v) { bs.validated = v; } static uint32_t last_final_block_num(const block_state_legacy& bs) { return bs.irreversible_blocknum(); } - static uint32_t final_on_strong_qc_block_num(const block_state_legacy& bs) { return bs.irreversible_blocknum(); } static uint32_t lastest_qc_claim_block_num(const block_state_legacy& bs) { return bs.irreversible_blocknum(); } - static bool qc_claim_update_needed(block_state_legacy&, const qc_claim_t&) { return false; } - static bool qc_claim_update_needed(block_state_legacy&, const block_state_legacy&) { return false; } - static void update_best_qc(block_state_legacy&, const qc_claim_t&) { } - static void update_best_qc(block_state_legacy&, const block_state_legacy&) { } - - // thread safe static uint32_t block_height(const block_state_legacy& bs) { return bs.block_num(); } }; @@ -149,7 +111,6 @@ namespace eosio::chain { full_branch_type fetch_full_branch_impl(const block_id_type& h) const; BSP search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; void mark_valid_impl( const BSP& h ); - void update_best_qc_impl( const block_id_type& id, const qc_claim_t& most_recent_ancestor_with_qc ); branch_type_pair fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; }; @@ -431,17 +392,6 @@ namespace eosio::chain { EOS_THROW( fork_database_exception, "duplicate block added", ("id", n->id()) ); } - // ancestor might have been updated since block_state was created - if (auto prev = index.find(n->previous()); prev != index.end()) { - if (BSAccessor::qc_claim_update_needed(*n, **prev)) { - auto& by_id_idx = index.template get(); - auto itr = by_id_idx.find(n->id()); - by_id_idx.modify( itr, [&]( auto& i ) { - BSAccessor::update_best_qc(*i, **prev); - } ); - } - } - auto candidate = index.template get().begin(); if( BSAccessor::is_valid(**candidate) ) { head = *candidate; @@ -692,50 +642,6 @@ namespace eosio::chain { } } - template - void fork_database_t::update_best_qc( const block_id_type& id, const qc_claim_t& best_qc_claim ) { - std::lock_guard g( my->mtx ); - my->update_best_qc_impl( id, best_qc_claim ); - } - - template - void fork_database_impl::update_best_qc_impl( const block_id_type& id, const qc_claim_t& best_qc_claim ) { - auto& by_id_idx = index.template get(); - - auto itr = by_id_idx.find( id ); - EOS_ASSERT( itr != by_id_idx.end(), fork_database_exception, - "block state not in fork database; cannot update", ("id", id) ); - - if (BSAccessor::qc_claim_update_needed(**itr, best_qc_claim)) { - by_id_idx.modify( itr, [&]( auto& i ) { - BSAccessor::update_best_qc(*i, best_qc_claim); - } ); - } - - // process descendants - vector descendants; - descendants.reserve(index.size()); - descendants.push_back(id); - auto& previdx = index.template get(); - for( uint32_t i = 0; i < descendants.size(); ++i ) { - auto previtr = previdx.lower_bound( descendants[i] ); - while( previtr != previdx.end() && (*previtr)->previous() == descendants[i] ) { - if (BSAccessor::qc_claim_update_needed(**previtr, best_qc_claim)) { - previdx.modify( previtr, [&](auto& i) { - BSAccessor::update_best_qc(*i, best_qc_claim); - }); - descendants.emplace_back( (*previtr)->id() ); - } - ++previtr; - } - } - - auto candidate = index.template get().begin(); - if( first_preferred( **candidate, *head ) ) { - head = *candidate; - } - } - template BSP fork_database_t::get_block(const block_id_type& id) const { std::lock_guard g( my->mtx ); diff --git a/libraries/chain/hotstuff/hotstuff.cpp b/libraries/chain/hotstuff/hotstuff.cpp index b429d5eb5c..7c9fe225cf 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -126,16 +126,15 @@ vote_status pending_quorum_certificate::add_weak_vote(std::span p } // thread safe, status, pre state , post state -std::tuple -pending_quorum_certificate::add_vote(block_num_type block_num, bool strong, std::span proposal_digest, size_t index, - const bls_public_key& pubkey, const bls_signature& sig, uint64_t weight) { +vote_status pending_quorum_certificate::add_vote(block_num_type block_num, bool strong, std::span proposal_digest, size_t index, + const bls_public_key& pubkey, const bls_signature& sig, uint64_t weight) { std::lock_guard g(*_mtx); auto pre_state = _state; vote_status s = strong ? add_strong_vote(proposal_digest, index, pubkey, sig, weight) : add_weak_vote(proposal_digest, index, pubkey, sig, weight); dlog("block_num: ${bn}, vote strong: ${sv}, status: ${s}, pre-state: ${pre}, post-state: ${state}, quorum_met: ${q}", ("bn", block_num)("sv", strong)("s", s)("pre", pre_state)("state", _state)("q", is_quorum_met_no_lock())); - return {s, pre_state, _state}; + return s; } // thread safe diff --git a/libraries/chain/hotstuff/test/finality_misc_tests.cpp b/libraries/chain/hotstuff/test/finality_misc_tests.cpp index 85bfdb72a8..ba58a06ed5 100644 --- a/libraries/chain/hotstuff/test/finality_misc_tests.cpp +++ b/libraries/chain/hotstuff/test/finality_misc_tests.cpp @@ -51,11 +51,11 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { pubkey.push_back(k.get_public_key()); auto weak_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index, uint64_t weight) { - return std::get<0>(qc.add_vote(0, false, digest, index, pubkey[index], sk[index].sign(digest), weight)); + return qc.add_vote(0, false, digest, index, pubkey[index], sk[index].sign(digest), weight); }; auto strong_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index, uint64_t weight) { - return std::get<0>(qc.add_vote(0, true, digest, index, pubkey[index], sk[index].sign(digest), weight)); + return qc.add_vote(0, true, digest, index, pubkey[index], sk[index].sign(digest), weight); }; constexpr uint64_t weight = 1; diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index e26f20c94c..bee4855c94 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -37,7 +37,7 @@ struct block_header_state { block_header header; protocol_feature_activation_set_ptr activated_protocol_features; - finality_core core; + finality_core core; // thread safe, not modified after creation incremental_merkle_tree proposal_mtree; incremental_merkle_tree finality_mtree; diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index dcedbefafa..3b65974a62 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -33,8 +33,6 @@ struct block_state : public block_header_state { // block_header_state provi // ------ updated for votes, used for fork_db ordering ------------------------------ private: bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches. - qc_claim_t best_qc_claim; // only modify/access while holding forkdb lock - core_metadata updated_core; // only modify/access while holding forkdb lock, updated block_header_state::core by best_qc_claim // ------ data members caching information available elsewhere ---------------------- bool pub_keys_recovered = false; @@ -63,9 +61,8 @@ struct block_state : public block_header_state { // block_header_state provi uint32_t last_qc_block_num() const { return core.latest_qc_claim().block_num; } uint32_t final_on_strong_qc_block_num() const { return core.final_on_strong_qc_block_num; } - // vote_status, pending_qc pre state, pending_qc post state - std::tuple - aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc + // vote_status + vote_status aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc void verify_qc(const valid_quorum_certificate& qc) const; // verify given qc is valid with respect block_state using bhs_t = block_header_state; @@ -94,4 +91,4 @@ using block_state_ptr = std::shared_ptr; } // namespace eosio::chain // not exporting pending_qc or valid_qc -FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(strong_digest)(weak_digest)(pending_qc)(valid_qc)(validated)(best_qc_claim)(updated_core) ) +FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(strong_digest)(weak_digest)(pending_qc)(valid_qc)(validated) ) diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index fe4398dfe8..3e04eacd65 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -113,11 +113,6 @@ namespace eosio::chain { void mark_valid( const BSP& h ); - /** - * Update finality_core for best qc - */ - void update_best_qc( const block_id_type& id, const qc_claim_t& best_qc_claim ); - private: unique_ptr> my; }; diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index b4e4b49b80..0fa20f4bfa 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -97,14 +97,14 @@ namespace eosio::chain { return s == state_t::strong || s == state_t::weak_achieved || s == state_t::weak_final; } - // thread safe, status, pre , post - std::tuple add_vote(block_num_type block_num, - bool strong, - std::span proposal_digest, - size_t index, - const bls_public_key& pubkey, - const bls_signature& sig, - uint64_t weight); + // thread safe + vote_status add_vote(block_num_type block_num, + bool strong, + std::span proposal_digest, + size_t index, + const bls_public_key& pubkey, + const bls_signature& sig, + uint64_t weight); state_t state() const { std::lock_guard g(*_mtx); return _state; }; valid_quorum_certificate to_valid_quorum_certificate() const; diff --git a/unittests/block_state_tests.cpp b/unittests/block_state_tests.cpp index c20149655b..0fb1b3c789 100644 --- a/unittests/block_state_tests.cpp +++ b/unittests/block_state_tests.cpp @@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bool strong = (i % 2 == 0); // alternate strong and weak auto sig = strong ? private_key[i].sign(strong_digest.to_uint8_span()) : private_key[i].sign(weak_digest); vote_message vote{ block_id, strong, public_key[i], sig }; - BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) == vote_status::success); + BOOST_REQUIRE(bsp->aggregate_vote(vote) == vote_status::success); } } @@ -58,7 +58,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1, bsp->active_finalizer_policy->max_weak_sum_before_weak_final() }; vote_message vote {block_id, true, public_key[0], private_key[1].sign(strong_digest.to_uint8_span()) }; - BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) != vote_status::success); + BOOST_REQUIRE(bsp->aggregate_vote(vote) != vote_status::success); } { // duplicate votes @@ -68,8 +68,8 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1, bsp->active_finalizer_policy->max_weak_sum_before_weak_final() }; vote_message vote {block_id, true, public_key[0], private_key[0].sign(strong_digest.to_uint8_span()) }; - BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) == vote_status::success); - BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) != vote_status::success); + BOOST_REQUIRE(bsp->aggregate_vote(vote) == vote_status::success); + BOOST_REQUIRE(bsp->aggregate_vote(vote) != vote_status::success); } { // public key does not exit in finalizer set @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bls_public_key new_public_key{ new_private_key.get_public_key() }; vote_message vote {block_id, true, new_public_key, private_key[0].sign(strong_digest.to_uint8_span()) }; - BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) != vote_status::success); + BOOST_REQUIRE(bsp->aggregate_vote(vote) != vote_status::success); } } FC_LOG_AND_RETHROW(); @@ -125,7 +125,7 @@ void do_quorum_test(const std::vector& weights, if( to_vote[i] ) { auto sig = strong ? private_key[i].sign(strong_digest.to_uint8_span()) : private_key[i].sign(weak_digest); vote_message vote{ block_id, strong, public_key[i], sig }; - BOOST_REQUIRE(std::get<0>(bsp->aggregate_vote(vote)) == vote_status::success); + BOOST_REQUIRE(bsp->aggregate_vote(vote) == vote_status::success); } } diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp index 11ed680a48..e2d7432030 100644 --- a/unittests/fork_db_tests.cpp +++ b/unittests/fork_db_tests.cpp @@ -24,7 +24,6 @@ struct block_state_accessor { root->block_id = genesis_id; root->header.timestamp = block_timestamp_type{10}; root->core = finality_core::create_core_for_genesis_block(10); - root->best_qc_claim = {.block_num = 10, .is_strong_qc = false}; return root; } @@ -38,15 +37,9 @@ struct block_state_accessor { .block_id = prev->id(), .timestamp = prev->timestamp() }; - bsp->core = prev->core.next(parent_block, prev->best_qc_claim); - bsp->best_qc_claim = bsp->core.latest_qc_claim(); - bsp->updated_core = bsp->core.next_metadata(bsp->best_qc_claim); + bsp->core = prev->core.next(parent_block, prev->core.latest_qc_claim()); return bsp; } - - static auto get_best_qc_claim(const block_state_ptr& bs) { - return bs->best_qc_claim; - } }; } // namespace eosio::chain @@ -55,7 +48,7 @@ using namespace eosio::chain; BOOST_AUTO_TEST_SUITE(fork_database_tests) -BOOST_AUTO_TEST_CASE(update_best_qc) try { +BOOST_AUTO_TEST_CASE(add_remove_test) try { fork_database_if_t forkdb; // Setup fork database with blocks based on a root of block 10 @@ -109,25 +102,6 @@ BOOST_AUTO_TEST_CASE(update_best_qc) try { forkdb.add(bsp13b, mark_valid_t::no, ignore_duplicate_t::no); // will throw if already exists forkdb.add(bsp14b, mark_valid_t::no, ignore_duplicate_t::no); // will throw if already exists - // test update_best_qc, should update descendants - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp11b).block_num == 10); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).block_num == 10); - forkdb.update_best_qc(bsp11b->id(), {.block_num = 11, .is_strong_qc = false}); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).block_num == 11); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).is_strong_qc == false); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13b).block_num == 11); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp14b).block_num == 11); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12bb).block_num == 11); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bb).block_num == 11); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bbb).block_num == 11); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12bbb).block_num == 11); - forkdb.update_best_qc(bsp13bb->id(), {.block_num = 11, .is_strong_qc = true}); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bb).block_num == 11); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bb).is_strong_qc == true); - forkdb.update_best_qc(bsp11b->id(), {.block_num = 11, .is_strong_qc = true}); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp12b).is_strong_qc == true); - BOOST_TEST(block_state_accessor::get_best_qc_claim(bsp13bbb).is_strong_qc == true); - } FC_LOG_AND_RETHROW(); BOOST_AUTO_TEST_SUITE_END() From 0f521d47e563702b7273a165d22c463e334b3507 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 29 Feb 2024 11:22:32 -0600 Subject: [PATCH 26/40] GH-2125 Advance LIB on producer since producer does not receive its own blocks --- libraries/chain/block_header_state.cpp | 2 +- libraries/chain/controller.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index b0a83331d7..aa7e57a26e 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -95,7 +95,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con uint16_t if_ext_id = instant_finality_extension::extension_id(); emplace_extension(result.header.header_extensions, if_ext_id, fc::raw::pack(new_if_ext)); - result.header_exts.emplace(if_ext_id, std::move(new_if_ext)); // TODO: does not appear to be used + result.header_exts.emplace(if_ext_id, std::move(new_if_ext)); // add protocol_feature_activation extension // ----------------------------------------- diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 309f0f591f..52d4c367f0 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2800,6 +2800,17 @@ struct controller_impl { }}); if( s == controller::block_status::incomplete ) { + fork_db.apply_if([&](auto& forkdb) { + const auto& bsp = std::get>(cb.bsp); + + uint16_t if_ext_id = instant_finality_extension::extension_id(); + assert(bsp->header_exts.count(if_ext_id) > 0); // in all instant_finality block headers + const auto& if_ext = std::get(bsp->header_exts.lower_bound(if_ext_id)->second); + if (if_ext.qc_claim.is_strong_qc) { + set_if_irreversible_block_num(bsp->core.final_on_strong_qc_block_num); + } + }); + log_irreversible(); } From 48f225326599eb341e8a4c5e8b5034fc639ef551 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 29 Feb 2024 13:08:41 -0600 Subject: [PATCH 27/40] GH-2125 Update tests now that votes do not advance LIB --- unittests/finality_test_cluster.cpp | 1 + unittests/finality_tests.cpp | 281 ++++++++++++++++------------ 2 files changed, 163 insertions(+), 119 deletions(-) diff --git a/unittests/finality_test_cluster.cpp b/unittests/finality_test_cluster.cpp index dff037028e..1af4cd4fb8 100644 --- a/unittests/finality_test_cluster.cpp +++ b/unittests/finality_test_cluster.cpp @@ -91,6 +91,7 @@ bool finality_test_cluster::produce_blocks_and_verify_lib_advancing() { for (auto i = 0; i < 3; ++i) { produce_and_push_block(); process_node1_vote(); + produce_and_push_block(); if (!node0_lib_advancing() || !node1_lib_advancing() || !node2_lib_advancing()) { return false; } diff --git a/unittests/finality_tests.cpp b/unittests/finality_tests.cpp index 132dc8c26a..6d68774fe5 100644 --- a/unittests/finality_tests.cpp +++ b/unittests/finality_tests.cpp @@ -14,6 +14,7 @@ BOOST_AUTO_TEST_CASE(two_votes) { try { cluster.produce_and_push_block(); // process node1's votes only cluster.process_node1_vote(); + cluster.produce_and_push_block(); // all nodes advance LIB BOOST_REQUIRE(cluster.node0_lib_advancing()); @@ -22,16 +23,38 @@ BOOST_AUTO_TEST_CASE(two_votes) { try { } } FC_LOG_AND_RETHROW() } -// verify LIB advances with all of the three finalizers voting -BOOST_AUTO_TEST_CASE(all_votes) { try { +// verify LIB does not advances with finalizers not voting. +BOOST_AUTO_TEST_CASE(no_votes) { try { finality_test_cluster cluster; + cluster.produce_and_push_block(); + cluster.node0_lib_advancing(); // reset + cluster.node1_lib_advancing(); // reset + cluster.node2_lib_advancing(); // reset for (auto i = 0; i < 3; ++i) { // node0 produces a block and pushes to node1 and node2 cluster.produce_and_push_block(); + // process no votes + cluster.produce_and_push_block(); + + // all nodes don't advance LIB + BOOST_REQUIRE(!cluster.node0_lib_advancing()); + BOOST_REQUIRE(!cluster.node1_lib_advancing()); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); + } +} FC_LOG_AND_RETHROW() } + +// verify LIB advances with all of the three finalizers voting +BOOST_AUTO_TEST_CASE(all_votes) { try { + finality_test_cluster cluster; + + cluster.produce_and_push_block(); + for (auto i = 0; i < 3; ++i) { // process node1 and node2's votes cluster.process_node1_vote(); cluster.process_node2_vote(); + // node0 produces a block and pushes to node1 and node2 + cluster.produce_and_push_block(); // all nodes advance LIB BOOST_REQUIRE(cluster.node0_lib_advancing()); @@ -44,10 +67,11 @@ BOOST_AUTO_TEST_CASE(all_votes) { try { BOOST_AUTO_TEST_CASE(conflicting_votes_strong_first) { try { finality_test_cluster cluster; + cluster.produce_and_push_block(); for (auto i = 0; i < 3; ++i) { - cluster.produce_and_push_block(); cluster.process_node1_vote(); // strong cluster.process_node2_vote(finality_test_cluster::vote_mode::weak); // weak + cluster.produce_and_push_block(); BOOST_REQUIRE(cluster.node0_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); @@ -59,10 +83,11 @@ BOOST_AUTO_TEST_CASE(conflicting_votes_strong_first) { try { BOOST_AUTO_TEST_CASE(conflicting_votes_weak_first) { try { finality_test_cluster cluster; + cluster.produce_and_push_block(); for (auto i = 0; i < 3; ++i) { - cluster.produce_and_push_block(); cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); // weak cluster.process_node2_vote(); // strong + cluster.produce_and_push_block(); BOOST_REQUIRE(cluster.node0_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); @@ -76,26 +101,28 @@ BOOST_AUTO_TEST_CASE(one_delayed_votes) { try { // hold the vote for the first block to simulate delay cluster.produce_and_push_block(); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - // LIB advanced on node1 because a new block was received + // LIB advanced on nodes because a new block was received + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); cluster.produce_and_push_block(); // vote block 0 (index 0) to make it have a strong QC, - // prompting LIB advacing on node0 + // prompting LIB advacing on node2 cluster.process_node1_vote(0); - BOOST_REQUIRE(cluster.node0_lib_advancing()); - BOOST_REQUIRE(!cluster.node1_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); + BOOST_REQUIRE(cluster.node1_lib_advancing()); // block 1 (index 1) has the same QC claim as block 0. It cannot move LIB cluster.process_node1_vote(1); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); // producing, pushing, and voting a new block makes LIB moving - cluster.produce_and_push_block(); cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); BOOST_REQUIRE(cluster.produce_blocks_and_verify_lib_advancing()); @@ -110,27 +137,33 @@ BOOST_AUTO_TEST_CASE(three_delayed_votes) { try { for (auto i = 0; i < 4; ++i) { cluster.produce_and_push_block(); } - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - // LIB advanced on node1 because a new block was received + // LIB advanced on nodes because a new block was received + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); + BOOST_REQUIRE(!cluster.node1_lib_advancing()); + // vote block 0 (index 0) to make it have a strong QC, - // prompting LIB advacing on node0 + // prompting LIB advacing on nodes cluster.process_node1_vote(0); - BOOST_REQUIRE(cluster.node0_lib_advancing()); - BOOST_REQUIRE(!cluster.node1_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); + BOOST_REQUIRE(cluster.node1_lib_advancing()); // blocks 1 to 3 have the same QC claim as block 0. It cannot move LIB for (auto i=1; i < 4; ++i) { cluster.process_node1_vote(i); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); } // producing, pushing, and voting a new block makes LIB moving - cluster.produce_and_push_block(); cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); BOOST_REQUIRE(cluster.produce_blocks_and_verify_lib_advancing()); @@ -150,22 +183,25 @@ BOOST_AUTO_TEST_CASE(out_of_order_votes) { try { // vote block 2 (index 2) to make it have a strong QC, // prompting LIB advacing cluster.process_node1_vote(2); + cluster.produce_and_push_block(); BOOST_REQUIRE(cluster.node0_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); // block 1 (index 1) has the same QC claim as block 2. It will not move LIB cluster.process_node1_vote(1); + cluster.produce_and_push_block(); BOOST_REQUIRE(!cluster.node0_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); // block 0 (index 0) has the same QC claim as block 2. It will not move LIB cluster.process_node1_vote(0); + cluster.produce_and_push_block(); BOOST_REQUIRE(!cluster.node0_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); // producing, pushing, and voting a new block makes LIB moving - cluster.produce_and_push_block(); cluster.process_node1_vote(); + cluster.produce_and_push_block(); BOOST_REQUIRE(cluster.node0_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); @@ -179,20 +215,23 @@ BOOST_AUTO_TEST_CASE(long_delayed_votes) { try { // Produce and push a block, vote on it after a long delay. constexpr uint32_t delayed_vote_index = 0; cluster.produce_and_push_block(); - // The block is not voted, so no strong QC is created and LIB does not advance on node0 - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - // The strong QC extension for prior block makes LIB advance on node1 + // The strong QC extension for prior block makes LIB advance on nodes + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); cluster.produce_and_push_block(); - cluster.process_node1_vote(); - // the vote makes a strong QC for the current block, prompting LIB advance on node0 - BOOST_REQUIRE(cluster.node0_lib_advancing()); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); + cluster.process_node1_vote(); + cluster.produce_and_push_block(); + // the vote makes a strong QC for the current block, prompting LIB advance on nodes + BOOST_REQUIRE(cluster.node2_lib_advancing()); + BOOST_REQUIRE(cluster.node1_lib_advancing()); + for (auto i = 2; i < 100; ++i) { - cluster.produce_and_push_block(); cluster.process_node1_vote(); + cluster.produce_and_push_block(); BOOST_REQUIRE(cluster.node0_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); } @@ -210,17 +249,21 @@ BOOST_AUTO_TEST_CASE(lost_votes) { try { // The block contains a strong QC extension for prior block cluster.produce_and_push_block(); - // The block is not voted, so no strong QC is created and LIB does not advance on node0 - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - // The strong QC extension for prior block makes LIB advance on node1 + // The strong QC extension for prior block makes LIB advance on nodes BOOST_REQUIRE(cluster.node1_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); cluster.produce_and_push_block(); + // The block is not voted, so no strong QC is created and LIB does not advance on nodes + BOOST_REQUIRE(!cluster.node1_lib_advancing()); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); + cluster.process_node1_vote(); + cluster.produce_and_push_block(); - // the vote makes a strong QC for the current block, prompting LIB advance on node0 - BOOST_REQUIRE(cluster.node0_lib_advancing()); - BOOST_REQUIRE(!cluster.node1_lib_advancing()); + // vote causes lib to advance + BOOST_REQUIRE(cluster.node1_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.produce_blocks_and_verify_lib_advancing()); } FC_LOG_AND_RETHROW() } @@ -232,28 +275,27 @@ BOOST_AUTO_TEST_CASE(one_weak_vote) { try { cluster.produce_and_push_block(); // Change the vote to a weak vote and process it cluster.process_node1_vote(0, finality_test_cluster::vote_mode::weak); - // A weak QC is created and LIB does not advance on node0 - BOOST_REQUIRE(!cluster.node0_lib_advancing()); // The strong QC extension for prior block makes LIB advance on node1 BOOST_REQUIRE(cluster.node1_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); cluster.produce_and_push_block(); - cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + // A weak QC is created and LIB does not advance on node2 + BOOST_REQUIRE(!cluster.node2_lib_advancing()); // no 2-chain was formed as prior block was not a strong block BOOST_REQUIRE(!cluster.node1_lib_advancing()); - cluster.produce_and_push_block(); cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); BOOST_REQUIRE(cluster.node1_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); - cluster.produce_and_push_block(); cluster.process_node1_vote(); + cluster.produce_and_push_block(); // the vote makes a strong QC and a higher final_on_strong_qc, - // prompting LIB advance on node0 - BOOST_REQUIRE(cluster.node0_lib_advancing()); + // prompting LIB advance on nodes BOOST_REQUIRE(cluster.node1_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); // now a 3 chain has formed. BOOST_REQUIRE(cluster.produce_blocks_and_verify_lib_advancing()); @@ -264,29 +306,32 @@ BOOST_AUTO_TEST_CASE(two_weak_votes) { try { // Produce and push a block cluster.produce_and_push_block(); - // Change the vote to a weak vote and process it - cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); - // A weak QC cannot advance LIB on node0 - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - // The strong QC extension for prior block makes LIB advance on node1 + // The strong QC extension for prior block makes LIB advance on nodes BOOST_REQUIRE(cluster.node1_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); - cluster.produce_and_push_block(); + // Change the vote to a weak vote and process it cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); - // A weak QC cannot advance LIB on node0 - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - // no 2-chain was formed as prior block was not a strong block + cluster.produce_and_push_block(); + // A weak QC cannot advance LIB on nodes + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); + cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); cluster.produce_and_push_block(); - cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + // A weak QC cannot advance LIB on node2 + BOOST_REQUIRE(!cluster.node2_lib_advancing()); + // no 2-chain was formed as prior block was not a strong block BOOST_REQUIRE(!cluster.node1_lib_advancing()); + cluster.process_node1_vote(); cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); + BOOST_REQUIRE(cluster.node1_lib_advancing()); + cluster.process_node1_vote(); - // the vote makes a strong QC for the current block, prompting LIB advance on node0 - BOOST_REQUIRE(cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); // now a 3 chain has formed. @@ -296,40 +341,42 @@ BOOST_AUTO_TEST_CASE(two_weak_votes) { try { BOOST_AUTO_TEST_CASE(intertwined_weak_votes) { try { finality_test_cluster cluster; - // Weak vote cluster.produce_and_push_block(); - cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); - // A weak QC cannot advance LIB on node0 - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - // The strong QC extension for prior block makes LIB advance on node1 + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); - // Strong vote + // Weak vote + cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); cluster.produce_and_push_block(); - cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); - // no 2-chain was formed as prior block was not a strong block + + // The strong QC extension for prior block makes LIB advance on nodes + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); - // Weak vote + // Strong vote + cluster.process_node1_vote(); cluster.produce_and_push_block(); - cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); - // A weak QC cannot advance LIB on node0 - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); - // Strong vote + // Weak vote + cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); cluster.produce_and_push_block(); - cluster.process_node1_vote(); - // the vote makes a strong QC for the current block, prompting LIB advance on node0 - BOOST_REQUIRE(cluster.node0_lib_advancing()); - // no 2-chain was formed as prior block was not a strong block + // A weak QC cannot advance LIB on nodes + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); // Strong vote + cluster.process_node1_vote(); cluster.produce_and_push_block(); + // the vote makes a strong QC for the current block, prompting LIB advance on node0 + BOOST_REQUIRE(cluster.node2_lib_advancing()); + BOOST_REQUIRE(cluster.node1_lib_advancing()); + + // Strong vote cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); BOOST_REQUIRE(cluster.produce_blocks_and_verify_lib_advancing()); @@ -339,42 +386,43 @@ BOOST_AUTO_TEST_CASE(intertwined_weak_votes) { try { BOOST_AUTO_TEST_CASE(weak_delayed_lost_vote) { try { finality_test_cluster cluster; - // A weak vote cluster.produce_and_push_block(); - cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); + // A weak vote + cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); + cluster.produce_and_push_block(); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); + BOOST_REQUIRE(!cluster.node1_lib_advancing()); + // A delayed vote (index 1) constexpr uint32_t delayed_index = 1; cluster.produce_and_push_block(); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); // A strong vote - cluster.produce_and_push_block(); cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); - BOOST_REQUIRE(!cluster.node1_lib_advancing()); - - // A lost vote cluster.produce_and_push_block(); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); - // The delayed vote arrives - cluster.process_node1_vote(delayed_index); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + // A lost vote + cluster.produce_and_push_block(); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); + // The delayed vote arrives, does not advance lib because it is weak + cluster.process_node1_vote(delayed_index); cluster.produce_and_push_block(); - cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); - cluster.produce_and_push_block(); + // strong vote advances lib cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); BOOST_REQUIRE(cluster.produce_blocks_and_verify_lib_advancing()); @@ -387,45 +435,41 @@ BOOST_AUTO_TEST_CASE(delayed_strong_weak_lost_vote) { try { // A delayed vote (index 0) constexpr uint32_t delayed_index = 0; cluster.produce_and_push_block(); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); // A strong vote - cluster.produce_and_push_block(); cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); - BOOST_REQUIRE(!cluster.node1_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); + BOOST_REQUIRE(cluster.node1_lib_advancing()); // A weak vote - cluster.produce_and_push_block(); cluster.process_node1_vote(finality_test_cluster::vote_mode::weak); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - BOOST_REQUIRE(cluster.node1_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); + BOOST_REQUIRE(!cluster.node1_lib_advancing()); // A strong vote - cluster.produce_and_push_block(); cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); - BOOST_REQUIRE(!cluster.node1_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); + BOOST_REQUIRE(cluster.node1_lib_advancing()); // A lost vote cluster.produce_and_push_block(); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - BOOST_REQUIRE(cluster.node1_lib_advancing()); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); + BOOST_REQUIRE(!cluster.node1_lib_advancing()); // The delayed vote arrives cluster.process_node1_vote(delayed_index); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); - BOOST_REQUIRE(!cluster.node1_lib_advancing()); - cluster.produce_and_push_block(); - cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + BOOST_REQUIRE(!cluster.node2_lib_advancing()); BOOST_REQUIRE(!cluster.node1_lib_advancing()); - cluster.produce_and_push_block(); cluster.process_node1_vote(); - BOOST_REQUIRE(cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); BOOST_REQUIRE(cluster.produce_blocks_and_verify_lib_advancing()); @@ -435,15 +479,15 @@ BOOST_AUTO_TEST_CASE(delayed_strong_weak_lost_vote) { try { BOOST_AUTO_TEST_CASE(duplicate_votes) { try { finality_test_cluster cluster; + cluster.produce_and_push_block(); for (auto i = 0; i < 5; ++i) { - cluster.produce_and_push_block(); cluster.process_node1_vote(i); - // vote again to make it duplicate BOOST_REQUIRE(cluster.process_node1_vote(i) == eosio::chain::vote_status::duplicate); + cluster.produce_and_push_block(); // verify duplicate votes do not affect LIB advancing - BOOST_REQUIRE(cluster.node0_lib_advancing()); + BOOST_REQUIRE(cluster.node2_lib_advancing()); BOOST_REQUIRE(cluster.node1_lib_advancing()); } } FC_LOG_AND_RETHROW() } @@ -454,19 +498,20 @@ BOOST_AUTO_TEST_CASE(unknown_proposal_votes) { try { // node0 produces a block and pushes to node1 cluster.produce_and_push_block(); - // intentionally corrupt proposal_id in node1's vote cluster.node1_corrupt_vote_proposal_id(); - // process the corrupted vote. LIB should not advance + // process the corrupted vote cluster.process_node1_vote(0); BOOST_REQUIRE(cluster.process_node1_vote(0) == eosio::chain::vote_status::unknown_block); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); + cluster.produce_and_push_block(); + BOOST_REQUIRE(cluster.node2_lib_advancing()); // restore to original vote cluster.node1_restore_to_original_vote(); // process the original vote. LIB should advance + cluster.produce_and_push_block(); cluster.process_node1_vote(0); BOOST_REQUIRE(cluster.produce_blocks_and_verify_lib_advancing()); @@ -485,7 +530,6 @@ BOOST_AUTO_TEST_CASE(unknown_finalizer_key_votes) { try { // process the corrupted vote. LIB should not advance cluster.process_node1_vote(0); BOOST_REQUIRE(cluster.process_node1_vote(0) == eosio::chain::vote_status::unknown_public_key); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); // restore to original vote cluster.node1_restore_to_original_vote(); @@ -508,7 +552,6 @@ BOOST_AUTO_TEST_CASE(corrupted_signature_votes) { try { // process the corrupted vote. LIB should not advance BOOST_REQUIRE(cluster.process_node1_vote(0) == eosio::chain::vote_status::invalid_signature); - BOOST_REQUIRE(!cluster.node0_lib_advancing()); // restore to original vote cluster.node1_restore_to_original_vote(); From cdb5381b03622c39d0cfd3a62fdde802fa4bf5d5 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 29 Feb 2024 13:29:38 -0600 Subject: [PATCH 28/40] GH-2125 trx can become irreversible much quicker than in non-IF --- tests/trx_finality_status_forked_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/trx_finality_status_forked_test.py b/tests/trx_finality_status_forked_test.py index d501d1dd01..cef075b0fd 100755 --- a/tests/trx_finality_status_forked_test.py +++ b/tests/trx_finality_status_forked_test.py @@ -184,8 +184,8 @@ def getState(status): retStatus = prodD.getTransactionStatus(transId) state = getState(retStatus) - assert state == inBlockState, \ - f"ERROR: getTransactionStatus didn't return \"{inBlockState}\" state.\n\nstatus: {json.dumps(retStatus, indent=1)}" + \ + assert state == inBlockState or state == irreversibleState, \ + f"ERROR: getTransactionStatus didn't return \"{inBlockState}\" or \"{irreversibleState}\" state.\n\nstatus: {json.dumps(retStatus, indent=1)}" + \ f"\n\nprod A info: {json.dumps(prodA.getInfo(), indent=1)}\n\nprod D info: {json.dumps(prodD.getInfo(), indent=1)}" afterForkInBlockState = retStatus From f9fa56bdb65681370c491d2404a485417d41c4e3 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 29 Feb 2024 14:25:06 -0600 Subject: [PATCH 29/40] GH-2125 Improve test implementation --- tests/trx_finality_status_forked_test.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/trx_finality_status_forked_test.py b/tests/trx_finality_status_forked_test.py index cef075b0fd..820c65d38e 100755 --- a/tests/trx_finality_status_forked_test.py +++ b/tests/trx_finality_status_forked_test.py @@ -163,12 +163,11 @@ def getState(status): Print("Wait for LIB to move, which indicates prodD may have forked out the branch") assert prodD.waitForLibToAdvance(60), \ "ERROR: Network did not reach consensus after bridge node was restarted." - if prodD.getTransactionStatus(transId)['state'] == forkedOutState: + retStatus = prodD.getTransactionStatus(transId) + state = getState(retStatus) + if state == forkedOutState: break - retStatus = prodD.getTransactionStatus(transId) - state = getState(retStatus) - assert state == forkedOutState, \ f"ERROR: getTransactionStatus didn't return \"{forkedOutState}\" state.\n\nstatus: {json.dumps(retStatus, indent=1)}" + \ f"\n\nprod A info: {json.dumps(prodA.getInfo(), indent=1)}\n\nprod D info: {json.dumps(prodD.getInfo(), indent=1)}" From 7f18b5be2c7d8d1ba19ddc5931bd343d811101f6 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 29 Feb 2024 14:25:44 -0600 Subject: [PATCH 30/40] GH-2125 Used claimed final_on_strong_qc_block_num --- libraries/chain/controller.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 52d4c367f0..747ec2cb9a 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2807,7 +2807,10 @@ struct controller_impl { assert(bsp->header_exts.count(if_ext_id) > 0); // in all instant_finality block headers const auto& if_ext = std::get(bsp->header_exts.lower_bound(if_ext_id)->second); if (if_ext.qc_claim.is_strong_qc) { - set_if_irreversible_block_num(bsp->core.final_on_strong_qc_block_num); + auto claimed = forkdb.search_on_branch(forkdb.head()->id(), if_ext.qc_claim.block_num); + if (claimed) { + set_if_irreversible_block_num(claimed->core.final_on_strong_qc_block_num); + } } }); From c88d6e371ffca20e5f1fc002efc43929459d1ae4 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 29 Feb 2024 15:47:11 -0600 Subject: [PATCH 31/40] GH-2125 Add search_on_head_branch --- libraries/chain/controller.cpp | 8 ++++---- libraries/chain/fork_database.cpp | 17 +++++++++++++++++ .../chain/include/eosio/chain/fork_database.hpp | 5 +++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 747ec2cb9a..da3741ce3d 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1019,7 +1019,7 @@ struct controller_impl { signed_block_ptr fork_db_fetch_block_by_num(uint32_t block_num) const { return fork_db.apply([&](const auto& forkdb) { - auto bsp = forkdb.search_on_branch(forkdb.head()->id(), block_num); + auto bsp = forkdb.search_on_head_branch(block_num); if (bsp) return bsp->block; return signed_block_ptr{}; }); @@ -1027,7 +1027,7 @@ struct controller_impl { std::optional fork_db_fetch_block_id_by_num(uint32_t block_num) const { return fork_db.apply>([&](const auto& forkdb) -> std::optional { - auto bsp = forkdb.search_on_branch(forkdb.head()->id(), block_num); + auto bsp = forkdb.search_on_head_branch(block_num); if (bsp) return bsp->id(); return {}; }); @@ -1039,7 +1039,7 @@ struct controller_impl { overloaded{ [](const fork_database_legacy_t&) -> block_state_ptr { return nullptr; }, [&](const fork_database_if_t&forkdb) -> block_state_ptr { - auto bsp = forkdb.search_on_branch(forkdb.head()->id(), block_num); + auto bsp = forkdb.search_on_head_branch(block_num); return bsp; } } @@ -2807,7 +2807,7 @@ struct controller_impl { assert(bsp->header_exts.count(if_ext_id) > 0); // in all instant_finality block headers const auto& if_ext = std::get(bsp->header_exts.lower_bound(if_ext_id)->second); if (if_ext.qc_claim.is_strong_qc) { - auto claimed = forkdb.search_on_branch(forkdb.head()->id(), if_ext.qc_claim.block_num); + auto claimed = forkdb.search_on_head_branch(if_ext.qc_claim.block_num); if (claimed) { set_if_irreversible_block_num(claimed->core.final_on_strong_qc_block_num); } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index ae363d9b76..4401ba2482 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -110,6 +110,7 @@ namespace eosio::chain { block_branch_t fetch_block_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; full_branch_type fetch_full_branch_impl(const block_id_type& h) const; BSP search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; + BSP search_on_head_branch_impl( uint32_t block_num ) const; void mark_valid_impl( const BSP& h ); branch_type_pair fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; @@ -515,6 +516,22 @@ namespace eosio::chain { return {}; } + template + BSP fork_database_t::search_on_head_branch( uint32_t block_num ) const { + std::lock_guard g(my->mtx); + return my->search_on_head_branch_impl(block_num); + } + + template + BSP fork_database_impl::search_on_head_branch_impl( uint32_t block_num ) const { + for (auto i = index.find(head->id()); i != index.end(); i = index.find((*i)->previous())) { + if ((*i)->block_num() == block_num) + return *i; + } + + return {}; + } + /** * Given two head blocks, return two branches of the fork graph that * end with a common ancestor (same prior block) diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 3e04eacd65..9482a069ed 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -105,6 +105,11 @@ namespace eosio::chain { */ BSP search_on_branch( const block_id_type& h, uint32_t block_num ) const; + /** + * search_on_branch( head()->id(), block_num) + */ + BSP search_on_head_branch( uint32_t block_num ) const; + /** * Given two head blocks, return two branches of the fork graph that * end with a common ancestor (same prior block) From 14b04b04ae4cfd7e33e33ea9ce6286cef139ee48 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 1 Mar 2024 08:02:47 -0600 Subject: [PATCH 32/40] GH-2125 Normalize types --- libraries/chain/fork_database.cpp | 107 +++++++++--------- libraries/chain/hotstuff/finalizer.cpp | 6 +- .../chain/include/eosio/chain/block_state.hpp | 2 +- .../eosio/chain/block_state_legacy.hpp | 2 +- .../include/eosio/chain/fork_database.hpp | 40 +++---- .../eosio/chain/hotstuff/finalizer.hpp | 4 +- .../unapplied_transaction_queue_tests.cpp | 24 ++-- 7 files changed, 94 insertions(+), 91 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 4401ba2482..f3e90aada7 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -42,7 +42,7 @@ namespace eosio::chain { template void log_bs(const char* desc, fork_database_impl& fork_db, const BS& lhs) { - using BSA = BS::fork_db_block_state_accessor; + using BSA = BS::fork_db_block_state_accessor_t; dlog( "fork_db ${f}, ${d} ${bn}, last_final_block_num ${lfbn}, final_on_strong_qc_block_num ${fsbn}, lastest_qc_claim_block_num ${lbn}, block_height ${bh}, id ${id}", ("f", (uint64_t)(&fork_db))("d", desc)("bn", lhs.block_num())("lfbn", BSA::last_final_block_num(lhs))("fsbn", BSA::final_on_strong_qc_block_num(lhs))("lbn", BSA::lastest_qc_claim_block_num(lhs))("bh", BSA::block_height(lhs))("id", lhs.id()) ); } @@ -50,35 +50,35 @@ namespace eosio::chain { // match comparison of by_best_branch template bool first_preferred( const BS& lhs, const BS& rhs ) { - using BSA = BS::fork_db_block_state_accessor; + using BSA = BS::fork_db_block_state_accessor_t; return std::make_tuple(BSA::last_final_block_num(lhs), BSA::lastest_qc_claim_block_num(lhs), BSA::block_height(lhs)) > std::make_tuple(BSA::last_final_block_num(rhs), BSA::lastest_qc_claim_block_num(rhs), BSA::block_height(rhs)); } template // either [block_state_legacy_ptr, block_state_ptr], same with block_header_state_ptr struct fork_database_impl { - using BS = BSP::element_type; - using BSAccessor = BS::fork_db_block_state_accessor; - using BHSP = BS::bhsp_t; - using BHS = BHSP::element_type; + using bs_t = BSP::element_type; + using bs_accessor_t = bs_t::fork_db_block_state_accessor_t; + using bhsp_t = bs_t::bhsp_t; + using bhs_t = bhsp_t::element_type; - using fork_db_t = fork_database_t; - using branch_type = fork_db_t::branch_type; - using full_branch_type = fork_db_t::full_branch_type; - using branch_type_pair = fork_db_t::branch_type_pair; + using fork_db_t = fork_database_t; + using branch_t = fork_db_t::branch_t; + using full_branch_t = fork_db_t::full_branch_t; + using branch_pair_t = fork_db_t::branch_pair_t; using fork_multi_index_type = multi_index_container< BSP, indexed_by< - hashed_unique, BOOST_MULTI_INDEX_CONST_MEM_FUN(BS, const block_id_type&, id), std::hash>, - ordered_non_unique, const_mem_fun>, + hashed_unique, BOOST_MULTI_INDEX_CONST_MEM_FUN(bs_t, const block_id_type&, id), std::hash>, + ordered_non_unique, const_mem_fun>, ordered_unique, - composite_key, - global_fun, - global_fun, - global_fun, - const_mem_fun + composite_key, + global_fun, + global_fun, + global_fun, + const_mem_fun >, composite_key_compare, std::greater, std::greater, std::greater, @@ -100,19 +100,19 @@ namespace eosio::chain { void close_impl( const std::filesystem::path& fork_db_file ); void add_impl( const BSP& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate, bool validate, validator_t& validator ); - BHSP get_block_header_impl( const block_id_type& id ) const; + bhsp_t get_block_header_impl( const block_id_type& id ) const; BSP get_block_impl( const block_id_type& id ) const; - void reset_root_impl( const BHS& root_bhs ); + void reset_root_impl( const bhs_t& root_bhs ); void rollback_head_to_root_impl(); void advance_root_impl( const block_id_type& id ); void remove_impl( const block_id_type& id ); - branch_type fetch_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; + 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; - full_branch_type fetch_full_branch_impl(const block_id_type& h) const; + full_branch_t fetch_full_branch_impl(const block_id_type& h) const; BSP search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; BSP search_on_head_branch_impl( uint32_t block_num ) const; void mark_valid_impl( const BSP& h ); - branch_type_pair fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; + branch_pair_t fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; }; @@ -160,17 +160,17 @@ namespace eosio::chain { ("max", fork_database::max_supported_version) ); - BHS state; + bhs_t state; fc::raw::unpack( ds, state ); reset_root_impl( state ); unsigned_int size; fc::raw::unpack( ds, size ); for( uint32_t i = 0, n = size.value; i < n; ++i ) { - BS s; + bs_t s; fc::raw::unpack( ds, s ); // do not populate transaction_metadatas, they will be created as needed in apply_block with appropriate key recovery s.header_exts = s.block->validate_and_extract_header_extensions(); - add_impl( std::make_shared( std::move( s ) ), mark_valid_t::no, ignore_duplicate_t::no, true, validator ); + add_impl( std::make_shared( std::move( s ) ), mark_valid_t::no, ignore_duplicate_t::no, true, validator ); } block_id_type head_id; fc::raw::unpack( ds, head_id ); @@ -185,7 +185,7 @@ namespace eosio::chain { } auto candidate = index.template get().begin(); - if( candidate == index.template get().end() || !BSAccessor::is_valid(**candidate) ) { + if( candidate == index.template get().end() || !bs_accessor_t::is_valid(**candidate) ) { EOS_ASSERT( head->id() == root->id(), fork_database_exception, "head not set to root despite no better option available; '${filename}' is likely corrupted", ("filename", fork_db_file) ); @@ -219,7 +219,7 @@ namespace eosio::chain { std::ofstream out( fork_db_file.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc ); fc::raw::pack( out, magic_number ); fc::raw::pack( out, fork_database::max_supported_version ); // write out current version which is always max_supported_version - fc::raw::pack( out, *static_cast(&*root) ); + fc::raw::pack( out, *static_cast(&*root) ); uint32_t num_blocks_in_fork_db = index.size(); fc::raw::pack( out, unsigned_int{num_blocks_in_fork_db} ); @@ -269,17 +269,17 @@ namespace eosio::chain { } template - void fork_database_t::reset_root( const BHS& root_bhs ) { + void fork_database_t::reset_root( const bhs_t& root_bhs ) { std::lock_guard g( my->mtx ); my->reset_root_impl(root_bhs); } template - void fork_database_impl::reset_root_impl( const BHS& root_bhs ) { + void fork_database_impl::reset_root_impl( const bhs_t& root_bhs ) { index.clear(); - root = std::make_shared(); - static_cast(*root) = root_bhs; - BSAccessor::set_valid(*root, true); + root = std::make_shared(); + static_cast(*root) = root_bhs; + bs_accessor_t::set_valid(*root, true); head = root; } @@ -295,7 +295,7 @@ namespace eosio::chain { auto itr = by_id_idx.begin(); while (itr != by_id_idx.end()) { by_id_idx.modify( itr, []( auto& i ) { - BSAccessor::set_valid(*i, false); + bs_accessor_t::set_valid(*i, false); } ); ++itr; } @@ -315,7 +315,7 @@ namespace eosio::chain { auto new_root = get_block_impl( id ); EOS_ASSERT( new_root, fork_database_exception, "cannot advance root to a block that does not exist in the fork database" ); - EOS_ASSERT( BSAccessor::is_valid(*new_root), fork_database_exception, + EOS_ASSERT( bs_accessor_t::is_valid(*new_root), fork_database_exception, "cannot advance root to a block that has not yet been validated" ); @@ -344,13 +344,13 @@ namespace eosio::chain { } template - fork_database_t::BHSP fork_database_t::get_block_header( const block_id_type& id ) const { + fork_database_t::bhsp_t fork_database_t::get_block_header( const block_id_type& id ) const { std::lock_guard g( my->mtx ); return my->get_block_header_impl( id ); } template - fork_database_impl::BHSP fork_database_impl::get_block_header_impl( const block_id_type& id ) const { + fork_database_impl::bhsp_t fork_database_impl::get_block_header_impl( const block_id_type& id ) const { if( root->id() == id ) { return root; } @@ -359,7 +359,7 @@ namespace eosio::chain { if( itr != index.end() ) return *itr; - return BHSP(); + return bhsp_t(); } template @@ -385,7 +385,7 @@ namespace eosio::chain { } if (mark_valid == mark_valid_t::yes) - BSAccessor::set_valid(*n, true); + bs_accessor_t::set_valid(*n, true); auto inserted = index.insert(n); if( !inserted.second ) { @@ -394,7 +394,7 @@ namespace eosio::chain { } auto candidate = index.template get().begin(); - if( BSAccessor::is_valid(**candidate) ) { + if( bs_accessor_t::is_valid(**candidate) ) { head = *candidate; } } @@ -433,7 +433,7 @@ namespace eosio::chain { const auto& indx = my->index.template get(); auto itr = indx.lower_bound( false ); - if( itr != indx.end() && !fork_database_impl::BSAccessor::is_valid(**itr) ) { + if( itr != indx.end() && !fork_database_impl::bs_accessor_t::is_valid(**itr) ) { if( first_preferred( **itr, *my->head ) ) return *itr; } @@ -442,16 +442,16 @@ namespace eosio::chain { } template - fork_database_t::branch_type + fork_database_t::branch_t fork_database_t::fetch_branch(const block_id_type& h, uint32_t trim_after_block_num) const { std::lock_guard g(my->mtx); return my->fetch_branch_impl(h, trim_after_block_num); } template - fork_database_t::branch_type + fork_database_t::branch_t fork_database_impl::fetch_branch_impl(const block_id_type& h, uint32_t trim_after_block_num) const { - branch_type result; + branch_t result; result.reserve(index.size()); for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { if ((*i)->block_num() <= trim_after_block_num) @@ -482,16 +482,16 @@ namespace eosio::chain { } template - fork_database_t::full_branch_type + fork_database_t::full_branch_t fork_database_t::fetch_full_branch(const block_id_type& h) const { std::lock_guard g(my->mtx); return my->fetch_full_branch_impl(h); } template - fork_database_t::full_branch_type + fork_database_t::full_branch_t fork_database_impl::fetch_full_branch_impl(const block_id_type& h) const { - full_branch_type result; + full_branch_t result; result.reserve(index.size()); for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { result.push_back(*i); @@ -537,16 +537,16 @@ namespace eosio::chain { * end with a common ancestor (same prior block) */ template - fork_database_t::branch_type_pair + fork_database_t::branch_pair_t fork_database_t::fetch_branch_from(const block_id_type& first, const block_id_type& second) const { std::lock_guard g(my->mtx); return my->fetch_branch_from_impl(first, second); } template - fork_database_t::branch_type_pair + fork_database_t::branch_pair_t fork_database_impl::fetch_branch_from_impl(const block_id_type& first, const block_id_type& second) const { - pair result; + branch_pair_t result; auto first_branch = (first == root->id()) ? root : get_block_impl(first); auto second_branch = (second == root->id()) ? root : get_block_impl(second); @@ -640,7 +640,7 @@ namespace eosio::chain { template void fork_database_impl::mark_valid_impl( const BSP& h ) { - if( BSAccessor::is_valid(*h) ) return; + if( bs_accessor_t::is_valid(*h) ) return; auto& by_id_idx = index.template get(); @@ -650,7 +650,7 @@ namespace eosio::chain { ("id", h->id()) ); by_id_idx.modify( itr, []( auto& i ) { - BSAccessor::set_valid(*i, true); + bs_accessor_t::set_valid(*i, true); } ); auto candidate = index.template get().begin(); @@ -751,6 +751,9 @@ namespace eosio::chain { } // do class instantiations + template struct fork_comparison; + template struct fork_comparison; + template class fork_database_t; template class fork_database_t; diff --git a/libraries/chain/hotstuff/finalizer.cpp b/libraries/chain/hotstuff/finalizer.cpp index e0b80e7258..b20fbda074 100644 --- a/libraries/chain/hotstuff/finalizer.cpp +++ b/libraries/chain/hotstuff/finalizer.cpp @@ -5,7 +5,7 @@ namespace eosio::chain { // ---------------------------------------------------------------------------------------- -block_header_state_ptr get_block_by_num(const fork_database_if_t::full_branch_type& branch, std::optional block_num) { +block_header_state_ptr get_block_by_num(const fork_database_if_t::full_branch_t& branch, std::optional block_num) { if (!block_num || branch.empty()) return block_state_ptr{}; @@ -16,7 +16,7 @@ block_header_state_ptr get_block_by_num(const fork_database_if_t::full_branch_ty } // ---------------------------------------------------------------------------------------- -bool extends(const fork_database_if_t::full_branch_type& branch, const block_id_type& id) { +bool extends(const fork_database_if_t::full_branch_t& branch, const block_id_type& id) { return !branch.empty() && std::any_of(++branch.cbegin(), branch.cend(), [&](const auto& h) { return h->id() == id; }); } @@ -34,7 +34,7 @@ finalizer::vote_decision finalizer::decide_vote(const block_state_ptr& proposal, return vote_decision::no_vote; } - std::optional p_branch; // a branch that includes the root. + std::optional p_branch; // a branch that includes the root. if (!fsi.lock.empty()) { // Liveness check : check if the height of this proposal's justification is higher diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 3b65974a62..45e84fc453 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -67,7 +67,7 @@ struct block_state : public block_header_state { // block_header_state provi using bhs_t = block_header_state; using bhsp_t = block_header_state_ptr; - using fork_db_block_state_accessor = block_state_accessor; + using fork_db_block_state_accessor_t = block_state_accessor; block_state() = default; block_state(const block_state&) = delete; diff --git a/libraries/chain/include/eosio/chain/block_state_legacy.hpp b/libraries/chain/include/eosio/chain/block_state_legacy.hpp index d2e8743a74..91c4b47085 100644 --- a/libraries/chain/include/eosio/chain/block_state_legacy.hpp +++ b/libraries/chain/include/eosio/chain/block_state_legacy.hpp @@ -47,7 +47,7 @@ namespace eosio::chain { const deque& trxs_metas() const { return _cached_trxs; } - using fork_db_block_state_accessor = block_state_legacy_accessor; + using fork_db_block_state_accessor_t = block_state_legacy_accessor; private: // internal use only, not thread safe friend struct block_state_legacy_accessor; friend struct fc::reflector; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 9482a069ed..99c367b64a 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -31,13 +31,13 @@ namespace eosio::chain { static constexpr uint32_t legacy_magic_number = 0x30510FDB; static constexpr uint32_t magic_number = 0x4242FDB; - using BS = BSP::element_type; - using BHSP = BS::bhsp_t; - using BHS = BHSP::element_type; using bsp_t = BSP; - using branch_type = std::vector; - using full_branch_type = std::vector; - using branch_type_pair = pair; + using bs_t = bsp_t::element_type; + using bhsp_t = bs_t::bhsp_t; + using bhs_t = bhsp_t::element_type; + using branch_t = std::vector; + using full_branch_t = std::vector; + using branch_pair_t = pair; explicit fork_database_t(uint32_t magic_number = legacy_magic_number); ~fork_database_t(); @@ -45,14 +45,14 @@ namespace eosio::chain { void open( const std::filesystem::path& fork_db_file, validator_t& validator ); void close( const std::filesystem::path& fork_db_file ); - BHSP get_block_header( const block_id_type& id ) const; - BSP get_block( const block_id_type& id ) const; + bhsp_t get_block_header( const block_id_type& id ) const; + bsp_t get_block( const block_id_type& id ) const; /** * Purges any existing blocks from the fork database and resets the root block_header_state to the provided value. * The head will also be reset to point to the root. */ - void reset_root( const BHS& root_bhs ); + void reset_root( const bhs_t& root_bhs ); /** * Removes validated flag from all blocks in fork database and resets head to point to the root. @@ -69,17 +69,17 @@ namespace eosio::chain { * Must link to existing block in fork database or the root. * @param mark_valid if true also mark next_block valid */ - void add( const BSP& next_block, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate ); + void add( const bsp_t& next_block, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate ); void remove( const block_id_type& id ); bool has_root() const; - BSP root() const; // undefined if !has_root() - BSP head() const; - BSP pending_head() const; + bsp_t root() const; // undefined if !has_root() + bsp_t head() const; + bsp_t pending_head() const; // only accessed by main thread, no mutex protection - BSP chain_head; + bsp_t chain_head; /** * Returns the sequence of block states resulting from trimming the branch from the @@ -89,7 +89,7 @@ namespace eosio::chain { * The order of the sequence is in descending block number order. * A block with an id of `h` must exist in the fork database otherwise this method will throw an exception. */ - branch_type fetch_branch( const block_id_type& h, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; + 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; /** @@ -97,26 +97,26 @@ namespace eosio::chain { * The order of the sequence is in descending block number order. * A block with an id of `h` must exist in the fork database otherwise this method will throw an exception. */ - full_branch_type fetch_full_branch( const block_id_type& h ) const; + full_branch_t fetch_full_branch( const block_id_type& h ) const; /** * Returns the block state with a block number of `block_num` that is on the branch that * contains a block with an id of`h`, or the empty shared pointer if no such block can be found. */ - BSP search_on_branch( const block_id_type& h, uint32_t block_num ) const; + bsp_t search_on_branch( const block_id_type& h, uint32_t block_num ) const; /** * search_on_branch( head()->id(), block_num) */ - BSP search_on_head_branch( uint32_t block_num ) const; + bsp_t search_on_head_branch( uint32_t block_num ) const; /** * Given two head blocks, return two branches of the fork graph that * end with a common ancestor (same prior block) */ - branch_type_pair fetch_branch_from(const block_id_type& first, const block_id_type& second) const; + branch_pair_t fetch_branch_from(const block_id_type& first, const block_id_type& second) const; - void mark_valid( const BSP& h ); + void mark_valid( const bsp_t& h ); private: unique_ptr> my; diff --git a/libraries/chain/include/eosio/chain/hotstuff/finalizer.hpp b/libraries/chain/include/eosio/chain/hotstuff/finalizer.hpp index f730e004eb..d7750a99d9 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/finalizer.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/finalizer.hpp @@ -85,8 +85,8 @@ namespace eosio::chain { safety_information fsi; private: - using branch_type = fork_database_if_t::branch_type; - using full_branch_type = fork_database_if_t::full_branch_type; + using branch_t = fork_database_if_t::branch_t; + using full_branch_t = fork_database_if_t::full_branch_t; vote_decision decide_vote(const block_state_ptr& proposal, const fork_database_if_t& fork_db); public: diff --git a/unittests/unapplied_transaction_queue_tests.cpp b/unittests/unapplied_transaction_queue_tests.cpp index 6ab4d14dc2..bb4bbeb838 100644 --- a/unittests/unapplied_transaction_queue_tests.cpp +++ b/unittests/unapplied_transaction_queue_tests.cpp @@ -79,7 +79,7 @@ auto create_test_block_state( deque trx_metas ) { return bsp; } -using branch_type_legacy = fork_database_legacy_t::branch_type; +using branch_legacy_t = fork_database_legacy_t::branch_t; template void add_forked( unapplied_transaction_queue& queue, const BRANCH_TYPE& forked_branch ) { @@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { auto bs1 = create_test_block_state( { trx1, trx2 } ); auto bs2 = create_test_block_state( { trx3, trx4, trx5 } ); auto bs3 = create_test_block_state( { trx6 } ); - add_forked( q, branch_type_legacy{ bs3, bs2, bs1, bs1 } ); // bs1 duplicate ignored + add_forked( q, branch_legacy_t{ bs3, bs2, bs1, bs1 } ); // bs1 duplicate ignored BOOST_CHECK( q.size() == 6u ); BOOST_REQUIRE( next( q ) == trx1 ); BOOST_CHECK( q.size() == 5u ); @@ -170,9 +170,9 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { // fifo forked auto bs4 = create_test_block_state( { trx7 } ); - add_forked( q, branch_type_legacy{ bs1 } ); - add_forked( q, branch_type_legacy{ bs3, bs2 } ); - add_forked( q, branch_type_legacy{ bs4 } ); + add_forked( q, branch_legacy_t{ bs1 } ); + add_forked( q, branch_legacy_t{ bs3, bs2 } ); + add_forked( q, branch_legacy_t{ bs4 } ); BOOST_CHECK( q.size() == 7u ); BOOST_REQUIRE( next( q ) == trx1 ); BOOST_CHECK( q.size() == 6u ); @@ -204,10 +204,10 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { // fifo forked, multi forks auto bs5 = create_test_block_state( { trx11, trx12, trx13 } ); auto bs6 = create_test_block_state( { trx11, trx15 } ); - add_forked( q, branch_type_legacy{ bs3, bs2, bs1 } ); - add_forked( q, branch_type_legacy{ bs4 } ); - add_forked( q, branch_type_legacy{ bs3, bs2 } ); // dups ignored - add_forked( q, branch_type_legacy{ bs6, bs5 } ); + add_forked( q, branch_legacy_t{ bs3, bs2, bs1 } ); + add_forked( q, branch_legacy_t{ bs4 } ); + add_forked( q, branch_legacy_t{ bs3, bs2 } ); // dups ignored + add_forked( q, branch_legacy_t{ bs6, bs5 } ); BOOST_CHECK_EQUAL( q.size(), 11u ); BOOST_REQUIRE( next( q ) == trx1 ); BOOST_CHECK( q.size() == 10u ); @@ -235,10 +235,10 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { BOOST_CHECK( q.empty() ); // altogether, order fifo: forked, aborted - add_forked( q, branch_type_legacy{ bs3, bs2, bs1 } ); + add_forked( q, branch_legacy_t{ bs3, bs2, bs1 } ); q.add_aborted( { trx9, trx14 } ); q.add_aborted( { trx18, trx19 } ); - add_forked( q, branch_type_legacy{ bs6, bs5, bs4 } ); + add_forked( q, branch_legacy_t{ bs6, bs5, bs4 } ); // verify order verify_order( q, q.begin(), 15 ); // verify type order @@ -304,7 +304,7 @@ BOOST_AUTO_TEST_CASE( unapplied_transaction_queue_test ) try { BOOST_REQUIRE( next( q ) == trx22 ); BOOST_CHECK( q.empty() ); - add_forked( q, branch_type_legacy{ bs3, bs2, bs1 } ); + add_forked( q, branch_legacy_t{ bs3, bs2, bs1 } ); q.add_aborted( { trx9, trx11 } ); q.clear(); BOOST_CHECK( q.empty() ); From 93a7fee1f72bd49c5489eb27397e2f0248123d44 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 1 Mar 2024 08:03:22 -0600 Subject: [PATCH 33/40] GH-2125 Add fork_comparison for logging --- libraries/chain/controller.cpp | 5 +++-- libraries/chain/fork_database.cpp | 8 ++++++++ .../chain/include/eosio/chain/fork_database.hpp | 12 ++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index da3741ce3d..df4406e555 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3542,8 +3542,9 @@ struct controller_impl { throw; } } else if( new_head->id() != head->id() ) { - ilog("switching forks from ${current_head_id} (block number ${current_head_num}) to ${new_head_id} (block number ${new_head_num})", - ("current_head_id", head->id())("current_head_num", head_block_num())("new_head_id", new_head->id())("new_head_num", new_head->block_num()) ); + ilog("switching forks from ${current_head_id} (block number ${current_head_num}) ${c} to ${new_head_id} (block number ${new_head_num}) ${n}", + ("current_head_id", head->id())("current_head_num", head_block_num())("new_head_id", new_head->id())("new_head_num", new_head->block_num()) + ("c", fork_comparison{*head})("n", fork_comparison{*new_head})); // not possible to log transaction specific infor when switching forks if (auto dm_logger = get_deep_mind_logger(false)) { diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index f3e90aada7..45dd46ae1a 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -36,6 +36,14 @@ namespace eosio::chain { static uint32_t block_height(const block_state_legacy& bs) { return bs.block_num(); } }; + template + fork_comparison::fork_comparison(const BS& bs) + : valid(BS::fork_db_block_state_accessor_t::is_valid(bs)) + , last_final_block_num(BS::fork_db_block_state_accessor_t::last_final_block_num(bs)) + , lastest_qc_claim_block_num(BS::fork_db_block_state_accessor_t::lastest_qc_claim_block_num(bs)) + , block_height(BS::fork_db_block_state_accessor_t::block_height(bs)) + {} + struct by_block_id; struct by_best_branch; struct by_prev; diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 99c367b64a..b17f9e0552 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -11,6 +11,16 @@ namespace eosio::chain { enum class mark_valid_t { no, yes }; enum class ignore_duplicate_t { no, yes }; + // Used for logging of comparison values used for best fork determination + template + struct fork_comparison { + explicit fork_comparison(const BS& bs); + const bool valid; + const uint32_t last_final_block_num{}; + const uint32_t lastest_qc_claim_block_num{}; + const uint32_t block_height{}; + }; + /** * @class fork_database_t * @brief manages light-weight state for all potential unconfirmed forks @@ -241,3 +251,5 @@ namespace eosio::chain { static constexpr uint32_t max_supported_version = 1; }; } /// eosio::chain + +FC_REFLECT_TEMPLATE((typename BS), eosio::chain::fork_comparison, (valid)(last_final_block_num)(lastest_qc_claim_block_num)(block_height)) From 04dcfeba6cfed317e84a64ae3657dd80b4192a26 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 1 Mar 2024 08:30:06 -0600 Subject: [PATCH 34/40] GH-2125 Additional type normalization --- libraries/chain/fork_database.cpp | 35 ++++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 45dd46ae1a..fc918b545d 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -65,7 +65,8 @@ namespace eosio::chain { template // either [block_state_legacy_ptr, block_state_ptr], same with block_header_state_ptr struct fork_database_impl { - using bs_t = BSP::element_type; + using bsp_t = BSP; + using bs_t = bsp_t::element_type; using bs_accessor_t = bs_t::fork_db_block_state_accessor_t; using bhsp_t = bs_t::bhsp_t; using bhs_t = bhsp_t::element_type; @@ -76,7 +77,7 @@ namespace eosio::chain { using branch_pair_t = fork_db_t::branch_pair_t; using fork_multi_index_type = multi_index_container< - BSP, + bsp_t, indexed_by< hashed_unique, BOOST_MULTI_INDEX_CONST_MEM_FUN(bs_t, const block_id_type&, id), std::hash>, ordered_non_unique, const_mem_fun>, @@ -98,18 +99,18 @@ namespace eosio::chain { std::mutex mtx; fork_multi_index_type index; - BSP root; // Only uses the block_header_state portion of block_state - BSP head; + bsp_t root; // Only uses the block_header_state portion of block_state + bsp_t head; const uint32_t magic_number; explicit fork_database_impl(uint32_t magic_number) : magic_number(magic_number) {} void open_impl( const std::filesystem::path& fork_db_file, validator_t& validator ); void close_impl( const std::filesystem::path& fork_db_file ); - void add_impl( const BSP& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate, bool validate, validator_t& validator ); + void add_impl( const bsp_t& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate, bool validate, validator_t& validator ); bhsp_t get_block_header_impl( const block_id_type& id ) const; - BSP get_block_impl( const block_id_type& id ) const; + bsp_t get_block_impl( const block_id_type& id ) const; void reset_root_impl( const bhs_t& root_bhs ); void rollback_head_to_root_impl(); void advance_root_impl( const block_id_type& id ); @@ -117,9 +118,9 @@ namespace eosio::chain { 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; full_branch_t fetch_full_branch_impl(const block_id_type& h) const; - BSP search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; - BSP search_on_head_branch_impl( uint32_t block_num ) const; - void mark_valid_impl( const BSP& h ); + bsp_t search_on_branch_impl( const block_id_type& h, uint32_t block_num ) const; + bsp_t search_on_head_branch_impl( uint32_t block_num ) const; + void mark_valid_impl( const bsp_t& h ); branch_pair_t fetch_branch_from_impl( const block_id_type& first, const block_id_type& second ) const; }; @@ -371,7 +372,7 @@ namespace eosio::chain { } template - void fork_database_impl::add_impl(const BSP& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate, bool validate, validator_t& validator) { + void fork_database_impl::add_impl(const bsp_t& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate, bool validate, validator_t& validator) { EOS_ASSERT( root, fork_database_exception, "root not yet set" ); EOS_ASSERT( n, fork_database_exception, "attempt to add null block state" ); @@ -408,7 +409,7 @@ namespace eosio::chain { } template - void fork_database_t::add( const BSP& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate ) { + void fork_database_t::add( const bsp_t& n, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate ) { std::lock_guard g( my->mtx ); my->add_impl( n, mark_valid, ignore_duplicate, false, []( block_timestamp_type timestamp, @@ -489,16 +490,16 @@ namespace eosio::chain { return result; } - template - fork_database_t::full_branch_t - fork_database_t::fetch_full_branch(const block_id_type& h) const { + template + fork_database_t::full_branch_t + fork_database_t::fetch_full_branch(const block_id_type& h) const { std::lock_guard g(my->mtx); return my->fetch_full_branch_impl(h); } - template - fork_database_t::full_branch_t - fork_database_impl::fetch_full_branch_impl(const block_id_type& h) const { + template + fork_database_t::full_branch_t + fork_database_impl::fetch_full_branch_impl(const block_id_type& h) const { full_branch_t result; result.reserve(index.size()); for (auto i = index.find(h); i != index.end(); i = index.find((*i)->previous())) { From fab0bbed66add1230eab8bcea3c5f175ed196abe Mon Sep 17 00:00:00 2001 From: greg7mdp Date: Fri, 1 Mar 2024 09:57:48 -0500 Subject: [PATCH 35/40] Use separate `by_best_branch` index definitions. --- libraries/chain/fork_database.cpp | 42 +++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index fc918b545d..465e5f0165 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -76,24 +76,40 @@ namespace eosio::chain { using full_branch_t = fork_db_t::full_branch_t; using branch_pair_t = fork_db_t::branch_pair_t; + using by_best_branch_legacy_t = + ordered_unique, + composite_key, + const_mem_fun, + const_mem_fun, + const_mem_fun + >, + composite_key_compare, std::greater, std::greater, sha256_less + >>; + + using by_best_branch_if_t = + ordered_unique, + composite_key, + const_mem_fun, + const_mem_fun, + const_mem_fun, + const_mem_fun + >, + composite_key_compare, std::greater, std::greater, + std::greater, sha256_less> + >; + + using by_best_branch_t = std::conditional_t, + by_best_branch_if_t, + by_best_branch_legacy_t>; + using fork_multi_index_type = multi_index_container< bsp_t, indexed_by< hashed_unique, BOOST_MULTI_INDEX_CONST_MEM_FUN(bs_t, const block_id_type&, id), std::hash>, ordered_non_unique, const_mem_fun>, - ordered_unique, - composite_key, - global_fun, - global_fun, - global_fun, - const_mem_fun - >, - composite_key_compare, - std::greater, std::greater, std::greater, - sha256_less - > - > + by_best_branch_t > >; From 31da03fe224807094985b7767bf20aaa5bd4985f Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 1 Mar 2024 10:20:55 -0600 Subject: [PATCH 36/40] GH-2125 Remove unneeded bs acccessor methods. Use log_fork_comparison instead of fork_comparison struct. --- libraries/chain/controller.cpp | 2 +- libraries/chain/fork_database.cpp | 54 +++++++++---------- .../chain/include/eosio/chain/block_state.hpp | 3 +- .../include/eosio/chain/fork_database.hpp | 12 +---- 4 files changed, 30 insertions(+), 41 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index df4406e555..f968c838cb 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3544,7 +3544,7 @@ struct controller_impl { } else if( new_head->id() != head->id() ) { ilog("switching forks from ${current_head_id} (block number ${current_head_num}) ${c} to ${new_head_id} (block number ${new_head_num}) ${n}", ("current_head_id", head->id())("current_head_num", head_block_num())("new_head_id", new_head->id())("new_head_num", new_head->block_num()) - ("c", fork_comparison{*head})("n", fork_comparison{*new_head})); + ("c", log_fork_comparison(*head))("n", log_fork_comparison(*new_head))); // not possible to log transaction specific infor when switching forks if (auto dm_logger = get_deep_mind_logger(false)) { diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 465e5f0165..50d945c2d8 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -23,44 +23,43 @@ namespace eosio::chain { struct block_state_accessor { static bool is_valid(const block_state& bs) { return bs.is_valid(); } static void set_valid(block_state& bs, bool v) { bs.validated = v; } - static uint32_t last_final_block_num(const block_state& bs) { return bs.core.last_final_block_num(); } - static uint32_t lastest_qc_claim_block_num(const block_state& bs) { return bs.core.latest_qc_claim().block_num; } - static uint32_t block_height(const block_state& bs) { return bs.timestamp().slot; } }; struct block_state_legacy_accessor { static bool is_valid(const block_state_legacy& bs) { return bs.is_valid(); } static void set_valid(block_state_legacy& bs, bool v) { bs.validated = v; } - static uint32_t last_final_block_num(const block_state_legacy& bs) { return bs.irreversible_blocknum(); } - static uint32_t lastest_qc_claim_block_num(const block_state_legacy& bs) { return bs.irreversible_blocknum(); } - static uint32_t block_height(const block_state_legacy& bs) { return bs.block_num(); } }; - template - fork_comparison::fork_comparison(const BS& bs) - : valid(BS::fork_db_block_state_accessor_t::is_valid(bs)) - , last_final_block_num(BS::fork_db_block_state_accessor_t::last_final_block_num(bs)) - , lastest_qc_claim_block_num(BS::fork_db_block_state_accessor_t::lastest_qc_claim_block_num(bs)) - , block_height(BS::fork_db_block_state_accessor_t::block_height(bs)) - {} + std::string log_fork_comparison(const block_state& bs) { + std::string r; + r += "[ valid: " + std::to_string(block_state_accessor::is_valid(bs)) + ", "; + r += "last_final_block_num: " + std::to_string(bs.last_final_block_num()) + ", "; + r += "last_qc_block_num: " + std::to_string(bs.last_qc_block_num()) + ", "; + r += "timestamp: " + bs.timestamp().to_time_point().to_iso_string() + " ]"; + return r; + } + + std::string log_fork_comparison(const block_state_legacy& bs) { + std::string r; + r += "[ valid: " + std::to_string(block_state_legacy_accessor::is_valid(bs)) + ", "; + r += "irreversible_blocknum: " + std::to_string(bs.irreversible_blocknum()) + ", "; + r += "block_num: " + std::to_string(bs.block_num()) + ", "; + r += "timestamp: " + bs.timestamp().to_time_point().to_iso_string() + " ]"; + return r; + } struct by_block_id; struct by_best_branch; struct by_prev; - template - void log_bs(const char* desc, fork_database_impl& fork_db, const BS& lhs) { - using BSA = BS::fork_db_block_state_accessor_t; - dlog( "fork_db ${f}, ${d} ${bn}, last_final_block_num ${lfbn}, final_on_strong_qc_block_num ${fsbn}, lastest_qc_claim_block_num ${lbn}, block_height ${bh}, id ${id}", - ("f", (uint64_t)(&fork_db))("d", desc)("bn", lhs.block_num())("lfbn", BSA::last_final_block_num(lhs))("fsbn", BSA::final_on_strong_qc_block_num(lhs))("lbn", BSA::lastest_qc_claim_block_num(lhs))("bh", BSA::block_height(lhs))("id", lhs.id()) ); - } - // match comparison of by_best_branch - template - bool first_preferred( const BS& lhs, const BS& rhs ) { - using BSA = BS::fork_db_block_state_accessor_t; - return std::make_tuple(BSA::last_final_block_num(lhs), BSA::lastest_qc_claim_block_num(lhs), BSA::block_height(lhs)) > - std::make_tuple(BSA::last_final_block_num(rhs), BSA::lastest_qc_claim_block_num(rhs), BSA::block_height(rhs)); + bool first_preferred( const block_state& lhs, const block_state& rhs ) { + return std::make_tuple(lhs.last_final_block_num(), lhs.last_qc_block_num(), lhs.timestamp()) > + std::make_tuple(rhs.last_final_block_num(), rhs.last_qc_block_num(), rhs.timestamp()); + } + bool first_preferred( const block_state_legacy& lhs, const block_state_legacy& rhs ) { + return std::make_tuple(lhs.irreversible_blocknum(), lhs.block_num()) > + std::make_tuple(rhs.irreversible_blocknum(), rhs.block_num()); } template // either [block_state_legacy_ptr, block_state_ptr], same with block_header_state_ptr @@ -91,7 +90,7 @@ namespace eosio::chain { ordered_unique, composite_key, - const_mem_fun, + const_mem_fun, const_mem_fun, const_mem_fun, const_mem_fun @@ -776,9 +775,6 @@ namespace eosio::chain { } // do class instantiations - template struct fork_comparison; - template struct fork_comparison; - template class fork_database_t; template class fork_database_t; diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index 45e84fc453..16f0150cb5 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -56,7 +56,8 @@ struct block_state : public block_header_state { // block_header_state provi uint32_t block_num() const { return block_header_state::block_num(); } block_timestamp_type timestamp() const { return block_header_state::timestamp(); } const extensions_type& header_extensions() const { return block_header_state::header.header_extensions; } - uint32_t irreversible_blocknum() const { return core.last_final_block_num(); } + uint32_t irreversible_blocknum() const { return core.last_final_block_num(); } // backwards compatibility + uint32_t last_final_block_num() const { return core.last_final_block_num(); } std::optional get_best_qc() const; uint32_t last_qc_block_num() const { return core.latest_qc_claim().block_num; } uint32_t final_on_strong_qc_block_num() const { return core.final_on_strong_qc_block_num; } diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index b17f9e0552..e0983f9153 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -12,14 +12,8 @@ namespace eosio::chain { enum class ignore_duplicate_t { no, yes }; // Used for logging of comparison values used for best fork determination - template - struct fork_comparison { - explicit fork_comparison(const BS& bs); - const bool valid; - const uint32_t last_final_block_num{}; - const uint32_t lastest_qc_claim_block_num{}; - const uint32_t block_height{}; - }; + std::string log_fork_comparison(const block_state& bs); + std::string log_fork_comparison(const block_state_legacy& bs); /** * @class fork_database_t @@ -251,5 +245,3 @@ namespace eosio::chain { static constexpr uint32_t max_supported_version = 1; }; } /// eosio::chain - -FC_REFLECT_TEMPLATE((typename BS), eosio::chain::fork_comparison, (valid)(last_final_block_num)(lastest_qc_claim_block_num)(block_height)) From bc827ec4dac884418a6f7ccedfb7be0873899c23 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 1 Mar 2024 10:57:07 -0600 Subject: [PATCH 37/40] GH-2125 Rename to make more descriptive --- libraries/chain/controller.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index f968c838cb..1839e16913 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1017,7 +1017,7 @@ struct controller_impl { }); } - signed_block_ptr fork_db_fetch_block_by_num(uint32_t block_num) const { + signed_block_ptr fetch_block_on_head_branch_by_num(uint32_t block_num) const { return fork_db.apply([&](const auto& forkdb) { auto bsp = forkdb.search_on_head_branch(block_num); if (bsp) return bsp->block; @@ -1025,7 +1025,7 @@ struct controller_impl { }); } - std::optional fork_db_fetch_block_id_by_num(uint32_t block_num) const { + std::optional fetch_block_id_on_head_branch_by_num(uint32_t block_num) const { return fork_db.apply>([&](const auto& forkdb) -> std::optional { auto bsp = forkdb.search_on_head_branch(block_num); if (bsp) return bsp->id(); @@ -1034,7 +1034,7 @@ struct controller_impl { } // search on the branch of head - block_state_ptr fork_db_fetch_bsp_by_num(uint32_t block_num) const { + block_state_ptr fetch_bsp_on_head_branch_by_num(uint32_t block_num) const { return fork_db.apply( overloaded{ [](const fork_database_legacy_t&) -> block_state_ptr { return nullptr; }, @@ -1047,7 +1047,7 @@ struct controller_impl { } // search on the branch of given id - block_state_ptr fork_db_fetch_bsp_by_num(const block_id_type& id, uint32_t block_num) const { + block_state_ptr fetch_bsp_on_branch_by_num(const block_id_type& id, uint32_t block_num) const { return fork_db.apply( overloaded{ [](const fork_database_legacy_t&) -> block_state_ptr { return nullptr; }, @@ -3171,7 +3171,7 @@ struct controller_impl { const auto& qc_ext = std::get(block_exts.lower_bound(qc_ext_id)->second); const auto& received_qc = qc_ext.qc.qc; - const auto bsp = fork_db_fetch_bsp_by_num( bsp_in->previous(), qc_ext.qc.block_num ); + const auto bsp = fetch_bsp_on_branch_by_num( bsp_in->previous(), qc_ext.qc.block_num ); if( !bsp ) { return; } @@ -3301,7 +3301,7 @@ struct controller_impl { ("s1", qc_proof.qc.is_strong())("s2", new_qc_claim.is_strong_qc)("b", block_num) ); // find the claimed block's block state on branch of id - auto bsp = fork_db_fetch_bsp_by_num( prev.id(), new_qc_claim.block_num ); + auto bsp = fetch_bsp_on_branch_by_num( prev.id(), new_qc_claim.block_num ); EOS_ASSERT( bsp, invalid_qc_claim, "Block state was not found in forkdb for block_num ${q}. Block number: ${b}", @@ -4510,7 +4510,7 @@ std::optional controller::fetch_block_header_by_id( const b } signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { try { - auto b = my->fork_db_fetch_block_by_num( block_num ); + auto b = my->fetch_block_on_head_branch_by_num( block_num ); if (b) return b; @@ -4518,7 +4518,7 @@ signed_block_ptr controller::fetch_block_by_number( uint32_t block_num )const { } FC_CAPTURE_AND_RETHROW( (block_num) ) } std::optional controller::fetch_block_header_by_number( uint32_t block_num )const { try { - auto b = my->fork_db_fetch_block_by_num(block_num); + auto b = my->fetch_block_on_head_branch_by_num(block_num); if (b) return *b; @@ -4532,7 +4532,7 @@ block_id_type controller::get_block_id_for_num( uint32_t block_num )const { try bool find_in_blog = (blog_head && block_num <= blog_head->block_num()); if( !find_in_blog ) { - std::optional id = my->fork_db_fetch_block_id_by_num(block_num); + std::optional id = my->fetch_block_id_on_head_branch_by_num(block_num); if (id) return *id; } From ee5291fcc4aee8e20c922278fa5bda7e0b101ddc Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 1 Mar 2024 10:57:28 -0600 Subject: [PATCH 38/40] GH-2125 Search on correct branch --- libraries/chain/controller.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 1839e16913..ca09fbfc39 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2807,7 +2807,8 @@ struct controller_impl { assert(bsp->header_exts.count(if_ext_id) > 0); // in all instant_finality block headers const auto& if_ext = std::get(bsp->header_exts.lower_bound(if_ext_id)->second); if (if_ext.qc_claim.is_strong_qc) { - auto claimed = forkdb.search_on_head_branch(if_ext.qc_claim.block_num); + // claim has already been verified + auto claimed = forkdb.search_branch(bsp->id(), if_ext.qc_claim.block_num); if (claimed) { set_if_irreversible_block_num(claimed->core.final_on_strong_qc_block_num); } From e3059bf3f389a28727006f98778b865dd444a7b2 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 1 Mar 2024 11:02:51 -0600 Subject: [PATCH 39/40] GH-2125 Fix compile error and some more type name changes --- libraries/chain/controller.cpp | 2 +- libraries/chain/fork_database.cpp | 4 ++-- libraries/chain/include/eosio/chain/fork_database.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index ca09fbfc39..dac27d7136 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -2808,7 +2808,7 @@ struct controller_impl { const auto& if_ext = std::get(bsp->header_exts.lower_bound(if_ext_id)->second); if (if_ext.qc_claim.is_strong_qc) { // claim has already been verified - auto claimed = forkdb.search_branch(bsp->id(), if_ext.qc_claim.block_num); + auto claimed = forkdb.search_on_branch(bsp->id(), if_ext.qc_claim.block_num); if (claimed) { set_if_irreversible_block_num(claimed->core.final_on_strong_qc_block_num); } diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 50d945c2d8..40fdf62dcb 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -657,13 +657,13 @@ namespace eosio::chain { } template - void fork_database_t::mark_valid( const BSP& h ) { + void fork_database_t::mark_valid( const bsp_t& h ) { std::lock_guard g( my->mtx ); my->mark_valid_impl( h ); } template - void fork_database_impl::mark_valid_impl( const BSP& h ) { + void fork_database_impl::mark_valid_impl( const bsp_t& h ) { if( bs_accessor_t::is_valid(*h) ) return; auto& by_id_idx = index.template get(); diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index e0983f9153..dfe75e3e33 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -4,7 +4,7 @@ namespace eosio::chain { - template + template struct fork_database_impl; using block_branch_t = std::vector; From 2a2a5da58099fb5701d1b746b4f257fec7b21b9e Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Fri, 1 Mar 2024 12:34:21 -0600 Subject: [PATCH 40/40] GH-2125 Simplify search_on_head_branch_impl and add new test cases --- libraries/chain/fork_database.cpp | 7 +------ unittests/fork_db_tests.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 40fdf62dcb..9fce0f19fb 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -548,12 +548,7 @@ namespace eosio::chain { template BSP fork_database_impl::search_on_head_branch_impl( uint32_t block_num ) const { - for (auto i = index.find(head->id()); i != index.end(); i = index.find((*i)->previous())) { - if ((*i)->block_num() == block_num) - return *i; - } - - return {}; + return search_on_branch_impl(head->id(), block_num); } /** diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp index e2d7432030..655b3e9544 100644 --- a/unittests/fork_db_tests.cpp +++ b/unittests/fork_db_tests.cpp @@ -102,6 +102,21 @@ BOOST_AUTO_TEST_CASE(add_remove_test) try { forkdb.add(bsp13b, mark_valid_t::no, ignore_duplicate_t::no); // will throw if already exists forkdb.add(bsp14b, mark_valid_t::no, ignore_duplicate_t::no); // will throw if already exists + // test search + BOOST_TEST(forkdb.search_on_branch( bsp13bb->id(), 11) == bsp11b); + BOOST_TEST(forkdb.search_on_branch( bsp13bb->id(), 9) == block_state_ptr{}); + + // test fetch branch + auto branch = forkdb.fetch_branch( bsp13b->id(), 12); + BOOST_REQUIRE(branch.size() == 2); + BOOST_TEST(branch[0] == bsp12b); + BOOST_TEST(branch[1] == bsp11b); + branch = forkdb.fetch_branch( bsp13bbb->id(), 13); + BOOST_REQUIRE(branch.size() == 3); + BOOST_TEST(branch[0] == bsp13bbb); + BOOST_TEST(branch[1] == bsp12bb); + BOOST_TEST(branch[2] == bsp11b); + } FC_LOG_AND_RETHROW(); BOOST_AUTO_TEST_SUITE_END()