Skip to content

Commit

Permalink
Merge bitcoin#21595: cli: create -addrinfo
Browse files Browse the repository at this point in the history
06c4320 cli: use C++17 std::array class template argument deduction (CTAD) (Jon Atack)
edf3167 addrinfo: raise helpfully on server error or incompatible server version (Jon Atack)
bb85cbc doc: add cli -addrinfo release note (Jon Atack)
5056a37 cli: add -addrinfo command (Jon Atack)
db4d2c2 cli: create AddrinfoRequestHandler class (Jon Atack)

Pull request description:

  While looking at issue bitcoin#21351, it turned out that the problem was a lack of tor v3 addresses known to the node. It became clear (e.g. bitcoin#21351 (comment)) 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 06c4320

Tree-SHA512: b668b47718a4ce052aff218789f3da629bca730592c18fcce9a51034d95a0a65f8e6da33dd47443cdd8f60c056c02696db175b0fe09a688e4385a76c1d8b7aeb
  • Loading branch information
laanwj authored and vijaydasmp committed Apr 12, 2024
1 parent d85f406 commit 1b464dd
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 11 deletions.
9 changes: 9 additions & 0 deletions doc/release-notes-21595.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
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. (#5983)

78 changes: 67 additions & 11 deletions src/bitcoin-cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
static constexpr int DEFAULT_WAIT_CLIENT_TIMEOUT = 0;
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";
Expand All @@ -62,6 +63,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: dash-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);
argsman.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
Expand Down Expand Up @@ -243,6 +245,60 @@ class BaseRequestHandler
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
{
Expand Down Expand Up @@ -318,13 +374,11 @@ class GetinfoRequestHandler: public BaseRequestHandler
class NetinfoRequestHandler : public BaseRequestHandler
{
private:
static constexpr int8_t UNKNOWN_NETWORK{-1};
static constexpr uint8_t m_networks_size{3};
const std::array<std::string, m_networks_size> m_networks{{"ipv4", "ipv6", "onion"}};
std::array<std::array<uint16_t, m_networks_size + 2>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total/block-relay)
static constexpr std::array m_networks{{"ipv4", "ipv6", "onion"}};
std::array<std::array<uint16_t, m_networks.size() + 2>, 3> m_counts{{{}}}; //!< Peer counts by (in/out/total, networks/total/block-relay)
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;
Expand Down Expand Up @@ -472,12 +526,12 @@ class NetinfoRequestHandler : public BaseRequestHandler
const bool is_outbound{!peer["inbound"].get_bool()};
const bool is_block_relay{!peer["relaytxes"].get_bool()};
++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(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(2).at(m_networks.size()); // total overall
if (is_block_relay) {
++m_counts.at(is_outbound).at(m_networks_size + 1); // in/out block-relay
++m_counts.at(2).at(m_networks_size + 1); // total block-relay
++m_counts.at(is_outbound).at(m_networks.size() + 1); // in/out block-relay
++m_counts.at(2).at(m_networks.size() + 1); // total block-relay
}
if (DetailsRequested()) {
// Push data for this peer to the peers vector.
Expand Down Expand Up @@ -539,9 +593,9 @@ class NetinfoRequestHandler : public BaseRequestHandler

// Report peer connection totals by type.
result += " ipv4 ipv6 onion total block-relay\n";
const std::array<std::string, 3> rows{{"in", "out", "total"}};
const std::array rows{"in", "out", "total"};
for (uint8_t i = 0; i < m_networks_size; ++i) {
result += strprintf("%-5s %5i %5i %5i %5i %5i\n", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2), m_counts.at(i).at(m_networks_size), m_counts.at(i).at(m_networks_size + 1));
result += strprintf("%-5s %5i %5i %5i %5i %5i\n", rows.at(i), m_counts.at(i).at(0), m_counts.at(i).at(1), m_counts.at(i).at(2), m_counts.at(i).at(m_networks.size()), m_counts.at(i).at(m_networks.size() + 1));
}

// Report local addresses, ports, and scores.
Expand Down Expand Up @@ -908,6 +962,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) {
Expand Down

0 comments on commit 1b464dd

Please sign in to comment.