perf(compiler): shrink OP_PUSH_TX preimage binding 760→428 bytes (Any-S construction) - #161
Open
brendan-elas wants to merge 1 commit into
Open
perf(compiler): shrink OP_PUSH_TX preimage binding 760→428 bytes (Any-S construction)#161brendan-elas wants to merge 1 commit into
brendan-elas wants to merge 1 commit into
Conversation
…-S construction) Replace the k=2/d=1 on-chain signature derivation in the BUG-100 preimage-binding blob with the Any-S construction: k=1 (r = Gx, no k-inverse multiply), signing key d = 2^248·Gx^-1 mod n so the r·d addend is built on-stack in 6 bytes, split/cat byte-reversal loops, and DER-encoding s directly from its minimal script-number encoding. The mod-n/low-S normalisation stays branchless (one OP_MOD + one OP_MUL), so analyzer execution-path structure is unchanged. Binding semantics are identical: the DER signature is still derived on-chain from the pushed preimage and checked with OP_CHECKSIGVERIFY against a fixed public key (now P = d·G, d public — binding never depended on key secrecy), so a decoupled preimage still fails. The blob remains a fixed constant across all seven tiers with the sighash flag patchable at the unique 01<flag>7e marker and a 7e21<pubkey>ad tail. Every covenant method shrinks by 332 bytes and drops one 256-bit OP_MUL and one OP_MOD per spend path. All tier pins, conformance goldens, analyzer reports, sdk-vertical artifacts, SDK fixture artifacts (re-lowered from their embedded ANF), and the Lean model's blob constant are regenerated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Shrinks the fixed OP_PUSH_TX preimage-binding construction (the BUG-100 fix blob emitted into every covenant method) from 760 to 428 bytes (−44%), and removes one 256-bit
OP_MULand oneOP_MODfrom every covenant spend path.The on-chain signature derivation switches from the
k=2, d=1construction to the Any-S construction (the OP_PUSH_TX signing-context technique from the BitcoinSXstd.sxLib#signCtxprimitive):kr = Gx, sos = z + r·d mod nneeds nok⁻¹multiply and no 33-bytek⁻¹push;r's DER field needs no0x00sign padd2²⁴⁸·Gx⁻¹ mod n(pubkey =02b405d7…83b0) —r·d ≡ 2²⁴⁸ (mod n), so the addend is built on-stack in 6 script bytes (OP_0 <0x1f> OP_NUM2BIN OP_1 OP_CAT) instead of a 33-byte constant31×(OP_1 OP_SPLIT)then31×(OP_SWAP OP_CAT))sNUM2BIN(32)→ reverse → strip zerosOP_DUP OP_0NOTEQUAL OP_SPLITdegrading to empty splits for shortsOP_MULs' = s + (s>n/2)·(n−2s)— oneOP_MOD+ oneOP_MUL, no extra constantsThe mod/low-S normalisation is deliberately kept branchless (no
OP_IF), so the static analyzers' execution-path structure is unchanged across all tiers.Security
Binding semantics are identical to the current construction: the DER signature is still derived on-chain from the pushed preimage and checked with
OP_CHECKSIGVERIFYagainst a fixed public key, sohash256(preimage)must equal the real tx sighash — a forged/decoupled continuation preimage (the BUG-100 exploit) still fails.dbeing public is irrelevant, as before: the binding never depended on key secrecy, only on the signature being derived from the preimage on-chain. Low-S uses the standard(r, n−s)malleation; the pubkey constant was re-derived off-chain from the curve parameters ((2²⁴⁸·Gx⁻¹ mod n)·G).The blob remains a fixed, opaque
raw_bytesconstant pinned byte-identically across all seven tiers, with the sighash flag patchable at the unchanged, still-unique byte-aligned01<flag>7emarker and a7e 21<pubkey> adtail.Blast radius
oppushtx-codegen.ts; legacy construction kept asemitCheckPreimageBindingLegacyfor reference/A-B tests) and the pinned hex in the go/rust/python/ruby/java/zig tiers (incl. rust's pubkey flag-anchor and go/zig tail constants).conformance/tests,sdk-output(generate-inputs + SDK tools),sdk-vertical(generate +--update-golden), analyzer reports (generate-goldens), decompiler templates.lowerToStack→ peephole →emit) and confirmed byte-exact.checkPreimageBindingBytesconstant + PipelineGolden.StackLowerTest/SighashCodegenTest, zig e2e/sighash/C27, TS stack-frame W4), plus 4 new deterministic DER/low-S edge-context tests (both malleation branches; 31-byte and ~2⁻¹⁶ 30-bytes).Test suites run (all locally, Apple Silicon)
pnpm vitest run— 9,048 passed (includes the interpreter e2e binding suite: genuine-preimage ACCEPT, forged/zeroed REJECT, 60-sighash sweep, 4 ground edge contexts; fuzzer differentials)conformance/runner72/72 ·sdk-output59 cases × 7 SDKs clean ·sdk-vertical39/39 × 7 tiers · analyzer 24 pass / 0 failcargo test843/0, Zigzig build test754/754, Javagradle test680/680 (+ runar-java SDK build), Python 1160/1160 (+ runar-py 737), Ruby rake 66/66 files, runar-rs 575/0, runar-zig SDK greenRUNAR_LEAN_STRICT=1 scripts/lean-verify.sh— goldenLoad / roundtrip / pipelineGolden gates pass (57/72 byte-exact, pending buckets unchanged)Future work (out of scope here)
d = Gx⁻¹ mod nvariant (s = z + 1, saving 5 more bytes) — kept the proven 2²⁴⁸ constant for this PR.