Skip to content

Instantly share code, notes, and snippets.

@chrisguida
Created June 23, 2026 01:38
Show Gist options
  • Select an option

  • Save chrisguida/783719a296d6d0683fdff8850dcfc8e9 to your computer and use it in GitHub Desktop.

Select an option

Save chrisguida/783719a296d6d0683fdff8850dcfc8e9 to your computer and use it in GitHub Desktop.
BIP54's coinbase nLockTime rule: the case for deferral

The case for deferring BIP54's coinbase nLockTime rule

A position piece. It argues one side — that this particular rule is premature and that cheaper, nLockTime-preserving alternatives exist — and backs the empirical claims against the historical record and recent mainnet data.

BIP54 ("Consensus Cleanup") bundles four soft-fork fixes. Three are uncontroversial — the timewarp fix, the 64-byte-transaction ban, and a per-transaction sigop limit. The fourth is contested: it requires every coinbase transaction's nLockTime to equal block_height − 1 (with nSequence ≠ 0xffffffff, so the locktime is actually enforced). This is the case for leaving that fourth rule out for now.

Thesis

The problem this rule solves is (1) ~53 years away (the first collision with coins at stake is ~2079), (2) already mitigated by deployed software, (3) not a credible attack even in principle, and (4) solvable later by cheaper rules that don't permanently consume the coinbase nLockTime. Spending a scarce consensus change now to burn nLockTime is therefore premature.

1. The real deadline is ~53 years out, and the status quo already handles it

BIP34 (height in the coinbase scriptSig, active since block 227,931 in 2013) already makes every modern coinbase unique. The only residual collisions are with pre-BIP34 coinbases. The first one that can occur at all is at block 1,983,702 (~2046), where Core already resumes BIP30 — but its target coinbase is itself long since spent, so even then there is nothing to overwrite. The first collision whose target still holds unspent coins is ~2079 — about 53 years away, roughly three times Bitcoin's entire existence to date (the network is not yet 20 years old; the genesis block was mined in January 2009).

And it is already handled: Bitcoin Core resumes BIP30 duplicate-txid checking unconditionally from that height (src/validation.cpp, BIP34_IMPLIES_BIP30_LIMIT = 1983702). That is an enforced consensus rule today, implemented everywhere that follows Bitcoin consensus. Nothing is broken; nothing is unhandled. The fix merely deletes a check — it does not patch a hole.

2. It isn't even a credible attack

Proof-of-work makes it nearly impossible. To forge a duplicate txid, the attacker's coinbase must be byte-identical to the ancient one — so the coinbase is frozen and contributes zero nonce space. Only the 80-byte header can roll: the 32-bit nonce, plus version-rolling (~2¹⁶) and ntime (~2¹³) ≈ 2⁶¹ ≈ 2×10¹⁸ hashes. A valid block needs ≈ 5×10²³ hashes, so the odds of mining that specific block with a frozen coinbase are ≈ 1 in 200,000 at today's difficulty — and far worse by 2046. The only escape is to roll an extranonce in a second transaction, which standard mining stacks (Stratum V1 and any likely successor) cannot do; it requires bespoke gear.

And the block subsidy makes it economically self-defeating. A byte-identical duplicate must reproduce the ancient coinbase's outputs exactly — including their total value. The soonest target, block 164,384 (Jan 2012), paid a 170 BTC coinbase (50 BTC subsidy plus a famous ~120 BTC accidental fee); the other candidates paid ~50 BTC. But a coinbase at height 1,983,702 may pay at most the subsidy — about 0.098 BTC by then — plus fees. Reproducing a 50–170 BTC coinbase would therefore require tens to ~170 BTC of fees in that single block, all of it paid to the original 2012 addresses — the attacker gains nothing and forfeits everything. The subsidy schedule alone turns the "attack" into an expensive donation.

3. The only durable motivation — stateless validation — doesn't need nLockTime

Stateless ("utreexo") validators verify every spend with an inclusion proof against the accumulator — but a coinbase spends nothing, so it carries no proof. A validator therefore can't verify that a coinbase's outputs don't already exist as unspent UTXOs — that would need proof-of-absence (non-membership), a whole new category of proof the Merkle-forest accumulator doesn't support. Making coinbase txids unique by construction is the clean fix: the validator never has to run that check. (Whether stateless validation strictly needs this in consensus, versus extra proof machinery, is debatable — but either way the guarantee is cheap.)

