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) 2020-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.5#include <coins.h>6#include <consensus/amount.h>7#include <consensus/tx_check.h>8#include <consensus/tx_verify.h>9#include <consensus/validation.h>10#include <kernel/chainstatemanager_opts.h>11#include <kernel/cs_main.h>12#include <policy/policy.h>13#include <primitives/block.h>14#include <primitives/transaction.h>15#include <script/interpreter.h>16#include <test/fuzz/FuzzedDataProvider.h>17#include <test/fuzz/fuzz.h>18#include <test/fuzz/util.h>19#include <test/util/coins.h>20#include <test/util/setup_common.h>21#include <txdb.h>22#include <util/hasher.h>23#include <util/threadpool.h>25#include <cassert>26#include <algorithm>27#include <cstdint>28#include <functional>29#include <limits>30#include <memory>31#include <optional>32#include <ranges>33#include <stdexcept>34#include <string>35#include <utility>36#include <vector>38namespace {39/**40* MutationGuardCoinsViewCache asserts that nothing mutates cacheCoins until41* BatchWrite is called. It keeps a snapshot of the cacheCoins state, which it42* uses for the assertion in BatchWrite. After the call to the superclass43* CCoinsViewCache::BatchWrite returns, it recomputes the snapshot at that44* moment.45*/46class MutationGuardCoinsViewCache final : public CCoinsViewCache47{48private:49struct CacheCoinSnapshot {50COutPoint outpoint;51bool dirty{false};52bool fresh{false};53Coin coin;54bool operator==(const CacheCoinSnapshot&) const = default;55};57std::vector<CacheCoinSnapshot> ComputeCacheCoinsSnapshot() const58{59std::vector<CacheCoinSnapshot> snapshot;60snapshot.reserve(cacheCoins.size());62for (const auto& [outpoint, entry] : cacheCoins) {63snapshot.emplace_back(outpoint, entry.IsDirty(), entry.IsFresh(), entry.coin);64}66std::ranges::sort(snapshot, std::less<>{}, &CacheCoinSnapshot::outpoint);67return snapshot;68}70mutable std::vector<CacheCoinSnapshot> m_expected_snapshot{ComputeCacheCoinsSnapshot()};72public:73void BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash) override74{75// Nothing must modify cacheCoins other than BatchWrite.76assert(ComputeCacheCoinsSnapshot() == m_expected_snapshot);77CCoinsViewCache::BatchWrite(cursor, block_hash);78m_expected_snapshot = ComputeCacheCoinsSnapshot();79}81using CCoinsViewCache::CCoinsViewCache;82};84// Reuse a single global thread pool across fuzz iterations. Creating and destroying a pool every85// iteration leaks memory, since iterations can run faster than the OS can tear down the threads.86std::shared_ptr<ThreadPool> g_thread_pool{std::make_shared<ThreadPool>("view_fuzz")};88void StartPoolIfNeeded()89{90if (!g_thread_pool->WorkersCount()) g_thread_pool->Start(DEFAULT_PREVOUTFETCH_THREADS);91}93//! Build a random block and seed a view with utxos for its inputs.94CBlock BuildRandomBlock(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& view)95{96CBlock block;97CMutableTransaction coinbase;98coinbase.vin.emplace_back();99block.vtx.push_back(MakeTransactionRef(coinbase));101CCoinsViewCache seed_cache{&view, /*deterministic=*/true};102seed_cache.SetBestBlock(uint256::ONE);104Txid prevhash{Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider))};105LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)106{107CMutableTransaction tx;108LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100)109{110const Txid txid{fuzzed_data_provider.ConsumeBool()111? Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider))112: prevhash};113const COutPoint outpoint{txid, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};114if (auto coin{ConsumeDeserializable<Coin>(fuzzed_data_provider)}; coin && !coin->IsSpent()) {115seed_cache.AddCoin(outpoint, std::move(*coin), /*possible_overwrite=*/true);116}117tx.vin.emplace_back(outpoint);118}119prevhash = tx.GetHash();120block.vtx.push_back(MakeTransactionRef(tx));121}123seed_cache.Flush();124return block;125}127} // namespace129void initialize_coins_view()130{131static const auto testing_setup = MakeNoLogFileContext<>();132}134void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& coins_view_cache, CCoinsView* backend_coins_view)135{136auto* const db{dynamic_cast<CCoinsViewDB*>(backend_coins_view)};137auto* const overlay{dynamic_cast<CoinsViewOverlay*>(&coins_view_cache)};138const bool is_db{db != nullptr};139bool good_data{true};140auto* original_backend{backend_coins_view};142if (is_db) coins_view_cache.SetBestBlock(uint256::ONE);143COutPoint random_out_point;144Coin random_coin;145CMutableTransaction random_mutable_transaction;146LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 10'000) {147CallOneOf(148fuzzed_data_provider,149[&] {150if (random_coin.IsSpent()) {151return;152}153COutPoint outpoint{random_out_point};154Coin coin{random_coin};155if (fuzzed_data_provider.ConsumeBool()) {156// We can only skip the check if no unspent coin exists for this outpoint.157const bool possible_overwrite{coins_view_cache.PeekCoin(outpoint) || fuzzed_data_provider.ConsumeBool()};158coins_view_cache.AddCoin(outpoint, std::move(coin), possible_overwrite);159} else {160coins_view_cache.EmplaceCoinInternalDANGER(outpoint, std::move(coin));161}162},163[&] {164if (overlay && !overlay->AllInputsConsumed()) return; // CoinsViewOverlay::Flush() must have all inputs consumed before being called165coins_view_cache.Flush(/*reallocate_cache=*/fuzzed_data_provider.ConsumeBool());166},167[&] {168if (overlay) return; // CoinsViewOverlay::Sync() is never called in production code169coins_view_cache.Sync();170},171[&] {172if (db) WITH_LOCK(::cs_main, (void)db->CompactFullAsync());173},174[&] {175uint256 best_block{ConsumeUInt256(fuzzed_data_provider)};176// `CCoinsViewDB::BatchWrite()` requires a non-null best block.177if (is_db && best_block.IsNull()) best_block = uint256::ONE;178coins_view_cache.SetBestBlock(best_block);179},180[&] {181(void)coins_view_cache.CreateResetGuard();182// Reset() clears the best block, so reseed db-backed caches.183if (is_db) {184const uint256 best_block{ConsumeUInt256(fuzzed_data_provider)};185if (best_block.IsNull()) {186good_data = false;187return;188}189coins_view_cache.SetBestBlock(best_block);190}191},192[&] {193Coin move_to;194(void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr);195},196[&] {197coins_view_cache.Uncache(random_out_point);198},199[&] {200if (overlay) return; // CoinsViewOverlay::SetBackend() is never called in production code201const bool use_original_backend{fuzzed_data_provider.ConsumeBool()};202if (use_original_backend && backend_coins_view != original_backend) {203// FRESH flags valid against the empty backend may be invalid204// against the original backend, so reset before restoring it.205(void)coins_view_cache.CreateResetGuard();206// Reset() clears the best block; db backends require a non-null hash.207if (is_db) coins_view_cache.SetBestBlock(uint256::ONE);208}209backend_coins_view = use_original_backend ? original_backend : &CoinsViewEmpty::Get();210coins_view_cache.SetBackend(*backend_coins_view);211},212[&] {213const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);214if (!opt_out_point) {215good_data = false;216return;217}218random_out_point = *opt_out_point;219},220[&] {221const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);222if (!opt_coin) {223good_data = false;224return;225}226random_coin = *opt_coin;227},228[&] {229const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);230if (!opt_mutable_transaction) {231good_data = false;232return;233}234random_mutable_transaction = *opt_mutable_transaction;235},236[&] {237CoinsCachePair sentinel{};238sentinel.second.SelfRef(sentinel);239size_t dirty_count{0};240CCoinsMapMemoryResource resource;241CCoinsMap coins_map{0, SaltedCoinsCacheHasher{/*deterministic=*/true}, CCoinsMap::key_equal{}, &resource};242LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 10'000) {243CCoinsCacheEntry coins_cache_entry;244if (fuzzed_data_provider.ConsumeBool()) {245coins_cache_entry.coin = random_coin;246} else {247const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);248if (!opt_coin) {249good_data = false;250return;
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.