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