And any structural distinguisher provides it without touching nLockTime. Two obvious ones, found in under an hour — better, non-obvious ones almost certainly exist:

  • (a) Require a small marker the ancient coinbases can't have — e.g. an extra zero-value, unspendable output. Pre-BIP34 coinbases don't carry one, so a modern coinbase can never byte-match an ancient one. It need only apply to non-segwit blocks, since segwit coinbases already carry a distinguishing witness commitment.
  • (b) Require coinbase scriptSig above a small floor from the collision height onward. Every collision-capable coinbase with coins still at stake has a scriptSig of ≤ 16 bytes, so > 16 suffices; ≥ 53 covers even the already-spent ones too (verified below). Either way a modern coinbase can't byte-match a short ancient one.

(Note: this deliberately does not propose "just require a witness commitment at those heights." Mandating one could interfere with miners pre-computing the next empty block, so it's avoided.)

Verification that (b) works

The set of collision-capable coinbases is finite and historical. Taking the canonical enumeration from Bitcoin Core PR #12204 (the same analysis that set BIP34_IMPLIES_BIP30_LIMIT) — all 15 pre-BIP34 coinbases whose "indicated height" exceeds their block height — and measuring each coinbase's scriptSig:

block indicated height ≈ collision date scriptSig bytes coinbase outputs
209,920 209,921 ~2012 (past) 32 spent
176,684 490,897 ~2017 (past) 4 spent
164,384 1,983,702 ~2046 8 spent
169,895 3,708,179 ~2079 9 spent
170,307 3,709,183 ~2079 9 spent
171,896 3,712,990 ~2079 9 spent
172,069 3,713,413 ~2079 8 partial — 0.0006 BTC unspent
172,357 3,714,082 ~2079 9 partial — 0.004 BTC unspent
172,428 3,714,265 ~2079 9 spent
183,669 3,761,471 ~2080 4 spent
196,988 4,275,806 ~2090 16 partial — 47.5 BTC unspent
174,151 5,208,854 ~2107 52 spent
201,577 5,327,833 ~2109 16 partial — 45.6 BTC unspent
206,039 7,299,941 ~2147 10 spent
206,354 7,299,941 ~2147 10 spent

Two of the indicated heights are already in the past — 209,921 (2012, when BIP30 was actively enforced) and 490,897 (2017, mined with a different coinbase) — both passed without incident; everything else is decades out. A fully-spent coinbase can't trigger a BIP30 violation (there's nothing to overwrite), and 11 of the 15 are fully spent — including both the soonest collision (block 164,384, ~2046) and the longest scriptSig (block 174,151, 52 bytes, ~2107). So the only BIP30-relevant coinbases are the four partial rows, and their scriptSigs top out at just 16 bytes — the rule could be as loose as len(coinbase scriptSig) > 16 and still cover every coinbase that could do harm (the soonest at ~2079 with 0.0006 BTC; the two sizable ones, ~46–47 BTC, at ~2090 and ~2109; §2's subsidy argument blocks those regardless).

Going all the way to ≥ 53 — covering the spent 52-byte outlier too, which doesn't even collide until ~2107 — buys only one extra thing: avoiding a duplicate txid of an already-spent coinbase. That is not a consensus problem (Bitcoin already contains duplicate txids, from the two 2010 BIP30 pairs) and is in any case vanishingly unlikely. The threshold is effectively a free parameter anywhere from ~17 to 53; the fix works at any of them. The cost is negligible: coinbase scriptSigs today are already mostly well over 53 bytes (median ~93), and the scriptSig is where the extranonce lives — so a minimum just guarantees more usable rolling room, and miners have decades to ensure they meet it.

4. nLockTime is the wrong field to spend

nLockTime is the most hash-efficient extranonce slot in the whole block: it's the last field serialized, so it can be rolled by re-running only the final SHA-256 compression against a cached midstate — valuable for low-latency rolling on ASICs/MCUs. Committing block height there removes that option for every miner, permanently. And the benefit unique to the nLockTime approach — reading the height back without parsing Script — is a convenience, not a requirement.

Note too that BIP54 constrains two coinbase fields — nLockTime and nSequence. Leaving both unpinned keeps any future cleanup simpler (see the timing note below).

A note on timing: the ASIC "timebomb"

This is the deepest reason to leave nLockTime alone, and it takes a few steps.

Today the mining ASIC is deliberately "dumb." There is a division of labor: a separate controller — nowadays an FPGA on the mining board, because a general-purpose CPU already can't keep pace — builds the coinbase, computes the merkle root, and feeds the chip finished 80-byte headers; the chip just grinds the 32-bit header nonce billions of times per second. It knows nothing about the coinbase, the transactions, or the block height. That ignorance is a feature — it keeps block construction in the miner's own hardware, not the hash chips. (That this job already needs an FPGA rather than a CPU is itself a sign of the pressure described next.)

As ASICs get faster, the extranonce must move into the chip. The ~4-billion header-nonce space is exhausted in a fraction of a second, so the controller must keep feeding fresh, extranonce-rolled work — and eventually can't keep up. The foreseen fix is to let the ASIC roll the extranonce itself, in silicon. To do that cheaply the extranonce wants to sit at the very end of the coinbase, where SHA-256's cached midstate means rolling it re-hashes almost nothing — i.e. exactly in nLockTime. A free nLockTime also lets the chip touch only those last bytes while leaving the rest of the coinbase fixed.

BIP54 takes that slot, with two consequences. First, the in-chip extranonce gets shoved into a dummy OP_RETURN near the end of the tx — a hack — or it disturbs more of the coinbase and so threatens split generation rewards (pools paying many miners directly through multiple coinbase outputs, rather than custodying the reward and paying out later). Second, and worse: because nLockTime must now equal height − 1 and is the last field, any in-chip rolling at the end of the tx forces the chip to emit the correct height — so the ASIC must become height-aware. Today it isn't.

A height-aware ASIC is a timebomb. Once the block height is baked into the silicon, a manufacturer — or a state coercing one — can program the chips to stop mining after a chosen height or date: a supply-chain kill switch that could brick a large share of global hashrate at a pre-set moment. And once nLockTime = height − 1 is consensus, it can't be undone without a hardfork. (The risk isn't wholly new — the chip already rolls the header timestamp, so it already has a partial clock — but BIP54 deepens it and couples it to coinbase construction.)

