diff --git a/src/bench/examples.cpp b/src/bench/examples.cpp index a78e041a4..20130a706 100644 --- a/src/bench/examples.cpp +++ b/src/bench/examples.cpp @@ -1,31 +1,31 @@ // 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 #include // Sanity test: this should loop ten times, and // min/max/average should be close to 100ms. static void Sleep100ms(benchmark::State &state) { while (state.KeepRunning()) { - MilliSleep(100); + UninterruptibleSleep(std::chrono::milliseconds{100}); } } BENCHMARK(Sleep100ms, 10); // Extremely fast-running benchmark: #include volatile double sum = 0.0; // volatile, global so not optimized away static void Trig(benchmark::State &state) { double d = 0.01; while (state.KeepRunning()) { sum += sin(d); d += 0.000001; } } BENCHMARK(Trig, 12 * 1000 * 1000); diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 4e4753ebf..599ae2990 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -1,658 +1,658 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::function G_TRANSLATION_FUN = nullptr; static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; static const int DEFAULT_HTTP_CLIENT_TIMEOUT = 900; static const bool DEFAULT_NAMED = false; static const int CONTINUE_EXECUTION = -1; static void SetupCliArgs() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); gArgs.AddArg("-?", "This help message", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-conf=", strprintf("Specify configuration file. Relative paths will be " "prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg( "-getinfo", "Get general information from the remote server. Note that unlike " "server-side RPC calls, the results of -getinfo is the result of " "multiple non-atomic requests. Some entries in the result may " "represent results from different states (e.g. wallet balance may be " "as of a different block from the chain state reported)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); SetupChainParamsBaseOptions(); gArgs.AddArg( "-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg( "-rpcconnect=", strprintf("Send commands to node running on (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-rpccookiefile=", "Location of the auth cookie. Relative paths will be prefixed " "by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcport=", strprintf("Connect to JSON-RPC on (default: %u, " "testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcuser=", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcpassword=", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcclienttimeout=", strprintf("Timeout in seconds during HTTP requests, or 0 for " "no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-stdinrpcpass", strprintf("Read RPC password from standard input as a single " "line. When combined with -stdin, the first line " "from standard input is used for the RPC password."), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until " "EOF/Ctrl-D (recommended for sensitive information such as " "passphrases)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); gArgs.AddArg( "-rpcwallet=", "Send RPC for non-default wallet on RPC server (needs to exactly match " "corresponding -wallet option passed to bitcoind)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); // Hidden gArgs.AddArg("-h", "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN); gArgs.AddArg("-help", "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN); } /** libevent event log callback */ static void libevent_log_cb(int severity, const char *msg) { #ifndef EVENT_LOG_ERR // EVENT_LOG_ERR was added in 2.0.19; but before then _EVENT_LOG_ERR existed. #define EVENT_LOG_ERR _EVENT_LOG_ERR #endif // Ignore everything other than errors if (severity >= EVENT_LOG_ERR) { throw std::runtime_error(strprintf("libevent error: %s", msg)); } } ////////////////////////////////////////////////////////////////////////////// // // Start // // // Exception thrown on connection error. This error is used to determine when // to wait if -rpcwait is given. // class CConnectionFailed : public std::runtime_error { public: explicit inline CConnectionFailed(const std::string &msg) : std::runtime_error(msg) {} }; // // This function returns either one of EXIT_ codes when it's expected to stop // the process or CONTINUE_EXECUTION when it's expected to continue further. // static int AppInitRPC(int argc, char *argv[]) { // // Parameters // SetupCliArgs(); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); return EXIT_FAILURE; } if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { std::string strUsage = PACKAGE_NAME " RPC client version " + FormatFullVersion() + "\n"; if (!gArgs.IsArgSet("-version")) { strUsage += "\n" "Usage: bitcoin-cli [options] [params] " "Send command to " PACKAGE_NAME "\n" "or: bitcoin-cli [options] -named " "[name=value]... Send command to " PACKAGE_NAME " (with named arguments)\n" "or: bitcoin-cli [options] help " "List commands\n" "or: bitcoin-cli [options] help Get " "help for a command\n"; strUsage += "\n" + gArgs.GetHelpMessage(); } tfm::format(std::cout, "%s", strUsage); if (argc < 2) { tfm::format(std::cerr, "Error: too few parameters\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } if (!CheckDataDirOption()) { tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")); return EXIT_FAILURE; } if (!gArgs.ReadConfigFiles(error, true)) { tfm::format(std::cerr, "Error reading configuration file: %s\n", error); return EXIT_FAILURE; } // Check for -chain, -testnet or -regtest parameter (BaseParams() calls are // only valid after this clause) try { SelectBaseParams(gArgs.GetChainName()); } catch (const std::exception &e) { tfm::format(std::cerr, "Error: %s\n", e.what()); return EXIT_FAILURE; } return CONTINUE_EXECUTION; } /** Reply structure for request_done to fill in */ struct HTTPReply { HTTPReply() : status(0), error(-1) {} int status; int error; std::string body; }; static const char *http_errorstring(int code) { switch (code) { #if LIBEVENT_VERSION_NUMBER >= 0x02010300 case EVREQ_HTTP_TIMEOUT: return "timeout reached"; case EVREQ_HTTP_EOF: return "EOF reached"; case EVREQ_HTTP_INVALID_HEADER: return "error while reading header, or invalid header"; case EVREQ_HTTP_BUFFER_ERROR: return "error encountered while reading or writing"; case EVREQ_HTTP_REQUEST_CANCEL: return "request was canceled"; case EVREQ_HTTP_DATA_TOO_LONG: return "response body is larger than allowed"; #endif default: return "unknown"; } } static void http_request_done(struct evhttp_request *req, void *ctx) { HTTPReply *reply = static_cast(ctx); if (req == nullptr) { /** * If req is nullptr, it means an error occurred while connecting: the * error code will have been passed to http_error_cb. */ reply->status = 0; return; } reply->status = evhttp_request_get_response_code(req); struct evbuffer *buf = evhttp_request_get_input_buffer(req); if (buf) { size_t size = evbuffer_get_length(buf); const char *data = (const char *)evbuffer_pullup(buf, size); if (data) { reply->body = std::string(data, size); } evbuffer_drain(buf, size); } } #if LIBEVENT_VERSION_NUMBER >= 0x02010300 static void http_error_cb(enum evhttp_request_error err, void *ctx) { HTTPReply *reply = static_cast(ctx); reply->error = err; } #endif /** * Class that handles the conversion from a command-line to a JSON-RPC request, * as well as converting back to a JSON object that can be shown as result. */ class BaseRequestHandler { public: virtual ~BaseRequestHandler() {} virtual UniValue PrepareRequest(const std::string &method, const std::vector &args) = 0; virtual UniValue ProcessReply(const UniValue &batch_in) = 0; }; /** Process getinfo requests */ class GetinfoRequestHandler : public BaseRequestHandler { public: const int ID_NETWORKINFO = 0; const int ID_BLOCKCHAININFO = 1; const int ID_WALLETINFO = 2; /** Create a simulated `getinfo` request. */ UniValue PrepareRequest(const std::string &method, const std::vector &args) override { if (!args.empty()) { throw std::runtime_error("-getinfo takes no arguments"); } UniValue result(UniValue::VARR); result.push_back( JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO)); result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO)); result.push_back( JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO)); return result; } /** Collect values from the batch and form a simulated `getinfo` reply. */ UniValue ProcessReply(const UniValue &batch_in) override { UniValue result(UniValue::VOBJ); std::vector batch = JSONRPCProcessBatchReply(batch_in, 3); // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass // them on getwalletinfo() is allowed to fail in case there is no // wallet. if (!batch[ID_NETWORKINFO]["error"].isNull()) { return batch[ID_NETWORKINFO]; } if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) { return batch[ID_BLOCKCHAININFO]; } result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]); result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]); if (!batch[ID_WALLETINFO].isNull()) { result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); } result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]); result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]); result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]); result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]); result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]); result.pushKV( "testnet", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"].get_str() == "test")); if (!batch[ID_WALLETINFO].isNull()) { result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]); result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]); if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) { result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]); } result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]); } result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]); result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]); return JSONRPCReplyObj(result, NullUniValue, 1); } }; /** Process default single requests */ class DefaultRequestHandler : public BaseRequestHandler { public: UniValue PrepareRequest(const std::string &method, const std::vector &args) override { UniValue params; if (gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { params = RPCConvertNamedValues(method, args); } else { params = RPCConvertValues(method, args); } return JSONRPCRequestObj(method, params, 1); } UniValue ProcessReply(const UniValue &reply) override { return reply.get_obj(); } }; static UniValue CallRPC(BaseRequestHandler *rh, const std::string &strMethod, const std::vector &args) { std::string host; // In preference order, we choose the following for the port: // 1. -rpcport // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) // 3. default port for chain int port = BaseParams().RPCPort(); SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); port = gArgs.GetArg("-rpcport", port); // Obtain event base raii_event_base base = obtain_event_base(); // Synchronously look up hostname raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port); evhttp_connection_set_timeout( evcon.get(), gArgs.GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT)); HTTPReply response; raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void *)&response); if (req == nullptr) { throw std::runtime_error("create http request failed"); } #if LIBEVENT_VERSION_NUMBER >= 0x02010300 evhttp_request_set_error_cb(req.get(), http_error_cb); #endif // Get credentials std::string strRPCUserColonPass; bool failedToGetAuthCookie = false; if (gArgs.GetArg("-rpcpassword", "") == "") { // Try fall back to cookie-based authentication if no password is // provided if (!GetAuthCookie(&strRPCUserColonPass)) { failedToGetAuthCookie = true; } } else { strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); } struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req.get()); assert(output_headers); evhttp_add_header(output_headers, "Host", host.c_str()); evhttp_add_header(output_headers, "Connection", "close"); evhttp_add_header( output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); // Attach request data std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n"; struct evbuffer *output_buffer = evhttp_request_get_output_buffer(req.get()); assert(output_buffer); evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); // check if we should use a special wallet endpoint std::string endpoint = "/"; if (!gArgs.GetArgs("-rpcwallet").empty()) { std::string walletName = gArgs.GetArg("-rpcwallet", ""); char *encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false); if (encodedURI) { endpoint = "/wallet/" + std::string(encodedURI); free(encodedURI); } else { throw CConnectionFailed("uri-encode failed"); } } int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str()); // ownership moved to evcon in above call req.release(); if (r != 0) { throw CConnectionFailed("send http request failed"); } event_base_dispatch(base.get()); if (response.status == 0) { std::string responseErrorMessage; if (response.error != -1) { responseErrorMessage = strprintf(" (error code %d - \"%s\")", response.error, http_errorstring(response.error)); } throw CConnectionFailed( strprintf("Could not connect to the server %s:%d%s\n\nMake sure " "the bitcoind server is running and that you are " "connecting to the correct RPC port.", host, port, responseErrorMessage)); } else if (response.status == HTTP_UNAUTHORIZED) { if (failedToGetAuthCookie) { throw std::runtime_error(strprintf( "Could not locate RPC credentials. No authentication cookie " "could be found, and RPC password is not set. See " "-rpcpassword and -stdinrpcpass. Configuration file: (%s)", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)) .string())); } else { throw std::runtime_error( "Authorization failed: Incorrect rpcuser or rpcpassword"); } } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) { throw std::runtime_error( strprintf("server returned HTTP error %d", response.status)); } else if (response.body.empty()) { throw std::runtime_error("no response from server"); } // Parse reply UniValue valReply(UniValue::VSTR); if (!valReply.read(response.body)) { throw std::runtime_error("couldn't parse reply from server"); } const UniValue reply = rh->ProcessReply(valReply); if (reply.empty()) { throw std::runtime_error( "expected reply to have result, error and id properties"); } return reply; } static int CommandLineRPC(int argc, char *argv[]) { std::string strPrint; int nRet = 0; try { // Skip switches while (argc > 1 && IsSwitchChar(argv[1][0])) { argc--; argv++; } std::string rpcPass; if (gArgs.GetBoolArg("-stdinrpcpass", false)) { if (!std::getline(std::cin, rpcPass)) { throw std::runtime_error("-stdinrpcpass specified but failed " "to read from standard input"); } gArgs.ForceSetArg("-rpcpassword", rpcPass); } std::vector args = std::vector(&argv[1], &argv[argc]); if (gArgs.GetBoolArg("-stdin", false)) { // Read one arg per line from stdin and append std::string line; while (std::getline(std::cin, line)) { args.push_back(line); } } std::unique_ptr rh; std::string method; if (gArgs.GetBoolArg("-getinfo", false)) { rh.reset(new GetinfoRequestHandler()); method = ""; } else { rh.reset(new DefaultRequestHandler()); if (args.size() < 1) { throw std::runtime_error( "too few parameters (need at least command)"); } method = args[0]; // Remove trailing method name from arguments vector args.erase(args.begin()); } // Execute and handle connection failures with -rpcwait const bool fWait = gArgs.GetBoolArg("-rpcwait", false); do { try { const UniValue reply = CallRPC(rh.get(), method, args); // Parse reply const UniValue &result = find_value(reply, "result"); const UniValue &error = find_value(reply, "error"); if (!error.isNull()) { // Error int code = error["code"].get_int(); if (fWait && code == RPC_IN_WARMUP) { throw CConnectionFailed("server in warmup"); } strPrint = "error: " + error.write(); nRet = abs(code); if (error.isObject()) { UniValue errCode = find_value(error, "code"); UniValue errMsg = find_value(error, "message"); strPrint = errCode.isNull() ? "" : "error code: " + errCode.getValStr() + "\n"; if (errMsg.isStr()) { strPrint += "error message:\n" + errMsg.get_str(); } if (errCode.isNum() && errCode.get_int() == RPC_WALLET_NOT_SPECIFIED) { strPrint += "\nTry adding " "\"-rpcwallet=\" option to " "bitcoin-cli command line."; } } } else { // Result if (result.isNull()) { strPrint = ""; } else if (result.isStr()) { strPrint = result.get_str(); } else { strPrint = result.write(2); } } // Connection succeeded, no need to retry. break; } catch (const CConnectionFailed &) { if (fWait) { - MilliSleep(1000); + UninterruptibleSleep(std::chrono::milliseconds{1000}); } else { throw; } } } while (fWait); } catch (const std::exception &e) { strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; } catch (...) { PrintExceptionContinue(nullptr, "CommandLineRPC()"); throw; } if (strPrint != "") { tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint); } return nRet; } int main(int argc, char *argv[]) { #ifdef WIN32 util::WinCmdLineArgs winArgs; std::tie(argc, argv) = winArgs.get(); #endif SetupEnvironment(); if (!SetupNetworking()) { tfm::format(std::cerr, "Error: Initializing networking failed\n"); return EXIT_FAILURE; } event_set_log_callback(&libevent_log_cb); try { int ret = AppInitRPC(argc, argv); if (ret != CONTINUE_EXECUTION) { return ret; } } catch (const std::exception &e) { PrintExceptionContinue(&e, "AppInitRPC()"); return EXIT_FAILURE; } catch (...) { PrintExceptionContinue(nullptr, "AppInitRPC()"); return EXIT_FAILURE; } int ret = EXIT_FAILURE; try { ret = CommandLineRPC(argc, argv); } catch (const std::exception &e) { PrintExceptionContinue(&e, "CommandLineRPC()"); } catch (...) { PrintExceptionContinue(nullptr, "CommandLineRPC()"); } return ret; } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index a659b2bf0..8b9824fd2 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -1,224 +1,224 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::function G_TRANSLATION_FUN = nullptr; /* Introduction text for doxygen: */ /*! \mainpage Developer documentation * * \section intro_sec Introduction * * This is the developer documentation of Bitcoin ABC * (https://www.bitcoinabc.org/). Bitcoin ABC is a client for the digital * currency called Bitcoin Cash (https://www.bitcoincash.org/), which enables * instant payments to anyone, anywhere in the world. Bitcoin Cash uses * peer-to-peer technology to operate with no central authority: managing * transactions and issuing money are carried out collectively by the network. * * The software is a community-driven open source project, released under the * MIT license. * * \section Navigation * Use the buttons Namespaces, Classes or * Files at the top of the page to start navigating the code. */ static void WaitForShutdown(NodeContext &node) { while (!ShutdownRequested()) { - MilliSleep(200); + UninterruptibleSleep(std::chrono::milliseconds{200}); } Interrupt(node); } ////////////////////////////////////////////////////////////////////////////// // // Start // static bool AppInit(int argc, char *argv[]) { // FIXME: Ideally, we'd like to build the config here, but that's currently // not possible as the whole application has too many global state. However, // this is a first step. auto &config = const_cast(GetConfig()); RPCServer rpcServer; HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer); NodeContext node; node.chain = interfaces::MakeChain(node); bool fRet = false; util::ThreadSetInternalName("init"); // // Parameters // // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's // main() SetupServerArgs(); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { return InitError( strprintf("Error parsing command line arguments: %s\n", error)); } // Process help and version before taking care about datadir if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { std::string strUsage = PACKAGE_NAME " Daemon version " + FormatFullVersion() + "\n"; if (gArgs.IsArgSet("-version")) { strUsage += FormatParagraph(LicenseInfo()); } else { strUsage += "\nUsage: bitcoind [options] " "Start " PACKAGE_NAME " Daemon\n"; strUsage += "\n" + gArgs.GetHelpMessage(); } tfm::format(std::cout, "%s", strUsage); return true; } try { if (!CheckDataDirOption()) { return InitError( strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""))); } if (!gArgs.ReadConfigFiles(error, true)) { return InitError( strprintf("Error reading configuration file: %s\n", error)); } // Check for -chain, -testnet or -regtest parameter (Params() calls are // only valid after this clause) try { SelectParams(gArgs.GetChainName()); } catch (const std::exception &e) { return InitError(strprintf("%s\n", e.what())); } // Make sure we create the net-specific data directory early on: if it // is new, this has a side effect of also creating // //wallets/. // // TODO: this should be removed once GetDataDir() no longer creates the // wallets/ subdirectory. // See more info at: // https://reviews.bitcoinabc.org/D3312 GetDataDir(true); // Error out when loose non-argument tokens are encountered on command // line for (int i = 1; i < argc; i++) { if (!IsSwitchChar(argv[i][0])) { return InitError( strprintf("Command line contains unexpected token '%s', " "see bitcoind -h for a list of options.\n", argv[i])); } } // -server defaults to true for bitcoind but not for the GUI so do this // here gArgs.SoftSetBoolArg("-server", true); // Set this early so that parameter interactions go to console InitLogging(); InitParameterInteraction(); if (!AppInitBasicSetup()) { // InitError will have been called with detailed error, which ends // up on console return false; } if (!AppInitParameterInteraction(config)) { // InitError will have been called with detailed error, which ends // up on console return false; } if (!AppInitSanityChecks()) { // InitError will have been called with detailed error, which ends // up on console return false; } if (gArgs.GetBoolArg("-daemon", false)) { #if HAVE_DECL_DAEMON #if defined(MAC_OSX) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif tfm::format(std::cout, PACKAGE_NAME " daemon starting\n"); // Daemonize if (daemon(1, 0)) { // don't chdir (1), do close FDs (0) return InitError( strprintf("daemon() failed: %s\n", strerror(errno))); } #if defined(MAC_OSX) #pragma GCC diagnostic pop #endif #else return InitError( "-daemon is not supported on this operating system\n"); #endif // HAVE_DECL_DAEMON } // Lock data directory after daemonization if (!AppInitLockDataDirectory()) { // If locking the data directory failed, exit immediately return false; } fRet = AppInitMain(config, rpcServer, httpRPCRequestProcessor, node); } catch (const std::exception &e) { PrintExceptionContinue(&e, "AppInit()"); } catch (...) { PrintExceptionContinue(nullptr, "AppInit()"); } if (!fRet) { Interrupt(node); } else { WaitForShutdown(node); } Shutdown(node); return fRet; } int main(int argc, char *argv[]) { #ifdef WIN32 util::WinCmdLineArgs winArgs; std::tie(argc, argv) = winArgs.get(); #endif SetupEnvironment(); // Connect bitcoind signal handlers noui_connect(); return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 82b45f572..bd5d7d51f 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -1,492 +1,493 @@ // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include // boost::trim #include #include #include #include #include #include #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: explicit HTTPRPCTimerInterface(struct event_base *_base) : base(_base) {} const char *Name() override { return "HTTP"; } RPCTimerBase *NewTimer(std::function &func, int64_t millis) override { return new HTTPRPCTimer(base, func, millis); } private: struct event_base *base; }; /* Pre-base64-encoded authentication token */ static std::string strRPCUserColonPass; /* Pre-base64-encoded authentication token */ static std::string strRPCCORSDomain; /* Stored RPC timer interface (for unregistration) */ static std::unique_ptr httpRPCTimerInterface; /* RPC Auth Whitelist */ static std::map> g_rpc_whitelist; static bool g_rpc_whitelist_default = false; 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(const std::string &strAuth, std::string &strAuthUsernameOut) { // Belt-and-suspenders measure if InitRPCAuthentication was not called. if (strRPCUserColonPass.empty()) { return false; } if (strAuth.substr(0, 6) != "Basic ") { return false; } std::string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); std::string strUserPass = DecodeBase64(strUserPass64); if (strUserPass.find(':') != std::string::npos) { strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':')); } // Check if authorized under single-user field if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) { return true; } return multiUserAuthorized(strUserPass); } static bool checkCORS(HTTPRequest *req) { // https://www.w3.org/TR/cors/#resource-requests // 1. If the Origin header is not present terminate this set of steps. // The request is outside the scope of this specification. std::pair origin = req->GetHeader("origin"); if (!origin.first) { return false; } // 2. If the value of the Origin header is not a case-sensitive match for // any of the values in list of origins do not set any additional headers // and terminate this set of steps. // Note: Always matching is acceptable since the list of origins can be // unbounded. if (origin.second != strRPCCORSDomain) { return false; } if (req->GetRequestMethod() == HTTPRequest::OPTIONS) { // 6.2 Preflight Request // In response to a preflight request the resource indicates which // methods and headers (other than simple methods and simple // headers) it is willing to handle and whether it supports // credentials. // Resources must use the following set of steps to determine which // additional headers to use in the response: // 3. Let method be the value as result of parsing the // Access-Control-Request-Method header. // If there is no Access-Control-Request-Method header or if parsing // failed, do not set any additional headers and terminate this set // of steps. The request is outside the scope of this specification. std::pair method = req->GetHeader("access-control-request-method"); if (!method.first) { return false; } // 4. Let header field-names be the values as result of parsing // the Access-Control-Request-Headers headers. // If there are no Access-Control-Request-Headers headers let header // field-names be the empty list. // If parsing failed do not set any additional headers and terminate // this set of steps. The request is outside the scope of this // specification. std::pair header_field_names = req->GetHeader("access-control-request-headers"); // 5. If method is not a case-sensitive match for any of the // values in list of methods do not set any additional headers // and terminate this set of steps. // Note: Always matching is acceptable since the list of methods // can be unbounded. if (method.second != "POST") { return false; } // 6. If any of the header field-names is not a ASCII case- // insensitive match for any of the values in list of headers do not // set any additional headers and terminate this set of steps. // Note: Always matching is acceptable since the list of headers can // be unbounded. const std::string &list_of_headers = "authorization,content-type"; // 7. If the resource supports credentials add a single // Access-Control-Allow-Origin header, with the value of the Origin // header as value, and add a single // Access-Control-Allow-Credentials header with the case-sensitive // string "true" as value. req->WriteHeader("Access-Control-Allow-Origin", origin.second); req->WriteHeader("Access-Control-Allow-Credentials", "true"); // 8. Optionally add a single Access-Control-Max-Age header with as // value the amount of seconds the user agent is allowed to cache // the result of the request. // 9. If method is a simple method this step may be skipped. // Add one or more Access-Control-Allow-Methods headers consisting // of (a subset of) the list of methods. // If a method is a simple method it does not need to be listed, but // this is not prohibited. // Note: Since the list of methods can be unbounded, simply // returning the method indicated by // Access-Control-Request-Method (if supported) can be enough. req->WriteHeader("Access-Control-Allow-Methods", method.second); // 10. If each of the header field-names is a simple header and none // is Content-Type, this step may be skipped. // Add one or more Access-Control-Allow-Headers headers consisting // of (a subset of) the list of headers. req->WriteHeader("Access-Control-Allow-Headers", header_field_names.first ? header_field_names.second : list_of_headers); req->WriteReply(HTTP_OK); return true; } // 6.1 Simple Cross-Origin Request, Actual Request, and Redirects // In response to a simple cross-origin request or actual request the // resource indicates whether or not to share the response. // If the resource has been relocated, it indicates whether to share its // new URL. // Resources must use the following set of steps to determine which // additional headers to use in the response: // 3. If the resource supports credentials add a single // Access-Control-Allow-Origin header, with the value of the Origin // header as value, and add a single Access-Control-Allow-Credentials // header with the case-sensitive string "true" as value. req->WriteHeader("Access-Control-Allow-Origin", origin.second); req->WriteHeader("Access-Control-Allow-Credentials", "true"); // 4. If the list of exposed headers is not empty add one or more // Access-Control-Expose-Headers headers, with as values the header // field names given in the list of exposed headers. req->WriteHeader("Access-Control-Expose-Headers", "WWW-Authenticate"); return false; } bool HTTPRPCRequestProcessor::ProcessHTTPRequest(HTTPRequest *req) { // First, check and/or set CORS headers if (checkCORS(req)) { return true; } // JSONRPC handles only POST if (req->GetRequestMethod() != HTTPRequest::POST) { req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests"); return false; } // Check authorization std::pair authHeader = req->GetHeader("authorization"); if (!authHeader.first) { req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA); req->WriteReply(HTTP_UNAUTHORIZED); return false; } JSONRPCRequest jreq; jreq.peerAddr = req->GetPeer().ToString(); if (!RPCAuthorized(authHeader.second, jreq.authUser)) { LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr); /** * 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); + UninterruptibleSleep( + std::chrono::milliseconds{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; bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser); if (!user_has_whitelist && g_rpc_whitelist_default) { LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser); req->WriteReply(HTTP_FORBIDDEN); return false; // singleton request } else if (valRequest.isObject()) { jreq.parse(valRequest); if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) { LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod); req->WriteReply(HTTP_FORBIDDEN); return false; } UniValue result = rpcServer.ExecuteCommand(config, jreq); // Send reply strReply = JSONRPCReply(result, NullUniValue, jreq.id); // array of requests } else if (valRequest.isArray()) { if (user_has_whitelist) { for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) { if (!valRequest[reqIdx].isObject()) { throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); } else { const UniValue &request = valRequest[reqIdx].get_obj(); // Parse method std::string strMethod = find_value(request, "method").get_str(); if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) { LogPrintf( "RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod); req->WriteReply(HTTP_FORBIDDEN); return false; } } } } 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() { if (gArgs.GetArg("-rpcpassword", "") == "") { LogPrintf("No rpcpassword set - using random cookie authentication.\n"); if (!GenerateAuthCookie(&strRPCUserColonPass)) { // Same message as AbortNode. uiInterface.ThreadSafeMessageBox( _("Error: A fatal internal error occurred, see debug.log for " "details") .translated, "", CClientUIInterface::MSG_ERROR); return false; } } else { LogPrintf("Config options rpcuser and rpcpassword will soon be " "deprecated. Locally-run instances may remove rpcuser to use " "cookie-based auth, or may be replaced with rpcauth. Please " "see share/rpcauth for rpcauth auth generation.\n"); strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); } strRPCCORSDomain = gArgs.GetArg("-rpccorsdomain", ""); if (gArgs.GetArg("-rpcauth", "") != "") { LogPrintf("Using rpcauth authentication.\n"); } g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist")); for (const std::string &strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) { auto pos = strRPCWhitelist.find(':'); std::string strUser = strRPCWhitelist.substr(0, pos); bool intersect = g_rpc_whitelist.count(strUser); std::set &whitelist = g_rpc_whitelist[strUser]; if (pos != std::string::npos) { std::string strWhitelist = strRPCWhitelist.substr(pos + 1); std::set new_whitelist; boost::split(new_whitelist, strWhitelist, boost::is_any_of(", ")); if (intersect) { std::set tmp_whitelist; std::set_intersection( new_whitelist.begin(), new_whitelist.end(), whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end())); new_whitelist = std::move(tmp_whitelist); } whitelist = std::move(new_whitelist); } } return true; } bool StartHTTPRPC(HTTPRPCRequestProcessor &httpRPCRequestProcessor) { LogPrint(BCLog::RPC, "Starting HTTP RPC server\n"); if (!InitRPCAuthentication()) { return false; } const std::function &rpcFunction = std::bind(&HTTPRPCRequestProcessor::DelegateHTTPRequest, &httpRPCRequestProcessor, std::placeholders::_2); RegisterHTTPHandler("/", true, rpcFunction); if (g_wallet_init_interface.HasWalletSupport()) { RegisterHTTPHandler("/wallet/", false, rpcFunction); } struct event_base *eventBase = EventBase(); assert(eventBase); httpRPCTimerInterface = std::make_unique(eventBase); RPCSetTimerInterface(httpRPCTimerInterface.get()); 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 (g_wallet_init_interface.HasWalletSupport()) { UnregisterHTTPHandler("/wallet/", false); } if (httpRPCTimerInterface) { RPCUnsetTimerInterface(httpRPCTimerInterface.get()); httpRPCTimerInterface.reset(); } } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index c66141b9a..2c66e81d0 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -1,536 +1,537 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2018 The Bitcoin Core developers // Copyright (c) 2018-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for unique_ptr #include #include static RecursiveMutex cs_rpcWarmup; static std::atomic g_rpc_running{false}; static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true; static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started"; /* Timer-creating functions */ static RPCTimerInterface *timerInterface = nullptr; /* Map of name to timer. */ static std::map> deadlineTimers; static bool ExecuteCommand(Config &config, const CRPCCommand &command, const JSONRPCRequest &request, UniValue &result, bool last_handler); struct RPCCommandExecutionInfo { std::string method; int64_t start; }; struct RPCServerInfo { Mutex mutex; std::list active_commands GUARDED_BY(mutex); }; static RPCServerInfo g_rpc_server_info; struct RPCCommandExecution { std::list::iterator it; explicit RPCCommandExecution(const std::string &method) { LOCK(g_rpc_server_info.mutex); it = g_rpc_server_info.active_commands.insert( g_rpc_server_info.active_commands.cend(), {method, GetTimeMicros()}); } ~RPCCommandExecution() { LOCK(g_rpc_server_info.mutex); g_rpc_server_info.active_commands.erase(it); } }; UniValue RPCServer::ExecuteCommand(Config &config, const JSONRPCRequest &request) const { // Return immediately if in warmup // This is retained from the old RPC implementation because a lot of state // is set during warmup that RPC commands may depend on. This can be // safely removed once global variable usage has been eliminated. { LOCK(cs_rpcWarmup); if (fRPCInWarmup) { throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); } } std::string commandName = request.strMethod; { auto commandsReadView = commands.getReadView(); auto iter = commandsReadView->find(commandName); if (iter != commandsReadView.end()) { return iter->second.get()->Execute(request); } } // TODO Remove the below call to tableRPC.execute() and only call it for // context-free RPC commands via an implementation of RPCCommand. // Check if context-free RPC method is valid and execute it return tableRPC.execute(config, request); } void RPCServer::RegisterCommand(std::unique_ptr command) { if (command != nullptr) { const std::string &commandName = command->GetName(); commands.getWriteView()->insert( std::make_pair(commandName, std::move(command))); } } static struct CRPCSignals { boost::signals2::signal Started; boost::signals2::signal Stopped; } g_rpcSignals; void RPCServerSignals::OnStarted(std::function slot) { g_rpcSignals.Started.connect(slot); } void RPCServerSignals::OnStopped(std::function slot) { g_rpcSignals.Stopped.connect(slot); } std::string CRPCTable::help(Config &config, const std::string &strCommand, const JSONRPCRequest &helpreq) const { std::string strRet; std::string category; std::set setDone; std::vector> vCommands; for (const auto &entry : mapCommands) { vCommands.push_back( std::make_pair(entry.second.front()->category + entry.first, entry.second.front())); } sort(vCommands.begin(), vCommands.end()); JSONRPCRequest jreq(helpreq); jreq.fHelp = true; jreq.params = UniValue(); for (const std::pair &command : vCommands) { const CRPCCommand *pcmd = command.second; std::string strMethod = pcmd->name; if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) { continue; } jreq.strMethod = strMethod; try { UniValue unused_result; if (setDone.insert(pcmd->unique_id).second) { pcmd->actor(config, jreq, unused_result, true /* last_handler */); } } catch (const std::exception &e) { // Help text is returned in an exception std::string strHelp = std::string(e.what()); if (strCommand == "") { if (strHelp.find('\n') != std::string::npos) { strHelp = strHelp.substr(0, strHelp.find('\n')); } if (category != pcmd->category) { if (!category.empty()) { strRet += "\n"; } category = pcmd->category; strRet += "== " + Capitalize(category) + " ==\n"; } } strRet += strHelp + "\n"; } } if (strRet == "") { strRet = strprintf("help: unknown command: %s\n", strCommand); } strRet = strRet.substr(0, strRet.size() - 1); return strRet; } static UniValue help(Config &config, const JSONRPCRequest &jsonRequest) { if (jsonRequest.fHelp || jsonRequest.params.size() > 1) { throw std::runtime_error(RPCHelpMan{ "help", "\nList all commands, or get help for a specified command.\n", { {"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"}, }, RPCResult{"\"text\" (string) The help text\n"}, RPCExamples{""}, } .ToString()); } std::string strCommand; if (jsonRequest.params.size() > 0) { strCommand = jsonRequest.params[0].get_str(); } return tableRPC.help(config, strCommand, jsonRequest); } static UniValue stop(const Config &config, const JSONRPCRequest &jsonRequest) { // Accept the deprecated and ignored 'detach' boolean argument // Also accept the hidden 'wait' integer argument (milliseconds) // For instance, 'stop 1000' makes the call wait 1 second before returning // to the client (intended for testing) if (jsonRequest.fHelp || jsonRequest.params.size() > 1) { throw std::runtime_error(RPCHelpMan{ "stop", "\nStop Bitcoin server.", {}, RPCResults{}, RPCExamples{""}, } .ToString()); } // Event loop will exit after current HTTP requests have been handled, so // this reply will get back to the client. StartShutdown(); if (jsonRequest.params[0].isNum()) { - MilliSleep(jsonRequest.params[0].get_int()); + UninterruptibleSleep( + std::chrono::milliseconds{jsonRequest.params[0].get_int()}); } return "Bitcoin server stopping"; } static UniValue uptime(const Config &config, const JSONRPCRequest &jsonRequest) { if (jsonRequest.fHelp || jsonRequest.params.size() > 0) { throw std::runtime_error(RPCHelpMan{ "uptime", "\nReturns the total uptime of the server.\n", {}, RPCResult{"ttt (numeric) The number of seconds that the " "server has been running\n"}, RPCExamples{HelpExampleCli("uptime", "") + HelpExampleRpc("uptime", "")}, } .ToString()); } return GetTime() - GetStartupTime(); } static UniValue getrpcinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 0) { throw std::runtime_error(RPCHelpMan{ "getrpcinfo", "\nReturns details of the RPC server.\n", {}, RPCResults{}, RPCExamples{""}, } .ToString()); } LOCK(g_rpc_server_info.mutex); UniValue active_commands(UniValue::VARR); for (const RPCCommandExecutionInfo &info : g_rpc_server_info.active_commands) { UniValue entry(UniValue::VOBJ); entry.pushKV("method", info.method); entry.pushKV("duration", GetTimeMicros() - info.start); active_commands.push_back(entry); } UniValue result(UniValue::VOBJ); result.pushKV("active_commands", active_commands); return result; } // clang-format off static const CRPCCommand vRPCCommands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- /* Overall control/query calls */ { "control", "getrpcinfo", getrpcinfo, {} }, { "control", "help", help, {"command"} }, { "control", "stop", stop, {"wait"} }, { "control", "uptime", uptime, {} }, }; // clang-format on CRPCTable::CRPCTable() { unsigned int vcidx; for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) { const CRPCCommand *pcmd; pcmd = &vRPCCommands[vcidx]; mapCommands[pcmd->name].push_back(pcmd); } } bool CRPCTable::appendCommand(const std::string &name, const CRPCCommand *pcmd) { if (IsRPCRunning()) { return false; } mapCommands[name].push_back(pcmd); return true; } bool CRPCTable::removeCommand(const std::string &name, const CRPCCommand *pcmd) { auto it = mapCommands.find(name); if (it != mapCommands.end()) { auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd); if (it->second.end() != new_end) { it->second.erase(new_end, it->second.end()); return true; } } return false; } void StartRPC() { LogPrint(BCLog::RPC, "Starting RPC\n"); g_rpc_running = true; g_rpcSignals.Started(); } void InterruptRPC() { LogPrint(BCLog::RPC, "Interrupting RPC\n"); // Interrupt e.g. running longpolls g_rpc_running = false; } void StopRPC() { LogPrint(BCLog::RPC, "Stopping RPC\n"); deadlineTimers.clear(); DeleteAuthCookie(); g_rpcSignals.Stopped(); } bool IsRPCRunning() { return g_rpc_running; } void SetRPCWarmupStatus(const std::string &newStatus) { LOCK(cs_rpcWarmup); rpcWarmupStatus = newStatus; } void SetRPCWarmupFinished() { LOCK(cs_rpcWarmup); assert(fRPCInWarmup); fRPCInWarmup = false; } bool RPCIsInWarmup(std::string *outStatus) { LOCK(cs_rpcWarmup); if (outStatus) { *outStatus = rpcWarmupStatus; } return fRPCInWarmup; } bool IsDeprecatedRPCEnabled(const ArgsManager &args, const std::string &method) { const std::vector enabled_methods = args.GetArgs("-deprecatedrpc"); return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end(); } static UniValue JSONRPCExecOne(Config &config, RPCServer &rpcServer, JSONRPCRequest jreq, const UniValue &req) { UniValue rpc_result(UniValue::VOBJ); try { jreq.parse(req); UniValue result = rpcServer.ExecuteCommand(config, jreq); rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); } catch (const UniValue &objError) { rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); } catch (const std::exception &e) { rpc_result = JSONRPCReplyObj( NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); } return rpc_result; } std::string JSONRPCExecBatch(Config &config, RPCServer &rpcServer, const JSONRPCRequest &jreq, const UniValue &vReq) { UniValue ret(UniValue::VARR); for (size_t i = 0; i < vReq.size(); i++) { ret.push_back(JSONRPCExecOne(config, rpcServer, jreq, vReq[i])); } return ret.write() + "\n"; } /** * Process named arguments into a vector of positional arguments, based on the * passed-in specification for the RPC call's arguments. */ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest &in, const std::vector &argNames) { JSONRPCRequest out = in; out.params = UniValue(UniValue::VARR); // Build a map of parameters, and remove ones that have been processed, so // that we can throw a focused error if there is an unknown one. const std::vector &keys = in.params.getKeys(); const std::vector &values = in.params.getValues(); std::unordered_map argsIn; for (size_t i = 0; i < keys.size(); ++i) { argsIn[keys[i]] = &values[i]; } // Process expected parameters. int hole = 0; for (const std::string &argNamePattern : argNames) { std::vector vargNames; boost::algorithm::split(vargNames, argNamePattern, boost::algorithm::is_any_of("|")); auto fr = argsIn.end(); for (const std::string &argName : vargNames) { fr = argsIn.find(argName); if (fr != argsIn.end()) { break; } } if (fr != argsIn.end()) { for (int i = 0; i < hole; ++i) { // Fill hole between specified parameters with JSON nulls, but // not at the end (for backwards compatibility with calls that // act based on number of specified parameters). out.params.push_back(UniValue()); } hole = 0; out.params.push_back(*fr->second); argsIn.erase(fr); } else { hole += 1; } } // If there are still arguments in the argsIn map, this is an error. if (!argsIn.empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first); } // Return request with named arguments transformed to positional arguments return out; } UniValue CRPCTable::execute(Config &config, const JSONRPCRequest &request) const { // Return immediately if in warmup { LOCK(cs_rpcWarmup); if (fRPCInWarmup) { throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); } } // Find method auto it = mapCommands.find(request.strMethod); if (it != mapCommands.end()) { UniValue result; for (const auto &command : it->second) { if (ExecuteCommand(config, *command, request, result, &command == &it->second.back())) { return result; } } } throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); } static bool ExecuteCommand(Config &config, const CRPCCommand &command, const JSONRPCRequest &request, UniValue &result, bool last_handler) { try { RPCCommandExecution execution(request.strMethod); // Execute, convert arguments to array if necessary if (request.params.isObject()) { return command.actor( config, transformNamedArguments(request, command.argNames), result, last_handler); } else { return command.actor(config, request, result, last_handler); } } catch (const std::exception &e) { throw JSONRPCError(RPC_MISC_ERROR, e.what()); } } std::vector CRPCTable::listCommands() const { std::vector commandList; for (const auto &i : mapCommands) { commandList.emplace_back(i.first); } return commandList; } void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) { if (!timerInterface) { timerInterface = iface; } } void RPCSetTimerInterface(RPCTimerInterface *iface) { timerInterface = iface; } void RPCUnsetTimerInterface(RPCTimerInterface *iface) { if (timerInterface == iface) { timerInterface = nullptr; } } void RPCRunLater(const std::string &name, std::function func, int64_t nSeconds) { if (!timerInterface) { throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); } deadlineTimers.erase(name); LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); deadlineTimers.emplace( name, std::unique_ptr( timerInterface->NewTimer(func, nSeconds * 1000))); } int RPCSerializationFlags() { return 0; } CRPCTable tableRPC; diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index b414a1740..344813f56 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -1,308 +1,308 @@ // Copyright (c) 2017-2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include