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.
251{252LOCK(::cs_main);253CCoinsViewCache& ibd_coinscache = chainman.ActiveChainstate().CoinsTip();254initial_size = ibd_coinscache.GetCacheSize();255size_t total_coins{0};257for (CTransactionRef& txn : m_coinbase_txns) {258COutPoint op{txn->GetHash(), 0};259BOOST_CHECK(ibd_coinscache.HaveCoin(op));260total_coins++;261}263BOOST_CHECK_EQUAL(total_coins, initial_total_coins);264BOOST_CHECK_EQUAL(initial_size, initial_total_coins);265}267Chainstate& validation_chainstate = chainman.ActiveChainstate();269// Snapshot should refuse to load at this height.270BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(this));271BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash);273// Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can274// be found.275constexpr int snapshot_height = 110;276mineBlocks(10);277initial_size += 10;278initial_total_coins += 10;280// Should not load malleated snapshots281BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(282this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {283// A UTXO is missing but count is correct284metadata.m_coins_count -= 1;286Txid txid;287auto_infile >> txid;288// coins size289(void)ReadCompactSize(auto_infile);290// vout index291(void)ReadCompactSize(auto_infile);292Coin coin;293auto_infile >> coin;294}));296BOOST_CHECK(!node::FindAssumeutxoChainstateDir(chainman.m_options.datadir));298BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(299this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {300// Coins count is larger than coins in file301metadata.m_coins_count += 1;302}));303BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(304this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {305// Coins count is smaller than coins in file306metadata.m_coins_count -= 1;307}));308BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(309this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {310// Wrong hash311metadata.m_base_blockhash = uint256::ZERO;312}));313BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(314this, [](AutoFile& auto_infile, SnapshotMetadata& metadata) {315// Wrong hash316metadata.m_base_blockhash = uint256::ONE;317}));319BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(this));320BOOST_CHECK(fs::exists(*node::FindAssumeutxoChainstateDir(chainman.m_options.datadir)));322// Ensure our active chain is the snapshot chainstate.323BOOST_CHECK(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());325Chainstate& snapshot_chainstate = chainman.ActiveChainstate();327{328LOCK(::cs_main);330fs::path found = *node::FindAssumeutxoChainstateDir(chainman.m_options.datadir);332// Note: WriteSnapshotBaseBlockhash() is implicitly tested above.333BOOST_CHECK_EQUAL(334*node::ReadSnapshotBaseBlockhash(found),335*Assert(chainman.CurrentChainstate().m_from_snapshot_blockhash));336}338const auto& au_data = ::Params().AssumeutxoForHeight(snapshot_height);339const CBlockIndex* tip = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip());341BOOST_CHECK_EQUAL(tip->m_chain_tx_count, au_data->m_chain_tx_count);343// To be checked against later when we try loading a subsequent snapshot.344uint256 loaded_snapshot_blockhash{*Assert(WITH_LOCK(chainman.GetMutex(), return chainman.CurrentChainstate().m_from_snapshot_blockhash))};346// Make some assertions about the both chainstates. These checks ensure the347// legacy chainstate hasn't changed and that the newly created chainstate348// reflects the expected content.349{350LOCK(::cs_main);351int chains_tested{0};353for (const auto& chainstate : chainman.m_chainstates) {354BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());355CCoinsViewCache& coinscache = chainstate->CoinsTip();357// Both caches will be empty initially.358BOOST_CHECK_EQUAL((unsigned int)0, coinscache.GetCacheSize());360size_t total_coins{0};362for (CTransactionRef& txn : m_coinbase_txns) {363COutPoint op{txn->GetHash(), 0};364BOOST_CHECK(coinscache.HaveCoin(op));365total_coins++;366}368BOOST_CHECK_EQUAL(initial_size , coinscache.GetCacheSize());369BOOST_CHECK_EQUAL(total_coins, initial_total_coins);370chains_tested++;371}373BOOST_CHECK_EQUAL(chains_tested, 2);374}376// Mine some new blocks on top of the activated snapshot chainstate.377constexpr size_t new_coins{100};378mineBlocks(new_coins); // Defined in TestChain100Setup.380{381LOCK(::cs_main);382size_t coins_in_active{0};383size_t coins_in_background{0};384size_t coins_missing_from_background{0};386for (const auto& chainstate : chainman.m_chainstates) {387BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());388CCoinsViewCache& coinscache = chainstate->CoinsTip();389bool is_background = chainstate.get() != &chainman.ActiveChainstate();391for (CTransactionRef& txn : m_coinbase_txns) {392COutPoint op{txn->GetHash(), 0};393if (coinscache.HaveCoin(op)) {394(is_background ? coins_in_background : coins_in_active)++;395} else if (is_background) {396coins_missing_from_background++;397}398}399}401BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);402BOOST_CHECK_EQUAL(coins_in_background, initial_total_coins);403BOOST_CHECK_EQUAL(coins_missing_from_background, new_coins);404}406// Snapshot should refuse to load after one has already loaded.407BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(this));409// Snapshot blockhash should be unchanged.410BOOST_CHECK_EQUAL(411*chainman.ActiveChainstate().m_from_snapshot_blockhash,412loaded_snapshot_blockhash);413return std::make_tuple(&validation_chainstate, &snapshot_chainstate);414}416// Simulate a restart of the node by flushing all state to disk, clearing the417// existing ChainstateManager, and unloading the block index.418//419// @returns a reference to the "restarted" ChainstateManager420ChainstateManager& SimulateNodeRestart()421{422ChainstateManager& chainman = *Assert(m_node.chainman);424BOOST_TEST_MESSAGE("Simulating node restart");425{426LOCK(chainman.GetMutex());427for (const auto& cs : chainman.m_chainstates) {428if (cs->CanFlushToDisk()) cs->ForceFlushStateToDisk();429}430}431{432// Process all callbacks referring to the old manager before wiping it.433m_node.validation_signals->SyncWithValidationInterfaceQueue();434LOCK(::cs_main);435chainman.ResetChainstates();436BOOST_CHECK_EQUAL(chainman.m_chainstates.size(), 0);437m_node.notifications = std::make_unique<KernelNotifications>(Assert(m_node.shutdown_request), m_node.exit_status, *Assert(m_node.warnings));438const ChainstateManager::Options chainman_opts{439.chainparams = ::Params(),440.datadir = chainman.m_options.datadir,441.notifications = *m_node.notifications,442.signals = m_node.validation_signals.get(),443};444const BlockManager::Options blockman_opts{445.chainparams = chainman_opts.chainparams,446.blocks_dir = m_args.GetBlocksDirPath(),447.notifications = chainman_opts.notifications,448.block_tree_db_params = DBParams{449.path = chainman.m_options.datadir / "blocks" / "index",450.cache_bytes = m_kernel_cache_sizes.block_tree_db,451.memory_only = m_block_tree_db_in_memory,452},453};454// For robustness, ensure the old manager is destroyed before creating a455// new one.456m_node.chainman.reset();457m_node.chainman = std::make_unique<ChainstateManager>(*Assert(m_node.shutdown_signal), chainman_opts, blockman_opts);458}459return *Assert(m_node.chainman);460}461};463//! Test basic snapshot activation.464BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, SnapshotTestSetup)465{466this->SetupSnapshot();467}469//! Test LoadBlockIndex behavior when multiple chainstates are in use.470//!471//! - First, verify that setBlockIndexCandidates is as expected when using a single,472//! fully-validating chainstate.473//!474//! - Then mark a region of the chain as missing data and introduce a second chainstate475//! that will tolerate assumed-valid blocks. Run LoadBlockIndex() and ensure that the first476//! chainstate only contains fully validated blocks and the other chainstate contains all blocks,477//! except those marked assume-valid, because those entries don't HAVE_DATA.478//!479BOOST_FIXTURE_TEST_CASE(chainstatemanager_loadblockindex, TestChain100Setup)480{481ChainstateManager& chainman = *Assert(m_node.chainman);482Chainstate& cs1 = chainman.ActiveChainstate();484int num_indexes{0};485// Blocks in range [assumed_valid_start_idx, last_assumed_valid_idx) will be486// marked as assumed-valid and not having data.487const int expected_assumed_valid{20};488const int last_assumed_valid_idx{111};489const int assumed_valid_start_idx = last_assumed_valid_idx - expected_assumed_valid;491// Mine to height 120, past the hardcoded regtest assumeutxo snapshot at492// height 110493mineBlocks(20);495CBlockIndex* validated_tip{nullptr};496CBlockIndex* assumed_base{nullptr};497CBlockIndex* assumed_tip{WITH_LOCK(chainman.GetMutex(), return chainman.ActiveChain().Tip())};498BOOST_CHECK_EQUAL(assumed_tip->nHeight, 120);500auto reload_all_block_indexes = [&]() {
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.