aboutsummaryrefslogtreecommitdiff
path: root/src/init.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/init.cpp')
-rw-r--r--src/init.cpp323
1 files changed, 132 insertions, 191 deletions
diff --git a/src/init.cpp b/src/init.cpp
index f659de3a02..015e17596c 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2020 The Bitcoin Core developers
+// Copyright (c) 2009-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.
@@ -29,13 +29,15 @@
#include <interfaces/init.h>
#include <interfaces/node.h>
#include <mapport.h>
-#include <miner.h>
#include <net.h>
#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
#include <node/blockstorage.h>
+#include <node/caches.h>
+#include <node/chainstate.h>
#include <node/context.h>
+#include <node/miner.h>
#include <node/ui_interface.h>
#include <policy/feerate.h>
#include <policy/fees.h>
@@ -59,6 +61,7 @@
#include <util/asmap.h>
#include <util/check.h>
#include <util/moneystr.h>
+#include <util/strencodings.h>
#include <util/string.h>
#include <util/syscall_sandbox.h>
#include <util/system.h>
@@ -92,6 +95,22 @@
#include <zmq/zmqrpc.h>
#endif
+using node::CacheSizes;
+using node::CalculateCacheSizes;
+using node::ChainstateLoadVerifyError;
+using node::ChainstateLoadingError;
+using node::CleanupBlockRevFiles;
+using node::DEFAULT_PRINTPRIORITY;
+using node::DEFAULT_STOPAFTERBLOCKIMPORT;
+using node::LoadChainstate;
+using node::NodeContext;
+using node::ThreadImport;
+using node::VerifyLoadedChainstate;
+using node::fHavePruned;
+using node::fPruneMode;
+using node::fReindex;
+using node::nPruneTarget;
+
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
@@ -436,7 +455,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'download' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target per 24h. Limit does not apply to peers with 'download' permission or blocks created within past week. 0 = no limit (default: %s). Optional suffix units [k|K|m|M|g|G|t|T] (default: M). Lowercase is 1000 base while uppercase is 1024 base", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -1109,6 +1128,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
{
const ArgsManager& args = *Assert(node.args);
const CChainParams& chainparams = Params();
+
+ auto opt_max_upload = ParseByteUnits(args.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET), ByteUnit::M);
+ if (!opt_max_upload) {
+ return InitError(strprintf(_("Unable to parse -maxuploadtarget: '%s' (possible integer overflow?)"), args.GetArg("-maxuploadtarget", "")));
+ }
+
// ********************************************************* Step 4a: application initialization
if (!CreatePidFile(args)) {
// Detailed error printed inside CreatePidFile().
@@ -1373,217 +1398,124 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
bool fReindexChainState = args.GetBoolArg("-reindex-chainstate", false);
// cache size calculations
- int64_t nTotalCache = (args.GetIntArg("-dbcache", nDefaultDbCache) << 20);
- nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
- nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
- int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
- nTotalCache -= nBlockTreeDBCache;
- int64_t nTxIndexCache = std::min(nTotalCache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
- nTotalCache -= nTxIndexCache;
- int64_t filter_index_cache = 0;
- if (!g_enabled_filter_types.empty()) {
- size_t n_indexes = g_enabled_filter_types.size();
- int64_t max_cache = std::min(nTotalCache / 8, max_filter_index_cache << 20);
- filter_index_cache = max_cache / n_indexes;
- nTotalCache -= filter_index_cache * n_indexes;
- }
- int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
- nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
- nTotalCache -= nCoinDBCache;
- int64_t nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache
+ CacheSizes cache_sizes = CalculateCacheSizes(args, g_enabled_filter_types.size());
+
int64_t nMempoolSizeMax = args.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
- LogPrintf("* Using %.1f MiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024));
+ LogPrintf("* Using %.1f MiB for block index database\n", cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
- LogPrintf("* Using %.1f MiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024));
+ LogPrintf("* Using %.1f MiB for transaction index database\n", cache_sizes.tx_index * (1.0 / 1024 / 1024));
}
for (BlockFilterType filter_type : g_enabled_filter_types) {
LogPrintf("* Using %.1f MiB for %s block filter index database\n",
- filter_index_cache * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
+ cache_sizes.filter_index * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
}
- LogPrintf("* Using %.1f MiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024));
- LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024));
+ LogPrintf("* Using %.1f MiB for chain state database\n", cache_sizes.coins_db * (1.0 / 1024 / 1024));
+ LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024));
bool fLoaded = false;
while (!fLoaded && !ShutdownRequested()) {
const bool fReset = fReindex;
- auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
- return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
- };
bilingual_str strLoadError;
uiInterface.InitMessage(_("Loading block index…").translated);
-
- do {
- const int64_t load_block_index_start_time = GetTimeMillis();
- try {
- LOCK(cs_main);
- chainman.InitializeChainstate(Assert(node.mempool.get()));
- chainman.m_total_coinstip_cache = nCoinCacheUsage;
- chainman.m_total_coinsdb_cache = nCoinDBCache;
-
- UnloadBlockIndex(node.mempool.get(), 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, false, 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 (ShutdownRequested()) break;
-
- // 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 (ShutdownRequested()) break;
- strLoadError = _("Error loading block database");
- break;
- }
-
+ const int64_t load_block_index_start_time = GetTimeMillis();
+ std::optional<ChainstateLoadingError> maybe_load_error;
+ try {
+ maybe_load_error = LoadChainstate(fReset,
+ chainman,
+ Assert(node.mempool.get()),
+ fPruneMode,
+ chainparams.GetConsensus(),
+ fReindexChainState,
+ cache_sizes.block_tree_db,
+ cache_sizes.coins_db,
+ cache_sizes.coins,
+ /*block_tree_db_in_memory=*/false,
+ /*coins_db_in_memory=*/false,
+ /*shutdown_requested=*/ShutdownRequested,
+ /*coins_error_cb=*/[]() {
+ uiInterface.ThreadSafeMessageBox(
+ _("Error reading from database, shutting down."),
+ "", CClientUIInterface::MSG_ERROR);
+ });
+ } catch (const std::exception& e) {
+ LogPrintf("%s\n", e.what());
+ maybe_load_error = ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED;
+ }
+ if (maybe_load_error.has_value()) {
+ switch (maybe_load_error.value()) {
+ case ChainstateLoadingError::ERROR_LOADING_BLOCK_DB:
+ strLoadError = _("Error loading block database");
+ break;
+ case ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK:
// If the loaded chain has a wrong genesis, bail out immediately
// (we're likely using a testnet datadir, or the other way around).
- if (!chainman.BlockIndex().empty() &&
- !chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
- return InitError(_("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) {
- strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
- break;
- }
-
- // 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()) {
- strLoadError = _("Error initializing block database");
- break;
- }
-
- // At this point we're either in reindex or we've loaded a useful
- // block tree into BlockIndex()!
-
- bool failed_chainstate_init = false;
-
- for (CChainState* chainstate : chainman.GetAll()) {
- chainstate->InitCoinsDB(
- /* cache_size_bytes */ nCoinDBCache,
- /* in_memory */ false,
- /* should_wipe */ fReset || fReindexChainState);
-
- chainstate->CoinsErrorCatcher().AddReadErrCallback([]() {
- uiInterface.ThreadSafeMessageBox(
- _("Error reading from database, shutting down."),
- "", CClientUIInterface::MSG_ERROR);
- });
-
- // 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()) {
- strLoadError = _("Error upgrading chainstate database");
- failed_chainstate_init = true;
- break;
- }
-
- // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
- if (!chainstate->ReplayBlocks()) {
- strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
- failed_chainstate_init = true;
- break;
- }
-
- // 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()) {
- strLoadError = _("Error initializing block database");
- failed_chainstate_init = true;
- break; // out of the per-chainstate loop
- }
- assert(chainstate->m_chain.Tip() != nullptr);
- }
- }
-
- if (failed_chainstate_init) {
- break; // out of the chainstate activation do-while
- }
- } catch (const std::exception& e) {
- LogPrintf("%s\n", e.what());
+ return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
+ case ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX:
+ strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
+ break;
+ case ChainstateLoadingError::ERROR_LOAD_GENESIS_BLOCK_FAILED:
+ strLoadError = _("Error initializing block database");
+ break;
+ case ChainstateLoadingError::ERROR_CHAINSTATE_UPGRADE_FAILED:
+ strLoadError = _("Error upgrading chainstate database");
+ break;
+ case ChainstateLoadingError::ERROR_REPLAYBLOCKS_FAILED:
+ strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.");
+ break;
+ case ChainstateLoadingError::ERROR_LOADCHAINTIP_FAILED:
+ strLoadError = _("Error initializing block database");
+ break;
+ case ChainstateLoadingError::ERROR_GENERIC_BLOCKDB_OPEN_FAILED:
strLoadError = _("Error opening block database");
break;
+ case ChainstateLoadingError::ERROR_BLOCKS_WITNESS_INSUFFICIENTLY_VALIDATED:
+ strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
+ chainparams.GetConsensus().SegwitHeight);
+ break;
+ case ChainstateLoadingError::SHUTDOWN_PROBED:
+ break;
}
-
- if (!fReset) {
- LOCK(cs_main);
- auto chainstates{chainman.GetAll()};
- if (std::any_of(chainstates.begin(), chainstates.end(),
- [](const CChainState* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
- strLoadError = strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
- chainparams.GetConsensus().SegwitHeight);
- break;
- }
- }
-
- bool failed_verification = false;
-
+ } else {
+ std::optional<ChainstateLoadVerifyError> maybe_verify_error;
try {
- LOCK(cs_main);
-
- for (CChainState* chainstate : chainman.GetAll()) {
- if (!is_coinsview_empty(chainstate)) {
- uiInterface.InitMessage(_("Verifying blocks…").translated);
- if (fHavePruned && args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) {
- LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
- MIN_BLOCKS_TO_KEEP);
- }
-
- const CBlockIndex* tip = chainstate->m_chain.Tip();
- RPCNotifyBlockChange(tip);
- if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
- strLoadError = _("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");
- failed_verification = true;
- break;
- }
-
- if (!CVerifyDB().VerifyDB(
- *chainstate, chainparams, chainstate->CoinsDB(),
- args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
- args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
- strLoadError = _("Corrupted block database detected");
- failed_verification = true;
- break;
- }
- }
+ uiInterface.InitMessage(_("Verifying blocks…").translated);
+ auto check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
+ if (fHavePruned && check_blocks > MIN_BLOCKS_TO_KEEP) {
+ LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
+ MIN_BLOCKS_TO_KEEP);
}
+ maybe_verify_error = VerifyLoadedChainstate(chainman,
+ fReset,
+ fReindexChainState,
+ chainparams.GetConsensus(),
+ check_blocks,
+ args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL),
+ /*get_unix_time_seconds=*/static_cast<int64_t(*)()>(GetTime));
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
- strLoadError = _("Error opening block database");
- failed_verification = true;
- break;
+ maybe_verify_error = ChainstateLoadVerifyError::ERROR_GENERIC_FAILURE;
}
-
- if (!failed_verification) {
+ if (maybe_verify_error.has_value()) {
+ switch (maybe_verify_error.value()) {
+ case ChainstateLoadVerifyError::ERROR_BLOCK_FROM_FUTURE:
+ strLoadError = _("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");
+ break;
+ case ChainstateLoadVerifyError::ERROR_CORRUPTED_BLOCK_DB:
+ strLoadError = _("Corrupted block database detected");
+ break;
+ case ChainstateLoadVerifyError::ERROR_GENERIC_FAILURE:
+ strLoadError = _("Error opening block database");
+ break;
+ }
+ } else {
fLoaded = true;
LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
}
- } while(false);
+ }
if (!fLoaded && !ShutdownRequested()) {
// first suggest a reindex
@@ -1619,14 +1551,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
return InitError(*error);
}
- g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex);
+ g_txindex = std::make_unique<TxIndex>(cache_sizes.tx_index, false, fReindex);
if (!g_txindex->Start(chainman.ActiveChainstate())) {
return false;
}
}
for (const auto& filter_type : g_enabled_filter_types) {
- InitBlockFilterIndex(filter_type, filter_index_cache, false, fReindex);
+ InitBlockFilterIndex(filter_type, cache_sizes.filter_index, false, fReindex);
if (!GetBlockFilterIndex(filter_type)->Start(chainman.ActiveChainstate())) {
return false;
}
@@ -1760,8 +1692,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
connOptions.nSendBufferMaxSize = 1000 * args.GetIntArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize = 1000 * args.GetIntArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.m_added_nodes = args.GetArgs("-addnode");
-
- connOptions.nMaxOutboundLimit = 1024 * 1024 * args.GetIntArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET);
+ connOptions.nMaxOutboundLimit = *opt_max_upload;
connOptions.m_peer_connect_timeout = peer_connect_timeout;
for (const std::string& bind_arg : args.GetArgs("-bind")) {
@@ -1851,7 +1782,17 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 13: finished
+ // At this point, the RPC is "started", but still in warmup, which means it
+ // cannot yet be called. Before we make it callable, we need to make sure
+ // that the RPC's view of the best block is valid and consistent with
+ // ChainstateManager's ActiveTip.
+ //
+ // If we do not do this, RPC's view of the best block will be height=0 and
+ // hash=0x0. This will lead to erroroneous responses for things like
+ // waitforblockheight.
+ RPCNotifyBlockChange(chainman.ActiveTip());
SetRPCWarmupFinished();
+
uiInterface.InitMessage(_("Done loading").translated);
for (const auto& client : node.chain_clients) {