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.
251CCoinsView* tip = base;252if (stack.size() > 0) {253tip = stack.back().get();254} else {255removed_all_caches = true;256}257stack.push_back(std::make_unique<CCoinsViewCacheTest>(tip));258if (stack.size() == 4) {259reached_4_caches = true;260}261}262}263}265// Verify coverage.266BOOST_CHECK(removed_all_caches);267BOOST_CHECK(reached_4_caches);268BOOST_CHECK(added_an_entry);269BOOST_CHECK(added_an_unspendable_entry);270BOOST_CHECK(removed_an_entry);271BOOST_CHECK(updated_an_entry);272BOOST_CHECK(found_an_entry);273BOOST_CHECK(missed_an_entry);274BOOST_CHECK(uncached_an_entry);275BOOST_CHECK(flushed_without_erase);276}277}; // struct CacheTest279BOOST_FIXTURE_TEST_SUITE(coins_tests_base, BasicTestingSetup)281// Run the above simulation for multiple base types.282BOOST_FIXTURE_TEST_CASE(coins_cache_base_simulation_test, CacheTest)283{284CCoinsViewTest base{m_rng};285SimulationTest(&base, false);286}288BOOST_AUTO_TEST_SUITE_END()290BOOST_FIXTURE_TEST_SUITE(coins_tests_dbbase, BasicTestingSetup)292BOOST_FIXTURE_TEST_CASE(coins_cache_dbbase_simulation_test, CacheTest)293{294CCoinsViewDB db_base{{.path = "test", .cache_bytes = 8_MiB, .memory_only = true}, {}};295SimulationTest(&db_base, true);296}298BOOST_AUTO_TEST_SUITE_END()300BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)302struct UpdateTest : BasicTestingSetup {303// Store of all necessary tx and undo data for next test304typedef std::map<COutPoint, std::tuple<CTransaction,CTxUndo,Coin>> UtxoData;305UtxoData utxoData;307UtxoData::iterator FindRandomFrom(const std::set<COutPoint> &utxoSet) {308assert(utxoSet.size());309auto utxoSetIt = utxoSet.lower_bound(COutPoint(Txid::FromUint256(m_rng.rand256()), 0));310if (utxoSetIt == utxoSet.end()) {311utxoSetIt = utxoSet.begin();312}313auto utxoDataIt = utxoData.find(*utxoSetIt);314assert(utxoDataIt != utxoData.end());315return utxoDataIt;316}317}; // struct UpdateTest320// This test is similar to the previous test321// except the emphasis is on testing the functionality of UpdateCoins322// random txs are created and UpdateCoins is used to update the cache stack323// In particular it is tested that spending a duplicate coinbase tx324// has the expected effect (the other duplicate is overwritten at all cache levels)325BOOST_FIXTURE_TEST_CASE(updatecoins_simulation_test, UpdateTest)326{327SeedRandomForTest(SeedRand::ZEROS);329bool spent_a_duplicate_coinbase = false;330// A simple map to track what we expect the cache stack to represent.331std::map<COutPoint, Coin> result;333// The cache stack.334CCoinsViewTest base{m_rng}; // A CCoinsViewTest at the bottom.335std::vector<std::unique_ptr<CCoinsViewCacheTest>> stack; // A stack of CCoinsViewCaches on top.336stack.push_back(std::make_unique<CCoinsViewCacheTest>(&base)); // Start with one cache.338// Track the txids we've used in various sets339std::set<COutPoint> coinbase_coins;340std::set<COutPoint> disconnected_coins;341std::set<COutPoint> duplicate_coins;342std::set<COutPoint> utxoset;344for (unsigned int i = 0; i < NUM_SIMULATION_ITERATIONS; i++) {345uint32_t randiter = m_rng.rand32();347// 19/20 txs add a new transaction348if (randiter % 20 < 19) {349CMutableTransaction tx;350tx.vin.resize(1);351tx.vout.resize(1);352tx.vout[0].nValue = i; //Keep txs unique unless intended to duplicate353tx.vout[0].scriptPubKey.assign(m_rng.rand32() & 0x3F, 0); // Random sizes so we can test memory usage accounting354const int height{int(m_rng.rand32() >> 1)};355Coin old_coin;357// 2/20 times create a new coinbase358if (randiter % 20 < 2 || coinbase_coins.size() < 10) {359// 1/10 of those times create a duplicate coinbase360if (m_rng.randrange(10) == 0 && coinbase_coins.size()) {361auto utxod = FindRandomFrom(coinbase_coins);362// Reuse the exact same coinbase363tx = CMutableTransaction{std::get<0>(utxod->second)};364// shouldn't be available for reconnection if it's been duplicated365disconnected_coins.erase(utxod->first);367duplicate_coins.insert(utxod->first);368}369else {370coinbase_coins.insert(COutPoint(tx.GetHash(), 0));371}372assert(CTransaction(tx).IsCoinBase());373}375// 17/20 times reconnect previous or add a regular tx376else {378COutPoint prevout;379// 1/20 times reconnect a previously disconnected tx380if (randiter % 20 == 2 && disconnected_coins.size()) {381auto utxod = FindRandomFrom(disconnected_coins);382tx = CMutableTransaction{std::get<0>(utxod->second)};383prevout = tx.vin[0].prevout;384if (!CTransaction(tx).IsCoinBase() && !utxoset.contains(prevout)) {385disconnected_coins.erase(utxod->first);386continue;387}389// If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate390if (utxoset.contains(utxod->first)) {391assert(CTransaction(tx).IsCoinBase());392assert(duplicate_coins.contains(utxod->first));393}394disconnected_coins.erase(utxod->first);395}397// 16/20 times create a regular tx398else {399auto utxod = FindRandomFrom(utxoset);400prevout = utxod->first;402// Construct the tx to spend the coins of prevouthash403tx.vin[0].prevout = prevout;404assert(!CTransaction(tx).IsCoinBase());405}406// In this simple test coins only have two states, spent or unspent, save the unspent state to restore407old_coin = result[prevout];408// Update the expected result of prevouthash to know these coins are spent409result[prevout].Clear();411utxoset.erase(prevout);413// The test is designed to ensure spending a duplicate coinbase will work properly414// if that ever happens and not resurrect the previously overwritten coinbase415if (duplicate_coins.contains(prevout)) {416spent_a_duplicate_coinbase = true;417}419}420// Update the expected result to know about the new output coins421assert(tx.vout.size() == 1);422const COutPoint outpoint(tx.GetHash(), 0);423result[outpoint] = Coin{tx.vout[0], height, CTransaction{tx}.IsCoinBase()};425// Call UpdateCoins on the top cache426CTxUndo undo;427UpdateCoins(CTransaction{tx}, *(stack.back()), undo, height);429// Update the utxo set for future spends430utxoset.insert(outpoint);432// Track this tx and undo info to use later433utxoData.emplace(outpoint, std::make_tuple(tx,undo,old_coin));434} else if (utxoset.size()) {435//1/20 times undo a previous transaction436auto utxod = FindRandomFrom(utxoset);438CTransaction &tx = std::get<0>(utxod->second);439CTxUndo &undo = std::get<1>(utxod->second);440Coin &orig_coin = std::get<2>(utxod->second);442// Update the expected result443// Remove new outputs444result[utxod->first].Clear();445// If not coinbase restore prevout446if (!tx.IsCoinBase()) {447result[tx.vin[0].prevout] = orig_coin;448}450// Disconnect the tx from the current UTXO451// See code in DisconnectBlock452// remove outputs453BOOST_CHECK(stack.back()->SpendCoin(utxod->first));454// restore inputs455if (!tx.IsCoinBase()) {456const COutPoint &out = tx.vin[0].prevout;457Coin coin = undo.vprevout[0];458ApplyTxInUndo(std::move(coin), *(stack.back()), out);459}460// Store as a candidate for reconnection461disconnected_coins.insert(utxod->first);463// Update the utxoset464utxoset.erase(utxod->first);465if (!tx.IsCoinBase())466utxoset.insert(tx.vin[0].prevout);467}469// Once every 1000 iterations and at the end, verify the full cache.470if (m_rng.randrange(1000) == 1 || i == NUM_SIMULATION_ITERATIONS - 1) {471for (const auto& entry : result) {472bool have = stack.back()->HaveCoin(entry.first);473const Coin& coin = stack.back()->AccessCoin(entry.first);474BOOST_CHECK(have == !coin.IsSpent());475BOOST_CHECK_EQUAL(coin, entry.second);476}477}479// One every 10 iterations, remove a random entry from the cache480if (utxoset.size() > 1 && m_rng.randrange(30) == 0) {481stack[m_rng.rand32() % stack.size()]->Uncache(FindRandomFrom(utxoset)->first);482}483if (disconnected_coins.size() > 1 && m_rng.randrange(30) == 0) {484stack[m_rng.rand32() % stack.size()]->Uncache(FindRandomFrom(disconnected_coins)->first);485}486if (duplicate_coins.size() > 1 && m_rng.randrange(30) == 0) {487stack[m_rng.rand32() % stack.size()]->Uncache(FindRandomFrom(duplicate_coins)->first);488}490if (m_rng.randrange(100) == 0) {491// Every 100 iterations, flush an intermediate cache492if (stack.size() > 1 && m_rng.randbool() == 0) {493unsigned int flushIndex = m_rng.randrange(stack.size() - 1);494stack[flushIndex]->Flush();495}496}497if (m_rng.randrange(100) == 0) {498// Every 100 iterations, change the cache stack.499if (stack.size() > 0 && m_rng.randbool() == 0) {500stack.back()->Flush();
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.