aboutsummaryrefslogtreecommitdiff
path: root/src/init.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/init.cpp')
-rw-r--r--src/init.cpp507
1 files changed, 297 insertions, 210 deletions
diff --git a/src/init.cpp b/src/init.cpp
index a637aac4d..8d9566edc 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -18,6 +18,7 @@
#include <compat/sanity.h>
#include <consensus/validation.h>
#include <fs.h>
+#include <hash.h>
#include <httprpc.h>
#include <httpserver.h>
#include <index/blockfilterindex.h>
@@ -42,6 +43,7 @@
#include <script/sigcache.h>
#include <script/standard.h>
#include <shutdown.h>
+#include <sync.h>
#include <timedata.h>
#include <torcontrol.h>
#include <txdb.h>
@@ -49,19 +51,19 @@
#include <ui_interface.h>
#include <util/asmap.h>
#include <util/moneystr.h>
+#include <util/string.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
#include <validation.h>
-#include <hash.h>
-
#include <validationinterface.h>
#include <walletinitinterface.h>
+#include <functional>
+#include <set>
#include <stdint.h>
#include <stdio.h>
-#include <set>
#ifndef WIN32
#include <attributes.h>
@@ -70,10 +72,9 @@
#include <sys/stat.h>
#endif
-#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/replace.hpp>
-#include <boost/algorithm/string/split.hpp>
-#include <boost/thread.hpp>
+#include <boost/signals2/signal.hpp>
+#include <boost/thread/thread.hpp>
#if ENABLE_ZMQ
#include <zmq/zmqabstractnotifier.h>
@@ -86,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
@@ -124,7 +121,7 @@ NODISCARD static bool CreatePidFile()
#endif
return true;
} else {
- return InitError(strprintf(_("Unable to create the PID file '%s': %s").translated, GetPidFile().string(), std::strerror(errno)));
+ return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno)));
}
}
@@ -155,6 +152,8 @@ NODISCARD static bool CreatePidFile()
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
+static std::thread g_load_block;
+
static boost::thread_group threadGroup;
void Interrupt(NodeContext& node)
@@ -175,11 +174,10 @@ void Interrupt(NodeContext& node)
void Shutdown(NodeContext& node)
{
+ static Mutex g_shutdown_mutex;
+ TRY_LOCK(g_shutdown_mutex, lock_shutdown);
+ if (!lock_shutdown) return;
LogPrintf("%s: In progress...\n", __func__);
- static RecursiveMutex cs_Shutdown;
- TRY_LOCK(cs_Shutdown, lockShutdown);
- if (!lockShutdown)
- return;
/// Note: Shutdown() must be able to handle cases in which initialization failed part of the way,
/// for example if the data directory was found to be locked.
@@ -200,13 +198,27 @@ 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
+ // CScheduler/checkqueue, threadGroup and load block thread.
if (node.scheduler) node.scheduler->stop();
+ if (g_load_block.joinable()) g_load_block.join();
threadGroup.interrupt_all();
threadGroup.join_all();
@@ -233,13 +245,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.
- {
+ if (node.chainman) {
LOCK(cs_main);
- if (g_chainstate && g_chainstate->CanFlushToDisk()) {
- g_chainstate->ForceFlushStateToDisk();
+ for (CChainState* chainstate : node.chainman->GetAll()) {
+ if (chainstate->CanFlushToDisk()) {
+ chainstate->ForceFlushStateToDisk();
+ }
}
}
@@ -261,11 +272,13 @@ void Shutdown(NodeContext& node)
// up with our current chain to avoid any strange pruning edge cases and make
// next startup faster by avoiding rescan.
- {
+ if (node.chainman) {
LOCK(cs_main);
- if (g_chainstate && g_chainstate->CanFlushToDisk()) {
- g_chainstate->ForceFlushStateToDisk();
- g_chainstate->ResetCoinsViews();
+ for (CChainState* chainstate : node.chainman->GetAll()) {
+ if (chainstate->CanFlushToDisk()) {
+ chainstate->ForceFlushStateToDisk();
+ chainstate->ResetCoinsViews();
+ }
}
pblocktree.reset();
}
@@ -281,6 +294,16 @@ void Shutdown(NodeContext& node)
}
#endif
+ node.chain_clients.clear();
+ UnregisterAllValidationInterfaces();
+ GetMainSignals().UnregisterBackgroundSignalScheduler();
+ globalVerifyHandle.reset();
+ ECC_Stop();
+ node.args = nullptr;
+ node.mempool = nullptr;
+ node.chainman = nullptr;
+ node.scheduler.reset();
+
try {
if (!fs::remove(GetPidFile())) {
LogPrintf("%s: Unable to remove PID file: File does not exist\n", __func__);
@@ -288,13 +311,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;
- node.scheduler.reset();
+
LogPrintf("%s: done\n", __func__);
}
@@ -336,19 +353,22 @@ static void registerSignalHandler(int signal, void(*handler)(int))
static boost::signals2::connection rpc_notify_block_change_connection;
static void OnRPCStarted()
{
- rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(&RPCNotifyBlockChange);
+ rpc_notify_block_change_connection = uiInterface.NotifyBlockTip_connect(std::bind(RPCNotifyBlockChange, std::placeholders::_2));
}
static void OnRPCStopped()
{
rpc_notify_block_change_connection.disconnect();
- RPCNotifyBlockChange(false, nullptr);
+ RPCNotifyBlockChange(nullptr);
g_best_block_cv.notify_all();
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 +395,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);
@@ -425,10 +445,11 @@ void SetupServerArgs()
gArgs.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.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);
- gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h). Limit does not apply to peers with 'noban' permission. 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ gArgs.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -448,11 +469,7 @@ void SetupServerArgs()
hidden_args.emplace_back("-upnp");
#endif
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 transactions that are already in the mempool; implies relay), "
- "relay (relay even in -blocksonly mode), "
- "and mempool (allow requesting BIP35 mempool contents). "
+ "Use [host]:port notation for IPv6. Allowed permissions: " + Join(NET_PERMISSIONS_DOC, ", ") + ". "
"Specify multiple permissions separated by commas (default: noban,mempool,relay). Can be specified multiple times.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
gArgs.AddArg("-whitelist=<[permissions@]IP address or network>", "Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or "
@@ -482,14 +499,7 @@ void SetupServerArgs()
#endif
gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
- gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: "
- "level 0 reads the blocks from disk, "
- "level 1 verifies block validity, "
- "level 2 verifies undo data, "
- "level 3 checks disconnection of tip blocks, "
- "and level 4 tries to reconnect the blocks, "
- "each level includes the checks of the previous levels "
- "(0-4, default: %u)", DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
+ gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: %s (0-4, default: %u)", Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block 295000 (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -503,11 +513,16 @@ void SetupServerArgs()
gArgs.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). "
- "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + ListLogCategories() + ".", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + LogInstance().LogCategoriesString() + ".",
+ ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
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);
@@ -564,13 +579,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) +
@@ -582,9 +596,9 @@ std::string LicenseInfo()
}
#if HAVE_SYSTEM
-static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex)
+static void BlockNotifyCallback(SynchronizationState sync_state, const CBlockIndex* pBlockIndex)
{
- if (initialSync || !pBlockIndex)
+ if (sync_state != SynchronizationState::POST_INIT || !pBlockIndex)
return;
std::string strCmd = gArgs.GetArg("-blocknotify", "");
@@ -600,7 +614,7 @@ static bool fHaveGenesis = false;
static Mutex g_genesis_wait_mutex;
static std::condition_variable g_genesis_wait_cv;
-static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex)
+static void BlockNotifyGenesisWait(const CBlockIndex* pBlockIndex)
{
if (pBlockIndex != nullptr) {
{
@@ -666,10 +680,9 @@ static void CleanupBlockRevFiles()
}
}
-static void ThreadImport(std::vector<fs::path> vImportFiles)
+static void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFiles)
{
const CChainParams& chainparams = Params();
- util::ThreadRename("loadblk");
ScheduleBatchPriority();
{
@@ -687,6 +700,10 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
break; // This error is logged in OpenBlockFile
LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile);
LoadExternalBlockFile(chainparams, file, &pos);
+ if (ShutdownRequested()) {
+ LogPrintf("Shutdown requested. Exit %s\n", __func__);
+ return;
+ }
nFile++;
}
pblocktree->WriteReindexing(false);
@@ -702,17 +719,27 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
if (file) {
LogPrintf("Importing blocks file %s...\n", path.string());
LoadExternalBlockFile(chainparams, file);
+ if (ShutdownRequested()) {
+ LogPrintf("Shutdown requested. Exit %s\n", __func__);
+ return;
+ }
} else {
LogPrintf("Warning: Could not open blocks file %s\n", path.string());
}
}
// 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", state.ToString());
- StartShutdown();
- return;
+
+ // We can't hold cs_main during ActivateBestChain even though we're accessing
+ // the 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 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)) {
@@ -733,32 +760,30 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
*/
static bool InitSanityCheck()
{
- if(!ECC_InitSanityCheck()) {
- InitError("Elliptic curve cryptography sanity check failure. Aborting.");
- return false;
+ if (!ECC_InitSanityCheck()) {
+ return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting."));
}
if (!glibc_sanity_test() || !glibcxx_sanity_test())
return false;
if (!Random_SanityCheck()) {
- InitError("OS cryptographic RNG sanity check failure. Aborting.");
- return false;
+ return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting."));
}
return true;
}
-static bool AppInitServers()
+static bool AppInitServers(const util::Ref& context)
{
RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
if (!InitHTTPServer())
return false;
StartRPC();
- if (!StartHTTPRPC())
+ if (!StartHTTPRPC(context))
return false;
- if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST();
+ if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
StartHTTPServer();
return true;
}
@@ -840,7 +865,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);
@@ -892,8 +919,9 @@ bool AppInitBasicSetup()
HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0);
#endif
- if (!SetupNetworking())
- return InitError("Initializing networking failed");
+ if (!SetupNetworking()) {
+ return InitError(Untranslated("Initializing networking failed."));
+ }
#ifndef WIN32
if (!gArgs.GetBoolArg("-sysperms", false)) {
@@ -925,21 +953,31 @@ bool AppInitParameterInteraction()
// also see: InitParameterInteraction()
- // Warn if network-specific options (-addnode, -connect, etc) are
+ // Error if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this network's section of the config file.
std::string network = gArgs.GetChainName();
+ bilingual_str errors;
for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) {
- return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section.").translated, arg, network, network));
+ errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, network, network);
+ }
+
+ if (!errors.empty()) {
+ return InitError(errors);
}
// Warn if unrecognized section name are present in the config file.
+ bilingual_str warnings;
for (const auto& section : gArgs.GetUnrecognizedSections()) {
- InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized.").translated, section.m_file, section.m_line, section.m_name));
+ warnings += strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized.") + Untranslated("\n"), section.m_file, section.m_line, section.m_name);
+ }
+
+ if (!warnings.empty()) {
+ InitWarning(warnings);
}
if (!fs::is_directory(GetBlocksDir())) {
- return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist.").translated, gArgs.GetArg("-blocksdir", "")));
+ return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", "")));
}
// parse and validate enabled filter types
@@ -951,25 +989,32 @@ bool AppInitParameterInteraction()
for (const auto& name : names) {
BlockFilterType filter_type;
if (!BlockFilterTypeByName(name, filter_type)) {
- return InitError(strprintf(_("Unknown -blockfilterindex value %s.").translated, name));
+ return InitError(strprintf(_("Unknown -blockfilterindex value %s."), name));
}
g_enabled_filter_types.insert(filter_type);
}
}
+ // Basic filters are the only supported filters. The basic filters index must be enabled
+ // to serve compact filters
+ if (gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS) &&
+ g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
+ return InitError(_("Cannot set -peerblockfilters without -blockfilterindex."));
+ }
+
// if using block pruning, then disallow txindex
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
- return InitError(_("Prune mode is incompatible with -txindex.").translated);
+ return InitError(_("Prune mode is incompatible with -txindex."));
if (!g_enabled_filter_types.empty()) {
- return InitError(_("Prune mode is incompatible with -blockfilterindex.").translated);
+ return InitError(_("Prune mode is incompatible with -blockfilterindex."));
}
}
// -bind and -whitebind can't be set when not listening
size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size();
if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
- return InitError("Cannot set -bind or -whitebind together with -listen=0");
+ return InitError(Untranslated("Cannot set -bind or -whitebind together with -listen=0"));
}
// Make sure enough file descriptors are available
@@ -987,11 +1032,11 @@ bool AppInitParameterInteraction()
#endif
nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
- return InitError(_("Not enough file descriptors available.").translated);
+ return InitError(_("Not enough file descriptors available."));
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections);
if (nMaxConnections < nUserMaxConnections)
- InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations.").translated, nUserMaxConnections, nMaxConnections));
+ InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
if (gArgs.IsArgSet("-debug")) {
@@ -1002,7 +1047,7 @@ bool AppInitParameterInteraction()
[](std::string cat){return cat == "0" || cat == "none";})) {
for (const auto& cat : categories) {
if (!LogInstance().EnableCategory(cat)) {
- InitWarning(strprintf(_("Unsupported logging category %s=%s.").translated, "-debug", cat));
+ InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));
}
}
}
@@ -1011,7 +1056,7 @@ bool AppInitParameterInteraction()
// Now remove the logging categories which were explicitly excluded
for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {
if (!LogInstance().DisableCategory(cat)) {
- InitWarning(strprintf(_("Unsupported logging category %s=%s.").translated, "-debugexclude", cat));
+ InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));
}
}
@@ -1032,7 +1077,7 @@ bool AppInitParameterInteraction()
if (gArgs.IsArgSet("-minimumchainwork")) {
const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", "");
if (!IsHexNumber(minChainWorkStr)) {
- return InitError(strprintf("Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr));
+ return InitError(strprintf(Untranslated("Invalid non-hex (%s) minimum chain work value specified"), minChainWorkStr));
}
nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
} else {
@@ -1047,21 +1092,21 @@ bool AppInitParameterInteraction()
int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
- return InitError(strprintf(_("-maxmempool must be at least %d MB").translated, std::ceil(nMempoolSizeMin / 1000000.0)));
+ return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
// and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
if (gArgs.IsArgSet("-incrementalrelayfee"))
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n))
- return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")).translated);
+ return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")));
incrementalRelayFee = CFeeRate(n);
}
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
int64_t nPruneArg = gArgs.GetArg("-prune", 0);
if (nPruneArg < 0) {
- return InitError(_("Prune cannot be configured with a negative value.").translated);
+ return InitError(_("Prune cannot be configured with a negative value."));
}
nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024;
if (nPruneArg == 1) { // manual pruning: -prune=1
@@ -1070,7 +1115,7 @@ bool AppInitParameterInteraction()
fPruneMode = true;
} else if (nPruneTarget) {
if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
- return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number.").translated, MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
+ return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
}
LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024);
fPruneMode = true;
@@ -1083,13 +1128,13 @@ bool AppInitParameterInteraction()
peer_connect_timeout = gArgs.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
- return InitError("peertimeout cannot be configured with a negative value.");
+ return InitError(Untranslated("peertimeout cannot be configured with a negative value."));
}
if (gArgs.IsArgSet("-minrelaytxfee")) {
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
- return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")).translated);
+ return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")));
}
// High fee check is done afterward in CWallet::CreateWalletFromFile()
::minRelayTxFee = CFeeRate(n);
@@ -1105,7 +1150,7 @@ bool AppInitParameterInteraction()
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n))
- return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")).translated);
+ return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")));
}
// Feerate used to define dust. Shouldn't be changed lightly as old
@@ -1114,13 +1159,13 @@ bool AppInitParameterInteraction()
{
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n))
- return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")).translated);
+ return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}
fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (!chainparams.IsTestChain() && !fRequireStandard) {
- return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
+ return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString()));
}
nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
@@ -1137,10 +1182,10 @@ bool AppInitParameterInteraction()
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0)
- return InitError("rpcserialversion must be non-negative.");
+ return InitError(Untranslated("rpcserialversion must be non-negative."));
if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
- return InitError("unknown rpcserialversion requested.");
+ return InitError(Untranslated("Unknown rpcserialversion requested."));
nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
@@ -1152,10 +1197,10 @@ static bool LockDataDirectory(bool probeOnly)
// Make sure only a single Bitcoin process is using the data directory.
fs::path datadir = GetDataDir();
if (!DirIsWritable(datadir)) {
- return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions.").translated, datadir.string()));
+ return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), datadir.string()));
}
if (!LockDirectory(datadir, ".lock", probeOnly)) {
- return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.").translated, datadir.string(), PACKAGE_NAME));
+ return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), datadir.string(), PACKAGE_NAME));
}
return true;
}
@@ -1173,7 +1218,7 @@ bool AppInitSanityChecks()
// Sanity check
if (!InitSanityCheck())
- return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down.").translated, PACKAGE_NAME));
+ return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME));
// Probe the data directory lock to give an early error message, if possible
// We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened,
@@ -1193,7 +1238,7 @@ bool AppInitLockDataDirectory()
return true;
}
-bool AppInitMain(NodeContext& node)
+bool AppInitMain(const util::Ref& context, NodeContext& node)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization
@@ -1209,7 +1254,7 @@ bool AppInitMain(NodeContext& node)
}
}
if (!LogInstance().StartLogging()) {
- return InitError(strprintf("Could not open debug log file %s",
+ return InitError(strprintf(Untranslated("Could not open debug log file %s"),
LogInstance().m_file_path.string()));
}
@@ -1224,7 +1269,7 @@ bool AppInitMain(NodeContext& node)
LogPrintf("Config file: %s\n", config_file_path.string());
} else if (gArgs.IsArgSet("-conf")) {
// Warn if no conf file exists at path provided by user
- InitWarning(strprintf(_("The specified config file %s does not exist\n").translated, config_file_path.string()));
+ InitWarning(strprintf(_("The specified config file %s does not exist\n"), config_file_path.string()));
} else {
// Not categorizing as "Warning" because it's the default behavior
LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string());
@@ -1278,7 +1323,7 @@ bool AppInitMain(NodeContext& node)
// Gather some entropy once per minute.
node.scheduler->scheduleEvery([]{
RandAddPeriodic();
- }, 60000);
+ }, std::chrono::minutes{1});
GetMainSignals().RegisterBackgroundSignalScheduler(*node.scheduler);
@@ -1295,7 +1340,6 @@ bool AppInitMain(NodeContext& node)
for (const auto& client : node.chain_clients) {
client->registerRpcs();
}
- g_rpc_node = &node;
#if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC);
#endif
@@ -1308,8 +1352,8 @@ bool AppInitMain(NodeContext& node)
if (gArgs.GetBoolArg("-server", false))
{
uiInterface.InitMessage_connect(SetRPCWarmupStatus);
- if (!AppInitServers())
- return InitError(_("Unable to start HTTP server. See debug log for details.").translated);
+ if (!AppInitServers(context))
+ return InitError(_("Unable to start HTTP server. See debug log for details."));
}
// ********************************************************* Step 5: verify wallet database integrity
@@ -1329,20 +1373,27 @@ 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;
+ assert(!node.chainman);
+ node.chainman = &g_chainman;
+ ChainstateManager& chainman = EnsureChainman(node);
- node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler));
+ node.peer_logic.reset(new PeerLogicValidation(node.connman.get(), node.banman.get(), *node.scheduler, *node.chainman, *node.mempool));
RegisterValidationInterface(node.peer_logic.get());
// sanitize comments per BIP-0014, format user agent and check total size
std::vector<std::string> uacomments;
for (const std::string& cmt : gArgs.GetArgs("-uacomment")) {
if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT))
- return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters.").translated, cmt));
+ return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt));
uacomments.push_back(cmt);
}
strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments);
if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) {
- return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.").translated,
+ return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."),
strSubVersion.size(), MAX_SUBVERSION_LENGTH));
}
@@ -1351,7 +1402,7 @@ bool AppInitMain(NodeContext& node)
for (const std::string& snet : gArgs.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
- return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'").translated, snet));
+ return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet));
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
@@ -1372,12 +1423,12 @@ bool AppInitMain(NodeContext& node)
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) {
- return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'").translated, proxyArg));
+ return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}
proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
if (!addrProxy.IsValid())
- return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'").translated, proxyArg));
+ return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
@@ -1396,11 +1447,11 @@ bool AppInitMain(NodeContext& node)
} else {
CService onionProxy;
if (!Lookup(onionArg, onionProxy, 9050, fNameLookup)) {
- return InitError(strprintf(_("Invalid -onion address or hostname: '%s'").translated, onionArg));
+ return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
}
proxyType addrOnion = proxyType(onionProxy, proxyRandomize);
if (!addrOnion.IsValid())
- return InitError(strprintf(_("Invalid -onion address or hostname: '%s'").translated, onionArg));
+ return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
SetProxy(NET_ONION, addrOnion);
SetReachable(NET_ONION, true);
}
@@ -1429,12 +1480,12 @@ bool AppInitMain(NodeContext& node)
asmap_path = GetDataDir() / asmap_path;
}
if (!fs::exists(asmap_path)) {
- InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path));
+ InitError(strprintf(_("Could not find asmap file %s"), 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));
+ InitError(strprintf(_("Could not parse asmap file %s"), asmap_path));
return false;
}
const uint256 asmap_version = SerializeHash(asmap);
@@ -1498,17 +1549,18 @@ bool AppInitMain(NodeContext& node)
bool fLoaded = false;
while (!fLoaded && !ShutdownRequested()) {
bool fReset = fReindex;
- std::string strLoadError;
+ 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();
- bool is_coinsview_empty;
try {
LOCK(cs_main);
- // This statement makes ::ChainstateActive() usable.
- g_chainstate = MakeUnique<CChainState>();
+ chainman.InitializeChainstate();
UnloadBlockIndex();
// new CBlockTreeDB tries to delete the existing file, which
@@ -1529,9 +1581,9 @@ bool AppInitMain(NodeContext& node)
// 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 (!LoadBlockIndex(chainparams)) {
+ if (!chainman.LoadBlockIndex(chainparams)) {
if (ShutdownRequested()) break;
- strLoadError = _("Error loading block database").translated;
+ strLoadError = _("Error loading block database");
break;
}
@@ -1539,13 +1591,13 @@ bool AppInitMain(NodeContext& node)
// (we're likely using a testnet datadir, or the other way around).
if (!::BlockIndex().empty() &&
!LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) {
- return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?").translated);
+ 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").translated;
+ strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
break;
}
@@ -1554,108 +1606,145 @@ bool AppInitMain(NodeContext& node)
// (otherwise we use the one already on disk).
// This is called again in ThreadImport after the reindex completes.
if (!fReindex && !LoadGenesisBlock(chainparams)) {
- strLoadError = _("Error initializing block database").translated;
+ strLoadError = _("Error initializing block database");
break;
}
// 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 : 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."),
+ "", 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;
+ }
- 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.");
+ 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");
+ 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());
- strLoadError = _("Error opening block database").translated;
+ strLoadError = _("Error opening block database");
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 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");
+ 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 : 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(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;
+ }
+
+ // 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");
+ failed_verification = true;
+ break;
+ }
}
}
} catch (const std::exception& e) {
LogPrintf("%s\n", e.what());
- strLoadError = _("Error opening block database").translated;
+ strLoadError = _("Error opening block database");
+ 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()) {
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeQuestion(
- strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?").translated,
- strLoadError + ".\nPlease restart with -reindex or -reindex-chainstate to recover.",
+ strLoadError + Untranslated(".\n\n") + _("Do you want to rebuild the block database now?"),
+ strLoadError.original + ".\nPlease restart with -reindex or -reindex-chainstate to recover.",
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
@@ -1678,11 +1767,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.
@@ -1716,8 +1800,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 : chainman.GetAll()) {
+ uiInterface.InitMessage(_("Pruning blockstore...").translated);
+ chainstate->PruneAndFlush();
+ }
}
}
@@ -1730,11 +1817,11 @@ bool AppInitMain(NodeContext& node)
// ********************************************************* Step 11: import blocks
if (!CheckDiskSpace(GetDataDir())) {
- InitError(strprintf(_("Error: Disk space is low for %s").translated, GetDataDir()));
+ InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir()));
return false;
}
if (!CheckDiskSpace(GetBlocksDir())) {
- InitError(strprintf(_("Error: Disk space is low for %s").translated, GetBlocksDir()));
+ InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir()));
return false;
}
@@ -1742,7 +1829,7 @@ bool AppInitMain(NodeContext& node)
// No locking, as this happens before any background thread is started.
boost::signals2::connection block_notify_genesis_wait_connection;
if (::ChainActive().Tip() == nullptr) {
- block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(BlockNotifyGenesisWait);
+ block_notify_genesis_wait_connection = uiInterface.NotifyBlockTip_connect(std::bind(BlockNotifyGenesisWait, std::placeholders::_2));
} else {
fHaveGenesis = true;
}
@@ -1757,7 +1844,7 @@ bool AppInitMain(NodeContext& node)
vImportFiles.push_back(strFile);
}
- threadGroup.create_thread(std::bind(&ThreadImport, vImportFiles));
+ g_load_block = std::thread(&TraceThread<std::function<void()>>, "loadblk", [=, &chainman]{ ThreadImport(chainman, vImportFiles); });
// Wait for genesis block to be processed
{
@@ -1803,7 +1890,7 @@ bool AppInitMain(NodeContext& node)
connOptions.m_max_outbound_full_relay = std::min(MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, connOptions.nMaxConnections);
connOptions.m_max_outbound_block_relay = std::min(MAX_BLOCKS_ONLY_CONNECTIONS, connOptions.nMaxConnections-connOptions.m_max_outbound_full_relay);
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
- connOptions.nMaxFeeler = 1;
+ connOptions.nMaxFeeler = MAX_FEELER_CONNECTIONS;
connOptions.nBestHeight = chain_active_height;
connOptions.uiInterface = &uiInterface;
connOptions.m_banman = node.banman.get();
@@ -1825,14 +1912,14 @@ bool AppInitMain(NodeContext& node)
}
for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
- std::string error;
+ bilingual_str error;
if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
connOptions.vWhiteBinds.push_back(whitebind);
}
for (const auto& net : gArgs.GetArgs("-whitelist")) {
NetWhitelistPermissions subnet;
- std::string error;
+ bilingual_str error;
if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}
@@ -1863,7 +1950,7 @@ bool AppInitMain(NodeContext& node)
BanMan* banman = node.banman.get();
node.scheduler->scheduleEvery([banman]{
banman->DumpBanlist();
- }, DUMP_BANS_INTERVAL * 1000);
+ }, DUMP_BANS_INTERVAL);
return true;
}