Skip to content

Commit 1f4375f

Browse files
committed
Merge #11580: Do not send (potentially) invalid headers in response to getheaders
725b79a [test] Verify node doesn't send headers that haven't been fully validated (Russell Yanofsky) 3788a84 Do not send (potentially) invalid headers in response to getheaders (Matt Corallo) Pull request description: Nowhere else in the protocol do we send headers which are for blocks we have not fully validated except in response to getheaders messages with a null locator. On my public node I have not seen any such request (whether for an invalid block or not) in at least two years of debug.log output, indicating that this should have minimal impact. Tree-SHA512: c1f6e0cdcdfb78ea577d555f9b3ceb1b4b60eff4f6cf313bfd8b576c9562d797bea73abc23f7011f249ae36dd539c715f3d20487ac03ace60e84e1b77c0c1e1a
2 parents 5e3f5e4 + 725b79a commit 1f4375f

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed

src/net_processing.cpp

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -781,11 +781,13 @@ void Misbehaving(NodeId pnode, int howmuch)
781781

782782
// To prevent fingerprinting attacks, only send blocks/headers outside of the
783783
// active chain if they are no more than a month older (both in time, and in
784-
// best equivalent proof of work) than the best header chain we know about.
785-
static bool StaleBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
784+
// best equivalent proof of work) than the best header chain we know about and
785+
// we fully-validated them at some point.
786+
static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
786787
{
787788
AssertLockHeld(cs_main);
788-
return (pindexBestHeader != nullptr) &&
789+
if (chainActive.Contains(pindex)) return true;
790+
return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
789791
(pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
790792
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
791793
}
@@ -1074,14 +1076,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
10741076
CValidationState dummy;
10751077
ActivateBestChain(dummy, Params(), a_recent_block);
10761078
}
1077-
if (chainActive.Contains(mi->second)) {
1078-
send = true;
1079-
} else {
1080-
send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) &&
1081-
StaleBlockRequestAllowed(mi->second, consensusParams);
1082-
if (!send) {
1083-
LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
1084-
}
1079+
send = BlockRequestAllowed(mi->second, consensusParams);
1080+
if (!send) {
1081+
LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
10851082
}
10861083
}
10871084
// disconnect node in case we have reached the outbound limit for serving historical blocks
@@ -2034,8 +2031,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
20342031
return true;
20352032
pindex = (*mi).second;
20362033

2037-
if (!chainActive.Contains(pindex) &&
2038-
!StaleBlockRequestAllowed(pindex, chainparams.GetConsensus())) {
2034+
if (!BlockRequestAllowed(pindex, chainparams.GetConsensus())) {
20392035
LogPrintf("%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId());
20402036
return true;
20412037
}

test/functional/sendheaders.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@
1010
receive inv's (omitted from testing description below, this is our control).
1111
Second node is used for creating reorgs.
1212
13+
test_null_locators
14+
==================
15+
16+
Sends two getheaders requests with null locator values. First request's hashstop
17+
value refers to validated block, while second request's hashstop value refers to
18+
a block which hasn't been validated. Verifies only the first request returns
19+
headers.
20+
21+
test_nonnull_locators
22+
=====================
23+
1324
Part 1: No headers announcements before "sendheaders"
1425
a. node mines a block [expect: inv]
1526
send getdata for the block [expect: block]
@@ -221,6 +232,29 @@ def run_test(self):
221232
inv_node.sync_with_ping()
222233
test_node.sync_with_ping()
223234

235+
self.test_null_locators(test_node)
236+
self.test_nonnull_locators(test_node, inv_node)
237+
238+
def test_null_locators(self, test_node):
239+
tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0])
240+
tip_hash = int(tip["hash"], 16)
241+
242+
self.log.info("Verify getheaders with null locator and valid hashstop returns headers.")
243+
test_node.clear_last_announcement()
244+
test_node.get_headers(locator=[], hashstop=tip_hash)
245+
assert_equal(test_node.check_last_announcement(headers=[tip_hash]), True)
246+
247+
self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.")
248+
block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1)
249+
block.solve()
250+
test_node.send_header_for_blocks([block])
251+
test_node.clear_last_announcement()
252+
test_node.get_headers(locator=[], hashstop=int(block.hash, 16))
253+
test_node.sync_with_ping()
254+
assert_equal(test_node.block_announced, False)
255+
test_node.send_message(msg_block(block))
256+
257+
def test_nonnull_locators(self, test_node, inv_node):
224258
tip = int(self.nodes[0].getbestblockhash(), 16)
225259

226260
# PART 1

0 commit comments

Comments
 (0)