So the asymmetry runs against acting now. The uniqueness fix is a soft fork that can be deployed closer to the deadline at leisure. Burning nLockTime (and nSequence) is the move that's hard to walk back: if the extranonce/timebomb pressure materializes, undoing a nLockTime = height − 1 rule would itself take a hardfork — far harder than the soft fork we're deferring. Better not to spend two coinbase fields now for a non-urgent problem a later soft fork can fix.

Anticipated objections (and why they don't change the conclusion)

  • "Do it once now, inside the already-coordinated cleanup fork, to avoid a second soft fork." — The uniqueness fix is itself a small soft fork, deployable closer to the deadline; a second soft fork is cheap. Permanently pinning two coinbase fields today for a non-urgent, non-attack problem is the expensive option — and walking back a mistaken nLockTime burn would need a hardfork.
  • "Stateless validation wants a hard invariant, not a probabilistic (PoW) assumption." — Agreed — which is exactly why the §3 alternatives give a hard structural invariant too. They simply provide it without touching nLockTime.
  • "A positive height commitment is cleaner than a negative distinguisher." — Perhaps, but "cleaner" is not "necessary." None of the concrete benefits requires the commitment to live in nLockTime.

Conclusion

There is no clock forcing this decision now. The problem is remote, already mitigated, not practically exploitable (proof-of-work and the subsidy schedule both block it), and the only lasting motivation (stateless validation) is satisfied by cheaper, narrower rules — at least one verified against the complete historical record and costing essentially nothing in practice. If two obviously-better fixes turn up in under an hour, better ones will turn up with deliberate design. Solve it later, with the smallest rule that does the job — and don't burn nLockTime.


References & reproduction

@darosior

darosior commented Jun 30, 2026

Copy link
Copy Markdown

I don't think this case is compelling.

First of all, the piece is getting the motivation for the BIP 54 coinbase rule wrong. The attack you suggest is already prevented by the BIP 30 check. As BIP 54 states, the motivation is precisely to not have to re-enable the BIP 30 check:

Resuming bip-0030 validation would unnecessarily increase block validation overhead and preclude alternative full node designs (such as BIP 182 Utreexo). Enforcing that new coinbase transactions are different from the early bip-0034 violations makes it possible to get rid of bip-0030 validation forever.

So the "real deadline" is not ~53 years out, but 20. This means we still have a bunch of time, but changing Bitcoin's consensus rules is a long and tedious process (for good reasons). Therefore it's good to be prepared well in advance to avoid rushing potentially less well-considered changes closer to the deadline. (I understand this is contrary to the author's own endeavours --i believe we just have an irreconcilable view of what is good for Bitcoin.)

Now, there is an infinite number of possible mitigations: any rule that forces future coinbase transactions to differ from all past ones would technically prevent a duplicate. But some mitigations are preferable to others. The first mitigation suggested in section 3 of this post was considered early on when work on the Consensus Cleanup resumed. Using nLockTime was later preferred because it is a less roundabout fix that also has the side-benefit of making the block height available from the coinbase transaction without parsing Script, and enables timelock enforcement that makes reasoning about validity easier. The second mitigation suggested in section 3 is a spectacularly bad way to design a consensus rule, even by the author's standards.

Again, all of this was already discussed repeatedly on development mailing lists (1, 2) and in the rationale section of the BIP, which this post fails to engage with:

The nLockTime field of transactions is a natural place to store a block height and is currently unused in coinbase transactions. Using it to enforce that new coinbase transactions differ from early bip-0034 violations also allows applications to recover the block height without having to parse Script. Leveraging the existing timelock mechanism makes the check self-contained: the same coinbase transaction cannot have been valid in a previous block12. This simplifies both reasoning and client implementation, since the bip-0030 check can be skipped entirely past Consensus Cleanup activation, regardless of the bip-0034 activation status13. One person raised the concern that the nLockTime field would be an ideal extranonce for ASIC controllers if such controllers ever became a bottleneck in mining operations. Others replied that the same benefits could be achieved by using a dummy output instead, should that ever become necessary. The authors believe the benefits of using nLockTime to differentiate coinbase transactions outweigh the theoretical cost of making it unavailable for extranonce rolling by ASIC controllers.

Finally, the "ASIC timebomb" section is where the author's LLM really loses it. The separate FGPA was needed to optimize covert ASICBoost, which not only we should not make protocol decisions to accommodate, but has also been superseded nowadays by overt ASICBoost, which rolls headers' nVersion field. The following reasoning is (flawed in other ways and) based on the erroneous assumption that the hashing chip only has 32 bits of space to grind. In practice modern miners use another 16 bits in the nVersion field (as per BIP 320), and sometimes even another 7 bits in nTime. BIP 323 reserves 24 bits of grinding spaces in nVersion, making it so the hashing chip can try 72 quadrillions values before requiring the controller to send in a new header. This means the single hashing chip would need to reach a hashrate of 72 PH/s to even exhaust the searching space in a whole second.

This section then goes from confused to ludicrous, when it suggests that making ASICs aware of the block height (which they are already through the controller, which must respect BIP 34 in crafting the coinbase transaction) would somehow facilitate State coercion of Bitcoin miners.

To conclude, the BIP 54 coinbase change is not an urgent, existential fix to Bitcoin. Nobody, to my knowledge, ever claimed it was. But from a low time preference perspective, it is nonetheless a net-positive one: at the short term cost of having to update mining pool software we get the long term benefit of never having to re-enable unnecessary overhead in block validation, as well as precluding alternative full node designs. And it makes sense to include it as part of a bundle of similar long term fixes, under BIP 54. In the future, please take Bitcoin consensus protocol discussions to open development forums, rather than in a Github gist, or on the social media account of the promoter of a contentious fork who blocks any dissenting voice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment