@ -816,6 +816,7 @@ struct NodeEvictionCandidate
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 , const NodeEvictionCandidate & b )
static bool ReverseCompareNodeMinPingTime ( const NodeEvictionCandidate & a , const NodeEvictionCandidate & b )
@ -828,6 +829,12 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
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 , const NodeEvictionCandidate & b ) {
static bool CompareNetGroupKeyed ( const NodeEvictionCandidate & a , const NodeEvictionCandidate & b ) {
return a . nKeyedNetGroup < b . nKeyedNetGroup ;
return a . nKeyedNetGroup < b . nKeyedNetGroup ;
}
}
@ -849,6 +856,14 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
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 >
@ -891,7 +906,7 @@ bool CConnman::AttemptToEvictConnection()
node - > nLastBlockTime , node - > nLastTXTime ,
node - > nLastBlockTime , node - > nLastTXTime ,
HasAllDesirableServiceFlags ( node - > nServices ) ,
HasAllDesirableServiceFlags ( node - > nServices ) ,
peer_relay_txes , peer_filter_not_null , node - > addr , node - > nKeyedNetGroup ,
peer_relay_txes , peer_filter_not_null , node - > addr , node - > nKeyedNetGroup ,
node - > m_prefer_evict };
node - > m_prefer_evict , node - > addr . IsLocal ( ) };
vEvictionCandidates . push_back ( candidate ) ;
vEvictionCandidates . push_back ( candidate ) ;
}
}
}
}
@ -907,12 +922,31 @@ bool CConnman::AttemptToEvictConnection()
// Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
// Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
// An attacker cannot manipulate this metric without performing useful work.
// An attacker cannot manipulate this metric without 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 longest.
// 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.
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
EraseLastKElements ( vEvictionCandidates , ReverseCompareNodeTimeConnected , vEvictionCandidates . size ( ) / 2 ) ;
// 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 ) ;
if ( vEvictionCandidates . empty ( ) ) return false ;
if ( vEvictionCandidates . empty ( ) ) return false ;