aboutsummaryrefslogtreecommitdiff
path: root/src/bitcoin-cli.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bitcoin-cli.cpp')
-rw-r--r--src/bitcoin-cli.cpp105
1 files changed, 50 insertions, 55 deletions
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 5523fff3b2..e64e2202ba 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -9,23 +9,27 @@
#include <chainparamsbase.h>
#include <clientversion.h>
+#include <compat/compat.h>
+#include <compat/stdin.h>
#include <policy/feerate.h>
#include <rpc/client.h>
#include <rpc/mining.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <tinyformat.h>
+#include <univalue.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <util/translation.h>
#include <util/url.h>
#include <algorithm>
+#include <chrono>
#include <cmath>
+#include <cstdio>
#include <functional>
#include <memory>
#include <optional>
-#include <stdio.h>
#include <string>
#include <tuple>
@@ -37,8 +41,10 @@
#include <event2/keyvalq_struct.h>
#include <support/events.h>
-#include <univalue.h>
-#include <compat/stdin.h>
+// The server returns time values from a mockable system clock, but it is not
+// trivial to get the mocked time from the server, nor is it needed for now, so
+// just use a plain system_clock.
+using CliClock = std::chrono::system_clock;
const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
UrlDecodeFn* const URL_DECODE = urlDecode;
@@ -174,17 +180,16 @@ static int AppInitRPC(int argc, char* argv[])
/** Reply structure for request_done to fill in */
struct HTTPReply
{
- HTTPReply(): status(0), error(-1) {}
+ HTTPReply() = default;
- int status;
- int error;
+ int status{0};
+ int error{-1};
std::string body;
};
static std::string http_errorstring(int code)
{
switch(code) {
-#if LIBEVENT_VERSION_NUMBER >= 0x02010300
case EVREQ_HTTP_TIMEOUT:
return "timeout reached";
case EVREQ_HTTP_EOF:
@@ -197,7 +202,6 @@ static std::string http_errorstring(int code)
return "request was canceled";
case EVREQ_HTTP_DATA_TOO_LONG:
return "response body is larger than allowed";
-#endif
default:
return "unknown";
}
@@ -228,13 +232,11 @@ static void http_request_done(struct evhttp_request *req, void *ctx)
}
}
-#if LIBEVENT_VERSION_NUMBER >= 0x02010300
static void http_error_cb(enum evhttp_request_error err, void *ctx)
{
HTTPReply *reply = static_cast<HTTPReply*>(ctx);
reply->error = err;
}
-#endif
/** Class that handles the conversion from a command-line to a JSON-RPC request,
* as well as converting back to a JSON object that can be shown as result.
@@ -242,7 +244,7 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx)
class BaseRequestHandler
{
public:
- virtual ~BaseRequestHandler() {}
+ virtual ~BaseRequestHandler() = default;
virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0;
virtual UniValue ProcessReply(const UniValue &batch_in) = 0;
};
@@ -411,8 +413,8 @@ private:
bool is_addr_relay_enabled;
bool is_bip152_hb_from;
bool is_bip152_hb_to;
- bool is_block_relay;
bool is_outbound;
+ bool is_tx_relay;
bool operator<(const Peer& rhs) const { return std::tie(is_outbound, min_ping) < std::tie(rhs.is_outbound, rhs.min_ping); }
};
std::vector<Peer> m_peers;
@@ -437,7 +439,6 @@ private:
if (conn_type == "addr-fetch") return "addr";
return "";
}
- const int64_t m_time_now{GetTimeSeconds()};
public:
static constexpr int ID_PEERINFO = 0;
@@ -466,9 +467,10 @@ public:
if (!batch[ID_NETWORKINFO]["error"].isNull()) return batch[ID_NETWORKINFO];
const UniValue& networkinfo{batch[ID_NETWORKINFO]["result"]};
- if (networkinfo["version"].get_int() < 209900) {
+ if (networkinfo["version"].getInt<int>() < 209900) {
throw std::runtime_error("-netinfo requires bitcoind server to be running v0.21.0 and up");
}
+ const int64_t time_now{TicksSinceEpoch<std::chrono::seconds>(CliClock::now())};
// Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
for (const UniValue& peer : batch[ID_PEERINFO]["result"].getValues()) {
@@ -476,7 +478,7 @@ public:
const int8_t network_id{NetworkStringToId(network)};
if (network_id == UNKNOWN_NETWORK) continue;
const bool is_outbound{!peer["inbound"].get_bool()};
- const bool is_block_relay{!peer["relaytxes"].get_bool()};
+ const bool is_tx_relay{peer["relaytxes"].isNull() ? true : peer["relaytxes"].get_bool()};
const std::string conn_type{peer["connection_type"].get_str()};
++m_counts.at(is_outbound).at(network_id); // in/out by network
++m_counts.at(is_outbound).at(NETWORKS.size()); // in/out overall
@@ -486,25 +488,25 @@ public:
if (conn_type == "manual") ++m_manual_peers_count;
if (DetailsRequested()) {
// Push data for this peer to the peers vector.
- const int peer_id{peer["id"].get_int()};
- const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].get_int()};
- const int version{peer["version"].get_int()};
- const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].get_int64()};
- const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].get_int64()};
- const int64_t conn_time{peer["conntime"].get_int64()};
- const int64_t last_blck{peer["last_block"].get_int64()};
- const int64_t last_recv{peer["lastrecv"].get_int64()};
- const int64_t last_send{peer["lastsend"].get_int64()};
- const int64_t last_trxn{peer["last_transaction"].get_int64()};
+ const int peer_id{peer["id"].getInt<int>()};
+ const int mapped_as{peer["mapped_as"].isNull() ? 0 : peer["mapped_as"].getInt<int>()};
+ const int version{peer["version"].getInt<int>()};
+ const int64_t addr_processed{peer["addr_processed"].isNull() ? 0 : peer["addr_processed"].getInt<int64_t>()};
+ const int64_t addr_rate_limited{peer["addr_rate_limited"].isNull() ? 0 : peer["addr_rate_limited"].getInt<int64_t>()};
+ const int64_t conn_time{peer["conntime"].getInt<int64_t>()};
+ const int64_t last_blck{peer["last_block"].getInt<int64_t>()};
+ const int64_t last_recv{peer["lastrecv"].getInt<int64_t>()};
+ const int64_t last_send{peer["lastsend"].getInt<int64_t>()};
+ const int64_t last_trxn{peer["last_transaction"].getInt<int64_t>()};
const double min_ping{peer["minping"].isNull() ? -1 : peer["minping"].get_real()};
const double ping{peer["pingtime"].isNull() ? -1 : peer["pingtime"].get_real()};
const std::string addr{peer["addr"].get_str()};
- const std::string age{conn_time == 0 ? "" : ToString((m_time_now - conn_time) / 60)};
+ const std::string age{conn_time == 0 ? "" : ToString((time_now - conn_time) / 60)};
const std::string sub_version{peer["subver"].get_str()};
const bool is_addr_relay_enabled{peer["addr_relay_enabled"].isNull() ? false : peer["addr_relay_enabled"].get_bool()};
const bool is_bip152_hb_from{peer["bip152_hb_from"].get_bool()};
const bool is_bip152_hb_to{peer["bip152_hb_to"].get_bool()};
- m_peers.push_back({addr, sub_version, conn_type, network, age, 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_block_relay, is_outbound});
+ m_peers.push_back({addr, sub_version, conn_type, network, age, 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});
m_max_addr_length = std::max(addr.length() + 1, m_max_addr_length);
m_max_addr_processed_length = std::max(ToString(addr_processed).length(), m_max_addr_processed_length);
m_max_addr_rate_limited_length = std::max(ToString(addr_rate_limited).length(), m_max_addr_rate_limited_length);
@@ -515,7 +517,7 @@ public:
}
// Generate report header.
- std::string result{strprintf("%s client %s%s - server %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].get_int(), networkinfo["subversion"].get_str())};
+ std::string result{strprintf("%s client %s%s - server %i%s\n\n", PACKAGE_NAME, FormatFullVersion(), ChainToString(), networkinfo["protocolversion"].getInt<int>(), networkinfo["subversion"].get_str())};
// Report detailed peer connections list sorted by direction and minimum ping time.
if (DetailsRequested() && !m_peers.empty()) {
@@ -535,10 +537,10 @@ public:
peer.network,
PingTimeToString(peer.min_ping),
PingTimeToString(peer.ping),
- peer.last_send ? ToString(m_time_now - peer.last_send) : "",
- peer.last_recv ? ToString(m_time_now - peer.last_recv) : "",
- peer.last_trxn ? ToString((m_time_now - peer.last_trxn) / 60) : peer.is_block_relay ? "*" : "",
- peer.last_blck ? ToString((m_time_now - peer.last_blck) / 60) : "",
+ peer.last_send ? ToString(time_now - peer.last_send) : "",
+ peer.last_recv ? ToString(time_now - peer.last_recv) : "",
+ peer.last_trxn ? ToString((time_now - peer.last_trxn) / 60) : peer.is_tx_relay ? "" : "*",
+ peer.last_blck ? ToString((time_now - peer.last_blck) / 60) : "",
strprintf("%s%s", peer.is_bip152_hb_to ? "." : " ", peer.is_bip152_hb_from ? "*" : " "),
m_max_addr_processed_length, // variable spacing
peer.addr_processed ? ToString(peer.addr_processed) : peer.is_addr_relay_enabled ? "" : ".",
@@ -596,7 +598,7 @@ public:
max_addr_size = std::max(addr["address"].get_str().length() + 1, max_addr_size);
}
for (const UniValue& addr : local_addrs) {
- result += strprintf("\n%-*s port %6i score %6i", max_addr_size, addr["address"].get_str(), addr["port"].get_int(), addr["score"].get_int());
+ result += strprintf("\n%-*s port %6i score %6i", max_addr_size, addr["address"].get_str(), addr["port"].getInt<int>(), addr["score"].getInt<int>());
}
}
@@ -745,11 +747,11 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
HTTPReply response;
raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
- if (req == nullptr)
+ if (req == nullptr) {
throw std::runtime_error("create http request failed");
-#if LIBEVENT_VERSION_NUMBER >= 0x02010300
+ }
+
evhttp_request_set_error_cb(req.get(), http_error_cb);
-#endif
// Get credentials
std::string strRPCUserColonPass;
@@ -805,7 +807,7 @@ static UniValue CallRPC(BaseRequestHandler* rh, const std::string& strMethod, co
if (failedToGetAuthCookie) {
throw std::runtime_error(strprintf(
"Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)",
- fs::PathToString(GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)))));
+ fs::PathToString(GetConfigFile(gArgs.GetPathArg("-conf", BITCOIN_CONF_FILENAME)))));
} else {
throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword");
}
@@ -842,21 +844,20 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
// Execute and handle connection failures with -rpcwait.
const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
const int timeout = gArgs.GetIntArg("-rpcwaittimeout", DEFAULT_WAIT_CLIENT_TIMEOUT);
- const auto deadline{GetTime<std::chrono::microseconds>() + 1s * timeout};
+ const auto deadline{std::chrono::steady_clock::now() + 1s * timeout};
do {
try {
response = CallRPC(rh, strMethod, args, rpcwallet);
if (fWait) {
const UniValue& error = find_value(response, "error");
- if (!error.isNull() && error["code"].get_int() == RPC_IN_WARMUP) {
+ if (!error.isNull() && error["code"].getInt<int>() == RPC_IN_WARMUP) {
throw CConnectionFailed("server in warmup");
}
}
break; // Connection succeeded, no need to retry.
} catch (const CConnectionFailed& e) {
- const auto now{GetTime<std::chrono::microseconds>()};
- if (fWait && (timeout <= 0 || now < deadline)) {
+ if (fWait && (timeout <= 0 || std::chrono::steady_clock::now() < deadline)) {
UninterruptibleSleep(1s);
} else {
throw CConnectionFailed(strprintf("timeout on transient error: %s", e.what()));
@@ -885,13 +886,13 @@ static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
if (err_msg.isStr()) {
strPrint += ("error message:\n" + err_msg.get_str());
}
- if (err_code.isNum() && err_code.get_int() == RPC_WALLET_NOT_SPECIFIED) {
+ if (err_code.isNum() && err_code.getInt<int>() == RPC_WALLET_NOT_SPECIFIED) {
strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
}
} else {
strPrint = "error: " + error.write();
}
- nRet = abs(error["code"].get_int());
+ nRet = abs(error["code"].getInt<int>());
}
/**
@@ -910,7 +911,7 @@ static void GetWalletBalances(UniValue& result)
UniValue balances(UniValue::VOBJ);
for (const UniValue& wallet : wallets.getValues()) {
- const std::string wallet_name = wallet.get_str();
+ const std::string& wallet_name = wallet.get_str();
const UniValue getbalances = ConnectAndCallRPC(&rh, "getbalances", /* args=*/{}, wallet_name);
const UniValue& balance = find_value(getbalances, "result")["mine"]["trusted"];
balances.pushKV(wallet_name, balance);
@@ -1059,7 +1060,9 @@ static void ParseGetInfoResult(UniValue& result)
result_string += "\n";
}
- result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, result["warnings"].getValStr());
+ const std::string warnings{result["warnings"].getValStr()};
+ result_string += strprintf("%sWarnings:%s %s", YELLOW, RESET, warnings.empty() ? "(none)" : warnings);
+
result.setStr(result_string);
}
@@ -1210,19 +1213,11 @@ static int CommandLineRPC(int argc, char *argv[])
return nRet;
}
-#ifdef WIN32
-// Export main() and ensure working ASLR on Windows.
-// Exporting a symbol will prevent the linker from stripping
-// the .reloc section from the binary, which is a requirement
-// for ASLR. This is a temporary workaround until a fixed
-// version of binutils is used for releases.
-__declspec(dllexport) int main(int argc, char* argv[])
+MAIN_FUNCTION
{
+#ifdef WIN32
util::WinCmdLineArgs winArgs;
std::tie(argc, argv) = winArgs.get();
-#else
-int main(int argc, char* argv[])
-{
#endif
SetupEnvironment();
if (!SetupNetworking()) {