MuSig2

MuSig builds on Schnorr signatures. It combines several signers' public keys and signatures into a single key and signature.

TLDR: Three signers produce one aggregate key and one 64-byte signature. On-chain it is indistinguishable from a single-signer Schnorr spend.

By the end of this article, we want Alice, Bob, and Carol to sign a transaction using MuSig.

We will explain step by step using both theory and code. Your task by the end is to write the code for this signature generation.

Theory

We will answer three questions: What? Why? How?

What is MuSig?

MuSig is simply a protocol for aggregating public keys and signatures.

SVG Image

Why do we need MuSig?

The reasons we use MuSig are:

  • Transaction Size/Fees: MuSig makes multi-sig look like single-sig. This means you pay the same fees whether one person or ten people sign.

  • Privacy: Nobody can tell if a transaction used multiple signers or just one. Your multi-sig setup stays private, as it looks identical to a regular transaction.

SVG Image

How does MuSig work?

Now we need your focus here, please. Grab a coffee.

Alice, Bob, and Carol want to create an aggregated signature such as:

sagg=sa+sb+sc\mathbf{s_{agg} = s_a + s_b + s_c}

To create this signature, we need to go through three main steps:

  1. Aggregating public keys.
  2. Aggregating nonces.
  3. Aggregating the signature.

Some of these steps require rounds of communication between participants:

  • Aggregating public keys does not require communication.
  • Aggregating nonces and signatures requires three rounds of communication (MuSig2 optimizes this to two rounds).

NOTE: This page teaches the original MuSig

Production software today uses MuSig2 (BIP327), a newer version of this protocol that needs only two rounds of communication.

SVG Image

Step 1: Public Key Aggregation (Offline Process)

A naive way to do public key aggregation is by summing up each participant's public key:

Pagg=Pa+Pb+Pc\mathbf{P_{agg} = P_a + P_b + P_c}

However, this approach is vulnerable to a key cancellation attack.

SVG Image

What is a key cancellation attack?

Imagine a scenario where Alice and Bob want to create a 2-of-2 aggregated signature.

How do we counteract the key cancellation attack?

To counteract this, we add a challenge factor to each participant's public key:

Pi=ci×Pi\mathbf{P'_i = c_i \times P_i}

The challenge factor is generated from a hash of all participants' individual public keys: first call = H(Pa, Pb, Pc), then ci = H(call | Pi).

This ensures that no individual participant can create a public key that offsets the public keys of others.

Here’s how we construct individual public keys and the aggregated public key:
(Remember, all of this happens offline. There’s no need for communication; public keys can be exchanged through offline channels.)

SVG Image

Exercise 1: Compute 3-of-3 MuSig public key

In this exercise, we'll use the generate_musig_key(pubkey_list) function to generate challenge factors for each participant and an aggregate MuSig pubkey.

generate_musig_key(pubkey_list) takes a list of the participants' public keys generate_musig_key([ECPubKey0, ECPubKey1, ...]) and returns a challenge map and the aggregate pubkey:

  • The challenge map contains ECPubKey_i, challenge_data_i key - value pairs.
  • The aggregate pubkey is an ECPubKey object.

Solution code


Now, let’s move to the next step: Aggregate Nonce.

Step 2: Aggregate Nonce (1st and 2nd Round of Communication)

The process of aggregating nonces is as follows:

  1. Alice, Bob, and Carol each generate a random nonce ka, kb, and kc.
  2. They calculate their respective nonce points: Ra = ka * G, Rb = kb * G, Rc = kc * G.
  3. They exchange these nonce points (Ra, Rb, and Rc).
  4. Finally, they calculate the aggregated nonce: Ragg = Ra + Rb + Rc

Where do the rounds of communication happen in this process?

The process of aggregating nonces is somewhat similar to aggregating public keys.

Except: Public keys (e.g., Pa, Pb, and Pc) stay the same for every signature process, but random numbers (ka, kb, kc) must change with every signature. So every new signature needs fresh rounds of communication.

How do we prevent cheating?

Nonce aggregation takes two rounds of communication:

  1. Exchange hash commitments: Alice, Bob, and Carol share H(Ra), H(Rb), and H(Rc).

  2. Exchange nonce points: They reveal Ra, Rb, and Rc.

The commitments come first so nobody can change their nonce point after seeing the others'.

Once all Ri values are shared, they compute the aggregated nonce: Ragg = Ra + Rb + Rc


Exercise 2: Compute 3-of-3 MuSig nonce

In this exercise, we'll generate nonces for individual participants, calculate the nonce point commitments, and then generate an aggregate nonce point.

Solution code

Step 3: Signature Aggregation (Final Step)

After computing the aggregate public key (PaggP_{agg}) and aggregate nonce (RaggR_{agg}), each participant calculates their partial signature:

si=ki+H(x(Ragg)Paggm)×di\mathbf{s_i = k_i + H(x(R_{agg}) \, || \, P_{agg} \, || \, m) \times d_i}

Where:

  • kik_i: Participant's random nonce.
  • did_i: Participant's challenge-tweaked private key (cidic_i \cdot d_i, the value computed in Step 1).
  • HH: Hash function with RaggR_{agg}, PaggP_{agg}, and the message mm.

Aggregating the Signatures

Participants exchange their sis_i values, and the aggregate signature is computed as:

sagg=sa+sb+sc\mathbf{s_{agg} = s_a + s_b + s_c}

This simplifies to:

sagg=kagg+H(x(Ragg)Paggm)×dagg\mathbf{s_{agg} = k_{agg} + H(x(R_{agg}) \, || \, P_{agg} \, || \, m) \times d_{agg}}

Where:

  • kagg=ka+kb+kck_{agg} = k_a + k_b + k_c (aggregate nonce scalar).
  • dagg=da+db+dcd_{agg} = d_a + d_b + d_c (aggregate private key scalar).

Final Signature

The final aggregate signature is:

(Ragg, sagg)

This pair can be verified using PaggP_{agg} and the message mm.


Exercise 3: Compute aggregated MuSig signature

In this exercise, each participant creates their partial signature. We then aggregate the partial signatures and verify the result against the aggregate public key.

Solution code

Alice, Bob, and Carol now share one key and one signature.


Suggest Edits