|
|
|
@ -962,6 +962,16 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
@@ -962,6 +962,16 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
|
|
|
|
|
return a.nTimeConnected > b.nTimeConnected; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//! Sort an array by the specified comparator, then erase the last K elements.
|
|
|
|
|
template<typename T, typename Comparator> |
|
|
|
|
static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, size_t k) |
|
|
|
|
{ |
|
|
|
|
std::sort(elements.begin(), elements.end(), comparator); |
|
|
|
|
size_t eraseSize = std::min(k, elements.size()); |
|
|
|
|
elements.erase(elements.end() - eraseSize, elements.end()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** Try to find a connection to evict when the node is full.
|
|
|
|
|
* Extreme care must be taken to avoid opening the node to attacker |
|
|
|
|
* triggered network partitioning. |
|
|
|
@ -991,42 +1001,23 @@ bool CConnman::AttemptToEvictConnection()
@@ -991,42 +1001,23 @@ bool CConnman::AttemptToEvictConnection()
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (vEvictionCandidates.empty()) return false; |
|
|
|
|
|
|
|
|
|
// Protect connections with certain characteristics
|
|
|
|
|
|
|
|
|
|
// Deterministically select 4 peers to protect by netgroup.
|
|
|
|
|
// An attacker cannot predict which netgroups will be protected
|
|
|
|
|
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNetGroupKeyed); |
|
|
|
|
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); |
|
|
|
|
|
|
|
|
|
if (vEvictionCandidates.empty()) return false; |
|
|
|
|
|
|
|
|
|
EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4); |
|
|
|
|
// Protect the 8 nodes with the lowest minimum ping time.
|
|
|
|
|
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
|
|
|
|
|
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeMinPingTime); |
|
|
|
|
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(8, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); |
|
|
|
|
|
|
|
|
|
if (vEvictionCandidates.empty()) return false; |
|
|
|
|
|
|
|
|
|
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8); |
|
|
|
|
// Protect 4 nodes that most recently sent us transactions.
|
|
|
|
|
// An attacker cannot manipulate this metric without performing useful work.
|
|
|
|
|
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeTXTime); |
|
|
|
|
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); |
|
|
|
|
|
|
|
|
|
if (vEvictionCandidates.empty()) return false; |
|
|
|
|
|
|
|
|
|
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4); |
|
|
|
|
// Protect 4 nodes that most recently sent us blocks.
|
|
|
|
|
// An attacker cannot manipulate this metric without performing useful work.
|
|
|
|
|
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockTime); |
|
|
|
|
vEvictionCandidates.erase(vEvictionCandidates.end() - std::min(4, static_cast<int>(vEvictionCandidates.size())), vEvictionCandidates.end()); |
|
|
|
|
|
|
|
|
|
if (vEvictionCandidates.empty()) return false; |
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); |
|
|
|
|
vEvictionCandidates.erase(vEvictionCandidates.end() - static_cast<int>(vEvictionCandidates.size() / 2), vEvictionCandidates.end()); |
|
|
|
|
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size() / 2); |
|
|
|
|
|
|
|
|
|
if (vEvictionCandidates.empty()) return false; |
|
|
|
|
|
|
|
|
@ -1037,12 +1028,12 @@ bool CConnman::AttemptToEvictConnection()
@@ -1037,12 +1028,12 @@ bool CConnman::AttemptToEvictConnection()
|
|
|
|
|
int64_t nMostConnectionsTime = 0; |
|
|
|
|
std::map<uint64_t, std::vector<NodeEvictionCandidate> > mapNetGroupNodes; |
|
|
|
|
for (const NodeEvictionCandidate &node : vEvictionCandidates) { |
|
|
|
|
mapNetGroupNodes[node.nKeyedNetGroup].push_back(node); |
|
|
|
|
int64_t grouptime = mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected; |
|
|
|
|
size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size(); |
|
|
|
|
std::vector<NodeEvictionCandidate> &group = mapNetGroupNodes[node.nKeyedNetGroup]; |
|
|
|
|
group.push_back(node); |
|
|
|
|
int64_t grouptime = group[0].nTimeConnected; |
|
|
|
|
|
|
|
|
|
if (groupsize > nMostConnections || (groupsize == nMostConnections && grouptime > nMostConnectionsTime)) { |
|
|
|
|
nMostConnections = groupsize; |
|
|
|
|
if (group.size() > nMostConnections || (group.size() == nMostConnections && grouptime > nMostConnectionsTime)) { |
|
|
|
|
nMostConnections = group.size(); |
|
|
|
|
nMostConnectionsTime = grouptime; |
|
|
|
|
naMostConnections = node.nKeyedNetGroup; |
|
|
|
|
} |
|
|
|
|