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