Skip to content

consensus: script corpus checked against Core v31.1 VerifyScript - #1057

Merged
reardencode merged 11 commits into
masterfrom
consensus/script-corpus
Oct 18, 2026
Merged

reardencode merged 11 commits into
masterfrom
consensus/script-corpus

Conversation

@rearden-grok

@rearden-grok rearden-grok Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A script corpus runs beside Bitcoin Core's JSON. Both the shipped verifier and Core v31.1 VerifyScript (the pinned submodule, not crates.io bitcoinconsensus) accept or reject every row the same way. secp256k1 stays the shared library.

Unknown tapleaf versions succeed. DISCOURAGE_UPGRADABLE_TAPROOT_VERSION is standardness, and confirm never sets it. Unknown witness programs still follow DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM.

Core files these rows would append to

The checked-in rows use Core's existing JSON dialects. A later Core PR can append them. No runner change is required for the rows that are already in script_tests.json form ([scriptSig, scriptPubKey, flags, result] or the witness form).

  • script_tests.json — the script, flag, and witness rows, including stack, numbers, conditionals, checksig, CLTV/CSV, P2SH, witness programs, tapleaf, and the fast-path credits.
  • tx_valid.json / tx_invalid.json — no new transaction-structure rows. Spend shape stays inside the script-test credit/spend builder.
  • sighash.json — the past-end SIGHASH_SINGLE rows (sighash-single-past-legacy, sighash-single-past-v0, sighash-single-past-tap). script_tests cannot express a transaction with zero outputs, so those rows carry "spend": { "n_outputs": 0 } here and belong in sighash.json upstream.

Test plan

  • cargo test -p rbitcoin-consensus --lib -- script_corpus_rows_match_our_interpreter core_script_tests_all_rows
  • cargo test --manifest-path script-oracle/Cargo.toml -- script_corpus_matches_core_verify_script
  • Script-crate line coverage of script:: (Core JSON plus this corpus). ./scripts/coverage.sh is not run on this VM.
  • GitHub qc, test, windows, macos, and coverage

The corpus uses Core script_tests.json row form. Our interpreter runs every row, and script-oracle compiles VerifyScript from the v31.1 submodule. The first row is an empty scriptSig and OP_1; both engines accept it.
Legacy, witness v0, and tapscript rows cover the 10,000-byte script cap, the 520-byte element cap (including an unexecuted IF), the 201-opcode cap, truncated PUSHDATA, and IF/ELSE/ENDIF structure. OP_VERIF and OP_VERNOTIF reject even when unexecuted. Tapscript OP_SUCCESS still accepts before the initial stack and element checks. script-oracle assembles the rows with the Core script assembler and checks them with v31.1 VerifyScript.
Each legacy disabled opcode rejects inside an unexecuted IF on legacy and witness v0, and accepts as OP_SUCCESS in tapscript. OP_RESERVED, OP_VER, the reserved opcodes, and both ends of the 187..254 success range reject when executed outside tapscript. Opcode 0xff and OP_RETURN reject only when executed. An OP_SUCCESS byte hidden inside a push does not succeed.
Arithmetic accepts a sum that does not fit in 4 bytes and rejects that value when a later numeric opcode decodes it. A 5-byte input rejects. 0 and negative zero match under NUMEQUAL and differ under EQUAL. WITHIN is inclusive of the low bound. Tapscript MINIMALIF rejects every IF argument except an empty vector or a single 0x01. Witness v0 does that only when the flag is on. A bare script ignores the flag. Both engines agree, including Bitcoin Core v31.1 VerifyScript.
Core script_tests.json already accepts and rejects each listed stack opcode.
This corpus names the empty-stack pop, 2ROT at five versus six items, and
FROMALTSTACK on an empty altstack.
Witness v0 NULLDUMMY follows SCRIPT_VERIFY_NULLDUMMY. Core v31.1
VerifyScript accepts a non-empty dummy when that flag is off.
Confirm still sets the flag on every segwit block.
P2SH execution, witness program lengths, unknown tapleaf versions, and
the P2PKH, P2WPKH, and key-path verifiers. Typed checks use the same
credit and spend as the corpus rows.
Unknown tapleaf versions follow DISCOURAGE_UPGRADABLE_TAPROOT_VERSION.
DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM still rejects unknown witness
programs and does not reject an unknown leaf. The script corpus and
Core's JSON take the remaining consensus lines. secp256k1 stays out
of scope.
Unknown leaf versions succeed. That discourage bit is standardness, and
confirm never sets it. Unknown witness programs still follow the
witness-program flag.
Master moved TxPrecompute and shared prevouts into this crate. The
coverage tests that build a wire prevout on an owned job follow that.
@rearden-grok
rearden-grok Bot force-pushed the consensus/script-corpus branch from 5ec17c4 to c96038e Compare October 17, 2026 23:10
@reardencode
reardencode merged commit 159d9c5 into 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.

1 participant