Page MenuHomePhabricator

No OneTemporary

diff --git a/src/fs.cpp b/src/fs.cpp
index 74c86303b..6b09dec34 100644
--- a/src/fs.cpp
+++ b/src/fs.cpp
@@ -1,14 +1,99 @@
// Copyright (c) 2017 The Bitcoin Core developers
// Copyright (c) 2019 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
+#ifndef WIN32
+#include <fcntl.h>
+#else
+#include <codecvt>
+#include <windows.h>
+#endif
+
namespace fsbridge {
FILE *fopen(const fs::path &p, const char *mode) {
return ::fopen(p.string().c_str(), mode);
}
+#ifndef WIN32
+
+static std::string GetErrorReason() {
+ return std::strerror(errno);
+}
+
+FileLock::FileLock(const fs::path &file) {
+ fd = open(file.string().c_str(), O_RDWR);
+ if (fd == -1) {
+ reason = GetErrorReason();
+ }
+}
+
+FileLock::~FileLock() {
+ if (fd != -1) {
+ close(fd);
+ }
+}
+
+bool FileLock::TryLock() {
+ if (fd == -1) {
+ return false;
+ }
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ if (fcntl(fd, F_SETLK, &lock) == -1) {
+ reason = GetErrorReason();
+ return false;
+ }
+ return true;
+}
+#else
+
+static std::string GetErrorReason() {
+ wchar_t *err;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<WCHAR *>(&err), 0, nullptr);
+ std::wstring err_str(err);
+ LocalFree(err);
+ return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(
+ err_str);
+}
+
+FileLock::FileLock(const fs::path &file) {
+ hFile = CreateFileW(file.wstring().c_str(), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ reason = GetErrorReason();
+ }
+}
+
+FileLock::~FileLock() {
+ if (hFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(hFile);
+ }
+}
+
+bool FileLock::TryLock() {
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+ _OVERLAPPED overlapped = {0};
+ if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
+ 0, 0, 0, &overlapped)) {
+ reason = GetErrorReason();
+ return false;
+ }
+ return true;
+}
+#endif
+
} // namespace fsbridge
diff --git a/src/fs.h b/src/fs.h
index bbd2a0c1e..6022f1b48 100644
--- a/src/fs.h
+++ b/src/fs.h
@@ -1,22 +1,43 @@
// Copyright (c) 2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_FS_H
#define BITCOIN_FS_H
#include <cstdio>
#include <string>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
/** Filesystem operations and types */
namespace fs = boost::filesystem;
/** Bridge operations to C stdio */
namespace fsbridge {
FILE *fopen(const fs::path &p, const char *mode);
+FILE *freopen(const fs::path &p, const char *mode, FILE *stream);
+
+class FileLock {
+public:
+ FileLock() = delete;
+ FileLock(const FileLock &) = delete;
+ FileLock(FileLock &&) = delete;
+ explicit FileLock(const fs::path &file);
+ ~FileLock();
+ bool TryLock();
+ std::string GetReason() { return reason; }
+
+private:
+ std::string reason;
+#ifndef WIN32
+ int fd = -1;
+#else
+ // INVALID_HANDLE_VALUE
+ void *hFile = (void *)-1;
+#endif
+};
}; // namespace fsbridge
#endif // BITCOIN_FS_H
diff --git a/src/init.cpp b/src/init.cpp
index 8e6103d04..6257a8f1e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,2481 +1,2481 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 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 <banman.h>
#include <chain.h>
#include <chainparams.h>
#include <checkpoints.h>
#include <compat/sanity.h>
#include <config.h>
#include <consensus/validation.h>
#include <flatfile.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 <shutdown.h>
#include <timedata.h>
#include <torcontrol.h>
#include <txdb.h>
#include <txmempool.h>
#include <ui_interface.h>
#include <util/moneystr.h>
#include <util/system.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/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp>
#if ENABLE_ZMQ
#include <zmq/zmqnotificationinterface.h>
#include <zmq/zmqrpc.h>
#endif
#ifndef WIN32
#include <csignal>
+#include <sys/stat.h>
#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;
// Dump addresses to banlist.dat every 15 minutes (900s)
static constexpr int DUMP_BANS_INTERVAL = 60 * 15;
std::unique_ptr<CConnman> g_connman;
std::unique_ptr<PeerLogicValidation> peerLogic;
std::unique_ptr<BanMan> g_banman;
#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
#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
// ShutdownRequested(), 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
// ShutdownRequested() getting set, and then does the normal Qt shutdown thing.
//
/**
* 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_banman.reset();
g_txindex.reset();
if (::g_mempool.IsLoaded() &&
gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool(::g_mempool);
}
// 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 (g_zmq_notification_interface) {
UnregisterValidationInterface(g_zmq_notification_interface);
delete g_zmq_notification_interface;
g_zmq_notification_interface = 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:
*/
#ifndef WIN32
static void HandleSIGTERM(int) {
StartShutdown();
}
static void HandleSIGHUP(int) {
LogInstance().m_reopen_file = true;
}
#else
static BOOL WINAPI consoleCtrlHandler(DWORD dwCtrlType) {
StartShutdown();
Sleep(INFINITE);
return true;
}
#endif
#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
static void OnRPCStarted() {
uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);
}
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. Relative paths will be "
"prefixed by datadir location. (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. Relative paths "
"will be prefixed by a net-specific datadir "
"location. (0 to disable; 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. Relative paths will be prefixed "
"by a net-specific datadir location. (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. When "
"in pruning mode or if blocks on disk might be corrupted, use "
"full -reindex instead.",
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",
strprintf("Allow DNS lookups for -addnode, -seednode and "
"-connect (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 used)",
false, OptionsCategory::CONNECTION);
gArgs.AddArg("-enablebip61",
strprintf("Send reject messages per BIP61 (default: %u)",
DEFAULT_ENABLE_BIP61),
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)",
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: "
"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),
true, OptionsCategory::DEBUG_TEST);
gArgs.AddArg("-checkblockindex",
strprintf("Do a full consistency check for mapBlockIndex, "
"setBlockIndexCandidates, chainActive and "
"mapBlocksUnlinked occasionally. (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 (default: "
"1 when no -daemon. To disable logging to file, set debuglogfile=0)",
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("-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 rejected 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("-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>[:port]",
"Bind to given address to listen for JSON-RPC connections. This option "
"is ignored unless -rpcallowip is also passed. Port is optional and "
"overrides -rpcport. Use [host]:port notation for IPv6. This option "
"can be specified multiple times (default: 127.0.0.1 and ::1 i.e., "
"localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., "
"all addresses)",
false, OptionsCategory::RPC);
gArgs.AddArg("-rpccookiefile=<loc>",
"Location of the auth cookie. Relative paths will be prefixed "
"by a net-specific datadir location. (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);
// Hidden options
gArgs.AddArg("-rpcssl", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-benchmark", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-socks", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-tor", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-debugnet", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-whitelistalwaysrelay", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-blockminsize", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-dbcrashratio", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-forcecompactdb", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-usehd", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-parkdeepreorg", "", false, OptionsCategory::HIDDEN);
gArgs.AddArg("-replayprotectionactivationtime", "", false,
OptionsCategory::HIDDEN);
// TODO remove after the May 2020 upgrade
gArgs.AddArg("-phononactivationtime", "", false, OptionsCategory::HIDDEN);
}
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.
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");
const auto directoryIterator = fs::directory_iterator{GetBlocksDir()};
for (const auto &file : directoryIterator) {
const auto fileName = file.path().filename().string();
if (fs::is_regular_file(file) && fileName.length() == 12 &&
fileName.substr(8, 4) == ".dat") {
if (fileName.substr(0, 3) == "blk") {
mapBlockFiles[fileName.substr(3, 5)] = file.path();
} else if (fileName.substr(0, 3) == "rev") {
remove(file.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 contiguousCounter = 0;
for (const auto &item : mapBlockFiles) {
if (atoi(item.first) == contiguousCounter) {
contiguousCounter++;
continue;
}
remove(item.second);
}
}
static void ThreadImport(const Config &config,
std::vector<fs::path> vImportFiles) {
RenameThread("bitcoin-loadblk");
ScheduleBatchPriority();
{
CImportingNow imp;
// -reindex
if (fReindex) {
int nFile = 0;
while (true) {
FlatFilePos pos(nFile, 0);
if (!fs::exists(GetBlockPosFilename(pos))) {
// 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 (%s)\n",
FormatStateMessage(state));
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_mempool);
}
::g_mempool.SetIsLoaded(!ShutdownRequested());
}
/** Sanity checks
* Ensure that Bitcoin is running in a usable environment with all
* necessary library support.
*/
static bool InitSanityCheck() {
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;
}
StartRPC();
if (!StartHTTPRPC(config, httpRPCRequestProcessor)) {
return false;
}
if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) {
return false;
}
StartHTTPServer();
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);
}
/**
* Initialize global loggers.
*
* Note that this is called very early in the process lifetime, so you should be
* careful about what global state you rely on here.
*/
void InitLogging() {
LogInstance().m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
LogInstance().m_file_path = AbsPathForConfigVal(
gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
// Add newlines to the logfile to distinguish this execution from the last
// one; called before console logging is set up, so this is only sent to
// debug.log.
LogPrintf("\n\n\n\n\n");
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);
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
std::string version_string = FormatFullVersion();
#ifdef DEBUG
version_string += " (debug build)";
#else
version_string += " (release build)";
#endif
LogPrintf("%s version %s\n", CLIENT_NAME, version_string);
}
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));
// 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);
#else
SetConsoleCtrlHandler(consoleCtrlHandler, true);
#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."),
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."));
}
}
// -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, 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 (std::none_of(
categories.begin(), categories.end(),
[](std::string cat) { return cat == "0" || cat == "none"; })) {
for (const auto &cat : categories) {
if (!LogInstance().EnableCategory(cat)) {
InitWarning(
strprintf(_("Unsupported logging category %s=%s."),
"-debug", cat));
}
}
}
}
// 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."),
"-debugexclude", cat));
}
}
// 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 = BlockHash::fromHex(
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);
}
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 = LogInstance();
if (logger.m_print_to_file) {
if (gArgs.GetBoolArg("-shrinkdebugfile",
logger.DefaultShrinkDebugFile())) {
// 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.OpenDebugLog()) {
return InitError(strprintf("Could not open debug log file %s",
logger.m_file_path.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 =
std::bind(&CScheduler::serviceQueue, &scheduler);
threadGroup.create_thread(std::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);
#if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC);
#endif
/**
* 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_banman);
g_banman = std::make_unique<BanMan>(
GetDataDir() / "banlist.dat", config.GetChainParams(), &uiInterface,
gArgs.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
assert(!g_connman);
g_connman = std::make_unique<CConnman>(
config, GetRand(std::numeric_limits<uint64_t>::max()),
GetRand(std::numeric_limits<uint64_t>::max()));
peerLogic.reset(new PeerLogicValidation(
g_connman.get(), g_banman.get(), scheduler,
gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61)));
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)) {
SetReachable(net, false);
}
}
}
// 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", "");
SetReachable(NET_ONION, false);
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
SetReachable(NET_ONION, true);
}
// -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
SetReachable(NET_ONION, false);
} 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);
SetReachable(NET_ONION, true);
}
}
// 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
g_zmq_notification_interface = CZMQNotificationInterface::Create();
if (g_zmq_notification_interface) {
RegisterValidationInterface(g_zmq_notification_interface);
}
#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 && !ShutdownRequested()) {
bool fReset = fReindex;
std::string strLoadError;
uiInterface.InitMessage(_("Loading block index..."));
nStart = GetTimeMillis();
do {
try {
LOCK(cs_main);
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 (ShutdownRequested()) {
break;
}
// LoadBlockIndex will load fHavePruned if we've ever removed a
// block file from disk.
// Note that it also sets fReindex based on the disk flag!
// From here on out fReindex and fReset mean something
// different!
if (!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(chainparams.GetConsensus(),
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);
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 && !ShutdownRequested()) {
// 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;
AbortShutdown();
} 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 (ShutdownRequested()) {
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
if (fLoaded) {
LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart);
}
// Encoded addresses using cashaddr instead of base58.
// We do this by default to avoid confusion with BTC addresses.
config.SetCashAddrEncoding(gArgs.GetBoolArg("-usecashaddr", true));
// 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(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"), 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(
std::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_banman = g_banman.get();
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 (!g_connman->Start(scheduler, connOptions)) {
return false;
}
// Step 13: finished
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading"));
g_wallet_init_interface.Start(scheduler);
scheduler.scheduleEvery(
[] {
g_banman->DumpBanlist();
return true;
},
DUMP_BANS_INTERVAL * 1000);
return true;
}
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 8cde4f17b..edafe0612 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1,1358 +1,1350 @@
// 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 <fs.h>
#include <util/system.h>
#include <chainparamsbase.h>
#include <fs.h>
#include <random.h>
#include <serialize.h>
#include <util/strencodings.h>
#include <util/time.h>
-#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp>
#include <cstdarg>
#include <memory>
#if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
#include <pthread.h>
#include <pthread_np.h>
#endif
#ifndef WIN32
// for posix_fallocate
#ifdef __linux__
#ifdef _POSIX_C_SOURCE
#undef _POSIX_C_SOURCE
#endif
#define _POSIX_C_SOURCE 200112L
#endif // __linux__
#include <algorithm>
#include <fcntl.h>
#include <sched.h>
#include <sys/resource.h>
#include <sys/stat.h>
#else
#ifdef _MSC_VER
#pragma warning(disable : 4786)
#pragma warning(disable : 4804)
#pragma warning(disable : 4805)
#pragma warning(disable : 4717)
#endif
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#define _WIN32_IE 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <codecvt>
#include <io.h> /* for _commit */
#include <shellapi.h>
#include <shlobj.h>
#endif
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#ifdef HAVE_MALLOPT_ARENA_MAX
#include <malloc.h>
#endif
// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime();
const char *const BITCOIN_CONF_FILENAME = "bitcoin.conf";
const char *const BITCOIN_PID_FILENAME = "bitcoind.pid";
ArgsManager gArgs;
/**
* A map that contains all the currently held directory locks. After successful
* locking, these will be held here until the global destructor cleans them up
* and thus automatically unlocks them, or ReleaseDirectoryLocks is called.
*/
-static std::map<std::string, std::unique_ptr<boost::interprocess::file_lock>>
- dir_locks;
+static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
/** Mutex to protect dir_locks. */
static std::mutex cs_dir_locks;
bool LockDirectory(const fs::path &directory, const std::string lockfile_name,
bool probe_only) {
std::lock_guard<std::mutex> ulock(cs_dir_locks);
fs::path pathLockFile = directory / lockfile_name;
// If a lock for this directory already exists in the map, don't try to
// re-lock it
if (dir_locks.count(pathLockFile.string())) {
return true;
}
// Create empty lock file if it doesn't exist.
FILE *file = fsbridge::fopen(pathLockFile, "a");
if (file) {
fclose(file);
}
-
- try {
- auto lock = std::make_unique<boost::interprocess::file_lock>(
- pathLockFile.string().c_str());
- if (!lock->try_lock()) {
- return false;
- }
- if (!probe_only) {
- // Lock successful and we're not just probing, put it into the map
- dir_locks.emplace(pathLockFile.string(), std::move(lock));
- }
- } catch (const boost::interprocess::interprocess_exception &e) {
+ auto lock = std::make_unique<fsbridge::FileLock>(pathLockFile);
+ if (!lock->TryLock()) {
return error("Error while attempting to lock directory %s: %s",
- directory.string(), e.what());
+ directory.string(), lock->GetReason());
+ }
+ if (!probe_only) {
+ // Lock successful and we're not just probing, put it into the map
+ dir_locks.emplace(pathLockFile.string(), std::move(lock));
}
return true;
}
void ReleaseDirectoryLocks() {
std::lock_guard<std::mutex> ulock(cs_dir_locks);
dir_locks.clear();
}
bool DirIsWritable(const fs::path &directory) {
fs::path tmpFile = directory / fs::unique_path();
FILE *file = fsbridge::fopen(tmpFile, "a");
if (!file) {
return false;
}
fclose(file);
remove(tmpFile);
return true;
}
bool CheckDiskSpace(const fs::path &dir, uint64_t additional_bytes) {
// 50 MiB
constexpr uint64_t min_disk_space = 52428800;
uint64_t free_bytes_available = fs::space(dir).available;
return free_bytes_available >= min_disk_space + additional_bytes;
}
/**
* Interpret a string argument as a boolean.
*
* The definition of atoi() requires that non-numeric string values like "foo",
* return 0. This means that if a user unintentionally supplies a non-integer
* argument here, the return value is always false. This means that -foo=false
* does what the user probably expects, but -foo=true is well defined but does
* not do what they probably expected.
*
* The return value of atoi() is undefined when given input not representable as
* an int. On most systems this means string value between "-2147483648" and
* "2147483647" are well defined (this method will return true). Setting
* -txindex=2147483648 on most systems, however, is probably undefined.
*
* For a more extensive discussion of this topic (and a wide range of opinions
* on the Right Way to change this code), see PR12713.
*/
static bool InterpretBool(const std::string &strValue) {
if (strValue.empty()) {
return true;
}
return (atoi(strValue) != 0);
}
/** Internal helper functions for ArgsManager */
class ArgsManagerHelper {
public:
typedef std::map<std::string, std::vector<std::string>> MapArgs;
/** Determine whether to use config settings in the default section,
* See also comments around ArgsManager::ArgsManager() below. */
static inline bool UseDefaultSection(const ArgsManager &am,
const std::string &arg)
EXCLUSIVE_LOCKS_REQUIRED(am.cs_args) {
return (am.m_network == CBaseChainParams::MAIN ||
am.m_network_only_args.count(arg) == 0);
}
/** Convert regular argument into the network-specific setting */
static inline std::string NetworkArg(const ArgsManager &am,
const std::string &arg) {
assert(arg.length() > 1 && arg[0] == '-');
return "-" + am.m_network + "." + arg.substr(1);
}
/** Find arguments in a map and add them to a vector */
static inline void AddArgs(std::vector<std::string> &res,
const MapArgs &map_args,
const std::string &arg) {
auto it = map_args.find(arg);
if (it != map_args.end()) {
res.insert(res.end(), it->second.begin(), it->second.end());
}
}
/**
* Return true/false if an argument is set in a map, and also
* return the first (or last) of the possibly multiple values it has
*/
static inline std::pair<bool, std::string>
GetArgHelper(const MapArgs &map_args, const std::string &arg,
bool getLast = false) {
auto it = map_args.find(arg);
if (it == map_args.end() || it->second.empty()) {
return std::make_pair(false, std::string());
}
if (getLast) {
return std::make_pair(true, it->second.back());
} else {
return std::make_pair(true, it->second.front());
}
}
/**
* Get the string value of an argument, returning a pair of a boolean
* indicating the argument was found, and the value for the argument
* if it was found (or the empty string if not found).
*/
static inline std::pair<bool, std::string> GetArg(const ArgsManager &am,
const std::string &arg) {
LOCK(am.cs_args);
std::pair<bool, std::string> found_result(false, std::string());
// We pass "true" to GetArgHelper in order to return the last
// argument value seen from the command line (so "bitcoind -foo=bar
// -foo=baz" gives GetArg(am,"foo")=={true,"baz"}
found_result = GetArgHelper(am.m_override_args, arg, true);
if (found_result.first) {
return found_result;
}
// But in contrast we return the first argument seen in a config file,
// so "foo=bar \n foo=baz" in the config file gives
// GetArg(am,"foo")={true,"bar"}
if (!am.m_network.empty()) {
found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg));
if (found_result.first) {
return found_result;
}
}
if (UseDefaultSection(am, arg)) {
found_result = GetArgHelper(am.m_config_args, arg);
if (found_result.first) {
return found_result;
}
}
return found_result;
}
/* Special test for -testnet and -regtest args, because we don't want to be
* confused by craziness like "[regtest] testnet=1"
*/
static inline bool GetNetBoolArg(const ArgsManager &am,
const std::string &net_arg)
EXCLUSIVE_LOCKS_REQUIRED(am.cs_args) {
std::pair<bool, std::string> found_result(false, std::string());
found_result = GetArgHelper(am.m_override_args, net_arg, true);
if (!found_result.first) {
found_result = GetArgHelper(am.m_config_args, net_arg, true);
if (!found_result.first) {
// not set
return false;
}
}
// is set, so evaluate
return InterpretBool(found_result.second);
}
};
/**
* Interpret -nofoo as if the user supplied -foo=0.
*
* This method also tracks when the -no form was supplied, and if so, checks
* whether there was a double-negative (-nofoo=0 -> -foo=1).
*
* If there was not a double negative, it removes the "no" from the key, and
* returns true, indicating the caller should clear the args vector to indicate
* a negated option.
*
* If there was a double negative, it removes "no" from the key, sets the value
* to "1" and returns false.
*
* If there was no "no", it leaves key and value untouched and returns false.
*
* Where an option was negated can be later checked using the IsArgNegated()
* method. One use case for this is to have a way to disable options that are
* not normally boolean (e.g. using -nodebuglogfile to request that debug log
* output is not sent to any file at all).
*/
static bool InterpretNegatedOption(std::string &key, std::string &val) {
assert(key[0] == '-');
size_t option_index = key.find('.');
if (option_index == std::string::npos) {
option_index = 1;
} else {
++option_index;
}
if (key.substr(option_index, 2) == "no") {
bool bool_val = InterpretBool(val);
key.erase(option_index, 2);
if (!bool_val) {
// Double negatives like -nofoo=0 are supported (but discouraged)
LogPrintf(
"Warning: parsed potentially confusing double-negative %s=%s\n",
key, val);
val = "1";
} else {
return true;
}
}
return false;
}
ArgsManager::ArgsManager()
: /* These options would cause cross-contamination if values for mainnet
* were used while running on regtest/testnet (or vice-versa).
* Setting them as section_only_args ensures that sharing a config file
* between mainnet and regtest/testnet won't cause problems due to these
* parameters by accident. */
m_network_only_args{
"-addnode", "-connect", "-port", "-bind",
"-rpcport", "-rpcbind", "-wallet",
} {
// nothing to do
}
void ArgsManager::WarnForSectionOnlyArgs() {
LOCK(cs_args);
// if there's no section selected, don't worry
if (m_network.empty()) {
return;
}
// if it's okay to use the default section for this network, don't worry
if (m_network == CBaseChainParams::MAIN) {
return;
}
for (const auto &arg : m_network_only_args) {
std::pair<bool, std::string> found_result;
// if this option is overridden it's fine
found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg);
if (found_result.first) {
continue;
}
// if there's a network-specific value for this option, it's fine
found_result = ArgsManagerHelper::GetArgHelper(
m_config_args, ArgsManagerHelper::NetworkArg(*this, arg));
if (found_result.first) {
continue;
}
// if there isn't a default value for this option, it's fine
found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg);
if (!found_result.first) {
continue;
}
// otherwise, issue a warning
LogPrintf("Warning: Config setting for %s only applied on %s network "
"when in [%s] section.\n",
arg, m_network, m_network);
}
}
void ArgsManager::SelectConfigNetwork(const std::string &network) {
LOCK(cs_args);
m_network = network;
}
bool ParseKeyValue(std::string &key, std::string &val) {
size_t is_index = key.find('=');
if (is_index != std::string::npos) {
val = key.substr(is_index + 1);
key.erase(is_index);
}
#ifdef WIN32
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
if (key[0] == '/') {
key[0] = '-';
}
#endif
if (key[0] != '-') {
return false;
}
// Transform --foo to -foo
if (key.length() > 1 && key[1] == '-') {
key.erase(0, 1);
}
return true;
}
bool ArgsManager::ParseParameters(int argc, const char *const argv[],
std::string &error) {
LOCK(cs_args);
m_override_args.clear();
for (int i = 1; i < argc; i++) {
std::string key(argv[i]);
std::string val;
if (!ParseKeyValue(key, val)) {
break;
}
// Check for -nofoo
if (InterpretNegatedOption(key, val)) {
m_override_args[key].clear();
} else {
m_override_args[key].push_back(val);
}
// Check that the arg is known
if (!(IsSwitchChar(key[0]) && key.size() == 1)) {
if (!IsArgKnown(key)) {
error = strprintf("Invalid parameter %s", key.c_str());
return false;
}
}
}
// we do not allow -includeconf from command line, so we clear it here
auto it = m_override_args.find("-includeconf");
if (it != m_override_args.end()) {
if (it->second.size() > 0) {
for (const auto &ic : it->second) {
error += "-includeconf cannot be used from commandline; "
"-includeconf=" +
ic + "\n";
}
return false;
}
}
return true;
}
bool ArgsManager::IsArgKnown(const std::string &key) const {
size_t option_index = key.find('.');
std::string arg_no_net;
if (option_index == std::string::npos) {
arg_no_net = key;
} else {
arg_no_net =
std::string("-") + key.substr(option_index + 1, std::string::npos);
}
LOCK(cs_args);
for (const auto &arg_map : m_available_args) {
if (arg_map.second.count(arg_no_net)) {
return true;
}
}
return false;
}
std::vector<std::string> ArgsManager::GetArgs(const std::string &strArg) const {
std::vector<std::string> result = {};
// special case
if (IsArgNegated(strArg)) {
return result;
}
LOCK(cs_args);
ArgsManagerHelper::AddArgs(result, m_override_args, strArg);
if (!m_network.empty()) {
ArgsManagerHelper::AddArgs(
result, m_config_args,
ArgsManagerHelper::NetworkArg(*this, strArg));
}
if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) {
ArgsManagerHelper::AddArgs(result, m_config_args, strArg);
}
return result;
}
bool ArgsManager::IsArgSet(const std::string &strArg) const {
// special case
if (IsArgNegated(strArg)) {
return true;
}
return ArgsManagerHelper::GetArg(*this, strArg).first;
}
bool ArgsManager::IsArgNegated(const std::string &strArg) const {
LOCK(cs_args);
const auto &ov = m_override_args.find(strArg);
if (ov != m_override_args.end()) {
return ov->second.empty();
}
if (!m_network.empty()) {
const auto &cfs =
m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg));
if (cfs != m_config_args.end()) {
return cfs->second.empty();
}
}
const auto &cf = m_config_args.find(strArg);
if (cf != m_config_args.end()) {
return cf->second.empty();
}
return false;
}
std::string ArgsManager::GetArg(const std::string &strArg,
const std::string &strDefault) const {
if (IsArgNegated(strArg)) {
return "0";
}
std::pair<bool, std::string> found_res =
ArgsManagerHelper::GetArg(*this, strArg);
if (found_res.first) {
return found_res.second;
}
return strDefault;
}
int64_t ArgsManager::GetArg(const std::string &strArg, int64_t nDefault) const {
if (IsArgNegated(strArg)) {
return 0;
}
std::pair<bool, std::string> found_res =
ArgsManagerHelper::GetArg(*this, strArg);
if (found_res.first) {
return atoi64(found_res.second);
}
return nDefault;
}
bool ArgsManager::GetBoolArg(const std::string &strArg, bool fDefault) const {
if (IsArgNegated(strArg)) {
return false;
}
std::pair<bool, std::string> found_res =
ArgsManagerHelper::GetArg(*this, strArg);
if (found_res.first) {
return InterpretBool(found_res.second);
}
return fDefault;
}
bool ArgsManager::SoftSetArg(const std::string &strArg,
const std::string &strValue) {
LOCK(cs_args);
if (IsArgSet(strArg)) {
return false;
}
ForceSetArg(strArg, strValue);
return true;
}
bool ArgsManager::SoftSetBoolArg(const std::string &strArg, bool fValue) {
if (fValue) {
return SoftSetArg(strArg, std::string("1"));
} else {
return SoftSetArg(strArg, std::string("0"));
}
}
void ArgsManager::ForceSetArg(const std::string &strArg,
const std::string &strValue) {
LOCK(cs_args);
m_override_args[strArg] = {strValue};
}
/**
* This function is only used for testing purpose so
* so we should not worry about element uniqueness and
* integrity of mapMultiArgs data structure
*/
void ArgsManager::ForceSetMultiArg(const std::string &strArg,
const std::string &strValue) {
LOCK(cs_args);
m_override_args[strArg].push_back(strValue);
}
void ArgsManager::AddArg(const std::string &name, const std::string &help,
const bool debug_only, const OptionsCategory &cat) {
// Split arg name from its help param
size_t eq_index = name.find('=');
if (eq_index == std::string::npos) {
eq_index = name.size();
}
LOCK(cs_args);
std::map<std::string, Arg> &arg_map = m_available_args[cat];
auto ret = arg_map.emplace(
name.substr(0, eq_index),
Arg(name.substr(eq_index, name.size() - eq_index), help, debug_only));
// Make sure an insertion actually happened.
assert(ret.second);
}
void ArgsManager::ClearArg(const std::string &strArg) {
LOCK(cs_args);
m_override_args.erase(strArg);
m_config_args.erase(strArg);
}
std::string ArgsManager::GetHelpMessage() const {
const bool show_debug = gArgs.GetBoolArg("-help-debug", false);
std::string usage = "";
LOCK(cs_args);
for (const auto &arg_map : m_available_args) {
switch (arg_map.first) {
case OptionsCategory::OPTIONS:
usage += HelpMessageGroup("Options:");
break;
case OptionsCategory::CONNECTION:
usage += HelpMessageGroup("Connection options:");
break;
case OptionsCategory::ZMQ:
usage += HelpMessageGroup("ZeroMQ notification options:");
break;
case OptionsCategory::DEBUG_TEST:
usage += HelpMessageGroup("Debugging/Testing options:");
break;
case OptionsCategory::NODE_RELAY:
usage += HelpMessageGroup("Node relay options:");
break;
case OptionsCategory::BLOCK_CREATION:
usage += HelpMessageGroup("Block creation options:");
break;
case OptionsCategory::RPC:
usage += HelpMessageGroup("RPC server options:");
break;
case OptionsCategory::WALLET:
usage += HelpMessageGroup("Wallet options:");
break;
case OptionsCategory::WALLET_DEBUG_TEST:
if (show_debug) {
usage +=
HelpMessageGroup("Wallet debugging/testing options:");
}
break;
case OptionsCategory::CHAINPARAMS:
usage += HelpMessageGroup("Chain selection options:");
break;
case OptionsCategory::GUI:
usage += HelpMessageGroup("UI Options:");
break;
case OptionsCategory::COMMANDS:
usage += HelpMessageGroup("Commands:");
break;
case OptionsCategory::REGISTER_COMMANDS:
usage += HelpMessageGroup("Register Commands:");
break;
default:
break;
}
// When we get to the hidden options, stop
if (arg_map.first == OptionsCategory::HIDDEN) {
break;
}
for (const auto &arg : arg_map.second) {
if (show_debug || !arg.second.m_debug_only) {
std::string name;
if (arg.second.m_help_param.empty()) {
name = arg.first;
} else {
name = arg.first + arg.second.m_help_param;
}
usage += HelpMessageOpt(name, arg.second.m_help_text);
}
}
}
return usage;
}
bool HelpRequested(const ArgsManager &args) {
return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help");
}
static const int screenWidth = 79;
static const int optIndent = 2;
static const int msgIndent = 7;
std::string HelpMessageGroup(const std::string &message) {
return std::string(message) + std::string("\n\n");
}
std::string HelpMessageOpt(const std::string &option,
const std::string &message) {
return std::string(optIndent, ' ') + std::string(option) +
std::string("\n") + std::string(msgIndent, ' ') +
FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
std::string("\n\n");
}
static std::string FormatException(const std::exception *pex,
const char *pszThread) {
#ifdef WIN32
char pszModule[MAX_PATH] = "";
GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule));
#else
const char *pszModule = "bitcoin";
#endif
if (pex) {
return strprintf("EXCEPTION: %s \n%s \n%s in %s \n",
typeid(*pex).name(), pex->what(), pszModule,
pszThread);
} else {
return strprintf("UNKNOWN EXCEPTION \n%s in %s \n",
pszModule, pszThread);
}
}
void PrintExceptionContinue(const std::exception *pex, const char *pszThread) {
std::string message = FormatException(pex, pszThread);
LogPrintf("\n\n************************\n%s\n", message);
fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
}
fs::path GetDefaultDataDir() {
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin
// Mac: ~/Library/Application Support/Bitcoin
// Unix: ~/.bitcoin
#ifdef WIN32
// Windows
return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin";
#else
fs::path pathRet;
char *pszHome = getenv("HOME");
if (pszHome == nullptr || strlen(pszHome) == 0) {
pathRet = fs::path("/");
} else {
pathRet = fs::path(pszHome);
}
#ifdef MAC_OSX
// Mac
return pathRet / "Library/Application Support/Bitcoin";
#else
// Unix
return pathRet / ".bitcoin";
#endif
#endif
}
static fs::path g_blocks_path_cache_net_specific;
static fs::path pathCached;
static fs::path pathCachedNetSpecific;
static CCriticalSection csPathCached;
const fs::path &GetBlocksDir() {
LOCK(csPathCached);
fs::path &path = g_blocks_path_cache_net_specific;
// This can be called during exceptions by LogPrintf(), so we cache the
// value so we don't have to do memory allocations after that.
if (!path.empty()) {
return path;
}
if (gArgs.IsArgSet("-blocksdir")) {
path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
if (!fs::is_directory(path)) {
path = "";
return path;
}
} else {
path = GetDataDir(false);
}
path /= BaseParams().DataDir();
path /= "blocks";
fs::create_directories(path);
return path;
}
const fs::path &GetDataDir(bool fNetSpecific) {
LOCK(csPathCached);
fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
// This can be called during exceptions by LogPrintf(), so we cache the
// value so we don't have to do memory allocations after that.
if (!path.empty()) {
return path;
}
if (gArgs.IsArgSet("-datadir")) {
path = fs::system_complete(gArgs.GetArg("-datadir", ""));
if (!fs::is_directory(path)) {
path = "";
return path;
}
} else {
path = GetDefaultDataDir();
}
if (fNetSpecific) {
path /= BaseParams().DataDir();
}
if (fs::create_directories(path)) {
// This is the first run, create wallets subdirectory too
//
// TODO: this is an ugly way to create the wallets/ directory and
// really shouldn't be done here. Once this is fixed, please
// also remove the corresponding line in bitcoind.cpp AppInit.
// See more info at:
// https://reviews.bitcoinabc.org/D3312
fs::create_directories(path / "wallets");
}
return path;
}
void ClearDatadirCache() {
LOCK(csPathCached);
pathCached = fs::path();
pathCachedNetSpecific = fs::path();
g_blocks_path_cache_net_specific = fs::path();
}
fs::path GetConfigFile(const std::string &confPath) {
return AbsPathForConfigVal(fs::path(confPath), false);
}
static std::string TrimString(const std::string &str,
const std::string &pattern) {
std::string::size_type front = str.find_first_not_of(pattern);
if (front == std::string::npos) {
return std::string();
}
std::string::size_type end = str.find_last_not_of(pattern);
return str.substr(front, end - front + 1);
}
static std::vector<std::pair<std::string, std::string>>
GetConfigOptions(std::istream &stream) {
std::vector<std::pair<std::string, std::string>> options;
std::string str, prefix;
std::string::size_type pos;
while (std::getline(stream, str)) {
if ((pos = str.find('#')) != std::string::npos) {
str = str.substr(0, pos);
}
const static std::string pattern = " \t\r\n";
str = TrimString(str, pattern);
if (!str.empty()) {
if (*str.begin() == '[' && *str.rbegin() == ']') {
prefix = str.substr(1, str.size() - 2) + '.';
} else if ((pos = str.find('=')) != std::string::npos) {
std::string name =
prefix + TrimString(str.substr(0, pos), pattern);
std::string value = TrimString(str.substr(pos + 1), pattern);
options.emplace_back(name, value);
}
}
}
return options;
}
bool ArgsManager::ReadConfigStream(std::istream &stream, std::string &error,
bool ignore_invalid_keys) {
LOCK(cs_args);
for (const std::pair<std::string, std::string> &option :
GetConfigOptions(stream)) {
std::string strKey = std::string("-") + option.first;
std::string strValue = option.second;
if (InterpretNegatedOption(strKey, strValue)) {
m_config_args[strKey].clear();
} else {
m_config_args[strKey].push_back(strValue);
}
// Check that the arg is known
if (!IsArgKnown(strKey) && !ignore_invalid_keys) {
error = strprintf("Invalid configuration value %s",
option.first.c_str());
return false;
}
}
return true;
}
bool ArgsManager::ReadConfigFiles(std::string &error,
bool ignore_invalid_keys) {
{
LOCK(cs_args);
m_config_args.clear();
}
const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
fs::ifstream stream(GetConfigFile(confPath));
// ok to not have a config file
if (stream.good()) {
if (!ReadConfigStream(stream, error, ignore_invalid_keys)) {
return false;
}
// if there is an -includeconf in the override args, but it is empty,
// that means the user passed '-noincludeconf' on the command line, in
// which case we should not include anything
bool emptyIncludeConf;
{
LOCK(cs_args);
emptyIncludeConf = m_override_args.count("-includeconf") == 0;
}
if (emptyIncludeConf) {
std::string chain_id = GetChainName();
std::vector<std::string> includeconf(GetArgs("-includeconf"));
{
// We haven't set m_network yet (that happens in
// SelectParams()), so manually check for network.includeconf
// args.
std::vector<std::string> includeconf_net(
GetArgs(std::string("-") + chain_id + ".includeconf"));
includeconf.insert(includeconf.end(), includeconf_net.begin(),
includeconf_net.end());
}
// Remove -includeconf from configuration, so we can warn about
// recursion later
{
LOCK(cs_args);
m_config_args.erase("-includeconf");
m_config_args.erase(std::string("-") + chain_id +
".includeconf");
}
for (const std::string &to_include : includeconf) {
fs::ifstream include_config(GetConfigFile(to_include));
if (include_config.good()) {
if (!ReadConfigStream(include_config, error,
ignore_invalid_keys)) {
return false;
}
LogPrintf("Included configuration file %s\n",
to_include.c_str());
} else {
error =
"Failed to include configuration file " + to_include;
return false;
}
}
// Warn about recursive -includeconf
includeconf = GetArgs("-includeconf");
{
std::vector<std::string> includeconf_net(
GetArgs(std::string("-") + chain_id + ".includeconf"));
includeconf.insert(includeconf.end(), includeconf_net.begin(),
includeconf_net.end());
std::string chain_id_final = GetChainName();
if (chain_id_final != chain_id) {
// Also warn about recursive includeconf for the chain that
// was specified in one of the includeconfs
includeconf_net = GetArgs(std::string("-") +
chain_id_final + ".includeconf");
includeconf.insert(includeconf.end(),
includeconf_net.begin(),
includeconf_net.end());
}
}
for (const std::string &to_include : includeconf) {
fprintf(stderr,
"warning: -includeconf cannot be used from included "
"files; ignoring -includeconf=%s\n",
to_include.c_str());
}
}
}
// If datadir is changed in .conf file:
ClearDatadirCache();
if (!fs::is_directory(GetDataDir(false))) {
error = strprintf("specified data directory \"%s\" does not exist.",
gArgs.GetArg("-datadir", "").c_str());
return false;
}
return true;
}
std::string ArgsManager::GetChainName() const {
LOCK(cs_args);
bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest");
bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet");
if (fTestNet && fRegTest) {
throw std::runtime_error(
"Invalid combination of -regtest and -testnet.");
}
if (fRegTest) {
return CBaseChainParams::REGTEST;
}
if (fTestNet) {
return CBaseChainParams::TESTNET;
}
return CBaseChainParams::MAIN;
}
#ifndef WIN32
fs::path GetPidFile() {
return AbsPathForConfigVal(
fs::path(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)));
}
void CreatePidFile(const fs::path &path, pid_t pid) {
FILE *file = fsbridge::fopen(path, "w");
if (file) {
fprintf(file, "%d\n", pid);
fclose(file);
}
}
#endif
bool RenameOver(fs::path src, fs::path dest) {
#ifdef WIN32
return MoveFileExA(src.string().c_str(), dest.string().c_str(),
MOVEFILE_REPLACE_EXISTING) != 0;
#else
int rc = std::rename(src.string().c_str(), dest.string().c_str());
return (rc == 0);
#endif /* WIN32 */
}
/**
* Ignores exceptions thrown by Boost's create_directories if the requested
* directory exists. Specifically handles case where path p exists, but it
* wasn't possible for the user to write to the parent directory.
*/
bool TryCreateDirectories(const fs::path &p) {
try {
return fs::create_directories(p);
} catch (const fs::filesystem_error &) {
if (!fs::exists(p) || !fs::is_directory(p)) {
throw;
}
}
// create_directory didn't create the directory, it had to have existed
// already.
return false;
}
bool FileCommit(FILE *file) {
// harmless if redundantly called
if (fflush(file) != 0) {
LogPrintf("%s: fflush failed: %d\n", __func__, errno);
return false;
}
#ifdef WIN32
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
if (FlushFileBuffers(hFile) == 0) {
LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__,
GetLastError());
return false;
}
#else
#if defined(__linux__) || defined(__NetBSD__)
// Ignore EINVAL for filesystems that don't support sync
if (fdatasync(fileno(file)) != 0 && errno != EINVAL) {
LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
return false;
}
#elif defined(MAC_OSX) && defined(F_FULLFSYNC)
// Manpage says "value other than -1" is returned on success
if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) {
LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
return false;
}
#else
if (fsync(fileno(file)) != 0 && errno != EINVAL) {
LogPrintf("%s: fsync failed: %d\n", __func__, errno);
return false;
}
#endif
#endif
return true;
}
bool TruncateFile(FILE *file, unsigned int length) {
#if defined(WIN32)
return _chsize(_fileno(file), length) == 0;
#else
return ftruncate(fileno(file), length) == 0;
#endif
}
/**
* This function tries to raise the file descriptor limit to the requested
* number. It returns the actual file descriptor limit (which may be more or
* less than nMinFD)
*/
int RaiseFileDescriptorLimit(int nMinFD) {
#if defined(WIN32)
return 2048;
#else
struct rlimit limitFD;
if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
if (limitFD.rlim_cur < (rlim_t)nMinFD) {
limitFD.rlim_cur = nMinFD;
if (limitFD.rlim_cur > limitFD.rlim_max) {
limitFD.rlim_cur = limitFD.rlim_max;
}
setrlimit(RLIMIT_NOFILE, &limitFD);
getrlimit(RLIMIT_NOFILE, &limitFD);
}
return limitFD.rlim_cur;
}
// getrlimit failed, assume it's fine.
return nMinFD;
#endif
}
/**
* This function tries to make a particular range of a file allocated
* (corresponding to disk space) it is advisory, and the range specified in the
* arguments will never contain live data.
*/
void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
#if defined(WIN32)
// Windows-specific version.
HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
LARGE_INTEGER nFileSize;
int64_t nEndPos = (int64_t)offset + length;
nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
nFileSize.u.HighPart = nEndPos >> 32;
SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
SetEndOfFile(hFile);
#elif defined(MAC_OSX)
// OSX specific version.
fstore_t fst;
fst.fst_flags = F_ALLOCATECONTIG;
fst.fst_posmode = F_PEOFPOSMODE;
fst.fst_offset = 0;
fst.fst_length = (off_t)offset + length;
fst.fst_bytesalloc = 0;
if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
fst.fst_flags = F_ALLOCATEALL;
fcntl(fileno(file), F_PREALLOCATE, &fst);
}
ftruncate(fileno(file), fst.fst_length);
#elif defined(__linux__)
// Version using posix_fallocate.
off_t nEndPos = (off_t)offset + length;
posix_fallocate(fileno(file), 0, nEndPos);
#else
// Fallback version
// TODO: just write one byte per block
static const char buf[65536] = {};
if (fseek(file, offset, SEEK_SET)) {
return;
}
while (length > 0) {
unsigned int now = 65536;
if (length < now) {
now = length;
}
// Allowed to fail; this function is advisory anyway.
fwrite(buf, 1, now, file);
length -= now;
}
#endif
}
#ifdef WIN32
fs::path GetSpecialFolderPath(int nFolder, bool fCreate) {
char pszPath[MAX_PATH] = "";
if (SHGetSpecialFolderPathA(nullptr, pszPath, nFolder, fCreate)) {
return fs::path(pszPath);
}
LogPrintf(
"SHGetSpecialFolderPathA() failed, could not obtain requested path.\n");
return fs::path("");
}
#endif
void runCommand(const std::string &strCommand) {
if (strCommand.empty()) {
return;
}
#ifndef WIN32
int nErr = ::system(strCommand.c_str());
#else
int nErr = ::_wsystem(
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>()
.from_bytes(strCommand)
.c_str());
#endif
if (nErr) {
LogPrintf("runCommand error: system(%s) returned %d\n", strCommand,
nErr);
}
}
void RenameThread(const char *name) {
#if defined(PR_SET_NAME)
// Only the first 15 characters are used (16 - NUL terminator)
::prctl(PR_SET_NAME, name, 0, 0, 0);
#elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
pthread_set_name_np(pthread_self(), name);
#elif defined(MAC_OSX)
pthread_setname_np(name);
#else
// Prevent warnings for unused parameters...
(void)name;
#endif
}
void SetupEnvironment() {
#ifdef HAVE_MALLOPT_ARENA_MAX
// glibc-specific: On 32-bit systems set the number of arenas to 1. By
// default, since glibc 2.10, the C library will create up to two heap
// arenas per core. This is known to cause excessive virtual address space
// usage in our usage. Work around it by setting the maximum number of
// arenas to 1.
if (sizeof(void *) == 4) {
mallopt(M_ARENA_MAX, 1);
}
#endif
// On most POSIX systems (e.g. Linux, but not BSD) the environment's locale may
// be invalid, in which case the "C" locale is used as fallback.
#if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && \
!defined(__OpenBSD__)
try {
// Raises a runtime error if current locale is invalid.
std::locale("");
} catch (const std::runtime_error &) {
setenv("LC_ALL", "C", 1);
}
#elif defined(WIN32)
// Set the default input/output charset is utf-8
SetConsoleCP(CP_UTF8);
SetConsoleOutputCP(CP_UTF8);
#endif
// The path locale is lazy initialized and to avoid deinitialization errors
// in multithreading environments, it is set explicitly by the main thread.
// A dummy locale is used to extract the internal default locale, used by
// fs::path, which is then used to explicitly imbue the path.
std::locale loc = fs::path::imbue(std::locale::classic());
#ifndef WIN32
fs::path::imbue(loc);
#else
fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
#endif
}
bool SetupNetworking() {
#ifdef WIN32
// Initialize Windows Sockets.
WSADATA wsadata;
int ret = WSAStartup(MAKEWORD(2, 2), &wsadata);
if (ret != NO_ERROR || LOBYTE(wsadata.wVersion) != 2 ||
HIBYTE(wsadata.wVersion) != 2) {
return false;
}
#endif
return true;
}
int GetNumCores() {
return boost::thread::physical_concurrency();
}
std::string CopyrightHolders(const std::string &strPrefix) {
return strPrefix +
strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION));
}
// Obtain the application startup time (used for uptime calculation)
int64_t GetStartupTime() {
return nStartupTime;
}
fs::path AbsPathForConfigVal(const fs::path &path, bool net_specific) {
return fs::absolute(path, GetDataDir(net_specific));
}
int ScheduleBatchPriority() {
#ifdef SCHED_BATCH
const static sched_param param{};
if (int ret = pthread_setschedparam(pthread_self(), SCHED_BATCH, &param)) {
LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(errno));
return ret;
}
return 0;
#else
return 1;
#endif
}
namespace util {
#ifdef WIN32
WinCmdLineArgs::WinCmdLineArgs() {
wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
argv = new char *[argc];
args.resize(argc);
for (int i = 0; i < argc; i++) {
args[i] = utf8_cvt.to_bytes(wargv[i]);
argv[i] = &*args[i].begin();
}
LocalFree(wargv);
}
WinCmdLineArgs::~WinCmdLineArgs() {
delete[] argv;
}
std::pair<int, char **> WinCmdLineArgs::get() {
return std::make_pair(argc, argv);
}
#endif
} // namespace util
diff --git a/test/lint/lint-boost-dependencies.sh b/test/lint/lint-boost-dependencies.sh
index 7b72cbed7..be81394ce 100755
--- a/test/lint/lint-boost-dependencies.sh
+++ b/test/lint/lint-boost-dependencies.sh
@@ -1,73 +1,72 @@
#!/usr/bin/env bash
#
# Copyright (c) 2018 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# Guard against accidental introduction of new Boost dependencies.
export LC_ALL=C
EXPECTED_BOOST_INCLUDES=(
boost/algorithm/string.hpp
boost/algorithm/string/classification.hpp
boost/algorithm/string/replace.hpp
boost/algorithm/string/split.hpp
boost/assign/std/vector.hpp
boost/chrono/chrono.hpp
boost/date_time/posix_time/posix_time.hpp
boost/filesystem.hpp
boost/filesystem/fstream.hpp
- boost/interprocess/sync/file_lock.hpp
boost/lexical_cast.hpp
boost/multi_index/composite_key.hpp
boost/multi_index/hashed_index.hpp
boost/multi_index/member.hpp
boost/multi_index/ordered_index.hpp
boost/multi_index/sequenced_index.hpp
boost/multi_index_container.hpp
boost/noncopyable.hpp
boost/optional.hpp
boost/preprocessor/cat.hpp
boost/preprocessor/stringize.hpp
boost/range/iterator.hpp
boost/range/adaptor/sliced.hpp
boost/scoped_array.hpp
boost/signals2/connection.hpp
boost/signals2/last_value.hpp
boost/signals2/signal.hpp
boost/test/unit_test.hpp
boost/thread.hpp
boost/thread/condition_variable.hpp
boost/thread/locks.hpp
boost/thread/mutex.hpp
boost/thread/shared_mutex.hpp
boost/thread/thread.hpp
boost/variant.hpp
boost/variant/apply_visitor.hpp
boost/variant/static_visitor.hpp
)
for BOOST_INCLUDE in $(git grep '^#include <boost/' -- "*.cpp" "*.h" | cut -f2 -d: | cut -f2 -d'<' | cut -f1 -d'>' | sort -u); do
IS_EXPECTED_INCLUDE=0
for EXPECTED_BOOST_INCLUDE in "${EXPECTED_BOOST_INCLUDES[@]}"; do
if [[ "${BOOST_INCLUDE}" == "${EXPECTED_BOOST_INCLUDE}" ]]; then
IS_EXPECTED_INCLUDE=1
break
fi
done
if [[ ${IS_EXPECTED_INCLUDE} == 0 ]]; then
echo "A new Boost dependency in the form of \"${BOOST_INCLUDE}\" appears to have been introduced:"
git grep "${BOOST_INCLUDE}" -- "*.cpp" "*.h"
echo
fi
done
for EXPECTED_BOOST_INCLUDE in "${EXPECTED_BOOST_INCLUDES[@]}"; do
if ! git grep -q "^#include <${EXPECTED_BOOST_INCLUDE}>" -- "*.cpp" "*.h"; then
echo "Good job! The Boost dependency \"${EXPECTED_BOOST_INCLUDE}\" is no longer used."
echo "Please remove it from EXPECTED_BOOST_INCLUDES in $0"
echo "to make sure this dependency is not accidentally reintroduced."
echo
fi
done

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 23:34 (1 d, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5860387
Default Alt Text
(151 KB)

Event Timeline