Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RPC][Net] Add getnodeaddresses RPC command #2503

Merged
merged 12 commits into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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