aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Posen <jimpo@coinbase.com>2017-08-22 14:09:04 -0700
committerJim Posen <jimpo@coinbase.com>2017-10-03 10:28:00 -0700
commita2be3b66b56bccc01dfa2fb992515ae56bbedd49 (patch)
tree23d54afb4683e9d27f6106940fbdb949346be700
parentdbc4ae03963014ab4b7957d62ba59dbd8f938c33 (diff)
[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.
-rw-r--r--src/net_processing.cpp37
1 files changed, 28 insertions, 9 deletions
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 7fced41d4f..b5e37722df 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -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
{