bitcoin-cli.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto 2 // Copyright (c) 2009-present 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 #include <bitcoin-build-config.h> // IWYU pragma: keep 7 8 #include <chainparamsbase.h> 9 #include <clientversion.h> 10 #include <common/args.h> 11 #include <common/system.h> 12 #include <compat/compat.h> 13 #include <compat/stdin.h> 14 #include <policy/feerate.h> 15 #include <rpc/client.h> 16 #include <rpc/mining.h> 17 #include <rpc/protocol.h> 18 #include <rpc/request.h> 19 #include <tinyformat.h> 20 #include <univalue.h> 21 #include <util/chaintype.h> 22 #include <util/exception.h> 23 #include <util/strencodings.h> 24 #include <util/time.h> 25 #include <util/translation.h> 26 27 #include <algorithm> 28 #include <chrono> 29 #include <cmath> 30 #include <cstdio> 31 #include <functional> 32 #include <memory> 33 #include <optional> 34 #include <string> 35 #include <tuple> 36 37 #ifndef WIN32 38 #include <unistd.h> 39 #endif 40 41 #include <event2/buffer.h> 42 #include <event2/keyvalq_struct.h> 43 #include <support/events.h> 44 45 using util::Join; 46 using util::ToString; 47 48 // The server returns time values from a mockable system clock, but it is not 49 // trivial to get the mocked time from the server, nor is it needed for now, so 50 // just use a plain system_clock. 51 using CliClock = std::chrono::system_clock; 52 53 const TranslateFn G_TRANSLATION_FUN{nullptr}; 54 55 static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; 56 static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; 57 static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0; 58 static const bool DEFAULT_NAMED=false; 59 static const int CONTINUE_EXECUTION=-1; 60 static constexpr uint8_t NETINFO_MAX_LEVEL{4}; 61 static constexpr int8_t UNKNOWN_NETWORK{-1}; 62 // See GetNetworkName() in netbase.cpp 63 static constexpr std::array NETWORKS{"not_publicly_routable", "ipv4", "ipv6", "onion", "i2p", "cjdns", "internal"}; 64 static constexpr std::array NETWORK_SHORT_NAMES{"npr", "ipv4", "ipv6", "onion", "i2p", "cjdns", "int"}; 65 static constexpr std::array UNREACHABLE_NETWORK_IDS{/*not_publicly_routable*/0, /*internal*/6}; 66 67 /** Default number of blocks to generate for RPC generatetoaddress. */ 68 static const std::string DEFAULT_NBLOCKS = "1"; 69 70 /** Default -color setting. */ 71 static const std::string DEFAULT_COLOR_SETTING{"auto"}; 72 73 static void SetupCliArgs(ArgsManager& argsman) 74 { 75 SetupHelpOptions(argsman); 76 77 const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN); 78 const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET); 79 const auto testnet4BaseParams = CreateBaseChainParams(ChainType::TESTNET4); 80 const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET); 81 const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST); 82 83 argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); 84 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); 85 argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); 86 argsman.AddArg("-generate", 87 strprintf("Generate blocks, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer " 88 "arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to " 89 "RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", 90 DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), 91 ArgsManager::ALLOW_ANY, OptionsCategory::CLI_COMMANDS); 92 argsman.AddArg("-addrinfo", "Get the number of addresses known to the node, per network and total.", ArgsManager::ALLOW_ANY, OptionsCategory::CLI_COMMANDS); 93 argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the output of -getinfo is the result of multiple non-atomic requests. Some entries in the output 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::CLI_COMMANDS); 94 argsman.AddArg("-netinfo", strprintf("Get network peer connection information from the remote server. An optional argument from 0 to %d can be passed for different peers listings (default: 0). If a non-zero value is passed, an additional \"outonly\" (or \"o\") argument can be passed to see outbound peers only. Pass \"help\" (or \"h\") for detailed help documentation.", NETINFO_MAX_LEVEL), ArgsManager::ALLOW_ANY, OptionsCategory::CLI_COMMANDS); 95 96 SetupChainParamsBaseOptions(argsman); 97 argsman.AddArg("-color=<when>", strprintf("Color setting for CLI output (default: %s). Valid values: always, auto (add color codes when standard output is connected to a terminal and OS is not WIN32), never. Only applies to the output of -getinfo.", DEFAULT_COLOR_SETTING), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); 98 argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); 99 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); 100 argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); 101 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); 102 argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); 103 argsman.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, testnet4: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), testnet4BaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); 104 argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); 105 argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); 106 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_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); 107 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); 108 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); 109 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); 110 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); 111 } 112 113 std::optional<std::string> RpcWalletName(const ArgsManager& args) 114 { 115 // Check IsArgNegated to return nullopt instead of "0" if -norpcwallet is specified 116 if (args.IsArgNegated("-rpcwallet")) return std::nullopt; 117 return args.GetArg("-rpcwallet"); 118 } 119 120 /** libevent event log callback */ 121 static void libevent_log_cb(int severity, const char *msg) 122 { 123 // Ignore everything other than errors 124 if (severity >= EVENT_LOG_ERR) { 125 throw std::runtime_error(strprintf("libevent error: %s", msg)); 126 } 127 } 128 129 // 130 // Exception thrown on connection error. This error is used to determine 131 // when to wait if -rpcwait is given. 132 // 133 struct CConnectionFailed : std::runtime_error { 134 explicit inline CConnectionFailed(const std::string& msg) : 135 std::runtime_error(msg) 136 {} 137 }; 138 139 // 140 // This function returns either one of EXIT_ codes when it's expected to stop the process or 141 // CONTINUE_EXECUTION when it's expected to continue further. 142 // 143 static int AppInitRPC(int argc, char* argv[]) 144 { 145 SetupCliArgs(gArgs); 146 std::string error; 147 if (!gArgs.ParseParameters(argc, argv, error)) { 148 tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error); 149 return EXIT_FAILURE; 150 } 151 if (argc < 2 || HelpRequested(gArgs) || gArgs.GetBoolArg("-version", false)) { 152 std::string strUsage = CLIENT_NAME " RPC client version " + FormatFullVersion() + "\n"; 153 154 if (gArgs.GetBoolArg("-version", false)) { 155 strUsage += FormatParagraph(LicenseInfo()); 156 } else { 157 strUsage += "\n" 158 "The bitcoin-cli utility provides a command line interface to interact with a " CLIENT_NAME " RPC server.\n" 159 "\nIt can be used to query network information, manage wallets, create or broadcast transactions, and control the " CLIENT_NAME " server.\n" 160 "\nUse the \"help\" command to list all commands. Use \"help <command>\" to show help for that command.\n" 161 "The -named option allows you to specify parameters using the key=value format, eliminating the need to pass unused positional parameters.\n" 162 "\n" 163 "Usage: bitcoin-cli [options] <command> [params]\n" 164 "or: bitcoin-cli [options] -named <command> [name=value]...\n" 165 "or: bitcoin-cli [options] help\n" 166 "or: bitcoin-cli [options] help <command>\n" 167 "\n"; 168 strUsage += "\n" + gArgs.GetHelpMessage(); 169 } 170 171 tfm::format(std::cout, "%s", strUsage); 172 if (argc < 2) { 173 tfm::format(std::cerr, "Error: too few parameters\n"); 174 return EXIT_FAILURE; 175 } 176 return EXIT_SUCCESS; 177 } 178 if (!CheckDataDirOption(gArgs)) { 179 tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")); 180 return EXIT_FAILURE; 181 } 182 if (!gArgs.ReadConfigFiles(error, true)) { 183 tfm::format(std::cerr, "Error reading configuration file: %s\n", error); 184 return EXIT_FAILURE; 185 } 186 // Check for chain settings (BaseParams() calls are only valid after this clause) 187 try { 188 SelectBaseParams(gArgs.GetChainType()); 189 } catch (const std::exception& e) { 190 tfm::format(std::cerr, "Error: %s\n", e.what()); 191 return EXIT_FAILURE; 192 } 193 return CONTINUE_EXECUTION; 194 } 195 196 197 /** Reply structure for request_done to fill in */ 198 struct HTTPReply 199 { 200 HTTPReply() = default; 201 202 int status{0}; 203 int error{-1}; 204 std::string body; 205 }; 206 207 static std::string http_errorstring(int code) 208 { 209 switch(code) { 210 case EVREQ_HTTP_TIMEOUT: 211 return "timeout reached"; 212 case EVREQ_HTTP_EOF: 213 return "EOF reached"; 214 case EVREQ_HTTP_INVALID_HEADER: 215 return "error while reading header, or invalid header"; 216 case EVREQ_HTTP_BUFFER_ERROR: 217 return "error encountered while reading or writing"; 218 case EVREQ_HTTP_REQUEST_CANCEL: 219 return "request was canceled"; 220 case EVREQ_HTTP_DATA_TOO_LONG: 221 return "response body is larger than allowed"; 222 default: 223 return "unknown"; 224 } 225 } 226 227 static void http_request_done(struct evhttp_request *req, void *ctx) 228 { 229 HTTPReply *reply = static_cast<HTTPReply*>(ctx); 230 231 if (req == nullptr) { 232 /* If req is nullptr, it means an error occurred while connecting: the 233 * error code will have been passed to http_error_cb. 234 */ 235 reply->status = 0; 236 return; 237 } 238 239 reply->status = evhttp_request_get_response_code(req); 240 241 struct evbuffer *buf = evhttp_request_get_input_buffer(req); 242 if (buf) 243 { 244 size_t size = evbuffer_get_length(buf); 245 const char *data = (const char*)evbuffer_pullup(buf, size); 246 if (data) 247 reply->body = std::string(data, size); 248 evbuffer_drain(buf, size); 249 } 250 } 251 252 static void http_error_cb(enum evhttp_request_error err, void *ctx) 253 { 254 HTTPReply *reply = static_cast<HTTPReply*>(ctx); 255 reply->error = err; 256 } 257 258 static int8_t NetworkStringToId(const std::string& str) 259 { 260 for (size_t i = 0; i < NETWORKS.size(); ++i) { 261 if (str == NETWORKS[i]) return i; 262 } 263 return UNKNOWN_NETWORK; 264 } 265 266 /** Handle the conversion from a command-line to a JSON-RPC request, 267 * as well as converting back to a JSON object that can be shown as result. 268 */ 269 struct BaseRequestHandler { 270 virtual ~BaseRequestHandler() = default; 271 virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0; 272 virtual UniValue ProcessReply(const UniValue &batch_in) = 0; 273 }; 274 275 /** Process addrinfo requests */ 276 struct AddrinfoRequestHandler : BaseRequestHandler { 277 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override 278 { 279 if (!args.empty()) { 280 throw std::runtime_error("-addrinfo takes no arguments"); 281 } 282 return JSONRPCRequestObj("getaddrmaninfo", NullUniValue, 1); 283 } 284 285 UniValue ProcessReply(const UniValue& reply) override 286 { 287 if (!reply["error"].isNull()) { 288 if (reply["error"]["code"].getInt<int>() == RPC_METHOD_NOT_FOUND) { 289 throw std::runtime_error("-addrinfo requires bitcoind v26.0 or later which supports getaddrmaninfo RPC. Please upgrade your node or use bitcoin-cli from the same version."); 290 } 291 return reply; 292 } 293 // Process getaddrmaninfo reply 294 const std::vector<std::string>& network_types{reply["result"].getKeys()}; 295 const std::vector<UniValue>& addrman_counts{reply["result"].getValues()}; 296 297 // Prepare result to return to user. 298 UniValue result{UniValue::VOBJ}, addresses{UniValue::VOBJ}; 299 300 for (size_t i = 0; i < network_types.size(); ++i) { 301 int addr_count = addrman_counts[i]["total"].getInt<int>(); 302 if (network_types[i] == "all_networks") { 303 addresses.pushKV("total", addr_count); 304 } else { 305 addresses.pushKV(network_types[i], addr_count); 306 } 307 } 308 result.pushKV("addresses_known", std::move(addresses)); 309 return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2); 310 } 311 }; 312 313 /** Process getinfo requests */ 314 struct GetinfoRequestHandler : BaseRequestHandler { 315 const int ID_NETWORKINFO = 0; 316 const int ID_BLOCKCHAININFO = 1; 317 const int ID_WALLETINFO = 2; 318 const int ID_BALANCES = 3; 319 320 /** Create a simulated `getinfo` request. */ 321 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override 322 { 323 if (!args.empty()) { 324 throw std::runtime_error("-getinfo takes no arguments"); 325 } 326 UniValue result(UniValue::VARR); 327 result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO)); 328 result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO)); 329 result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO)); 330 result.push_back(JSONRPCRequestObj("getbalances", NullUniValue, ID_BALANCES)); 331 return result; 332 } 333 334 /** Collect values from the batch and form a simulated `getinfo` reply. */ 335 UniValue ProcessReply(const UniValue &batch_in) override 336 { 337 UniValue result(UniValue::VOBJ); 338 const std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in); 339 // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on; 340 // getwalletinfo() and getbalances() are allowed to fail if there is no wallet. 341 if (!batch[ID_NETWORKINFO]["error"].isNull()) { 342 return batch[ID_NETWORKINFO]; 343 } 344 if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) { 345 return batch[ID_BLOCKCHAININFO]; 346 } 347 result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]); 348 result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]); 349 result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]); 350 result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]); 351 result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]); 352 353 UniValue connections(UniValue::VOBJ); 354 connections.pushKV("in", batch[ID_NETWORKINFO]["result"]["connections_in"]); 355 connections.pushKV("out", batch[ID_NETWORKINFO]["result"]["connections_out"]); 356 connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]); 357 result.pushKV("connections", std::move(connections)); 358 359 result.pushKV("networks", batch[ID_NETWORKINFO]["result"]["networks"]); 360 result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]); 361 result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"])); 362 if (!batch[ID_WALLETINFO]["result"].isNull()) { 363 result.pushKV("has_wallet", true); 364 result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]); 365 result.pushKV("walletname", batch[ID_WALLETINFO]["result"]["walletname"]); 366 if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) { 367 result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]); 368 } 369 } 370 if (!batch[ID_BALANCES]["result"].isNull()) { 371 result.pushKV("balance", batch[ID_BALANCES]["result"]["mine"]["trusted"]); 372 } 373 result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]); 374 result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]); 375 return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2); 376 } 377 }; 378 379 /** Process netinfo requests */ 380 class NetinfoRequestHandler : public BaseRequestHandler 381 { 382 private: 383 std::array<std::array<uint16_t, NETWORKS.size() + 1>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total) 384 uint8_t m_block_relay_peers_count{0}; 385 uint8_t m_manual_peers_count{0}; 386 uint8_t m_details_level{0}; //!< Optional user-supplied arg to set dashboard details level 387 bool DetailsRequested() const { return m_details_level; } 388 bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; } 389 bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; } 390 bool m_outbound_only_selected{false}; 391 bool m_is_asmap_on{false}; 392 size_t m_max_addr_length{0}; 393 size_t m_max_addr_processed_length{5}; 394 size_t m_max_addr_rate_limited_length{6}; 395 size_t m_max_age_length{5}; 396 size_t m_max_id_length{2}; 397 size_t m_max_services_length{6}; 398 struct Peer { 399 std::string addr; 400 std::string sub_version; 401 std::string conn_type; 402 std::string network; 403 std::string age; 404 std::string services; 405 std::string transport_protocol_type; 406 double min_ping; 407 double ping; 408 int64_t addr_processed; 409 int64_t addr_rate_limited; 410 int64_t last_blck; 411 int64_t last_recv; 412 int64_t last_send; 413 int64_t last_trxn; 414 int id; 415 int mapped_as; 416 int version; 417 bool is_addr_relay_enabled; 418 bool is_bip152_hb_from; 419 bool is_bip152_hb_to; 420 bool is_outbound; 421 bool is_tx_relay; 422 bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); } 423 }; 424 std::vector<Peer> m_peers; 425 std::string ChainToString() const 426 { 427 switch (gArgs.GetChainType()) { 428 case ChainType::TESTNET4: 429 return " testnet4"; 430 case ChainType::TESTNET: 431 return " testnet"; 432 case ChainType::SIGNET: 433 return " signet"; 434 case ChainType::REGTEST: 435 return " regtest"; 436 case ChainType::MAIN: 437 return ""; 438 } 439 assert(false); 440 } 441 std::string PingTimeToString(double seconds) const 442 { 443 if (seconds < 0) return ""; 444 const double milliseconds{round(1000 * seconds)}; 445 return milliseconds > 999999 ? "-" : ToString(milliseconds); 446 } 447 std::string ConnectionTypeForNetinfo(const std::string& conn_type) const 448 { 449 if (conn_type == "outbound-full-relay") return "full"; 450 if (conn_type == "block-relay-only") return "block"; 451 if (conn_type == "manual" || conn_type == "feeler") return conn_type; 452 if (conn_type == "addr-fetch") return "addr"; 453 if (conn_type == "private-broadcast") return "priv"; 454 return ""; 455 } 456 std::string FormatServices(const UniValue& services) 457 { 458 std::string str; 459 for (size_t i = 0; i < services.size(); ++i) { 460 const std::string s{services[i].get_str()}; 461 str += s == "NETWORK_LIMITED" ? 'l' : s == "P2P_V2" ? '2' : ToLower(s[0]); 462 } 463 return str; 464 } 465 static std::string ServicesList(const UniValue& services) 466 { 467 std::string str{services.size() ? services[0].get_str() : ""}; 468 for (size_t i{1}; i < services.size(); ++i) { 469 str += ", " + services[i].get_str(); 470 } 471 for (auto& c: str) { 472 c = (c == '_' ? ' ' : ToLower(c)); 473 } 474 return str; 475 } 476 477 public: 478 static constexpr int ID_PEERINFO = 0; 479 static constexpr int ID_NETWORKINFO = 1; 480 481 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override 482 { 483 if (!args.empty()) { 484 uint8_t n{0}; 485 if (const auto res{ToIntegral<uint8_t>(args.at(0))}) { 486 n = *res; 487 m_details_level = std::min(n, NETINFO_MAX_LEVEL); 488 } else { 489 throw std::runtime_error(strprintf("invalid -netinfo level argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0))); 490 } 491 if (args.size() > 1) { 492 if (std::string_view s{args.at(1)}; n && (s == "o" || s == "outonly")) { 493 m_outbound_only_selected = true; 494 } else if (n) { 495 throw std::runtime_error(strprintf("invalid -netinfo outonly argument: %s\nFor more information, run: bitcoin-cli -netinfo help", s)); 496 } else { 497 throw std::runtime_error(strprintf("invalid -netinfo outonly argument: %s\nThe outonly argument is only valid for a level greater than 0 (the first argument). For more information, run: bitcoin-cli -netinfo help", s)); 498 } 499 } 500 } 501 UniValue result(UniValue::VARR); 502 result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO)); 503 result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO)); 504 return result; 505 } 506 507 UniValue ProcessReply(const UniValue& batch_in) override 508 { 509 const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)}; 510 if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO]; 511 if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO]; 512 513 const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]}; 514 if (networkinfo["version"].getInt<int>() < 209900) { 515 throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up"); 516 } 517 const int64_t time_now{TicksSinceEpoch<std::chrono::seconds>(CliClock::now())}; 518 519 // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs. 520 for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) { 521 const std::string network{peer["network"].get_str()}; 522 const int8_t network_id{NetworkStringToId(network)}; 523 if (network_id == UNKNOWN_NETWORK) continue; 524 const bool is_outbound{!peer["inbound"].get_bool()}; 525 const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()}; 526 const std::string conn_type{peer["connection_type"].get_str()}; 527 ++m_counts.at(is_outbound).at(network_id); // in/out by network 528 ++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall 529 ++m_counts.at(2).at(network_id); // total by network 530 ++m_counts.at(2).at(NETWORKS.size()); // total overall 531 if (conn_type == "block-relay-only") ++m_block_relay_peers_count; 532 if (conn_type == "manual") ++m_manual_peers_count; 533 if (m_outbound_only_selected && !is_outbound) continue; 534 if (DetailsRequested()) { 535 // Push data for this peer to the peers vector. 536 const int peer_id{peer["id"].getInt<int>()}; 537 const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].getInt<int>()}; 538 const int version{peer["version"].getInt<int>()}; 539 const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].getInt<int64_t>()}; 540 const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].getInt<int64_t>()}; 541 const int64_t conn_time{peer["conntime"].getInt<int64_t>()}; 542 const int64_t last_blck{peer["last_block"].getInt<int64_t>()}; 543 const int64_t last_recv{peer["lastrecv"].getInt<int64_t>()}; 544 const int64_t last_send{peer["lastsend"].getInt<int64_t>()}; 545 const int64_t last_trxn{peer["last_transaction"].getInt<int64_t>()}; 546 const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()}; 547 const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()}; 548 const std::string addr{peer["addr"].get_str()}; 549 const std::string age{conn_time == 0 ? "" : ToString((time_now - conn_time) / 60)}; 550 const std::string services{FormatServices(peer["servicesnames"])}; 551 const std::string sub_version{peer["subver"].get_str()}; 552 const std::string transport{peer["transport_protocol_type"].isNull() ? "v1" : peer["transport_protocol_type"].get_str()}; 553 const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()}; 554 const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()}; 555 const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()}; 556 m_peers.push_back({addr, sub_version, conn_type, NETWORK_SHORT_NAMES[network_id], age, services, transport, min_ping, ping, addr_processed, addr_rate_limited, last_blck, last_recv, last_send, last_trxn, peer_id, mapped_as, version, is_addr_relay_enabled, is_bip152_hb_from, is_bip152_hb_to, is_outbound, is_tx_relay}); 557 m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length); 558 m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length); 559 m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length); 560 m_max_age_length = std::max(age.length(), m_max_age_length); 561 m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length); 562 m_max_services_length = std::max(services.length(), m_max_services_length); 563 m_is_asmap_on |= (mapped_as != 0); 564 } 565 } 566 567 // Generate report header. 568 const std::string services{DetailsRequested() ? strprintf(" - services %s", FormatServices(networkinfo["localservicesnames"])) : ""}; 569 std::string result{strprintf("%s client %s%s - server %i%s%s\n\n", CLIENT_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].getInt<int>(), networkinfo["subversion"].get_str(), services)}; 570 571 // Report detailed peer connections list sorted by direction and minimum ping time. 572 if (DetailsRequested() && !m_peers.empty()) { 573 std::sort(m_peers.begin(), m_peers.end()); 574 result += strprintf("<-> type net %*s v mping ping send recv txn blk hb %*s%*s%*s ", 575 m_max_services_length, "serv", 576 m_max_addr_processed_length, "addrp", 577 m_max_addr_rate_limited_length, "addrl", 578 m_max_age_length, "age"); 579 if (m_is_asmap_on) result += " asmap "; 580 result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : ""); 581 for (const Peer& peer : m_peers) { 582 std::string version{ToString(peer.version) + peer.sub_version}; 583 result += strprintf( 584 "%3s %6s %5s %*s %2s%7s%7s%5s%5s%5s%5s %2s %*s%*s%*s%*i %*s %-*s%s\n", 585 peer.is_outbound ? "out" : "in", 586 ConnectionTypeForNetinfo(peer.conn_type), 587 peer.network, 588 m_max_services_length, // variable spacing 589 peer.services, 590 (peer.transport_protocol_type.size() == 2 && peer.transport_protocol_type[0] == 'v') ? peer.transport_protocol_type[1] : ' ', 591 PingTimeToString(peer.min_ping), 592 PingTimeToString(peer.ping), 593 peer.last_send ? ToString(time_now - peer.last_send) : "", 594 peer.last_recv ? ToString(time_now - peer.last_recv) : "", 595 peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*", 596 peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "", 597 strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "), 598 m_max_addr_processed_length, // variable spacing 599 peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".", 600 m_max_addr_rate_limited_length, // variable spacing 601 peer.addr_rate_limited ? ToString(peer.addr_rate_limited) : "", 602 m_max_age_length, // variable spacing 603 peer.age, 604 m_is_asmap_on ? 7 : 0, // variable spacing 605 m_is_asmap_on && peer.mapped_as ? ToString(peer.mapped_as) : "", 606 m_max_id_length, // variable spacing 607 peer.id, 608 IsAddressSelected() ? m_max_addr_length : 0, // variable spacing 609 IsAddressSelected() ? peer.addr : "", 610 IsVersionSelected() && version != "0" ? version : ""); 611 } 612 result += strprintf(" %*s ms ms sec sec min min %*s\n\n", m_max_services_length, "", m_max_age_length, "min"); 613 } 614 615 // Report peer connection totals by type. 616 result += " "; 617 std::vector<int8_t> reachable_networks; 618 for (const UniValue& network : networkinfo["networks"].getValues()) { 619 if (network["reachable"].get_bool()) { 620 const std::string& network_name{network["name"].get_str()}; 621 const int8_t network_id{NetworkStringToId(network_name)}; 622 if (network_id == UNKNOWN_NETWORK) continue; 623 result += strprintf("%8s", network_name); // column header 624 reachable_networks.push_back(network_id); 625 } 626 }; 627 628 for (const size_t network_id : UNREACHABLE_NETWORK_IDS) { 629 if (m_counts.at(2).at(network_id) == 0) continue; 630 result += strprintf("%8s", NETWORK_SHORT_NAMES.at(network_id)); // column header 631 reachable_networks.push_back(network_id); 632 } 633 634 result += " total block"; 635 if (m_manual_peers_count) result += " manual"; 636 637 const std::array rows{"in", "out", "total"}; 638 for (size_t i = 0; i < rows.size(); ++i) { 639 result += strprintf("\n%-5s", rows[i]); // row header 640 for (int8_t n : reachable_networks) { 641 result += strprintf("%8i", m_counts.at(i).at(n)); // network peers count 642 } 643 result += strprintf(" %5i", m_counts.at(i).at(NETWORKS.size())); // total peers count 644 if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts 645 result += strprintf(" %5i", m_block_relay_peers_count); 646 if (m_manual_peers_count) result += strprintf(" %5i", m_manual_peers_count); 647 } 648 } 649 650 // Report local services, addresses, ports, and scores. 651 if (!DetailsRequested()) { 652 result += strprintf("\n\nLocal services: %s", ServicesList(networkinfo["localservicesnames"])); 653 } 654 result += "\n\nLocal addresses"; 655 const std::vector<UniValue>& local_addrs{networkinfo["localaddresses"].getValues()}; 656 if (local_addrs.empty()) { 657 result += ": n/a\n"; 658 } else { 659 size_t max_addr_size{0}; 660 for (const UniValue& addr : local_addrs) { 661 max_addr_size = std::max(addr["address"].get_str().length() + 1, max_addr_size); 662 } 663 for (const UniValue& addr : local_addrs) { 664 result += strprintf("\n%-*s port %6i score %6i", max_addr_size, addr["address"].get_str(), addr["port"].getInt<int>(), addr["score"].getInt<int>()); 665 } 666 } 667 668 return JSONRPCReplyObj(UniValue{result}, NullUniValue, /*id=*/1, JSONRPCVersion::V2); 669 } 670 671 const std::string m_help_doc{ 672 "-netinfo (level [outonly]) | help\n\n" 673 "Returns a network peer connections dashboard with information from the remote server.\n" 674 "This human-readable interface will change regularly and is not intended to be a stable API.\n" 675 "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n" 676 + strprintf("An optional argument from 0 to %d can be passed for different peers listings; values above %d up to 255 are parsed as %d.\n", NETINFO_MAX_LEVEL, NETINFO_MAX_LEVEL, NETINFO_MAX_LEVEL) + 677 "If that argument is passed, an optional additional \"outonly\" argument may be passed to obtain the listing with outbound peers only.\n" 678 "Pass \"help\" or \"h\" to see this detailed help documentation.\n" 679 "If more than two arguments are passed, only the first two are read and parsed.\n" 680 "Suggestion: use -netinfo with the Linux watch(1) command for a live dashboard; see example below.\n\n" 681 "Arguments:\n" 682 + strprintf("1. level (integer 0-%d, optional) Specify the info level of the peers dashboard (default 0):\n", NETINFO_MAX_LEVEL) + 683 " 0 - Peer counts for each reachable network as well as for block relay peers\n" 684 " and manual peers, and the list of local addresses and ports\n" 685 " 1 - Like 0 but preceded by a peers listing (without address and version columns)\n" 686 " 2 - Like 1 but with an address column\n" 687 " 3 - Like 1 but with a version column\n" 688 " 4 - Like 1 but with both address and version columns\n" 689 "2. outonly (\"outonly\" or \"o\", optional) Return the peers listing with outbound peers only, i.e. to save screen space\n" 690 " when a node has many inbound peers. Only valid if a level is passed.\n\n" 691 "help (\"help\" or \"h\", optional) Print this help documentation instead of the dashboard.\n\n" 692 "Result:\n\n" 693 + strprintf("* The peers listing in levels 1-%d displays all of the peers sorted by direction and minimum ping time:\n\n", NETINFO_MAX_LEVEL) + 694 " Column Description\n" 695 " ------ -----------\n" 696 " <-> Direction\n" 697 " \"in\" - inbound connections are those initiated by the peer\n" 698 " \"out\" - outbound connections are those initiated by us\n" 699 " type Type of peer connection\n" 700 " \"full\" - full relay, the default\n" 701 " \"block\" - block relay; like full relay but does not relay transactions or addresses\n" 702 " \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n" 703 " \"feeler\" - short-lived connection for testing addresses\n" 704 " \"addr\" - address fetch; short-lived connection for requesting addresses\n" 705 " \"priv\" - private broadcast; short-lived connection for broadcasting our transactions\n" 706 " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", \"cjdns\", or \"npr\" (not publicly routable))\n" 707 " serv Services offered by the peer\n" 708 " \"n\" - NETWORK: peer can serve the full block chain\n" 709 " \"b\" - BLOOM: peer can handle bloom-filtered connections (see BIP 111)\n" 710 " \"w\" - WITNESS: peer can be asked for blocks and transactions with witness data (SegWit)\n" 711 " \"c\" - COMPACT_FILTERS: peer can handle basic block filter requests (see BIPs 157 and 158)\n" 712 " \"l\" - NETWORK_LIMITED: peer limited to serving only the last 288 blocks (~2 days)\n" 713 " \"2\" - P2P_V2: peer supports version 2 P2P transport protocol, as defined in BIP 324\n" 714 " \"u\" - UNKNOWN: unrecognized bit flag\n" 715 " v Version of transport protocol used for the connection\n" 716 " mping Minimum observed ping time, in milliseconds (ms)\n" 717 " ping Last observed ping time, in milliseconds (ms)\n" 718 " send Time since last message sent to the peer, in seconds\n" 719 " recv Time since last message received from the peer, in seconds\n" 720 " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n" 721 " \"*\" - we do not relay transactions to this peer (getpeerinfo \"relaytxes\" is false)\n" 722 " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n" 723 " hb High-bandwidth BIP152 compact block relay\n" 724 " \".\" (to) - we selected the peer as a high-bandwidth peer\n" 725 " \"*\" (from) - the peer selected us as a high-bandwidth peer\n" 726 " addrp Total number of addresses processed, excluding those dropped due to rate limiting\n" 727 " \".\" - we do not relay addresses to this peer (getpeerinfo \"addr_relay_enabled\" is false)\n" 728 " addrl Total number of addresses dropped due to rate limiting\n" 729 " age Duration of connection to the peer, in minutes\n" 730 " asmap Mapped AS (Autonomous System) number at the end of the BGP route to the peer, used for diversifying\n" 731 " peer selection (only displayed if the -asmap config option is set)\n" 732 " id Peer index, in increasing order of peer connections since node startup\n" 733 " address IP address and port of the peer\n" 734 " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n" 735 "* The peer counts table displays the number of peers for each reachable network as well as\n" 736 " the number of block relay peers and manual peers.\n\n" 737 "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n" 738 "Examples:\n\n" 739 "Peer counts table of reachable networks and list of local addresses\n" 740 "> bitcoin-cli -netinfo\n\n" 741 "The same, preceded by a peers listing without address and version columns\n" 742 "> bitcoin-cli -netinfo 1\n\n" 743 "Full dashboard\n" 744 + strprintf("> bitcoin-cli -netinfo %d\n\n", NETINFO_MAX_LEVEL) + 745 "Full dashboard, but with outbound peers only\n" 746 + strprintf("> bitcoin-cli -netinfo %d outonly\n\n", NETINFO_MAX_LEVEL) + 747 "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n" 748 + strprintf("> watch --interval 1 --no-title bitcoin-cli -netinfo %d\n\n", NETINFO_MAX_LEVEL) + 749 "See this help\n" 750 "> bitcoin-cli -netinfo help\n"}; 751 }; 752 753 /** Process RPC generatetoaddress request. */ 754 class GenerateToAddressRequestHandler : public BaseRequestHandler 755 { 756 public: 757 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override 758 { 759 address_str = args.at(1); 760 UniValue params{RPCConvertValues("generatetoaddress", args)}; 761 return JSONRPCRequestObj("generatetoaddress", params, 1); 762 } 763 764 UniValue ProcessReply(const UniValue &reply) override 765 { 766 UniValue result(UniValue::VOBJ); 767 result.pushKV("address", address_str); 768 result.pushKV("blocks", reply.get_obj()["result"]); 769 return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2); 770 } 771 protected: 772 std::string address_str; 773 }; 774 775 /** Process default single requests */ 776 struct DefaultRequestHandler : BaseRequestHandler { 777 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override 778 { 779 UniValue params; 780 if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { 781 params = RPCConvertNamedValues(method, args); 782 } else { 783 params = RPCConvertValues(method, args); 784 } 785 return JSONRPCRequestObj(method, params, 1); 786 } 787 788 UniValue ProcessReply(const UniValue &reply) override 789 { 790 return reply.get_obj(); 791 } 792 }; 793 794 static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {}) 795 { 796 std::string host; 797 // In preference order, we choose the following for the port: 798 // 1. -rpcport 799 // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) 800 // 3. default port for chain 801 uint16_t port{BaseParams().RPCPort()}; 802 { 803 uint16_t rpcconnect_port{0}; 804 const std::string rpcconnect_str = gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT); 805 if (!SplitHostPort(rpcconnect_str, rpcconnect_port, host)) { 806 // Uses argument provided as-is 807 // (rather than value parsed) 808 // to aid the user in troubleshooting 809 throw std::runtime_error(strprintf("Invalid port provided in -rpcconnect: %s", rpcconnect_str)); 810 } else { 811 if (rpcconnect_port != 0) { 812 // Use the valid port provided in rpcconnect 813 port = rpcconnect_port; 814 } // else, no port was provided in rpcconnect (continue using default one) 815 } 816 817 if (std::optional<std::string> rpcport_arg = gArgs.GetArg("-rpcport")) { 818 // -rpcport was specified 819 const uint16_t rpcport_int{ToIntegral<uint16_t>(rpcport_arg.value()).value_or(0)}; 820 if (rpcport_int == 0) { 821 // Uses argument provided as-is 822 // (rather than value parsed) 823 // to aid the user in troubleshooting 824 throw std::runtime_error(strprintf("Invalid port provided in -rpcport: %s", rpcport_arg.value())); 825 } 826 827 // Use the valid port provided 828 port = rpcport_int; 829 830 // If there was a valid port provided in rpcconnect, 831 // rpcconnect_port is non-zero. 832 if (rpcconnect_port != 0) { 833 tfm::format(std::cerr, "Warning: Port specified in both -rpcconnect and -rpcport. Using -rpcport %u\n", port); 834 } 835 } 836 } 837 838 // Obtain event base 839 raii_event_base base = obtain_event_base(); 840 841 // Synchronously look up hostname 842 raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port); 843 844 // Set connection timeout 845 { 846 const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT); 847 if (timeout > 0) { 848 evhttp_connection_set_timeout(evcon.get(), timeout); 849 } else { 850 // Indefinite request timeouts are not possible in libevent-http, so we 851 // set the timeout to a very long time period instead. 852 853 constexpr int YEAR_IN_SECONDS = 31556952; // Average length of year in Gregorian calendar 854 evhttp_connection_set_timeout(evcon.get(), 5 * YEAR_IN_SECONDS); 855 } 856 } 857 858 HTTPReply response; 859 raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response); 860 if (req == nullptr) { 861 throw std::runtime_error("create http request failed"); 862 } 863 864 evhttp_request_set_error_cb(req.get(), http_error_cb); 865 866 // Get credentials 867 std::string strRPCUserColonPass; 868 bool failedToGetAuthCookie = false; 869 if (gArgs.GetArg("-rpcpassword", "") == "") { 870 // Try fall back to cookie-based authentication if no password is provided 871 if (!GetAuthCookie(&strRPCUserColonPass)) { 872 failedToGetAuthCookie = true; 873 } 874 } else { 875 strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); 876 } 877 878 struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get()); 879 assert(output_headers); 880 evhttp_add_header(output_headers, "Host", host.c_str()); 881 evhttp_add_header(output_headers, "Connection", "close"); 882 evhttp_add_header(output_headers, "Content-Type", "application/json"); 883 evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); 884 885 // Attach request data 886 std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n"; 887 struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get()); 888 assert(output_buffer); 889 evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); 890 891 // check if we should use a special wallet endpoint 892 std::string endpoint = "/"; 893 if (rpcwallet) { 894 char* encodedURI = evhttp_uriencode(rpcwallet->data(), rpcwallet->size(), false); 895 if (encodedURI) { 896 endpoint = "/wallet/" + std::string(encodedURI); 897 free(encodedURI); 898 } else { 899 throw CConnectionFailed("uri-encode failed"); 900 } 901 } 902 int r = evhttp_make_request(evcon.get(), req.release(), EVHTTP_REQ_POST, endpoint.c_str()); 903 if (r != 0) { 904 throw CConnectionFailed("send http request failed"); 905 } 906 907 event_base_dispatch(base.get()); 908 909 if (response.status == 0) { 910 std::string responseErrorMessage; 911 if (response.error != -1) { 912 responseErrorMessage = strprintf(" (error code %d - \"%s\")", response.error, http_errorstring(response.error)); 913 } 914 throw CConnectionFailed(strprintf("Could not connect to the server %s:%d%s\n\n" 915 "Make sure the bitcoind server is running and that you are connecting to the correct RPC port.\n" 916 "Use \"bitcoin-cli -help\" for more info.", 917 host, port, responseErrorMessage)); 918 } else if (response.status == HTTP_UNAUTHORIZED) { 919 if (failedToGetAuthCookie) { 920 throw std::runtime_error(strprintf( 921 "Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)", 922 fs::PathToString(gArgs.GetConfigFilePath()))); 923 } else { 924 throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword"); 925 } 926 } else if (response.status == HTTP_SERVICE_UNAVAILABLE) { 927 throw std::runtime_error(strprintf("Server response: %s", response.body)); 928 } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) 929 throw std::runtime_error(strprintf("server returned HTTP error %d", response.status)); 930 else if (response.body.empty()) 931 throw std::runtime_error("no response from server"); 932 933 // Parse reply 934 UniValue valReply(UniValue::VSTR); 935 if (!valReply.read(response.body)) 936 throw std::runtime_error("couldn't parse reply from server"); 937 UniValue reply = rh->ProcessReply(valReply); 938 if (reply.empty()) 939 throw std::runtime_error("expected reply to have result, error and id properties"); 940 941 return reply; 942 } 943 944 /** 945 * ConnectAndCallRPC wraps CallRPC with -rpcwait and an exception handler. 946 * 947 * @param[in] rh Pointer to RequestHandler. 948 * @param[in] strMethod Reference to const string method to forward to CallRPC. 949 * @param[in] rpcwallet Reference to const optional string wallet name to forward to CallRPC. 950 * @returns the RPC response as a UniValue object. 951 * @throws a CConnectionFailed std::runtime_error if connection failed or RPC server still in warmup. 952 */ 953 static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {}) 954 { 955 UniValue response(UniValue::VOBJ); 956 // Execute and handle connection failures with -rpcwait. 957 const bool fWait = gArgs.GetBoolArg("-rpcwait", false); 958 const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT); 959 const auto deadline{std::chrono::steady_clock::now() + 1s * timeout}; 960 961 do { 962 try { 963 response = CallRPC(rh, strMethod, args, rpcwallet); 964 if (fWait) { 965 const UniValue& error = response.find_value("error"); 966 if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) { 967 throw CConnectionFailed("server in warmup"); 968 } 969 } 970 break; // Connection succeeded, no need to retry. 971 } catch (const CConnectionFailed& e) { 972 if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) { 973 UninterruptibleSleep(1s); 974 } else { 975 throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what())); 976 } 977 } 978 } while (fWait); 979 return response; 980 } 981 982 /** Parse UniValue result to update the message to print to std::cout. */ 983 static void ParseResult(const UniValue& result, std::string& strPrint) 984 { 985 if (result.isNull()) return; 986 strPrint = result.isStr() ? result.get_str() : result.write(2); 987 } 988 989 /** Parse UniValue error to update the message to print to std::cerr and the code to return. */ 990 static void ParseError(const UniValue& error, std::string& strPrint, int& nRet) 991 { 992 if (error.isObject()) { 993 const UniValue& err_code = error.find_value("code"); 994 const UniValue& err_msg = error.find_value("message"); 995 if (!err_code.isNull()) { 996 strPrint = "error code: " + err_code.getValStr() + "\n"; 997 } 998 if (err_msg.isStr()) { 999 strPrint += ("error message:\n" + err_msg.get_str()); 1000 } 1001 if (err_code.isNum() && err_code.getInt<int>() == RPC_WALLET_NOT_SPECIFIED) { 1002 strPrint += " Or for the CLI, specify the \"-rpcwallet=<walletname>\" option before the command"; 1003 strPrint += " (run \"bitcoin-cli -h\" for help or \"bitcoin-cli listwallets\" to see which wallets are currently loaded)."; 1004 } 1005 } else { 1006 strPrint = "error: " + error.write(); 1007 } 1008 nRet = abs(error["code"].getInt<int>()); 1009 } 1010 1011 /** 1012 * GetWalletBalances calls listwallets; if more than one wallet is loaded, it then 1013 * fetches mine.trusted balances for each loaded wallet and pushes them to `result`. 1014 * 1015 * @param result Reference to UniValue object the wallet names and balances are pushed to. 1016 */ 1017 static void GetWalletBalances(UniValue& result) 1018 { 1019 DefaultRequestHandler rh; 1020 const UniValue listwallets = ConnectAndCallRPC(&rh, "listwallets", /* args=*/{}); 1021 if (!listwallets.find_value("error").isNull()) return; 1022 const UniValue& wallets = listwallets.find_value("result"); 1023 if (wallets.size() <= 1) return; 1024 1025 UniValue balances(UniValue::VOBJ); 1026 for (const UniValue& wallet : wallets.getValues()) { 1027 const std::string& wallet_name = wallet.get_str(); 1028 const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name); 1029 const UniValue& balance = getbalances.find_value("result")["mine"]["trusted"]; 1030 balances.pushKV(wallet_name, balance); 1031 } 1032 result.pushKV("balances", std::move(balances)); 1033 } 1034 1035 /** 1036 * GetProgressBar constructs a progress bar with 5% intervals. 1037 * 1038 * @param[in] progress The proportion of the progress bar to be filled between 0 and 1. 1039 * @param[out] progress_bar String representation of the progress bar. 1040 */ 1041 static void GetProgressBar(double progress, std::string& progress_bar) 1042 { 1043 if (progress < 0 || progress > 1) return; 1044 1045 static constexpr double INCREMENT{0.05}; 1046 static const std::string COMPLETE_BAR{"\u2592"}; 1047 static const std::string INCOMPLETE_BAR{"\u2591"}; 1048 1049 for (int i = 0; i < progress / INCREMENT; ++i) { 1050 progress_bar += COMPLETE_BAR; 1051 } 1052 1053 for (int i = 0; i < (1 - progress) / INCREMENT; ++i) { 1054 progress_bar += INCOMPLETE_BAR; 1055 } 1056 } 1057 1058 /** 1059 * ParseGetInfoResult takes in -getinfo result in UniValue object and parses it 1060 * into a user friendly UniValue string to be printed on the console. 1061 * @param[out] result Reference to UniValue result containing the -getinfo output. 1062 */ 1063 static void ParseGetInfoResult(UniValue& result) 1064 { 1065 if (!result.find_value("error").isNull()) return; 1066 1067 std::string RESET, GREEN, BLUE, YELLOW, MAGENTA, CYAN; 1068 bool should_colorize = false; 1069 1070 #ifndef WIN32 1071 if (isatty(fileno(stdout))) { 1072 // By default, only print colored text if OS is not WIN32 and stdout is connected to a terminal. 1073 should_colorize = true; 1074 } 1075 #endif 1076 1077 { 1078 const std::string color{gArgs.GetArg("-color", DEFAULT_COLOR_SETTING)}; 1079 if (color == "always") { 1080 should_colorize = true; 1081 } else if (color == "never") { 1082 should_colorize = false; 1083 } else if (color != "auto") { 1084 throw std::runtime_error("Invalid value for -color option. Valid values: always, auto, never."); 1085 } 1086 } 1087 1088 if (should_colorize) { 1089 RESET = "\x1B[0m"; 1090 GREEN = "\x1B[32m"; 1091 BLUE = "\x1B[34m"; 1092 YELLOW = "\x1B[33m"; 1093 MAGENTA = "\x1B[35m"; 1094 CYAN = "\x1B[36m"; 1095 } 1096 1097 std::string result_string = strprintf("%sChain: %s%s\n", BLUE, result["chain"].getValStr(), RESET); 1098 result_string += strprintf("Blocks: %s\n", result["blocks"].getValStr()); 1099 result_string += strprintf("Headers: %s\n", result["headers"].getValStr()); 1100 1101 const double ibd_progress{result["verificationprogress"].get_real()}; 1102 std::string ibd_progress_bar; 1103 // Display the progress bar only if IBD progress is less than 99% 1104 if (ibd_progress < 0.99) { 1105 GetProgressBar(ibd_progress, ibd_progress_bar); 1106 // Add padding between progress bar and IBD progress 1107 ibd_progress_bar += " "; 1108 } 1109 1110 result_string += strprintf("Verification progress: %s%.4f%%\n", ibd_progress_bar, ibd_progress * 100); 1111 result_string += strprintf("Difficulty: %s\n\n", result["difficulty"].getValStr()); 1112 1113 result_string += strprintf( 1114 "%sNetwork: in %s, out %s, total %s%s\n", 1115 GREEN, 1116 result["connections"]["in"].getValStr(), 1117 result["connections"]["out"].getValStr(), 1118 result["connections"]["total"].getValStr(), 1119 RESET); 1120 result_string += strprintf("Version: %s\n", result["version"].getValStr()); 1121 result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr()); 1122 1123 // proxies 1124 std::map<std::string, std::vector<std::string>> proxy_networks; 1125 std::vector<std::string> ordered_proxies; 1126 1127 for (const UniValue& network : result["networks"].getValues()) { 1128 const std::string proxy = network["proxy"].getValStr(); 1129 if (proxy.empty()) continue; 1130 // Add proxy to ordered_proxy if has not been processed 1131 if (!proxy_networks.contains(proxy)) ordered_proxies.push_back(proxy); 1132 1133 proxy_networks[proxy].push_back(network["name"].getValStr()); 1134 } 1135 1136 std::vector<std::string> formatted_proxies; 1137 formatted_proxies.reserve(ordered_proxies.size()); 1138 for (const std::string& proxy : ordered_proxies) { 1139 formatted_proxies.emplace_back(strprintf("%s (%s)", proxy, Join(proxy_networks.find(proxy)->second, ", "))); 1140 } 1141 result_string += strprintf("Proxies: %s\n", formatted_proxies.empty() ? "n/a" : Join(formatted_proxies, ", ")); 1142 1143 result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr()); 1144 1145 if (!result["has_wallet"].isNull()) { 1146 const std::string walletname = result["walletname"].getValStr(); 1147 result_string += strprintf("%sWallet: %s%s\n", MAGENTA, walletname.empty() ? "\"\"" : walletname, RESET); 1148 1149 result_string += strprintf("Keypool size: %s\n", result["keypoolsize"].getValStr()); 1150 if (!result["unlocked_until"].isNull()) { 1151 result_string += strprintf("Unlocked until: %s\n", result["unlocked_until"].getValStr()); 1152 } 1153 } 1154 if (!result["balance"].isNull()) { 1155 result_string += strprintf("%sBalance:%s %s\n\n", CYAN, RESET, result["balance"].getValStr()); 1156 } 1157 1158 if (!result["balances"].isNull()) { 1159 result_string += strprintf("%sBalances%s\n", CYAN, RESET); 1160 1161 size_t max_balance_length{10}; 1162 1163 for (const std::string& wallet : result["balances"].getKeys()) { 1164 max_balance_length = std::max(result["balances"][wallet].getValStr().length(), max_balance_length); 1165 } 1166 1167 for (const std::string& wallet : result["balances"].getKeys()) { 1168 result_string += strprintf("%*s %s\n", 1169 max_balance_length, 1170 result["balances"][wallet].getValStr(), 1171 wallet.empty() ? "\"\"" : wallet); 1172 } 1173 result_string += "\n"; 1174 } 1175 1176 const std::string warnings{result["warnings"].getValStr()}; 1177 result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, warnings.empty() ? "(none)" : warnings); 1178 1179 result.setStr(result_string); 1180 } 1181 1182 /** 1183 * Call RPC getnewaddress. 1184 * @returns getnewaddress response as a UniValue object. 1185 */ 1186 static UniValue GetNewAddress() 1187 { 1188 DefaultRequestHandler rh; 1189 return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, RpcWalletName(gArgs)); 1190 } 1191 1192 /** 1193 * Check bounds and set up args for RPC generatetoaddress params: nblocks, address, maxtries. 1194 * @param[in] address Reference to const string address to insert into the args. 1195 * @param args Reference to vector of string args to modify. 1196 */ 1197 static void SetGenerateToAddressArgs(const std::string& address, std::vector<std::string>& args) 1198 { 1199 if (args.size() > 2) throw std::runtime_error("too many arguments (maximum 2 for nblocks and maxtries)"); 1200 if (args.size() == 0) { 1201 args.emplace_back(DEFAULT_NBLOCKS); 1202 } else if (args.at(0) == "0") { 1203 throw std::runtime_error("the first argument (number of blocks to generate, default: " + DEFAULT_NBLOCKS + ") must be an integer value greater than zero"); 1204 } 1205 args.emplace(args.begin() + 1, address); 1206 } 1207 1208 static int CommandLineRPC(int argc, char *argv[]) 1209 { 1210 std::string strPrint; 1211 int nRet = 0; 1212 try { 1213 // Skip switches 1214 while (argc > 1 && IsSwitchChar(argv[1][0])) { 1215 argc--; 1216 argv++; 1217 } 1218 std::string rpcPass; 1219 if (gArgs.GetBoolArg("-stdinrpcpass", false)) { 1220 NO_STDIN_ECHO(); 1221 if (!StdinReady()) { 1222 fputs("RPC password> ", stderr); 1223 fflush(stderr); 1224 } 1225 if (!std::getline(std::cin, rpcPass)) { 1226 throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input"); 1227 } 1228 if (StdinTerminal()) { 1229 fputc('\n', stdout); 1230 } 1231 gArgs.ForceSetArg("-rpcpassword", rpcPass); 1232 } 1233 std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]); 1234 if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) { 1235 NO_STDIN_ECHO(); 1236 std::string walletPass; 1237 if (args.size() < 1 || !args[0].starts_with("walletpassphrase")) { 1238 throw std::runtime_error("-stdinwalletpassphrase is only applicable for walletpassphrase(change)"); 1239 } 1240 if (!StdinReady()) { 1241 fputs("Wallet passphrase> ", stderr); 1242 fflush(stderr); 1243 } 1244 if (!std::getline(std::cin, walletPass)) { 1245 throw std::runtime_error("-stdinwalletpassphrase specified but failed to read from standard input"); 1246 } 1247 if (StdinTerminal()) { 1248 fputc('\n', stdout); 1249 } 1250 args.insert(args.begin() + 1, walletPass); 1251 } 1252 if (gArgs.GetBoolArg("-stdin", false)) { 1253 // Read one arg per line from stdin and append 1254 std::string line; 1255 while (std::getline(std::cin, line)) { 1256 args.push_back(line); 1257 } 1258 if (StdinTerminal()) { 1259 fputc('\n', stdout); 1260 } 1261 } 1262 gArgs.CheckMultipleCLIArgs(); 1263 std::unique_ptr<BaseRequestHandler> rh; 1264 std::string method; 1265 if (gArgs.GetBoolArg("-getinfo", false)) { 1266 rh.reset(new GetinfoRequestHandler()); 1267 } else if (gArgs.GetBoolArg("-netinfo", false)) { 1268 if (!args.empty() && (args.at(0) == "h" || args.at(0) == "help")) { 1269 tfm::format(std::cout, "%s\n", NetinfoRequestHandler().m_help_doc); 1270 return 0; 1271 } 1272 rh.reset(new NetinfoRequestHandler()); 1273 } else if (gArgs.GetBoolArg("-generate", false)) { 1274 const UniValue getnewaddress{GetNewAddress()}; 1275 const UniValue& error{getnewaddress.find_value("error")}; 1276 if (error.isNull()) { 1277 SetGenerateToAddressArgs(getnewaddress.find_value("result").get_str(), args); 1278 rh.reset(new GenerateToAddressRequestHandler()); 1279 } else { 1280 ParseError(error, strPrint, nRet); 1281 } 1282 } else if (gArgs.GetBoolArg("-addrinfo", false)) { 1283 rh.reset(new AddrinfoRequestHandler()); 1284 } else { 1285 rh.reset(new DefaultRequestHandler()); 1286 if (args.size() < 1) { 1287 throw std::runtime_error("too few parameters (need at least command)"); 1288 } 1289 method = args[0]; 1290 args.erase(args.begin()); // Remove trailing method name from arguments vector 1291 } 1292 if (nRet == 0) { 1293 // Perform RPC call 1294 const std::optional<std::string> wallet_name{RpcWalletName(gArgs)}; 1295 const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name); 1296 1297 // Parse reply 1298 UniValue result = reply.find_value("result"); 1299 const UniValue& error = reply.find_value("error"); 1300 if (error.isNull()) { 1301 if (gArgs.GetBoolArg("-getinfo", false)) { 1302 if (!wallet_name) { 1303 GetWalletBalances(result); // fetch multiwallet balances and append to result 1304 } 1305 ParseGetInfoResult(result); 1306 } 1307 1308 ParseResult(result, strPrint); 1309 } else { 1310 ParseError(error, strPrint, nRet); 1311 } 1312 } 1313 } catch (const std::exception& e) { 1314 strPrint = std::string("error: ") + e.what(); 1315 nRet = EXIT_FAILURE; 1316 } catch (...) { 1317 PrintExceptionContinue(nullptr, "CommandLineRPC()"); 1318 throw; 1319 } 1320 1321 if (strPrint != "") { 1322 tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint); 1323 } 1324 return nRet; 1325 } 1326 1327 MAIN_FUNCTION 1328 { 1329 SetupEnvironment(); 1330 if (!SetupNetworking()) { 1331 tfm::format(std::cerr, "Error: Initializing networking failed\n"); 1332 return EXIT_FAILURE; 1333 } 1334 event_set_log_callback(&libevent_log_cb); 1335 1336 try { 1337 int ret = AppInitRPC(argc, argv); 1338 if (ret != CONTINUE_EXECUTION) 1339 return ret; 1340 } 1341 catch (const std::exception& e) { 1342 PrintExceptionContinue(&e, "AppInitRPC()"); 1343 return EXIT_FAILURE; 1344 } catch (...) { 1345 PrintExceptionContinue(nullptr, "AppInitRPC()"); 1346 return EXIT_FAILURE; 1347 } 1348 1349 int ret = EXIT_FAILURE; 1350 try { 1351 ret = CommandLineRPC(argc, argv); 1352 } 1353 catch (const std::exception& e) { 1354 PrintExceptionContinue(&e, "CommandLineRPC()"); 1355 } catch (...) { 1356 PrintExceptionContinue(nullptr, "CommandLineRPC()"); 1357 } 1358 return ret; 1359 }