TeachMeBitcoin

Key aggregation (off-chain computation)

From TeachMeBitcoin, the free encyclopedia Reading time: 2 min

21. How OP_CHECKSIGADD Enables Taproot Multisig

Overview

Taproot's combination of Schnorr signatures, key aggregation, and OP_CHECKSIGADD enables a spectrum of multisig constructions that are more efficient, more private, and more flexible than legacy multisig.

Three Approaches to Taproot Multisig

Approach 1: MuSig2 Key Aggregation (Most Private)

For N-of-N schemes, all participants can aggregate their public keys into a single key and produce a single Schnorr signature. This appears as a simple key-spend on-chain:

# Key aggregation (off-chain computation)
individual_keys = [pk_alice, pk_bob, pk_carol]
aggregated_key = MuSig2.key_aggregate(individual_keys)

# Taproot output
scriptPubKey = OP_1 <aggregated_key>  # P2TR key-spend

# Spending: a single 64-byte Schnorr sig from the MuSig2 protocol
# On-chain: looks identical to a single-key spend
# Privacy: observers can't tell it's 3-of-3 or 1-of-1

Approach 2: Tapscript with OP_CHECKSIGADD (General Threshold)

For M-of-N where M < N, use a Tapscript leaf:

# 3-of-5 Tapscript using OP_CHECKSIGADD
<pk1> OP_CHECKSIG
<pk2> OP_CHECKSIGADD
<pk3> OP_CHECKSIGADD
<pk4> OP_CHECKSIGADD
<pk5> OP_CHECKSIGADD
OP_3
OP_GREATERTHANOREQUAL

Approach 3: FROST for Threshold Schnorr

FROST (Flexible Round-Optimized Schnorr Threshold signatures) allows M-of-N threshold signing where the on-chain result is a single Schnorr signature — the threshold is completely hidden:

# FROST: 3-of-5 threshold where on-chain appears as single-key spend
# Participants: Alice, Bob, Carol, Dave, Eve (any 3 can sign)

# Key generation (off-chain)
(secret_shares, threshold_pubkey) = FROST.keygen(5, 3)

# Signing (any 3 participants)
partial_sigs = [FROST.sign(share_alice, msg), 
                FROST.sign(share_bob, msg),
                FROST.sign(share_carol, msg)]
final_sig = FROST.aggregate(partial_sigs)

# On-chain output: P2TR with threshold_pubkey
# On-chain signature: a single 64-byte Schnorr sig
# Privacy: indistinguishable from any other P2TR spend

Script Tree for Complex Multisig Policies

Taproot's MAST (Merklized Abstract Syntax Trees) allows multiple multisig policies as separate leaves:

# Policy: "2-of-3 normally, or 1-of-3 after 1 year"
internal_key = key_aggregate(pk_alice, pk_bob, pk_carol)

leaf_a = Script("2-of-3 via OP_CHECKSIGADD with alice, bob, carol")
leaf_b = Script("1-of-3 with 52560 blocks CLTV timelock")

taproot_tree = TaprootTree([leaf_a, leaf_b])
taproot_output_key = tweak(internal_key, taproot_tree.root)

# Spending via leaf_a (normal case):
witness = [<sig_alice>, <>, <sig_carol>, <leaf_a_script>, <control_block_for_leaf_a>]

# Spending via leaf_b (after timeout):
witness = [<sig_alice>, <leaf_b_script>, <control_block_for_leaf_b>]

Weight and Fee Efficiency

``` Script Type | vBytes for 2-of-3 input

☕ 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!