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