Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13114902
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
223 KB
Subscribers
None
View Options
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 ¶ms,
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 ¶ms,
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
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Mar 2, 08:34 (18 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187092
Default Alt Text
(223 KB)
Attached To
rSTAGING Bitcoin ABC staging
Event Timeline
Log In to Comment