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 : }
|