diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 9fa368a427..6be1713c1e 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -1,413 +1,413 @@ // 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\""; /** RPC auth failure delay to make brute-forcing expensive */ static const int64_t RPC_AUTH_BRUTE_FORCE_DELAY = 250; /** * 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; }; /* Stored RPC timer interface (for unregistration) */ -static HTTPRPCTimerInterface *httpRPCTimerInterface = 0; +static HTTPRPCTimerInterface *httpRPCTimerInterface = nullptr; 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); for (const std::string &strRPCAuth : gArgs.GetArgs("-rpcauth")) { // Search for multi-user login/pass "rpcauth" from config 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(Config &config, const std::string &strAuth, std::string &strAuthUsernameOut) { // Belt-and-suspenders measure if InitRPCAuthentication was not called. if (config.GetRPCUserAndPassword().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, config.GetRPCUserAndPassword())) { return true; } return multiUserAuthorized(strUserPass); } static bool checkCORS(Config &config, 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 != config.GetRPCCORSDomain()) { 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; } bool HTTPRPCRequestProcessor::ProcessHTTPRequest(HTTPRequest *req) { // First, check and/or set CORS headers if (checkCORS(config, 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(config, 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(RPC_AUTH_BRUTE_FORCE_DELAY); 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 = rpcServer.ExecuteCommand(config, jreq); // Send reply strReply = JSONRPCReply(result, NullUniValue, jreq.id); } else if (valRequest.isArray()) { // array of requests strReply = JSONRPCExecBatch(config, rpcServer, 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(Config &config) { if (gArgs.GetArg("-rpcpassword", "") == "") { LogPrintf("No rpcpassword set - using random cookie authentication\n"); std::string generatedUserAndPassword; if (!GenerateAuthCookie(&generatedUserAndPassword)) { // Same message as AbortNode. uiInterface.ThreadSafeMessageBox( _("Error: A fatal internal error occurred, see debug.log for " "details"), "", CClientUIInterface::MSG_ERROR); return false; } config.SetRPCUserAndPassword(generatedUserAndPassword); } 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"); config.SetRPCUserAndPassword(gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "")); } config.SetRPCCORSDomain(gArgs.GetArg("-rpccorsdomain", "")); return true; } bool StartHTTPRPC(Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor) { LogPrint(BCLog::RPC, "Starting HTTP RPC server\n"); if (!InitRPCAuthentication(config)) { return false; } const std::function &rpcFunction = std::bind(&HTTPRPCRequestProcessor::DelegateHTTPRequest, &httpRPCRequestProcessor, std::placeholders::_2); RegisterHTTPHandler("/", true, rpcFunction); #ifdef ENABLE_WALLET // ifdef can be removed once we switch to better endpoint support and API // versioning RegisterHTTPHandler("/wallet/", false, rpcFunction); #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; + httpRPCTimerInterface = nullptr; } } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index cf8f3dd9d5..412fadbd7d 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -1,669 +1,669 @@ // Copyright (c) 2015-2016 The Bitcoin Core developers // Copyright (c) 2018 The Bitcoin 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 */ CWaitableCriticalSection 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) { 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; { WAIT_LOCK(cs, lock); 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() { 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); } }; 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; +static struct event_base *eventBase = nullptr; //! HTTP server -struct evhttp *eventHTTP = 0; +struct evhttp *eventHTTP = nullptr; //! 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; +static WorkQueue *workQueue = nullptr; //! 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)); 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"; case HTTPRequest::POST: return "POST"; case HTTPRequest::HEAD: return "HEAD"; case HTTPRequest::PUT: return "PUT"; 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: Create a new event_base for Libevent use base = event_base_new(); if (!base) { LogPrintf("Couldn't create an event_base: exiting\n"); return false; } // XXX RAII: Create a new evhttp object to handle requests 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; + eventHTTP = nullptr; } if (eventBase) { event_base_free(eventBase); - eventBase = 0; + eventBase = nullptr; } 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); + ev->trigger(nullptr); replySent = true; // transferred back to main thread. - req = 0; + req = nullptr; } 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; case EVHTTP_REQ_POST: return POST; case EVHTTP_REQ_HEAD: return HEAD; case EVHTTP_REQ_PUT: return PUT; case EVHTTP_REQ_OPTIONS: return OPTIONS; default: return UNKNOWN; } } 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/support/lockedpool.h b/src/support/lockedpool.h index 8a1a89519d..56b099364a 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -1,249 +1,249 @@ // Copyright (c) 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_SUPPORT_LOCKEDPOOL_H #define BITCOIN_SUPPORT_LOCKEDPOOL_H #include #include #include #include #include /** * OS-dependent allocation and deallocation of locked/pinned memory pages. * Abstract base class. */ class LockedPageAllocator { public: virtual ~LockedPageAllocator() {} /** * Allocate and lock memory pages. * If len is not a multiple of the system page size, it is rounded up. * Returns 0 in case of allocation failure. * * If locking the memory pages could not be accomplished it will still * return the memory, however the lockingSuccess flag will be false. * lockingSuccess is undefined if the allocation fails. */ virtual void *AllocateLocked(size_t len, bool *lockingSuccess) = 0; /** * Unlock and free memory pages. * Clear the memory before unlocking. */ virtual void FreeLocked(void *addr, size_t len) = 0; /** * Get the total limit on the amount of memory that may be locked by this * process, in bytes. Return size_t max if there is no limit or the limit is * unknown. Return 0 if no memory can be locked at all. */ virtual size_t GetLimit() = 0; }; /** * An arena manages a contiguous region of memory by dividing it into chunks. */ class Arena { public: Arena(void *base, size_t size, size_t alignment); virtual ~Arena(); /** Memory statistics. */ struct Stats { size_t used; size_t free; size_t total; size_t chunks_used; size_t chunks_free; }; /** * Allocate size bytes from this arena. * Returns pointer on success, or 0 if memory is full or the application * tried to allocate 0 bytes. */ void *alloc(size_t size); /** * Free a previously allocated chunk of memory. * Freeing the zero pointer has no effect. * Raises std::runtime_error in case of error. */ void free(void *ptr); /** Get arena usage statistics */ Stats stats() const; #ifdef ARENA_DEBUG void walk() const; #endif /** * Return whether a pointer points inside this arena. * This returns base <= ptr < (base+size) so only use it for (inclusive) * chunk starting addresses. */ bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; } private: // non construction-copyable Arena(const Arena &other) = delete; // non copyable Arena &operator=(const Arena &) = delete; /** * Map of chunk address to chunk information. This class makes use of the * sorted order to merge previous and next chunks during deallocation. */ std::map chunks_free; std::map chunks_used; /** Base address of arena */ char *base; /** End address of arena */ char *end; /** Minimum chunk alignment */ size_t alignment; }; /** * Pool for locked memory chunks. * * To avoid sensitive key data from being swapped to disk, the memory in this * pool is locked/pinned. * * An arena manages a contiguous region of memory. The pool starts out with one * arena but can grow to multiple arenas if the need arises. * * Unlike a normal C heap, the administrative structures are separate from the * managed memory. This has been done as the sizes and bases of objects are not * in themselves sensitive information, as to conserve precious locked memory. * In some operating systems the amount of memory that can be locked is small. */ class LockedPool { public: /** * Size of one arena of locked memory. This is a compromise. * Do not set this too low, as managing many arenas will increase allocation * and deallocation overhead. Setting it too high allocates more locked * memory from the OS than strictly necessary. */ static const size_t ARENA_SIZE = 256 * 1024; /** * Chunk alignment. Another compromise. Setting this too high will waste * memory, setting it too low will facilitate fragmentation. */ static const size_t ARENA_ALIGN = 16; /** * Callback when allocation succeeds but locking fails. */ typedef bool (*LockingFailed_Callback)(); /** Memory statistics. */ struct Stats { size_t used; size_t free; size_t total; size_t locked; size_t chunks_used; size_t chunks_free; }; /** * Create a new LockedPool. This takes ownership of the MemoryPageLocker, * you can only instantiate this with LockedPool(std::move(...)). * * The second argument is an optional callback when locking a newly * allocated arena failed. If this callback is provided and returns false, * the allocation fails (hard fail), if it returns true the allocation * proceeds, but it could warn. */ LockedPool(std::unique_ptr allocator, - LockingFailed_Callback lf_cb_in = 0); + LockingFailed_Callback lf_cb_in = nullptr); ~LockedPool(); /** * Allocate size bytes from this arena. * Returns pointer on success, or 0 if memory is full or the application * tried to allocate 0 bytes. */ void *alloc(size_t size); /** * Free a previously allocated chunk of memory. * Freeing the zero pointer has no effect. * Raises std::runtime_error in case of error. */ void free(void *ptr); /** Get pool usage statistics */ Stats stats() const; private: // non construction-copyable LockedPool(const LockedPool &other) = delete; // non copyable LockedPool &operator=(const LockedPool &) = delete; std::unique_ptr allocator; /** Create an arena from locked pages */ class LockedPageArena : public Arena { public: LockedPageArena(LockedPageAllocator *alloc_in, void *base_in, size_t size, size_t align); ~LockedPageArena(); private: void *base; size_t size; LockedPageAllocator *allocator; }; bool new_arena(size_t size, size_t align); std::list arenas; LockingFailed_Callback lf_cb; size_t cumulative_bytes_locked; /** * Mutex protects access to this pool's data structures, including arenas. */ mutable std::mutex mutex; }; /** * Singleton class to keep track of locked (ie, non-swappable) memory, for use * in std::allocator templates. * * Some implementations of the STL allocate memory in some constructors (i.e., * see MSVC's vector implementation where it allocates 1 byte of memory in * the allocator). Due to the unpredictable order of static initializers, we * have to make sure the LockedPoolManager instance exists before any other * STL-based objects that use secure_allocator are created. So instead of having * LockedPoolManager also be static-initialized, it is created on demand. */ class LockedPoolManager : public LockedPool { public: /** Return the current instance, or create it once */ static LockedPoolManager &Instance() { std::call_once(LockedPoolManager::init_flag, LockedPoolManager::CreateInstance); return *LockedPoolManager::_instance; } private: LockedPoolManager(std::unique_ptr allocator); /** Create a new LockedPoolManager specialized to the OS */ static void CreateInstance(); /** Called when locking fails, warn the user here */ static bool LockingFailed(); static LockedPoolManager *_instance; static std::once_flag init_flag; }; #endif // BITCOIN_SUPPORT_LOCKEDPOOL_H diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index d8bc4413e9..9e429c2720 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -1,812 +1,812 @@ // Copyright (c) 2015-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "torcontrol.h" #include "crypto/hmac_sha256.h" #include "net.h" #include "netbase.h" #include "util.h" #include "utilstrencodings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** Default control port */ const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:9051"; /** Tor cookie size (from control-spec.txt) */ static const int TOR_COOKIE_SIZE = 32; /** Size of client/server nonce for SAFECOOKIE */ static const int TOR_NONCE_SIZE = 32; /** For computing serverHash in SAFECOOKIE */ static const std::string TOR_SAFE_SERVERKEY = "Tor safe cookie authentication server-to-controller hash"; /** For computing clientHash in SAFECOOKIE */ static const std::string TOR_SAFE_CLIENTKEY = "Tor safe cookie authentication controller-to-server hash"; /** Exponential backoff configuration - initial timeout in seconds */ static const float RECONNECT_TIMEOUT_START = 1.0; /** Exponential backoff configuration - growth factor */ static const float RECONNECT_TIMEOUT_EXP = 1.5; /** * Maximum length for lines received on TorControlConnection. * tor-control-spec.txt mentions that there is explicitly no limit defined to * line length, this is belt-and-suspenders sanity limit to prevent memory * exhaustion. */ static const int MAX_LINE_LENGTH = 100000; /****** Low-level TorControlConnection ********/ /** Reply from Tor, can be single or multi-line */ class TorControlReply { public: TorControlReply() { Clear(); } int code; std::vector lines; void Clear() { code = 0; lines.clear(); } }; /** * Low-level handling for Tor control connection. * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt */ class TorControlConnection { public: typedef std::function ConnectionCB; typedef std::function ReplyHandlerCB; /** Create a new TorControlConnection. */ TorControlConnection(struct event_base *base); ~TorControlConnection(); /** * Connect to a Tor control port. * target is address of the form host:port. * connected is the handler that is called when connection is successfully * established. * disconnected is a handler that is called when the connection is broken. * Return true on success. */ bool Connect(const std::string &target, const ConnectionCB &connected, const ConnectionCB &disconnected); /** * Disconnect from Tor control port. */ bool Disconnect(); /** * Send a command, register a handler for the reply. * A trailing CRLF is automatically added. * Return true on success. */ bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler); /** Response handlers for async replies */ boost::signals2::signal async_handler; private: /** Callback when ready for use */ std::function connected; /** Callback when connection lost */ std::function disconnected; /** Libevent event base */ struct event_base *base; /** Connection to control socket */ struct bufferevent *b_conn; /** Message being received */ TorControlReply message; /** Response handlers */ std::deque reply_handlers; /** Libevent handlers: internal */ static void readcb(struct bufferevent *bev, void *ctx); static void eventcb(struct bufferevent *bev, short what, void *ctx); }; TorControlConnection::TorControlConnection(struct event_base *_base) - : base(_base), b_conn(0) {} + : base(_base), b_conn(nullptr) {} TorControlConnection::~TorControlConnection() { if (b_conn) { bufferevent_free(b_conn); } } void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) { TorControlConnection *self = (TorControlConnection *)ctx; struct evbuffer *input = bufferevent_get_input(bev); size_t n_read_out = 0; char *line; assert(input); // If there is not a whole line to read, evbuffer_readln returns nullptr while ((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) != nullptr) { std::string s(line, n_read_out); free(line); // Short line if (s.size() < 4) { continue; } // (-|+| ) self->message.code = atoi(s.substr(0, 3)); self->message.lines.push_back(s.substr(4)); // '-','+' or ' ' char ch = s[3]; if (ch == ' ') { // Final line, dispatch reply and clean up if (self->message.code >= 600) { // Dispatch async notifications to async handler. // Synchronous and asynchronous messages are never interleaved self->async_handler(*self, self->message); } else { if (!self->reply_handlers.empty()) { // Invoke reply handler with message self->reply_handlers.front()(*self, self->message); self->reply_handlers.pop_front(); } else { LogPrint(BCLog::TOR, "tor: Received unexpected sync reply %i\n", self->message.code); } } self->message.Clear(); } } // Check for size of buffer - protect against memory exhaustion with very // long lines. Do this after evbuffer_readln to make sure all full lines // have been removed from the buffer. Everything left is an incomplete line. if (evbuffer_get_length(input) > MAX_LINE_LENGTH) { LogPrintf("tor: Disconnecting because MAX_LINE_LENGTH exceeded\n"); self->Disconnect(); } } void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ctx) { TorControlConnection *self = (TorControlConnection *)ctx; if (what & BEV_EVENT_CONNECTED) { LogPrint(BCLog::TOR, "tor: Successfully connected!\n"); self->connected(*self); } else if (what & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) { if (what & BEV_EVENT_ERROR) { LogPrint(BCLog::TOR, "tor: Error connecting to Tor control socket\n"); } else { LogPrint(BCLog::TOR, "tor: End of stream\n"); } self->Disconnect(); self->disconnected(*self); } } bool TorControlConnection::Connect(const std::string &target, const ConnectionCB &_connected, const ConnectionCB &_disconnected) { if (b_conn) { Disconnect(); } // Parse target address:port struct sockaddr_storage connect_to_addr; int connect_to_addrlen = sizeof(connect_to_addr); if (evutil_parse_sockaddr_port(target.c_str(), (struct sockaddr *)&connect_to_addr, &connect_to_addrlen) < 0) { LogPrintf("tor: Error parsing socket address %s\n", target); return false; } // Create a new socket, set up callbacks and enable notification bits b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); if (!b_conn) { return false; } bufferevent_setcb(b_conn, TorControlConnection::readcb, nullptr, TorControlConnection::eventcb, this); bufferevent_enable(b_conn, EV_READ | EV_WRITE); this->connected = _connected; this->disconnected = _disconnected; // Finally, connect to target if (bufferevent_socket_connect(b_conn, (struct sockaddr *)&connect_to_addr, connect_to_addrlen) < 0) { LogPrintf("tor: Error connecting to address %s\n", target); return false; } return true; } bool TorControlConnection::Disconnect() { if (b_conn) { bufferevent_free(b_conn); } - b_conn = 0; + b_conn = nullptr; return true; } bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB &reply_handler) { if (!b_conn) { return false; } struct evbuffer *buf = bufferevent_get_output(b_conn); if (!buf) { return false; } evbuffer_add(buf, cmd.data(), cmd.size()); evbuffer_add(buf, "\r\n", 2); reply_handlers.push_back(reply_handler); return true; } /****** General parsing utilities ********/ /* Split reply line in the form 'AUTH METHODS=...' into a type * 'AUTH' and arguments 'METHODS=...'. */ static std::pair SplitTorReplyLine(const std::string &s) { size_t ptr = 0; std::string type; while (ptr < s.size() && s[ptr] != ' ') { type.push_back(s[ptr]); ++ptr; } if (ptr < s.size()) { // skip ' ' ++ptr; } return make_pair(type, s.substr(ptr)); } /** * Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE * COOKIEFILE=".../control_auth_cookie"'. */ static std::map ParseTorReplyMapping(const std::string &s) { std::map mapping; size_t ptr = 0; while (ptr < s.size()) { std::string key, value; while (ptr < s.size() && s[ptr] != '=') { key.push_back(s[ptr]); ++ptr; } // unexpected end of line if (ptr == s.size()) { return std::map(); } // skip '=' ++ptr; // Quoted string if (ptr < s.size() && s[ptr] == '"') { // skip '=' ++ptr; bool escape_next = false; while (ptr < s.size() && (!escape_next && s[ptr] != '"')) { escape_next = (s[ptr] == '\\'); value.push_back(s[ptr]); ++ptr; } // unexpected end of line if (ptr == s.size()) { return std::map(); } // skip closing '"' ++ptr; /* TODO: unescape value - according to the spec this depends on the * context, some strings use C-LogPrintf style escape codes, some * don't. So may be better handled at the call site. */ } else { // Unquoted value. Note that values can contain '=' at will, just no // spaces while (ptr < s.size() && s[ptr] != ' ') { value.push_back(s[ptr]); ++ptr; } } if (ptr < s.size() && s[ptr] == ' ') { // skip ' ' after key=value ++ptr; } mapping[key] = value; } return mapping; } /** * Read full contents of a file and return them in a std::string. * Returns a pair . * If an error occurred, status will be false, otherwise status will be true and * the data will be returned in string. * * @param maxsize Puts a maximum size limit on the file that is read. If the * file is larger than this, truncated data * (with len > maxsize) will be returned. */ static std::pair ReadBinaryFile(const fs::path &filename, size_t maxsize = std::numeric_limits::max()) { FILE *f = fsbridge::fopen(filename, "rb"); if (f == nullptr) { return std::make_pair(false, ""); } std::string retval; char buffer[128]; size_t n; while ((n = fread(buffer, 1, sizeof(buffer), f)) > 0) { retval.append(buffer, buffer + n); if (retval.size() > maxsize) { break; } } fclose(f); return std::make_pair(true, retval); } /** * Write contents of std::string to a file. * @return true on success. */ static bool WriteBinaryFile(const fs::path &filename, const std::string &data) { FILE *f = fsbridge::fopen(filename, "wb"); if (f == nullptr) { return false; } if (fwrite(data.data(), 1, data.size(), f) != data.size()) { fclose(f); return false; } fclose(f); return true; } /****** Bitcoin specific TorController implementation ********/ /** * Controller that connects to Tor control socket, authenticate, then create * and maintain a ephemeral hidden service. */ class TorController { public: TorController(struct event_base *base, const std::string &target); ~TorController(); /** Get name fo file to store private key in */ fs::path GetPrivateKeyFile(); /** Reconnect, after getting disconnected */ void Reconnect(); private: struct event_base *base; std::string target; TorControlConnection conn; std::string private_key; std::string service_id; bool reconnect; struct event *reconnect_ev; float reconnect_timeout; CService service; /** Cookie for SAFECOOKIE auth */ std::vector cookie; /** ClientNonce for SAFECOOKIE auth */ std::vector clientNonce; /** Callback for ADD_ONION result */ void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply); /** Callback for AUTHENTICATE result */ void auth_cb(TorControlConnection &conn, const TorControlReply &reply); /** Callback for AUTHCHALLENGE result */ void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply); /** Callback for PROTOCOLINFO result */ void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply); /** Callback after successful connection */ void connected_cb(TorControlConnection &conn); /** Callback after connection lost or failed connection attempt */ void disconnected_cb(TorControlConnection &conn); /** Callback for reconnect timer */ static void reconnect_cb(evutil_socket_t fd, short what, void *arg); }; TorController::TorController(struct event_base *_base, const std::string &_target) : base(_base), target(_target), conn(base), reconnect(true), reconnect_ev(0), reconnect_timeout(RECONNECT_TIMEOUT_START) { reconnect_ev = event_new(base, -1, 0, reconnect_cb, this); if (!reconnect_ev) { LogPrintf( "tor: Failed to create event for reconnection: out of memory?\n"); } // Start connection attempts immediately if (!conn.Connect(_target, boost::bind(&TorController::connected_cb, this, _1), boost::bind(&TorController::disconnected_cb, this, _1))) { LogPrintf("tor: Initiating connection to Tor control port %s failed\n", _target); } // Read service private key if cached std::pair pkf = ReadBinaryFile(GetPrivateKeyFile()); if (pkf.first) { LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", GetPrivateKeyFile()); private_key = pkf.second; } } TorController::~TorController() { if (reconnect_ev) { event_free(reconnect_ev); - reconnect_ev = 0; + reconnect_ev = nullptr; } if (service.IsValid()) { RemoveLocal(service); } } void TorController::add_onion_cb(TorControlConnection &_conn, const TorControlReply &reply) { if (reply.code == 250) { LogPrint(BCLog::TOR, "tor: ADD_ONION successful\n"); for (const std::string &s : reply.lines) { std::map m = ParseTorReplyMapping(s); std::map::iterator i; if ((i = m.find("ServiceID")) != m.end()) { service_id = i->second; } if ((i = m.find("PrivateKey")) != m.end()) { private_key = i->second; } } service = LookupNumeric(std::string(service_id + ".onion").c_str(), GetListenPort()); LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString()); if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", GetPrivateKeyFile()); } else { LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile()); } AddLocal(service, LOCAL_MANUAL); // ... onion requested - keep connection open } else if (reply.code == 510) { // 510 Unrecognized command LogPrintf("tor: Add onion failed with unrecognized command (You " "probably need to upgrade Tor)\n"); } else { LogPrintf("tor: Add onion failed; error code %d\n", reply.code); } } void TorController::auth_cb(TorControlConnection &_conn, const TorControlReply &reply) { if (reply.code == 250) { LogPrint(BCLog::TOR, "tor: Authentication successful\n"); // Now that we know Tor is running setup the proxy for onion addresses // if -onion isn't set to something else. if (gArgs.GetArg("-onion", "") == "") { CService resolved(LookupNumeric("127.0.0.1", 9050)); proxyType addrOnion = proxyType(resolved, true); SetProxy(NET_TOR, addrOnion); SetLimited(NET_TOR, false); } // Finally - now create the service // No private key, generate one if (private_key.empty()) { // Explicitly request RSA1024 - see issue #9214 private_key = "NEW:RSA1024"; } // Request hidden service, redirect port. // Note that the 'virtual' port doesn't have to be the same as our // internal port, but this is just a convenient choice. TODO; refactor // the shutdown sequence some day. _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()), boost::bind(&TorController::add_onion_cb, this, _1, _2)); } else { LogPrintf("tor: Authentication failed\n"); } } /** Compute Tor SAFECOOKIE response. * * ServerHash is computed as: * HMAC-SHA256("Tor safe cookie authentication server-to-controller hash", * CookieString | ClientNonce | ServerNonce) * (with the HMAC key as its first argument) * * After a controller sends a successful AUTHCHALLENGE command, the * next command sent on the connection must be an AUTHENTICATE command, * and the only authentication string which that AUTHENTICATE command * will accept is: * * HMAC-SHA256("Tor safe cookie authentication controller-to-server hash", * CookieString | ClientNonce | ServerNonce) * */ static std::vector ComputeResponse(const std::string &key, const std::vector &cookie, const std::vector &clientNonce, const std::vector &serverNonce) { CHMAC_SHA256 computeHash((const uint8_t *)key.data(), key.size()); std::vector computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0); computeHash.Write(cookie.data(), cookie.size()); computeHash.Write(clientNonce.data(), clientNonce.size()); computeHash.Write(serverNonce.data(), serverNonce.size()); computeHash.Finalize(computedHash.data()); return computedHash; } void TorController::authchallenge_cb(TorControlConnection &_conn, const TorControlReply &reply) { if (reply.code == 250) { LogPrint(BCLog::TOR, "tor: SAFECOOKIE authentication challenge successful\n"); std::pair l = SplitTorReplyLine(reply.lines[0]); if (l.first == "AUTHCHALLENGE") { std::map m = ParseTorReplyMapping(l.second); std::vector serverHash = ParseHex(m["SERVERHASH"]); std::vector serverNonce = ParseHex(m["SERVERNONCE"]); LogPrint(BCLog::TOR, "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce)); if (serverNonce.size() != 32) { LogPrintf( "tor: ServerNonce is not 32 bytes, as required by spec\n"); return; } std::vector computedServerHash = ComputeResponse( TOR_SAFE_SERVERKEY, cookie, clientNonce, serverNonce); if (computedServerHash != serverHash) { LogPrintf("tor: ServerHash %s does not match expected " "ServerHash %s\n", HexStr(serverHash), HexStr(computedServerHash)); return; } std::vector computedClientHash = ComputeResponse( TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce); _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2)); } else { LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n"); } } else { LogPrintf("tor: SAFECOOKIE authentication challenge failed\n"); } } void TorController::protocolinfo_cb(TorControlConnection &_conn, const TorControlReply &reply) { if (reply.code == 250) { std::set methods; std::string cookiefile; /* * 250-AUTH METHODS=COOKIE,SAFECOOKIE * COOKIEFILE="/home/x/.tor/control_auth_cookie" * 250-AUTH METHODS=NULL * 250-AUTH METHODS=HASHEDPASSWORD */ for (const std::string &s : reply.lines) { std::pair l = SplitTorReplyLine(s); if (l.first == "AUTH") { std::map m = ParseTorReplyMapping(l.second); std::map::iterator i; if ((i = m.find("METHODS")) != m.end()) { boost::split(methods, i->second, boost::is_any_of(",")); } if ((i = m.find("COOKIEFILE")) != m.end()) { cookiefile = i->second; } } else if (l.first == "VERSION") { std::map m = ParseTorReplyMapping(l.second); std::map::iterator i; if ((i = m.find("Tor")) != m.end()) { LogPrint(BCLog::TOR, "tor: Connected to Tor version %s\n", i->second); } } } for (const std::string &s : methods) { LogPrint(BCLog::TOR, "tor: Supported authentication method: %s\n", s); } // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use // HASHEDPASSWORD /* Authentication: * cookie: hex-encoded ~/.tor/control_auth_cookie * password: "password" */ std::string torpassword = gArgs.GetArg("-torpassword", ""); if (!torpassword.empty()) { if (methods.count("HASHEDPASSWORD")) { LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n"); boost::replace_all(torpassword, "\"", "\\\""); _conn.Command( "AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); } else { LogPrintf("tor: Password provided with -torpassword, but " "HASHEDPASSWORD authentication is not available\n"); } } else if (methods.count("NULL")) { LogPrint(BCLog::TOR, "tor: Using NULL authentication\n"); _conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2)); } else if (methods.count("SAFECOOKIE")) { // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, " "reading cookie authentication from %s\n", cookiefile); std::pair status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), // boost::bind(&TorController::auth_cb, this, _1, _2)); cookie = std::vector(status_cookie.second.begin(), status_cookie.second.end()); clientNonce = std::vector(TOR_NONCE_SIZE, 0); GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE); _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2)); } else { if (status_cookie.first) { LogPrintf("tor: Authentication cookie %s is not exactly %i " "bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE); } else { LogPrintf("tor: Authentication cookie %s could not be " "opened (check permissions)\n", cookiefile); } } } else if (methods.count("HASHEDPASSWORD")) { LogPrintf("tor: The only supported authentication mechanism left " "is password, but no password provided with " "-torpassword\n"); } else { LogPrintf("tor: No supported authentication method\n"); } } else { LogPrintf("tor: Requesting protocol info failed\n"); } } void TorController::connected_cb(TorControlConnection &_conn) { reconnect_timeout = RECONNECT_TIMEOUT_START; // First send a PROTOCOLINFO command to figure out what authentication is // expected if (!_conn.Command( "PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2))) { LogPrintf("tor: Error sending initial protocolinfo command\n"); } } void TorController::disconnected_cb(TorControlConnection &_conn) { // Stop advertising service when disconnected if (service.IsValid()) { RemoveLocal(service); } service = CService(); if (!reconnect) { return; } LogPrint(BCLog::TOR, "tor: Not connected to Tor control port %s, trying to reconnect\n", target); // Single-shot timer for reconnect. Use exponential backoff. struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0)); if (reconnect_ev) { event_add(reconnect_ev, &time); } reconnect_timeout *= RECONNECT_TIMEOUT_EXP; } void TorController::Reconnect() { /* Try to reconnect and reestablish if we get booted - for example, Tor may * be restarting. */ if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1), boost::bind(&TorController::disconnected_cb, this, _1))) { LogPrintf( "tor: Re-initiating connection to Tor control port %s failed\n", target); } } fs::path TorController::GetPrivateKeyFile() { return GetDataDir() / "onion_private_key"; } void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) { TorController *self = (TorController *)arg; self->Reconnect(); } /****** Thread ********/ static struct event_base *gBase; static std::thread torControlThread; static void TorControlThread() { TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL)); event_base_dispatch(gBase); } void StartTorControl() { assert(!gBase); #ifdef WIN32 evthread_use_windows_threads(); #else evthread_use_pthreads(); #endif gBase = event_base_new(); if (!gBase) { LogPrintf("tor: Unable to create event_base\n"); return; } torControlThread = std::thread( std::bind(&TraceThread, "torcontrol", &TorControlThread)); } void InterruptTorControl() { if (gBase) { LogPrintf("tor: Thread interrupt\n"); event_base_loopbreak(gBase); } } void StopTorControl() { if (gBase) { torControlThread.join(); event_base_free(gBase); gBase = nullptr; } } diff --git a/src/validation.h b/src/validation.h index 81183a15b8..043213d365 100644 --- a/src/validation.h +++ b/src/validation.h @@ -1,718 +1,718 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Copyright (c) 2017 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_VALIDATION_H #define BITCOIN_VALIDATION_H #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "amount.h" #include "blockfileinfo.h" #include "coins.h" #include "consensus/consensus.h" #include "diskblockpos.h" #include "fs.h" #include "protocol.h" // For CMessageHeader::MessageMagic #include "script/script_error.h" #include "sync.h" #include "versionbits.h" #include #include #include #include #include #include #include #include #include class CBlockIndex; class CBlockTreeDB; class CBloomFilter; class CChainParams; class CCoinsViewDB; class CConnman; class CInv; class Config; class CScriptCheck; class CTxMemPool; class CTxUndo; class CValidationInterface; class CValidationState; struct CDiskBlockPos; struct ChainTxData; struct PrecomputedTransactionData; struct LockPoints; #define MIN_TRANSACTION_SIZE \ (::GetSerializeSize(CTransaction(), SER_NETWORK, PROTOCOL_VERSION)) /** Default for DEFAULT_WHITELISTRELAY. */ static const bool DEFAULT_WHITELISTRELAY = true; /** Default for DEFAULT_WHITELISTFORCERELAY. */ static const bool DEFAULT_WHITELISTFORCERELAY = true; /** Default for -minrelaytxfee, minimum relay fee for transactions */ static const Amount DEFAULT_MIN_RELAY_TX_FEE_PER_KB(1000 * SATOSHI); /** Default for -excessutxocharge for transactions transactions */ static const Amount DEFAULT_UTXO_FEE = Amount::zero(); //! -maxtxfee default static const Amount DEFAULT_TRANSACTION_MAXFEE(COIN / 10); //! Discourage users to set fees higher than this amount (in satoshis) per kB static const Amount HIGH_TX_FEE_PER_KB(COIN / 100); /** * -maxtxfee will warn if called with a higher fee than this amount (in satoshis */ static const Amount HIGH_MAX_TX_FEE(100 * HIGH_TX_FEE_PER_KB); /** Default for -limitancestorcount, max number of in-mempool ancestors */ static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25; /** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool * ancestors */ static const unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT = 101; /** Default for -limitdescendantcount, max number of in-mempool descendants */ static const unsigned int DEFAULT_DESCENDANT_LIMIT = 25; /** Default for -limitdescendantsize, maximum kilobytes of in-mempool * descendants */ static const unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT = 101; /** Default for -mempoolexpiry, expiration time for mempool transactions in * hours */ static const unsigned int DEFAULT_MEMPOOL_EXPIRY = 336; /** The maximum size of a blk?????.dat file (since 0.8) */ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB /** Maximum number of script-checking threads allowed */ static const int MAX_SCRIPTCHECK_THREADS = 16; /** -par default (number of script-checking threads, 0 = auto) */ static const int DEFAULT_SCRIPTCHECK_THREADS = 0; /** * Number of blocks that can be requested at any given time from a single peer. */ static const int MAX_BLOCKS_IN_TRANSIT_PER_PEER = 16; /** * Timeout in seconds during which a peer must stall block download progress * before being disconnected. */ static const unsigned int BLOCK_STALLING_TIMEOUT = 2; /** * Number of headers sent in one getheaders result. We rely on the assumption * that if a peer sends less than this number, we reached its tip. Changing this * value is a protocol upgrade. */ static const unsigned int MAX_HEADERS_RESULTS = 2000; /** * Maximum depth of blocks we're willing to serve as compact blocks to peers * when requested. For older blocks, a regular BLOCK response will be sent. */ static const int MAX_CMPCTBLOCK_DEPTH = 5; /** * Maximum depth of blocks we're willing to respond to GETBLOCKTXN requests for. */ static const int MAX_BLOCKTXN_DEPTH = 10; /** * Size of the "block download window": how far ahead of our current height do * we fetch ? Larger windows tolerate larger download speed differences between * peer, but increase the potential degree of disordering of blocks on disk * (which make reindexing and in the future perhaps pruning harder). We'll * probably want to make this a per-peer adaptive value at some point. */ static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; /** Time to wait (in seconds) between writing blocks/block index to disk. */ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60; /** Time to wait (in seconds) between flushing chainstate to disk. */ static const unsigned int DATABASE_FLUSH_INTERVAL = 24 * 60 * 60; /** Maximum length of reject messages. */ static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; /** Average delay between local address broadcasts in seconds. */ static const unsigned int AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24 * 24 * 60; /** Average delay between peer address broadcasts in seconds. */ static const unsigned int AVG_ADDRESS_BROADCAST_INTERVAL = 30; /** * Average delay between trickled inventory transmissions in seconds. * Blocks and whitelisted receivers bypass this, outbound peers get half this * delay. */ static const unsigned int INVENTORY_BROADCAST_INTERVAL = 5; /** * Maximum number of inventory items to send per transmission. * Limits the impact of low-fee transaction floods. */ static const unsigned int INVENTORY_BROADCAST_MAX_PER_MB = 7 * INVENTORY_BROADCAST_INTERVAL; /** Average delay between feefilter broadcasts in seconds. */ static const unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; /** Maximum feefilter broadcast delay after significant change. */ static const unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60; /** Block download timeout base, expressed in millionths of the block interval * (i.e. 10 min) */ static const int64_t BLOCK_DOWNLOAD_TIMEOUT_BASE = 1000000; /** * Additional block download timeout per parallel downloading peer (i.e. 5 min) */ static const int64_t BLOCK_DOWNLOAD_TIMEOUT_PER_PEER = 500000; static const unsigned int DEFAULT_LIMITFREERELAY = 0; static const bool DEFAULT_RELAYPRIORITY = true; static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60; /** * Maximum age of our tip in seconds for us to be considered current for fee * estimation. */ static const int64_t MAX_FEE_ESTIMATION_TIP_AGE = 3 * 60 * 60; /** Default for -permitbaremultisig */ static const bool DEFAULT_PERMIT_BAREMULTISIG = true; static const bool DEFAULT_CHECKPOINTS_ENABLED = true; static const bool DEFAULT_TXINDEX = false; static const unsigned int DEFAULT_BANSCORE_THRESHOLD = 100; /** Default for -persistmempool */ static const bool DEFAULT_PERSIST_MEMPOOL = true; /** Default for using fee filter */ static const bool DEFAULT_FEEFILTER = true; /** * Maximum number of headers to announce when relaying blocks with headers * message. */ static const unsigned int MAX_BLOCKS_TO_ANNOUNCE = 8; /** Maximum number of unconnecting headers announcements before DoS score */ static const int MAX_UNCONNECTING_HEADERS = 10; static const bool DEFAULT_PEERBLOOMFILTERS = true; /** Default for -stopatheight */ static const int DEFAULT_STOPATHEIGHT = 0; /** Default for -maxreorgdepth */ static const int DEFAULT_MAX_REORG_DEPTH = 10; /** * Default for -finalizationdelay * This is the minimum time between a block header reception and the block * finalization. * This value should be >> block propagation and validation time */ static const int64_t DEFAULT_MIN_FINALIZATION_DELAY = 2 * 60 * 60; extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern CTxMemPool g_mempool; extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; extern const std::string strMessageMagic; extern CWaitableCriticalSection g_best_block_mutex; extern CConditionVariable g_best_block_cv; extern uint256 g_best_block; extern std::atomic_bool fImporting; extern bool fReindex; extern int nScriptCheckThreads; extern bool fTxIndex; extern bool fIsBareMultisigStd; extern bool fRequireStandard; extern bool fCheckBlockIndex; extern bool fCheckpointsEnabled; extern size_t nCoinCacheUsage; /** * Absolute maximum transaction fee (in satoshis) used by wallet and mempool * (rejects high fee in sendrawtransaction) */ extern Amount maxTxFee; /** * If the tip is older than this (in seconds), the node is considered to be in * initial block download. */ extern int64_t nMaxTipAge; /** * Block hash whose ancestors we will assume to have valid scripts without * checking them. */ extern uint256 hashAssumeValid; /** * Minimum work we will assume exists on some valid chain. */ extern arith_uint256 nMinimumChainWork; /** * Best header we've seen so far (used for getheaders queries' starting points). */ extern CBlockIndex *pindexBestHeader; /** Minimum disk space required - used in CheckDiskSpace() */ static const uint64_t nMinDiskSpace = 52428800; /** Pruning-related variables and constants */ /** True if any block files have ever been pruned. */ extern bool fHavePruned; /** True if we're running in -prune mode. */ extern bool fPruneMode; /** Number of MiB of block files that we're trying to stay below. */ extern uint64_t nPruneTarget; /** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of * chainActive.Tip() will not be pruned. */ static const unsigned int MIN_BLOCKS_TO_KEEP = 288; static const signed int DEFAULT_CHECKBLOCKS = 6; static const unsigned int DEFAULT_CHECKLEVEL = 3; /** * Require that user allocate at least 550MB for block & undo files (blk???.dat * and rev???.dat) * At 1MB per block, 288 blocks = 288MB. * Add 15% for Undo data = 331MB * Add 20% for Orphan block rate = 397MB * We want the low water mark after pruning to be at least 397 MB and since we * prune in full block file chunks, we need the high water mark which triggers * the prune to be one 128MB block file + added 15% undo data = 147MB greater * for a total of 545MB. Setting the target to > than 550MB will make it likely * we can respect the target. */ static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES = 550 * 1024 * 1024; class BlockValidationOptions { private: bool checkPoW : 1; bool checkMerkleRoot : 1; public: // Do full validation by default BlockValidationOptions() : checkPoW(true), checkMerkleRoot(true) {} BlockValidationOptions(bool checkPoWIn, bool checkMerkleRootIn) : checkPoW(checkPoWIn), checkMerkleRoot(checkMerkleRootIn) {} bool shouldValidatePoW() const { return checkPoW; } bool shouldValidateMerkleRoot() const { return checkMerkleRoot; } }; /** * Process an incoming block. This only returns after the best known valid * block is made active. Note that it does not, however, guarantee that the * specific block passed to it has been checked for validity! * * If you want to *possibly* get feedback on whether pblock is valid, you must * install a CValidationInterface (see validationinterface.h) - this will have * its BlockChecked method called whenever *any* block completes validation. * * Note that we guarantee that either the proof-of-work is valid on pblock, or * (and possibly also) BlockChecked will have been called. * * Call without cs_main held. * * @param[in] config The global config. * @param[in] pblock The block we want to process. * @param[in] fForceProcessing Process this block even if unrequested; used * for non-network block sources and whitelisted peers. * @param[out] fNewBlock A boolean which is set to indicate if the block was * first received via this call. * @return True if the block is accepted as a valid block. */ bool ProcessNewBlock(const Config &config, const std::shared_ptr pblock, bool fForceProcessing, bool *fNewBlock); /** * Process incoming block headers. * * Call without cs_main held. * * @param[in] config The config. * @param[in] block The block headers themselves. * @param[out] state This may be set to an Error state if any error * occurred processing them. * @param[out] ppindex If set, the pointer will be set to point to the * last new block index object for the given headers. * @param[out] first_invalid First header that fails validation, if one exists. * @return True if block headers were accepted as valid. */ bool ProcessNewBlockHeaders(const Config &config, const std::vector &block, CValidationState &state, const CBlockIndex **ppindex = nullptr, CBlockHeader *first_invalid = nullptr); /** * Check whether enough disk space is available for an incoming block. */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); /** * Open a block file (blk?????.dat). */ FILE *OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); /** * Translation to a filesystem path. */ fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix); /** * Import blocks from an external file. */ bool LoadExternalBlockFile(const Config &config, FILE *fileIn, CDiskBlockPos *dbp = nullptr); /** * Ensures we have a genesis block in the block tree, possibly writing one to * disk. */ bool LoadGenesisBlock(const CChainParams &chainparams); /** * Load the block tree and coins database from disk, initializing state if we're * running with -reindex. */ bool LoadBlockIndex(const Config &config); /** * Update the chain tip based on database information. */ bool LoadChainTip(const Config &config); /** * Unload database information. */ void UnloadBlockIndex(); /** * Run an instance of the script checking thread. */ void ThreadScriptCheck(); /** * Check whether we are doing an initial block download (synchronizing from disk * or network) */ bool IsInitialBlockDownload(); /** * Retrieve a transaction (from memory pool, or from disk, if possible). */ bool GetTransaction(const Config &config, const TxId &txid, CTransactionRef &tx, uint256 &hashBlock, bool fAllowSlow = false); /** * Find the best known block, and make it the active tip of the block chain. * If it fails, the tip is not updated. * * pblock is either nullptr or a pointer to a block that is already loaded * in memory (to avoid loading it from disk again). * * Returns true if a new chain tip was set. */ bool ActivateBestChain( const Config &config, CValidationState &state, std::shared_ptr pblock = std::shared_ptr()); Amount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams); /** * Guess verification progress (as a fraction between 0.0=genesis and * 1.0=current tip). */ double GuessVerificationProgress(const ChainTxData &data, CBlockIndex *pindex); /** * Mark one block file as pruned. */ void PruneOneBlockFile(const int fileNumber); /** * Actually unlink the specified files */ void UnlinkPrunedFiles(const std::set &setFilesToPrune); /** Create a new block index entry for a given block hash */ CBlockIndex *InsertBlockIndex(uint256 hash); /** Flush all state, indexes and buffers to disk. */ void FlushStateToDisk(); /** Prune block files and flush state to disk. */ void PruneAndFlush(); /** Prune block files up to a given height */ void PruneBlockFilesManual(int nPruneUpToHeight); /** * (try to) add transaction to memory pool */ bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, bool *pfMissingInputs, bool fOverrideMempoolLimit = false, const Amount nAbsurdFee = Amount::zero()); /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state); /** * Check whether all inputs of this transaction are valid (no double spends, * scripts & sigs, amounts). This does not modify the UTXO set. * * If pvChecks is not nullptr, script checks are pushed onto it instead of being * performed inline. Any script checks which are not necessary (eg due to script * execution cache hits) are, obviously, not pushed onto pvChecks/run. * * Setting sigCacheStore/scriptCacheStore to false will remove elements from the * corresponding cache which are matched. This is useful for checking blocks * where we will likely never need the cache entry again. */ bool CheckInputs(const CTransaction &tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, const uint32_t flags, bool sigCacheStore, bool scriptCacheStore, const PrecomputedTransactionData &txdata, std::vector *pvChecks = nullptr); /** * Mark all the coins corresponding to a given transaction inputs as spent. */ void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, int nHeight); /** * Apply the effects of this transaction on the UTXO set represented by view. */ void UpdateCoins(CCoinsViewCache &view, const CTransaction &tx, int nHeight); void UpdateCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, int nHeight); /** * Test whether the LockPoints height and time are still valid on the current * chain. */ bool TestLockPointValidity(const LockPoints *lp); /** * Check if transaction will be BIP 68 final in the next block to be created. * * Simulates calling SequenceLocks() with data from the tip of the current * active chain. Optionally stores in LockPoints the resulting height and time * calculated and the hash of the block needed for calculation or skips the * calculation and uses the LockPoints passed in for evaluation. The LockPoints * should not be considered valid if CheckSequenceLocks returns false. * * See consensus/consensus.h for flag definitions. */ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints *lp = nullptr, bool useExistingLockPoints = false); /** * Closure representing one script verification. * Note that this stores references to the spending transaction. */ class CScriptCheck { private: CScript scriptPubKey; Amount amount; const CTransaction *ptxTo; unsigned int nIn; uint32_t nFlags; bool cacheStore; ScriptError error; PrecomputedTransactionData txdata; public: CScriptCheck() - : amount(), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), + : amount(), ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata() {} CScriptCheck(const CScript &scriptPubKeyIn, const Amount amountIn, const CTransaction &txToIn, unsigned int nInIn, uint32_t nFlagsIn, bool cacheIn, const PrecomputedTransactionData &txdataIn) : scriptPubKey(scriptPubKeyIn), amount(amountIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) {} bool operator()(); void swap(CScriptCheck &check) { scriptPubKey.swap(check.scriptPubKey); std::swap(ptxTo, check.ptxTo); std::swap(amount, check.amount); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); std::swap(error, check.error); std::swap(txdata, check.txdata); } ScriptError GetScriptError() const { return error; } }; /** Functions for disk access for blocks */ bool ReadBlockFromDisk(CBlock &block, const CDiskBlockPos &pos, const Config &config); bool ReadBlockFromDisk(CBlock &block, const CBlockIndex *pindex, const Config &config); /** Functions for validating blocks and updating the block tree */ /** * Context-independent validity checks. * * Returns true if the provided block is valid (has valid header, * transactions are valid, block is a valid size, etc.) */ bool CheckBlock( const Config &Config, const CBlock &block, CValidationState &state, BlockValidationOptions validationOptions = BlockValidationOptions()); /** * This is a variant of ContextualCheckTransaction which computes the contextual * check for a transaction based on the chain tip. * * See consensus/consensus.h for flag definitions. */ bool ContextualCheckTransactionForCurrentBlock(const Config &config, const CTransaction &tx, CValidationState &state, int flags = -1); /** * Check a block is completely valid from start to finish (only works on top of * our current best block, with cs_main held) */ bool TestBlockValidity( const Config &config, CValidationState &state, const CBlock &block, CBlockIndex *pindexPrev, BlockValidationOptions validationOptions = BlockValidationOptions()); /** * When there are blocks in the active chain with missing data, rewind the * chainstate and remove them from the block index. */ bool RewindBlockIndex(const Config &config); /** * RAII wrapper for VerifyDB: Verify consistency of the block and coin * databases. */ class CVerifyDB { public: CVerifyDB(); ~CVerifyDB(); bool VerifyDB(const Config &config, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth); }; /** Replay blocks that aren't fully applied to the database. */ bool ReplayBlocks(const Config &config, CCoinsView *view); /** Find the last common block between the parameter chain and a locator. */ CBlockIndex *FindForkInGlobalIndex(const CChain &chain, const CBlockLocator &locator); /** * Treats a block as if it were received before others with the same work, * making it the active chain tip if applicable. Successive calls to * PreciousBlock() will override the effects of earlier calls. The effects of * calls to PreciousBlock() are not retained across restarts. * * Returns true if the provided block index successfully became the chain tip. */ bool PreciousBlock(const Config &config, CValidationState &state, CBlockIndex *pindex); /** * Mark a block as finalized. * A finalized block can not be reorged in any way. */ bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, CBlockIndex *pindex); /** Mark a block as invalid. */ bool InvalidateBlock(const Config &config, CValidationState &state, CBlockIndex *pindex); /** Park a block. */ bool ParkBlock(const Config &config, CValidationState &state, CBlockIndex *pindex); /** Remove invalidity status from a block and its descendants. */ bool ResetBlockFailureFlags(CBlockIndex *pindex); /** Remove parked status from a block and its descendants. */ bool UnparkBlockAndChildren(CBlockIndex *pindex); /** Remove parked status from a block. */ bool UnparkBlock(CBlockIndex *pindex); /** * Retrieve the topmost finalized block. */ const CBlockIndex *GetFinalizedBlock(); /** * Checks if a block is finalized. */ bool IsBlockFinalized(const CBlockIndex *pindex); /** The currently-connected chain of blocks (protected by cs_main). */ extern CChain chainActive; /** * Global variable that points to the coins database (protected by cs_main) */ extern std::unique_ptr pcoinsdbview; /** * Global variable that points to the active CCoinsView (protected by cs_main) */ extern std::unique_ptr pcoinsTip; /** * Global variable that points to the active block tree (protected by cs_main) */ extern std::unique_ptr pblocktree; /** * Return the spend height, which is one more than the inputs.GetBestBlock(). * While checking, GetBestBlock() refers to the parent block. (protected by * cs_main) * This is also true for mempool checks. */ int GetSpendHeight(const CCoinsViewCache &inputs); extern VersionBitsCache versionbitscache; /** * Determine what nVersion a new block should use. */ int32_t ComputeBlockVersion(const CBlockIndex *pindexPrev, const Consensus::Params ¶ms); /** * Reject codes greater or equal to this can be returned by AcceptToMemPool or * AcceptBlock for blocks/transactions, to signal internal conditions. They * cannot and should not be sent over the P2P network. */ static const unsigned int REJECT_INTERNAL = 0x100; /** Too high fee. Can not be triggered by P2P transactions */ static const unsigned int REJECT_HIGHFEE = 0x100; /** Transaction is already known (either in mempool or blockchain) */ static const unsigned int REJECT_ALREADY_KNOWN = 0x101; /** Transaction conflicts with a transaction already known */ static const unsigned int REJECT_CONFLICT = 0x102; /** Block conflicts with a transaction already known */ static const unsigned int REJECT_AGAINST_FINALIZED = 0x103; /** Get block file info entry for one block file */ CBlockFileInfo *GetBlockFileInfo(size_t n); /** Dump the mempool to disk. */ void DumpMempool(); /** Load the mempool from disk. */ bool LoadMempool(const Config &config); #endif // BITCOIN_VALIDATION_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 9e96a612c1..1dd1d54997 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1,3701 +1,3701 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "amount.h" #include "chain.h" #include "chainparams.h" // for GetConsensus. #include "config.h" #include "consensus/validation.h" #include "core_io.h" #include "dstencode.h" #include "net.h" #include "policy/fees.h" #include "policy/policy.h" #include "rpc/mining.h" #include "rpc/misc.h" #include "rpc/safemode.h" #include "rpc/server.h" #include "timedata.h" #include "util.h" #include "utilmoneystr.h" #include "validation.h" #include "wallet.h" #include "wallet/coincontrol.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" #include "walletdb.h" // Input src/init.h (not wallet/init.h) for StartShutdown #include #include #include static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; static std::string urlDecode(const std::string &urlEncoded) { std::string res; if (!urlEncoded.empty()) { char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr); if (decoded) { res = std::string(decoded); free(decoded); } } return res; } CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest &request) { if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) { // wallet endpoint was used std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size())); for (CWalletRef pwallet : ::vpwallets) { if (pwallet->GetName() == requestedWallet) { return pwallet; } } throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); } return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet *const pwallet) { return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to " "be set with walletpassphrase " "call." : ""; } bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException) { if (pwallet) { return true; } if (avoidException) { return false; } if (::vpwallets.empty()) { // Note: It isn't currently possible to trigger this error because // wallet RPC methods aren't registered unless a wallet is loaded. But // this error is being kept as a precaution, because it's possible in // the future that wallet RPC methods might get or remain registered // when no wallets are loaded. throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet " "method is disabled because " "no wallet is loaded)"); } throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED, "Wallet file not specified (must request wallet RPC " "through /wallet/ uri-path)."); } void EnsureWalletIsUnlocked(CWallet *const pwallet) { if (pwallet->IsLocked()) { throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the " "wallet passphrase with " "walletpassphrase first."); } } void WalletTxToJSON(const CWalletTx &wtx, UniValue &entry) { int confirms = wtx.GetDepthInMainChain(); entry.pushKV("confirmations", confirms); if (wtx.IsCoinBase()) { entry.pushKV("generated", true); } if (confirms > 0) { entry.pushKV("blockhash", wtx.hashBlock.GetHex()); entry.pushKV("blockindex", wtx.nIndex); entry.pushKV("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()); } else { entry.pushKV("trusted", wtx.IsTrusted()); } uint256 hash = wtx.GetId(); entry.pushKV("txid", hash.GetHex()); UniValue conflicts(UniValue::VARR); for (const uint256 &conflict : wtx.GetConflicts()) { conflicts.push_back(conflict.GetHex()); } entry.pushKV("walletconflicts", conflicts); entry.pushKV("time", wtx.GetTxTime()); entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived); for (const std::pair &item : wtx.mapValue) { entry.pushKV(item.first, item.second); } } std::string LabelFromValue(const UniValue &value) { std::string label = value.get_str(); if (label == "*") { throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name"); } return label; } static UniValue getnewaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getnewaddress ( \"label\" )\n" "\nReturns a new Bitcoin address for receiving payments.\n" "If 'label' is specified, it is added to the address book \n" "so payments received with the address will be associated with " "'label'.\n" "\nArguments:\n" "1. \"label\" (string, optional) The label name for the " "address to be linked to. If not provided, the default label \"\" " "is used. It can also be set to the empty string \"\" to represent " "the default label. The label does not need to exist, it will be " "created if there is no label by the given name.\n" "\nResult:\n" "\"address\" (string) The new bitcoin address\n" "\nExamples:\n" + HelpExampleRpc("getnewaddress", "")); } LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label; if (request.params.size() > 0) { label = LabelFromValue(request.params[0]); } if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); } // Generate a new key that is added to wallet CPubKey newKey; if (!pwallet->GetKeyFromPool(newKey)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } CKeyID keyID = newKey.GetID(); pwallet->SetAddressBook(keyID, label, "receive"); return EncodeDestination(keyID); } CTxDestination GetLabelAddress(CWallet *const pwallet, const std::string &label, bool bForceNew = false) { CPubKey pubKey; if (!pwallet->GetLabelAddress(pubKey, label, bForceNew)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } return pubKey.GetID(); } UniValue getlabeladdress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getlabeladdress \"label\"\n" "\nReturns the current Bitcoin address for receiving payments to " "this label.\n" "\nArguments:\n" "1. \"label\" (string, required) The label name for the " "address. It can also be set to the empty string \"\" to represent " "the default label. The label does not need to exist, it will be " "created and a new address created if there is no label by the " "given name.\n" "\nResult:\n" "\"address\" (string) The label bitcoin address\n" "\nExamples:\n" + HelpExampleCli("getlabeladdress", "") + HelpExampleCli("getlabeladdress", "\"\"") + HelpExampleCli("getlabeladdress", "\"mylabel\"") + HelpExampleRpc("getlabeladdress", "\"mylabel\"")); } LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label = LabelFromValue(request.params[0]); UniValue ret(UniValue::VSTR); ret = EncodeDestination(GetLabelAddress(pwallet, label)); return ret; } static UniValue getrawchangeaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getrawchangeaddress\n" "\nReturns a new Bitcoin address, for receiving change.\n" "This is for use with raw transactions, NOT normal use.\n" "\nResult:\n" "\"address\" (string) The address\n" "\nExamples:\n" + HelpExampleCli("getrawchangeaddress", "") + HelpExampleRpc("getrawchangeaddress", "")); } LOCK2(cs_main, pwallet->cs_wallet); if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); } CReserveKey reservekey(pwallet); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey, true)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } reservekey.KeepKey(); CKeyID keyID = vchPubKey.GetID(); return EncodeDestination(keyID); } UniValue setlabel(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "setlabel \"address\" \"label\"\n" "\nSets the label associated with the given address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "be associated with a label.\n" "2. \"label\" (string, required) The label to assign the " "address to.\n" "\nExamples:\n" + HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + HelpExampleRpc( "setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string label; if (!request.params[1].isNull()) { label = LabelFromValue(request.params[1]); } // Only add the label if the address is yours. if (IsMine(*pwallet, dest)) { // Detect when changing the label of an address that is the 'unused // current key' of another label: if (pwallet->mapAddressBook.count(dest)) { std::string old_label = pwallet->mapAddressBook[dest].name; if (dest == GetLabelAddress(pwallet, old_label)) { GetLabelAddress(pwallet, old_label, true); } } pwallet->SetAddressBook(dest, label, "receive"); } else { throw JSONRPCError(RPC_MISC_ERROR, "setlabel can only be used with own address"); } return NullUniValue; } static UniValue getaccount(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaccount \"address\"\n" "\nDEPRECATED. Returns the account associated with the given " "address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for " "account lookup.\n" "\nResult:\n" "\"accountname\" (string) the account address\n" "\nExamples:\n" + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"")); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string strAccount; std::map::iterator mi = pwallet->mapAddressBook.find(dest); if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; } return strAccount; } static UniValue getaddressesbyaccount(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaddressesbyaccount \"account\"\n" "\nDEPRECATED. Returns the list of addresses for the given " "account.\n" "\nArguments:\n" "1. \"account\" (string, required) The account name.\n" "\nResult:\n" "[ (json array of string)\n" " \"address\" (string) a bitcoin address associated with " "the given account\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"")); } LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = LabelFromValue(request.params[0]); // Find all addresses that have the given account UniValue ret(UniValue::VARR); for (const std::pair &item : pwallet->mapAddressBook) { const CTxDestination &dest = item.first; const std::string &strName = item.second.name; if (strName == strAccount) { ret.push_back(EncodeDestination(dest)); } } return ret; } static void SendMoney(CWallet *const pwallet, const CTxDestination &address, Amount nValue, bool fSubtractFeeFromAmount, CWalletTx &wtxNew) { Amount curBalance = pwallet->GetBalance(); // Check amount if (nValue <= Amount::zero()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); } if (nValue > curBalance) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); } if (pwallet->GetBroadcastTransactions() && !g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } // Parse Bitcoin address CScript scriptPubKey = GetScriptForDestination(address); // Create and send the transaction CReserveKey reservekey(pwallet); Amount nFeeRequired; std::string strError; std::vector vecSend; int nChangePosRet = -1; CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; vecSend.push_back(recipient); if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) { strError = strprintf("Error: This transaction requires a " "transaction fee of at least %s", FormatMoney(nFeeRequired)); } throw JSONRPCError(RPC_WALLET_ERROR, strError); } CValidationState state; if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) { strError = strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()); throw JSONRPCError(RPC_WALLET_ERROR, strError); } } static UniValue sendtoaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) { throw std::runtime_error( "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" " "subtractfeefromamount )\n" "\nSend an amount to a given address.\n" + HelpRequiringPassphrase(pwallet) + "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address " "to send to.\n" "2. \"amount\" (numeric or string, required) The " "amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" "3. \"comment\" (string, optional) A comment used to " "store what the transaction is for. \n" " This is not part of the transaction, " "just kept in your wallet.\n" "4. \"comment_to\" (string, optional) A comment to store " "the name of the person or organization \n" " to which you're sending the " "transaction. This is not part of the \n" " transaction, just kept in your " "wallet.\n" "5. subtractfeefromamount (boolean, optional, default=false) The " "fee will be deducted from the amount being sent.\n" " The recipient will receive less " "bitcoins than you enter in the amount field.\n" "\nResult:\n" "\"txid\" (string) The transaction id.\n" "\nExamples:\n" + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay" "dd\" 0.1 \"donation\" \"seans " "outpost\"") + HelpExampleCli( "sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay" "dd\", 0.1, \"donation\", \"seans " "outpost\"")); } ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } // Amount Amount nAmount = AmountFromValue(request.params[1]); if (nAmount <= Amount::zero()) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); } // Wallet comments CWalletTx wtx; if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty()) { wtx.mapValue["comment"] = request.params[2].get_str(); } if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty()) { wtx.mapValue["to"] = request.params[3].get_str(); } bool fSubtractFeeFromAmount = false; if (request.params.size() > 4) { fSubtractFeeFromAmount = request.params[4].get_bool(); } EnsureWalletIsUnlocked(pwallet); SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, wtx); return wtx.GetId().GetHex(); } static UniValue listaddressgroupings(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp) { throw std::runtime_error( "listaddressgroupings\n" "\nLists groups of addresses which have had their common " "ownership\n" "made public by common use as inputs or as the resulting change\n" "in past transactions\n" "\nResult:\n" "[\n" " [\n" " [\n" " \"address\", (string) The bitcoin address\n" " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"label\" (string, optional) The label\n" " ]\n" " ,...\n" " ]\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("listaddressgroupings", "") + HelpExampleRpc("listaddressgroupings", "")); } ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); std::map balances = pwallet->GetAddressBalances(); for (const std::set &grouping : pwallet->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); for (const CTxDestination &address : grouping) { UniValue addressInfo(UniValue::VARR); addressInfo.push_back(EncodeDestination(address)); addressInfo.push_back(ValueFromAmount(balances[address])); if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) { addressInfo.push_back( pwallet->mapAddressBook.find(address)->second.name); } jsonGrouping.push_back(addressInfo); } jsonGroupings.push_back(jsonGrouping); } return jsonGroupings; } static UniValue signmessage(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "signmessage \"address\" \"message\"\n" "\nSign a message with the private key of an address" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "use for the private key.\n" "2. \"message\" (string, required) The message to create a " "signature of.\n" "\nResult:\n" "\"signature\" (string) The signature of the message " "encoded in base 64\n" "\nExamples:\n" "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" + HelpExampleCli( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\" \"signature\" \"my " "message\"") + "\nAs json rpc\n" + HelpExampleRpc( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")); } LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); CTxDestination dest = DecodeDestination(strAddress, config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } const CKeyID *keyID = boost::get(&dest); if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } CKey key; if (!pwallet->GetKey(*keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; std::vector vchSig; if (!key.SignCompact(ss.GetHash(), vchSig)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); } return EncodeBase64(&vchSig[0], vchSig.size()); } static UniValue getreceivedbyaddress(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getreceivedbyaddress \"address\" ( minconf )\n" "\nReturns the total amount received by the given address in " "transactions with at least minconf confirmations.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for " "transactions.\n" "2. minconf (numeric, optional, default=1) Only " "include transactions confirmed at least this many times.\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" "\nExamples:\n" "\nThe amount from transactions with at least 1 confirmation\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + "\nThe amount including unconfirmed transactions, zero " "confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") + "\nThe amount with at least 6 confirmation, very safe\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + "\nAs a json rpc call\n" + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")); } ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwallet, scriptPubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet"); } // Minimum confirmations int nMinDepth = 1; if (request.params.size() > 1) { nMinDepth = request.params[1].get_int(); } // Tally Amount nAmount = Amount::zero(); for (const std::pair &pairWtx : pwallet->mapWallet) { const CWalletTx &wtx = pairWtx.second; CValidationState state; if (wtx.IsCoinBase() || !ContextualCheckTransactionForCurrentBlock(config, *wtx.tx, state)) { continue; } for (const CTxOut &txout : wtx.tx->vout) { if (txout.scriptPubKey == scriptPubKey) { if (wtx.GetDepthInMainChain() >= nMinDepth) { nAmount += txout.nValue; } } } } return ValueFromAmount(nAmount); } UniValue getreceivedbylabel(const Config &config, const JSONRPCRequest &request) { CWallet *const pwallet = GetWalletForJSONRPCRequest(request); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getreceivedbylabel \"label\" ( minconf )\n" "\nReturns the total amount received by addresses with