Skip to content

consensus: catch a BIP30 duplicate inside one confirm batch - #1059

Merged
reardencode merged 1 commit into
reardencode:masterfrom
smolcars:consensus/bip30-same-batch
Oct 18, 2026
Merged

reardencode merged 1 commit into
reardencode:masterfrom
smolcars:consensus/bip30-same-batch

Conversation

@niteshbalusu11

Copy link
Copy Markdown
Contributor

Summary

Two blocks with the same coinbase txid in one confirm batch were both accepted. The BIP30 lookup only sees connected blocks, and the first block of the batch is not connected yet. Core rejects the second block.

This can't really happen on any live network anymore: BIP34 makes every coinbase unique, and mainnet's only pre-BIP34 duplicates are the two grandfathered blocks. It's only reachable on regtest with -testactivationheight=bip34@N. The risk is small, but it's a Core mismatch and the fix is small, so we might as well have it.

 reject_bip30_unspent_overwrite (per block, in batch order)
   mainnet repeat / BIP34 skips      (unchanged, still first)
+  txid created earlier in this batch with an output not yet spent -> bad-txns-BIP30
   store lookup of connected creates (unchanged)
+  remember this block's txids and spendable outputs for the rest of the batch

Evidence

  • Before: bip30_duplicate_coinbase_in_one_batch fails: the batch returns Ok for the duplicate.
    After: it passes, and the same blocks sent in two batches are still rejected (control case).

Merge Danger

Door: two-way

Blast Radius: consensus. Only a real duplicate txid can match, which needs BIP34 off.

🤖 Generated with Claude Code

The BIP30 check looked up earlier creates of a txid in the store, which only
sees connected blocks. An earlier block of the same write batch is not
connected yet, so a duplicate coinbase one block later in the same batch was
accepted. Core rejects it.

Structural validation now remembers each checked block's txids and spendable
outputs for the rest of the batch, and a later duplicate with an output not
yet spent in the batch is rejected through the same rule. The mainnet repeat
exceptions and BIP34 skips still run first.

Only reachable below BIP34, for example regtest with
-testactivationheight=bip34@N.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@reardencode

Copy link
Copy Markdown
Owner

Damn. I'm gonna dig deep on this, could sworn grok and I had covered this one before.

@niteshbalusu11

Copy link
Copy Markdown
Contributor Author

Verified again against real Bitcoin Core 31.1 on regtest with -testactivationheight=bip34@1000: block 1 is accepted, block 2 reusing block 1's exact coinbase is rejected with bad-txns-BIP30, and a control block 2 with a different coinbase is accepted. rbitcoin rejects that block 2 when it arrives in a later batch (that case was covered), but accepted it when both blocks landed in the same confirm batch, so the bug is real.

@reardencode

Copy link
Copy Markdown
Owner

gonna have grok do a full audit of our consensus rules and tests around batches. The last 2 actual consensus bugs we've fixed have been things that were correct 1 block at a time but not in batches. bleh.

@niteshbalusu11

Copy link
Copy Markdown
Contributor Author

I found one more. Opening PR 😬

@rearden-grok rearden-grok Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Summary

The batch map closes the real hole: a txid created by an earlier block in the same confirm write is checked before the store lookup, which cannot see those creates yet. The rule matches Core ConnectBlock — HaveCoin before this block's spends, unspendable outputs are not coins, BIP34-ancestry and the two mainnet repeats still skip, and a fully spent prior is allowed. pending_spent on this path is the structural set, filled only after the check, and begin_batch clears the maps on every write, so this block cannot hide a coin and a failed batch cannot poison the next one.

Issue counts by severity

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

@reardencode
reardencode merged commit 7b34ecf into reardencode:master Oct 18, 2026
18 checks passed
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.

2 participants