aboutsummaryrefslogtreecommitdiff
path: root/src/init.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/init.cpp')
-rw-r--r--src/init.cpp347
1 files changed, 212 insertions, 135 deletions
diff --git a/src/init.cpp b/src/init.cpp
index 90d2624c7..83b3f2f47 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -47,12 +47,11 @@
#include <txdb.h>
#include <txmempool.h>
#include <ui_interface.h>
+#include <util/asmap.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
-#include <util/validation.h>
-#include <util/asmap.h>
#include <validation.h>
#include <hash.h>
@@ -74,6 +73,7 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
+#include <boost/signals2/signal.hpp>
#include <boost/thread.hpp>
#if ENABLE_ZMQ
@@ -87,10 +87,6 @@ static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
-// Dump addresses to banlist.dat every 15 minutes (900s)
-static constexpr int DUMP_BANS_INTERVAL = 60 * 15;
-
-
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for
// accessing block files don't count towards the fd_set size limit
@@ -157,7 +153,6 @@ NODISCARD static bool CreatePidFile()
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
static boost::thread_group threadGroup;
-static CScheduler scheduler;
void Interrupt(NodeContext& node)
{
@@ -202,12 +197,26 @@ void Shutdown(NodeContext& node)
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
if (node.peer_logic) UnregisterValidationInterface(node.peer_logic.get());
- if (node.connman) node.connman->Stop();
+ // Follow the lock order requirements:
+ // * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraOutboundCount
+ // which locks cs_vNodes.
+ // * ProcessMessage locks cs_main and g_cs_orphans before indirectly calling ForEachNode which
+ // locks cs_vNodes.
+ // * CConnman::Stop calls DeleteNode, which calls FinalizeNode, which locks cs_main and calls
+ // EraseOrphansFor, which locks g_cs_orphans.
+ //
+ // Thus the implicit locking order requirement is: (1) cs_main, (2) g_cs_orphans, (3) cs_vNodes.
+ if (node.connman) {
+ node.connman->StopThreads();
+ LOCK2(::cs_main, ::g_cs_orphans);
+ node.connman->StopNodes();
+ }
StopTorControl();
// After everything has been shut down, but before things get flushed, stop the
// CScheduler/checkqueue threadGroup
+ if (node.scheduler) node.scheduler->stop();
threadGroup.interrupt_all();
threadGroup.join_all();
@@ -234,13 +243,12 @@ void Shutdown(NodeContext& node)
}
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
- //
- // g_chainstate is referenced here directly (instead of ::ChainstateActive()) because it
- // may not have been initialized yet.
{
LOCK(cs_main);
- if (g_chainstate && g_chainstate->CanFlushToDisk()) {
- g_chainstate->ForceFlushStateToDisk();
+ for (CChainState* chainstate : g_chainman.GetAll()) {
+ if (chainstate->CanFlushToDisk()) {
+ chainstate->ForceFlushStateToDisk();
+ }
}
}
@@ -264,9 +272,11 @@ void Shutdown(NodeContext& node)
{
LOCK(cs_main);
- if (g_chainstate && g_chainstate->CanFlushToDisk()) {
- g_chainstate->ForceFlushStateToDisk();
- g_chainstate->ResetCoinsViews();
+ for (CChainState* chainstate : g_chainman.GetAll()) {
+ if (chainstate->CanFlushToDisk()) {
+ chainstate->ForceFlushStateToDisk();
+ chainstate->ResetCoinsViews();
+ }
}
pblocktree.reset();
}
@@ -282,6 +292,15 @@ void Shutdown(NodeContext& node)
}
#endif
+ node.chain_clients.clear();
+ UnregisterAllValidationInterfaces();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
+ globalVerifyHandle.reset();
+ ECC_Stop();
+ node.args = nullptr;
+ if (node.mempool) node.mempool = nullptr;
+ node.scheduler.reset();
+
try {
if (!fs::remove(GetPidFile())) {
LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__);
@@ -289,12 +308,7 @@ void Shutdown(NodeContext& node)
} catch (const fs::filesystem_error& e) {
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
}
- node.chain_clients.clear();
- UnregisterAllValidationInterfaces();
- GetMainSignals().UnregisterBackgroundSignalScheduler();
- globalVerifyHandle.reset();
- ECC_Stop();
- if (node.mempool) node.mempool = nullptr;
+
LogPrintf("%s: done\n", __func__);
}
@@ -347,8 +361,11 @@ static void OnRPCStopped()
LogPrint(BCLog::RPC, "RPC stopped.\n");
}
-void SetupServerArgs()
+void SetupServerArgs(NodeContext& node)
{
+ assert(!node.args);
+ node.args = &gArgs;
+
SetupHelpOptions(gArgs);
gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); // server-only for now
@@ -375,7 +392,7 @@ void SetupServerArgs()
gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
- gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Transactions from the wallet, RPC and relay whitelisted inbound peers are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Automatic broadcast and rebroadcast of any transactions from inbound peers is disabled, unless '-whitelistforcerelay' is '1', in which case whitelisted peers' transactions will be relayed. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
@@ -409,6 +426,7 @@ void SetupServerArgs()
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
@@ -437,7 +455,6 @@ void SetupServerArgs()
gArgs.AddArg("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);
- gArgs.AddArg("-asmap=<file>", "Specify asn mapping used for bucketing of the peers. Path should be relative to the -datadir path.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
#ifdef USE_UPNP
#if USE_UPNP
gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -450,7 +467,7 @@ void SetupServerArgs()
gArgs.AddArg("-whitebind=<[permissions@]addr>", "Bind to given address and whitelist peers connecting to it. "
"Use [host]:port notation for IPv6. Allowed permissions are bloomfilter (allow requesting BIP37 filtered blocks and transactions), "
"noban (do not ban for misbehavior), "
- "forcerelay (relay even non-standard transactions), "
+ "forcerelay (relay transactions that are already in the mempool; implies relay), "
"relay (relay even in -blocksonly mode), "
"and mempool (allow requesting BIP35 mempool contents). "
"Specify multiple permissions separated by commas (default: noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -507,7 +524,11 @@ void SetupServerArgs()
gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+#ifdef HAVE_THREAD_LOCAL
gArgs.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+#else
+ hidden_args.emplace_back("-logthreadnames");
+#endif
gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -527,7 +548,7 @@ void SetupServerArgs()
gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
- gArgs.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool or violate local relay policy. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
+ gArgs.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
gArgs.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted inbound peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
@@ -564,13 +585,12 @@ void SetupServerArgs()
std::string LicenseInfo()
{
const std::string URL_SOURCE_CODE = "<https://github.com/bitcoin/bitcoin>";
- const std::string URL_WEBSITE = "<https://bitcoincore.org>";
return CopyrightHolders(strprintf(_("Copyright (C) %i-%i").translated, 2009, COPYRIGHT_YEAR) + " ") + "\n" +
"\n" +
strprintf(_("Please contribute if you find %s useful. "
"Visit %s for further information about the software.").translated,
- PACKAGE_NAME, URL_WEBSITE) +
+ PACKAGE_NAME, "<" PACKAGE_URL ">") +
"\n" +
strprintf(_("The source code is available from %s.").translated,
URL_SOURCE_CODE) +
@@ -708,11 +728,17 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
}
// scan for better chains in the block chain database, that are not yet connected in the active best chain
- BlockValidationState state;
- if (!ActivateBestChain(state, chainparams)) {
- LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state));
- StartShutdown();
- return;
+
+ // We can't hold cs_main during ActivateBestChain even though we're accessing
+ // the g_chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve
+ // the relevant pointers before the ABC call.
+ for (CChainState* chainstate : WITH_LOCK(::cs_main, return g_chainman.GetAll())) {
+ BlockValidationState state;
+ if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) {
+ LogPrintf("Failed to connect best block (%s)\n", state.ToString());
+ StartShutdown();
+ return;
+ }
}
if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
@@ -840,7 +866,9 @@ void InitLogging()
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
+#ifdef HAVE_THREAD_LOCAL
LogInstance().m_log_threadnames = gArgs.GetBoolArg("-logthreadnames", DEFAULT_LOGTHREADNAMES);
+#endif
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
@@ -1268,16 +1296,19 @@ bool AppInitMain(NodeContext& node)
}
}
+ assert(!node.scheduler);
+ node.scheduler = MakeUnique<CScheduler>();
+
// Start the lightweight task scheduler thread
- CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler);
+ CScheduler::Function serviceLoop = [&node]{ node.scheduler->serviceQueue(); };
threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
// Gather some entropy once per minute.
- scheduler.scheduleEvery([]{
+ node.scheduler->scheduleEvery([]{
RandAddPeriodic();
- }, 60000);
+ }, std::chrono::minutes{1});
- GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
+ GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
// Create client interfaces for wallets that are supposed to be loaded
// according to -wallet and -disablewallet options. This only constructs
@@ -1326,8 +1357,12 @@ bool AppInitMain(NodeContext& node)
node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", &uiInterface, gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!node.connman);
node.connman = std::unique_ptr<CConnman>(new CConnman(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max())));
+ // Make mempool generally available in the node context. For example the connection manager, wallet, or RPC threads,
+ // which are all started after this, may use it from the node context.
+ assert(!node.mempool);
+ node.mempool = &::mempool;
- node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), scheduler));
+ node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler, *node.mempool));
RegisterValidationInterface(node.peer_logic.get());
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1416,6 +1451,31 @@ bool AppInitMain(NodeContext& node)
return InitError(ResolveErrMsg("externalip", strAddr));
}
+ // Read asmap file if configured
+ if (gArgs.IsArgSet("-asmap")) {
+ fs::path asmap_path = fs::path(gArgs.GetArg("-asmap", ""));
+ if (asmap_path.empty()) {
+ asmap_path = DEFAULT_ASMAP_FILENAME;
+ }
+ if (!asmap_path.is_absolute()) {
+ asmap_path = GetDataDir() / asmap_path;
+ }
+ if (!fs::exists(asmap_path)) {
+ InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path));
+ return false;
+ }
+ std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path);
+ if (asmap.size() == 0) {
+ InitError(strprintf(_("Could not parse asmap file %s").translated, asmap_path));
+ return false;
+ }
+ const uint256 asmap_version = SerializeHash(asmap);
+ node.connman->SetAsmap(std::move(asmap));
+ LogPrintf("Using asmap version %s for IP bucketing\n", asmap_version.ToString());
+ } else {
+ LogPrintf("Using /16 prefix for IP bucketing\n");
+ }
+
#if ENABLE_ZMQ
g_zmq_notification_interface = CZMQNotificationInterface::Create();
@@ -1470,17 +1530,18 @@ bool AppInitMain(NodeContext& node)
bool fLoaded = false;
while (!fLoaded && !ShutdownRequested()) {
bool fReset = fReindex;
+ auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
+ return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull();
+ };
std::string strLoadError;
uiInterface.InitMessage(_("Loading block index...").translated);
do {
const int64_t load_block_index_start_time = GetTimeMillis();
- bool is_coinsview_empty;
try {
LOCK(cs_main);
- // This statement makes ::ChainstateActive() usable.
- g_chainstate = MakeUnique<CChainState>();
+ g_chainman.InitializeChainstate();
UnloadBlockIndex();
// new CBlockTreeDB tries to delete the existing file, which
@@ -1533,43 +1594,53 @@ bool AppInitMain(NodeContext& node)
// At this point we're either in reindex or we've loaded a useful
// block tree into BlockIndex()!
- ::ChainstateActive().InitCoinsDB(
- /* cache_size_bytes */ nCoinDBCache,
- /* in_memory */ false,
- /* should_wipe */ fReset || fReindexChainState);
-
- ::ChainstateActive().CoinsErrorCatcher().AddReadErrCallback([]() {
- uiInterface.ThreadSafeMessageBox(
- _("Error reading from database, shutting down.").translated,
- "", 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 (!::ChainstateActive().CoinsDB().Upgrade()) {
- strLoadError = _("Error upgrading chainstate database").translated;
- break;
- }
-
- // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
- if (!::ChainstateActive().ReplayBlocks(chainparams)) {
- strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated;
- break;
- }
-
- // The on-disk coinsdb is now in a good state, create the cache
- ::ChainstateActive().InitCoinsCache();
- assert(::ChainstateActive().CanFlushToDisk());
+ bool failed_chainstate_init = false;
+
+ for (CChainState* chainstate : g_chainman.GetAll()) {
+ LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
+ chainstate->InitCoinsDB(
+ /* cache_size_bytes */ nCoinDBCache,
+ /* in_memory */ false,
+ /* should_wipe */ fReset || fReindexChainState);
+
+ chainstate->CoinsErrorCatcher().AddReadErrCallback([]() {
+ uiInterface.ThreadSafeMessageBox(
+ _("Error reading from database, shutting down.").translated,
+ "", 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").translated;
+ failed_chainstate_init = true;
+ break;
+ }
- is_coinsview_empty = fReset || fReindexChainState ||
- ::ChainstateActive().CoinsTip().GetBestBlock().IsNull();
- if (!is_coinsview_empty) {
- // LoadChainTip initializes the chain based on CoinsTip()'s best block
- if (!::ChainstateActive().LoadChainTip(chainparams)) {
- strLoadError = _("Error initializing block database").translated;
+ // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
+ if (!chainstate->ReplayBlocks(chainparams)) {
+ strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated;
+ failed_chainstate_init = true;
break;
}
- assert(::ChainActive().Tip() != nullptr);
+
+ // The on-disk coinsdb is now in a good state, create the cache
+ chainstate->InitCoinsCache();
+ assert(chainstate->CanFlushToDisk());
+
+ if (!is_coinsview_empty(chainstate)) {
+ // LoadChainTip initializes the chain based on CoinsTip()'s best block
+ if (!chainstate->LoadChainTip(chainparams)) {
+ strLoadError = _("Error initializing block database").translated;
+ 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());
@@ -1577,49 +1648,76 @@ bool AppInitMain(NodeContext& node)
break;
}
- if (!fReset) {
- // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
- // It both disconnects blocks based on ::ChainActive(), and drops block data in
- // BlockIndex() based on lack of available witness data.
- uiInterface.InitMessage(_("Rewinding blocks...").translated);
- if (!RewindBlockIndex(chainparams)) {
- strLoadError = _("Unable to rewind the database to a pre-fork state. You will need to redownload the blockchain").translated;
- break;
+ bool failed_rewind{false};
+ // Can't hold cs_main while calling RewindBlockIndex, so retrieve the relevant
+ // chainstates beforehand.
+ for (CChainState* chainstate : WITH_LOCK(::cs_main, return g_chainman.GetAll())) {
+ if (!fReset) {
+ // Note that RewindBlockIndex MUST run even if we're about to -reindex-chainstate.
+ // It both disconnects blocks based on the chainstate, and drops block data in
+ // BlockIndex() based on lack of available witness data.
+ uiInterface.InitMessage(_("Rewinding blocks...").translated);
+ if (!chainstate->RewindBlockIndex(chainparams)) {
+ strLoadError = _(
+ "Unable to rewind the database to a pre-fork state. "
+ "You will need to redownload the blockchain").translated;
+ failed_rewind = true;
+ break; // out of the per-chainstate loop
+ }
}
}
+ if (failed_rewind) {
+ break; // out of the chainstate activation do-while
+ }
+
+ bool failed_verification = false;
+
try {
LOCK(cs_main);
- if (!is_coinsview_empty) {
- uiInterface.InitMessage(_("Verifying blocks...").translated);
- if (fHavePruned && gArgs.GetArg("-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);
- }
-
- CBlockIndex* tip = ::ChainActive().Tip();
- RPCNotifyBlockChange(true, 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").translated;
- break;
- }
- if (!CVerifyDB().VerifyDB(chainparams, &::ChainstateActive().CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
- gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
- strLoadError = _("Corrupted block database detected").translated;
- break;
+ for (CChainState* chainstate : g_chainman.GetAll()) {
+ if (!is_coinsview_empty(chainstate)) {
+ uiInterface.InitMessage(_("Verifying blocks...").translated);
+ if (fHavePruned && gArgs.GetArg("-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(true, 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").translated;
+ failed_verification = true;
+ break;
+ }
+
+ // Only verify the DB of the active chainstate. This is fixed in later
+ // work when we allow VerifyDB to be parameterized by chainstate.
+ if (&::ChainstateActive() == chainstate &&
+ !CVerifyDB().VerifyDB(
+ chainparams, &chainstate->CoinsDB(),
+ gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
+ gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
+ strLoadError = _("Corrupted block database detected").translated;
+ failed_verification = true;
+ break;
+ }
}
}
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
strLoadError = _("Error opening block database").translated;
+ failed_verification = true;
break;
}
- fLoaded = true;
- LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
+ if (!failed_verification) {
+ fLoaded = true;
+ LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time);
+ }
} while(false);
if (!fLoaded && !ShutdownRequested()) {
@@ -1650,11 +1748,6 @@ bool AppInitMain(NodeContext& node)
return false;
}
- // Now that the chain state is loaded, make mempool generally available in the node context. For example the
- // connection manager, wallet, or RPC threads, which are all started after this, may use it from the node context.
- assert(!node.mempool);
- node.mempool = &::mempool;
-
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
@@ -1688,8 +1781,11 @@ bool AppInitMain(NodeContext& node)
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
if (!fReindex) {
- uiInterface.InitMessage(_("Pruning blockstore...").translated);
- ::ChainstateActive().PruneAndFlush();
+ LOCK(cs_main);
+ for (CChainState* chainstate : g_chainman.GetAll()) {
+ uiInterface.InitMessage(_("Pruning blockstore...").translated);
+ chainstate->PruneAndFlush();
+ }
}
}
@@ -1819,42 +1915,23 @@ bool AppInitMain(NodeContext& node)
connOptions.m_specified_outgoing = connect;
}
}
- if (!node.connman->Start(scheduler, connOptions)) {
+ if (!node.connman->Start(*node.scheduler, connOptions)) {
return false;
}
- // Read asmap file if configured
- if (gArgs.IsArgSet("-asmap")) {
- std::string asmap_file = gArgs.GetArg("-asmap", "");
- if (asmap_file.empty()) {
- asmap_file = DEFAULT_ASMAP_FILENAME;
- }
- const fs::path asmap_path = GetDataDir() / asmap_file;
- std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path);
- if (asmap.size() == 0) {
- InitError(strprintf(_("Could not find or parse specified asmap: '%s'").translated, asmap_path));
- return false;
- }
- const uint256 asmap_version = SerializeHash(asmap);
- node.connman->SetAsmap(std::move(asmap));
- LogPrintf("Using asmap version %s for IP bucketing.\n", asmap_version.ToString());
- } else {
- LogPrintf("Using /16 prefix for IP bucketing.\n");
- }
-
// ********************************************************* Step 13: finished
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading").translated);
for (const auto& client : node.chain_clients) {
- client->start(scheduler);
+ client->start(*node.scheduler);
}
BanMan* banman = node.banman.get();
- scheduler.scheduleEvery([banman]{
+ node.scheduler->scheduleEvery([banman]{
banman->DumpBanlist();
- }, DUMP_BANS_INTERVAL * 1000);
+ }, DUMP_BANS_INTERVAL);
return true;
}