diff --git a/src/avalanche/peermanager.h b/src/avalanche/peermanager.h --- a/src/avalanche/peermanager.h +++ b/src/avalanche/peermanager.h @@ -216,6 +216,12 @@ return it != pview.end() && func(*it); } + template + bool forPeer(const PeerId &peerid, Callable &&func) const { + auto it = peers.find(peerid); + return it != peers.end() && func(*it); + } + template void forEachPeer(Callable &&func) const { for (const auto &p : peers) { func(p); diff --git a/src/net.h b/src/net.h --- a/src/net.h +++ b/src/net.h @@ -1322,6 +1322,7 @@ bool fAvalanche; double availabilityScore; PeerId peerid; + uint32_t peerScore; }; [[nodiscard]] std::optional diff --git a/src/net.cpp b/src/net.cpp --- a/src/net.cpp +++ b/src/net.cpp @@ -947,6 +947,7 @@ static void ProtectAvalancheNodes(std::vector &candidates) { std::map preferredNodes; + uint64_t minimalPeerScore = 0; for (auto &c : candidates) { if (!c.fAvalanche || c.peerid == NO_PEER) { @@ -955,6 +956,8 @@ auto handle = preferredNodes.extract(c.peerid); if (!handle) { + // Count the score once per peer + minimalPeerScore += c.peerScore; preferredNodes.emplace(c.peerid, c); continue; } @@ -966,10 +969,13 @@ preferredNodes.insert(std::move(handle)); } - // Remove the nodes while keeping the ordering of the other elements + // Remove the nodes while keeping the ordering of the other elements, unless + // the peer has less than ~0.1% chance of being selected for polling. + minimalPeerScore /= 1000; for (auto cit = candidates.begin(); cit != candidates.end();) { auto nit = preferredNodes.find(cit->peerid); - if (nit != preferredNodes.end() && nit->second.id == cit->id) { + if (nit != preferredNodes.end() && nit->second.id == cit->id && + cit->peerScore >= minimalPeerScore) { cit = candidates.erase(cit); } else { ++cit; @@ -1135,17 +1141,25 @@ double availabilityScore = std::numeric_limits::lowest(); PeerId peerid = NO_PEER; + uint32_t peerScore = 0; bool fAvalanche = node->m_avalanche_state != nullptr; if (fAvalanche) { availabilityScore = node->m_avalanche_state->getAvailabilityScore(); g_avalanche->withPeerManager( - [&node, &peerid](const avalanche::PeerManager &pm) { - pm.forNode(node->GetId(), - [&peerid](const avalanche::Node &n) { - peerid = n.peerid; - return true; - }); + [&node, &peerid, + &peerScore](const avalanche::PeerManager &pm) { + if (pm.forNode(node->GetId(), + [&peerid](const avalanche::Node &n) { + peerid = n.peerid; + return true; + })) { + pm.forPeer(peerid, + [&peerScore](const avalanche::Peer &p) { + peerScore = p.getScore(); + return true; + }); + } }); } @@ -1165,6 +1179,7 @@ fAvalanche, availabilityScore, peerid, + peerScore, }; vEvictionCandidates.push_back(candidate); } diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -795,6 +795,8 @@ /* fAvalanche */ random_context.randbool(), /* availabilityScore */ double(random_context.randrange(-1)), /* peerid */ PeerId(random_context.randrange(100)), + /* peerScore */ + static_cast(random_context.randrange(100)), }); } return candidates; @@ -943,6 +945,7 @@ candidate.availabilityScore = double(number_of_nodes - candidate.id); candidate.peerid = PeerId(candidate.id / 10); + candidate.peerScore = 1; }, protectedNodes, random_context)); @@ -971,11 +974,26 @@ candidate.availabilityScore = double(number_of_nodes - candidate.id); candidate.peerid = PeerId(candidate.id / 42); + candidate.peerScore = 1; }, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 42, 84, 126, 168}, random_context)); + // A peer with low score should not be protected from eviction. + BOOST_CHECK(!IsEvicted( + number_of_nodes, + [number_of_nodes](NodeEvictionCandidate &candidate) { + candidate.fAvalanche = true; + candidate.availabilityScore = + double(number_of_nodes - candidate.id); + candidate.peerid = PeerId(candidate.id / 42); + candidate.peerScore = candidate.peerid == 3 ? 1 : 500; + }, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 42, 84, 168}, + random_context)); + // An eviction is expected given >= 49 random eviction candidates. // The eviction logic protects at most four peers by net group, // eight by lowest ping time, four by last time of novel tx, four by