0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-08 10:31:50 -05:00

Merge #21595: cli: create -addrinfo

06c43201a7 cli: use C++17 std::array class template argument deduction (CTAD) (Jon Atack)
edf3167151 addrinfo: raise helpfully on server error or incompatible server version (Jon Atack)
bb85cbc4f7 doc: add cli -addrinfo release note (Jon Atack)
5056a37624 cli: add -addrinfo command (Jon Atack)
db4d2c282a cli: create AddrinfoRequestHandler class (Jon Atack)

Pull request description:

  While looking at issue #21351, it turned out that the problem was a lack of tor v3 addresses known to the node. It became clear (e.g. https://github.com/bitcoin/bitcoin/issues/21351#issuecomment-811004779) that a CLI command returning the number of addresses the node knows per network (with a tor v2 / v3 breakdown) would be very helpful. This patch adds that.

  `-addrinfo` is useful to see if your node knows enough addresses in a network to use options like `-onlynet=<network>`, or to upgrade to the upcoming tor release that no longer supports tor v2, for which you'll need to be sure your node knows enough tor v3 peers.

  ```
  $ bitcoin-cli --help | grep -A1 addrinfo
    -addrinfo
         Get the number of addresses known to the node, per network and total.

  $ bitcoin-cli -addrinfo
  {
    "addresses_known": {
      "ipv4": 14406,
      "ipv6": 2511,
      "torv2": 5563,
      "torv3": 2842,
      "i2p": 8,
      "total": 25330
    }
  }

  $ bitcoin-cli -addrinfo 1
  error: -addrinfo takes no arguments
  ```

  This can be manually tested, for example, with commands like this:
  ```
  $ bitcoin-cli getnodeaddresses 0 | jq '.[] | (select(.address | contains(".onion")) | select(.address | length <= 22)) | .address' | wc -l
  5563
  $ bitcoin-cli getnodeaddresses 0 | jq '.[] | (select(.address | contains(".onion")) | select(.address | length > 22)) | .address' | wc -l
  2842
  $ bitcoin-cli getnodeaddresses 0 | jq '.[] | .address' | wc -l
  25330
  ```

ACKs for top commit:
  laanwj:
    Tested ACK 06c43201a7

Tree-SHA512: b668b47718a4ce052aff218789f3da629bca730592c18fcce9a51034d95a0a65f8e6da33dd47443cdd8f60c056c02696db175b0fe09a688e4385a76c1d8b7aeb
This commit is contained in:
W. J. van der Laan 2021-04-20 14:05:23 +02:00
commit 0180453471
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
2 changed files with 73 additions and 11 deletions

View file

