Skip to content

Commit

Permalink
Merge #2503: [RPC][Net] Add getnodeaddresses RPC command
Browse files Browse the repository at this point in the history
e9d28da [Doc] Document getnodeaddresses in release notes (Fuzzbawls)
ebe2a46 test: improve getnodeaddresses coverage, test by network (Fuzzbawls)
91ac5c7 rpc: enable filtering getnodeaddresses by network (Fuzzbawls)
badfc49 p2p: allow CConnman::GetAddresses() by network, add doxygen (Fuzzbawls)
fa78233 p2p: allow CAddrMan::GetAddr() by network, add doxygen (Fuzzbawls)
a4d2733 p2p: pull time call out of loop in CAddrMan::GetAddr_() (Fuzzbawls)
d5c1250 p2p: enable CAddrMan::GetAddr_() by network, add doxygen (Fuzzbawls)
67e2c2f [RPC] add network field to getnodeaddresses (Fuzzbawls)
b4832b2 [Net] Add addpeeraddress RPC method (Fuzzbawls)
1a31b67 [test] Test that getnodeaddresses() can return all known addresses (Fuzzbawls)
e83fd0e [Addrman] Specify max addresses and pct when calling GetAddresses() (Fuzzbawls)
792655d [RPC] Add getnodeaddresses RPC command (chris-belcher)

Pull request description:

  Implement a new RPC command (`getnodeaddresses`) that provides RPC access to the node's addrman. It may be useful for PIVX wallets to broadcast their transactions over tor for improved privacy without using the centralized DNS seeds.

  I've included upstream improvements to this feature here as well, and this RPC command was used to scrape the Tor v3 hardcoded seed nodes added in #2502

  ---------
  Based on the following upstream PRs:
  * bitcoin#13152
  * bitcoin#19658
  * bitcoin#21594
  * bitcoin#21843

ACKs for top commit:
  furszy:
    all good, tested ACK e9d28da.
  random-zebra:
    utACK e9d28da and merging...

Tree-SHA512: 2e50108504ac8e172c2e31eb64813d6aae6b6819cf197eace2e4e15686906708b2f82262909faad00ce64af0f61945089a36b857064d3ce2398b3acb14e4ad7d
  • Loading branch information
random-zebra committed Aug 12, 2021
2 parents 51055cb + e9d28da commit 69979ce
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 40 deletions.
24 changes: 24 additions & 0 deletions doc/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,30 @@ Low-level RPC changes
now the empty string `""` instead of `"wallet.dat"`. If PIVX is started
with any `-wallet=<path>` options, there is no change in behavior, and the
name of any wallet is just its `<path>` string.

### New RPC Commands

* `getnodeaddresses`
```
getnodeaddresses ( count "network" )
Return known addresses which can potentially be used to find new nodes in the network
Arguments:
1. count (numeric, optional) The maximum number of addresses to return. Specify 0 to return all known addresses.
2. "network" (string, optional) Return only addresses of the specified network. Can be one of: ipv4, ipv6, onion.
Result:
[
{
"time": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) when the node was last seen
"services": n, (numeric) The services offered by the node
"address": "host", (string) The address of the node
"port": n, (numeric) The port number of the node
"network": "xxxx" (string) The network (ipv4, ipv6, onion) the node connected through
}
,...
]
```

Database cache memory increased
--------------------------------
Expand Down
27 changes: 20 additions & 7 deletions src/addrman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
#include "addrman.h"

#include "hash.h"
#include "streams.h"
#include "logging.h"
#include "netaddress.h"
#include "optional.h"
#include "streams.h"
#include "serialize.h"


Expand Down Expand Up @@ -481,13 +483,18 @@ int CAddrMan::Check_()
}
#endif

void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr)
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, Optional<Network> network)
{
unsigned int nNodes = ADDRMAN_GETADDR_MAX_PCT * vRandom.size() / 100;
if (nNodes > ADDRMAN_GETADDR_MAX)
nNodes = ADDRMAN_GETADDR_MAX;
size_t nNodes = vRandom.size();
if (max_pct != 0) {
nNodes = max_pct * nNodes / 100;
}
if (max_addresses != 0) {
nNodes = std::min(nNodes, max_addresses);
}

// gather a list of random nodes, skipping those of low quality
const int64_t now{GetAdjustedTime()};
for (unsigned int n = 0; n < vRandom.size(); n++) {
if (vAddr.size() >= nNodes)
break;
Expand All @@ -497,8 +504,14 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr)
assert(mapInfo.count(vRandom[n]) == 1);

const CAddrInfo& ai = mapInfo[vRandom[n]];
if (!ai.IsTerrible())
vAddr.push_back(ai);

// Filter by network (optional)
if (network != nullopt && ai.GetNetClass() != network) continue;

// Filter for quality
if (ai.IsTerrible(now)) continue;

vAddr.push_back(ai);
}
}

