Skip to content

Commit 9344dee

Browse files
laanwjcodablock
authored andcommitted
Merge bitcoin#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
1 parent d1a6022 commit 9344dee

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed

qa/rpc-tests/sendheaders.py

+34
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]
@@ -279,6 +290,29 @@ def run_test(self):
279290
inv_node.wait_for_verack()
280291
test_node.wait_for_verack()
281292

293+
self.test_null_locators(test_node)
294+
self.test_nonnull_locators(test_node, inv_node)
295+
296+
def test_null_locators(self, test_node):
297+
tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0])
298+
tip_hash = int(tip["hash"], 16)
299+
300+
self.log.info("Verify getheaders with null locator and valid hashstop returns headers.")
301+
test_node.clear_last_announcement()
302+
test_node.get_headers(locator=[], hashstop=tip_hash)
303+
assert_equal(test_node.check_last_announcement(headers=[tip_hash]), True)
304+
305+
self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.")
306+
block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1)
307+
block.solve()
308+
test_node.send_header_for_blocks([block])
309+
test_node.clear_last_announcement()
310+
test_node.get_headers(locator=[], hashstop=int(block.hash, 16))
311+
test_node.sync_with_ping()
312+
assert_equal(test_node.block_announced, False)
313+
test_node.send_message(msg_block(block))
314+
315+
def test_nonnull_locators(self, test_node, inv_node):
282316
tip = int(self.nodes[0].getbestblockhash(), 16)
283317

284318
# PART 1

src/net_processing.cpp

+9-13
Original file line numberDiff line numberDiff line change
@@ -771,11 +771,13 @@ bool IsBanned(NodeId pnode)
771771

772772
// To prevent fingerprinting attacks, only send blocks/headers outside of the
773773
// active chain if they are no more than a month older (both in time, and in
774-
// best equivalent proof of work) than the best header chain we know about.
775-
static bool StaleBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
774+
// best equivalent proof of work) than the best header chain we know about and
775+
// we fully-validated them at some point.
776+
static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams)
776777
{
777778
AssertLockHeld(cs_main);
778-
return (pindexBestHeader != nullptr) &&
779+
if (chainActive.Contains(pindex)) return true;
780+
return pindex->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) &&
779781
(pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) &&
780782
(GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT);
781783
}
@@ -1084,14 +1086,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
10841086
CValidationState dummy;
10851087
ActivateBestChain(dummy, Params(), a_recent_block);
10861088
}
1087-
if (chainActive.Contains(mi->second)) {
1088-
send = true;
1089-
} else {
1090-
send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) &&
1091-
StaleBlockRequestAllowed(mi->second, consensusParams);
1092-
if (!send) {
1093-
LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
1094-
}
1089+
send = BlockRequestAllowed(mi->second, consensusParams);
1090+
if (!send) {
1091+
LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId());
10951092
}
10961093
}
10971094
// disconnect node in case we have reached the outbound limit for serving historical blocks
@@ -1986,8 +1983,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
19861983
return true;
19871984
pindex = (*mi).second;
19881985

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

0 commit comments

Comments
 (0)