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