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.
2751if (!setFilesToPrune.empty()) {2752fFlushForPrune = true;2753if (!m_blockman.m_have_pruned) {2754m_blockman.m_block_tree_db->WriteFlag("prunedblockfiles", true);2755m_blockman.m_have_pruned = true;2756}2757}2758}2759const auto nNow{NodeClock::now()};2760// The cache is large and we're within 10% and 10 MiB of the limit, but we have time now (not in the middle of a block processing).2761bool fCacheLarge = mode == FlushStateMode::PERIODIC && cache_state >= CoinsCacheSizeState::LARGE;2762// The cache is over the limit, we have to write now.2763bool fCacheCritical = mode == FlushStateMode::IF_NEEDED && cache_state >= CoinsCacheSizeState::CRITICAL;2764// It's been a while since we wrote the block index and chain state to disk. Do this frequently, so we don't need to redownload or reindex after a crash.2765bool fPeriodicWrite = mode == FlushStateMode::PERIODIC && nNow >= m_next_write;2766const auto empty_cache{(mode == FlushStateMode::FORCE_FLUSH) || fCacheLarge || fCacheCritical};2767// Combine all conditions that result in a write to disk.2768bool should_write = (mode == FlushStateMode::FORCE_SYNC) || empty_cache || fPeriodicWrite || fFlushForPrune;2769// Write blocks, block index and best chain related state to disk.2770if (should_write) {2771LogDebug(BCLog::COINDB, "Writing chainstate to disk: flush mode=%s, prune=%d, large=%d, critical=%d, periodic=%d",2772FlushStateModeNames[size_t(mode)], fFlushForPrune, fCacheLarge, fCacheCritical, fPeriodicWrite);2774// Ensure we can write block index2775if (!CheckDiskSpace(m_blockman.m_opts.blocks_dir)) {2776return FatalError(m_chainman.GetNotifications(), state, _("Disk space is too low!"));2777}2778{2779LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH);2781// First make sure all block and undo data is flushed to disk.2782// TODO: Handle return error, or add detailed comment why it is2783// safe to not return an error upon failure.2784if (!m_blockman.FlushChainstateBlockFile(m_chain.Height())) {2785LogWarning("%s: Failed to flush block file.\n", __func__);2786}2787}2789// Then update all block file information (which may refer to block and undo files).2790{2791LOG_TIME_MILLIS_WITH_CATEGORY("write block index to disk", BCLog::BENCH);2793m_blockman.WriteBlockIndexDB();2794}2795// Finally remove any pruned files2796if (fFlushForPrune) {2797LOG_TIME_MILLIS_WITH_CATEGORY("unlink pruned files", BCLog::BENCH);2799m_blockman.UnlinkPrunedFiles(setFilesToPrune);2800}2802if (!CoinsTip().GetBestBlock().IsNull()) {2803// Typical Coin structures on disk are around 48 bytes in size.2804// Pushing a new one to the database can cause it to be written2805// twice (once in the log, and once in the tables). This is already2806// an overestimation, as most will delete an existing entry or2807// overwrite one. Still, use a conservative safety factor of 2.2808if (!CheckDiskSpace(m_chainman.m_options.datadir, 48 * 2 * 2 * CoinsTip().GetDirtyCount())) {2809return FatalError(m_chainman.GetNotifications(), state, _("Disk space is too low!"));2810}2811// Flush the chainstate (which may refer to block index entries).2812empty_cache ? CoinsTip().Flush() : CoinsTip().Sync();2813m_last_flushed_block = m_blockman.LookupBlockIndex(CoinsTip().GetBestBlock());2814full_flush_completed = true;2815TRACEPOINT(utxocache, flush,2816int64_t{Ticks<std::chrono::microseconds>(NodeClock::now() - nNow)},2817(uint32_t)mode,2818(uint64_t)coins_count,2819(uint64_t)coins_mem_usage,2820(bool)fFlushForPrune);2821}2822}2824if (should_write || m_next_write == NodeClock::time_point::max()) {2825constexpr auto range{DATABASE_WRITE_INTERVAL_MAX - DATABASE_WRITE_INTERVAL_MIN};2826m_next_write = FastRandomContext().rand_uniform_delay(NodeClock::now() + DATABASE_WRITE_INTERVAL_MIN, range);2827}2828}2829if (full_flush_completed) {2830if (m_chainman.m_options.signals) {2831m_chainman.m_options.signals->ChainStateFlushed(this->GetRole(), GetLocator(m_last_flushed_block));2832}2834if (!m_chainman.m_interrupt && ShouldCompactChainstate(m_chainman.IsInitialBlockDownload())) {2835try {2836CoinsDB().CompactFullAsync();2837} catch (const std::exception& e) {2838LogWarning("Failed to start chainstate compaction (%s)", e.what());2839}2840}2841}2842} catch (const std::runtime_error& e) {2843return FatalError(m_chainman.GetNotifications(), state, strprintf(_("System error while flushing: %s"), e.what()));2844}2845return true;2846}2848void Chainstate::ForceFlushStateToDisk(bool wipe_cache)2849{2850BlockValidationState state;2851if (!this->FlushStateToDisk(state, wipe_cache ? FlushStateMode::FORCE_FLUSH : FlushStateMode::FORCE_SYNC)) {2852LogWarning("Failed to force flush state (%s)", state.ToString());2853}2854}2856void Chainstate::PruneAndFlush()2857{2858BlockValidationState state;2859m_blockman.m_check_for_pruning = true;2860if (!this->FlushStateToDisk(state, FlushStateMode::NONE)) {2861LogWarning("Failed to flush state (%s)", state.ToString());2862}2863}2865static void UpdateTipLog(2866const ChainstateManager& chainman,2867const CCoinsViewCache& coins_tip,2868const CBlockIndex* tip,2869const std::string& func_name,2870const std::string& prefix,2871const std::string& warning_messages,2872const bool background_validation) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)2873{2875AssertLockHeld(::cs_main);2877// Disable rate limiting as this may log frequently during IBD.2878LogInfo(util::log::NO_RATE_LIMIT, "%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n",2879prefix, func_name,2880tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion,2881log(tip->nChainWork.getdouble()) / log(2.0), tip->m_chain_tx_count,2882FormatISO8601DateTime(tip->GetBlockTime()),2883background_validation ? chainman.GetBackgroundVerificationProgress(*tip) : chainman.GuessVerificationProgress(tip),2884coins_tip.DynamicMemoryUsage() / double(1_MiB),2885coins_tip.GetCacheSize(),2886!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages) : "");2887}2889void Chainstate::UpdateTip(const CBlockIndex* pindexNew)2890{2891AssertLockHeld(::cs_main);2892const auto& coins_tip = this->CoinsTip();2894// The remainder of the function isn't relevant if we are not acting on2895// the active chainstate, so return if need be.2896if (this != &m_chainman.ActiveChainstate()) {2897// Only log every so often so that we don't bury log messages at the tip.2898constexpr int BACKGROUND_LOG_INTERVAL = 2000;2899if (pindexNew->nHeight % BACKGROUND_LOG_INTERVAL == 0) {2900UpdateTipLog(m_chainman, coins_tip, pindexNew, __func__, "[background validation] ", "", /*background_validation=*/true);2901}2902return;2903}2905// New best block2906if (m_mempool) {2907m_mempool->AddTransactionsUpdated(1);2908}2910std::vector<bilingual_str> warning_messages;2911if (!m_chainman.IsInitialBlockDownload()) {2912auto bits = m_chainman.m_versionbitscache.CheckUnknownActivations(pindexNew, m_chainman.GetParams());2913for (auto [bit, active] : bits) {2914const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);2915if (active) {2916m_chainman.GetNotifications().warningSet(kernel::Warning::UNKNOWN_NEW_RULES_ACTIVATED, warning);2917} else {2918warning_messages.push_back(warning);2919}2920}2921}2922UpdateTipLog(m_chainman, coins_tip, pindexNew, __func__, "",2923util::Join(warning_messages, Untranslated(", ")).original, /*background_validation=*/false);2924}2926/** Disconnect m_chain's tip.2927* After calling, the mempool will be in an inconsistent state, with2928* transactions from disconnected blocks being added to disconnectpool. You2929* should make the mempool consistent again by calling MaybeUpdateMempoolForReorg.2930* with cs_main held.2931*2932* If disconnectpool is nullptr, then no disconnected transactions are added to2933* disconnectpool (note that the caller is responsible for mempool consistency2934* in any case).2935*/2936bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTransactions* disconnectpool)2937{2938AssertLockHeld(cs_main);2939if (m_mempool) AssertLockHeld(m_mempool->cs);2941CBlockIndex *pindexDelete = m_chain.Tip();2942assert(pindexDelete);2943assert(pindexDelete->pprev);2944// Read block from disk.2945std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();2946CBlock& block = *pblock;2947if (!m_blockman.ReadBlock(block, *pindexDelete)) {2948LogError("DisconnectTip(): Failed to read block\n");2949return false;2950}2951// Apply the block atomically to the chain state.2952const auto time_start{SteadyClock::now()};2953{2954CCoinsViewCache view(&CoinsTip());2955assert(view.GetBestBlock() == pindexDelete->GetBlockHash());2956if (DisconnectBlock(block, pindexDelete, view) != DISCONNECT_OK) {2957LogError("DisconnectTip(): DisconnectBlock %s failed\n", pindexDelete->GetBlockHash().ToString());2958return false;2959}2960view.Flush(/*reallocate_cache=*/false); // local CCoinsViewCache goes out of scope2961}2962LogDebug(BCLog::BENCH, "- Disconnect block: %.2fms\n",2963Ticks<MillisecondsDouble>(SteadyClock::now() - time_start));2965{2966// Prune locks that began at or after the tip should be moved backward so they get a chance to reorg2967const int max_height_first{pindexDelete->nHeight - 1};2968for (auto& prune_lock : m_blockman.m_prune_locks) {2969if (prune_lock.second.height_first <= max_height_first) continue;2971prune_lock.second.height_first = max_height_first;2972LogDebug(BCLog::PRUNE, "%s prune lock moved back to %d\n", prune_lock.first, max_height_first);2973}2974}2976// Write the chain state to disk, if necessary.2977if (!FlushStateToDisk(state, FlushStateMode::IF_NEEDED)) {2978return false;2979}2981if (disconnectpool && m_mempool) {2982// Save transactions to re-add to mempool at end of reorg. If any entries are evicted for2983// exceeding memory limits, remove them and their descendants from the mempool.2984for (auto&& evicted_tx : disconnectpool->AddTransactionsFromBlock(block.vtx)) {2985m_mempool->removeRecursive(*evicted_tx, MemPoolRemovalReason::REORG);2986}2987}2989m_chain.SetTip(*pindexDelete->pprev);2990m_chainman.UpdateIBDStatus();2992UpdateTip(pindexDelete->pprev);2993// Let wallets know transactions went from 1-confirmed to2994// 0-confirmed or conflicted:2995if (m_chainman.m_options.signals) {2996m_chainman.m_options.signals->BlockDisconnected(std::move(pblock), pindexDelete);2997}2998return true;2999}