clementine_core/
musig2.rs

1//! # MuSig2
2//!
3//! Helper functions for the MuSig2 signature scheme.
4use crate::aggregator::VerifierId;
5#[cfg(test)]
6use crate::bitvm_client::SECP;
7#[cfg(test)]
8use bitcoin::secp256k1::SecretKey;
9use bitcoin::{
10    hashes::Hash,
11    key::Keypair,
12    secp256k1::{schnorr, Message, PublicKey},
13    TapNodeHash, XOnlyPublicKey,
14};
15use clementine_errors::BridgeError;
16use eyre::Context;
17use lazy_static::lazy_static;
18use secp256k1::{
19    musig::{
20        new_nonce_pair, AggregatedNonce, KeyAggCache, PartialSignature, PublicNonce, SecretNonce,
21        Session, SessionSecretRand,
22    },
23    Scalar, SECP256K1,
24};
25use sha2::{Digest, Sha256};
26
27#[cfg(test)]
28pub type MuSigNoncePair = (SecretNonce, PublicNonce);
29
30#[cfg(test)]
31pub fn from_secp_xonly(xpk: secp256k1::XOnlyPublicKey) -> XOnlyPublicKey {
32    XOnlyPublicKey::from_slice(&xpk.serialize()).expect("serialized pubkey is valid")
33}
34
35pub fn to_secp_pk(pk: PublicKey) -> secp256k1::PublicKey {
36    secp256k1::PublicKey::from_slice(&pk.serialize()).expect("serialized pubkey is valid")
37}
38
39#[cfg(test)]
40pub fn from_secp_pk(pk: secp256k1::PublicKey) -> PublicKey {
41    PublicKey::from_slice(&pk.serialize()).expect("serialized pubkey is valid")
42}
43
44#[cfg(test)]
45pub fn to_secp_sk(sk: SecretKey) -> secp256k1::SecretKey {
46    secp256k1::SecretKey::from_slice(&sk.secret_bytes()).expect("serialized secret key is valid")
47}
48
49pub fn to_secp_kp(kp: &Keypair) -> secp256k1::Keypair {
50    secp256k1::Keypair::from_seckey_slice(SECP256K1, &kp.secret_bytes())
51        .expect("serialized secret key is valid")
52}
53
54#[cfg(test)]
55pub fn from_secp_kp(kp: &secp256k1::Keypair) -> Keypair {
56    Keypair::from_seckey_slice(&SECP, &kp.secret_bytes()).expect("serialized secret key is valid")
57}
58
59pub fn from_secp_sig(sig: secp256k1::schnorr::Signature) -> schnorr::Signature {
60    schnorr::Signature::from_slice(&sig.to_byte_array()).expect("serialized signature is valid")
61}
62
63pub fn to_secp_msg(msg: &Message) -> secp256k1::Message {
64    secp256k1::Message::from_digest(*msg.as_ref())
65}
66
67/// Possible Musig2 modes.
68#[derive(Debug, Clone, Copy)]
69#[allow(dead_code)] // Variants used via create_key_agg_cache in tests
70pub enum Musig2Mode {
71    /// No taproot tweak.
72    ScriptSpend,
73    /// Taproot tweak with aggregated public key.
74    OnlyKeySpend,
75    /// Taproot tweak with tweaked aggregated public key.
76    KeySpendWithScript(TapNodeHash),
77}
78
79/// sha256(b"TapTweak")
80const TAPROOT_TWEAK_TAG_DIGEST: [u8; 32] = [
81    0xe8, 0x0f, 0xe1, 0x63, 0x9c, 0x9c, 0xa0, 0x50, 0xe3, 0xaf, 0x1b, 0x39, 0xc1, 0x43, 0xc6, 0x3e,
82    0x42, 0x9c, 0xbc, 0xeb, 0x15, 0xd9, 0x40, 0xfb, 0xb5, 0xc5, 0xa1, 0xf4, 0xaf, 0x57, 0xc5, 0xe9,
83];
84
85lazy_static! {
86    pub static ref TAPROOT_TWEAK_TAGGED_HASH: Sha256 = Sha256::new()
87        .chain_update(TAPROOT_TWEAK_TAG_DIGEST)
88        .chain_update(TAPROOT_TWEAK_TAG_DIGEST);
89}
90
91fn create_key_agg_cache(
92    mut public_keys: Vec<PublicKey>,
93    mode: Option<Musig2Mode>,
94) -> Result<KeyAggCache, BridgeError> {
95    if public_keys.is_empty() {
96        return Err(BridgeError::from(eyre::eyre!(
97            "MuSig2 Error: cannot create key aggregation cache (no public keys provided)"
98        )));
99    }
100    public_keys.sort();
101    let secp_pubkeys: Vec<secp256k1::PublicKey> =
102        public_keys.iter().map(|pk| to_secp_pk(*pk)).collect();
103    let pubkeys_ref: Vec<&secp256k1::PublicKey> = secp_pubkeys.iter().collect();
104    let pubkeys_ref = pubkeys_ref.as_slice();
105
106    let mut musig_key_agg_cache = KeyAggCache::new(SECP256K1, pubkeys_ref);
107    let agg_key = musig_key_agg_cache.agg_pk();
108
109    if let Some(mode) = mode {
110        match mode {
111            Musig2Mode::ScriptSpend => (),
112            Musig2Mode::OnlyKeySpend => {
113                // sha256(C, C, IPK) where C = sha256("TapTweak")
114                let xonly_tweak = TAPROOT_TWEAK_TAGGED_HASH
115                    .clone()
116                    .chain_update(agg_key.serialize())
117                    .finalize();
118
119                musig_key_agg_cache
120                    .pubkey_xonly_tweak_add(
121                        SECP256K1,
122                        &Scalar::from_be_bytes(xonly_tweak.into())
123                            .wrap_err("Failed to create scalar from xonly tweak bytes")?,
124                    )
125                    .wrap_err("Failed to tweak aggregated public key")?;
126            }
127            Musig2Mode::KeySpendWithScript(merkle_root) => {
128                // sha256(C, C, IPK, s) where C = sha256("TapTweak")
129                let xonly_tweak = TAPROOT_TWEAK_TAGGED_HASH
130                    .clone()
131                    .chain_update(agg_key.serialize())
132                    .chain_update(merkle_root.to_raw_hash().to_byte_array())
133                    .finalize();
134
135                musig_key_agg_cache
136                    .pubkey_xonly_tweak_add(
137                        SECP256K1,
138                        &Scalar::from_be_bytes(xonly_tweak.into())
139                            .wrap_err("Failed to create scalar from xonly tweak bytes")?,
140                    )
141                    .wrap_err("Failed to tweak aggregated public key")?;
142            }
143        }
144    };
145
146    Ok(musig_key_agg_cache)
147}
148
149pub trait AggregateFromPublicKeys {
150    fn from_musig2_pks(
151        pks: Vec<PublicKey>,
152        tweak: Option<Musig2Mode>,
153    ) -> Result<XOnlyPublicKey, BridgeError>;
154}
155
156impl AggregateFromPublicKeys for XOnlyPublicKey {
157    fn from_musig2_pks(
158        pks: Vec<PublicKey>,
159        tweak: Option<Musig2Mode>,
160    ) -> Result<XOnlyPublicKey, BridgeError> {
161        let musig_key_agg_cache = create_key_agg_cache(pks, tweak)?;
162
163        Ok(
164            XOnlyPublicKey::from_slice(&musig_key_agg_cache.agg_pk().serialize())
165                .wrap_err("Failed to create XOnlyPublicKey from aggregated public key")?,
166        )
167    }
168}
169
170// Aggregates the public nonces into a single aggregated nonce.
171pub fn aggregate_nonces(pub_nonces: &[&PublicNonce]) -> Result<AggregatedNonce, BridgeError> {
172    if pub_nonces.is_empty() {
173        return Err(BridgeError::from(eyre::eyre!(
174            "MuSig2 Error: cannot aggregate nonces (no public nonces provided)"
175        )));
176    }
177    Ok(AggregatedNonce::new(SECP256K1, pub_nonces))
178}
179
180/// Aggregates the partial signatures into a single aggregated signature and verifies the aggregated signature. If PARTIAL_SIG_VERIFICATION is set to true, each partial signature will also be verified.
181pub fn aggregate_partial_signatures(
182    pks: Vec<PublicKey>,
183    tweak: Option<Musig2Mode>,
184    agg_nonce: AggregatedNonce,
185    partial_sigs: &[(PartialSignature, PublicNonce)],
186    message: Message,
187) -> Result<schnorr::Signature, BridgeError> {
188    let musig_key_agg_cache = create_key_agg_cache(pks.clone(), tweak)?;
189    let secp_message = to_secp_msg(&message);
190
191    let session = Session::new(SECP256K1, &musig_key_agg_cache, agg_nonce, secp_message);
192
193    let partial_sigs_and_nonces: Vec<&(PartialSignature, PublicNonce)> =
194        partial_sigs.iter().collect();
195    let partial_sigs: Vec<&PartialSignature> =
196        partial_sigs_and_nonces.iter().map(|(sig, _)| sig).collect();
197    // enable partial signature verification with an environment variable to see which verifier is giving bad partial signatures
198    // this is an env var so that we can turn it off for better performance
199    let enable_partial_sig_verification = std::env::var("PARTIAL_SIG_VERIFICATION")
200        .ok()
201        .map(|value| {
202            matches!(
203                value.to_ascii_lowercase().as_str(),
204                "1" | "true" | "yes" | "y" | "on"
205            )
206        })
207        .unwrap_or(false);
208
209    if enable_partial_sig_verification {
210        let mut partial_sig_verification_errors = Vec::new();
211        for ((partial_sig, pub_nonce), pub_key) in
212            partial_sigs_and_nonces.into_iter().zip(pks.into_iter())
213        {
214            if !session.partial_verify(
215                SECP256K1,
216                &musig_key_agg_cache,
217                *partial_sig,
218                *pub_nonce,
219                to_secp_pk(pub_key),
220            ) {
221                let verifier_id = VerifierId(pub_key);
222                partial_sig_verification_errors.push(format!("{verifier_id}"));
223            }
224        }
225        if !partial_sig_verification_errors.is_empty() {
226            let error_msg = format!(
227                "MuSig2 Error: partial signature verification failed for verifiers: {}",
228                partial_sig_verification_errors.join(", ")
229            );
230            tracing::error!("{error_msg}");
231            return Err(BridgeError::from(eyre::eyre!(error_msg)));
232        }
233    }
234    let final_sig = session.partial_sig_agg(&partial_sigs);
235
236    SECP256K1
237        .verify_schnorr(
238            &final_sig.assume_valid(),
239            secp_message.as_ref(),
240            &musig_key_agg_cache.agg_pk(),
241        )
242        .wrap_err("Failed to verify schnorr signature")?;
243
244    Ok(from_secp_sig(final_sig.assume_valid()))
245}
246
247/// Generates a pair of nonces, one secret and one public. Be careful,
248/// DO NOT REUSE the same pair of nonces for multiple transactions. It will cause
249/// you to leak your secret key. For more information. See:
250/// <https://medium.com/blockstream/musig-dn-schnorr-multisignatures-with-verifiably-deterministic-nonces-27424b5df9d6#e3b6>.
251pub fn nonce_pair(keypair: &Keypair) -> Result<(SecretNonce, PublicNonce), BridgeError> {
252    let musig_session_sec_rand = SessionSecretRand::new();
253
254    Ok(new_nonce_pair(
255        SECP256K1,
256        musig_session_sec_rand,
257        None,
258        None,
259        to_secp_kp(keypair).public_key(),
260        None,
261        None,
262    ))
263}
264
265pub fn partial_sign(
266    pks: Vec<PublicKey>,
267    // Aggregated tweak, if there is any. This is useful for
268    // Taproot key-spends, since we might have script-spend conditions.
269    tweak: Option<Musig2Mode>,
270    sec_nonce: SecretNonce,
271    agg_nonce: AggregatedNonce,
272    keypair: Keypair,
273    sighash: Message,
274) -> Result<PartialSignature, BridgeError> {
275    let musig_key_agg_cache = create_key_agg_cache(pks, tweak)?;
276
277    let session = Session::new(
278        SECP256K1,
279        &musig_key_agg_cache,
280        agg_nonce,
281        to_secp_msg(&sighash),
282    );
283
284    Ok(session.partial_sign(
285        SECP256K1,
286        sec_nonce,
287        &to_secp_kp(&keypair),
288        &musig_key_agg_cache,
289    ))
290}
291
292#[cfg(test)]
293mod tests {
294    use super::{nonce_pair, MuSigNoncePair, Musig2Mode};
295    use crate::builder::script::{CheckSig, OtherSpendable, SpendableScript};
296    use crate::builder::transaction::DEFAULT_SEQUENCE;
297    use crate::rpc::clementine::NormalSignatureKind;
298    use crate::{
299        bitvm_client::{self, SECP},
300        builder::{
301            self,
302            transaction::{input::SpendableTxIn, output::UnspentTxOut, TxHandlerBuilder},
303        },
304        musig2::{
305            aggregate_nonces, aggregate_partial_signatures, create_key_agg_cache, from_secp_xonly,
306            partial_sign, AggregateFromPublicKeys,
307        },
308    };
309    use bitcoin::{
310        hashes::Hash,
311        key::Keypair,
312        script,
313        secp256k1::{schnorr, Message, PublicKey},
314        Amount, OutPoint, TapNodeHash, TapSighashType, TxOut, Txid, XOnlyPublicKey,
315    };
316    use clementine_errors::BridgeError;
317    use clementine_primitives::TransactionType;
318    use secp256k1::rand::Rng;
319    use std::sync::Arc;
320    use std::vec;
321
322    /// Generates random key and nonce pairs for a given number of signers.
323    fn create_key_and_nonce_pairs(num_signers: usize) -> (Vec<Keypair>, Vec<MuSigNoncePair>) {
324        let mut key_pairs = Vec::new();
325        let mut nonce_pairs = Vec::new();
326
327        for _ in 0..num_signers {
328            let key_pair = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
329            let nonce_pair = nonce_pair(&key_pair).unwrap();
330
331            key_pairs.push(key_pair);
332            nonce_pairs.push(nonce_pair);
333        }
334
335        (key_pairs, nonce_pairs)
336    }
337
338    #[test]
339    fn musig2_raw_without_a_tweak() {
340        let (key_pairs, nonce_pairs) = create_key_and_nonce_pairs(3);
341        let message = Message::from_digest(secp256k1::rand::rng().random());
342
343        let public_keys = key_pairs
344            .iter()
345            .map(|kp| kp.public_key())
346            .collect::<Vec<PublicKey>>();
347        let agg_pk = XOnlyPublicKey::from_musig2_pks(public_keys.clone(), None).unwrap();
348
349        let aggregated_nonce = super::aggregate_nonces(
350            nonce_pairs
351                .iter()
352                .map(|(_, musig_pub_nonce)| musig_pub_nonce)
353                .collect::<Vec<_>>()
354                .as_slice(),
355        )
356        .unwrap();
357
358        let partial_sigs = key_pairs
359            .into_iter()
360            .zip(nonce_pairs)
361            .map(|(kp, nonce_pair)| {
362                (
363                    super::partial_sign(
364                        public_keys.clone(),
365                        None,
366                        nonce_pair.0,
367                        aggregated_nonce,
368                        kp,
369                        message,
370                    )
371                    .unwrap(),
372                    nonce_pair.1,
373                )
374            })
375            .collect::<Vec<_>>();
376
377        let final_signature = super::aggregate_partial_signatures(
378            public_keys,
379            None,
380            aggregated_nonce,
381            &partial_sigs,
382            message,
383        )
384        .unwrap();
385
386        SECP.verify_schnorr(&final_signature, &message, &agg_pk)
387            .unwrap();
388    }
389
390    #[test]
391    fn musig2_raw_fail_if_partial_sigs_invalid() {
392        let kp_0 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
393        let kp_1 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
394        let kp_2 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
395
396        let message = Message::from_digest(secp256k1::rand::rng().random());
397
398        let pks = vec![kp_0.public_key(), kp_1.public_key(), kp_2.public_key()];
399
400        let (sec_nonce_0, pub_nonce_0) = super::nonce_pair(&kp_0).unwrap();
401        let (sec_nonce_1, pub_nonce_1) = super::nonce_pair(&kp_1).unwrap();
402        let (sec_nonce_2, pub_nonce_2) = super::nonce_pair(&kp_2).unwrap();
403
404        let agg_nonce =
405            super::aggregate_nonces(&[&pub_nonce_0, &pub_nonce_1, &pub_nonce_2]).unwrap();
406
407        let partial_sig_0 = (
408            super::partial_sign(pks.clone(), None, sec_nonce_0, agg_nonce, kp_0, message).unwrap(),
409            pub_nonce_0,
410        );
411        let partial_sig_1 = (
412            super::partial_sign(pks.clone(), None, sec_nonce_1, agg_nonce, kp_1, message).unwrap(),
413            pub_nonce_1,
414        );
415        // Oops, a verifier accidentally added some tweak!
416        let partial_sig_2 = (
417            super::partial_sign(
418                pks.clone(),
419                Some(Musig2Mode::KeySpendWithScript(
420                    TapNodeHash::from_slice(&[1u8; 32]).unwrap(),
421                )),
422                sec_nonce_2,
423                agg_nonce,
424                kp_2,
425                message,
426            )
427            .unwrap(),
428            pub_nonce_2,
429        );
430        let partial_sigs = vec![partial_sig_0, partial_sig_1, partial_sig_2];
431
432        let final_signature: Result<schnorr::Signature, BridgeError> =
433            super::aggregate_partial_signatures(pks, None, agg_nonce, &partial_sigs, message);
434
435        println!("final_signature: {final_signature:?}");
436
437        assert!(final_signature.is_err());
438    }
439
440    #[test]
441    fn musig2_sig_with_tweak() {
442        let (key_pairs, nonce_pairs) = create_key_and_nonce_pairs(3);
443        let message = Message::from_digest(secp256k1::rand::rng().random());
444        let tweak: [u8; 32] = secp256k1::rand::rng().random();
445
446        let public_keys = key_pairs
447            .iter()
448            .map(|kp| kp.public_key())
449            .collect::<Vec<PublicKey>>();
450        let aggregated_pk: XOnlyPublicKey = XOnlyPublicKey::from_musig2_pks(
451            public_keys.clone(),
452            Some(Musig2Mode::KeySpendWithScript(
453                TapNodeHash::from_slice(&tweak).unwrap(),
454            )),
455        )
456        .unwrap();
457
458        let aggregated_nonce = super::aggregate_nonces(
459            nonce_pairs
460                .iter()
461                .map(|(_, musig_pub_nonce)| musig_pub_nonce)
462                .collect::<Vec<_>>()
463                .as_slice(),
464        )
465        .unwrap();
466
467        let partial_sigs = key_pairs
468            .into_iter()
469            .zip(nonce_pairs)
470            .map(|(kp, nonce_pair)| {
471                (
472                    super::partial_sign(
473                        public_keys.clone(),
474                        Some(Musig2Mode::KeySpendWithScript(
475                            TapNodeHash::from_slice(&tweak).unwrap(),
476                        )),
477                        nonce_pair.0,
478                        aggregated_nonce,
479                        kp,
480                        message,
481                    )
482                    .unwrap(),
483                    nonce_pair.1,
484                )
485            })
486            .collect::<Vec<_>>();
487
488        let final_signature = super::aggregate_partial_signatures(
489            public_keys,
490            Some(Musig2Mode::KeySpendWithScript(
491                TapNodeHash::from_slice(&tweak).unwrap(),
492            )),
493            aggregated_nonce,
494            &partial_sigs,
495            message,
496        )
497        .unwrap();
498
499        SECP.verify_schnorr(&final_signature, &message, &aggregated_pk)
500            .unwrap();
501    }
502
503    #[test]
504    fn musig2_tweak_fail() {
505        let kp_0 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
506        let kp_1 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
507        let kp_2 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
508
509        let message = Message::from_digest(secp256k1::rand::rng().random::<[u8; 32]>());
510
511        let tweak: [u8; 32] = secp256k1::rand::rng().random();
512
513        let pks = vec![kp_0.public_key(), kp_1.public_key(), kp_2.public_key()];
514
515        let (sec_nonce_0, pub_nonce_0) = super::nonce_pair(&kp_0).unwrap();
516        let (sec_nonce_1, pub_nonce_1) = super::nonce_pair(&kp_1).unwrap();
517        let (sec_nonce_2, pub_nonce_2) = super::nonce_pair(&kp_2).unwrap();
518
519        let agg_nonce =
520            super::aggregate_nonces(&[&pub_nonce_0, &pub_nonce_1, &pub_nonce_2]).unwrap();
521
522        let partial_sig_0 = (
523            super::partial_sign(
524                pks.clone(),
525                Some(Musig2Mode::KeySpendWithScript(
526                    TapNodeHash::from_slice(&tweak).unwrap(),
527                )),
528                sec_nonce_0,
529                agg_nonce,
530                kp_0,
531                message,
532            )
533            .unwrap(),
534            pub_nonce_0,
535        );
536        let partial_sig_1 = (
537            super::partial_sign(
538                pks.clone(),
539                Some(Musig2Mode::KeySpendWithScript(
540                    TapNodeHash::from_slice(&tweak).unwrap(),
541                )),
542                sec_nonce_1,
543                agg_nonce,
544                kp_1,
545                message,
546            )
547            .unwrap(),
548            pub_nonce_1,
549        );
550        // Oops, a verifier accidentally forgot to put the tweak!
551        let partial_sig_2 = (
552            super::partial_sign(pks.clone(), None, sec_nonce_2, agg_nonce, kp_2, message).unwrap(),
553            pub_nonce_2,
554        );
555        let partial_sigs = vec![partial_sig_0, partial_sig_1, partial_sig_2];
556
557        let final_signature = super::aggregate_partial_signatures(
558            pks,
559            Some(Musig2Mode::KeySpendWithScript(
560                TapNodeHash::from_slice(&tweak).unwrap(),
561            )),
562            agg_nonce,
563            &partial_sigs,
564            message,
565        );
566
567        assert!(final_signature.is_err());
568    }
569
570    #[test]
571    fn musig2_key_spend() {
572        let (key_pairs, nonce_pairs) = create_key_and_nonce_pairs(2);
573        let public_keys = key_pairs
574            .iter()
575            .map(|key_pair| key_pair.public_key())
576            .collect::<Vec<PublicKey>>();
577
578        let untweaked_xonly_pubkey =
579            XOnlyPublicKey::from_musig2_pks(public_keys.clone(), None).unwrap();
580
581        let agg_nonce = super::aggregate_nonces(
582            nonce_pairs
583                .iter()
584                .map(|(_, musig_pub_nonce)| musig_pub_nonce)
585                .collect::<Vec<_>>()
586                .as_slice(),
587        )
588        .unwrap();
589
590        let dummy_script = script::Builder::new().push_int(1).into_script();
591        let scripts: Vec<Arc<dyn SpendableScript>> =
592            vec![Arc::new(OtherSpendable::new(dummy_script))];
593        let receiving_address = bitcoin::Address::p2tr(
594            &SECP,
595            *bitvm_client::UNSPENDABLE_XONLY_PUBKEY,
596            None,
597            bitcoin::Network::Regtest,
598        );
599        let (sending_address, sending_address_spend_info) =
600            builder::address::create_taproot_address(
601                &scripts
602                    .iter()
603                    .map(|a| a.to_script_buf())
604                    .collect::<Vec<_>>(),
605                Some(untweaked_xonly_pubkey),
606                bitcoin::Network::Regtest,
607            );
608
609        let prevout = TxOut {
610            value: Amount::from_sat(100_000_000),
611            script_pubkey: sending_address.script_pubkey(),
612        };
613        let utxo = OutPoint {
614            txid: Txid::from_byte_array([0u8; 32]),
615            vout: 0,
616        };
617        let mut builder = TxHandlerBuilder::new(TransactionType::Dummy);
618        builder = builder
619            .add_input(
620                NormalSignatureKind::OperatorSighashDefault,
621                SpendableTxIn::new(
622                    utxo,
623                    prevout.clone(),
624                    scripts.clone(),
625                    Some(sending_address_spend_info.clone()),
626                ),
627                builder::script::SpendPath::Unknown,
628                DEFAULT_SEQUENCE,
629            )
630            .add_output(UnspentTxOut::from_partial(TxOut {
631                value: Amount::from_sat(99_000_000),
632                script_pubkey: receiving_address.script_pubkey(),
633            }));
634
635        let tx_details = builder.finalize();
636
637        let message = Message::from_digest(
638            tx_details
639                .calculate_pubkey_spend_sighash(0, TapSighashType::Default)
640                .unwrap()
641                .to_byte_array(),
642        );
643        let merkle_root = sending_address_spend_info.merkle_root().unwrap();
644
645        let partial_sigs = key_pairs
646            .into_iter()
647            .zip(nonce_pairs)
648            .map(|(kp, nonce_pair)| {
649                (
650                    super::partial_sign(
651                        public_keys.clone(),
652                        Some(Musig2Mode::KeySpendWithScript(merkle_root)),
653                        nonce_pair.0,
654                        agg_nonce,
655                        kp,
656                        message,
657                    )
658                    .unwrap(),
659                    nonce_pair.1,
660                )
661            })
662            .collect::<Vec<_>>();
663
664        let final_signature = super::aggregate_partial_signatures(
665            public_keys.clone(),
666            Some(Musig2Mode::KeySpendWithScript(merkle_root)),
667            agg_nonce,
668            &partial_sigs,
669            message,
670        )
671        .unwrap();
672
673        let musig_agg_xonly_pubkey = XOnlyPublicKey::from_musig2_pks(
674            public_keys,
675            Some(Musig2Mode::KeySpendWithScript(merkle_root)),
676        )
677        .unwrap();
678
679        SECP.verify_schnorr(&final_signature, &message, &musig_agg_xonly_pubkey)
680            .unwrap();
681    }
682
683    #[test]
684    fn musig2_script_spend() {
685        let (key_pairs, nonce_pairs) = create_key_and_nonce_pairs(2);
686        let public_keys = key_pairs
687            .iter()
688            .map(|key_pair| key_pair.public_key())
689            .collect::<Vec<PublicKey>>();
690
691        let agg_nonce = super::aggregate_nonces(
692            nonce_pairs
693                .iter()
694                .map(|x| &x.1)
695                .collect::<Vec<_>>()
696                .as_slice(),
697        )
698        .unwrap();
699        let musig_agg_xonly_pubkey_wrapped =
700            XOnlyPublicKey::from_musig2_pks(public_keys.clone(), None).unwrap();
701
702        let scripts: Vec<Arc<dyn SpendableScript>> =
703            vec![Arc::new(CheckSig::new(musig_agg_xonly_pubkey_wrapped))];
704
705        let receiving_address = bitcoin::Address::p2tr(
706            &SECP,
707            *bitvm_client::UNSPENDABLE_XONLY_PUBKEY,
708            None,
709            bitcoin::Network::Regtest,
710        );
711        let (sending_address, sending_address_spend_info) =
712            builder::address::create_taproot_address(
713                &scripts
714                    .iter()
715                    .map(|a| a.to_script_buf())
716                    .collect::<Vec<_>>(),
717                None,
718                bitcoin::Network::Regtest,
719            );
720
721        let prevout = TxOut {
722            value: Amount::from_sat(100_000_000),
723            script_pubkey: sending_address.script_pubkey(),
724        };
725        let utxo = OutPoint {
726            txid: Txid::from_byte_array([0u8; 32]),
727            vout: 0,
728        };
729
730        let tx_details = TxHandlerBuilder::new(TransactionType::Dummy)
731            .add_input(
732                NormalSignatureKind::OperatorSighashDefault,
733                SpendableTxIn::new(
734                    utxo,
735                    prevout,
736                    scripts,
737                    Some(sending_address_spend_info.clone()),
738                ),
739                builder::script::SpendPath::Unknown,
740                DEFAULT_SEQUENCE,
741            )
742            .add_output(UnspentTxOut::from_partial(TxOut {
743                value: Amount::from_sat(99_000_000),
744                script_pubkey: receiving_address.script_pubkey(),
745            }))
746            .finalize();
747
748        let message = Message::from_digest(
749            tx_details
750                .calculate_script_spend_sighash_indexed(0, 0, bitcoin::TapSighashType::Default)
751                .unwrap()
752                .to_byte_array(),
753        );
754
755        let partial_sigs = key_pairs
756            .into_iter()
757            .zip(nonce_pairs)
758            .map(|(kp, nonce_pair)| {
759                (
760                    super::partial_sign(
761                        public_keys.clone(),
762                        None,
763                        nonce_pair.0,
764                        agg_nonce,
765                        kp,
766                        message,
767                    )
768                    .unwrap(),
769                    nonce_pair.1,
770                )
771            })
772            .collect::<Vec<_>>();
773
774        let final_signature = super::aggregate_partial_signatures(
775            public_keys,
776            None,
777            agg_nonce,
778            &partial_sigs,
779            message,
780        )
781        .unwrap();
782
783        SECP.verify_schnorr(&final_signature, &message, &musig_agg_xonly_pubkey_wrapped)
784            .unwrap();
785    }
786
787    #[test]
788    fn different_aggregated_keys_for_different_musig2_modes() {
789        let kp1 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
790        let kp2 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
791        let public_keys = vec![kp1.public_key(), kp2.public_key()];
792
793        let key_agg_cache = create_key_agg_cache(public_keys.clone(), None).unwrap();
794        let agg_pk_no_tweak = from_secp_xonly(key_agg_cache.agg_pk());
795
796        let key_agg_cache =
797            create_key_agg_cache(public_keys.clone(), Some(Musig2Mode::ScriptSpend)).unwrap();
798        let agg_pk_script_spend = from_secp_xonly(key_agg_cache.agg_pk());
799
800        let key_agg_cache =
801            create_key_agg_cache(public_keys.clone(), Some(Musig2Mode::OnlyKeySpend)).unwrap();
802        let agg_pk_key_tweak = from_secp_xonly(key_agg_cache.agg_pk());
803
804        let key_agg_cache = create_key_agg_cache(
805            public_keys.clone(),
806            Some(Musig2Mode::KeySpendWithScript(
807                TapNodeHash::from_slice(&[1u8; 32]).unwrap(),
808            )),
809        )
810        .unwrap();
811        let agg_pk_script_tweak = from_secp_xonly(key_agg_cache.agg_pk());
812
813        assert_eq!(agg_pk_no_tweak, agg_pk_script_spend);
814
815        assert_ne!(agg_pk_no_tweak, agg_pk_script_tweak);
816        assert_ne!(agg_pk_no_tweak, agg_pk_key_tweak);
817        assert_ne!(agg_pk_script_tweak, agg_pk_key_tweak);
818        assert_ne!(agg_pk_script_tweak, agg_pk_script_spend);
819        assert_ne!(agg_pk_key_tweak, agg_pk_script_spend);
820    }
821
822    #[test]
823    fn signing_checks_for_different_musig2_modes() {
824        let kp1 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
825        let kp2 = Keypair::new(&SECP, &mut bitcoin::secp256k1::rand::thread_rng());
826        let public_keys = vec![kp1.public_key(), kp2.public_key()];
827
828        let message = Message::from_digest(secp256k1::rand::rng().random());
829        let key_spend_with_script_tweak =
830            Musig2Mode::KeySpendWithScript(TapNodeHash::from_slice(&[0x45u8; 32]).unwrap());
831
832        let key_agg_cache =
833            create_key_agg_cache(public_keys.clone(), Some(key_spend_with_script_tweak)).unwrap();
834        let agg_pk_script_tweak = from_secp_xonly(key_agg_cache.agg_pk());
835
836        let (sec_nonce1, pub_nonce1) = nonce_pair(&kp1).unwrap();
837        let (sec_nonce2, pub_nonce2) = nonce_pair(&kp2).unwrap();
838        let agg_nonce = aggregate_nonces(&[&pub_nonce1, &pub_nonce2]).unwrap();
839
840        let partial_sig1 = partial_sign(
841            public_keys.clone(),
842            Some(key_spend_with_script_tweak),
843            sec_nonce1,
844            agg_nonce,
845            kp1,
846            message,
847        )
848        .unwrap();
849        let partial_sig2 = partial_sign(
850            public_keys.clone(),
851            Some(key_spend_with_script_tweak),
852            sec_nonce2,
853            agg_nonce,
854            kp2,
855            message,
856        )
857        .unwrap();
858
859        let final_sig = aggregate_partial_signatures(
860            public_keys.clone(),
861            Some(key_spend_with_script_tweak),
862            agg_nonce,
863            &[(partial_sig1, pub_nonce1), (partial_sig2, pub_nonce2)],
864            message,
865        )
866        .unwrap();
867
868        SECP.verify_schnorr(&final_sig, &message, &agg_pk_script_tweak)
869            .unwrap();
870
871        // Verification will fail with a untweaked aggregate public key against
872        // a signature created with a tweaked aggregate public key.
873        let key_agg_cache = create_key_agg_cache(public_keys, None).unwrap();
874        let agg_pk_no_tweak = from_secp_xonly(key_agg_cache.agg_pk());
875        assert!(SECP
876            .verify_schnorr(&final_sig, &message, &agg_pk_no_tweak)
877            .is_err());
878    }
879}