0diff --git i/src/net.cpp w/src/net.cpp
  1index ddf33c38a..64900b8b5 100644
  2--- i/src/net.cpp
  3+++ w/src/net.cpp
  4@@ -41,12 +41,13 @@
  5 // with Ubuntu 16.04 LTS and Debian 8 libminiupnpc-dev packages.
  6 static_assert(MINIUPNPC_API_VERSION >= 10, "miniUPnPc API version >= 10 assumed");
  7 #endif
  8 
  9 #include <algorithm>
 10 #include <cstdint>
 11+#include <functional> /* std::function */
 12 #include <unordered_map>
 13 
 14 #include <math.h>
 15 
 16 /** Maximum number of block-relay-only anchor connections */
 17 static constexpr size_t MAX_BLOCK_RELAY_ONLY_ANCHORS = 2;
 18@@ -893,38 +894,46 @@ static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const
 19     if (a.fRelayTxes != b.fRelayTxes) return a.fRelayTxes;
 20     if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime;
 21     if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices;
 22     return a.nTimeConnected > b.nTimeConnected;
 23 }
 24 
 25-//! Sort an array by the specified comparator, then erase the last K elements.
 26-template<typename T, typename Comparator>
 27-static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, size_t k)
 28+//! Sort an array by the specified comparator, then erase each one of the last K elements if
 29+//! `pred(element)` returns true.
 30+template <typename T, typename Comparator>
 31+static void EraseLastKElements(
 32+    std::vector<T>& elements,
 33+    Comparator comparator,
 34+    size_t k,
 35+    std::function<bool(const T&)> pred = [](const T&) { return true; })
 36 {
 37     std::sort(elements.begin(), elements.end(), comparator);
 38     size_t eraseSize = std::min(k, elements.size());
 39-    elements.erase(elements.end() - eraseSize, elements.end());
 40+    elements.erase(std::remove_if(elements.end() - eraseSize, elements.end(), pred),
 41+                   elements.end());
 42 }
 43 
 44 [[nodiscard]] Optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
 45 {
 46     // Protect connections with certain characteristics
 47 
 48+    std::function<bool(const NodeEvictionCandidate&)> pred;
 49+
 50     // Deterministically select 4 peers to protect by netgroup.
 51     // An attacker cannot predict which netgroups will be protected
 52     EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4);
 53     // Protect the 8 nodes with the lowest minimum ping time.
 54     // An attacker cannot manipulate this metric without physically moving nodes closer to the target.
 55     EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8);
 56     // Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
 57     // An attacker cannot manipulate this metric without performing useful work.
 58     EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
 59     // Protect up to 8 non-tx-relay peers that have sent us novel blocks.
 60-    std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockRelayOnlyTime);
 61-    size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
 62-    vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return !n.fRelayTxes && n.fRelevantServices; }), vEvictionCandidates.end());
 63+    const size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
 64+    pred = [](const NodeEvictionCandidate& n) { return !n.fRelayTxes && n.fRelevantServices; };
 65+    EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, erase_size, pred);
 66 
 67     // Protect 4 nodes that most recently sent us novel blocks.
 68     // An attacker cannot manipulate this metric without performing useful work.
 69     EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
 70 
 71     // Protect the half of the remaining nodes which have been connected the longest.
 72@@ -933,22 +942,22 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator,
 73     // they're not longest-uptime overall. This helps protect tor peers, which
 74     // tend to be otherwise disadvantaged under our eviction criteria.
 75     const size_t initial_size = vEvictionCandidates.size();
 76     size_t total_protect_size = initial_size / 2;
 77 
 78     // Pick out up to 1/4 peers that are connected via our tor onion service, sorted by longest uptime.
 79-    std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareOnionTimeConnected);
 80     const size_t local_erase_size = total_protect_size / 2;
 81-    vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - local_erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.m_is_onion; }), vEvictionCandidates.end());
 82+    pred = [](const NodeEvictionCandidate& n) { return n.m_is_onion; };
 83+    EraseLastKElements(vEvictionCandidates, CompareOnionTimeConnected, local_erase_size, pred);
 84 
 85     // If no onion peers were removed, extend the same protection to localhost peers, as manually
 86     // configured hidden services not using -bind will not be detected as inbound onion connections.
 87     if (initial_size == vEvictionCandidates.size()) {
 88         // Pick out up to 1/4 peers that are localhost, sorted by longest uptime.
 89-        std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareLocalHostTimeConnected);
 90-        vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - local_erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.m_is_local; }), vEvictionCandidates.end());
 91+        pred = [](const NodeEvictionCandidate& n) { return n.m_is_local; };
 92+        EraseLastKElements(vEvictionCandidates, CompareLocalHostTimeConnected, local_erase_size, pred);
 93     }
 94 
 95     // Calculate how many we removed, and update our total number of peers that
 96     // we want to protect based on uptime accordingly.
 97     total_protect_size -= initial_size - vEvictionCandidates.size();
 98     EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
 99@@ -956,14 +965,15 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator,
100     if (vEvictionCandidates.empty()) return nullopt;
101 
102     // If any remaining peers are preferred for eviction consider only them.
103     // This happens after the other preferences since if a peer is really the best by other criteria (esp relaying blocks)
104     //  then we probably don't want to evict it no matter what.
105     if (std::any_of(vEvictionCandidates.begin(),vEvictionCandidates.end(),[](NodeEvictionCandidate const &n){return n.prefer_evict;})) {
106-        vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.begin(),vEvictionCandidates.end(),
107-                                  [](NodeEvictionCandidate const &n){return !n.prefer_evict;}),vEvictionCandidates.end());
108+        pred = [](const NodeEvictionCandidate& n) { return !n.prefer_evict; };
109+        EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected,
110+                           vEvictionCandidates.size(), pred);
111     }
112 
113     // Identify the network group with the most connections and youngest member.
114     // (vEvictionCandidates is already sorted by reverse connect time)
115     uint64_t naMostConnections;
116     unsigned int nMostConnections = 0;