Inside Bitcoin
Opening the selected file…
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) 2012-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>7#include <consensus/consensus.h>8#include <primitives/block.h>9#include <random.h>10#include <uint256.h>11#include <util/log.h>12#include <util/threadpool.h>13#include <util/trace.h>15#include <ranges>16#include <unordered_set>18TRACEPOINT_SEMAPHORE(utxocache, add);19TRACEPOINT_SEMAPHORE(utxocache, spent);20TRACEPOINT_SEMAPHORE(utxocache, uncache);22SaltedCoinsCacheHasher::SaltedCoinsCacheHasher(bool deterministic)23: m_hasher{24deterministic ? 0x8e819f2607a18de6 : FastRandomContext().rand64(),25deterministic ? 0xf4020d2e3983b0eb : FastRandomContext().rand64()}26{27}29CoinsViewEmpty& CoinsViewEmpty::Get()30{31static CoinsViewEmpty instance;32return instance;33}35std::optional<Coin> CCoinsViewCache::PeekCoin(const COutPoint& outpoint) const36{37if (auto it{cacheCoins.find(outpoint)}; it != cacheCoins.end()) {38return it->second.coin.IsSpent() ? std::nullopt : std::optional{it->second.coin};39}40return base->PeekCoin(outpoint);41}43CCoinsViewCache::CCoinsViewCache(CCoinsView* in_base, bool deterministic) :44CCoinsViewBacked(in_base), m_deterministic(deterministic),45cacheCoins(0, SaltedCoinsCacheHasher{/*deterministic=*/deterministic}, CCoinsMap::key_equal{}, &m_cache_coins_memory_resource)46{47m_sentinel.second.SelfRef(m_sentinel);48}50size_t CCoinsViewCache::DynamicMemoryUsage() const {51return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;52}54std::optional<Coin> CCoinsViewCache::FetchCoinFromBase(const COutPoint& outpoint) const55{56return base->GetCoin(outpoint);57}59CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const {60const auto [ret, inserted] = cacheCoins.try_emplace(outpoint);61if (inserted) {62if (auto coin{FetchCoinFromBase(outpoint)}) {63ret->second.coin = std::move(*coin);64cachedCoinsUsage += ret->second.coin.DynamicMemoryUsage();65Assert(!ret->second.coin.IsSpent());66} else {67cacheCoins.erase(ret);68return cacheCoins.end();69}70}71return ret;72}74std::optional<Coin> CCoinsViewCache::GetCoin(const COutPoint& outpoint) const75{76if (auto it{FetchCoin(outpoint)}; it != cacheCoins.end() && !it->second.coin.IsSpent()) return it->second.coin;77return std::nullopt;78}80void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {81assert(!coin.IsSpent());82if (coin.out.scriptPubKey.IsUnspendable()) return;83CCoinsMap::iterator it;84bool inserted;85std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::tuple<>());86bool fresh = false;87if (!possible_overwrite) {88if (!it->second.coin.IsSpent()) {89throw std::logic_error("Attempted to overwrite an unspent coin (when possible_overwrite is false)");90}91// If the coin exists in this cache as a spent coin and is DIRTY, then92// its spentness hasn't been flushed to the parent cache. We're93// re-adding the coin to this cache now but we can't mark it as FRESH.94// If we mark it FRESH and then spend it before the cache is flushed95// we would remove it from this cache and would never flush spentness96// to the parent cache.97//98// Re-adding a spent coin can happen in the case of a re-org (the coin99// is 'spent' when the block adding it is disconnected and then100// re-added when it is also added in a newly connected block).101//102// If the coin doesn't exist in the current cache, or is spent but not103// DIRTY, then it can be marked FRESH.104fresh = !it->second.IsDirty();105}106if (!inserted) {107Assume(TrySub(m_dirty_count, it->second.IsDirty()));108Assume(TrySub(cachedCoinsUsage, it->second.coin.DynamicMemoryUsage()));109}110it->second.coin = std::move(coin);111CCoinsCacheEntry::SetDirty(*it, m_sentinel);112++m_dirty_count;113if (fresh) CCoinsCacheEntry::SetFresh(*it, m_sentinel);114cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();115TRACEPOINT(utxocache, add,116outpoint.hash.data(),117(uint32_t)outpoint.n,118(uint32_t)it->second.coin.nHeight,119(int64_t)it->second.coin.out.nValue,120(bool)it->second.coin.IsCoinBase());121}123void CCoinsViewCache::EmplaceCoinInternalDANGER(const COutPoint& outpoint, Coin&& coin) {124const auto mem_usage{coin.DynamicMemoryUsage()};125auto [it, inserted] = cacheCoins.try_emplace(outpoint, std::move(coin));126if (inserted) {127CCoinsCacheEntry::SetDirty(*it, m_sentinel);128++m_dirty_count;129cachedCoinsUsage += mem_usage;130}131}133void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {134bool fCoinbase = tx.IsCoinBase();135const Txid& txid = tx.GetHash();136for (size_t i = 0; i < tx.vout.size(); ++i) {137bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;138// Coinbase transactions can always be overwritten, in order to correctly139// deal with the pre-BIP30 occurrences of duplicate coinbase transactions.140cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite);141}142}144bool CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {145CCoinsMap::iterator it = FetchCoin(outpoint);146if (it == cacheCoins.end()) return false;147Assume(TrySub(m_dirty_count, it->second.IsDirty()));148Assume(TrySub(cachedCoinsUsage, it->second.coin.DynamicMemoryUsage()));149TRACEPOINT(utxocache, spent,150outpoint.hash.data(),151(uint32_t)outpoint.n,152(uint32_t)it->second.coin.nHeight,153(int64_t)it->second.coin.out.nValue,154(bool)it->second.coin.IsCoinBase());155if (moveout) {156*moveout = std::move(it->second.coin);157}158if (it->second.IsFresh()) {159cacheCoins.erase(it);160} else {161CCoinsCacheEntry::SetDirty(*it, m_sentinel);162++m_dirty_count;163it->second.coin.Clear();164}165return true;166}168static const Coin coinEmpty;170const Coin& CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {171CCoinsMap::const_iterator it = FetchCoin(outpoint);172if (it == cacheCoins.end()) {173return coinEmpty;174} else {175return it->second.coin;176}177}179bool CCoinsViewCache::HaveCoin(const COutPoint& outpoint) const180{181CCoinsMap::const_iterator it = FetchCoin(outpoint);182return (it != cacheCoins.end() && !it->second.coin.IsSpent());183}185bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {186CCoinsMap::const_iterator it = cacheCoins.find(outpoint);187return (it != cacheCoins.end() && !it->second.coin.IsSpent());188}190uint256 CCoinsViewCache::GetBestBlock() const {191if (m_block_hash.IsNull())192m_block_hash = base->GetBestBlock();193return m_block_hash;194}196void CCoinsViewCache::SetBestBlock(const uint256& in_block_hash)197{198m_block_hash = in_block_hash;199}201void CCoinsViewCache::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& in_block_hash)202{203for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {204if (!it->second.IsDirty()) { // TODO a cursor can only contain dirty entries205continue;206}207auto [itUs, inserted]{cacheCoins.try_emplace(it->first)};208if (inserted) {209if (it->second.IsFresh() && it->second.coin.IsSpent()) {210cacheCoins.erase(itUs); // TODO fresh coins should have been removed at spend211} else {212// The parent cache does not have an entry, while the child cache does.213// Move the data up and mark it as dirty.214CCoinsCacheEntry& entry{itUs->second};215assert(entry.coin.DynamicMemoryUsage() == 0);216if (cursor.WillErase(*it)) {217// Since this entry will be erased,218// we can move the coin into us instead of copying it219entry.coin = std::move(it->second.coin);220} else {221entry.coin = it->second.coin;222}223CCoinsCacheEntry::SetDirty(*itUs, m_sentinel);224++m_dirty_count;225cachedCoinsUsage += entry.coin.DynamicMemoryUsage();226// We can mark it FRESH in the parent if it was FRESH in the child227// Otherwise it might have just been flushed from the parent's cache228// and already exist in the grandparent229if (it->second.IsFresh()) CCoinsCacheEntry::SetFresh(*itUs, m_sentinel);230}231} else {232// Found the entry in the parent cache233if (it->second.IsFresh() && !itUs->second.coin.IsSpent()) {234// The coin was marked FRESH in the child cache, but the coin235// exists in the parent cache. If this ever happens, it means236// the FRESH flag was misapplied and there is a logic error in237// the calling code.238throw std::logic_error("FRESH flag misapplied to coin that exists in parent cache");239}241if (itUs->second.IsFresh() && it->second.coin.IsSpent()) {242// The grandparent cache does not have an entry, and the coin243// has been spent. We can just delete it from the parent cache.244Assume(TrySub(m_dirty_count, itUs->second.IsDirty()));245Assume(TrySub(cachedCoinsUsage, itUs->second.coin.DynamicMemoryUsage()));246cacheCoins.erase(itUs);247} else {248// A normal modification.249Assume(TrySub(cachedCoinsUsage, itUs->second.coin.DynamicMemoryUsage()));250if (cursor.WillErase(*it)) {