Expand Down
30 changes: 19 additions & 11 deletions src/addrman.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "clientversion.h"
#include "netaddress.h"
#include "optional.h"
#include "protocol.h"
#include "random.h"
#include "sync.h"
Expand Down Expand Up @@ -159,12 +160,6 @@ class CAddrInfo : public CAddress
//! how recent a successful connection should be before we allow an address to be evicted from tried
#define ADDRMAN_REPLACEMENT_HOURS 4

//! the maximum percentage of nodes to return in a getaddr call
#define ADDRMAN_GETADDR_MAX_PCT 23

//! the maximum number of nodes to return in a getaddr call
#define ADDRMAN_GETADDR_MAX 2500

//! Convenience
#define ADDRMAN_TRIED_BUCKET_COUNT (1 << ADDRMAN_TRIED_BUCKET_COUNT_LOG2)
#define ADDRMAN_NEW_BUCKET_COUNT (1 << ADDRMAN_NEW_BUCKET_COUNT_LOG2)
Expand Down Expand Up @@ -288,8 +283,15 @@ friend class CAddrManTest;
int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
#endif

//! Select several addresses at once.
void GetAddr_(std::vector<CAddress>& vAddr) EXCLUSIVE_LOCKS_REQUIRED(cs);
/**
* Return all or many randomly selected addresses, optionally by network.
*
* @param[out] vAddr Vector of randomly selected addresses from vRandom.
* @param[in] max_addresses Maximum number of addresses to return (0 = all).
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
* @param[in] network Select only addresses of this network (nullopt = all).
*/
void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, Optional<Network> network) EXCLUSIVE_LOCKS_REQUIRED(cs);

//! Mark an entry as currently-connected-to.
void Connected_(const CService& addr, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
Expand Down Expand Up @@ -702,14 +704,20 @@ friend class CAddrManTest;
return addrRet;
}

//! Return a bunch of addresses, selected at random.
std::vector<CAddress> GetAddr()
/**
* Return all or many randomly selected addresses, optionally by network.
*
* @param[in] max_addresses Maximum number of addresses to return (0 = all).
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
* @param[in] network Select only addresses of this network (nullopt = all).
*/
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, Optional<Network> network)
{
Check();
std::vector<CAddress> vAddr;
{
LOCK(cs);
GetAddr_(vAddr);
GetAddr_(vAddr, max_addresses, max_pct, network);
}
Check();
return vAddr;
Expand Down
10 changes: 6 additions & 4 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
#include "crypto/common.h"
#include "crypto/sha256.h"
#include "guiinterface.h"
#include "netaddress.h"
#include "netbase.h"
#include "netmessagemaker.h"
#include "optional.h"
#include "primitives/transaction.h"
#include "scheduler.h"
#include "validation.h"
Expand Down Expand Up @@ -2137,14 +2139,14 @@ void CConnman::AddNewAddress(const CAddress& addr, const CAddress& addrFrom, int
addrman.Add(addr, addrFrom, nTimePenalty);
}

void CConnman::AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty)
bool CConnman::AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty)
{
addrman.Add(vAddr, addrFrom, nTimePenalty);
return addrman.Add(vAddr, addrFrom, nTimePenalty);
}

std::vector<CAddress> CConnman::GetAddresses()
std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct, Optional<Network> network)
{
return addrman.GetAddr();
return addrman.GetAddr(max_addresses, max_pct, network);
}

bool CConnman::AddNode(const std::string& strNode)
Expand Down
15 changes: 11 additions & 4 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ static const int FEELER_INTERVAL = 120;
static const unsigned int MAX_INV_SZ = 50000;
/** The maximum number of entries in a locator */
static const unsigned int MAX_LOCATOR_SZ = 101;
/** The maximum number of new addresses to accumulate before announcing. */
static const unsigned int MAX_ADDR_TO_SEND = 1000;
/** The maximum number of addresses from our addrman to return in response to a getaddr message. */
static constexpr size_t MAX_ADDR_TO_SEND = 1000;
/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024;
/** Maximum length of strSubVer in `version` message */
Expand Down Expand Up @@ -216,8 +216,15 @@ class CConnman
void SetServices(const CService &addr, ServiceFlags nServices);
void MarkAddressGood(const CAddress& addr);
void AddNewAddress(const CAddress& addr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
std::vector<CAddress> GetAddresses();
bool AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
/**
* Return all or many randomly selected addresses, optionally by network.
*
* @param[in] max_addresses Maximum number of addresses to return (0 = all).
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
* @param[in] network Select only addresses of this network (nullopt = all).
*/
std::vector<CAddress> GetAddresses(size_t max_addresses, size_t max_pct, Optional<Network> network);

// Denial-of-service detection/prevention
// The idea is to detect peers that are behaving
Expand Down
5 changes: 4 additions & 1 deletion src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ int64_t nTimeBestReceived = 0; // Used only to inform the wallet of when we las

static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]

