1use std::collections::HashSet;
6use std::str::FromStr;
7use std::{fmt, vec};
8
9use bitcoin::secp256k1::schnorr::Signature;
10use serde::de::DeserializeOwned;
11use serde::{Deserialize, Serialize};
12use thiserror::Error;
13
14use super::nut00::Witness;
15use super::nut01::PublicKey;
16use super::nut05::MeltRequest;
17use super::{Kind, Nut10Secret, Proof, Proofs, SecretKey};
18use crate::nut10::{
19 get_pubkeys_and_required_sigs, Conditions, SpendingConditionVerification, MAX_LOCKING_SLOTS,
20};
21use crate::nuts::nut00::BlindedMessage;
22use crate::util::unix_time;
23use crate::{SpendingConditions, SwapRequest};
24
25pub mod serde_p2pk_witness;
26
27#[derive(Debug, Error)]
29pub enum Error {
30 #[error("Secret is not a p2pk secret")]
32 IncorrectSecretKind,
33 #[error("Witness is not a p2pk witness")]
35 IncorrectWitnessKind,
36 #[error("Locktime in past")]
38 LocktimeInPast,
39 #[error("Invalid signature")]
41 InvalidSignature,
42 #[error("Unknown tag P2PK secret")]
44 UnknownTag,
45 #[error("Unknown sigflag")]
47 UnknownSigFlag,
48 #[error("P2PK spend conditions are not met")]
50 SpendConditionsNotMet,
51 #[error("P2PK required in secret data")]
53 P2PKPubkeyRequired,
54 #[error("Witness signatures not provided")]
56 SignaturesNotProvided,
57 #[error("Duplicate signature from the same pubkey detected")]
59 DuplicateSignature,
60 #[error("Duplicate public key in multisig (same x-coordinate)")]
62 DuplicatePubkey,
63 #[error(
65 "Too many pubkeys, {slots} slots provided, maximum allowed is {MAX_LOCKING_SLOTS} in total"
66 )]
67 TooManyPubkeys {
68 slots: usize,
70 },
71 #[error("refund keys require a locktime")]
73 RefundKeysRequireLocktime,
74 #[error(
76 "Impossible multisig: required {required} signatures but only {available} keys available"
77 )]
78 ImpossibleMultisigConfiguration {
79 required: u64,
81 available: u64,
83 },
84 #[error("Impossible refund multisig: required {required} signatures but only {available} refund keys available")]
86 ImpossibleRefundMultisigConfiguration {
87 required: u64,
89 available: u64,
91 },
92 #[error("P2PK does not support preimage requirements")]
94 PreimageNotSupportedInP2PK,
95 #[error("SIG_ALL proofs must be verified using a different method")]
97 SigAllNotSupportedHere,
98 #[error("Number of required signatures must be 1 or greater")]
100 ZeroSignaturesRequired,
101 #[error(transparent)]
103 SerdeJsonError(#[from] serde_json::Error),
104 #[error(transparent)]
106 Secp256k1(#[from] bitcoin::secp256k1::Error),
107 #[error(transparent)]
109 NUT01(#[from] crate::nuts::nut01::Error),
110}
111
112#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
114pub struct P2PKWitness {
115 pub signatures: Vec<String>,
117}
118
119impl P2PKWitness {
120 #[inline]
121 pub fn is_empty(&self) -> bool {
123 self.signatures.is_empty()
124 }
125}
126
127impl Proof {
128 pub fn sign_p2pk(&mut self, secret_key: SecretKey) -> Result<(), Error> {
130 let msg: Vec<u8> = self.secret.to_bytes();
131 let signature: Signature = secret_key.sign(&msg)?;
132
133 let signatures = vec![signature.to_string()];
134
135 match self.witness.as_mut() {
136 Some(witness) => {
137 witness.add_signatures(signatures);
138 }
139 None => {
140 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
141 p2pk_witness.add_signatures(signatures);
142 self.witness = Some(p2pk_witness);
143 }
144 };
145
146 Ok(())
147 }
148
149 pub fn verify_p2pk(&self) -> Result<(), Error> {
157 let secret: Nut10Secret = self.secret.clone().try_into()?;
158 let spending_conditions: Conditions = secret
159 .secret_data()
160 .tags()
161 .cloned()
162 .unwrap_or_default()
163 .try_into()
164 .map_err(|_| Error::SpendConditionsNotMet)?;
165
166 if spending_conditions.sig_flag == SigFlag::SigAll {
167 return Err(Error::SigAllNotSupportedHere);
168 }
169
170 if secret.kind() != Kind::P2PK {
171 return Err(Error::IncorrectSecretKind);
172 }
173
174 let now = unix_time();
176 let requirements =
177 super::nut10::get_pubkeys_and_required_sigs(&secret, now).map_err(|err| match err {
178 super::nut10::Error::NUT11(nut11_err) => nut11_err,
179 _ => Error::SpendConditionsNotMet,
180 })?;
181
182 if requirements.preimage_needed {
183 return Err(Error::PreimageNotSupportedInP2PK);
184 }
185
186 let witness_signatures = match &self.witness {
188 Some(witness) => witness.signatures(),
189 None => None,
190 };
191
192 let msg: &[u8] = self.secret.as_bytes();
193
194 {
197 let primary_valid = witness_signatures
198 .as_ref()
199 .and_then(|sigs| {
200 sigs.iter()
201 .map(|s| Signature::from_str(s))
202 .collect::<Result<Vec<_>, _>>()
203 .ok()
204 })
205 .and_then(|sigs| valid_signatures(msg, &requirements.pubkeys, &sigs).ok())
206 .is_some_and(|count| count >= requirements.required_sigs);
207
208 if primary_valid {
209 return Ok(());
210 }
211 }
212
213 {
215 if let Some(refund_path) = &requirements.refund_path {
216 if refund_path.required_sigs == 0 {
218 return Ok(());
219 }
220
221 let refund_valid = witness_signatures
223 .as_ref()
224 .and_then(|sigs| {
225 sigs.iter()
226 .map(|s| Signature::from_str(s))
227 .collect::<Result<Vec<_>, _>>()
228 .ok()
229 })
230 .and_then(|sigs| valid_signatures(msg, &refund_path.pubkeys, &sigs).ok())
231 .is_some_and(|count| count >= refund_path.required_sigs);
232
233 if refund_valid {
234 return Ok(());
235 }
236 }
237 }
238
239 if witness_signatures.is_none() {
241 Err(Error::SignaturesNotProvided)
242 } else {
243 Err(Error::SpendConditionsNotMet)
244 }
245 }
246}
247
248impl SpendingConditions {
249 pub fn new_p2pk(pubkey: PublicKey, conditions: Option<Conditions>) -> Self {
251 Self::P2PKConditions {
252 data: pubkey,
253 conditions,
254 }
255 }
256}
257
258pub(crate) fn extract_signatures_from_witness(
263 witness: &super::Witness,
264) -> Result<Vec<bitcoin::secp256k1::schnorr::Signature>, Error> {
265 use std::str::FromStr;
266
267 let witness_sigs = witness.signatures().ok_or(Error::SignaturesNotProvided)?;
268
269 witness_sigs
270 .iter()
271 .map(|s| bitcoin::secp256k1::schnorr::Signature::from_str(s))
272 .collect::<Result<Vec<_>, _>>()
273 .map_err(|_| Error::InvalidSignature)
274}
275
276pub(crate) fn verify_sig_all_p2pk(first_input: &Proof, msg_to_sign: String) -> Result<(), Error> {
286 let first_secret =
288 Nut10Secret::try_from(&first_input.secret).map_err(|_| Error::IncorrectSecretKind)?;
289
290 let current_time = crate::util::unix_time();
292
293 let requirements = get_pubkeys_and_required_sigs(&first_secret, current_time)
295 .map_err(|_| Error::SpendConditionsNotMet)?;
296
297 debug_assert!(
298 !requirements.preimage_needed,
299 "P2PK should never require preimage"
300 );
301
302 if let Some(refund_path) = &requirements.refund_path {
305 if refund_path.required_sigs == 0 {
306 return Ok(());
307 }
308 }
309
310 let first_witness = first_input
312 .witness
313 .as_ref()
314 .ok_or(Error::SignaturesNotProvided)?;
315
316 {
319 let primary_valid = extract_signatures_from_witness(first_witness)
320 .ok()
321 .and_then(|sigs| {
322 valid_signatures(msg_to_sign.as_bytes(), &requirements.pubkeys, &sigs).ok()
323 })
324 .is_some_and(|count| count >= requirements.required_sigs);
325
326 if primary_valid {
327 return Ok(());
328 }
329 }
330
331 {
333 if let Some(refund_path) = &requirements.refund_path {
334 let signatures = extract_signatures_from_witness(first_witness)?;
335 let valid_sig_count =
336 valid_signatures(msg_to_sign.as_bytes(), &refund_path.pubkeys, &signatures)
337 .map_err(|_| Error::InvalidSignature)?;
338
339 if valid_sig_count >= refund_path.required_sigs {
340 return Ok(());
341 }
342 }
343 }
344
345 Err(Error::SpendConditionsNotMet)
347}
348
349pub(crate) fn valid_signatures(
352 msg: &[u8],
353 pubkeys: &[PublicKey],
354 signatures: &[Signature],
355) -> Result<u64, Error> {
356 let mut verified_pubkeys = HashSet::new();
357
358 for pubkey in pubkeys {
359 for signature in signatures {
360 if pubkey.verify(msg, signature).is_ok() {
361 let x_only = pubkey.x_only_public_key();
363 if !verified_pubkeys.insert(x_only) {
365 return Err(Error::DuplicateSignature);
366 }
367 }
368 }
369 }
370
371 Ok(verified_pubkeys.len() as u64)
372}
373
374impl BlindedMessage {
375 pub fn sign_p2pk(&mut self, secret_key: SecretKey) -> Result<(), Error> {
377 let msg: [u8; 33] = self.blinded_secret.to_bytes();
378 let signature: Signature = secret_key.sign(&msg)?;
379
380 let signatures = vec![signature.to_string()];
381
382 match self.witness.as_mut() {
383 Some(witness) => {
384 witness.add_signatures(signatures);
385 }
386 None => {
387 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
388 p2pk_witness.add_signatures(signatures);
389 self.witness = Some(p2pk_witness);
390 }
391 };
392
393 Ok(())
394 }
395
396 pub fn verify_p2pk(&self, pubkeys: &Vec<PublicKey>, required_sigs: u64) -> Result<(), Error> {
398 let mut verified_pubkeys = HashSet::new();
399 if let Some(witness) = &self.witness {
400 for signature in witness
401 .signatures()
402 .ok_or(Error::SignaturesNotProvided)?
403 .iter()
404 {
405 for v in pubkeys {
406 let msg = &self.blinded_secret.to_bytes();
407 let sig = Signature::from_str(signature)?;
408
409 if v.verify(msg, &sig).is_ok() {
410 let x_only = v.x_only_public_key();
411 if !verified_pubkeys.insert(x_only) {
412 return Err(Error::DuplicateSignature);
413 }
414 } else {
415 tracing::debug!(
416 "Could not verify signature: {sig} on message: {}",
417 self.blinded_secret
418 )
419 }
420 }
421 }
422 }
423
424 let valid_sigs = verified_pubkeys.len() as u64;
425
426 if valid_sigs.ge(&required_sigs) {
427 Ok(())
428 } else {
429 Err(Error::SpendConditionsNotMet)
430 }
431 }
432}
433
434#[derive(
438 Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash,
439)]
440pub enum SigFlag {
441 #[default]
442 SigInputs,
446 SigAll,
448}
449
450impl fmt::Display for SigFlag {
451 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
452 match self {
453 Self::SigAll => write!(f, "SIG_ALL"),
454 Self::SigInputs => write!(f, "SIG_INPUTS"),
455 }
456 }
457}
458
459impl FromStr for SigFlag {
460 type Err = Error;
461 fn from_str(tag: &str) -> Result<Self, Self::Err> {
462 match tag {
463 "SIG_ALL" => Ok(Self::SigAll),
464 "SIG_INPUTS" => Ok(Self::SigInputs),
465 _ => Err(Error::UnknownSigFlag),
466 }
467 }
468}
469
470pub fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag {
473 let mut sig_flag = SigFlag::SigInputs;
474 let mut pubkeys = HashSet::new();
475 let mut sigs_required = 1;
476 for proof in proofs {
477 if let Ok(secret) = Nut10Secret::try_from(proof.secret) {
478 if secret.kind().eq(&Kind::P2PK) {
479 if let Ok(verifying_key) = PublicKey::from_str(secret.secret_data().data()) {
480 pubkeys.insert(verifying_key);
481 }
482 }
483
484 if let Some(tags) = secret.secret_data().tags() {
485 if let Ok(conditions) = Conditions::try_from(tags.clone()) {
486 if conditions.sig_flag.eq(&SigFlag::SigAll) {
487 sig_flag = SigFlag::SigAll;
488 }
489
490 if let Some(sigs) = conditions.num_sigs {
491 if sigs > sigs_required {
492 sigs_required = sigs;
493 }
494 }
495
496 if let Some(pubs) = conditions.pubkeys {
497 pubkeys.extend(pubs);
498 }
499 }
500 }
501 }
502 }
503
504 EnforceSigFlag {
505 sig_flag,
506 pubkeys,
507 sigs_required,
508 }
509}
510
511#[derive(Debug, Clone, PartialEq, Eq)]
513pub struct EnforceSigFlag {
514 pub sig_flag: SigFlag,
516 pub pubkeys: HashSet<PublicKey>,
518 pub sigs_required: u64,
520}
521
522impl SwapRequest {
523 pub fn sign_sig_all(&mut self, secret_key: SecretKey) -> Result<(), Error> {
525 let msg = self.sig_all_msg_to_sign();
527 let signature = secret_key.sign(msg.as_bytes())?;
528
529 let first_input = self
531 .inputs_mut()
532 .first_mut()
533 .ok_or(Error::IncorrectSecretKind)?;
534
535 match first_input.witness.as_mut() {
536 Some(witness) => {
537 witness.add_signatures(vec![signature.to_string()]);
538 }
539 None => {
540 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
541 p2pk_witness.add_signatures(vec![signature.to_string()]);
542 first_input.witness = Some(p2pk_witness);
543 }
544 };
545
546 Ok(())
547 }
548}
549
550impl<Q> MeltRequest<Q>
551where
552 Q: std::fmt::Display + Serialize + DeserializeOwned,
553{
554 pub fn sign_sig_all(&mut self, secret_key: SecretKey) -> Result<(), Error> {
556 let msg = self.sig_all_msg_to_sign();
558 let signature = secret_key.sign(msg.as_bytes())?;
559
560 let first_input = self
562 .inputs_mut()
563 .first_mut()
564 .ok_or(Error::SpendConditionsNotMet)?;
565
566 match first_input.witness.as_mut() {
567 Some(witness) => {
568 witness.add_signatures(vec![signature.to_string()]);
569 }
570 None => {
571 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
572 p2pk_witness.add_signatures(vec![signature.to_string()]);
573 first_input.witness = Some(p2pk_witness);
574 }
575 };
576
577 Ok(())
578 }
579}
580
581#[cfg(feature = "mint")]
582#[cfg(test)]
583mod tests {
584 use std::str::FromStr;
585
586 use uuid::Uuid;
587
588 use super::*;
589 use crate::nuts::Id;
590 use crate::quote_id::QuoteId;
591 use crate::secret::Secret;
592 use crate::{Amount, BlindedMessage};
593
594 fn proof_for_enforce_sig_flag(num_sigs: u64) -> Proof {
595 let primary = SecretKey::generate().public_key();
596 let signer_two = SecretKey::generate().public_key();
597 let signer_three = SecretKey::generate().public_key();
598 let conditions = Conditions {
599 pubkeys: Some(vec![signer_two, signer_three]),
600 num_sigs: Some(num_sigs),
601 sig_flag: SigFlag::SigAll,
602 ..Default::default()
603 };
604 let secret: Secret = SpendingConditions::new_p2pk(primary, Some(conditions))
605 .try_into()
606 .unwrap();
607
608 Proof {
609 amount: Amount::ONE,
610 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
611 secret,
612 c: primary,
613 witness: None,
614 dleq: None,
615 p2pk_e: None,
616 }
617 }
618
619 #[test]
620 fn test_secret_ser() {
621 let data = PublicKey::from_str(
622 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
623 )
624 .unwrap();
625
626 let conditions = Conditions {
627 locktime: Some(99999),
628 pubkeys: Some(vec![
629 PublicKey::from_str(
630 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
631 )
632 .unwrap(),
633 PublicKey::from_str(
634 "023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c54",
635 )
636 .unwrap(),
637 ]),
638 refund_keys: Some(vec![PublicKey::from_str(
639 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
640 )
641 .unwrap()]),
642 num_sigs: Some(2),
643 sig_flag: SigFlag::SigAll,
644 num_sigs_refund: None,
645 };
646
647 let secret: Nut10Secret = SpendingConditions::new_p2pk(data, Some(conditions))
648 .try_into()
649 .unwrap();
650
651 let secret_str = serde_json::to_string(&secret).unwrap();
652
653 let secret_der: Nut10Secret = serde_json::from_str(&secret_str).unwrap();
654
655 assert_eq!(secret_der, secret);
656 }
657
658 #[test]
659 fn sign_proof() {
660 let secret_key =
661 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
662 .unwrap();
663
664 let signing_key_two =
665 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
666 .unwrap();
667
668 let signing_key_three =
669 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
670 .unwrap();
671 let v_key: PublicKey = secret_key.public_key();
672 let v_key_two: PublicKey = signing_key_two.public_key();
673 let v_key_three: PublicKey = signing_key_three.public_key();
674
675 let conditions = Conditions {
676 locktime: Some(21000000000),
677 pubkeys: Some(vec![v_key_two, v_key_three]),
678 refund_keys: Some(vec![v_key]),
679 num_sigs: Some(2),
680 sig_flag: SigFlag::SigInputs,
681 num_sigs_refund: None,
682 };
683
684 let secret: Secret = SpendingConditions::new_p2pk(v_key, Some(conditions))
685 .try_into()
686 .unwrap();
687
688 let mut proof = Proof {
689 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
690 amount: Amount::ZERO,
691 secret,
692 c: PublicKey::from_str(
693 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
694 )
695 .unwrap(),
696 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
697 dleq: None,
698 p2pk_e: None,
699 };
700
701 proof.sign_p2pk(secret_key).unwrap();
702 proof.sign_p2pk(signing_key_two).unwrap();
703
704 assert!(proof.verify_p2pk().is_ok());
705 }
706
707 #[test]
708 fn test_p2pk_witness_is_empty_tracks_signatures() {
709 assert!(P2PKWitness::default().is_empty());
710 assert!(!P2PKWitness {
711 signatures: vec!["signature".to_string()],
712 }
713 .is_empty());
714 }
715
716 #[test]
717 fn test_enforce_sig_flag_keeps_highest_required_signature_count() {
718 let proofs = vec![proof_for_enforce_sig_flag(2), proof_for_enforce_sig_flag(3)]
719 .into_iter()
720 .collect();
721
722 let enforced = enforce_sig_flag(proofs);
723
724 assert_eq!(enforced.sig_flag, SigFlag::SigAll);
725 assert_eq!(enforced.sigs_required, 3);
726 assert_eq!(enforced.pubkeys.len(), 6);
727 }
728
729 #[test]
730 fn test_verify() {
731 let json: &str = r#"{
733 "amount":1,
734 "secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]",
735 "C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
736 "id":"009a1f293253e41e",
737 "witness":"{\"signatures\":[\"60f3c9b766770b46caac1d27e1ae6b77c8866ebaeba0b9489fe6a15a837eaa6fcd6eaa825499c72ac342983983fd3ba3a8a41f56677cc99ffd73da68b59e1383\"]}"
738 }"#;
739 let valid_proof: Proof = serde_json::from_str(json).unwrap();
740
741 valid_proof.verify_p2pk().unwrap();
742 assert!(valid_proof.verify_p2pk().is_ok());
743
744 let invalid_proof = r#"{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"3426df9730d365a9d18d79bed2f3e78e9172d7107c55306ac5ddd1b2d065893366cfa24ff3c874ebf1fc22360ba5888ddf6ff5dbcb9e5f2f5a1368f7afc64f15\"]}"}"#;
746
747 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
748
749 assert!(invalid_proof.verify_p2pk().is_err());
750 }
751
752 #[test]
753 fn verify_multi_sig() {
754 let valid_proof = r#"{"amount":0,"secret":"[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\",\"9a72ca2d4d5075be5b511ee48dbc5e45f259bcf4a4e8bf18587f433098a9cd61ff9737dc6e8022de57c76560214c4568377792d4c2c6432886cc7050487a1f22\"]}"}"#;
756
757 let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap();
758
759 assert!(valid_proof.verify_p2pk().is_ok());
760
761 let invalid_proof = r#"{"amount":0,"secret":"[\"P2PK\",{\"nonce\":\"0ed3fcb22c649dd7bbbdcca36e0c52d4f0187dd3b6a19efcc2bfbebb5f85b2a1\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\"]}"}"#;
763
764 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
765
766 assert!(invalid_proof.verify_p2pk().is_err());
768 }
769
770 #[test]
771 fn test_valid_signatures_duplicate() {
772 let secret_key = SecretKey::generate();
773 let pubkey = secret_key.public_key();
774 let msg = b"test message";
775 let sig = secret_key.sign(msg).unwrap();
776
777 assert_eq!(valid_signatures(msg, &[pubkey], &[sig]).unwrap(), 1);
779
780 let res = valid_signatures(msg, &[pubkey], &[sig, sig]);
782 assert!(res.is_err());
783 assert!(matches!(res.unwrap_err(), Error::DuplicateSignature));
784 }
785
786 #[test]
787 fn test_blinded_message_verify_p2pk_duplicate_sig() {
788 let secret_key = SecretKey::generate();
789 let pubkey = secret_key.public_key();
790
791 let mut blinded_message = BlindedMessage {
792 amount: Amount::ZERO,
793 blinded_secret: pubkey,
794 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
795 witness: None,
796 };
797
798 blinded_message.sign_p2pk(secret_key).unwrap();
800
801 if let Some(Witness::P2PKWitness(p2pk_witness)) = &mut blinded_message.witness {
803 let sig = p2pk_witness.signatures[0].clone();
804 p2pk_witness.signatures.push(sig);
805 }
806
807 let res = blinded_message.verify_p2pk(&vec![pubkey], 1);
809 assert!(res.is_err());
810 assert!(
811 matches!(res.unwrap_err(), Error::DuplicateSignature),
812 "Expected DuplicateSignature error"
813 );
814 }
815
816 #[test]
817 fn test_blinded_message_verify_p2pk_accepts_single_valid_signature() {
818 let secret_key = SecretKey::generate();
819 let pubkey = secret_key.public_key();
820
821 let mut blinded_message = BlindedMessage {
822 amount: Amount::ZERO,
823 blinded_secret: pubkey,
824 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
825 witness: None,
826 };
827
828 blinded_message.sign_p2pk(secret_key).unwrap();
829
830 assert!(blinded_message.verify_p2pk(&vec![pubkey], 1).is_ok());
831 }
832
833 #[test]
834 fn test_blinded_message_verify_p2pk_duplicate_xonly() {
835 let secret_key = SecretKey::generate();
836 let pubkey = secret_key.public_key();
837
838 let mut bytes = pubkey.to_bytes();
840 bytes[0] = if bytes[0] == 0x02 { 0x03 } else { 0x02 };
841 let pubkey_alt = PublicKey::from_slice(&bytes).unwrap();
842
843 let mut blinded_message = BlindedMessage {
844 amount: Amount::ZERO,
845 blinded_secret: pubkey,
846 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
847 witness: None,
848 };
849
850 blinded_message.sign_p2pk(secret_key).unwrap();
852
853 let res = blinded_message.verify_p2pk(&vec![pubkey, pubkey_alt], 1);
855 assert!(res.is_err());
856 assert!(
857 matches!(res.unwrap_err(), Error::DuplicateSignature),
858 "Expected DuplicateSignature error for same x-only pubkeys"
859 );
860 }
861
862 #[test]
866 fn test_duplicate_key_in_main_pathway() {
867 let secret_key = SecretKey::generate();
868 let pubkey = secret_key.public_key();
869 let mut bytes = pubkey.to_bytes();
870 bytes[0] = 0x02;
871 let pk_02 = PublicKey::from_slice(&bytes).unwrap();
872 bytes[0] = 0x03;
873 let pk_03 = PublicKey::from_slice(&bytes).unwrap();
874
875 let conditions = Conditions {
877 locktime: None,
878 pubkeys: Some(vec![pk_03]),
879 refund_keys: None,
880 num_sigs: Some(2),
881 sig_flag: SigFlag::SigInputs,
882 num_sigs_refund: None,
883 };
884
885 let secret: Secret = Nut10Secret::new(
886 Kind::P2PK,
887 crate::nuts::SecretData::new(pk_02.to_hex(), Some(conditions)),
888 )
889 .try_into()
890 .unwrap();
891
892 let mut proof = Proof {
893 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
894 amount: Amount::ZERO,
895 secret,
896 c: PublicKey::from_str(
897 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
898 )
899 .unwrap(),
900 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
901 dleq: None,
902 p2pk_e: None,
903 };
904
905 proof.sign_p2pk(secret_key.clone()).unwrap();
907 if let Some(Witness::P2PKWitness(w)) = &mut proof.witness {
909 w.signatures.push(w.signatures[0].clone());
910 }
911
912 let res = proof.verify_p2pk();
913 assert!(res.is_err(), "Expected an error but got {:?}", res);
914 let err = res.unwrap_err();
915 assert!(
916 matches!(err, Error::DuplicatePubkey),
917 "Expected DuplicatePubkey, got {:?}",
918 err
919 );
920 }
921
922 #[test]
923 fn test_duplicate_key_in_both_pathways() {
924 let secret_key = SecretKey::generate();
925 let pubkey = secret_key.public_key();
926 let mut bytes = pubkey.to_bytes();
927 bytes[0] = 0x02;
928 let pk_02 = PublicKey::from_slice(&bytes).unwrap();
929 bytes[0] = 0x03;
930 let pk_03 = PublicKey::from_slice(&bytes).unwrap();
931
932 let conditions = Conditions {
934 locktime: Some(0),
935 pubkeys: None,
936 refund_keys: Some(vec![pk_03, pk_02]), num_sigs: None,
938 sig_flag: SigFlag::SigInputs,
939 num_sigs_refund: Some(2),
940 };
941
942 let secret: Secret = Nut10Secret::new(
943 Kind::P2PK,
944 crate::nuts::SecretData::new(secret_key.public_key().to_hex(), Some(conditions)),
945 )
946 .try_into()
947 .unwrap();
948
949 let proof = Proof {
950 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
951 amount: Amount::ZERO,
952 secret,
953 c: PublicKey::from_str(
954 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
955 )
956 .unwrap(),
957 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
958 dleq: None,
959 p2pk_e: None,
960 };
961
962 let res = proof.verify_p2pk();
963 assert!(res.is_err(), "Expected an error but got {:?}", res);
964 let err = res.unwrap_err();
965 assert!(
966 matches!(err, Error::DuplicatePubkey),
967 "Expected DuplicatePubkey, got {:?}",
968 err
969 );
970 }
971
972 #[test]
973 fn verify_refund() {
974 let valid_proof = r#"{"amount":1,"id":"009a1f293253e41e","secret":"[\"P2PK\",{\"nonce\":\"902685f492ef3bb2ca35a47ddbba484a3365d143b9776d453947dcbf1ddf9689\",\"data\":\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"03142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","witness":"{\"signatures\":[\"710507b4bc202355c91ea3c147c0d0189c75e179d995e566336afd759cb342bcad9a593345f559d9b9e108ac2c9b5bd9f0b4b6a295028a98606a0a2e95eb54f7\"]}"}"#;
975
976 let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap();
977 assert!(valid_proof.verify_p2pk().is_ok());
978
979 let invalid_proof = r#"{"amount":1,"id":"009a1f293253e41e","secret":"[\"P2PK\",{\"nonce\":\"64c46e5d30df27286166814b71b5d69801704f23a7ad626b05688fbdb48dcc98\",\"data\":\"026f6a2b1d709dbca78124a9f30a742985f7eddd894e72f637f7085bf69b997b9a\",\"tags\":[[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"03142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"locktime\",\"21\"],[\"n_sigs\",\"2\"],[\"refund\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\"],[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","witness":"{\"signatures\":[\"f661d3dc046d636d47cb3d06586da42c498f0300373d1c2a4f417a44252cdf3809bce207c8888f934dba0d2b1671f1b8622d526840f2d5883e571b462630c1ff\"]}"}"#;
980
981 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
982
983 assert!(invalid_proof.verify_p2pk().is_err());
984 }
985
986 #[test]
987 fn sig_with_non_refund_keys_after_locktime() {
988 let secret_key =
989 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
990 .unwrap();
991
992 let signing_key_two =
993 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
994 .unwrap();
995
996 let signing_key_three =
997 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
998 .unwrap();
999 let v_key: PublicKey = secret_key.public_key();
1000 let v_key_two: PublicKey = signing_key_two.public_key();
1001 let v_key_three: PublicKey = signing_key_three.public_key();
1002
1003 let conditions = Conditions {
1004 locktime: Some(21),
1005 pubkeys: Some(vec![v_key_three]),
1006 refund_keys: Some(vec![v_key, v_key_two]),
1007 num_sigs: None,
1008 sig_flag: SigFlag::SigInputs,
1009 num_sigs_refund: Some(2),
1010 };
1011
1012 let secret: Secret = SpendingConditions::new_p2pk(v_key, Some(conditions))
1013 .try_into()
1014 .unwrap();
1015
1016 let mut proof = Proof {
1017 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
1018 amount: Amount::ZERO,
1019 secret,
1020 c: PublicKey::from_str(
1021 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
1022 )
1023 .unwrap(),
1024 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
1025 dleq: None,
1026 p2pk_e: None,
1027 };
1028
1029 proof.sign_p2pk(signing_key_three.clone()).unwrap();
1030
1031 assert!(proof.verify_p2pk().is_ok());
1034
1035 proof.witness = None;
1036
1037 proof.sign_p2pk(secret_key).unwrap();
1040 assert!(
1041 proof.verify_p2pk().is_ok(),
1042 "Data key signature should satisfy primary path"
1043 );
1044
1045 proof.sign_p2pk(signing_key_two).unwrap();
1047 assert!(proof.verify_p2pk().is_ok());
1048 }
1049
1050 fn create_test_proof(secret: Secret, pubkey: PublicKey, id: &str) -> Proof {
1052 Proof {
1053 keyset_id: Id::from_str(id).unwrap(),
1054 amount: Amount::ZERO,
1055 secret,
1056 c: pubkey,
1057 witness: None,
1058 dleq: None,
1059 p2pk_e: None,
1060 }
1061 }
1062
1063 fn create_test_secret(pubkey: PublicKey, conditions: Conditions) -> Secret {
1064 SpendingConditions::new_p2pk(pubkey, Some(conditions))
1065 .try_into()
1066 .unwrap()
1067 }
1068
1069 fn create_test_blinded_msg(pubkey: PublicKey) -> BlindedMessage {
1070 BlindedMessage {
1071 amount: Amount::ZERO,
1072 blinded_secret: pubkey,
1073 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
1074 witness: None,
1075 }
1076 }
1077
1078 #[test]
1079 fn test_melt_sig_all_basic_signing() {
1080 let secret_key =
1081 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1082 .unwrap();
1083 let pubkey = secret_key.public_key();
1084
1085 let conditions = Conditions {
1087 sig_flag: SigFlag::SigAll,
1088 ..Default::default()
1089 };
1090
1091 let secret = create_test_secret(pubkey, conditions);
1092 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
1093 let blinded_msg = create_test_blinded_msg(pubkey);
1094
1095 let mut melt = MeltRequest::new(
1097 QuoteId::UUID(Uuid::new_v4()),
1098 vec![proof],
1099 Some(vec![blinded_msg]),
1100 );
1101
1102 assert!(
1104 melt.verify_spending_conditions().is_err(),
1105 "Unsigned melt request should fail verification"
1106 );
1107
1108 assert!(
1110 melt.sign_sig_all(secret_key).is_ok(),
1111 "Signing should succeed"
1112 );
1113
1114 assert!(
1116 melt.verify_spending_conditions().is_ok(),
1117 "Signed melt request should pass verification"
1118 );
1119 }
1120
1121 #[test]
1122 fn test_melt_sig_all_unauthorized_key() {
1123 let secret_key =
1124 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1125 .unwrap();
1126 let pubkey = secret_key.public_key();
1127
1128 let conditions = Conditions {
1130 sig_flag: SigFlag::SigAll,
1131 pubkeys: None,
1132 ..Default::default()
1133 };
1134
1135 let secret = create_test_secret(pubkey, conditions);
1136 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
1137
1138 let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof], None);
1139
1140 let unauthorized_key =
1142 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
1143 .unwrap();
1144 melt.sign_sig_all(unauthorized_key).unwrap();
1145
1146 assert!(
1148 melt.verify_spending_conditions().is_err(),
1149 "Verification should fail with unauthorized key signature"
1150 );
1151 }
1152
1153 #[test]
1154 fn test_melt_sig_all_wrong_flag() {
1155 let secret_key =
1156 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1157 .unwrap();
1158 let pubkey = secret_key.public_key();
1159
1160 let conditions = Conditions {
1162 sig_flag: SigFlag::SigInputs,
1163 pubkeys: None,
1164 ..Default::default()
1165 };
1166
1167 let secret = create_test_secret(pubkey, conditions);
1168 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
1169
1170 let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof], None);
1171
1172 melt.sign_sig_all(secret_key).unwrap();
1174
1175 assert!(
1177 melt.verify_spending_conditions().is_err(),
1178 "Verification should fail with SIG_INPUTS flag when expecting SIG_ALL"
1179 );
1180 }
1181
1182 #[test]
1183 fn test_melt_sig_all_multiple_inputs() {
1184 let secret_key =
1185 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1186 .unwrap();
1187 let pubkey = secret_key.public_key();
1188
1189 let conditions = Conditions {
1191 sig_flag: SigFlag::SigAll,
1192 ..Default::default()
1193 };
1194
1195 let secret = create_test_secret(pubkey, conditions);
1196
1197 let proof1 = create_test_proof(secret.clone(), pubkey, "009a1f293253e41e");
1199 let proof2 = create_test_proof(secret, pubkey, "009a1f293253e41f");
1200
1201 let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof1, proof2], None);
1202
1203 assert!(
1205 melt.sign_sig_all(secret_key).is_ok(),
1206 "Signing with multiple matching inputs should succeed"
1207 );
1208 assert!(
1209 melt.verify_spending_conditions().is_ok(),
1210 "Verification should succeed with multiple matching inputs"
1211 );
1212 }
1213
1214 #[test]
1215 fn test_melt_sig_all_mismatched_inputs() {
1216 let secret_key =
1217 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1218 .unwrap();
1219 let pubkey = secret_key.public_key();
1220
1221 let conditions1 = Conditions {
1223 sig_flag: SigFlag::SigAll,
1224 ..Default::default()
1225 };
1226 let secret1 = create_test_secret(pubkey, conditions1.clone());
1227 let proof1 = create_test_proof(secret1, pubkey, "009a1f293253e41e");
1228
1229 let conditions2 = conditions1.clone();
1231 let secret2: Secret = SpendingConditions::new_p2pk(
1232 PublicKey::from_str(
1233 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
1234 )
1235 .unwrap(),
1236 Some(conditions2),
1237 )
1238 .try_into()
1239 .unwrap();
1240 let proof2 = create_test_proof(secret2, pubkey, "009a1f293253e41f");
1241
1242 let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof1, proof2], None);
1243
1244 melt.sign_sig_all(secret_key).unwrap();
1246
1247 assert!(
1249 melt.verify_spending_conditions().is_err(),
1250 "Verification should fail with mismatched input secrets"
1251 );
1252 }
1253
1254 #[test]
1255 fn test_melt_sig_all_multiple_signatures() {
1256 let secret_key1 =
1257 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1258 .unwrap();
1259 let pubkey1 = secret_key1.public_key();
1260
1261 let secret_key2 =
1262 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
1263 .unwrap();
1264 let pubkey2 = secret_key2.public_key();
1265
1266 let conditions = Conditions {
1268 sig_flag: SigFlag::SigAll,
1269 num_sigs: Some(2),
1270 pubkeys: Some(vec![pubkey2]),
1271 ..Default::default()
1272 };
1273
1274 let secret = create_test_secret(pubkey1, conditions);
1275 let proof = create_test_proof(secret, pubkey1, "009a1f293253e41e");
1276
1277 let mut melt = MeltRequest::new(
1278 Uuid::new_v4(),
1279 vec![proof],
1280 Some(vec![create_test_blinded_msg(
1281 SecretKey::generate().public_key(),
1282 )]),
1283 );
1284
1285 assert!(
1287 melt.sign_sig_all(secret_key1).is_ok(),
1288 "First signature should succeed"
1289 );
1290 assert!(
1291 melt.verify_spending_conditions().is_err(),
1292 "Single signature should not verify when two required"
1293 );
1294
1295 assert!(
1297 melt.sign_sig_all(secret_key2).is_ok(),
1298 "Second signature should succeed"
1299 );
1300
1301 assert!(
1302 melt.verify_spending_conditions().is_ok(),
1303 "Both signatures should verify successfully"
1304 );
1305 }
1306
1307 #[test]
1308 fn test_melt_sig_all_message_components() {
1309 let secret_key =
1310 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1311 .unwrap();
1312 let pubkey = secret_key.public_key();
1313
1314 let conditions = Conditions {
1315 sig_flag: SigFlag::SigAll,
1316 pubkeys: None,
1317 ..Default::default()
1318 };
1319
1320 let secret = create_test_secret(pubkey, conditions);
1321 let proof = create_test_proof(secret.clone(), pubkey, "009a1f293253e41e");
1322 let blinded_msg = create_test_blinded_msg(pubkey);
1323 let quote_id = Uuid::new_v4();
1324
1325 let melt = MeltRequest::new(quote_id, vec![proof], Some(vec![blinded_msg.clone()]));
1326
1327 let msg = melt.sig_all_msg_to_sign();
1329
1330 assert!(
1332 msg.contains(&secret.to_string()),
1333 "Message should contain secret"
1334 );
1335 assert!(
1336 msg.contains(&blinded_msg.blinded_secret.to_hex()),
1337 "Message should contain blinded message in hex format"
1338 );
1339 assert!(
1340 msg.contains("e_id.to_string()),
1341 "Message should contain quote ID"
1342 );
1343 }
1344
1345 #[test]
1347 fn test_sig_all_swap_single_sig() {
1348 let valid_swap = r#"{
1350 "inputs": [
1351 {
1352 "amount": 2,
1353 "id": "00bfa73302d12ffd",
1354 "secret": "[\"P2PK\",{\"nonce\":\"c7f280eb55c1e8564e03db06973e94bc9b666d9e1ca42ad278408fe625950303\",\"data\":\"030d8acedfe072c9fa449a1efe0817157403fbec460d8e79f957966056e5dd76c1\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1355 "C": "02c97ee3d1db41cf0a3ddb601724be8711a032950811bf326f8219c50c4808d3cd",
1356 "witness": "{\"signatures\":[\"ce017ca25b1b97df2f72e4b49f69ac26a240ce14b3690a8fe619d41ccc42d3c1282e073f85acd36dc50011638906f35b56615f24e4d03e8effe8257f6a808538\"]}"
1357 }
1358 ],
1359 "outputs": [
1360 {
1361 "amount": 2,
1362 "id": "00bfa73302d12ffd",
1363 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1364 }
1365 ]
1366}"#;
1367
1368 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1369
1370 let msg_to_sign = valid_swap.sig_all_msg_to_sign();
1372 assert_eq!(
1373 msg_to_sign,
1374 "[\"P2PK\",{\"nonce\":\"c7f280eb55c1e8564e03db06973e94bc9b666d9e1ca42ad278408fe625950303\",\"data\":\"030d8acedfe072c9fa449a1efe0817157403fbec460d8e79f957966056e5dd76c1\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]02c97ee3d1db41cf0a3ddb601724be8711a032950811bf326f8219c50c4808d3cd2038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1375 );
1376
1377 use bitcoin::hashes::{sha256, Hash};
1379 let msg_hash = sha256::Hash::hash(msg_to_sign.as_bytes());
1380 assert_eq!(
1381 msg_hash.to_string(),
1382 "de7f9e3ca0fcc5ed3258fcf83dbf1be7fa78a5ed6da7bf2aa60d61e9dc6eb09a"
1383 );
1384
1385 assert!(
1386 valid_swap.verify_spending_conditions().is_ok(),
1387 "Valid SIG_ALL swap request should verify"
1388 );
1389 }
1390
1391 #[test]
1392 fn test_sig_all_swap_single_sig_2() {
1393 let valid_swap = r#"{
1395 "inputs": [
1396 {
1397 "amount": 2,
1398 "id": "00bfa73302d12ffd",
1399 "secret": "[\"P2PK\",{\"nonce\":\"c7f280eb55c1e8564e03db06973e94bc9b666d9e1ca42ad278408fe625950303\",\"data\":\"030d8acedfe072c9fa449a1efe0817157403fbec460d8e79f957966056e5dd76c1\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1400 "C": "02c97ee3d1db41cf0a3ddb601724be8711a032950811bf326f8219c50c4808d3cd",
1401 "witness": "{\"signatures\":[\"ce017ca25b1b97df2f72e4b49f69ac26a240ce14b3690a8fe619d41ccc42d3c1282e073f85acd36dc50011638906f35b56615f24e4d03e8effe8257f6a808538\"]}"
1402 }
1403 ],
1404 "outputs": [
1405 {
1406 "amount": 2,
1407 "id": "00bfa73302d12ffd",
1408 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1409 }
1410 ]
1411}"#;
1412
1413 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1414
1415 assert!(
1416 valid_swap.verify_spending_conditions().is_ok(),
1417 "Valid SIG_ALL swap request should verify"
1418 );
1419 }
1420
1421 #[test]
1422 fn test_sig_all_multiple_secrets() {
1423 let invalid_swap = r#"{
1425 "inputs": [
1426 {
1427 "amount": 1,
1428 "id": "00bfa73302d12ffd",
1429 "secret": "[\"P2PK\",{\"nonce\":\"fa6dd3fac9086c153878dec90b9e37163d38ff2ecf8b37db6470e9d185abbbae\",\"data\":\"033b42b04e659fed13b669f8b16cdaffc3ee5738608810cf97a7631d09bd01399d\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1430 "C": "024d232312bab25af2e73f41d56864d378edca9109ae8f76e1030e02e585847786",
1431 "witness": "{\"signatures\":[\"27b4d260a1186e3b62a26c0d14ffeab3b9f7c3889e78707b8fd3836b473a00601afbd53a2288ad20a624a8bbe3344453215ea075fc0ce479dd8666fd3d9162cc\"]}"
1432 },
1433 {
1434 "amount": 2,
1435 "id": "00bfa73302d12ffd",
1436 "secret": "[\"P2PK\",{\"nonce\":\"4007b21fc5f5b1d4920bc0a08b158d98fd0fb2b0b0262b57ff53c6c5d6c2ae8c\",\"data\":\"033b42b04e659fed13b669f8b16cdaffc3ee5738608810cf97a7631d09bd01399d\",\"tags\":[[\"locktime\",\"122222222222222\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1437 "C": "02417400f2af09772219c831501afcbab4efb3b2e75175635d5474069608deb641"
1438 }
1439 ],
1440 "outputs": [
1441 {
1442 "amount": 1,
1443 "id": "00bfa73302d12ffd",
1444 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1445 },
1446 {
1447 "amount": 1,
1448 "id": "00bfa73302d12ffd",
1449 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1450 },
1451 {
1452 "amount": 1,
1453 "id": "00bfa73302d12ffd",
1454 "B_": "02c0d4fce02a7a0f09e3f1bca952db910b17e81a7ebcbce62cd8dcfb127d21e37b"
1455 }
1456 ]
1457}"#;
1458
1459 let invalid_swap: SwapRequest = serde_json::from_str(invalid_swap).unwrap();
1460
1461 assert!(
1462 invalid_swap.verify_spending_conditions().is_err(),
1463 "Invalid swap with multiple secrets shouldn't be accepted"
1464 );
1465 }
1466
1467 #[test]
1468 fn test_sig_all_multiple_signatures_provided() {
1469 let valid_swap = r#"{
1471 "inputs": [
1472 {
1473 "amount": 2,
1474 "id": "00bfa73302d12ffd",
1475 "secret": "[\"P2PK\",{\"nonce\":\"04bfd885fc982d553711092d037fdceb7320fd8f96b0d4fd6d31a65b83b94272\",\"data\":\"0275e78025b558dbe6cb8fdd032a2e7613ca14fda5c1f4c4e3427f5077a7bd90e4\",\"tags\":[[\"pubkeys\",\"035163650bbd5ed4be7693f40f340346ba548b941074e9138b67ef6c42755f3449\",\"02817d22a8edc44c4141e192995a7976647c335092199f9e076a170c7336e2f5cc\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1476 "C": "03866a09946562482c576ca989d06371e412b221890804c7da8887d321380755be",
1477 "witness": "{\"signatures\":[\"be1d72c5ca16a93c5a34f25ec63ce632ddc3176787dac363321af3fd0f55d1927e07451bc451ffe5c682d76688ea9925d7977dffbb15bd79763b527f474734b0\",\"669d6d10d7ed35395009f222f6c7bdc28a378a1ebb72ee43117be5754648501da3bedf2fd6ff0c7849ac92683538c60af0af504102e40f2d8daca8e08b1ca16b\"]}"
1478 }
1479 ],
1480 "outputs": [
1481 {
1482 "amount": 2,
1483 "id": "00bfa73302d12ffd",
1484 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1485 }
1486 ]
1487}"#;
1488
1489 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1490
1491 assert!(
1492 valid_swap.verify_spending_conditions().is_ok(),
1493 "Valid swap with multiple signatures should be accepted"
1494 );
1495 }
1496
1497 #[test]
1498 fn test_sig_all_mixed_pubkeys_and_refund() {
1499 let invalid_swap = r#"{
1501 "inputs": [
1502 {
1503 "amount": 2,
1504 "id": "00bfa73302d12ffd",
1505 "secret": "[\"P2PK\",{\"nonce\":\"3e9253419a11f0a541dd6baeddecf8356fc864b5d061f12f05632bc3aee6b5c4\",\"data\":\"0343cca0e48ce9e3fdcddba4637ff8cdbf6f5ed9cfdf1873e63827e760f0ed4db5\",\"tags\":[[\"pubkeys\",\"0235e0a719f8b046cee90f55a59b1cdd6ca75ce23e49cbcd82c9e5b7310e21ebcd\",\"020443f98b356e021bae82bdfc05ff433cab21e27fca9ab7b0995aedb2e7aabc43\"],[\"locktime\",\"100\"],[\"n_sigs\",\"2\"],[\"refund\",\"026b432e62b041bf9cdae534203739c73fa506c9a2d6aa58a52bc601a1dec421e1\",\"02e3494a2e07e7f6e7d4567e0da7a563592bff1e121df2383667f15b83e9168a9e\"],[\"n_sigs_refund\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1506 "C": "026c12ee3bffa5c617debcf823bf1af6a9b47145b699f2737bba3394f0893eb869",
1507 "witness": "{\"signatures\":[\"bfe884145ce6512331324321c3946dfd812428a53656b108b59d26559a186ba2ab45e5be9ce94e2dff0d09078e25ccb82d06a8b3a63cd3dc67065b8f77292776\",\"236e5cc9c30f85a893a29a4302e41e6f2015caef4229f28fa65e2f5c9d55515cc9a1852093a81a5095055d85fd55bf4da124e55354b56e0a39e83b58b0afc197\"]}"
1508 }
1509 ],
1510 "outputs": [
1511 {
1512 "amount": 1,
1513 "id": "00bfa73302d12ffd",
1514 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1515 },
1516 {
1517 "amount": 1,
1518 "id": "00bfa73302d12ffd",
1519 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1520 }
1521 ]
1522}"#;
1523
1524 let invalid_swap: SwapRequest = serde_json::from_str(invalid_swap).unwrap();
1525
1526 assert!(
1527 invalid_swap.verify_spending_conditions().is_err(),
1528 "Invalid swap with mixed refunds and pubkeys shouldn't be accepted"
1529 );
1530 }
1531
1532 #[test]
1533 fn test_sig_all_locktime_passed_with_valid_refund_key_sigs() {
1534 let valid_swap = r#"{
1536 "inputs": [
1537 {
1538 "amount": 2,
1539 "id": "00bfa73302d12ffd",
1540 "secret": "[\"P2PK\",{\"nonce\":\"9ea35553beb18d553d0a53120d0175a0991ca6109370338406eed007b26eacd1\",\"data\":\"02af21e09300af92e7b48c48afdb12e22933738cfb9bba67b27c00c679aae3ec25\",\"tags\":[[\"locktime\",\"1\"],[\"refund\",\"02637c19143c58b2c58bd378400a7b82bdc91d6dedaeb803b28640ef7d28a887ac\",\"0345c7fdf7ec7c8e746cca264bf27509eb4edb9ac421f8fbfab1dec64945a4d797\"],[\"n_sigs_refund\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1541 "C": "03dd83536fbbcbb74ccb3c87147df26753fd499cc2c095f74367fff0fb459c312e",
1542 "witness": "{\"signatures\":[\"23b58ef28cd22f3dff421121240ddd621deee83a3bc229fd67019c2e338d91e2c61577e081e1375dbab369307bba265e887857110ca3b4bd949211a0a298805f\",\"7e75948ef1513564fdcecfcbd389deac67c730f7004f8631ba90c0844d3e8c0cf470b656306877df5141f65fd3b7e85445a8452c3323ab273e6d0d44843817ed\"]}"
1543 }
1544 ],
1545 "outputs": [
1546 {
1547 "amount": 2,
1548 "id": "00bfa73302d12ffd",
1549 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1550 }
1551 ]
1552}"#;
1553
1554 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1555
1556 assert!(
1557 valid_swap.verify_spending_conditions().is_ok(),
1558 "Valid post-locktime swap with refund keys should be accepted"
1559 );
1560 }
1561
1562 #[test]
1563 fn test_sig_all_htlc_and_pubkey() {
1564 let valid_swap = r#"{
1566 "inputs": [
1567 {
1568 "amount": 2,
1569 "id": "00bfa73302d12ffd",
1570 "secret": "[\"HTLC\",{\"nonce\":\"d730dd70cd7ec6e687829857de8e70aab2b970712f4dbe288343eca20e63c28c\",\"data\":\"ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5\",\"tags\":[[\"pubkeys\",\"0350cda8a1d5257dbd6ba8401a9a27384b9ab699e636e986101172167799469b14\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1571 "C": "03ff6567e2e6c31db5cb7189dab2b5121930086791c93899e4eff3dda61cb57273",
1572 "witness": "{\"preimage\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"signatures\":[\"a4c00a9ad07f9936e404494fda99a9b935c82d7c053173b304b8663124c81d4b00f64a225f5acf41043ca52b06382722bd04ded0fbeb0fcc404eed3b24778b88\"]}"
1573 }
1574 ],
1575 "outputs": [
1576 {
1577 "amount": 2,
1578 "id": "00bfa73302d12ffd",
1579 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1580 }
1581 ]
1582}"#;
1583
1584 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1585
1586 assert!(
1587 valid_swap.verify_spending_conditions().is_ok(),
1588 "Valid swap with htlc and pubkey should be accepted"
1589 );
1590 }
1591
1592 #[test]
1593 fn test_sig_all_enforce_locktime_with_only_refund_signed() {
1594 let invalid_swap = r#"{
1596 "inputs": [
1597 {
1598 "amount": 2,
1599 "id": "00bfa73302d12ffd",
1600 "secret": "[\"HTLC\",{\"nonce\":\"512c4045f12fdfd6f55059669c189e040c37c1ce2f8be104ed6aec296acce4e9\",\"data\":\"ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5\",\"tags\":[[\"pubkeys\",\"03ba83defd31c63f8841d188f0d41b5bb3af1bb3c08d0ba46f8f1d26a4d45e8cad\"],[\"locktime\",\"4854185133\"],[\"refund\",\"032f1008a79c722e93a1b4b853f85f38283f9ef74ee4c5c91293eb1cc3c5e46e34\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1601 "C": "02207abeff828146f1fc3909c74613d5605bd057f16791994b3c91f045b39a6939",
1602 "witness": "{\"preimage\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"signatures\":[\"7816d57871bde5be2e4281065dbe5b15f641d8f1ed9437a3ae556464d6f9b8a0a2e6660337a915f2c26dce1453a416daf682b8fb593b67a0750fce071e0759b9\"]}"
1603 }
1604 ],
1605 "outputs": [
1606 {
1607 "amount": 1,
1608 "id": "00bfa73302d12ffd",
1609 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1610 },
1611 {
1612 "amount": 1,
1613 "id": "00bfa73302d12ffd",
1614 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1615 }
1616 ]
1617}"#;
1618
1619 let invalid_swap: SwapRequest = serde_json::from_str(invalid_swap).unwrap();
1620
1621 assert!(
1622 invalid_swap.verify_spending_conditions().is_err(),
1623 "Invalid swap with pre-locktime conditions not met shouldn't be accepted"
1624 );
1625 }
1626
1627 #[test]
1628 fn test_sig_all_htlc_post_locktime() {
1629 let valid_swap = r#"{
1633 "inputs": [
1634 {
1635 "amount": 2,
1636 "id": "00bfa73302d12ffd",
1637 "secret": "[\"HTLC\",{\"nonce\":\"c9b0fabb8007c0db4bef64d5d128cdcf3c79e8bb780c3294adf4c88e96c32647\",\"data\":\"ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5\",\"tags\":[[\"pubkeys\",\"039e6ec7e922abb4162235b3a42965eb11510b07b7461f6b1a17478b1c9c64d100\"],[\"locktime\",\"1\"],[\"refund\",\"02ce1bbd2c9a4be8029c9a6435ad601c45677f5cde81f8a7f0ed535e0039d0eb6c\",\"03c43c00ff57f63cfa9e732f0520c342123e21331d0121139f1b636921eeec095f\"],[\"n_sigs_refund\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1638 "C": "0344b6f1471cf18a8cbae0e624018c816be5e3a9b04dcb7689f64173c1ae90a3a5",
1639 "witness": "{\"preimage\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"signatures\":[\"98e21672d409cc782c720f203d8284f0af0c8713f18167499f9f101b7050c3e657fb0e57478ebd8bd561c31aa6c30f4cd20ec38c73f5755b7b4ddee693bca5a5\",\"693f40129dbf905ed9c8008081c694f72a36de354f9f4fa7a61b389cf781f62a0ae0586612fb2eb504faaf897fefb6742309186117f4743bcebcb8e350e975e2\"]}"
1640 }
1641 ],
1642 "outputs": [
1643 {
1644 "amount": 2,
1645 "id": "00bfa73302d12ffd",
1646 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1647 }
1648 ]
1649}"#;
1650
1651 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1652
1653 assert!(
1654 valid_swap.verify_spending_conditions().is_ok(),
1655 "Valid post-locktime swap with htlc should be accepted"
1656 );
1657 }
1658
1659 #[test]
1660 fn test_sig_all_swap_mismatched_inputs() {
1661 let invalid_swap = r#"{
1663 "inputs": [
1664 {
1665 "amount": 1,
1666 "id": "00bfa73302d12ffd",
1667 "secret": "[\"P2PK\",{\"nonce\":\"e2a221fe361f19d95c5c3312ccff3ffa075b4fe37beec99de85a6ee70568385b\",\"data\":\"03dad7f9c588f4cbb55c2e1b7b802fa2bbc63a614d9e9ecdf56a8e7ee8ca65be86\",\"tags\":[[\"pubkeys\",\"025f2af63fd65ca97c3bde4070549683e72769d28def2f1cd3d63576cd9c2ffa6c\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1668 "C": "02a79c09b0605f4e7a21976b511cc7be01cdaeac54b29645258c84f2e74bff13f6",
1669 "witness": "{\"signatures\":[\"b42c7af7e98ca4e3bba8b73702120970286196340b340c21299676dbc7b10cafaa7baeb243affc01afce3218616cf8b3f6b4baaf4414fedb31b0c6653912f769\",\"17781910e2d806cae464f8a692929ee31124c0cd7eaf1e0d94292c6cbc122da09076b649080b8de9201f87d83b99fe04e33d701817eb287d1cdd9c4d0410e625\"]}"
1670 },
1671 {
1672 "amount": 2,
1673 "id": "00bfa73302d12ffd",
1674 "secret": "[\"P2PK\",{\"nonce\":\"973c78b5e84c0986209dc14ba57682baf38fa4c1ea60c4c5f6834779a1a13e6d\",\"data\":\"02685df03c777837bc7155bd2d0d8e98eede7e956a4cd8a9edac84532584e68e0f\",\"tags\":[[\"pubkeys\",\"025f2af63fd65ca97c3bde4070549683e72769d28def2f1cd3d63576cd9c2ffa6c\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1675 "C": "02be48c564cf6a7b4d09fbaf3a78a153a79f687ac4623e48ce1788effc3fb1e024"
1676 }
1677 ],
1678 "outputs": [
1679 {
1680 "amount": 1,
1681 "id": "00bfa73302d12ffd",
1682 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1683 },
1684 {
1685 "amount": 1,
1686 "id": "00bfa73302d12ffd",
1687 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1688 },
1689 {
1690 "amount": 1,
1691 "id": "00bfa73302d12ffd",
1692 "B_": "02c0d4fce02a7a0f09e3f1bca952db910b17e81a7ebcbce62cd8dcfb127d21e37b"
1693 }
1694 ]
1695 }"#;
1696
1697 let invalid_swap: SwapRequest = serde_json::from_str(invalid_swap).unwrap();
1698 assert!(
1699 invalid_swap.verify_spending_conditions().is_err(),
1700 "Invalid SIG_ALL swap request should fail verification"
1701 );
1702 }
1703
1704 #[test]
1705 fn test_sig_all_mixed_pubkeys_and_refund_pubkeys() {
1706 let invalidsig_all_swap = r#"{
1708 "inputs": [
1709 {
1710 "amount": 2,
1711 "id": "00bfa73302d12ffd",
1712 "secret": "[\"P2PK\",{\"nonce\":\"cc93775c74df53d7c97eb37f72018d166a45ce4f4c65f11c4014b19acd02bd2f\",\"data\":\"02f515ab63e973e0dadfc284bf2ef330b01aa99c3ff775d88272f9c17afa25568c\",\"tags\":[[\"pubkeys\",\"026925e5bb547a3ec6b2d9b8934e23b882f54f89b2a9f45300bf81fd1b311d9c97\"],[\"n_sigs\",\"2\"],[\"refund\",\"03c8cd46b7e6592c41df38bc54dce2555586e7adbb15cc80a02d1a05829677286d\"],[\"n_sigs_refund\",\"1\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1713 "C": "03f6d40d0ab11f4082ee7e977534a6fcd151394d647cde4ab122157e6d755410fd",
1714 "witness": "{\"signatures\":[\"a9f61c2b7161a50839bf7f3e2e1cb9bd7bdacd2ce62c0d458a5969db44646dad409a282241b412e8b191cc7432bcfebf16ad72339a9fb966ca71c8bd971662cc\",\"aa778ec15fe9408e1989c712c823e833f33d45780b9a25555ea76004b05d495e99fd326914484f92e7e91f919ee575e79add26e9d4bbe4349d7333d7e0021af7\"]}"
1715 }
1716 ],
1717 "outputs": [
1718 {
1719 "amount": 1,
1720 "id": "00bfa73302d12ffd",
1721 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1722 },
1723 {
1724 "amount": 1,
1725 "id": "00bfa73302d12ffd",
1726 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1727 }
1728 ]
1729}"#;
1730
1731 let invalid_swap: SwapRequest = serde_json::from_str(invalidsig_all_swap).unwrap();
1732 assert!(
1733 invalid_swap.verify_spending_conditions().is_err(),
1734 "Invalid SIG_ALL swap request should fail verification"
1735 );
1736 }
1737
1738 #[test]
1739 fn test_sig_all_htlc_unexpired_timelock_refund_signature() {
1740 let invalidsig_all_swap = r#"{
1742 "inputs": [
1743 {
1744 "amount": 2,
1745 "id": "00bfa73302d12ffd",
1746 "secret": "[\"HTLC\",{\"nonce\":\"b6f0c59ea4084369d4196e1318477121c2451d59ae767060e083cb6846e6bbe0\",\"data\":\"ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5\",\"tags\":[[\"pubkeys\",\"0329fdfde4becf9ff871129653ff6464bb2c922fbcba442e6166a8b5849599604f\"],[\"locktime\",\"4854185133\"],[\"refund\",\"035fcf4a5393e4bdef0567aa0b8a9555edba36e5fcb283f3bbce52d86a687817d3\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1747 "C": "024fbbee3f3cc306a48841ba327435b64de20b8b172b98296a3e573c673d52562b",
1748 "witness": "{\"preimage\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"signatures\":[\"7526819070a291f731e77acfbe9da71ddc0f748fd2a3e6c2510bc83c61daaa656df345afa3832fe7cb94352c8835a4794ad499760729c0be29417387d1fc3cd1\"]}"
1749 }
1750 ],
1751 "outputs": [
1752 {
1753 "amount": 2,
1754 "id": "00bfa73302d12ffd",
1755 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1756 }
1757 ]
1758}"#;
1759
1760 let invalid_swap: SwapRequest = serde_json::from_str(invalidsig_all_swap).unwrap();
1761 assert!(
1762 invalid_swap.verify_spending_conditions().is_err(),
1763 "Invalid SIG_ALL swap request should fail verification"
1764 );
1765 }
1766
1767 #[test]
1769 fn test_sig_all_melt() {
1770 let valid_melt = r#"{
1773 "quote": "cF8911fzT88aEi1d-6boZZkq5lYxbUSVs-HbJxK0",
1774 "inputs": [
1775 {
1776 "amount": 2,
1777 "id": "00bfa73302d12ffd",
1778 "secret": "[\"P2PK\",{\"nonce\":\"bbf9edf441d17097e39f5095a3313ba24d3055ab8a32f758ff41c10d45c4f3de\",\"data\":\"029116d32e7da635c8feeb9f1f4559eb3d9b42d400f9d22a64834d89cde0eb6835\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1779 "C": "02a9d461ff36448469dccf828fa143833ae71c689886ac51b62c8d61ddaa10028b",
1780 "witness": "{\"signatures\":[\"478224fbe715e34f78cb33451db6fcf8ab948afb8bd04ff1a952c92e562ac0f7c1cb5e61809410635be0aa94d0448f7f7959bd5762cc3802b0a00ff58b2da747\"]}"
1781 }
1782 ],
1783 "outputs": [
1784 {
1785 "amount": 0,
1786 "id": "00bfa73302d12ffd",
1787 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1788 }
1789 ]
1790}"#;
1791
1792 let valid_melt: MeltRequest<String> = serde_json::from_str(valid_melt).unwrap();
1793
1794 let msg_to_sign = valid_melt.sig_all_msg_to_sign();
1796 assert_eq!(
1797 msg_to_sign,
1798 r#"["P2PK",{"nonce":"bbf9edf441d17097e39f5095a3313ba24d3055ab8a32f758ff41c10d45c4f3de","data":"029116d32e7da635c8feeb9f1f4559eb3d9b42d400f9d22a64834d89cde0eb6835","tags":[["sigflag","SIG_ALL"]]}]02a9d461ff36448469dccf828fa143833ae71c689886ac51b62c8d61ddaa10028b0038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39cF8911fzT88aEi1d-6boZZkq5lYxbUSVs-HbJxK0"#
1799 );
1800
1801 use bitcoin::hashes::{sha256, Hash};
1803 let msg_hash = sha256::Hash::hash(msg_to_sign.as_bytes());
1804 assert_eq!(
1805 msg_hash.to_string(),
1806 "9efa1067cc7dc870f4074f695115829c3cd817a6866c3b84e9814adf3c3cf262"
1807 );
1808
1809 assert!(
1810 valid_melt.verify_spending_conditions().is_ok(),
1811 "Valid SIG_ALL melt request should verify"
1812 );
1813 }
1814
1815 #[test]
1816 fn test_sig_all_valid_melt() {
1817 let valid_melt = r#"{
1819 "quote": "cF8911fzT88aEi1d-6boZZkq5lYxbUSVs-HbJxK0",
1820 "inputs": [
1821 {
1822 "amount": 2,
1823 "id": "00bfa73302d12ffd",
1824 "secret": "[\"P2PK\",{\"nonce\":\"bbf9edf441d17097e39f5095a3313ba24d3055ab8a32f758ff41c10d45c4f3de\",\"data\":\"029116d32e7da635c8feeb9f1f4559eb3d9b42d400f9d22a64834d89cde0eb6835\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1825 "C": "02a9d461ff36448469dccf828fa143833ae71c689886ac51b62c8d61ddaa10028b",
1826 "witness": "{\"signatures\":[\"478224fbe715e34f78cb33451db6fcf8ab948afb8bd04ff1a952c92e562ac0f7c1cb5e61809410635be0aa94d0448f7f7959bd5762cc3802b0a00ff58b2da747\"]}"
1827 }
1828 ],
1829 "outputs": [
1830 {
1831 "amount": 0,
1832 "id": "00bfa73302d12ffd",
1833 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1834 }
1835 ]
1836}"#;
1837
1838 let valid_melt: MeltRequest<String> = serde_json::from_str(valid_melt).unwrap();
1839
1840 assert!(
1841 valid_melt.verify_spending_conditions().is_ok(),
1842 "Valid SIG_ALL melt request should verify"
1843 );
1844 }
1845
1846 #[test]
1847 fn test_sig_all_valid_multisig_melt() {
1848 let valid_melt = r#"{
1850 "quote": "Db3qEMVwFN2tf_1JxbZp29aL5cVXpSMIwpYfyOVF",
1851 "inputs": [
1852 {
1853 "amount": 2,
1854 "id": "00bfa73302d12ffd",
1855 "secret": "[\"P2PK\",{\"nonce\":\"68d7822538740e4f9c9ebf5183ef6c4501c7a9bca4e509ce2e41e1d62e7b8a99\",\"data\":\"0394e841bd59aeadce16380df6174cb29c9fea83b0b65b226575e6d73cc5a1bd59\",\"tags\":[[\"pubkeys\",\"033d892d7ad2a7d53708b7a5a2af101cbcef69522bd368eacf55fcb4f1b0494058\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1856 "C": "03a70c42ec9d7192422c7f7a3ad017deda309fb4a2453fcf9357795ea706cc87a9",
1857 "witness": "{\"signatures\":[\"ed739970d003f703da2f101a51767b63858f4894468cc334be04aa3befab1617a81e3eef093441afb499974152d279e59d9582a31dc68adbc17ffc22a2516086\",\"f9efe1c70eb61e7ad8bd615c50ff850410a4135ea73ba5fd8e12a734743ad045e575e9e76ea5c52c8e7908d3ad5c0eaae93337e5c11109e52848dc328d6757a2\"]}"
1858 }
1859 ],
1860 "outputs": [
1861 {
1862 "amount": 0,
1863 "id": "00bfa73302d12ffd",
1864 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1865 }
1866 ]
1867}"#;
1868
1869 let valid_melt: MeltRequest<String> = serde_json::from_str(valid_melt).unwrap();
1870
1871 assert!(
1872 valid_melt.verify_spending_conditions().is_ok(),
1873 "Valid SIG_ALL melt request should verify"
1874 );
1875 }
1876
1877 #[test]
1878 fn test_sig_all_melt_wrong_sig() {
1879 let invalid_melt = r#"{
1881 "inputs": [{
1882 "amount": 1,
1883 "secret": "[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1884 "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
1885 "id": "009a1f293253e41e",
1886 "witness": "{\"signatures\":[\"3426df9730d365a9d18d79bed2f3e78e9172d7107c55306ac5ddd1b2d065893366cfa24ff3c874ebf1fc22360ba5888ddf6ff5dbcb9e5f2f5a1368f7afc64f15\"]}"
1887 }],
1888 "quote": "test_quote_123",
1889 "outputs": null
1890 }"#;
1891
1892 let invalid_melt: MeltRequest<String> = serde_json::from_str(invalid_melt).unwrap();
1893 assert!(
1894 invalid_melt.verify_spending_conditions().is_err(),
1895 "Invalid SIG_ALL melt request should fail verification"
1896 );
1897 }
1898
1899 #[test]
1900 #[ignore]
1901 fn test_sig_all_melt_msg_to_sign() {
1902 let multisig_melt = r#"{
1903 "quote": "uHwJ-f6HFAC-lU2dMw0KOu6gd5S571FXQQHioYMD",
1904 "inputs": [
1905 {
1906 "amount": 4,
1907 "id": "00bfa73302d12ffd",
1908 "secret": "[\"P2PK\",{\"nonce\":\"f5c26c928fb4433131780105eac330338bb9c0af2b2fd29fad9e4f18c4a96d84\",\"data\":\"03c4840e19277822bfeecf104dcd3f38d95b33249983ac6fed755869f23484fb2a\",\"tags\":[[\"pubkeys\",\"0256dcc53d9330e0bc6e9b3d47c26287695aba9fe55cafdde6f46ef56e09582bfb\"],[\"n_sigs\",\"1\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1909 "C": "02174667f98114abeb741f4964bdc88a3b86efde0afa38f791094c1e07e5df3beb",
1910 "witness": "{\"signatures\":[\"abeeceba92bc7d1c514844ddb354d1e88a9776dfb55d3cdc5c289240386e401c3d983b68371ce5530e86c8fc4ff90195982a262f83fa8a5335b43e75af5f5fc7\"]}"
1911 }
1912 ],
1913 "outputs": [
1914 {
1915 "amount": 0,
1916 "id": "00bfa73302d12ffd",
1917 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1918 }
1919 ]
1920}"#;
1921
1922 let multisig_melt: MeltRequest<String> = serde_json::from_str(multisig_melt).unwrap();
1923
1924 assert!(
1925 multisig_melt.verify_spending_conditions().is_ok(),
1926 "melt request with SIG_ALL should succeed"
1927 );
1928
1929 let msg_to_sign = multisig_melt.sig_all_msg_to_sign();
1930
1931 assert_eq!(
1932 msg_to_sign,
1933 r#"["P2PK",{"nonce":"f5c26c928fb4433131780105eac330338bb9c0af2b2fd29fad9e4f18c4a96d84","data":"03c4840e19277822bfeecf104dcd3f38d95b33249983ac6fed755869f23484fb2a","tags":[["pubkeys","0256dcc53d9330e0bc6e9b3d47c26287695aba9fe55cafdde6f46ef56e09582bfb"],["n_sigs","1"],["sigflag","SIG_ALL"]]}]02174667f98114abeb741f4964bdc88a3b86efde0afa38f791094c1e07e5df3beb000bfa73302d12ffd038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39uHwJ-f6HFAC-lU2dMw0KOu6gd5S571FXQQHioYMD"#
1934 );
1935 }
1936
1937 #[test]
1938 #[ignore]
1939 fn test_sig_all_melt_multi_sig() {
1940 let multisig_melt = r#"{
1942 "quote": "wYHbJm5S1GTL28tDHoUAwcvb-31vu5kfDhnLxV9D",
1943 "inputs": [
1944 {
1945 "amount": 4,
1946 "id": "00bfa73302d12ffd",
1947 "secret": "[\"P2PK\",{\"nonce\":\"1705e988054354b703bc9103472cc5646ec76ed557517410186fa827c19c444d\",\"data\":\"024c8b5ec0e560f1fc77d7872ab75dd10a00af73a8ba715b81093b800849cb21fb\",\"tags\":[[\"pubkeys\",\"028d32bc906b3724724244812c450f688c548020f5d5a8c1d6cd1075650933d1a3\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1948 "C": "02f2a0ff12c4dd95f2476662f1df49e5126f09a5ea1f3ce13b985db57661953072",
1949 "witness": "{\"signatures\":[\"a98a2616716d7813394a54ddc82234e5c47f0ddbddb98ccd1cad25236758fa235c8ae64d9fccd15efbe0ad5eba52a3df8433e9f1c05bc50defcb9161a5bd4bc4\",\"dd418cbbb23276dab8d72632ee77de730b932a3c6e8e15bc8802cef13db0b346915fe6e04e7fae03c3b5af026e25f71a24dc05b28135f0a9b69bc6c7289b6b8d\"]}"
1950 }
1951 ],
1952 "outputs": [
1953 {
1954 "amount": 0,
1955 "id": "00bfa73302d12ffd",
1956 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1957 }
1958 ]
1959}"#;
1960
1961 let multisig_melt: MeltRequest<String> = serde_json::from_str(multisig_melt).unwrap();
1962 assert!(
1963 multisig_melt.verify_spending_conditions().is_ok(),
1964 "Multi-sig SIG_ALL melt request should verify with both signatures"
1965 );
1966
1967 let insufficient_sigs_melt = r#"{
1969 "inputs": [{
1970 "amount": 1,
1971 "secret": "[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_ALL\"],[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"]]}]",
1972 "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
1973 "id": "009a1f293253e41e",
1974 "witness": "{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\"]}"
1975 }],
1976 "quote": "test_quote_123",
1977 "outputs": null
1978 }"#;
1979
1980 let insufficient_sigs_melt: MeltRequest<String> =
1981 serde_json::from_str(insufficient_sigs_melt).unwrap();
1982 assert!(
1983 insufficient_sigs_melt.verify_spending_conditions().is_err(),
1984 "Multi-sig SIG_ALL melt request should fail with insufficient signatures"
1985 );
1986 }
1987
1988 fn create_test_keys() -> (SecretKey, PublicKey) {
1990 let secret_key =
1991 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1992 .unwrap();
1993 let pubkey = secret_key.public_key();
1994 (secret_key, pubkey)
1995 }
1996
1997 #[test]
1998 fn test_sig_all_basic_signing_verification() {
1999 let (secret_key, pubkey) = create_test_keys();
2000
2001 let conditions = Conditions {
2003 sig_flag: SigFlag::SigAll,
2004 ..Default::default()
2005 };
2006
2007 let secret = create_test_secret(pubkey, conditions);
2008 let proof1 = create_test_proof(secret.clone(), pubkey, "009a1f293253e41e");
2009 let proof2 = create_test_proof(secret, pubkey, "009a1f293253e41f");
2010 let blinded_msg = create_test_blinded_msg(pubkey);
2011
2012 let mut swap = SwapRequest::new(vec![proof1, proof2], vec![blinded_msg]);
2014 assert!(
2015 swap.verify_spending_conditions().is_err(),
2016 "Unsigned swap should fail verification"
2017 );
2018
2019 assert!(
2020 swap.sign_sig_all(secret_key).is_ok(),
2021 "Signing should succeed"
2022 );
2023
2024 println!("{}", serde_json::to_string(&swap).unwrap());
2025
2026 assert!(
2027 swap.verify_spending_conditions().is_ok(),
2028 "Signed swap should pass verification"
2029 );
2030 }
2031
2032 #[test]
2033 fn test_sig_all_unauthorized_key() {
2034 let (_secret_key, pubkey) = create_test_keys();
2035
2036 let conditions = Conditions {
2037 sig_flag: SigFlag::SigAll,
2038 ..Default::default()
2039 };
2040
2041 let secret = create_test_secret(pubkey, conditions);
2042 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
2043 let blinded_msg = create_test_blinded_msg(pubkey);
2044
2045 let unauthorized_key =
2047 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
2048 .unwrap();
2049
2050 let mut swap = SwapRequest::new(vec![proof], vec![blinded_msg]);
2051
2052 swap.sign_sig_all(unauthorized_key).unwrap();
2054
2055 assert!(
2057 swap.verify_spending_conditions().is_err(),
2058 "Verification should fail with unauthorized key signature"
2059 );
2060 }
2061
2062 #[test]
2063 fn test_sig_all_mismatched_secrets() {
2064 let (secret_key, pubkey) = create_test_keys();
2065
2066 let conditions = Conditions {
2067 sig_flag: SigFlag::SigAll,
2068 ..Default::default()
2069 };
2070
2071 let secret1 = create_test_secret(pubkey, conditions.clone());
2073
2074 let different_secret: Secret = SpendingConditions::new_p2pk(
2076 PublicKey::from_str(
2077 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2078 )
2079 .unwrap(),
2080 Some(conditions),
2081 )
2082 .try_into()
2083 .unwrap();
2084
2085 let proof1 = create_test_proof(secret1, pubkey, "009a1f293253e41e");
2086 let proof2 = create_test_proof(different_secret, pubkey, "009a1f293253e41f");
2087 let blinded_msg = create_test_blinded_msg(pubkey);
2088
2089 let mut swap = SwapRequest::new(vec![proof1, proof2], vec![blinded_msg]);
2090
2091 swap.sign_sig_all(secret_key).unwrap();
2093
2094 assert!(
2096 swap.verify_spending_conditions().is_err(),
2097 "Verification should fail with mismatched secrets"
2098 );
2099 }
2100
2101 #[test]
2102 fn test_sig_all_wrong_flag() {
2103 let (secret_key, pubkey) = create_test_keys();
2104
2105 let sig_inputs_conditions = Conditions {
2107 sig_flag: SigFlag::SigInputs,
2108 ..Default::default()
2109 };
2110
2111 let secret = create_test_secret(pubkey, sig_inputs_conditions);
2112 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
2113 let blinded_msg = create_test_blinded_msg(pubkey);
2114
2115 let mut swap = SwapRequest::new(vec![proof], vec![blinded_msg]);
2116
2117 swap.sign_sig_all(secret_key).unwrap();
2119
2120 assert!(
2122 swap.verify_spending_conditions().is_err(),
2123 "Verification should fail with SIG_INPUTS flag when sign_sig_all was used"
2124 );
2125 }
2126
2127 #[test]
2128 fn test_sig_all_multiple_signatures() {
2129 let (secret_key1, pubkey1) = create_test_keys();
2130 let secret_key2 =
2131 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
2132 .unwrap();
2133 let pubkey2 = secret_key2.public_key();
2134
2135 let conditions = Conditions {
2137 num_sigs: Some(2),
2138 sig_flag: SigFlag::SigAll,
2139 pubkeys: Some(vec![pubkey2]),
2140 ..Default::default()
2141 };
2142
2143 let secret = create_test_secret(pubkey1, conditions);
2144 let proof = create_test_proof(secret, pubkey1, "009a1f293253e41e");
2145 let blinded_msg = create_test_blinded_msg(pubkey1);
2146
2147 let mut swap = SwapRequest::new(vec![proof], vec![blinded_msg]);
2148
2149 assert!(
2151 swap.sign_sig_all(secret_key1).is_ok(),
2152 "First signature should succeed"
2153 );
2154 assert!(
2155 swap.verify_spending_conditions().is_err(),
2156 "Single signature should not verify when two required"
2157 );
2158
2159 assert!(
2161 swap.sign_sig_all(secret_key2).is_ok(),
2162 "Second signature should succeed"
2163 );
2164
2165 assert!(
2166 swap.verify_spending_conditions().is_ok(),
2167 "Both signatures should verify"
2168 );
2169 }
2170
2171 #[test]
2172 fn test_conditions_valid_multisig() {
2173 let pubkey1 = PublicKey::from_str(
2174 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2175 )
2176 .unwrap();
2177 let pubkey2 = PublicKey::from_str(
2178 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2179 )
2180 .unwrap();
2181
2182 let result = Conditions::new(
2184 None,
2185 Some(vec![pubkey1, pubkey2]),
2186 None,
2187 Some(2),
2188 None,
2189 None,
2190 );
2191 assert!(result.is_ok(), "2-of-3 multisig should be valid");
2192 }
2193
2194 #[test]
2195 fn test_conditions_impossible_multisig() {
2196 let pubkey1 = PublicKey::from_str(
2197 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2198 )
2199 .unwrap();
2200 let pubkey2 = PublicKey::from_str(
2201 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2202 )
2203 .unwrap();
2204
2205 let result = Conditions::new(
2207 None,
2208 Some(vec![pubkey1, pubkey2]),
2209 None,
2210 Some(5),
2211 None,
2212 None,
2213 );
2214 assert!(result.is_err(), "5-of-3 multisig should be impossible");
2215 let err = result.unwrap_err();
2216 assert!(
2217 matches!(
2218 err,
2219 crate::nut10::Error::NUT11(Error::ImpossibleMultisigConfiguration {
2220 required: 5,
2221 available: 3,
2222 })
2223 ),
2224 "Expected ImpossibleMultisigConfiguration, got: {err:?}"
2225 );
2226 }
2227
2228 #[test]
2229 fn test_conditions_valid_single_sig_no_pubkeys() {
2230 let result = Conditions::new(None, None, None, Some(1), None, None);
2232 assert!(result.is_ok(), "1-of-1 (data key only) should be valid");
2233 }
2234
2235 #[test]
2236 fn test_conditions_impossible_no_pubkeys() {
2237 let result = Conditions::new(None, None, None, Some(2), None, None);
2239 assert!(result.is_err(), "2-of-1 multisig should be impossible");
2240 let err = result.unwrap_err();
2241 assert!(
2242 matches!(
2243 err,
2244 crate::nut10::Error::NUT11(Error::ImpossibleMultisigConfiguration {
2245 required: 2,
2246 available: 1,
2247 })
2248 ),
2249 "Expected ImpossibleMultisigConfiguration, got: {err:?}"
2250 );
2251 }
2252
2253 #[test]
2254 fn test_conditions_impossible_refund_multisig() {
2255 let refund_key1 = PublicKey::from_str(
2256 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2257 )
2258 .unwrap();
2259
2260 let result = Conditions::new(None, None, Some(vec![refund_key1]), None, None, Some(3));
2262 assert!(
2263 result.is_err(),
2264 "3-of-1 refund multisig should be impossible"
2265 );
2266 let err = result.unwrap_err();
2267 assert!(
2268 matches!(
2269 err,
2270 crate::nut10::Error::NUT11(Error::ImpossibleRefundMultisigConfiguration {
2271 required: 3,
2272 available: 1,
2273 })
2274 ),
2275 "Expected ImpossibleRefundMultisigConfiguration, got: {err:?}"
2276 );
2277 }
2278
2279 #[test]
2280 fn test_conditions_valid_refund_multisig() {
2281 let refund_key1 = PublicKey::from_str(
2282 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2283 )
2284 .unwrap();
2285 let refund_key2 = PublicKey::from_str(
2286 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2287 )
2288 .unwrap();
2289
2290 let result = Conditions::new(
2292 Some(crate::util::unix_time() + 3600),
2293 None,
2294 Some(vec![refund_key1, refund_key2]),
2295 None,
2296 None,
2297 Some(2),
2298 );
2299 assert!(result.is_ok(), "2-of-2 refund multisig should be valid");
2300 }
2301
2302 fn duplicate_x_only_pair() -> (PublicKey, PublicKey) {
2305 let secret_key = SecretKey::generate();
2306 let mut bytes = secret_key.public_key().to_bytes();
2307 bytes[0] = 0x02;
2308 let pk_02 = PublicKey::from_slice(&bytes).unwrap();
2309 bytes[0] = 0x03;
2310 let pk_03 = PublicKey::from_slice(&bytes).unwrap();
2311 (pk_02, pk_03)
2312 }
2313
2314 fn assert_duplicate_pubkey(err: crate::nut10::Error) {
2315 assert!(
2316 matches!(err, crate::nut10::Error::NUT11(Error::DuplicatePubkey)),
2317 "Expected DuplicatePubkey, got: {err:?}"
2318 );
2319 }
2320
2321 #[test]
2325 fn test_duplicate_key_rejected_at_construction() {
2326 let (pk_02, pk_03) = duplicate_x_only_pair();
2327
2328 let conditions = Conditions {
2329 locktime: None,
2330 pubkeys: Some(vec![pk_03]),
2331 refund_keys: None,
2332 num_sigs: None,
2333 sig_flag: SigFlag::SigInputs,
2334 num_sigs_refund: None,
2335 };
2336
2337 let result: Result<Secret, _> =
2338 SpendingConditions::new_p2pk(pk_02, Some(conditions)).try_into();
2339
2340 assert_duplicate_pubkey(result.expect_err("duplicate key should be rejected"));
2341 }
2342
2343 #[test]
2346 fn test_duplicate_key_multisig_reports_the_duplicate() {
2347 let (pk_02, pk_03) = duplicate_x_only_pair();
2348
2349 let conditions = Conditions {
2350 locktime: None,
2351 pubkeys: Some(vec![pk_03]),
2352 refund_keys: None,
2353 num_sigs: Some(2),
2354 sig_flag: SigFlag::SigInputs,
2355 num_sigs_refund: None,
2356 };
2357
2358 let result: Result<Secret, _> =
2359 SpendingConditions::new_p2pk(pk_02, Some(conditions)).try_into();
2360
2361 assert_duplicate_pubkey(result.expect_err("2-of-2 over one key should be rejected"));
2362 }
2363
2364 #[test]
2366 fn test_impossible_multisig_is_still_reported() {
2367 let data = SecretKey::generate().public_key();
2368 let other = SecretKey::generate().public_key();
2369
2370 let conditions = Conditions {
2371 locktime: None,
2372 pubkeys: Some(vec![other]),
2373 refund_keys: None,
2374 num_sigs: Some(3),
2375 sig_flag: SigFlag::SigInputs,
2376 num_sigs_refund: None,
2377 };
2378
2379 let result: Result<Secret, _> =
2380 SpendingConditions::new_p2pk(data, Some(conditions)).try_into();
2381 let err = result.expect_err("3-of-2 should be rejected");
2382
2383 assert!(
2384 matches!(
2385 err,
2386 crate::nut10::Error::NUT11(Error::ImpossibleMultisigConfiguration {
2387 required: 3,
2388 available: 2,
2389 })
2390 ),
2391 "Expected ImpossibleMultisigConfiguration, got: {err:?}"
2392 );
2393 }
2394
2395 #[test]
2396 fn test_duplicate_refund_key_rejected_at_construction() {
2397 let (pk_02, pk_03) = duplicate_x_only_pair();
2398
2399 let conditions = Conditions {
2400 locktime: Some(unix_time() + 3600),
2401 pubkeys: None,
2402 refund_keys: Some(vec![pk_02, pk_03]),
2403 num_sigs: None,
2404 sig_flag: SigFlag::SigInputs,
2405 num_sigs_refund: None,
2406 };
2407
2408 let result: Result<Secret, _> =
2409 SpendingConditions::new_p2pk(SecretKey::generate().public_key(), Some(conditions))
2410 .try_into();
2411
2412 assert_duplicate_pubkey(result.expect_err("duplicate refund key should be rejected"));
2413 }
2414
2415 #[test]
2418 fn test_key_may_appear_in_both_pathways() {
2419 let pubkey = SecretKey::generate().public_key();
2420
2421 let conditions = Conditions {
2422 locktime: Some(unix_time() + 3600),
2423 pubkeys: None,
2424 refund_keys: Some(vec![pubkey]),
2425 num_sigs: None,
2426 sig_flag: SigFlag::SigInputs,
2427 num_sigs_refund: None,
2428 };
2429
2430 let secret: Result<Secret, _> =
2431 SpendingConditions::new_p2pk(pubkey, Some(conditions)).try_into();
2432
2433 assert!(
2434 secret.is_ok(),
2435 "a key in both pathways is not a duplicate: {secret:?}"
2436 );
2437 }
2438
2439 #[test]
2440 fn test_htlc_duplicate_pubkeys_rejected_at_construction() {
2441 use bitcoin::hashes::{sha256, Hash};
2442
2443 let (pk_02, pk_03) = duplicate_x_only_pair();
2444
2445 let conditions = Conditions {
2446 locktime: None,
2447 pubkeys: Some(vec![pk_02, pk_03]),
2448 refund_keys: None,
2449 num_sigs: None,
2450 sig_flag: SigFlag::SigInputs,
2451 num_sigs_refund: None,
2452 };
2453
2454 let result: Result<Secret, _> = SpendingConditions::HTLCConditions {
2455 data: sha256::Hash::hash(b"preimage"),
2456 conditions: Some(conditions),
2457 }
2458 .try_into();
2459
2460 assert_duplicate_pubkey(result.expect_err("duplicate HTLC key should be rejected"));
2461 }
2462
2463 #[test]
2464 fn test_htlc_duplicate_refund_key_rejected_at_construction() {
2465 use bitcoin::hashes::{sha256, Hash};
2466
2467 let (pk_02, pk_03) = duplicate_x_only_pair();
2468
2469 let conditions = Conditions {
2470 locktime: Some(unix_time() + 3600),
2471 pubkeys: None,
2472 refund_keys: Some(vec![pk_02, pk_03]),
2473 num_sigs: None,
2474 sig_flag: SigFlag::SigInputs,
2475 num_sigs_refund: None,
2476 };
2477
2478 let result: Result<Secret, _> = SpendingConditions::HTLCConditions {
2479 data: sha256::Hash::hash(b"preimage"),
2480 conditions: Some(conditions),
2481 }
2482 .try_into();
2483
2484 assert_duplicate_pubkey(result.expect_err("duplicate refund key should be rejected"));
2485 }
2486
2487 #[test]
2490 fn test_conditions_new_rejects_duplicates_in_each_list() {
2491 let (pk_02, pk_03) = duplicate_x_only_pair();
2492
2493 let err = Conditions::new(None, Some(vec![pk_02, pk_03]), None, None, None, None)
2494 .expect_err("duplicate pubkeys should be rejected");
2495 assert_duplicate_pubkey(err);
2496
2497 let err = Conditions::new(
2498 Some(unix_time() + 3600),
2499 None,
2500 Some(vec![pk_02, pk_03]),
2501 None,
2502 None,
2503 None,
2504 )
2505 .expect_err("duplicate refund keys should be rejected");
2506 assert_duplicate_pubkey(err);
2507 }
2508
2509 #[test]
2512 fn test_plain_p2pk_is_not_slot_capped() {
2513 let data = SecretKey::generate().public_key();
2514 let pubkeys: Vec<PublicKey> = (0..11)
2515 .map(|_| SecretKey::generate().public_key())
2516 .collect();
2517
2518 let conditions = Conditions {
2519 locktime: None,
2520 pubkeys: Some(pubkeys),
2521 refund_keys: None,
2522 num_sigs: Some(1),
2523 sig_flag: SigFlag::SigInputs,
2524 num_sigs_refund: None,
2525 };
2526
2527 let secret: Secret = SpendingConditions::new_p2pk(data, Some(conditions))
2528 .try_into()
2529 .expect("twelve keys are a valid NUT-11 lock");
2530
2531 let nut10: Nut10Secret = secret.try_into().unwrap();
2532 let requirements = get_pubkeys_and_required_sigs(&nut10, unix_time()).unwrap();
2533
2534 assert_eq!(requirements.pubkeys.len(), 12);
2535 }
2536
2537 #[test]
2540 fn test_plain_htlc_is_not_slot_capped() {
2541 use bitcoin::hashes::{sha256, Hash};
2542
2543 let pubkeys: Vec<PublicKey> = (0..11)
2544 .map(|_| SecretKey::generate().public_key())
2545 .collect();
2546 let refund_key = SecretKey::generate().public_key();
2547
2548 let conditions = Conditions {
2549 locktime: None,
2550 pubkeys: Some(pubkeys.clone()),
2551 refund_keys: None,
2552 num_sigs: Some(1),
2553 sig_flag: SigFlag::SigInputs,
2554 num_sigs_refund: None,
2555 };
2556 let hash = sha256::Hash::hash(b"preimage");
2557
2558 let result: Result<Secret, _> = SpendingConditions::HTLCConditions {
2559 data: hash,
2560 conditions: Some(conditions),
2561 }
2562 .try_into();
2563 assert!(
2564 result.is_ok(),
2565 "eleven pubkeys should build: {:?}",
2566 result.err()
2567 );
2568
2569 let conditions = Conditions {
2570 locktime: Some(unix_time() + 3600),
2571 pubkeys: Some(pubkeys[..10].to_vec()),
2572 refund_keys: Some(vec![refund_key]),
2573 num_sigs: Some(1),
2574 sig_flag: SigFlag::SigInputs,
2575 num_sigs_refund: Some(1),
2576 };
2577
2578 let result: Result<Secret, _> = SpendingConditions::HTLCConditions {
2579 data: hash,
2580 conditions: Some(conditions),
2581 }
2582 .try_into();
2583 assert!(
2584 result.is_ok(),
2585 "ten pubkeys plus a refund key should build: {:?}",
2586 result.err()
2587 );
2588 }
2589
2590 #[test]
2593 fn test_conditions_refund_keys_require_locktime() {
2594 let refund_key = PublicKey::from_str(
2595 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2596 )
2597 .unwrap();
2598
2599 let err = Conditions::new(None, None, Some(vec![refund_key]), None, None, None)
2600 .expect_err("refund keys without a locktime should be rejected");
2601
2602 assert!(
2603 matches!(
2604 err,
2605 crate::nut10::Error::NUT11(Error::RefundKeysRequireLocktime)
2606 ),
2607 "Expected RefundKeysRequireLocktime, got: {err:?}"
2608 );
2609 }
2610
2611 #[test]
2612 fn test_conditions_impossible_refund_no_keys() {
2613 let result = Conditions::new(None, None, None, None, None, Some(1));
2615 assert!(
2616 result.is_err(),
2617 "1-of-0 refund multisig should be impossible"
2618 );
2619 let err = result.unwrap_err();
2620 assert!(
2621 matches!(
2622 err,
2623 crate::nut10::Error::NUT11(Error::ImpossibleRefundMultisigConfiguration {
2624 required: 1,
2625 available: 0,
2626 })
2627 ),
2628 "Expected ImpossibleRefundMultisigConfiguration, got: {err:?}"
2629 );
2630 }
2631
2632 #[test]
2633 fn test_conditions_num_sigs_exact_boundary() {
2634 let pubkey1 = PublicKey::from_str(
2635 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2636 )
2637 .unwrap();
2638 let pubkey2 = PublicKey::from_str(
2639 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2640 )
2641 .unwrap();
2642
2643 let result = Conditions::new(
2645 None,
2646 Some(vec![pubkey1, pubkey2]),
2647 None,
2648 Some(3),
2649 None,
2650 None,
2651 );
2652 assert!(result.is_ok(), "3-of-3 (exact keys count) should be valid");
2653
2654 let result = Conditions::new(
2656 None,
2657 Some(vec![pubkey1, pubkey2]),
2658 None,
2659 Some(4),
2660 None,
2661 None,
2662 );
2663 assert!(result.is_err(), "4-of-3 should be impossible");
2664 }
2665
2666 #[test]
2667 fn test_p2pk_num_sigs_zero_bypasses_signature_requirement() {
2668 let data_pubkey = PublicKey::from_str(
2677 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198",
2678 )
2679 .unwrap();
2680
2681 let conditions = Conditions {
2682 locktime: None,
2683 pubkeys: None,
2684 refund_keys: None,
2685 num_sigs: Some(0), sig_flag: SigFlag::SigInputs,
2687 num_sigs_refund: None,
2688 };
2689
2690 let result: Result<Secret, _> =
2691 SpendingConditions::new_p2pk(data_pubkey, Some(conditions)).try_into();
2692
2693 assert!(matches!(
2694 result,
2695 Err(crate::nut10::Error::NUT11(Error::ZeroSignaturesRequired))
2696 ));
2697 }
2698
2699 #[test]
2700 fn test_sig_all_p2pk_nsigs_zero_bypasses_sig_check() {
2701 let data_pubkey = PublicKey::from_str(
2702 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198",
2703 )
2704 .unwrap();
2705 let extra_pubkey = PublicKey::from_str(
2706 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
2707 )
2708 .unwrap();
2709
2710 let nut10_secret = Nut10Secret::new(
2718 crate::nuts::nut10::Kind::P2PK,
2719 crate::nuts::nut10::SecretData::new(
2720 data_pubkey.to_string(),
2721 Some(vec![
2722 vec!["pubkeys".to_string(), extra_pubkey.to_string()],
2723 vec!["n_sigs".to_string(), "0".to_string()],
2724 vec!["sigflag".to_string(), "SIG_ALL".to_string()],
2725 ]),
2726 ),
2727 );
2728 let conditions_res = crate::nuts::nut10::Conditions::try_from(
2729 nut10_secret.secret_data().tags().cloned().unwrap(),
2730 );
2731 assert!(
2732 conditions_res.is_err(),
2733 "Conditions should fail to parse due to n_sigs=0"
2734 );
2735 }
2736
2737 #[test]
2738 fn test_refund_path_num_sigs_refund_zero_bypasses_signature_check() {
2739 let data_pubkey = PublicKey::from_str(
2749 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198",
2750 )
2751 .unwrap();
2752
2753 let refund_pubkey = PublicKey::from_str(
2754 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
2755 )
2756 .unwrap();
2757
2758 let nut10_secret = Nut10Secret::new(
2759 crate::nuts::nut10::Kind::P2PK,
2760 crate::nuts::nut10::SecretData::new(
2761 data_pubkey.to_string(),
2762 Some(vec![
2763 vec!["locktime".to_string(), "1".to_string()],
2764 vec!["refund".to_string(), refund_pubkey.to_string()],
2765 vec!["n_sigs".to_string(), "1".to_string()],
2766 vec!["n_sigs_refund".to_string(), "0".to_string()],
2767 ]),
2768 ),
2769 );
2770 let conditions_res = crate::nuts::nut10::Conditions::try_from(
2771 nut10_secret.secret_data().tags().cloned().unwrap(),
2772 );
2773 assert!(
2774 conditions_res.is_err(),
2775 "Conditions should fail to parse due to n_sigs=0"
2776 );
2777 }
2778
2779 #[test]
2780 fn test_sig_all_p2pk_nsigs_refund_zero_bypasses_refund_sig_check() {
2781 let data_pubkey = PublicKey::from_str(
2782 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198",
2783 )
2784 .unwrap();
2785 let refund_pubkey = PublicKey::from_str(
2786 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
2787 )
2788 .unwrap();
2789
2790 let nut10_secret = Nut10Secret::new(
2791 crate::nuts::nut10::Kind::P2PK,
2792 crate::nuts::nut10::SecretData::new(
2793 data_pubkey.to_string(),
2794 Some(vec![
2795 vec!["locktime".to_string(), "1".to_string()],
2796 vec!["refund".to_string(), refund_pubkey.to_string()],
2797 vec!["n_sigs".to_string(), "1".to_string()],
2798 vec!["n_sigs_refund".to_string(), "0".to_string()],
2799 vec!["sigflag".to_string(), "SIG_ALL".to_string()],
2800 ]),
2801 ),
2802 );
2803 let conditions_res = crate::nuts::nut10::Conditions::try_from(
2804 nut10_secret.secret_data().tags().cloned().unwrap(),
2805 );
2806 assert!(
2807 conditions_res.is_err(),
2808 "Conditions should fail to parse due to n_sigs_refund=0"
2809 );
2810 }
2811}