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}253m_dirty_blockindex.insert(pindexNew);255return pindexNew;256}258void BlockManager::AddUnlinkedBlock(CBlockIndex* block)259{260AssertLockHeld(cs_main);261Assume(block != nullptr);262Assume(block->nStatus & BLOCK_HAVE_DATA);263auto range = m_blocks_unlinked.equal_range(block->pprev);264for (auto it = range.first; it != range.second; ++it) {265if (it->second == block) return; // don't insert duplicates266}267m_blocks_unlinked.emplace(block->pprev, block);268}270void BlockManager::PruneOneBlockFile(const int fileNumber)271{272AssertLockHeld(cs_main);274for (auto& entry : m_block_index) {275CBlockIndex* pindex = &entry.second;276if (pindex->nFile == fileNumber) {277pindex->nStatus &= ~BLOCK_HAVE_DATA;278pindex->nStatus &= ~BLOCK_HAVE_UNDO;279pindex->nFile = 0;280pindex->nDataPos = 0;281pindex->nUndoPos = 0;282m_dirty_blockindex.insert(pindex);284// Prune from m_blocks_unlinked -- any block we prune would have285// to be downloaded again in order to consider its chain, at which286// point it would be considered as a candidate for287// m_blocks_unlinked or setBlockIndexCandidates.288auto range = m_blocks_unlinked.equal_range(pindex->pprev);289while (range.first != range.second) {290std::multimap<CBlockIndex*, CBlockIndex*>::iterator _it = range.first;291range.first++;292if (_it->second == pindex) {293m_blocks_unlinked.erase(_it);294}295}296}297}299m_blockfile_info.at(fileNumber) = CBlockFileInfo{};300m_dirty_fileinfo.insert(fileNumber);301}303void BlockManager::FindFilesToPruneManual(304std::set<int>& setFilesToPrune,305int nManualPruneHeight,306const Chainstate& chain)307{308assert(IsPruneMode() && nManualPruneHeight > 0);310LOCK(::cs_main);311if (chain.m_chain.Height() < 0) {312return;313}315const auto [min_block_to_prune, last_block_can_prune] = chain.GetPruneRange(nManualPruneHeight);317int count = 0;318for (int fileNumber = 0; fileNumber < this->MaxBlockfileNum(); fileNumber++) {319const auto& fileinfo = m_blockfile_info[fileNumber];320if (fileinfo.nSize == 0 || fileinfo.nHeightLast > (unsigned)last_block_can_prune || fileinfo.nHeightFirst < (unsigned)min_block_to_prune) {321continue;322}324PruneOneBlockFile(fileNumber);325setFilesToPrune.insert(fileNumber);326count++;327}328LogInfo("[%s] Prune (Manual): prune_height=%d removed %d blk/rev pairs",329chain.GetRole(), last_block_can_prune, count);330}332void BlockManager::FindFilesToPrune(333std::set<int>& setFilesToPrune,334int last_prune,335const Chainstate& chain,336ChainstateManager& chainman)337{338LOCK(::cs_main);339// Compute `target` value with maximum size (in bytes) of blocks below the340// `last_prune` height which should be preserved and not pruned. The341// `target` value will be derived from the -prune preference provided by the342// user. If there is a historical chainstate being used to populate indexes343// and validate the snapshot, the target is divided by two so half of the344// block storage will be reserved for the historical chainstate, and the345// other half will be reserved for the most-work chainstate.346const int num_chainstates{chainman.HistoricalChainstate() ? 2 : 1};347const auto target = std::max(348MIN_DISK_SPACE_FOR_BLOCK_FILES, GetPruneTarget() / num_chainstates);349const uint64_t target_sync_height = chainman.m_best_header->nHeight;351if (chain.m_chain.Height() < 0 || target == 0) {352return;353}354if (static_cast<uint64_t>(chain.m_chain.Height()) <= chainman.GetParams().PruneAfterHeight()) {355return;356}358const auto [min_block_to_prune, last_block_can_prune] = chain.GetPruneRange(last_prune);360uint64_t nCurrentUsage = CalculateCurrentUsage();361// We don't check to prune until after we've allocated new space for files362// So we should leave a buffer under our target to account for another allocation363// before the next pruning.364uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;365uint64_t nBytesToPrune;366int count = 0;368if (nCurrentUsage + nBuffer >= target) {369// On a prune event, the chainstate DB is flushed.370// To avoid excessive prune events negating the benefit of high dbcache371// values, we should not prune too rapidly.372// So when pruning in IBD, increase the buffer to avoid a re-prune too soon.373const auto chain_tip_height = chain.m_chain.Height();374if (chainman.IsInitialBlockDownload() && target_sync_height > (uint64_t)chain_tip_height) {375// Since this is only relevant during IBD, we assume blocks are at least 1 MB on average376static constexpr uint64_t average_block_size = 1000000; /* 1 MB */377const uint64_t remaining_blocks = target_sync_height - chain_tip_height;378nBuffer += average_block_size * remaining_blocks;379}381for (int fileNumber = 0; fileNumber < this->MaxBlockfileNum(); fileNumber++) {382const auto& fileinfo = m_blockfile_info[fileNumber];383nBytesToPrune = fileinfo.nSize + fileinfo.nUndoSize;385if (fileinfo.nSize == 0) {386continue;387}389if (nCurrentUsage + nBuffer < target) { // are we below our target?390break;391}393// don't prune files that could have a block that's not within the allowable394// prune range for the chain being pruned.395if (fileinfo.nHeightLast > (unsigned)last_block_can_prune || fileinfo.nHeightFirst < (unsigned)min_block_to_prune) {396continue;397}399PruneOneBlockFile(fileNumber);400// Queue up the files for removal401setFilesToPrune.insert(fileNumber);402nCurrentUsage -= nBytesToPrune;403count++;404}405}407LogDebug(BCLog::PRUNE, "[%s] target=%dMiB actual=%dMiB diff=%dMiB min_height=%d max_prune_height=%d removed %d blk/rev pairs\n",408chain.GetRole(), target / 1_MiB, nCurrentUsage / 1_MiB,409(int64_t(target) - int64_t(nCurrentUsage)) / int64_t(1_MiB),410min_block_to_prune, last_block_can_prune, count);411}413void BlockManager::UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) {414AssertLockHeld(::cs_main);415m_prune_locks[name] = lock_info;416}418bool BlockManager::DeletePruneLock(const std::string& name)419{420AssertLockHeld(::cs_main);421return m_prune_locks.erase(name) > 0;422}424CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)425{426AssertLockHeld(cs_main);428if (hash.IsNull()) {429return nullptr;430}432const auto [mi, inserted]{m_block_index.try_emplace(hash)};433CBlockIndex* pindex = &(*mi).second;434if (inserted) {435pindex->phashBlock = &((*mi).first);436}437return pindex;438}440bool BlockManager::LoadBlockIndex(const std::optional<uint256>& snapshot_blockhash)441{442if (!m_block_tree_db->LoadBlockIndexGuts(443GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); }, m_interrupt)) {444return false;445}447if (snapshot_blockhash) {448const std::optional<AssumeutxoData> maybe_au_data = GetParams().AssumeutxoForBlockhash(*snapshot_blockhash);449if (!maybe_au_data) {450m_opts.notifications.fatalError(strprintf(_("Assumeutxo data not found for the given blockhash '%s'."), snapshot_blockhash->ToString()));451return false;452}453const AssumeutxoData& au_data = *Assert(maybe_au_data);454m_snapshot_height = au_data.height;455CBlockIndex* base{LookupBlockIndex(*snapshot_blockhash)};457// Since m_chain_tx_count (responsible for estimated progress) isn't persisted458// to disk, we must bootstrap the value for assumedvalid chainstates459// from the hardcoded assumeutxo chainparams.460base->m_chain_tx_count = au_data.m_chain_tx_count;461LogInfo("[snapshot] set m_chain_tx_count=%d for %s", au_data.m_chain_tx_count, snapshot_blockhash->ToString());462} else {463// If this isn't called with a snapshot blockhash, make sure the cached snapshot height464// is null. This is relevant during snapshot completion, when the blockman may be loaded465// with a height that then needs to be cleared after the snapshot is fully validated.466m_snapshot_height.reset();467}469Assert(m_snapshot_height.has_value() == snapshot_blockhash.has_value());471// Calculate nChainWork472std::vector<CBlockIndex*> vSortedByHeight{GetAllBlockIndices()};473std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),474CBlockIndexHeightOnlyComparator());476CBlockIndex* previous_index{nullptr};477for (CBlockIndex* pindex : vSortedByHeight) {478if (m_interrupt) return false;479if (previous_index && pindex->nHeight > previous_index->nHeight + 1) {480LogError("%s: block index is non-contiguous, index of height %d missing\n", __func__, previous_index->nHeight + 1);481return false;482}483previous_index = pindex;484pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);485pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);487// We can link the chain of blocks for which we've received transactions at some point, or488// blocks that are assumed-valid on the basis of snapshot load (see489// PopulateAndValidateSnapshot()).490// Pruned nodes may have deleted the block.491if (pindex->nTx > 0) {492if (pindex->pprev) {493if (m_snapshot_height && pindex->nHeight == *m_snapshot_height &&494pindex->GetBlockHash() == *snapshot_blockhash) {495// Should have been set above; don't disturb it with code below.496Assert(pindex->m_chain_tx_count > 0);497} else if (pindex->pprev->m_chain_tx_count > 0) {498pindex->m_chain_tx_count = pindex->pprev->m_chain_tx_count + pindex->nTx;499} else {500pindex->m_chain_tx_count = 0;