Merge #13152: [rpc] Add getnodeaddresses RPC command

a2eb6f5405 [rpc] Add getnodeaddresses RPC command (chris-belcher)

Pull request description:

  Implements issue https://github.com/bitcoin/bitcoin/issues/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
This commit is contained in:
MarcoFalke 2018-09-18 19:49:24 -04:00
commit d26278988f
No known key found for this signature in database
GPG key ID: D2EA4850E7528B25
4 changed files with 97 additions and 0 deletions

View file

@ -0,0 +1,4 @@
New RPC methods
------------
- `getnodeaddresses` returns peer addresses known to this node. It may be used to connect to nodes over TCP without using the DNS seeds.

View file

@ -163,6 +163,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "rescanblockchain", 0, "start_height"},
{ "rescanblockchain", 1, "stop_height"},
{ "createwallet", 1, "disable_private_keys"},
{ "getnodeaddresses", 0, "count"},
};
// clang-format on

View file

@ -626,6 +626,58 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
return g_connman->GetNetworkActive();
}
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;
}
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@ -642,6 +694,7 @@ static const CRPCCommand commands[] =
{ "network", "listbanned", &listbanned, {} },
{ "network", "clearbanned", &clearbanned, {} },
{ "network", "setnetworkactive", &setnetworkactive, {"state"} },
{ "network", "getnodeaddresses", &getnodeaddresses, {"count"} },
};
// clang-format on

View file

@ -13,11 +13,14 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
assert_greater_than,
assert_raises_rpc_error,
connect_nodes_bi,
p2p_port,
wait_until,
)
from test_framework.mininode import P2PInterface
from test_framework.messages import CAddress, msg_addr, NODE_NETWORK, NODE_WITNESS
class NetTest(BitcoinTestFramework):
def set_test_params(self):
@ -31,6 +34,7 @@ class NetTest(BitcoinTestFramework):
self._test_getnetworkinginfo()
self._test_getaddednodeinfo()
self._test_getpeerinfo()
self._test_getnodeaddresses()
def _test_connection_count(self):
# connect_nodes_bi connects each node to the other
@ -101,5 +105,40 @@ class NetTest(BitcoinTestFramework):
assert_equal(peer_info[0][0]['minfeefilter'], Decimal("0.00000500"))
assert_equal(peer_info[1][0]['minfeefilter'], Decimal("0.00001000"))
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 | NODE_WITNESS
addr.ip = a
addr.port = 8333
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 | NODE_WITNESS)
assert a["address"] in imported_addrs
assert_equal(a["port"], 8333)
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()