Skip to content

Commit 5d0ca81

Browse files
committed
Add recently accepted blocks and txn to AttemptToEvictConnection.
This protects any not-already-protected peers who were the most recent four to relay transactions and most recent four to send blocks to us.
1 parent 32b7294 commit 5d0ca81

File tree

4 files changed

+61
-10
lines changed

4 files changed

+61
-10
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());
@@ -5040,6 +5044,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
50405044
RelayTransaction(tx);
50415045
vWorkQueue.push_back(inv.hash);
50425046

5047+
pfrom->nLastTXTime = GetTime();
5048+
50435049
LogPrint("mempool", "AcceptToMemoryPool: peer=%d: accepted %s (poolsz %u txn, %u kB)\n",
50445050
pfrom->id,
50455051
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 & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,11 @@ struct NodeEvictionCandidate
838838
NodeId id;
839839
int64_t nTimeConnected;
840840
int64_t nMinPingUsecTime;
841+
int64_t nLastBlockTime;
842+
int64_t nLastTXTime;
843+
bool fNetworkNode;
844+
bool fRelayTxes;
845+
bool fBloomFilter;
841846
CAddress addr;
842847
uint64_t nKeyedNetGroup;
843848
};
@@ -854,7 +859,24 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate &a, cons
854859

855860
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
856861
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+
}
858880

859881
/** Try to find a connection to evict when the node is full.
860882
* Extreme care must be taken to avoid opening the node to attacker
@@ -864,7 +886,7 @@ static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvict
864886
* to forge. In order to partition a node the attacker must be
865887
* simultaneously better at all of them than honest peers.
866888
*/
867-
static bool AttemptToEvictConnection(bool fPreferNewConnection) {
889+
static bool AttemptToEvictConnection() {
868890
std::vector<NodeEvictionCandidate> vEvictionCandidates;
869891
{
870892
LOCK(cs_vNodes);
@@ -876,7 +898,9 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
876898
continue;
877899
if (node->fDisconnect)
878900
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};
880904
vEvictionCandidates.push_back(candidate);
881905
}
882906
}
@@ -899,6 +923,20 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
899923

900924
if (vEvictionCandidates.empty()) return false;
901925

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+
902940
// Protect the half of the remaining nodes which have been connected the longest.
903941
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
904942
std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), ReverseCompareNodeTimeConnected);
@@ -999,7 +1037,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) {
9991037

10001038
if (nInbound >= nMaxInbound)
10011039
{
1002-
if (!AttemptToEvictConnection(whitelisted)) {
1040+
if (!AttemptToEvictConnection()) {
10031041
// No connection to evict, disconnect the new connection
10041042
LogPrint("net", "failed to find an eviction candidate - connection dropped (full)\n");
10051043
CloseSocket(hSocket);
@@ -2358,6 +2396,8 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa
23582396
fSentAddr = false;
23592397
pfilter = new CBloomFilter();
23602398
timeLastMempoolReq = 0;
2399+
nLastBlockTime = 0;
2400+
nLastTXTime = 0;
23612401
nPingNonceSent = 0;
23622402
nPingUsecStart = 0;
23632403
nPingUsecTime = 0;

src/net.h

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

417417
// Last time a "MEMPOOL" request was serviced.
418418
std::atomic<int64_t> timeLastMempoolReq;
419+
420+
// Block and TXN accept times
421+
std::atomic<int64_t> nLastBlockTime;
422+
std::atomic<int64_t> nLastTXTime;
423+
419424
// Ping time measurement:
420425
// The pong reply we're expecting, or 0 if no pong expected.
421426
uint64_t nPingNonceSent;

0 commit comments

Comments
 (0)