diff options
Diffstat (limited to 'src/bitcoin-cli.cpp')
| -rw-r--r-- | src/bitcoin-cli.cpp | 516 |
1 files changed, 387 insertions, 129 deletions
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index ea349b197..a60d3b3b6 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -1,42 +1,54 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2013 The Bitcoin developers -// Distributed under the MIT/X11 software license, see the accompanying +// Copyright (c) 2009-2017 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 "chainparamsbase.h" -#include "clientversion.h" -#include "rpcclient.h" -#include "rpcprotocol.h" -#include "util.h" -#include "utilstrencodings.h" +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif -#include <boost/filesystem/operations.hpp> +#include <chainparamsbase.h> +#include <clientversion.h> +#include <fs.h> +#include <rpc/client.h> +#include <rpc/protocol.h> +#include <util.h> +#include <utilstrencodings.h> -#define _(x) std::string(x) /* Keep the _() around in case gettext or such will be used later to translate non-UI */ +#include <stdio.h> -using namespace std; -using namespace boost; -using namespace boost::asio; -using namespace json_spirit; +#include <event2/buffer.h> +#include <event2/keyvalq_struct.h> +#include <support/events.h> + +#include <univalue.h> + +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; std::string HelpMessageCli() { - string strUsage; - strUsage += _("Options:") + "\n"; - strUsage += " -? " + _("This help message") + "\n"; - strUsage += " -conf=<file> " + strprintf(_("Specify configuration file (default: %s)"), "bitcoin.conf") + "\n"; - strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; - strUsage += " -testnet " + _("Use the test network") + "\n"; - strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " - "solved instantly. This is intended for regression testing tools and app development.") + "\n"; - strUsage += " -rpcconnect=<ip> " + strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1") + "\n"; - strUsage += " -rpcport=<port> " + strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), 8332, 18332) + "\n"; - strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n"; - strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; - strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; - - strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; - strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; + const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); + const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); + std::string strUsage; + strUsage += HelpMessageGroup(_("Options:")); + strUsage += HelpMessageOpt("-?", _("This help message")); + strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); + strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); + strUsage += HelpMessageOpt("-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)")); + AppendParamsHelpMessages(strUsage); + strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED)); + strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); + strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT)); + strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections")); + strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); + strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); + strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); + strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in bitcoind directory, required if bitcoind/-Qt runs with multiple wallets)")); + strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.")); + strUsage += HelpMessageOpt("-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."))); return strUsage; } @@ -60,17 +72,22 @@ public: }; -static bool AppInitRPC(int argc, char* argv[]) +// +// 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 // - ParseParameters(argc, argv); - if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) { - std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n"; - if (!mapArgs.count("-version")) { + gArgs.ParseParameters(argc, argv); + if (argc<2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) { + std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n"; + if (!gArgs.IsArgSet("-version")) { strUsage += "\n" + _("Usage:") + "\n" + - " bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin Core") + "\n" + + " bitcoin-cli [options] <command> [params] " + strprintf(_("Send command to %s"), _(PACKAGE_NAME)) + "\n" + + " bitcoin-cli [options] -named <command> [name=value] ... " + strprintf(_("Send command to %s (with named arguments)"), _(PACKAGE_NAME)) + "\n" + " bitcoin-cli [options] help " + _("List commands") + "\n" + " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n"; @@ -78,87 +95,292 @@ static bool AppInitRPC(int argc, char* argv[]) } fprintf(stdout, "%s", strUsage.c_str()); - return false; + if (argc < 2) { + fprintf(stderr, "Error: too few parameters\n"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } - if (!boost::filesystem::is_directory(GetDataDir(false))) { - fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); - return false; + if (!fs::is_directory(GetDataDir(false))) { + fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); + return EXIT_FAILURE; } try { - ReadConfigFile(mapArgs, mapMultiArgs); - } catch(std::exception &e) { + gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); + } catch (const std::exception& e) { fprintf(stderr,"Error reading configuration file: %s\n", e.what()); - return false; + return EXIT_FAILURE; } // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) - if (!SelectBaseParamsFromCommandLine()) { - fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n"); - return false; + try { + SelectBaseParams(ChainNameFromCommandLine()); + } catch (const std::exception& e) { + fprintf(stderr, "Error: %s\n", e.what()); + return EXIT_FAILURE; } - return true; + if (gArgs.GetBoolArg("-rpcssl", false)) + { + fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n"); + return EXIT_FAILURE; + } + return CONTINUE_EXECUTION; } -Object CallRPC(const string& strMethod, const Array& params) + +/** Reply structure for request_done to fill in */ +struct HTTPReply +{ + HTTPReply(): status(0), error(-1) {} + + int status; + int error; + std::string body; +}; + +const char *http_errorstring(int code) { - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( - _("You must set rpcpassword=<password> in the configuration file:\n%s\n" - "If the file does not exist, create it with owner-readable-only file permissions."), - GetConfigFile().string().c_str())); - - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl", false); - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3); - asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context); - SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL); - iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d); - - const bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort()))); - if (!fConnected) - throw CConnectionFailed("couldn't connect to server"); - - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map<string, string> mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; - - // Receive HTTP reply status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - - // Receive HTTP reply message headers and body - map<string, string> mapHeaders; - string strReply; - ReadHTTPMessage(stream, mapHeaders, strReply, nProto, std::numeric_limits<size_t>::max()); - - if (nStatus == HTTP_UNAUTHORIZED) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); - else if (strReply.empty()) - throw runtime_error("no response from server"); + 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<HTTPReply*>(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<HTTPReply*>(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<std::string>& 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<std::string>& 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<UniValue> 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<std::string>& 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<std::string>& 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; + if (gArgs.GetArg("-rpcpassword", "") == "") { + // Try fall back to cookie-based authentication if no password is provided + if (!GetAuthCookie(&strRPCUserColonPass)) { + 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().c_str())); + + } + } 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 = "/"; + std::string walletName = gArgs.GetArg("-rpcwallet", ""); + if (!walletName.empty()) { + 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()); + req.release(); // ownership moved to evcon in above call + if (r != 0) { + throw CConnectionFailed("send http request failed"); + } + + event_base_dispatch(base.get()); + + if (response.status == 0) + throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error)); + else if (response.status == HTTP_UNAUTHORIZED) + throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + 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 - Value valReply; - if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); + 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 runtime_error("expected reply to have result, error and id properties"); + throw std::runtime_error("expected reply to have result, error and id properties"); return reply; } int CommandLineRPC(int argc, char *argv[]) { - string strPrint; + std::string strPrint; int nRet = 0; try { // Skip switches @@ -166,47 +388,78 @@ int CommandLineRPC(int argc, char *argv[]) argc--; argv++; } - - // Method - if (argc < 2) - throw runtime_error("too few parameters"); - string strMethod = argv[1]; - - // Parameters default to strings - std::vector<std::string> strParams(&argv[2], &argv[argc]); - Array params = RPCConvertValues(strMethod, strParams); + 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<std::string> args = std::vector<std::string>(&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<BaseRequestHandler> 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]; + args.erase(args.begin()); // Remove trailing method name from arguments vector + } // Execute and handle connection failures with -rpcwait - const bool fWait = GetBoolArg("-rpcwait", false); + const bool fWait = gArgs.GetBoolArg("-rpcwait", false); do { try { - const Object reply = CallRPC(strMethod, params); + const UniValue reply = CallRPC(rh.get(), method, args); // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); + const UniValue& result = find_value(reply, "result"); + const UniValue& error = find_value(reply, "error"); - if (error.type() != null_type) { + if (!error.isNull()) { // Error - const int code = find_value(error.get_obj(), "code").get_int(); + int code = error["code"].get_int(); if (fWait && code == RPC_IN_WARMUP) throw CConnectionFailed("server in warmup"); - strPrint = "error: " + write_string(error, false); + 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=<filename>\" option to bitcoin-cli command line."; + } + } } else { // Result - if (result.type() == null_type) + if (result.isNull()) strPrint = ""; - else if (result.type() == str_type) + else if (result.isStr()) strPrint = result.get_str(); else - strPrint = write_string(result, true); + strPrint = result.write(2); } - // Connection succeeded, no need to retry. break; } - catch (const CConnectionFailed& e) { + catch (const CConnectionFailed&) { if (fWait) MilliSleep(1000); else @@ -214,15 +467,15 @@ int CommandLineRPC(int argc, char *argv[]) } } while (fWait); } - catch (boost::thread_interrupted) { + catch (const boost::thread_interrupted&) { throw; } - catch (std::exception& e) { - strPrint = string("error: ") + e.what(); + catch (const std::exception& e) { + strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; } catch (...) { - PrintExceptionContinue(NULL, "CommandLineRPC()"); + PrintExceptionContinue(nullptr, "CommandLineRPC()"); throw; } @@ -235,16 +488,21 @@ int CommandLineRPC(int argc, char *argv[]) int main(int argc, char* argv[]) { SetupEnvironment(); + if (!SetupNetworking()) { + fprintf(stderr, "Error: Initializing networking failed\n"); + return EXIT_FAILURE; + } try { - if(!AppInitRPC(argc, argv)) - return EXIT_FAILURE; + int ret = AppInitRPC(argc, argv); + if (ret != CONTINUE_EXECUTION) + return ret; } - catch (std::exception& e) { + catch (const std::exception& e) { PrintExceptionContinue(&e, "AppInitRPC()"); return EXIT_FAILURE; } catch (...) { - PrintExceptionContinue(NULL, "AppInitRPC()"); + PrintExceptionContinue(nullptr, "AppInitRPC()"); return EXIT_FAILURE; } @@ -252,10 +510,10 @@ int main(int argc, char* argv[]) try { ret = CommandLineRPC(argc, argv); } - catch (std::exception& e) { + catch (const std::exception& e) { PrintExceptionContinue(&e, "CommandLineRPC()"); } catch (...) { - PrintExceptionContinue(NULL, "CommandLineRPC()"); + PrintExceptionContinue(nullptr, "CommandLineRPC()"); } return ret; } |