diff --git a/src/init.cpp b/src/init.cpp index 3fafdad4c8..e552610bc9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,2464 +1,2465 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include <config/bitcoin-config.h> #endif #include <init.h> #include <addrman.h> #include <amount.h> #include <chain.h> #include <chainparams.h> #include <checkpoints.h> #include <compat/sanity.h> #include <config.h> #include <consensus/validation.h> #include <diskblockpos.h> #include <fs.h> #include <httprpc.h> #include <httpserver.h> #include <index/txindex.h> #include <key.h> #include <miner.h> #include <net.h> #include <net_processing.h> #include <netbase.h> #include <policy/policy.h> #include <rpc/blockchain.h> #include <rpc/register.h> #include <rpc/server.h> #include <scheduler.h> #include <script/scriptcache.h> #include <script/sigcache.h> #include <script/standard.h> #include <timedata.h> #include <torcontrol.h> #include <txdb.h> #include <txmempool.h> #include <ui_interface.h> #include <util.h> #include <utilmoneystr.h> #include <validation.h> #include <validationinterface.h> #include <walletinitinterface.h> #include <warnings.h> #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/bind.hpp> #include <boost/interprocess/sync/file_lock.hpp> #include <boost/thread.hpp> #if ENABLE_ZMQ #include <zmq/zmqnotificationinterface.h> #endif #ifndef WIN32 #include <csignal> #endif #include <cstdint> #include <cstdio> #include <memory> static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; std::unique_ptr<CConnman> g_connman; std::unique_ptr<PeerLogicValidation> peerLogic; #if !(ENABLE_WALLET) class DummyWalletInit : public WalletInitInterface { public: void AddWalletOptions() const override {} bool ParameterInteraction() const override { return true; } void RegisterRPC(CRPCTable &) const override {} bool Verify(const CChainParams &chainParams) const override { return true; } bool Open(const CChainParams &chainParams) const override { LogPrintf("No wallet support compiled in!\n"); return true; } void Start(CScheduler &scheduler) const override {} void Flush() const override {} void Stop() const override {} void Close() const override {} }; const WalletInitInterface &g_wallet_init_interface = DummyWalletInit(); #endif #if ENABLE_ZMQ static CZMQNotificationInterface *pzmqNotificationInterface = nullptr; #endif #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 anyway. #define MIN_CORE_FILEDESCRIPTORS 0 #else #define MIN_CORE_FILEDESCRIPTORS 150 #endif ////////////////////////////////////////////////////////////////////////////// // // Shutdown // // // Thread management and startup/shutdown: // // The network-processing threads are all part of a thread group created by // AppInit() or the Qt main() function. // // A clean exit happens when StartShutdown() or the SIGTERM signal handler sets // fRequestShutdown, which triggers the DetectShutdownThread(), which interrupts // the main thread group. DetectShutdownThread() then exits, which causes // AppInit() to continue (it .joins the shutdown thread). Shutdown() is then // called to clean up database connections, and stop other threads that should // only be stopped after the main network-processing threads have exited. // // Shutdown for Qt is very similar, only it uses a QTimer to detect // fRequestShutdown getting set, and then does the normal Qt shutdown thing. // std::atomic<bool> fRequestShutdown(false); void StartShutdown() { fRequestShutdown = true; } bool ShutdownRequested() { return fRequestShutdown; } /** * This is a minimally invasive approach to shutdown on LevelDB read errors from * the chainstate, while keeping user interface out of the common library, which * is shared between bitcoind, and bitcoin-qt and non-server tools. */ class CCoinsViewErrorCatcher final : public CCoinsViewBacked { public: explicit CCoinsViewErrorCatcher(CCoinsView *view) : CCoinsViewBacked(view) {} bool GetCoin(const COutPoint &outpoint, Coin &coin) const override { try { return CCoinsViewBacked::GetCoin(outpoint, coin); } catch (const std::runtime_error &e) { uiInterface.ThreadSafeMessageBox( _("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR); LogPrintf("Error reading from database: %s\n", e.what()); // Starting the shutdown sequence and returning false to the caller // would be interpreted as 'entry not found' (as opposed to unable // to read data), and could lead to invalid interpretation. Just // exit immediately, as we can't continue anyway, and all writes // should be atomic. abort(); } } // Writes do not need similar protection, as failure to write is handled by // the caller. }; static std::unique_ptr<CCoinsViewErrorCatcher> pcoinscatcher; static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle; static boost::thread_group threadGroup; static CScheduler scheduler; void Interrupt() { InterruptHTTPServer(); InterruptHTTPRPC(); InterruptRPC(); InterruptREST(); InterruptTorControl(); InterruptMapPort(); if (g_connman) { g_connman->Interrupt(); } if (g_txindex) { g_txindex->Interrupt(); } } void Shutdown() { LogPrintf("%s: In progress...\n", __func__); static CCriticalSection 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. Be sure that anything that writes files or flushes caches /// only does this if the respective module was initialized. RenameThread("bitcoin-shutoff"); g_mempool.AddTransactionsUpdated(1); StopHTTPRPC(); StopREST(); StopRPC(); StopHTTPServer(); g_wallet_init_interface.Flush(); StopMapPort(); // Because these depend on each-other, we make sure that neither can be // using the other before destroying them. if (peerLogic) { UnregisterValidationInterface(peerLogic.get()); } if (g_connman) { g_connman->Stop(); } if (g_txindex) { g_txindex->Stop(); } StopTorControl(); // After everything has been shut down, but before things get flushed, stop // the CScheduler/checkqueue threadGroup threadGroup.interrupt_all(); threadGroup.join_all(); // After the threads that potentially access these pointers have been // stopped, destruct and reset all to nullptr. peerLogic.reset(); g_connman.reset(); g_txindex.reset(); if (g_is_mempool_loaded && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { DumpMempool(); } // FlushStateToDisk generates a ChainStateFlushed callback, which we should // avoid missing if (pcoinsTip != nullptr) { FlushStateToDisk(); } // After there are no more peers/RPC left to give us new data which may // generate CValidationInterface callbacks, flush them... GetMainSignals().FlushBackgroundCallbacks(); // Any future callbacks will be dropped. This should absolutely be safe - if // missing a callback results in an unrecoverable situation, unclean // shutdown would too. The only reason to do the above flushes is to let the // wallet catch up with our current chain to avoid any strange pruning edge // cases and make next startup faster by avoiding rescan. { LOCK(cs_main); if (pcoinsTip != nullptr) { FlushStateToDisk(); } pcoinsTip.reset(); pcoinscatcher.reset(); pcoinsdbview.reset(); pblocktree.reset(); } g_wallet_init_interface.Stop(); #if ENABLE_ZMQ if (pzmqNotificationInterface) { UnregisterValidationInterface(pzmqNotificationInterface); delete pzmqNotificationInterface; pzmqNotificationInterface = nullptr; } #endif #ifndef WIN32 try { fs::remove(GetPidFile()); } catch (const fs::filesystem_error &e) { LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what()); } #endif UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); GetMainSignals().UnregisterWithMempoolSignals(g_mempool); g_wallet_init_interface.Close(); globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); } /** * Signal handlers are very limited in what they are allowed to do. * The execution context the handler is invoked in is not guaranteed, * so we restrict handler operations to just touching variables: */ static void HandleSIGTERM(int) { fRequestShutdown = true; } static void HandleSIGHUP(int) { GetLogger().m_reopen_file = true; } #ifndef WIN32 static void registerSignalHandler(int signal, void (*handler)(int)) { struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(signal, &sa, NULL); } #endif -void OnRPCStarted() { +static void OnRPCStarted() { uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange); } -void OnRPCStopped() { +static void OnRPCStopped() { uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange); RPCNotifyBlockChange(false, nullptr); g_best_block_cv.notify_all(); LogPrint(BCLog::RPC, "RPC stopped.\n"); } void SetupServerArgs() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); // Set all of the args and their help // When adding new options to the categories, please keep and ensure // alphabetical ordering. Do not translate _(...) -help-debug options, Many // technical terms, and only a very small audience, so is unnecessary stress // to translators. gArgs.AddArg("-?", _("Print this help message and exit"), false, OptionsCategory::OPTIONS); gArgs.AddArg("-version", _("Print version and exit"), false, OptionsCategory::OPTIONS); gArgs.AddArg("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we " "see a really long fork (%s in cmd is replaced by message)"), false, OptionsCategory::OPTIONS); gArgs.AddArg( "-assumevalid=<hex>", strprintf( _("If this block is in the chain assume that it and its ancestors " "are valid and potentially skip their script verification (0 to " "verify all, default: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), false, OptionsCategory::OPTIONS); gArgs.AddArg("-blocksdir=<dir>", _("Specify directory to hold blocks subdirectory for *.dat " "files (default: <datadir>)"), false, OptionsCategory::OPTIONS); gArgs.AddArg("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is " "replaced by block hash)"), false, OptionsCategory::OPTIONS); gArgs.AddArg( "-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block " "reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), false, OptionsCategory::OPTIONS); gArgs.AddArg( "-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %d)"), DEFAULT_BLOCKSONLY), true, OptionsCategory::OPTIONS); gArgs.AddArg("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS); gArgs.AddArg("-datadir=<dir>", _("Specify data directory"), false, OptionsCategory::OPTIONS); gArgs.AddArg( "-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS); gArgs.AddArg( "-dbcache=<n>", strprintf( _("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS); gArgs.AddArg( "-debuglogfile=<file>", strprintf( _("Specify location of debug log file: this can be an absolute " "path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS); gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by " "our mempool min fee (default: %d)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS); gArgs.AddArg("-finalizationdelay=<n>", strprintf("Set the minimum amount of time to wait between a " "block header reception and the block finalization. " "Unit is seconds (default: %d)", DEFAULT_MIN_FINALIZATION_DELAY), false, OptionsCategory::OPTIONS); gArgs.AddArg( "-includeconf=<file>", _("Specify additional configuration file, relative to the -datadir " "path (only useable from configuration file, not command line)"), false, OptionsCategory::OPTIONS); gArgs.AddArg( "-maxreorgdepth=<n>", strprintf("Configure at what depth blocks are considered final " "(default: %d). Use -1 to disable.", DEFAULT_MAX_REORG_DEPTH), false, OptionsCategory::OPTIONS); gArgs.AddArg("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"), false, OptionsCategory::OPTIONS); gArgs.AddArg("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool " "below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE), false, OptionsCategory::OPTIONS); gArgs.AddArg("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable " "transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS), false, OptionsCategory::OPTIONS); gArgs.AddArg("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool " "longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY), false, OptionsCategory::OPTIONS); gArgs.AddArg( "-minimumchainwork=<hex>", strprintf( "Minimum work assumed to exist on a valid chain in hex " "(default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), true, OptionsCategory::OPTIONS); gArgs.AddArg( "-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, " "0 = auto, <0 = leave that many cores free, default: %d)"), -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), false, OptionsCategory::OPTIONS); gArgs.AddArg("-persistmempool", strprintf(_("Whether to save the mempool on shutdown " "and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL), false, OptionsCategory::OPTIONS); #ifndef WIN32 gArgs.AddArg( "-pid=<file>", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME), false, OptionsCategory::OPTIONS); #endif gArgs.AddArg( "-prune=<n>", strprintf( _("Reduce storage requirements by enabling pruning (deleting) of " "old blocks. This allows the pruneblockchain RPC to be called to " "delete specific blocks, and enables automatic pruning of old " "blocks if a target size in MiB is provided. This mode is " "incompatible with -txindex and -rescan. " "Warning: Reverting this setting requires re-downloading the " "entire blockchain. " "(default: 0 = disable pruning blocks, 1 = allow manual pruning " "via RPC, >%u = automatically prune block files to stay under " "the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), false, OptionsCategory::OPTIONS); gArgs.AddArg("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks"), false, OptionsCategory::OPTIONS); gArgs.AddArg("-reindex", _("Rebuild chain state and block index from the blk*.dat " "files on disk"), false, OptionsCategory::OPTIONS); #ifndef WIN32 gArgs.AddArg( "-sysperms", _("Create new files with system default permissions, instead of umask " "077 (only effective with disabled wallet functionality)"), false, OptionsCategory::OPTIONS); #endif gArgs.AddArg("-txindex", strprintf(_("Maintain a full transaction index, used by the " "getrawtransaction rpc call (default: %d)"), DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS); gArgs.AddArg("-usecashaddr", _("Use Cash Address for destination encoding instead " "of base58 (activate by default on Jan, 14)"), false, 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)"), false, OptionsCategory::CONNECTION); gArgs.AddArg( "-banscore=<n>", strprintf( _("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION); gArgs.AddArg("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from " "reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION); gArgs.AddArg("-bind=<addr>", _("Bind to given address and always listen on it. Use " "[host]:port notation for IPv6"), false, OptionsCategory::CONNECTION); gArgs.AddArg("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables " "automatic connections (the rules for this peer are the " "same as for -addnode)"), false, OptionsCategory::CONNECTION); gArgs.AddArg("-discover", _("Discover own IP addresses (default: 1 when listening and " "no -externalip or -proxy)"), false, OptionsCategory::CONNECTION); gArgs.AddArg("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %d)"), DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION); gArgs.AddArg("-dnsseed", _("Query for peer addresses via DNS lookup, if low on " "addresses (default: 1 unless -connect/-noconnect)"), false, OptionsCategory::CONNECTION); gArgs.AddArg("-externalip=<ip>", _("Specify your own public address"), false, OptionsCategory::CONNECTION); gArgs.AddArg( "-forcednsseed", strprintf( _("Always query for peer addresses via DNS lookup (default: %d)"), DEFAULT_FORCEDNSSEED), false, OptionsCategory::CONNECTION); gArgs.AddArg("-listen", _("Accept connections from outside (default: 1 if no -proxy " "or -connect/-noconnect)"), false, OptionsCategory::CONNECTION); gArgs.AddArg( "-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION), false, OptionsCategory::CONNECTION); gArgs.AddArg( "-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS), false, OptionsCategory::CONNECTION); gArgs.AddArg("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 " "bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER), false, OptionsCategory::CONNECTION); gArgs.AddArg("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 " "bytes (default: %u)"), DEFAULT_MAXSENDBUFFER), false, 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), false, OptionsCategory::CONNECTION); gArgs.AddArg("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor " "hidden services (default: %s)"), "-proxy"), false, OptionsCategory::CONNECTION); gArgs.AddArg( "-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"), false, OptionsCategory::CONNECTION); gArgs.AddArg("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with " "bloom filters (default: %d)"), DEFAULT_PEERBLOOMFILTERS), false, OptionsCategory::CONNECTION); gArgs.AddArg("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %d)"), DEFAULT_PERMIT_BAREMULTISIG), false, OptionsCategory::CONNECTION); gArgs.AddArg( "-port=<port>", strprintf( _("Listen for connections on <port> (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION); gArgs.AddArg("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"), false, OptionsCategory::CONNECTION); gArgs.AddArg( "-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This " "enables Tor stream isolation (default: %d)"), DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION); gArgs.AddArg( "-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"), false, OptionsCategory::CONNECTION); gArgs.AddArg("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds " "(minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION); gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening " "enabled (default: %s)"), DEFAULT_TOR_CONTROL), false, OptionsCategory::CONNECTION); gArgs.AddArg("-torpassword=<pass>", _("Tor control port password (default: empty)"), false, 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)"), false, OptionsCategory::CONNECTION); #else gArgs.AddArg( "-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0), false, OptionsCategory::CONNECTION); #endif #endif gArgs.AddArg("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to " "it. Use [host]:port notation for IPv6"), false, OptionsCategory::CONNECTION); gArgs.AddArg("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. " "1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be " "specified multiple times.") + " " + _("Whitelisted peers cannot be DoS banned and their " "transactions are always relayed, even if they are " "already in the mempool, useful e.g. for a gateway"), false, 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), false, OptionsCategory::CONNECTION); g_wallet_init_interface.AddWalletOptions(); #if ENABLE_ZMQ gArgs.AddArg("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>"), false, OptionsCategory::ZMQ); gArgs.AddArg("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>"), false, OptionsCategory::ZMQ); gArgs.AddArg("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>"), false, OptionsCategory::ZMQ); gArgs.AddArg("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"), false, OptionsCategory::ZMQ); #endif gArgs.AddArg( "-checkblocks=<n>", strprintf( _("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-checklevel=<n>", strprintf(_("How thorough the block verification of " "-checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, " "setBlockIndexCandidates, chainActive and mapBlocksUnlinked " "occasionally. Also sets -checkmempool (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-checkpoints", strprintf("Only accept block chain matching built-in " "checkpoints (default: %d)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %d)", DEFAULT_STOPAFTERBLOCKIMPORT), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-stopatheight", strprintf("Stop running after reaching the given height in " "the main chain (default: %u)", DEFAULT_STOPATHEIGHT), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool " "ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool " "ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> " "or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), true, OptionsCategory::DEBUG_TEST); 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), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-debug=<category>", strprintf(_("Output debugging information (default: %u, " "supplying <category> is optional)"), 0) + ". " + _("If <category> is not supplied or if <category> = 1, " "output all debugging information.") + _("<category> can be:") + " " + ListLogCategories() + ".", false, 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.")), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-help-debug", _("Show all debugging options (usage: --help -help-debug)"), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-logips", strprintf(_("Include IP addresses in debug output (default: %d)"), DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %d)"), DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %d)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-maxsigcachesize=<n>", strprintf("Limit size of signature cache to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-maxscriptcachesize=<n>", strprintf("Limit size of script cache to <n> MiB (default: %u)", DEFAULT_MAX_SCRIPT_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in " "initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg( "-printtoconsole", _("Send trace/debug info to console instead of debug.log file"), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-printpriority", strprintf("Log transaction priority and fee per kB when " "mining blocks (default: %d)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when " "no -debug)"), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-uacomment=<cmt>", _("Append comment to the user agent string"), false, OptionsCategory::DEBUG_TEST); SetupChainParamsBaseOptions(); gArgs.AddArg( "-acceptnonstdtxn", strprintf( "Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", defaultChainParams->RequireStandard()), true, OptionsCategory::NODE_RELAY); gArgs.AddArg("-excessiveblocksize=<n>", strprintf(_("Do not accept blocks larger than this limit, in " "bytes (default: %d)"), DEFAULT_MAX_BLOCK_SIZE), true, OptionsCategory::NODE_RELAY); gArgs.AddArg( "-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an " "output such that it will cost about 1/3 of its value in " "fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY); gArgs.AddArg("-limitfreerelay=<n>", strprintf("Continuously rate-limit free transactions to " "<n>*1000 bytes per minute (default: %u)", DEFAULT_LIMITFREERELAY), true, OptionsCategory::NODE_RELAY); gArgs.AddArg("-relaypriority", strprintf("Require high priority for relaying free or low-fee " "transactions (default: %d)", DEFAULT_RELAYPRIORITY), true, OptionsCategory::NODE_RELAY); gArgs.AddArg("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for " "relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP), false, OptionsCategory::NODE_RELAY); gArgs.AddArg( "-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %d)"), DEFAULT_ACCEPT_DATACARRIER), false, OptionsCategory::NODE_RELAY); gArgs.AddArg("-datacarriersize", strprintf(_("Maximum size of data in data carrier " "transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY), false, 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_PER_KB)), false, OptionsCategory::NODE_RELAY); gArgs.AddArg( "-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted " "peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY), false, OptionsCategory::NODE_RELAY); gArgs.AddArg( "-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even " "if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY); // Not sure this really belongs here, but it will do for now. // FIXME: This doesn't work anyways. gArgs.AddArg("-excessutxocharge=<amt>", strprintf(_("Fees (in %s/kB) to charge per utxo created for " "relaying, and mining (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_UTXO_FEE)), true, OptionsCategory::NODE_RELAY); gArgs.AddArg("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_MAX_GENERATED_BLOCK_SIZE), false, OptionsCategory::BLOCK_CREATION); gArgs.AddArg( "-blockprioritypercentage=<n>", strprintf(_("Set maximum percentage of a block reserved to " "high-priority/low-fee transactions (default: %d)"), DEFAULT_BLOCK_PRIORITY_PERCENTAGE), false, OptionsCategory::BLOCK_CREATION); gArgs.AddArg( "-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be " "included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB)), false, OptionsCategory::BLOCK_CREATION); gArgs.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", true, OptionsCategory::BLOCK_CREATION); gArgs.AddArg("-server", _("Accept command line and JSON-RPC commands"), false, OptionsCategory::RPC); gArgs.AddArg("-rest", strprintf(_("Accept public REST requests (default: %d)"), DEFAULT_REST_ENABLE), false, OptionsCategory::RPC); gArgs.AddArg( "-rpcbind=<addr>", _("Bind to given address to listen for JSON-RPC connections. Use " "[host]:port notation for IPv6. This option can be specified " "multiple times (default: bind to all interfaces)"), false, OptionsCategory::RPC); gArgs.AddArg("-rpccookiefile=<loc>", _("Location of the auth cookie (default: data dir)"), false, OptionsCategory::RPC); gArgs.AddArg("-rpcuser=<user>", _("Username for JSON-RPC connections"), false, OptionsCategory::RPC); gArgs.AddArg("-rpcpassword=<pw>", _("Password for JSON-RPC connections"), false, OptionsCategory::RPC); gArgs.AddArg( "-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field " "<userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical " "python script is included in share/rpcuser. The client then " "connects normally using the " "rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This " "option can be specified multiple times"), false, OptionsCategory::RPC); gArgs.AddArg( "-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or " "testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::RPC); gArgs.AddArg("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid " "for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask " "(e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. " "1.2.3.4/24). This option can be specified multiple times"), false, OptionsCategory::RPC); gArgs.AddArg( "-rpcthreads=<n>", strprintf( _("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS), false, OptionsCategory::RPC); gArgs.AddArg( "-rpccorsdomain=value", "Domain from which to accept cross origin requests (browser enforced)", false, OptionsCategory::RPC); gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC " "calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), true, OptionsCategory::RPC); gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), true, OptionsCategory::RPC); } std::string LicenseInfo() { const std::string URL_SOURCE_CODE = "<https://github.com/Bitcoin-ABC/bitcoin-abc>"; const std::string URL_WEBSITE = "<https://www.bitcoinabc.org>"; return CopyrightHolders( strprintf(_("Copyright (C) %i-%i"), 2009, COPYRIGHT_YEAR) + " ") + "\n" + "\n" + strprintf(_("Please contribute if you find %s useful. " "Visit %s for further information about the software."), PACKAGE_NAME, URL_WEBSITE) + "\n" + strprintf(_("The source code is available from %s."), URL_SOURCE_CODE) + "\n" + "\n" + _("This is experimental software.") + "\n" + strprintf(_("Distributed under the MIT software license, see the " "accompanying file %s or %s"), "COPYING", "<https://opensource.org/licenses/MIT>") + "\n" + "\n" + strprintf(_("This product includes software developed by the " "OpenSSL Project for use in the OpenSSL Toolkit %s and " "cryptographic software written by Eric Young and UPnP " "software written by Thomas Bernard."), "<https://www.openssl.org>") + "\n"; } static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex) { if (initialSync || !pBlockIndex) { return; } std::string strCmd = gArgs.GetArg("-blocknotify", ""); if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); std::thread t(runCommand, strCmd); // thread runs free t.detach(); } } 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) { if (pBlockIndex != nullptr) { { LOCK(g_genesis_wait_mutex); fHaveGenesis = true; } g_genesis_wait_cv.notify_all(); } } struct CImportingNow { CImportingNow() { assert(fImporting == false); fImporting = true; } ~CImportingNow() { assert(fImporting == true); fImporting = false; } }; // If we're using -prune with -reindex, then delete block files that will be // ignored by the reindex. Since reindexing works by starting at block file 0 // and looping until a blockfile is missing, do the same here to delete any // later block files after a gap. Also delete all rev files since they'll be // rewritten by the reindex anyway. This ensures that vinfoBlockFile is in sync // with what's actually on disk by the time we start downloading, so that // pruning works correctly. -void CleanupBlockRevFiles() { +static void CleanupBlockRevFiles() { std::map<std::string, fs::path> mapBlockFiles; // Glob all blk?????.dat and rev?????.dat files from the blocks directory. // Remove the rev files immediately and insert the blk file paths into an // ordered map keyed by block file index. LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for " "-reindex with -prune\n"); fs::path blocksdir = GetBlocksDir(); for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) { if (is_regular_file(*it) && it->path().filename().string().length() == 12 && it->path().filename().string().substr(8, 4) == ".dat") { if (it->path().filename().string().substr(0, 3) == "blk") { mapBlockFiles[it->path().filename().string().substr(3, 5)] = it->path(); } else if (it->path().filename().string().substr(0, 3) == "rev") { remove(it->path()); } } } // Remove all block files that aren't part of a contiguous set starting at // zero by walking the ordered map (keys are block file indices) by keeping // a separate counter. Once we hit a gap (or if 0 doesn't exist) start // removing block files. int nContigCounter = 0; for (const std::pair<std::string, fs::path> &item : mapBlockFiles) { if (atoi(item.first) == nContigCounter) { nContigCounter++; continue; } remove(item.second); } } -void ThreadImport(const Config &config, std::vector<fs::path> vImportFiles) { +static void ThreadImport(const Config &config, + std::vector<fs::path> vImportFiles) { RenameThread("bitcoin-loadblk"); { CImportingNow imp; // -reindex if (fReindex) { int nFile = 0; while (true) { CDiskBlockPos pos(nFile, 0); if (!fs::exists(GetBlockPosFilename(pos, "blk"))) { // No block files left to reindex break; } FILE *file = OpenBlockFile(pos, true); if (!file) { // This error is logged in OpenBlockFile break; } LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); LoadExternalBlockFile(config, file, &pos); nFile++; } pblocktree->WriteReindexing(false); fReindex = false; LogPrintf("Reindexing finished\n"); // To avoid ending up in a situation without genesis block, re-try // initializing (no-op if reindexing worked): LoadGenesisBlock(config.GetChainParams()); } // hardcoded $DATADIR/bootstrap.dat fs::path pathBootstrap = GetDataDir() / "bootstrap.dat"; if (fs::exists(pathBootstrap)) { FILE *file = fsbridge::fopen(pathBootstrap, "rb"); if (file) { fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; LogPrintf("Importing bootstrap.dat...\n"); LoadExternalBlockFile(config, file); RenameOver(pathBootstrap, pathBootstrapOld); } else { LogPrintf("Warning: Could not open bootstrap file %s\n", pathBootstrap.string()); } } // -loadblock= for (const fs::path &path : vImportFiles) { FILE *file = fsbridge::fopen(path, "rb"); if (file) { LogPrintf("Importing blocks file %s...\n", path.string()); LoadExternalBlockFile(config, file); } 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 CValidationState state; if (!ActivateBestChain(config, state)) { LogPrintf("Failed to connect best block"); StartShutdown(); return; } if (gArgs.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) { LogPrintf("Stopping after block import\n"); StartShutdown(); return; } } // End scope of CImportingNow if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { LoadMempool(config); } g_is_mempool_loaded = !fRequestShutdown; } /** Sanity checks * Ensure that Bitcoin is running in a usable environment with all * necessary library support. */ -bool InitSanityCheck(void) { +static bool InitSanityCheck(void) { if (!ECC_InitSanityCheck()) { InitError( "Elliptic curve cryptography sanity check failure. Aborting."); return false; } if (!glibc_sanity_test() || !glibcxx_sanity_test()) { return false; } if (!Random_SanityCheck()) { InitError("OS cryptographic RNG sanity check failure. Aborting."); return false; } return true; } static bool AppInitServers(Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) { RPCServerSignals::OnStarted(&OnRPCStarted); RPCServerSignals::OnStopped(&OnRPCStopped); if (!InitHTTPServer(config)) { return false; } if (!StartRPC()) { return false; } if (!StartHTTPRPC(config, httpRPCRequestProcessor)) { return false; } if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) { return false; } if (!StartHTTPServer()) { return false; } return true; } // Parameter interaction based on rules void InitParameterInteraction() { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified. if (gArgs.IsArgSet("-bind")) { if (gArgs.SoftSetBoolArg("-listen", true)) { LogPrintf( "%s: parameter interaction: -bind set -> setting -listen=1\n", __func__); } } if (gArgs.IsArgSet("-whitebind")) { if (gArgs.SoftSetBoolArg("-listen", true)) { LogPrintf("%s: parameter interaction: -whitebind set -> setting " "-listen=1\n", __func__); } } if (gArgs.IsArgSet("-connect")) { // when only connecting to trusted nodes, do not seed via DNS, or listen // by default. if (gArgs.SoftSetBoolArg("-dnsseed", false)) { LogPrintf("%s: parameter interaction: -connect set -> setting " "-dnsseed=0\n", __func__); } if (gArgs.SoftSetBoolArg("-listen", false)) { LogPrintf("%s: parameter interaction: -connect set -> setting " "-listen=0\n", __func__); } } if (gArgs.IsArgSet("-proxy")) { // to protect privacy, do not listen by default if a default proxy // server is specified. if (gArgs.SoftSetBoolArg("-listen", false)) { LogPrintf( "%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__); } // to protect privacy, do not use UPNP when a proxy is set. The user may // still specify -listen=1 to listen locally, so don't rely on this // happening through -listen below. if (gArgs.SoftSetBoolArg("-upnp", false)) { LogPrintf( "%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__); } // to protect privacy, do not discover addresses by default if (gArgs.SoftSetBoolArg("-discover", false)) { LogPrintf("%s: parameter interaction: -proxy set -> setting " "-discover=0\n", __func__); } } if (!gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) { // do not map ports or try to retrieve public IP when not listening // (pointless) if (gArgs.SoftSetBoolArg("-upnp", false)) { LogPrintf( "%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__); } if (gArgs.SoftSetBoolArg("-discover", false)) { LogPrintf( "%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__); } if (gArgs.SoftSetBoolArg("-listenonion", false)) { LogPrintf("%s: parameter interaction: -listen=0 -> setting " "-listenonion=0\n", __func__); } } if (gArgs.IsArgSet("-externalip")) { // if an explicit public IP is specified, do not try to find others if (gArgs.SoftSetBoolArg("-discover", false)) { LogPrintf("%s: parameter interaction: -externalip set -> setting " "-discover=0\n", __func__); } } // disable whitelistrelay in blocksonly mode if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) { if (gArgs.SoftSetBoolArg("-whitelistrelay", false)) { LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting " "-whitelistrelay=0\n", __func__); } } // Forcing relay from whitelisted hosts implies we will accept relays from // them in the first place. if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) { LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> " "setting -whitelistrelay=1\n", __func__); } } // Warn 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. gArgs.WarnForSectionOnlyArgs(); } static std::string ResolveErrMsg(const char *const optname, const std::string &strBind) { return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind); } void InitLogging() { BCLog::Logger &logger = GetLogger(); logger.m_print_to_console = gArgs.GetBoolArg("-printtoconsole", false); logger.m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); logger.m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); LogPrintf("%s version %s\n", CLIENT_NAME, FormatFullVersion()); } namespace { // Variables internal to initialization process only int nMaxConnections; int nUserMaxConnections; int nFD; ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED); } // namespace [[noreturn]] static void new_handler_terminate() { // Rather than throwing std::bad-alloc if allocation fails, terminate // immediately to (try to) avoid chain corruption. Since LogPrintf may // itself allocate memory, set the handler directly to terminate first. std::set_new_handler(std::terminate); LogPrintf("Error: Out of memory. Terminating.\n"); // The log was successful, terminate now. std::terminate(); }; bool AppInitBasicSetup() { // Step 1: setup #ifdef _MSC_VER // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, 0)); #endif #if _MSC_VER >= 1400 // Disable confusing "helpful" text message on abort, Ctrl-C _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif #ifdef WIN32 // Enable Data Execution Prevention (DEP) // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 // A failure is non-critical and needs no further attention! #ifndef PROCESS_DEP_ENABLE // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= // 0x0601 (Windows 7), which is not correct. Can be removed, when GCCs winbase.h // is fixed! #define PROCESS_DEP_ENABLE 0x00000001 #endif typedef BOOL(WINAPI * PSETPROCDEPPOL)(DWORD); PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress( GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); if (setProcDEPPol != nullptr) { setProcDEPPol(PROCESS_DEP_ENABLE); } #endif if (!SetupNetworking()) { return InitError("Initializing networking failed"); } #ifndef WIN32 if (!gArgs.GetBoolArg("-sysperms", false)) { umask(077); } // Clean shutdown on SIGTERM registerSignalHandler(SIGTERM, HandleSIGTERM); registerSignalHandler(SIGINT, HandleSIGTERM); // Reopen debug.log on SIGHUP registerSignalHandler(SIGHUP, HandleSIGHUP); // Ignore SIGPIPE, otherwise it will bring the daemon down if the client // closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif std::set_new_handler(new_handler_terminate); return true; } bool AppInitParameterInteraction(Config &config) { const CChainParams &chainparams = config.GetChainParams(); // Step 2: parameter interactions // also see: InitParameterInteraction() if (!fs::is_directory(GetBlocksDir())) { return InitError( strprintf(_("Specified blocks directory \"%s\" does not exist.\n"), gArgs.GetArg("-blocksdir", "").c_str())); } // 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.")); } } // if space reserved for high priority transactions is misconfigured // stop program execution and warn the user with a proper error message const int64_t blkprio = gArgs.GetArg("-blockprioritypercentage", DEFAULT_BLOCK_PRIORITY_PERCENTAGE); if (!config.SetBlockPriorityPercentage(blkprio)) { return InitError(_("Block priority percentage has to belong to the " "[0..100] interval.")); } // -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"); } // Make sure enough file descriptors are available int nBind = std::max(nUserBind, size_t(1)); nUserMaxConnections = gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS)), 0); nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS); if (nFD < MIN_CORE_FILEDESCRIPTORS) { 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."), nUserMaxConnections, nMaxConnections)); } // Step 3: parameter-to-internal-flags if (gArgs.IsArgSet("-debug")) { // Special-case: if -debug=0/-nodebug is set, turn off debugging // messages const std::vector<std::string> &categories = gArgs.GetArgs("-debug"); if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) { for (const auto &cat : categories) { BCLog::LogFlags flag = BCLog::NONE; if (!GetLogCategory(flag, cat)) { InitWarning( strprintf(_("Unsupported logging category %s=%s."), "-debug", cat)); continue; } GetLogger().EnableCategory(flag); } } } // Now remove the logging categories which were explicitly excluded for (const std::string &cat : gArgs.GetArgs("-debugexclude")) { BCLog::LogFlags flag = BCLog::NONE; if (!GetLogCategory(flag, cat)) { InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat)); continue; } GetLogger().DisableCategory(flag); } // Check for -debugnet if (gArgs.GetBoolArg("-debugnet", false)) { InitWarning( _("Unsupported argument -debugnet ignored, use -debug=net.")); } // Check for -socks - as this is a privacy risk to continue, exit here if (gArgs.IsArgSet("-socks")) { return InitError( _("Unsupported argument -socks found. Setting SOCKS version isn't " "possible anymore, only SOCKS5 proxies are supported.")); } // Check for -tor - as this is a privacy risk to continue, exit here if (gArgs.GetBoolArg("-tor", false)) { return InitError(_("Unsupported argument -tor found, use -onion.")); } if (gArgs.GetBoolArg("-benchmark", false)) { InitWarning( _("Unsupported argument -benchmark ignored, use -debug=bench.")); } if (gArgs.GetBoolArg("-whitelistalwaysrelay", false)) { InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use " "-whitelistrelay and/or -whitelistforcerelay.")); } if (gArgs.IsArgSet("-blockminsize")) { InitWarning("Unsupported argument -blockminsize ignored."); } // Checkmempool and checkblockindex default to true in regtest mode int ratio = std::min<int>( std::max<int>( gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); if (ratio != 0) { g_mempool.setSanityCheck(1.0 / ratio); } fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckpointsEnabled = gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); if (fCheckpointsEnabled) { LogPrintf("Checkpoints will be verified.\n"); } else { LogPrintf("Skipping checkpoint verification.\n"); } hashAssumeValid = uint256S( gArgs.GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex())); if (!hashAssumeValid.IsNull()) { LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex()); } else { LogPrintf("Validating signatures for all blocks.\n"); } 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)); } nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr)); } else { nMinimumChainWork = UintToArith256(chainparams.GetConsensus().nMinimumChainWork); } LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex()); if (nMinimumChainWork < UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainparams.GetConsensus().nMinimumChainWork.GetHex()); } // mempool limits 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"), std::ceil(nMempoolSizeMin / 1000000.0))); } // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS); if (nScriptCheckThreads <= 0) { nScriptCheckThreads += GetNumCores(); } if (nScriptCheckThreads <= 1) { nScriptCheckThreads = 0; } else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) { nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; } // Configure excessive block size. const uint64_t nProposedExcessiveBlockSize = gArgs.GetArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE); if (!config.SetMaxBlockSize(nProposedExcessiveBlockSize)) { return InitError( _("Excessive block size must be > 1,000,000 bytes (1MB)")); } // Check blockmaxsize does not exceed maximum accepted block size. const uint64_t nProposedMaxGeneratedBlockSize = gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE); if (nProposedMaxGeneratedBlockSize > config.GetMaxBlockSize()) { auto msg = _("Max generated block size (blockmaxsize) cannot exceed " "the excessive block size (excessiveblocksize)"); return InitError(msg); } // 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.")); } nPruneTarget = (uint64_t)nPruneArg * 1024 * 1024; if (nPruneArg == 1) { // manual pruning: -prune=1 LogPrintf("Block pruning enabled. Use RPC call " "pruneblockchain(height) to manually prune block and undo " "files.\n"); nPruneTarget = std::numeric_limits<uint64_t>::max(); 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."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); } LogPrintf("Prune configured to target %uMiB on disk for block and undo " "files.\n", nPruneTarget / 1024 / 1024); fPruneMode = true; } nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); if (nConnectTimeout <= 0) { nConnectTimeout = DEFAULT_CONNECT_TIMEOUT; } // Obtain the amount to charge excess UTXO if (gArgs.IsArgSet("-excessutxocharge")) { Amount n = Amount::zero(); auto parsed = ParseMoney(gArgs.GetArg("-excessutxocharge", ""), n); if (!parsed || Amount::zero() > n) { return InitError(AmountErrMsg( "excessutxocharge", gArgs.GetArg("-excessutxocharge", ""))); } config.SetExcessUTXOCharge(n); } else { config.SetExcessUTXOCharge(DEFAULT_UTXO_FEE); } // Fee-per-kilobyte amount considered the same as "free". If you are mining, // be careful setting this: if you set it to zero then a transaction spammer // can cheaply fill blocks using 1-satoshi-fee transactions. It should be // set above the real cost to you of processing a transaction. if (gArgs.IsArgSet("-minrelaytxfee")) { Amount n = Amount::zero(); auto parsed = ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n); if (!parsed || n == Amount::zero()) { return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); } // High fee check is done afterward in WalletParameterInteraction() ::minRelayTxFee = CFeeRate(n); } // Sanity check argument for min fee for including tx in block // TODO: Harmonize which arguments need sanity checking and where that // happens. if (gArgs.IsArgSet("-blockmintxfee")) { Amount n = Amount::zero(); if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) { return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", ""))); } } // Feerate used to define dust. Shouldn't be changed lightly as old // implementations may inadvertently create non-standard transactions. if (gArgs.IsArgSet("-dustrelayfee")) { Amount n = Amount::zero(); auto parsed = ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n); if (!parsed || Amount::zero() == n) { return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); } dustRelayFee = CFeeRate(n); } fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); if (chainparams.RequireStandard() && !fRequireStandard) { return InitError( strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); } nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); if (!g_wallet_init_interface.ParameterInteraction()) { return false; } fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); // Option to startup with mocktime set (used for regression testing): SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) { nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); } // Signal Bitcoin Cash support. // TODO: remove some time after the hardfork when no longer needed // to differentiate the network nodes. nLocalServices = ServiceFlags(nLocalServices | NODE_BITCOIN_CASH); nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); return true; } 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."), datadir.string())); } if (!LockDirectory(datadir, ".lock", probeOnly)) { return InitError(strprintf(_("Cannot obtain a lock on data directory " "%s. %s is probably already running."), datadir.string(), _(PACKAGE_NAME))); } return true; } bool AppInitSanityChecks() { // Step 4: sanity checks // Initialize elliptic curve code std::string sha256_algo = SHA256AutoDetect(); LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo); RandomInit(); ECC_Start(); globalVerifyHandle.reset(new ECCVerifyHandle()); // Sanity check if (!InitSanityCheck()) { 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, and a fork will cause weird behavior to it. return LockDataDirectory(true); } bool AppInitLockDataDirectory() { // After daemonization get the data directory lock again and hold on to it // until exit. This creates a slight window for a race condition to happen, // however this condition is harmless: it will at most make us exit without // printing a message to console. if (!LockDataDirectory(false)) { // Detailed error printed inside LockDataDirectory return false; } return true; } bool AppInitMain(Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor) { // Step 4a: application initialization const CChainParams &chainparams = config.GetChainParams(); #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif BCLog::Logger &logger = GetLogger(); bool default_shrinkdebugfile = logger.DefaultShrinkDebugFile(); if (gArgs.GetBoolArg("-shrinkdebugfile", default_shrinkdebugfile)) { // Do this first since it both loads a bunch of debug.log into memory, // and because this needs to happen before any other debug.log printing. logger.ShrinkDebugFile(); } if (logger.m_print_to_file) { if (!logger.OpenDebugLog()) { return InitError(strprintf("Could not open debug log file %s", logger.GetDebugLogPath().string())); } } if (!logger.m_log_timestamps) { LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime())); } LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); LogPrintf("Using data directory %s\n", GetDataDir().string()); LogPrintf( "Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); LogPrintf("Using at most %i automatic connections (%i file descriptors " "available)\n", nMaxConnections, nFD); // Warn about relative -datadir path. if (gArgs.IsArgSet("-datadir") && !fs::path(gArgs.GetArg("-datadir", "")).is_absolute()) { LogPrintf("Warning: relative datadir option '%s' specified, which will " "be interpreted relative to the current working directory " "'%s'. This is fragile, because if bitcoin is started in the " "future from a different location, it will be unable to " "locate the current data files. There could also be data " "loss if bitcoin is started while in a temporary " "directory.\n", gArgs.GetArg("-datadir", ""), fs::current_path().string()); } InitSignatureCache(); InitScriptExecutionCache(); LogPrintf("Using %u threads for script verification\n", nScriptCheckThreads); if (nScriptCheckThreads) { for (int i = 0; i < nScriptCheckThreads - 1; i++) { threadGroup.create_thread(&ThreadScriptCheck); } } // Start the lightweight task scheduler thread CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler); threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop)); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); GetMainSignals().RegisterWithMempoolSignals(g_mempool); /** * Register RPC commands regardless of -server setting so they will be * available in the GUI RPC console even if external calls are disabled. */ RegisterAllRPCCommands(config, rpcServer, tableRPC); g_wallet_init_interface.RegisterRPC(tableRPC); /** * Start the RPC server. It will be started in "warmup" mode and not * process calls yet (but it will verify that the server is there and will * be ready later). Warmup mode will be completed when initialisation is * finished. */ if (gArgs.GetBoolArg("-server", false)) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); if (!AppInitServers(config, httpRPCRequestProcessor)) { return InitError( _("Unable to start HTTP server. See debug log for details.")); } } // Step 5: verify wallet database integrity if (!g_wallet_init_interface.Verify(chainparams)) { return false; } // Step 6: network initialization // Note that we absolutely cannot open any actual connections // until the very end ("start node") as the UTXO/block state // is not yet setup and may end up being set up twice if we // need to reindex later. assert(!g_connman); g_connman = std::unique_ptr<CConnman>( new CConnman(config, GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()))); CConnman &connman = *g_connman; peerLogic.reset(new PeerLogicValidation(&connman, scheduler)); RegisterValidationInterface(peerLogic.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."), cmt)); uacomments.push_back(cmt); } const std::string 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."), strSubVersion.size(), MAX_SUBVERSION_LENGTH)); } if (gArgs.IsArgSet("-onlynet")) { std::set<enum Network> nets; 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'"), snet)); } nets.insert(net); } for (int n = 0; n < NET_MAX; n++) { enum Network net = (enum Network)n; if (!nets.count(net)) SetLimited(net); } } // Check for host lookup allowed before parsing any network related // parameters fNameLookup = gArgs.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP); bool proxyRandomize = gArgs.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE); // -proxy sets a proxy for all outgoing network traffic // -noproxy (or -proxy=0) as well as the empty string can be used to not set // a proxy, this is the default std::string proxyArg = gArgs.GetArg("-proxy", ""); SetLimited(NET_ONION); if (proxyArg != "" && proxyArg != "0") { CService proxyAddr; if (!Lookup(proxyArg.c_str(), proxyAddr, 9050, fNameLookup)) { 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'"), proxyArg)); } SetProxy(NET_IPV4, addrProxy); SetProxy(NET_IPV6, addrProxy); SetProxy(NET_ONION, addrProxy); SetNameProxy(addrProxy); // by default, -proxy sets onion as reachable, unless -noonion later SetLimited(NET_ONION, false); } // -onion can be used to set only a proxy for .onion, or override normal // proxy for .onion addresses. // -noonion (or -onion=0) disables connecting to .onion entirely. An empty // string is used to not override the onion proxy (in which case it defaults // to -proxy set above, or none) std::string onionArg = gArgs.GetArg("-onion", ""); if (onionArg != "") { if (onionArg == "0") { // Handle -noonion/-onion=0 SetLimited(NET_ONION); // set onions as unreachable } else { CService onionProxy; if (!Lookup(onionArg.c_str(), onionProxy, 9050, fNameLookup)) { 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'"), onionArg)); } SetProxy(NET_ONION, addrOnion); SetLimited(NET_ONION, false); } } // see Step 2: parameter interactions for more information about these fListen = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN); fDiscover = gArgs.GetBoolArg("-discover", true); fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY); for (const std::string &strAddr : gArgs.GetArgs("-externalip")) { CService addrLocal; if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) { AddLocal(addrLocal, LOCAL_MANUAL); } else { return InitError(ResolveErrMsg("externalip", strAddr)); } } #if ENABLE_ZMQ pzmqNotificationInterface = CZMQNotificationInterface::Create(); if (pzmqNotificationInterface) { RegisterValidationInterface(pzmqNotificationInterface); } #endif // unlimited unless -maxuploadtarget is set uint64_t nMaxOutboundLimit = 0; uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME; if (gArgs.IsArgSet("-maxuploadtarget")) { nMaxOutboundLimit = gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 * 1024; } // Step 7: load block chain fReindex = gArgs.GetBoolArg("-reindex", false); bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false); // cache size calculations int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20); // total cache cannot be less than nMinDbCache nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be greater than nMaxDbcache nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); int64_t nBlockTreeDBCache = std::min(nTotalCache / 8, nMaxBlockDBCache << 20); nTotalCache -= nBlockTreeDBCache; int64_t nTxIndexCache = std::min(nTotalCache / 8, gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0); nTotalCache -= nTxIndexCache; // use 25%-50% of the remainder for disk cache int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // cap total coins db cache nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); nTotalCache -= nCoinDBCache; // the rest goes to in-memory cache nCoinCacheUsage = nTotalCache; int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; LogPrintf("Cache configuration:\n"); LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { LogPrintf("* Using %.1fMiB for transaction index database\n", nTxIndexCache * (1.0 / 1024 / 1024)); } LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for in-memory UTXO set (plus up to %.1fMiB of " "unused mempool space)\n", nCoinCacheUsage * (1.0 / 1024 / 1024), nMempoolSizeMax * (1.0 / 1024 / 1024)); int64_t nStart = 0; bool fLoaded = false; while (!fLoaded && !fRequestShutdown) { bool fReset = fReindex; std::string strLoadError; uiInterface.InitMessage(_("Loading block index...")); LOCK(cs_main); nStart = GetTimeMillis(); do { try { UnloadBlockIndex(); pcoinsTip.reset(); pcoinsdbview.reset(); pcoinscatcher.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 (fRequestShutdown) { 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 (!LoadBlockIndex(config)) { strLoadError = _("Error loading block database"); break; } // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way // around). if (!mapBlockIndex.empty() && !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 && !LoadGenesisBlock(chainparams)) { strLoadError = _("Error initializing block database"); break; } // At this point we're either in reindex or we've loaded a // useful block tree into mapBlockIndex! pcoinsdbview.reset(new CCoinsViewDB( nCoinDBCache, false, fReset || fReindexChainState)); pcoinscatcher.reset( new CCoinsViewErrorCatcher(pcoinsdbview.get())); // If necessary, upgrade from older database format. // This is a no-op if we cleared the coinsviewdb with -reindex // or -reindex-chainstate if (!pcoinsdbview->Upgrade()) { strLoadError = _("Error upgrading chainstate database"); break; } // ReplayBlocks is a no-op if we cleared the coinsviewdb with // -reindex or -reindex-chainstate if (!ReplayBlocks(config, pcoinsdbview.get())) { strLoadError = _("Unable to replay blocks. You will need to rebuild " "the database using -reindex-chainstate."); break; } // The on-disk coinsdb is now in a good state, create the cache pcoinsTip.reset(new CCoinsViewCache(pcoinscatcher.get())); bool is_coinsview_empty = fReset || fReindexChainState || pcoinsTip->GetBestBlock().IsNull(); if (!is_coinsview_empty) { // LoadChainTip sets chainActive based on pcoinsTip's best // block if (!LoadChainTip(config)) { strLoadError = _("Error initializing block database"); break; } assert(chainActive.Tip() != nullptr); } 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 mapBlockIndex // based on lack of available witness data. uiInterface.InitMessage(_("Rewinding blocks...")); if (!RewindBlockIndex(config)) { strLoadError = _("Unable to rewind the database to a " "pre-fork state. You will need to " "redownload the blockchain"); break; } } if (!is_coinsview_empty) { uiInterface.InitMessage(_("Verifying blocks...")); 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() + MAX_FUTURE_BLOCK_TIME) { 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; } if (!CVerifyDB().VerifyDB( config, pcoinsdbview.get(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { strLoadError = _("Corrupted block database detected"); break; } } } catch (const std::exception &e) { LogPrintf("%s\n", e.what()); strLoadError = _("Error opening block database"); break; } fLoaded = true; } while (false); if (!fLoaded && !fRequestShutdown) { // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeQuestion( strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"), strLoadError + ".\nPlease restart with -reindex or " "-reindex-chainstate to recover.", "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; fRequestShutdown = false; } else { LogPrintf("Aborted block database rebuild. Exiting.\n"); return false; } } else { return InitError(strLoadError); } } } // As LoadBlockIndex can take several minutes, it's possible the user // requested to kill the GUI during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly // overkill. if (fRequestShutdown) { LogPrintf("Shutdown requested. Exiting.\n"); return false; } if (fLoaded) { LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); } // Encoded addresses using cashaddr instead of base58 // Activates by default on Jan, 14 config.SetCashAddrEncoding( gArgs.GetBoolArg("-usecashaddr", GetAdjustedTime() > 1515900000)); // Step 8: load indexers if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { g_txindex = std::make_unique<TxIndex>(nTxIndexCache, false, fReindex); g_txindex->Start(); } // Step 9: load wallet if (!g_wallet_init_interface.Open(chainparams)) { return false; } // Step 10: data directory maintenance // if pruning, unset the service bit and perform the initial blockstore // prune after any wallet rescanning has taken place. if (fPruneMode) { LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK); if (!fReindex) { uiInterface.InitMessage(_("Pruning blockstore...")); PruneAndFlush(); } } // Step 11: import blocks if (!CheckDiskSpace(/* additional_bytes */ 0, /* blocks_dir */ false)) { InitError( strprintf(_("Error: Disk space is low for %s"), GetDataDir())); return false; } if (!CheckDiskSpace(/* additional_bytes */ 0, /* blocks_dir */ true)) { InitError( strprintf(_("Error: Disk space is low for %s"), GetBlocksDir())); return false; } // Either install a handler to notify us when genesis activates, or set // fHaveGenesis directly. // No locking, as this happens before any background thread is started. if (chainActive.Tip() == nullptr) { uiInterface.NotifyBlockTip.connect(BlockNotifyGenesisWait); } else { fHaveGenesis = true; } if (gArgs.IsArgSet("-blocknotify")) { uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); } std::vector<fs::path> vImportFiles; for (const std::string &strFile : gArgs.GetArgs("-loadblock")) { vImportFiles.push_back(strFile); } threadGroup.create_thread( boost::bind(&ThreadImport, std::ref(config), vImportFiles)); // Wait for genesis block to be processed { WAIT_LOCK(g_genesis_wait_mutex, lock); // We previously could hang here if StartShutdown() is called prior to // ThreadImport getting started, so instead we just wait on a timer to // check ShutdownRequested() regularly. while (!fHaveGenesis && !ShutdownRequested()) { g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500)); } uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); } if (ShutdownRequested()) { return false; } // Step 12: start node int chain_active_height; //// debug print { LOCK(cs_main); LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); chain_active_height = chainActive.Height(); } LogPrintf("nBestHeight = %d\n", chain_active_height); if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) { StartTorControl(); } Discover(); // Map ports with UPnP if (gArgs.GetBoolArg("-upnp", DEFAULT_UPNP)) { StartMapPort(); } CConnman::Options connOptions; connOptions.nLocalServices = nLocalServices; connOptions.nMaxConnections = nMaxConnections; connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; connOptions.nMaxFeeler = 1; connOptions.nBestHeight = chain_active_height; connOptions.uiInterface = &uiInterface; connOptions.m_msgproc = peerLogic.get(); connOptions.nSendBufferMaxSize = 1000 * gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); connOptions.nReceiveFloodSize = 1000 * gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); connOptions.m_added_nodes = gArgs.GetArgs("-addnode"); connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; connOptions.nMaxOutboundLimit = nMaxOutboundLimit; for (const std::string &strBind : gArgs.GetArgs("-bind")) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) { return InitError(ResolveErrMsg("bind", strBind)); } connOptions.vBinds.push_back(addrBind); } for (const std::string &strBind : gArgs.GetArgs("-whitebind")) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, 0, false)) { return InitError(ResolveErrMsg("whitebind", strBind)); } if (addrBind.GetPort() == 0) { return InitError(strprintf( _("Need to specify a port with -whitebind: '%s'"), strBind)); } connOptions.vWhiteBinds.push_back(addrBind); } for (const auto &net : gArgs.GetArgs("-whitelist")) { CSubNet subnet; LookupSubNet(net.c_str(), subnet); if (!subnet.IsValid()) { return InitError(strprintf( _("Invalid netmask specified in -whitelist: '%s'"), net)); } connOptions.vWhitelistedRange.push_back(subnet); } connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); // Initiate outbound connections unless connect=0 connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect"); if (!connOptions.m_use_addrman_outgoing) { const auto connect = gArgs.GetArgs("-connect"); if (connect.size() != 1 || connect[0] != "0") { connOptions.m_specified_outgoing = connect; } } if (!connman.Start(scheduler, connOptions)) { return false; } // Step 13: finished SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); g_wallet_init_interface.Start(scheduler); return true; }