[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.
This commit is contained in:
parent
dbc4ae0396
commit
a2be3b66b5
1 changed files with 28 additions and 9 deletions
|
@ -61,6 +61,14 @@ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUAR
|
|||
|
||||
static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]
|
||||
|
||||
/// Age after which a stale block will no longer be served if requested as
|
||||
/// protection against fingerprinting. Set to one month, denominated in seconds.
|
||||
static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60;
|
||||
|
||||
/// Age after which a block is considered historical for purposes of rate
|
||||
/// limiting block relay. Set to one week, denominated in seconds.
|
||||
static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60;
|
||||
|
||||
// Internal stuff
|
||||
namespace {
|
||||
/** Number of nodes with fSyncStarted. */
|
||||
|
@ -706,6 +714,17 @@ void Misbehaving(NodeId pnode, int howmuch)
|
|||
// blockchain -> download logic notification
|
||||
//
|
||||
|
||||
// To prevent fingerprinting attacks, only send blocks/headers outside of the
|
||||
// active chain if they are no more than a month older (both in time, and in
|
||||
// best equivalent proof of work) than the best header chain we know about.
|
||||
static bool StaleBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
return (pindexBestHeader != nullptr) &&
|
||||
(pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
|
||||
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
|
||||
}
|
||||
|
||||
PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) {
|
||||
// Initialize global variables that cannot be constructed at startup.
|
||||
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001));
|
||||
|
@ -983,13 +1002,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
|
|||
if (chainActive.Contains(mi->second)) {
|
||||
send = true;
|
||||
} else {
|
||||
static const int nOneMonth = 30 * 24 * 60 * 60;
|
||||
// To prevent fingerprinting attacks, only send blocks outside of the active
|
||||
// chain if they are valid, and no more than a month older (both in time, and in
|
||||
// best equivalent proof of work) than the best header chain we know about.
|
||||
send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
|
||||
(pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) &&
|
||||
(GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth);
|
||||
send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) &&
|
||||
StaleBlockRequestAllowed(mi->second, consensusParams);
|
||||
if (!send) {
|
||||
LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
|
||||
}
|
||||
|
@ -997,8 +1011,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
|
|||
}
|
||||
// disconnect node in case we have reached the outbound limit for serving historical blocks
|
||||
// never disconnect whitelisted nodes
|
||||
static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical
|
||||
if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
|
||||
if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
|
||||
{
|
||||
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
|
||||
|
||||
|
@ -1723,6 +1736,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
|
|||
if (mi == mapBlockIndex.end())
|
||||
return true;
|
||||
pindex = (*mi).second;
|
||||
|
||||
if (!chainActive.Contains(pindex) &&
|
||||
!StaleBlockRequestAllowed(pindex, chainparams.GetConsensus())) {
|
||||
LogPrintf("%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue