LCOV - code coverage report
Current view: top level - src/index - base.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 201 261 77.0 %
Date: 2022-04-21 14:51:19 Functions: 18 24 75.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 69 88 78.4 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2017-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 <chainparams.h>
#       6                 :            : #include <index/base.h>
#       7                 :            : #include <node/blockstorage.h>
#       8                 :            : #include <node/ui_interface.h>
#       9                 :            : #include <shutdown.h>
#      10                 :            : #include <tinyformat.h>
#      11                 :            : #include <util/syscall_sandbox.h>
#      12                 :            : #include <util/thread.h>
#      13                 :            : #include <util/translation.h>
#      14                 :            : #include <validation.h> // For g_chainman
#      15                 :            : #include <warnings.h>
#      16                 :            : 
#      17                 :            : using node::ReadBlockFromDisk;
#      18                 :            : 
#      19                 :            : constexpr uint8_t DB_BEST_BLOCK{'B'};
#      20                 :            : 
#      21                 :            : constexpr int64_t SYNC_LOG_INTERVAL = 30; // seconds
#      22                 :            : constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
#      23                 :            : 
#      24                 :            : template <typename... Args>
#      25                 :            : static void FatalError(const char* fmt, const Args&... args)
#      26                 :          0 : {
#      27                 :          0 :     std::string strMessage = tfm::format(fmt, args...);
#      28                 :          0 :     SetMiscWarning(Untranslated(strMessage));
#      29                 :          0 :     LogPrintf("*** %s\n", strMessage);
#      30                 :          0 :     AbortError(_("A fatal internal error occurred, see debug.log for details"));
#      31                 :          0 :     StartShutdown();
#      32                 :          0 : }
#      33                 :            : 
#      34                 :            : BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate) :
#      35                 :            :     CDBWrapper(path, n_cache_size, f_memory, f_wipe, f_obfuscate)
#      36                 :         67 : {}
#      37                 :            : 
#      38                 :            : bool BaseIndex::DB::ReadBestBlock(CBlockLocator& locator) const
#      39                 :         61 : {
#      40                 :         61 :     bool success = Read(DB_BEST_BLOCK, locator);
#      41         [ +  + ]:         61 :     if (!success) {
#      42                 :         29 :         locator.SetNull();
#      43                 :         29 :     }
#      44                 :         61 :     return success;
#      45                 :         61 : }
#      46                 :            : 
#      47                 :            : void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator)
#      48                 :        138 : {
#      49                 :        138 :     batch.Write(DB_BEST_BLOCK, locator);
#      50                 :        138 : }
#      51                 :            : 
#      52                 :            : BaseIndex::~BaseIndex()
#      53                 :         67 : {
#      54                 :         67 :     Interrupt();
#      55                 :         67 :     Stop();
#      56                 :         67 : }
#      57                 :            : 
#      58                 :            : bool BaseIndex::Init()
#      59                 :         61 : {
#      60                 :         61 :     CBlockLocator locator;
#      61         [ +  + ]:         61 :     if (!GetDB().ReadBestBlock(locator)) {
#      62                 :         29 :         locator.SetNull();
#      63                 :         29 :     }
#      64                 :            : 
#      65                 :         61 :     LOCK(cs_main);
#      66                 :         61 :     CChain& active_chain = m_chainstate->m_chain;
#      67         [ +  + ]:         61 :     if (locator.IsNull()) {
#      68                 :         29 :         m_best_block_index = nullptr;
#      69                 :         32 :     } else {
#      70                 :         32 :         m_best_block_index = m_chainstate->FindForkInGlobalIndex(locator);
#      71                 :         32 :     }
#      72                 :         61 :     m_synced = m_best_block_index.load() == active_chain.Tip();
#      73         [ +  + ]:         61 :     if (!m_synced) {
#      74                 :         30 :         bool prune_violation = false;
#      75         [ +  + ]:         30 :         if (!m_best_block_index) {
#      76                 :            :             // index is not built yet
#      77                 :            :             // make sure we have all block data back to the genesis
#      78                 :         17 :             const CBlockIndex* block = active_chain.Tip();
#      79 [ +  + ][ +  - ]:       2614 :             while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) {
#      80                 :       2597 :                 block = block->pprev;
#      81                 :       2597 :             }
#      82                 :         17 :             prune_violation = block != active_chain.Genesis();
#      83                 :         17 :         }
#      84                 :            :         // in case the index has a best block set and is not fully synced
#      85                 :            :         // check if we have the required blocks to continue building the index
#      86                 :         13 :         else {
#      87                 :         13 :             const CBlockIndex* block_to_test = m_best_block_index.load();
#      88         [ -  + ]:         13 :             if (!active_chain.Contains(block_to_test)) {
#      89                 :            :                 // if the bestblock is not part of the mainchain, find the fork
#      90                 :            :                 // and make sure we have all data down to the fork
#      91                 :          0 :                 block_to_test = active_chain.FindFork(block_to_test);
#      92                 :          0 :             }
#      93                 :         13 :             const CBlockIndex* block = active_chain.Tip();
#      94                 :         13 :             prune_violation = true;
#      95                 :            :             // check backwards from the tip if we have all block data until we reach the indexes bestblock
#      96 [ +  - ][ +  - ]:       3532 :             while (block_to_test && block && (block->nStatus & BLOCK_HAVE_DATA)) {
#                 [ +  + ]
#      97         [ +  + ]:       3531 :                 if (block_to_test == block) {
#      98                 :         12 :                     prune_violation = false;
#      99                 :         12 :                     break;
#     100                 :         12 :                 }
#     101                 :            :                 // block->pprev must exist at this point, since block_to_test is part of the chain
#     102                 :            :                 // and thus must be encountered when going backwards from the tip
#     103                 :       3519 :                 assert(block->pprev);
#     104                 :          0 :                 block = block->pprev;
#     105                 :       3519 :             }
#     106                 :         13 :         }
#     107         [ +  + ]:         30 :         if (prune_violation) {
#     108                 :          1 :             return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName()));
#     109                 :          1 :         }
#     110                 :         30 :     }
#     111                 :         60 :     return true;
#     112                 :         61 : }
#     113                 :            : 
#     114                 :            : static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain& chain) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
#     115                 :       3190 : {
#     116                 :       3190 :     AssertLockHeld(cs_main);
#     117                 :            : 
#     118         [ +  + ]:       3190 :     if (!pindex_prev) {
#     119                 :         17 :         return chain.Genesis();
#     120                 :         17 :     }
#     121                 :            : 
#     122                 :       3173 :     const CBlockIndex* pindex = chain.Next(pindex_prev);
#     123         [ +  + ]:       3173 :     if (pindex) {
#     124                 :       3156 :         return pindex;
#     125                 :       3156 :     }
#     126                 :            : 
#     127                 :         17 :     return chain.Next(chain.FindFork(pindex_prev));
#     128                 :       3173 : }
#     129                 :            : 
#     130                 :            : void BaseIndex::ThreadSync()
#     131                 :         60 : {
#     132                 :         60 :     SetSyscallSandboxPolicy(SyscallSandboxPolicy::TX_INDEX);
#     133                 :         60 :     const CBlockIndex* pindex = m_best_block_index.load();
#     134         [ +  + ]:         60 :     if (!m_synced) {
#     135                 :         29 :         auto& consensus_params = Params().GetConsensus();
#     136                 :            : 
#     137                 :         29 :         int64_t last_log_time = 0;
#     138                 :         29 :         int64_t last_locator_write_time = 0;
#     139                 :       3202 :         while (true) {
#     140         [ +  + ]:       3202 :             if (m_interrupt) {
#     141                 :         12 :                 m_best_block_index = pindex;
#     142                 :            :                 // No need to handle errors in Commit. If it fails, the error will be already be
#     143                 :            :                 // logged. The best way to recover is to continue, as index cannot be corrupted by
#     144                 :            :                 // a missed commit to disk for an advanced index state.
#     145                 :         12 :                 Commit();
#     146                 :         12 :                 return;
#     147                 :         12 :             }
#     148                 :            : 
#     149                 :       3190 :             {
#     150                 :       3190 :                 LOCK(cs_main);
#     151                 :       3190 :                 const CBlockIndex* pindex_next = NextSyncBlock(pindex, m_chainstate->m_chain);
#     152         [ +  + ]:       3190 :                 if (!pindex_next) {
#     153                 :         17 :                     m_best_block_index = pindex;
#     154                 :         17 :                     m_synced = true;
#     155                 :            :                     // No need to handle errors in Commit. See rationale above.
#     156                 :         17 :                     Commit();
#     157                 :         17 :                     break;
#     158                 :         17 :                 }
#     159 [ -  + ][ #  # ]:       3173 :                 if (pindex_next->pprev != pindex && !Rewind(pindex, pindex_next->pprev)) {
#     160                 :          0 :                     FatalError("%s: Failed to rewind index %s to a previous chain tip",
#     161                 :          0 :                                __func__, GetName());
#     162                 :          0 :                     return;
#     163                 :          0 :                 }
#     164                 :       3173 :                 pindex = pindex_next;
#     165                 :       3173 :             }
#     166                 :            : 
#     167                 :          0 :             int64_t current_time = GetTime();
#     168         [ +  + ]:       3173 :             if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
#     169                 :         29 :                 LogPrintf("Syncing %s with block chain from height %d\n",
#     170                 :         29 :                           GetName(), pindex->nHeight);
#     171                 :         29 :                 last_log_time = current_time;
#     172                 :         29 :             }
#     173                 :            : 
#     174         [ +  + ]:       3173 :             if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) {
#     175                 :         29 :                 m_best_block_index = pindex;
#     176                 :         29 :                 last_locator_write_time = current_time;
#     177                 :            :                 // No need to handle errors in Commit. See rationale above.
#     178                 :         29 :                 Commit();
#     179                 :         29 :             }
#     180                 :            : 
#     181                 :       3173 :             CBlock block;
#     182         [ -  + ]:       3173 :             if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
#     183                 :          0 :                 FatalError("%s: Failed to read block %s from disk",
#     184                 :          0 :                            __func__, pindex->GetBlockHash().ToString());
#     185                 :          0 :                 return;
#     186                 :          0 :             }
#     187         [ -  + ]:       3173 :             if (!WriteBlock(block, pindex)) {
#     188                 :          0 :                 FatalError("%s: Failed to write block %s to index database",
#     189                 :          0 :                            __func__, pindex->GetBlockHash().ToString());
#     190                 :          0 :                 return;
#     191                 :          0 :             }
#     192                 :       3173 :         }
#     193                 :         29 :     }
#     194                 :            : 
#     195         [ +  + ]:         48 :     if (pindex) {
#     196                 :         36 :         LogPrintf("%s is enabled at height %d\n", GetName(), pindex->nHeight);
#     197                 :         36 :     } else {
#     198                 :         12 :         LogPrintf("%s is enabled\n", GetName());
#     199                 :         12 :     }
#     200                 :         48 : }
#     201                 :            : 
#     202                 :            : bool BaseIndex::Commit()
#     203                 :        138 : {
#     204                 :        138 :     CDBBatch batch(GetDB());
#     205 [ -  + ][ -  + ]:        138 :     if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) {
#     206                 :          0 :         return error("%s: Failed to commit latest %s state", __func__, GetName());
#     207                 :          0 :     }
#     208                 :        138 :     return true;
#     209                 :        138 : }
#     210                 :            : 
#     211                 :            : bool BaseIndex::CommitInternal(CDBBatch& batch)
#     212                 :        138 : {
#     213                 :        138 :     LOCK(cs_main);
#     214                 :            :     // Don't commit anything if we haven't indexed any block yet
#     215                 :            :     // (this could happen if init is interrupted).
#     216         [ -  + ]:        138 :     if (m_best_block_index == nullptr) {
#     217                 :          0 :         return false;
#     218                 :          0 :     }
#     219                 :        138 :     GetDB().WriteBestBlock(batch, m_chainstate->m_chain.GetLocator(m_best_block_index));
#     220                 :        138 :     return true;
#     221                 :        138 : }
#     222                 :            : 
#     223                 :            : bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
#     224                 :         11 : {
#     225                 :         11 :     assert(current_tip == m_best_block_index);
#     226                 :          0 :     assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
#     227                 :            : 
#     228                 :            :     // In the case of a reorg, ensure persisted block locator is not stale.
#     229                 :            :     // Pruning has a minimum of 288 blocks-to-keep and getting the index
#     230                 :            :     // out of sync may be possible but a users fault.
#     231                 :            :     // In case we reorg beyond the pruned depth, ReadBlockFromDisk would
#     232                 :            :     // throw and lead to a graceful shutdown
#     233                 :          0 :     m_best_block_index = new_tip;
#     234         [ -  + ]:         11 :     if (!Commit()) {
#     235                 :            :         // If commit fails, revert the best block index to avoid corruption.
#     236                 :          0 :         m_best_block_index = current_tip;
#     237                 :          0 :         return false;
#     238                 :          0 :     }
#     239                 :            : 
#     240                 :         11 :     return true;
#     241                 :         11 : }
#     242                 :            : 
#     243                 :            : void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)
#     244                 :       5556 : {
#     245         [ +  + ]:       5556 :     if (!m_synced) {
#     246                 :          1 :         return;
#     247                 :          1 :     }
#     248                 :            : 
#     249                 :       5555 :     const CBlockIndex* best_block_index = m_best_block_index.load();
#     250         [ +  + ]:       5555 :     if (!best_block_index) {
#     251         [ -  + ]:         12 :         if (pindex->nHeight != 0) {
#     252                 :          0 :             FatalError("%s: First block connected is not the genesis block (height=%d)",
#     253                 :          0 :                        __func__, pindex->nHeight);
#     254                 :          0 :             return;
#     255                 :          0 :         }
#     256                 :       5543 :     } else {
#     257                 :            :         // Ensure block connects to an ancestor of the current best block. This should be the case
#     258                 :            :         // most of the time, but may not be immediately after the sync thread catches up and sets
#     259                 :            :         // m_synced. Consider the case where there is a reorg and the blocks on the stale branch are
#     260                 :            :         // in the ValidationInterface queue backlog even after the sync thread has caught up to the
#     261                 :            :         // new chain tip. In this unlikely event, log a warning and let the queue clear.
#     262         [ -  + ]:       5543 :         if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) {
#     263                 :          0 :             LogPrintf("%s: WARNING: Block %s does not connect to an ancestor of " /* Continued */
#     264                 :          0 :                       "known best chain (tip=%s); not updating index\n",
#     265                 :          0 :                       __func__, pindex->GetBlockHash().ToString(),
#     266                 :          0 :                       best_block_index->GetBlockHash().ToString());
#     267                 :          0 :             return;
#     268                 :          0 :         }
#     269 [ +  + ][ -  + ]:       5543 :         if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) {
#     270                 :          0 :             FatalError("%s: Failed to rewind index %s to a previous chain tip",
#     271                 :          0 :                        __func__, GetName());
#     272                 :          0 :             return;
#     273                 :          0 :         }
#     274                 :       5543 :     }
#     275                 :            : 
#     276         [ +  - ]:       5555 :     if (WriteBlock(*block, pindex)) {
#     277                 :       5555 :         m_best_block_index = pindex;
#     278                 :       5555 :     } else {
#     279                 :          0 :         FatalError("%s: Failed to write block %s to index",
#     280                 :          0 :                    __func__, pindex->GetBlockHash().ToString());
#     281                 :          0 :         return;
#     282                 :          0 :     }
#     283                 :       5555 : }
#     284                 :            : 
#     285                 :            : void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
#     286                 :         82 : {
#     287         [ +  + ]:         82 :     if (!m_synced) {
#     288                 :         13 :         return;
#     289                 :         13 :     }
#     290                 :            : 
#     291                 :         69 :     const uint256& locator_tip_hash = locator.vHave.front();
#     292                 :         69 :     const CBlockIndex* locator_tip_index;
#     293                 :         69 :     {
#     294                 :         69 :         LOCK(cs_main);
#     295                 :         69 :         locator_tip_index = m_chainstate->m_blockman.LookupBlockIndex(locator_tip_hash);
#     296                 :         69 :     }
#     297                 :            : 
#     298         [ -  + ]:         69 :     if (!locator_tip_index) {
#     299                 :          0 :         FatalError("%s: First block (hash=%s) in locator was not found",
#     300                 :          0 :                    __func__, locator_tip_hash.ToString());
#     301                 :          0 :         return;
#     302                 :          0 :     }
#     303                 :            : 
#     304                 :            :     // This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail
#     305                 :            :     // immediately after the sync thread catches up and sets m_synced. Consider the case where
#     306                 :            :     // there is a reorg and the blocks on the stale branch are in the ValidationInterface queue
#     307                 :            :     // backlog even after the sync thread has caught up to the new chain tip. In this unlikely
#     308                 :            :     // event, log a warning and let the queue clear.
#     309                 :         69 :     const CBlockIndex* best_block_index = m_best_block_index.load();
#     310         [ -  + ]:         69 :     if (best_block_index->GetAncestor(locator_tip_index->nHeight) != locator_tip_index) {
#     311                 :          0 :         LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known best " /* Continued */
#     312                 :          0 :                   "chain (tip=%s); not writing index locator\n",
#     313                 :          0 :                   __func__, locator_tip_hash.ToString(),
#     314                 :          0 :                   best_block_index->GetBlockHash().ToString());
#     315                 :          0 :         return;
#     316                 :          0 :     }
#     317                 :            : 
#     318                 :            :     // No need to handle errors in Commit. If it fails, the error will be already be logged. The
#     319                 :            :     // best way to recover is to continue, as index cannot be corrupted by a missed commit to disk
#     320                 :            :     // for an advanced index state.
#     321                 :         69 :     Commit();
#     322                 :         69 : }
#     323                 :            : 
#     324                 :            : bool BaseIndex::BlockUntilSyncedToCurrentChain() const
#     325                 :        431 : {
#     326                 :        431 :     AssertLockNotHeld(cs_main);
#     327                 :            : 
#     328         [ +  + ]:        431 :     if (!m_synced) {
#     329                 :        314 :         return false;
#     330                 :        314 :     }
#     331                 :            : 
#     332                 :        117 :     {
#     333                 :            :         // Skip the queue-draining stuff if we know we're caught up with
#     334                 :            :         // m_chain.Tip().
#     335                 :        117 :         LOCK(cs_main);
#     336                 :        117 :         const CBlockIndex* chain_tip = m_chainstate->m_chain.Tip();
#     337                 :        117 :         const CBlockIndex* best_block_index = m_best_block_index.load();
#     338         [ +  + ]:        117 :         if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
#     339                 :         86 :             return true;
#     340                 :         86 :         }
#     341                 :        117 :     }
#     342                 :            : 
#     343                 :         31 :     LogPrintf("%s: %s is catching up on block notifications\n", __func__, GetName());
#     344                 :         31 :     SyncWithValidationInterfaceQueue();
#     345                 :         31 :     return true;
#     346                 :        117 : }
#     347                 :            : 
#     348                 :            : void BaseIndex::Interrupt()
#     349                 :        120 : {
#     350                 :        120 :     m_interrupt();
#     351                 :        120 : }
#     352                 :            : 
#     353                 :            : bool BaseIndex::Start(CChainState& active_chainstate)
#     354                 :         61 : {
#     355                 :         61 :     m_chainstate = &active_chainstate;
#     356                 :            :     // Need to register this ValidationInterface before running Init(), so that
#     357                 :            :     // callbacks are not missed if Init sets m_synced to true.
#     358                 :         61 :     RegisterValidationInterface(this);
#     359         [ +  + ]:         61 :     if (!Init()) {
#     360                 :          1 :         return false;
#     361                 :          1 :     }
#     362                 :            : 
#     363                 :         60 :     m_thread_sync = std::thread(&util::TraceThread, GetName(), [this] { ThreadSync(); });
#     364                 :         60 :     return true;
#     365                 :         61 : }
#     366                 :            : 
#     367                 :            : void BaseIndex::Stop()
#     368                 :        128 : {
#     369                 :        128 :     UnregisterValidationInterface(this);
#     370                 :            : 
#     371         [ +  + ]:        128 :     if (m_thread_sync.joinable()) {
#     372                 :         60 :         m_thread_sync.join();
#     373                 :         60 :     }
#     374                 :        128 : }
#     375                 :            : 
#     376                 :            : IndexSummary BaseIndex::GetSummary() const
#     377                 :        939 : {
#     378                 :        939 :     IndexSummary summary{};
#     379                 :        939 :     summary.name = GetName();
#     380                 :        939 :     summary.synced = m_synced;
#     381         [ +  + ]:        939 :     summary.best_block_height = m_best_block_index ? m_best_block_index.load()->nHeight : 0;
#     382                 :        939 :     return summary;
#     383                 :        939 : }

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a