Skip to content

Commit a2be3b6

Browse files
author
Jim Posen
committed
[net] Ignore getheaders requests for very old side blocks
Sending a getheaders message with an empty locator and a stop hash is a request for a single header by hash. The node will respond with headers for blocks not in the main chain as well as those in the main chain. To avoid fingerprinting, the node should, however, ignore requests for headers on side branches that are too old.
1 parent dbc4ae0 commit a2be3b6

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

src/net_processing.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUAR
6161

6262
static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
6363

64+
/// Age after which a stale block will no longer be served if requested as
65+
/// protection against fingerprinting. Set to one month, denominated in seconds.
66+
static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
67+
68+
/// Age after which a block is considered historical for purposes of rate
69+
/// limiting block relay. Set to one week, denominated in seconds.
70+
static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
71+
6472
// Internal stuff
6573
namespace {
6674
/** Number of nodes with fSyncStarted. */
@@ -706,6 +714,17 @@ void Misbehaving(NodeId pnode, int howmuch)
706714
// blockchain -> download logic notification
707715
//
708716

717+
// To prevent fingerprinting attacks, only send blocks/headers outside of the
718+
// active chain if they are no more than a month older (both in time, and in
719+
// best equivalent proof of work) than the best header chain we know about.
720+
static bool StaleBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
721+
{
722+
AssertLockHeld(cs_main);
723+
return (pindexBestHeader != nullptr) &&
724+
(pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
725+
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
726+
}
727+
709728
PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {
710729
// Initialize global variables that cannot be constructed at startup.
711730
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
@@ -983,22 +1002,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
9831002
if (chainActive.Contains(mi->second)) {
9841003
send = true;
9851004
} else {
986-
static const int nOneMonth = 30 * 24 * 60 * 60;
987-
// To prevent fingerprinting attacks, only send blocks outside of the active
988-
// chain if they are valid, and no more than a month older (both in time, and in
989-
// best equivalent proof of work) than the best header chain we know about.
990-
send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
991-
(pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) &&
992-
(GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth);
1005+
send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) &&
1006+
StaleBlockRequestAllowed(mi->second, consensusParams);
9931007
if (!send) {
9941008
LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
9951009
}
9961010
}
9971011
}
9981012
// disconnect node in case we have reached the outbound limit for serving historical blocks
9991013
// never disconnect whitelisted nodes
1000-
static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical
1001-
if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
1014+
if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
10021015
{
10031016
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
10041017

@@ -1723,6 +1736,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
17231736
if (mi == mapBlockIndex.end())
17241737
return true;
17251738
pindex = (*mi).second;
1739+
1740+
if (!chainActive.Contains(pindex) &&
1741+
!StaleBlockRequestAllowed(pindex, chainparams.GetConsensus())) {
1742+
LogPrintf("%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId());
1743+
return true;
1744+
}
17261745
}
17271746
else
17281747
{

0 commit comments

Comments
 (0)