Page MenuHomePhabricator

No OneTemporary

diff --git a/src/bench/bench.h b/src/bench/bench.h
index acee8014b8..66cc7f2e83 100644
--- a/src/bench/bench.h
+++ b/src/bench/bench.h
@@ -1,82 +1,83 @@
// Copyright (c) 2015-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.
#ifndef BITCOIN_BENCH_BENCH_H
#define BITCOIN_BENCH_BENCH_H
+#include <functional>
+#include <limits>
#include <map>
#include <string>
-#include <boost/function.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
// Simple micro-benchmarking framework; API mostly matches a subset of the
// Google Benchmark framework (see https://github.com/google/benchmark). Wny not
// use the Google Benchmark framework? Because adding Yet Another Dependency
// (that uses cmake as its build system and has lots of features we don't need)
// isn't worth it.
/*
* Usage:
static void CODE_TO_TIME(benchmark::State& state)
{
... do any setup needed...
while (state.KeepRunning()) {
... do stuff you want to time...
}
... do any cleanup needed...
}
BENCHMARK(CODE_TO_TIME);
*/
namespace benchmark {
class State {
std::string name;
double maxElapsed;
double beginTime;
double lastTime, minTime, maxTime, countMaskInv;
uint64_t count;
uint64_t countMask;
uint64_t beginCycles;
uint64_t lastCycles;
uint64_t minCycles;
uint64_t maxCycles;
public:
State(std::string _name, double _maxElapsed)
: name(_name), maxElapsed(_maxElapsed), count(0) {
minTime = std::numeric_limits<double>::max();
maxTime = std::numeric_limits<double>::min();
minCycles = std::numeric_limits<uint64_t>::max();
maxCycles = std::numeric_limits<uint64_t>::min();
countMask = 1;
countMaskInv = 1. / (countMask + 1);
}
bool KeepRunning();
};
-typedef boost::function<void(State &)> BenchFunction;
+typedef std::function<void(State &)> BenchFunction;
class BenchRunner {
typedef std::map<std::string, BenchFunction> BenchmarkMap;
static BenchmarkMap &benchmarks();
public:
BenchRunner(std::string name, BenchFunction func);
static void RunAll(double elapsedTimeForOne = 1.0);
};
}
// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo);
#define BENCHMARK(n) \
benchmark::BenchRunner BOOST_PP_CAT(bench_, BOOST_PP_CAT(__LINE__, n))( \
BOOST_PP_STRINGIZE(n), n);
#endif // BITCOIN_BENCH_BENCH_H
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index d9ed62abbb..cfe6c204b1 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -1,267 +1,267 @@
// Copyright (c) 2015-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.
#include "httprpc.h"
#include "base58.h"
#include "chainparams.h"
#include "config.h"
#include "crypto/hmac_sha256.h"
#include "httpserver.h"
#include "random.h"
#include "rpc/protocol.h"
#include "rpc/server.h"
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include "utilstrencodings.h"
#include <cstdio>
#include <boost/algorithm/string.hpp> // boost::trim
/** WWW-Authenticate to present with 401 Unauthorized response */
static const char *WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
/** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
* re-lock the wallet.
*/
class HTTPRPCTimer : public RPCTimerBase {
public:
- HTTPRPCTimer(struct event_base *eventBase,
- boost::function<void(void)> &func, int64_t millis)
+ HTTPRPCTimer(struct event_base *eventBase, std::function<void(void)> &func,
+ int64_t millis)
: ev(eventBase, false, func) {
struct timeval tv;
tv.tv_sec = millis / 1000;
tv.tv_usec = (millis % 1000) * 1000;
ev.trigger(&tv);
}
private:
HTTPEvent ev;
};
class HTTPRPCTimerInterface : public RPCTimerInterface {
public:
HTTPRPCTimerInterface(struct event_base *_base) : base(_base) {}
const char *Name() { return "HTTP"; }
- RPCTimerBase *NewTimer(boost::function<void(void)> &func, int64_t millis) {
+ RPCTimerBase *NewTimer(std::function<void(void)> &func, int64_t millis) {
return new HTTPRPCTimer(base, func, millis);
}
private:
struct event_base *base;
};
/* Pre-base64-encoded authentication token */
static std::string strRPCUserColonPass;
/* Stored RPC timer interface (for unregistration) */
static HTTPRPCTimerInterface *httpRPCTimerInterface = 0;
static void JSONErrorReply(HTTPRequest *req, const UniValue &objError,
const UniValue &id) {
// Send error reply from json-rpc error object.
int nStatus = HTTP_INTERNAL_SERVER_ERROR;
int code = find_value(objError, "code").get_int();
if (code == RPC_INVALID_REQUEST)
nStatus = HTTP_BAD_REQUEST;
else if (code == RPC_METHOD_NOT_FOUND)
nStatus = HTTP_NOT_FOUND;
std::string strReply = JSONRPCReply(NullUniValue, objError, id);
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(nStatus, strReply);
}
// This function checks username and password against -rpcauth entries from
// config file.
static bool multiUserAuthorized(std::string strUserPass) {
if (strUserPass.find(":") == std::string::npos) {
return false;
}
std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);
if (mapMultiArgs.count("-rpcauth") > 0) {
// Search for multi-user login/pass "rpcauth" from config
for (const std::string &strRPCAuth : mapMultiArgs.at("-rpcauth")) {
std::vector<std::string> vFields;
boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
if (vFields.size() != 3) {
// Incorrect formatting in config file
continue;
}
std::string strName = vFields[0];
if (!TimingResistantEqual(strName, strUser)) {
continue;
}
std::string strSalt = vFields[1];
std::string strHash = vFields[2];
static const unsigned int KEY_SIZE = 32;
uint8_t out[KEY_SIZE];
CHMAC_SHA256(reinterpret_cast<const uint8_t *>(strSalt.c_str()),
strSalt.size())
.Write(reinterpret_cast<const uint8_t *>(strPass.c_str()),
strPass.size())
.Finalize(out);
std::vector<uint8_t> hexvec(out, out + KEY_SIZE);
std::string strHashFromPass = HexStr(hexvec);
if (TimingResistantEqual(strHashFromPass, strHash)) {
return true;
}
}
}
return false;
}
static bool RPCAuthorized(const std::string &strAuth,
std::string &strAuthUsernameOut) {
// Belt-and-suspenders measure if InitRPCAuthentication was not called.
if (strRPCUserColonPass.empty()) {
return false;
}
if (strAuth.substr(0, 6) != "Basic ") {
return false;
}
std::string strUserPass64 = strAuth.substr(6);
boost::trim(strUserPass64);
std::string strUserPass = DecodeBase64(strUserPass64);
if (strUserPass.find(":") != std::string::npos) {
strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":"));
}
// Check if authorized under single-user field
if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
return true;
}
return multiUserAuthorized(strUserPass);
}
static bool HTTPReq_JSONRPC(Config &config, HTTPRequest *req,
const std::string &) {
// JSONRPC handles only POST
if (req->GetRequestMethod() != HTTPRequest::POST) {
req->WriteReply(HTTP_BAD_METHOD,
"JSONRPC server handles only POST requests");
return false;
}
// Check authorization
std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
if (!authHeader.first) {
req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
req->WriteReply(HTTP_UNAUTHORIZED);
return false;
}
JSONRPCRequest jreq;
if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n",
req->GetPeer().ToString());
/* Deter brute-forcing.
* If this results in a DoS the user really shouldn't have their RPC
* port exposed. */
MilliSleep(250);
req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
req->WriteReply(HTTP_UNAUTHORIZED);
return false;
}
try {
// Parse request
UniValue valRequest;
if (!valRequest.read(req->ReadBody()))
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
// Set the URI
jreq.URI = req->GetURI();
std::string strReply;
// singleton request
if (valRequest.isObject()) {
jreq.parse(valRequest);
UniValue result = tableRPC.execute(config, jreq);
// Send reply
strReply = JSONRPCReply(result, NullUniValue, jreq.id);
// array of requests
} else if (valRequest.isArray()) {
strReply = JSONRPCExecBatch(config, valRequest.get_array());
} else {
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
}
req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strReply);
} catch (const UniValue &objError) {
JSONErrorReply(req, objError, jreq.id);
return false;
} catch (const std::exception &e) {
JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
return false;
}
return true;
}
static bool InitRPCAuthentication() {
if (GetArg("-rpcpassword", "") == "") {
LogPrintf("No rpcpassword set - using random cookie authentication\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
// Same message as AbortNode.
uiInterface.ThreadSafeMessageBox(
_("Error: A fatal internal error occurred, see debug.log for "
"details"),
"", CClientUIInterface::MSG_ERROR);
return false;
}
} else {
LogPrintf("Config options rpcuser and rpcpassword will soon be "
"deprecated. Locally-run instances may remove rpcuser to use "
"cookie-based auth, or may be replaced with rpcauth. Please "
"see share/rpcuser for rpcauth auth generation.\n");
strRPCUserColonPass =
GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", "");
}
return true;
}
bool StartHTTPRPC() {
LogPrint("rpc", "Starting HTTP RPC server\n");
if (!InitRPCAuthentication()) return false;
RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
assert(EventBase());
httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
RPCSetTimerInterface(httpRPCTimerInterface);
return true;
}
void InterruptHTTPRPC() {
LogPrint("rpc", "Interrupting HTTP RPC server\n");
}
void StopHTTPRPC() {
LogPrint("rpc", "Stopping HTTP RPC server\n");
UnregisterHTTPHandler("/", true);
if (httpRPCTimerInterface) {
RPCUnsetTimerInterface(httpRPCTimerInterface);
delete httpRPCTimerInterface;
httpRPCTimerInterface = 0;
}
}
diff --git a/src/init.cpp b/src/init.cpp
index 5dc98d7fe9..75beac2b7e 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,2162 +1,2161 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "init.h"
#include "addrman.h"
#include "amount.h"
#include "chain.h"
#include "chainparams.h"
#include "checkpoints.h"
#include "compat/sanity.h"
#include "config.h"
#include "consensus/validation.h"
#include "httprpc.h"
#include "httpserver.h"
#include "key.h"
#include "miner.h"
#include "net.h"
#include "net_processing.h"
#include "netbase.h"
#include "policy/policy.h"
#include "rpc/register.h"
#include "rpc/server.h"
#include "scheduler.h"
#include "script/sigcache.h"
#include "script/standard.h"
#include "timedata.h"
#include "torcontrol.h"
#include "txdb.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "util.h"
#include "utilmoneystr.h"
#include "validation.h"
#include "validationinterface.h"
#ifdef ENABLE_WALLET
#include "wallet/rpcdump.h"
#include "wallet/wallet.h"
#endif
#include "warnings.h"
#include <cstdint>
#include <cstdio>
#include <memory>
#ifndef WIN32
#include <signal.h>
#endif
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
-#include <boost/function.hpp>
#include <boost/interprocess/sync/file_lock.hpp>
#include <boost/thread.hpp>
#include <openssl/crypto.h>
#if ENABLE_ZMQ
#include "zmq/zmqnotificationinterface.h"
#endif
bool fFeeEstimatesInitialized = false;
static const bool DEFAULT_PROXYRANDOMIZE = true;
static const bool DEFAULT_REST_ENABLE = false;
static const bool DEFAULT_DISABLE_SAFEMODE = false;
static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false;
std::unique_ptr<CConnman> g_connman;
std::unique_ptr<PeerLogicValidation> peerLogic;
#if ENABLE_ZMQ
static CZMQNotificationInterface *pzmqNotificationInterface = nullptr;
#endif
#ifdef WIN32
// Win32 LevelDB doesn't use filedescriptors, and the ones used for accessing
// block files don't count towards the fd_set size limit anyway.
#define MIN_CORE_FILEDESCRIPTORS 0
#else
#define MIN_CORE_FILEDESCRIPTORS 150
#endif
/** Used to pass flags to the Bind() function */
enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1),
BF_WHITELIST = (1U << 2),
};
static const char *FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
//////////////////////////////////////////////////////////////////////////////
//
// Shutdown
//
//
// Thread management and startup/shutdown:
//
// The network-processing threads are all part of a thread group created by
// AppInit() or the Qt main() function.
//
// A clean exit happens when StartShutdown() or the SIGTERM signal handler sets
// fRequestShutdown, which triggers the DetectShutdownThread(), which interrupts
// the main thread group. DetectShutdownThread() then exits, which causes
// AppInit() to continue (it .joins the shutdown thread). Shutdown() is then
// called to clean up database connections, and stop other threads that should
// only be stopped after the main network-processing threads have exited.
//
// Note that if running -daemon the parent process returns from AppInit2 before
// adding any threads to the threadGroup, so .join_all() returns immediately and
// the parent exits from main().
//
// Shutdown for Qt is very similar, only it uses a QTimer to detect
// fRequestShutdown getting set, and then does the normal Qt shutdown thing.
//
std::atomic<bool> fRequestShutdown(false);
std::atomic<bool> fDumpMempoolLater(false);
void StartShutdown() {
fRequestShutdown = true;
}
bool ShutdownRequested() {
return fRequestShutdown;
}
/**
* This is a minimally invasive approach to shutdown on LevelDB read errors from
* the chainstate, while keeping user interface out of the common library, which
* is shared between bitcoind, and bitcoin-qt and non-server tools.
*/
class CCoinsViewErrorCatcher : public CCoinsViewBacked {
public:
CCoinsViewErrorCatcher(CCoinsView *view) : CCoinsViewBacked(view) {}
bool GetCoins(const uint256 &txid, CCoins &coins) const {
try {
return CCoinsViewBacked::GetCoins(txid, coins);
} 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 CCoinsViewDB *pcoinsdbview = nullptr;
static CCoinsViewErrorCatcher *pcoinscatcher = nullptr;
static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle;
void Interrupt(boost::thread_group &threadGroup) {
InterruptHTTPServer();
InterruptHTTPRPC();
InterruptRPC();
InterruptREST();
InterruptTorControl();
if (g_connman) g_connman->Interrupt();
threadGroup.interrupt_all();
}
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 AppInit2() 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");
mempool.AddTransactionsUpdated(1);
StopHTTPRPC();
StopREST();
StopRPC();
StopHTTPServer();
#ifdef ENABLE_WALLET
if (pwalletMain) pwalletMain->Flush(false);
#endif
MapPort(false);
UnregisterValidationInterface(peerLogic.get());
peerLogic.reset();
g_connman.reset();
StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
if (fDumpMempoolLater) DumpMempool();
if (fFeeEstimatesInitialized) {
boost::filesystem::path est_path =
GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_fileout(fopen(est_path.string().c_str(), "wb"), SER_DISK,
CLIENT_VERSION);
if (!est_fileout.IsNull())
mempool.WriteFeeEstimates(est_fileout);
else
LogPrintf("%s: Failed to write fee estimates to %s\n", __func__,
est_path.string());
fFeeEstimatesInitialized = false;
}
{
LOCK(cs_main);
if (pcoinsTip != nullptr) {
FlushStateToDisk();
}
delete pcoinsTip;
pcoinsTip = nullptr;
delete pcoinscatcher;
pcoinscatcher = nullptr;
delete pcoinsdbview;
pcoinsdbview = nullptr;
delete pblocktree;
pblocktree = nullptr;
}
#ifdef ENABLE_WALLET
if (pwalletMain) pwalletMain->Flush(true);
#endif
#if ENABLE_ZMQ
if (pzmqNotificationInterface) {
UnregisterValidationInterface(pzmqNotificationInterface);
delete pzmqNotificationInterface;
pzmqNotificationInterface = nullptr;
}
#endif
#ifndef WIN32
try {
boost::filesystem::remove(GetPidFile());
} catch (const boost::filesystem::filesystem_error &e) {
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
}
#endif
UnregisterAllValidationInterfaces();
#ifdef ENABLE_WALLET
delete pwalletMain;
pwalletMain = nullptr;
#endif
globalVerifyHandle.reset();
ECC_Stop();
LogPrintf("%s: done\n", __func__);
}
/**
* Signal handlers are very limited in what they are allowed to do, so:
*/
void HandleSIGTERM(int) {
fRequestShutdown = true;
}
void HandleSIGHUP(int) {
fReopenDebugLog = true;
}
static bool Bind(CConnman &connman, const CService &addr, unsigned int flags) {
if (!(flags & BF_EXPLICIT) && IsLimited(addr)) return false;
std::string strError;
if (!connman.BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) {
if (flags & BF_REPORT_ERROR) return InitError(strError);
return false;
}
return true;
}
void OnRPCStarted() {
uiInterface.NotifyBlockTip.connect(&RPCNotifyBlockChange);
}
void OnRPCStopped() {
uiInterface.NotifyBlockTip.disconnect(&RPCNotifyBlockChange);
RPCNotifyBlockChange(false, nullptr);
cvBlockChange.notify_all();
LogPrint("rpc", "RPC stopped.\n");
}
void OnRPCPreCommand(const CRPCCommand &cmd) {
// Observe safe mode.
std::string strWarning = GetWarnings("rpc");
if (strWarning != "" &&
!GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) &&
!cmd.okSafeMode)
throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE,
std::string("Safe mode: ") + strWarning);
}
std::string HelpMessage(HelpMessageMode mode) {
const bool showDebug = GetBoolArg("-help-debug", false);
// 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.
std::string strUsage = HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("Print this help message and exit"));
strUsage += HelpMessageOpt("-version", _("Print version and exit"));
strUsage += HelpMessageOpt(
"-alertnotify=<cmd>",
_("Execute command when a relevant alert is received or we see a "
"really long fork (%s in cmd is replaced by message)"));
strUsage += HelpMessageOpt("-blocknotify=<cmd>",
_("Execute command when the best block changes "
"(%s in cmd is replaced by block hash)"));
if (showDebug)
strUsage += HelpMessageOpt(
"-blocksonly",
strprintf(
_("Whether to operate in a blocks only mode (default: %u)"),
DEFAULT_BLOCKSONLY));
strUsage += HelpMessageOpt(
"-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)"),
Params(CBaseChainParams::MAIN)
.GetConsensus()
.defaultAssumeValid.GetHex(),
Params(CBaseChainParams::TESTNET)
.GetConsensus()
.defaultAssumeValid.GetHex()));
strUsage += HelpMessageOpt(
"-conf=<file>", strprintf(_("Specify configuration file (default: %s)"),
BITCOIN_CONF_FILENAME));
if (mode == HMM_BITCOIND) {
#if HAVE_DECL_DAEMON
strUsage += HelpMessageOpt(
"-daemon",
_("Run in the background as a daemon and accept commands"));
#endif
}
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
strUsage += HelpMessageOpt(
"-dbcache=<n>",
strprintf(
_("Set database cache size in megabytes (%d to %d, default: %d)"),
nMinDbCache, nMaxDbCache, nDefaultDbCache));
if (showDebug)
strUsage += HelpMessageOpt(
"-feefilter", strprintf("Tell other nodes to filter invs to us by "
"our mempool min fee (default: %u)",
DEFAULT_FEEFILTER));
strUsage += HelpMessageOpt(
"-loadblock=<file>",
_("Imports blocks from external blk000??.dat file on startup"));
strUsage += HelpMessageOpt(
"-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable "
"transactions in memory (default: %u)"),
DEFAULT_MAX_ORPHAN_TRANSACTIONS));
strUsage += HelpMessageOpt("-maxmempool=<n>",
strprintf(_("Keep the transaction memory pool "
"below <n> megabytes (default: %u)"),
DEFAULT_MAX_MEMPOOL_SIZE));
strUsage +=
HelpMessageOpt("-mempoolexpiry=<n>",
strprintf(_("Do not keep transactions in the mempool "
"longer than <n> hours (default: %u)"),
DEFAULT_MEMPOOL_EXPIRY));
strUsage += HelpMessageOpt(
"-blockreconstructionextratxn=<n>",
strprintf(_("Extra transactions to keep in memory for compact block "
"reconstructions (default: %u)"),
DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
strUsage += HelpMessageOpt(
"-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));
#ifndef WIN32
strUsage += HelpMessageOpt(
"-pid=<file>",
strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME));
#endif
strUsage += HelpMessageOpt(
"-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));
strUsage += HelpMessageOpt(
"-reindex-chainstate",
_("Rebuild chain state from the currently indexed blocks"));
strUsage +=
HelpMessageOpt("-reindex", _("Rebuild chain state and block index from "
"the blk*.dat files on disk"));
#ifndef WIN32
strUsage += HelpMessageOpt(
"-sysperms",
_("Create new files with system default permissions, instead of umask "
"077 (only effective with disabled wallet functionality)"));
#endif
strUsage += HelpMessageOpt(
"-txindex", strprintf(_("Maintain a full transaction index, used by "
"the getrawtransaction rpc call (default: %u)"),
DEFAULT_TXINDEX));
strUsage += HelpMessageGroup(_("Connection options:"));
strUsage += HelpMessageOpt(
"-addnode=<ip>",
_("Add a node to connect to and attempt to keep the connection open"));
strUsage += HelpMessageOpt(
"-banscore=<n>",
strprintf(
_("Threshold for disconnecting misbehaving peers (default: %u)"),
DEFAULT_BANSCORE_THRESHOLD));
strUsage += HelpMessageOpt(
"-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving "
"peers from reconnecting (default: %u)"),
DEFAULT_MISBEHAVING_BANTIME));
strUsage += HelpMessageOpt("-bind=<addr>",
_("Bind to given address and always listen on "
"it. Use [host]:port notation for IPv6"));
strUsage +=
HelpMessageOpt("-connect=<ip>",
_("Connect only to the specified node(s); -noconnect or "
"-connect=0 alone to disable automatic connections"));
strUsage += HelpMessageOpt("-discover",
_("Discover own IP addresses (default: 1 when "
"listening and no -externalip or -proxy)"));
strUsage += HelpMessageOpt(
"-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") +
" " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP));
strUsage += HelpMessageOpt(
"-dnsseed", _("Query for peer addresses via DNS lookup, if low on "
"addresses (default: 1 unless -connect/-noconnect)"));
strUsage += HelpMessageOpt("-externalip=<ip>",
_("Specify your own public address"));
strUsage += HelpMessageOpt(
"-forcednsseed",
strprintf(
_("Always query for peer addresses via DNS lookup (default: %u)"),
DEFAULT_FORCEDNSSEED));
strUsage +=
HelpMessageOpt("-listen", _("Accept connections from outside (default: "
"1 if no -proxy or -connect/-noconnect)"));
strUsage += HelpMessageOpt(
"-listenonion",
strprintf(_("Automatically create Tor hidden service (default: %d)"),
DEFAULT_LISTEN_ONION));
strUsage += HelpMessageOpt(
"-maxconnections=<n>",
strprintf(_("Maintain at most <n> connections to peers (default: %u)"),
DEFAULT_MAX_PEER_CONNECTIONS));
strUsage +=
HelpMessageOpt("-maxreceivebuffer=<n>",
strprintf(_("Maximum per-connection receive buffer, "
"<n>*1000 bytes (default: %u)"),
DEFAULT_MAXRECEIVEBUFFER));
strUsage += HelpMessageOpt(
"-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, "
"<n>*1000 bytes (default: %u)"),
DEFAULT_MAXSENDBUFFER));
strUsage += HelpMessageOpt(
"-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));
strUsage +=
HelpMessageOpt("-onion=<ip:port>",
strprintf(_("Use separate SOCKS5 proxy to reach peers "
"via Tor hidden services (default: %s)"),
"-proxy"));
strUsage += HelpMessageOpt(
"-onlynet=<net>",
_("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"));
strUsage +=
HelpMessageOpt("-permitbaremultisig",
strprintf(_("Relay non-P2SH multisig (default: %u)"),
DEFAULT_PERMIT_BAREMULTISIG));
strUsage += HelpMessageOpt(
"-peerbloomfilters",
strprintf(_("Support filtering of blocks and transaction with bloom "
"filters (default: %u)"),
DEFAULT_PEERBLOOMFILTERS));
strUsage += HelpMessageOpt(
"-port=<port>",
strprintf(
_("Listen for connections on <port> (default: %u or testnet: %u)"),
Params(CBaseChainParams::MAIN).GetDefaultPort(),
Params(CBaseChainParams::TESTNET).GetDefaultPort()));
strUsage +=
HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"));
strUsage += HelpMessageOpt(
"-proxyrandomize",
strprintf(_("Randomize credentials for every proxy connection. This "
"enables Tor stream isolation (default: %u)"),
DEFAULT_PROXYRANDOMIZE));
strUsage += HelpMessageOpt(
"-seednode=<ip>",
_("Connect to a node to retrieve peer addresses, and disconnect"));
strUsage += HelpMessageOpt(
"-timeout=<n>", strprintf(_("Specify connection timeout in "
"milliseconds (minimum: 1, default: %d)"),
DEFAULT_CONNECT_TIMEOUT));
strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>",
strprintf(_("Tor control port to use if onion "
"listening enabled (default: %s)"),
DEFAULT_TOR_CONTROL));
strUsage += HelpMessageOpt("-torpassword=<pass>",
_("Tor control port password (default: empty)"));
#ifdef USE_UPNP
#if USE_UPNP
strUsage +=
HelpMessageOpt("-upnp", _("Use UPnP to map the listening port "
"(default: 1 when listening and no -proxy)"));
#else
strUsage += HelpMessageOpt(
"-upnp",
strprintf(_("Use UPnP to map the listening port (default: %u)"), 0));
#endif
#endif
strUsage +=
HelpMessageOpt("-whitebind=<addr>",
_("Bind to given address and whitelist peers connecting "
"to it. Use [host]:port notation for IPv6"));
strUsage += HelpMessageOpt(
"-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"));
strUsage += HelpMessageOpt(
"-whitelistrelay",
strprintf(_("Accept relayed transactions received from whitelisted "
"peers even when not relaying transactions (default: %d)"),
DEFAULT_WHITELISTRELAY));
strUsage += HelpMessageOpt(
"-whitelistforcerelay",
strprintf(_("Force relay of transactions from whitelisted peers even "
"if they violate local relay policy (default: %d)"),
DEFAULT_WHITELISTFORCERELAY));
strUsage += HelpMessageOpt(
"-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));
#ifdef ENABLE_WALLET
strUsage += CWallet::GetWalletHelpString(showDebug);
#endif
#if ENABLE_ZMQ
strUsage += HelpMessageGroup(_("ZeroMQ notification options:"));
strUsage += HelpMessageOpt("-zmqpubhashblock=<address>",
_("Enable publish hash block in <address>"));
strUsage +=
HelpMessageOpt("-zmqpubhashtx=<address>",
_("Enable publish hash transaction in <address>"));
strUsage += HelpMessageOpt("-zmqpubrawblock=<address>",
_("Enable publish raw block in <address>"));
strUsage +=
HelpMessageOpt("-zmqpubrawtx=<address>",
_("Enable publish raw transaction in <address>"));
#endif
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
strUsage += HelpMessageOpt("-uacomment=<cmt>",
_("Append comment to the user agent string"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-checkblocks=<n>",
strprintf(
_("How many blocks to check at startup (default: %u, 0 = all)"),
DEFAULT_CHECKBLOCKS));
strUsage +=
HelpMessageOpt("-checklevel=<n>",
strprintf(_("How thorough the block verification of "
"-checkblocks is (0-4, default: %u)"),
DEFAULT_CHECKLEVEL));
strUsage += HelpMessageOpt(
"-checkblockindex",
strprintf(
"Do a full consistency check for mapBlockIndex, "
"setBlockIndexCandidates, chainActive and mapBlocksUnlinked "
"occasionally. Also sets -checkmempool (default: %u)",
Params(CBaseChainParams::MAIN).DefaultConsistencyChecks()));
strUsage += HelpMessageOpt(
"-checkmempool=<n>",
strprintf(
"Run checks every <n> transactions (default: %u)",
Params(CBaseChainParams::MAIN).DefaultConsistencyChecks()));
strUsage += HelpMessageOpt(
"-checkpoints", strprintf("Disable expensive verification for "
"known chain history (default: %u)",
DEFAULT_CHECKPOINTS_ENABLED));
strUsage += HelpMessageOpt(
"-disablesafemode", strprintf("Disable safemode, override a real "
"safe mode event (default: %u)",
DEFAULT_DISABLE_SAFEMODE));
strUsage += HelpMessageOpt(
"-testsafemode",
strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE));
strUsage +=
HelpMessageOpt("-dropmessagestest=<n>",
"Randomly drop 1 of every <n> network messages");
strUsage +=
HelpMessageOpt("-fuzzmessagestest=<n>",
"Randomly fuzz 1 of every <n> network messages");
strUsage += HelpMessageOpt(
"-stopafterblockimport",
strprintf(
"Stop running after importing blocks from disk (default: %u)",
DEFAULT_STOPAFTERBLOCKIMPORT));
strUsage += HelpMessageOpt(
"-limitancestorcount=<n>",
strprintf("Do not accept transactions if number of in-mempool "
"ancestors is <n> or more (default: %u)",
DEFAULT_ANCESTOR_LIMIT));
strUsage +=
HelpMessageOpt("-limitancestorsize=<n>",
strprintf("Do not accept transactions whose size "
"with all in-mempool ancestors exceeds "
"<n> kilobytes (default: %u)",
DEFAULT_ANCESTOR_SIZE_LIMIT));
strUsage += HelpMessageOpt(
"-limitdescendantcount=<n>",
strprintf("Do not accept transactions if any ancestor would have "
"<n> or more in-mempool descendants (default: %u)",
DEFAULT_DESCENDANT_LIMIT));
strUsage += HelpMessageOpt(
"-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));
strUsage += HelpMessageOpt("-bip9params=deployment:start:end",
"Use given start/end times for specified "
"BIP9 deployment (regtest-only)");
}
std::string debugCategories =
"addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, "
"mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, "
"selectcoins, tor, zmq"; // Don't translate these and qt below
if (mode == HMM_BITCOIN_QT) debugCategories += ", qt";
strUsage += HelpMessageOpt(
"-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:") + " " + debugCategories + ".");
if (showDebug)
strUsage += HelpMessageOpt(
"-nodebug", "Turn off debugging messages, same as -debug=0");
strUsage += HelpMessageOpt(
"-help-debug",
_("Show all debugging options (usage: --help -help-debug)"));
strUsage += HelpMessageOpt(
"-logips",
strprintf(_("Include IP addresses in debug output (default: %u)"),
DEFAULT_LOGIPS));
strUsage += HelpMessageOpt(
"-logtimestamps",
strprintf(_("Prepend debug output with timestamp (default: %u)"),
DEFAULT_LOGTIMESTAMPS));
if (showDebug) {
strUsage += HelpMessageOpt(
"-logtimemicros",
strprintf(
"Add microsecond precision to debug timestamps (default: %u)",
DEFAULT_LOGTIMEMICROS));
strUsage += HelpMessageOpt(
"-mocktime=<n>",
"Replace actual time with <n> seconds since epoch (default: 0)");
strUsage += HelpMessageOpt(
"-limitfreerelay=<n>",
strprintf("Continuously rate-limit free transactions to <n>*1000 "
"bytes per minute (default: %u)",
DEFAULT_LIMITFREERELAY));
strUsage +=
HelpMessageOpt("-relaypriority",
strprintf("Require high priority for relaying free "
"or low-fee transactions (default: %u)",
DEFAULT_RELAYPRIORITY));
strUsage += HelpMessageOpt(
"-maxsigcachesize=<n>",
strprintf("Limit size of signature cache to <n> MiB (default: %u)",
DEFAULT_MAX_SIG_CACHE_SIZE));
strUsage += HelpMessageOpt(
"-maxtipage=<n>",
strprintf("Maximum tip age in seconds to consider node in initial "
"block download (default: %u)",
DEFAULT_MAX_TIP_AGE));
}
strUsage += HelpMessageOpt(
"-minrelaytxfee=<amt>",
strprintf(
_("Fees (in %s/kB) smaller than this are considered zero fee for "
"relaying, mining and transaction creation (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)));
strUsage += HelpMessageOpt(
"-maxtxfee=<amt>",
strprintf(_("Maximum total fees (in %s) to use in a single wallet "
"transaction or raw transaction; setting this too low may "
"abort large transactions (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)));
strUsage += HelpMessageOpt(
"-printtoconsole",
_("Send trace/debug info to console instead of debug.log file"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-printpriority", strprintf("Log transaction priority and fee per "
"kB when mining blocks (default: %u)",
DEFAULT_PRINTPRIORITY));
}
strUsage += HelpMessageOpt("-shrinkdebugfile",
_("Shrink debug.log file on client startup "
"(default: 1 when no -debug)"));
AppendParamsHelpMessages(strUsage, showDebug);
strUsage += HelpMessageGroup(_("Node relay options:"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-acceptnonstdtxn",
strprintf(
"Relay and mine \"non-standard\" transactions (%sdefault: %u)",
"testnet/regtest only; ",
!Params(CBaseChainParams::TESTNET).RequireStandard()));
strUsage +=
HelpMessageOpt("-excessiveblocksize=<n>",
strprintf(_("Do not accept blocks larger than this "
"limit, in bytes (default: %d)"),
LEGACY_MAX_BLOCK_SIZE));
strUsage += HelpMessageOpt(
"-incrementalrelayfee=<amt>",
strprintf(
"Fee rate (in %s/kB) used to define cost of relay, used for "
"mempool limiting and BIP 125 replacement. (default: %s)",
CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)));
strUsage += HelpMessageOpt(
"-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)));
}
strUsage +=
HelpMessageOpt("-bytespersigop",
strprintf(_("Equivalent bytes per sigop in transactions "
"for relay and mining (default: %u)"),
DEFAULT_BYTES_PER_SIGOP));
strUsage += HelpMessageOpt(
"-datacarrier",
strprintf(_("Relay and mine data carrier transactions (default: %u)"),
DEFAULT_ACCEPT_DATACARRIER));
strUsage += HelpMessageOpt(
"-datacarriersize",
strprintf(_("Maximum size of data in data carrier transactions we "
"relay and mine (default: %u)"),
MAX_OP_RETURN_RELAY));
strUsage += HelpMessageGroup(_("Block creation options:"));
strUsage += HelpMessageOpt(
"-blockmaxsize=<n>",
strprintf(_("Set maximum block size in bytes (default: %d)"),
DEFAULT_MAX_GENERATED_BLOCK_SIZE));
strUsage +=
HelpMessageOpt("-blockprioritysize=<n>",
strprintf(_("Set maximum size of high-priority/low-fee "
"transactions in bytes (default: %d)"),
DEFAULT_BLOCK_PRIORITY_SIZE));
strUsage += HelpMessageOpt(
"-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)));
if (showDebug)
strUsage +=
HelpMessageOpt("-blockversion=<n>",
"Override block version to test forking scenarios");
strUsage += HelpMessageGroup(_("RPC server options:"));
strUsage += HelpMessageOpt("-server",
_("Accept command line and JSON-RPC commands"));
strUsage += HelpMessageOpt(
"-rest", strprintf(_("Accept public REST requests (default: %u)"),
DEFAULT_REST_ENABLE));
strUsage += HelpMessageOpt(
"-rpcbind=<addr>",
_("Bind to given address to listen for JSON-RPC connections. Use "
"[host]:port notation for IPv6. This option can be specified "
"multiple times (default: bind to all interfaces)"));
strUsage +=
HelpMessageOpt("-rpccookiefile=<loc>",
_("Location of the auth cookie (default: data dir)"));
strUsage += HelpMessageOpt("-rpcuser=<user>",
_("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcpassword=<pw>",
_("Password for JSON-RPC connections"));
strUsage += HelpMessageOpt(
"-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"));
strUsage += HelpMessageOpt(
"-rpcport=<port>",
strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or "
"testnet: %u)"),
BaseParams(CBaseChainParams::MAIN).RPCPort(),
BaseParams(CBaseChainParams::TESTNET).RPCPort()));
strUsage += HelpMessageOpt(
"-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"));
strUsage += HelpMessageOpt(
"-rpcthreads=<n>",
strprintf(
_("Set the number of threads to service RPC calls (default: %d)"),
DEFAULT_HTTP_THREADS));
if (showDebug) {
strUsage += HelpMessageOpt(
"-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to "
"service RPC calls (default: %d)",
DEFAULT_HTTP_WORKQUEUE));
strUsage += HelpMessageOpt(
"-rpcservertimeout=<n>",
strprintf("Timeout during HTTP requests (default: %d)",
DEFAULT_HTTP_SERVER_TIMEOUT));
}
return strUsage;
}
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 = GetArg("-blocknotify", "");
boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
}
static bool fHaveGenesis = false;
static boost::mutex cs_GenesisWait;
static CConditionVariable condvar_GenesisWait;
static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) {
if (pBlockIndex != nullptr) {
{
boost::unique_lock<boost::mutex> lock_GenesisWait(cs_GenesisWait);
fHaveGenesis = true;
}
condvar_GenesisWait.notify_all();
}
}
struct CImportingNow {
CImportingNow() {
assert(fImporting == false);
fImporting = true;
}
~CImportingNow() {
assert(fImporting == true);
fImporting = false;
}
};
// If we're using -prune with -reindex, then delete block files that will be
// ignored by the reindex. Since reindexing works by starting at block file 0
// and looping until a blockfile is missing, do the same here to delete any
// later block files after a gap. Also delete all rev files since they'll be
// rewritten by the reindex anyway. This ensures that vinfoBlockFile is in sync
// with what's actually on disk by the time we start downloading, so that
// pruning works correctly.
void CleanupBlockRevFiles() {
std::map<std::string, boost::filesystem::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");
boost::filesystem::path blocksdir = GetDataDir() / "blocks";
for (boost::filesystem::directory_iterator it(blocksdir);
it != boost::filesystem::directory_iterator(); it++) {
if (is_regular_file(*it) &&
it->path().filename().string().length() == 12 &&
it->path().filename().string().substr(8, 4) == ".dat") {
if (it->path().filename().string().substr(0, 3) == "blk")
mapBlockFiles[it->path().filename().string().substr(3, 5)] =
it->path();
else if (it->path().filename().string().substr(0, 3) == "rev")
remove(it->path());
}
}
// Remove all block files that aren't part of a contiguous set starting at
// zero by walking the ordered map (keys are block file indices) by keeping
// a separate counter. Once we hit a gap (or if 0 doesn't exist) start
// removing block files.
int nContigCounter = 0;
for (const std::pair<std::string, boost::filesystem::path> &item :
mapBlockFiles) {
if (atoi(item.first) == nContigCounter) {
nContigCounter++;
continue;
}
remove(item.second);
}
}
void ThreadImport(const Config &config,
std::vector<boost::filesystem::path> vImportFiles) {
RenameThread("bitcoin-loadblk");
{
CImportingNow imp;
// -reindex
if (fReindex) {
int nFile = 0;
while (true) {
CDiskBlockPos pos(nFile, 0);
if (!boost::filesystem::exists(GetBlockPosFilename(pos, "blk")))
break; // No block files left to reindex
FILE *file = OpenBlockFile(pos, true);
if (!file) break; // This error is logged in OpenBlockFile
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):
InitBlockIndex(config);
}
// hardcoded $DATADIR/bootstrap.dat
boost::filesystem::path pathBootstrap = GetDataDir() / "bootstrap.dat";
if (boost::filesystem::exists(pathBootstrap)) {
FILE *file = fopen(pathBootstrap.string().c_str(), "rb");
if (file) {
boost::filesystem::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 boost::filesystem::path &path : vImportFiles) {
FILE *file = fopen(path.string().c_str(), "rb");
if (file) {
LogPrintf("Importing blocks file %s...\n", path.string());
LoadExternalBlockFile(config, file);
} else {
LogPrintf("Warning: Could not open blocks file %s\n",
path.string());
}
}
// scan for better chains in the block chain database, that are not yet
// connected in the active best chain
CValidationState state;
if (!ActivateBestChain(config, state)) {
LogPrintf("Failed to connect best block");
StartShutdown();
}
if (GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
}
} // End scope of CImportingNow
LoadMempool(config);
fDumpMempoolLater = !fRequestShutdown;
}
/** Sanity checks
* Ensure that Bitcoin is running in a usable environment with all
* necessary library support.
*/
bool InitSanityCheck(void) {
if (!ECC_InitSanityCheck()) {
InitError(
"Elliptic curve cryptography sanity check failure. Aborting.");
return false;
}
if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false;
return true;
}
static bool AppInitServers(Config &config, boost::thread_group &threadGroup) {
RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped);
RPCServer::OnPreCommand(&OnRPCPreCommand);
if (!InitHTTPServer(config)) return false;
if (!StartRPC()) return false;
if (!StartHTTPRPC()) return false;
if (GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST()) return false;
if (!StartHTTPServer()) return false;
return true;
}
// Parameter interaction based on rules
void InitParameterInteraction() {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified.
if (IsArgSet("-bind")) {
if (SoftSetBoolArg("-listen", true))
LogPrintf(
"%s: parameter interaction: -bind set -> setting -listen=1\n",
__func__);
}
if (IsArgSet("-whitebind")) {
if (SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -whitebind set -> setting "
"-listen=1\n",
__func__);
}
if (mapMultiArgs.count("-connect") &&
mapMultiArgs.at("-connect").size() > 0) {
// when only connecting to trusted nodes, do not seed via DNS, or listen
// by default.
if (SoftSetBoolArg("-dnsseed", false))
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-dnsseed=0\n",
__func__);
if (SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-listen=0\n",
__func__);
}
if (IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy
// server is specified.
if (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 (SoftSetBoolArg("-upnp", false))
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -upnp=0\n",
__func__);
// to protect privacy, do not discover addresses by default
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting "
"-discover=0\n",
__func__);
}
if (!GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening
// (pointless)
if (SoftSetBoolArg("-upnp", false))
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -upnp=0\n",
__func__);
if (SoftSetBoolArg("-discover", false))
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -discover=0\n",
__func__);
if (SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting "
"-listenonion=0\n",
__func__);
}
if (IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -externalip set -> setting "
"-discover=0\n",
__func__);
}
// disable whitelistrelay in blocksonly mode
if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (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 (GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> "
"setting -whitelistrelay=1\n",
__func__);
}
}
static std::string ResolveErrMsg(const char *const optname,
const std::string &strBind) {
return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
}
void InitLogging() {
fPrintToConsole = GetBoolArg("-printtoconsole", false);
fLogTimestamps = GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
fLogTimeMicros = GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
fLogIPs = GetBoolArg("-logips", DEFAULT_LOGIPS);
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("%s version %s\n", CLIENT_NAME, FormatFullVersion());
}
namespace { // Variables internal to initialization process only
ServiceFlags nRelevantServices = NODE_NETWORK;
int nMaxConnections;
int nUserMaxConnections;
int nFD;
ServiceFlags nLocalServices = NODE_NETWORK;
} // namespace
[[noreturn]] static void new_handler_terminate() {
// Rather than throwing std::bad-alloc if allocation fails, terminate
// immediately to (try to) avoid chain corruption. Since LogPrintf may
// itself allocate memory, set the handler directly to terminate first.
std::set_new_handler(std::terminate);
LogPrintf("Error: Out of memory. Terminating.\n");
// The log was successful, terminate now.
std::terminate();
};
bool AppInitBasicSetup() {
// Step 1: setup
#ifdef _MSC_VER
// Turn off Microsoft heap dump noise
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, nullptr,
OPEN_EXISTING, 0, 0));
#endif
#if _MSC_VER >= 1400
// Disable confusing "helpful" text message on abort, Ctrl-C
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif
#ifdef WIN32
// Enable Data Execution Prevention (DEP)
// Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008
// A failure is non-critical and needs no further attention!
#ifndef PROCESS_DEP_ENABLE
// We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >=
// 0x0601 (Windows 7), which is not correct. Can be removed, when GCCs winbase.h
// is fixed!
#define PROCESS_DEP_ENABLE 0x00000001
#endif
typedef BOOL(WINAPI * PSETPROCDEPPOL)(DWORD);
PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(
GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy");
if (setProcDEPPol != nullptr) setProcDEPPol(PROCESS_DEP_ENABLE);
#endif
if (!SetupNetworking()) return InitError("Initializing networking failed");
#ifndef WIN32
if (!GetBoolArg("-sysperms", false)) {
umask(077);
}
// Clean shutdown on SIGTERM
struct sigaction sa;
sa.sa_handler = HandleSIGTERM;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
// Reopen debug.log on SIGHUP
struct sigaction sa_hup;
sa_hup.sa_handler = HandleSIGHUP;
sigemptyset(&sa_hup.sa_mask);
sa_hup.sa_flags = 0;
sigaction(SIGHUP, &sa_hup, nullptr);
// Ignore SIGPIPE, otherwise it will bring the daemon down if the client
// closes unexpectedly
signal(SIGPIPE, SIG_IGN);
#endif
std::set_new_handler(new_handler_terminate);
return true;
}
bool AppInitParameterInteraction(Config &config) {
const CChainParams &chainparams = Params();
// Step 2: parameter interactions
// also see: InitParameterInteraction()
// if using block pruning, then disallow txindex
if (GetArg("-prune", 0)) {
if (GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
}
// Make sure enough file descriptors are available
int nBind = std::max(
(mapMultiArgs.count("-bind") ? mapMultiArgs.at("-bind").size() : 0) +
(mapMultiArgs.count("-whitebind")
? mapMultiArgs.at("-whitebind").size()
: 0),
size_t(1));
nUserMaxConnections =
GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
nMaxConnections =
std::max(std::min(nMaxConnections,
(int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS -
MAX_ADDNODE_CONNECTIONS)),
0);
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS +
MAX_ADDNODE_CONNECTIONS);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
nMaxConnections =
std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS,
nMaxConnections);
if (nMaxConnections < nUserMaxConnections)
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, "
"because of system limitations."),
nUserMaxConnections, nMaxConnections));
// Step 3: parameter-to-internal-flags
fDebug = mapMultiArgs.count("-debug");
// Special-case: if -debug=0/-nodebug is set, turn off debugging messages
if (fDebug) {
const std::vector<std::string> &categories = mapMultiArgs.at("-debug");
if (GetBoolArg("-nodebug", false) ||
find(categories.begin(), categories.end(), std::string("0")) !=
categories.end())
fDebug = false;
}
// Check for -debugnet
if (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 (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 (GetBoolArg("-tor", false))
return InitError(_("Unsupported argument -tor found, use -onion."));
if (GetBoolArg("-benchmark", false))
InitWarning(
_("Unsupported argument -benchmark ignored, use -debug=bench."));
if (GetBoolArg("-whitelistalwaysrelay", false))
InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use "
"-whitelistrelay and/or -whitelistforcerelay."));
if (IsArgSet("-blockminsize"))
InitWarning("Unsupported argument -blockminsize ignored.");
// Checkmempool and checkblockindex default to true in regtest mode
int ratio = std::min<int>(
std::max<int>(GetArg("-checkmempool",
chainparams.DefaultConsistencyChecks() ? 1 : 0),
0),
1000000);
if (ratio != 0) {
mempool.setSanityCheck(1.0 / ratio);
}
fCheckBlockIndex =
GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled =
GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
hashAssumeValid = uint256S(
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");
// mempool limits
int64_t nMempoolSizeMax =
GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin =
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)));
// Incremental relay fee sets the minimimum feerate increase necessary for
// BIP 125 replacement in the mempool and the amount the mempool min fee
// increases above the feerate of txs evicted due to mempool limiting.
if (IsArgSet("-incrementalrelayfee")) {
CAmount n = 0;
if (!ParseMoney(GetArg("-incrementalrelayfee", ""), n))
return InitError(AmountErrMsg("incrementalrelayfee",
GetArg("-incrementalrelayfee", "")));
incrementalRelayFee = CFeeRate(n);
}
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = 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 =
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 =
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 = 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;
}
RegisterAllRPCCommands(tableRPC);
#ifdef ENABLE_WALLET
RegisterWalletRPCCommands(tableRPC);
RegisterDumpRPCCommands(tableRPC);
#endif
nConnectTimeout = GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
// Fee-per-kilobyte amount considered the same as "free". If you are mining,
// be careful setting this: if you set it to zero then a transaction spammer
// can cheaply fill blocks using 1-satoshi-fee transactions. It should be
// set above the real cost to you of processing a transaction.
if (IsArgSet("-minrelaytxfee")) {
CAmount n = 0;
if (!ParseMoney(GetArg("-minrelaytxfee", ""), n) || 0 == n)
return InitError(
AmountErrMsg("minrelaytxfee", GetArg("-minrelaytxfee", "")));
// High fee check is done afterward in CWallet::ParameterInteraction()
::minRelayTxFee = CFeeRate(n);
} else if (incrementalRelayFee > ::minRelayTxFee) {
// Allow only setting incrementalRelayFee to control both
::minRelayTxFee = incrementalRelayFee;
LogPrintf(
"Increasing minrelaytxfee to %s to match incrementalrelayfee\n",
::minRelayTxFee.ToString());
}
// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that
// happens.
if (IsArgSet("-blockmintxfee")) {
CAmount n = 0;
if (!ParseMoney(GetArg("-blockmintxfee", ""), n))
return InitError(
AmountErrMsg("blockmintxfee", GetArg("-blockmintxfee", "")));
}
// Feerate used to define dust. Shouldn't be changed lightly as old
// implementations may inadvertently create non-standard transactions.
if (IsArgSet("-dustrelayfee")) {
CAmount n = 0;
if (!ParseMoney(GetArg("-dustrelayfee", ""), n) || 0 == n)
return InitError(
AmountErrMsg("dustrelayfee", GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}
fRequireStandard =
!GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (chainparams.RequireStandard() && !fRequireStandard)
return InitError(
strprintf("acceptnonstdtxn is not currently supported for %s chain",
chainparams.NetworkIDString()));
nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp);
#ifdef ENABLE_WALLET
if (!CWallet::ParameterInteraction()) return false;
#endif
fIsBareMultisigStd =
GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier = GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes);
// Option to startup with mocktime set (used for regression testing):
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (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);
// Preferentially keep peers which service NODE_BITCOIN_CASH
nRelevantServices = ServiceFlags(nRelevantServices | NODE_BITCOIN_CASH);
nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
if (mapMultiArgs.count("-bip9params")) {
// Allow overriding BIP9 parameters for testing
if (!chainparams.MineBlocksOnDemand()) {
return InitError(
"BIP9 parameters may only be overridden on regtest.");
}
const std::vector<std::string> &deployments =
mapMultiArgs.at("-bip9params");
for (auto i : deployments) {
std::vector<std::string> vDeploymentParams;
boost::split(vDeploymentParams, i, boost::is_any_of(":"));
if (vDeploymentParams.size() != 3) {
return InitError("BIP9 parameters malformed, expecting "
"deployment:start:end");
}
int64_t nStartTime, nTimeout;
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
return InitError(
strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
}
if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
return InitError(
strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
}
bool found = false;
for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS;
++j) {
if (vDeploymentParams[0].compare(
VersionBitsDeploymentInfo[j].name) == 0) {
UpdateRegtestBIP9Parameters(Consensus::DeploymentPos(j),
nStartTime, nTimeout);
found = true;
LogPrintf("Setting BIP9 activation parameters for %s to "
"start=%ld, timeout=%ld\n",
vDeploymentParams[0], nStartTime, nTimeout);
break;
}
}
if (!found) {
return InitError(
strprintf("Invalid deployment (%s)", vDeploymentParams[0]));
}
}
}
return true;
}
static bool LockDataDirectory(bool probeOnly) {
std::string strDataDir = GetDataDir().string();
// Make sure only a single Bitcoin process is using the data directory.
boost::filesystem::path pathLockFile = GetDataDir() / ".lock";
// empty lock file; created if it doesn't exist.
FILE *file = fopen(pathLockFile.string().c_str(), "a");
if (file) fclose(file);
try {
static boost::interprocess::file_lock lock(
pathLockFile.string().c_str());
if (!lock.try_lock()) {
return InitError(
strprintf(_("Cannot obtain a lock on data directory %s. %s is "
"probably already running."),
strDataDir, _(PACKAGE_NAME)));
}
if (probeOnly) {
lock.unlock();
}
} catch (const boost::interprocess::interprocess_exception &e) {
return InitError(strprintf(_("Cannot obtain a lock on data directory "
"%s. %s is probably already running.") +
" %s.",
strDataDir, _(PACKAGE_NAME), e.what()));
}
return true;
}
bool AppInitSanityChecks() {
// Step 4: sanity checks
// Initialize elliptic curve code
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
return LockDataDirectory(true);
}
bool AppInitMain(Config &config, boost::thread_group &threadGroup,
CScheduler &scheduler) {
const CChainParams &chainparams = Params();
// Step 4a: application initialization
// 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;
}
#ifndef WIN32
CreatePidFile(GetPidFile(), getpid());
#endif
if (GetBoolArg("-shrinkdebugfile", !fDebug)) {
// 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.
ShrinkDebugFile();
}
if (fPrintToDebugLog) OpenDebugLog();
if (!fLogTimestamps)
LogPrintf("Startup time: %s\n",
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()));
LogPrintf("Default data directory %s\n", GetDefaultDataDir().string());
LogPrintf("Using data directory %s\n", GetDataDir().string());
LogPrintf("Using config file %s\n",
GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
LogPrintf("Using at most %i automatic connections (%i file descriptors "
"available)\n",
nMaxConnections, nFD);
InitSignatureCache();
LogPrintf("Using %u threads for script verification\n",
nScriptCheckThreads);
if (nScriptCheckThreads) {
for (int i = 0; i < nScriptCheckThreads - 1; i++)
threadGroup.create_thread(&ThreadScriptCheck);
}
// Start the lightweight task scheduler thread
CScheduler::Function serviceLoop =
boost::bind(&CScheduler::serviceQueue, &scheduler);
threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>,
"scheduler", serviceLoop));
/* Start the RPC server already. It will be started in "warmup" mode
* and not really process calls already (but it will signify connections
* that the server is there and will be ready later). Warmup mode will
* be disabled when initialisation is finished.
*/
if (GetBoolArg("-server", false)) {
uiInterface.InitMessage.connect(SetRPCWarmupStatus);
if (!AppInitServers(config, threadGroup))
return InitError(
_("Unable to start HTTP server. See debug log for details."));
}
int64_t nStart;
// Step 5: verify wallet database integrity
#ifdef ENABLE_WALLET
if (!CWallet::Verify()) return false;
#endif
// Step 6: network initialization
// Note that we absolutely cannot open any actual connections
// until the very end ("start node") as the UTXO/block state
// is not yet setup and may end up being set up twice if we
// need to reindex later.
assert(!g_connman);
g_connman = std::unique_ptr<CConnman>(
new CConnman(config, GetRand(std::numeric_limits<uint64_t>::max()),
GetRand(std::numeric_limits<uint64_t>::max())));
CConnman &connman = *g_connman;
peerLogic.reset(new PeerLogicValidation(&connman));
RegisterValidationInterface(peerLogic.get());
RegisterNodeSignals(GetNodeSignals());
if (mapMultiArgs.count("-onlynet")) {
std::set<enum Network> nets;
for (const std::string &snet : mapMultiArgs.at("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(
_("Unknown network specified in -onlynet: '%s'"), snet));
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (!nets.count(net)) SetLimited(net);
}
}
if (mapMultiArgs.count("-whitelist")) {
for (const std::string &net : mapMultiArgs.at("-whitelist")) {
CSubNet subnet;
LookupSubNet(net.c_str(), subnet);
if (!subnet.IsValid())
return InitError(strprintf(
_("Invalid netmask specified in -whitelist: '%s'"), net));
connman.AddWhitelistedRange(subnet);
}
}
bool proxyRandomize = 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 = GetArg("-proxy", "");
SetLimited(NET_TOR);
if (proxyArg != "" && proxyArg != "0") {
CService resolved(LookupNumeric(proxyArg.c_str(), 9050));
proxyType addrProxy = proxyType(resolved, proxyRandomize);
if (!addrProxy.IsValid())
return InitError(
strprintf(_("Invalid -proxy address: '%s'"), proxyArg));
SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_TOR, addrProxy);
SetNameProxy(addrProxy);
SetLimited(NET_TOR, false); // by default, -proxy sets onion as
// reachable, unless -noonion later
}
// -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 = GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
SetLimited(NET_TOR); // set onions as unreachable
} else {
CService resolved(LookupNumeric(onionArg.c_str(), 9050));
proxyType addrOnion = proxyType(resolved, proxyRandomize);
if (!addrOnion.IsValid())
return InitError(
strprintf(_("Invalid -onion address: '%s'"), onionArg));
SetProxy(NET_TOR, addrOnion);
SetLimited(NET_TOR, false);
}
}
// see Step 2: parameter interactions for more information about these
fListen = GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = GetBoolArg("-discover", true);
fNameLookup = GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
fRelayTxes = !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
if (fListen) {
bool fBound = false;
if (mapMultiArgs.count("-bind")) {
for (const std::string &strBind : mapMultiArgs.at("-bind")) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(ResolveErrMsg("bind", strBind));
fBound |=
Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
}
}
if (mapMultiArgs.count("-whitebind")) {
for (const std::string &strBind : mapMultiArgs.at("-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));
fBound |= Bind(connman, addrBind,
(BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
}
}
if (!mapMultiArgs.count("-bind") && !mapMultiArgs.count("-whitebind")) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
fBound |=
Bind(connman, CService(in6addr_any, GetListenPort()), BF_NONE);
fBound |= Bind(connman, CService(inaddr_any, GetListenPort()),
!fBound ? BF_REPORT_ERROR : BF_NONE);
}
if (!fBound)
return InitError(_("Failed to listen on any port. Use -listen=0 if "
"you want this."));
}
if (mapMultiArgs.count("-externalip")) {
for (const std::string &strAddr : mapMultiArgs.at("-externalip")) {
CService addrLocal;
if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(),
fNameLookup) &&
addrLocal.IsValid())
AddLocal(addrLocal, LOCAL_MANUAL);
else
return InitError(ResolveErrMsg("externalip", strAddr));
}
}
if (mapMultiArgs.count("-seednode")) {
for (const std::string &strDest : mapMultiArgs.at("-seednode")) {
connman.AddOneShot(strDest);
}
}
#if ENABLE_ZMQ
pzmqNotificationInterface = CZMQNotificationInterface::Create();
if (pzmqNotificationInterface) {
RegisterValidationInterface(pzmqNotificationInterface);
}
#endif
// unlimited unless -maxuploadtarget is set
uint64_t nMaxOutboundLimit = 0;
uint64_t nMaxOutboundTimeframe = MAX_UPLOAD_TIMEFRAME;
if (IsArgSet("-maxuploadtarget")) {
nMaxOutboundLimit =
GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 * 1024;
}
// Step 7: load block chain
fReindex = GetBoolArg("-reindex", false);
bool fReindexChainState = GetBoolArg("-reindex-chainstate", false);
// Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/
boost::filesystem::path blocksDir = GetDataDir() / "blocks";
if (!boost::filesystem::exists(blocksDir)) {
boost::filesystem::create_directories(blocksDir);
bool linked = false;
for (unsigned int i = 1; i < 10000; i++) {
boost::filesystem::path source =
GetDataDir() / strprintf("blk%04u.dat", i);
if (!boost::filesystem::exists(source)) break;
boost::filesystem::path dest =
blocksDir / strprintf("blk%05u.dat", i - 1);
try {
boost::filesystem::create_hard_link(source, dest);
LogPrintf("Hardlinked %s -> %s\n", source.string(),
dest.string());
linked = true;
} catch (const boost::filesystem::filesystem_error &e) {
// Note: hardlink creation failing is not a disaster, it just
// means blocks will get re-downloaded from peers.
LogPrintf("Error hardlinking blk%04u.dat: %s\n", i, e.what());
break;
}
}
if (linked) {
fReindex = true;
}
}
// cache size calculations
int64_t nTotalCache = (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 = nTotalCache / 8;
nBlockTreeDBCache =
std::min(nBlockTreeDBCache, (GetBoolArg("-txindex", DEFAULT_TXINDEX)
? nMaxBlockDBAndTxIndexCache
: nMaxBlockDBCache)
<< 20);
nTotalCache -= nBlockTreeDBCache;
// 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 =
GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1fMiB for block index database\n",
nBlockTreeDBCache * (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));
bool fLoaded = false;
while (!fLoaded) {
bool fReset = fReindex;
std::string strLoadError;
uiInterface.InitMessage(_("Loading block index..."));
nStart = GetTimeMillis();
do {
try {
UnloadBlockIndex();
delete pcoinsTip;
delete pcoinsdbview;
delete pcoinscatcher;
delete pblocktree;
pblocktree =
new CBlockTreeDB(nBlockTreeDBCache, false, fReindex);
pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false,
fReindex || fReindexChainState);
pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview);
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
if (fReindex) {
pblocktree->WriteReindexing(true);
// If we're reindexing in prune mode, wipe away unusable
// block files and all undo data files
if (fPruneMode) CleanupBlockRevFiles();
}
if (!LoadBlockIndex(chainparams)) {
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() &&
mapBlockIndex.count(
chainparams.GetConsensus().hashGenesisBlock) == 0)
return InitError(_("Incorrect or no genesis block found. "
"Wrong datadir for network?"));
// Initialize the block index (no-op if non-empty database was
// already loaded)
if (!InitBlockIndex(config)) {
strLoadError = _("Error initializing block database");
break;
}
// Check for changed -txindex state
if (fTxIndex != GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
strLoadError = _("You need to rebuild the database using "
"-reindex-chainstate to change -txindex");
break;
}
// 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;
}
if (!fReindex && chainActive.Tip() != nullptr) {
uiInterface.InitMessage(_("Rewinding blocks..."));
if (!RewindBlockIndex(config, chainparams)) {
strLoadError = _("Unable to rewind the database to a "
"pre-fork state. You will need to "
"redownload the blockchain");
break;
}
}
uiInterface.InitMessage(_("Verifying blocks..."));
if (fHavePruned &&
GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) >
MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d "
"blocks; only checking available blocks",
MIN_BLOCKS_TO_KEEP);
}
{
LOCK(cs_main);
CBlockIndex *tip = chainActive.Tip();
RPCNotifyBlockChange(true, tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
strLoadError =
_("The block database contains a block which "
"appears to be from the future. "
"This may be due to your computer's date and "
"time being set incorrectly. "
"Only rebuild the block database if you are sure "
"that your computer's date and time are correct");
break;
}
}
if (!CVerifyDB().VerifyDB(
config, chainparams, pcoinsdbview,
GetArg("-checklevel", DEFAULT_CHECKLEVEL),
GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
break;
}
} catch (const std::exception &e) {
if (fDebug) LogPrintf("%s\n", e.what());
strLoadError = _("Error opening block database");
break;
}
fLoaded = true;
} while (false);
if (!fLoaded) {
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeQuestion(
strLoadError + ".\n\n" +
_("Do you want to rebuild the block database now?"),
strLoadError + ".\nPlease restart with -reindex or "
"-reindex-chainstate to recover.",
"", CClientUIInterface::MSG_ERROR |
CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
fRequestShutdown = false;
} else {
LogPrintf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
return InitError(strLoadError);
}
}
}
// As LoadBlockIndex can take several minutes, it's possible the user
// requested to kill the GUI during the last operation. If so, exit.
// As the program has not fully started yet, Shutdown() is possibly
// overkill.
if (fRequestShutdown) {
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart);
boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_filein(fopen(est_path.string().c_str(), "rb"), SER_DISK,
CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
if (!est_filein.IsNull()) mempool.ReadFeeEstimates(est_filein);
fFeeEstimatesInitialized = true;
// Step 8: load wallet
#ifdef ENABLE_WALLET
if (!CWallet::InitLoadWallet()) return false;
#else
LogPrintf("No wallet support compiled in!\n");
#endif
// Step 9: 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 10: import blocks
if (!CheckDiskSpace()) 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 (IsArgSet("-blocknotify"))
uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
std::vector<boost::filesystem::path> vImportFiles;
if (mapMultiArgs.count("-loadblock")) {
for (const std::string &strFile : mapMultiArgs.at("-loadblock")) {
vImportFiles.push_back(strFile);
}
}
threadGroup.create_thread(
boost::bind(&ThreadImport, boost::ref(config), vImportFiles));
// Wait for genesis block to be processed
{
boost::unique_lock<boost::mutex> lock(cs_GenesisWait);
while (!fHaveGenesis) {
condvar_GenesisWait.wait(lock);
}
uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait);
}
// Step 11: start node
//// debug print
LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size());
LogPrintf("nBestHeight = %d\n", chainActive.Height());
if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(threadGroup, scheduler);
Discover(threadGroup);
// Map ports with UPnP
MapPort(GetBoolArg("-upnp", DEFAULT_UPNP));
std::string strNodeError;
CConnman::Options connOptions;
connOptions.nLocalServices = nLocalServices;
connOptions.nRelevantServices = nRelevantServices;
connOptions.nMaxConnections = nMaxConnections;
connOptions.nMaxOutbound =
std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections);
connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS;
connOptions.nMaxFeeler = 1;
connOptions.nBestHeight = chainActive.Height();
connOptions.uiInterface = &uiInterface;
connOptions.nSendBufferMaxSize =
1000 * GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize =
1000 * GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER);
connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe;
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
if (!connman.Start(scheduler, strNodeError, connOptions))
return InitError(strNodeError);
// Step 12: finished
SetRPCWarmupFinished();
uiInterface.InitMessage(_("Done loading"));
#ifdef ENABLE_WALLET
if (pwalletMain) pwalletMain->postInitProcess(threadGroup);
#endif
return !fRequestShutdown;
}
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index c19129229a..ec480b5435 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1,1285 +1,1285 @@
// Copyright (c) 2011-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 "rpcconsole.h"
#include "ui_debugwindow.h"
#include "bantablemodel.h"
#include "bantablemodel.h"
#include "clientmodel.h"
#include "guiutil.h"
#include "platformstyle.h"
#include "chainparams.h"
#include "config.h"
#include "netbase.h"
#include "rpc/client.h"
#include "rpc/server.h"
#include "util.h"
#include <openssl/crypto.h>
#include <univalue.h>
#ifdef ENABLE_WALLET
#include <db_cxx.h>
#endif
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QScrollBar>
#include <QSettings>
#include <QSignalMapper>
#include <QStringList>
#include <QThread>
#include <QTime>
#include <QTimer>
#if QT_VERSION < 0x050000
#include <QUrl>
#endif
// TODO: add a scrollback limit, as there is currently none
// TODO: make it possible to filter out categories (esp debug messages when
// implemented)
// TODO: receive errors and debug messages through ClientModel
const int CONSOLE_HISTORY = 50;
const int INITIAL_TRAFFIC_GRAPH_MINS = 30;
const QSize FONT_RANGE(4, 40);
const char fontSizeSettingsKey[] = "consoleFontSize";
const struct {
const char *url;
const char *source;
} ICON_MAPPING[] = {{"cmd-request", ":/icons/tx_input"},
{"cmd-reply", ":/icons/tx_output"},
{"cmd-error", ":/icons/tx_output"},
{"misc", ":/icons/tx_inout"},
{nullptr, nullptr}};
namespace {
// don't add private key handling cmd's to the history
const QStringList historyFilter = QStringList() << "importprivkey"
<< "importmulti"
<< "signmessagewithprivkey"
<< "signrawtransaction"
<< "walletpassphrase"
<< "walletpassphrasechange"
<< "encryptwallet";
}
/* Object for executing console RPC commands in a separate thread.
*/
class RPCExecutor : public QObject {
Q_OBJECT
public Q_SLOTS:
void request(const QString &command);
Q_SIGNALS:
void reply(int category, const QString &command);
};
/** Class for handling RPC timers
* (used for e.g. re-locking the wallet after a timeout)
*/
class QtRPCTimerBase : public QObject, public RPCTimerBase {
Q_OBJECT
public:
- QtRPCTimerBase(boost::function<void(void)> &_func, int64_t millis)
+ QtRPCTimerBase(std::function<void(void)> &_func, int64_t millis)
: func(_func) {
timer.setSingleShot(true);
connect(&timer, SIGNAL(timeout()), this, SLOT(timeout()));
timer.start(millis);
}
~QtRPCTimerBase() {}
private Q_SLOTS:
void timeout() { func(); }
private:
QTimer timer;
- boost::function<void(void)> func;
+ std::function<void(void)> func;
};
class QtRPCTimerInterface : public RPCTimerInterface {
public:
~QtRPCTimerInterface() {}
const char *Name() { return "Qt"; }
- RPCTimerBase *NewTimer(boost::function<void(void)> &func, int64_t millis) {
+ RPCTimerBase *NewTimer(std::function<void(void)> &func, int64_t millis) {
return new QtRPCTimerBase(func, millis);
}
};
#include "rpcconsole.moc"
/**
* Split shell command line into a list of arguments and optionally execute the
* command(s).
* Aims to emulate \c bash and friends.
*
* - Command nesting is possible with parenthesis; for example:
* validateaddress(getnewaddress())
* - Arguments are delimited with whitespace or comma
* - Extra whitespace at the beginning and end and between arguments will be
* ignored
* - Text can be "double" or 'single' quoted
* - The backslash \c \ is used as escape character
* - Outside quotes, any character can be escaped
* - Within double quotes, only escape \c " and backslashes before a \c " or
* another backslash
* - Within single quotes, no escaping is possible and no special
* interpretation takes place
*
* @param[out] result stringified Result from the executed command(chain)
* @param[in] strCommand Command line to split
* @param[in] fExecute set true if you want the command to be executed
* @param[out] pstrFilteredOut Command line, filtered to remove any sensitive
* data
*/
bool RPCConsole::RPCParseCommandLine(std::string &strResult,
const std::string &strCommand,
const bool fExecute,
std::string *const pstrFilteredOut) {
std::vector<std::vector<std::string>> stack;
stack.push_back(std::vector<std::string>());
enum CmdParseState {
STATE_EATING_SPACES,
STATE_EATING_SPACES_IN_ARG,
STATE_EATING_SPACES_IN_BRACKETS,
STATE_ARGUMENT,
STATE_SINGLEQUOTED,
STATE_DOUBLEQUOTED,
STATE_ESCAPE_OUTER,
STATE_ESCAPE_DOUBLEQUOTED,
STATE_COMMAND_EXECUTED,
STATE_COMMAND_EXECUTED_INNER
} state = STATE_EATING_SPACES;
std::string curarg;
UniValue lastResult;
unsigned nDepthInsideSensitive = 0;
size_t filter_begin_pos = 0, chpos;
std::vector<std::pair<size_t, size_t>> filter_ranges;
auto add_to_current_stack = [&](const std::string &strArg) {
if (stack.back().empty() && (!nDepthInsideSensitive) &&
historyFilter.contains(QString::fromStdString(strArg),
Qt::CaseInsensitive)) {
nDepthInsideSensitive = 1;
filter_begin_pos = chpos;
}
// Make sure stack is not empty before adding something
if (stack.empty()) {
stack.push_back(std::vector<std::string>());
}
stack.back().push_back(strArg);
};
auto close_out_params = [&]() {
if (nDepthInsideSensitive) {
if (!--nDepthInsideSensitive) {
assert(filter_begin_pos);
filter_ranges.push_back(
std::make_pair(filter_begin_pos, chpos));
filter_begin_pos = 0;
}
}
stack.pop_back();
};
std::string strCommandTerminated = strCommand;
if (strCommandTerminated.back() != '\n') strCommandTerminated += "\n";
for (chpos = 0; chpos < strCommandTerminated.size(); ++chpos) {
char ch = strCommandTerminated[chpos];
switch (state) {
case STATE_COMMAND_EXECUTED_INNER:
case STATE_COMMAND_EXECUTED: {
bool breakParsing = true;
switch (ch) {
case '[':
curarg.clear();
state = STATE_COMMAND_EXECUTED_INNER;
break;
default:
if (state == STATE_COMMAND_EXECUTED_INNER) {
if (ch != ']') {
// append char to the current argument (which is
// also used for the query command)
curarg += ch;
break;
}
if (curarg.size() && fExecute) {
// if we have a value query, query arrays with
// index and objects with a string key
UniValue subelement;
if (lastResult.isArray()) {
for (char argch : curarg)
if (!std::isdigit(argch))
throw std::runtime_error(
"Invalid result query");
subelement =
lastResult[atoi(curarg.c_str())];
} else if (lastResult.isObject())
subelement = find_value(lastResult, curarg);
else {
// no array or object: abort
throw std::runtime_error(
"Invalid result query");
}
lastResult = subelement;
}
state = STATE_COMMAND_EXECUTED;
break;
}
// don't break parsing when the char is required for the
// next argument
breakParsing = false;
// pop the stack and return the result to the current
// command arguments
close_out_params();
// don't stringify the json in case of a string to avoid
// doublequotes
if (lastResult.isStr())
curarg = lastResult.get_str();
else
curarg = lastResult.write(2);
// if we have a non empty result, use it as stack
// argument otherwise as general result
if (curarg.size()) {
if (stack.size())
add_to_current_stack(curarg);
else
strResult = curarg;
}
curarg.clear();
// assume eating space state
state = STATE_EATING_SPACES;
}
if (breakParsing) {
break;
}
}
// FALLTHROUGH
case STATE_ARGUMENT: // In or after argument
case STATE_EATING_SPACES_IN_ARG:
case STATE_EATING_SPACES_IN_BRACKETS:
case STATE_EATING_SPACES: // Handle runs of whitespace
switch (ch) {
case '"':
state = STATE_DOUBLEQUOTED;
break;
case '\'':
state = STATE_SINGLEQUOTED;
break;
case '\\':
state = STATE_ESCAPE_OUTER;
break;
case '(':
case ')':
case '\n':
if (state == STATE_EATING_SPACES_IN_ARG)
throw std::runtime_error("Invalid Syntax");
if (state == STATE_ARGUMENT) {
if (ch == '(' && stack.size() &&
stack.back().size() > 0) {
if (nDepthInsideSensitive) {
++nDepthInsideSensitive;
}
stack.push_back(std::vector<std::string>());
}
// don't allow commands after executed commands on
// baselevel
if (!stack.size())
throw std::runtime_error("Invalid Syntax");
add_to_current_stack(curarg);
curarg.clear();
state = STATE_EATING_SPACES_IN_BRACKETS;
}
if ((ch == ')' || ch == '\n') && stack.size() > 0) {
if (fExecute) {
// Convert argument list to JSON objects in
// method-dependent way, and pass it along with
// the method name to the dispatcher.
JSONRPCRequest req;
req.params = RPCConvertValues(
stack.back()[0],
std::vector<std::string>(
stack.back().begin() + 1,
stack.back().end()));
req.strMethod = stack.back()[0];
GlobalConfig config;
lastResult = tableRPC.execute(config, req);
}
state = STATE_COMMAND_EXECUTED;
curarg.clear();
}
break;
case ' ':
case ',':
case '\t':
if (state == STATE_EATING_SPACES_IN_ARG &&
curarg.empty() && ch == ',')
throw std::runtime_error("Invalid Syntax");
else if (state == STATE_ARGUMENT) // Space ends argument
{
add_to_current_stack(curarg);
curarg.clear();
}
if ((state == STATE_EATING_SPACES_IN_BRACKETS ||
state == STATE_ARGUMENT) &&
ch == ',') {
state = STATE_EATING_SPACES_IN_ARG;
break;
}
state = STATE_EATING_SPACES;
break;
default:
curarg += ch;
state = STATE_ARGUMENT;
}
break;
case STATE_SINGLEQUOTED: // Single-quoted string
switch (ch) {
case '\'':
state = STATE_ARGUMENT;
break;
default:
curarg += ch;
}
break;
case STATE_DOUBLEQUOTED: // Double-quoted string
switch (ch) {
case '"':
state = STATE_ARGUMENT;
break;
case '\\':
state = STATE_ESCAPE_DOUBLEQUOTED;
break;
default:
curarg += ch;
}
break;
case STATE_ESCAPE_OUTER: // '\' outside quotes
curarg += ch;
state = STATE_ARGUMENT;
break;
case STATE_ESCAPE_DOUBLEQUOTED: // '\' in double-quoted text
if (ch != '"' && ch != '\\')
curarg += '\\'; // keep '\' for everything but the quote and
// '\' itself
curarg += ch;
state = STATE_DOUBLEQUOTED;
break;
}
}
if (pstrFilteredOut) {
if (STATE_COMMAND_EXECUTED == state) {
assert(!stack.empty());
close_out_params();
}
*pstrFilteredOut = strCommand;
for (auto i = filter_ranges.rbegin(); i != filter_ranges.rend(); ++i) {
pstrFilteredOut->replace(i->first, i->second - i->first, "(…)");
}
}
switch (state) // final state
{
case STATE_COMMAND_EXECUTED:
if (lastResult.isStr())
strResult = lastResult.get_str();
else
strResult = lastResult.write(2);
// FALLTHROUGH
case STATE_ARGUMENT:
case STATE_EATING_SPACES:
return true;
default: // ERROR to end in one of the other states
return false;
}
}
void RPCExecutor::request(const QString &command) {
try {
std::string result;
std::string executableCommand = command.toStdString() + "\n";
if (!RPCConsole::RPCExecuteCommandLine(result, executableCommand)) {
Q_EMIT reply(RPCConsole::CMD_ERROR,
QString("Parse error: unbalanced ' or \""));
return;
}
Q_EMIT reply(RPCConsole::CMD_REPLY, QString::fromStdString(result));
} catch (UniValue &objError) {
try // Nice formatting for standard-format error
{
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
Q_EMIT reply(RPCConsole::CMD_ERROR,
QString::fromStdString(message) + " (code " +
QString::number(code) + ")");
} catch (const std::runtime_error &) {
// raised when converting to invalid type, i.e. missing code or
// message. Show raw JSON object.
Q_EMIT reply(RPCConsole::CMD_ERROR,
QString::fromStdString(objError.write()));
}
} catch (const std::exception &e) {
Q_EMIT reply(RPCConsole::CMD_ERROR,
QString("Error: ") + QString::fromStdString(e.what()));
}
}
RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent)
: QWidget(parent), ui(new Ui::RPCConsole), clientModel(0), historyPtr(0),
platformStyle(_platformStyle), peersTableContextMenu(0),
banTableContextMenu(0), consoleFontSize(0) {
ui->setupUi(this);
GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this);
ui->openDebugLogfileButton->setToolTip(
ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME)));
if (platformStyle->getImagesOnButtons()) {
ui->openDebugLogfileButton->setIcon(
platformStyle->SingleColorIcon(":/icons/export"));
}
ui->clearButton->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
ui->fontBiggerButton->setIcon(
platformStyle->SingleColorIcon(":/icons/fontbigger"));
ui->fontSmallerButton->setIcon(
platformStyle->SingleColorIcon(":/icons/fontsmaller"));
// Install event filter for up and down arrow
ui->lineEdit->installEventFilter(this);
ui->messagesWidget->installEventFilter(this);
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(ui->fontBiggerButton, SIGNAL(clicked()), this, SLOT(fontBigger()));
connect(ui->fontSmallerButton, SIGNAL(clicked()), this,
SLOT(fontSmaller()));
connect(ui->btnClearTrafficGraph, SIGNAL(clicked()), ui->trafficGraph,
SLOT(clear()));
// set library version labels
#ifdef ENABLE_WALLET
ui->berkeleyDBVersion->setText(DbEnv::version(0, 0, 0));
#else
ui->label_berkeleyDBVersion->hide();
ui->berkeleyDBVersion->hide();
#endif
// Register RPC timer interface
rpcTimerInterface = new QtRPCTimerInterface();
// avoid accidentally overwriting an existing, non QTThread
// based timer interface
RPCSetTimerInterfaceIfUnset(rpcTimerInterface);
setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS);
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
QSettings settings;
consoleFontSize =
settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize())
.toInt();
clear();
}
RPCConsole::~RPCConsole() {
GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this);
RPCUnsetTimerInterface(rpcTimerInterface);
delete rpcTimerInterface;
delete ui;
}
bool RPCConsole::eventFilter(QObject *obj, QEvent *event) {
if (event->type() == QEvent::KeyPress) // Special key handling
{
QKeyEvent *keyevt = static_cast<QKeyEvent *>(event);
int key = keyevt->key();
Qt::KeyboardModifiers mod = keyevt->modifiers();
switch (key) {
case Qt::Key_Up:
if (obj == ui->lineEdit) {
browseHistory(-1);
return true;
}
break;
case Qt::Key_Down:
if (obj == ui->lineEdit) {
browseHistory(1);
return true;
}
break;
case Qt::Key_PageUp: /* pass paging keys to messages widget */
case Qt::Key_PageDown:
if (obj == ui->lineEdit) {
QApplication::postEvent(ui->messagesWidget,
new QKeyEvent(*keyevt));
return true;
}
break;
case Qt::Key_Return:
case Qt::Key_Enter:
// forward these events to lineEdit
if (obj == autoCompleter->popup()) {
QApplication::postEvent(ui->lineEdit,
new QKeyEvent(*keyevt));
return true;
}
break;
default:
// Typing in messages widget brings focus to line edit, and
// redirects key there. Exclude most combinations and keys that
// emit no text, except paste shortcuts.
if (obj == ui->messagesWidget &&
((!mod && !keyevt->text().isEmpty() &&
key != Qt::Key_Tab) ||
((mod & Qt::ControlModifier) && key == Qt::Key_V) ||
((mod & Qt::ShiftModifier) && key == Qt::Key_Insert))) {
ui->lineEdit->setFocus();
QApplication::postEvent(ui->lineEdit,
new QKeyEvent(*keyevt));
return true;
}
}
}
return QWidget::eventFilter(obj, event);
}
void RPCConsole::setClientModel(ClientModel *model) {
clientModel = model;
ui->trafficGraph->setClientModel(model);
if (model && clientModel->getPeerTableModel() &&
clientModel->getBanTableModel()) {
// Keep up to date with client
setNumConnections(model->getNumConnections());
connect(model, SIGNAL(numConnectionsChanged(int)), this,
SLOT(setNumConnections(int)));
setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(),
model->getVerificationProgress(nullptr), false);
connect(model, SIGNAL(numBlocksChanged(int, QDateTime, double, bool)),
this, SLOT(setNumBlocks(int, QDateTime, double, bool)));
updateNetworkState();
connect(model, SIGNAL(networkActiveChanged(bool)), this,
SLOT(setNetworkActive(bool)));
updateTrafficStats(model->getTotalBytesRecv(),
model->getTotalBytesSent());
connect(model, SIGNAL(bytesChanged(quint64, quint64)), this,
SLOT(updateTrafficStats(quint64, quint64)));
connect(model, SIGNAL(mempoolSizeChanged(long, size_t)), this,
SLOT(setMempoolSize(long, size_t)));
// set up peer table
ui->peerWidget->setModel(model->getPeerTableModel());
ui->peerWidget->verticalHeader()->hide();
ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->peerWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui->peerWidget->setContextMenuPolicy(Qt::CustomContextMenu);
ui->peerWidget->setColumnWidth(PeerTableModel::Address,
ADDRESS_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Subversion,
SUBVERSION_COLUMN_WIDTH);
ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH);
ui->peerWidget->horizontalHeader()->setStretchLastSection(true);
// create peer table context menu actions
QAction *disconnectAction = new QAction(tr("&Disconnect"), this);
QAction *banAction1h =
new QAction(tr("Ban for") + " " + tr("1 &hour"), this);
QAction *banAction24h =
new QAction(tr("Ban for") + " " + tr("1 &day"), this);
QAction *banAction7d =
new QAction(tr("Ban for") + " " + tr("1 &week"), this);
QAction *banAction365d =
new QAction(tr("Ban for") + " " + tr("1 &year"), this);
// create peer table context menu
peersTableContextMenu = new QMenu(this);
peersTableContextMenu->addAction(disconnectAction);
peersTableContextMenu->addAction(banAction1h);
peersTableContextMenu->addAction(banAction24h);
peersTableContextMenu->addAction(banAction7d);
peersTableContextMenu->addAction(banAction365d);
// Add a signal mapping to allow dynamic context menu arguments. We need
// to use int (instead of int64_t), because signal mapper only supports
// int or objects, which is okay because max bantime (1 year) is <
// int_max.
QSignalMapper *signalMapper = new QSignalMapper(this);
signalMapper->setMapping(banAction1h, 60 * 60);
signalMapper->setMapping(banAction24h, 60 * 60 * 24);
signalMapper->setMapping(banAction7d, 60 * 60 * 24 * 7);
signalMapper->setMapping(banAction365d, 60 * 60 * 24 * 365);
connect(banAction1h, SIGNAL(triggered()), signalMapper, SLOT(map()));
connect(banAction24h, SIGNAL(triggered()), signalMapper, SLOT(map()));
connect(banAction7d, SIGNAL(triggered()), signalMapper, SLOT(map()));
connect(banAction365d, SIGNAL(triggered()), signalMapper, SLOT(map()));
connect(signalMapper, SIGNAL(mapped(int)), this,
SLOT(banSelectedNode(int)));
// peer table context menu signals
connect(ui->peerWidget,
SIGNAL(customContextMenuRequested(const QPoint &)), this,
SLOT(showPeersTableContextMenu(const QPoint &)));
connect(disconnectAction, SIGNAL(triggered()), this,
SLOT(disconnectSelectedNode()));
// peer table signal handling - update peer details when selecting new
// node
connect(ui->peerWidget->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection &,
const QItemSelection &)),
this, SLOT(peerSelected(const QItemSelection &,
const QItemSelection &)));
// peer table signal handling - update peer details when new nodes are
// added to the model
connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this,
SLOT(peerLayoutChanged()));
// peer table signal handling - cache selected node ids
connect(model->getPeerTableModel(), SIGNAL(layoutAboutToBeChanged()),
this, SLOT(peerLayoutAboutToChange()));
// set up ban table
ui->banlistWidget->setModel(model->getBanTableModel());
ui->banlistWidget->verticalHeader()->hide();
ui->banlistWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->banlistWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
ui->banlistWidget->setSelectionMode(QAbstractItemView::SingleSelection);
ui->banlistWidget->setContextMenuPolicy(Qt::CustomContextMenu);
ui->banlistWidget->setColumnWidth(BanTableModel::Address,
BANSUBNET_COLUMN_WIDTH);
ui->banlistWidget->setColumnWidth(BanTableModel::Bantime,
BANTIME_COLUMN_WIDTH);
ui->banlistWidget->horizontalHeader()->setStretchLastSection(true);
// create ban table context menu action
QAction *unbanAction = new QAction(tr("&Unban"), this);
// create ban table context menu
banTableContextMenu = new QMenu(this);
banTableContextMenu->addAction(unbanAction);
// ban table context menu signals
connect(ui->banlistWidget,
SIGNAL(customContextMenuRequested(const QPoint &)), this,
SLOT(showBanTableContextMenu(const QPoint &)));
connect(unbanAction, SIGNAL(triggered()), this,
SLOT(unbanSelectedNode()));
// ban table signal handling - clear peer details when clicking a peer
// in the ban table
connect(ui->banlistWidget, SIGNAL(clicked(const QModelIndex &)), this,
SLOT(clearSelectedNode()));
// ban table signal handling - ensure ban table is shown or hidden (if
// empty)
connect(model->getBanTableModel(), SIGNAL(layoutChanged()), this,
SLOT(showOrHideBanTableIfRequired()));
showOrHideBanTableIfRequired();
// Provide initial values
ui->clientVersion->setText(model->formatFullVersion());
ui->clientUserAgent->setText(model->formatSubVersion());
ui->dataDir->setText(model->dataDir());
ui->startupTime->setText(model->formatClientStartupTime());
ui->networkName->setText(
QString::fromStdString(Params().NetworkIDString()));
// Setup autocomplete and attach it
QStringList wordList;
std::vector<std::string> commandList = tableRPC.listCommands();
for (size_t i = 0; i < commandList.size(); ++i) {
wordList << commandList[i].c_str();
}
autoCompleter = new QCompleter(wordList, this);
ui->lineEdit->setCompleter(autoCompleter);
autoCompleter->popup()->installEventFilter(this);
// Start thread to execute RPC commands.
startExecutor();
}
if (!model) {
// Client model is being set to 0, this means shutdown() is about to be
// called. Make sure we clean up the executor thread
Q_EMIT stopExecutor();
thread.wait();
}
}
static QString categoryClass(int category) {
switch (category) {
case RPCConsole::CMD_REQUEST:
return "cmd-request";
break;
case RPCConsole::CMD_REPLY:
return "cmd-reply";
break;
case RPCConsole::CMD_ERROR:
return "cmd-error";
break;
default:
return "misc";
}
}
void RPCConsole::fontBigger() {
setFontSize(consoleFontSize + 1);
}
void RPCConsole::fontSmaller() {
setFontSize(consoleFontSize - 1);
}
void RPCConsole::setFontSize(int newSize) {
QSettings settings;
// don't allow a insane font size
if (newSize < FONT_RANGE.width() || newSize > FONT_RANGE.height()) return;
// temp. store the console content
QString str = ui->messagesWidget->toHtml();
// replace font tags size in current content
str.replace(QString("font-size:%1pt").arg(consoleFontSize),
QString("font-size:%1pt").arg(newSize));
// store the new font size
consoleFontSize = newSize;
settings.setValue(fontSizeSettingsKey, consoleFontSize);
// clear console (reset icon sizes, default stylesheet) and re-add the
// content
float oldPosFactor = 1.0 /
ui->messagesWidget->verticalScrollBar()->maximum() *
ui->messagesWidget->verticalScrollBar()->value();
clear(false);
ui->messagesWidget->setHtml(str);
ui->messagesWidget->verticalScrollBar()->setValue(
oldPosFactor * ui->messagesWidget->verticalScrollBar()->maximum());
}
void RPCConsole::clear(bool clearHistory) {
ui->messagesWidget->clear();
if (clearHistory) {
history.clear();
historyPtr = 0;
}
ui->lineEdit->clear();
ui->lineEdit->setFocus();
// Add smoothly scaled icon images.
// (when using width/height on an img, Qt uses nearest instead of linear
// interpolation)
for (int i = 0; ICON_MAPPING[i].url; ++i) {
ui->messagesWidget->document()->addResource(
QTextDocument::ImageResource, QUrl(ICON_MAPPING[i].url),
platformStyle->SingleColorImage(ICON_MAPPING[i].source)
.scaled(QSize(consoleFontSize * 2, consoleFontSize * 2),
Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
}
// Set default style sheet
QFontInfo fixedFontInfo(GUIUtil::fixedPitchFont());
ui->messagesWidget->document()->setDefaultStyleSheet(
QString("table { }"
"td.time { color: #808080; font-size: %2; padding-top: 3px; } "
"td.message { font-family: %1; font-size: %2; "
"white-space:pre-wrap; } "
"td.cmd-request { color: #006060; } "
"td.cmd-error { color: red; } "
".secwarning { color: red; }"
"b { color: #006060; } ")
.arg(fixedFontInfo.family(), QString("%1pt").arg(consoleFontSize)));
message(CMD_REPLY,
(tr("Welcome to the %1 RPC console.").arg(tr(PACKAGE_NAME)) +
"<br>" + tr("Use up and down arrows to navigate history, and "
"<b>Ctrl-L</b> to clear screen.") +
"<br>" +
tr("Type <b>help</b> for an overview of available commands.")) +
"<br><span class=\"secwarning\">" +
tr("WARNING: Scammers have been active, telling users to type "
"commands here, stealing their wallet contents. Do not use "
"this console without fully understanding the ramification "
"of a command.") +
"</span>",
true);
}
void RPCConsole::keyPressEvent(QKeyEvent *event) {
if (windowType() != Qt::Widget && event->key() == Qt::Key_Escape) {
close();
}
}
void RPCConsole::message(int category, const QString &message, bool html) {
QTime time = QTime::currentTime();
QString timeString = time.toString();
QString out;
out += "<table><tr><td class=\"time\" width=\"65\">" + timeString + "</td>";
out += "<td class=\"icon\" width=\"32\"><img src=\"" +
categoryClass(category) + "\"></td>";
out += "<td class=\"message " + categoryClass(category) +
"\" valign=\"middle\">";
if (html)
out += message;
else
out += GUIUtil::HtmlEscape(message, false);
out += "</td></tr></table>";
ui->messagesWidget->append(out);
}
void RPCConsole::updateNetworkState() {
QString connections =
QString::number(clientModel->getNumConnections()) + " (";
connections +=
tr("In:") + " " +
QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / ";
connections +=
tr("Out:") + " " +
QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")";
if (!clientModel->getNetworkActive()) {
connections += " (" + tr("Network activity disabled") + ")";
}
ui->numberOfConnections->setText(connections);
}
void RPCConsole::setNumConnections(int count) {
if (!clientModel) return;
updateNetworkState();
}
void RPCConsole::setNetworkActive(bool networkActive) {
updateNetworkState();
}
void RPCConsole::setNumBlocks(int count, const QDateTime &blockDate,
double nVerificationProgress, bool headers) {
if (!headers) {
ui->numberOfBlocks->setText(QString::number(count));
ui->lastBlockTime->setText(blockDate.toString());
}
}
void RPCConsole::setMempoolSize(long numberOfTxs, size_t dynUsage) {
ui->mempoolNumberTxs->setText(QString::number(numberOfTxs));
if (dynUsage < 1000000)
ui->mempoolSize->setText(QString::number(dynUsage / 1000.0, 'f', 2) +
" KB");
else
ui->mempoolSize->setText(QString::number(dynUsage / 1000000.0, 'f', 2) +
" MB");
}
void RPCConsole::on_lineEdit_returnPressed() {
QString cmd = ui->lineEdit->text();
if (!cmd.isEmpty()) {
std::string strFilteredCmd;
try {
std::string dummy;
if (!RPCParseCommandLine(dummy, cmd.toStdString(), false,
&strFilteredCmd)) {
// Failed to parse command, so we cannot even filter it for the
// history
throw std::runtime_error("Invalid command line");
}
} catch (const std::exception &e) {
QMessageBox::critical(this, "Error",
QString("Error: ") +
QString::fromStdString(e.what()));
return;
}
ui->lineEdit->clear();
cmdBeforeBrowsing = QString();
message(CMD_REQUEST, cmd);
Q_EMIT cmdRequest(cmd);
cmd = QString::fromStdString(strFilteredCmd);
// Remove command, if already in history
history.removeOne(cmd);
// Append command to history
history.append(cmd);
// Enforce maximum history size
while (history.size() > CONSOLE_HISTORY)
history.removeFirst();
// Set pointer to end of history
historyPtr = history.size();
// Scroll console view to end
scrollToEnd();
}
}
void RPCConsole::browseHistory(int offset) {
// store current text when start browsing through the history
if (historyPtr == history.size()) {
cmdBeforeBrowsing = ui->lineEdit->text();
}
historyPtr += offset;
if (historyPtr < 0) historyPtr = 0;
if (historyPtr > history.size()) historyPtr = history.size();
QString cmd;
if (historyPtr < history.size())
cmd = history.at(historyPtr);
else if (!cmdBeforeBrowsing.isNull()) {
cmd = cmdBeforeBrowsing;
}
ui->lineEdit->setText(cmd);
}
void RPCConsole::startExecutor() {
RPCExecutor *executor = new RPCExecutor();
executor->moveToThread(&thread);
// Replies from executor object must go to this object
connect(executor, SIGNAL(reply(int, QString)), this,
SLOT(message(int, QString)));
// Requests from this object must go to executor
connect(this, SIGNAL(cmdRequest(QString)), executor,
SLOT(request(QString)));
// On stopExecutor signal
// - quit the Qt event loop in the execution thread
connect(this, SIGNAL(stopExecutor()), &thread, SLOT(quit()));
// - queue executor for deletion (in execution thread)
connect(&thread, SIGNAL(finished()), executor, SLOT(deleteLater()),
Qt::DirectConnection);
// Default implementation of QThread::run() simply spins up an event loop in
// the thread, which is what we want.
thread.start();
}
void RPCConsole::on_tabWidget_currentChanged(int index) {
if (ui->tabWidget->widget(index) == ui->tab_console)
ui->lineEdit->setFocus();
else if (ui->tabWidget->widget(index) != ui->tab_peers)
clearSelectedNode();
}
void RPCConsole::on_openDebugLogfileButton_clicked() {
GUIUtil::openDebugLogfile();
}
void RPCConsole::scrollToEnd() {
QScrollBar *scrollbar = ui->messagesWidget->verticalScrollBar();
scrollbar->setValue(scrollbar->maximum());
}
void RPCConsole::on_sldGraphRange_valueChanged(int value) {
const int multiplier = 5; // each position on the slider represents 5 min
int mins = value * multiplier;
setTrafficGraphRange(mins);
}
QString RPCConsole::FormatBytes(quint64 bytes) {
if (bytes < 1024) return QString(tr("%1 B")).arg(bytes);
if (bytes < 1024 * 1024) return QString(tr("%1 KB")).arg(bytes / 1024);
if (bytes < 1024 * 1024 * 1024)
return QString(tr("%1 MB")).arg(bytes / 1024 / 1024);
return QString(tr("%1 GB")).arg(bytes / 1024 / 1024 / 1024);
}
void RPCConsole::setTrafficGraphRange(int mins) {
ui->trafficGraph->setGraphRangeMins(mins);
ui->lblGraphRange->setText(GUIUtil::formatDurationStr(mins * 60));
}
void RPCConsole::updateTrafficStats(quint64 totalBytesIn,
quint64 totalBytesOut) {
ui->lblBytesIn->setText(FormatBytes(totalBytesIn));
ui->lblBytesOut->setText(FormatBytes(totalBytesOut));
}
void RPCConsole::peerSelected(const QItemSelection &selected,
const QItemSelection &deselected) {
Q_UNUSED(deselected);
if (!clientModel || !clientModel->getPeerTableModel() ||
selected.indexes().isEmpty())
return;
const CNodeCombinedStats *stats =
clientModel->getPeerTableModel()->getNodeStats(
selected.indexes().first().row());
if (stats) updateNodeDetail(stats);
}
void RPCConsole::peerLayoutAboutToChange() {
QModelIndexList selected =
ui->peerWidget->selectionModel()->selectedIndexes();
cachedNodeids.clear();
for (int i = 0; i < selected.size(); i++) {
const CNodeCombinedStats *stats =
clientModel->getPeerTableModel()->getNodeStats(
selected.at(i).row());
cachedNodeids.append(stats->nodeStats.nodeid);
}
}
void RPCConsole::peerLayoutChanged() {
if (!clientModel || !clientModel->getPeerTableModel()) return;
const CNodeCombinedStats *stats = nullptr;
bool fUnselect = false;
bool fReselect = false;
if (cachedNodeids.empty()) // no node selected yet
return;
// find the currently selected row
int selectedRow = -1;
QModelIndexList selectedModelIndex =
ui->peerWidget->selectionModel()->selectedIndexes();
if (!selectedModelIndex.isEmpty()) {
selectedRow = selectedModelIndex.first().row();
}
// check if our detail node has a row in the table (it may not necessarily
// be at selectedRow since its position can change after a layout change)
int detailNodeRow =
clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeids.first());
if (detailNodeRow < 0) {
// detail node disappeared from table (node disconnected)
fUnselect = true;
} else {
if (detailNodeRow != selectedRow) {
// detail node moved position
fUnselect = true;
fReselect = true;
}
// get fresh stats on the detail node.
stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
}
if (fUnselect && selectedRow >= 0) {
clearSelectedNode();
}
if (fReselect) {
for (int i = 0; i < cachedNodeids.size(); i++) {
ui->peerWidget->selectRow(
clientModel->getPeerTableModel()->getRowByNodeId(
cachedNodeids.at(i)));
}
}
if (stats) updateNodeDetail(stats);
}
void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) {
// update the detail ui with latest node information
QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName) +
" ");
peerAddrDetails +=
tr("(node id: %1)").arg(QString::number(stats->nodeStats.nodeid));
if (!stats->nodeStats.addrLocal.empty())
peerAddrDetails += "<br />" +
tr("via %1").arg(QString::fromStdString(
stats->nodeStats.addrLocal));
ui->peerHeading->setText(peerAddrDetails);
ui->peerServices->setText(
GUIUtil::formatServicesStr(stats->nodeStats.nServices));
ui->peerLastSend->setText(
stats->nodeStats.nLastSend
? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() -
stats->nodeStats.nLastSend)
: tr("never"));
ui->peerLastRecv->setText(
stats->nodeStats.nLastRecv
? GUIUtil::formatDurationStr(GetSystemTimeInSeconds() -
stats->nodeStats.nLastRecv)
: tr("never"));
ui->peerBytesSent->setText(FormatBytes(stats->nodeStats.nSendBytes));
ui->peerBytesRecv->setText(FormatBytes(stats->nodeStats.nRecvBytes));
ui->peerConnTime->setText(GUIUtil::formatDurationStr(
GetSystemTimeInSeconds() - stats->nodeStats.nTimeConnected));
ui->peerPingTime->setText(
GUIUtil::formatPingTime(stats->nodeStats.dPingTime));
ui->peerPingWait->setText(
GUIUtil::formatPingTime(stats->nodeStats.dPingWait));
ui->peerMinPing->setText(
GUIUtil::formatPingTime(stats->nodeStats.dMinPing));
ui->timeoffset->setText(
GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset));
ui->peerVersion->setText(
QString("%1").arg(QString::number(stats->nodeStats.nVersion)));
ui->peerSubversion->setText(
QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound")
: tr("Outbound"));
ui->peerHeight->setText(
QString("%1").arg(QString::number(stats->nodeStats.nStartingHeight)));
ui->peerWhitelisted->setText(stats->nodeStats.fWhitelisted ? tr("Yes")
: tr("No"));
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
if (stats->fNodeStateStatsAvailable) {
// Ban score is init to 0
ui->peerBanScore->setText(
QString("%1").arg(stats->nodeStateStats.nMisbehavior));
// Sync height is init to -1
if (stats->nodeStateStats.nSyncHeight > -1)
ui->peerSyncHeight->setText(
QString("%1").arg(stats->nodeStateStats.nSyncHeight));
else
ui->peerSyncHeight->setText(tr("Unknown"));
// Common height is init to -1
if (stats->nodeStateStats.nCommonHeight > -1)
ui->peerCommonHeight->setText(
QString("%1").arg(stats->nodeStateStats.nCommonHeight));
else
ui->peerCommonHeight->setText(tr("Unknown"));
}
ui->detailWidget->show();
}
void RPCConsole::resizeEvent(QResizeEvent *event) {
QWidget::resizeEvent(event);
}
void RPCConsole::showEvent(QShowEvent *event) {
QWidget::showEvent(event);
if (!clientModel || !clientModel->getPeerTableModel()) return;
// start PeerTableModel auto refresh
clientModel->getPeerTableModel()->startAutoRefresh();
}
void RPCConsole::hideEvent(QHideEvent *event) {
QWidget::hideEvent(event);
if (!clientModel || !clientModel->getPeerTableModel()) return;
// stop PeerTableModel auto refresh
clientModel->getPeerTableModel()->stopAutoRefresh();
}
void RPCConsole::showPeersTableContextMenu(const QPoint &point) {
QModelIndex index = ui->peerWidget->indexAt(point);
if (index.isValid()) peersTableContextMenu->exec(QCursor::pos());
}
void RPCConsole::showBanTableContextMenu(const QPoint &point) {
QModelIndex index = ui->banlistWidget->indexAt(point);
if (index.isValid()) banTableContextMenu->exec(QCursor::pos());
}
void RPCConsole::disconnectSelectedNode() {
if (!g_connman) return;
// Get selected peer addresses
QList<QModelIndex> nodes =
GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
for (int i = 0; i < nodes.count(); i++) {
// Get currently selected peer address
NodeId id = nodes.at(i).data().toLongLong();
// Find the node, disconnect it and clear the selected node
if (g_connman->DisconnectNode(id)) clearSelectedNode();
}
}
void RPCConsole::banSelectedNode(int bantime) {
if (!clientModel || !g_connman) return;
// Get selected peer addresses
QList<QModelIndex> nodes =
GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId);
for (int i = 0; i < nodes.count(); i++) {
// Get currently selected peer address
NodeId id = nodes.at(i).data().toLongLong();
// Get currently selected peer address
int detailNodeRow =
clientModel->getPeerTableModel()->getRowByNodeId(id);
if (detailNodeRow < 0) return;
// Find possible nodes, ban it and clear the selected node
const CNodeCombinedStats *stats =
clientModel->getPeerTableModel()->getNodeStats(detailNodeRow);
if (stats) {
g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded,
bantime);
}
}
clearSelectedNode();
clientModel->getBanTableModel()->refresh();
}
void RPCConsole::unbanSelectedNode() {
if (!clientModel) return;
// Get selected ban addresses
QList<QModelIndex> nodes =
GUIUtil::getEntryData(ui->banlistWidget, BanTableModel::Address);
for (int i = 0; i < nodes.count(); i++) {
// Get currently selected ban address
QString strNode = nodes.at(i).data().toString();
CSubNet possibleSubnet;
LookupSubNet(strNode.toStdString().c_str(), possibleSubnet);
if (possibleSubnet.IsValid() && g_connman) {
g_connman->Unban(possibleSubnet);
clientModel->getBanTableModel()->refresh();
}
}
}
void RPCConsole::clearSelectedNode() {
ui->peerWidget->selectionModel()->clearSelection();
cachedNodeids.clear();
ui->detailWidget->hide();
ui->peerHeading->setText(tr("Select a peer to view detailed information."));
}
void RPCConsole::showOrHideBanTableIfRequired() {
if (!clientModel) return;
bool visible = clientModel->getBanTableModel()->shouldShow();
ui->banlistWidget->setVisible(visible);
ui->banHeading->setVisible(visible);
}
void RPCConsole::setTabFocus(enum TabTypes tabType) {
ui->tabWidget->setCurrentIndex(tabType);
}
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 3c2a6e6d47..29d11871be 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -1,522 +1,522 @@
// Copyright (c) 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.
#include "rpc/server.h"
#include "base58.h"
#include "config.h"
#include "init.h"
#include "random.h"
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include <univalue.h>
#include <boost/algorithm/string/case_conv.hpp> // for to_upper()
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/thread.hpp>
#include <memory> // for unique_ptr
#include <set>
#include <unordered_map>
using namespace RPCServer;
static bool fRPCRunning = false;
static bool fRPCInWarmup = true;
static std::string rpcWarmupStatus("RPC server started");
static CCriticalSection cs_rpcWarmup;
/* Timer-creating functions */
static RPCTimerInterface *timerInterface = nullptr;
/* Map of name to timer. */
static std::map<std::string, std::unique_ptr<RPCTimerBase>> deadlineTimers;
static struct CRPCSignals {
boost::signals2::signal<void()> Started;
boost::signals2::signal<void()> Stopped;
boost::signals2::signal<void(const CRPCCommand &)> PreCommand;
boost::signals2::signal<void(const CRPCCommand &)> PostCommand;
} g_rpcSignals;
-void RPCServer::OnStarted(boost::function<void()> slot) {
+void RPCServer::OnStarted(std::function<void()> slot) {
g_rpcSignals.Started.connect(slot);
}
-void RPCServer::OnStopped(boost::function<void()> slot) {
+void RPCServer::OnStopped(std::function<void()> slot) {
g_rpcSignals.Stopped.connect(slot);
}
-void RPCServer::OnPreCommand(boost::function<void(const CRPCCommand &)> slot) {
+void RPCServer::OnPreCommand(std::function<void(const CRPCCommand &)> slot) {
g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
}
-void RPCServer::OnPostCommand(boost::function<void(const CRPCCommand &)> slot) {
+void RPCServer::OnPostCommand(std::function<void(const CRPCCommand &)> slot) {
g_rpcSignals.PostCommand.connect(boost::bind(slot, _1));
}
void RPCTypeCheck(const UniValue &params,
const std::list<UniValue::VType> &typesExpected,
bool fAllowNull) {
unsigned int i = 0;
for (UniValue::VType t : typesExpected) {
if (params.size() <= i) break;
const UniValue &v = params[i];
if (!(fAllowNull && v.isNull())) {
RPCTypeCheckArgument(v, t);
}
i++;
}
}
void RPCTypeCheckArgument(const UniValue &value, UniValue::VType typeExpected) {
if (value.type() != typeExpected) {
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s",
uvTypeName(typeExpected),
uvTypeName(value.type())));
}
}
void RPCTypeCheckObj(const UniValue &o,
const std::map<std::string, UniValueType> &typesExpected,
bool fAllowNull, bool fStrict) {
for (const auto &t : typesExpected) {
const UniValue &v = find_value(o, t.first);
if (!fAllowNull && v.isNull())
throw JSONRPCError(RPC_TYPE_ERROR,
strprintf("Missing %s", t.first));
if (!(t.second.typeAny || v.type() == t.second.type ||
(fAllowNull && v.isNull()))) {
std::string err = strprintf("Expected type %s for %s, got %s",
uvTypeName(t.second.type), t.first,
uvTypeName(v.type()));
throw JSONRPCError(RPC_TYPE_ERROR, err);
}
}
if (fStrict) {
for (const std::string &k : o.getKeys()) {
if (typesExpected.count(k) == 0) {
std::string err = strprintf("Unexpected key %s", k);
throw JSONRPCError(RPC_TYPE_ERROR, err);
}
}
}
}
CAmount AmountFromValue(const UniValue &value) {
if (!value.isNum() && !value.isStr())
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
CAmount amount;
if (!ParseFixedPoint(value.getValStr(), 8, &amount))
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
if (!MoneyRange(amount))
throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
return amount;
}
UniValue ValueFromAmount(const CAmount &amount) {
bool sign = amount < 0;
int64_t n_abs = (sign ? -amount : amount);
int64_t quotient = n_abs / COIN;
int64_t remainder = n_abs % COIN;
return UniValue(UniValue::VNUM, strprintf("%s%d.%08d", sign ? "-" : "",
quotient, remainder));
}
uint256 ParseHashV(const UniValue &v, std::string strName) {
std::string strHex;
if (v.isStr()) strHex = v.get_str();
// Note: IsHex("") is false
if (!IsHex(strHex))
throw JSONRPCError(RPC_INVALID_PARAMETER,
strName + " must be hexadecimal string (not '" +
strHex + "')");
if (64 != strHex.length())
throw JSONRPCError(RPC_INVALID_PARAMETER,
strprintf("%s must be of length %d (not %d)",
strName, 64, strHex.length()));
uint256 result;
result.SetHex(strHex);
return result;
}
uint256 ParseHashO(const UniValue &o, std::string strKey) {
return ParseHashV(find_value(o, strKey), strKey);
}
std::vector<uint8_t> ParseHexV(const UniValue &v, std::string strName) {
std::string strHex;
if (v.isStr()) strHex = v.get_str();
if (!IsHex(strHex))
throw JSONRPCError(RPC_INVALID_PARAMETER,
strName + " must be hexadecimal string (not '" +
strHex + "')");
return ParseHex(strHex);
}
std::vector<uint8_t> ParseHexO(const UniValue &o, std::string strKey) {
return ParseHexV(find_value(o, strKey), strKey);
}
/**
* Note: This interface may still be subject to change.
*/
std::string CRPCTable::help(Config &config,
const std::string &strCommand) const {
std::string strRet;
std::string category;
std::set<rpcfn_type> setDone;
std::vector<std::pair<std::string, const CRPCCommand *>> vCommands;
for (std::map<std::string, const CRPCCommand *>::const_iterator mi =
mapCommands.begin();
mi != mapCommands.end(); ++mi)
vCommands.push_back(
std::make_pair(mi->second->category + mi->first, mi->second));
sort(vCommands.begin(), vCommands.end());
for (const std::pair<std::string, const CRPCCommand *> &command :
vCommands) {
const CRPCCommand *pcmd = command.second;
std::string strMethod = pcmd->name;
// We already filter duplicates, but these deprecated screw up the sort
// order
if (strMethod.find("label") != std::string::npos) continue;
if ((strCommand != "" || pcmd->category == "hidden") &&
strMethod != strCommand)
continue;
try {
JSONRPCRequest jreq;
jreq.fHelp = true;
rpcfn_type pfn = pcmd->actor;
if (setDone.insert(pfn).second) pfn(config, jreq);
} catch (const std::exception &e) {
// Help text is returned in an exception
std::string strHelp = std::string(e.what());
if (strCommand == "") {
if (strHelp.find('\n') != std::string::npos)
strHelp = strHelp.substr(0, strHelp.find('\n'));
if (category != pcmd->category) {
if (!category.empty()) strRet += "\n";
category = pcmd->category;
std::string firstLetter = category.substr(0, 1);
boost::to_upper(firstLetter);
strRet +=
"== " + firstLetter + category.substr(1) + " ==\n";
}
}
strRet += strHelp + "\n";
}
}
if (strRet == "")
strRet = strprintf("help: unknown command: %s\n", strCommand);
strRet = strRet.substr(0, strRet.size() - 1);
return strRet;
}
static UniValue help(Config &config, const JSONRPCRequest &jsonRequest) {
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error(
"help ( \"command\" )\n"
"\nList all commands, or get help for a specified command.\n"
"\nArguments:\n"
"1. \"command\" (string, optional) The command to get help on\n"
"\nResult:\n"
"\"text\" (string) The help text\n");
std::string strCommand;
if (jsonRequest.params.size() > 0)
strCommand = jsonRequest.params[0].get_str();
return tableRPC.help(config, strCommand);
}
static UniValue stop(const Config &config, const JSONRPCRequest &jsonRequest) {
// Accept the deprecated and ignored 'detach' boolean argument
if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
throw std::runtime_error("stop\n"
"\nStop Bitcoin server.");
// Event loop will exit after current HTTP requests have been handled, so
// this reply will get back to the client.
StartShutdown();
return "Bitcoin server stopping";
}
/**
* Call Table
*/
// clang-format off
static const CRPCCommand vRPCCommands[] = {
// category name actor (function) okSafe argNames
// ------------------- ------------------------ ---------------------- ------ ----------
/* Overall control/query calls */
{ "control", "help", help, true, {"command"} },
{ "control", "stop", stop, true, {} },
};
// clang-format on
CRPCTable::CRPCTable() {
unsigned int vcidx;
for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0]));
vcidx++) {
const CRPCCommand *pcmd;
pcmd = &vRPCCommands[vcidx];
mapCommands[pcmd->name] = pcmd;
}
}
const CRPCCommand *CRPCTable::operator[](const std::string &name) const {
std::map<std::string, const CRPCCommand *>::const_iterator it =
mapCommands.find(name);
if (it == mapCommands.end()) return nullptr;
return (*it).second;
}
bool CRPCTable::appendCommand(const std::string &name,
const CRPCCommand *pcmd) {
if (IsRPCRunning()) return false;
// don't allow overwriting for now
std::map<std::string, const CRPCCommand *>::const_iterator it =
mapCommands.find(name);
if (it != mapCommands.end()) return false;
mapCommands[name] = pcmd;
return true;
}
bool StartRPC() {
LogPrint("rpc", "Starting RPC\n");
fRPCRunning = true;
g_rpcSignals.Started();
return true;
}
void InterruptRPC() {
LogPrint("rpc", "Interrupting RPC\n");
// Interrupt e.g. running longpolls
fRPCRunning = false;
}
void StopRPC() {
LogPrint("rpc", "Stopping RPC\n");
deadlineTimers.clear();
DeleteAuthCookie();
g_rpcSignals.Stopped();
}
bool IsRPCRunning() {
return fRPCRunning;
}
void SetRPCWarmupStatus(const std::string &newStatus) {
LOCK(cs_rpcWarmup);
rpcWarmupStatus = newStatus;
}
void SetRPCWarmupFinished() {
LOCK(cs_rpcWarmup);
assert(fRPCInWarmup);
fRPCInWarmup = false;
}
bool RPCIsInWarmup(std::string *outStatus) {
LOCK(cs_rpcWarmup);
if (outStatus) *outStatus = rpcWarmupStatus;
return fRPCInWarmup;
}
void JSONRPCRequest::parse(const UniValue &valRequest) {
// Parse request
if (!valRequest.isObject())
throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
const UniValue &request = valRequest.get_obj();
// Parse id now so errors from here on will have the id
id = find_value(request, "id");
// Parse method
UniValue valMethod = find_value(request, "method");
if (valMethod.isNull())
throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
if (!valMethod.isStr())
throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
if (strMethod != "getblocktemplate")
LogPrint("rpc", "ThreadRPCServer method=%s\n",
SanitizeString(strMethod));
// Parse params
UniValue valParams = find_value(request, "params");
if (valParams.isArray() || valParams.isObject())
params = valParams;
else if (valParams.isNull())
params = UniValue(UniValue::VARR);
else
throw JSONRPCError(RPC_INVALID_REQUEST,
"Params must be an array or object");
}
static UniValue JSONRPCExecOne(Config &config, const UniValue &req) {
UniValue rpc_result(UniValue::VOBJ);
JSONRPCRequest jreq;
try {
jreq.parse(req);
UniValue result = tableRPC.execute(config, jreq);
rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
} catch (const UniValue &objError) {
rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
} catch (const std::exception &e) {
rpc_result = JSONRPCReplyObj(
NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
}
return rpc_result;
}
std::string JSONRPCExecBatch(Config &config, const UniValue &vReq) {
UniValue ret(UniValue::VARR);
for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) {
ret.push_back(JSONRPCExecOne(config, vReq[reqIdx]));
}
return ret.write() + "\n";
}
/**
* Process named arguments into a vector of positional arguments, based on the
* passed-in specification for the RPC call's arguments.
*/
static inline JSONRPCRequest
transformNamedArguments(const JSONRPCRequest &in,
const std::vector<std::string> &argNames) {
JSONRPCRequest out = in;
out.params = UniValue(UniValue::VARR);
// Build a map of parameters, and remove ones that have been processed, so
// that we can throw a focused error if there is an unknown one.
const std::vector<std::string> &keys = in.params.getKeys();
const std::vector<UniValue> &values = in.params.getValues();
std::unordered_map<std::string, const UniValue *> argsIn;
for (size_t i = 0; i < keys.size(); ++i) {
argsIn[keys[i]] = &values[i];
}
// Process expected parameters.
int hole = 0;
for (const std::string &argName : argNames) {
auto fr = argsIn.find(argName);
if (fr != argsIn.end()) {
for (int i = 0; i < hole; ++i) {
// Fill hole between specified parameters with JSON nulls, but
// not at the end (for backwards compatibility with calls that
// act based on number of specified parameters).
out.params.push_back(UniValue());
}
hole = 0;
out.params.push_back(*fr->second);
argsIn.erase(fr);
} else {
hole += 1;
}
}
// If there are still arguments in the argsIn map, this is an error.
if (!argsIn.empty()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Unknown named parameter " + argsIn.begin()->first);
}
// Return request with named arguments transformed to positional arguments
return out;
}
UniValue CRPCTable::execute(Config &config,
const JSONRPCRequest &request) const {
// Return immediately if in warmup
{
LOCK(cs_rpcWarmup);
if (fRPCInWarmup) throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
}
// Find method
const CRPCCommand *pcmd = tableRPC[request.strMethod];
if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
g_rpcSignals.PreCommand(*pcmd);
try {
// Execute, convert arguments to array if necessary
if (request.params.isObject()) {
return pcmd->actor(
config, transformNamedArguments(request, pcmd->argNames));
} else {
return pcmd->actor(config, request);
}
} catch (const std::exception &e) {
throw JSONRPCError(RPC_MISC_ERROR, e.what());
}
g_rpcSignals.PostCommand(*pcmd);
}
std::vector<std::string> CRPCTable::listCommands() const {
std::vector<std::string> commandList;
typedef std::map<std::string, const CRPCCommand *> commandMap;
std::transform(mapCommands.begin(), mapCommands.end(),
std::back_inserter(commandList),
boost::bind(&commandMap::value_type::first, _1));
return commandList;
}
std::string HelpExampleCli(const std::string &methodname,
const std::string &args) {
return "> bitcoin-cli " + methodname + " " + args + "\n";
}
std::string HelpExampleRpc(const std::string &methodname,
const std::string &args) {
return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", "
"\"id\":\"curltest\", "
"\"method\": \"" +
methodname + "\", \"params\": [" + args +
"] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
}
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) {
if (!timerInterface) timerInterface = iface;
}
void RPCSetTimerInterface(RPCTimerInterface *iface) {
timerInterface = iface;
}
void RPCUnsetTimerInterface(RPCTimerInterface *iface) {
if (timerInterface == iface) timerInterface = nullptr;
}
-void RPCRunLater(const std::string &name, boost::function<void(void)> func,
+void RPCRunLater(const std::string &name, std::function<void(void)> func,
int64_t nSeconds) {
if (!timerInterface)
throw JSONRPCError(RPC_INTERNAL_ERROR,
"No timer handler registered for RPC");
deadlineTimers.erase(name);
LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name,
nSeconds, timerInterface->Name());
deadlineTimers.emplace(
name, std::unique_ptr<RPCTimerBase>(
timerInterface->NewTimer(func, nSeconds * 1000)));
}
int RPCSerializationFlags() {
return 0;
}
CRPCTable tableRPC;
diff --git a/src/rpc/server.h b/src/rpc/server.h
index 8abbedcefd..be1a32b4a4 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -1,242 +1,240 @@
// Copyright (c) 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.
#ifndef BITCOIN_RPCSERVER_H
#define BITCOIN_RPCSERVER_H
#include "amount.h"
#include "rpc/protocol.h"
#include "uint256.h"
#include <cstdint>
#include <functional>
#include <list>
#include <map>
#include <string>
-#include <boost/function.hpp>
-
#include <univalue.h>
static const unsigned int DEFAULT_RPC_SERIALIZE_VERSION = 1;
class CRPCCommand;
namespace RPCServer {
-void OnStarted(boost::function<void()> slot);
-void OnStopped(boost::function<void()> slot);
-void OnPreCommand(boost::function<void(const CRPCCommand &)> slot);
-void OnPostCommand(boost::function<void(const CRPCCommand &)> slot);
+void OnStarted(std::function<void()> slot);
+void OnStopped(std::function<void()> slot);
+void OnPreCommand(std::function<void(const CRPCCommand &)> slot);
+void OnPostCommand(std::function<void(const CRPCCommand &)> slot);
}
class CBlockIndex;
class Config;
class CNetAddr;
/** Wrapper for UniValue::VType, which includes typeAny:
* Used to denote don't care type. Only used by RPCTypeCheckObj */
struct UniValueType {
UniValueType(UniValue::VType _type) : typeAny(false), type(_type) {}
UniValueType() : typeAny(true) {}
bool typeAny;
UniValue::VType type;
};
class JSONRPCRequest {
public:
UniValue id;
std::string strMethod;
UniValue params;
bool fHelp;
std::string URI;
std::string authUser;
JSONRPCRequest() {
id = NullUniValue;
params = NullUniValue;
fHelp = false;
}
void parse(const UniValue &valRequest);
};
/** Query whether RPC is running */
bool IsRPCRunning();
/**
* Set the RPC warmup status. When this is done, all RPC calls will error out
* immediately with RPC_IN_WARMUP.
*/
void SetRPCWarmupStatus(const std::string &newStatus);
/* Mark warmup as done. RPC calls will be processed from now on. */
void SetRPCWarmupFinished();
/* returns the current warmup state. */
bool RPCIsInWarmup(std::string *statusOut);
/**
* Type-check arguments; throws JSONRPCError if wrong type given. Does not check
* that the right number of arguments are passed, just that any passed are the
* correct type.
*/
void RPCTypeCheck(const UniValue &params,
const std::list<UniValue::VType> &typesExpected,
bool fAllowNull = false);
/**
* Type-check one argument; throws JSONRPCError if wrong type given.
*/
void RPCTypeCheckArgument(const UniValue &value, UniValue::VType typeExpected);
/*
Check for expected keys/value types in an Object.
*/
void RPCTypeCheckObj(const UniValue &o,
const std::map<std::string, UniValueType> &typesExpected,
bool fAllowNull = false, bool fStrict = false);
/** Opaque base class for timers returned by NewTimerFunc.
* This provides no methods at the moment, but makes sure that delete cleans up
* the whole state.
*/
class RPCTimerBase {
public:
virtual ~RPCTimerBase() {}
};
/**
* RPC timer "driver".
*/
class RPCTimerInterface {
public:
virtual ~RPCTimerInterface() {}
/** Implementation name */
virtual const char *Name() = 0;
/** Factory function for timers.
* RPC will call the function to create a timer that will call func in
* *millis* milliseconds.
* @note As the RPC mechanism is backend-neutral, it can use different
* implementations of timers.
* This is needed to cope with the case in which there is no HTTP server,
* but only GUI RPC console, and to break the dependency of pcserver on
* httprpc.
*/
- virtual RPCTimerBase *NewTimer(boost::function<void(void)> &func,
+ virtual RPCTimerBase *NewTimer(std::function<void(void)> &func,
int64_t millis) = 0;
};
/** Set the factory function for timers */
void RPCSetTimerInterface(RPCTimerInterface *iface);
/** Set the factory function for timer, but only, if unset */
void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface);
/** Unset factory function for timers */
void RPCUnsetTimerInterface(RPCTimerInterface *iface);
/**
* Run func nSeconds from now.
* Overrides previous timer <name> (if any).
*/
-void RPCRunLater(const std::string &name, boost::function<void(void)> func,
+void RPCRunLater(const std::string &name, std::function<void(void)> func,
int64_t nSeconds);
typedef UniValue (*rpcfn_type)(Config &config,
const JSONRPCRequest &jsonRequest);
typedef UniValue (*const_rpcfn_type)(const Config &config,
const JSONRPCRequest &jsonRequest);
class CRPCCommand {
public:
std::string category;
std::string name;
rpcfn_type actor;
bool okSafeMode;
std::vector<std::string> argNames;
CRPCCommand(std::string _category, std::string _name, rpcfn_type _actor,
bool _okSafeMode, std::vector<std::string> _argNames)
: category{std::move(_category)}, name{std::move(_name)}, actor{_actor},
okSafeMode{_okSafeMode}, argNames{std::move(_argNames)} {}
/**
* It is safe to cast from void(const int*) to void(int*) but C++ do not
* understand type variance. As a result, we need to do the dirty job
* ourselves.
*/
CRPCCommand(std::string _category, std::string _name,
const_rpcfn_type _actor, bool _okSafeMode,
std::vector<std::string> _argNames)
: category{std::move(_category)}, name{std::move(_name)},
actor{reinterpret_cast<rpcfn_type>(_actor)}, okSafeMode{_okSafeMode},
argNames{std::move(_argNames)} {}
};
/**
* Bitcoin RPC command dispatcher.
*/
class CRPCTable {
private:
std::map<std::string, const CRPCCommand *> mapCommands;
public:
CRPCTable();
const CRPCCommand *operator[](const std::string &name) const;
std::string help(Config &config, const std::string &name) const;
/**
* Execute a method.
* @param request The JSONRPCRequest to execute
* @returns Result of the call.
* @throws an exception (UniValue) when an error happens.
*/
UniValue execute(Config &config, const JSONRPCRequest &request) const;
/**
* Returns a list of registered commands
* @returns List of registered commands.
*/
std::vector<std::string> listCommands() const;
/**
* Appends a CRPCCommand to the dispatch table.
* Returns false if RPC server is already running (dump concurrency
* protection).
* Commands cannot be overwritten (returns false).
*/
bool appendCommand(const std::string &name, const CRPCCommand *pcmd);
};
extern CRPCTable tableRPC;
/**
* Utilities: convert hex-encoded Values
* (throws error if not hex).
*/
extern uint256 ParseHashV(const UniValue &v, std::string strName);
extern uint256 ParseHashO(const UniValue &o, std::string strKey);
extern std::vector<uint8_t> ParseHexV(const UniValue &v, std::string strName);
extern std::vector<uint8_t> ParseHexO(const UniValue &o, std::string strKey);
extern int64_t nWalletUnlockTime;
extern CAmount AmountFromValue(const UniValue &value);
extern UniValue ValueFromAmount(const CAmount &amount);
extern double GetDifficulty(const CBlockIndex *blockindex = nullptr);
extern std::string HelpRequiringPassphrase();
extern std::string HelpExampleCli(const std::string &methodname,
const std::string &args);
extern std::string HelpExampleRpc(const std::string &methodname,
const std::string &args);
extern void EnsureWalletIsUnlocked();
bool StartRPC();
void InterruptRPC();
void StopRPC();
std::string JSONRPCExecBatch(Config &config, const UniValue &vReq);
void RPCNotifyBlockChange(bool ibd, const CBlockIndex *);
// Retrieves any serialization flags requested in command line argument
int RPCSerializationFlags();
#endif // BITCOIN_RPCSERVER_H
diff --git a/src/scheduler.h b/src/scheduler.h
index c8590d146b..036d1887a6 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -1,84 +1,83 @@
// Copyright (c) 2015 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_SCHEDULER_H
#define BITCOIN_SCHEDULER_H
//
// NOTE:
-// boost::thread / boost::function / boost::chrono should be ported to
-// std::thread / std::function / std::chrono when we support C++11.
+// boost::thread / boost::chrono should be ported to
+// std::thread / std::chrono when we support C++11.
//
#include <boost/chrono/chrono.hpp>
-#include <boost/function.hpp>
#include <boost/thread.hpp>
#include <map>
//
// Simple class for background tasks that should be run periodically or once
// "after a while"
//
// Usage:
//
// CScheduler* s = new CScheduler();
// s->scheduleFromNow(doSomething, 11); // Assuming a: void doSomething() { }
// s->scheduleFromNow(boost::bind(Class::func, this, argument), 3);
// boost::thread* t = new boost::thread(boost::bind(CScheduler::serviceQueue,
// s));
//
// ... then at program shutdown, clean up the thread running serviceQueue:
// t->interrupt();
// t->join();
// delete t;
// delete s; // Must be done after thread is interrupted/joined.
//
class CScheduler {
public:
CScheduler();
~CScheduler();
- typedef boost::function<void(void)> Function;
+ typedef std::function<void(void)> Function;
// Call func at/after time t
void schedule(Function f, boost::chrono::system_clock::time_point t);
// Convenience method: call f once deltaSeconds from now
void scheduleFromNow(Function f, int64_t deltaSeconds);
// Another convenience method: call f approximately every deltaSeconds
// forever, starting deltaSeconds from now. To be more precise: every time f
// is finished, it is rescheduled to run deltaSeconds later. If you need
// more accurate scheduling, don't use this method.
void scheduleEvery(Function f, int64_t deltaSeconds);
// To keep things as simple as possible, there is no unschedule.
// Services the queue 'forever'. Should be run in a thread, and interrupted
// using boost::interrupt_thread
void serviceQueue();
// Tell any threads running serviceQueue to stop as soon as they're done
// servicing whatever task they're currently servicing (drain=false) or when
// there is no work left to be done (drain=true)
void stop(bool drain = false);
// Returns number of tasks waiting to be serviced, and first and last task
// times
size_t getQueueInfo(boost::chrono::system_clock::time_point &first,
boost::chrono::system_clock::time_point &last) const;
private:
std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue;
boost::condition_variable newTaskScheduled;
mutable boost::mutex newTaskMutex;
int nThreadsServicingQueue;
bool stopRequested;
bool stopWhenEmpty;
bool shouldStop() {
return stopRequested || (stopWhenEmpty && taskQueue.empty());
}
};
#endif
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 0f1bcb50d3..8af2eb3980 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -1,768 +1,766 @@
// Copyright (c) 2015-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.
#include "torcontrol.h"
#include "crypto/hmac_sha256.h"
#include "net.h"
#include "netbase.h"
#include "util.h"
#include "utilstrencodings.h"
#include <cstdlib>
#include <deque>
#include <set>
#include <vector>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/bind.hpp>
-#include <boost/function.hpp>
#include <boost/signals2/signal.hpp>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <event2/event.h>
#include <event2/thread.h>
#include <event2/util.h>
/** Default control port */
const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:9051";
/** Tor cookie size (from control-spec.txt) */
static const int TOR_COOKIE_SIZE = 32;
/** Size of client/server nonce for SAFECOOKIE */
static const int TOR_NONCE_SIZE = 32;
/** For computing serverHash in SAFECOOKIE */
static const std::string TOR_SAFE_SERVERKEY =
"Tor safe cookie authentication server-to-controller hash";
/** For computing clientHash in SAFECOOKIE */
static const std::string TOR_SAFE_CLIENTKEY =
"Tor safe cookie authentication controller-to-server hash";
/** Exponential backoff configuration - initial timeout in seconds */
static const float RECONNECT_TIMEOUT_START = 1.0;
/** Exponential backoff configuration - growth factor */
static const float RECONNECT_TIMEOUT_EXP = 1.5;
/**
* Maximum length for lines received on TorControlConnection.
* tor-control-spec.txt mentions that there is explicitly no limit defined to
* line length, this is belt-and-suspenders sanity limit to prevent memory
* exhaustion.
*/
static const int MAX_LINE_LENGTH = 100000;
/****** Low-level TorControlConnection ********/
/** Reply from Tor, can be single or multi-line */
class TorControlReply {
public:
TorControlReply() { Clear(); }
int code;
std::vector<std::string> lines;
void Clear() {
code = 0;
lines.clear();
}
};
/**
* Low-level handling for Tor control connection.
* Speaks the SMTP-like protocol as defined in torspec/control-spec.txt
*/
class TorControlConnection {
public:
- typedef boost::function<void(TorControlConnection &)> ConnectionCB;
- typedef boost::function<void(TorControlConnection &,
- const TorControlReply &)>
+ typedef std::function<void(TorControlConnection &)> ConnectionCB;
+ typedef std::function<void(TorControlConnection &, const TorControlReply &)>
ReplyHandlerCB;
/** Create a new TorControlConnection.
*/
TorControlConnection(struct event_base *base);
~TorControlConnection();
/**
* Connect to a Tor control port.
* target is address of the form host:port.
* connected is the handler that is called when connection is successfully
* established.
* disconnected is a handler that is called when the connection is broken.
* Return true on success.
*/
bool Connect(const std::string &target, const ConnectionCB &connected,
const ConnectionCB &disconnected);
/**
* Disconnect from Tor control port.
*/
bool Disconnect();
/**
* Send a command, register a handler for the reply.
* A trailing CRLF is automatically added.
* Return true on success.
*/
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler);
/** Response handlers for async replies */
boost::signals2::signal<void(TorControlConnection &,
const TorControlReply &)>
async_handler;
private:
/** Callback when ready for use */
- boost::function<void(TorControlConnection &)> connected;
+ std::function<void(TorControlConnection &)> connected;
/** Callback when connection lost */
- boost::function<void(TorControlConnection &)> disconnected;
+ std::function<void(TorControlConnection &)> disconnected;
/** Libevent event base */
struct event_base *base;
/** Connection to control socket */
struct bufferevent *b_conn;
/** Message being received */
TorControlReply message;
/** Response handlers */
std::deque<ReplyHandlerCB> reply_handlers;
/** Libevent handlers: internal */
static void readcb(struct bufferevent *bev, void *ctx);
static void eventcb(struct bufferevent *bev, short what, void *ctx);
};
TorControlConnection::TorControlConnection(struct event_base *_base)
: base(_base), b_conn(0) {}
TorControlConnection::~TorControlConnection() {
if (b_conn) bufferevent_free(b_conn);
}
void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) {
TorControlConnection *self = (TorControlConnection *)ctx;
struct evbuffer *input = bufferevent_get_input(bev);
size_t n_read_out = 0;
char *line;
assert(input);
// If there is not a whole line to read, evbuffer_readln returns nullptr
while ((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) !=
nullptr) {
std::string s(line, n_read_out);
free(line);
// Short line
if (s.size() < 4) continue;
// <status>(-|+| )<data><CRLF>
self->message.code = atoi(s.substr(0, 3));
self->message.lines.push_back(s.substr(4));
// '-','+' or ' '
char ch = s[3];
if (ch == ' ') {
// Final line, dispatch reply and clean up
if (self->message.code >= 600) {
// Dispatch async notifications to async handler.
// Synchronous and asynchronous messages are never interleaved
self->async_handler(*self, self->message);
} else {
if (!self->reply_handlers.empty()) {
// Invoke reply handler with message
self->reply_handlers.front()(*self, self->message);
self->reply_handlers.pop_front();
} else {
LogPrint("tor", "tor: Received unexpected sync reply %i\n",
self->message.code);
}
}
self->message.Clear();
}
}
// Check for size of buffer - protect against memory exhaustion with very
// long lines. Do this after evbuffer_readln to make sure all full lines
// have been removed from the buffer. Everything left is an incomplete line.
if (evbuffer_get_length(input) > MAX_LINE_LENGTH) {
LogPrintf("tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
self->Disconnect();
}
}
void TorControlConnection::eventcb(struct bufferevent *bev, short what,
void *ctx) {
TorControlConnection *self = (TorControlConnection *)ctx;
if (what & BEV_EVENT_CONNECTED) {
LogPrint("tor", "tor: Successfully connected!\n");
self->connected(*self);
} else if (what & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
if (what & BEV_EVENT_ERROR)
LogPrint("tor", "tor: Error connecting to Tor control socket\n");
else
LogPrint("tor", "tor: End of stream\n");
self->Disconnect();
self->disconnected(*self);
}
}
bool TorControlConnection::Connect(const std::string &target,
const ConnectionCB &_connected,
const ConnectionCB &_disconnected) {
if (b_conn) Disconnect();
// Parse target address:port
struct sockaddr_storage connect_to_addr;
int connect_to_addrlen = sizeof(connect_to_addr);
if (evutil_parse_sockaddr_port(target.c_str(),
(struct sockaddr *)&connect_to_addr,
&connect_to_addrlen) < 0) {
LogPrintf("tor: Error parsing socket address %s\n", target);
return false;
}
// Create a new socket, set up callbacks and enable notification bits
b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
if (!b_conn) return false;
bufferevent_setcb(b_conn, TorControlConnection::readcb, nullptr,
TorControlConnection::eventcb, this);
bufferevent_enable(b_conn, EV_READ | EV_WRITE);
this->connected = _connected;
this->disconnected = _disconnected;
// Finally, connect to target
if (bufferevent_socket_connect(b_conn, (struct sockaddr *)&connect_to_addr,
connect_to_addrlen) < 0) {
LogPrintf("tor: Error connecting to address %s\n", target);
return false;
}
return true;
}
bool TorControlConnection::Disconnect() {
if (b_conn) bufferevent_free(b_conn);
b_conn = 0;
return true;
}
bool TorControlConnection::Command(const std::string &cmd,
const ReplyHandlerCB &reply_handler) {
if (!b_conn) return false;
struct evbuffer *buf = bufferevent_get_output(b_conn);
if (!buf) return false;
evbuffer_add(buf, cmd.data(), cmd.size());
evbuffer_add(buf, "\r\n", 2);
reply_handlers.push_back(reply_handler);
return true;
}
/****** General parsing utilities ********/
/* Split reply line in the form 'AUTH METHODS=...' into a type
* 'AUTH' and arguments 'METHODS=...'.
*/
static std::pair<std::string, std::string>
SplitTorReplyLine(const std::string &s) {
size_t ptr = 0;
std::string type;
while (ptr < s.size() && s[ptr] != ' ') {
type.push_back(s[ptr]);
++ptr;
}
if (ptr < s.size()) ++ptr; // skip ' '
return make_pair(type, s.substr(ptr));
}
/**
* Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE
* COOKIEFILE=".../control_auth_cookie"'.
*/
static std::map<std::string, std::string>
ParseTorReplyMapping(const std::string &s) {
std::map<std::string, std::string> mapping;
size_t ptr = 0;
while (ptr < s.size()) {
std::string key, value;
while (ptr < s.size() && s[ptr] != '=') {
key.push_back(s[ptr]);
++ptr;
}
// unexpected end of line
if (ptr == s.size()) return std::map<std::string, std::string>();
// skip '='
++ptr;
// Quoted string
if (ptr < s.size() && s[ptr] == '"') {
// skip '='
++ptr;
bool escape_next = false;
while (ptr < s.size() && (!escape_next && s[ptr] != '"')) {
escape_next = (s[ptr] == '\\');
value.push_back(s[ptr]);
++ptr;
}
// unexpected end of line
if (ptr == s.size()) return std::map<std::string, std::string>();
// skip closing '"'
++ptr;
/* TODO: unescape value - according to the spec this depends on the
* context, some strings use C-LogPrintf style escape codes, some
* don't. So may be better handled at the call site.
*/
} else {
// Unquoted value. Note that values can contain '=' at will, just no
// spaces
while (ptr < s.size() && s[ptr] != ' ') {
value.push_back(s[ptr]);
++ptr;
}
}
if (ptr < s.size() && s[ptr] == ' ') {
// skip ' ' after key=value
++ptr;
}
mapping[key] = value;
}
return mapping;
}
/**
* Read full contents of a file and return them in a std::string.
* Returns a pair <status, string>.
* If an error occurred, status will be false, otherwise status will be true and
* the data will be returned in string.
*
* @param maxsize Puts a maximum size limit on the file that is read. If the
* file is larger than this, truncated data
* (with len > maxsize) will be returned.
*/
static std::pair<bool, std::string>
ReadBinaryFile(const std::string &filename,
size_t maxsize = std::numeric_limits<size_t>::max()) {
FILE *f = fopen(filename.c_str(), "rb");
if (f == nullptr) return std::make_pair(false, "");
std::string retval;
char buffer[128];
size_t n;
while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) {
retval.append(buffer, buffer + n);
if (retval.size() > maxsize) break;
}
fclose(f);
return std::make_pair(true, retval);
}
/**
* Write contents of std::string to a file.
* @return true on success.
*/
static bool WriteBinaryFile(const std::string &filename,
const std::string &data) {
FILE *f = fopen(filename.c_str(), "wb");
if (f == nullptr) return false;
if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
fclose(f);
return false;
}
fclose(f);
return true;
}
/****** Bitcoin specific TorController implementation ********/
/**
* Controller that connects to Tor control socket, authenticate, then create
* and maintain a ephemeral hidden service.
*/
class TorController {
public:
TorController(struct event_base *base, const std::string &target);
~TorController();
/** Get name fo file to store private key in */
std::string GetPrivateKeyFile();
/** Reconnect, after getting disconnected */
void Reconnect();
private:
struct event_base *base;
std::string target;
TorControlConnection conn;
std::string private_key;
std::string service_id;
bool reconnect;
struct event *reconnect_ev;
float reconnect_timeout;
CService service;
/** Cookie for SAFECOOKIE auth */
std::vector<uint8_t> cookie;
/** ClientNonce for SAFECOOKIE auth */
std::vector<uint8_t> clientNonce;
/** Callback for ADD_ONION result */
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply);
/** Callback for AUTHENTICATE result */
void auth_cb(TorControlConnection &conn, const TorControlReply &reply);
/** Callback for AUTHCHALLENGE result */
void authchallenge_cb(TorControlConnection &conn,
const TorControlReply &reply);
/** Callback for PROTOCOLINFO result */
void protocolinfo_cb(TorControlConnection &conn,
const TorControlReply &reply);
/** Callback after successful connection */
void connected_cb(TorControlConnection &conn);
/** Callback after connection lost or failed connection attempt */
void disconnected_cb(TorControlConnection &conn);
/** Callback for reconnect timer */
static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
};
TorController::TorController(struct event_base *_base,
const std::string &_target)
: base(_base), target(_target), conn(base), reconnect(true),
reconnect_ev(0), reconnect_timeout(RECONNECT_TIMEOUT_START) {
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
if (!reconnect_ev)
LogPrintf(
"tor: Failed to create event for reconnection: out of memory?\n");
// Start connection attempts immediately
if (!conn.Connect(_target,
boost::bind(&TorController::connected_cb, this, _1),
boost::bind(&TorController::disconnected_cb, this, _1))) {
LogPrintf("tor: Initiating connection to Tor control port %s failed\n",
_target);
}
// Read service private key if cached
std::pair<bool, std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
if (pkf.first) {
LogPrint("tor", "tor: Reading cached private key from %s\n",
GetPrivateKeyFile());
private_key = pkf.second;
}
}
TorController::~TorController() {
if (reconnect_ev) {
event_free(reconnect_ev);
reconnect_ev = 0;
}
if (service.IsValid()) {
RemoveLocal(service);
}
}
void TorController::add_onion_cb(TorControlConnection &_conn,
const TorControlReply &reply) {
if (reply.code == 250) {
LogPrint("tor", "tor: ADD_ONION successful\n");
for (const std::string &s : reply.lines) {
std::map<std::string, std::string> m = ParseTorReplyMapping(s);
std::map<std::string, std::string>::iterator i;
if ((i = m.find("ServiceID")) != m.end()) service_id = i->second;
if ((i = m.find("PrivateKey")) != m.end()) private_key = i->second;
}
service = LookupNumeric(std::string(service_id + ".onion").c_str(),
GetListenPort());
LogPrintf("tor: Got service ID %s, advertising service %s\n",
service_id, service.ToString());
if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) {
LogPrint("tor", "tor: Cached service private key to %s\n",
GetPrivateKeyFile());
} else {
LogPrintf("tor: Error writing service private key to %s\n",
GetPrivateKeyFile());
}
AddLocal(service, LOCAL_MANUAL);
// ... onion requested - keep connection open
} else if (reply.code == 510) { // 510 Unrecognized command
LogPrintf("tor: Add onion failed with unrecognized command (You "
"probably need to upgrade Tor)\n");
} else {
LogPrintf("tor: Add onion failed; error code %d\n", reply.code);
}
}
void TorController::auth_cb(TorControlConnection &_conn,
const TorControlReply &reply) {
if (reply.code == 250) {
LogPrint("tor", "tor: Authentication successful\n");
// Now that we know Tor is running setup the proxy for onion addresses
// if -onion isn't set to something else.
if (GetArg("-onion", "") == "") {
CService resolved(LookupNumeric("127.0.0.1", 9050));
proxyType addrOnion = proxyType(resolved, true);
SetProxy(NET_TOR, addrOnion);
SetLimited(NET_TOR, false);
}
// Finally - now create the service
// No private key, generate one
if (private_key.empty()) {
// Explicitly request RSA1024 - see issue #9214
private_key = "NEW:RSA1024";
}
// Request hidden service, redirect port.
// Note that the 'virtual' port doesn't have to be the same as our
// internal port, but this is just a convenient choice. TODO; refactor
// the shutdown sequence some day.
_conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i",
private_key, GetListenPort(), GetListenPort()),
boost::bind(&TorController::add_onion_cb, this, _1, _2));
} else {
LogPrintf("tor: Authentication failed\n");
}
}
/** Compute Tor SAFECOOKIE response.
*
* ServerHash is computed as:
* HMAC-SHA256("Tor safe cookie authentication server-to-controller hash",
* CookieString | ClientNonce | ServerNonce)
* (with the HMAC key as its first argument)
*
* After a controller sends a successful AUTHCHALLENGE command, the
* next command sent on the connection must be an AUTHENTICATE command,
* and the only authentication string which that AUTHENTICATE command
* will accept is:
*
* HMAC-SHA256("Tor safe cookie authentication controller-to-server hash",
* CookieString | ClientNonce | ServerNonce)
*
*/
static std::vector<uint8_t>
ComputeResponse(const std::string &key, const std::vector<uint8_t> &cookie,
const std::vector<uint8_t> &clientNonce,
const std::vector<uint8_t> &serverNonce) {
CHMAC_SHA256 computeHash((const uint8_t *)key.data(), key.size());
std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0);
computeHash.Write(cookie.data(), cookie.size());
computeHash.Write(clientNonce.data(), clientNonce.size());
computeHash.Write(serverNonce.data(), serverNonce.size());
computeHash.Finalize(computedHash.data());
return computedHash;
}
void TorController::authchallenge_cb(TorControlConnection &_conn,
const TorControlReply &reply) {
if (reply.code == 250) {
LogPrint("tor",
"tor: SAFECOOKIE authentication challenge successful\n");
std::pair<std::string, std::string> l =
SplitTorReplyLine(reply.lines[0]);
if (l.first == "AUTHCHALLENGE") {
std::map<std::string, std::string> m =
ParseTorReplyMapping(l.second);
std::vector<uint8_t> serverHash = ParseHex(m["SERVERHASH"]);
std::vector<uint8_t> serverNonce = ParseHex(m["SERVERNONCE"]);
LogPrint("tor", "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n",
HexStr(serverHash), HexStr(serverNonce));
if (serverNonce.size() != 32) {
LogPrintf(
"tor: ServerNonce is not 32 bytes, as required by spec\n");
return;
}
std::vector<uint8_t> computedServerHash = ComputeResponse(
TOR_SAFE_SERVERKEY, cookie, clientNonce, serverNonce);
if (computedServerHash != serverHash) {
LogPrintf("tor: ServerHash %s does not match expected "
"ServerHash %s\n",
HexStr(serverHash), HexStr(computedServerHash));
return;
}
std::vector<uint8_t> computedClientHash = ComputeResponse(
TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce);
_conn.Command("AUTHENTICATE " + HexStr(computedClientHash),
boost::bind(&TorController::auth_cb, this, _1, _2));
} else {
LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n");
}
} else {
LogPrintf("tor: SAFECOOKIE authentication challenge failed\n");
}
}
void TorController::protocolinfo_cb(TorControlConnection &_conn,
const TorControlReply &reply) {
if (reply.code == 250) {
std::set<std::string> methods;
std::string cookiefile;
/*
* 250-AUTH METHODS=COOKIE,SAFECOOKIE
* COOKIEFILE="/home/x/.tor/control_auth_cookie"
* 250-AUTH METHODS=NULL
* 250-AUTH METHODS=HASHEDPASSWORD
*/
for (const std::string &s : reply.lines) {
std::pair<std::string, std::string> l = SplitTorReplyLine(s);
if (l.first == "AUTH") {
std::map<std::string, std::string> m =
ParseTorReplyMapping(l.second);
std::map<std::string, std::string>::iterator i;
if ((i = m.find("METHODS")) != m.end())
boost::split(methods, i->second, boost::is_any_of(","));
if ((i = m.find("COOKIEFILE")) != m.end())
cookiefile = i->second;
} else if (l.first == "VERSION") {
std::map<std::string, std::string> m =
ParseTorReplyMapping(l.second);
std::map<std::string, std::string>::iterator i;
if ((i = m.find("Tor")) != m.end()) {
LogPrint("tor", "tor: Connected to Tor version %s\n",
i->second);
}
}
}
for (const std::string &s : methods) {
LogPrint("tor", "tor: Supported authentication method: %s\n", s);
}
// Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use
// HASHEDPASSWORD
/* Authentication:
* cookie: hex-encoded ~/.tor/control_auth_cookie
* password: "password"
*/
std::string torpassword = GetArg("-torpassword", "");
if (!torpassword.empty()) {
if (methods.count("HASHEDPASSWORD")) {
LogPrint("tor", "tor: Using HASHEDPASSWORD authentication\n");
boost::replace_all(torpassword, "\"", "\\\"");
_conn.Command(
"AUTHENTICATE \"" + torpassword + "\"",
boost::bind(&TorController::auth_cb, this, _1, _2));
} else {
LogPrintf("tor: Password provided with -torpassword, but "
"HASHEDPASSWORD authentication is not available\n");
}
} else if (methods.count("NULL")) {
LogPrint("tor", "tor: Using NULL authentication\n");
_conn.Command("AUTHENTICATE",
boost::bind(&TorController::auth_cb, this, _1, _2));
} else if (methods.count("SAFECOOKIE")) {
// Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie
LogPrint("tor", "tor: Using SAFECOOKIE authentication, reading "
"cookie authentication from %s\n",
cookiefile);
std::pair<bool, std::string> status_cookie =
ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE);
if (status_cookie.first &&
status_cookie.second.size() == TOR_COOKIE_SIZE) {
// _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second),
// boost::bind(&TorController::auth_cb, this, _1, _2));
cookie = std::vector<uint8_t>(status_cookie.second.begin(),
status_cookie.second.end());
clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0);
GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE);
_conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce),
boost::bind(&TorController::authchallenge_cb,
this, _1, _2));
} else {
if (status_cookie.first) {
LogPrintf("tor: Authentication cookie %s is not exactly %i "
"bytes, as is required by the spec\n",
cookiefile, TOR_COOKIE_SIZE);
} else {
LogPrintf("tor: Authentication cookie %s could not be "
"opened (check permissions)\n",
cookiefile);
}
}
} else if (methods.count("HASHEDPASSWORD")) {
LogPrintf("tor: The only supported authentication mechanism left "
"is password, but no password provided with "
"-torpassword\n");
} else {
LogPrintf("tor: No supported authentication method\n");
}
} else {
LogPrintf("tor: Requesting protocol info failed\n");
}
}
void TorController::connected_cb(TorControlConnection &_conn) {
reconnect_timeout = RECONNECT_TIMEOUT_START;
// First send a PROTOCOLINFO command to figure out what authentication is
// expected
if (!_conn.Command(
"PROTOCOLINFO 1",
boost::bind(&TorController::protocolinfo_cb, this, _1, _2)))
LogPrintf("tor: Error sending initial protocolinfo command\n");
}
void TorController::disconnected_cb(TorControlConnection &_conn) {
// Stop advertising service when disconnected
if (service.IsValid()) RemoveLocal(service);
service = CService();
if (!reconnect) return;
LogPrint("tor",
"tor: Not connected to Tor control port %s, trying to reconnect\n",
target);
// Single-shot timer for reconnect. Use exponential backoff.
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
if (reconnect_ev) event_add(reconnect_ev, &time);
reconnect_timeout *= RECONNECT_TIMEOUT_EXP;
}
void TorController::Reconnect() {
/* Try to reconnect and reestablish if we get booted - for example, Tor may
* be restarting.
*/
if (!conn.Connect(target,
boost::bind(&TorController::connected_cb, this, _1),
boost::bind(&TorController::disconnected_cb, this, _1))) {
LogPrintf(
"tor: Re-initiating connection to Tor control port %s failed\n",
target);
}
}
std::string TorController::GetPrivateKeyFile() {
return (GetDataDir() / "onion_private_key").string();
}
void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) {
TorController *self = (TorController *)arg;
self->Reconnect();
}
/****** Thread ********/
struct event_base *base;
boost::thread torControlThread;
static void TorControlThread() {
TorController ctrl(base, GetArg("-torcontrol", DEFAULT_TOR_CONTROL));
event_base_dispatch(base);
}
void StartTorControl(boost::thread_group &threadGroup, CScheduler &scheduler) {
assert(!base);
#ifdef WIN32
evthread_use_windows_threads();
#else
evthread_use_pthreads();
#endif
base = event_base_new();
if (!base) {
LogPrintf("tor: Unable to create event_base\n");
return;
}
torControlThread = boost::thread(
boost::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread));
}
void InterruptTorControl() {
if (base) {
LogPrintf("tor: Thread interrupt\n");
event_base_loopbreak(base);
}
}
void StopTorControl() {
if (base) {
torControlThread.join();
event_base_free(base);
base = 0;
}
}
diff --git a/src/txdb.cpp b/src/txdb.cpp
index d442a33709..63ed72010f 100644
--- a/src/txdb.cpp
+++ b/src/txdb.cpp
@@ -1,224 +1,224 @@
// 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.
#include "txdb.h"
#include "chainparams.h"
#include "hash.h"
#include "pow.h"
#include "uint256.h"
#include <cstdint>
#include <boost/thread.hpp>
static const char DB_COINS = 'c';
static const char DB_BLOCK_FILES = 'f';
static const char DB_TXINDEX = 't';
static const char DB_BLOCK_INDEX = 'b';
static const char DB_BEST_BLOCK = 'B';
static const char DB_FLAG = 'F';
static const char DB_REINDEX_FLAG = 'R';
static const char DB_LAST_BLOCK = 'l';
CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe)
: db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true) {}
bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const {
return db.Read(std::make_pair(DB_COINS, txid), coins);
}
bool CCoinsViewDB::HaveCoins(const uint256 &txid) const {
return db.Exists(std::make_pair(DB_COINS, txid));
}
uint256 CCoinsViewDB::GetBestBlock() const {
uint256 hashBestChain;
if (!db.Read(DB_BEST_BLOCK, hashBestChain)) return uint256();
return hashBestChain;
}
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
CDBBatch batch(db);
size_t count = 0;
size_t changed = 0;
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
if (it->second.coins.IsPruned())
batch.Erase(std::make_pair(DB_COINS, it->first));
else
batch.Write(std::make_pair(DB_COINS, it->first),
it->second.coins);
changed++;
}
count++;
CCoinsMap::iterator itOld = it++;
mapCoins.erase(itOld);
}
if (!hashBlock.IsNull()) batch.Write(DB_BEST_BLOCK, hashBlock);
LogPrint(
"coindb",
"Committing %u changed transactions (out of %u) to coin database...\n",
(unsigned int)changed, (unsigned int)count);
return db.WriteBatch(batch);
}
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe)
: CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory,
fWipe) {}
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
return Read(std::make_pair(DB_BLOCK_FILES, nFile), info);
}
bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
if (fReindexing)
return Write(DB_REINDEX_FLAG, '1');
else
return Erase(DB_REINDEX_FLAG);
}
bool CBlockTreeDB::ReadReindexing(bool &fReindexing) {
fReindexing = Exists(DB_REINDEX_FLAG);
return true;
}
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
return Read(DB_LAST_BLOCK, nFile);
}
CCoinsViewCursor *CCoinsViewDB::Cursor() const {
CCoinsViewDBCursor *i = new CCoinsViewDBCursor(
const_cast<CDBWrapper *>(&db)->NewIterator(), GetBestBlock());
/**
* It seems that there are no "const iterators" for LevelDB. Since we only
* need read operations on it, use a const-cast to get around that
* restriction.
*/
i->pcursor->Seek(DB_COINS);
// Cache key of first record
i->pcursor->GetKey(i->keyTmp);
return i;
}
bool CCoinsViewDBCursor::GetKey(uint256 &key) const {
// Return cached key
if (keyTmp.first == DB_COINS) {
key = keyTmp.second;
return true;
}
return false;
}
bool CCoinsViewDBCursor::GetValue(CCoins &coins) const {
return pcursor->GetValue(coins);
}
unsigned int CCoinsViewDBCursor::GetValueSize() const {
return pcursor->GetValueSize();
}
bool CCoinsViewDBCursor::Valid() const {
return keyTmp.first == DB_COINS;
}
void CCoinsViewDBCursor::Next() {
pcursor->Next();
if (!pcursor->Valid() || !pcursor->GetKey(keyTmp))
// Invalidate cached key after last record so that Valid() and GetKey()
// return false
keyTmp.first = 0;
}
bool CBlockTreeDB::WriteBatchSync(
const std::vector<std::pair<int, const CBlockFileInfo *>> &fileInfo,
int nLastFile, const std::vector<const CBlockIndex *> &blockinfo) {
CDBBatch batch(*this);
for (std::vector<std::pair<int, const CBlockFileInfo *>>::const_iterator
it = fileInfo.begin();
it != fileInfo.end(); it++) {
batch.Write(std::make_pair(DB_BLOCK_FILES, it->first), *it->second);
}
batch.Write(DB_LAST_BLOCK, nLastFile);
for (std::vector<const CBlockIndex *>::const_iterator it =
blockinfo.begin();
it != blockinfo.end(); it++) {
batch.Write(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()),
CDiskBlockIndex(*it));
}
return WriteBatch(batch, true);
}
bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
return Read(std::make_pair(DB_TXINDEX, txid), pos);
}
bool CBlockTreeDB::WriteTxIndex(
const std::vector<std::pair<uint256, CDiskTxPos>> &vect) {
CDBBatch batch(*this);
for (std::vector<std::pair<uint256, CDiskTxPos>>::const_iterator it =
vect.begin();
it != vect.end(); it++)
batch.Write(std::make_pair(DB_TXINDEX, it->first), it->second);
return WriteBatch(batch);
}
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
}
bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
char ch;
if (!Read(std::make_pair(DB_FLAG, name), ch)) return false;
fValue = ch == '1';
return true;
}
bool CBlockTreeDB::LoadBlockIndexGuts(
- boost::function<CBlockIndex *(const uint256 &)> insertBlockIndex) {
+ std::function<CBlockIndex *(const uint256 &)> insertBlockIndex) {
std::unique_ptr<CDBIterator> pcursor(NewIterator());
pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256()));
// Load mapBlockIndex
while (pcursor->Valid()) {
boost::this_thread::interruption_point();
std::pair<char, uint256> key;
if (!pcursor->GetKey(key) || key.first != DB_BLOCK_INDEX) {
break;
}
CDiskBlockIndex diskindex;
if (!pcursor->GetValue(diskindex)) {
return error("LoadBlockIndex() : failed to read value");
}
// Construct block index object
CBlockIndex *pindexNew = insertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
pindexNew->nHeight = diskindex.nHeight;
pindexNew->nFile = diskindex.nFile;
pindexNew->nDataPos = diskindex.nDataPos;
pindexNew->nUndoPos = diskindex.nUndoPos;
pindexNew->nVersion = diskindex.nVersion;
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
pindexNew->nTime = diskindex.nTime;
pindexNew->nBits = diskindex.nBits;
pindexNew->nNonce = diskindex.nNonce;
pindexNew->nStatus = diskindex.nStatus;
pindexNew->nTx = diskindex.nTx;
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits,
Params().GetConsensus()))
return error("LoadBlockIndex(): CheckProofOfWork failed: %s",
pindexNew->ToString());
pcursor->Next();
}
return true;
}
diff --git a/src/txdb.h b/src/txdb.h
index 6e772c6f44..29841d4903 100644
--- a/src/txdb.h
+++ b/src/txdb.h
@@ -1,129 +1,127 @@
// 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.
#ifndef BITCOIN_TXDB_H
#define BITCOIN_TXDB_H
#include "chain.h"
#include "coins.h"
#include "dbwrapper.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
-#include <boost/function.hpp>
-
class CBlockIndex;
class CCoinsViewDBCursor;
class uint256;
//! Compensate for extra memory peak (x1.5-x1.9) at flush time.
static constexpr int DB_PEAK_USAGE_FACTOR = 2;
//! No need to periodic flush if at least this much space still available.
static constexpr int MAX_BLOCK_COINSDB_USAGE = 200 * DB_PEAK_USAGE_FACTOR;
//! Always periodic flush if less than this much space still available.
static constexpr int MIN_BLOCK_COINSDB_USAGE = 50 * DB_PEAK_USAGE_FACTOR;
//! -dbcache default (MiB)
static const int64_t nDefaultDbCache = 450;
//! max. -dbcache (MiB)
static const int64_t nMaxDbCache = sizeof(void *) > 4 ? 16384 : 1024;
//! min. -dbcache (MiB)
static const int64_t nMinDbCache = 4;
//! Max memory allocated to block tree DB specific cache, if no -txindex (MiB)
static const int64_t nMaxBlockDBCache = 2;
//! Max memory allocated to block tree DB specific cache, if -txindex (MiB)
// Unlike for the UTXO database, for the txindex scenario the leveldb cache make
// a meaningful difference:
// https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991
static const int64_t nMaxBlockDBAndTxIndexCache = 1024;
//! Max memory allocated to coin DB specific cache (MiB)
static const int64_t nMaxCoinsDBCache = 8;
struct CDiskTxPos : public CDiskBlockPos {
unsigned int nTxOffset; // after header
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
READWRITE(*(CDiskBlockPos *)this);
READWRITE(VARINT(nTxOffset));
}
CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn)
: CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {}
CDiskTxPos() { SetNull(); }
void SetNull() {
CDiskBlockPos::SetNull();
nTxOffset = 0;
}
};
/** CCoinsView backed by the coin database (chainstate/) */
class CCoinsViewDB : public CCoinsView {
protected:
CDBWrapper db;
public:
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
bool GetCoins(const uint256 &txid, CCoins &coins) const;
bool HaveCoins(const uint256 &txid) const;
uint256 GetBestBlock() const;
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
CCoinsViewCursor *Cursor() const;
};
/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
class CCoinsViewDBCursor : public CCoinsViewCursor {
public:
~CCoinsViewDBCursor() {}
bool GetKey(uint256 &key) const;
bool GetValue(CCoins &coins) const;
unsigned int GetValueSize() const;
bool Valid() const;
void Next();
private:
CCoinsViewDBCursor(CDBIterator *pcursorIn, const uint256 &hashBlockIn)
: CCoinsViewCursor(hashBlockIn), pcursor(pcursorIn) {}
std::unique_ptr<CDBIterator> pcursor;
std::pair<char, uint256> keyTmp;
friend class CCoinsViewDB;
};
/** Access to the block database (blocks/index/) */
class CBlockTreeDB : public CDBWrapper {
public:
CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
private:
CBlockTreeDB(const CBlockTreeDB &);
void operator=(const CBlockTreeDB &);
public:
bool WriteBatchSync(
const std::vector<std::pair<int, const CBlockFileInfo *>> &fileInfo,
int nLastFile, const std::vector<const CBlockIndex *> &blockinfo);
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
bool ReadLastBlockFile(int &nFile);
bool WriteReindexing(bool fReindex);
bool ReadReindexing(bool &fReindex);
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos>> &list);
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts(
- boost::function<CBlockIndex *(const uint256 &)> insertBlockIndex);
+ std::function<CBlockIndex *(const uint256 &)> insertBlockIndex);
};
#endif // BITCOIN_TXDB_H

File Metadata

Mime Type
text/x-diff
Expires
Sun, Mar 2, 08:34 (21 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187092
Default Alt Text
(223 KB)

Event Timeline