Merge bitcoin/bitcoin#35351: net: Disallow invalid HeadersSyncState due to lagging clock

ff3e2e4ebd net: Trigger process abort when behind start block MTP (Hodlinator)
1883cecb4d test: Characterize lagging-clock headers presync (Hodlinator)

Pull request description:

  ### Problem

  Headers presync computes `m_max_commitments` from the elapsed time since the chain-start MTP plus `MAX_FUTURE_BLOCK_TIME`. When the local system clock is more than `MAX_FUTURE_BLOCK_TIME` behind the chain-start MTP, that elapsed value is negative, but it is used in arithmetic assigned to the unsigned commitment cap. This can turn the intended zero bound into a large cap, letting low-work headers presync continue instead of aborting when a reasonable commitment cap would have been exceeded.

  ### Fix

  Instead of allowing an invalid `HeadersSyncState` object to be created, emit an error and **abort the node process**.

  Typically, the node will detect that the system clock is set too far in the past when comparing it to the chain tip during chain state loading and shut down before we start syncing headers. So in practice this is very unlikely to make a difference (might be possible if the system clock jumps backwards after we loaded the chain state).

  #### Commits

  * Add functional and unit characterization tests [pinning the current behavior](https://github.com/bitcoin/bitcoin/pull/35260).
  * The fix, along with corresponding test changes.

  ---

  Replaces #35208 which was clamping `m_max_commitments` to zero and then letting the `HeadersSyncState` consume headers until the block height either reached the the next `commitment_period` point and aborted, or reached the minimum work threshold and succeeded (possible when having been offline for >144 blocks).

ACKs for top commit:
  l0rinc:
    diff and code review ACK ff3e2e4ebd
  sedited:
    ACK ff3e2e4ebd
  mzumsande:
    Code Review ACK [ff3e2e4](ff3e2e4ebd)

Tree-SHA512: bdd82fd0609309aa4bea026db1b607ae856c53403ec01b2511fa2ccae9db4ff1bb9e39523b446583c09ae53823275b8a603050d9090b61fabb84fab35e458f28
This commit is contained in:
merge-script
2026-08-31 18:35:19 +02:00
5 changed files with 54 additions and 5 deletions

View File

@@ -15,12 +15,14 @@ from test_framework.messages import (
)
from test_framework.blocktools import (
MAX_FUTURE_BLOCK_TIME,
NORMAL_GBT_REQUEST_PARAMS,
create_block,
)
from test_framework.util import assert_equal
import re
import time
NODE1_BLOCKS_REQUIRED = 15
@@ -144,6 +146,16 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework):
# getpeerinfo should show a sync in progress
assert_equal(node.getpeerinfo()[0]['presynced_headers'], 2000)
self.log.info("Test whether a lagging clock aborts low-work headers sync")
node.disconnect_p2ps()
node.setmocktime(node.getblockheader(node.getblockhash(0))['mediantime'] - MAX_FUTURE_BLOCK_TIME - 1)
p2p = node.add_p2p_connection(P2PInterface())
p2p.send_without_ping(headers_message)
node.wait_until_stopped(expect_error=True, expected_ret_code=[-6, # Unix
3, # Windows native
0xC0000409], # Windows cross builds
expected_stderr=re.compile("Failure when attempting to initiate headers sync: System clock"))
def test_large_reorgs_can_succeed(self):
self.log.info("Test that a 2000+ block reorg, starting from a point that is more than 2000 blocks before a locator entry, can succeed")