Skip to content

Commit da384a2

Browse files
committed
rpc: getrawaddrman for addrman entries
Exposing address manager table entries in a hidden RPC allows to introspect addrman tables in tests and during development. As response JSON object the following FORMAT1 is choosen: { "table": { "<bucket>/<position>": { "address": "..", "port": .., ... }, "<bucket>/<position>": { "address": "..", "port": .., ... }, "<bucket>/<position>": { "address": "..", "port": .., ... }, ... } } An alternative would be FORMAT2 { "table": { "bucket": { "position": { "address": "..", "port": .., ... }, "position": { "address": "..", "port": .., ... }, .. }, "bucket": { "position": { "address": "..", "port": .., ... }, .. }, } } FORMAT1 and FORMAT2 have different encodings for the location of the address in the address manager. While FORMAT2 might be easier to process for downstream tools, it also mimics internal addrman mappings, which might change at some point. Users not interested in the address location can ignore the location key. They don't have to adapt to a new RPC response format, when the internal addrman layout changes. Additionally, FORMAT1 is also slightly easier to to iterate in downstream tools. The RPC response-building implemenation complexcity is lower with FORMAT1 as we can more easily build a "<bucket>/<position>" key than a multiple "bucket" objects with multiple "position" objects (FORMAT2).
1 parent 8909667 commit da384a2

File tree

5 files changed

+127
-1
lines changed

5 files changed

+127
-1
lines changed

src/addrman.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,30 @@ std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct
838838
return addresses;
839839
}
840840

841+
std::vector<std::pair<AddrInfo, AddressPosition>> AddrManImpl::GetEntries_(bool from_tried) const
842+
{
843+
AssertLockHeld(cs);
844+
845+
const int bucket_count = from_tried ? ADDRMAN_TRIED_BUCKET_COUNT : ADDRMAN_NEW_BUCKET_COUNT;
846+
std::vector<std::pair<AddrInfo, AddressPosition>> infos;
847+
for (int bucket = 0; bucket < bucket_count; ++bucket) {
848+
for (int position = 0; position < ADDRMAN_BUCKET_SIZE; ++position) {
849+
int id = GetEntry(from_tried, bucket, position);
850+
if (id >= 0) {
851+
AddrInfo info = mapInfo.at(id);
852+
AddressPosition location = AddressPosition(
853+
from_tried,
854+
/*multiplicity_in=*/from_tried ? 1 : info.nRefCount,
855+
bucket,
856+
position);
857+
infos.push_back(std::make_pair(info, location));
858+
}
859+
}
860+
}
861+
862+
return infos;
863+
}
864+
841865
void AddrManImpl::Connected_(const CService& addr, NodeSeconds time)
842866
{
843867
AssertLockHeld(cs);
@@ -1199,6 +1223,15 @@ std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct,
11991223
return addresses;
12001224
}
12011225

1226+
std::vector<std::pair<AddrInfo, AddressPosition>> AddrManImpl::GetEntries(bool from_tried) const
1227+
{
1228+
LOCK(cs);
1229+
Check();
1230+
auto addrInfos = GetEntries_(from_tried);
1231+
Check();
1232+
return addrInfos;
1233+
}
1234+
12021235
void AddrManImpl::Connected(const CService& addr, NodeSeconds time)
12031236
{
12041237
LOCK(cs);
@@ -1289,6 +1322,11 @@ std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std
12891322
return m_impl->GetAddr(max_addresses, max_pct, network);
12901323
}
12911324

1325+
std::vector<std::pair<AddrInfo, AddressPosition>> AddrMan::GetEntries(bool use_tried) const
1326+
{
1327+
return m_impl->GetEntries(use_tried);
1328+
}
1329+
12921330
void AddrMan::Connected(const CService& addr, NodeSeconds time)
12931331
{
12941332
m_impl->Connected(addr, time);

src/addrman.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ class InvalidAddrManVersionError : public std::ios_base::failure
2525
};
2626

2727
class AddrManImpl;
28+
class AddrInfo;
2829

2930
/** Default for -checkaddrman */
3031
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS{0};
3132

