aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/net.cpp38
1 files changed, 36 insertions, 2 deletions
diff --git a/src/net.cpp b/src/net.cpp
index dc34743c5b..347da142a5 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -816,6 +816,7 @@ struct NodeEvictionCandidate
CAddress addr;
uint64_t nKeyedNetGroup;
bool prefer_evict;
+ bool m_is_local;
};
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@@ -828,6 +829,12 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
return a.nTimeConnected > b.nTimeConnected;
}
+static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
+{
+ if (a.m_is_local != b.m_is_local) return b.m_is_local;
+ return a.nTimeConnected > b.nTimeConnected;
+}
+
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
return a.nKeyedNetGroup < b.nKeyedNetGroup;
}
@@ -849,6 +856,14 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
return a.nTimeConnected > b.nTimeConnected;
}
+// Pick out the potential block-relay only peers, and sort them by last block time.
+static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
+{
+ if (a.fRelayTxes != b.fRelayTxes) return a.fRelayTxes;
+ if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime;
+ if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices;
+ return a.nTimeConnected > b.nTimeConnected;
+}
//! Sort an array by the specified comparator, then erase the last K elements.
template<typename T, typename Comparator>
@@ -891,7 +906,7 @@ bool CConnman::AttemptToEvictConnection()
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->addr, node->nKeyedNetGroup,
- node->m_prefer_evict};
+ node->m_prefer_evict, node->addr.IsLocal()};
vEvictionCandidates.push_back(candidate);
}
}
@@ -907,12 +922,31 @@ bool CConnman::AttemptToEvictConnection()
// Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
+ // Protect up to 8 non-tx-relay peers that have sent us novel blocks.
+ std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockRelayOnlyTime);
+ size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
+ vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return !n.fRelayTxes && n.fRelevantServices; }), vEvictionCandidates.end());
+
// Protect 4 nodes that most recently sent us novel blocks.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
+
// Protect the half of the remaining nodes which have been connected the longest.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2);
+ // Reserve half of these protected spots for localhost peers, even if
+ // they're not longest-uptime overall. This helps protect tor peers, which
+ // tend to be otherwise disadvantaged under our eviction criteria.
+ size_t initial_size = vEvictionCandidates.size();
+ size_t total_protect_size = initial_size / 2;
+
+ // Pick out up to 1/4 peers that are localhost, sorted by longest uptime.
+ std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareLocalHostTimeConnected);
+ size_t local_erase_size = total_protect_size / 2;
+ vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - local_erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.m_is_local; }), vEvictionCandidates.end());
+ // Calculate how many we removed, and update our total number of peers that
+ // we want to protect based on uptime accordingly.
+ total_protect_size -= initial_size - vEvictionCandidates.size();
+ EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
if (vEvictionCandidates.empty()) return false;