@@ -838,6 +838,11 @@ struct NodeEvictionCandidate
838
838
NodeId id;
839
839
int64_t nTimeConnected;
840
840
int64_t nMinPingUsecTime;
841
+ int64_t nLastBlockTime;
842
+ int64_t nLastTXTime;
843
+ bool fNetworkNode ;
844
+ bool fRelayTxes ;
845
+ bool fBloomFilter ;
841
846
CAddress addr;
842
847
uint64_t nKeyedNetGroup;
843
848
};
@@ -854,7 +859,24 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
854
859
855
860
static bool CompareNetGroupKeyed (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
856
861
return a.nKeyedNetGroup < b.nKeyedNetGroup ;
857
- };
862
+ }
863
+
864
+ static bool CompareNodeBlockTime (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
865
+ {
866
+ // There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block.
867
+ if (a.nLastBlockTime != b.nLastBlockTime ) return a.nLastBlockTime < b.nLastBlockTime ;
868
+ if (a.fNetworkNode != b.fNetworkNode ) return b.fNetworkNode ;
869
+ return a.nTimeConnected > b.nTimeConnected ;
870
+ }
871
+
872
+ static bool CompareNodeTXTime (const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
873
+ {
874
+ // 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.
875
+ if (a.nLastTXTime != b.nLastTXTime ) return a.nLastTXTime < b.nLastTXTime ;
876
+ if (a.fRelayTxes != b.fRelayTxes ) return b.fRelayTxes ;
877
+ if (a.fBloomFilter != b.fBloomFilter ) return a.fBloomFilter ;
878
+ return a.nTimeConnected > b.nTimeConnected ;
879
+ }
858
880
859
881
/* * Try to find a connection to evict when the node is full.
860
882
* Extreme care must be taken to avoid opening the node to attacker
@@ -864,7 +886,7 @@ static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvict
864
886
* to forge. In order to partition a node the attacker must be
865
887
* simultaneously better at all of them than honest peers.
866
888
*/
867
- static bool AttemptToEvictConnection (bool fPreferNewConnection ) {
889
+ static bool AttemptToEvictConnection () {
868
890
std::vector<NodeEvictionCandidate> vEvictionCandidates;
869
891
{
870
892
LOCK (cs_vNodes);
@@ -876,7 +898,9 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
876
898
continue ;
877
899
if (node->fDisconnect )
878
900
continue ;
879
- NodeEvictionCandidate candidate = {node->id , node->nTimeConnected , node->nMinPingUsecTime , node->addr , node->nKeyedNetGroup };
901
+ NodeEvictionCandidate candidate = {node->id , node->nTimeConnected , node->nMinPingUsecTime ,
902
+ node->nLastBlockTime , node->nLastTXTime , node->fNetworkNode ,
903
+ node->fRelayTxes , node->pfilter != NULL , node->addr , node->nKeyedNetGroup };
880
904
vEvictionCandidates.push_back (candidate);
881
905
}
882
906
}
@@ -899,6 +923,20 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
899
923
900
924
if (vEvictionCandidates.empty ()) return false ;
901
925
926
+ // Protect 4 nodes that most recently sent us transactions.
927
+ // An attacker cannot manipulate this metric without performing useful work.
928
+ std::sort (vEvictionCandidates.begin (), vEvictionCandidates.end (), CompareNodeTXTime);
929
+ vEvictionCandidates.erase (vEvictionCandidates.end () - std::min (4 , static_cast <int >(vEvictionCandidates.size ())), vEvictionCandidates.end ());
930
+
931
+ if (vEvictionCandidates.empty ()) return false ;
932
+
933
+ // Protect 4 nodes that most recently sent us blocks.
934
+ // An attacker cannot manipulate this metric without performing useful work.
935
+ std::sort (vEvictionCandidates.begin (), vEvictionCandidates.end (), CompareNodeBlockTime);
936
+ vEvictionCandidates.erase (vEvictionCandidates.end () - std::min (4 , static_cast <int >(vEvictionCandidates.size ())), vEvictionCandidates.end ());
937
+
938
+ if (vEvictionCandidates.empty ()) return false ;
939
+
902
940
// Protect the half of the remaining nodes which have been connected the longest.
903
941
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
904
942
std::sort (vEvictionCandidates.begin (), vEvictionCandidates.end (), ReverseCompareNodeTimeConnected);
@@ -999,7 +1037,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
999
1037
1000
1038
if (nInbound >= nMaxInbound)
1001
1039
{
1002
- if (!AttemptToEvictConnection (whitelisted )) {
1040
+ if (!AttemptToEvictConnection ()) {
1003
1041
// No connection to evict, disconnect the new connection
1004
1042
LogPrint (" net" , " failed to find an eviction candidate - connection dropped (full)\n " );
1005
1043
CloseSocket (hSocket);
@@ -2358,6 +2396,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
2358
2396
fSentAddr = false ;
2359
2397
pfilter = new CBloomFilter ();
2360
2398
timeLastMempoolReq = 0 ;
2399
+ nLastBlockTime = 0 ;
2400
+ nLastTXTime = 0 ;
2361
2401
nPingNonceSent = 0 ;
2362
2402
nPingUsecStart = 0 ;
2363
2403
nPingUsecTime = 0 ;
0 commit comments