From c8eb8dae51039aa1938e7040001a149210e87275 Mon Sep 17 00:00:00 2001 From: stratospher <44024636+stratospher@users.noreply.github.com> Date: Sun, 29 Jan 2023 18:59:00 +0530 Subject: [PATCH 1/2] rpc: Introduce getaddrmaninfo for count of addresses stored in new/tried table --- src/rpc/net.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++ src/test/fuzz/rpc.cpp | 1 + 2 files changed, 51 insertions(+) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index f7b6c68344667..e9e9ad62aeb33 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -1016,6 +1016,55 @@ static RPCHelpMan sendmsgtopeer() }; } +static RPCHelpMan getaddrmaninfo() +{ + return RPCHelpMan{"getaddrmaninfo", + "\nProvides information about the node's address manager by returning the number of " + "addresses in the `new` and `tried` tables and their sum for all networks.\n" + "This RPC is for testing only.\n", + {}, + RPCResult{ + RPCResult::Type::OBJ_DYN, "", "json object with network type as keys", + { + {RPCResult::Type::OBJ, "network", "the network (" + Join(GetNetworkNames(), ", ") + ")", + { + {RPCResult::Type::NUM, "new", "number of addresses in the new table, which represent potential peers the node has discovered but hasn't yet successfully connected to."}, + {RPCResult::Type::NUM, "tried", "number of addresses in the tried table, which represent peers the node has successfully connected to in the past."}, + {RPCResult::Type::NUM, "total", "total number of addresses in both new/tried tables"}, + }}, + } + }, + RPCExamples{ + HelpExampleCli("getaddrmaninfo", "") + + HelpExampleRpc("getaddrmaninfo", "") + }, + [&](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); + for (int n = 0; n < NET_MAX; ++n) { + enum Network network = static_cast(n); + if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue; + UniValue obj(UniValue::VOBJ); + obj.pushKV("new", node.addrman->Size(network, true)); + obj.pushKV("tried", node.addrman->Size(network, false)); + obj.pushKV("total", node.addrman->Size(network)); + ret.pushKV(GetNetworkName(network), obj); + } + UniValue obj(UniValue::VOBJ); + obj.pushKV("new", node.addrman->Size(std::nullopt, true)); + obj.pushKV("tried", node.addrman->Size(std::nullopt, false)); + obj.pushKV("total", node.addrman->Size()); + ret.pushKV("all_networks", obj); + return ret; + }, + }; +} + void RegisterNetRPCCommands(CRPCTable& t) { static const CRPCCommand commands[]{ @@ -1035,6 +1084,7 @@ void RegisterNetRPCCommands(CRPCTable& t) {"hidden", &addconnection}, {"hidden", &addpeeraddress}, {"hidden", &sendmsgtopeer}, + {"hidden", &getaddrmaninfo}, }; for (const auto& c : commands) { t.appendCommand(c.name, &c); diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp index 24ec0e4a73722..543c9933bb935 100644 --- a/src/test/fuzz/rpc.cpp +++ b/src/test/fuzz/rpc.cpp @@ -110,6 +110,7 @@ const std::vector RPC_COMMANDS_SAFE_FOR_FUZZING{ "generate", "generateblock", "getaddednodeinfo", + "getaddrmaninfo", "getbestblockhash", "getblock", "getblockchaininfo", From 28bac81a346c0b68273fa73af924f7096cb3f41d Mon Sep 17 00:00:00 2001 From: stratospher <44024636+stratospher@users.noreply.github.com> Date: Sun, 29 Jan 2023 23:54:43 +0530 Subject: [PATCH 2/2] test: add functional test for getaddrmaninfo --- test/functional/rpc_net.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 255f5108a2556..4fbe22a8462af 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -66,6 +66,7 @@ def run_test(self): self.test_getnodeaddresses() self.test_addpeeraddress() self.test_sendmsgtopeer() + self.test_getaddrmaninfo() def test_connection_count(self): self.log.info("Test getconnectioncount") @@ -360,6 +361,28 @@ def test_sendmsgtopeer(self): node.sendmsgtopeer(peer_id=0, msg_type="addr", msg=zero_byte_string.hex()) self.wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0, timeout=10) + def test_getaddrmaninfo(self): + self.log.info("Test getaddrmaninfo") + node = self.nodes[1] + + self.log.debug("Test that getaddrmaninfo is a hidden RPC") + # It is hidden from general help, but its detailed help may be called directly. + assert "getaddrmaninfo" not in node.help() + assert "getaddrmaninfo" in node.help("getaddrmaninfo") + + # current count of ipv4 addresses in addrman is {'new':1, 'tried':1} + self.log.info("Test that count of addresses in addrman match expected values") + res = node.getaddrmaninfo() + assert_equal(res["ipv4"]["new"], 1) + assert_equal(res["ipv4"]["tried"], 1) + assert_equal(res["ipv4"]["total"], 2) + assert_equal(res["all_networks"]["new"], 1) + assert_equal(res["all_networks"]["tried"], 1) + assert_equal(res["all_networks"]["total"], 2) + for net in ["ipv6", "onion", "i2p", "cjdns"]: + assert_equal(res[net]["new"], 0) + assert_equal(res[net]["tried"], 0) + assert_equal(res[net]["total"], 0) if __name__ == '__main__': NetTest().main()