mining: getblocktemplate proposal mode validates like submitblock (#663) - #710
mining: getblocktemplate proposal mode validates like submitblock (#663)#710bkeroack wants to merge 2 commits into
Conversation
478d020 to
9dab8b4
Compare
ebfa8ad to
0f32ae8
Compare
0f32ae8 to
0154907
Compare
|
Force-pushed a fix for the Lint failure. The new test The rest of the stack was rebased onto the amended commit; every branch above this one has a byte-identical tree to before the rebase. |
Proposal mode ran a hand-written loop that reimplemented a subset of `connect_block`. It skipped script verification by its own admission, and with it BIP 68 sequence locks, the block sigop cost and BIP 30 — so a miner asking whether a block would be accepted was told "yes" for blocks `submitblock` then rejected. `connect_block` is pure: it takes `&dyn Store`, reads, and returns a `StoreBatch` its caller writes. Proposal mode calls it and drops the batch, which is exactly Core's `ConnectBlock(fJustCheck=true)` against a throwaway coins view. There is now one implementation of the rules, and `accept_lock` is held for the duration as Core holds `cs_main`. Also: a block already in the index answers `duplicate` / `duplicate-invalid` / `duplicate-inconclusive` rather than `inconclusive-not-best-prevblk`; a non-string `mode` is `-8 Invalid mode` instead of being read as "not proposal" and answered with a template; proposal mode with no string `data` is `-3`; an internal error is `-25`. `check_block` gains Core's ordering — the merkle root is checked before the size limits, so a block wrong in both ways answers `bad-txnmrklroot` — and Core's legacy-sigop ceiling, which fires before any prevout is resolved. Both are in the block-consensus matrix and the live differential. `-blockversion` is read: Core stamps it on the template header under `MineBlocksOnDemand()`, and satd listed the key and ignored it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011H5HUWaezLTYJCZDpgaioL
…nclusive` Core answers a proposal for a known block from `IsValid(BLOCK_VALID_SCRIPTS)`, a validity level pruning does not lower: a pruned block was connected and judged, only its data is gone. satd folded `Pruned` into the "not decided" arm and told a miner the node had never reached a verdict. The match is now exhaustive so a new status cannot fall into either arm unnoticed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011H5HUWaezLTYJCZDpgaioL
0154907 to
c74b91e
Compare
|
Independent review pass — one fix landed on this branch.
Verified against Core v31.1 🤖 Generated with Claude Code |
Closes #663.
Second in a four-PR stack, based on #709. Merge order: #709 → this → #662-tips → #660-bounds.
Proposal mode answered a different question from
submitblockBIP 22's proposal mode exists so a miner can ask "would you accept this block?" before spending hash power on it. Core answers it with
TestBlockValidity, which ends inConnectBlock(fJustCheck=true)against a throwaway coins view — the same code path a real submission takes, with the write discarded.satd answered it with a second, hand-written loop. It said so itself:
Along with script verification it had no BIP 68 sequence-lock check, no block sigop accounting and no BIP 30 test. A miner proposing a block with any of those was told it was fine and then had it rejected on submission.
The loop is gone.
connect_blockis pure — it takes&dyn Store, reads, and hands its caller aStoreBatchto write — so proposal mode calls it and drops the batch. One implementation of the rules, no way for the two answers to drift. It holdsaccept_lockfor the duration, as Core holdscs_main: the tip must not move between the "builds on the tip" test and the connect.Three smaller divergences went with it:
duplicate,duplicate-invalidorduplicate-inconclusivedepending on what it decided last time (rpc/mining.cpp). satd reportedinconclusive-not-best-prevblkfor all of them, because a block already on the chain does not build on the tip.modeanddata. A present-but-non-stringmodewas read as "not proposal", so a caller asking for something satd did not understand was quietly handed a template. It is-8 Invalid modenow, and proposal mode with no stringdatais-3(Core'sRPC_TYPE_ERROR), not-8.-25, Core'sRPC_VERIFY_ERROR.check_blockordering, and a missing sigop gateCore's
CheckBlockrunsCheckMerkleRootfirst, ahead of the size limits, because "all potential-corruption validation must be done before we do any transaction validation": a peer that sent the wrong transactions for a header must not cause that header to be marked invalid. satd tested size first, so a block both oversized and merkle-broken answeredbad-blk-lengthwhere Core answersbad-txnmrklroot.satd also had no equivalent of Core's legacy-sigop ceiling in
CheckBlock— a cheap, context-free gate that fires before any prevout is resolved. It counted sigops only inconnect_block, so a block over the ceiling that also spent nothing resolvable reportedbad-txns-inputs-missingorspent.Both are in
feature_block_consensus.rsand in the live differential against bitcoind.-blockversionCore stamps it on the template header under
MineBlocksOnDemand()— regtest only — which is howmining_basic.pysets up forking scenarios. satd listed the key among its known configuration options and never read it. Applied here on regtest alone;mining_basic.pystill needs thetestdummyversionbits entry before its row can flip.Verification
Three cargo gates green. Perturbation-proved: the duplicate lookup, the
accept_lock, writing the batch instead of dropping it, the merkle ordering, and the legacy-sigop gate — each by a named failing test.New tests: proposal mode enforces BIP 68 and
accept_blockagrees; a proposal for a block on the chain isduplicatewhile an unknown off-tip block staysinconclusive-not-best-prevblk; a proposal moves neither the tip, nor the index, nor the UTXO set, and accepting the same block does; a proposal blocks onaccept_lock; proposal mode andsubmitblockreturn the same verdict over RPC;modeanddatacodes;-blockversion.🤖 Generated with Claude Code
https://claude.ai/code/session_011H5HUWaezLTYJCZDpgaioL