Skip to content

Commit

Permalink
[RPC] Add getnodeaddresses RPC command
Browse files Browse the repository at this point in the history
New getnodeaddresses call gives access via RPC to the peers known by
the node. It may be useful for PIVX wallets to broadcast their
transactions over tor for improved privacy without using the
centralized DNS seeds. getnodeaddresses is very similar to the getaddr
p2p method.

Tests the new rpc call by feeding IP address to a test node via the p2p
protocol, then obtaining someone of those addresses with
getnodeaddresses and checking that they are a subset.
  • Loading branch information
chris-belcher authored and Fuzzbawls committed Aug 11, 2021
1 parent 34ef597 commit 792655d
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/rpc/client.cpp
Expand Up @@ -62,6 +62,7 @@ static const CRPCConvertParam vRPCConvertParams[] = {
{ "getshieldbalance", 2, "include_watchonly" },
{ "getnetworkhashps", 0, "nblocks" },
{ "getnetworkhashps", 1, "height" },
{ "getnodeaddresses", 0, "count" },
{ "getrawmempool", 0, "verbose" },
{ "getrawtransaction", 1, "verbose" },
{ "getreceivedbyaddress", 1, "minconf" },
Expand Down
56 changes: 56 additions & 0 deletions src/rpc/net.cpp
Expand Up @@ -559,6 +559,61 @@ UniValue clearbanned(const JSONRPCRequest& request)
return NullUniValue;
}

static UniValue getnodeaddresses(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getnodeaddresses ( count )\n"
"\nReturn known addresses which can potentially be used to find new nodes in the network\n"

"\nArguments:\n"
"1. \"count\" (numeric, optional) How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) +
" or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses. (default = 1)\n"

"\nResult:\n"
"[\n"
" {\n"
" \"time\": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen\n"
" \"services\": n, (numeric) The services offered\n"
" \"address\": \"host\", (string) The address of the node\n"
" \"port\": n (numeric) The port of the node\n"
" }\n"
" ,...\n"
"]\n"

"\nExamples:\n"
+ HelpExampleCli("getnodeaddresses", "8")
+ HelpExampleRpc("getnodeaddresses", "8")
);
}
if (!g_connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}

int count = 1;
if (!request.params[0].isNull()) {
count = request.params[0].get_int();
if (count <= 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
}
}
// returns a shuffled list of CAddress
std::vector<CAddress> vAddr = g_connman->GetAddresses();
UniValue ret(UniValue::VARR);

int address_return_count = std::min<int>(count, vAddr.size());
for (int i = 0; i < address_return_count; ++i) {
UniValue obj(UniValue::VOBJ);
const CAddress& addr = vAddr[i];
obj.pushKV("time", (int)addr.nTime);
obj.pushKV("services", (uint64_t)addr.nServices);
obj.pushKV("address", addr.ToStringIP());
obj.pushKV("port", addr.GetPort());
ret.push_back(obj);
}
return ret;
}

static const CRPCCommand commands[] =
{ // category name actor (function) okSafe argNames
// --------------------- ------------------------ ----------------------- ------ --------
Expand All @@ -569,6 +624,7 @@ static const CRPCCommand commands[] =
{ "network", "getconnectioncount", &getconnectioncount, true, {} },
{ "network", "getnettotals", &getnettotals, true, {} },
{ "network", "getnetworkinfo", &getnetworkinfo, true, {} },
{ "network", "getnodeaddresses", &getnodeaddresses, true, {"count"} },
{ "network", "getpeerinfo", &getpeerinfo, true, {} },
{ "network", "listbanned", &listbanned, true, {} },
{ "network", "ping", &ping, true, {} },
Expand Down
41 changes: 40 additions & 1 deletion test/functional/rpc_net.py
Expand Up @@ -7,10 +7,13 @@
Tests correspond to code in rpc/net.cpp.
"""

from test_framework.messages import CAddress, msg_addr, NODE_NETWORK
from test_framework.mininode import P2PInterface
from test_framework.test_framework import PivxTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
assert_greater_than,
assert_raises_rpc_error,
connect_nodes,
disconnect_nodes,
Expand All @@ -32,7 +35,8 @@ def run_test(self):
self._test_getnettotals()
self._test_getnetworkinginfo()
self._test_getaddednodeinfo()
#self._test_getpeerinfo()
# self._test_getpeerinfo()
self._test_getnodeaddresses()

def _test_connection_count(self):
assert_equal(self.nodes[0].getconnectioncount(), 2)
Expand Down Expand Up @@ -93,5 +97,40 @@ def _test_getpeerinfo(self):
assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr'])
assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr'])

def _test_getnodeaddresses(self):
self.nodes[0].add_p2p_connection(P2PInterface())

# send some addresses to the node via the p2p message addr
msg = msg_addr()
imported_addrs = []
for i in range(256):
a = "123.123.123.{}".format(i)
imported_addrs.append(a)
addr = CAddress()
addr.time = 100000000
addr.nServices = NODE_NETWORK
addr.ip = a
addr.port = 51472
msg.addrs.append(addr)
self.nodes[0].p2p.send_and_ping(msg)

# obtain addresses via rpc call and check they were ones sent in before
REQUEST_COUNT = 10
node_addresses = self.nodes[0].getnodeaddresses(REQUEST_COUNT)
assert_equal(len(node_addresses), REQUEST_COUNT)
for a in node_addresses:
assert_greater_than(a["time"], 1527811200) # 1st June 2018
assert_equal(a["services"], NODE_NETWORK)
assert a["address"] in imported_addrs
assert_equal(a["port"], 51472)

assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1)

# 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))

if __name__ == '__main__':
NetTest().main()

0 comments on commit 792655d

Please sign in to comment.