diff options
Diffstat (limited to 'src/node/chainstate.cpp')
-rw-r--r-- | src/node/chainstate.cpp | 146 |
1 files changed, 83 insertions, 63 deletions
diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index d03b9dcac6..c4dd9ba6c5 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -4,69 +4,90 @@ #include <node/chainstate.h> +#include <chain.h> +#include <coins.h> #include <consensus/params.h> #include <node/blockstorage.h> +#include <node/caches.h> +#include <sync.h> +#include <threadsafety.h> +#include <tinyformat.h> +#include <txdb.h> +#include <uint256.h> +#include <util/time.h> +#include <util/translation.h> #include <validation.h> +#include <algorithm> +#include <atomic> +#include <cassert> +#include <memory> +#include <vector> + namespace node { -std::optional<ChainstateLoadingError> 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<bool()> shutdown_requested, - std::function<void()> coins_error_cb) +ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, + const ChainstateLoadOptions& options) { auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { - return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull(); + return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull(); }; - LOCK(cs_main); - chainman.InitializeChainstate(mempool); - chainman.m_total_coinstip_cache = nCoinCacheUsage; - chainman.m_total_coinsdb_cache = nCoinDBCache; + if (!hashAssumeValid.IsNull()) { + LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex()); + } else { + LogPrintf("Validating signatures for all blocks.\n"); + } + LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex()); + if (nMinimumChainWork < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) { + LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex()); + } + if (nPruneTarget == std::numeric_limits<uint64_t>::max()) { + LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); + } else if (nPruneTarget) { + LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024); + } - UnloadBlockIndex(mempool, chainman); + LOCK(cs_main); + chainman.InitializeChainstate(options.mempool); + chainman.m_total_coinstip_cache = cache_sizes.coins; + chainman.m_total_coinsdb_cache = cache_sizes.coins_db; 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)); + pblocktree.reset(new CBlockTreeDB(cache_sizes.block_tree_db, options.block_tree_db_in_memory, options.reindex)); - if (fReset) { + if (options.reindex) { pblocktree->WriteReindexing(true); //If we're reindexing in prune mode, wipe away unusable block files and all undo data files - if (fPruneMode) + if (options.prune) { CleanupBlockRevFiles(); + } } - if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED; + if (options.check_interrupt && options.check_interrupt()) return {ChainstateLoadStatus::INTERRUPTED, {}}; - // LoadBlockIndex will load fHavePruned if we've ever removed a + // LoadBlockIndex will load m_have_pruned 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! + // Note that it also sets fReindex global based on the disk flag! + // From here on, fReindex and options.reindex values may be different! if (!chainman.LoadBlockIndex()) { - if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED; - return ChainstateLoadingError::ERROR_LOADING_BLOCK_DB; + if (options.check_interrupt && options.check_interrupt()) return {ChainstateLoadStatus::INTERRUPTED, {}}; + return {ChainstateLoadStatus::FAILURE, _("Error loading block database")}; } if (!chainman.BlockIndex().empty() && - !chainman.m_blockman.LookupBlockIndex(consensus_params.hashGenesisBlock)) { - return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK; + !chainman.m_blockman.LookupBlockIndex(chainman.GetConsensus().hashGenesisBlock)) { + // If the loaded chain has a wrong genesis, bail out immediately + // (we're likely using a testnet datadir, or the other way around). + return {ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB, _("Incorrect or no genesis block found. Wrong datadir for network?")}; } // 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; + if (chainman.m_blockman.m_have_pruned && !options.prune) { + return {ChainstateLoadStatus::FAILURE, _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain")}; } // At this point blocktree args are consistent with what's on disk. @@ -74,7 +95,7 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset, // (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; + return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")}; } // At this point we're either in reindex or we've loaded a useful @@ -82,59 +103,56 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset, for (CChainState* chainstate : chainman.GetAll()) { chainstate->InitCoinsDB( - /* cache_size_bytes */ nCoinDBCache, - /* in_memory */ coins_db_in_memory, - /* should_wipe */ fReset || fReindexChainState); + /*cache_size_bytes=*/cache_sizes.coins_db, + /*in_memory=*/options.coins_db_in_memory, + /*should_wipe=*/options.reindex || options.reindex_chainstate); - if (coins_error_cb) { - chainstate->CoinsErrorCatcher().AddReadErrCallback(coins_error_cb); + if (options.coins_error_cb) { + chainstate->CoinsErrorCatcher().AddReadErrCallback(options.coins_error_cb); } - // If necessary, upgrade from older database format. + // Refuse to load unsupported 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 (chainstate->CoinsDB().NeedsUpgrade()) { + return {ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB, _("Unsupported chainstate database format found. " + "Please restart with -reindex-chainstate. This will " + "rebuild the chainstate database.")}; } // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate if (!chainstate->ReplayBlocks()) { - return ChainstateLoadingError::ERROR_REPLAYBLOCKS_FAILED; + return {ChainstateLoadStatus::FAILURE, _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.")}; } // The on-disk coinsdb is now in a good state, create the cache - chainstate->InitCoinsCache(nCoinCacheUsage); + chainstate->InitCoinsCache(cache_sizes.coins); 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; + return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")}; } assert(chainstate->m_chain.Tip() != nullptr); } } - if (!fReset) { + if (!options.reindex) { auto chainstates{chainman.GetAll()}; if (std::any_of(chainstates.begin(), chainstates.end(), [](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) { - return ChainstateLoadingError::ERROR_BLOCKS_WITNESS_INSUFFICIENTLY_VALIDATED; - } + return {ChainstateLoadStatus::FAILURE, strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."), + chainman.GetConsensus().SegwitHeight)}; + }; } - return std::nullopt; + return {ChainstateLoadStatus::SUCCESS, {}}; } -std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManager& chainman, - bool fReset, - bool fReindexChainState, - const Consensus::Params& consensus_params, - int check_blocks, - int check_level, - std::function<int64_t()> get_unix_time_seconds) +ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options) { auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { - return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull(); + return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull(); }; LOCK(cs_main); @@ -142,19 +160,21 @@ std::optional<ChainstateLoadVerifyError> VerifyLoadedChainstate(ChainstateManage 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 (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) { + return {ChainstateLoadStatus::FAILURE, _("The block database contains a block which appears to be from the future. " + "This may be due to your computer's date and time being set incorrectly. " + "Only rebuild the block database if you are sure that your computer's date and time are correct")}; } if (!CVerifyDB().VerifyDB( - *chainstate, consensus_params, chainstate->CoinsDB(), - check_level, - check_blocks)) { - return ChainstateLoadVerifyError::ERROR_CORRUPTED_BLOCK_DB; + *chainstate, chainman.GetConsensus(), chainstate->CoinsDB(), + options.check_level, + options.check_blocks)) { + return {ChainstateLoadStatus::FAILURE, _("Corrupted block database detected")}; } } } - return std::nullopt; + return {ChainstateLoadStatus::SUCCESS, {}}; } } // namespace node |