/** the maximum percentage of addresses from our addrman to return in response to a getaddr message. */
static constexpr size_t MAX_PCT_ADDR_TO_SEND = 23;

struct IteratorComparator
{
template<typename I>
Expand Down Expand Up @@ -1746,7 +1749,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
// getaddr message mitigates the attack.
else if ((strCommand == NetMsgType::GETADDR) && (pfrom->fInbound)) {
pfrom->vAddrToSend.clear();
std::vector<CAddress> vAddr = connman->GetAddresses();
std::vector<CAddress> vAddr = connman->GetAddresses(MAX_ADDR_TO_SEND, MAX_PCT_ADDR_TO_SEND, /* network */ nullopt);
FastRandomContext insecure_rand;
for (const CAddress& addr : vAddr)
pfrom->PushAddress(addr, insecure_rand);
Expand Down
2 changes: 1 addition & 1 deletion src/netaddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ uint32_t CNetAddr::GetLinkedIPv4() const
assert(false);
}

uint32_t CNetAddr::GetNetClass() const
Network CNetAddr::GetNetClass() const
{
// Make sure that if we return NET_IPV6, then IsIPv6() is true. The callers expect that.

Expand Down
2 changes: 1 addition & 1 deletion src/netaddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ class CNetAddr
std::string ToStringIP() const;
uint64_t GetHash() const;
bool GetInAddr(struct in_addr* pipv4Addr) const;
uint32_t GetNetClass() const;
Network GetNetClass() const;

//! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
uint32_t GetLinkedIPv4() const;
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class CRPCConvertParam
static const CRPCConvertParam vRPCConvertParams[] = {
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
{ "addpeeraddress", 1, "port" },
{ "autocombinerewards", 0, "enable" },
{ "autocombinerewards", 1, "threshold" },
{ "createmultisig", 0, "nrequired" },
Expand Down Expand Up @@ -62,6 +63,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
110 changes: 110 additions & 0 deletions src/rpc/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "net.h"
#include "netbase.h"
#include "net_processing.h"
#include "optional.h"
#include "protocol.h"
#include "sync.h"
#include "timedata.h"
Expand Down Expand Up @@ -559,6 +560,111 @@ UniValue clearbanned(const JSONRPCRequest& request)
return NullUniValue;
}

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

"\nArguments:\n"
"1. count (numeric, optional) The maximum number of addresses to return. Specify 0 to return all known addresses.\n"
"2. \"network\" (string, optional) Return only addresses of the specified network. Can be one of: ipv4, ipv6, onion."

"\nResult:\n"
"[\n"
" {\n"
" \"time\": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) when the node was last seen\n"
" \"services\": n, (numeric) The services offered by the node\n"
" \"address\": \"host\", (string) The address of the node\n"
" \"port\": n, (numeric) The port number of the node\n"
" \"network\": \"xxxx\" (string) The network (ipv4, ipv6, onion) the node connected through\n"
" }\n"
" ,...\n"
"]\n"

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

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 Optional<Network> network{request.params[1].isNull() ? nullopt : 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{g_connman->GetAddresses(count, /* max_pct */ 0, network)};
UniValue ret(UniValue::VARR);

for (const CAddress& addr : vAddr) {
UniValue obj(UniValue::VOBJ);
obj.pushKV("time", (int)addr.nTime);
obj.pushKV("services", (uint64_t)addr.nServices);
obj.pushKV("address", addr.ToStringIP());
obj.pushKV("port", addr.GetPort());
obj.pushKV("network", GetNetworkName(addr.GetNetClass()));
ret.push_back(obj);
}
return ret;
}

static UniValue addpeeraddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"addpeeraddress \"address\" port\n"
"\nAdd the address of a potential peer to the address manager. This RPC is for testing only.\n"

"\nArguments\n"
"1. \"address\" (string, required) The IP address of the peer\n"
"2. port (numeric, required) The port of the peer\n"

"\nResult:\n"
"{\n"
" \"success\": true|false (boolean) Whether the peer address was successfully added to the address manager\n"
"}\n"

"\nExamples:\n"
+ HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 51472")
+ HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 51472"));
}
if (!g_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 (!g_connman->AddNewAddresses({address}, address)) {
obj.pushKV("success", false);
return obj;
}

obj.pushKV("success", true);
return obj;
}

static const CRPCCommand commands[] =
{ // category name actor (function) okSafe argNames
// --------------------- ------------------------ ----------------------- ------ --------
Expand All @@ -569,10 +675,14 @@ 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, {} },
{ "network", "setban", &setban, true, {"subnet", "command", "bantime", "absolute"} },

// Hidden, for testing only
{ "hidden", "addpeeraddress", &addpeeraddress, true, {"address", "port"} },
};

void RegisterNetRPCCommands(CRPCTable &tableRPC)
Expand Down
Loading

0 comments on commit 69979ce

Please sign in to comment.