mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-07-15 21:32:40 +02:00
Extract ProtectEvictionCandidatesByRatio from SelectNodeToEvict
to allow deterministic unit testing of the ratio-based peer eviction protection logic, which protects peers having longer connection times and those connected via higher-latency networks. Add documentation.
This commit is contained in:
39
src/net.cpp
39
src/net.cpp
@ -879,6 +879,26 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator,
|
|||||||
elements.erase(elements.end() - eraseSize, elements.end());
|
elements.erase(elements.end() - eraseSize, elements.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates)
|
||||||
|
{
|
||||||
|
// 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.
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
|
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
|
||||||
{
|
{
|
||||||
// Protect connections with certain characteristics
|
// Protect connections with certain characteristics
|
||||||
@ -901,22 +921,9 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator,
|
|||||||
// 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 longest.
|
// Protect some of the remaining eviction candidates by ratios of desirable
|
||||||
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
|
// or disadvantaged characteristics.
|
||||||
// Reserve half of these protected spots for localhost peers, even if
|
ProtectEvictionCandidatesByRatio(vEvictionCandidates);
|
||||||
// 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 std::nullopt;
|
if (vEvictionCandidates.empty()) return std::nullopt;
|
||||||
|
|
||||||
|
26
src/net.h
26
src/net.h
@ -1283,6 +1283,32 @@ struct NodeEvictionCandidate
|
|||||||
bool m_is_local;
|
bool m_is_local;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select an inbound peer to evict after filtering out (protecting) peers having
|
||||||
|
* distinct, difficult-to-forge characteristics. The protection logic picks out
|
||||||
|
* fixed numbers of desirable peers per various criteria, followed by ratios of
|
||||||
|
* desirable or disadvantaged peers. If any eviction candidates remain, the
|
||||||
|
* selection logic chooses a peer to evict.
|
||||||
|
*/
|
||||||
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates);
|
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates);
|
||||||
|
|
||||||
|
/** Protect desirable or disadvantaged inbound peers from eviction by ratio.
|
||||||
|
*
|
||||||
|
* This function protects half of the peers which have been connected the
|
||||||
|
* longest, to replicate the non-eviction implicit behavior and preclude attacks
|
||||||
|
* that start later.
|
||||||
|
*
|
||||||
|
* Half of these protected spots (1/4 of the total) are reserved for localhost
|
||||||
|
* peers, if any, sorted by longest uptime, even if they're not longest uptime
|
||||||
|
* overall.
|
||||||
|
*
|
||||||
|
* This helps protect onion peers, which tend to be otherwise disadvantaged
|
||||||
|
* under our eviction criteria for their higher min ping times relative to IPv4
|
||||||
|
* and IPv6 peers, and favorise the diversity of peer connections.
|
||||||
|
*
|
||||||
|
* This function was extracted from SelectNodeToEvict() to be able to test the
|
||||||
|
* ratio-based protection logic deterministically.
|
||||||
|
*/
|
||||||
|
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates);
|
||||||
|
|
||||||
#endif // BITCOIN_NET_H
|
#endif // BITCOIN_NET_H
|
||||||
|
Reference in New Issue
Block a user