Skip to content

Commit 5ef3b69

Browse files
committed
Merge #11524: [net] De-duplicate connection eviction logic
5ce7cb9 [net] De-duplicate connection eviction logic (Thomas Snider) Pull request description: While reviewing the safeguards against deliberate node isolation on the network by malicious actors, I found a good de-duplication candidate. I think this form is much more legible (the type of `cutoffs` notwithstanding). ReverseCompareNodeTimeConnected is not included in the list since the cutoff size is a function of the remaining number of nodes in the candidate eviction set. Tree-SHA512: ed17999fa9250dcf8448329219324477117e4ecd2d41dedd72ad253e44630eef50b3232c420f1862ebbfb9b8c94efbba1a235b519e39ff5946865c7d69a75280
2 parents 5776582 + 5ce7cb9 commit 5ef3b69

File tree

1 file changed

+20
-29
lines changed

1 file changed

+20
-29
lines changed

src/net.cpp

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,16 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
961961
return a.nTimeConnected > b.nTimeConnected;
962962
}
963963

964+
965+
//! Sort an array by the specified comparator, then erase the last K elements.
966+
template<typename T, typename Comparator>
967+
static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, size_t k)
968+
{
969+
std::sort(elements.begin(), elements.end(), comparator);
970+
size_t eraseSize = std::min(k, elements.size());
971+
elements.erase(elements.end() - eraseSize, elements.end());
972+
}
973+
964974
/** Try to find a connection to evict when the node is full.
965975
* Extreme care must be taken to avoid opening the node to attacker
966976
* triggered network partitioning.
@@ -990,42 +1000,23 @@ bool CConnman::AttemptToEvictConnection()
9901000
}
9911001
}
9921002

993-
if (vEvictionCandidates.empty()) return false;
994-
9951003
// Protect connections with certain characteristics
9961004

9971005
// Deterministically select 4 peers to protect by netgroup.
9981006
// An attacker cannot predict which netgroups will be protected
999-
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed);
1000-
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
1001-
1002-
if (vEvictionCandidates.empty()) return false;
1003-
1007+
EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4);
10041008
// Protect the 8 nodes with the lowest minimum ping time.
10051009
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
1006-
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime);
1007-
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
1008-
1009-
if (vEvictionCandidates.empty()) return false;
1010-
1010+
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8);
10111011
// Protect 4 nodes that most recently sent us transactions.
10121012
// An attacker cannot manipulate this metric without performing useful work.
1013-
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime);
1014-
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
1015-
1016-
if (vEvictionCandidates.empty()) return false;
1017-
1013+
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
10181014
// Protect 4 nodes that most recently sent us blocks.
10191015
// An attacker cannot manipulate this metric without performing useful work.
1020-
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime);
1021-
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end());
1022-
1023-
if (vEvictionCandidates.empty()) return false;
1024-
1016+
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
10251017
// Protect the half of the remaining nodes which have been connected the longest.
10261018
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
1027-
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
1028-
vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end());
1019+
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2);
10291020

10301021
if (vEvictionCandidates.empty()) return false;
10311022

@@ -1036,12 +1027,12 @@ bool CConnman::AttemptToEvictConnection()
10361027
int64_t nMostConnectionsTime = 0;
10371028
std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes;
10381029
for (const NodeEvictionCandidate &node : vEvictionCandidates) {
1039-
mapNetGroupNodes[node.nKeyedNetGroup].push_back(node);
1040-
int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected;
1041-
size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size();
1030+
std::vector<NodeEvictionCandidate> &group = mapNetGroupNodes[node.nKeyedNetGroup];
1031+
group.push_back(node);
1032+
int64_t grouptime = group[0].nTimeConnected;
10421033

1043-
if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) {
1044-
nMostConnections = groupsize;
1034+
if (group.size() > nMostConnections || (group.size() == nMostConnections && grouptime > nMostConnectionsTime)) {
1035+
nMostConnections = group.size();
10451036
nMostConnectionsTime = grouptime;
10461037
naMostConnections = node.nKeyedNetGroup;
10471038
}

0 commit comments

Comments
 (0)