32-
/** Test-only struct, capturing info about an address in AddrMan */
33+
/** Location information for an address in AddrMan */
3334
struct AddressPosition {
3435
// Whether the address is in the new or tried table
3536
const bool tried;
@@ -168,6 +169,17 @@ class AddrMan
168169
*/
169170
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const;
170171

172+
/**
173+
* Returns an information-location pair for all addresses in the selected addrman table.
174+
* If an address appears multiple times in the new table, an information-location pair
175+
* is returned for each occurence. Addresses only ever appear once in the tried table.
176+
*
177+
* @param[in] from_tried Selects which table to return entries from.
178+
*
179+
* @return A vector consisting of pairs of AddrInfo and AddressPosition.
180+
*/
181+
std::vector<std::pair<AddrInfo, AddressPosition>> GetEntries(bool from_tried) const;
182+
171183
/** We have successfully connected to this peer. Calling this function
172184
* updates the CAddress's nTime, which is used in our IsTerrible()
173185
* decisions and gossiped to peers. Callers should be careful that updating

src/addrman_impl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ class AddrManImpl
132132
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
133133
EXCLUSIVE_LOCKS_REQUIRED(!cs);
134134

135+
std::vector<std::pair<AddrInfo, AddressPosition>> GetEntries(bool from_tried) const
136+
EXCLUSIVE_LOCKS_REQUIRED(!cs);
137+
135138
void Connected(const CService& addr, NodeSeconds time)
136139
EXCLUSIVE_LOCKS_REQUIRED(!cs);
137140

@@ -260,6 +263,8 @@ class AddrManImpl
260263

261264
std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
262265

266+
std::vector<std::pair<AddrInfo, AddressPosition>> GetEntries_(bool from_tried) const EXCLUSIVE_LOCKS_REQUIRED(cs);
267+
263268
void Connected_(const CService& addr, NodeSeconds time) EXCLUSIVE_LOCKS_REQUIRED(cs);
264269

265270
void SetServices_(const CService& addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);

src/rpc/net.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <rpc/server.h>
66

77
#include <addrman.h>
8+
#include <addrman_impl.h>
89
#include <banman.h>
910
#include <chainparams.h>
1011
#include <clientversion.h>
@@ -1063,6 +1064,74 @@ static RPCHelpMan getaddrmaninfo()
10631064
};
10641065
}
10651066

1067+
UniValue AddrmanEntryToJSON(const AddrInfo& info)
1068+
{
1069+
UniValue ret(UniValue::VOBJ);
1070+
ret.pushKV("address", info.ToStringAddr());
1071+
ret.pushKV("port", info.GetPort());
1072+
ret.pushKV("services", (uint64_t)info.nServices);
1073+
ret.pushKV("time", int64_t{TicksSinceEpoch<std::chrono::seconds>(info.nTime)});
1074+
ret.pushKV("network", GetNetworkName(info.GetNetClass()));
1075+
ret.pushKV("source", info.source.ToStringAddr());
1076+
ret.pushKV("source_network", GetNetworkName(info.source.GetNetClass()));
1077+
return ret;
1078+
}
1079+
1080+
UniValue AddrmanTableToJSON(const std::vector<std::pair<AddrInfo, AddressPosition>>& tableInfos)
1081+
{
1082+
UniValue table(UniValue::VOBJ);
1083+
for (const auto& e : tableInfos) {
1084+
AddrInfo info = e.first;
1085+
AddressPosition location = e.second;
1086+
std::ostringstream key;
1087+
key << location.bucket << "/" << location.position;
1088+
// Address manager tables have unique entries so there is no advantage
1089+
// in using UniValue::pushKV, which checks if the key already exists
1090+
// in O(N). UniValue::pushKVEnd is used instead which currently is O(1).
1091+
table.pushKVEnd(key.str(), AddrmanEntryToJSON(info));
1092+
}
1093+
return table;
1094+
}
1095+
1096+
static RPCHelpMan getrawaddrman()
1097+
{
1098+
return RPCHelpMan{"getrawaddrman",
1099+
"EXPERIMENTAL warning: this call may be changed in future releases.\n"
1100+
"\nReturns information on all address manager entries for the new and tried tables.\n",
1101+
{},
1102+
RPCResult{
1103+
RPCResult::Type::OBJ_DYN, "", "", {
1104+
{RPCResult::Type::OBJ_DYN, "table", "buckets with addresses in the address manager table ( new, tried )", {
1105+
{RPCResult::Type::OBJ, "bucket/position", "the location in the address manager table (<bucket>/<position>)", {
1106+
{RPCResult::Type::STR, "address", "The address of the node"},
1107+
{RPCResult::Type::NUM, "port", "The port number of the node"},
1108+
{RPCResult::Type::STR, "network", "The network (" + Join(GetNetworkNames(), ", ") + ") of the address"},
1109+
{RPCResult::Type::NUM, "services", "The services offered by the node"},
1110+
{RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " when the node was last seen"},
1111+
{RPCResult::Type::STR, "source", "The address that relayed the address to us"},
1112+
{RPCResult::Type::STR, "source_network", "The network (" + Join(GetNetworkNames(), ", ") + ") of the source address"},
1113+
}}
1114+
}}
1115+
}
1116+
},
1117+
RPCExamples{
1118+
HelpExampleCli("getrawaddrman", "")
1119+
+ HelpExampleRpc("getrawaddrman", "")
1120+
},
1121+
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
1122+
NodeContext& node = EnsureAnyNodeContext(request.context);
1123+
if (!node.addrman) {
1124+
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Address manager functionality missing or disabled");
1125+
}
1126+
1127+
UniValue ret(UniValue::VOBJ);
1128+
ret.pushKV("new", AddrmanTableToJSON(node.addrman->GetEntries(false)));
1129+
ret.pushKV("tried", AddrmanTableToJSON(node.addrman->GetEntries(true)));
1130+
return ret;
1131+
},
1132+
};
1133+
}
1134+
10661135
void RegisterNetRPCCommands(CRPCTable& t)
10671136
{
10681137
static const CRPCCommand commands[]{
@@ -1083,6 +1152,7 @@ void RegisterNetRPCCommands(CRPCTable& t)
10831152
{"hidden", &addpeeraddress},
10841153
{"hidden", &sendmsgtopeer},
10851154
{"hidden", &getaddrmaninfo},
1155+
{"hidden", &getrawaddrman},
10861156
};
10871157
for (const auto& c : commands) {
10881158
t.appendCommand(c.name, &c);

src/test/fuzz/rpc.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
140140
"getnodeaddresses",
141141
"getpeerinfo",
142142
"getprioritisedtransactions",
143+
"getrawaddrman",
143144
"getrawmempool",
144145
"getrawtransaction",
145146
"getrpcinfo",

0 commit comments

Comments
 (0)