LCOV - code coverage report
Current view: top level - src - bitcoin-cli.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 328 542 60.5 %
Date: 2020-09-26 01:30:44 Functions: 47 74 63.5 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2020 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #if defined(HAVE_CONFIG_H)
       7             : #include <config/bitcoin-config.h>
       8             : #endif
       9             : 
      10             : #include <chainparamsbase.h>
      11             : #include <clientversion.h>
      12             : #include <optional.h>
      13             : #include <rpc/client.h>
      14             : #include <rpc/mining.h>
      15             : #include <rpc/protocol.h>
      16             : #include <rpc/request.h>
      17             : #include <util/strencodings.h>
      18             : #include <util/system.h>
      19             : #include <util/translation.h>
      20             : #include <util/url.h>
      21             : 
      22             : #include <functional>
      23             : #include <memory>
      24             : #include <stdio.h>
      25             : #include <string>
      26             : #include <tuple>
      27             : 
      28             : #include <event2/buffer.h>
      29             : #include <event2/keyvalq_struct.h>
      30             : #include <support/events.h>
      31             : 
      32             : #include <univalue.h>
      33             : #include <compat/stdin.h>
      34             : 
      35         419 : const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
      36             : UrlDecodeFn* const URL_DECODE = urlDecode;
      37             : 
      38             : static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
      39             : static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
      40             : static const bool DEFAULT_NAMED=false;
      41             : static const int CONTINUE_EXECUTION=-1;
      42         419 : static const std::string ONION{".onion"};
      43         419 : static const size_t ONION_LEN{ONION.size()};
      44             : 
      45             : /** Default number of blocks to generate for RPC generatetoaddress. */
      46         419 : static const std::string DEFAULT_NBLOCKS = "1";
      47             : 
      48         419 : static void SetupCliArgs(ArgsManager& argsman)
      49             : {
      50         419 :     SetupHelpOptions(argsman);
      51             : 
      52         419 :     const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
      53         419 :     const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
      54         419 :     const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST);
      55             : 
      56         419 :     argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      57         419 :     argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      58         419 :     argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      59         419 :     argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC generatenewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      60         419 :     argsman.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);
      61         419 :     argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0).", ArgsManager::ALLOW_INT, OptionsCategory::OPTIONS);
      62             : 
      63         419 :     SetupChainParamsBaseOptions(argsman);
      64         419 :     argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      65         419 :     argsman.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      66         419 :     argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      67         419 :     argsman.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      68         419 :     argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      69         419 :     argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
      70         419 :     argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      71         419 :     argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      72         419 :     argsman.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind). This changes the RPC endpoint used, e.g. http://127.0.0.1:8332/wallet/<walletname>", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      73         419 :     argsman.AddArg("-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.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      74         419 :     argsman.AddArg("-stdinrpcpass", "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. When combined with -stdinwalletpassphrase, -stdinrpcpass consumes the first line, and -stdinwalletpassphrase consumes the second.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      75         419 :     argsman.AddArg("-stdinwalletpassphrase", "Read wallet passphrase from standard input as a single line. When combined with -stdin, the first line from standard input is used for the wallet passphrase.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
      76         419 : }
      77             : 
      78             : /** libevent event log callback */
      79           0 : static void libevent_log_cb(int severity, const char *msg)
      80             : {
      81             : #ifndef EVENT_LOG_ERR // EVENT_LOG_ERR was added in 2.0.19; but before then _EVENT_LOG_ERR existed.
      82             : # define EVENT_LOG_ERR _EVENT_LOG_ERR
      83             : #endif
      84             :     // Ignore everything other than errors
      85           0 :     if (severity >= EVENT_LOG_ERR) {
      86           0 :         throw std::runtime_error(strprintf("libevent error: %s", msg));
      87             :     }
      88           0 : }
      89             : 
      90             : //////////////////////////////////////////////////////////////////////////////
      91             : //
      92             : // Start
      93             : //
      94             : 
      95             : //
      96             : // Exception thrown on connection error.  This error is used to determine
      97             : // when to wait if -rpcwait is given.
      98             : //
      99           6 : class CConnectionFailed : public std::runtime_error
     100             : {
     101             : public:
     102             : 
     103           6 :     explicit inline CConnectionFailed(const std::string& msg) :
     104           3 :         std::runtime_error(msg)
     105           6 :     {}
     106             : 
     107             : };
     108             : 
     109             : //
     110             : // This function returns either one of EXIT_ codes when it's expected to stop the process or
     111             : // CONTINUE_EXECUTION when it's expected to continue further.
     112             : //
     113         419 : static int AppInitRPC(int argc, char* argv[])
     114             : {
     115             :     //
     116             :     // Parameters
     117             :     //
     118         419 :     SetupCliArgs(gArgs);
     119         419 :     std::string error;
     120         419 :     if (!gArgs.ParseParameters(argc, argv, error)) {
     121           0 :         tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error);
     122           0 :         return EXIT_FAILURE;
     123             :     }
     124         419 :     if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
     125           1 :         std::string strUsage = PACKAGE_NAME " RPC client version " + FormatFullVersion() + "\n";
     126           1 :         if (!gArgs.IsArgSet("-version")) {
     127           0 :             strUsage += "\n"
     128             :                 "Usage:  bitcoin-cli [options] <command> [params]  Send command to " PACKAGE_NAME "\n"
     129             :                 "or:     bitcoin-cli [options] -named <command> [name=value]...  Send command to " PACKAGE_NAME " (with named arguments)\n"
     130             :                 "or:     bitcoin-cli [options] help                List commands\n"
     131             :                 "or:     bitcoin-cli [options] help <command>      Get help for a command\n";
     132           0 :             strUsage += "\n" + gArgs.GetHelpMessage();
     133           0 :         }
     134             : 
     135           1 :         tfm::format(std::cout, "%s", strUsage);
     136           1 :         if (argc < 2) {
     137           0 :             tfm::format(std::cerr, "Error: too few parameters\n");
     138           0 :             return EXIT_FAILURE;
     139             :         }
     140           1 :         return EXIT_SUCCESS;
     141           1 :     }
     142         418 :     if (!CheckDataDirOption()) {
     143           0 :         tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""));
     144           0 :         return EXIT_FAILURE;
     145             :     }
     146         418 :     if (!gArgs.ReadConfigFiles(error, true)) {
     147           0 :         tfm::format(std::cerr, "Error reading configuration file: %s\n", error);
     148           0 :         return EXIT_FAILURE;
     149             :     }
     150             :     // Check for -chain, -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
     151             :     try {
     152         418 :         SelectBaseParams(gArgs.GetChainName());
     153           0 :     } catch (const std::exception& e) {
     154           0 :         tfm::format(std::cerr, "Error: %s\n", e.what());
     155             :         return EXIT_FAILURE;
     156           0 :     }
     157         418 :     return CONTINUE_EXECUTION;
     158         419 : }
     159             : 
     160             : 
     161             : /** Reply structure for request_done to fill in */
     162         876 : struct HTTPReply
     163             : {
     164         876 :     HTTPReply(): status(0), error(-1) {}
     165             : 
     166             :     int status;
     167             :     int error;
     168             :     std::string body;
     169             : };
     170             : 
     171           0 : static std::string http_errorstring(int code)
     172             : {
     173           0 :     switch(code) {
     174             : #if LIBEVENT_VERSION_NUMBER >= 0x02010300
     175             :     case EVREQ_HTTP_TIMEOUT:
     176           0 :         return "timeout reached";
     177             :     case EVREQ_HTTP_EOF:
     178           0 :         return "EOF reached";
     179             :     case EVREQ_HTTP_INVALID_HEADER:
     180           0 :         return "error while reading header, or invalid header";
     181             :     case EVREQ_HTTP_BUFFER_ERROR:
     182           0 :         return "error encountered while reading or writing";
     183             :     case EVREQ_HTTP_REQUEST_CANCEL:
     184           0 :         return "request was canceled";
     185             :     case EVREQ_HTTP_DATA_TOO_LONG:
     186           0 :         return "response body is larger than allowed";
     187             : #endif
     188             :     default:
     189           0 :         return "unknown";
     190             :     }
     191           0 : }
     192             : 
     193         435 : static void http_request_done(struct evhttp_request *req, void *ctx)
     194             : {
     195         435 :     HTTPReply *reply = static_cast<HTTPReply*>(ctx);
     196             : 
     197         435 :     if (req == nullptr) {
     198             :         /* If req is nullptr, it means an error occurred while connecting: the
     199             :          * error code will have been passed to http_error_cb.
     200             :          */
     201           0 :         reply->status = 0;
     202           0 :         return;
     203             :     }
     204             : 
     205         435 :     reply->status = evhttp_request_get_response_code(req);
     206             : 
     207         435 :     struct evbuffer *buf = evhttp_request_get_input_buffer(req);
     208         435 :     if (buf)
     209             :     {
     210         435 :         size_t size = evbuffer_get_length(buf);
     211         435 :         const char *data = (const char*)evbuffer_pullup(buf, size);
     212         435 :         if (data)
     213         431 :             reply->body = std::string(data, size);
     214         435 :         evbuffer_drain(buf, size);
     215         435 :     }
     216         435 : }
     217             : 
     218             : #if LIBEVENT_VERSION_NUMBER >= 0x02010300
     219           0 : static void http_error_cb(enum evhttp_request_error err, void *ctx)
     220             : {
     221           0 :     HTTPReply *reply = static_cast<HTTPReply*>(ctx);
     222           0 :     reply->error = err;
     223           0 : }
     224             : #endif
     225             : 
     226             : /** Class that handles the conversion from a command-line to a JSON-RPC request,
     227             :  * as well as converting back to a JSON object that can be shown as result.
     228             :  */
     229         431 : class BaseRequestHandler
     230             : {
     231             : public:
     232         431 :     virtual ~BaseRequestHandler() {}
     233             :     virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0;
     234             :     virtual UniValue ProcessReply(const UniValue &batch_in) = 0;
     235             : };
     236             : 
     237             : /** Process getinfo requests */
     238          66 : class GetinfoRequestHandler: public BaseRequestHandler
     239             : {
     240             : public:
     241          11 :     const int ID_NETWORKINFO = 0;
     242          11 :     const int ID_BLOCKCHAININFO = 1;
     243          11 :     const int ID_WALLETINFO = 2;
     244          11 :     const int ID_BALANCES = 3;
     245             : 
     246             :     /** Create a simulated `getinfo` request. */
     247          11 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     248             :     {
     249          11 :         if (!args.empty()) {
     250           1 :             throw std::runtime_error("-getinfo takes no arguments");
     251             :         }
     252          10 :         UniValue result(UniValue::VARR);
     253          10 :         result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
     254          10 :         result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO));
     255          10 :         result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO));
     256          10 :         result.push_back(JSONRPCRequestObj("getbalances", NullUniValue, ID_BALANCES));
     257             :         return result;
     258          10 :     }
     259             : 
     260             :     /** Collect values from the batch and form a simulated `getinfo` reply. */
     261          10 :     UniValue ProcessReply(const UniValue &batch_in) override
     262             :     {
     263          10 :         UniValue result(UniValue::VOBJ);
     264          10 :         const std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in);
     265             :         // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on;
     266             :         // getwalletinfo() and getbalances() are allowed to fail if there is no wallet.
     267          10 :         if (!batch[ID_NETWORKINFO]["error"].isNull()) {
     268           0 :             return batch[ID_NETWORKINFO];
     269             :         }
     270          10 :         if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) {
     271           0 :             return batch[ID_BLOCKCHAININFO];
     272             :         }
     273          10 :         result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]);
     274          10 :         result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]);
     275          10 :         result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]);
     276          10 :         result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]);
     277          10 :         result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]);
     278             : 
     279          10 :         UniValue connections(UniValue::VOBJ);
     280          10 :         connections.pushKV("in", batch[ID_NETWORKINFO]["result"]["connections_in"]);
     281          10 :         connections.pushKV("out", batch[ID_NETWORKINFO]["result"]["connections_out"]);
     282          10 :         connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]);
     283          10 :         result.pushKV("connections", connections);
     284             : 
     285          10 :         result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
     286          10 :         result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
     287          10 :         result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
     288          10 :         if (!batch[ID_WALLETINFO]["result"].isNull()) {
     289           6 :             result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]);
     290           6 :             if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) {
     291           5 :                 result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]);
     292           5 :             }
     293           6 :             result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]);
     294           6 :         }
     295          10 :         if (!batch[ID_BALANCES]["result"].isNull()) {
     296           6 :             result.pushKV("balance", batch[ID_BALANCES]["result"]["mine"]["trusted"]);
     297           6 :         }
     298          10 :         result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]);
     299          10 :         result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]);
     300          10 :         return JSONRPCReplyObj(result, NullUniValue, 1);
     301          10 :     }
     302             : };
     303             : 
     304             : /** Process netinfo requests */
     305           0 : class NetinfoRequestHandler : public BaseRequestHandler
     306             : {
     307             : private:
     308           0 :     bool IsAddrIPv6(const std::string& addr) const
     309             :     {
     310           0 :         return !addr.empty() && addr.front() == '[';
     311             :     }
     312           0 :     bool IsInboundOnion(const std::string& addr_local, int mapped_as) const
     313             :     {
     314           0 :         return mapped_as == 0 && addr_local.find(ONION) != std::string::npos;
     315             :     }
     316           0 :     bool IsOutboundOnion(const std::string& addr, int mapped_as) const
     317             :     {
     318           0 :         const size_t addr_len{addr.size()};
     319           0 :         const size_t onion_pos{addr.rfind(ONION)};
     320           0 :         return mapped_as == 0 && onion_pos != std::string::npos && addr_len > ONION_LEN &&
     321           0 :                (onion_pos == addr_len - ONION_LEN || onion_pos == addr.find_last_of(":") - ONION_LEN);
     322             :     }
     323           0 :     uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level
     324           0 :     bool DetailsRequested() const { return m_details_level > 0 && m_details_level < 5; }
     325           0 :     bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; }
     326           0 :     bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
     327             :     enum struct NetType {
     328             :         ipv4,
     329             :         ipv6,
     330             :         onion,
     331             :     };
     332           0 :     struct Peer {
     333             :         int id;
     334             :         int mapped_as;
     335             :         int version;
     336             :         int64_t conn_time;
     337             :         int64_t last_blck;
     338             :         int64_t last_recv;
     339             :         int64_t last_send;
     340             :         int64_t last_trxn;
     341             :         double min_ping;
     342             :         double ping;
     343             :         std::string addr;
     344             :         std::string sub_version;
     345             :         NetType net_type;
     346             :         bool is_block_relay;
     347             :         bool is_outbound;
     348           0 :         bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
     349             :     };
     350           0 :     std::string NetTypeEnumToString(NetType t)
     351             :     {
     352           0 :         switch (t) {
     353           0 :         case NetType::ipv4: return "ipv4";
     354           0 :         case NetType::ipv6: return "ipv6";
     355           0 :         case NetType::onion: return "onion";
     356             :         } // no default case, so the compiler can warn about missing cases
     357           0 :         assert(false);
     358           0 :     }
     359           0 :     std::string ChainToString() const
     360             :     {
     361           0 :         if (gArgs.GetChainName() == CBaseChainParams::TESTNET) return " testnet";
     362           0 :         if (gArgs.GetChainName() == CBaseChainParams::REGTEST) return " regtest";
     363           0 :         return "";
     364           0 :     }
     365             : public:
     366           0 :     const int ID_PEERINFO = 0;
     367           0 :     const int ID_NETWORKINFO = 1;
     368             : 
     369           0 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     370             :     {
     371           0 :         if (!args.empty()) {
     372           0 :             uint8_t n{0};
     373           0 :             if (ParseUInt8(args.at(0), &n)) {
     374           0 :                 m_details_level = n;
     375           0 :             }
     376           0 :         }
     377           0 :         UniValue result(UniValue::VARR);
     378           0 :         result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO));
     379           0 :         result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
     380             :         return result;
     381           0 :     }
     382             : 
     383           0 :     UniValue ProcessReply(const UniValue& batch_in) override
     384             :     {
     385           0 :         const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
     386           0 :         if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
     387           0 :         if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
     388             : 
     389           0 :         const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
     390           0 :         if (networkinfo["version"].get_int() < 209900) {
     391           0 :             throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
     392             :         }
     393             : 
     394             :         // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
     395           0 :         const int64_t time_now{GetSystemTimeInSeconds()};
     396           0 :         int ipv4_i{0}, ipv6_i{0}, onion_i{0}, block_relay_i{0}, total_i{0}; // inbound conn counters
     397           0 :         int ipv4_o{0}, ipv6_o{0}, onion_o{0}, block_relay_o{0}, total_o{0}; // outbound conn counters
     398           0 :         size_t max_peer_id_length{2}, max_addr_length{0};
     399             :         bool is_asmap_on{false};
     400           0 :         std::vector<Peer> peers;
     401           0 :         const UniValue& getpeerinfo{batch[ID_PEERINFO]["result"]};
     402             : 
     403           0 :         for (const UniValue& peer : getpeerinfo.getValues()) {
     404           0 :             const std::string addr{peer["addr"].get_str()};
     405           0 :             const std::string addr_local{peer["addrlocal"].isNull() ? "" : peer["addrlocal"].get_str()};
     406           0 :             const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].get_int()};
     407           0 :             const bool is_block_relay{!peer["relaytxes"].get_bool()};
     408           0 :             const bool is_inbound{peer["inbound"].get_bool()};
     409             :             NetType net_type{NetType::ipv4};
     410           0 :             if (is_inbound) {
     411           0 :                 if (IsAddrIPv6(addr)) {
     412             :                     net_type = NetType::ipv6;
     413           0 :                     ++ipv6_i;
     414           0 :                 } else if (IsInboundOnion(addr_local, mapped_as)) {
     415             :                     net_type = NetType::onion;
     416           0 :                     ++onion_i;
     417           0 :                 } else {
     418           0 :                     ++ipv4_i;
     419             :                 }
     420           0 :                 if (is_block_relay) ++block_relay_i;
     421             :             } else {
     422           0 :                 if (IsAddrIPv6(addr)) {
     423             :                     net_type = NetType::ipv6;
     424           0 :                     ++ipv6_o;
     425           0 :                 } else if (IsOutboundOnion(addr, mapped_as)) {
     426             :                     net_type = NetType::onion;
     427           0 :                     ++onion_o;
     428           0 :                 } else {
     429           0 :                     ++ipv4_o;
     430             :                 }
     431           0 :                 if (is_block_relay) ++block_relay_o;
     432             :             }
     433           0 :             if (DetailsRequested()) {
     434             :                 // Push data for this peer to the peers vector.
     435           0 :                 const int peer_id{peer["id"].get_int()};
     436           0 :                 const int version{peer["version"].get_int()};
     437           0 :                 const std::string sub_version{peer["subver"].get_str()};
     438           0 :                 const int64_t conn_time{peer["conntime"].get_int64()};
     439           0 :                 const int64_t last_blck{peer["last_block"].get_int64()};
     440           0 :                 const int64_t last_recv{peer["lastrecv"].get_int64()};
     441           0 :                 const int64_t last_send{peer["lastsend"].get_int64()};
     442           0 :                 const int64_t last_trxn{peer["last_transaction"].get_int64()};
     443           0 :                 const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
     444           0 :                 const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
     445           0 :                 peers.push_back({peer_id, mapped_as, version, conn_time, last_blck, last_recv, last_send, last_trxn, min_ping, ping, addr, sub_version, net_type, is_block_relay, !is_inbound});
     446           0 :                 max_peer_id_length = std::max(ToString(peer_id).length(), max_peer_id_length);
     447           0 :                 max_addr_length = std::max(addr.length() + 1, max_addr_length);
     448           0 :                 is_asmap_on |= (mapped_as != 0);
     449           0 :             }
     450           0 :         }
     451             : 
     452             :         // Generate report header.
     453           0 :         std::string result{strprintf("%s %s%s - %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())};
     454             : 
     455             :         // Report detailed peer connections list sorted by direction and minimum ping time.
     456           0 :         if (DetailsRequested() && !peers.empty()) {
     457           0 :             std::sort(peers.begin(), peers.end());
     458           0 :             result += "Peer connections sorted by direction and min ping\n<-> relay   net mping   ping send recv  txn  blk uptime ";
     459           0 :             if (is_asmap_on) result += " asmap ";
     460           0 :             result += strprintf("%*s %-*s%s\n", max_peer_id_length, "id", IsAddressSelected() ? max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
     461           0 :             for (const Peer& peer : peers) {
     462           0 :                 std::string version{ToString(peer.version) + peer.sub_version};
     463           0 :                 result += strprintf(
     464             :                     "%3s %5s %5s%6s%7s%5s%5s%5s%5s%7s%*i %*s %-*s%s\n",
     465           0 :                     peer.is_outbound ? "out" : "in",
     466           0 :                     peer.is_block_relay ? "block" : "full",
     467           0 :                     NetTypeEnumToString(peer.net_type),
     468           0 :                     peer.min_ping == -1 ? "" : ToString(round(1000 * peer.min_ping)),
     469           0 :                     peer.ping == -1 ? "" : ToString(round(1000 * peer.ping)),
     470           0 :                     peer.last_send == 0 ? "" : ToString(time_now - peer.last_send),
     471           0 :                     peer.last_recv == 0 ? "" : ToString(time_now - peer.last_recv),
     472           0 :                     peer.last_trxn == 0 ? "" : ToString((time_now - peer.last_trxn) / 60),
     473           0 :                     peer.last_blck == 0 ? "" : ToString((time_now - peer.last_blck) / 60),
     474           0 :                     peer.conn_time == 0 ? "" : ToString((time_now - peer.conn_time) / 60),
     475           0 :                     is_asmap_on ? 7 : 0, // variable spacing
     476           0 :                     is_asmap_on && peer.mapped_as != 0 ? ToString(peer.mapped_as) : "",
     477             :                     max_peer_id_length, // variable spacing
     478           0 :                     peer.id,
     479           0 :                     IsAddressSelected() ? max_addr_length : 0, // variable spacing
     480           0 :                     IsAddressSelected() ? peer.addr : "",
     481           0 :                     IsVersionSelected() && version != "0" ? version : "");
     482           0 :             }
     483           0 :             result += "                   ms     ms  sec  sec  min  min    min\n\n";
     484             :         }
     485             : 
     486             :         // Report peer connection totals by type.
     487           0 :         total_i = ipv4_i + ipv6_i + onion_i;
     488           0 :         total_o = ipv4_o + ipv6_o + onion_o;
     489           0 :         result += "        ipv4    ipv6   onion   total  block-relay\n";
     490           0 :         result += strprintf("in     %5i   %5i   %5i   %5i   %5i\n", ipv4_i, ipv6_i, onion_i, total_i, block_relay_i);
     491           0 :         result += strprintf("out    %5i   %5i   %5i   %5i   %5i\n", ipv4_o, ipv6_o, onion_o, total_o, block_relay_o);
     492           0 :         result += strprintf("total  %5i   %5i   %5i   %5i   %5i\n", ipv4_i + ipv4_o, ipv6_i + ipv6_o, onion_i + onion_o, total_i + total_o, block_relay_i + block_relay_o);
     493             : 
     494             :         // Report local addresses, ports, and scores.
     495           0 :         result += "\nLocal addresses";
     496           0 :         const UniValue& local_addrs{networkinfo["localaddresses"]};
     497           0 :         if (local_addrs.empty()) {
     498           0 :             result += ": n/a\n";
     499             :         } else {
     500           0 :             for (const UniValue& addr : local_addrs.getValues()) {
     501           0 :                 result += strprintf("\n%-40i  port %5i     score %6i", addr["address"].get_str(), addr["port"].get_int(), addr["score"].get_int());
     502             :             }
     503             :         }
     504             : 
     505           0 :         return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1);
     506           0 :     }
     507             : };
     508             : 
     509             : /** Process RPC generatetoaddress request. */
     510          45 : class GenerateToAddressRequestHandler : public BaseRequestHandler
     511             : {
     512             : public:
     513           9 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     514             :     {
     515           9 :         address_str = args.at(1);
     516           9 :         UniValue params{RPCConvertValues("generatetoaddress", args)};
     517           7 :         return JSONRPCRequestObj("generatetoaddress", params, 1);
     518           9 :     }
     519             : 
     520           7 :     UniValue ProcessReply(const UniValue &reply) override
     521             :     {
     522           7 :         UniValue result(UniValue::VOBJ);
     523           7 :         result.pushKV("address", address_str);
     524           7 :         result.pushKV("blocks", reply.get_obj()["result"]);
     525           7 :         return JSONRPCReplyObj(result, NullUniValue, 1);
     526           7 :     }
     527             : protected:
     528             :     std::string address_str;
     529             : };
     530             : 
     531             : /** Process default single requests */
     532        2030 : class DefaultRequestHandler: public BaseRequestHandler {
     533             : public:
     534         418 :     UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
     535             :     {
     536         418 :         UniValue params;
     537         418 :         if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
     538          35 :             params = RPCConvertNamedValues(method, args);
     539          35 :         } else {
     540         383 :             params = RPCConvertValues(method, args);
     541             :         }
     542         418 :         return JSONRPCRequestObj(method, params, 1);
     543         418 :     }
     544             : 
     545         414 :     UniValue ProcessReply(const UniValue &reply) override
     546             :     {
     547         414 :         return reply.get_obj();
     548             :     }
     549             : };
     550             : 
     551         438 : static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const Optional<std::string>& rpcwallet = {})
     552             : {
     553         438 :     std::string host;
     554             :     // In preference order, we choose the following for the port:
     555             :     //     1. -rpcport
     556             :     //     2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
     557             :     //     3. default port for chain
     558         438 :     int port = BaseParams().RPCPort();
     559         438 :     SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
     560         438 :     port = gArgs.GetArg("-rpcport", port);
     561             : 
     562             :     // Obtain event base
     563         438 :     raii_event_base base = obtain_event_base();
     564             : 
     565             :     // Synchronously look up hostname
     566         438 :     raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
     567             : 
     568             :     // Set connection timeout
     569             :     {
     570         438 :         const int timeout = gArgs.GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
     571         438 :         if (timeout > 0) {
     572         438 :             evhttp_connection_set_timeout(evcon.get(), timeout);
     573             :         } else {
     574             :             // Indefinite request timeouts are not possible in libevent-http, so we
     575             :             // set the timeout to a very long time period instead.
     576             : 
     577             :             constexpr int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar
     578           0 :             evhttp_connection_set_timeout(evcon.get(), 5 * YEAR_IN_SECONDS);
     579           0 :         }
     580           0 :     }
     581             : 
     582         438 :     HTTPReply response;
     583         438 :     raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
     584         438 :     if (req == nullptr)
     585           0 :         throw std::runtime_error("create http request failed");
     586             : #if LIBEVENT_VERSION_NUMBER >= 0x02010300
     587         438 :     evhttp_request_set_error_cb(req.get(), http_error_cb);
     588             : #endif
     589             : 
     590             :     // Get credentials
     591         438 :     std::string strRPCUserColonPass;
     592             :     bool failedToGetAuthCookie = false;
     593         438 :     if (gArgs.GetArg("-rpcpassword", "") == "") {
     594             :         // Try fall back to cookie-based authentication if no password is provided
     595         434 :         if (!GetAuthCookie(&strRPCUserColonPass)) {
     596             :             failedToGetAuthCookie = true;
     597           1 :         }
     598             :     } else {
     599           4 :         strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
     600             :     }
     601             : 
     602         438 :     struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
     603         438 :     assert(output_headers);
     604         438 :     evhttp_add_header(output_headers, "Host", host.c_str());
     605         438 :     evhttp_add_header(output_headers, "Connection", "close");
     606         438 :     evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
     607             : 
     608             :     // Attach request data
     609         441 :     std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";
     610         435 :     struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
     611         435 :     assert(output_buffer);
     612         435 :     evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
     613             : 
     614             :     // check if we should use a special wallet endpoint
     615         435 :     std::string endpoint = "/";
     616         435 :     if (rpcwallet) {
     617         192 :         char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false);
     618         192 :         if (encodedURI) {
     619         192 :             endpoint = "/wallet/" + std::string(encodedURI);
     620         192 :             free(encodedURI);
     621             :         } else {
     622           0 :             throw CConnectionFailed("uri-encode failed");
     623             :         }
     624         192 :     }
     625         435 :     int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str());
     626         435 :     req.release(); // ownership moved to evcon in above call
     627         435 :     if (r != 0) {
     628           0 :         throw CConnectionFailed("send http request failed");
     629             :     }
     630             : 
     631         435 :     event_base_dispatch(base.get());
     632             : 
     633         435 :     if (response.status == 0) {
     634           1 :         std::string responseErrorMessage;
     635           1 :         if (response.error != -1) {
     636           0 :             responseErrorMessage = strprintf(" (error code %d - \"%s\")", response.error, http_errorstring(response.error));
     637           0 :         }
     638           1 :         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));
     639         435 :     } else if (response.status == HTTP_UNAUTHORIZED) {
     640           3 :         if (failedToGetAuthCookie) {
     641           2 :             throw std::runtime_error(strprintf(
     642             :                 "Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set.  See -rpcpassword and -stdinrpcpass.  Configuration file: (%s)",
     643           1 :                 GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string()));
     644             :         } else {
     645           2 :             throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword");
     646             :         }
     647         431 :     } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
     648           0 :         throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
     649         431 :     else if (response.body.empty())
     650           0 :         throw std::runtime_error("no response from server");
     651             : 
     652             :     // Parse reply
     653         431 :     UniValue valReply(UniValue::VSTR);
     654         431 :     if (!valReply.read(response.body))
     655           0 :         throw std::runtime_error("couldn't parse reply from server");
     656         431 :     const UniValue reply = rh->ProcessReply(valReply);
     657         431 :     if (reply.empty())
     658           0 :         throw std::runtime_error("expected reply to have result, error and id properties");
     659             : 
     660             :     return reply;
     661         439 : }
     662             : 
     663             : /**
     664             :  * ConnectAndCallRPC wraps CallRPC with -rpcwait and an exception handler.
     665             :  *
     666             :  * @param[in] rh         Pointer to RequestHandler.
     667             :  * @param[in] strMethod  Reference to const string method to forward to CallRPC.
     668             :  * @param[in] rpcwallet  Reference to const optional string wallet name to forward to CallRPC.
     669             :  * @returns the RPC response as a UniValue object.
     670             :  * @throws a CConnectionFailed std::runtime_error if connection failed or RPC server still in warmup.
     671             :  */
     672         436 : static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const Optional<std::string>& rpcwallet = {})
     673             : {
     674         443 :     UniValue response(UniValue::VOBJ);
     675             :     // Execute and handle connection failures with -rpcwait.
     676         436 :     const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
     677         436 :     do {
     678             :         try {
     679         438 :             response = CallRPC(rh, strMethod, args, rpcwallet);
     680         431 :             if (fWait) {
     681           3 :                 const UniValue& error = find_value(response, "error");
     682           3 :                 if (!error.isNull() && error["code"].get_int() == RPC_IN_WARMUP) {
     683           2 :                     throw CConnectionFailed("server in warmup");
     684             :                 }
     685           3 :             }
     686             :             break; // Connection succeeded, no need to retry.
     687           9 :         } catch (const CConnectionFailed&) {
     688           3 :             if (fWait) {
     689           2 :                 UninterruptibleSleep(std::chrono::milliseconds{1000});
     690             :             } else {
     691           1 :                 throw;
     692             :             }
     693           3 :         }
     694           2 :     } while (fWait);
     695             :     return response;
     696         446 : }
     697             : 
     698             : /** Parse UniValue result to update the message to print to std::cout. */
     699         343 : static void ParseResult(const UniValue& result, std::string& strPrint)
     700             : {
     701         343 :     if (result.isNull()) return;
     702         311 :     strPrint = result.isStr() ? result.get_str() : result.write(2);
     703         343 : }
     704             : 
     705             : /** Parse UniValue error to update the message to print to std::cerr and the code to return. */
     706          64 : static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
     707             : {
     708          64 :     if (error.isObject()) {
     709          64 :         const UniValue& err_code = find_value(error, "code");
     710          64 :         const UniValue& err_msg = find_value(error, "message");
     711          64 :         if (!err_code.isNull()) {
     712          64 :             strPrint = "error code: " + err_code.getValStr() + "\n";
     713          64 :         }
     714          64 :         if (err_msg.isStr()) {
     715          64 :             strPrint += ("error message:\n" + err_msg.get_str());
     716          64 :         }
     717          64 :         if (err_code.isNum() && err_code.get_int() == RPC_WALLET_NOT_SPECIFIED) {
     718           6 :             strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
     719           6 :         }
     720          64 :     } else {
     721           0 :         strPrint = "error: " + error.write();
     722             :     }
     723          64 :     nRet = abs(error["code"].get_int());
     724          64 : }
     725             : 
     726             : /**
     727             :  * GetWalletBalances calls listwallets; if more than one wallet is loaded, it then
     728             :  * fetches mine.trusted balances for each loaded wallet and pushes them to `result`.
     729             :  *
     730             :  * @param result  Reference to UniValue object the wallet names and balances are pushed to.
     731             :  */
     732           4 : static void GetWalletBalances(UniValue& result)
     733             : {
     734           4 :     DefaultRequestHandler rh;
     735           4 :     const UniValue listwallets = ConnectAndCallRPC(&rh, "listwallets", /* args=*/{});
     736           4 :     if (!find_value(listwallets, "error").isNull()) return;
     737           4 :     const UniValue& wallets = find_value(listwallets, "result");
     738           4 :     if (wallets.size() <= 1) return;
     739             : 
     740           2 :     UniValue balances(UniValue::VOBJ);
     741           7 :     for (const UniValue& wallet : wallets.getValues()) {
     742           5 :         const std::string wallet_name = wallet.get_str();
     743           5 :         const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name);
     744           5 :         const UniValue& balance = find_value(getbalances, "result")["mine"]["trusted"];
     745           5 :         balances.pushKV(wallet_name, balance);
     746           5 :     }
     747           2 :     result.pushKV("balances", balances);
     748           6 : }
     749             : 
     750             : /**
     751             :  * Call RPC getnewaddress.
     752             :  * @returns getnewaddress response as a UniValue object.
     753             :  */
     754          21 : static UniValue GetNewAddress()
     755             : {
     756          21 :     Optional<std::string> wallet_name{};
     757          21 :     if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
     758          21 :     DefaultRequestHandler rh;
     759          21 :     return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, wallet_name);
     760          21 : }
     761             : 
     762             : /**
     763             :  * Check bounds and set up args for RPC generatetoaddress params: nblocks, address, maxtries.
     764             :  * @param[in] address  Reference to const string address to insert into the args.
     765             :  * @param     args     Reference to vector of string args to modify.
     766             :  */
     767          13 : static void SetGenerateToAddressArgs(const std::string& address, std::vector<std::string>& args)
     768             : {
     769          15 :     if (args.size() > 2) throw std::runtime_error("too many arguments (maximum 2 for nblocks and maxtries)");
     770          11 :     if (args.size() == 0) {
     771           3 :         args.emplace_back(DEFAULT_NBLOCKS);
     772          11 :     } else if (args.at(0) == "0") {
     773           2 :         throw std::runtime_error("the first argument (number of blocks to generate, default: " + DEFAULT_NBLOCKS + ") must be an integer value greater than zero");
     774             :     }
     775           9 :     args.emplace(args.begin() + 1, address);
     776          11 : }
     777             : 
     778         418 : static int CommandLineRPC(int argc, char *argv[])
     779             : {
     780         418 :     std::string strPrint;
     781         418 :     int nRet = 0;
     782             :     try {
     783             :         // Skip switches
     784        1100 :         while (argc > 1 && IsSwitchChar(argv[1][0])) {
     785         682 :             argc--;
     786         682 :             argv++;
     787             :         }
     788         418 :         std::string rpcPass;
     789         418 :         if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
     790           4 :             NO_STDIN_ECHO();
     791           4 :             if (!StdinReady()) {
     792           0 :                 fputs("RPC password> ", stderr);
     793           0 :                 fflush(stderr);
     794             :             }
     795           4 :             if (!std::getline(std::cin, rpcPass)) {
     796           0 :                 throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input");
     797             :             }
     798           4 :             if (StdinTerminal()) {
     799           0 :                 fputc('\n', stdout);
     800             :             }
     801           4 :             gArgs.ForceSetArg("-rpcpassword", rpcPass);
     802           4 :         }
     803         418 :         std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
     804         418 :         if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
     805           0 :             NO_STDIN_ECHO();
     806           0 :             std::string walletPass;
     807           0 :             if (args.size() < 1 || args[0].substr(0, 16) != "walletpassphrase") {
     808           0 :                 throw std::runtime_error("-stdinwalletpassphrase is only applicable for walletpassphrase(change)");
     809             :             }
     810           0 :             if (!StdinReady()) {
     811           0 :                 fputs("Wallet passphrase> ", stderr);
     812           0 :                 fflush(stderr);
     813             :             }
     814           0 :             if (!std::getline(std::cin, walletPass)) {
     815           0 :                 throw std::runtime_error("-stdinwalletpassphrase specified but failed to read from standard input");
     816             :             }
     817           0 :             if (StdinTerminal()) {
     818           0 :                 fputc('\n', stdout);
     819             :             }
     820           0 :             args.insert(args.begin() + 1, walletPass);
     821           0 :         }
     822         418 :         if (gArgs.GetBoolArg("-stdin", false)) {
     823             :             // Read one arg per line from stdin and append
     824           2 :             std::string line;
     825           4 :             while (std::getline(std::cin, line)) {
     826           2 :                 args.push_back(line);
     827             :             }
     828           2 :             if (StdinTerminal()) {
     829           0 :                 fputc('\n', stdout);
     830             :             }
     831           2 :         }
     832         418 :         std::unique_ptr<BaseRequestHandler> rh;
     833         418 :         std::string method;
     834         418 :         if (gArgs.IsArgSet("-getinfo")) {
     835          11 :             rh.reset(new GetinfoRequestHandler());
     836         418 :         } else if (gArgs.GetBoolArg("-netinfo", false)) {
     837           0 :             rh.reset(new NetinfoRequestHandler());
     838         407 :         } else if (gArgs.GetBoolArg("-generate", false)) {
     839          21 :             const UniValue getnewaddress{GetNewAddress()};
     840          21 :             const UniValue& error{find_value(getnewaddress, "error")};
     841          21 :             if (error.isNull()) {
     842          13 :                 SetGenerateToAddressArgs(find_value(getnewaddress, "result").get_str(), args);
     843           9 :                 rh.reset(new GenerateToAddressRequestHandler());
     844           9 :             } else {
     845           8 :                 ParseError(error, strPrint, nRet);
     846             :             }
     847          21 :         } else {
     848         386 :             rh.reset(new DefaultRequestHandler());
     849         386 :             if (args.size() < 1) {
     850           0 :                 throw std::runtime_error("too few parameters (need at least command)");
     851             :             }
     852         386 :             method = args[0];
     853         386 :             args.erase(args.begin()); // Remove trailing method name from arguments vector
     854             :         }
     855         414 :         if (nRet == 0) {
     856             :             // Perform RPC call
     857         406 :             Optional<std::string> wallet_name{};
     858         406 :             if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
     859         406 :             const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
     860             : 
     861             :             // Parse reply
     862         399 :             UniValue result = find_value(reply, "result");
     863         399 :             const UniValue& error = find_value(reply, "error");
     864         399 :             if (error.isNull()) {
     865         343 :                 if (gArgs.IsArgSet("-getinfo") && !gArgs.IsArgSet("-rpcwallet")) {
     866           4 :                     GetWalletBalances(result); // fetch multiwallet balances and append to result
     867             :                 }
     868         343 :                 ParseResult(result, strPrint);
     869             :             } else {
     870          56 :                 ParseError(error, strPrint, nRet);
     871             :             }
     872         406 :         }
     873         418 :     } catch (const std::exception& e) {
     874          11 :         strPrint = std::string("error: ") + e.what();
     875          11 :         nRet = EXIT_FAILURE;
     876          11 :     } catch (...) {
     877           0 :         PrintExceptionContinue(nullptr, "CommandLineRPC()");
     878           0 :         throw;
     879          11 :     }
     880             : 
     881         418 :     if (strPrint != "") {
     882         386 :         tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
     883             :     }
     884         418 :     return nRet;
     885         429 : }
     886             : 
     887             : #ifdef WIN32
     888             : // Export main() and ensure working ASLR on Windows.
     889             : // Exporting a symbol will prevent the linker from stripping
     890             : // the .reloc section from the binary, which is a requirement
     891             : // for ASLR. This is a temporary workaround until a fixed
     892             : // version of binutils is used for releases.
     893             : __declspec(dllexport) int main(int argc, char* argv[])
     894             : {
     895             :     util::WinCmdLineArgs winArgs;
     896             :     std::tie(argc, argv) = winArgs.get();
     897             : #else
     898         419 : int main(int argc, char* argv[])
     899             : {
     900             : #endif
     901         419 :     SetupEnvironment();
     902         419 :     if (!SetupNetworking()) {
     903           0 :         tfm::format(std::cerr, "Error: Initializing networking failed\n");
     904           0 :         return EXIT_FAILURE;
     905             :     }
     906         419 :     event_set_log_callback(&libevent_log_cb);
     907             : 
     908             :     try {
     909         419 :         int ret = AppInitRPC(argc, argv);
     910         419 :         if (ret != CONTINUE_EXECUTION)
     911           1 :             return ret;
     912         418 :     }
     913             :     catch (const std::exception& e) {
     914           0 :         PrintExceptionContinue(&e, "AppInitRPC()");
     915             :         return EXIT_FAILURE;
     916           0 :     } catch (...) {
     917           0 :         PrintExceptionContinue(nullptr, "AppInitRPC()");
     918             :         return EXIT_FAILURE;
     919           0 :     }
     920             : 
     921             :     int ret = EXIT_FAILURE;
     922             :     try {
     923         418 :         ret = CommandLineRPC(argc, argv);
     924         418 :     }
     925             :     catch (const std::exception& e) {
     926           0 :         PrintExceptionContinue(&e, "CommandLineRPC()");
     927           0 :     } catch (...) {
     928           0 :         PrintExceptionContinue(nullptr, "CommandLineRPC()");
     929           0 :     }
     930             :     return ret;
     931         419 : }

Generated by: LCOV version 1.15