From b8a772c57250ea0000f880959b6eca898d811d4f Mon Sep 17 00:00:00 2001 From: Kevin Heifner Date: Tue, 19 Mar 2024 10:27:23 -0500 Subject: [PATCH] GH-2057 Fix race condition on switch_from_legacy. Fix startup in savanna. --- libraries/chain/controller.cpp | 19 ++++--------------- libraries/chain/fork_database.cpp | 13 +++++++++++-- .../include/eosio/chain/fork_database.hpp | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index 2e8bd796fe..76887f44be 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -1254,7 +1254,6 @@ struct controller_impl { void transition_to_savanna() { assert(chain_head.header().contains_header_extension(instant_finality_extension::extension_id())); - fork_db.switch_from_legacy(); // create savanna forkdb if not already created // copy head branch branch from legacy forkdb legacy to savanna forkdb fork_database_legacy_t::branch_t legacy_branch; block_state_legacy_ptr legacy_root; @@ -1266,13 +1265,9 @@ struct controller_impl { assert(!!legacy_root); assert(read_mode == db_read_mode::IRREVERSIBLE || !legacy_branch.empty()); ilog("Transitioning to savanna, IF Genesis Block ${gb}, IF Critical Block ${cb}", ("gb", legacy_root->block_num())("cb", chain_head.block_num())); + auto new_root = block_state::create_if_genesis_block(*legacy_root); + fork_db.switch_from_legacy(new_root); fork_db.apply_s([&](auto& forkdb) { - if (!forkdb.root()) { - auto new_root = block_state::create_if_genesis_block(*legacy_root); - forkdb.reset_root(new_root); - } else { - assert(forkdb.root()->id() == legacy_root->id()); - } block_state_ptr prev = forkdb.root(); for (auto bitr = legacy_branch.rbegin(); bitr != legacy_branch.rend(); ++bitr) { const bool skip_validate_signee = true; // validated already @@ -1545,10 +1540,7 @@ struct controller_impl { if (fork_db.version_in_use() == fork_database::in_use_t::legacy) { // switch to savanna if needed apply_s(chain_head, [&](const auto& head) { - fork_db.switch_from_legacy(); - fork_db.apply_s([&](auto& forkdb) { - forkdb.reset_root(head); - }); + fork_db.switch_from_legacy(head); }); } }; @@ -1653,10 +1645,7 @@ struct controller_impl { if (block_states.second && head->header.contains_header_extension(instant_finality_extension::extension_id())) { // snapshot generated in transition to savanna if (fork_db.version_in_use() == fork_database::in_use_t::legacy) { - fork_db.switch_from_legacy(); - fork_db.apply_s([&](auto& forkdb) { - forkdb.reset_root(block_states.second); - }); + fork_db.switch_from_legacy(block_states.second); } } }); diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 137c02edda..e6ecf7761b 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -787,11 +787,20 @@ namespace eosio::chain { } // only called from the main thread - void fork_database::switch_from_legacy() { + void fork_database::switch_from_legacy(const block_state_ptr& root) { // no need to close fork_db because we don't want to write anything out, file is removed on open // threads may be accessing (or locked on mutex about to access legacy forkdb) so don't delete it until program exit if (in_use == in_use_t::legacy) { - in_use = in_use_t::both; + fork_db_s.reset_root(root); + if (fork_db_l.has_root()) { + in_use = in_use_t::both; + } else { + in_use = in_use_t::savanna; + } + } else if (in_use == in_use_t::both) { + assert(fork_db_s.root()->id() == root->id()); // should always set the same root + } else { + assert(false); } } diff --git a/libraries/chain/include/eosio/chain/fork_database.hpp b/libraries/chain/include/eosio/chain/fork_database.hpp index 18c8c410ed..fb79116e65 100644 --- a/libraries/chain/include/eosio/chain/fork_database.hpp +++ b/libraries/chain/include/eosio/chain/fork_database.hpp @@ -155,7 +155,7 @@ namespace eosio::chain { void close(); // switches to using both legacy and savanna during transition - void switch_from_legacy(); + void switch_from_legacy(const block_state_ptr& root); in_use_t version_in_use() const { return in_use.load(); }