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