Skip to content

Commit 518b858

Browse files
committed
addrman, net: filter during selection via AddrPolicy to avoid underfill
Add AddrMan::AddrPolicy, a predicate returning true to exclude an address during selection. This allows callers to apply custom filtering directly in AddrMan rather than post-processing results. Use the new policy in CConnman::GetAddressesUnsafe to exclude banned and discouraged peers while filling address requests. This fixes underfilled results observed when banned peers were removed after selection, causing P2P responses from `GETADDR` to return fewer entries than requested. Also log the number of filtered entries for debugging and diagnostics.
1 parent a14e7b9 commit 518b858

File tree

5 files changed

+57
-18
lines changed

5 files changed

+57
-18
lines changed

src/addrman.cpp

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ nid_type AddrManImpl::GetEntry(bool use_tried, size_t bucket, size_t position) c
809809
return -1;
810810
}
811811

812-
std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
812+
std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered, const AddrMan::AddrPolicy& policy) const
813813
{
814814
AssertLockHeld(cs);
815815
Assume(max_pct <= 100);
@@ -827,6 +827,7 @@ std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct
827827
const auto now{Now<NodeSeconds>()};
828828
std::vector<CAddress> addresses;
829829
addresses.reserve(nNodes);
830+
size_t filtered_addresses = 0;
830831
for (unsigned int n = 0; n < vRandom.size(); n++) {
831832
if (addresses.size() >= nNodes)
832833
break;
@@ -839,14 +840,26 @@ std::vector<CAddress> AddrManImpl::GetAddr_(size_t max_addresses, size_t max_pct
839840
const AddrInfo& ai{it->second};
840841

841842
// Filter by network (optional)
842-
if (network != std::nullopt && ai.GetNetClass() != network) continue;
843+
if (network != std::nullopt && ai.GetNetClass() != network) {
844+
++filtered_addresses;
845+
continue;
846+
}
843847

844848
// Filter for quality
845-
if (ai.IsTerrible(now) && filtered) continue;
849+
if (ai.IsTerrible(now) && filtered) {
850+
++filtered_addresses;
851+
continue;
852+
}
853+
854+
// Filter by policy
855+
if (policy && policy(ai)) {
856+
++filtered_addresses;
857+
continue;
858+
}
846859

847860
addresses.push_back(ai);
848861
}
849-
LogDebug(BCLog::ADDRMAN, "GetAddr returned %d random addresses\n", addresses.size());
862+
LogDebug(BCLog::ADDRMAN, "GetAddr returned %zu random addresses, %zu addresses filtered\n", addresses.size(), filtered_addresses);
850863
return addresses;
851864
}
852865

@@ -1226,11 +1239,11 @@ std::pair<CAddress, NodeSeconds> AddrManImpl::Select(bool new_only, const std::u
12261239
return addrRet;
12271240
}
12281241

1229-
std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
1242+
std::vector<CAddress> AddrManImpl::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered, const AddrMan::AddrPolicy& policy) const
12301243
{
12311244
LOCK(cs);
12321245
Check();
1233-
auto addresses = GetAddr_(max_addresses, max_pct, network, filtered);
1246+
auto addresses = GetAddr_(max_addresses, max_pct, network, filtered, policy);
12341247
Check();
12351248
return addresses;
12361249
}
@@ -1329,9 +1342,9 @@ std::pair<CAddress, NodeSeconds> AddrMan::Select(bool new_only, const std::unord
13291342
return m_impl->Select(new_only, networks);
13301343
}
13311344

1332-
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
1345+
std::vector<CAddress> AddrMan::GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered, const AddrPolicy& policy) const
13331346
{
1334-
return m_impl->GetAddr(max_addresses, max_pct, network, filtered);
1347+
return m_impl->GetAddr(max_addresses, max_pct, network, filtered, policy);
13351348
}
13361349

13371350
std::vector<std::pair<AddrInfo, AddressPosition>> AddrMan::GetEntries(bool use_tried) const

src/addrman.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <unordered_set>
1919
#include <utility>
2020
#include <vector>
21+
#include <functional>
2122

2223
class InvalidAddrManVersionError : public std::ios_base::failure
2324
{
@@ -91,6 +92,12 @@ class AddrMan
9192
const std::unique_ptr<AddrManImpl> m_impl;
9293

9394
public:
95+
/** Predicate used to exclude addresses during selection.
96+
* Return true to skip the given address.
97+
* Must be non-blocking and must not acquire locks that can conflict with AddrMan::cs.
98+
*/
99+
using AddrPolicy = std::function<bool(const CAddress&)>;
100+
94101
explicit AddrMan(const NetGroupManager& netgroupman, bool deterministic, int32_t consistency_check_ratio);
95102

96103
~AddrMan();
@@ -169,10 +176,11 @@ class AddrMan
169176
* @param[in] max_pct Maximum percentage of addresses to return (0 = all). Value must be from 0 to 100.
170177
* @param[in] network Select only addresses of this network (nullopt = all).
171178
* @param[in] filtered Select only addresses that are considered good quality (false = all).
179+
* @param[in] policy Optional predicate to exclude candidates during selection (true = exclude).
172180
*
173181
* @return A vector of randomly selected addresses from vRandom.
174182
*/
175-
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const;
183+
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true, const AddrPolicy& policy = {}) const;
176184

