Skip to content

Commit d262789

Browse files
author
MarcoFalke
committed
Merge #13152: [rpc] Add getnodeaddresses RPC command
a2eb6f5 [rpc] Add getnodeaddresses RPC command (chris-belcher) Pull request description: Implements issue bitcoin/bitcoin#9463 New getnodeaddresses call gives access via RPC to the peers known by the node. It may be useful for bitcoin 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. Please advise me on the best approach for writing an automated test. By my reading the getaddr p2p method also isn't really tested. Tree-SHA512: ad03abf518847476495b76a2f5394b8030aa86654429167fa618e21460abb505c10ef9817ec1b80472320d41d0aff5dc94a8efce023aaaaf5e81386aa92b852b
2 parents 24f095d + a2eb6f5 commit d262789

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed

doc/release-notes-13152.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
New RPC methods
2+
------------
3+
4+
- `getnodeaddresses` returns peer addresses known to this node. It may be used to connect to nodes over TCP without using the DNS seeds.

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
163163
{ "rescanblockchain", 0, "start_height"},
164164
{ "rescanblockchain", 1, "stop_height"},
165165
{ "createwallet", 1, "disable_private_keys"},
166+
{ "getnodeaddresses", 0, "count"},
166167
};
167168
// clang-format on
168169

src/rpc/net.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,58 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
626626
return g_connman->GetNetworkActive();
627627
}
628628

629+
static UniValue getnodeaddresses(const JSONRPCRequest& request)
630+
{
631+
if (request.fHelp || request.params.size() > 1) {
632+
throw std::runtime_error(
633+
"getnodeaddresses ( count )\n"
634+
"\nReturn known addresses which can potentially be used to find new nodes in the network\n"
635+
"\nArguments:\n"
636+
"1. \"count\" (numeric, optional) How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) +
637+
" or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses. (default = 1)\n"
638+
"\nResult:\n"
639+
"[\n"
640+
" {\n"
641+
" \"time\": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen\n"
642+
" \"services\": n, (numeric) The services offered\n"
643+
" \"address\": \"host\", (string) The address of the node\n"
644+
" \"port\": n (numeric) The port of the node\n"
645+
" }\n"
646+
" ,....\n"
647+
"]\n"
648+
"\nExamples:\n"
649+
+ HelpExampleCli("getnodeaddresses", "8")
650+
+ HelpExampleRpc("getnodeaddresses", "8")
651+
);
652+
}
653+
if (!g_connman) {
654+
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
655+
}
656+
657+
int count = 1;
658+
if (!request.params[0].isNull()) {
659+
count = request.params[0].get_int();
660+
if (count <= 0) {
661+
throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
662+
}
663+
}
664+
// returns a shuffled list of CAddress
665+
std::vector<CAddress> vAddr = g_connman->GetAddresses();
666+
UniValue ret(UniValue::VARR);
667+
668+
int address_return_count = std::min<int>(count, vAddr.size());
669+
for (int i = 0; i < address_return_count; ++i) {
670+
UniValue obj(UniValue::VOBJ);
671+
const CAddress& addr = vAddr[i];
672+
obj.pushKV("time", (int)addr.nTime);
673+
obj.pushKV("services", (uint64_t)addr.nServices);
674+
obj.pushKV("address", addr.ToStringIP());
675+
obj.pushKV("port", addr.GetPort());
676+
ret.push_back(obj);
677+
}
678+
return ret;
679+
}
680+
629681
// clang-format off
630682
static const CRPCCommand commands[] =
631683
{ // category name actor (function) argNames
@@ -642,6 +694,7 @@ static const CRPCCommand commands[] =
642694
{ "network", "listbanned", &listbanned, {} },
643695
{ "network", "clearbanned", &clearbanned, {} },
644696
{ "network", "setnetworkactive", &setnetworkactive, {"state"} },
697+
{ "network", "getnodeaddresses", &getnodeaddresses, {"count"} },
645698
};
646699
// clang-format on
647700

test/functional/rpc_net.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313
from test_framework.util import (
1414
assert_equal,
1515
assert_greater_than_or_equal,
16+
assert_greater_than,
1617
assert_raises_rpc_error,
1718
connect_nodes_bi,
1819
p2p_port,
1920
wait_until,
2021
)
22+
from test_framework.mininode import P2PInterface
23+
from test_framework.messages import CAddress, msg_addr, NODE_NETWORK, NODE_WITNESS
2124

2225
class NetTest(BitcoinTestFramework):
2326
def set_test_params(self):
@@ -31,6 +34,7 @@ def run_test(self):
3134
self._test_getnetworkinginfo()
3235
self._test_getaddednodeinfo()
3336
self._test_getpeerinfo()
37+
self._test_getnodeaddresses()
3438

3539
def _test_connection_count(self):
3640
# connect_nodes_bi connects each node to the other
@@ -101,5 +105,40 @@ def _test_getpeerinfo(self):
101105
assert_equal(peer_info[0][0]['minfeefilter'], Decimal("0.00000500"))
102106
assert_equal(peer_info[1][0]['minfeefilter'], Decimal("0.00001000"))
103107

108+
def _test_getnodeaddresses(self):
109+
self.nodes[0].add_p2p_connection(P2PInterface())
110+
111+
# send some addresses to the node via the p2p message addr
112+
msg = msg_addr()
113+
imported_addrs = []
114+
for i in range(256):
115+
a = "123.123.123.{}".format(i)
116+
imported_addrs.append(a)
117+
addr = CAddress()
118+
addr.time = 100000000
119+
addr.nServices = NODE_NETWORK | NODE_WITNESS
120+
addr.ip = a
121+
addr.port = 8333
122+
msg.addrs.append(addr)
123+
self.nodes[0].p2p.send_and_ping(msg)
124+
125+
# obtain addresses via rpc call and check they were ones sent in before
126+
REQUEST_COUNT = 10
127+
node_addresses = self.nodes[0].getnodeaddresses(REQUEST_COUNT)
128+
assert_equal(len(node_addresses), REQUEST_COUNT)
129+
for a in node_addresses:
130+
assert_greater_than(a["time"], 1527811200) # 1st June 2018
131+
assert_equal(a["services"], NODE_NETWORK | NODE_WITNESS)
132+
assert a["address"] in imported_addrs
133+
assert_equal(a["port"], 8333)
134+
135+
assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1)
136+
137+
# addrman's size cannot be known reliably after insertion, as hash collisions may occur
138+
# so only test that requesting a large number of addresses returns less than that
139+
LARGE_REQUEST_COUNT = 10000
140+
node_addresses = self.nodes[0].getnodeaddresses(LARGE_REQUEST_COUNT)
141+
assert_greater_than(LARGE_REQUEST_COUNT, len(node_addresses))
142+
104143
if __name__ == '__main__':
105144
NetTest().main()

0 commit comments

Comments
 (0)