@@ -816,6 +816,7 @@ struct NodeEvictionCandidate
816
816
CAddress addr;
817
817
uint64_t nKeyedNetGroup;
818
818
bool prefer_evict;
819
+ bool m_is_local;
819
820
};
820
821
821
822
static bool ReverseCompareNodeMinPingTime (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
@@ -828,6 +829,12 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
828
829
return a.nTimeConnected > b.nTimeConnected ;
829
830
}
830
831
832
+ static bool CompareLocalHostTimeConnected (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
833
+ {
834
+ if (a.m_is_local != b.m_is_local ) return b.m_is_local ;
835
+ return a.nTimeConnected > b.nTimeConnected ;
836
+ }
837
+
831
838
static bool CompareNetGroupKeyed (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
832
839
return a.nKeyedNetGroup < b.nKeyedNetGroup ;
833
840
}
@@ -849,6 +856,14 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
849
856
return a.nTimeConnected > b.nTimeConnected ;
850
857
}
851
858
859
+ // Pick out the potential block-relay only peers, and sort them by last block time.
860
+ static bool CompareNodeBlockRelayOnlyTime (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
861
+ {
862
+ if (a.fRelayTxes != b.fRelayTxes ) return a.fRelayTxes ;
863
+ if (a.nLastBlockTime != b.nLastBlockTime ) return a.nLastBlockTime < b.nLastBlockTime ;
864
+ if (a.fRelevantServices != b.fRelevantServices ) return b.fRelevantServices ;
865
+ return a.nTimeConnected > b.nTimeConnected ;
866
+ }
852
867
853
868
// ! Sort an array by the specified comparator, then erase the last K elements.
854
869
template <typename T, typename Comparator>
@@ -891,7 +906,7 @@ bool CConnman::AttemptToEvictConnection()
891
906
node->nLastBlockTime , node->nLastTXTime ,
892
907
HasAllDesirableServiceFlags (node->nServices ),
893
908
peer_relay_txes, peer_filter_not_null, node->addr , node->nKeyedNetGroup ,
894
- node->m_prefer_evict };
909
+ node->m_prefer_evict , node-> addr . IsLocal () };
895
910
vEvictionCandidates.push_back (candidate);
896
911
}
897
912
}
@@ -907,12 +922,31 @@ bool CConnman::AttemptToEvictConnection()
907
922
// Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
908
923
// An attacker cannot manipulate this metric without performing useful work.
909
924
EraseLastKElements (vEvictionCandidates, CompareNodeTXTime, 4 );
925
+ // Protect up to 8 non-tx-relay peers that have sent us novel blocks.
926
+ std::sort (vEvictionCandidates.begin (), vEvictionCandidates.end (), CompareNodeBlockRelayOnlyTime);
927
+ size_t erase_size = std::min (size_t (8 ), vEvictionCandidates.size ());
928
+ vEvictionCandidates.erase (std::remove_if (vEvictionCandidates.end () - erase_size, vEvictionCandidates.end (), [](NodeEvictionCandidate const &n) { return !n.fRelayTxes && n.fRelevantServices ; }), vEvictionCandidates.end ());
929
+
910
930
// Protect 4 nodes that most recently sent us novel blocks.
911
931
// An attacker cannot manipulate this metric without performing useful work.
912
932
EraseLastKElements (vEvictionCandidates, CompareNodeBlockTime, 4 );
933
+
913
934
// Protect the half of the remaining nodes which have been connected the longest.
914
935
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
915
- EraseLastKElements (vEvictionCandidates, ReverseCompareNodeTimeConnected, vEvictionCandidates.size () / 2 );
936
+ // Reserve half of these protected spots for localhost peers, even if
937
+ // they're not longest-uptime overall. This helps protect tor peers, which
938
+ // tend to be otherwise disadvantaged under our eviction criteria.
939
+ size_t initial_size = vEvictionCandidates.size ();
940
+ size_t total_protect_size = initial_size / 2 ;
941
+
942
+ // Pick out up to 1/4 peers that are localhost, sorted by longest uptime.
943
+ std::sort (vEvictionCandidates.begin (), vEvictionCandidates.end (), CompareLocalHostTimeConnected);
944
+ size_t local_erase_size = total_protect_size / 2 ;
945
+ vEvictionCandidates.erase (std::remove_if (vEvictionCandidates.end () - local_erase_size, vEvictionCandidates.end (), [](NodeEvictionCandidate const &n) { return n.m_is_local ; }), vEvictionCandidates.end ());
946
+ // Calculate how many we removed, and update our total number of peers that
947
+ // we want to protect based on uptime accordingly.
948
+ total_protect_size -= initial_size - vEvictionCandidates.size ();
949
+ EraseLastKElements (vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
916
950
917
951
if (vEvictionCandidates.empty ()) return false ;
918
952
0 commit comments