Emulate Any SIGHASH Flag Without a Fork
By Programming It in a Smart Contract
We develop a novel approach to emulate any SIGHASH flag, by simply coding the logic in a smart contract. It requires no protocol change and is thus much more practical and flexible than adding a hardcoded flag through a fork, every time a new use case is conceived.
A SIGHASH flag decides which part of a transaction is signed by the signature. Specifically, it controls which of the following 10 items are covered by the signature.
There are three basic flags: SIGHASH_ALL, SIGHASH_NONE,and SIGHASH_SINGLE. There is also a modifier flag SIGHASH_ANYONECANPAY, resulting six combinations.
There have been proposals to add more flags, to customize signing various parts of a transaction not possible under existing flags. One example is listed below:
However, each of them has to be hardcoded in the node software and thus requires a potentially contentious fork.
Emulate Any SIGHASH Flag
We provide a framework to emulate arbitrary SIGHASH flag. The new SIGHASH flag can simply be added in the form of a smart contract and thus requires no upgrade to Bitcoin at all. Overall, it works in three steps:
- Fetch the current sighash using OP_PUSH_TX
- Modify/mask the sighash per the new flag semantics
- Check the signature against the new sighash, using ECDSA signature algorithm.
As an example, we implement SIGHASH_ANYPREVOUT.
SIGHASH_ANYPREVOUT (previously named SIGHASH_NOINPUT) in BIP-118 excludes the identifier for the UTXO being spent from the signature. A transaction signed with it is not linked to a specific UTXO and can spend any UTXO from addresses with the same public key (or spending conditions).
This can be used, for example, when a user wants to authorize a third-party application to spend her coins. She can pre-sign with SIGHASH_ANYPREVOUT and the application can reuse the signature when spending in her absence, again and again.
The following contract checks that the input signature (i.e., Sig sig) does not cover the UTXO being spent, equivalent to signing using SIGHASH_ANYPREVOUT.
Step 1: Line 10 ensures sighash is for the current transaction using OP_PUSH_TX.
Step 2: Line 13–17 set item 2, 3, and 4 of sighash to all 0's, i.e., blanking out the input UTXO.
The same approach can be extended to emulate any flags. For example, blanking item 2 and 3 equals SIGHASH_ANYONECANPAY, and blanking item 6 is essentially SIGHASH_WITHOUT_PREV_VALUE. The expressiveness of Bitcoin smart contracts enables arbitrary flags.