@@ -841,6 +841,11 @@ struct NodeEvictionCandidate
841
841
NodeId id;
842
842
int64_t nTimeConnected;
843
843
int64_t nMinPingUsecTime;
844
+ int64_t nLastBlockTime;
845
+ int64_t nLastTXTime;
846
+ bool fNetworkNode ;
847
+ bool fRelayTxes ;
848
+ bool fBloomFilter ;
844
849
CAddress addr;
845
850
uint64_t nKeyedNetGroup;
846
851
};
@@ -857,7 +862,24 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
857
862
858
863
static bool CompareNetGroupKeyed (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
859
864
return a.nKeyedNetGroup < b.nKeyedNetGroup ;
860
- };
865
+ }
866
+
867
+ static bool CompareNodeBlockTime (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
868
+ {
869
+ // There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block.
870
+ if (a.nLastBlockTime != b.nLastBlockTime ) return a.nLastBlockTime < b.nLastBlockTime ;
871
+ if (a.fNetworkNode != b.fNetworkNode ) return b.fNetworkNode ;
872
+ return a.nTimeConnected > b.nTimeConnected ;
873
+ }
874
+
875
+ static bool CompareNodeTXTime (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
876
+ {
877
+ // There is a fall-through here because it is common for a node to have more than a few peers that have not yet relayed txn.
878
+ if (a.nLastTXTime != b.nLastTXTime ) return a.nLastTXTime < b.nLastTXTime ;
879
+ if (a.fRelayTxes != b.fRelayTxes ) return b.fRelayTxes ;
880
+ if (a.fBloomFilter != b.fBloomFilter ) return a.fBloomFilter ;
881
+ return a.nTimeConnected > b.nTimeConnected ;
882
+ }
861
883
862
884
/* * Try to find a connection to evict when the node is full.
863
885
* Extreme care must be taken to avoid opening the node to attacker
@@ -867,7 +889,7 @@ static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvict
867
889
* to forge. In order to partition a node the attacker must be
868
890
* simultaneously better at all of them than honest peers.
869
891
*/
870
- static bool AttemptToEvictConnection (bool fPreferNewConnection ) {
892
+ static bool AttemptToEvictConnection () {
871
893
std::vector<NodeEvictionCandidate> vEvictionCandidates;
872
894
{
873
895
LOCK (cs_vNodes);
@@ -879,7 +901,9 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
879
901
continue ;
880
902
if (node->fDisconnect )
881
903
continue ;
882
- NodeEvictionCandidate candidate = {node->id , node->nTimeConnected , node->nMinPingUsecTime , node->addr , node->nKeyedNetGroup };
904
+ NodeEvictionCandidate candidate = {node->id , node->nTimeConnected , node->nMinPingUsecTime ,
905
+ node->nLastBlockTime , node->nLastTXTime , node->fNetworkNode ,
906
+ node->fRelayTxes , node->pfilter != NULL , node->addr , node->nKeyedNetGroup };
883
907
vEvictionCandidates.push_back (candidate);
884
908
}
885
909
}
@@ -902,6 +926,20 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
902
926
903
927
if (vEvictionCandidates.empty ()) return false ;
904
928
929
+ // Protect 4 nodes that most recently sent us transactions.
930
+ // An attacker cannot manipulate this metric without performing useful work.
931
+ std::sort (vEvictionCandidates.begin (), vEvictionCandidates.end (), CompareNodeTXTime);
932
+ vEvictionCandidates.erase (vEvictionCandidates.end () - std::min (4 , static_cast <int >(vEvictionCandidates.size ())), vEvictionCandidates.end ());
933
+
934
+ if (vEvictionCandidates.empty ()) return false ;
935
+
936
+ // Protect 4 nodes that most recently sent us blocks.
937
+ // An attacker cannot manipulate this metric without performing useful work.
938
+ std::sort (vEvictionCandidates.begin (), vEvictionCandidates.end (), CompareNodeBlockTime);
939
+ vEvictionCandidates.erase (vEvictionCandidates.end () - std::min (4 , static_cast <int >(vEvictionCandidates.size ())), vEvictionCandidates.end ());
940
+
941
+ if (vEvictionCandidates.empty ()) return false ;
942
+
905
943
// Protect the half of the remaining nodes which have been connected the longest.
906
944
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
907
945
std::sort (vEvictionCandidates.begin (), vEvictionCandidates.end (), ReverseCompareNodeTimeConnected);
@@ -930,13 +968,6 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
930
968
// Reduce to the network group with the most connections
931
969
vEvictionCandidates = std::move (mapAddrCounts[naMostConnections]);
932
970
933
- // Do not disconnect peers if there is only one unprotected connection from their network group.
934
- // This step excessively favors netgroup diversity, and should be removed once more protective criteria are established.
935
- if (vEvictionCandidates.size () <= 1 )
936
- // unless we prefer the new connection (for whitelisted peers)
937
- if (!fPreferNewConnection )
938
- return false ;
939
-
940
971
// Disconnect from the network group with the most connections
941
972
NodeId evicted = vEvictionCandidates.front ().id ;
942
973
LOCK (cs_vNodes);
@@ -1002,7 +1033,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
1002
1033
1003
1034
if (nInbound >= nMaxInbound)
1004
1035
{
1005
- if (!AttemptToEvictConnection (whitelisted )) {
1036
+ if (!AttemptToEvictConnection ()) {
1006
1037
// No connection to evict, disconnect the new connection
1007
1038
LogPrint (" net" , " failed to find an eviction candidate - connection dropped (full)\n " );
1008
1039
CloseSocket (hSocket);
@@ -2380,6 +2411,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
2380
2411
fSentAddr = false ;
2381
2412
pfilter = new CBloomFilter ();
2382
2413
timeLastMempoolReq = 0 ;
2414
+ nLastBlockTime = 0 ;
2415
+ nLastTXTime = 0 ;
2383
2416
nPingNonceSent = 0 ;
2384
2417
nPingUsecStart = 0 ;
2385
2418
nPingUsecTime = 0 ;
0 commit comments