Skip to content

Commit

Permalink
merge bitcoin#21843: enable GetAddr, GetAddresses, and getnodeaddress…
Browse files Browse the repository at this point in the history
…es by network

continuation of cf27db8 from dash#5491

includes:
- 6c98c09
- 3f89c0e
- ce6bca8
  • Loading branch information
kwvg committed Apr 12, 2024
1 parent 51edeb0 commit ff3497c
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 22 deletions.
3 changes: 3 additions & 0 deletions doc/release-notes-5978.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ RPC changes

- The `getnodeaddresses` RPC now returns a "network" field indicating the
network type (ipv4, ipv6, onion, or i2p) for each address.

- `getnodeaddresses` now also accepts a "network" argument (ipv4, ipv6, onion,
or i2p) to return only addresses of the specified network.
15 changes: 12 additions & 3 deletions src/rpc/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,7 @@ static RPCHelpMan getnodeaddresses()
"\nReturn known addresses, which can potentially be used to find new nodes in the network.\n",
{
{"count", RPCArg::Type::NUM, /* default */ "1", "The maximum number of addresses to return. Specify 0 to return all known addresses."},
{"network", RPCArg::Type::STR, /* default */ "all networks", "Return only addresses of the specified network. Can be one of: " + Join(GetNetworkNames(), ", ") + "."},
},
RPCResult{
RPCResult::Type::ARR, "", "",
Expand All @@ -920,7 +921,10 @@ static RPCHelpMan getnodeaddresses()
},
RPCExamples{
HelpExampleCli("getnodeaddresses", "8")
+ HelpExampleRpc("getnodeaddresses", "8")
+ HelpExampleCli("getnodeaddresses", "4 \"i2p\"")
+ HelpExampleCli("-named getnodeaddresses", "network=onion count=12")
+ HelpExampleRpc("getnodeaddresses", "8")
+ HelpExampleRpc("getnodeaddresses", "4, \"i2p\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
Expand All @@ -932,8 +936,13 @@ static RPCHelpMan getnodeaddresses()
const int count{request.params[0].isNull() ? 1 : request.params[0].get_int()};
if (count < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");

const std::optional<Network> network{request.params[1].isNull() ? std::nullopt : std::optional<Network>{ParseNetwork(request.params[1].get_str())}};
if (network == NET_UNROUTABLE) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Network not recognized: %s", request.params[1].get_str()));
}

// returns a shuffled list of CAddress
const std::vector<CAddress> vAddr{node.connman->GetAddresses(count, /* max_pct */ 0, /* network */ std::nullopt)};
const std::vector<CAddress> vAddr{node.connman->GetAddresses(count, /* max_pct */ 0, network)};
UniValue ret(UniValue::VARR);

for (const CAddress& addr : vAddr) {
Expand Down Expand Up @@ -1020,7 +1029,7 @@ static const CRPCCommand commands[] =
{ "network", "clearbanned", &clearbanned, {} },
{ "network", "cleardiscouraged", &cleardiscouraged, {} },
{ "network", "setnetworkactive", &setnetworkactive, {"state"} },
{ "network", "getnodeaddresses", &getnodeaddresses, {"count"} },
{ "network", "getnodeaddresses", &getnodeaddresses, {"count", "network"} },

{ "hidden", "addconnection", &addconnection, {"address", "connection_type"} },
{ "hidden", "addpeeraddress", &addpeeraddress, {"address", "port"} },
Expand Down
49 changes: 30 additions & 19 deletions test/functional/rpc_net.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,43 +204,54 @@ def test_service_flags(self):
def test_getnodeaddresses(self):
self.log.info("Test getnodeaddresses")
self.nodes[0].add_p2p_connection(P2PInterface())
services = NODE_NETWORK

# Add some addresses to the Address Manager over RPC. Due to the way
# bucket and bucket position are calculated, some of these addresses
# will collide.
# Add an IPv6 address to the address manager.
ipv6_addr = "1233:3432:2434:2343:3234:2345:6546:4534"
self.nodes[0].addpeeraddress(address=ipv6_addr, port=8333)

# Add 10,000 IPv4 addresses to the address manager. Due to the way bucket
# and bucket positions are calculated, some of these addresses will collide.
imported_addrs = []
for i in range(10000):
first_octet = i >> 8
second_octet = i % 256
a = "{}.{}.1.1".format(first_octet, second_octet) # IPV4
a = f"{first_octet}.{second_octet}.1.1"
imported_addrs.append(a)
self.nodes[0].addpeeraddress(a, 8333)

# Obtain addresses via rpc call and check they were ones sent in before.
#
# Maximum possible addresses in addrman is 10000, although actual
# number will usually be less due to bucket and bucket position
# collisions.
node_addresses = self.nodes[0].getnodeaddresses(0)
# Fetch the addresses via the RPC and test the results.
assert_equal(len(self.nodes[0].getnodeaddresses()), 1) # default count is 1
assert_equal(len(self.nodes[0].getnodeaddresses(count=2)), 2)
assert_equal(len(self.nodes[0].getnodeaddresses(network="ipv4", count=8)), 8)

# Maximum possible addresses in AddrMan is 10000. The actual number will
# usually be less due to bucket and bucket position collisions.
node_addresses = self.nodes[0].getnodeaddresses(0, "ipv4")
assert_greater_than(len(node_addresses), 5000)
assert_greater_than(10000, len(node_addresses))
for a in node_addresses:
assert_equal(a["time"], self.mocktime)
assert_equal(a["services"], NODE_NETWORK)
assert_equal(a["services"], services)
assert a["address"] in imported_addrs
assert_equal(a["port"], 8333)
assert_equal(a["network"], "ipv4")

node_addresses = self.nodes[0].getnodeaddresses(1)
assert_equal(len(node_addresses), 1)
# Test the IPv6 address.
res = self.nodes[0].getnodeaddresses(0, "ipv6")
assert_equal(len(res), 1)
assert_equal(res[0]["address"], ipv6_addr)
assert_equal(res[0]["network"], "ipv6")
assert_equal(res[0]["port"], 8333)
assert_equal(res[0]["services"], services)

assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1)
# Test for the absence of onion and I2P addresses.
for network in ["onion", "i2p"]:
assert_equal(self.nodes[0].getnodeaddresses(0, network), [])

# addrman's size cannot be known reliably after insertion, as hash collisions may occur
# so only test that requesting a large number of addresses returns less than that
LARGE_REQUEST_COUNT = 10000
node_addresses = self.nodes[0].getnodeaddresses(LARGE_REQUEST_COUNT)
assert_greater_than(LARGE_REQUEST_COUNT, len(node_addresses))
# Test invalid arguments.
assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1)
assert_raises_rpc_error(-8, "Network not recognized: Foo", self.nodes[0].getnodeaddresses, 1, "Foo")


if __name__ == '__main__':
Expand Down

0 comments on commit ff3497c

Please sign in to comment.