Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
752// Test that simulating a shutdown (resetting ChainstateManager) and then performing753// chainstate reinitializing successfully reloads both chainstates.754ChainstateManager& chainman_restarted = this->SimulateNodeRestart();756BOOST_TEST_MESSAGE("Performing Load/Verify/Activate of chainstate");758// This call reinitializes the chainstates.759this->LoadVerifyActivateChainstate();761{762LOCK(chainman_restarted.GetMutex());763BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 2);764// Background chainstate has height of 109 not 110 here due to a quirk765// of the LoadVerifyActivate only calling ActivateBestChain on one766// chainstate. The height would be 110 after a real restart, but it's767// fine for this test which is focused on the snapshot chainstate.768BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[0]->m_chain.Height(), 109);769BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[1]->m_chain.Height(), 210);771BOOST_CHECK(chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash);772BOOST_CHECK(chainman_restarted.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED);774BOOST_CHECK_EQUAL(chainman_restarted.ActiveTip()->GetBlockHash(), snapshot_tip_hash);775BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210);776BOOST_CHECK_EQUAL(chainman_restarted.HistoricalChainstate()->m_chain.Height(), 109);777}779BOOST_TEST_MESSAGE(780"Ensure we can mine blocks on top of the initialized snapshot chainstate");781mineBlocks(10);782{783LOCK(chainman_restarted.GetMutex());784BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 220);786// Background chainstate should be unaware of new blocks on the snapshot787// chainstate, but the block disconnected above is now reattached.788BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 2);789BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[0]->m_chain.Height(), 110);790BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates[1]->m_chain.Height(), 220);791BOOST_CHECK_EQUAL(chainman_restarted.HistoricalChainstate(), nullptr);792}793}795BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup)796{797this->SetupSnapshot();799ChainstateManager& chainman = *Assert(m_node.chainman);800Chainstate& active_cs = chainman.ActiveChainstate();801Chainstate& validated_cs{*Assert(WITH_LOCK(cs_main, return chainman.HistoricalChainstate()))};802auto tip_cache_before_complete = active_cs.m_coinstip_cache_size_bytes;803auto db_cache_before_complete = active_cs.m_coinsdb_cache_size_bytes;805SnapshotCompletionResult res;806m_node.notifications->m_shutdown_on_fatal_error = false;808fs::path snapshot_chainstate_dir = *node::FindAssumeutxoChainstateDir(chainman.m_options.datadir);809BOOST_CHECK(fs::exists(snapshot_chainstate_dir));810BOOST_CHECK_EQUAL(snapshot_chainstate_dir, gArgs.GetDataDirNet() / "chainstate_snapshot");812BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_from_snapshot_blockhash));813const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(),814return chainman.ActiveTip()->GetBlockHash());816res = WITH_LOCK(::cs_main, return chainman.MaybeValidateSnapshot(validated_cs, active_cs));817BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SUCCESS);819BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_assumeutxo == Assumeutxo::VALIDATED));820BOOST_CHECK(WITH_LOCK(::cs_main, return chainman.CurrentChainstate().m_from_snapshot_blockhash));821BOOST_CHECK_EQUAL(WITH_LOCK(chainman.GetMutex(), return chainman.HistoricalChainstate()), nullptr);823// Cache should have been rebalanced and reallocated to the "only" remaining824// chainstate.825BOOST_CHECK(active_cs.m_coinstip_cache_size_bytes > tip_cache_before_complete);826BOOST_CHECK(active_cs.m_coinsdb_cache_size_bytes > db_cache_before_complete);828// Trying completion again should return false.829res = WITH_LOCK(::cs_main, return chainman.MaybeValidateSnapshot(validated_cs, active_cs));830BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SKIPPED);832// The invalid snapshot path should not have been used.833fs::path snapshot_invalid_dir = gArgs.GetDataDirNet() / "chainstate_snapshot_INVALID";834BOOST_CHECK(!fs::exists(snapshot_invalid_dir));835// chainstate_snapshot should still exist.836BOOST_CHECK(fs::exists(snapshot_chainstate_dir));838// Test that simulating a shutdown (resetting ChainstateManager) and then performing839// chainstate reinitializing successfully cleans up the background-validation840// chainstate data, and we end up with a single chainstate that is at tip.841ChainstateManager& chainman_restarted = this->SimulateNodeRestart();843BOOST_TEST_MESSAGE("Performing Load/Verify/Activate of chainstate");845// This call reinitializes the chainstates, and should clean up the now unnecessary846// background-validation leveldb contents.847this->LoadVerifyActivateChainstate();849BOOST_CHECK(!fs::exists(snapshot_invalid_dir));850// chainstate_snapshot should now *not* exist.851BOOST_CHECK(!fs::exists(snapshot_chainstate_dir));853const Chainstate& active_cs2 = chainman_restarted.ActiveChainstate();855{856LOCK(chainman_restarted.GetMutex());857BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 1);858BOOST_CHECK(!chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash);859BOOST_CHECK(active_cs2.m_coinstip_cache_size_bytes > tip_cache_before_complete);860BOOST_CHECK(active_cs2.m_coinsdb_cache_size_bytes > db_cache_before_complete);862BOOST_CHECK_EQUAL(chainman_restarted.ActiveTip()->GetBlockHash(), snapshot_tip_hash);863BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210);864}866BOOST_TEST_MESSAGE(867"Ensure we can mine blocks on top of the \"new\" IBD chainstate");868mineBlocks(10);869{870LOCK(chainman_restarted.GetMutex());871BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 220);872}873}875BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, SnapshotTestSetup)876{877auto chainstates = this->SetupSnapshot();878Chainstate& validation_chainstate = *std::get<0>(chainstates);879Chainstate& unvalidated_cs = *std::get<1>(chainstates);880ChainstateManager& chainman = *Assert(m_node.chainman);881SnapshotCompletionResult res;882m_node.notifications->m_shutdown_on_fatal_error = false;884// Test tampering with the IBD UTXO set with an extra coin to ensure it causes885// snapshot completion to fail.886CCoinsViewCache& ibd_coins = WITH_LOCK(::cs_main,887return validation_chainstate.CoinsTip());888Coin badcoin;889badcoin.out.nValue = m_rng.rand32();890badcoin.nHeight = 1;891badcoin.out.scriptPubKey.assign(m_rng.randbits(6), 0);892Txid txid = Txid::FromUint256(m_rng.rand256());893ibd_coins.AddCoin(COutPoint(txid, 0), std::move(badcoin), false);895fs::path snapshot_chainstate_dir = gArgs.GetDataDirNet() / "chainstate_snapshot";896BOOST_CHECK(fs::exists(snapshot_chainstate_dir));898{899ASSERT_DEBUG_LOG("failed to validate the -assumeutxo snapshot state");900res = WITH_LOCK(::cs_main, return chainman.MaybeValidateSnapshot(validation_chainstate, unvalidated_cs));901BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH);902}904{905LOCK(chainman.GetMutex());906BOOST_CHECK_EQUAL(chainman.m_chainstates.size(), 2);907BOOST_CHECK(chainman.m_chainstates[0]->m_assumeutxo == Assumeutxo::VALIDATED);908BOOST_CHECK(!chainman.m_chainstates[0]->SnapshotBase());909BOOST_CHECK(chainman.m_chainstates[1]->m_assumeutxo == Assumeutxo::INVALID);910BOOST_CHECK(chainman.m_chainstates[1]->SnapshotBase());911}913fs::path snapshot_invalid_dir = gArgs.GetDataDirNet() / "chainstate_snapshot_INVALID";914BOOST_CHECK(fs::exists(snapshot_invalid_dir));916// Test that simulating a shutdown (resetting ChainstateManager) and then performing917// chainstate reinitializing successfully loads only the fully-validated918// chainstate data, and we end up with a single chainstate that is at tip.919ChainstateManager& chainman_restarted = this->SimulateNodeRestart();921BOOST_TEST_MESSAGE("Performing Load/Verify/Activate of chainstate");923// This call reinitializes the chainstates, and should clean up the now unnecessary924// background-validation leveldb contents.925this->LoadVerifyActivateChainstate();927BOOST_CHECK(fs::exists(snapshot_invalid_dir));928BOOST_CHECK(!fs::exists(snapshot_chainstate_dir));930{931LOCK(::cs_main);932BOOST_CHECK_EQUAL(chainman_restarted.m_chainstates.size(), 1);933BOOST_CHECK(!chainman_restarted.CurrentChainstate().m_from_snapshot_blockhash);934BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 210);935}937BOOST_TEST_MESSAGE(938"Ensure we can mine blocks on top of the \"new\" IBD chainstate");939mineBlocks(10);940{941LOCK(::cs_main);942BOOST_CHECK_EQUAL(chainman_restarted.ActiveHeight(), 220);943}944}946/** Helper function to parse args into args_man and return the result of applying them to opts */947template <typename Options>948util::Result<Options> SetOptsFromArgs(ArgsManager& args_man, Options opts,949const std::vector<const char*>& args)950{951const auto argv{Cat({"ignore"}, args)};952std::string error{};953if (!args_man.ParseParameters(argv.size(), argv.data(), error)) {954return util::Error{Untranslated("ParseParameters failed with error: " + error)};955}956const auto result{node::ApplyArgsManOptions(args_man, opts)};957if (!result) return util::Error{util::ErrorString(result)};958return opts;959}961BOOST_FIXTURE_TEST_CASE(chainstatemanager_args, BasicTestingSetup)962{963//! Try to apply the provided args to a ChainstateManager::Options964auto get_opts = [&](const std::vector<const char*>& args) {965static kernel::Notifications notifications{};966static const ChainstateManager::Options options{967.chainparams = ::Params(),968.datadir = {},969.notifications = notifications};970return SetOptsFromArgs(*this->m_node.args, options, args);971};972//! Like get_opts, but requires the provided args to be valid and unwraps the result973auto get_valid_opts = [&](const std::vector<const char*>& args) {974const auto result{get_opts(args)};975BOOST_REQUIRE_MESSAGE(result, util::ErrorString(result).original);976return *result;977};979// test -assumevalid980BOOST_CHECK(!get_valid_opts({}).assumed_valid_block);981BOOST_CHECK_EQUAL(get_valid_opts({"-assumevalid="}).assumed_valid_block, uint256::ZERO);982BOOST_CHECK_EQUAL(get_valid_opts({"-assumevalid=0"}).assumed_valid_block, uint256::ZERO);983BOOST_CHECK_EQUAL(get_valid_opts({"-noassumevalid"}).assumed_valid_block, uint256::ZERO);984BOOST_CHECK_EQUAL(get_valid_opts({"-assumevalid=0x12"}).assumed_valid_block, uint256{0x12});986std::string assume_valid{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"};987BOOST_CHECK_EQUAL(get_valid_opts({("-assumevalid=" + assume_valid).c_str()}).assumed_valid_block, uint256::FromHex(assume_valid));989BOOST_CHECK(!get_opts({"-assumevalid=xyz"})); // invalid hex characters990BOOST_CHECK(!get_opts({"-assumevalid=01234567890123456789012345678901234567890123456789012345678901234"})); // > 64 hex chars992// test -minimumchainwork993BOOST_CHECK(!get_valid_opts({}).minimum_chain_work);994BOOST_CHECK_EQUAL(get_valid_opts({"-minimumchainwork=0"}).minimum_chain_work, arith_uint256());995BOOST_CHECK_EQUAL(get_valid_opts({"-nominimumchainwork"}).minimum_chain_work, arith_uint256());996BOOST_CHECK_EQUAL(get_valid_opts({"-minimumchainwork=0x1234"}).minimum_chain_work, arith_uint256{0x1234});998std::string minimum_chainwork{"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"};999BOOST_CHECK_EQUAL(get_valid_opts({("-minimumchainwork=" + minimum_chainwork).c_str()}).minimum_chain_work, UintToArith256(uint256::FromHex(minimum_chainwork).value()));
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.