Skip to content

consensus: skip BIP30 when this write holds the BIP34 block - #1072

Merged
reardencode merged 3 commits into
masterfrom
consensus/batch-bip30-ancestor
Oct 18, 2026
Merged

reardencode merged 3 commits into
masterfrom
consensus/batch-bip30-ancestor

Conversation

@rearden-grok

@rearden-grok rearden-grok Bot commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

A confirm write that contains the BIP34 block now skips BIP30 on later blocks in that write when this block's hash is the network BIP34 hash. The skip used to read only header_at_height, which moves at Class C, after structural. One-block connect already skipped once that ancestor matched.

A different hash at that height keeps BIP30 on. A height this write does not contain, and that is not confirmed, does the same. Regtest and signet still have no bip34_hash, so BIP30 stays on unless a test sets it. The lookup does not use get_header_plan.

The accept case is a pre-BIP34 coinbase whose scriptSig already pushes the later height, with that output still unspent. One write of heights 1..=3 reaches tip 3. Connecting those blocks one at a time does too. A wrong ancestor hash rejects block 3 both ways.

Local gates: fmt, deny, ast-grep, clippy -D warnings, ci-os-smoke. The workspace suite was not run on this VM.

The ancestry check read only the committed header. Class C updates that
index after structural, so a later block in the same write still ran
BIP30. The skip now reads this write's prepared header, then
header_at_height. A miss, or a different hash, leaves BIP30 on.

@rearden-grok rearden-grok Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

The BIP30 ancestry skip now reads this write's prepared (height, hash) before header_at_height, so a block later in the same write as the BIP34 block can skip the way one-block connect already did. The hash bytes match the header record (block.block_hash().to_byte_array() both at stamp and in Prepared.hash), get_header_plan is not consulted, and a miss leaves BIP30 on. Every production structural_validate_spends call goes through structural_run on the full merged prepared slice (load slices concatenated by append_contiguous); heights in that slice are unique and contiguous from tip+1, and the height <= bip34_height guard runs before lookup, so a later header in the write cannot false-skip. The regtest fixture pushes height 3 on an equal subsidy at heights 1 and 3, the wrong-hash case cannot match hash_at(2), and the limit plus the two is_bip30_repeat exceptions are unchanged. No correctness issue found.

Issue counts by severity

  • bugs: 0
  • suggestions: 0
  • nits: 0

Look at the prepared header only when this write contains the BIP34
height. After that block is confirmed, keep the hash until disconnect.
The header row stays the durable record.
Resolve the header once per write. Remember it for later writes.
Drop it only when the disconnected block is that height or below.

@rearden-grok rearden-grok Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

The BIP34 ancestor is resolved once per confirm write. A write that contains that height uses the prepared header and does not remember it until class_c_commit returns, so a failed write cannot skip BIP30 on the next chain. Later writes reuse the remembered hash. It is dropped only when disconnect removes that height or a block below it; a higher disconnect keeps it. The mutex is not held across header_at_height.

Prepared.hash and the confirmed header record are the same block_hash().to_byte_array() bytes. Merged writes are one contiguous prepared slice (append_contiguous requires last+1). A height gap makes the index check return no in-write header instead of a neighbor. bip30_skipped_for_bip34_ancestry still refuses the skip at and above height 1,983,702, and when the hash does not match. The two BIP30 repeat exceptions are unchanged. get_header_plan is not consulted. Regtest and signet still have no bip34_hash.

Open-time tip shrink goes through the store before this memory exists. Live disconnects all go through disconnect_tip_body. A reorg during an in-flight write can leave a stale local for that write only; Class C still has to land on tip+1, so that local cannot commit onto the replacement chain. The reorg test disconnects the BIP34 block and requires the replacement to reject. No false accept found.

Issue counts by severity

  • bugs: 0
  • suggestions: 0
  • nits: 0

@reardencode
reardencode merged commit 89aa096 into master Oct 18, 2026
18 checks passed
@rearden-grok
rearden-grok Bot deleted the consensus/batch-bip30-ancestor branch October 19, 2026 02:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant