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.
751*/752for (auto base_value : {ABSENT, SPENT, VALUE1}) {753CheckAddCoin(base_value, MISSING, VALUE3, VALUE3_DIRTY_FRESH, false);754CheckAddCoin(base_value, MISSING, VALUE3, VALUE3_DIRTY, true );756CheckAddCoin(base_value, SPENT_CLEAN, VALUE3, VALUE3_DIRTY_FRESH, false);757CheckAddCoin(base_value, SPENT_CLEAN, VALUE3, VALUE3_DIRTY, true );758CheckAddCoin(base_value, SPENT_FRESH, VALUE3, VALUE3_DIRTY_FRESH, false);759CheckAddCoin(base_value, SPENT_FRESH, VALUE3, VALUE3_DIRTY_FRESH, true );760CheckAddCoin(base_value, SPENT_DIRTY, VALUE3, VALUE3_DIRTY, false);761CheckAddCoin(base_value, SPENT_DIRTY, VALUE3, VALUE3_DIRTY, true );762CheckAddCoin(base_value, SPENT_DIRTY_FRESH, VALUE3, VALUE3_DIRTY_FRESH, false);763CheckAddCoin(base_value, SPENT_DIRTY_FRESH, VALUE3, VALUE3_DIRTY_FRESH, true );765CheckAddCoin(base_value, VALUE2_CLEAN, VALUE3, EX_OVERWRITE_UNSPENT, false);766CheckAddCoin(base_value, VALUE2_CLEAN, VALUE3, VALUE3_DIRTY, true );767CheckAddCoin(base_value, VALUE2_FRESH, VALUE3, EX_OVERWRITE_UNSPENT, false);768CheckAddCoin(base_value, VALUE2_FRESH, VALUE3, VALUE3_DIRTY_FRESH, true );769CheckAddCoin(base_value, VALUE2_DIRTY, VALUE3, EX_OVERWRITE_UNSPENT, false);770CheckAddCoin(base_value, VALUE2_DIRTY, VALUE3, VALUE3_DIRTY, true );771CheckAddCoin(base_value, VALUE2_DIRTY_FRESH, VALUE3, EX_OVERWRITE_UNSPENT, false);772CheckAddCoin(base_value, VALUE2_DIRTY_FRESH, VALUE3, VALUE3_DIRTY_FRESH, true );773}774}776static void CheckWriteCoins(const MaybeCoin& parent, const MaybeCoin& child, const CoinOrError& expected)777{778SingleEntryCacheTest test{ABSENT, parent};779auto write_coins{[&] { WriteCoinsViewEntry(test.cache, child); }};780if (auto* expected_coin{std::get_if<MaybeCoin>(&expected)}) {781write_coins();782test.cache.SelfTest(/*sanity_check=*/false);783BOOST_CHECK_EQUAL(GetCoinsMapEntry(test.cache.map()), *expected_coin);784} else {785BOOST_CHECK_EXCEPTION(write_coins(), std::logic_error, HasReason(std::get<std::string>(expected)));786}787}789BOOST_AUTO_TEST_CASE(ccoins_write)790{791/* Check BatchWrite behavior, flushing one entry from a child cache to a792* parent cache, and checking the resulting entry in the parent cache793* after the write.794* Parent Child Expected795*/796CheckWriteCoins(MISSING, MISSING, MISSING );797CheckWriteCoins(MISSING, SPENT_DIRTY, SPENT_DIRTY );798CheckWriteCoins(MISSING, SPENT_DIRTY_FRESH, MISSING );799CheckWriteCoins(MISSING, VALUE2_DIRTY, VALUE2_DIRTY );800CheckWriteCoins(MISSING, VALUE2_DIRTY_FRESH, VALUE2_DIRTY_FRESH );801CheckWriteCoins(SPENT_CLEAN, MISSING, SPENT_CLEAN );802CheckWriteCoins(SPENT_FRESH, MISSING, SPENT_FRESH );803CheckWriteCoins(SPENT_DIRTY, MISSING, SPENT_DIRTY );804CheckWriteCoins(SPENT_DIRTY_FRESH, MISSING, SPENT_DIRTY_FRESH );806CheckWriteCoins(SPENT_CLEAN, SPENT_DIRTY, SPENT_DIRTY );807CheckWriteCoins(SPENT_CLEAN, SPENT_DIRTY_FRESH, SPENT_DIRTY );808CheckWriteCoins(SPENT_FRESH, SPENT_DIRTY, MISSING );809CheckWriteCoins(SPENT_FRESH, SPENT_DIRTY_FRESH, MISSING );810CheckWriteCoins(SPENT_DIRTY, SPENT_DIRTY, SPENT_DIRTY );811CheckWriteCoins(SPENT_DIRTY, SPENT_DIRTY_FRESH, SPENT_DIRTY );812CheckWriteCoins(SPENT_DIRTY_FRESH, SPENT_DIRTY, MISSING );813CheckWriteCoins(SPENT_DIRTY_FRESH, SPENT_DIRTY_FRESH, MISSING );815CheckWriteCoins(SPENT_CLEAN, VALUE2_DIRTY, VALUE2_DIRTY );816CheckWriteCoins(SPENT_CLEAN, VALUE2_DIRTY_FRESH, VALUE2_DIRTY );817CheckWriteCoins(SPENT_FRESH, VALUE2_DIRTY, VALUE2_DIRTY_FRESH );818CheckWriteCoins(SPENT_FRESH, VALUE2_DIRTY_FRESH, VALUE2_DIRTY_FRESH );819CheckWriteCoins(SPENT_DIRTY, VALUE2_DIRTY, VALUE2_DIRTY );820CheckWriteCoins(SPENT_DIRTY, VALUE2_DIRTY_FRESH, VALUE2_DIRTY );821CheckWriteCoins(SPENT_DIRTY_FRESH, VALUE2_DIRTY, VALUE2_DIRTY_FRESH );822CheckWriteCoins(SPENT_DIRTY_FRESH, VALUE2_DIRTY_FRESH, VALUE2_DIRTY_FRESH );824CheckWriteCoins(VALUE1_CLEAN, MISSING, VALUE1_CLEAN );825CheckWriteCoins(VALUE1_FRESH, MISSING, VALUE1_FRESH );826CheckWriteCoins(VALUE1_DIRTY, MISSING, VALUE1_DIRTY );827CheckWriteCoins(VALUE1_DIRTY_FRESH, MISSING, VALUE1_DIRTY_FRESH );828CheckWriteCoins(VALUE1_CLEAN, SPENT_DIRTY, SPENT_DIRTY );829CheckWriteCoins(VALUE1_CLEAN, SPENT_DIRTY_FRESH, EX_FRESH_MISAPPLIED);830CheckWriteCoins(VALUE1_FRESH, SPENT_DIRTY, MISSING );831CheckWriteCoins(VALUE1_FRESH, SPENT_DIRTY_FRESH, EX_FRESH_MISAPPLIED);832CheckWriteCoins(VALUE1_DIRTY, SPENT_DIRTY, SPENT_DIRTY );833CheckWriteCoins(VALUE1_DIRTY, SPENT_DIRTY_FRESH, EX_FRESH_MISAPPLIED);834CheckWriteCoins(VALUE1_DIRTY_FRESH, SPENT_DIRTY, MISSING );835CheckWriteCoins(VALUE1_DIRTY_FRESH, SPENT_DIRTY_FRESH, EX_FRESH_MISAPPLIED);837CheckWriteCoins(VALUE1_CLEAN, VALUE2_DIRTY, VALUE2_DIRTY );838CheckWriteCoins(VALUE1_CLEAN, VALUE2_DIRTY_FRESH, EX_FRESH_MISAPPLIED);839CheckWriteCoins(VALUE1_FRESH, VALUE2_DIRTY, VALUE2_DIRTY_FRESH );840CheckWriteCoins(VALUE1_FRESH, VALUE2_DIRTY_FRESH, EX_FRESH_MISAPPLIED);841CheckWriteCoins(VALUE1_DIRTY, VALUE2_DIRTY, VALUE2_DIRTY );842CheckWriteCoins(VALUE1_DIRTY, VALUE2_DIRTY_FRESH, EX_FRESH_MISAPPLIED);843CheckWriteCoins(VALUE1_DIRTY_FRESH, VALUE2_DIRTY, VALUE2_DIRTY_FRESH );844CheckWriteCoins(VALUE1_DIRTY_FRESH, VALUE2_DIRTY_FRESH, EX_FRESH_MISAPPLIED);846// The checks above omit cases where the child state is not DIRTY, since847// they would be too repetitive (the parent cache is never updated in these848// cases). The loop below covers these cases and makes sure the parent cache849// is always left unchanged.850for (const MaybeCoin& parent : {MISSING,851SPENT_CLEAN, SPENT_DIRTY, SPENT_FRESH, SPENT_DIRTY_FRESH,852VALUE1_CLEAN, VALUE1_DIRTY, VALUE1_FRESH, VALUE1_DIRTY_FRESH}) {853for (const MaybeCoin& child : {MISSING,854SPENT_CLEAN, SPENT_FRESH,855VALUE2_CLEAN, VALUE2_FRESH}) {856auto expected{CoinOrError{parent}}; // TODO test failure cases as well857CheckWriteCoins(parent, child, expected);858}859}860}862struct FlushTest : BasicTestingSetup {863Coin MakeCoin()864{865Coin coin;866coin.out.nValue = m_rng.rand32();867coin.nHeight = m_rng.randrange(4096);868coin.fCoinBase = false;869return coin;870}873//! For CCoinsViewCache instances backed by either another cache instance or874//! leveldb, test cache behavior and flag state (DIRTY/FRESH) by875//!876//! 1. Adding a random coin to the child-most cache,877//! 2. Flushing all caches (without erasing),878//! 3. Ensure the entry still exists in the cache and has been written to parent,879//! 4. (if `do_erasing_flush`) Flushing the caches again (with erasing),880//! 5. (if `do_erasing_flush`) Ensure the entry has been written to the parent and is no longer in the cache,881//! 6. Spend the coin, ensure it no longer exists in the parent.882//!883void TestFlushBehavior(884CCoinsViewCacheTest* view,885CCoinsViewDB& base,886std::vector<std::unique_ptr<CCoinsViewCacheTest>>& all_caches,887bool do_erasing_flush)888{889size_t cache_usage;890size_t cache_size;892auto flush_all = [this, &all_caches](bool erase) {893// Flush in reverse order to ensure that flushes happen from children up.894for (auto i = all_caches.rbegin(); i != all_caches.rend(); ++i) {895auto& cache = *i;896cache->SanityCheck();897// block_hash must be filled before flushing to disk; value is898// unimportant here. This is normally done during connect/disconnect block.899cache->SetBestBlock(m_rng.rand256());900erase ? cache->Flush() : cache->Sync();901}902};904Txid txid = Txid::FromUint256(m_rng.rand256());905COutPoint outp = COutPoint(txid, 0);906Coin coin = MakeCoin();907// Ensure the coins views haven't seen this coin before.908BOOST_CHECK(!base.HaveCoin(outp));909BOOST_CHECK(!view->HaveCoin(outp));911// --- 1. Adding a random coin to the child cache912//913view->AddCoin(outp, Coin(coin), false);915cache_usage = view->DynamicMemoryUsage();916cache_size = view->map().size();918// `base` shouldn't have coin (no flush yet) but `view` should have cached it.919BOOST_CHECK(!base.HaveCoin(outp));920BOOST_CHECK(view->HaveCoin(outp));922BOOST_CHECK_EQUAL(GetCoinsMapEntry(view->map(), outp), CoinEntry(coin.out.nValue, CoinEntry::State::DIRTY_FRESH));924// --- 2. Flushing all caches (without erasing)925//926flush_all(/*erase=*/ false);928// CoinsMap usage should be unchanged since we didn't erase anything.929BOOST_CHECK_EQUAL(cache_usage, view->DynamicMemoryUsage());930BOOST_CHECK_EQUAL(cache_size, view->map().size());932// --- 3. Ensuring the entry still exists in the cache and has been written to parent933//934BOOST_CHECK_EQUAL(GetCoinsMapEntry(view->map(), outp), CoinEntry(coin.out.nValue, CoinEntry::State::CLEAN)); // State should have been wiped.936// Both views should now have the coin.937BOOST_CHECK(base.HaveCoin(outp));938BOOST_CHECK(view->HaveCoin(outp));940if (do_erasing_flush) {941// --- 4. Flushing the caches again (with erasing)942//943flush_all(/*erase=*/ true);945// Memory does not necessarily go down due to the map using a memory pool946BOOST_TEST(view->DynamicMemoryUsage() <= cache_usage);947// Size of the cache must go down though948BOOST_TEST(view->map().size() < cache_size);950// --- 5. Ensuring the entry is no longer in the cache951//952BOOST_CHECK(!GetCoinsMapEntry(view->map(), outp));953view->AccessCoin(outp);954BOOST_CHECK_EQUAL(GetCoinsMapEntry(view->map(), outp), CoinEntry(coin.out.nValue, CoinEntry::State::CLEAN));955}957// Can't overwrite an entry without specifying that an overwrite is958// expected.959BOOST_CHECK_THROW(960view->AddCoin(outp, Coin(coin), /*possible_overwrite=*/ false),961std::logic_error);963// --- 6. Spend the coin.964//965BOOST_CHECK(view->SpendCoin(outp));967// The coin should be in the cache, but spent and marked dirty.968BOOST_CHECK_EQUAL(GetCoinsMapEntry(view->map(), outp), SPENT_DIRTY);969BOOST_CHECK(!view->HaveCoin(outp)); // Coin should be considered spent in `view`.970BOOST_CHECK(base.HaveCoin(outp)); // But coin should still be unspent in `base`.972flush_all(/*erase=*/ false);974// Coin should be considered spent in both views.975BOOST_CHECK(!view->HaveCoin(outp));976BOOST_CHECK(!base.HaveCoin(outp));978// Spent coin should not be spendable.979BOOST_CHECK(!view->SpendCoin(outp));981// --- Bonus check: ensure that a coin added to the base view via one cache982// can be spent by another cache which has never seen it.983//984txid = Txid::FromUint256(m_rng.rand256());985outp = COutPoint(txid, 0);986coin = MakeCoin();987BOOST_CHECK(!base.HaveCoin(outp));988BOOST_CHECK(!all_caches[0]->HaveCoin(outp));989BOOST_CHECK(!all_caches[1]->HaveCoin(outp));991all_caches[0]->AddCoin(outp, std::move(coin), false);992all_caches[0]->Sync();993BOOST_CHECK(base.HaveCoin(outp));994BOOST_CHECK(all_caches[0]->HaveCoin(outp));995BOOST_CHECK(!all_caches[1]->HaveCoinInCache(outp));997BOOST_CHECK(all_caches[1]->SpendCoin(outp));998flush_all(/*erase=*/ false);999BOOST_CHECK(!base.HaveCoin(outp));1000BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
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.