OP_SIGHASH: Early discussion, exploration, use-cases

OP_SIGHASH: Early Discussion, Exploration, Use-Cases

This thread is for discussion and early exploration of a potential OP_SIGHASH opcode, following on from a conversation in the BCH Builders Telegram Group.

This is not yet a CHIP — just an open discussion to explore whether this idea is useful enough to develop further (if someone else wants to take lead and develop to CHIP standards, please feel free to!)


The Idea

An opcode that returns the transaction’s signature hash (sighash) without actually checking a signature:

<sighash_type> OP_SIGHASH → <32-byte sighash digest>

Think of it as OP_CHECKSIG minus the signature verification — just the hashing part.

When you combine it with OP_CAT and OP_CHECKDATASIGVERIFY, you get a nice pattern:

<sighash_type> OP_SIGHASH   // Get the tx sighash
<custom_data> OP_CAT        // Append our own data
<sig> <pubkey> OP_CHECKDATASIGVERIFY  // Verify one signature over the whole thing

This creates a signed message of the form sighash || custom_data — effectively letting you sign both the transaction and arbitrary extra data with a single signature.


Why Bother?

The Unlocking Script Malleability Problem

Here’s a concrete problem this could solve.

Say you want to store some data on-chain inside an unlocking script (see this example). Your script might look like:

// Redeem Script
OP_DROP          // Drop the data
<pubkey> OP_CHECKSIG

// Unlocking Script  
<signature>
<data>           // e.g. 1500 bytes of arbitrary data

The problem: since unlocking bytecode isn’t included in the sighash (a signature can’t sign itself), that <data> can be modified by anyone relaying the transaction. A malicious node could swap it out entirely, and the transaction would still be valid.

Current Workaround: Two Signatures

Today, you might protect against this by adding a second signature over the data using OP_CHECKDATASIG:

// Redeem Script
OP_DUP
<pubkey> OP_CHECKDATASIGVERIFY  // Verify data signature
OP_DROP
<pubkey> OP_CHECKSIG             // Verify tx signature

// Unlocking Script
<tx_signature>      // 65 bytes
<data_signature>    // 64 bytes (extra cost!)
<data>

This works but allows some forms of replay attack and you’re paying for two signatures (~128 bytes) when conceptually you only need one.

(NOTE: You could probably protect against replay by signing against the Outpoint).

With OP_SIGHASH: One Signature

// Redeem Script
<0x41> OP_SIGHASH               // Get sighash
<data> OP_CAT                   // Append the data
<sig> <pubkey> OP_CHECKDATASIGVERIFY  // One signature covers both

// Unlocking Script
<signature>         // ~64 bytes (that's it!)
<data>

You save ~64 bytes, and the data is cryptographically bound to this exact transaction — not just signed separately.


Other Potential Use Cases

I haven’t fully explored these, but here are some directions that might be interesting:

Covenants with State Commitments

An oracle could authorize specific state transitions while being bound to the exact transaction:

<state_hash>
<0x41> OP_SIGHASH
OP_CAT                          // sighash || state_hash
<oracle_sig> <oracle_pubkey> OP_CHECKDATASIGVERIFY

Multi-Party Approval of Shared Data

Multiple parties could each sign off on the same data being included:

<shared_data>
<0x41> OP_SIGHASH
OP_SWAP OP_CAT                  // sighash || shared_data
OP_DUP
<sig_A> <pubkey_A> OP_CHECKDATASIGVERIFY
<sig_B> <pubkey_B> OP_CHECKDATASIGVERIFY

What Else?

I’d be keen to hear if anyone sees other applications for this pattern.


Alternatives Considered

Manual SigHash Construction (with Loops)

Once we have looping operations (scheduled for May 2026), you could theoretically construct the sighash manually using existing opcodes. @bitcoincashautist has an example for BTC’s OP_CTV that weighs in at ~153 bytes. OP_CTV is probably more expensive to implement than most SigHash algos, but it would still be of considerable size.

TxV5 Detached Signatures

The TxV5 CHIP proposes “detached signatures” which would provide comprehensive malleability protections. If that gets implemented, it might be the better long-term solution for many use cases.

That said, OP_SIGHASH is a much smaller change and might still be useful even with TxV5.


Open Questions

Some things I haven’t thought through yet:

  1. Are there security considerations I’m missing? Exposing the sighash seems safe since it’s already used in signature verification, but I’d appreciate more eyes on this.

  2. Should this support additional sighash types? @bitcoincashautist mentioned looking at something like SIGHASH_ANYPREVOUT. Is that worth bundling with this, or is it a separate discussion?

  3. What should the opcode cost be? It’s doing the same hashing work as OP_CHECKSIG minus the signature verification.

  4. Are there better use cases than malleability protection? The sighash || custom_data pattern feels like it could enable things I haven’t thought of.

4 Likes

