BIP-110

Response

On BlockSlop and late BIP-110 upgrades

You can leave two nodes with different histories after a messy late upgrade. That still does not mean BIP-110's rules are broken.

BIP110.orgSource: BlockSlop by Dathon Pwn

The sequence they describe is real enough: run without BIP-110, accept a block old rules allow, later flip on enforcement on the same datadir, and default startup may keep trusting the tip you already saved. Fresh BIP-110 validation still rejects those blocks. So does rebuilding chainstate. What you have is a late-upgrade footgun and a marketing label that overshoots.

What holds up

The reported violations die in ConnectBlock(). Fresh nodes reject them. -reindex-chainstate drops them. The soft fork checks themselves are fine.

Where the write-up goes too far

Two nodes only disagree because one of them never ran the new rules on that block. That is a datadir / deployment problem. Calling it a consensus bug makes people hear “the soft fork is broken.”

What they found

Nodes do not re-check the whole chain every time you restart. They keep a UTXO set and validity flags so restarts stay usable. BIP-110's checks sit inside ConnectBlock(). When that path runs—mandatory signaling, output size, script limits—the violations in the report get rejected.

Late upgrade is the awkward case. You connect a block under old rules, shut down, then start an enforcing build on the same files. Default verification only digs a little (check level 3, last few blocks). It does not reconnect deep history. So the tip you saved earlier can stick around, while a brand-new BIP-110 node looking at the same block for the first time would throw it out.

Your saved chainstate came from software that was not enforcing BIP-110 yet. The new binary trusts a lot of that work on boot.

When the checks actually run
ConnectBlock() rejects the tested BIP-110 violations.-reindex-chainstate reconnects history and drops them.A fresh enforcing node never accepts them.
Default boot does almost no reconnect
DEFAULT_CHECKBLOCKS = 6DEFAULT_CHECKLEVEL = 3# level 3 disconnects recent blocks# only level 4 reconnects via ConnectBlock()

The “consensus bug” wording

People hear “consensus bug” and think nodes that claim the same rules still disagree after those rules run. Here one node never applied BIP-110 to the contested block. The report even spells out the limits:

  • No mainnet block is claimed to violate BIP-110.
  • A node already enforcing does not accept a new violating block.
  • Nodes that enforced BIP-110 from the first relevant block are outside the scenario.
  • Rebuilding chainstate rejects the test blocks the same way fresh validation does.

So we are talking about upgrade packaging and how operators share a datadir across rule sets. The tree even has a comment in ConnectBlock() about late soft-fork upgrades being hard, and points at SegWit's special guard as one way that particular deployment handled it. Missing a second special guard is an engineering choice. It does not mean the RDTS rules fail when they run.

Markers and fail-closed startup would still be useful. Say that plainly. Do not sell a datadir footgun as proof the soft fork is unsound.

Same mess the other way

Run BIP-110, then late-switch to Core or pre-RDTS Knots on the same datadir, and you can get the reverse headache. After activation, an RDTS body reject leaves failure flags in the block index. Non-RDTS software may need reconsiderblock before it will follow that branch again. During mandatory signaling, rejected headers often never made it into the index, so switching clients and restarting is usually enough.

Node-in-a-box docs already walk people through that recovery. We do not call Bitcoin Core consensus-buggy because it will not magically clean every mixed-rules datadir for you. Apply the same standard when someone upgrades into BIP-110.

Start9 already treated it that way. They shipped packaging recovery both ways: Core or pre-RDTS Knots → BIP-110 Knots (knots PR), and BIP-110 Knots → Core or pre-RDTS (core PR). Same recovery module across the Core and Knots packages, pruned included. That is what a deployment fix looks like when people take the footgun seriously without rewriting the whole argument as a consensus failure.

Hot-swapping clients with different rules on one chainstate is on the operator. Both directions need care. Neither one is special evidence against BIP-110.

How much has to go wrong

You need all four of these. Drop one and the headline stops matching the setup.

What has to line up for the late-upgrade gap
ConditionWhat has to happenWhy that is a tall order
1. No enforcement yetYour node accepts a block while running software that is not enforcing BIP-110.If you already enforced BIP-110, you never get here.
2. Block would fail under BIP-110Old rules accept the block; the BIP-110 phase in force at that height would reject it.Nobody has pointed at a mainnet block like this, and the rules only matter once mandatory signaling or active RDTS kicks in.
3. You follow it and save itThat block has enough work that the non-enforcing node makes it part of its tip history and writes chainstate.A bad branch that never becomes your tip does not stick you there.
4. You enable BIP-110 on the same datadirLater you start an enforcing client against that datadir and skip a reconnect/rebuild.Restarting a node that was already enforcing is a different story.

Even then, you only sit on the retained bad ancestor if that history is still the most-worked chain your node will follow. A dead-end minority branch, or a cleaner branch with more work, means you are not stuck the way the short summary suggests.

The “N + 1 chains” picture needs even more: several sufficiently-worked BIP-110-invalid histories, different groups upgrading only after different bad ancestors are already saved, and those groups keeping build on those histories while claiming they enforce. That is people hanging on to pre-enforcement history, not a silent flaw in how BIP-110 defines validity.

Tools you already have

Bitcoin has always assumed you may need to fix local state after a software change or a bad history. Default boot does not re-prove the entire chain. The knobs for this already exist.

If you mixed rule sets or distrust the tip
SituationToolWhat you get
You may have late-upgraded on an unpruned node-reindex-chainstate -assumevalid=0Reconnects history under the rules you run now
You know a retained block is badinvalidateblock <hash>Marks it failed and gets you off that branch
You left BIP-110 after a body-level RDTS rejectreconsiderblock <hash>Clears failure flags so non-RDTS software can follow that branch
Pruned node and the old blocks are gonefull -reindex / redownloadOnly option when partial checks cannot prove the history

The report itself says check level 4 and chainstate reindex catch the retained ancestor on an unpruned node. That matches how Bitcoin handles other integrity problems: you ask for the heavy check. The node will not always reindex for you when something looks off.

On Start9, the appliance layer is already wrapping those transitions. Their recovery module covers switching into BIP-110 and switching out, including pruned installs, instead of leaving every operator to invent the sequence by hand.

Auto-detecting an unsafe late upgrade and refusing to start would still be nicer upstream. Having to run a repair command, or having your packager run it for you, does not turn this into a consensus failure.

What would still help

Packaging fixes like Start9's already make the operator path safer. Upstream can still do more. SegWit shipped a marker and a startup scan for a related upgrade hazard. A BIP-110 provenance bit, a ruleset note on the chainstate tip, or a hard refuse (“this datadir crossed an applicable height without enforcement; rebuild”) would make late upgrades harder to botch on plain bitcoind too. Ship that before any BIP-110 rule applies on mainnet and enforcing nodes can mark history as they go, so later transitions get caught early.

Treat it as release engineering and operator safety. Add regression tests for non-enforcing → enforcing datadir reuse. Keep the wording honest. Nice safeguard. Oversight worth fixing. Still a long way from “BIP-110 consensus is broken.”

Bottom line

The checks work when they run. Fresh nodes enforce them. Rebuilds enforce them. The path in the report is mixing non-enforcing and enforcing software on one datadir, then expecting a normal boot to rewrite history for you. Packagers are already shipping recovery for both directions. Improve the upgrade path more if you want. Do not dress that up as a consensus bug.