TeachMeBitcoin

Tapscript 2-of-3 using OP_CHECKSIGADD

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

20. OP_CHECKSIGADD — The Tapscript Replacement

Overview

OP_CHECKSIGADD is a new opcode introduced in Tapscript (BIP 342, part of Taproot). It provides a cleaner alternative to OP_CHECKMULTISIG for implementing threshold signature schemes, without the off-by-one bug, the dummy element requirement, or the signature ordering constraints.

Opcode value: 0xba (decimal 186) — only valid in Tapscript
Stack input: <sig> <n> <pubkey>
Stack output: n + 1 if signature is valid, else n

How OP_CHECKSIGADD Works

Stack before: [ ... | <sig> | <n> | <pubkey> ]
                                   ↑ top

Logic:
  pop pubkey
  pop n (integer)
  pop sig

  if sig is empty:
    push n (unchanged — empty sig means "skip this key")
  elif sig is valid for pubkey:
    push n + 1 (increment counter)
  else:
    FAIL (invalid non-empty sig terminates script — NULLFAIL rule)

Stack after (if valid): [ ... | n+1 ]
Stack after (if skipped): [ ... | n ]

Building a 2-of-3 Multisig

# Tapscript 2-of-3 using OP_CHECKSIGADD
<pubkey1>
OP_CHECKSIG       # Initialize: 1 if sig1 valid, 0 if sig1 empty
<pubkey2>
OP_CHECKSIGADD    # Add: count becomes 2 if sig2 valid
<pubkey3>
OP_CHECKSIGADD    # Add: count becomes 3 if sig3 valid
OP_2
OP_GREATERTHANOREQUAL   # Check if count >= 2

Execution trace for valid 2-of-3 (sig1 and sig3 provided, sig2 omitted):

Initial stack:  [ <sig1> | <> | <sig3> ]  (empty sig for pubkey2)
Push pubkey1:   [ <sig1> | <> | <sig3> | <pubkey1> ]
OP_CHECKSIG:    [ <> | <sig3> | 1 ]        (sig1 matched pubkey1)
Push pubkey2:   [ <> | <sig3> | 1 | <pubkey2> ]
OP_CHECKSIGADD: [ <sig3> | 1 ]             (empty sig → n unchanged)
Push pubkey3:   [ <sig3> | 1 | <pubkey3> ]
OP_CHECKSIGADD: [ 2 ]                      (sig3 matched pubkey3)
Push OP_2:      [ 2 | 2 ]
OP_GREATERTHANOREQUAL: [ 1 ]               (2 >= 2 → true)

No Dummy Element Required

Legacy OP_CHECKMULTISIG requires:
  OP_0 <sig1> <sig2> OP_2 <pk1> <pk2> <pk3> OP_3 OP_CHECKMULTISIG
  ↑ Dummy element needed

OP_CHECKSIGADD requires:
  <sig1> <> <sig3> <pk1> OP_CHECKSIG <pk2> OP_CHECKSIGADD <pk3> OP_CHECKSIGADD OP_2 OP_GREATERTHANOREQUAL
  ↑ No dummy needed — clean design

Schnorr Signature Integration

In Tapscript, OP_CHECKSIGADD verifies Schnorr signatures:

# Tapscript signature format for OP_CHECKSIGADD:
<schnorr_sig>  = 64 bytes (r + s concatenated, no DER encoding)
              OR
<schnorr_sig>  = 65 bytes (64-byte sig + 1-byte sighash type, if non-default)

<empty>        = b''  (0 bytes — means "I don't contribute this key")

# Public key format:
<pubkey>       = 32 bytes (x-only compressed pubkey)
☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!