consensus: banned-attacker rule is output-only, but its stated threat model is a resurrected UTXO #76
Labels
No labels
enhancement
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
SubGeniusFinance/Offerings-to-Cthulhu#76
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Params().BannedAttackerScripts()is enforced in two places, and both check onlyvout:src/main.cpp:893—AcceptToMemoryPool, rejects a tx that pays a banned scriptsrc/main.cpp:2356—ConnectBlock, same check over every tx in the blockGood: it is enforced at the block level too, so it is consensus and not merely mempool policy.
The gap is what the rule is for. From
chainparams.cpp:A resurrected pre-attack UTXO does not arrive by someone paying one of those scripts. It arrives because a reorg restores outputs that already existed on the original chain. Nothing creates a new
voutin that path, so neither check fires — and once those coins are in the UTXO set they are freely spendable, because nothing inspects thescriptPubKeyof a spent prevout.So the rule as written prevents new value being sent to the 533,983-OFF wallets, but does not freeze value that appears at them — which is the one scenario the comment names.
Severity: low
The named scenario is already blocked twice over, independently of this rule:
MAX_REORG_DEPTH = 100inActivateBestChainrejects any reorg deeper than 100 blocks past the active tipThe attack is ~870K blocks behind the recovered tip, so reaching it would require defeating both. This is a defense-in-depth mismatch between what the comment promises and what the code does — not a live exploitable path.
Suggested fix
An input-side check in
ConnectBlock, where the spent coins are already in hand via theCCoinsViewCache: for each non-coinbase input, look up the prevout'sscriptPubKeyand reject if it is in the banned set. Roughly a dozen lines next to the existingvoutloop.Two caveats worth deciding on before anyone writes it:
How this was found
Read during a bug sweep of the OFF-specific consensus surface, 2026-08-21. Also checked and found correct in the same pass: the banlist persistence in #17, the per-peer sync height in #18, LWMA-3 including its bootstrap walk-back, the mid-grind emergency-valve template rebuild in
miner.cpp, and theMAX_REORG_DEPTHcommon-ancestor walk.