Branch data Line data Source code
# 1 : : // Copyright (c) 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/chainstate.h>
# 6 : :
# 7 : : #include <consensus/params.h>
# 8 : : #include <node/blockstorage.h>
# 9 : : #include <validation.h>
# 10 : :
# 11 : : namespace node {
# 12 : : std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
# 13 : : ChainstateManager& chainman,
# 14 : : CTxMemPool* mempool,
# 15 : : bool fPruneMode,
# 16 : : const Consensus::Params& consensus_params,
# 17 : : bool fReindexChainState,
# 18 : : int64_t nBlockTreeDBCache,
# 19 : : int64_t nCoinDBCache,
# 20 : : int64_t nCoinCacheUsage,
# 21 : : bool block_tree_db_in_memory,
# 22 : : bool coins_db_in_memory,
# 23 : : std::function<bool()> shutdown_requested,
# 24 : : std::function<void()> coins_error_cb)
# 25 : 940 : {
# 26 : 940 : auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
# 27 [ + + ][ + + ]: 934 : return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
# [ + + ]
# 28 : 934 : };
# 29 : :
# 30 : 940 : LOCK(cs_main);
# 31 : 940 : chainman.InitializeChainstate(mempool);
# 32 : 940 : chainman.m_total_coinstip_cache = nCoinCacheUsage;
# 33 : 940 : chainman.m_total_coinsdb_cache = nCoinDBCache;
# 34 : :
# 35 : 940 : UnloadBlockIndex(mempool, chainman);
# 36 : :
# 37 : 940 : auto& pblocktree{chainman.m_blockman.m_block_tree_db};
# 38 : : // new CBlockTreeDB tries to delete the existing file, which
# 39 : : // fails if it's still open from the previous loop. Close it first:
# 40 : 940 : pblocktree.reset();
# 41 : 940 : pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, block_tree_db_in_memory, fReset));
# 42 : :
# 43 [ + + ]: 940 : if (fReset) {
# 44 : 10 : pblocktree->WriteReindexing(true);
# 45 : : //If we're reindexing in prune mode, wipe away unusable block files and all undo data files
# 46 [ + + ]: 10 : if (fPruneMode)
# 47 : 1 : CleanupBlockRevFiles();
# 48 : 10 : }
# 49 : :
# 50 [ + + ][ + + ]: 940 : if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
# 51 : :
# 52 : : // LoadBlockIndex will load fHavePruned if we've ever removed a
# 53 : : // block file from disk.
# 54 : : // Note that it also sets fReindex based on the disk flag!
# 55 : : // From here on out fReindex and fReset mean something different!
# 56 [ + + ]: 938 : if (!chainman.LoadBlockIndex()) {
# 57 [ + - ][ + + ]: 2 : if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
# 58 : 1 : return ChainstateLoadingError::ERROR_LOADING_BLOCK_DB;
# 59 : 2 : }
# 60 : :
# 61 [ + + ]: 936 : if (!chainman.BlockIndex().empty() &&
# 62 [ - + ]: 936 : !chainman.m_blockman.LookupBlockIndex(consensus_params.hashGenesisBlock)) {
# 63 : 0 : return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK;
# 64 : 0 : }
# 65 : :
# 66 : : // Check for changed -prune state. What we are concerned about is a user who has pruned blocks
# 67 : : // in the past, but is now trying to run unpruned.
# 68 [ + + ][ - + ]: 936 : if (fHavePruned && !fPruneMode) {
# 69 : 0 : return ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX;
# 70 : 0 : }
# 71 : :
# 72 : : // At this point blocktree args are consistent with what's on disk.
# 73 : : // If we're not mid-reindex (based on disk + args), add a genesis block on disk
# 74 : : // (otherwise we use the one already on disk).
# 75 : : // This is called again in ThreadImport after the reindex completes.
# 76 [ + + ][ - + ]: 936 : if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock()) {
# 77 : 0 : return ChainstateLoadingError::ERROR_LOAD_GENESIS_BLOCK_FAILED;
# 78 : 0 : }
# 79 : :
# 80 : : // At this point we're either in reindex or we've loaded a useful
# 81 : : // block tree into BlockIndex()!
# 82 : :
# 83 [ + + ]: 936 : for (CChainState* chainstate : chainman.GetAll()) {
# 84 : 935 : chainstate->InitCoinsDB(
# 85 : 935 : /*cache_size_bytes=*/nCoinDBCache,
# 86 : 935 : /*in_memory=*/coins_db_in_memory,
# 87 [ + + ][ + + ]: 935 : /*should_wipe=*/fReset || fReindexChainState);
# 88 : :
# 89 [ + + ]: 935 : if (coins_error_cb) {
# 90 : 733 : chainstate->CoinsErrorCatcher().AddReadErrCallback(coins_error_cb);
# 91 : 733 : }
# 92 : :
# 93 : : // Refuse to load unsupported database format.
# 94 : : // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
# 95 [ - + ]: 935 : if (chainstate->CoinsDB().NeedsUpgrade()) {
# 96 : 0 : return ChainstateLoadingError::ERROR_CHAINSTATE_UPGRADE_FAILED;
# 97 : 0 : }
# 98 : :
# 99 : : // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
# 100 [ - + ]: 935 : if (!chainstate->ReplayBlocks()) {
# 101 : 0 : return ChainstateLoadingError::ERROR_REPLAYBLOCKS_FAILED;
# 102 : 0 : }
# 103 : :
# 104 : : // The on-disk coinsdb is now in a good state, create the cache
# 105 : 935 : chainstate->InitCoinsCache(nCoinCacheUsage);
# 106 : 935 : assert(chainstate->CanFlushToDisk());
# 107 : :
# 108 [ + + ]: 935 : if (!is_coinsview_empty(chainstate)) {
# 109 : : // LoadChainTip initializes the chain based on CoinsTip()'s best block
# 110 [ - + ]: 465 : if (!chainstate->LoadChainTip()) {
# 111 : 0 : return ChainstateLoadingError::ERROR_LOADCHAINTIP_FAILED;
# 112 : 0 : }
# 113 : 465 : assert(chainstate->m_chain.Tip() != nullptr);
# 114 : 465 : }
# 115 : 935 : }
# 116 : :
# 117 [ + + ]: 936 : if (!fReset) {
# 118 : 924 : auto chainstates{chainman.GetAll()};
# 119 [ + + ]: 924 : if (std::any_of(chainstates.begin(), chainstates.end(),
# 120 : 924 : [](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
# 121 : 1 : return ChainstateLoadingError::ERROR_BLOCKS_WITNESS_INSUFFICIENTLY_VALIDATED;
# 122 : 1 : }
# 123 : 924 : }
# 124 : :
# 125 : 935 : return std::nullopt;
# 126 : 936 : }
# 127 : :
# 128 : : std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManager& chainman,
# 129 : : bool fReset,
# 130 : : bool fReindexChainState,
# 131 : : const Consensus::Params& consensus_params,
# 132 : : int check_blocks,
# 133 : : int check_level,
# 134 : : std::function<int64_t()> get_unix_time_seconds)
# 135 : 933 : {
# 136 : 933 : auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
# 137 [ + + ][ + + ]: 933 : return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
# [ + + ]
# 138 : 933 : };
# 139 : :
# 140 : 933 : LOCK(cs_main);
# 141 : :
# 142 [ + + ]: 933 : for (CChainState* chainstate : chainman.GetAll()) {
# 143 [ + + ]: 933 : if (!is_coinsview_empty(chainstate)) {
# 144 : 464 : const CBlockIndex* tip = chainstate->m_chain.Tip();
# 145 [ + - ][ + + ]: 464 : if (tip && tip->nTime > get_unix_time_seconds() + MAX_FUTURE_BLOCK_TIME) {
# 146 : 1 : return ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE;
# 147 : 1 : }
# 148 : :
# 149 [ + + ]: 463 : if (!CVerifyDB().VerifyDB(
# 150 : 463 : *chainstate, consensus_params, chainstate->CoinsDB(),
# 151 : 463 : check_level,
# 152 : 463 : check_blocks)) {
# 153 : 1 : return ChainstateLoadVerifyError::ERROR_CORRUPTED_BLOCK_DB;
# 154 : 1 : }
# 155 : 463 : }
# 156 : 933 : }
# 157 : :
# 158 : 931 : return std::nullopt;
# 159 : 933 : }
# 160 : : } // namespace node
|