@ -136,6 +136,12 @@ Changes to Wallet or GUI related settings can be found in the GUI or Wallet sect
Tools and Utilities
-------------------
- A new CLI `-addrinfo` command returns the number of addresses known to the
node per network type (including Tor v2 versus v3) and total. This can be
useful to see if the node knows enough addresses in a network to use options
like `-onlynet=<network>` or to upgrade to current and future Tor releases
that support Tor v3 addresses only. (#21595)
Wallet
------

View file

@ -42,6 +42,7 @@ static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
static const bool DEFAULT_NAMED=false;
static const int CONTINUE_EXECUTION=-1;
static constexpr int8_t UNKNOWN_NETWORK{-1};
/** Default number of blocks to generate for RPC generatetoaddress. */
static const std::string DEFAULT_NBLOCKS = "1";
@ -59,6 +60,7 @@ static void SetupCliArgs(ArgsManager& argsman)
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);
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-addrinfo", "Get the number of addresses known to the node, per network and total.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result 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::OPTIONS);
argsman.AddArg("-netinfo", "Get network peer connection information from the remote server. An optional integer argument from 0 to 4 can be passed for different peers listings (default: 0). Pass \"help\" for detailed help documentation.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@ -228,6 +230,60 @@ public:
virtual UniValue ProcessReply(const UniValue &batch_in) = 0;
};
/** Process addrinfo requests */
class AddrinfoRequestHandler : public BaseRequestHandler
{
private:
static constexpr std::array m_networks{"ipv4", "ipv6", "torv2", "torv3", "i2p"};
int8_t NetworkStringToId(const std::string& str) const
{
for (size_t i = 0; i < m_networks.size(); ++i) {
if (str == m_networks.at(i)) return i;
}
return UNKNOWN_NETWORK;
}
public:
UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
{
if (!args.empty()) {
throw std::runtime_error("-addrinfo takes no arguments");
}
UniValue params{RPCConvertValues("getnodeaddresses", std::vector<std::string>{{"0"}})};
return JSONRPCRequestObj("getnodeaddresses", params, 1);
}
UniValue ProcessReply(const UniValue& reply) override
{
if (!reply["error"].isNull()) return reply;
const std::vector<UniValue>& nodes{reply["result"].getValues()};
if (!nodes.empty() && nodes.at(0)["network"].isNull()) {
throw std::runtime_error("-addrinfo requires bitcoind server to be running v22.0 and up");
}
// Count the number of peers we know by network, including torv2 versus torv3.
std::array<uint64_t, m_networks.size()> counts{{}};
for (const UniValue& node : nodes) {
std::string network_name{node["network"].get_str()};
if (network_name == "onion") {
network_name = node["address"].get_str().size() > 22 ? "torv3" : "torv2";
}
const int8_t network_id{NetworkStringToId(network_name)};
if (network_id == UNKNOWN_NETWORK) continue;
++counts.at(network_id);
}
// Prepare result to return to user.
UniValue result{UniValue::VOBJ}, addresses{UniValue::VOBJ};
uint64_t total{0}; // Total address count
for (size_t i = 0; i < m_networks.size(); ++i) {
addresses.pushKV(m_networks.at(i), counts.at(i));
total += counts.at(i);
}
addresses.pushKV("total", total);
result.pushKV("addresses_known", addresses);
return JSONRPCReplyObj(result, NullUniValue, 1);
}
};
/** Process getinfo requests */
class GetinfoRequestHandler: public BaseRequestHandler
{
@ -299,16 +355,14 @@ public:
class NetinfoRequestHandler : public BaseRequestHandler
{
private:
static constexpr int8_t UNKNOWN_NETWORK{-1};
static constexpr uint8_t m_networks_size{4};
static constexpr uint8_t MAX_DETAIL_LEVEL{4};
const std::array<std::string, m_networks_size> m_networks{{"ipv4", "ipv6", "onion", "i2p"}};
std::array<std::array<uint16_t, m_networks_size + 1>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total)
static constexpr std::array m_networks{"ipv4", "ipv6", "onion", "i2p"};
std::array<std::array<uint16_t, m_networks.size() + 1>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total)
uint8_t m_block_relay_peers_count{0};
uint8_t m_manual_peers_count{0};
int8_t NetworkStringToId(const std::string& str) const
{
for (uint8_t i = 0; i < m_networks_size; ++i) {
for (size_t i = 0; i < m_networks.size(); ++i) {
if (str == m_networks.at(i)) return i;
}
return UNKNOWN_NETWORK;
@ -405,10 +459,10 @@ public:
const bool is_outbound{!peer["inbound"].get_bool()};
const bool is_block_relay{!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(m_networks_size); // in/out overall
++m_counts.at(2).at(network_id); // total by network
++m_counts.at(2).at(m_networks_size); // total overall
++m_counts.at(is_outbound).at(network_id); // in/out by network
++m_counts.at(is_outbound).at(m_networks.size()); // in/out overall
++m_counts.at(2).at(network_id); // total by network
++m_counts.at(2).at(m_networks.size()); // total overall
if (conn_type == "block-relay-only") ++m_block_relay_peers_count;
if (conn_type == "manual") ++m_manual_peers_count;
if (DetailsRequested()) {
@ -478,11 +532,11 @@ public:
if (any_i2p_peers) result += " i2p";
result += " total block";
if (m_manual_peers_count) result += " manual";
const std::array<std::string, 3> rows{{"in", "out", "total"}};
const std::array rows{"in", "out", "total"};
for (uint8_t i = 0; i < 3; ++i) {
result += strprintf("\n%-5s %5i %5i %5i", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2)); // ipv4/ipv6/onion peers counts
if (any_i2p_peers) result += strprintf(" %5i", m_counts.at(i).at(3)); // i2p peers count
result += strprintf(" %5i", m_counts.at(i).at(m_networks_size)); // total peers count
result += strprintf(" %5i", m_counts.at(i).at(m_networks.size())); // total peers count
if (i == 1) { // the outbound row has two extra columns for block relay and manual peer counts
result += strprintf(" %5i", m_block_relay_peers_count);
if (m_manual_peers_count) result += strprintf(" %5i", m_manual_peers_count);
@ -914,6 +968,8 @@ static int CommandLineRPC(int argc, char *argv[])
} else {
ParseError(error, strPrint, nRet);
}
} else if (gArgs.GetBoolArg("-addrinfo", false)) {
rh.reset(new AddrinfoRequestHandler());
} else {
rh.reset(new DefaultRequestHandler());
if (args.size() < 1) {