@ -961,6 +961,16 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
@@ -961,6 +961,16 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
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 taken to avoid opening the node to attacker
* triggered network partitioning .
@ -990,42 +1000,23 @@ bool CConnman::AttemptToEvictConnection()
@@ -990,42 +1000,23 @@ bool CConnman::AttemptToEvictConnection()
}
}
if ( vEvictionCandidates . empty ( ) ) return false ;
// Protect connections with certain characteristics
// Deterministically select 4 peers to protect by netgroup.
// An attacker cannot predict which netgroups will be protected
std : : sort ( vEvictionCandidates . begin ( ) , vEvictionCandidates . end ( ) , CompareNetGroupKeyed ) ;
vEvictionCandidates . erase ( vEvictionCandidates . end ( ) - std : : min ( 4 , static_cast < int > ( vEvictionCandidates . size ( ) ) ) , vEvictionCandidates . end ( ) ) ;
if ( vEvictionCandidates . empty ( ) ) return false ;
EraseLastKElements ( vEvictionCandidates , CompareNetGroupKeyed , 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 ;
EraseLastKElements ( vEvictionCandidates , ReverseCompareNodeMinPingTime , 8 ) ;
// 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 ;
EraseLastKElements ( vEvictionCandidates , CompareNodeTXTime , 4 ) ;
// 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 ;
EraseLastKElements ( vEvictionCandidates , CompareNodeBlockTime , 4 ) ;
// 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.
std : : sort ( vEvictionCandidates . begin ( ) , vEvictionCandidates . end ( ) , ReverseCompareNodeTimeConnected ) ;
vEvictionCandidates . erase ( vEvictionCandidates . end ( ) - static_cast < int > ( vEvictionCandidates . size ( ) / 2 ) , vEvictionCandidates . end ( ) ) ;
EraseLastKElements ( vEvictionCandidates , ReverseCompareNodeTimeConnected , vEvictionCandidates . size ( ) / 2 ) ;
if ( vEvictionCandidates . empty ( ) ) return false ;
@ -1036,12 +1027,12 @@ bool CConnman::AttemptToEvictConnection()
@@ -1036,12 +1027,12 @@ bool CConnman::AttemptToEvictConnection()
int64_t nMostConnectionsTime = 0 ;
std : : map < uint64_t , std : : vector < NodeEvictionCandidate > > mapNetGroupNodes ;
for ( const NodeEvictionCandidate & node : vEvictionCandidates ) {
mapNetGroupNodes [ node . nKeyedNetGroup ] . push_back ( node ) ;
int64_t grouptime = mapNetGroupNodes [ node . nKeyedNetGroup ] [ 0 ] . nTimeConnected ;
size_t groupsize = mapNetGroupNodes [ node . nKeyedNetGroup ] . size ( ) ;
std : : vector < NodeEvictionCandidate > & group = mapNetGroupNodes [ node . nKeyedNetGroup ] ;
group . push_back ( node ) ;
int64_t grouptime = group [ 0 ] . nTimeConnected ;
if ( groupsize > nMostConnections | | ( groupsize = = nMostConnections & & grouptime > nMostConnectionsTime ) ) {
nMostConnections = groupsize ;
if ( group . size ( ) > nMostConnections | | ( group . size ( ) = = nMostConnections & & grouptime > nMostConnectionsTime ) ) {
nMostConnections = group . size ( ) ;
nMostConnectionsTime = grouptime ;
naMostConnections = node . nKeyedNetGroup ;
}