LCOV - code coverage report
Current view: top level - src/node - blockstorage.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 434 555 78.2 %
Date: 2026-06-25 07:23:43 Functions: 45 47 95.7 %

          Line data    Source code
       1             : // Copyright (c) 2011-2021 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <node/blockstorage.h>
       6             : 
       7             : #include <chain.h>
       8             : #include <chainparams.h>
       9             : #include <clientversion.h>
      10             : #include <consensus/validation.h>
      11             : #include <flatfile.h>
      12             : #include <fs.h>
      13             : #include <hash.h>
      14             : #include <pow.h>
      15             : #include <shutdown.h>
      16             : #include <streams.h>
      17             : #include <undo.h>
      18             : #include <util/system.h>
      19             : #include <validation.h>
      20             : #include <walletinitinterface.h>
      21             : 
      22             : #include <map>
      23             : #include <ranges>
      24             : #include <unordered_map>
      25             : 
      26             : namespace node {
      27             : std::atomic_bool fImporting(false);
      28             : std::atomic_bool fReindex(false);
      29             : bool fPruneMode = false;
      30             : uint64_t nPruneTarget = 0;
      31             : 
      32   764669065 : bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
      33             : {
      34             :     // First sort by most total work, ...
      35   764669065 :     if (pa->nChainWork > pb->nChainWork) return false;
      36   628957415 :     if (pa->nChainWork < pb->nChainWork) return true;
      37             : 
      38             :     // ... then by earliest time received, ...
      39     7792486 :     if (pa->nSequenceId < pb->nSequenceId) return false;
      40     7736116 :     if (pa->nSequenceId > pb->nSequenceId) return true;
      41             : 
      42             :     // Use pointer address as tie breaker (should only happen with blocks
      43             :     // loaded from disk, as those all have id 0).
      44     7718435 :     if (pa < pb) return false;
      45     7717800 :     if (pa > pb) return true;
      46             : 
      47             :     // Identical blocks.
      48     7716784 :     return false;
      49   764669065 : }
      50             : 
      51     5781602 : bool CBlockIndexHeightOnlyComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
      52             : {
      53     5781602 :     return pa->nHeight < pb->nHeight;
      54             : }
      55             : 
      56             : static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
      57             : static FlatFileSeq BlockFileSeq();
      58             : static FlatFileSeq UndoFileSeq();
      59             : 
      60        5984 : std::vector<CBlockIndex*> BlockManager::GetAllBlockIndices()
      61             : {
      62        5984 :     AssertLockHeld(cs_main);
      63        5984 :     std::vector<CBlockIndex*> rv;
      64        5984 :     rv.reserve(m_block_index.size());
      65      653908 :     for (auto& [_, block_index] : m_block_index) {
      66      647924 :         rv.push_back(&block_index);
      67             :     }
      68        5984 :     return rv;
      69        5984 : }
      70             : 
      71     2534241 : CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash)
      72             : {
      73     2534241 :     AssertLockHeld(cs_main);
      74     2534241 :     BlockMap::iterator it = m_block_index.find(hash);
      75     2534241 :     return it == m_block_index.end() ? nullptr : &it->second;
      76             : }
      77             : 
      78      259504 : const CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) const
      79             : {
      80      259504 :     AssertLockHeld(cs_main);
      81      259504 :     BlockMap::const_iterator it = m_block_index.find(hash);
      82      259504 :     return it == m_block_index.end() ? nullptr : &it->second;
      83             : }
      84             : 
      85      270019 : CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, const uint256& hash, CBlockIndex*& best_header,
      86             :                                            enum BlockStatus nStatus)
      87             : {
      88      270019 :     assert(!(nStatus & BLOCK_FAILED_MASK)); // no failed blocks allowed
      89      270019 :     AssertLockHeld(cs_main);
      90             : 
      91      270019 :     auto [mi, inserted] = m_block_index.try_emplace(hash, block);
      92      270019 :     if (!inserted) {
      93           5 :         return &mi->second;
      94             :     }
      95      270014 :     CBlockIndex* pindexNew = &(*mi).second;
      96             : 
      97             :     // We assign the sequence id to blocks only when the full data is available,
      98             :     // to avoid miners withholding blocks but broadcasting headers, to get a
      99             :     // competitive advantage.
     100      270014 :     pindexNew->nSequenceId = 0;
     101             : 
     102      270014 :     pindexNew->phashBlock = &((*mi).first);
     103      270014 :     BlockMap::iterator miPrev = m_block_index.find(block.hashPrevBlock);
     104      270014 :     if (miPrev != m_block_index.end()) {
     105      268946 :         pindexNew->pprev = &(*miPrev).second;
     106      268946 :         pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
     107      268946 :         pindexNew->BuildSkip();
     108      268946 :     }
     109      270014 :     pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime);
     110      270014 :     pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + GetBlockProof(*pindexNew);
     111      270014 :     if (nStatus & BLOCK_VALID_MASK) {
     112      270008 :         pindexNew->RaiseValidity(nStatus);
     113      270008 :         if (best_header == nullptr || best_header->nChainWork < pindexNew->nChainWork) {
     114      264898 :             best_header = pindexNew;
     115      264898 :         }
     116      270008 :     } else {
     117           6 :         pindexNew->RaiseValidity(BLOCK_VALID_TREE); // required validity level
     118           6 :         pindexNew->nStatus |= nStatus;
     119             :     }
     120             : 
     121      270014 :     m_dirty_blockindex.insert(pindexNew);
     122             : 
     123             :     // track prevBlockHash -> pindex (multimap)
     124      270014 :     if (pindexNew->pprev) {
     125      268946 :         m_prev_block_index.emplace(pindexNew->pprev->GetBlockHash(), pindexNew);
     126      268946 :     }
     127             : 
     128      270014 :     return pindexNew;
     129      270019 : }
     130             : 
     131           3 : void BlockManager::PruneOneBlockFile(const int fileNumber)
     132             : {
     133           3 :     AssertLockHeld(cs_main);
     134           3 :     LOCK(cs_LastBlockFile);
     135             : 
     136         309 :     for (auto& entry : m_block_index) {
     137         306 :         CBlockIndex* pindex = &entry.second;
     138         306 :         if (pindex->nFile == fileNumber) {
     139         203 :             pindex->nStatus &= ~BLOCK_HAVE_DATA;
     140         203 :             pindex->nStatus &= ~BLOCK_HAVE_UNDO;
     141         203 :             pindex->nFile = 0;
     142         203 :             pindex->nDataPos = 0;
     143         203 :             pindex->nUndoPos = 0;
     144         203 :             m_dirty_blockindex.insert(pindex);
     145             : 
     146             :             // Prune from m_blocks_unlinked -- any block we prune would have
     147             :             // to be downloaded again in order to consider its chain, at which
     148             :             // point it would be considered as a candidate for
     149             :             // m_blocks_unlinked or setBlockIndexCandidates.
     150         203 :             auto range = m_blocks_unlinked.equal_range(pindex->pprev);
     151         203 :             while (range.first != range.second) {
     152           0 :                 std::multimap<CBlockIndex*, CBlockIndex*>::iterator _it = range.first;
     153           0 :                 range.first++;
     154           0 :                 if (_it->second == pindex) {
     155           0 :                     m_blocks_unlinked.erase(_it);
     156           0 :                 }
     157             :             }
     158         203 :         }
     159             :     }
     160             : 
     161           3 :     m_blockfile_info.at(fileNumber) = CBlockFileInfo{};
     162           3 :     m_dirty_fileinfo.insert(fileNumber);
     163           3 : }
     164             : 
     165           0 : void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
     166             : {
     167           0 :     assert(fPruneMode && nManualPruneHeight > 0);
     168             : 
     169           0 :     LOCK2(cs_main, cs_LastBlockFile);
     170           0 :     if (chain_tip_height < 0) {
     171           0 :         return;
     172             :     }
     173             : 
     174             :     // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
     175           0 :     unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
     176           0 :     int count = 0;
     177           0 :     for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) {
     178           0 :         if (m_blockfile_info[fileNumber].nSize == 0 || m_blockfile_info[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
     179           0 :             continue;
     180             :         }
     181           0 :         PruneOneBlockFile(fileNumber);
     182           0 :         setFilesToPrune.insert(fileNumber);
     183           0 :         count++;
     184           0 :     }
     185           0 :     LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count);
     186           0 : }
     187             : 
     188          30 : void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd)
     189             : {
     190          30 :     LOCK2(cs_main, cs_LastBlockFile);
     191          30 :     if (chain_tip_height < 0 || nPruneTarget == 0) {
     192          10 :         return;
     193             :     }
     194          20 :     if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
     195          20 :         return;
     196             :     }
     197             : 
     198           0 :     unsigned int nLastBlockWeCanPrune{(unsigned)std::min(prune_height, chain_tip_height - static_cast<int>(MIN_BLOCKS_TO_KEEP))};
     199           0 :     uint64_t nCurrentUsage = CalculateCurrentUsage();
     200             :     // We don't check to prune until after we've allocated new space for files
     201             :     // So we should leave a buffer under our target to account for another allocation
     202             :     // before the next pruning.
     203           0 :     uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
     204             :     uint64_t nBytesToPrune;
     205           0 :     int count = 0;
     206             : 
     207           0 :     if (nCurrentUsage + nBuffer >= nPruneTarget) {
     208             :         // On a prune event, the chainstate DB is flushed.
     209             :         // To avoid excessive prune events negating the benefit of high dbcache
     210             :         // values, we should not prune too rapidly.
     211             :         // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
     212           0 :         if (is_ibd) {
     213             :             // Since this is only relevant during IBD, we use a fixed 10%
     214           0 :             nBuffer += nPruneTarget / 10;
     215           0 :         }
     216             : 
     217           0 :         for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) {
     218           0 :             nBytesToPrune = m_blockfile_info[fileNumber].nSize + m_blockfile_info[fileNumber].nUndoSize;
     219             : 
     220           0 :             if (m_blockfile_info[fileNumber].nSize == 0) {
     221           0 :                 continue;
     222             :             }
     223             : 
     224           0 :             if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
     225           0 :                 break;
     226             :             }
     227             : 
     228             :             // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
     229           0 :             if (m_blockfile_info[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
     230           0 :                 continue;
     231             :             }
     232             : 
     233           0 :             PruneOneBlockFile(fileNumber);
     234             :             // Queue up the files for removal
     235           0 :             setFilesToPrune.insert(fileNumber);
     236           0 :             nCurrentUsage -= nBytesToPrune;
     237           0 :             count++;
     238           0 :         }
     239           0 :     }
     240             : 
     241           0 :     LogPrint(BCLog::PRUNE, "target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
     242             :            nPruneTarget/1024/1024, nCurrentUsage/1024/1024,
     243             :            ((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024,
     244             :            nLastBlockWeCanPrune, count);
     245          30 : }
     246             : 
     247       99399 : void BlockManager::UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) {
     248       99399 :     AssertLockHeld(::cs_main);
     249       99399 :     m_prune_locks[name] = lock_info;
     250       99399 : }
     251             : 
     252      648226 : CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
     253             : {
     254      648226 :     AssertLockHeld(cs_main);
     255             : 
     256      648226 :     if (hash.IsNull()) {
     257        1992 :         return nullptr;
     258             :     }
     259             : 
     260     1292468 :     const auto [mi, inserted]{m_block_index.try_emplace(hash)};
     261      646234 :     CBlockIndex* pindex = &(*mi).second;
     262      646234 :     if (inserted) {
     263      324161 :         pindex->phashBlock = &((*mi).first);
     264      324161 :     }
     265      646234 :     return pindex;
     266      648226 : }
     267             : 
     268        2994 : bool BlockManager::LoadBlockIndex()
     269             : {
     270      651220 :     if (!m_block_tree_db->LoadBlockIndexGuts(GetConsensus(), [this](const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return this->InsertBlockIndex(hash); })) {
     271           0 :         return false;
     272             :     }
     273             : 
     274      327357 :     for (auto& [_, block_index] : m_block_index) {
     275             :         // build m_blockman.m_prev_block_index
     276      324363 :         if (block_index.pprev) {
     277      966963 :             m_prev_block_index.emplace(block_index.pprev->GetBlockHash(), &block_index);
     278      322321 :         }
     279             :     }
     280             : 
     281             :     // Calculate nChainWork
     282        2994 :     std::vector<CBlockIndex*> vSortedByHeight{GetAllBlockIndices()};
     283        2994 :     std::sort(vSortedByHeight.begin(), vSortedByHeight.end(),
     284             :               CBlockIndexHeightOnlyComparator());
     285             : 
     286        2994 :     CBlockIndex* previous_index{nullptr};
     287      327035 :     for (CBlockIndex* pindex : vSortedByHeight) {
     288      324043 :         if (ShutdownRequested()) return false;
     289      324043 :         if (previous_index && pindex->nHeight > previous_index->nHeight + 1) {
     290           2 :             return error("%s: block index is non-contiguous, index of height %d missing", __func__, previous_index->nHeight + 1);
     291             :         }
     292      324041 :         previous_index = pindex;
     293      324041 :         pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex);
     294      324041 :         pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
     295             : 
     296             :         // We can link the chain of blocks for which we've received transactions at some point, or
     297             :         // blocks that are assumed-valid on the basis of snapshot load (see
     298             :         // PopulateAndValidateSnapshot()).
     299             :         // Pruned nodes may have deleted the block.
     300      324041 :         if (pindex->nTx > 0) {
     301      323461 :             if (pindex->pprev) {
     302      321467 :                 if (pindex->pprev->nChainTx > 0) {
     303      321467 :                     pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
     304      321467 :                 } else {
     305           0 :                     pindex->nChainTx = 0;
     306           0 :                     m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
     307             :                 }
     308      321467 :             } else {
     309        1994 :                 pindex->nChainTx = pindex->nTx;
     310             :             }
     311      323461 :         }
     312      324041 :         if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
     313           0 :             pindex->nStatus |= BLOCK_FAILED_CHILD;
     314           0 :             m_dirty_blockindex.insert(pindex);
     315           0 :         }
     316      324041 :         if (pindex->pprev) {
     317      321999 :             pindex->BuildSkip();
     318      321999 :         }
     319             :     }
     320             : 
     321        2992 :     return true;
     322        2994 : }
     323             : 
     324        7520 : bool BlockManager::WriteBlockIndexDB()
     325             : {
     326        7520 :     AssertLockHeld(::cs_main);
     327        7520 :     std::vector<std::pair<int, const CBlockFileInfo*>> vFiles;
     328        7520 :     vFiles.reserve(m_dirty_fileinfo.size());
     329       11098 :     for (std::set<int>::iterator it = m_dirty_fileinfo.begin(); it != m_dirty_fileinfo.end();) {
     330        3578 :         vFiles.emplace_back(*it, &m_blockfile_info[*it]);
     331        3578 :         m_dirty_fileinfo.erase(it++);
     332             :     }
     333        7520 :     std::vector<const CBlockIndex*> vBlocks;
     334        7520 :     vBlocks.reserve(m_dirty_blockindex.size());
     335      255323 :     for (std::set<CBlockIndex*>::iterator it = m_dirty_blockindex.begin(); it != m_dirty_blockindex.end();) {
     336      247803 :         vBlocks.push_back(*it);
     337      247803 :         m_dirty_blockindex.erase(it++);
     338             :     }
     339        7520 :     if (!m_block_tree_db->WriteBatchSync(vFiles, m_last_blockfile, vBlocks)) {
     340           0 :         return false;
     341             :     }
     342        7520 :     return true;
     343        7520 : }
     344             : 
     345        2994 : bool BlockManager::LoadBlockIndexDB()
     346             : {
     347        2994 :     if (!LoadBlockIndex()) {
     348           2 :         return false;
     349             :     }
     350             : 
     351             :     // Load block file info
     352        2992 :     m_block_tree_db->ReadLastBlockFile(m_last_blockfile);
     353        2992 :     m_blockfile_info.resize(m_last_blockfile + 1);
     354        2992 :     LogPrintf("%s: last block file = %i\n", __func__, m_last_blockfile);
     355        5984 :     for (int nFile = 0; nFile <= m_last_blockfile; nFile++) {
     356        2992 :         m_block_tree_db->ReadBlockFileInfo(nFile, m_blockfile_info[nFile]);
     357        2992 :     }
     358        2992 :     LogPrintf("%s: last block file info: %s\n", __func__, m_blockfile_info[m_last_blockfile].ToString());
     359        2992 :     for (int nFile = m_last_blockfile + 1; true; nFile++) {
     360        2992 :         CBlockFileInfo info;
     361        2992 :         if (m_block_tree_db->ReadBlockFileInfo(nFile, info)) {
     362           0 :             m_blockfile_info.push_back(info);
     363           0 :         } else {
     364        2992 :             break;
     365             :         }
     366           0 :     }
     367             : 
     368             :     // Check presence of blk files
     369        2992 :     LogPrintf("Checking all blk files are present...\n");
     370        2992 :     std::set<int> setBlkDataFiles;
     371      326955 :     for (const auto& [_, block_index] : m_block_index) {
     372      323963 :         if (block_index.nStatus & BLOCK_HAVE_DATA) {
     373      323411 :             setBlkDataFiles.insert(block_index.nFile);
     374      323411 :         }
     375             :     }
     376        4982 :     for (std::set<int>::iterator it = setBlkDataFiles.begin(); it != setBlkDataFiles.end(); it++) {
     377        1992 :         FlatFilePos pos(*it, 0);
     378        1992 :         if (CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION).IsNull()) {
     379           2 :             return false;
     380             :         }
     381        1990 :     }
     382             : 
     383             :     // Check whether we have ever pruned block & undo files
     384        2990 :     m_block_tree_db->ReadFlag("prunedblockfiles", m_have_pruned);
     385        2990 :     if (m_have_pruned) {
     386           0 :         LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
     387           0 :     }
     388             : 
     389             :     // Check whether we need to continue reindexing
     390        2990 :     bool fReindexing = false;
     391        2990 :     m_block_tree_db->ReadReindexing(fReindexing);
     392        2990 :     if (fReindexing) fReindex = true;
     393             : 
     394             :     // Migrate old synchronous index data from block index database
     395             :     // to async indexes in separate databases
     396        2990 :     if (!m_block_tree_db->MigrateOldIndexData()) {
     397           0 :         return false;
     398             :     }
     399             : 
     400        2990 :     return true;
     401        2994 : }
     402             : 
     403      356608 : const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
     404             : {
     405      356608 :     const MapCheckpoints& checkpoints = data.mapCheckpoints;
     406             : 
     407      371658 :     for (const MapCheckpoints::value_type& i : checkpoints | std::views::reverse) {
     408      371006 :         const uint256& hash = i.second;
     409      371006 :         const CBlockIndex* pindex = LookupBlockIndex(hash);
     410      371006 :         if (pindex) {
     411      355956 :             return pindex;
     412             :         }
     413             :     }
     414         652 :     return nullptr;
     415      356608 : }
     416             : 
     417        5622 : bool BlockManager::IsBlockPruned(const CBlockIndex* pblockindex)
     418             : {
     419        5622 :     AssertLockHeld(::cs_main);
     420        5622 :     return (m_have_pruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
     421             : }
     422             : 
     423         747 : const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& start_block)
     424             : {
     425         747 :     AssertLockHeld(::cs_main);
     426         747 :     const CBlockIndex* last_block = &start_block;
     427      104045 :     while (last_block->pprev && (last_block->pprev->nStatus & BLOCK_HAVE_DATA)) {
     428      103298 :         last_block = last_block->pprev;
     429             :     }
     430         747 :     return last_block;
     431             : }
     432             : 
     433             : // If we're using -prune with -reindex, then delete block files that will be ignored by the
     434             : // reindex.  Since reindexing works by starting at block file 0 and looping until a blockfile
     435             : // is missing, do the same here to delete any later block files after a gap.  Also delete all
     436             : // rev files since they'll be rewritten by the reindex anyway.  This ensures that m_blockfile_info
     437             : // is in sync with what's actually on disk by the time we start downloading, so that pruning
     438             : // works correctly.
     439           0 : void CleanupBlockRevFiles()
     440             : {
     441           0 :     std::map<std::string, fs::path> mapBlockFiles;
     442             : 
     443             :     // Glob all blk?????.dat and rev?????.dat files from the blocks directory.
     444             :     // Remove the rev files immediately and insert the blk file paths into an
     445             :     // ordered map keyed by block file index.
     446           0 :     LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
     447           0 :     const fs::path& blocksdir = gArgs.GetBlocksDirPath();
     448           0 :     for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
     449           0 :         const std::string path = fs::PathToString(it->path().filename());
     450           0 :         if (fs::is_regular_file(*it) &&
     451           0 :             path.length() == 12 &&
     452           0 :             path.substr(8,4) == ".dat")
     453             :         {
     454           0 :             if (path.substr(0, 3) == "blk") {
     455           0 :                 mapBlockFiles[path.substr(3, 5)] = it->path();
     456           0 :             } else if (path.substr(0, 3) == "rev") {
     457           0 :                 remove(it->path());
     458           0 :             }
     459           0 :         }
     460           0 :     }
     461             : 
     462             :     // Remove all block files that aren't part of a contiguous set starting at
     463             :     // zero by walking the ordered map (keys are block file indices) by
     464             :     // keeping a separate counter.  Once we hit a gap (or if 0 doesn't exist)
     465             :     // start removing block files.
     466           0 :     int nContigCounter = 0;
     467           0 :     for (const std::pair<const std::string, fs::path>& item : mapBlockFiles) {
     468           0 :         if (LocaleIndependentAtoi<int>(item.first) == nContigCounter) {
     469           0 :             nContigCounter++;
     470           0 :             continue;
     471             :         }
     472           0 :         remove(item.second);
     473             :     }
     474           0 : }
     475             : 
     476           2 : CBlockFileInfo* BlockManager::GetBlockFileInfo(size_t n)
     477             : {
     478           2 :     LOCK(cs_LastBlockFile);
     479             : 
     480           2 :     return &m_blockfile_info.at(n);
     481           2 : }
     482             : 
     483      246789 : static bool UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
     484             : {
     485             :     // Open history file to append
     486      246789 :     CAutoFile fileout(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
     487      246789 :     if (fileout.IsNull()) {
     488           0 :         return error("%s: OpenUndoFile failed", __func__);
     489             :     }
     490             : 
     491             :     // Write index header
     492      246789 :     unsigned int nSize = GetSerializeSize(blockundo, fileout.GetVersion());
     493      246789 :     fileout << messageStart << nSize;
     494             : 
     495             :     // Write undo data
     496      246789 :     long fileOutPos = ftell(fileout.Get());
     497      246789 :     if (fileOutPos < 0) {
     498           0 :         return error("%s: ftell failed", __func__);
     499             :     }
     500      246789 :     pos.nPos = (unsigned int)fileOutPos;
     501      246789 :     fileout << blockundo;
     502             : 
     503             :     // calculate & write checksum
     504      246789 :     HashWriter hasher{};
     505      246789 :     hasher << hashBlock;
     506      246789 :     hasher << blockundo;
     507      246789 :     fileout << hasher.GetHash();
     508             : 
     509      246789 :     return true;
     510      246789 : }
     511             : 
     512      189390 : bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex)
     513             : {
     514      378780 :     const FlatFilePos pos{WITH_LOCK(::cs_main, return pindex->GetUndoPos())};
     515             : 
     516      189390 :     if (pos.IsNull()) {
     517           0 :         return error("%s: no undo data available", __func__);
     518             :     }
     519             : 
     520             :     // Open history file to read
     521      189390 :     CAutoFile filein(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
     522      189390 :     if (filein.IsNull()) {
     523          18 :         return error("%s: OpenUndoFile failed", __func__);
     524             :     }
     525             : 
     526             :     // Read block
     527      189372 :     uint256 hashChecksum;
     528      189372 :     CHashVerifier<CAutoFile> verifier(&filein); // We need a CHashVerifier as reserializing may lose data
     529             :     try {
     530      189372 :         verifier << pindex->pprev->GetBlockHash();
     531      189371 :         verifier >> blockundo;
     532      189370 :         filein >> hashChecksum;
     533      189372 :     } catch (const std::exception& e) {
     534           2 :         return error("%s: Deserialize or I/O error - %s", __func__, e.what());
     535           2 :     }
     536             : 
     537             :     // Verify checksum
     538      189370 :     if (hashChecksum != verifier.GetHash()) {
     539           0 :         return error("%s: Checksum mismatch", __func__);
     540             :     }
     541             : 
     542      189370 :     return true;
     543      189392 : }
     544             : 
     545        7548 : void BlockManager::FlushUndoFile(int block_file, bool finalize)
     546             : {
     547        7548 :     FlatFilePos undo_pos_old(block_file, m_blockfile_info[block_file].nUndoSize);
     548        7548 :     if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
     549           0 :         AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error.");
     550           0 :     }
     551        7548 : }
     552             : 
     553        7548 : void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo)
     554             : {
     555        7548 :     LOCK(cs_LastBlockFile);
     556        7548 :     FlatFilePos block_pos_old(m_last_blockfile, m_blockfile_info[m_last_blockfile].nSize);
     557        7548 :     if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
     558           0 :         AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error.");
     559           0 :     }
     560             :     // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks,
     561             :     // e.g. during IBD or a sync after a node going offline
     562        7548 :     if (!fFinalize || finalize_undo) FlushUndoFile(m_last_blockfile, finalize_undo);
     563        7548 : }
     564             : 
     565       15136 : uint64_t BlockManager::CalculateCurrentUsage()
     566             : {
     567       15136 :     LOCK(cs_LastBlockFile);
     568             : 
     569       15136 :     uint64_t retval = 0;
     570       30272 :     for (const CBlockFileInfo& file : m_blockfile_info) {
     571       15136 :         retval += file.nSize + file.nUndoSize;
     572             :     }
     573       15136 :     return retval;
     574       15136 : }
     575             : 
     576           3 : void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
     577             : {
     578           6 :     for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
     579           3 :         FlatFilePos pos(*it, 0);
     580           3 :         fs::remove(BlockFileSeq().FileName(pos));
     581           3 :         fs::remove(UndoFileSeq().FileName(pos));
     582           3 :         LogPrint(BCLog::BLOCKSTORE, "Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
     583           3 :     }
     584           3 : }
     585             : 
     586     1226792 : static FlatFileSeq BlockFileSeq()
     587             : {
     588     2453584 :     return FlatFileSeq(gArgs.GetBlocksDirPath(), "blk",
     589     2453148 :                        gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ :
     590     1226356 :                         (gArgs.GetBoolArg("-tinyblk", false) ? 0x10000 /* 64kb */ : BLOCKFILE_CHUNK_SIZE));
     591           0 : }
     592             : 
     593      690519 : static FlatFileSeq UndoFileSeq()
     594             : {
     595      690519 :     return FlatFileSeq(gArgs.GetBlocksDirPath(), "rev", gArgs.GetBoolArg("-tinyblk", false) ? 0x10000 /* 64kb */ : UNDOFILE_CHUNK_SIZE);
     596           0 : }
     597             : 
     598      980630 : FILE* OpenBlockFile(const FlatFilePos& pos, bool fReadOnly)
     599             : {
     600      980630 :     return BlockFileSeq().Open(pos, fReadOnly);
     601           0 : }
     602             : 
     603             : /** Open an undo file (rev?????.dat) */
     604      436179 : static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly)
     605             : {
     606      436179 :     return UndoFileSeq().Open(pos, fReadOnly);
     607           0 : }
     608             : 
     609         130 : fs::path GetBlockPosFilename(const FlatFilePos& pos)
     610             : {
     611         130 :     return BlockFileSeq().FileName(pos);
     612           0 : }
     613             : 
     614      248577 : bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown)
     615             : {
     616      248577 :     LOCK(cs_LastBlockFile);
     617             : 
     618      248577 :     unsigned int nFile = fKnown ? pos.nFile : m_last_blockfile;
     619      248577 :     if (m_blockfile_info.size() <= nFile) {
     620          69 :         m_blockfile_info.resize(nFile + 1);
     621          69 :     }
     622             : 
     623      248577 :     bool finalize_undo = false;
     624      248577 :     if (!fKnown) {
     625      238481 :         unsigned int max_blockfile_size{MAX_BLOCKFILE_SIZE};
     626             :         // Use smaller blockfiles in test-only -fastprune mode - but avoid
     627             :         // the possibility of having a block not fit into the block file.
     628      238481 :         if (gArgs.GetBoolArg("-fastprune", false)) {
     629         206 :             max_blockfile_size = 0x10000; // 64kiB
     630         206 :             if (nAddSize >= max_blockfile_size) {
     631             :                 // dynamically adjust the blockfile size to be larger than the added size
     632           2 :                 max_blockfile_size = nAddSize + 1;
     633           2 :             }
     634         206 :         }
     635      238481 :         assert(nAddSize < max_blockfile_size);
     636      238509 :         while (m_blockfile_info[nFile].nSize + nAddSize >= max_blockfile_size) {
     637             :             // when the undo file is keeping up with the block file, we want to flush it explicitly
     638             :             // when it is lagging behind (more blocks arrive than are being connected), we let the
     639             :             // undo block write case handle it
     640          28 :             finalize_undo = (m_blockfile_info[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight);
     641          28 :             nFile++;
     642          28 :             if (m_blockfile_info.size() <= nFile) {
     643          28 :                 m_blockfile_info.resize(nFile + 1);
     644          28 :             }
     645             :         }
     646      238481 :         pos.nFile = nFile;
     647      238481 :         pos.nPos = m_blockfile_info[nFile].nSize;
     648      238481 :     }
     649             : 
     650      248577 :     if ((int)nFile != m_last_blockfile) {
     651          28 :         if (!fKnown) {
     652          28 :             LogPrint(BCLog::BLOCKSTORE, "Leaving block file %i: %s\n", m_last_blockfile, m_blockfile_info[m_last_blockfile].ToString());
     653          28 :         }
     654          28 :         FlushBlockFile(!fKnown, finalize_undo);
     655          28 :         m_last_blockfile = nFile;
     656          28 :     }
     657             : 
     658      248577 :     m_blockfile_info[nFile].AddBlock(nHeight, nTime);
     659      248577 :     if (fKnown) {
     660       10096 :         m_blockfile_info[nFile].nSize = std::max(pos.nPos + nAddSize, m_blockfile_info[nFile].nSize);
     661       10096 :     } else {
     662      238481 :         m_blockfile_info[nFile].nSize += nAddSize;
     663             :     }
     664             : 
     665      248577 :     if (!fKnown) {
     666             :         bool out_of_space;
     667      238481 :         size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
     668      238481 :         if (out_of_space) {
     669           0 :             return AbortNode("Disk space is too low!", _("Disk space is too low!"));
     670             :         }
     671      238481 :         if (bytes_allocated != 0 && fPruneMode) {
     672          10 :             m_check_for_pruning = true;
     673          10 :         }
     674      238481 :     }
     675             : 
     676      248577 :     m_dirty_fileinfo.insert(nFile);
     677      248577 :     return true;
     678      248577 : }
     679             : 
     680      246789 : bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize)
     681             : {
     682      246789 :     pos.nFile = nFile;
     683             : 
     684      246789 :     LOCK(cs_LastBlockFile);
     685             : 
     686      246789 :     pos.nPos = m_blockfile_info[nFile].nUndoSize;
     687      246789 :     m_blockfile_info[nFile].nUndoSize += nAddSize;
     688      246789 :     m_dirty_fileinfo.insert(nFile);
     689             : 
     690             :     bool out_of_space;
     691      246789 :     size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
     692      246789 :     if (out_of_space) {
     693           0 :         return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
     694             :     }
     695      246789 :     if (bytes_allocated != 0 && fPruneMode) {
     696           4 :         m_check_for_pruning = true;
     697           4 :     }
     698             : 
     699      246789 :     return true;
     700      246789 : }
     701             : 
     702      238481 : static bool WriteBlockToDisk(const CBlock& block, FlatFilePos& pos, const CMessageHeader::MessageStartChars& messageStart)
     703             : {
     704             :     // Open history file to append
     705      238481 :     CAutoFile fileout(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
     706      238481 :     if (fileout.IsNull()) {
     707           0 :         return error("WriteBlockToDisk: OpenBlockFile failed");
     708             :     }
     709             : 
     710             :     // Write index header
     711      238481 :     unsigned int nSize = GetSerializeSize(block, fileout.GetVersion());
     712      238481 :     fileout << messageStart << nSize;
     713             : 
     714             :     // Write block
     715      238481 :     long fileOutPos = ftell(fileout.Get());
     716      238481 :     if (fileOutPos < 0) {
     717           0 :         return error("WriteBlockToDisk: ftell failed");
     718             :     }
     719      238481 :     pos.nPos = (unsigned int)fileOutPos;
     720      238481 :     fileout << block;
     721             : 
     722      238481 :     return true;
     723      238481 : }
     724             : 
     725      252982 : bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex& block)
     726             : {
     727      252982 :     AssertLockHeld(::cs_main);
     728             :     // Write undo information to disk
     729      252982 :     if (block.GetUndoPos().IsNull()) {
     730      246789 :         FlatFilePos _pos;
     731      246789 :         if (!FindUndoPos(state, block.nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40)) {
     732           0 :             return error("ConnectBlock(): FindUndoPos failed");
     733             :         }
     734      246789 :         if (!UndoWriteToDisk(blockundo, _pos, block.pprev->GetBlockHash(), GetParams().MessageStart())) {
     735           0 :             return AbortNode(state, "Failed to write undo data");
     736             :         }
     737             :         // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order)
     738             :         // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height
     739             :         // in the block file info as below; note that this does not catch the case where the undo writes are keeping up
     740             :         // with the block writes (usually when a synced up node is getting newly mined blocks) -- this case is caught in
     741             :         // the FindBlockPos function
     742      246789 :         if (_pos.nFile < m_last_blockfile && static_cast<uint32_t>(block.nHeight) == m_blockfile_info[_pos.nFile].nHeightLast) {
     743           0 :             FlushUndoFile(_pos.nFile, true);
     744           0 :         }
     745             : 
     746             :         // update nUndoPos in block index
     747      246789 :         block.nUndoPos = _pos.nPos;
     748      246789 :         block.nStatus |= BLOCK_HAVE_UNDO;
     749      246789 :         m_dirty_blockindex.insert(&block);
     750      246789 :     }
     751             : 
     752      252982 :     return true;
     753      252982 : }
     754             : 
     755      712321 : std::optional<uint256> ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams)
     756             : {
     757      712321 :     block.SetNull();
     758             : 
     759             :     // Open history file to read
     760      712321 :     CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
     761      712321 :     if (filein.IsNull()) {
     762         135 :         error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString());
     763         135 :         return std::nullopt;
     764             :     }
     765             : 
     766             :     // Read block
     767             :     try {
     768      712177 :         filein >> block;
     769      712177 :     } catch (const std::exception& e) {
     770           2 :         error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString());
     771           2 :         return std::nullopt;
     772           2 :     }
     773             : 
     774             :     // Check the header
     775      712175 :     const uint256 hash{block.GetHash()};
     776      712176 :     if (!CheckProofOfWork(hash, block.nBits, consensusParams)) {
     777           0 :         error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
     778           0 :         return std::nullopt;
     779             :     }
     780             : 
     781      712174 :     return hash;
     782      712325 : }
     783             : 
     784      712153 : bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
     785             : {
     786     1424306 :     const FlatFilePos block_pos{WITH_LOCK(cs_main, return pindex->GetBlockPos())};
     787             : 
     788      712153 :     const auto hash{ReadBlockFromDisk(block, block_pos, consensusParams)};
     789      712153 :     if (!hash) {
     790         137 :         return false;
     791             :     }
     792      712016 :     if (*hash != pindex->GetBlockHash()) {
     793           0 :         return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s",
     794           0 :                      pindex->ToString(), block_pos.ToString());
     795             :     }
     796      712016 :     return true;
     797      712153 : }
     798             : 
     799      248577 : FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const FlatFilePos* dbp)
     800             : {
     801      248577 :     unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION);
     802      248577 :     FlatFilePos blockPos;
     803      248577 :     const auto position_known {dbp != nullptr};
     804      248577 :     if (position_known) {
     805       10096 :         blockPos = *dbp;
     806       10096 :     } else {
     807             :         // when known, blockPos.nPos points at the offset of the block data in the blk file. that already accounts for
     808             :         // the serialization header present in the file (the 4 magic message start bytes + the 4 length bytes = 8 bytes = BLOCK_SERIALIZATION_HEADER_SIZE).
     809             :         // we add BLOCK_SERIALIZATION_HEADER_SIZE only for new blocks since they will have the serialization header added when written to disk.
     810      238481 :         nBlockSize += static_cast<unsigned int>(BLOCK_SERIALIZATION_HEADER_SIZE);
     811             :     }
     812      248577 :     if (!FindBlockPos(blockPos, nBlockSize, nHeight, active_chain, block.GetBlockTime(), position_known)) {
     813           0 :         error("%s: FindBlockPos failed", __func__);
     814           0 :         return FlatFilePos();
     815             :     }
     816      248577 :     if (!position_known) {
     817      238481 :         if (!WriteBlockToDisk(block, blockPos, GetParams().MessageStart())) {
     818           0 :             AbortNode("Failed to write block");
     819           0 :             return FlatFilePos();
     820             :         }
     821      238481 :     }
     822      248577 :     return blockPos;
     823      248577 : }
     824             : 
     825             : struct CImportingNow {
     826        5662 :     CImportingNow()
     827        2831 :     {
     828        2831 :         assert(fImporting == false);
     829        2831 :         fImporting = true;
     830        5662 :     }
     831             : 
     832        5662 :     ~CImportingNow()
     833        2831 :     {
     834        2831 :         assert(fImporting == true);
     835        2831 :         fImporting = false;
     836        5662 :     }
     837             : };
     838             : 
     839        2831 : void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles, const ArgsManager& args)
     840             : {
     841        2831 :     ScheduleBatchPriority();
     842             : 
     843             :     {
     844        2831 :         CImportingNow imp;
     845             : 
     846             :         // -reindex
     847        2831 :         if (fReindex) {
     848          65 :             int nFile = 0;
     849             :             // Map of disk positions for blocks with unknown parent (only used for reindex);
     850             :             // parent hash -> child disk position, multiple children can have the same parent.
     851          65 :             std::multimap<uint256, FlatFilePos> blocks_with_unknown_parent;
     852         130 :             while (true) {
     853         130 :                 FlatFilePos pos(nFile, 0);
     854         130 :                 if (!fs::exists(GetBlockPosFilename(pos))) {
     855          65 :                     break; // No block files left to reindex
     856             :                 }
     857          65 :                 FILE* file = OpenBlockFile(pos, true);
     858          65 :                 if (!file) {
     859           0 :                     break; // This error is logged in OpenBlockFile
     860             :                 }
     861          65 :                 LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
     862          65 :                 chainman.ActiveChainstate().LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent);
     863          65 :                 if (ShutdownRequested()) {
     864           0 :                     LogPrintf("Shutdown requested. Exit %s\n", __func__);
     865           0 :                     return;
     866             :                 }
     867          65 :                 nFile++;
     868             :             }
     869         130 :             WITH_LOCK(::cs_main, chainman.m_blockman.m_block_tree_db->WriteReindexing(false));
     870          65 :             fReindex = false;
     871          65 :             LogPrintf("Reindexing finished\n");
     872             :             // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked):
     873          65 :             chainman.ActiveChainstate().LoadGenesisBlock();
     874          65 :         }
     875             : 
     876             :         // -loadblock=
     877        2833 :         for (const fs::path& path : vImportFiles) {
     878           2 :             FILE *file = fsbridge::fopen(path, "rb");
     879           2 :             if (file) {
     880           2 :                 LogPrintf("Importing blocks file %s...\n", fs::PathToString(path));
     881           2 :                 chainman.ActiveChainstate().LoadExternalBlockFile(file);
     882           2 :                 if (ShutdownRequested()) {
     883           0 :                     LogPrintf("Shutdown requested. Exit %s\n", __func__);
     884           0 :                     return;
     885             :                 }
     886           2 :             } else {
     887           0 :                 LogPrintf("Warning: Could not open blocks file %s\n", fs::PathToString(path));
     888             :             }
     889             :         }
     890             : 
     891             :         // scan for better chains in the block chain database, that are not yet connected in the active best chain
     892             : 
     893             :         // We can't hold cs_main during ActivateBestChain even though we're accessing
     894             :         // the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
     895             :         // the relevant pointers before the ABC call.
     896        8493 :         for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) {
     897        2831 :             BlockValidationState state;
     898        2831 :             if (!chainstate->ActivateBestChain(state, nullptr)) {
     899           0 :                 LogPrintf("Failed to connect best block (%s)\n", state.ToString());
     900           0 :                 StartShutdown();
     901           0 :                 return;
     902             :             }
     903        2831 :         }
     904             : 
     905        2831 :         if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
     906           0 :             LogPrintf("Stopping after block import\n");
     907           0 :             StartShutdown();
     908           0 :             return;
     909             :         }
     910        2831 :     } // End scope of CImportingNow
     911             : 
     912        2831 :     chainman.ActiveChainstate().LoadMempool(args);
     913        2831 : }
     914             : } // namespace node

Generated by: LCOV version 1.16