Changeset View
Changeset View
Standalone View
Standalone View
src/net.cpp
// Copyright (c) 2009-2010 Satoshi Nakamoto | // Copyright (c) 2009-2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2019 The Bitcoin Core developers | // Copyright (c) 2009-2019 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#if defined(HAVE_CONFIG_H) | #if defined(HAVE_CONFIG_H) | ||||
#include <config/bitcoin-config.h> | #include <config/bitcoin-config.h> | ||||
#endif | #endif | ||||
#include <net.h> | #include <net.h> | ||||
#include <avalanche/avalanche.h> | #include <avalanche/avalanche.h> | ||||
#include <avalanche/peermanager.h> | |||||
#include <avalanche/processor.h> | |||||
#include <banman.h> | #include <banman.h> | ||||
#include <clientversion.h> | #include <clientversion.h> | ||||
#include <config.h> | #include <config.h> | ||||
#include <consensus/consensus.h> | #include <consensus/consensus.h> | ||||
#include <crypto/sha256.h> | #include <crypto/sha256.h> | ||||
#include <dnsseeds.h> | #include <dnsseeds.h> | ||||
#include <netbase.h> | #include <netbase.h> | ||||
#include <node/ui_interface.h> | #include <node/ui_interface.h> | ||||
▲ Show 20 Lines • Show All 916 Lines • ▼ Show 20 Lines | static bool CompareNodeAvailabilityScore(const NodeEvictionCandidate &a, | ||||
// Equality can happen if the score has not been computed yet. | // Equality can happen if the score has not been computed yet. | ||||
if (a.availabilityScore != b.availabilityScore) { | if (a.availabilityScore != b.availabilityScore) { | ||||
return a.availabilityScore < b.availabilityScore; | return a.availabilityScore < b.availabilityScore; | ||||
} | } | ||||
return a.nTimeConnected > b.nTimeConnected; | return a.nTimeConnected > b.nTimeConnected; | ||||
} | } | ||||
static void | |||||
ProtectAvalancheNodes(std::vector<NodeEvictionCandidate> &candidates) { | |||||
std::map<PeerId, NodeEvictionCandidate> preferredNodes; | |||||
for (auto &c : candidates) { | |||||
if (!c.fAvalanche || c.peerid == NO_PEER) { | |||||
continue; | |||||
} | |||||
auto handle = preferredNodes.extract(c.peerid); | |||||
if (!handle) { | |||||
preferredNodes.emplace(c.peerid, c); | |||||
continue; | |||||
} | |||||
auto &mappedCandidate = handle.mapped(); | |||||
if (c.availabilityScore > mappedCandidate.availabilityScore) { | |||||
mappedCandidate = c; | |||||
} | |||||
preferredNodes.insert(std::move(handle)); | |||||
} | |||||
deadalnix: The datastructure you are building here already exists in the PeerManager, no? It seems like… | |||||
FabienAuthorUnsubmitted Done Inline ActionsThere is no such structure, the peerid is managed by the avalanche::PeerManager and the score is part of the AvalancheState of each CNode. Fabien: There is no such structure, the peerid is managed by the avalanche::PeerManager and the score… | |||||
// Remove the nodes while keeping the ordering of the other elements | |||||
for (auto cit = candidates.begin(); cit != candidates.end();) { | |||||
auto nit = preferredNodes.find(cit->peerid); | |||||
if (nit != preferredNodes.end() && nit->second.id == cit->id) { | |||||
cit = candidates.erase(cit); | |||||
} else { | |||||
++cit; | |||||
} | |||||
} | |||||
} | |||||
//! 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 | SelectNodeToEvict(std::vector<NodeEvictionCandidate> &&vEvictionCandidates) { | ||||
// Protect up to 8 non-tx-relay peers that have sent us novel blocks. | // Protect up to 8 non-tx-relay peers that have sent us novel blocks. | ||||
EraseLastKElementsIf(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8, | EraseLastKElementsIf(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8, | ||||
[](NodeEvictionCandidate const &n) { | [](NodeEvictionCandidate const &n) { | ||||
return !n.fRelayTxes && n.fRelevantServices; | return !n.fRelayTxes && n.fRelevantServices; | ||||
}); | }); | ||||
// 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 node with the highest availability score for each avalanche | |||||
// peer | |||||
ProtectAvalancheNodes(vEvictionCandidates); | |||||
// Protect up to 16 nodes that have the highest avalanche availability | // Protect up to 16 nodes that have the highest avalanche availability | ||||
// score. | // score. | ||||
EraseLastKElementsIf( | EraseLastKElementsIf( | ||||
vEvictionCandidates, CompareNodeAvailabilityScore, 16, | vEvictionCandidates, CompareNodeAvailabilityScore, 16, | ||||
[](NodeEvictionCandidate const &n) { return n.fAvalanche; }); | [](NodeEvictionCandidate const &n) { return n.fAvalanche; }); | ||||
// 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 | ||||
▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | std::vector<NodeEvictionCandidate> vEvictionCandidates; | ||||
bool peer_filter_not_null = false; | bool peer_filter_not_null = false; | ||||
if (node->m_tx_relay != nullptr) { | if (node->m_tx_relay != nullptr) { | ||||
LOCK(node->m_tx_relay->cs_filter); | LOCK(node->m_tx_relay->cs_filter); | ||||
peer_relay_txes = node->m_tx_relay->fRelayTxes; | peer_relay_txes = node->m_tx_relay->fRelayTxes; | ||||
peer_filter_not_null = node->m_tx_relay->pfilter != nullptr; | peer_filter_not_null = node->m_tx_relay->pfilter != nullptr; | ||||
} | } | ||||
double availabilityScore = std::numeric_limits<double>::lowest(); | double availabilityScore = std::numeric_limits<double>::lowest(); | ||||
PeerId peerid = NO_PEER; | |||||
bool fAvalanche = node->m_avalanche_state != nullptr; | bool fAvalanche = node->m_avalanche_state != nullptr; | ||||
if (fAvalanche) { | if (fAvalanche) { | ||||
availabilityScore = | availabilityScore = | ||||
node->m_avalanche_state->getAvailabilityScore(); | node->m_avalanche_state->getAvailabilityScore(); | ||||
g_avalanche->withPeerManager( | |||||
[&node, &peerid](const avalanche::PeerManager &pm) { | |||||
deadalnixUnsubmitted Not Done Inline ActionsThese lambdas do not outlive the call, so using a generic & is fine, IMO. deadalnix: These lambdas do not outlive the call, so using a generic `&` is fine, IMO. | |||||
pm.forNode(node->GetId(), | |||||
[&peerid](const avalanche::Node &n) { | |||||
peerid = n.peerid; | |||||
return true; | |||||
}); | |||||
}); | |||||
} | } | ||||
NodeEvictionCandidate candidate = { | NodeEvictionCandidate candidate = { | ||||
node->GetId(), | node->GetId(), | ||||
node->nTimeConnected, | node->nTimeConnected, | ||||
node->nMinPingUsecTime, | node->nMinPingUsecTime, | ||||
node->nLastBlockTime, | node->nLastBlockTime, | ||||
node->nLastProofTime, | node->nLastProofTime, | ||||
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->nKeyedNetGroup, | node->nKeyedNetGroup, | ||||
node->m_prefer_evict, | node->m_prefer_evict, | ||||
node->addr.IsLocal(), | node->addr.IsLocal(), | ||||
fAvalanche, | fAvalanche, | ||||
availabilityScore}; | availabilityScore, | ||||
peerid, | |||||
}; | |||||
vEvictionCandidates.push_back(candidate); | vEvictionCandidates.push_back(candidate); | ||||
} | } | ||||
} | } | ||||
const std::optional<NodeId> node_id_to_evict = | const std::optional<NodeId> node_id_to_evict = | ||||
SelectNodeToEvict(std::move(vEvictionCandidates)); | SelectNodeToEvict(std::move(vEvictionCandidates)); | ||||
if (!node_id_to_evict) { | if (!node_id_to_evict) { | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 2,164 Lines • Show Last 20 Lines |
The datastructure you are building here already exists in the PeerManager, no? It seems like this is calling for refactoring to leverage this.