Robin Linus’s How CTV+CSFS improves BitVM bridges raises a specific problem: how to bind inputA so it can only be spent together with a specific inputB. AJ Towns pointed out at /t/1591/8 that the original construction binds to scriptSig bytes rather than the outpoint itself. I posted a sketch at /t/1591/29 — using the sha_prevouts field of the sighash preimage as the binding anchor. This post gives the full witness layout, script reasoning.
What was done
The script (~196 bytes) hardcodes B’s outpoint (36 bytes of raw data — a transparent tweak factor). The witness supplies A’s outpoint. The script computes SHA256(witness_A_outpoint || B_outpoint_hardcoded) on stack and verifies it equals the sha_prevouts segment extracted from the chunked-witness preimage. Same-signature binding — one Schnorr signature satisfying both OP_CHECKSIG and OP_CHECKSIGFROMSTACK — ties the witness-supplied preimage to the actual transaction.
The witness is split along preimage field boundaries (each item < 80 bytes for standardness). The script uses OP_CAT to reassemble the 212-byte preimage, computes the TapSighash tagged hash on stack, then runs CSFS + CHECKSIG against the same 64-byte signature.
OP_CAT and OP_CHECKSIGFROMSTACK are both active on Bitcoin Inquisition signet, where the experiment runs.
Attack: spending A + C (substituting a different UTXO for B) — rejected by testmempoolaccept with Script failed an OP_EQUALVERIFY operation.
What this means in practice
For BitVM bridges: if B (the challenge anchor) is burned by a challenger, B is no longer in the UTXO set, and any transaction listing B as an input is invalid at the consensus level. The operator cannot withdraw A without proving B’s continued existence. More broadly, sighash preimage decomposition is a general method — sha_prevouts is one of several fields that can serve as a binding anchor.
This is brilliant engineering for the Script era, Aaron. But manually stitching 212-byte preimages together with OP_CAT just to bind two inputs feels like using flint to start a fire inside a nuclear reactor.
While you guys are doing stack gymnastics on Inquisition and praying for a 2030 soft fork, we are enforcing this exact topology directly in Simplicity (.simf) with a single mathematical assertion.
In our TUSM architecture, we don’t hack preimages to simulate binding. The state is locked into the Taproot address itself via pure SHA-256 streaming. Absolute L1 bivalence. Pass or Trap.
Let OP_CAT rest in peace and come forge in the steel with us. The covenant is the consensus.
Taproot-native output binding via sighash preimage decomposition — does this replace CTV?
sha_outputs (bytes 138–170 of the BIP-341 SigMsg preimage) is SHA256(serialized outputs) — the same digest OP_CHECKTEMPLATEVERIFY commits to. Reading this field with the same chunked-witness pattern as the OP gives output binding semantically equivalent to CTV.
What was done
The tapscript hardcodes the 32-byte expected sha_outputs. The witness supplies the preimage in chunks split along the field boundary:
Each chunk stays under 80 bytes for standardness. The script OP_EQUALVERIFYs the sha_outputs chunk against the hardcoded value, locks one chunk’s size with OP_SIZE, and lets same-signature binding anchor the rest — any shifted reassembly would require a SHA256 collision against the hardcoded value.
The script then reassembles the full 212-byte preimage with OP_CAT, computes the TapSighash tagged hash on-stack, and runs OP_CHECKSIGFROMSTACK + OP_CHECKSIG against the same 64-byte Schnorr signature. If one signature passes both checks, the witness preimage must equal the real sighash — so the sha_outputs chunk that was verified is the real sha_outputs of this transaction.
OP_CAT and OP_CHECKSIGFROMSTACK are both active on Bitcoin Inquisition signet, where the experiment runs.
Spend A with the bound output (positive case) — confirmed: 2f345180... (block 300379). Witness item 5 of that spend is the hardcoded sha_outputs value, byte-for-byte.
Attack: spend A with a different output (substituting amount or scriptPubKey) — rejected by testmempoolaccept with Script failed an OP_EQUALVERIFY operation. The substitution changes the real sha_outputs, the hardcoded check fails, and the spend never makes it into a block.
What this means — does this replace CTV?
For output-binding semantics, yes: this construction enforces what CTV enforces. For deployment economics, no: the witness carries five preimage chunks plus the signature, and the script is larger than a single OP_CTV invocation. The point isn’t size or elegance — it’s that the capability sits on already-activated opcodes, alongside the OP’s sha_prevouts binding:
OP (Post #1): sha_prevouts — input binding (which UTXOs are spent, beyond CTV)
this reply: sha_outputs — output binding (where the funds go, CTV-equivalent)
Same technique, different sighash field, dual covenant semantics.