From f5b58d8701072b8ada69fa4d703b067d1e4d4621 Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Thu, 18 Apr 2024 12:07:01 -0500 Subject: [PATCH] GH-46 Do not advance LIB past HEAD. It is possible that HEAD is not on the branch that contains the LIB block. If on a fork, then only advance to our HEAD and only if on the same branch as LIB block. --- libraries/chain/controller.cpp | 11 ++++---- libraries/chain/fork_database.cpp | 27 ++++++++++++++++++- .../include/eosio/chain/fork_database.hpp | 5 ++++ unittests/fork_db_tests.cpp | 11 ++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 2540e65318..801269bad4 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1398,17 +1398,18 @@ struct controller_impl { ("lib_num", lib_num)("bn", fork_db_root_block_num()) ); } - block_id_type irreversible_block_id = if_irreversible_block_id.load(); - uint32_t if_lib_num = block_header::num_from_id(irreversible_block_id); - const uint32_t new_lib_num = if_lib_num > 0 ? if_lib_num : fork_db_head_irreversible_blocknum(); + const block_id_type irreversible_block_id = if_irreversible_block_id.load(); + const uint32_t savanna_lib_num = block_header::num_from_id(irreversible_block_id); + const bool savanna = savanna_lib_num > 0; + const uint32_t new_lib_num = savanna ? savanna_lib_num : fork_db_head_irreversible_blocknum(); if( new_lib_num <= lib_num ) return; bool savanna_transistion_required = false; auto mark_branch_irreversible = [&, this](auto& forkdb) { - auto branch = (if_lib_num > 0) ? forkdb.fetch_branch( irreversible_block_id, new_lib_num) - : forkdb.fetch_branch( fork_db_head_or_pending(forkdb)->id(), new_lib_num ); + auto branch = savanna ? forkdb.fetch_head_branch( irreversible_block_id, new_lib_num) + : forkdb.fetch_branch( fork_db_head_or_pending(forkdb)->id(), new_lib_num ); try { auto should_process = [&](auto& bsp) { // Only make irreversible blocks that have been validated. Blocks in the fork database may not be on our current best head diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 6ec9d9034d..259c05b893 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -135,6 +135,7 @@ namespace eosio::chain { void remove_impl( const block_id_type& id ); branch_t fetch_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; block_branch_t fetch_block_branch_impl( const block_id_type& h, uint32_t trim_after_block_num ) const; + branch_t fetch_head_branch_impl( const block_id_type& b, uint32_t trim_after_block_num ) const; full_branch_t fetch_full_branch_impl(const block_id_type& h) const; bsp_t search_on_branch_impl( const block_id_type& h, uint32_t block_num, include_root_t include_root ) const; bsp_t search_on_head_branch_impl( uint32_t block_num, include_root_t include_root ) const; @@ -438,6 +439,30 @@ namespace eosio::chain { return result; } + template + fork_database_t::branch_t + fork_database_t::fetch_head_branch(const block_id_type& b, uint32_t trim_after_block_num) const { + std::lock_guard g(my->mtx); + return my->fetch_head_branch_impl(b, trim_after_block_num); + } + + template + fork_database_t::branch_t + fork_database_impl::fetch_head_branch_impl(const block_id_type& b, uint32_t trim_after_block_num) const { + branch_t result; + if (!head) + return result; + result.reserve(index.size()); + bool found_branch = false; + for (auto i = index.find(head->id()); i != index.end(); i = index.find((*i)->previous())) { + if ((*i)->id() == b) + found_branch = true; + if (found_branch && (*i)->block_num() <= trim_after_block_num) + result.push_back(*i); + } + return result; + } + template block_branch_t fork_database_t::fetch_block_branch(const block_id_type& h, uint32_t trim_after_block_num) const { @@ -594,7 +619,7 @@ namespace eosio::chain { for( uint32_t i = 0; i < remove_queue.size(); ++i ) { EOS_ASSERT( remove_queue[i] != head_id, fork_database_exception, - "removing the block and its descendants would remove the current head block" ); + "removing the block and its descendants would remove the current head block ${id}", ("id", head_id) ); auto previtr = previdx.lower_bound( remove_queue[i] ); while( previtr != previdx.end() && (*previtr)->previous() == remove_queue[i] ) { diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 39d37cef00..d6273e6e9b 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -96,6 +96,11 @@ namespace eosio::chain { branch_t fetch_branch( const block_id_type& h, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; block_branch_t fetch_block_branch( const block_id_type& h, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; + /** + * Similar to fetch_branch but only returns up to head or empty if b not on head branch. + */ + branch_t fetch_head_branch( const block_id_type& b, uint32_t trim_after_block_num = std::numeric_limits::max() ) const; + /** * Returns full branch of block_header_state pointers including the root. * The order of the sequence is in descending block number order. diff --git a/unittests/fork_db_tests.cpp b/unittests/fork_db_tests.cpp index 05d5bc8ebe..38d0709881 100644 --- a/unittests/fork_db_tests.cpp +++ b/unittests/fork_db_tests.cpp @@ -117,6 +117,17 @@ BOOST_AUTO_TEST_CASE(add_remove_test) try { BOOST_TEST(branch[1] == bsp12bb); BOOST_TEST(branch[2] == bsp11b); + // test fetch head branch + BOOST_TEST(forkdb.head()->id() == root->id()); + forkdb.mark_valid(bsp13a); + BOOST_TEST(forkdb.head()->id() == bsp13a->id()); + branch = forkdb.fetch_head_branch(bsp11c->id()); + BOOST_TEST(branch.empty()); // bsp11c not on bsp13a branch + branch = forkdb.fetch_head_branch(bsp12a->id()); + BOOST_REQUIRE(branch.size() == 2); + BOOST_TEST(branch[0] == bsp12a); + BOOST_TEST(branch[1] == bsp11a); + } FC_LOG_AND_RETHROW(); BOOST_AUTO_TEST_SUITE_END()