Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
bitcoin-cli.cpp
Go to the documentation of this file.
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/license_info.h>
12#include <common/system.h>
13#include <common/url.h>
14#include <compat/compat.h>
15#include <compat/stdin.h>
16#include <interfaces/init.h>
17#include <interfaces/ipc.h>
18#include <interfaces/rpc.h>
19#include <netbase.h>
20#include <policy/feerate.h>
21#include <rpc/client.h>
22#include <rpc/mining.h>
23#include <rpc/protocol.h>
24#include <rpc/request.h>
25#include <tinyformat.h>
26#include <univalue.h>
27#include <util/chaintype.h>
28#include <util/exception.h>
29#include <util/sock.h>
30#include <util/strencodings.h>
31#include <util/string.h>
32#include <util/time.h>
33#include <util/translation.h>
34
35#include <algorithm>
36#include <chrono>
37#include <cmath>
38#include <cstdio>
39#include <functional>
40#include <limits>
41#include <memory>
42#include <optional>
43#include <span>
44#include <string>
45#include <string_view>
46#include <thread>
47#include <tuple>
48
49#ifndef WIN32
50#include <unistd.h>
51#endif
52
53using util::Join;
54using util::ToString;
55
56// The server returns time values from a mockable system clock, but it is not
57// trivial to get the mocked time from the server, nor is it needed for now, so
58// just use a plain system_clock.
59using CliClock = std::chrono::system_clock;
60
62
63static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
64static constexpr const char* DEFAULT_RPC_REQ_ID{"1"};
65static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
66static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;
67static const bool DEFAULT_NAMED=false;
68static const int CONTINUE_EXECUTION=-1;
69static constexpr uint8_t NETINFO_MAX_LEVEL{4};
70static constexpr int8_t UNKNOWN_NETWORK{-1};
71// See GetNetworkName() in netbase.cpp
72static constexpr std::array NETWORKS{"not_publicly_routable", "ipv4", "ipv6", "onion", "i2p", "cjdns", "internal"};
73static constexpr std::array NETWORK_SHORT_NAMES{"npr", "ipv4", "ipv6", "onion", "i2p", "cjdns", "int"};
74static constexpr std::array UNREACHABLE_NETWORK_IDS{/*not_publicly_routable*/0, /*internal*/6};
75
77static const std::string DEFAULT_NBLOCKS = "1";
78
80static const std::string DEFAULT_COLOR_SETTING{"auto"};
81
82struct HTTPError : std::runtime_error {
83 explicit inline HTTPError(const std::string& msg) : std::runtime_error(msg) {}
84};
85
93{
94 std::vector<std::pair<std::string, std::string>> m_headers;
95
96public:
98 std::optional<std::string> FindFirst(std::string_view key) const;
99};
100
101// Named Read() in HTTPHeaders (see PR #35182).
103{
104 // Headers https://httpwg.org/specs/rfc9110.html#rfc.section.6.3
105 // A sequence of Field Lines https://httpwg.org/specs/rfc9110.html#rfc.section.5.2
106 while (auto maybe_line = reader.ReadLine()) {
107 const std::string_view line = *maybe_line;
108
109 // An empty line indicates end of the headers section https://www.rfc-editor.org/rfc/rfc2616#section-4
110 if (line.empty()) return;
111
112 // Header line must have at least one ":"
113 // keys are not allowed to have delimiters like ":" but values are
114 // https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.2
115 const size_t pos{line.find(':')};
116 if (pos == std::string::npos) throw HTTPError{"Header missing colon (:)"};
117
118 // Whitespace is optional
119 std::string key = util::TrimString(std::string_view(line).substr(0, pos));
120 std::string value = util::TrimString(std::string_view(line).substr(pos + 1));
121
122 // Header keys are Field Names: https://httpwg.org/specs/rfc9110.html#fields.names
123 // which consist of "tokens": https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.2
124 // that can not be empty.
125 if (key.empty()) throw HTTPError{"Empty header name"};
126
127 m_headers.emplace_back(std::move(key), std::move(value));
128 }
129}
130
131std::optional<std::string> HTTPResponseHeaders::FindFirst(std::string_view key) const
132{
133 for (const auto& item : m_headers) {
134 if (CaseInsensitiveEqual(key, item.first)) {
135 return item.second;
136 }
137 }
138 return std::nullopt;
139}
140
142{
144
150
151 argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
152 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);
153 argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
154 argsman.AddArg("-generate",
155 strprintf("Generate blocks, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer "
156 "arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to "
157 "RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000",
160 argsman.AddArg("-addrinfo", "Get the number of addresses known to the node, per network and total.", ArgsManager::ALLOW_ANY, OptionsCategory::CLI_COMMANDS);
161 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);
162 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);
163
165 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);
166 argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
167 argsman.AddArg("-rpcid=<id>", strprintf("Set a custom JSON-RPC request ID string (default: %s)", DEFAULT_RPC_REQ_ID), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION | ArgsManager::DISALLOW_ELISION, OptionsCategory::OPTIONS);
168 argsman.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for the longest supported timeout (about 24 days). Not implemented for IPC connections (see -ipcconnect). (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
169 argsman.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
170 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);
171 argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
172 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);
173 argsman.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
174 argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
175 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);
176 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);
177 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);
178 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);
179 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);
180 argsman.AddArg("-ipcconnect=<address>", "Connect to bitcoin-node through IPC socket instead of TCP socket to execute requests. Valid <address> values are 'auto' to try to connect to default socket path at <datadir>/node.sock but fall back to TCP if it is not available, 'unix' to connect to the default socket and fail if it isn't available, or 'unix:<socket path>' to connect to a socket at a nonstandard path. -noipcconnect can be specified to avoid attempting to use IPC at all. Default value: auto", ArgsManager::ALLOW_ANY, OptionsCategory::IPC);
181}
182
183std::optional<std::string> RpcWalletName(const ArgsManager& args)
184{
185 // Check IsArgNegated to return nullopt instead of "0" if -norpcwallet is specified
186 if (args.IsArgNegated("-rpcwallet")) return std::nullopt;
187 return args.GetArg("-rpcwallet");
188}
189
190//
191// Exception thrown on connection error. This error is used to determine
192// when to wait if -rpcwait is given.
193//
194struct CConnectionFailed : std::runtime_error {
195 explicit inline CConnectionFailed(const std::string& msg) :
196 std::runtime_error(msg)
197 {}
198};
199
200//
201// This function returns either one of EXIT_ codes when it's expected to stop the process or
202// CONTINUE_EXECUTION when it's expected to continue further.
203//
204static int AppInitRPC(int argc, char* argv[])
205{
207 std::string error;
208 if (!gArgs.ParseParameters(argc, argv, error)) {
209 tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error);
210 return EXIT_FAILURE;
211 }
212 if (argc < 2 || HelpRequested(gArgs) || gArgs.GetBoolArg("-version", false)) {
213 std::string strUsage = CLIENT_NAME " RPC client version " + FormatFullVersion() + "\n";
214
215 if (gArgs.GetBoolArg("-version", false)) {
217 } else {
218 strUsage += "\n"
219 "The bitcoin-cli utility provides a command line interface to interact with a " CLIENT_NAME " RPC server.\n"
220 "\nIt can be used to query network information, manage wallets, create or broadcast transactions, and control the " CLIENT_NAME " server.\n"
221 "\nUse the \"help\" command to list all commands. Use \"help <command>\" to show help for that command.\n"
222 "The -named option allows you to specify parameters using the key=value format, eliminating the need to pass unused positional parameters.\n"
223 "\n"
224 "Usage: bitcoin-cli [options] <command> [params]\n"
225 "or: bitcoin-cli [options] -named <command> [name=value]...\n"
226 "or: bitcoin-cli [options] help\n"
227 "or: bitcoin-cli [options] help <command>\n"
228 "\n";
229 strUsage += "\n" + gArgs.GetHelpMessage();
230 }
231
232 tfm::format(std::cout, "%s", strUsage);
233 if (argc < 2) {
234 tfm::format(std::cerr, "Error: too few parameters\n");
235 return EXIT_FAILURE;
236 }
237 return EXIT_SUCCESS;
238 }
240 tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""));
241 return EXIT_FAILURE;
242 }
243 if (!gArgs.ReadConfigFiles(error, true)) {
244 tfm::format(std::cerr, "Error reading configuration file: %s\n", error);
245 return EXIT_FAILURE;
246 }
247 // Check for chain settings (BaseParams() calls are only valid after this clause)
248 try {
250 } catch (const std::exception& e) {
251 tfm::format(std::cerr, "Error: %s\n", e.what());
252 return EXIT_FAILURE;
253 }
254 return CONTINUE_EXECUTION;
255}
256
258{
259 int status{0};
260 std::string body;
261};
262
263static int8_t NetworkStringToId(const std::string& str)
264{
265 for (size_t i = 0; i < NETWORKS.size(); ++i) {
266 if (str == NETWORKS[i]) return i;
267 }
268 return UNKNOWN_NETWORK;
269}
270
275 virtual ~BaseRequestHandler() = default;
276 virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0;
278};
279
282 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
283 {
284 if (!args.empty()) {
285 throw std::runtime_error("-addrinfo takes no arguments");
286 }
287 return JSONRPCRequestObj("getaddrmaninfo", NullUniValue, 1);
288 }
289
290 UniValue ProcessReply(const UniValue& reply) override
291 {
292 if (!reply["error"].isNull()) {
293 if (reply["error"]["code"].getInt<int>() == RPC_METHOD_NOT_FOUND) {
294 throw std::runtime_error("-addrinfo requires bitcoind v26.0 or later which supports getaddrmaninfo RPC. Please upgrade your node or use bitcoin-cli from the same version.");
295 }
296 return reply;
297 }
298 // Process getaddrmaninfo reply
299 const std::vector<std::string>& network_types{reply["result"].getKeys()};
300 const std::vector<UniValue>& addrman_counts{reply["result"].getValues()};
301
302 // Prepare result to return to user.
304
305 for (size_t i = 0; i < network_types.size(); ++i) {
306 int addr_count = addrman_counts[i]["total"].getInt<int>();
307 if (network_types[i] == "all_networks") {
308 addresses.pushKV("total", addr_count);
309 } else {
311 }
312 }
313 result.pushKV("addresses_known", std::move(addresses));
314 return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);
315 }
316};
317
320 const int ID_NETWORKINFO = 0;
321 const int ID_BLOCKCHAININFO = 1;
322 const int ID_WALLETINFO = 2;
323 const int ID_BALANCES = 3;
324
326 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
327 {
328 if (!args.empty()) {
329 throw std::runtime_error("-getinfo takes no arguments");
330 }
331 UniValue result(UniValue::VARR);
332 result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
333 result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO));
334 result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO));
335 result.push_back(JSONRPCRequestObj("getbalances", NullUniValue, ID_BALANCES));
336 return result;
337 }
338
341 {
342 UniValue result(UniValue::VOBJ);
343 const std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in);
344 // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on;
345 // getwalletinfo() and getbalances() are allowed to fail if there is no wallet.
346 if (!batch[ID_NETWORKINFO]["error"].isNull()) {
347 return batch[ID_NETWORKINFO];
348 }
349 if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) {
350 return batch[ID_BLOCKCHAININFO];
351 }
352 result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]);
353 result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]);
354 result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]);
355 result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]);
356 result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]);
357
359 connections.pushKV("in", batch[ID_NETWORKINFO]["result"]["connections_in"]);
360 connections.pushKV("out", batch[ID_NETWORKINFO]["result"]["connections_out"]);
361 connections.pushKV("total", batch[ID_NETWORKINFO]["result"]["connections"]);
362 result.pushKV("connections", std::move(connections));
363
364 result.pushKV("networks", batch[ID_NETWORKINFO]["result"]["networks"]);
365 result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
366 result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
367 if (!batch[ID_WALLETINFO]["result"].isNull()) {
368 result.pushKV("has_wallet", true);
369 result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]);
370 result.pushKV("walletname", batch[ID_WALLETINFO]["result"]["walletname"]);
371 if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) {
372 result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]);
373 }
374 }
375 if (!batch[ID_BALANCES]["result"].isNull()) {
376 result.pushKV("balance", batch[ID_BALANCES]["result"]["mine"]["trusted"]);
377 }
378 result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]);
379 result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]);
380 return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);
381 }
382};
383
386{
387private:
388 std::array<std::array<uint16_t, NETWORKS.size() + 1>, 3> m_counts{{{}}};
392 bool DetailsRequested() const { return m_details_level; }
393 bool IsAddressSelected() const { return m_details_level == 2 || m_details_level == 4; }
394 bool IsVersionSelected() const { return m_details_level == 3 || m_details_level == 4; }
396 bool m_is_asmap_on{false};
403 struct Peer {
404 std::string addr;
405 std::string sub_version;
406 std::string conn_type;
407 std::string network;
408 std::string age;
409 std::string services;
411 double min_ping;
412 double ping;
419 int id;
427 bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
428 };
429 std::vector<Peer> m_peers;
430 std::string ChainToString() const
431 {
432 switch (gArgs.GetChainType()) {
434 return " testnet4";
436 return " testnet";
438 return " signet";
440 return " regtest";
441 case ChainType::MAIN:
442 return "";
443 }
444 assert(false);
445 }
446 std::string PingTimeToString(double seconds) const
447 {
448 if (seconds < 0) return "";
449 const double milliseconds{round(1000 * seconds)};
450 return milliseconds > 999999 ? "-" : ToString(milliseconds);
451 }
452 std::string ConnectionTypeForNetinfo(const std::string& conn_type) const
453 {
454 if (conn_type == "outbound-full-relay") return "full";
455 if (conn_type == "block-relay-only") return "block";
456 if (conn_type == "manual" || conn_type == "feeler") return conn_type;
457 if (conn_type == "addr-fetch") return "addr";
458 if (conn_type == "private-broadcast") return "priv";
459 return "";
460 }
461 std::string FormatServices(const UniValue& services)
462 {
463 std::string str;
464 for (size_t i = 0; i < services.size(); ++i) {
465 const std::string s{services[i].get_str()};
466 str += s == "NETWORK_LIMITED" ? 'l' : s == "P2P_V2" ? '2' : ToLower(s[0]);
467 }
468 return str;
469 }
470 static std::string ServicesList(const UniValue& services)
471 {
472 std::string str{services.size() ? services[0].get_str() : ""};
473 for (size_t i{1}; i < services.size(); ++i) {
474 str += ", " + services[i].get_str();
475 }
476 for (auto& c: str) {
477 c = (c == '_' ? ' ' : ToLower(c));
478 }
479 return str;
480 }
481
482public:
483 static constexpr int ID_PEERINFO = 0;
484 static constexpr int ID_NETWORKINFO = 1;
485
486 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
487 {
488 if (!args.empty()) {
489 uint8_t n{0};
490 if (const auto res{ToIntegral<uint8_t>(args.at(0))}) {
491 n = *res;
492 m_details_level = std::min(n, NETINFO_MAX_LEVEL);
493 } else {
494 throw std::runtime_error(strprintf("invalid -netinfo level argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0)));
495 }
496 if (args.size() > 1) {
497 if (std::string_view s{args.at(1)}; n && (s == "o" || s == "outonly")) {
499 } else if (n) {
500 throw std::runtime_error(strprintf("invalid -netinfo outonly argument: %s\nFor more information, run: bitcoin-cli -netinfo help", s));
501 } else {
502 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));
503 }
504 }
505 }
506 UniValue result(UniValue::VARR);
507 result.push_back(JSONRPCRequestObj("getpeerinfo", NullUniValue, ID_PEERINFO));
508 result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
509 return result;
510 }
511
513 {
514 const std::vector<UniValue> batch{JSONRPCProcessBatchReply(batch_in)};
515 if (!batch[ID_PEERINFO]["error"].isNull()) return batch[ID_PEERINFO];
516 if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
517
518 const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
519 if (networkinfo["version"].getInt<int>() < 209900) {
520 throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
521 }
523
524 // Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
525 for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) {
526 const std::string network{peer["network"].get_str()};
527 const int8_t network_id{NetworkStringToId(network)};
528 if (network_id == UNKNOWN_NETWORK) continue;
529 const bool is_outbound{!peer["inbound"].get_bool()};
530 const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()};
531 const std::string conn_type{peer["connection_type"].get_str()};
532 ++m_counts.at(is_outbound).at(network_id); // in/out by network
533 ++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall
534 ++m_counts.at(2).at(network_id); // total by network
535 ++m_counts.at(2).at(NETWORKS.size()); // total overall
536 if (conn_type == "block-relay-only") ++m_block_relay_peers_count;
537 if (conn_type == "manual") ++m_manual_peers_count;
538 if (m_outbound_only_selected && !is_outbound) continue;
539 if (DetailsRequested()) {
540 // Push data for this peer to the peers vector.
541 const int peer_id{peer["id"].getInt<int>()};
542 const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].getInt<int>()};
543 const int version{peer["version"].getInt<int>()};
544 const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].getInt<int64_t>()};
545 const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].getInt<int64_t>()};
546 const int64_t conn_time{peer["conntime"].getInt<int64_t>()};
547 const int64_t last_blck{peer["last_block"].getInt<int64_t>()};
548 const int64_t last_recv{peer["lastrecv"].getInt<int64_t>()};
549 const int64_t last_send{peer["lastsend"].getInt<int64_t>()};
550 const int64_t last_trxn{peer["last_transaction"].getInt<int64_t>()};
551 const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
552 const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
553 const std::string addr{peer["addr"].get_str()};
554 const std::string age{conn_time == 0 ? "" : ToString((time_now - conn_time) / 60)};
555 const std::string services{FormatServices(peer["servicesnames"])};
556 const std::string sub_version{peer["subver"].get_str()};
557 const std::string transport{peer["transport_protocol_type"].isNull() ? "v1" : peer["transport_protocol_type"].get_str()};
558 const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
559 const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
560 const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
561 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});
562 m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
563 m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
564 m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
565 m_max_age_length = std::max(age.length(), m_max_age_length);
566 m_max_id_length = std::max(ToString(peer_id).length(), m_max_id_length);
567 m_max_services_length = std::max(services.length(), m_max_services_length);
568 m_is_asmap_on |= (mapped_as != 0);
569 }
570 }
571
572 // Generate report header.
573 const std::string services{DetailsRequested() ? strprintf(" - services %s", FormatServices(networkinfo["localservicesnames"])) : ""};
574 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)};
575
576 // Report detailed peer connections list sorted by direction and minimum ping time.
577 if (DetailsRequested() && !m_peers.empty()) {
578 std::sort(m_peers.begin(), m_peers.end());
579 result += strprintf("<-> type net %*s v mping ping send recv txn blk hb %*s%*s%*s ",
580 m_max_services_length, "serv",
583 m_max_age_length, "age");
584 if (m_is_asmap_on) result += " asmap ";
585 result += strprintf("%*s %-*s%s\n", m_max_id_length, "id", IsAddressSelected() ? m_max_addr_length : 0, IsAddressSelected() ? "address" : "", IsVersionSelected() ? "version" : "");
586 for (const Peer& peer : m_peers) {
587 std::string version{ToString(peer.version) + peer.sub_version};
588 result += strprintf(
589 "%3s %6s %5s %*s %2s%7s%7s%5s%5s%5s%5s %2s %*s%*s%*s%*i %*s %-*s%s\n",
590 peer.is_outbound ? "out" : "in",
591 ConnectionTypeForNetinfo(peer.conn_type),
592 peer.network,
593 m_max_services_length, // variable spacing
594 peer.services,
595 (peer.transport_protocol_type.size() == 2 && peer.transport_protocol_type[0] == 'v') ? peer.transport_protocol_type[1] : ' ',
596 PingTimeToString(peer.min_ping),
597 PingTimeToString(peer.ping),
598 peer.last_send ? ToString(time_now - peer.last_send) : "",
599 peer.last_recv ? ToString(time_now - peer.last_recv) : "",
600 peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*",
601 peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "",
602 strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
603 m_max_addr_processed_length, // variable spacing
604 peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".",
605 m_max_addr_rate_limited_length, // variable spacing
606 peer.addr_rate_limited ? ToString(peer.addr_rate_limited) : "",
607 m_max_age_length, // variable spacing
608 peer.age,
609 m_is_asmap_on ? 7 : 0, // variable spacing
610 m_is_asmap_on && peer.mapped_as ? ToString(peer.mapped_as) : "",
611 m_max_id_length, // variable spacing
612 peer.id,
613 IsAddressSelected() ? m_max_addr_length : 0, // variable spacing
614 IsAddressSelected() ? peer.addr : "",
615 IsVersionSelected() && version != "0" ? version : "");
616 }
617 result += strprintf(" %*s ms ms sec sec min min %*s\n\n", m_max_services_length, "", m_max_age_length, "min");
618 }
619
620 // Report peer connection totals by type.
621 result += " ";
622 std::vector<int8_t> reachable_networks;
623 for (const UniValue& network : networkinfo["networks"].getValues()) {
624 if (network["reachable"].get_bool()) {
625 const std::string& network_name{network["name"].get_str()};
627 if (network_id == UNKNOWN_NETWORK) continue;
628 result += strprintf("%8s", network_name); // column header
630 }
631 };
632
633 for (const size_t network_id : UNREACHABLE_NETWORK_IDS) {
634 if (m_counts.at(2).at(network_id) == 0) continue;
635 result += strprintf("%8s", NETWORK_SHORT_NAMES.at(network_id)); // column header
637 }
638
639 result += " total block";
640 if (m_manual_peers_count) result += " manual";
641
642 const std::array rows{"in", "out", "total"};
643 for (size_t i = 0; i < rows.size(); ++i) {
644 result += strprintf("\n%-5s", rows[i]); // row header
645 for (int8_t n : reachable_networks) {
646 result += strprintf("%8i", m_counts.at(i).at(n)); // network peers count
647 }
648 result += strprintf(" %5i", m_counts.at(i).at(NETWORKS.size())); // total peers count
649 if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
650 result += strprintf(" %5i", m_block_relay_peers_count);
652 }
653 }
654
655 // Report local services, addresses, ports, and scores.
656 if (!DetailsRequested()) {
657 result += strprintf("\n\nLocal services: %s", ServicesList(networkinfo["localservicesnames"]));
658 }
659 result += "\n\nLocal addresses";
660 const std::vector<UniValue>& local_addrs{networkinfo["localaddresses"].getValues()};
661 if (local_addrs.empty()) {
662 result += ": n/a\n";
663 } else {
664 size_t max_addr_size{0};
665 for (const UniValue& addr : local_addrs) {
666 max_addr_size = std::max(addr["address"].get_str().length() + 1, max_addr_size);
667 }
668 for (const UniValue& addr : local_addrs) {
669 result += strprintf("\n%-*s port %6i score %6i", max_addr_size, addr["address"].get_str(), addr["port"].getInt<int>(), addr["score"].getInt<int>());
670 }
671 }
672
673 return JSONRPCReplyObj(UniValue{result}, NullUniValue, /*id=*/1, JSONRPCVersion::V2);
674 }
675
676 const std::string m_help_doc{
677 "-netinfo (level [outonly]) | help\n\n"
678 "Returns a network peer connections dashboard with information from the remote server.\n"
679 "This human-readable interface will change regularly and is not intended to be a stable API.\n"
680 "Under the hood, -netinfo fetches the data by calling getpeerinfo and getnetworkinfo.\n"
681 + 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) +
682 "If that argument is passed, an optional additional \"outonly\" argument may be passed to obtain the listing with outbound peers only.\n"
683 "Pass \"help\" or \"h\" to see this detailed help documentation.\n"
684 "If more than two arguments are passed, only the first two are read and parsed.\n"
685 "Suggestion: use -netinfo with the Linux watch(1) command for a live dashboard; see example below.\n\n"
686 "Arguments:\n"
687 + strprintf("1. level (integer 0-%d, optional) Specify the info level of the peers dashboard (default 0):\n", NETINFO_MAX_LEVEL) +
688 " 0 - Peer counts for each reachable network as well as for block relay peers\n"
689 " and manual peers, and the list of local addresses and ports\n"
690 " 1 - Like 0 but preceded by a peers listing (without address and version columns)\n"
691 " 2 - Like 1 but with an address column\n"
692 " 3 - Like 1 but with a version column\n"
693 " 4 - Like 1 but with both address and version columns\n"
694 "2. outonly (\"outonly\" or \"o\", optional) Return the peers listing with outbound peers only, i.e. to save screen space\n"
695 " when a node has many inbound peers. Only valid if a level is passed.\n\n"
696 "help (\"help\" or \"h\", optional) Print this help documentation instead of the dashboard.\n\n"
697 "Result:\n\n"
698 + 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) +
699 " Column Description\n"
700 " ------ -----------\n"
701 " <-> Direction\n"
702 " \"in\" - inbound connections are those initiated by the peer\n"
703 " \"out\" - outbound connections are those initiated by us\n"
704 " type Type of peer connection\n"
705 " \"full\" - full relay, the default\n"
706 " \"block\" - block relay; like full relay but does not relay transactions or addresses\n"
707 " \"manual\" - peer we manually added using RPC addnode or the -addnode/-connect config options\n"
708 " \"feeler\" - short-lived connection for testing addresses\n"
709 " \"addr\" - address fetch; short-lived connection for requesting addresses\n"
710 " \"priv\" - private broadcast; short-lived connection for broadcasting our transactions\n"
711 " net Network the peer connected through (\"ipv4\", \"ipv6\", \"onion\", \"i2p\", \"cjdns\", or \"npr\" (not publicly routable))\n"
712 " serv Services offered by the peer\n"
713 " \"n\" - NETWORK: peer can serve the full block chain\n"
714 " \"b\" - BLOOM: peer can handle bloom-filtered connections (see BIP 111)\n"
715 " \"w\" - WITNESS: peer can be asked for blocks and transactions with witness data (SegWit)\n"
716 " \"c\" - COMPACT_FILTERS: peer can handle basic block filter requests (see BIPs 157 and 158)\n"
717 " \"l\" - NETWORK_LIMITED: peer limited to serving only the last 288 blocks (~2 days)\n"
718 " \"2\" - P2P_V2: peer supports version 2 P2P transport protocol, as defined in BIP 324\n"
719 " \"u\" - UNKNOWN: unrecognized bit flag\n"
720 " v Version of transport protocol used for the connection\n"
721 " mping Minimum observed ping time, in milliseconds (ms)\n"
722 " ping Last observed ping time, in milliseconds (ms)\n"
723 " send Time since last message sent to the peer, in seconds\n"
724 " recv Time since last message received from the peer, in seconds\n"
725 " txn Time since last novel transaction received from the peer and accepted into our mempool, in minutes\n"
726 " \"*\" - we do not relay transactions to this peer (getpeerinfo \"relaytxes\" is false)\n"
727 " blk Time since last novel block passing initial validity checks received from the peer, in minutes\n"
728 " hb High-bandwidth BIP152 compact block relay\n"
729 " \".\" (to) - we selected the peer as a high-bandwidth peer\n"
730 " \"*\" (from) - the peer selected us as a high-bandwidth peer\n"
731 " addrp Total number of addresses processed, excluding those dropped due to rate limiting\n"
732 " \".\" - we do not relay addresses to this peer (getpeerinfo \"addr_relay_enabled\" is false)\n"
733 " addrl Total number of addresses dropped due to rate limiting\n"
734 " age Duration of connection to the peer, in minutes\n"
735 " asmap Mapped AS (Autonomous System) number at the end of the BGP route to the peer, used for diversifying\n"
736 " peer selection (only displayed if the -asmap config option is set)\n"
737 " id Peer index, in increasing order of peer connections since node startup\n"
738 " address IP address and port of the peer\n"
739 " version Peer version and subversion concatenated, e.g. \"70016/Satoshi:21.0.0/\"\n\n"
740 "* The peer counts table displays the number of peers for each reachable network as well as\n"
741 " the number of block relay peers and manual peers.\n\n"
742 "* The local addresses table lists each local address broadcast by the node, the port, and the score.\n\n"
743 "Examples:\n\n"
744 "Peer counts table of reachable networks and list of local addresses\n"
745 "> bitcoin-cli -netinfo\n\n"
746 "The same, preceded by a peers listing without address and version columns\n"
747 "> bitcoin-cli -netinfo 1\n\n"
748 "Full dashboard\n"
749 + strprintf("> bitcoin-cli -netinfo %d\n\n", NETINFO_MAX_LEVEL) +
750 "Full dashboard, but with outbound peers only\n"
751 + strprintf("> bitcoin-cli -netinfo %d outonly\n\n", NETINFO_MAX_LEVEL) +
752 "Full live dashboard, adjust --interval or --no-title as needed (Linux)\n"
753 + strprintf("> watch --interval 1 --no-title bitcoin-cli -netinfo %d\n\n", NETINFO_MAX_LEVEL) +
754 "See this help\n"
755 "> bitcoin-cli -netinfo help\n"};
756};
757
760{
761public:
762 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
763 {
764 address_str = args.at(1);
765 UniValue params{RPCConvertValues("generatetoaddress", args)};
766 return JSONRPCRequestObj("generatetoaddress", params, 1);
767 }
768
769 UniValue ProcessReply(const UniValue &reply) override
770 {
771 UniValue result(UniValue::VOBJ);
772 result.pushKV("address", address_str);
773 result.pushKV("blocks", reply.get_obj()["result"]);
774 return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V2);
775 }
776protected:
777 std::string address_str;
778};
779
782 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
783 {
784 UniValue params;
785 if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
786 params = RPCConvertNamedValues(method, args);
787 } else {
788 params = RPCConvertValues(method, args);
789 }
791 return JSONRPCRequestObj(method, params, id);
792 }
793
794 UniValue ProcessReply(const UniValue &reply) override
795 {
796 return reply.get_obj();
797 }
798};
799
800static std::optional<UniValue> CallIPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::string& endpoint, const std::string& username)
801{
802 auto ipcconnect{gArgs.GetArg("-ipcconnect", "auto")};
803 if (ipcconnect == "0") return {}; // Do not attempt IPC if -ipcconnect is disabled.
804 if (gArgs.IsArgSet("-rpcconnect") && !gArgs.IsArgNegated("-rpcconnect")) {
805 if (ipcconnect == "auto") return {}; // Use HTTP if -ipcconnect=auto is set and -rpcconnect is enabled.
806 throw std::runtime_error("-rpcconnect and -ipcconnect options cannot both be enabled");
807 }
808
809 std::unique_ptr<interfaces::Init> local_init{interfaces::MakeBasicInit("bitcoin-cli")};
810 if (!local_init || !local_init->ipc()) {
811 if (ipcconnect == "auto") return {}; // Use HTTP if -ipcconnect=auto is set and there is no IPC support.
812 throw std::runtime_error("bitcoin-cli was not built with IPC support");
813 }
814
815 std::unique_ptr<interfaces::Init> node_init;
816 try {
817 node_init = local_init->ipc()->connectAddress(ipcconnect);
818 if (!node_init) return {}; // Fall back to HTTP if -ipcconnect=auto connect failed.
819 } catch (const std::exception& e) {
820 // Catch connect error if -ipcconnect=unix was specified
821 throw CConnectionFailed{strprintf("%s\n\n"
822 "Probably bitcoin-node is not running or not listening on a unix socket. Can be started with:\n\n"
823 " bitcoin-node -chain=%s -ipcbind=unix", e.what(), gArgs.GetChainTypeString())};
824 }
825
826 std::unique_ptr<interfaces::Rpc> rpc{node_init->makeRpc()};
827 assert(rpc);
828 UniValue request{rh->PrepareRequest(strMethod, args)};
829 UniValue reply{rpc->executeRpc(std::move(request), endpoint, username)};
830 return rh->ProcessReply(reply);
831}
832
837{
838public:
839 static HTTPClient Connect(const std::string& host, uint16_t port, std::chrono::seconds timeout);
840
841 HTTPResponse Post(const std::string& endpoint,
842 std::span<const std::pair<std::string, std::string>> headers,
843 const std::string& body);
844
845private:
846 // Signal that the peer closed the connection cleanly. Used in the read-until-close fallback.
848
849 std::unique_ptr<Sock> m_socket;
850 std::string m_host;
851 std::chrono::seconds m_timeout;
852
853 HTTPClient(std::unique_ptr<Sock>&& socket, const std::string& host, std::chrono::seconds timeout)
854 : m_socket(std::move(socket)), m_host(host), m_timeout(timeout) {}
855 bool SendRequest(std::string_view request);
857 std::optional<std::string> Recv();
858};
859
860HTTPClient HTTPClient::Connect(const std::string& host, uint16_t port, std::chrono::seconds timeout)
861{
862 std::vector<CService> services = Lookup(host, port, /*fAllowLookup=*/true, /*nMaxSolutions=*/256);
863 if (services.empty()) {
864 throw CConnectionFailed(strprintf("Could not resolve host: %s", host));
865 }
866
867 const auto deadline{std::chrono::steady_clock::now() + timeout};
868 for (const CService& service : services) {
869 const auto time_left{std::chrono::duration_cast<std::chrono::milliseconds>(deadline - std::chrono::steady_clock::now())};
870 if (time_left.count() <= 0) break;
871
872 auto sock = ConnectDirectly(service, /*manual_connection=*/true, time_left);
873 if (sock) return HTTPClient{std::move(sock), host, timeout};
874 }
875
876 throw CConnectionFailed{"Could not connect to the server"};
877}
878
880 std::span<const std::pair<std::string, std::string>> headers,
881 const std::string& body)
882{
883 try {
884 // Build HTTP request
885 std::string request = strprintf("POST %s HTTP/1.1\r\n"
886 "Host: %s\r\n"
887 "Connection: close\r\n"
888 "Content-Length: %d\r\n",
889 endpoint, m_host, body.size());
890
891 for (const auto& [name, value] : headers) {
892 request += strprintf("%s: %s\r\n", name, value);
893 }
894 request += "\r\n";
895 request += body;
896
897 if (!SendRequest(request)) {
898 throw CConnectionFailed("Failed to send HTTP request");
899 }
900
901 return ReadResponse();
902 } catch (const HTTPError& e) {
903 throw CConnectionFailed(strprintf("HTTP error: %s", e.what()));
904 }
905}
906
907bool HTTPClient::SendRequest(std::string_view request)
908{
909 while (!request.empty()) {
910 Sock::Event event{0};
911 if (!m_socket->Wait(m_timeout, Sock::SendEvent, &event)) {
912 return false;
913 }
914
915 // The wait either timed out or the socket reported an error without becoming writable.
916 if (!(event & Sock::SendEvent)) {
917 return false;
918 }
919
920 ssize_t sent = m_socket->Send(request.data(), request.size(), MSG_NOSIGNAL);
921 if (sent < 0) {
922 int err = WSAGetLastError();
923 if (!IOErrorIsPermanent(err)) {
924 std::this_thread::yield();
925 continue;
926 }
927 return false;
928 }
929 request.remove_prefix(sent);
930 }
931 return true;
932}
933
935{
937 std::string buffer;
938
939 // Read data until we have complete headers
940 size_t headers_end = 0;
941
942 while (headers_end == 0) {
943 if (auto result{Recv()}) {
944 buffer.append(*result);
945 } else {
946 std::this_thread::yield();
947 continue;
948 }
949
950 // Check for header terminator
951 size_t pos = buffer.find("\r\n\r\n");
952 if (pos != std::string::npos) {
953 headers_end = pos + 4;
954 }
955 }
956
957 // Parse http status
958 util::LineReader reader(std::string_view{buffer.data(), headers_end}, headers_end);
959 auto status_line = reader.ReadLine();
960 if (!status_line) {
961 throw HTTPError{"Failed to read status line"};
962 }
963
964 const std::string_view status_str = *status_line;
965 // Minimum status line is "HTTP/X.Y NNN" (e.g. "HTTP/1.1 200"), 12 characters.
966 if (status_str.size() < 12 || !status_str.starts_with("HTTP/")) {
967 throw HTTPError{"Invalid status line"};
968 }
969
970 size_t space1 = status_str.find(' ');
971 if (space1 == std::string::npos || space1 + 4 > status_str.size()) {
972 throw HTTPError{"Invalid status line format"};
973 }
974
975 const std::string_view status_code_str = status_str.substr(space1 + 1, 3);
977 if (!status_code) {
978 throw HTTPError{"Invalid status code"};
979 }
980 response.status = *status_code;
981
984
985 // Determine body length
986 std::optional<size_t> content_length;
987 bool chunked = false;
988
989 // RFC 9112 §6.3 says responses with both Transfer-Encoding and Content-Length
990 // must be rejected. We are more lenient: Transfer-Encoding takes precedence
991 // and Content-Length is ignored.
992 auto transfer_encoding = headers.FindFirst("transfer-encoding");
993 if (transfer_encoding && ToLower(*transfer_encoding).find("chunked") != std::string::npos) {
994 chunked = true;
995 } else {
996 auto content_length_header = headers.FindFirst("content-length");
999 if (!content_length) {
1000 throw HTTPError{"Invalid Content-Length"};
1001 }
1002 }
1003 }
1004
1005 // Remove headers data from buffer, so only initial body data remains
1006 buffer.erase(0, headers_end);
1007
1008 // Read remaining body
1009 if (chunked) {
1010 // Handle chunked transfer encoding
1011 std::string body;
1012
1013 while (true) {
1014 // Try to parse a chunk from current buffer
1015 std::string_view chunk_data{buffer};
1016 size_t line_end = chunk_data.find("\r\n");
1017
1018 if (line_end != std::string::npos) {
1019 // Parse chunk size
1020 std::string_view size_str = chunk_data.substr(0, line_end);
1021 // Ignore chunk extensions
1022 size_t semi = size_str.find(';');
1023 if (semi != std::string::npos) {
1024 size_str = size_str.substr(0, semi);
1025 }
1026
1028 if (!chunk_size) {
1029 throw HTTPError{"Invalid chunk size"};
1030 }
1031
1032 if (*chunk_size == 0) {
1033 // Allow (but ignore) Chunked Trailer section, by
1034 // reading CRLF-terminated lines until we read an empty line,
1035 // which indicates the end of this response.
1036 // See https://httpwg.org/specs/rfc9112.html#rfc.section.7.1.2
1037 buffer.erase(0, line_end + 2);
1038 while (true) {
1039 size_t crlf_pos = buffer.find("\r\n");
1040 if (crlf_pos == std::string::npos) {
1041 // Need more data
1042 if (auto result{Recv()}) {
1043 buffer.append(*result);
1044 } else {
1045 std::this_thread::yield();
1046 }
1047 continue;
1048 }
1049 buffer.erase(0, crlf_pos + 2);
1050 if (crlf_pos == 0) break;
1051 }
1052 break;
1053 }
1054
1055 // Check if we have the full chunk
1056 size_t chunk_start = line_end + 2;
1057 if (*chunk_size > std::numeric_limits<size_t>::max() - chunk_start - 2) {
1058 throw HTTPError{"Chunk size too large"};
1059 }
1060 size_t chunk_end = chunk_start + *chunk_size + 2; // +2 for trailing CRLF
1061
1062 if (buffer.size() >= chunk_end) {
1063 // Extract chunk data
1064 body.append(buffer, chunk_start, *chunk_size);
1065
1066 // Remove processed data
1067 buffer.erase(0, chunk_end);
1068 continue;
1069 }
1070 }
1071
1072 // Need more data
1073 while (true) {
1074 if (auto result{Recv()}) {
1075 buffer.append(*result);
1076 break;
1077 } else {
1078 std::this_thread::yield();
1079 }
1080 }
1081 }
1082
1083 response.body = std::move(body);
1084 } else if (content_length) {
1085 // Fixed content length
1086 while (buffer.size() < *content_length) {
1087 if (auto result{Recv()}) {
1088 buffer.append(*result);
1089 } else {
1090 std::this_thread::yield();
1091 }
1092 }
1093
1094 // Possibly shrink buffer in case we got a larger response than
1095 // originally specified.
1096 buffer.resize(*content_length);
1097 response.body = std::move(buffer);
1098 } else {
1099 // No Content-Length and not chunked: read until the peer closes the
1100 // connection (RFC 9112 §6.3, HTTP/1.0 fallback).
1101 try {
1102 while (true) {
1103 if (auto result{Recv()}) {
1104 buffer.append(*result);
1105 } else {
1106 std::this_thread::yield();
1107 }
1108 }
1109 } catch (const RecvEOF&) {}
1110 response.body = std::move(buffer);
1111 }
1112
1113 return response;
1114}
1115
1116std::optional<std::string> HTTPClient::Recv()
1117{
1118 auto wait_for_readable{[this](std::chrono::milliseconds timeout) -> bool {
1119 Sock::Event event{0};
1120 if (!m_socket->Wait(timeout, Sock::RecvEvent, &event)) {
1121 return false;
1122 }
1123 return (event & Sock::RecvEvent) != 0;
1124 }};
1125
1127 throw CConnectionFailed{"timeout"};
1128 }
1129
1130 char recv_buf[4096];
1131 ssize_t nrecv = m_socket->Recv(recv_buf, sizeof(recv_buf), /*flags=*/0);
1132
1133 if (nrecv < 0) {
1134 int err = WSAGetLastError();
1135 if (!IOErrorIsPermanent(err)) {
1136 return std::nullopt;
1137 }
1138 throw CConnectionFailed{strprintf("Read error: %s", NetworkErrorString(err))};
1139 }
1140
1141 if (nrecv == 0) {
1142 throw RecvEOF{"EOF"};
1143 }
1144
1145 return std::string{recv_buf, static_cast<size_t>(nrecv)};
1146}
1147
1148static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::string& endpoint, const std::string& username)
1149{
1150 std::string host;
1151 // In preference order, we choose the following for the port:
1152 // 1. -rpcport
1153 // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
1154 // 3. default port for chain
1155 uint16_t port{BaseParams().RPCPort()};
1156 {
1158 const std::string rpcconnect_str = gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
1160 // Uses argument provided as-is
1161 // (rather than value parsed)
1162 // to aid the user in troubleshooting
1163 throw std::runtime_error(strprintf("Invalid port provided in -rpcconnect: %s", rpcconnect_str));
1164 } else {
1165 if (rpcconnect_port != 0) {
1166 // Use the valid port provided in rpcconnect
1167 port = rpcconnect_port;
1168 } // else, no port was provided in rpcconnect (continue using default one)
1169 }
1170
1171 if (std::optional<std::string> rpcport_arg = gArgs.GetArg("-rpcport")) {
1172 // -rpcport was specified
1173 const uint16_t rpcport_int{ToIntegral<uint16_t>(rpcport_arg.value()).value_or(0)};
1174 if (rpcport_int == 0) {
1175 // Uses argument provided as-is
1176 // (rather than value parsed)
1177 // to aid the user in troubleshooting
1178 throw std::runtime_error(strprintf("Invalid port provided in -rpcport: %s", rpcport_arg.value()));
1179 }
1180
1181 // Use the valid port provided
1182 port = rpcport_int;
1183
1184 // If there was a valid port provided in rpcconnect,
1185 // rpcconnect_port is non-zero.
1186 if (rpcconnect_port != 0) {
1187 tfm::format(std::cerr, "Warning: Port specified in both -rpcconnect and -rpcport. Using -rpcport %u\n", port);
1188 }
1189 }
1190 }
1191
1192 // Set connection timeout
1193 const int timeout = gArgs.GetIntArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT);
1194 std::chrono::seconds timeout_duration;
1195 if (timeout > 0) {
1196 timeout_duration = std::chrono::seconds(timeout);
1197 } else {
1198 // There is no indefinite wait, so use the longest one that Sock supports.
1200 }
1201
1202 // Get credentials
1203 std::string rpc_credentials;
1204 std::optional<AuthCookieResult> auth_cookie_result;
1205 if (gArgs.GetArg("-rpcpassword", "") == "") {
1206 // Try fall back to cookie-based authentication if no password is provided
1208 } else {
1209 rpc_credentials = username + ":" + gArgs.GetArg("-rpcpassword", "");
1210 }
1211
1212 const std::pair<std::string, std::string> headers[]{
1213 {"Content-Type", "application/json"},
1214 {"Authorization", "Basic " + EncodeBase64(rpc_credentials)},
1215 };
1216 std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";
1217
1219 try {
1222 } catch (const CConnectionFailed& e) {
1223 const std::string formatted_error{*e.what() ? strprintf(" (%s)", e.what()) : ""};
1224 throw CConnectionFailed(strprintf("Error while attempting to communicate with server %s:%d%s\n\n"
1225 "Make sure the bitcoind server is running and that you are connecting to the correct RPC port.\n"
1226 "Use \"bitcoin-cli -help\" for more info.",
1227 host, port, formatted_error));
1228 }
1229
1230 if (response.status == HTTP_UNAUTHORIZED) {
1231 std::string error{"Authorization failed: "};
1232 bool read_err = false;
1233 if (auth_cookie_result.has_value()) {
1234 switch (*auth_cookie_result) {
1236 error += "Failed to read cookie file and no rpcpassword was specified.";
1237 read_err = true;
1238 break;
1240 error += "Cookie file was disabled via -norpccookiefile and no rpcpassword was specified.";
1241 break;
1243 error += "Cookie file credentials were invalid and no rpcpassword was specified.";
1244 break;
1245 }
1246 } else {
1247 error += "Incorrect rpcuser or rpcpassword were specified.";
1248 }
1249 error += strprintf(" Configuration file: (%s)", fs::PathToString(gArgs.GetConfigFilePath()));
1251 error += "\nIs your -signetchallenge correct for custom signets?";
1252 }
1253 throw std::runtime_error(error);
1254 } else if (response.status == HTTP_SERVICE_UNAVAILABLE) {
1255 throw std::runtime_error(strprintf("Server response: %s", response.body));
1256 } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
1257 throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
1258 else if (response.body.empty())
1259 throw std::runtime_error("no response from server");
1260
1261 // Parse reply
1263 if (!valReply.read(response.body))
1264 throw std::runtime_error("couldn't parse reply from server");
1265 UniValue reply = rh->ProcessReply(valReply);
1266 if (reply.empty())
1267 throw std::runtime_error("expected reply to have result, error and id properties");
1268
1269 return reply;
1270}
1271
1281static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& strMethod, const std::vector<std::string>& args, const std::optional<std::string>& rpcwallet = {})
1282{
1284 // Execute and handle connection failures with -rpcwait.
1285 const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
1286 const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
1287 const auto deadline{std::chrono::steady_clock::now() + 1s * timeout};
1288
1289 // check if we should use a special wallet endpoint
1290 std::string endpoint = "/";
1291 if (rpcwallet) {
1292 endpoint = "/wallet/" + UrlEncode(*rpcwallet);
1293 }
1294
1295 std::string username{gArgs.GetArg("-rpcuser", "")};
1296 do {
1297 try {
1298 if (auto ipc_response{CallIPC(rh, strMethod, args, endpoint, username)}) {
1299 response = std::move(*ipc_response);
1300 } else {
1301 response = CallRPC(rh, strMethod, args, endpoint, username);
1302 }
1303 if (fWait) {
1304 const UniValue& error = response.find_value("error");
1305 if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) {
1306 throw CConnectionFailed("server in warmup");
1307 }
1308 }
1309 break; // Connection succeeded, no need to retry.
1310 } catch (const CConnectionFailed& e) {
1311 if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) {
1313 } else if (fWait) {
1314 throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
1315 } else {
1316 throw;
1317 }
1318 }
1319 } while (fWait);
1320 return response;
1321}
1322
1324static void ParseResult(const UniValue& result, std::string& strPrint)
1325{
1326 if (result.isNull()) return;
1327 strPrint = result.isStr() ? result.get_str() : result.write(2);
1328}
1329
1331static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
1332{
1333 if (error.isObject()) {
1334 const UniValue& err_code = error.find_value("code");
1335 const UniValue& err_msg = error.find_value("message");
1336 if (!err_code.isNull()) {
1337 strPrint = "error code: " + err_code.getValStr() + "\n";
1338 }
1339 if (err_msg.isStr()) {
1340 strPrint += ("error message:\n" + err_msg.get_str());
1341 }
1342 if (err_code.isNum() && err_code.getInt<int>() == RPC_WALLET_NOT_SPECIFIED) {
1343 strPrint += " Or for the CLI, specify the \"-rpcwallet=<walletname>\" option before the command";
1344 strPrint += " (run \"bitcoin-cli -h\" for help or \"bitcoin-cli listwallets\" to see which wallets are currently loaded).";
1345 }
1346 } else {
1347 strPrint = "error: " + error.write();
1348 }
1349 nRet = abs(error["code"].getInt<int>());
1350}
1351
1358static void GetWalletBalances(UniValue& result)
1359{
1361 const UniValue listwallets = ConnectAndCallRPC(&rh, "listwallets", /* args=*/{});
1362 if (!listwallets.find_value("error").isNull()) return;
1363 const UniValue& wallets = listwallets.find_value("result");
1364 if (wallets.size() <= 1) return;
1365
1367 for (const UniValue& wallet : wallets.getValues()) {
1368 const std::string& wallet_name = wallet.get_str();
1369 const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name);
1370 const UniValue& balance = getbalances.find_value("result")["mine"]["trusted"];
1371 balances.pushKV(wallet_name, balance);
1372 }
1373 result.pushKV("balances", std::move(balances));
1374}
1375
1382static void GetProgressBar(double progress, std::string& progress_bar)
1383{
1384 if (progress < 0 || progress > 1) return;
1385
1386 static constexpr double INCREMENT{0.05};
1387 static const std::string COMPLETE_BAR{"\u2592"};
1388 static const std::string INCOMPLETE_BAR{"\u2591"};
1389
1390 for (int i = 0; i < progress / INCREMENT; ++i) {
1392 }
1393
1394 for (int i = 0; i < (1 - progress) / INCREMENT; ++i) {
1396 }
1397}
1398
1404static void ParseGetInfoResult(UniValue& result)
1405{
1406 if (!result.find_value("error").isNull()) return;
1407
1408 std::string RESET, GREEN, BLUE, YELLOW, MAGENTA, CYAN;
1409 bool should_colorize = false;
1410
1411#ifndef WIN32
1412 if (isatty(fileno(stdout))) {
1413 // By default, only print colored text if OS is not WIN32 and stdout is connected to a terminal.
1414 should_colorize = true;
1415 }
1416#endif
1417
1418 {
1419 const std::string color{gArgs.GetArg("-color", DEFAULT_COLOR_SETTING)};
1420 if (color == "always") {
1421 should_colorize = true;
1422 } else if (color == "never") {
1423 should_colorize = false;
1424 } else if (color != "auto") {
1425 throw std::runtime_error("Invalid value for -color option. Valid values: always, auto, never.");
1426 }
1427 }
1428
1429 if (should_colorize) {
1430 RESET = "\x1B[0m";
1431 GREEN = "\x1B[32m";
1432 BLUE = "\x1B[34m";
1433 YELLOW = "\x1B[33m";
1434 MAGENTA = "\x1B[35m";
1435 CYAN = "\x1B[36m";
1436 }
1437
1438 std::string result_string = strprintf("%sChain: %s%s\n", BLUE, result["chain"].getValStr(), RESET);
1439 result_string += strprintf("Blocks: %s\n", result["blocks"].getValStr());
1440 result_string += strprintf("Headers: %s\n", result["headers"].getValStr());
1441
1442 const double ibd_progress{result["verificationprogress"].get_real()};
1443 std::string ibd_progress_bar;
1444 // Display the progress bar only if IBD progress is less than 99%
1445 if (ibd_progress < 0.99) {
1447 // Add padding between progress bar and IBD progress
1448 ibd_progress_bar += ' ';
1449 }
1450
1451 result_string += strprintf("Verification progress: %s%.4f%%\n", ibd_progress_bar, ibd_progress * 100);
1452 result_string += strprintf("Difficulty: %s\n\n", result["difficulty"].getValStr());
1453
1455 "%sNetwork: in %s, out %s, total %s%s\n",
1456 GREEN,
1457 result["connections"]["in"].getValStr(),
1458 result["connections"]["out"].getValStr(),
1459 result["connections"]["total"].getValStr(),
1460 RESET);
1461 result_string += strprintf("Version: %s\n", result["version"].getValStr());
1462 result_string += strprintf("Time offset (s): %s\n", result["timeoffset"].getValStr());
1463
1464 // proxies
1465 std::map<std::string, std::vector<std::string>> proxy_networks;
1466 std::vector<std::string> ordered_proxies;
1467
1468 for (const UniValue& network : result["networks"].getValues()) {
1469 const std::string proxy = network["proxy"].getValStr();
1470 if (proxy.empty()) continue;
1471 // Add proxy to ordered_proxy if has not been processed
1472 if (!proxy_networks.contains(proxy)) ordered_proxies.push_back(proxy);
1473
1474 proxy_networks[proxy].push_back(network["name"].getValStr());
1475 }
1476
1477 std::vector<std::string> formatted_proxies;
1478 formatted_proxies.reserve(ordered_proxies.size());
1479 for (const std::string& proxy : ordered_proxies) {
1480 formatted_proxies.emplace_back(strprintf("%s (%s)", proxy, Join(proxy_networks.find(proxy)->second, ", ")));
1481 }
1482 result_string += strprintf("Proxies: %s\n", formatted_proxies.empty() ? "n/a" : Join(formatted_proxies, ", "));
1483
1484 result_string += strprintf("Min tx relay fee rate (%s/kvB): %s\n\n", CURRENCY_UNIT, result["relayfee"].getValStr());
1485
1486 if (!result["has_wallet"].isNull()) {
1487 const std::string walletname = result["walletname"].getValStr();
1488 result_string += strprintf("%sWallet: %s%s\n", MAGENTA, walletname.empty() ? "\"\"" : walletname, RESET);
1489
1490 result_string += strprintf("Keypool size: %s\n", result["keypoolsize"].getValStr());
1491 if (!result["unlocked_until"].isNull()) {
1492 result_string += strprintf("Unlocked until: %s\n", result["unlocked_until"].getValStr());
1493 }
1494 }
1495 if (!result["balance"].isNull()) {
1496 result_string += strprintf("%sBalance:%s %s\n\n", CYAN, RESET, result["balance"].getValStr());
1497 }
1498
1499 if (!result["balances"].isNull()) {
1500 result_string += strprintf("%sBalances%s\n", CYAN, RESET);
1501
1502 size_t max_balance_length{10};
1503
1504 for (const std::string& wallet : result["balances"].getKeys()) {
1505 max_balance_length = std::max(result["balances"][wallet].getValStr().length(), max_balance_length);
1506 }
1507
1508 for (const std::string& wallet : result["balances"].getKeys()) {
1509 result_string += strprintf("%*s %s\n",
1511 result["balances"][wallet].getValStr(),
1512 wallet.empty() ? "\"\"" : wallet);
1513 }
1514 result_string += '\n';
1515 }
1516
1517 const std::string warnings{result["warnings"].getValStr()};
1518 result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, warnings.empty() ? "(none)" : warnings);
1519
1520 result.setStr(result_string);
1521}
1522
1528{
1530 return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, RpcWalletName(gArgs));
1531}
1532
1538static void SetGenerateToAddressArgs(const std::string& address, std::vector<std::string>& args)
1539{
1540 if (args.size() > 2) throw std::runtime_error("too many arguments (maximum 2 for nblocks and maxtries)");
1541 if (args.size() == 0) {
1542 args.emplace_back(DEFAULT_NBLOCKS);
1543 } else if (args.at(0) == "0") {
1544 throw std::runtime_error("the first argument (number of blocks to generate, default: " + DEFAULT_NBLOCKS + ") must be an integer value greater than zero");
1545 }
1546 args.emplace(args.begin() + 1, address);
1547}
1548
1549static int CommandLineRPC(int argc, char *argv[])
1550{
1551 std::string strPrint;
1552 int nRet = 0;
1553 try {
1554 // Skip switches
1555 while (argc > 1 && IsSwitchChar(argv[1][0])) {
1556 argc--;
1557 argv++;
1558 }
1559 std::string rpcPass;
1560 if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
1561 NO_STDIN_ECHO();
1562 if (!StdinReady()) {
1563 fputs("RPC password> ", stderr);
1564 fflush(stderr);
1565 }
1566 if (!std::getline(std::cin, rpcPass)) {
1567 throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input");
1568 }
1569 if (StdinTerminal()) {
1570 fputc('\n', stdout);
1571 }
1572 gArgs.ForceSetArg("-rpcpassword", rpcPass);
1573 }
1574 std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
1575 if (gArgs.GetBoolArg("-stdinwalletpassphrase", false)) {
1576 NO_STDIN_ECHO();
1577 std::string walletPass;
1578 if (args.size() < 1 || !args[0].starts_with("walletpassphrase")) {
1579 throw std::runtime_error("-stdinwalletpassphrase is only applicable for walletpassphrase(change)");
1580 }
1581 if (!StdinReady()) {
1582 fputs("Wallet passphrase> ", stderr);
1583 fflush(stderr);
1584 }
1585 if (!std::getline(std::cin, walletPass)) {
1586 throw std::runtime_error("-stdinwalletpassphrase specified but failed to read from standard input");
1587 }
1588 if (StdinTerminal()) {
1589 fputc('\n', stdout);
1590 }
1591 args.insert(args.begin() + 1, walletPass);
1592 }
1593 if (gArgs.GetBoolArg("-stdin", false)) {
1594 // Read one arg per line from stdin and append
1595 std::string line;
1596 while (std::getline(std::cin, line)) {
1597 args.push_back(line);
1598 }
1599 if (StdinTerminal()) {
1600 fputc('\n', stdout);
1601 }
1602 }
1604 std::unique_ptr<BaseRequestHandler> rh;
1605 std::string method;
1606 if (gArgs.GetBoolArg("-getinfo", false)) {
1607 rh.reset(new GetinfoRequestHandler());
1608 } else if (gArgs.GetBoolArg("-netinfo", false)) {
1609 if (!args.empty() && (args.at(0) == "h" || args.at(0) == "help")) {
1610 tfm::format(std::cout, "%s\n", NetinfoRequestHandler().m_help_doc);
1611 return 0;
1612 }
1613 rh.reset(new NetinfoRequestHandler());
1614 } else if (gArgs.GetBoolArg("-generate", false)) {
1615 const UniValue getnewaddress{GetNewAddress()};
1616 const UniValue& error{getnewaddress.find_value("error")};
1617 if (error.isNull()) {
1618 SetGenerateToAddressArgs(getnewaddress.find_value("result").get_str(), args);
1620 } else {
1621 ParseError(error, strPrint, nRet);
1622 }
1623 } else if (gArgs.GetBoolArg("-addrinfo", false)) {
1624 rh.reset(new AddrinfoRequestHandler());
1625 } else {
1626 rh.reset(new DefaultRequestHandler());
1627 if (args.size() < 1) {
1628 throw std::runtime_error("too few parameters (need at least command)");
1629 }
1630 method = args[0];
1631 args.erase(args.begin()); // Remove trailing method name from arguments vector
1632 }
1633 if (nRet == 0) {
1634 // Perform RPC call
1635 const std::optional<std::string> wallet_name{RpcWalletName(gArgs)};
1636 const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
1637
1638 // Parse reply
1639 UniValue result = reply.find_value("result");
1640 const UniValue& error = reply.find_value("error");
1641 if (error.isNull()) {
1642 if (gArgs.GetBoolArg("-getinfo", false)) {
1643 if (!wallet_name) {
1644 GetWalletBalances(result); // fetch multiwallet balances and append to result
1645 }
1646 ParseGetInfoResult(result);
1647 }
1648
1649 ParseResult(result, strPrint);
1650 } else {
1651 ParseError(error, strPrint, nRet);
1652 }
1653 }
1654 } catch (const std::exception& e) {
1655 strPrint = std::string("error: ") + e.what();
1657 } catch (...) {
1658 PrintExceptionContinue(nullptr, "CommandLineRPC()");
1659 throw;
1660 }
1661
1662 if (strPrint != "") {
1663 tfm::format(nRet == 0 ? std::cout : std::cerr, "%s\n", strPrint);
1664 }
1665 return nRet;
1666}
1667
1669{
1672 tfm::format(std::cerr, "Error: Initializing networking failed\n");
1673 return EXIT_FAILURE;
1674 }
1675
1676 try {
1677 int ret = AppInitRPC(argc, argv);
1678 if (ret != CONTINUE_EXECUTION)
1679 return ret;
1680 }
1681 catch (const std::exception& e) {
1682 PrintExceptionContinue(&e, "AppInitRPC()");
1683 return EXIT_FAILURE;
1684 } catch (...) {
1685 PrintExceptionContinue(nullptr, "AppInitRPC()");
1686 return EXIT_FAILURE;
1687 }
1688
1690 try {
1692 }
1693 catch (const std::exception& e) {
1694 PrintExceptionContinue(&e, "CommandLineRPC()");
1695 } catch (...) {
1696 PrintExceptionContinue(nullptr, "CommandLineRPC()");
1697 }
1698 return ret;
1699}
bool HelpRequested(const ArgsManager &args)
Definition args.cpp:796
void SetupHelpOptions(ArgsManager &args)
Add help options to the args manager.
Definition args.cpp:801
bool CheckDataDirOption(const ArgsManager &args)
Definition args.cpp:875
ArgsManager gArgs
Definition args.cpp:39
const char *const BITCOIN_CONF_FILENAME
Definition args.cpp:36
bool IsSwitchChar(char c)
Definition args.h:44
static const char DEFAULT_RPCCONNECT[]
static constexpr int8_t UNKNOWN_NETWORK
static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT
static const int CONTINUE_EXECUTION
static int AppInitRPC(int argc, char *argv[])
static void ParseError(const UniValue &error, std::string &strPrint, int &nRet)
Parse UniValue error to update the message to print to std::cerr and the code to return.
static constexpr std::array UNREACHABLE_NETWORK_IDS
static int CommandLineRPC(int argc, char *argv[])
static constexpr uint8_t NETINFO_MAX_LEVEL
static const int DEFAULT_HTTP_CLIENT_TIMEOUT
static void ParseGetInfoResult(UniValue &result)
ParseGetInfoResult takes in -getinfo result in UniValue object and parses it into a user friendly Uni...
int ret
static std::optional< UniValue > CallIPC(BaseRequestHandler *rh, const std::string &strMethod, const std::vector< std::string > &args, const std::string &endpoint, const std::string &username)
static void ParseResult(const UniValue &result, std::string &strPrint)
Parse UniValue result to update the message to print to std::cout.
static const std::string DEFAULT_NBLOCKS
Default number of blocks to generate for RPC generatetoaddress.
static UniValue ConnectAndCallRPC(BaseRequestHandler *rh, const std::string &strMethod, const std::vector< std::string > &args, const std::optional< std::string > &rpcwallet={})
ConnectAndCallRPC wraps CallRPC with -rpcwait and an exception handler.
static void SetGenerateToAddressArgs(const std::string &address, std::vector< std::string > &args)
Check bounds and set up args for RPC generatetoaddress params: nblocks, address, maxtries.
static void GetWalletBalances(UniValue &result)
GetWalletBalances calls listwallets; if more than one wallet is loaded, it then fetches mine....
static constexpr std::array NETWORKS
static void SetupCliArgs(ArgsManager &argsman)
static const std::string DEFAULT_COLOR_SETTING
Default -color setting.
std::optional< std::string > RpcWalletName(const ArgsManager &args)
const TranslateFn G_TRANSLATION_FUN
Translate string to current locale using Qt.
std::chrono::system_clock CliClock
static constexpr const char * DEFAULT_RPC_REQ_ID
static constexpr std::array NETWORK_SHORT_NAMES
static int8_t NetworkStringToId(const std::string &str)
static const bool DEFAULT_NAMED
static void GetProgressBar(double progress, std::string &progress_bar)
GetProgressBar constructs a progress bar with 5% intervals.
static UniValue CallRPC(BaseRequestHandler *rh, const std::string &strMethod, const std::vector< std::string > &args, const std::string &endpoint, const std::string &username)
MAIN_FUNCTION
static UniValue GetNewAddress()
Call RPC getnewaddress.
SetupEnvironment()
Definition system.cpp:56
std::string strPrint
return EXIT_SUCCESS
ArgsManager & args
Definition bitcoind.cpp:280
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
void SetupChainParamsBaseOptions(ArgsManager &argsman)
Set the arguments for chainparams.
void SelectBaseParams(const ChainType chain)
Sets the params returned by Params() to those for the given chain.
std::unique_ptr< CBaseChainParams > CreateBaseChainParams(const ArgsManager &args, const ChainType chain)
Port numbers for incoming Tor connections (8334, 18334, 38334, 48334, 18445) have been chosen arbitra...
bool ParseParameters(int argc, const char *const argv[], std::string &error) EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Definition args.cpp:176
ChainType GetChainType() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Returns the appropriate chain type from the program arguments.
Definition args.cpp:894
void CheckMultipleCLIArgs() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Check CLI command args.
Definition args.cpp:687
@ NETWORK_ONLY
Definition args.h:123
@ ALLOW_ANY
disable validation
Definition args.h:109
@ DISALLOW_NEGATION
disallow -nofoo syntax
Definition args.h:114
@ DISALLOW_ELISION
disallow -foo syntax that doesn't assign any value
Definition args.h:115
fs::path GetConfigFilePath() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return config file path (read-only)
Definition args.cpp:881
bool ReadConfigFiles(std::string &error, bool ignore_invalid_keys=false) EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Definition config.cpp:132
void ForceSetArg(const std::string &strArg, const std::string &strValue) EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Definition args.cpp:611
std::string GetArg(const std::string &strArg, const std::string &strDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return string argument or default value.
Definition args.cpp:518
std::string GetChainTypeString() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Returns the appropriate chain type string from the program arguments.
Definition args.cpp:901
bool IsArgSet(const std::string &strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return true if the given argument has been manually set.
Definition args.cpp:432
bool IsArgNegated(const std::string &strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return true if the argument was originally passed as a negated option, i.e.
Definition args.cpp:513
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Definition args.h:318
bool GetBoolArg(const std::string &strArg, bool fDefault) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return boolean argument or default value.
Definition args.cpp:561
std::string GetHelpMessage() const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Get the help string.
Definition args.cpp:704
uint16_t RPCPort() const
A combination of a network address (CNetAddr) and a (TCP) port.
Definition netaddress.h:531
Process RPC generatetoaddress request.
UniValue PrepareRequest(const std::string &method, const std::vector< std::string > &args) override
UniValue ProcessReply(const UniValue &reply) override
Simple synchronous HTTP client using Sock class.
std::chrono::seconds m_timeout
std::optional< std::string > Recv()
bool SendRequest(std::string_view request)
static HTTPClient Connect(const std::string &host, uint16_t port, std::chrono::seconds timeout)
HTTPClient(std::unique_ptr< Sock > &&socket, const std::string &host, std::chrono::seconds timeout)
HTTPResponse Post(const std::string &endpoint, std::span< const std::pair< std::string, std::string > > headers, const std::string &body)
std::unique_ptr< Sock > m_socket
HTTPResponse ReadResponse()
std::string m_host
std::optional< std::string > FindFirst(std::string_view key) const
bool Read(util::LineReader &reader, bool write=true)
Parses the headers of an HTTP response.
std::optional< std::string > FindFirst(std::string_view key) const
void Read(util::LineReader &reader)
std::vector< std::pair< std::string, std::string > > m_headers
Process netinfo requests.
bool DetailsRequested() const
std::vector< Peer > m_peers
UniValue ProcessReply(const UniValue &batch_in) override
static std::string ServicesList(const UniValue &services)
uint8_t m_details_level
Optional user-supplied arg to set dashboard details level.
size_t m_max_addr_rate_limited_length
bool IsAddressSelected() const
std::string FormatServices(const UniValue &services)
std::string ConnectionTypeForNetinfo(const std::string &conn_type) const
bool IsVersionSelected() const
const std::string m_help_doc
static constexpr int ID_PEERINFO
std::string ChainToString() const
UniValue PrepareRequest(const std::string &method, const std::vector< std::string > &args) override
std::array< std::array< uint16_t, NETWORKS.size()+1 >, 3 > m_counts
Peer counts by (in/out/total, networks/total)
static constexpr int ID_NETWORKINFO
std::string PingTimeToString(double seconds) const
static constexpr Event RecvEvent
If passed to Wait(), then it will wait for readiness to read from the socket.
Definition sock.h:151
uint8_t Event
Definition sock.h:146
static constexpr Event SendEvent
If passed to Wait(), then it will wait for readiness to send to the socket.
Definition sock.h:156
static constexpr std::chrono::seconds MAX_WAIT_TIMEOUT
Maximum timeout supported by Wait() and WaitMany() (about 24.8 days).
Definition sock.h:170
void push_back(UniValue val)
Definition univalue.cpp:103
const std::string & get_str() const
const UniValue & find_value(std::string_view key) const
Definition univalue.cpp:232
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
bool isNull() const
Definition univalue.h:81
const std::string & getValStr() const
Definition univalue.h:68
const UniValue & get_obj() const
size_t size() const
Definition univalue.h:71
const std::vector< UniValue > & getValues() const
const std::vector< std::string > & getKeys() const
bool empty() const
Definition univalue.h:69
bool isStr() const
Definition univalue.h:85
Int getInt() const
Definition univalue.h:143
void setStr(std::string str)
Definition univalue.cpp:84
void pushKV(std::string key, UniValue val)
Definition univalue.cpp:125
double get_real() const
bool isObject() const
Definition univalue.h:88
std::optional< std::string_view > ReadLine() LIFETIMEBOUND
Returns a string from current iterator position up to (but not including) next and advances iterator...
Definition string.cpp:35
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert command lines arguments to params object when -named is disabled.
Definition client.cpp:451
UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert command line arguments to params object when -named is enabled.
Definition client.cpp:491
std::string FormatFullVersion()
bool SetupNetworking()
Definition system.cpp:82
#define WSAGetLastError()
Definition compat.h:59
#define MSG_NOSIGNAL
Definition compat.h:110
void PrintExceptionContinue(const std::exception *pex, std::string_view thread_name)
Definition exception.cpp:36
const std::string CURRENCY_UNIT
Definition feerate.h:19
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition fs.h:160
util::LineReader reader
HTTPHeaders headers
std::string LicenseInfo()
Returns licensing information (for -version)
std::unique_ptr< ProxyClient< messages::FooInterface > > client
std::unique_ptr< Init > MakeBasicInit(const char *exe_name, const char *process_argv0)
Return implementation of Init interface for a basic IPC client that doesn't provide any IPC services ...
Definition basic.cpp:25
void format(std::ostream &out, FormatStringCheck< sizeof...(Args)> fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
std::string_view TrimStringView(std::string_view str LIFETIMEBOUND, std::string_view pattern=" \f\n\r\t\v")
Definition string.h:163
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition string.h:250
std::string TrimString(std::string_view str, std::string_view pattern=" \f\n\r\t\v")
Definition string.h:173
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition string.h:209
std::unique_ptr< Sock > ConnectDirectly(const CService &dest, bool manual_connection)
Create a socket and try to connect to the specified service.
Definition netbase.cpp:650
std::vector< CService > Lookup(const std::string &name, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function)
Resolve a service string to its corresponding service.
Definition netbase.cpp:191
std::vector< UniValue > JSONRPCProcessBatchReply(const UniValue &in)
Parse JSON-RPC batch reply into a vector.
Definition request.cpp:181
UniValue JSONRPCRequestObj(const std::string &strMethod, const UniValue &params, const UniValue &id)
JSON-RPC protocol.
Definition request.cpp:46
AuthCookieResult GetAuthCookie(std::string &cookie_out)
Read the RPC authentication cookie from disk.
Definition request.cpp:153
UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional< UniValue > id, JSONRPCVersion jsonrpc_version)
Definition request.cpp:56
const char * name
Definition rest.cpp:71
constexpr uint64_t DEFAULT_MAX_TRIES
Default max iterations to try in RPC generatetodescriptor, generatetoaddress, and generateblock.
Definition mining.h:11
static RPCMethod ping()
Definition net.cpp:110
@ HTTP_BAD_REQUEST
Definition protocol.h:16
@ HTTP_SERVICE_UNAVAILABLE
Definition protocol.h:23
@ HTTP_UNAUTHORIZED
Definition protocol.h:17
@ HTTP_NOT_FOUND
Definition protocol.h:19
@ HTTP_INTERNAL_SERVER_ERROR
Definition protocol.h:22
@ RPC_WALLET_NOT_SPECIFIED
No wallet specified (error when there are multiple wallets loaded)
Definition protocol.h:107
@ RPC_METHOD_NOT_FOUND
Definition protocol.h:57
@ RPC_IN_WARMUP
Client still warming up.
Definition protocol.h:75
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Definition sock.cpp:430
bool IOErrorIsPermanent(int err)
Definition sock.h:26
bool StdinReady()
Definition stdin.cpp:65
bool StdinTerminal()
Definition stdin.cpp:56
#define NO_STDIN_ECHO()
Definition stdin.h:13
Process addrinfo requests.
UniValue ProcessReply(const UniValue &reply) override
UniValue PrepareRequest(const std::string &method, const std::vector< std::string > &args) override
Handle the conversion from a command-line to a JSON-RPC request, as well as converting back to a JSON...
virtual UniValue ProcessReply(const UniValue &batch_in)=0
virtual ~BaseRequestHandler()=default
virtual UniValue PrepareRequest(const std::string &method, const std::vector< std::string > &args)=0
CConnectionFailed(const std::string &msg)
Process default single requests.
UniValue PrepareRequest(const std::string &method, const std::vector< std::string > &args) override
UniValue ProcessReply(const UniValue &reply) override
Process getinfo requests.
UniValue PrepareRequest(const std::string &method, const std::vector< std::string > &args) override
Create a simulated getinfo request.
UniValue ProcessReply(const UniValue &batch_in) override
Collect values from the batch and form a simulated getinfo reply.
HTTPError(const std::string &msg)
std::string body
bool operator<(const Peer &rhs) const
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
std::function< std::string(const char *)> TranslateFn
Translate a message to the native language of the user.
Definition translation.h:16
const UniValue NullUniValue
Definition univalue.cpp:15
std::string UrlEncode(std::string_view str)
Definition url.cpp:42
bool CaseInsensitiveEqual(std::string_view s1, std::string_view s2)
Locale-independent, ASCII-only comparator.
std::string FormatParagraph(std::string_view in, size_t width, size_t indent)
Format a paragraph of text to a fixed width, adding spaces for indentation to any added line.
bool SplitHostPort(std::string_view in, uint16_t &portOut, std::string &hostOut)
Splits socket address string into host string and port value.
std::string EncodeBase64(std::span< const unsigned char > input)
std::string ToLower(std::string_view str)
Returns the lowercase equivalent of the given string.
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition time.cpp:30
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82
assert(!tx.IsCoinBase())