Use for Script-implemented signing schemes like Quantumroot or Lamport. Some have tried implementing secp256r1, too. Sighash generation is common to all of them, and right now they each have to roll their own. Having OP_SIGHASH would let these new signatures keep the standard sighashes, making implementations easier and would help preserve the current standard with these novel signing methods.

Adding SIGHASH_NOINPUT would enable alternative ways to implement contracts like the XMR-BCH swap. The simple swap probably wouldn’t benefit since introspection is compact for 1-in-1-out, but extending it to 2 inputs or outputs would already see benefits.

3 Likes

Interesting idea! The most compelling usecase for an OP_SIGHASH indeed seems to be the “Script-implemented signing schemes”:

Using one OP_CHECKDATASIGVERIFY to cover both the transaction and arbitrary extra data with one signature is clever! It’s addressing a third-party malleability vector where unlocking-script data isn’t committed by the standard sighash. So indeed a general solution to this class of problem would be the detached signatures mentioned in the " Alternatives Considered":


using the ‘153 bytes’ emulation estimate, it seems like an OP_SIGHASH would offer a ~10% bytesize savings for quantumroot on post quantum signatures. If we expect quantumroot to be a significant driver of network usage then this could be significant! It would also simplify implementation and auditability

3 Likes

Note that OP_TXHASH would be a more fitting name: it’s literally a hash generated from pieces of the TX it is invoked in. This has been proposed under such name in BTC circles, I didn’t dig into tech details but I imagine their variant would be more complicated since it would have to be implemented as a Taproot SF.


I want to point out one fundamental difference between OP_CHECKSIG and building a preimage using TX introspection opcodes and then verifying a signature for it with OP_CHECKDATASIG. The introspection method typically means that the template is committed in the locking script and signer has no option to change what he’ll sign for. With OP_CHECKSIG the signer has the option to change what he’s signing for at the time of signing (by choosing the sighash byte) as opposed to the choice being made at the time of UTXO creation (rigid script that generates the “message” to be signed).

Now, with loops and functions, we can actually make contracts that give the signer unlimited flexibility to choose his sighash. The contract could be designed to allow the signer to push his own function body as unlocking data. The locking script will execute it and produce a sighash which will be verified against committed key. So, if we already have this functionality, why would we need a dedicated OP_TXHASH?

  1. Familiarity: it would use exact same “recipes” as current CHECKSIG
  2. Standardization: node & other libraries already have the standard recipes in their codebase, so could be easily extended for this
  3. Size: invoking a standardized recipe would use 2 bytes (sighash byte, opcode). Rolling your own can be ~150 bytes.
2 Likes

Technically, I probably agree with this. In practice though, I wonder if it might be confusing as devs might confuse it with the actual tx outpoint hash (e.g. in LibAuth this is named outpointTransactionHash).

I feel like OP_SIGHASH might be more intuitive for that reason - but I wouldn’t object to OP_TXHASH either.

The introspection method typically means that the template is committed in the locking script and signer has no option to change what he’ll sign for. With OP_CHECKSIG the signer has the option to change what he’s signing for at the time of signing (by choosing the sighash byte) as opposed to the choice being made at the time of UTXO creation (rigid script that generates the “message” to be signed).

One other point on this because, at first, I thought this may’ve been a new use-case that we’d be unlocking. But, then I realized we can probably achieve the same thing currently by just checking the last byte of whatever sig was published. e.g.

<sig> // sig on the stack
<64> OP_SPLIT OP_NIP // get the last byte of that sig (byte 65)
<desiredSigHashType> OP_EQUALVERIFY // make sure that the signature is of a specific sighash type

OP_SIGHASH/OP_TXHASH might make this more clear/explicit though.

1 Like

Yeah, on a second thought I feel the same, OP_SIGHASH immediately communicates what it’s about, even though it could be used for things other than signing.

I actually used this in some contract where I wanted to force the signer to use SIGHASH_ALL, can’t remember which one. But, I was talking about the opposite – let the signer have maximum optionality in setting conditions in which the signed UTXO may be spent, and that’s actually achieved with functions and checkdatasig.

Like this:

// Authenticate the verifier function body
OP_DUP OP_TOALTSTACK
<owner_key.public_key>
OP_CHECKDATASIGVERIFY
// Execute it
OP_FROMALTSTACK <0> OP_DEFINE <0> OP_INVOKE

Example BitAuthIDE template.

This script is simply saying: this UTXO is owned by this pubkey, and the owner can set whatever spending conditions he wants by signing for a particular function body and its produced result.

1 Like

@bitcoincashautist is right on the money with his suggestion. In my custom secp256k1 and p256 signature verification scripts, I use ~600bytes to create a sighash to verify against. It’s also not perfect due to the limitation of not being able to find commit-less NFTs hidden behind FTs of the same category (that can’t be worked around unless another op_code is introduced).

OP_SIGHASH would make that unnecessary.

4 Likes