|
|
@ -838,6 +838,11 @@ struct NodeEvictionCandidate |
|
|
|
NodeId id; |
|
|
|
NodeId id; |
|
|
|
int64_t nTimeConnected; |
|
|
|
int64_t nTimeConnected; |
|
|
|
int64_t nMinPingUsecTime; |
|
|
|
int64_t nMinPingUsecTime; |
|
|
|
|
|
|
|
int64_t nLastBlockTime; |
|
|
|
|
|
|
|
int64_t nLastTXTime; |
|
|
|
|
|
|
|
bool fNetworkNode; |
|
|
|
|
|
|
|
bool fRelayTxes; |
|
|
|
|
|
|
|
bool fBloomFilter; |
|
|
|
CAddress addr; |
|
|
|
CAddress addr; |
|
|
|
uint64_t nKeyedNetGroup; |
|
|
|
uint64_t nKeyedNetGroup; |
|
|
|
}; |
|
|
|
}; |
|
|
@ -854,7 +859,24 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool CompareNodeBlockTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block.
|
|
|
|
|
|
|
|
if (a.nLastBlockTime != b.nLastBlockTime) return a.nLastBlockTime < b.nLastBlockTime; |
|
|
|
|
|
|
|
if (a.fNetworkNode != b.fNetworkNode) return b.fNetworkNode; |
|
|
|
|
|
|
|
return a.nTimeConnected > b.nTimeConnected; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
if (a.nLastTXTime != b.nLastTXTime) return a.nLastTXTime < b.nLastTXTime; |
|
|
|
|
|
|
|
if (a.fRelayTxes != b.fRelayTxes) return b.fRelayTxes; |
|
|
|
|
|
|
|
if (a.fBloomFilter != b.fBloomFilter) return a.fBloomFilter; |
|
|
|
|
|
|
|
return a.nTimeConnected > b.nTimeConnected; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Try to find a connection to evict when the node is full.
|
|
|
|
/** Try to find a connection to evict when the node is full.
|
|
|
|
* Extreme care must be taken to avoid opening the node to attacker |
|
|
|
* Extreme care must be taken to avoid opening the node to attacker |
|
|
@ -864,7 +886,7 @@ static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvict |
|
|
|
* to forge. In order to partition a node the attacker must be |
|
|
|
* to forge. In order to partition a node the attacker must be |
|
|
|
* simultaneously better at all of them than honest peers. |
|
|
|
* simultaneously better at all of them than honest peers. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
static bool AttemptToEvictConnection(bool fPreferNewConnection) { |
|
|
|
static bool AttemptToEvictConnection() { |
|
|
|
std::vector<NodeEvictionCandidate> vEvictionCandidates; |
|
|
|
std::vector<NodeEvictionCandidate> vEvictionCandidates; |
|
|
|
{ |
|
|
|
{ |
|
|
|
LOCK(cs_vNodes); |
|
|
|
LOCK(cs_vNodes); |
|
|
@ -876,7 +898,9 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
if (node->fDisconnect) |
|
|
|
if (node->fDisconnect) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime, node->addr, node->nKeyedNetGroup}; |
|
|
|
NodeEvictionCandidate candidate = {node->id, node->nTimeConnected, node->nMinPingUsecTime, |
|
|
|
|
|
|
|
node->nLastBlockTime, node->nLastTXTime, node->fNetworkNode, |
|
|
|
|
|
|
|
node->fRelayTxes, node->pfilter != NULL, node->addr, node->nKeyedNetGroup}; |
|
|
|
vEvictionCandidates.push_back(candidate); |
|
|
|
vEvictionCandidates.push_back(candidate); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -899,6 +923,20 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) { |
|
|
|
|
|
|
|
|
|
|
|
if (vEvictionCandidates.empty()) return false; |
|
|
|
if (vEvictionCandidates.empty()) return false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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; |
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); |
|
|
|
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected); |
|
|
@ -999,7 +1037,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { |
|
|
|
|
|
|
|
|
|
|
|
if (nInbound >= nMaxInbound) |
|
|
|
if (nInbound >= nMaxInbound) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!AttemptToEvictConnection(whitelisted)) { |
|
|
|
if (!AttemptToEvictConnection()) { |
|
|
|
// No connection to evict, disconnect the new connection
|
|
|
|
// No connection to evict, disconnect the new connection
|
|
|
|
LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n"); |
|
|
|
LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n"); |
|
|
|
CloseSocket(hSocket); |
|
|
|
CloseSocket(hSocket); |
|
|
@ -2358,6 +2396,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa |
|
|
|
fSentAddr = false; |
|
|
|
fSentAddr = false; |
|
|
|
pfilter = new CBloomFilter(); |
|
|
|
pfilter = new CBloomFilter(); |
|
|
|
timeLastMempoolReq = 0; |
|
|
|
timeLastMempoolReq = 0; |
|
|
|
|
|
|
|
nLastBlockTime = 0; |
|
|
|
|
|
|
|
nLastTXTime = 0; |
|
|
|
nPingNonceSent = 0; |
|
|
|
nPingNonceSent = 0; |
|
|
|
nPingUsecStart = 0; |
|
|
|
nPingUsecStart = 0; |
|
|
|
nPingUsecTime = 0; |
|
|
|
nPingUsecTime = 0; |
|
|
|