Changeset View
Changeset View
Standalone View
Standalone View
src/net.cpp
Show First 20 Lines • Show All 842 Lines • ▼ Show 20 Lines | struct NodeEvictionCandidate { | ||||
int64_t nLastBlockTime; | int64_t nLastBlockTime; | ||||
int64_t nLastTXTime; | int64_t nLastTXTime; | ||||
bool fRelevantServices; | bool fRelevantServices; | ||||
bool fRelayTxes; | bool fRelayTxes; | ||||
bool fBloomFilter; | bool fBloomFilter; | ||||
CAddress addr; | CAddress addr; | ||||
uint64_t nKeyedNetGroup; | uint64_t nKeyedNetGroup; | ||||
bool prefer_evict; | bool prefer_evict; | ||||
bool m_is_local; | |||||
}; | }; | ||||
static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, | static bool ReverseCompareNodeMinPingTime(const NodeEvictionCandidate &a, | ||||
const NodeEvictionCandidate &b) { | const NodeEvictionCandidate &b) { | ||||
return a.nMinPingUsecTime > b.nMinPingUsecTime; | return a.nMinPingUsecTime > b.nMinPingUsecTime; | ||||
} | } | ||||
static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, | static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, | ||||
const NodeEvictionCandidate &b) { | const NodeEvictionCandidate &b) { | ||||
return a.nTimeConnected > b.nTimeConnected; | 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, | static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, | ||||
const NodeEvictionCandidate &b) { | const NodeEvictionCandidate &b) { | ||||
return a.nKeyedNetGroup < b.nKeyedNetGroup; | return a.nKeyedNetGroup < b.nKeyedNetGroup; | ||||
} | } | ||||
static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, | static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, | ||||
const NodeEvictionCandidate &b) { | const NodeEvictionCandidate &b) { | ||||
// There is a fall-through here because it is common for a node to have many | // There is a fall-through here because it is common for a node to have many | ||||
Show All 23 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; | ||||
} | } | ||||
// 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. | //! Sort an array by the specified comparator, then erase the last K elements. | ||||
template <typename T, typename Comparator> | template <typename T, typename Comparator> | ||||
static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, | static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, | ||||
size_t k) { | size_t k) { | ||||
std::sort(elements.begin(), elements.end(), comparator); | std::sort(elements.begin(), elements.end(), comparator); | ||||
size_t eraseSize = std::min(k, elements.size()); | size_t eraseSize = std::min(k, elements.size()); | ||||
elements.erase(elements.end() - eraseSize, elements.end()); | elements.erase(elements.end() - eraseSize, elements.end()); | ||||
} | } | ||||
Show All 35 Lines | std::vector<NodeEvictionCandidate> vEvictionCandidates; | ||||
node->nMinPingUsecTime, | node->nMinPingUsecTime, | ||||
node->nLastBlockTime, | node->nLastBlockTime, | ||||
node->nLastTXTime, | node->nLastTXTime, | ||||
HasAllDesirableServiceFlags(node->nServices), | HasAllDesirableServiceFlags(node->nServices), | ||||
peer_relay_txes, | peer_relay_txes, | ||||
peer_filter_not_null, | peer_filter_not_null, | ||||
node->addr, | node->addr, | ||||
node->nKeyedNetGroup, | node->nKeyedNetGroup, | ||||
node->m_prefer_evict}; | node->m_prefer_evict, | ||||
node->addr.IsLocal()}; | |||||
vEvictionCandidates.push_back(candidate); | vEvictionCandidates.push_back(candidate); | ||||
} | } | ||||
} | } | ||||
// Protect connections with certain characteristics | // Protect connections with certain characteristics | ||||
// Deterministically select 4 peers to protect by netgroup. | // Deterministically select 4 peers to protect by netgroup. | ||||
// An attacker cannot predict which netgroups will be protected | // An attacker cannot predict which netgroups will be protected | ||||
EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4); | EraseLastKElements(vEvictionCandidates, CompareNetGroupKeyed, 4); | ||||
// Protect the 8 nodes with the lowest minimum ping time. | // Protect the 8 nodes with the lowest minimum ping time. | ||||
// An attacker cannot manipulate this metric without physically moving nodes | // An attacker cannot manipulate this metric without physically moving nodes | ||||
// closer to the target. | // closer to the target. | ||||
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8); | EraseLastKElements(vEvictionCandidates, ReverseCompareNodeMinPingTime, 8); | ||||
// Protect 4 nodes that most recently sent us novel transactions accepted | // Protect 4 nodes that most recently sent us novel transactions accepted | ||||
// into our mempool. An attacker cannot manipulate this metric without | // into our mempool. An attacker cannot manipulate this metric without | ||||
// performing useful work. | // performing useful work. | ||||
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4); | 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. | // Protect 4 nodes that most recently sent us novel blocks. | ||||
// An attacker cannot manipulate this metric without performing useful work. | // An attacker cannot manipulate this metric without performing useful work. | ||||
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4); | EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4); | ||||
// 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. | ||||
// 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, | EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, | ||||
vEvictionCandidates.size() / 2); | total_protect_size); | ||||
if (vEvictionCandidates.empty()) { | if (vEvictionCandidates.empty()) { | ||||
return false; | return false; | ||||
} | } | ||||
// If any remaining peers are preferred for eviction consider only them. | // If any remaining peers are preferred for eviction consider only them. | ||||
// This happens after the other preferences since if a peer is really the | // This happens after the other preferences since if a peer is really the | ||||
// best by other criteria (esp relaying blocks) | // best by other criteria (esp relaying blocks) | ||||
▲ Show 20 Lines • Show All 2,182 Lines • Show Last 20 Lines |