diff --git a/doc/release-notes.md b/doc/release-notes.md
index 6562c6a0d5..7035f7856b 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,6 +1,7 @@
Bitcoin ABC version 0.17.1 is now available from:
This release includes the following features and fixes:
+ - Added CORS headers and pre-flight request support via RPC
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 4e7c6f6ca4..bc46cd5e89 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -1,270 +1,401 @@
// 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 // boost::trim
#include
/** 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, std::function &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() override { return "HTTP"; }
RPCTimerBase *NewTimer(std::function &func,
int64_t millis) override {
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;
+/* RPC CORS Domain, allowed Origin */
+static std::string strRPCCORSDomain;
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 (gArgs.IsArgSet("-rpcauth")) {
// Search for multi-user login/pass "rpcauth" from config
for (const std::string &strRPCAuth : gArgs.GetArgs("-rpcauth")) {
std::vector 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(strSalt.c_str()),
strSalt.size())
.Write(reinterpret_cast(strPass.c_str()),
strPass.size())
.Finalize(out);
std::vector 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 checkCORS(HTTPRequest *req) {
+ // https://www.w3.org/TR/cors/#resource-requests
+
+ // 1. If the Origin header is not present terminate this set of steps.
+ // The request is outside the scope of this specification.
+ std::pair origin = req->GetHeader("origin");
+ if (!origin.first) {
+ return false;
+ }
+
+ // 2. If the value of the Origin header is not a case-sensitive match for
+ // any of the values in list of origins do not set any additional headers
+ // and terminate this set of steps.
+ // Note: Always matching is acceptable since the list of origins can be
+ // unbounded.
+ if (origin.second != strRPCCORSDomain) {
+ return false;
+ }
+
+ if (req->GetRequestMethod() == HTTPRequest::OPTIONS) {
+ // 6.2 Preflight Request
+ // In response to a preflight request the resource indicates which
+ // methods and headers (other than simple methods and simple
+ // headers) it is willing to handle and whether it supports
+ // credentials.
+ // Resources must use the following set of steps to determine which
+ // additional headers to use in the response:
+
+ // 3. Let method be the value as result of parsing the
+ // Access-Control-Request-Method header.
+ // If there is no Access-Control-Request-Method header or if parsing
+ // failed, do not set any additional headers and terminate this set
+ // of steps. The request is outside the scope of this specification.
+ std::pair method =
+ req->GetHeader("access-control-request-method");
+ if (!method.first) {
+ return false;
+ }
+
+ // 4. Let header field-names be the values as result of parsing
+ // the Access-Control-Request-Headers headers.
+ // If there are no Access-Control-Request-Headers headers let header
+ // field-names be the empty list.
+ // If parsing failed do not set any additional headers and terminate
+ // this set of steps. The request is outside the scope of this
+ // specification.
+ std::pair header_field_names =
+ req->GetHeader("access-control-request-headers");
+
+ // 5. If method is not a case-sensitive match for any of the
+ // values in list of methods do not set any additional headers
+ // and terminate this set of steps.
+ // Note: Always matching is acceptable since the list of methods
+ // can be unbounded.
+ if (method.second != "POST") {
+ return false;
+ }
+
+ // 6. If any of the header field-names is not a ASCII case-
+ // insensitive match for any of the values in list of headers do not
+ // set any additional headers and terminate this set of steps.
+ // Note: Always matching is acceptable since the list of headers can
+ // be unbounded.
+ const std::string& list_of_headers = "authorization,content-type";
+
+ // 7. If the resource supports credentials add a single
+ // Access-Control-Allow-Origin header, with the value of the Origin
+ // header as value, and add a single
+ // Access-Control-Allow-Credentials header with the case-sensitive
+ // string "true" as value.
+ req->WriteHeader("Access-Control-Allow-Origin", origin.second);
+ req->WriteHeader("Access-Control-Allow-Credentials", "true");
+
+ // 8. Optionally add a single Access-Control-Max-Age header with as
+ // value the amount of seconds the user agent is allowed to cache
+ // the result of the request.
+
+ // 9. If method is a simple method this step may be skipped.
+ // Add one or more Access-Control-Allow-Methods headers consisting
+ // of (a subset of) the list of methods.
+ // If a method is a simple method it does not need to be listed, but
+ // this is not prohibited.
+ // Note: Since the list of methods can be unbounded, simply
+ // returning the method indicated by
+ // Access-Control-Request-Method (if supported) can be enough.
+ req->WriteHeader("Access-Control-Allow-Methods", method.second);
+
+ // 10. If each of the header field-names is a simple header and none
+ // is Content-Type, this step may be skipped.
+ // Add one or more Access-Control-Allow-Headers headers consisting
+ // of (a subset of) the list of headers.
+ req->WriteHeader(
+ "Access-Control-Allow-Headers",
+ header_field_names.first ? header_field_names.second
+ : list_of_headers);
+ req->WriteReply(HTTP_OK);
+ return true;
+ }
+
+ // 6.1 Simple Cross-Origin Request, Actual Request, and Redirects
+ // In response to a simple cross-origin request or actual request the
+ // resource indicates whether or not to share the response.
+ // If the resource has been relocated, it indicates whether to share its
+ // new URL.
+ // Resources must use the following set of steps to determine which
+ // additional headers to use in the response:
+
+ // 3. If the resource supports credentials add a single
+ // Access-Control-Allow-Origin header, with the value of the Origin
+ // header as value, and add a single Access-Control-Allow-Credentials
+ // header with the case-sensitive string "true" as value.
+ req->WriteHeader("Access-Control-Allow-Origin", origin.second);
+ req->WriteHeader("Access-Control-Allow-Credentials", "true");
+
+ // 4. If the list of exposed headers is not empty add one or more
+ // Access-Control-Expose-Headers headers, with as values the header
+ // field names given in the list of exposed headers.
+ req->WriteHeader("Access-Control-Expose-Headers", "WWW-Authenticate");
+
+ return false;
+}
+
static bool HTTPReq_JSONRPC(Config &config, HTTPRequest *req,
const std::string &) {
+ // First, check and/or set CORS headers
+ if (checkCORS(req)) {
+ return true;
+ }
+
// 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 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);
} else if (valRequest.isArray()) {
// array of requests
strReply = JSONRPCExecBatch(config, jreq, 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 (gArgs.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 = gArgs.GetArg("-rpcuser", "") + ":" +
gArgs.GetArg("-rpcpassword", "");
}
+
+ strRPCCORSDomain = gArgs.GetArg("-rpccorsdomain", "");
return true;
}
bool StartHTTPRPC() {
LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
if (!InitRPCAuthentication()) return false;
RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
#ifdef ENABLE_WALLET
// ifdef can be removed once we switch to better endpoint support and API
// versioning
RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
#endif
assert(EventBase());
httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
RPCSetTimerInterface(httpRPCTimerInterface);
return true;
}
void InterruptHTTPRPC() {
LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
}
void StopHTTPRPC() {
LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
UnregisterHTTPHandler("/", true);
if (httpRPCTimerInterface) {
RPCUnsetTimerInterface(httpRPCTimerInterface);
delete httpRPCTimerInterface;
httpRPCTimerInterface = 0;
}
}
diff --git a/src/httpserver.cpp b/src/httpserver.cpp
index 835d33c01a..488f9438a9 100644
--- a/src/httpserver.cpp
+++ b/src/httpserver.cpp
@@ -1,669 +1,671 @@
// 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 "httpserver.h"
#include "chainparamsbase.h"
#include "compat.h"
#include "config.h"
#include "netbase.h"
#include "rpc/protocol.h" // For HTTP status codes
#include "sync.h"
#include "ui_interface.h"
#include "util.h"
#include "utilstrencodings.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef EVENT__HAVE_NETINET_IN_H
#include
#ifdef _XOPEN_SOURCE_EXTENDED
#include
#endif
#endif
#include
#include
#include
#include
/** Maximum size of http request (request line + headers) */
static const size_t MAX_HEADERS_SIZE = 8192;
/**
* Maximum HTTP post body size. Twice the maximum block size is added to this
* value in practice.
*/
static const size_t MIN_SUPPORTED_BODY_SIZE = 0x02000000;
/** HTTP request work item */
class HTTPWorkItem final : public HTTPClosure {
public:
HTTPWorkItem(Config &_config, std::unique_ptr _req,
const std::string &_path, const HTTPRequestHandler &_func)
: req(std::move(_req)), path(_path), func(_func), config(&_config) {}
void operator()() override { func(*config, req.get(), path); }
std::unique_ptr req;
private:
std::string path;
HTTPRequestHandler func;
Config *config;
};
/**
* Simple work queue for distributing work over multiple threads.
* Work items are simply callable objects.
*/
template class WorkQueue {
private:
/** Mutex protects entire object */
std::mutex cs;
std::condition_variable cond;
std::deque> queue;
bool running;
size_t maxDepth;
int numThreads;
/** RAII object to keep track of number of running worker threads */
class ThreadCounter {
public:
WorkQueue &wq;
ThreadCounter(WorkQueue &w) : wq(w) {
std::lock_guard lock(wq.cs);
wq.numThreads += 1;
}
~ThreadCounter() {
std::lock_guard lock(wq.cs);
wq.numThreads -= 1;
wq.cond.notify_all();
}
};
public:
WorkQueue(size_t _maxDepth)
: running(true), maxDepth(_maxDepth), numThreads(0) {}
/** Precondition: worker threads have all stopped
* (call WaitExit)
*/
~WorkQueue() {}
/** Enqueue a work item */
bool Enqueue(WorkItem *item) {
std::unique_lock lock(cs);
if (queue.size() >= maxDepth) {
return false;
}
queue.emplace_back(std::unique_ptr(item));
cond.notify_one();
return true;
}
/** Thread function */
void Run() {
ThreadCounter count(*this);
while (true) {
std::unique_ptr i;
{
std::unique_lock lock(cs);
while (running && queue.empty())
cond.wait(lock);
if (!running) break;
i = std::move(queue.front());
queue.pop_front();
}
(*i)();
}
}
/** Interrupt and exit loops */
void Interrupt() {
std::unique_lock lock(cs);
running = false;
cond.notify_all();
}
/** Wait for worker threads to exit */
void WaitExit() {
std::unique_lock lock(cs);
while (numThreads > 0)
cond.wait(lock);
}
/** Return current depth of queue */
size_t Depth() {
std::unique_lock lock(cs);
return queue.size();
}
};
struct HTTPPathHandler {
HTTPPathHandler() {}
HTTPPathHandler(std::string _prefix, bool _exactMatch,
HTTPRequestHandler _handler)
: prefix(_prefix), exactMatch(_exactMatch), handler(_handler) {}
std::string prefix;
bool exactMatch;
HTTPRequestHandler handler;
};
/** HTTP module state */
//! libevent event loop
static struct event_base *eventBase = 0;
//! HTTP server
struct evhttp *eventHTTP = 0;
//! List of subnets to allow RPC connections from
static std::vector rpc_allow_subnets;
//! Work queue for handling longer requests off the event loop thread
static WorkQueue *workQueue = 0;
//! Handlers for (sub)paths
std::vector pathHandlers;
//! Bound listening sockets
std::vector boundSockets;
/** Check if a network address is allowed to access the HTTP server */
static bool ClientAllowed(const CNetAddr &netaddr) {
if (!netaddr.IsValid()) return false;
for (const CSubNet &subnet : rpc_allow_subnets)
if (subnet.Match(netaddr)) return true;
return false;
}
/** Initialize ACL list for HTTP server */
static bool InitHTTPAllowList() {
rpc_allow_subnets.clear();
CNetAddr localv4;
CNetAddr localv6;
LookupHost("127.0.0.1", localv4, false);
LookupHost("::1", localv6, false);
// always allow IPv4 local subnet.
rpc_allow_subnets.push_back(CSubNet(localv4, 8));
// always allow IPv6 localhost.
rpc_allow_subnets.push_back(CSubNet(localv6));
if (gArgs.IsArgSet("-rpcallowip")) {
for (const std::string &strAllow : gArgs.GetArgs("-rpcallowip")) {
CSubNet subnet;
LookupSubNet(strAllow.c_str(), subnet);
if (!subnet.IsValid()) {
uiInterface.ThreadSafeMessageBox(
strprintf("Invalid -rpcallowip subnet specification: %s. "
"Valid 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).",
strAllow),
"", CClientUIInterface::MSG_ERROR);
return false;
}
rpc_allow_subnets.push_back(subnet);
}
}
std::string strAllowed;
for (const CSubNet &subnet : rpc_allow_subnets)
strAllowed += subnet.ToString() + " ";
LogPrint(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed);
return true;
}
/** HTTP request method as string - use for logging only */
static std::string RequestMethodString(HTTPRequest::RequestMethod m) {
switch (m) {
case HTTPRequest::GET:
return "GET";
- break;
case HTTPRequest::POST:
return "POST";
- break;
case HTTPRequest::HEAD:
return "HEAD";
- break;
case HTTPRequest::PUT:
return "PUT";
- break;
+ case HTTPRequest::OPTIONS:
+ return "OPTIONS";
default:
return "unknown";
}
}
/** HTTP request callback */
static void http_request_cb(struct evhttp_request *req, void *arg) {
Config &config = *reinterpret_cast(arg);
std::unique_ptr hreq(new HTTPRequest(req));
LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(),
hreq->GetPeer().ToString());
// Early address-based allow check
if (!ClientAllowed(hreq->GetPeer())) {
hreq->WriteReply(HTTP_FORBIDDEN);
return;
}
// Early reject unknown HTTP methods
if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
hreq->WriteReply(HTTP_BADMETHOD);
return;
}
// Find registered handler for prefix
std::string strURI = hreq->GetURI();
std::string path;
std::vector::const_iterator i = pathHandlers.begin();
std::vector::const_iterator iend = pathHandlers.end();
for (; i != iend; ++i) {
bool match = false;
if (i->exactMatch) {
match = (strURI == i->prefix);
} else {
match = (strURI.substr(0, i->prefix.size()) == i->prefix);
}
if (match) {
path = strURI.substr(i->prefix.size());
break;
}
}
// Dispatch to worker thread.
if (i != iend) {
std::unique_ptr item(
new HTTPWorkItem(config, std::move(hreq), path, i->handler));
assert(workQueue);
if (workQueue->Enqueue(item.get())) {
/* if true, queue took ownership */
item.release();
} else {
LogPrintf("WARNING: request rejected because http work queue depth "
"exceeded, it can be increased with the -rpcworkqueue= "
"setting\n");
item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded");
}
} else {
hreq->WriteReply(HTTP_NOTFOUND);
}
}
/** Callback to reject HTTP requests after shutdown. */
static void http_reject_request_cb(struct evhttp_request *req, void *) {
LogPrint(BCLog::HTTP, "Rejecting request while shutting down\n");
evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr);
}
/** Event dispatcher thread */
static bool ThreadHTTP(struct event_base *base, struct evhttp *http) {
RenameThread("bitcoin-http");
LogPrint(BCLog::HTTP, "Entering http event loop\n");
event_base_dispatch(base);
// Event loop will be interrupted by InterruptHTTPServer()
LogPrint(BCLog::HTTP, "Exited http event loop\n");
return event_base_got_break(base) == 0;
}
/** Bind HTTP server to specified addresses */
static bool HTTPBindAddresses(struct evhttp *http) {
int defaultPort = gArgs.GetArg("-rpcport", BaseParams().RPCPort());
std::vector> endpoints;
// Determine what addresses to bind to
if (!gArgs.IsArgSet("-rpcallowip")) {
// Default to loopback if not allowing external IPs.
endpoints.push_back(std::make_pair("::1", defaultPort));
endpoints.push_back(std::make_pair("127.0.0.1", defaultPort));
if (gArgs.IsArgSet("-rpcbind")) {
LogPrintf("WARNING: option -rpcbind was ignored because "
"-rpcallowip was not specified, refusing to allow "
"everyone to connect\n");
}
} else if (gArgs.IsArgSet("-rpcbind")) {
// Specific bind address.
for (const std::string &strRPCBind : gArgs.GetArgs("-rpcbind")) {
int port = defaultPort;
std::string host;
SplitHostPort(strRPCBind, port, host);
endpoints.push_back(std::make_pair(host, port));
}
} else {
// No specific bind address specified, bind to any.
endpoints.push_back(std::make_pair("::", defaultPort));
endpoints.push_back(std::make_pair("0.0.0.0", defaultPort));
}
// Bind addresses
for (std::vector>::iterator i =
endpoints.begin();
i != endpoints.end(); ++i) {
LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first,
i->second);
evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(
http, i->first.empty() ? nullptr : i->first.c_str(), i->second);
if (bind_handle) {
boundSockets.push_back(bind_handle);
} else {
LogPrintf("Binding RPC on address %s port %i failed.\n", i->first,
i->second);
}
}
return !boundSockets.empty();
}
/** Simple wrapper to set thread name and run work queue */
static void HTTPWorkQueueRun(WorkQueue *queue) {
RenameThread("bitcoin-httpworker");
queue->Run();
}
/** libevent event log callback */
static void libevent_log_cb(int severity, const char *msg) {
#ifndef EVENT_LOG_WARN
// EVENT_LOG_WARN was added in 2.0.19; but before then _EVENT_LOG_WARN existed.
#define EVENT_LOG_WARN _EVENT_LOG_WARN
#endif
// Log warn messages and higher without debug category.
if (severity >= EVENT_LOG_WARN) {
LogPrintf("libevent: %s\n", msg);
} else {
LogPrint(BCLog::LIBEVENT, "libevent: %s\n", msg);
}
}
bool InitHTTPServer(Config &config) {
struct evhttp *http = 0;
struct event_base *base = 0;
if (!InitHTTPAllowList()) return false;
if (gArgs.GetBoolArg("-rpcssl", false)) {
uiInterface.ThreadSafeMessageBox(
"SSL mode for RPC (-rpcssl) is no longer supported.", "",
CClientUIInterface::MSG_ERROR);
return false;
}
// Redirect libevent's logging to our own log
event_set_log_callback(&libevent_log_cb);
#if LIBEVENT_VERSION_NUMBER >= 0x02010100
// If -debug=libevent, set full libevent debugging.
// Otherwise, disable all libevent debugging.
if (LogAcceptCategory(BCLog::LIBEVENT)) {
event_enable_debug_logging(EVENT_DBG_ALL);
} else {
event_enable_debug_logging(EVENT_DBG_NONE);
}
#endif
#ifdef WIN32
evthread_use_windows_threads();
#else
evthread_use_pthreads();
#endif
// XXX RAII
base = event_base_new();
if (!base) {
LogPrintf("Couldn't create an event_base: exiting\n");
return false;
}
/* Create a new evhttp object to handle requests. */
// XXX RAII
http = evhttp_new(base);
if (!http) {
LogPrintf("couldn't create evhttp. Exiting.\n");
event_base_free(base);
return false;
}
evhttp_set_timeout(
http, gArgs.GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
evhttp_set_max_body_size(
http, MIN_SUPPORTED_BODY_SIZE + 2 * config.GetMaxBlockSize());
evhttp_set_gencb(http, http_request_cb, &config);
+ // Only POST and OPTIONS are supported, but we return HTTP 405 for the
+ // others
+ evhttp_set_allowed_methods(http,
+ EVHTTP_REQ_GET | EVHTTP_REQ_POST |
+ EVHTTP_REQ_HEAD | EVHTTP_REQ_PUT |
+ EVHTTP_REQ_DELETE | EVHTTP_REQ_OPTIONS);
+
if (!HTTPBindAddresses(http)) {
LogPrintf("Unable to bind any endpoint for RPC server\n");
evhttp_free(http);
event_base_free(base);
return false;
}
LogPrint(BCLog::HTTP, "Initialized HTTP server\n");
int workQueueDepth = std::max(
(long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth);
workQueue = new WorkQueue(workQueueDepth);
eventBase = base;
eventHTTP = http;
return true;
}
std::thread threadHTTP;
std::future threadResult;
bool StartHTTPServer() {
LogPrint(BCLog::HTTP, "Starting HTTP server\n");
int rpcThreads =
std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
std::packaged_task task(ThreadHTTP);
threadResult = task.get_future();
threadHTTP = std::thread(std::move(task), eventBase, eventHTTP);
for (int i = 0; i < rpcThreads; i++) {
std::thread rpc_worker(HTTPWorkQueueRun, workQueue);
rpc_worker.detach();
}
return true;
}
void InterruptHTTPServer() {
LogPrint(BCLog::HTTP, "Interrupting HTTP server\n");
if (eventHTTP) {
// Unlisten sockets
for (evhttp_bound_socket *socket : boundSockets) {
evhttp_del_accept_socket(eventHTTP, socket);
}
// Reject requests on current connections
evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr);
}
if (workQueue) workQueue->Interrupt();
}
void StopHTTPServer() {
LogPrint(BCLog::HTTP, "Stopping HTTP server\n");
if (workQueue) {
LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n");
workQueue->WaitExit();
delete workQueue;
}
if (eventBase) {
LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
// Give event loop a few seconds to exit (to send back last RPC
// responses), then break it. Before this was solved with
// event_base_loopexit, but that didn't work as expected in at least
// libevent 2.0.21 and always introduced a delay. In libevent master
// that appears to be solved, so in the future that solution could be
// used again (if desirable).
// (see discussion in https://github.com/bitcoin/bitcoin/pull/6990)
if (threadResult.valid() &&
threadResult.wait_for(std::chrono::milliseconds(2000)) ==
std::future_status::timeout) {
LogPrintf("HTTP event loop did not exit within allotted time, "
"sending loopbreak\n");
event_base_loopbreak(eventBase);
}
threadHTTP.join();
}
if (eventHTTP) {
evhttp_free(eventHTTP);
eventHTTP = 0;
}
if (eventBase) {
event_base_free(eventBase);
eventBase = 0;
}
LogPrint(BCLog::HTTP, "Stopped HTTP server\n");
}
struct event_base *EventBase() {
return eventBase;
}
static void httpevent_callback_fn(evutil_socket_t, short, void *data) {
// Static handler: simply call inner handler
HTTPEvent *self = ((HTTPEvent *)data);
self->handler();
if (self->deleteWhenTriggered) delete self;
}
HTTPEvent::HTTPEvent(struct event_base *base, bool _deleteWhenTriggered,
const std::function &_handler)
: deleteWhenTriggered(_deleteWhenTriggered), handler(_handler) {
ev = event_new(base, -1, 0, httpevent_callback_fn, this);
assert(ev);
}
HTTPEvent::~HTTPEvent() {
event_free(ev);
}
void HTTPEvent::trigger(struct timeval *tv) {
if (tv == nullptr) {
// Immediately trigger event in main thread.
event_active(ev, 0, 0);
} else {
// Trigger after timeval passed.
evtimer_add(ev, tv);
}
}
HTTPRequest::HTTPRequest(struct evhttp_request *_req)
: req(_req), replySent(false) {}
HTTPRequest::~HTTPRequest() {
if (!replySent) {
// Keep track of whether reply was sent to avoid request leaks
LogPrintf("%s: Unhandled request\n", __func__);
WriteReply(HTTP_INTERNAL, "Unhandled request");
}
// evhttpd cleans up the request, as long as a reply was sent.
}
std::pair HTTPRequest::GetHeader(const std::string &hdr) {
const struct evkeyvalq *headers = evhttp_request_get_input_headers(req);
assert(headers);
const char *val = evhttp_find_header(headers, hdr.c_str());
if (val)
return std::make_pair(true, val);
else
return std::make_pair(false, "");
}
std::string HTTPRequest::ReadBody() {
struct evbuffer *buf = evhttp_request_get_input_buffer(req);
if (!buf) return "";
size_t size = evbuffer_get_length(buf);
/** Trivial implementation: if this is ever a performance bottleneck,
* internal copying can be avoided in multi-segment buffers by using
* evbuffer_peek and an awkward loop. Though in that case, it'd be even
* better to not copy into an intermediate string but use a stream
* abstraction to consume the evbuffer on the fly in the parsing algorithm.
*/
const char *data = (const char *)evbuffer_pullup(buf, size);
// returns nullptr in case of empty buffer.
if (!data) {
return "";
}
std::string rv(data, size);
evbuffer_drain(buf, size);
return rv;
}
void HTTPRequest::WriteHeader(const std::string &hdr,
const std::string &value) {
struct evkeyvalq *headers = evhttp_request_get_output_headers(req);
assert(headers);
evhttp_add_header(headers, hdr.c_str(), value.c_str());
}
/** Closure sent to main thread to request a reply to be sent to a HTTP request.
* Replies must be sent in the main loop in the main http thread, this cannot be
* done from worker threads.
*/
void HTTPRequest::WriteReply(int nStatus, const std::string &strReply) {
assert(!replySent && req);
// Send event to main http thread to send reply message
struct evbuffer *evb = evhttp_request_get_output_buffer(req);
assert(evb);
evbuffer_add(evb, strReply.data(), strReply.size());
HTTPEvent *ev =
new HTTPEvent(eventBase, true, std::bind(evhttp_send_reply, req,
nStatus, (const char *)nullptr,
(struct evbuffer *)nullptr));
ev->trigger(0);
replySent = true;
// transferred back to main thread.
req = 0;
}
CService HTTPRequest::GetPeer() {
evhttp_connection *con = evhttp_request_get_connection(req);
CService peer;
if (con) {
// evhttp retains ownership over returned address string
const char *address = "";
uint16_t port = 0;
evhttp_connection_get_peer(con, (char **)&address, &port);
peer = LookupNumeric(address, port);
}
return peer;
}
std::string HTTPRequest::GetURI() {
return evhttp_request_get_uri(req);
}
HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() {
switch (evhttp_request_get_command(req)) {
case EVHTTP_REQ_GET:
return GET;
- break;
case EVHTTP_REQ_POST:
return POST;
- break;
case EVHTTP_REQ_HEAD:
return HEAD;
- break;
case EVHTTP_REQ_PUT:
return PUT;
- break;
+ case EVHTTP_REQ_OPTIONS:
+ return OPTIONS;
default:
return UNKNOWN;
- break;
}
}
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch,
const HTTPRequestHandler &handler) {
LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n",
prefix, exactMatch);
pathHandlers.push_back(HTTPPathHandler(prefix, exactMatch, handler));
}
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) {
std::vector::iterator i = pathHandlers.begin();
std::vector::iterator iend = pathHandlers.end();
for (; i != iend; ++i)
if (i->prefix == prefix && i->exactMatch == exactMatch) break;
if (i != iend) {
LogPrint(BCLog::HTTP,
"Unregistering HTTP handler for %s (exactmatch %d)\n", prefix,
exactMatch);
pathHandlers.erase(i);
}
}
diff --git a/src/httpserver.h b/src/httpserver.h
index eb277dbdf2..ef58d19dab 100644
--- a/src/httpserver.h
+++ b/src/httpserver.h
@@ -1,150 +1,150 @@
// 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_HTTPSERVER_H
#define BITCOIN_HTTPSERVER_H
#include
#include
#include
static const int DEFAULT_HTTP_THREADS = 4;
static const int DEFAULT_HTTP_WORKQUEUE = 16;
static const int DEFAULT_HTTP_SERVER_TIMEOUT = 30;
struct evhttp_request;
struct event_base;
class Config;
class CService;
class HTTPRequest;
/** Initialize HTTP server.
* Call this before RegisterHTTPHandler or EventBase().
*/
bool InitHTTPServer(Config &config);
/** Start HTTP server.
* This is separate from InitHTTPServer to give users race-condition-free time
* to register their handlers between InitHTTPServer and StartHTTPServer.
*/
bool StartHTTPServer();
/** Interrupt HTTP server threads */
void InterruptHTTPServer();
/** Stop HTTP server */
void StopHTTPServer();
/** Handler for requests to a certain HTTP path */
typedef std::function
HTTPRequestHandler;
/** Register handler for prefix.
* If multiple handlers match a prefix, the first-registered one will
* be invoked.
*/
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch,
const HTTPRequestHandler &handler);
/** Unregister handler for prefix */
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch);
/** Return evhttp event base. This can be used by submodules to
* queue timers or custom events.
*/
struct event_base *EventBase();
/** In-flight HTTP request.
* Thin C++ wrapper around evhttp_request.
*/
class HTTPRequest {
private:
struct evhttp_request *req;
bool replySent;
public:
HTTPRequest(struct evhttp_request *req);
~HTTPRequest();
- enum RequestMethod { UNKNOWN, GET, POST, HEAD, PUT };
+ enum RequestMethod { UNKNOWN, GET, POST, HEAD, PUT, OPTIONS };
/** Get requested URI.
*/
std::string GetURI();
/** Get CService (address:ip) for the origin of the http request.
*/
CService GetPeer();
/** Get request method.
*/
RequestMethod GetRequestMethod();
/**
* Get the request header specified by hdr, or an empty string.
* Return an pair (isPresent,string).
*/
std::pair GetHeader(const std::string &hdr);
/**
* Read request body.
*
* @note As this consumes the underlying buffer, call this only once.
* Repeated calls will return an empty string.
*/
std::string ReadBody();
/**
* Write output header.
*
* @note call this before calling WriteErrorReply or Reply.
*/
void WriteHeader(const std::string &hdr, const std::string &value);
/**
* Write HTTP reply.
* nStatus is the HTTP status code to send.
* strReply is the body of the reply. Keep it empty to send a standard
* message.
*
* @note Can be called only once. As this will give the request back to the
* main thread, do not call any other HTTPRequest methods after calling
* this.
*/
void WriteReply(int nStatus, const std::string &strReply = "");
};
/** Event handler closure.
*/
class HTTPClosure {
public:
virtual void operator()() = 0;
virtual ~HTTPClosure() {}
};
/** Event class. This can be used either as an cross-thread trigger or as a
* timer.
*/
class HTTPEvent {
public:
/** Create a new event.
* deleteWhenTriggered deletes this event object after the event is
* triggered (and the handler called)
* handler is the handler to call when the event is triggered.
*/
HTTPEvent(struct event_base *base, bool deleteWhenTriggered,
const std::function &handler);
~HTTPEvent();
/** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger
* it after
* the given time has elapsed.
*/
void trigger(struct timeval *tv);
bool deleteWhenTriggered;
std::function handler;
private:
struct event *ev;
};
#endif // BITCOIN_HTTPSERVER_H
diff --git a/src/init.cpp b/src/init.cpp
index 20a4e8594f..941268711f 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1,2322 +1,2325 @@
// 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 "fs.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/scriptcache.h"
#include "script/sigcache.h"
#include "script/standard.h"
#include "timedata.h"
#include "torcontrol.h"
#include "txdb.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "util.h"
#include "utilmoneystr.h"
#include "validation.h"
#include "validationinterface.h"
#ifdef ENABLE_WALLET
#include "wallet/rpcdump.h"
#include "wallet/wallet.h"
#endif
#include "warnings.h"
#include
#include
#include
#ifndef WIN32
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#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 g_connman;
std::unique_ptr 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 fRequestShutdown(false);
std::atomic 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 final : public CCoinsViewBacked {
public:
CCoinsViewErrorCatcher(CCoinsView *view) : CCoinsViewBacked(view) {}
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override {
try {
return CCoinsViewBacked::GetCoin(outpoint, coin);
} catch (const std::runtime_error &e) {
uiInterface.ThreadSafeMessageBox(
_("Error reading from database, shutting down."), "",
CClientUIInterface::MSG_ERROR);
LogPrintf("Error reading from database: %s\n", e.what());
// Starting the shutdown sequence and returning false to the caller
// would be interpreted as 'entry not found' (as opposed to unable
// to read data), and could lead to invalid interpretation. Just
// exit immediately, as we can't continue anyway, and all writes
// should be atomic.
abort();
}
}
// Writes do not need similar protection, as failure to write is handled by
// the caller.
};
static CCoinsViewDB *pcoinsdbview = nullptr;
static CCoinsViewErrorCatcher *pcoinscatcher = nullptr;
static std::unique_ptr 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
for (CWalletRef pwallet : vpwallets) {
pwallet->Flush(false);
}
#endif
MapPort(false);
UnregisterValidationInterface(peerLogic.get());
peerLogic.reset();
g_connman.reset();
StopTorControl();
UnregisterNodeSignals(GetNodeSignals());
if (fDumpMempoolLater &&
gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
DumpMempool();
}
if (fFeeEstimatesInitialized) {
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_fileout(fsbridge::fopen(est_path, "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
for (CWalletRef pwallet : vpwallets) {
pwallet->Flush(true);
}
#endif
#if ENABLE_ZMQ
if (pzmqNotificationInterface) {
UnregisterValidationInterface(pzmqNotificationInterface);
delete pzmqNotificationInterface;
pzmqNotificationInterface = nullptr;
}
#endif
#ifndef WIN32
try {
fs::remove(GetPidFile());
} catch (const fs::filesystem_error &e) {
LogPrintf("%s: Unable to remove pidfile: %s\n", __func__, e.what());
}
#endif
UnregisterAllValidationInterfaces();
#ifdef ENABLE_WALLET
for (CWalletRef pwallet : vpwallets) {
delete pwallet;
}
vpwallets.clear();
#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) {
GetLogger().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(BCLog::RPC, "RPC stopped.\n");
}
void OnRPCPreCommand(const CRPCCommand &cmd) {
// Observe safe mode.
std::string strWarning = GetWarnings("rpc");
if (strWarning != "" &&
!gArgs.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 auto defaultBaseParams =
CreateBaseChainParams(CBaseChainParams::MAIN);
const auto testnetBaseParams =
CreateBaseChainParams(CBaseChainParams::TESTNET);
const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN);
const auto testnetChainParams =
CreateChainParams(CBaseChainParams::TESTNET);
const bool showDebug = gArgs.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=",
_("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=",
_("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: %d)"),
DEFAULT_BLOCKSONLY));
strUsage += HelpMessageOpt(
"-assumevalid=",
strprintf(
_("If this block is in the chain assume that it and its ancestors "
"are valid and potentially skip their script verification (0 to "
"verify all, default: %s, testnet: %s)"),
defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(),
testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()));
strUsage += HelpMessageOpt(
"-conf=", 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=", _("Specify data directory"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-dbbatchsize",
strprintf(
"Maximum database write batch size in bytes (default: %u)",
nDefaultDbBatchSize));
}
strUsage += HelpMessageOpt(
"-dbcache=",
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: %d)",
DEFAULT_FEEFILTER));
}
strUsage += HelpMessageOpt(
"-loadblock=",
_("Imports blocks from external blk000??.dat file on startup"));
strUsage += HelpMessageOpt(
"-maxorphantx=", strprintf(_("Keep at most unconnectable "
"transactions in memory (default: %u)"),
DEFAULT_MAX_ORPHAN_TRANSACTIONS));
strUsage += HelpMessageOpt("-maxmempool=",
strprintf(_("Keep the transaction memory pool "
"below megabytes (default: %u)"),
DEFAULT_MAX_MEMPOOL_SIZE));
strUsage +=
HelpMessageOpt("-mempoolexpiry=",
strprintf(_("Do not keep transactions in the mempool "
"longer than hours (default: %u)"),
DEFAULT_MEMPOOL_EXPIRY));
if (showDebug) {
strUsage += HelpMessageOpt(
"-minimumchainwork=",
strprintf(
"Minimum work assumed to exist on a valid chain in hex "
"(default: %s, testnet: %s)",
defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(),
testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()));
}
strUsage +=
HelpMessageOpt("-persistmempool",
strprintf(_("Whether to save the mempool on shutdown "
"and load on restart (default: %u)"),
DEFAULT_PERSIST_MEMPOOL));
strUsage += HelpMessageOpt(
"-blockreconstructionextratxn=",
strprintf(_("Extra transactions to keep in memory for compact block "
"reconstructions (default: %u)"),
DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
strUsage += HelpMessageOpt(
"-par=",
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=",
strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME));
#endif
strUsage += HelpMessageOpt(
"-prune=",
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: %d)"),
DEFAULT_TXINDEX));
strUsage += HelpMessageOpt(
"-usecashaddr", _("Use Cash Address for destination encoding instead "
"of base58 (activate by default on Jan, 14)"));
strUsage += HelpMessageGroup(_("Connection options:"));
strUsage += HelpMessageOpt(
"-addnode=",
_("Add a node to connect to and attempt to keep the connection open"));
strUsage += HelpMessageOpt(
"-banscore=",
strprintf(
_("Threshold for disconnecting misbehaving peers (default: %u)"),
DEFAULT_BANSCORE_THRESHOLD));
strUsage += HelpMessageOpt(
"-bantime=", strprintf(_("Number of seconds to keep misbehaving "
"peers from reconnecting (default: %u)"),
DEFAULT_MISBEHAVING_BANTIME));
strUsage += HelpMessageOpt("-bind=",
_("Bind to given address and always listen on "
"it. Use [host]:port notation for IPv6"));
strUsage +=
HelpMessageOpt("-connect=",
_("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: %d)"), 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=",
_("Specify your own public address"));
strUsage += HelpMessageOpt(
"-forcednsseed",
strprintf(
_("Always query for peer addresses via DNS lookup (default: %d)"),
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=",
strprintf(_("Maintain at most connections to peers (default: %u)"),
DEFAULT_MAX_PEER_CONNECTIONS));
strUsage +=
HelpMessageOpt("-maxreceivebuffer=",
strprintf(_("Maximum per-connection receive buffer, "
"*1000 bytes (default: %u)"),
DEFAULT_MAXRECEIVEBUFFER));
strUsage += HelpMessageOpt(
"-maxsendbuffer=", strprintf(_("Maximum per-connection send buffer, "
"*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=",
strprintf(_("Use separate SOCKS5 proxy to reach peers "
"via Tor hidden services (default: %s)"),
"-proxy"));
strUsage += HelpMessageOpt(
"-onlynet=",
_("Only connect to nodes in network (ipv4, ipv6 or onion)"));
strUsage +=
HelpMessageOpt("-permitbaremultisig",
strprintf(_("Relay non-P2SH multisig (default: %d)"),
DEFAULT_PERMIT_BAREMULTISIG));
strUsage += HelpMessageOpt(
"-peerbloomfilters",
strprintf(_("Support filtering of blocks and transaction with bloom "
"filters (default: %d)"),
DEFAULT_PEERBLOOMFILTERS));
strUsage += HelpMessageOpt(
"-port=",
strprintf(
_("Listen for connections on (default: %u or testnet: %u)"),
defaultChainParams->GetDefaultPort(),
testnetChainParams->GetDefaultPort()));
strUsage +=
HelpMessageOpt("-proxy=", _("Connect through SOCKS5 proxy"));
strUsage += HelpMessageOpt(
"-proxyrandomize",
strprintf(_("Randomize credentials for every proxy connection. This "
"enables Tor stream isolation (default: %d)"),
DEFAULT_PROXYRANDOMIZE));
strUsage += HelpMessageOpt(
"-seednode=",
_("Connect to a node to retrieve peer addresses, and disconnect"));
strUsage += HelpMessageOpt(
"-timeout=", strprintf(_("Specify connection timeout in "
"milliseconds (minimum: 1, default: %d)"),
DEFAULT_CONNECT_TIMEOUT));
strUsage += HelpMessageOpt("-torcontrol=:",
strprintf(_("Tor control port to use if onion "
"listening enabled (default: %s)"),
DEFAULT_TOR_CONTROL));
strUsage += HelpMessageOpt("-torpassword=",
_("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=",
_("Bind to given address and whitelist peers connecting "
"to it. Use [host]:port notation for IPv6"));
strUsage += HelpMessageOpt(
"-whitelist=",
_("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=",
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=",
_("Enable publish hash block in "));
strUsage +=
HelpMessageOpt("-zmqpubhashtx=",
_("Enable publish hash transaction in "));
strUsage += HelpMessageOpt("-zmqpubrawblock=",
_("Enable publish raw block in "));
strUsage +=
HelpMessageOpt("-zmqpubrawtx=",
_("Enable publish raw transaction in "));
#endif
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
strUsage += HelpMessageOpt("-uacomment=",
_("Append comment to the user agent string"));
if (showDebug) {
strUsage += HelpMessageOpt(
"-checkblocks=",
strprintf(
_("How many blocks to check at startup (default: %u, 0 = all)"),
DEFAULT_CHECKBLOCKS));
strUsage +=
HelpMessageOpt("-checklevel=",
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)",
defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt(
"-checkmempool=",
strprintf("Run checks every transactions (default: %u)",
defaultChainParams->DefaultConsistencyChecks()));
strUsage += HelpMessageOpt(
"-checkpoints", strprintf("Disable expensive verification for "
"known chain history (default: %d)",
DEFAULT_CHECKPOINTS_ENABLED));
strUsage += HelpMessageOpt(
"-disablesafemode", strprintf("Disable safemode, override a real "
"safe mode event (default: %d)",
DEFAULT_DISABLE_SAFEMODE));
strUsage += HelpMessageOpt(
"-testsafemode",
strprintf("Force safe mode (default: %d)", DEFAULT_TESTSAFEMODE));
strUsage +=
HelpMessageOpt("-dropmessagestest=",
"Randomly drop 1 of every network messages");
strUsage +=
HelpMessageOpt("-fuzzmessagestest=",
"Randomly fuzz 1 of every network messages");
strUsage += HelpMessageOpt(
"-stopafterblockimport",
strprintf(
"Stop running after importing blocks from disk (default: %d)",
DEFAULT_STOPAFTERBLOCKIMPORT));
strUsage += HelpMessageOpt(
"-stopatheight", strprintf("Stop running after reaching the given "
"height in the main chain (default: %u)",
DEFAULT_STOPATHEIGHT));
strUsage += HelpMessageOpt(
"-limitancestorcount=",
strprintf("Do not accept transactions if number of in-mempool "
"ancestors is or more (default: %u)",
DEFAULT_ANCESTOR_LIMIT));
strUsage +=
HelpMessageOpt("-limitancestorsize=",
strprintf("Do not accept transactions whose size "
"with all in-mempool ancestors exceeds "
" kilobytes (default: %u)",
DEFAULT_ANCESTOR_SIZE_LIMIT));
strUsage += HelpMessageOpt(
"-limitdescendantcount=",
strprintf("Do not accept transactions if any ancestor would have "
" or more in-mempool descendants (default: %u)",
DEFAULT_DESCENDANT_LIMIT));
strUsage += HelpMessageOpt(
"-limitdescendantsize=",
strprintf("Do not accept transactions if any ancestor would have "
"more than 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)");
}
strUsage += HelpMessageOpt(
"-debug=",
strprintf(_("Output debugging information (default: %u, supplying "
" is optional)"),
0) +
". " + _("If is not supplied or if = 1, "
"output all debugging information.") +
_(" can be:") + " " + ListLogCategories() + ".");
strUsage += HelpMessageOpt(
"-debugexclude=",
strprintf(_("Exclude debugging information for a category. Can be used "
"in conjunction with -debug=1 to output debug logs for all "
"categories except one or more specified categories.")));
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: %d)"),
DEFAULT_LOGIPS));
strUsage += HelpMessageOpt(
"-logtimestamps",
strprintf(_("Prepend debug output with timestamp (default: %d)"),
DEFAULT_LOGTIMESTAMPS));
if (showDebug) {
strUsage += HelpMessageOpt(
"-logtimemicros",
strprintf(
"Add microsecond precision to debug timestamps (default: %d)",
DEFAULT_LOGTIMEMICROS));
strUsage += HelpMessageOpt(
"-mocktime=",
"Replace actual time with seconds since epoch (default: 0)");
strUsage += HelpMessageOpt(
"-limitfreerelay=",
strprintf("Continuously rate-limit free transactions to *1000 "
"bytes per minute (default: %u)",
DEFAULT_LIMITFREERELAY));
strUsage +=
HelpMessageOpt("-relaypriority",
strprintf("Require high priority for relaying free "
"or low-fee transactions (default: %d)",
DEFAULT_RELAYPRIORITY));
strUsage += HelpMessageOpt(
"-maxsigcachesize=",
strprintf("Limit size of signature cache to MiB (default: %u)",
DEFAULT_MAX_SIG_CACHE_SIZE));
strUsage += HelpMessageOpt(
"-maxscriptcachesize=",
strprintf("Limit size of script cache to MiB (default: %u)",
DEFAULT_MAX_SCRIPT_CACHE_SIZE));
strUsage += HelpMessageOpt(
"-maxtipage=",
strprintf("Maximum tip age in seconds to consider node in initial "
"block download (default: %u)",
DEFAULT_MAX_TIP_AGE));
}
strUsage += HelpMessageOpt(
"-excessutxocharge=",
strprintf(_("Fees (in %s/kB) to charge per utxo created for"
"relaying, and mining (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_UTXO_FEE)));
strUsage += HelpMessageOpt(
"-minrelaytxfee=",
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=",
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: %d)",
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; ",
defaultChainParams->RequireStandard()));
strUsage +=
HelpMessageOpt("-excessiveblocksize=",
strprintf(_("Do not accept blocks larger than this "
"limit, in bytes (default: %d)"),
DEFAULT_MAX_BLOCK_SIZE));
strUsage += HelpMessageOpt(
"-incrementalrelayfee=",
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=",
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: %d)"),
DEFAULT_ACCEPT_DATACARRIER));
strUsage += HelpMessageOpt(
"-datacarriersize",
strprintf(
_("Maximum size of data in data carrier transactions we relay and "
"mine (pre-fork default: %u, post-fork default: %u)"),
MAX_OP_RETURN_RELAY, MAX_OP_RETURN_RELAY_LARGE));
strUsage += HelpMessageGroup(_("Block creation options:"));
strUsage += HelpMessageOpt(
"-blockmaxsize=",
strprintf(_("Set maximum block size in bytes (default: %d)"),
DEFAULT_MAX_GENERATED_BLOCK_SIZE));
strUsage += HelpMessageOpt(
"-blockprioritypercentage=",
strprintf(_("Set maximum percentage of a block reserved to "
"high-priority/low-fee transactions (default: %d)"),
DEFAULT_BLOCK_PRIORITY_PERCENTAGE));
strUsage += HelpMessageOpt(
"-blockmintxfee=",
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=",
"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: %d)"),
DEFAULT_REST_ENABLE));
strUsage += HelpMessageOpt(
"-rpcbind=",
_("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=",
_("Location of the auth cookie (default: data dir)"));
strUsage += HelpMessageOpt("-rpcuser=",
_("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcpassword=",
_("Password for JSON-RPC connections"));
strUsage += HelpMessageOpt(
"-rpcauth=",
_("Username and hashed password for JSON-RPC connections. The field "
" comes in the format: :$. A canonical "
"python script is included in share/rpcuser. The client then "
"connects normally using the "
"rpcuser=/rpcpassword= pair of arguments. This "
"option can be specified multiple times"));
strUsage += HelpMessageOpt(
"-rpcport=",
strprintf(_("Listen for JSON-RPC connections on (default: %u or "
"testnet: %u)"),
defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
strUsage += HelpMessageOpt(
"-rpcallowip=",
_("Allow JSON-RPC connections from specified source. Valid for "
"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=",
strprintf(
_("Set the number of threads to service RPC calls (default: %d)"),
DEFAULT_HTTP_THREADS));
+ strUsage += HelpMessageOpt(
+ "-rpccorsdomain=value",
+ "Domain from which to accept cross origin requests (browser enforced)");
if (showDebug) {
strUsage += HelpMessageOpt(
"-rpcworkqueue=", strprintf("Set the depth of the work queue to "
"service RPC calls (default: %d)",
DEFAULT_HTTP_WORKQUEUE));
strUsage += HelpMessageOpt(
"-rpcservertimeout=",
strprintf("Timeout during HTTP requests (default: %d)",
DEFAULT_HTTP_SERVER_TIMEOUT));
}
return strUsage;
}
std::string LicenseInfo() {
const std::string URL_SOURCE_CODE =
"";
const std::string URL_WEBSITE = "";
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", "") +
"\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."),
"") +
"\n";
}
static void BlockNotifyCallback(bool initialSync,
const CBlockIndex *pBlockIndex) {
if (initialSync || !pBlockIndex) return;
std::string strCmd = gArgs.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 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 mapBlockFiles;
// Glob all blk?????.dat and rev?????.dat files from the blocks directory.
// Remove the rev files immediately and insert the blk file paths into an
// ordered map keyed by block file index.
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for "
"-reindex with -prune\n");
fs::path blocksdir = GetDataDir() / "blocks";
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator();
it++) {
if (is_regular_file(*it) &&
it->path().filename().string().length() == 12 &&
it->path().filename().string().substr(8, 4) == ".dat") {
if (it->path().filename().string().substr(0, 3) == "blk")
mapBlockFiles[it->path().filename().string().substr(3, 5)] =
it->path();
else if (it->path().filename().string().substr(0, 3) == "rev")
remove(it->path());
}
}
// Remove all block files that aren't part of a contiguous set starting at
// zero by walking the ordered map (keys are block file indices) by keeping
// a separate counter. Once we hit a gap (or if 0 doesn't exist) start
// removing block files.
int nContigCounter = 0;
for (const std::pair &item : mapBlockFiles) {
if (atoi(item.first) == nContigCounter) {
nContigCounter++;
continue;
}
remove(item.second);
}
}
void ThreadImport(const Config &config, std::vector vImportFiles) {
RenameThread("bitcoin-loadblk");
{
CImportingNow imp;
// -reindex
if (fReindex) {
int nFile = 0;
while (true) {
CDiskBlockPos pos(nFile, 0);
if (!fs::exists(GetBlockPosFilename(pos, "blk"))) {
// No block files left to reindex
break;
}
FILE *file = OpenBlockFile(pos, true);
if (!file) {
// This error is logged in OpenBlockFile
break;
}
LogPrintf("Reindexing block file blk%05u.dat...\n",
(unsigned int)nFile);
LoadExternalBlockFile(config, file, &pos);
nFile++;
}
pblocktree->WriteReindexing(false);
fReindex = false;
LogPrintf("Reindexing finished\n");
// To avoid ending up in a situation without genesis block, re-try
// initializing (no-op if reindexing worked):
InitBlockIndex(config);
}
// hardcoded $DATADIR/bootstrap.dat
fs::path pathBootstrap = GetDataDir() / "bootstrap.dat";
if (fs::exists(pathBootstrap)) {
FILE *file = fsbridge::fopen(pathBootstrap, "rb");
if (file) {
fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old";
LogPrintf("Importing bootstrap.dat...\n");
LoadExternalBlockFile(config, file);
RenameOver(pathBootstrap, pathBootstrapOld);
} else {
LogPrintf("Warning: Could not open bootstrap file %s\n",
pathBootstrap.string());
}
}
// -loadblock=
for (const fs::path &path : vImportFiles) {
FILE *file = fsbridge::fopen(path, "rb");
if (file) {
LogPrintf("Importing blocks file %s...\n", path.string());
LoadExternalBlockFile(config, file);
} else {
LogPrintf("Warning: Could not open blocks file %s\n",
path.string());
}
}
// scan for better chains in the block chain database, that are not yet
// connected in the active best chain
CValidationState state;
if (!ActivateBestChain(config, state)) {
LogPrintf("Failed to connect best block");
StartShutdown();
}
if (gArgs.GetBoolArg("-stopafterblockimport",
DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogPrintf("Stopping after block import\n");
StartShutdown();
}
} // End scope of CImportingNow
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
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;
}
if (!Random_SanityCheck()) {
InitError("OS cryptographic RNG sanity check failure. Aborting.");
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 (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE) && !StartREST())
return false;
if (!StartHTTPServer()) return false;
return true;
}
// Parameter interaction based on rules
void InitParameterInteraction() {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified.
if (gArgs.IsArgSet("-bind")) {
if (gArgs.SoftSetBoolArg("-listen", true))
LogPrintf(
"%s: parameter interaction: -bind set -> setting -listen=1\n",
__func__);
}
if (gArgs.IsArgSet("-whitebind")) {
if (gArgs.SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -whitebind set -> setting "
"-listen=1\n",
__func__);
}
if (gArgs.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen
// by default.
if (gArgs.SoftSetBoolArg("-dnsseed", false))
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-dnsseed=0\n",
__func__);
if (gArgs.SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -connect set -> setting "
"-listen=0\n",
__func__);
}
if (gArgs.IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy
// server is specified.
if (gArgs.SoftSetBoolArg("-listen", false))
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -listen=0\n",
__func__);
// to protect privacy, do not use UPNP when a proxy is set. The user may
// still specify -listen=1 to listen locally, so don't rely on this
// happening through -listen below.
if (gArgs.SoftSetBoolArg("-upnp", false))
LogPrintf(
"%s: parameter interaction: -proxy set -> setting -upnp=0\n",
__func__);
// to protect privacy, do not discover addresses by default
if (gArgs.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting "
"-discover=0\n",
__func__);
}
if (!gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening
// (pointless)
if (gArgs.SoftSetBoolArg("-upnp", false))
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -upnp=0\n",
__func__);
if (gArgs.SoftSetBoolArg("-discover", false))
LogPrintf(
"%s: parameter interaction: -listen=0 -> setting -discover=0\n",
__func__);
if (gArgs.SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting "
"-listenonion=0\n",
__func__);
}
if (gArgs.IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
if (gArgs.SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -externalip set -> setting "
"-discover=0\n",
__func__);
}
// disable whitelistrelay in blocksonly mode
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (gArgs.SoftSetBoolArg("-whitelistrelay", false))
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting "
"-whitelistrelay=0\n",
__func__);
}
// Forcing relay from whitelisted hosts implies we will accept relays from
// them in the first place.
if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (gArgs.SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> "
"setting -whitelistrelay=1\n",
__func__);
}
}
static std::string ResolveErrMsg(const char *const optname,
const std::string &strBind) {
return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind);
}
void InitLogging() {
BCLog::Logger &logger = GetLogger();
logger.fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false);
logger.fLogTimestamps =
gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
logger.fLogTimeMicros =
gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS);
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
LogPrintf("%s version %s\n", CLIENT_NAME, FormatFullVersion());
}
namespace { // Variables internal to initialization process only
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 (!gArgs.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 = config.GetChainParams();
// Step 2: parameter interactions
// also see: InitParameterInteraction()
// if using block pruning, then disallow txindex
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
}
// if space reserved for high priority transactions is misconfigured
// stop program execution and warn the user with a proper error message
const int64_t blkprio = gArgs.GetArg("-blockprioritypercentage",
DEFAULT_BLOCK_PRIORITY_PERCENTAGE);
if (!config.SetBlockPriorityPercentage(blkprio)) {
return InitError(_("Block priority percentage has to belong to the "
"[0..100] interval."));
}
// Make sure enough file descriptors are available
int nBind = std::max(
(gArgs.IsArgSet("-bind") ? gArgs.GetArgs("-bind").size() : 0) +
(gArgs.IsArgSet("-whitebind") ? gArgs.GetArgs("-whitebind").size()
: 0),
size_t(1));
nUserMaxConnections =
gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
// Trim requested connection counts, to fit into system limitations
nMaxConnections =
std::max(std::min(nMaxConnections,
(int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS -
MAX_ADDNODE_CONNECTIONS)),
0);
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS +
MAX_ADDNODE_CONNECTIONS);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
nMaxConnections =
std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS,
nMaxConnections);
if (nMaxConnections < nUserMaxConnections) {
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, "
"because of system limitations."),
nUserMaxConnections, nMaxConnections));
}
// Step 3: parameter-to-internal-flags
if (gArgs.IsArgSet("-debug")) {
// Special-case: if -debug=0/-nodebug is set, turn off debugging
// messages
const std::vector &categories = gArgs.GetArgs("-debug");
if (find(categories.begin(), categories.end(), std::string("0")) ==
categories.end()) {
for (const auto &cat : categories) {
uint32_t flag = 0;
if (!GetLogCategory(&flag, &cat)) {
InitWarning(
strprintf(_("Unsupported logging category %s=%s."),
"-debug", cat));
}
logCategories |= flag;
}
}
}
// Now remove the logging categories which were explicitly excluded
if (gArgs.IsArgSet("-debugexclude")) {
for (const std::string &cat : gArgs.GetArgs("-debugexclude")) {
uint32_t flag;
if (!GetLogCategory(&flag, &cat)) {
InitWarning(strprintf(_("Unsupported logging category %s=%s."),
"-debugexclude", cat));
}
logCategories &= ~flag;
}
}
// Check for -debugnet
if (gArgs.GetBoolArg("-debugnet", false))
InitWarning(
_("Unsupported argument -debugnet ignored, use -debug=net."));
// Check for -socks - as this is a privacy risk to continue, exit here
if (gArgs.IsArgSet("-socks"))
return InitError(
_("Unsupported argument -socks found. Setting SOCKS version isn't "
"possible anymore, only SOCKS5 proxies are supported."));
// Check for -tor - as this is a privacy risk to continue, exit here
if (gArgs.GetBoolArg("-tor", false))
return InitError(_("Unsupported argument -tor found, use -onion."));
if (gArgs.GetBoolArg("-benchmark", false))
InitWarning(
_("Unsupported argument -benchmark ignored, use -debug=bench."));
if (gArgs.GetBoolArg("-whitelistalwaysrelay", false))
InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use "
"-whitelistrelay and/or -whitelistforcerelay."));
if (gArgs.IsArgSet("-blockminsize"))
InitWarning("Unsupported argument -blockminsize ignored.");
// Checkmempool and checkblockindex default to true in regtest mode
int ratio = std::min(
std::max(
gArgs.GetArg("-checkmempool",
chainparams.DefaultConsistencyChecks() ? 1 : 0),
0),
1000000);
if (ratio != 0) {
mempool.setSanityCheck(1.0 / ratio);
}
fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex",
chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled =
gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
hashAssumeValid = uint256S(
gArgs.GetArg("-assumevalid",
chainparams.GetConsensus().defaultAssumeValid.GetHex()));
if (!hashAssumeValid.IsNull())
LogPrintf("Assuming ancestors of block %s have valid signatures.\n",
hashAssumeValid.GetHex());
else
LogPrintf("Validating signatures for all blocks.\n");
if (gArgs.IsArgSet("-minimumchainwork")) {
const std::string minChainWorkStr =
gArgs.GetArg("-minimumchainwork", "");
if (!IsHexNumber(minChainWorkStr)) {
return InitError(strprintf(
"Invalid non-hex (%s) minimum chain work value specified",
minChainWorkStr));
}
nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr));
} else {
nMinimumChainWork =
UintToArith256(chainparams.GetConsensus().nMinimumChainWork);
}
LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex());
if (nMinimumChainWork <
UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
LogPrintf("Warning: nMinimumChainWork set below default value of %s\n",
chainparams.GetConsensus().nMinimumChainWork.GetHex());
}
// mempool limits
int64_t nMempoolSizeMax =
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin =
gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) *
1000 * 40;
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
return InitError(strprintf(_("-maxmempool must be at least %d MB"),
std::ceil(nMempoolSizeMin / 1000000.0)));
// 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 (gArgs.IsArgSet("-incrementalrelayfee")) {
Amount n(0);
if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n))
return InitError(
AmountErrMsg("incrementalrelayfee",
gArgs.GetArg("-incrementalrelayfee", "")));
incrementalRelayFee = CFeeRate(n);
}
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
nScriptCheckThreads = gArgs.GetArg("-par", DEFAULT_SCRIPTCHECK_THREADS);
if (nScriptCheckThreads <= 0) nScriptCheckThreads += GetNumCores();
if (nScriptCheckThreads <= 1)
nScriptCheckThreads = 0;
else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;
// Configure excessive block size.
const uint64_t nProposedExcessiveBlockSize =
gArgs.GetArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE);
if (!config.SetMaxBlockSize(nProposedExcessiveBlockSize)) {
return InitError(
_("Excessive block size must be > 1,000,000 bytes (1MB)"));
}
// Check blockmaxsize does not exceed maximum accepted block size.
const uint64_t nProposedMaxGeneratedBlockSize =
gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
if (nProposedMaxGeneratedBlockSize > config.GetMaxBlockSize()) {
auto msg = _("Max generated block size (blockmaxsize) cannot exceed "
"the excessive block size (excessiveblocksize)");
return InitError(msg);
}
// block pruning; get the amount of disk space (in MiB) to allot for block &
// undo files
int64_t nPruneArg = gArgs.GetArg("-prune", 0);
if (nPruneArg < 0) {
return InitError(
_("Prune cannot be configured with a negative value."));
}
nPruneTarget = (uint64_t)nPruneArg * 1024 * 1024;
if (nPruneArg == 1) { // manual pruning: -prune=1
LogPrintf("Block pruning enabled. Use RPC call "
"pruneblockchain(height) to manually prune block and undo "
"files.\n");
nPruneTarget = std::numeric_limits::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 = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
// Obtain the amount to charge excess UTXO
if (gArgs.IsArgSet("-excessutxocharge")) {
Amount n(0);
auto parsed = ParseMoney(gArgs.GetArg("-excessutxocharge", ""), n);
if (!parsed || Amount(0) > n)
return InitError(AmountErrMsg(
"excessutxocharge", gArgs.GetArg("-excessutxocharge", "")));
config.SetExcessUTXOCharge(n);
} else {
config.SetExcessUTXOCharge(DEFAULT_UTXO_FEE);
}
// Fee-per-kilobyte amount considered the same as "free". If you are mining,
// be careful setting this: if you set it to zero then a transaction spammer
// can cheaply fill blocks using 1-satoshi-fee transactions. It should be
// set above the real cost to you of processing a transaction.
if (gArgs.IsArgSet("-minrelaytxfee")) {
Amount n(0);
auto parsed = ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n);
if (!parsed || Amount(0) == n)
return InitError(AmountErrMsg("minrelaytxfee",
gArgs.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 (gArgs.IsArgSet("-blockmintxfee")) {
Amount n(0);
if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n))
return InitError(AmountErrMsg("blockmintxfee",
gArgs.GetArg("-blockmintxfee", "")));
}
// Feerate used to define dust. Shouldn't be changed lightly as old
// implementations may inadvertently create non-standard transactions.
if (gArgs.IsArgSet("-dustrelayfee")) {
Amount n(0);
auto parsed = ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n);
if (!parsed || Amount(0) == n)
return InitError(AmountErrMsg("dustrelayfee",
gArgs.GetArg("-dustrelayfee", "")));
dustRelayFee = CFeeRate(n);
}
fRequireStandard =
!gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
if (chainparams.RequireStandard() && !fRequireStandard)
return InitError(
strprintf("acceptnonstdtxn is not currently supported for %s chain",
chainparams.NetworkIDString()));
nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp);
#ifdef ENABLE_WALLET
if (!CWallet::ParameterInteraction()) return false;
#endif
fIsBareMultisigStd =
gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
fAcceptDatacarrier =
gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER);
// Option to startup with mocktime set (used for regression testing):
SetMockTime(gArgs.GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
// Signal Bitcoin Cash support.
// TODO: remove some time after the hardfork when no longer needed
// to differentiate the network nodes.
nLocalServices = ServiceFlags(nLocalServices | NODE_BITCOIN_CASH);
nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
if (gArgs.IsArgSet("-bip9params")) {
// Allow overriding BIP9 parameters for testing
if (!chainparams.MineBlocksOnDemand()) {
return InitError(
"BIP9 parameters may only be overridden on regtest.");
}
for (const std::string &strDeployment : gArgs.GetArgs("-bip9params")) {
std::vector vDeploymentParams;
boost::split(vDeploymentParams, strDeployment,
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) {
UpdateBIP9Parameters(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.
fs::path pathLockFile = GetDataDir() / ".lock";
// empty lock file; created if it doesn't exist.
FILE *file = fsbridge::fopen(pathLockFile, "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
std::string sha256_algo = SHA256AutoDetect();
LogPrintf("Using the '%s' SHA256 implementation\n", sha256_algo);
RandomInit();
ECC_Start();
globalVerifyHandle.reset(new ECCVerifyHandle());
// Sanity check
if (!InitSanityCheck()) {
return InitError(strprintf(
_("Initialization sanity check failed. %s is shutting down."),
_(PACKAGE_NAME)));
}
// Probe the data directory lock to give an early error message, if possible
return LockDataDirectory(true);
}
bool AppInitMain(Config &config, boost::thread_group &threadGroup,
CScheduler &scheduler) {
const CChainParams &chainparams = config.GetChainParams();
// 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
BCLog::Logger &logger = GetLogger();
if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories != BCLog::NONE)) {
// 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 (logger.fPrintToDebugLog) {
OpenDebugLog();
}
if (!logger.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(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string());
LogPrintf("Using at most %i automatic connections (%i file descriptors "
"available)\n",
nMaxConnections, nFD);
InitSignatureCache();
InitScriptExecutionCache();
LogPrintf("Using %u threads for script verification\n",
nScriptCheckThreads);
if (nScriptCheckThreads) {
for (int i = 0; i < nScriptCheckThreads - 1; i++) {
threadGroup.create_thread(&ThreadScriptCheck);
}
}
// Start the lightweight task scheduler thread
CScheduler::Function serviceLoop =
boost::bind(&CScheduler::serviceQueue, &scheduler);
threadGroup.create_thread(boost::bind(&TraceThread,
"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 (gArgs.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(chainparams)) {
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(
new CConnman(config, GetRand(std::numeric_limits::max()),
GetRand(std::numeric_limits::max())));
CConnman &connman = *g_connman;
peerLogic.reset(new PeerLogicValidation(&connman));
RegisterValidationInterface(peerLogic.get());
RegisterNodeSignals(GetNodeSignals());
if (gArgs.IsArgSet("-onlynet")) {
std::set nets;
for (const std::string &snet : gArgs.GetArgs("-onlynet")) {
enum Network net = ParseNetwork(snet);
if (net == NET_UNROUTABLE)
return InitError(strprintf(
_("Unknown network specified in -onlynet: '%s'"), snet));
nets.insert(net);
}
for (int n = 0; n < NET_MAX; n++) {
enum Network net = (enum Network)n;
if (!nets.count(net)) SetLimited(net);
}
}
if (gArgs.IsArgSet("-whitelist")) {
for (const std::string &net : gArgs.GetArgs("-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 =
gArgs.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set
// a proxy, this is the default
std::string proxyArg = gArgs.GetArg("-proxy", "");
SetLimited(NET_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 = gArgs.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 = gArgs.GetBoolArg("-listen", DEFAULT_LISTEN);
fDiscover = gArgs.GetBoolArg("-discover", true);
fNameLookup = gArgs.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);
fRelayTxes = !gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY);
if (fListen) {
bool fBound = false;
if (gArgs.IsArgSet("-bind")) {
for (const std::string &strBind : gArgs.GetArgs("-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 (gArgs.IsArgSet("-whitebind")) {
for (const std::string &strBind : gArgs.GetArgs("-whitebind")) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
return InitError(ResolveErrMsg("whitebind", strBind));
}
if (addrBind.GetPort() == 0) {
return InitError(strprintf(
_("Need to specify a port with -whitebind: '%s'"),
strBind));
}
fBound |= Bind(connman, addrBind,
(BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
}
}
if (!gArgs.IsArgSet("-bind") && !gArgs.IsArgSet("-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 (gArgs.IsArgSet("-externalip")) {
for (const std::string &strAddr : gArgs.GetArgs("-externalip")) {
CService addrLocal;
if (Lookup(strAddr.c_str(), addrLocal, GetListenPort(),
fNameLookup) &&
addrLocal.IsValid()) {
AddLocal(addrLocal, LOCAL_MANUAL);
} else {
return InitError(ResolveErrMsg("externalip", strAddr));
}
}
}
if (gArgs.IsArgSet("-seednode")) {
for (const std::string &strDest : gArgs.GetArgs("-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 (gArgs.IsArgSet("-maxuploadtarget")) {
nMaxOutboundLimit =
gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET) * 1024 *
1024;
}
// Step 7: load block chain
fReindex = gArgs.GetBoolArg("-reindex", false);
bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false);
// cache size calculations
int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20);
// total cache cannot be less than nMinDbCache
nTotalCache = std::max(nTotalCache, nMinDbCache << 20);
// total cache cannot be greater than nMaxDbcache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20);
int64_t nBlockTreeDBCache = nTotalCache / 8;
nBlockTreeDBCache = std::min(nBlockTreeDBCache,
(gArgs.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 =
gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
LogPrintf("Cache configuration:\n");
LogPrintf("* Using %.1fMiB for block index database\n",
nBlockTreeDBCache * (1.0 / 1024 / 1024));
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 && !fRequestShutdown) {
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);
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();
}
} else if (!pcoinsdbview->Upgrade()) {
strLoadError = _("Error upgrading chainstate database");
break;
}
if (fRequestShutdown) break;
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 != gArgs.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 (!ReplayBlocks(config, pcoinsdbview)) {
strLoadError =
_("Unable to replay blocks. You will need to rebuild "
"the database using -reindex-chainstate.");
break;
}
pcoinsTip = new CCoinsViewCache(pcoinscatcher);
LoadChainTip(chainparams);
if (!fReindex && chainActive.Tip() != nullptr) {
uiInterface.InitMessage(_("Rewinding blocks..."));
if (!RewindBlockIndex(config)) {
strLoadError = _("Unable to rewind the database to a "
"pre-fork state. You will need to "
"redownload the blockchain");
break;
}
}
uiInterface.InitMessage(_("Verifying blocks..."));
if (fHavePruned &&
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) >
MIN_BLOCKS_TO_KEEP) {
LogPrintf("Prune: pruned datadir may not have more than %d "
"blocks; only checking available blocks",
MIN_BLOCKS_TO_KEEP);
}
{
LOCK(cs_main);
CBlockIndex *tip = chainActive.Tip();
RPCNotifyBlockChange(true, tip);
if (tip &&
tip->nTime >
GetAdjustedTime() + MAX_FUTURE_BLOCK_TIME) {
strLoadError =
_("The block database contains a block which "
"appears to be from the future. "
"This may be due to your computer's date and "
"time being set incorrectly. "
"Only rebuild the block database if you are sure "
"that your computer's date and time are correct");
break;
}
}
if (!CVerifyDB().VerifyDB(
config, pcoinsdbview,
gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL),
gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) {
strLoadError = _("Corrupted block database detected");
break;
}
} catch (const std::exception &e) {
LogPrintf("%s\n", e.what());
strLoadError = _("Error opening block database");
break;
}
fLoaded = true;
} while (false);
if (!fLoaded && !fRequestShutdown) {
// first suggest a reindex
if (!fReset) {
bool fRet = uiInterface.ThreadSafeQuestion(
strLoadError + ".\n\n" +
_("Do you want to rebuild the block database now?"),
strLoadError + ".\nPlease restart with -reindex or "
"-reindex-chainstate to recover.",
"",
CClientUIInterface::MSG_ERROR |
CClientUIInterface::BTN_ABORT);
if (fRet) {
fReindex = true;
fRequestShutdown = false;
} else {
LogPrintf("Aborted block database rebuild. Exiting.\n");
return false;
}
} else {
return InitError(strLoadError);
}
}
}
// As LoadBlockIndex can take several minutes, it's possible the user
// requested to kill the GUI during the last operation. If so, exit.
// As the program has not fully started yet, Shutdown() is possibly
// overkill.
if (fRequestShutdown) {
LogPrintf("Shutdown requested. Exiting.\n");
return false;
}
LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart);
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK,
CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
if (!est_filein.IsNull()) mempool.ReadFeeEstimates(est_filein);
fFeeEstimatesInitialized = true;
// Encoded addresses using cashaddr instead of base58
// Activates by default on Jan, 14
config.SetCashAddrEncoding(
gArgs.GetBoolArg("-usecashaddr", GetAdjustedTime() > 1515900000));
// Step 8: load wallet
#ifdef ENABLE_WALLET
if (!CWallet::InitLoadWallet(chainparams)) 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 (gArgs.IsArgSet("-blocknotify")) {
uiInterface.NotifyBlockTip.connect(BlockNotifyCallback);
}
std::vector vImportFiles;
if (gArgs.IsArgSet("-loadblock")) {
for (const std::string &strFile : gArgs.GetArgs("-loadblock")) {
vImportFiles.push_back(strFile);
}
}
threadGroup.create_thread(
boost::bind(&ThreadImport, std::ref(config), vImportFiles));
// Wait for genesis block to be processed
{
boost::unique_lock 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 (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
StartTorControl(threadGroup, scheduler);
}
Discover(threadGroup);
// Map ports with UPnP
MapPort(gArgs.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 * gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER);
connOptions.nReceiveFloodSize =
1000 * gArgs.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
for (CWalletRef pwallet : vpwallets) {
pwallet->postInitProcess(scheduler);
}
#endif
return !fRequestShutdown;
}
diff --git a/test/functional/httpbasics.py b/test/functional/httpbasics.py
index 3105fd70b8..7e63c8749d 100755
--- a/test/functional/httpbasics.py
+++ b/test/functional/httpbasics.py
@@ -1,125 +1,175 @@
#!/usr/bin/env python3
# Copyright (c) 2014-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.
#
# Test rpc http basics
#
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
import http.client
import urllib.parse
class HTTPBasicsTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 3
def setup_network(self):
+ self.extra_args = [["-rpccorsdomain=null"], [], []]
self.setup_nodes()
def run_test(self):
#
# lowlevel check for http persistent connection #
#
url = urllib.parse.urlparse(self.nodes[0].url)
authpair = url.username + ':' + url.password
headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
conn = http.client.HTTPConnection(url.hostname, url.port)
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
assert(conn.sock != None)
# according to http/1.1 connection must still be open!
# send 2nd request without closing connection
conn.request('POST', '/', '{"method": "getchaintips"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
# must also response with a correct json-rpc message
assert(conn.sock != None)
# according to http/1.1 connection must still be open!
conn.close()
# same should be if we add keep-alive because this should be the std.
# behaviour
headers = {"Authorization": "Basic " +
str_to_b64str(authpair), "Connection": "keep-alive"}
conn = http.client.HTTPConnection(url.hostname, url.port)
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
assert(conn.sock != None)
# according to http/1.1 connection must still be open!
# send 2nd request without closing connection
conn.request('POST', '/', '{"method": "getchaintips"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
# must also response with a correct json-rpc message
assert(conn.sock != None)
# according to http/1.1 connection must still be open!
conn.close()
# now do the same with "Connection: close"
headers = {"Authorization": "Basic " +
str_to_b64str(authpair), "Connection": "close"}
conn = http.client.HTTPConnection(url.hostname, url.port)
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
assert(conn.sock == None)
# now the connection must be closed after the response
# node1 (2nd node) is running with disabled keep-alive option
urlNode1 = urllib.parse.urlparse(self.nodes[1].url)
authpair = urlNode1.username + ':' + urlNode1.password
headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
conn = http.client.HTTPConnection(urlNode1.hostname, urlNode1.port)
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
# node2 (third node) is running with standard keep-alive parameters
# which means keep-alive is on
urlNode2 = urllib.parse.urlparse(self.nodes[2].url)
authpair = urlNode2.username + ':' + urlNode2.password
headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
conn.connect()
conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
out1 = conn.getresponse().read()
assert(b'"error":null' in out1)
assert(conn.sock != None)
# connection must be closed because bitcoind should use
# keep-alive by default
# Check excessive request size
conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
conn.connect()
conn.request('GET', '/' + ('x' * 1000), '', headers)
out1 = conn.getresponse()
assert_equal(out1.status, http.client.NOT_FOUND)
conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
conn.connect()
conn.request('GET', '/' + ('x' * 10000), '', headers)
out1 = conn.getresponse()
assert_equal(out1.status, http.client.BAD_REQUEST)
+ # Check Standard CORS request
+ origin = "null"
+
+ conn = http.client.HTTPConnection(url.hostname, url.port)
+ conn.connect()
+ authpair = url.username + ':' + url.password
+ headers = {"Authorization": "Basic " + str_to_b64str(authpair),
+ "Origin": origin}
+ conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
+ out1 = conn.getresponse()
+ assert_equal(out1.status, http.client.OK)
+ assert_equal(out1.headers["Access-Control-Allow-Origin"], origin)
+ assert_equal(out1.headers["Access-Control-Allow-Credentials"], "true")
+ assert_equal(out1.headers["Access-Control-Expose-Headers"],
+ "WWW-Authenticate")
+ assert(b'"error":null' in out1.read())
+
+ # Check Pre-flight CORS request
+ corsheaders = {"Origin": origin,
+ "Access-Control-Request-Method": "POST"}
+ conn.request('OPTIONS', '/', None, corsheaders)
+ out1 = conn.getresponse()
+ assert_equal(out1.status, http.client.OK)
+ assert_equal(out1.headers["Access-Control-Allow-Origin"], origin)
+ assert_equal(out1.headers["Access-Control-Allow-Credentials"], "true")
+ assert_equal(out1.headers["Access-Control-Allow-Methods"], "POST")
+ assert_equal(out1.headers["Access-Control-Allow-Headers"],
+ "authorization,content-type")
+ assert_equal(b'', out1.read())
+
+ # Check Standard CORS request to node without CORS, expected failure
+ conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
+ conn.connect()
+ authpair = url.username + ':' + url.password
+ headers = {"Authorization": "Basic " + str_to_b64str(authpair),
+ "Origin": origin}
+ conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
+ out1 = conn.getresponse()
+ assert_equal(out1.status, http.client.UNAUTHORIZED)
+ assert_equal(b'', out1.read())
+
+ # Check Pre-flight CORS request to node without CORS, expected failure
+ corsheaders = {"Origin": origin,
+ "Access-Control-Request-Method": "POST"}
+ conn.request('OPTIONS', '/', None, corsheaders)
+ out1 = conn.getresponse()
+ assert_equal(out1.status, http.client.METHOD_NOT_ALLOWED)
+ assert_equal(b'JSONRPC server handles only POST requests', out1.read())
+
if __name__ == '__main__':
HTTPBasicsTest().main()