1use 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#[derive(Debug, Clone, Copy)]
69#[allow(dead_code)] pub enum Musig2Mode {
71 ScriptSpend,
73 OnlyKeySpend,
75 KeySpendWithScript(TapNodeHash),
77}
78
79const 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 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 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
170pub 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
180pub 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 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
247pub 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 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 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 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 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 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}