Tapscript 2-of-3 using OP_CHECKSIGADD
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)
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: