Skip to content

Instantly share code, notes, and snippets.

@markblundeberg
Last active March 29, 2022 20:57
Show Gist options
  • Save markblundeberg/bd4b02ea5af91687ab60f0998ab1f634 to your computer and use it in GitHub Desktop.
Save markblundeberg/bd4b02ea5af91687ab60f0998ab1f634 to your computer and use it in GitHub Desktop.
SCRIPT_VERIFY_MINIMALIF not needed: making unmalleable smart contracts in BCH

It's quite common to see smart contract constructions like this:

OP_IF
    <clause 1 conditions>
    <pubkey1> OP_CHECKSIG
OP_ELSE
    <clause 2 conditions>
    <pubkey2> OP_CHECKSIG
OP_ENDIF

A problem with using this in unconfirmed transaction chains is that this is malleable -- anyone can modify the scriptsig stack value passed to OP_IF. A script flag SCRIPT_VERIFY_MINIMALIF has been proposed to fix this, and the flag even exists in some BCH clients like Bitcoin ABC though it is never activated.

Actually, no consensus changes are needed, rather, the smart contracts themselves can enforce unmalleability. One brute-force fix is to demand that the input be either canonically False (empty array) or canonically True (1-byte array [0x01]):

OP_DUP OP_DUP OP_0NOTEQUAL OP_EQUALVERIFY
OP_IF
    <clause 1 conditions>
    <pubkey1> OP_CHECKSIG
OP_ELSE
    <clause 2 conditions>
    <pubkey2> OP_CHECKSIG
OP_ENDIF

But, there is an even better way. We can take advantage of the fact that OP_CHECKSIG can return True/False, depending on whether it receives a valid signature or not. Crucially, Bitcoin Cash has the 'NULLFAIL' rule that means OP_CHECKSIG can only return False if the signature is an empty array. Thus we have:

<pubkey1> OP_CHECKSIG
OP_IF
    <clause 1 conditions>
    OP_1  // or, make sure clause leaves a nonzero value on stack
OP_ELSE
    <clause 2 conditions>
    <pubkey2> OP_CHECKSIG
OP_ENDIF

The last two constructions are unmalleable, provided we've adopted the rest of BIP62 provisions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment