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.
1// Copyright (c) 2019-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.4//5#include <chainparams.h>6#include <consensus/validation.h>7#include <kernel/disconnected_transactions.h>8#include <node/chainstatemanager_args.h>9#include <node/kernel_notifications.h>10#include <node/utxo_snapshot.h>11#include <random.h>12#include <rpc/blockchain.h>13#include <sync.h>14#include <test/util/chainstate.h>15#include <test/util/common.h>16#include <test/util/logging.h>17#include <test/util/random.h>18#include <test/util/setup_common.h>19#include <test/util/validation.h>20#include <uint256.h>21#include <util/byte_units.h>22#include <util/result.h>23#include <util/vector.h>24#include <validation.h>25#include <validationinterface.h>27#include <tinyformat.h>29#include <vector>31#include <boost/test/unit_test.hpp>33using node::BlockManager;34using node::KernelNotifications;35using node::SnapshotMetadata;37BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, TestingSetup)39//! Basic tests for ChainstateManager.40//!41//! First create a legacy (IBD) chainstate, then create a snapshot chainstate.42BOOST_FIXTURE_TEST_CASE(chainstatemanager, TestChain100Setup)43{44ChainstateManager& manager = *m_node.chainman;46BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash));48// Create a legacy (IBD) chainstate.49//50Chainstate& c1 = manager.ActiveChainstate();52BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash));53{54LOCK(manager.GetMutex());55BOOST_CHECK_EQUAL(manager.m_chainstates.size(), 1);56BOOST_CHECK_EQUAL(manager.m_chainstates[0].get(), &c1);57}59auto& active_chain = WITH_LOCK(manager.GetMutex(), return manager.ActiveChain());60BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain);62// Get to a valid assumeutxo tip (per chainparams);63mineBlocks(10);64BOOST_CHECK_EQUAL(WITH_LOCK(manager.GetMutex(), return manager.ActiveHeight()), 110);65auto active_tip = WITH_LOCK(manager.GetMutex(), return manager.ActiveTip());66auto exp_tip = c1.m_chain.Tip();67BOOST_CHECK_EQUAL(active_tip, exp_tip);69BOOST_CHECK(WITH_LOCK(::cs_main, return !manager.CurrentChainstate().m_from_snapshot_blockhash));71// Create a snapshot-based chainstate.72//73const uint256 snapshot_blockhash = active_tip->GetBlockHash();74Chainstate& c2{WITH_LOCK(::cs_main, return manager.AddChainstate(std::make_unique<Chainstate>(nullptr, manager.m_blockman, manager, snapshot_blockhash)))};75c2.InitCoinsDB(76/*cache_size_bytes=*/8_MiB, /*in_memory=*/true, /*should_wipe=*/false);77{78LOCK(::cs_main);79c2.InitCoinsCache(8_MiB);80c2.CoinsTip().SetBestBlock(active_tip->GetBlockHash());81for (const auto& cs : manager.m_chainstates) {82cs->ClearBlockIndexCandidates();83}84c2.LoadChainTip();85for (const auto& cs : manager.m_chainstates) {86cs->PopulateBlockIndexCandidates();87}88}89BlockValidationState _;90BOOST_CHECK(c2.ActivateBestChain(_, nullptr));92BOOST_CHECK_EQUAL(WITH_LOCK(::cs_main, return *manager.CurrentChainstate().m_from_snapshot_blockhash), snapshot_blockhash);93BOOST_CHECK(WITH_LOCK(::cs_main, return manager.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED));94BOOST_CHECK_EQUAL(&c2, &manager.ActiveChainstate());95BOOST_CHECK(&c1 != &manager.ActiveChainstate());96{97LOCK(manager.GetMutex());98BOOST_CHECK_EQUAL(manager.m_chainstates.size(), 2);99BOOST_CHECK_EQUAL(manager.m_chainstates[0].get(), &c1);100BOOST_CHECK_EQUAL(manager.m_chainstates[1].get(), &c2);101}103auto& active_chain2 = WITH_LOCK(manager.GetMutex(), return manager.ActiveChain());104BOOST_CHECK_EQUAL(&active_chain2, &c2.m_chain);106BOOST_CHECK_EQUAL(WITH_LOCK(manager.GetMutex(), return manager.ActiveHeight()), 110);107mineBlocks(1);108BOOST_CHECK_EQUAL(WITH_LOCK(manager.GetMutex(), return manager.ActiveHeight()), 111);109BOOST_CHECK_EQUAL(WITH_LOCK(manager.GetMutex(), return c1.m_chain.Height()), 110);111auto active_tip2 = WITH_LOCK(manager.GetMutex(), return manager.ActiveTip());112BOOST_CHECK_EQUAL(active_tip, active_tip2->pprev);113BOOST_CHECK_EQUAL(active_tip, c1.m_chain.Tip());114BOOST_CHECK_EQUAL(active_tip2, c2.m_chain.Tip());116// Let scheduler events finish running to avoid accessing memory that is going to be unloaded117m_node.validation_signals->SyncWithValidationInterfaceQueue();118}120BOOST_FIXTURE_TEST_CASE(chainstatemanager_delete_chainstate_no_mempool, ChainTestingSetup)121{122auto& manager{*Assert(m_node.chainman)};123auto& validated{WITH_LOCK(::cs_main, return manager.InitializeChainstate(/*mempool=*/nullptr))};124auto& snapshot{WITH_LOCK(::cs_main, return manager.AddChainstate(std::make_unique<Chainstate>(nullptr, manager.m_blockman, manager, uint256::ONE)))};125WITH_LOCK(::cs_main, validated.SetTargetBlock(nullptr));126BOOST_CHECK(WITH_LOCK(::cs_main, return manager.DeleteChainstate(snapshot))); // Accept Kernel's null mempool127}129//! Test rebalancing the caches associated with each chainstate.130BOOST_FIXTURE_TEST_CASE(chainstatemanager_rebalance_caches, TestChain100Setup)131{132ChainstateManager& manager = *m_node.chainman;134size_t max_cache = 10000;135manager.m_total_coinsdb_cache = max_cache;136manager.m_total_coinstip_cache = max_cache;138std::vector<Chainstate*> chainstates;140// Create a legacy (IBD) chainstate.141//142Chainstate& c1 = manager.ActiveChainstate();143chainstates.push_back(&c1);144{145LOCK(::cs_main);146c1.InitCoinsCache(8_MiB);147manager.MaybeRebalanceCaches();148}150BOOST_CHECK_EQUAL(c1.m_coinstip_cache_size_bytes, max_cache);151BOOST_CHECK_EQUAL(c1.m_coinsdb_cache_size_bytes, max_cache);153// Create a snapshot-based chainstate.154//155CBlockIndex* snapshot_base{WITH_LOCK(manager.GetMutex(), return manager.ActiveChain()[manager.ActiveChain().Height() / 2])};156Chainstate& c2{WITH_LOCK(::cs_main, return manager.AddChainstate(std::make_unique<Chainstate>(nullptr, manager.m_blockman, manager, *snapshot_base->phashBlock)))};157chainstates.push_back(&c2);158c2.InitCoinsDB(159/*cache_size_bytes=*/8_MiB, /*in_memory=*/true, /*should_wipe=*/false);161// Reset IBD state so IsInitialBlockDownload() returns true and causes162// MaybeRebalanceCaches() to prioritize the snapshot chainstate, giving it163// more cache space than the snapshot chainstate. Calling ResetIbd() is164// necessary because m_cached_is_ibd is already latched to false before165// the test starts due to the test setup. After ResetIbd() is called,166// IsInitialBlockDownload() will return true because at this point the active167// chainstate has a null chain tip.168static_cast<TestChainstateManager&>(manager).ResetIbd();170{171LOCK(::cs_main);172c2.InitCoinsCache(8_MiB);173manager.MaybeRebalanceCaches();174}176BOOST_CHECK_EQUAL(c1.m_coinstip_cache_size_bytes, size_t(max_cache * 0.05));177BOOST_CHECK_EQUAL(c1.m_coinsdb_cache_size_bytes, size_t(max_cache * 0.05));178BOOST_CHECK_EQUAL(c2.m_coinstip_cache_size_bytes, size_t(max_cache * 0.95));179BOOST_CHECK_EQUAL(c2.m_coinsdb_cache_size_bytes, size_t(max_cache * 0.95));180}182BOOST_FIXTURE_TEST_CASE(chainstatemanager_ibd_exit_after_loading_blocks, ChainTestingSetup)183{184CBlockIndex tip;185ChainstateManager& chainman{*Assert(m_node.chainman)};186auto apply{[&](bool cached_is_ibd, bool loading_blocks, bool tip_exists, bool enough_work, bool tip_recent) {187LOCK(::cs_main);188chainman.ResetChainstates();189chainman.InitializeChainstate(m_node.mempool.get());191const auto recent_time{Now<NodeSeconds>() - chainman.m_options.max_tip_age};193chainman.m_cached_is_ibd.store(cached_is_ibd, std::memory_order_relaxed);194chainman.m_blockman.m_importing = loading_blocks;195if (tip_exists) {196tip.nChainWork = chainman.MinimumChainWork() - (enough_work ? 0 : 1);197tip.nTime = (recent_time - (tip_recent ? 0h : 100h)).time_since_epoch().count();198chainman.ActiveChain().SetTip(tip);199} else {200assert(!chainman.ActiveChain().Tip());201}202chainman.UpdateIBDStatus();203}};205for (const bool cached_is_ibd : {false, true}) {206for (const bool loading_blocks : {false, true}) {207for (const bool tip_exists : {false, true}) {208for (const bool enough_work : {false, true}) {209for (const bool tip_recent : {false, true}) {210apply(cached_is_ibd, loading_blocks, tip_exists, enough_work, tip_recent);211const bool expected_ibd = cached_is_ibd && (loading_blocks || !tip_exists || !enough_work || !tip_recent);212BOOST_CHECK_EQUAL(chainman.IsInitialBlockDownload(), expected_ibd);213}214}215}216}217}218}220struct SnapshotTestSetup : TestChain100Setup {221// Run with coinsdb on the filesystem to support, e.g., moving invalidated222// chainstate dirs to "*_invalid".223//224// Note that this means the tests run considerably slower than in-memory DB225// tests, but we can't otherwise test this functionality since it relies on226// destructive filesystem operations.227SnapshotTestSetup() : TestChain100Setup{228{},229{230.coins_db_in_memory = false,231.block_tree_db_in_memory = false,232},233}234{235}237std::tuple<Chainstate*, Chainstate*> SetupSnapshot()238{239ChainstateManager& chainman = *Assert(m_node.chainman);241{242LOCK(::cs_main);243BOOST_CHECK(!chainman.CurrentChainstate().m_from_snapshot_blockhash);244BOOST_CHECK(!node::FindAssumeutxoChainstateDir(chainman.m_options.datadir));245}247size_t initial_size;248size_t initial_total_coins{100};250// Make some initial assertions about the contents of the chainstate.
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.