diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -338,7 +338,7 @@ // Addrman functions void SetServices(const CService &addr, ServiceFlags nServices); void MarkAddressGood(const CAddress &addr); - void AddNewAddresses(const std::vector &vAddr, + bool AddNewAddresses(const std::vector &vAddr, const CAddress &addrFrom, int64_t nTimePenalty = 0); std::vector GetAddresses(size_t max_addresses, size_t max_pct); /** diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -2859,9 +2859,9 @@ addrman.Good(addr); } -void CConnman::AddNewAddresses(const std::vector &vAddr, +bool CConnman::AddNewAddresses(const std::vector &vAddr, const CAddress &addrFrom, int64_t nTimePenalty) { - addrman.Add(vAddr, addrFrom, nTimePenalty); + return addrman.Add(vAddr, addrFrom, nTimePenalty); } std::vector CConnman::GetAddresses(size_t max_addresses, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -158,6 +158,7 @@ {"createwallet", 4, "avoid_reuse"}, {"createwallet", 5, "descriptors"}, {"getnodeaddresses", 0, "count"}, + {"addpeeraddress", 1, "port"}, {"stop", 0, "wait"}, // Avalanche {"addavalanchenode", 0, "nodeid"}, diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -981,6 +981,63 @@ return ret; } +static UniValue addpeeraddress(const Config &config, + const JSONRPCRequest &request) { + RPCHelpMan{ + "addpeeraddress", + "\nAdd the address of a potential peer to the address manager. This " + "RPC is for testing only.\n", + { + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, + "The IP address of the peer"}, + {"port", RPCArg::Type::NUM, RPCArg::Optional::NO, + "The port of the peer"}, + }, + RPCResult{ + RPCResult::Type::OBJ, + "", + "", + { + {RPCResult::Type::BOOL, "success", + "whether the peer address was successfully added to the " + "address manager"}, + }, + }, + RPCExamples{HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 8333") + + HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 8333")}, + } + .Check(request); + + NodeContext &node = EnsureNodeContext(request.context); + if (!node.connman) { + throw JSONRPCError( + RPC_CLIENT_P2P_DISABLED, + "Error: Peer-to-peer functionality missing or disabled"); + } + + UniValue obj(UniValue::VOBJ); + + std::string addr_string = request.params[0].get_str(); + uint16_t port = request.params[1].get_int(); + + CNetAddr net_addr; + if (!LookupHost(addr_string, net_addr, false)) { + obj.pushKV("success", false); + return obj; + } + CAddress address = CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK)); + address.nTime = GetAdjustedTime(); + // The source address is set equal to the address. This is equivalent to the + // peer announcing itself. + if (!node.connman->AddNewAddresses({address}, address)) { + obj.pushKV("success", false); + return obj; + } + + obj.pushKV("success", true); + return obj; +} + void RegisterNetRPCCommands(CRPCTable &t) { // clang-format off static const CRPCCommand commands[] = { @@ -999,6 +1056,7 @@ { "network", "clearbanned", clearbanned, {} }, { "network", "setnetworkactive", setnetworkactive, {"state"} }, { "network", "getnodeaddresses", getnodeaddresses, {"count"} }, + { "hidden", "addpeeraddress", addpeeraddress, {"address", "port"} }, }; // clang-format on diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -15,11 +15,7 @@ from test_framework.key import ECKey from test_framework.p2p import P2PInterface import test_framework.messages -from test_framework.messages import ( - CAddress, - msg_addr, - NODE_NETWORK, -) +from test_framework.messages import NODE_NETWORK from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_approx, @@ -212,29 +208,25 @@ self.log.info("Test getnodeaddresses") self.nodes[0].add_p2p_connection(P2PInterface()) - # send some addresses to the node via the p2p message addr - msg = msg_addr() + # 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. imported_addrs = [] - for i in range(256): - a = "123.123.123.{}".format(i) + for i in range(10000): + first_octet = i >> 8 + second_octet = i % 256 + a = "{}.{}.1.1".format(first_octet, second_octet) imported_addrs.append(a) - addr = CAddress() - addr.time = 100000000 - addr.nServices = NODE_NETWORK - addr.ip = a - addr.port = 8333 - msg.addrs.append(addr) - self.nodes[0].p2p.send_and_ping(msg) + self.nodes[0].addpeeraddress(a, 8333) # Obtain addresses via rpc call and check they were ones sent in before. # - # All addresses added above are in the same netgroup and so are assigned - # to the same bucket. Maximum possible addresses in addrman is therefore - # 64, although actual number will usually be slightly less due to - # BucketPosition collisions. + # 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) - assert_greater_than(len(node_addresses), 50) - assert_greater_than(65, len(node_addresses)) + assert_greater_than(len(node_addresses), 5000) + assert_greater_than(10000, len(node_addresses)) for a in node_addresses: assert_greater_than(a["time"], 1527811200) # 1st June 2018 assert_equal(a["services"], NODE_NETWORK)