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.
251// Since this entry will be erased,252// we can move the coin into us instead of copying it253itUs->second.coin = std::move(it->second.coin);254} else {255itUs->second.coin = it->second.coin;256}257cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();258if (!itUs->second.IsDirty()) {259CCoinsCacheEntry::SetDirty(*itUs, m_sentinel);260++m_dirty_count;261}262// NOTE: It isn't safe to mark the coin as FRESH in the parent263// cache. If it already existed and was spent in the parent264// cache then marking it FRESH would prevent that spentness265// from being flushed to the grandparent.266}267}268}269SetBestBlock(in_block_hash);270}272void CCoinsViewCache::Flush(bool reallocate_cache)273{274auto cursor{CoinsViewCacheCursor(m_dirty_count, m_sentinel, cacheCoins, /*will_erase=*/true)};275base->BatchWrite(cursor, m_block_hash);276Assume(m_dirty_count == 0);277cacheCoins.clear();278if (reallocate_cache) {279ReallocateCache();280}281cachedCoinsUsage = 0;282}284void CCoinsViewCache::Sync()285{286auto cursor{CoinsViewCacheCursor(m_dirty_count, m_sentinel, cacheCoins, /*will_erase=*/false)};287base->BatchWrite(cursor, m_block_hash);288Assume(m_dirty_count == 0);289if (m_sentinel.second.Next() != &m_sentinel) {290/* BatchWrite must clear flags of all entries */291throw std::logic_error("Not all unspent flagged entries were cleared");292}293}295void CCoinsViewCache::Reset() noexcept296{297cacheCoins.clear();298cachedCoinsUsage = 0;299m_dirty_count = 0;300SetBestBlock(uint256::ZERO);301}303void CCoinsViewCache::Uncache(const COutPoint& hash)304{305CCoinsMap::iterator it = cacheCoins.find(hash);306if (it != cacheCoins.end() && !it->second.IsDirty()) {307Assume(TrySub(cachedCoinsUsage, it->second.coin.DynamicMemoryUsage()));308TRACEPOINT(utxocache, uncache,309hash.hash.data(),310(uint32_t)hash.n,311(uint32_t)it->second.coin.nHeight,312(int64_t)it->second.coin.out.nValue,313(bool)it->second.coin.IsCoinBase());314cacheCoins.erase(it);315}316}318unsigned int CCoinsViewCache::GetCacheSize() const {319return cacheCoins.size();320}322bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const323{324if (!tx.IsCoinBase()) {325for (unsigned int i = 0; i < tx.vin.size(); i++) {326if (!HaveCoin(tx.vin[i].prevout)) {327return false;328}329}330}331return true;332}334void CCoinsViewCache::ReallocateCache()335{336// Cache should be empty when we're calling this.337assert(cacheCoins.size() == 0);338cacheCoins.~CCoinsMap();339m_cache_coins_memory_resource.~CCoinsMapMemoryResource();340::new (&m_cache_coins_memory_resource) CCoinsMapMemoryResource{};341::new (&cacheCoins) CCoinsMap{0, SaltedCoinsCacheHasher{/*deterministic=*/m_deterministic}, CCoinsMap::key_equal{}, &m_cache_coins_memory_resource};342}344void CCoinsViewCache::SanityCheck() const345{346size_t recomputed_usage = 0;347size_t count_dirty = 0;348for (const auto& [_, entry] : cacheCoins) {349if (entry.coin.IsSpent()) {350assert(entry.IsDirty() && !entry.IsFresh()); // A spent coin must be dirty and cannot be fresh351} else {352assert(entry.IsDirty() || !entry.IsFresh()); // An unspent coin must not be fresh if not dirty353}355// Recompute cachedCoinsUsage.356recomputed_usage += entry.coin.DynamicMemoryUsage();358// Count the number of entries we expect in the linked list.359if (entry.IsDirty()) ++count_dirty;360}361// Iterate over the linked list of flagged entries.362size_t count_linked = 0;363for (auto it = m_sentinel.second.Next(); it != &m_sentinel; it = it->second.Next()) {364// Verify linked list integrity.365assert(it->second.Next()->second.Prev() == it);366assert(it->second.Prev()->second.Next() == it);367// Verify they are actually flagged.368assert(it->second.IsDirty());369// Count the number of entries actually in the list.370++count_linked;371}372assert(count_dirty == count_linked && count_dirty == m_dirty_count);373assert(recomputed_usage == cachedCoinsUsage);374}376CCoinsViewCache::ResetGuard CoinsViewOverlay::StartFetching(const CBlock& block LIFETIMEBOUND) noexcept377{378Assert(m_futures.empty());379Assert(m_inputs.empty());380Assert(m_input_head.load(std::memory_order_relaxed) == 0);381Assert(m_input_tail == 0);382if (const auto workers_count{m_thread_pool->WorkersCount()}; workers_count > 0 && block.vtx.size() > 1) {383// Loop through the block inputs and set their prevouts in the queue.384// Filter inputs that spend outputs created earlier in the same block. These outputs will be created385// directly in the cache from the tx that creates them, so they will not be requested from a base view.386std::unordered_set<Txid, SaltedCoinsCacheHasher> earlier_txids;387earlier_txids.reserve(block.vtx.size());388earlier_txids.emplace(block.vtx[0]->GetHash());389for (const auto& tx : block.vtx | std::views::drop(1)) {390for (const auto& input : tx->vin) {391if (!earlier_txids.contains(input.prevout.hash)) m_inputs.emplace_back(input.prevout);392}393earlier_txids.emplace(tx->GetHash());394}395// Only submit tasks if we have something to fetch.396if (m_inputs.size()) {397std::vector<std::function<void()>> tasks(workers_count, [this] {398while (ProcessInput()) {}399});400if (auto futures{m_thread_pool->Submit(std::move(tasks))}) {401m_futures = std::move(*futures);402} else {403// Submit can fail if a shared owner of the thread pool outside of this class calls Stop() or404// Interrupt() on a different thread after we call WorkersCount() above. In that case parallel405// fetching will not make progress, so we clear the inputs to fall back to single threaded fetching.406LogWarning("Failed to submit prevout fetch tasks (%s); falling back to single-threaded fetching for this block.", SubmitErrorString(futures.error()));407m_inputs.clear();408StopFetching(); // Assert nothing changed if we failed to start tasks.409}410}411}412return CreateResetGuard();413}415static const uint64_t MIN_TRANSACTION_OUTPUT_WEIGHT{WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut())};416static const uint64_t MAX_OUTPUTS_PER_BLOCK{MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT};418const Coin& AccessByTxid(const CCoinsViewCache& view, const Txid& txid)419{420COutPoint iter(txid, 0);421while (iter.n < MAX_OUTPUTS_PER_BLOCK) {422const Coin& alternate = view.AccessCoin(iter);423if (!alternate.IsSpent()) return alternate;424++iter.n;425}426return coinEmpty;427}429template <typename ReturnType, typename Func>430static ReturnType ExecuteBackedWrapper(Func func, const std::vector<std::function<void()>>& err_callbacks)431{432try {433return func();434} catch(const std::runtime_error& e) {435for (const auto& f : err_callbacks) {436f();437}438LogError("Error reading from database: %s\n", e.what());439// Starting the shutdown sequence and returning false to the caller would be440// interpreted as 'entry not found' (as opposed to unable to read data), and441// could lead to invalid interpretation. Just exit immediately, as we can't442// continue anyway, and all writes should be atomic.443std::abort();444}445}447std::optional<Coin> CCoinsViewErrorCatcher::GetCoin(const COutPoint& outpoint) const448{449return ExecuteBackedWrapper<std::optional<Coin>>([&]() { return CCoinsViewBacked::GetCoin(outpoint); }, m_err_callbacks);450}452bool CCoinsViewErrorCatcher::HaveCoin(const COutPoint& outpoint) const453{454return ExecuteBackedWrapper<bool>([&]() { return CCoinsViewBacked::HaveCoin(outpoint); }, m_err_callbacks);455}457std::optional<Coin> CCoinsViewErrorCatcher::PeekCoin(const COutPoint& outpoint) const458{459return ExecuteBackedWrapper<std::optional<Coin>>([&]() { return CCoinsViewBacked::PeekCoin(outpoint); }, m_err_callbacks);460}