177185
/**
178186
* Returns an information-location pair for all addresses in the selected addrman table.

src/addrman_impl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class AddrManImpl
135135
std::pair<CAddress, NodeSeconds> Select(bool new_only, const std::unordered_set<Network>& networks) const
136136
EXCLUSIVE_LOCKS_REQUIRED(!cs);
137137

138-
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const
138+
std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true, const AddrMan::AddrPolicy& policy = {}) const
139139
EXCLUSIVE_LOCKS_REQUIRED(!cs);
140140

141141
std::vector<std::pair<AddrInfo, AddressPosition>> GetEntries(bool from_tried) const
@@ -267,7 +267,7 @@ class AddrManImpl
267267
* */
268268
nid_type GetEntry(bool use_tried, size_t bucket, size_t position) const EXCLUSIVE_LOCKS_REQUIRED(cs);
269269

270-
std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true) const EXCLUSIVE_LOCKS_REQUIRED(cs);
270+
std::vector<CAddress> GetAddr_(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered = true, const AddrMan::AddrPolicy& policy = {}) const EXCLUSIVE_LOCKS_REQUIRED(cs);
271271

272272
std::vector<std::pair<AddrInfo, AddressPosition>> GetEntries_(bool from_tried) const EXCLUSIVE_LOCKS_REQUIRED(cs);
273273

src/net.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3507,13 +3507,10 @@ CConnman::~CConnman()
35073507

35083508
std::vector<CAddress> CConnman::GetAddressesUnsafe(size_t max_addresses, size_t max_pct, std::optional<Network> network, const bool filtered) const
35093509
{
3510-
std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct, network, filtered);
3511-
if (m_banman) {
3512-
addresses.erase(std::remove_if(addresses.begin(), addresses.end(),
3513-
[this](const CAddress& addr){return m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr);}),
3514-
addresses.end());
3515-
}
3516-
return addresses;
3510+
const AddrMan::AddrPolicy policy = [this](const CAddress& addr) {
3511+
return m_banman && (m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr));
3512+
};
3513+
return addrman.GetAddr(/*max_addresses=*/max_addresses, /*max_pct=*/max_pct, /*network=*/network, /*filtered=*/filtered, /*policy=*/policy);
35173514
}
35183515

35193516
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)

src/test/addrman_tests.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,27 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
407407
// Net processing asks for 23% of addresses. 23% of 5 is 1 rounded down.
408408
BOOST_CHECK_EQUAL(addrman->GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt).size(), 1U);
409409

410+
// Test AddrMan::GetAddr filtering with various AddrPolicy rules.
411+
const size_t addedAddresses = addrman->Size();
412+
const AddrMan::AddrPolicy policyNoSkip = [](const CAddress&) { return false; };
413+
const AddrMan::AddrPolicy policySkipAll = [](const CAddress&) { return true; };
414+
const AddrMan::AddrPolicy policyPortPolicy = [](const CAddress& addr) { return addr.GetPort() != 8333; };
415+
416+
const std::vector<CAddress> vAddrNoPolicy = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false);
417+
const std::vector<CAddress> vAddrPolicyNoSkip = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false, /*policy=*/policyNoSkip);
418+
const std::vector<CAddress> vAddrPolicySkipAll = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false, /*policy=*/policySkipAll);
419+
const std::vector<CAddress> vAddrPolicyPort = addrman->GetAddr(/*max_addresses=*/0, /*max_pct=*/0, /*network=*/std::nullopt, /*filtered=*/false, /*policy=*/policyPortPolicy);
420+
const std::set<CAddress> vExpectedAddresses = {addr1, addr3, addr4, addr5};
421+
422+
BOOST_CHECK_EQUAL(vAddrNoPolicy.size(), addedAddresses);
423+
BOOST_CHECK_EQUAL(vAddrPolicyNoSkip.size(), vAddrNoPolicy.size());
424+
BOOST_CHECK_EQUAL(vAddrPolicySkipAll.size(), 0);
425+
426+
// Check that vAddrPolicyPort contains exactly the same items as vExpectedAddresses
427+
BOOST_CHECK_EQUAL(vAddrPolicyPort.size(), vExpectedAddresses.size());
428+
BOOST_CHECK(std::all_of(vAddrPolicyPort.begin(), vAddrPolicyPort.end(), [&](const CAddress& a){ return vExpectedAddresses.contains(a); }));
429+
BOOST_CHECK(std::none_of(vAddrPolicyPort.begin(), vAddrPolicyPort.end(), policyPortPolicy));
430+
410431
// Test: Ensure GetAddr works with new and tried addresses.
411432
BOOST_CHECK(addrman->Good(CAddress(addr1, NODE_NONE)));
412433
BOOST_CHECK(addrman->Good(CAddress(addr2, NODE_NONE)));

0 commit comments

Comments
 (0)