You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This article is tailored to 2of2 Multisig and how to use
the library musig2 for adaptor signatures.
An adaptor signature is a construction, where one party must reveal a secret when it wants to use a signature prepared
by another party.
This explanation is tailored towards 2of2 scriptless Multisig. Generalising to n-of-n should be straight forward, if
needed.
Let's assume Bob wants Alice to reveal the secret $t$. Bob (and Alice) will create a signature for a payout transaction
of a 2of2 multisig such that when Alice uses it, she must reveal $t$ to Bob.
Let
$$T=t \cdot G $$$$\begin{aligned}
\text{where}& \hspace{1000pt} \\
t & \text{ is secret adaptor, the secret which will be revealed} \\
T &\text{ is public adaptor} \\
G &\text{ is the Generator point of secp256k1} \\
m &\text{ (the message) is the serialisation of a transaction which spends the output of the 2of2 Multisig.}
\end{aligned}
$$
The function $H_{tagged}(x_1,...,x_n)$ is a hash function where the name 'tagged' is used as literal to init the hash
and if $x_i$ is a Curvepoint then we use the compressed x-key instead. Operator '||' stands for concatenation.
Technically the key aggregation is of course not part of the signing process. However, for creating the signature using
MuSig2, the aggregated key must be constructed in this way.
$P_a = p_a \cdot G$ is Alice Public Key
generated from random secret key (must not be related nor coming from HD wallet, just ephemeral). The $P_a$ is send to Bob.
by calling KeyAggContext::new([P_a,P_b]) with the pubkeys of all participants.
$a_i$ is called the coefficients.
Sign round 1
Alice creates 2 Nonces:
$R_{a,1}$ and $R_{a,2}$ and sends them around. She keeps the secret nonce $r_{a,1}$ and $r_{a,2}$ for herself.
This is done via SecNonce::generate(...).public_nonce() and sending the public nonce to Bob.
Up to here, its independent of the message and can be precalculated.
Sign round 2
At this point in time the public Adaptor $T$ must be known to both parties. From collected Nonce the aggregated Nonce $R$ is calculated:
$$\begin{aligned}
(2)~~ R_1 &= R_{a,1} + R_{b,1} \\
R_2 &= R_{a,2} + R_{b,2} \\
b &= H_{non}(R_1 , R_2, P, m) \\
(3)~~ R &= R_1 + b \cdot R_2 + T
\end{aligned}$$
Note that the $T$ is added to $R$, this seperates normal MuSig2 from adaptive MuSig2.
This is done via AggNonce::sum() and musig2::adaptor::sign_partial(T). This is split into 2 methods, the aggregated Nounce is in musig2
without Adaptor T, it will be added in the second method right before generating the partial signature.
$s_a$ is the partial signature calculated via musig2::adaptor::sign_partial(T) (Note that the public Nonces have been
exchanged already)
Note, that when Bob calculates his partial signature with (4) he can be sure, that his signature can only be used with
the adaptor $T$, since $R$ is part of the hash-function and $R$ is dependent of $T$.
The partial signature get exchanged. And the other side can verify the validity of the partial signature.
We can multiply equation (4) with $G$ and verify the partial signature with this form:
$$s_a \cdot G = R_{a,1} + b \cdot R_{a,2}+a_a \cdot H_{sig}(R,P,m) \cdot P_a$$
This is being done by calling
musig2::adaptor::verify_partial() which is also done within musig2::adaptor::sign_partial(T). Partial signature
from other parties should be verified using this method. AdamISZ pointed out that there is a possible
Forgery with a fake key in MuSig2. This does not
apply to our use case, since the necessary ramification for the forgery are not given.
Signature aggregation
The partial signatures get aggregated using
$$s=s_a+s_b$$
with the method musig2::adaptor::aggregate_partial_signatures(). Since we added a $T$ into the signature (3), this is only a
pre-signature, not a valid signature.
We can prove that this pre-signature is indeed a valid adaptor signature by multiplying with $G$ and
setting $e:=H_{sig}(R,P,m)$:
$$\begin{aligned}
s\cdot G &= s_a\cdot G+s_b\cdot G ;| ~~ with (4) \\
&=(r_{a,1} + b \cdot r_{a,2} + a_a \cdot e \cdot p_a)\cdot G + (r_{b,1}+b \cdot r_{b,2} + a_b \cdot e \cdot p_b)\cdot G \\
&=R_{a,1}+b\cdot R_{a,2} + a_a\cdot e \cdot P_a + R_{b,1}+b\cdot R_{b,2} + a_b\cdot e \cdot P_b;| ~~ with (2) \\
&= R_1 + b \cdot R_2 + a_a \cdot e \cdot P_a + a_b \cdot e \cdot P_b;| ~~ with (1) \\
&= R_1 + b \cdot R_2 + e \cdot P;| ~~ with (3) \\
&= R - T + e \cdot P
\end{aligned}$$
that means
$$\begin{aligned}
s \cdot G + T &= R+e \cdot P \\
\Leftrightarrow (s + t) \cdot G &= R + e \cdot P
\end{aligned}$$
so with the discrete logarithm (DLOG) of $T$, which is $t$, we would have a valid signature.
This is being verified by calling musig2::adaptor::aggregate_partial_signatures().
Alice
has the knowledge of $t$ and can make this a valid signature.
Alice must "adapt" the pre-signature with tusing adaptor_signature.adapt(adaptor_secret) and
gets $s' = s+t$. To verify that the final signature $s'$ is correct, we call musig2::verify_single()
Alice can use the signature $Sig(s',R)$ and broadcast the Transaction. In the mempool or on the
blockchain, Bob can
find $s'$. Bob can calculate $t=s' - s$ by using adaptor_signature.reveal_secret(&valid_signature)
with $s, s'$.