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}252return std::nullopt;253};255/** Flush changes in top cache to the one below. */256auto flush = [&]() {257assert(caches.size() >= 1);258auto& cache = sim_caches[caches.size()];259auto& prev_cache = sim_caches[caches.size() - 1];260for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {261if (cache.entry[outpointidx].entrytype != EntryType::NONE) {262prev_cache.entry[outpointidx] = cache.entry[outpointidx];263cache.entry[outpointidx].entrytype = EntryType::NONE;264}265}266};268/** Helper creating a fetch scope for the top cache (which must be a CoinsViewOverlay). */269const auto make_fetch_scope{[&] {270auto& overlay{static_cast<CoinsViewOverlay&>(*caches.back())};271return std::make_unique<OverlayFetchScope>(overlay, data.block);272}};274// Main simulation loop: read commands from the fuzzer input, and apply them275// to both the real cache stack and the simulation.276FuzzedDataProvider provider(buffer.data(), buffer.size());277LIMITED_WHILE (provider.remaining_bytes(), 10000) {278// Every operation (except "Change height") moves current height forward,279// so it functions as a kind of epoch, making ~all UTXOs unique.280++current_height;281// Make sure there is always at least one CCoinsViewCache.282if (caches.empty()) {283caches.emplace_back(new CCoinsViewCache(&bottom, /*deterministic=*/true));284fetch_scopes.emplace_back();285sim_caches[caches.size()].Wipe();286}287assert(caches.size() == fetch_scopes.size());289// Execute command.290CallOneOf(291provider,293[&]() { // PeekCoin/GetCoin294uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);295// Look up in simulation data.296auto sim = lookup(outpointidx);297// Look up in real caches.298auto realcoin = provider.ConsumeBool() ?299caches.back()->PeekCoin(data.outpoints[outpointidx]) :300caches.back()->GetCoin(data.outpoints[outpointidx]);301// Compare results.302if (!sim.has_value()) {303assert(!realcoin);304} else {305assert(realcoin && !realcoin->IsSpent());306const auto& simcoin = data.coins[sim->first];307assert(realcoin->out == simcoin.out);308assert(realcoin->fCoinBase == simcoin.fCoinBase);309assert(realcoin->nHeight == sim->second);310}311},313[&]() { // HaveCoin314uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);315// Look up in simulation data.316auto sim = lookup(outpointidx);317// Look up in real caches.318auto real = caches.back()->HaveCoin(data.outpoints[outpointidx]);319// Compare results.320assert(sim.has_value() == real);321},323[&]() { // HaveCoinInCache324uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);325// Invoke on real cache (there is no equivalent in simulation, so nothing to compare result with).326(void)caches.back()->HaveCoinInCache(data.outpoints[outpointidx]);327},329[&]() { // AccessCoin330uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);331// Look up in simulation data.332auto sim = lookup(outpointidx);333// Look up in real caches.334const auto& realcoin = caches.back()->AccessCoin(data.outpoints[outpointidx]);335// Compare results.336if (!sim.has_value()) {337assert(realcoin.IsSpent());338} else {339assert(!realcoin.IsSpent());340const auto& simcoin = data.coins[sim->first];341assert(simcoin.out == realcoin.out);342assert(simcoin.fCoinBase == realcoin.fCoinBase);343assert(realcoin.nHeight == sim->second);344}345},347[&]() { // AddCoin (only possible_overwrite if necessary)348uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);349uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);350// Look up in simulation data (to know whether we must set possible_overwrite or not).351auto sim = lookup(outpointidx);352// Invoke on real caches.353Coin coin = data.coins[coinidx];354coin.nHeight = current_height;355caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin), sim.has_value());356// Apply to simulation data.357auto& entry = sim_caches[caches.size()].entry[outpointidx];358entry.entrytype = EntryType::UNSPENT;359entry.coinidx = coinidx;360entry.height = current_height;361},363[&]() { // AddCoin (always possible_overwrite)364uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);365uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);366// Invoke on real caches.367Coin coin = data.coins[coinidx];368coin.nHeight = current_height;369caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin), true);370// Apply to simulation data.371auto& entry = sim_caches[caches.size()].entry[outpointidx];372entry.entrytype = EntryType::UNSPENT;373entry.coinidx = coinidx;374entry.height = current_height;375},377[&]() { // SpendCoin (moveto = nullptr)378uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);379// Invoke on real caches.380caches.back()->SpendCoin(data.outpoints[outpointidx], nullptr);381// Apply to simulation data.382sim_caches[caches.size()].entry[outpointidx].entrytype = EntryType::SPENT;383},385[&]() { // SpendCoin (with moveto)386uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);387// Look up in simulation data (to compare the returned *moveto with).388auto sim = lookup(outpointidx);389// Invoke on real caches.390Coin realcoin;391caches.back()->SpendCoin(data.outpoints[outpointidx], &realcoin);392// Apply to simulation data.393sim_caches[caches.size()].entry[outpointidx].entrytype = EntryType::SPENT;394// Compare *moveto with the value expected based on simulation data.395if (!sim.has_value()) {396assert(realcoin.IsSpent());397} else {398assert(!realcoin.IsSpent());399const auto& simcoin = data.coins[sim->first];400assert(simcoin.out == realcoin.out);401assert(simcoin.fCoinBase == realcoin.fCoinBase);402assert(realcoin.nHeight == sim->second);403}404},406[&]() { // Uncache407uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);408// Apply to real caches (there is no equivalent in our simulation).409caches.back()->Uncache(data.outpoints[outpointidx]);410},412[&]() { // Add a cache level (if not already at the max).413if (caches.size() != MAX_CACHES) {414// Apply to real caches.415if (provider.ConsumeBool()) {416caches.emplace_back(new CCoinsViewCache(&*caches.back(), /*deterministic=*/true));417fetch_scopes.emplace_back();418} else {419caches.emplace_back(new CoinsViewOverlay(&*caches.back(), g_thread_pool, /*deterministic=*/true));420fetch_scopes.emplace_back(make_fetch_scope());421}422// Apply to simulation data.423sim_caches[caches.size()].Wipe();424}425},427[&]() { // Remove a cache level.428// Apply to real caches (this reduces caches.size(), implicitly doing the same on the simulation data).429caches.back()->SanityCheck();430fetch_scopes.pop_back();431caches.pop_back();432},434[&]() { // Flush.435// CoinsViewOverlay::Flush() must have all inputs consumed before being called436if (auto* overlay{dynamic_cast<CoinsViewOverlay*>(caches.back().get())};437overlay && !overlay->AllInputsConsumed()) {438return;439}440// Apply to simulation data.441flush();442// Apply to real caches.443caches.back()->Flush(/*reallocate_cache=*/provider.ConsumeBool());444},446[&]() { // Sync.447if (fetch_scopes.back()) return; // CoinsViewOverlay::Sync() is never called in production448// Apply to simulation data (note that in our simulation, syncing and flushing is the same thing).449flush();450// Apply to real caches.451caches.back()->Sync();452},454[&]() { // Reset.455sim_caches[caches.size()].Wipe();456// Apply to real caches. Optionally start fetching again.457if (fetch_scopes.back() && provider.ConsumeBool()) {458fetch_scopes.back().reset(); // Stop fetching before starting again.459fetch_scopes.back() = make_fetch_scope();460} else {461(void)caches.back()->CreateResetGuard();462}463},465[&]() { // GetCacheSize466(void)caches.back()->GetCacheSize();467},469[&]() { // DynamicMemoryUsage470(void)caches.back()->DynamicMemoryUsage();471},473[&]() { // Change height474current_height = provider.ConsumeIntegralInRange<uint32_t>(1, current_height - 1);475}476);477}479// Sanity check all the remaining caches480for (const auto& cache : caches) {481cache->SanityCheck();482}484// Full comparison between caches and simulation data, from bottom to top,485for (unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {486auto& cache = *caches[sim_idx - 1];487size_t cache_size = 0;489for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {490cache_size += cache.HaveCoinInCache(data.outpoints[outpointidx]);491const auto real{cache.PeekCoin(data.outpoints[outpointidx])};492auto sim = lookup(outpointidx, sim_idx);493if (!sim.has_value()) {494assert(!real);495} else {496assert(!real->IsSpent());497assert(real->out == data.coins[sim->first].out);498assert(real->fCoinBase == data.coins[sim->first].fCoinBase);499assert(real->nHeight == sim->second);500}
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.