LCOV - code coverage report
Current view: top level - src/index - base.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 191 254 75.2 %
Date: 2021-06-29 14:35:33 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: 65 86 75.6 %

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

Generated by: LCOV version 1.14