Skip to content

Commit e4bb4a8

Browse files
committed
Merge #8084: Add recently accepted blocks and txn to AttemptToEvictConnection.
6ee7f05 Allow disconnecting a netgroup with only one member in eviction. (Gregory Maxwell) 5d0ca81 Add recently accepted blocks and txn to AttemptToEvictConnection. (Gregory Maxwell)
2 parents 0a64777 + 6ee7f05 commit e4bb4a8

File tree

4 files changed

+61
-17
lines changed

4 files changed

+61
-17
lines changed

src/main.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3449,8 +3449,9 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state
34493449
}
34503450

34513451
/** Store block on disk. If dbp is non-NULL, the file is known to already reside on disk */
3452-
static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp)
3452+
static bool AcceptBlock(const CBlock& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock)
34533453
{
3454+
if (fNewBlock) *fNewBlock = false;
34543455
AssertLockHeld(cs_main);
34553456

34563457
CBlockIndex *pindexDummy = NULL;
@@ -3479,6 +3480,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha
34793480
if (!fHasMoreWork) return true; // Don't process less-work chains
34803481
if (fTooFarAhead) return true; // Block height is too high
34813482
}
3483+
if (fNewBlock) *fNewBlock = true;
34823484

34833485
if ((!CheckBlock(block, state, chainparams.GetConsensus(), GetAdjustedTime())) || !ContextualCheckBlock(block, state, pindex->pprev)) {
34843486
if (state.IsInvalid() && !state.CorruptionPossible()) {
@@ -3526,7 +3528,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned
35263528
}
35273529

35283530

3529-
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp)
3531+
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp)
35303532
{
35313533
{
35323534
LOCK(cs_main);
@@ -3535,9 +3537,11 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c
35353537

35363538
// Store to disk
35373539
CBlockIndex *pindex = NULL;
3538-
bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp);
3540+
bool fNewBlock = false;
3541+
bool ret = AcceptBlock(*pblock, state, chainparams, &pindex, fRequested, dbp, &fNewBlock);
35393542
if (pindex && pfrom) {
35403543
mapBlockSource[pindex->GetBlockHash()] = pfrom->GetId();
3544+
if (fNewBlock) pfrom->nLastBlockTime = GetTime();
35413545
}
35423546
CheckBlockIndex(chainparams.GetConsensus());
35433547
if (!ret)
@@ -4107,7 +4111,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
41074111
if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) {
41084112
LOCK(cs_main);
41094113
CValidationState state;
4110-
if (AcceptBlock(block, state, chainparams, NULL, true, dbp))
4114+
if (AcceptBlock(block, state, chainparams, NULL, true, dbp, NULL))
41114115
nLoaded++;
41124116
if (state.IsError())
41134117
break;
@@ -4140,7 +4144,7 @@ bool LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, CDiskB
41404144
head.ToString());
41414145
LOCK(cs_main);
41424146
CValidationState dummy;
4143-
if (AcceptBlock(block, dummy, chainparams, NULL, true, &it->second))
4147+
if (AcceptBlock(block, dummy, chainparams, NULL, true, &it->second, NULL))
41444148
{
41454149
nLoaded++;
41464150
queue.push_back(block.GetHash());
@@ -5058,6 +5062,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
50585062
RelayTransaction(tx);
50595063
vWorkQueue.push_back(inv.hash);
50605064

5065+
pfrom->nLastTXTime = GetTime();
5066+
50615067
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
50625068
pfrom->id,
50635069
tx.GetHash().ToString(),

src/main.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals);
215215
* @param[out] dbp The already known disk position of pblock, or NULL if not yet stored.
216216
* @return True if state.IsValid()
217217
*/
218-
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, const CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp);
218+
bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp);
219219
/** Check whether enough disk space is available for an incoming block */
220220
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
221221
/** Open a block file (blk?????.dat) */

src/net.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,11 @@ struct NodeEvictionCandidate
841841
NodeId id;
842842
int64_t nTimeConnected;
843843
int64_t nMinPingUsecTime;
844+
int64_t nLastBlockTime;
845+
int64_t nLastTXTime;
846+
bool fNetworkNode;
847+
bool fRelayTxes;
848+
bool fBloomFilter;
844849
CAddress addr;
845850
uint64_t nKeyedNetGroup;
846851
};
@@ -857,7 +862,24 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
857862

858863
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
859864
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+
}
861883

862884
/** Try to find a connection to evict when the node is full.
863885
* Extreme care must be taken to avoid opening the node to attacker
@@ -867,7 +889,7 @@ static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvict
867889
* to forge. In order to partition a node the attacker must be
868890
* simultaneously better at all of them than honest peers.
869891
*/
870-
static bool AttemptToEvictConnection(bool fPreferNewConnection) {
892+
static bool AttemptToEvictConnection() {
871893
std::vector<NodeEvictionCandidate> vEvictionCandidates;
872894
{
873895
LOCK(cs_vNodes);
@@ -879,7 +901,9 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
879901
continue;
880902
if (node->fDisconnect)
881903
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};
883907
vEvictionCandidates.push_back(candidate);
884908
}
885909
}
@@ -902,6 +926,20 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
902926

903927
if (vEvictionCandidates.empty()) return false;
904928

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+
905943
// Protect the half of the remaining nodes which have been connected the longest.
906944
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
907945
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
@@ -930,13 +968,6 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
930968
// Reduce to the network group with the most connections
931969
vEvictionCandidates = std::move(mapAddrCounts[naMostConnections]);
932970

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-
940971
// Disconnect from the network group with the most connections
941972
NodeId evicted = vEvictionCandidates.front().id;
942973
LOCK(cs_vNodes);
@@ -1002,7 +1033,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
10021033

10031034
if (nInbound >= nMaxInbound)
10041035
{
1005-
if (!AttemptToEvictConnection(whitelisted)) {
1036+
if (!AttemptToEvictConnection()) {
10061037
// No connection to evict, disconnect the new connection
10071038
LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n");
10081039
CloseSocket(hSocket);
@@ -2380,6 +2411,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
23802411
fSentAddr = false;
23812412
pfilter = new CBloomFilter();
23822413
timeLastMempoolReq = 0;
2414+
nLastBlockTime = 0;
2415+
nLastTXTime = 0;
23832416
nPingNonceSent = 0;
23842417
nPingUsecStart = 0;
23852418
nPingUsecTime = 0;

src/net.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,11 @@ class CNode
419419

420420
// Last time a "MEMPOOL" request was serviced.
421421
std::atomic<int64_t> timeLastMempoolReq;
422+
423+
// Block and TXN accept times
424+
std::atomic<int64_t> nLastBlockTime;
425+
std::atomic<int64_t> nLastTXTime;
426+
422427
// Ping time measurement:
423428
// The pong reply we're expecting, or 0 if no pong expected.
424429
uint64_t nPingNonceSent;

0 commit comments

Comments
 (0)