diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index dda590b82..ba9cc18e7 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -1,170 +1,165 @@ // Copyright (c) 2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include std::optional LoadChainstate(bool fReset, ChainstateManager &chainman, CTxMemPool *mempool, bool fPruneMode_, const Consensus::Params &consensus_params, bool fReindexChainState, int64_t nBlockTreeDBCache, int64_t nCoinDBCache, int64_t nCoinCacheUsage, bool block_tree_db_in_memory, bool coins_db_in_memory, std::function shutdown_requested, std::function coins_error_cb) { auto is_coinsview_empty = [&](CChainState *chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull(); }; - { - LOCK(cs_main); - chainman.InitializeChainstate(mempool); - chainman.m_total_coinstip_cache = nCoinCacheUsage; - chainman.m_total_coinsdb_cache = nCoinDBCache; - - UnloadBlockIndex(mempool, chainman); - - auto &pblocktree{chainman.m_blockman.m_block_tree_db}; - // new CBlockTreeDB tries to delete the existing file, which - // fails if it's still open from the previous loop. Close it first: - pblocktree.reset(); - pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, - block_tree_db_in_memory, fReset)); - - if (fReset) { - pblocktree->WriteReindexing(true); - // If we're reindexing in prune mode, wipe away unusable block - // files and all undo data files - if (fPruneMode_) { - CleanupBlockRevFiles(); - } + LOCK(cs_main); + chainman.InitializeChainstate(mempool); + chainman.m_total_coinstip_cache = nCoinCacheUsage; + chainman.m_total_coinsdb_cache = nCoinDBCache; + + UnloadBlockIndex(mempool, chainman); + + auto &pblocktree{chainman.m_blockman.m_block_tree_db}; + // new CBlockTreeDB tries to delete the existing file, which + // fails if it's still open from the previous loop. Close it first: + pblocktree.reset(); + pblocktree.reset( + new CBlockTreeDB(nBlockTreeDBCache, block_tree_db_in_memory, fReset)); + + if (fReset) { + pblocktree->WriteReindexing(true); + // If we're reindexing in prune mode, wipe away unusable block + // files and all undo data files + if (fPruneMode_) { + CleanupBlockRevFiles(); } + } - // If necessary, upgrade from older database format. - // This is a no-op if we cleared the block tree db with -reindex - // or -reindex-chainstate - if (!pblocktree->Upgrade(consensus_params)) { - return ChainstateLoadingError::ERROR_UPGRADING_BLOCK_DB; - } + // If necessary, upgrade from older database format. + // This is a no-op if we cleared the block tree db with -reindex + // or -reindex-chainstate + if (!pblocktree->Upgrade(consensus_params)) { + return ChainstateLoadingError::ERROR_UPGRADING_BLOCK_DB; + } + + if (shutdown_requested && shutdown_requested()) { + return ChainstateLoadingError::SHUTDOWN_PROBED; + } + // LoadBlockIndex will load fHavePruned if we've ever removed a + // block file from disk. + // Note that it also sets fReindex based on the disk flag! + // From here on out fReindex and fReset mean something different! + if (!chainman.LoadBlockIndex()) { if (shutdown_requested && shutdown_requested()) { return ChainstateLoadingError::SHUTDOWN_PROBED; } + return ChainstateLoadingError::ERROR_LOADING_BLOCK_DB; + } - // LoadBlockIndex will load fHavePruned if we've ever removed a - // block file from disk. - // Note that it also sets fReindex based on the disk flag! - // From here on out fReindex and fReset mean something different! - if (!chainman.LoadBlockIndex()) { - if (shutdown_requested && shutdown_requested()) { - return ChainstateLoadingError::SHUTDOWN_PROBED; - } - return ChainstateLoadingError::ERROR_LOADING_BLOCK_DB; - } + if (!chainman.BlockIndex().empty() && + !chainman.m_blockman.LookupBlockIndex( + consensus_params.hashGenesisBlock)) { + return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK; + } - if (!chainman.BlockIndex().empty() && - !chainman.m_blockman.LookupBlockIndex( - consensus_params.hashGenesisBlock)) { - return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK; - } + // Check for changed -prune state. What we are concerned about is a + // user who has pruned blocks in the past, but is now trying to run + // unpruned. + if (fHavePruned && !fPruneMode_) { + return ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX; + } - // Check for changed -prune state. What we are concerned about is a - // user who has pruned blocks in the past, but is now trying to run - // unpruned. - if (fHavePruned && !fPruneMode_) { - return ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX; - } + // At this point blocktree args are consistent with what's on disk. + // If we're not mid-reindex (based on disk + args), add a genesis + // block on disk (otherwise we use the one already on disk). This is + // called again in ThreadImport after the reindex completes. + if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock()) { + return ChainstateLoadingError::ERROR_LOAD_GENESIS_BLOCK_FAILED; + } - // At this point blocktree args are consistent with what's on disk. - // If we're not mid-reindex (based on disk + args), add a genesis - // block on disk (otherwise we use the one already on disk). This is - // called again in ThreadImport after the reindex completes. - if (!fReindex && !chainman.ActiveChainstate().LoadGenesisBlock()) { - return ChainstateLoadingError::ERROR_LOAD_GENESIS_BLOCK_FAILED; - } + // At this point we're either in reindex or we've loaded a useful + // block tree into BlockIndex()! - // At this point we're either in reindex or we've loaded a useful - // block tree into BlockIndex()! + for (CChainState *chainstate : chainman.GetAll()) { + chainstate->InitCoinsDB( + /* cache_size_bytes */ nCoinDBCache, + /* in_memory */ coins_db_in_memory, + /* should_wipe */ fReset || fReindexChainState); - for (CChainState *chainstate : chainman.GetAll()) { - chainstate->InitCoinsDB( - /* cache_size_bytes */ nCoinDBCache, - /* in_memory */ coins_db_in_memory, - /* should_wipe */ fReset || fReindexChainState); + if (coins_error_cb) { + chainstate->CoinsErrorCatcher().AddReadErrCallback(coins_error_cb); + } - if (coins_error_cb) { - chainstate->CoinsErrorCatcher().AddReadErrCallback( - coins_error_cb); - } + // If necessary, upgrade from older database format. + // This is a no-op if we cleared the coinsviewdb with -reindex + // or -reindex-chainstate + if (!chainstate->CoinsDB().Upgrade()) { + return ChainstateLoadingError::ERROR_CHAINSTATE_UPGRADE_FAILED; + } - // If necessary, upgrade from older database format. - // This is a no-op if we cleared the coinsviewdb with -reindex - // or -reindex-chainstate - if (!chainstate->CoinsDB().Upgrade()) { - return ChainstateLoadingError::ERROR_CHAINSTATE_UPGRADE_FAILED; - } + // ReplayBlocks is a no-op if we cleared the coinsviewdb with + // -reindex or -reindex-chainstate + if (!chainstate->ReplayBlocks()) { + return ChainstateLoadingError::ERROR_REPLAYBLOCKS_FAILED; + } - // ReplayBlocks is a no-op if we cleared the coinsviewdb with - // -reindex or -reindex-chainstate - if (!chainstate->ReplayBlocks()) { - return ChainstateLoadingError::ERROR_REPLAYBLOCKS_FAILED; - } + // The on-disk coinsdb is now in a good state, create the cache + chainstate->InitCoinsCache(nCoinCacheUsage); + assert(chainstate->CanFlushToDisk()); - // The on-disk coinsdb is now in a good state, create the cache - chainstate->InitCoinsCache(nCoinCacheUsage); - assert(chainstate->CanFlushToDisk()); - - if (!is_coinsview_empty(chainstate)) { - // LoadChainTip initializes the chain based on CoinsTip()'s - // best block - if (!chainstate->LoadChainTip()) { - return ChainstateLoadingError::ERROR_LOADCHAINTIP_FAILED; - } - assert(chainstate->m_chain.Tip() != nullptr); + if (!is_coinsview_empty(chainstate)) { + // LoadChainTip initializes the chain based on CoinsTip()'s + // best block + if (!chainstate->LoadChainTip()) { + return ChainstateLoadingError::ERROR_LOADCHAINTIP_FAILED; } + assert(chainstate->m_chain.Tip() != nullptr); } } return std::nullopt; } std::optional VerifyLoadedChainstate(ChainstateManager &chainman, bool fReset, bool fReindexChainState, const Config &config, unsigned int check_blocks, unsigned int check_level, std::function get_unix_time_seconds) { auto is_coinsview_empty = [&](CChainState *chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull(); }; - { - LOCK(cs_main); - - for (CChainState *chainstate : chainman.GetAll()) { - if (!is_coinsview_empty(chainstate)) { - const CBlockIndex *tip = chainstate->m_chain.Tip(); - if (tip && tip->nTime > get_unix_time_seconds() + - MAX_FUTURE_BLOCK_TIME) { - return ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE; - } - - if (!CVerifyDB().VerifyDB(*chainstate, config, - chainstate->CoinsDB(), check_level, - check_blocks)) { - return ChainstateLoadVerifyError::ERROR_CORRUPTED_BLOCK_DB; - } + LOCK(cs_main); + + for (CChainState *chainstate : chainman.GetAll()) { + if (!is_coinsview_empty(chainstate)) { + const CBlockIndex *tip = chainstate->m_chain.Tip(); + if (tip && + tip->nTime > get_unix_time_seconds() + MAX_FUTURE_BLOCK_TIME) { + return ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE; + } + + if (!CVerifyDB().VerifyDB(*chainstate, config, + chainstate->CoinsDB(), check_level, + check_blocks)) { + return ChainstateLoadVerifyError::ERROR_CORRUPTED_BLOCK_DB; } } } return std::nullopt; }