Changeset View
Changeset View
Standalone View
Standalone View
src/net.cpp
Show First 20 Lines • Show All 1,016 Lines • ▼ Show 20 Lines | static bool CompareNodeTXTime(const NodeEvictionCandidate &a, | ||||
if (a.fBloomFilter != b.fBloomFilter) { | if (a.fBloomFilter != b.fBloomFilter) { | ||||
return a.fBloomFilter; | return a.fBloomFilter; | ||||
} | } | ||||
return a.nTimeConnected > b.nTimeConnected; | 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 | * Try to find a connection to evict when the node is full. | ||||
* taken to avoid opening the node to attacker triggered network partitioning. | * Extreme care must be taken to avoid opening the node to attacker triggered | ||||
* network partitioning. | |||||
* The strategy used here is to protect a small number of peers for each of | * The strategy used here is to protect a small number of peers for each of | ||||
* several distinct characteristics which are difficult to forge. In order to | * several distinct characteristics which are difficult to forge. In order to | ||||
* partition a node the attacker must be simultaneously better at all of them | * partition a node the attacker must be simultaneously better at all of them | ||||
* than honest peers. | * than honest peers. | ||||
*/ | */ | ||||
bool CConnman::AttemptToEvictConnection() { | bool CConnman::AttemptToEvictConnection() { | ||||
std::vector<NodeEvictionCandidate> vEvictionCandidates; | std::vector<NodeEvictionCandidate> vEvictionCandidates; | ||||
{ | { | ||||
Show All 13 Lines | std::vector<NodeEvictionCandidate> vEvictionCandidates; | ||||
node->fRelayTxes, | node->fRelayTxes, | ||||
node->pfilter != nullptr, | node->pfilter != nullptr, | ||||
node->addr, | node->addr, | ||||
node->nKeyedNetGroup}; | node->nKeyedNetGroup}; | ||||
vEvictionCandidates.push_back(candidate); | vEvictionCandidates.push_back(candidate); | ||||
} | } | ||||
} | } | ||||
if (vEvictionCandidates.empty()) { | |||||
return false; | |||||
} | |||||
// Protect connections with certain characteristics | // Protect connections with certain characteristics | ||||
// Deterministically select 4 peers to protect by netgroup. An attacker | // Deterministically select 4 peers to protect by netgroup. | ||||
// cannot predict which netgroups will be protected. | // An attacker cannot predict which netgroups will be protected | ||||
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), | EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4); | ||||
CompareNetGroupKeyed); | // Protect the 8 nodes with the lowest minimum ping time. | ||||
vEvictionCandidates.erase( | // An attacker cannot manipulate this metric without physically moving nodes | ||||
vEvictionCandidates.end() - | // closer to the target. | ||||
std::min(4, static_cast<int>(vEvictionCandidates.size())), | EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8); | ||||
vEvictionCandidates.end()); | // Protect 4 nodes that most recently sent us transactions. | ||||
// An attacker cannot manipulate this metric without performing useful work. | |||||
if (vEvictionCandidates.empty()) { | EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4); | ||||
return false; | // Protect 4 nodes that most recently sent us blocks. | ||||
} | // An attacker cannot manipulate this metric without performing useful work. | ||||
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 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; | |||||
} | |||||
// 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; | |||||
} | |||||
// 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; | |||||
} | |||||
// Protect the half of the remaining nodes which have been connected the | // Protect the half of the remaining nodes which have been connected the | ||||
// longest. This replicates the non-eviction implicit behavior, and | // longest. This replicates the non-eviction implicit behavior, and | ||||
// precludes attacks that start later. | // precludes attacks that start later. | ||||
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), | EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, | ||||
ReverseCompareNodeTimeConnected); | vEvictionCandidates.size() / 2); | ||||
vEvictionCandidates.erase( | |||||
vEvictionCandidates.end() - | |||||
static_cast<int>(vEvictionCandidates.size() / 2), | |||||
vEvictionCandidates.end()); | |||||
if (vEvictionCandidates.empty()) { | if (vEvictionCandidates.empty()) { | ||||
return false; | return false; | ||||
} | } | ||||
// Identify the network group with the most connections and youngest member. | // Identify the network group with the most connections and youngest member. | ||||
// (vEvictionCandidates is already sorted by reverse connect time) | // (vEvictionCandidates is already sorted by reverse connect time) | ||||
uint64_t naMostConnections; | uint64_t naMostConnections; | ||||
unsigned int nMostConnections = 0; | unsigned int nMostConnections = 0; | ||||
int64_t nMostConnectionsTime = 0; | int64_t nMostConnectionsTime = 0; | ||||
std::map<uint64_t, std::vector<NodeEvictionCandidate>> mapNetGroupNodes; | std::map<uint64_t, std::vector<NodeEvictionCandidate>> mapNetGroupNodes; | ||||
for (const NodeEvictionCandidate &node : vEvictionCandidates) { | for (const NodeEvictionCandidate &node : vEvictionCandidates) { | ||||
mapNetGroupNodes[node.nKeyedNetGroup].push_back(node); | std::vector<NodeEvictionCandidate> &group = | ||||
int64_t grouptime = | mapNetGroupNodes[node.nKeyedNetGroup]; | ||||
mapNetGroupNodes[node.nKeyedNetGroup][0].nTimeConnected; | group.push_back(node); | ||||
size_t groupsize = mapNetGroupNodes[node.nKeyedNetGroup].size(); | int64_t grouptime = group[0].nTimeConnected; | ||||
size_t group_size = group.size(); | |||||
if (groupsize > nMostConnections || | if (group_size > nMostConnections || | ||||
(groupsize == nMostConnections && | (group_size == nMostConnections && | ||||
grouptime > nMostConnectionsTime)) { | grouptime > nMostConnectionsTime)) { | ||||
nMostConnections = groupsize; | nMostConnections = group_size; | ||||
nMostConnectionsTime = grouptime; | nMostConnectionsTime = grouptime; | ||||
naMostConnections = node.nKeyedNetGroup; | naMostConnections = node.nKeyedNetGroup; | ||||
} | } | ||||
} | } | ||||
// Reduce to the network group with the most connections | // Reduce to the network group with the most connections | ||||
vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); | vEvictionCandidates = std::move(mapNetGroupNodes[naMostConnections]); | ||||
▲ Show 20 Lines • Show All 2,022 Lines • Show Last 20 Lines |