aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
author0xb10c <b10c@b10c.me>2023-09-20 19:37:45 +0200
committer0xb10c <b10c@b10c.me>2023-10-02 15:34:28 +0200
commitda384a286bd84a97e7ebe7a64654c5be20ab2df1 (patch)
tree443d2d048e0692a702f6ac46c1d85c573a5d67e8 /src/rpc
parent8909667ab8a65478e0eaa6495299305ad836e835 (diff)
rpc: getrawaddrman for addrman entries
Exposing address manager table entries in a hidden RPC allows to introspect addrman tables in tests and during development. As response JSON object the following FORMAT1 is choosen: { "table": { "<bucket>/<position>": { "address": "..", "port": .., ... }, "<bucket>/<position>": { "address": "..", "port": .., ... }, "<bucket>/<position>": { "address": "..", "port": .., ... }, ... } } An alternative would be FORMAT2 { "table": { "bucket": { "position": { "address": "..", "port": .., ... }, "position": { "address": "..", "port": .., ... }, .. }, "bucket": { "position": { "address": "..", "port": .., ... }, .. }, } } FORMAT1 and FORMAT2 have different encodings for the location of the address in the address manager. While FORMAT2 might be easier to process for downstream tools, it also mimics internal addrman mappings, which might change at some point. Users not interested in the address location can ignore the location key. They don't have to adapt to a new RPC response format, when the internal addrman layout changes. Additionally, FORMAT1 is also slightly easier to to iterate in downstream tools. The RPC response-building implemenation complexcity is lower with FORMAT1 as we can more easily build a "<bucket>/<position>" key than a multiple "bucket" objects with multiple "position" objects (FORMAT2).
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/net.cpp70
1 files changed, 70 insertions, 0 deletions
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 6af62641bd..8ebc6a73cf 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -5,6 +5,7 @@
#include <rpc/server.h>
#include <addrman.h>
+#include <addrman_impl.h>
#include <banman.h>
#include <chainparams.h>
#include <clientversion.h>
@@ -1063,6 +1064,74 @@ static RPCHelpMan getaddrmaninfo()
};
}
+UniValue AddrmanEntryToJSON(const AddrInfo& info)
+{
+ UniValue ret(UniValue::VOBJ);
+ ret.pushKV("address", info.ToStringAddr());
+ ret.pushKV("port", info.GetPort());
+ ret.pushKV("services", (uint64_t)info.nServices);
+ ret.pushKV("time", int64_t{TicksSinceEpoch<std::chrono::seconds>(info.nTime)});
+ ret.pushKV("network", GetNetworkName(info.GetNetClass()));
+ ret.pushKV("source", info.source.ToStringAddr());
+ ret.pushKV("source_network", GetNetworkName(info.source.GetNetClass()));
+ return ret;
+}
+
+UniValue AddrmanTableToJSON(const std::vector<std::pair<AddrInfo, AddressPosition>>& tableInfos)
+{
+ UniValue table(UniValue::VOBJ);
+ for (const auto& e : tableInfos) {
+ AddrInfo info = e.first;
+ AddressPosition location = e.second;
+ std::ostringstream key;
+ key << location.bucket << "/" << location.position;
+ // Address manager tables have unique entries so there is no advantage
+ // in using UniValue::pushKV, which checks if the key already exists
+ // in O(N). UniValue::pushKVEnd is used instead which currently is O(1).
+ table.pushKVEnd(key.str(), AddrmanEntryToJSON(info));
+ }
+ return table;
+}
+
+static RPCHelpMan getrawaddrman()
+{
+ return RPCHelpMan{"getrawaddrman",
+ "EXPERIMENTAL warning: this call may be changed in future releases.\n"
+ "\nReturns information on all address manager entries for the new and tried tables.\n",
+ {},
+ RPCResult{
+ RPCResult::Type::OBJ_DYN, "", "", {
+ {RPCResult::Type::OBJ_DYN, "table", "buckets with addresses in the address manager table ( new, tried )", {
+ {RPCResult::Type::OBJ, "bucket/position", "the location in the address manager table (<bucket>/<position>)", {
+ {RPCResult::Type::STR, "address", "The address of the node"},
+ {RPCResult::Type::NUM, "port", "The port number of the node"},
+ {RPCResult::Type::STR, "network", "The network (" + Join(GetNetworkNames(), ", ") + ") of the address"},
+ {RPCResult::Type::NUM, "services", "The services offered by the node"},
+ {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " when the node was last seen"},
+ {RPCResult::Type::STR, "source", "The address that relayed the address to us"},
+ {RPCResult::Type::STR, "source_network", "The network (" + Join(GetNetworkNames(), ", ") + ") of the source address"},
+ }}
+ }}
+ }
+ },
+ RPCExamples{
+ HelpExampleCli("getrawaddrman", "")
+ + HelpExampleRpc("getrawaddrman", "")
+ },
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
+ NodeContext& node = EnsureAnyNodeContext(request.context);
+ if (!node.addrman) {
+ throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled");
+ }
+
+ UniValue ret(UniValue::VOBJ);
+ ret.pushKV("new", AddrmanTableToJSON(node.addrman->GetEntries(false)));
+ ret.pushKV("tried", AddrmanTableToJSON(node.addrman->GetEntries(true)));
+ return ret;
+ },
+ };
+}
+
void RegisterNetRPCCommands(CRPCTable& t)
{
static const CRPCCommand commands[]{
@@ -1083,6 +1152,7 @@ void RegisterNetRPCCommands(CRPCTable& t)
{"hidden", &addpeeraddress},
{"hidden", &sendmsgtopeer},
{"hidden", &getaddrmaninfo},
+ {"hidden", &getrawaddrman},
};
for (const auto& c : commands) {
t.appendCommand(c.name, &c);