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::{get_pubkeys_and_required_sigs, Conditions, SpendingConditionVerification};
19use crate::nuts::nut00::BlindedMessage;
20use crate::util::unix_time;
21use crate::{SpendingConditions, SwapRequest};
22
23pub mod serde_p2pk_witness;
24
25#[derive(Debug, Error)]
27pub enum Error {
28 #[error("Secret is not a p2pk secret")]
30 IncorrectSecretKind,
31 #[error("Witness is not a p2pk witness")]
33 IncorrectWitnessKind,
34 #[error("Locktime in past")]
36 LocktimeInPast,
37 #[error("Invalid signature")]
39 InvalidSignature,
40 #[error("Unknown tag P2PK secret")]
42 UnknownTag,
43 #[error("Unknown sigflag")]
45 UnknownSigFlag,
46 #[error("P2PK spend conditions are not met")]
48 SpendConditionsNotMet,
49 #[error("P2PK required in secret data")]
51 P2PKPubkeyRequired,
52 #[error("Witness signatures not provided")]
54 SignaturesNotProvided,
55 #[error("Duplicate signature from the same pubkey detected")]
57 DuplicateSignature,
58 #[error("Duplicate public key in multisig (same x-coordinate)")]
60 DuplicatePubkey,
61 #[error(
63 "Impossible multisig: required {required} signatures but only {available} keys available"
64 )]
65 ImpossibleMultisigConfiguration {
66 required: u64,
68 available: u64,
70 },
71 #[error("Impossible refund multisig: required {required} signatures but only {available} refund keys available")]
73 ImpossibleRefundMultisigConfiguration {
74 required: u64,
76 available: u64,
78 },
79 #[error("P2PK does not support preimage requirements")]
81 PreimageNotSupportedInP2PK,
82 #[error("SIG_ALL proofs must be verified using a different method")]
84 SigAllNotSupportedHere,
85 #[error("Number of required signatures must be 1 or greater")]
87 ZeroSignaturesRequired,
88 #[error(transparent)]
90 SerdeJsonError(#[from] serde_json::Error),
91 #[error(transparent)]
93 Secp256k1(#[from] bitcoin::secp256k1::Error),
94 #[error(transparent)]
96 NUT01(#[from] crate::nuts::nut01::Error),
97}
98
99#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
101pub struct P2PKWitness {
102 pub signatures: Vec<String>,
104}
105
106impl P2PKWitness {
107 #[inline]
108 pub fn is_empty(&self) -> bool {
110 self.signatures.is_empty()
111 }
112}
113
114impl Proof {
115 pub fn sign_p2pk(&mut self, secret_key: SecretKey) -> Result<(), Error> {
117 let msg: Vec<u8> = self.secret.to_bytes();
118 let signature: Signature = secret_key.sign(&msg)?;
119
120 let signatures = vec![signature.to_string()];
121
122 match self.witness.as_mut() {
123 Some(witness) => {
124 witness.add_signatures(signatures);
125 }
126 None => {
127 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
128 p2pk_witness.add_signatures(signatures);
129 self.witness = Some(p2pk_witness);
130 }
131 };
132
133 Ok(())
134 }
135
136 pub fn verify_p2pk(&self) -> Result<(), Error> {
144 let secret: Nut10Secret = self.secret.clone().try_into()?;
145 let spending_conditions: Conditions = secret
146 .secret_data()
147 .tags()
148 .cloned()
149 .unwrap_or_default()
150 .try_into()
151 .map_err(|_| Error::SpendConditionsNotMet)?;
152
153 if spending_conditions.sig_flag == SigFlag::SigAll {
154 return Err(Error::SigAllNotSupportedHere);
155 }
156
157 if secret.kind() != Kind::P2PK {
158 return Err(Error::IncorrectSecretKind);
159 }
160
161 let now = unix_time();
163 let requirements =
164 super::nut10::get_pubkeys_and_required_sigs(&secret, now).map_err(|err| match err {
165 super::nut10::Error::NUT11(nut11_err) => nut11_err,
166 _ => Error::SpendConditionsNotMet,
167 })?;
168
169 if requirements.preimage_needed {
170 return Err(Error::PreimageNotSupportedInP2PK);
171 }
172
173 let witness_signatures = match &self.witness {
175 Some(witness) => witness.signatures(),
176 None => None,
177 };
178
179 let msg: &[u8] = self.secret.as_bytes();
180
181 {
184 let primary_valid = witness_signatures
185 .as_ref()
186 .and_then(|sigs| {
187 sigs.iter()
188 .map(|s| Signature::from_str(s))
189 .collect::<Result<Vec<_>, _>>()
190 .ok()
191 })
192 .and_then(|sigs| valid_signatures(msg, &requirements.pubkeys, &sigs).ok())
193 .is_some_and(|count| count >= requirements.required_sigs);
194
195 if primary_valid {
196 return Ok(());
197 }
198 }
199
200 {
202 if let Some(refund_path) = &requirements.refund_path {
203 if refund_path.required_sigs == 0 {
205 return Ok(());
206 }
207
208 let refund_valid = witness_signatures
210 .as_ref()
211 .and_then(|sigs| {
212 sigs.iter()
213 .map(|s| Signature::from_str(s))
214 .collect::<Result<Vec<_>, _>>()
215 .ok()
216 })
217 .and_then(|sigs| valid_signatures(msg, &refund_path.pubkeys, &sigs).ok())
218 .is_some_and(|count| count >= refund_path.required_sigs);
219
220 if refund_valid {
221 return Ok(());
222 }
223 }
224 }
225
226 if witness_signatures.is_none() {
228 Err(Error::SignaturesNotProvided)
229 } else {
230 Err(Error::SpendConditionsNotMet)
231 }
232 }
233}
234
235impl SpendingConditions {
236 pub fn new_p2pk(pubkey: PublicKey, conditions: Option<Conditions>) -> Self {
238 Self::P2PKConditions {
239 data: pubkey,
240 conditions,
241 }
242 }
243}
244
245pub(crate) fn extract_signatures_from_witness(
250 witness: &super::Witness,
251) -> Result<Vec<bitcoin::secp256k1::schnorr::Signature>, Error> {
252 use std::str::FromStr;
253
254 let witness_sigs = witness.signatures().ok_or(Error::SignaturesNotProvided)?;
255
256 witness_sigs
257 .iter()
258 .map(|s| bitcoin::secp256k1::schnorr::Signature::from_str(s))
259 .collect::<Result<Vec<_>, _>>()
260 .map_err(|_| Error::InvalidSignature)
261}
262
263pub(crate) fn verify_sig_all_p2pk(first_input: &Proof, msg_to_sign: String) -> Result<(), Error> {
273 let first_secret =
275 Nut10Secret::try_from(&first_input.secret).map_err(|_| Error::IncorrectSecretKind)?;
276
277 let current_time = crate::util::unix_time();
279
280 let requirements = get_pubkeys_and_required_sigs(&first_secret, current_time)
282 .map_err(|_| Error::SpendConditionsNotMet)?;
283
284 debug_assert!(
285 !requirements.preimage_needed,
286 "P2PK should never require preimage"
287 );
288
289 if let Some(refund_path) = &requirements.refund_path {
292 if refund_path.required_sigs == 0 {
293 return Ok(());
294 }
295 }
296
297 let first_witness = first_input
299 .witness
300 .as_ref()
301 .ok_or(Error::SignaturesNotProvided)?;
302
303 {
306 let primary_valid = extract_signatures_from_witness(first_witness)
307 .ok()
308 .and_then(|sigs| {
309 valid_signatures(msg_to_sign.as_bytes(), &requirements.pubkeys, &sigs).ok()
310 })
311 .is_some_and(|count| count >= requirements.required_sigs);
312
313 if primary_valid {
314 return Ok(());
315 }
316 }
317
318 {
320 if let Some(refund_path) = &requirements.refund_path {
321 let signatures = extract_signatures_from_witness(first_witness)?;
322 let valid_sig_count =
323 valid_signatures(msg_to_sign.as_bytes(), &refund_path.pubkeys, &signatures)
324 .map_err(|_| Error::InvalidSignature)?;
325
326 if valid_sig_count >= refund_path.required_sigs {
327 return Ok(());
328 }
329 }
330 }
331
332 Err(Error::SpendConditionsNotMet)
334}
335
336pub(crate) fn valid_signatures(
339 msg: &[u8],
340 pubkeys: &[PublicKey],
341 signatures: &[Signature],
342) -> Result<u64, Error> {
343 let mut verified_pubkeys = HashSet::new();
344
345 for pubkey in pubkeys {
346 for signature in signatures {
347 if pubkey.verify(msg, signature).is_ok() {
348 let x_only = pubkey.x_only_public_key();
350 if !verified_pubkeys.insert(x_only) {
352 return Err(Error::DuplicateSignature);
353 }
354 }
355 }
356 }
357
358 Ok(verified_pubkeys.len() as u64)
359}
360
361impl BlindedMessage {
362 pub fn sign_p2pk(&mut self, secret_key: SecretKey) -> Result<(), Error> {
364 let msg: [u8; 33] = self.blinded_secret.to_bytes();
365 let signature: Signature = secret_key.sign(&msg)?;
366
367 let signatures = vec![signature.to_string()];
368
369 match self.witness.as_mut() {
370 Some(witness) => {
371 witness.add_signatures(signatures);
372 }
373 None => {
374 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
375 p2pk_witness.add_signatures(signatures);
376 self.witness = Some(p2pk_witness);
377 }
378 };
379
380 Ok(())
381 }
382
383 pub fn verify_p2pk(&self, pubkeys: &Vec<PublicKey>, required_sigs: u64) -> Result<(), Error> {
385 let mut verified_pubkeys = HashSet::new();
386 if let Some(witness) = &self.witness {
387 for signature in witness
388 .signatures()
389 .ok_or(Error::SignaturesNotProvided)?
390 .iter()
391 {
392 for v in pubkeys {
393 let msg = &self.blinded_secret.to_bytes();
394 let sig = Signature::from_str(signature)?;
395
396 if v.verify(msg, &sig).is_ok() {
397 let x_only = v.x_only_public_key();
398 if !verified_pubkeys.insert(x_only) {
399 return Err(Error::DuplicateSignature);
400 }
401 } else {
402 tracing::debug!(
403 "Could not verify signature: {sig} on message: {}",
404 self.blinded_secret
405 )
406 }
407 }
408 }
409 }
410
411 let valid_sigs = verified_pubkeys.len() as u64;
412
413 if valid_sigs.ge(&required_sigs) {
414 Ok(())
415 } else {
416 Err(Error::SpendConditionsNotMet)
417 }
418 }
419}
420
421#[derive(
425 Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash,
426)]
427pub enum SigFlag {
428 #[default]
429 SigInputs,
433 SigAll,
435}
436
437impl fmt::Display for SigFlag {
438 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
439 match self {
440 Self::SigAll => write!(f, "SIG_ALL"),
441 Self::SigInputs => write!(f, "SIG_INPUTS"),
442 }
443 }
444}
445
446impl FromStr for SigFlag {
447 type Err = Error;
448 fn from_str(tag: &str) -> Result<Self, Self::Err> {
449 match tag {
450 "SIG_ALL" => Ok(Self::SigAll),
451 "SIG_INPUTS" => Ok(Self::SigInputs),
452 _ => Err(Error::UnknownSigFlag),
453 }
454 }
455}
456
457pub fn enforce_sig_flag(proofs: Proofs) -> EnforceSigFlag {
460 let mut sig_flag = SigFlag::SigInputs;
461 let mut pubkeys = HashSet::new();
462 let mut sigs_required = 1;
463 for proof in proofs {
464 if let Ok(secret) = Nut10Secret::try_from(proof.secret) {
465 if secret.kind().eq(&Kind::P2PK) {
466 if let Ok(verifying_key) = PublicKey::from_str(secret.secret_data().data()) {
467 pubkeys.insert(verifying_key);
468 }
469 }
470
471 if let Some(tags) = secret.secret_data().tags() {
472 if let Ok(conditions) = Conditions::try_from(tags.clone()) {
473 if conditions.sig_flag.eq(&SigFlag::SigAll) {
474 sig_flag = SigFlag::SigAll;
475 }
476
477 if let Some(sigs) = conditions.num_sigs {
478 if sigs > sigs_required {
479 sigs_required = sigs;
480 }
481 }
482
483 if let Some(pubs) = conditions.pubkeys {
484 pubkeys.extend(pubs);
485 }
486 }
487 }
488 }
489 }
490
491 EnforceSigFlag {
492 sig_flag,
493 pubkeys,
494 sigs_required,
495 }
496}
497
498#[derive(Debug, Clone, PartialEq, Eq)]
500pub struct EnforceSigFlag {
501 pub sig_flag: SigFlag,
503 pub pubkeys: HashSet<PublicKey>,
505 pub sigs_required: u64,
507}
508
509impl SwapRequest {
510 pub fn sign_sig_all(&mut self, secret_key: SecretKey) -> Result<(), Error> {
512 let msg = self.sig_all_msg_to_sign();
514 let signature = secret_key.sign(msg.as_bytes())?;
515
516 let first_input = self
518 .inputs_mut()
519 .first_mut()
520 .ok_or(Error::IncorrectSecretKind)?;
521
522 match first_input.witness.as_mut() {
523 Some(witness) => {
524 witness.add_signatures(vec![signature.to_string()]);
525 }
526 None => {
527 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
528 p2pk_witness.add_signatures(vec![signature.to_string()]);
529 first_input.witness = Some(p2pk_witness);
530 }
531 };
532
533 Ok(())
534 }
535}
536
537impl<Q> MeltRequest<Q>
538where
539 Q: std::fmt::Display + Serialize + DeserializeOwned,
540{
541 pub fn sign_sig_all(&mut self, secret_key: SecretKey) -> Result<(), Error> {
543 let msg = self.sig_all_msg_to_sign();
545 let signature = secret_key.sign(msg.as_bytes())?;
546
547 let first_input = self
549 .inputs_mut()
550 .first_mut()
551 .ok_or(Error::SpendConditionsNotMet)?;
552
553 match first_input.witness.as_mut() {
554 Some(witness) => {
555 witness.add_signatures(vec![signature.to_string()]);
556 }
557 None => {
558 let mut p2pk_witness = Witness::P2PKWitness(P2PKWitness::default());
559 p2pk_witness.add_signatures(vec![signature.to_string()]);
560 first_input.witness = Some(p2pk_witness);
561 }
562 };
563
564 Ok(())
565 }
566}
567
568#[cfg(feature = "mint")]
569#[cfg(test)]
570mod tests {
571 use std::str::FromStr;
572
573 use uuid::Uuid;
574
575 use super::*;
576 use crate::nuts::Id;
577 use crate::quote_id::QuoteId;
578 use crate::secret::Secret;
579 use crate::{Amount, BlindedMessage};
580
581 #[test]
582 fn test_secret_ser() {
583 let data = PublicKey::from_str(
584 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
585 )
586 .unwrap();
587
588 let conditions = Conditions {
589 locktime: Some(99999),
590 pubkeys: Some(vec![
591 PublicKey::from_str(
592 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
593 )
594 .unwrap(),
595 PublicKey::from_str(
596 "023192200a0cfd3867e48eb63b03ff599c7e46c8f4e41146b2d281173ca6c50c54",
597 )
598 .unwrap(),
599 ]),
600 refund_keys: Some(vec![PublicKey::from_str(
601 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
602 )
603 .unwrap()]),
604 num_sigs: Some(2),
605 sig_flag: SigFlag::SigAll,
606 num_sigs_refund: None,
607 };
608
609 let secret: Nut10Secret = SpendingConditions::new_p2pk(data, Some(conditions)).into();
610
611 let secret_str = serde_json::to_string(&secret).unwrap();
612
613 let secret_der: Nut10Secret = serde_json::from_str(&secret_str).unwrap();
614
615 assert_eq!(secret_der, secret);
616 }
617
618 #[test]
619 fn sign_proof() {
620 let secret_key =
621 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
622 .unwrap();
623
624 let signing_key_two =
625 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
626 .unwrap();
627
628 let signing_key_three =
629 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
630 .unwrap();
631 let v_key: PublicKey = secret_key.public_key();
632 let v_key_two: PublicKey = signing_key_two.public_key();
633 let v_key_three: PublicKey = signing_key_three.public_key();
634
635 let conditions = Conditions {
636 locktime: Some(21000000000),
637 pubkeys: Some(vec![v_key_two, v_key_three]),
638 refund_keys: Some(vec![v_key]),
639 num_sigs: Some(2),
640 sig_flag: SigFlag::SigInputs,
641 num_sigs_refund: None,
642 };
643
644 let secret: Secret = SpendingConditions::new_p2pk(v_key, Some(conditions))
645 .try_into()
646 .unwrap();
647
648 let mut proof = Proof {
649 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
650 amount: Amount::ZERO,
651 secret,
652 c: PublicKey::from_str(
653 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
654 )
655 .unwrap(),
656 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
657 dleq: None,
658 p2pk_e: None,
659 };
660
661 proof.sign_p2pk(secret_key).unwrap();
662 proof.sign_p2pk(signing_key_two).unwrap();
663
664 assert!(proof.verify_p2pk().is_ok());
665 }
666
667 #[test]
668 fn test_verify() {
669 let json: &str = r#"{
671 "amount":1,
672 "secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]",
673 "C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
674 "id":"009a1f293253e41e",
675 "witness":"{\"signatures\":[\"60f3c9b766770b46caac1d27e1ae6b77c8866ebaeba0b9489fe6a15a837eaa6fcd6eaa825499c72ac342983983fd3ba3a8a41f56677cc99ffd73da68b59e1383\"]}"
676 }"#;
677 let valid_proof: Proof = serde_json::from_str(json).unwrap();
678
679 valid_proof.verify_p2pk().unwrap();
680 assert!(valid_proof.verify_p2pk().is_ok());
681
682 let invalid_proof = r#"{"amount":1,"secret":"[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_INPUTS\"]]}]","C":"02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904","id":"009a1f293253e41e","witness":"{\"signatures\":[\"3426df9730d365a9d18d79bed2f3e78e9172d7107c55306ac5ddd1b2d065893366cfa24ff3c874ebf1fc22360ba5888ddf6ff5dbcb9e5f2f5a1368f7afc64f15\"]}"}"#;
684
685 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
686
687 assert!(invalid_proof.verify_p2pk().is_err());
688 }
689
690 #[test]
691 fn verify_multi_sig() {
692 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\"]}"}"#;
694
695 let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap();
696
697 assert!(valid_proof.verify_p2pk().is_ok());
698
699 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\"]}"}"#;
701
702 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
703
704 assert!(invalid_proof.verify_p2pk().is_err());
706 }
707
708 #[test]
709 fn test_valid_signatures_duplicate() {
710 let secret_key = SecretKey::generate();
711 let pubkey = secret_key.public_key();
712 let msg = b"test message";
713 let sig = secret_key.sign(msg).unwrap();
714
715 assert_eq!(valid_signatures(msg, &[pubkey], &[sig]).unwrap(), 1);
717
718 let res = valid_signatures(msg, &[pubkey], &[sig, sig]);
720 assert!(res.is_err());
721 assert!(matches!(res.unwrap_err(), Error::DuplicateSignature));
722 }
723
724 #[test]
725 fn test_blinded_message_verify_p2pk_duplicate_sig() {
726 let secret_key = SecretKey::generate();
727 let pubkey = secret_key.public_key();
728
729 let mut blinded_message = BlindedMessage {
730 amount: Amount::ZERO,
731 blinded_secret: pubkey,
732 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
733 witness: None,
734 };
735
736 blinded_message.sign_p2pk(secret_key).unwrap();
738
739 if let Some(Witness::P2PKWitness(p2pk_witness)) = &mut blinded_message.witness {
741 let sig = p2pk_witness.signatures[0].clone();
742 p2pk_witness.signatures.push(sig);
743 }
744
745 let res = blinded_message.verify_p2pk(&vec![pubkey], 1);
747 assert!(res.is_err());
748 assert!(
749 matches!(res.unwrap_err(), Error::DuplicateSignature),
750 "Expected DuplicateSignature error"
751 );
752 }
753
754 #[test]
755 fn test_blinded_message_verify_p2pk_duplicate_xonly() {
756 let secret_key = SecretKey::generate();
757 let pubkey = secret_key.public_key();
758
759 let mut bytes = pubkey.to_bytes();
761 bytes[0] = if bytes[0] == 0x02 { 0x03 } else { 0x02 };
762 let pubkey_alt = PublicKey::from_slice(&bytes).unwrap();
763
764 let mut blinded_message = BlindedMessage {
765 amount: Amount::ZERO,
766 blinded_secret: pubkey,
767 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
768 witness: None,
769 };
770
771 blinded_message.sign_p2pk(secret_key).unwrap();
773
774 let res = blinded_message.verify_p2pk(&vec![pubkey, pubkey_alt], 1);
776 assert!(res.is_err());
777 assert!(
778 matches!(res.unwrap_err(), Error::DuplicateSignature),
779 "Expected DuplicateSignature error for same x-only pubkeys"
780 );
781 }
782
783 #[test]
784 fn test_duplicate_key_in_main_pathway() {
785 let secret_key = SecretKey::generate();
786 let pubkey = secret_key.public_key();
787 let mut bytes = pubkey.to_bytes();
788 bytes[0] = 0x02;
789 let pk_02 = PublicKey::from_slice(&bytes).unwrap();
790 bytes[0] = 0x03;
791 let pk_03 = PublicKey::from_slice(&bytes).unwrap();
792
793 let conditions = Conditions {
795 locktime: None,
796 pubkeys: Some(vec![pk_03]),
797 refund_keys: None,
798 num_sigs: Some(2),
799 sig_flag: SigFlag::SigInputs,
800 num_sigs_refund: None,
801 };
802
803 let secret: Secret = SpendingConditions::new_p2pk(pk_02, Some(conditions))
804 .try_into()
805 .unwrap();
806
807 let mut proof = Proof {
808 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
809 amount: Amount::ZERO,
810 secret,
811 c: PublicKey::from_str(
812 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
813 )
814 .unwrap(),
815 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
816 dleq: None,
817 p2pk_e: None,
818 };
819
820 proof.sign_p2pk(secret_key.clone()).unwrap();
822 if let Some(Witness::P2PKWitness(w)) = &mut proof.witness {
824 w.signatures.push(w.signatures[0].clone());
825 }
826
827 let res = proof.verify_p2pk();
828 assert!(res.is_err(), "Expected an error but got {:?}", res);
829 let err = res.unwrap_err();
830 assert!(
831 matches!(err, Error::DuplicatePubkey),
832 "Expected DuplicatePubkey, got {:?}",
833 err
834 );
835 }
836
837 #[test]
838 fn test_duplicate_key_in_both_pathways() {
839 let secret_key = SecretKey::generate();
840 let pubkey = secret_key.public_key();
841 let mut bytes = pubkey.to_bytes();
842 bytes[0] = 0x02;
843 let pk_02 = PublicKey::from_slice(&bytes).unwrap();
844 bytes[0] = 0x03;
845 let pk_03 = PublicKey::from_slice(&bytes).unwrap();
846
847 let conditions = Conditions {
849 locktime: Some(0),
850 pubkeys: None,
851 refund_keys: Some(vec![pk_03, pk_02]), num_sigs: None,
853 sig_flag: SigFlag::SigInputs,
854 num_sigs_refund: Some(2),
855 };
856
857 let secret: Secret =
858 SpendingConditions::new_p2pk(secret_key.public_key(), Some(conditions))
859 .try_into()
860 .unwrap();
861
862 let proof = Proof {
863 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
864 amount: Amount::ZERO,
865 secret,
866 c: PublicKey::from_str(
867 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
868 )
869 .unwrap(),
870 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
871 dleq: None,
872 p2pk_e: None,
873 };
874
875 let res = proof.verify_p2pk();
876 assert!(res.is_err(), "Expected an error but got {:?}", res);
877 let err = res.unwrap_err();
878 assert!(
879 matches!(err, Error::DuplicatePubkey),
880 "Expected DuplicatePubkey, got {:?}",
881 err
882 );
883 }
884
885 #[test]
886 fn verify_refund() {
887 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\"]}"}"#;
888
889 let valid_proof: Proof = serde_json::from_str(valid_proof).unwrap();
890 assert!(valid_proof.verify_p2pk().is_ok());
891
892 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\"]}"}"#;
893
894 let invalid_proof: Proof = serde_json::from_str(invalid_proof).unwrap();
895
896 assert!(invalid_proof.verify_p2pk().is_err());
897 }
898
899 #[test]
900 fn sig_with_non_refund_keys_after_locktime() {
901 let secret_key =
902 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
903 .unwrap();
904
905 let signing_key_two =
906 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
907 .unwrap();
908
909 let signing_key_three =
910 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
911 .unwrap();
912 let v_key: PublicKey = secret_key.public_key();
913 let v_key_two: PublicKey = signing_key_two.public_key();
914 let v_key_three: PublicKey = signing_key_three.public_key();
915
916 let conditions = Conditions {
917 locktime: Some(21),
918 pubkeys: Some(vec![v_key_three]),
919 refund_keys: Some(vec![v_key, v_key_two]),
920 num_sigs: None,
921 sig_flag: SigFlag::SigInputs,
922 num_sigs_refund: Some(2),
923 };
924
925 let secret: Secret = SpendingConditions::new_p2pk(v_key, Some(conditions))
926 .try_into()
927 .unwrap();
928
929 let mut proof = Proof {
930 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
931 amount: Amount::ZERO,
932 secret,
933 c: PublicKey::from_str(
934 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
935 )
936 .unwrap(),
937 witness: Some(Witness::P2PKWitness(P2PKWitness { signatures: vec![] })),
938 dleq: None,
939 p2pk_e: None,
940 };
941
942 proof.sign_p2pk(signing_key_three.clone()).unwrap();
943
944 assert!(proof.verify_p2pk().is_ok());
947
948 proof.witness = None;
949
950 proof.sign_p2pk(secret_key).unwrap();
953 assert!(
954 proof.verify_p2pk().is_ok(),
955 "Data key signature should satisfy primary path"
956 );
957
958 proof.sign_p2pk(signing_key_two).unwrap();
960 assert!(proof.verify_p2pk().is_ok());
961 }
962
963 fn create_test_proof(secret: Secret, pubkey: PublicKey, id: &str) -> Proof {
965 Proof {
966 keyset_id: Id::from_str(id).unwrap(),
967 amount: Amount::ZERO,
968 secret,
969 c: pubkey,
970 witness: None,
971 dleq: None,
972 p2pk_e: None,
973 }
974 }
975
976 fn create_test_secret(pubkey: PublicKey, conditions: Conditions) -> Secret {
977 SpendingConditions::new_p2pk(pubkey, Some(conditions))
978 .try_into()
979 .unwrap()
980 }
981
982 fn create_test_blinded_msg(pubkey: PublicKey) -> BlindedMessage {
983 BlindedMessage {
984 amount: Amount::ZERO,
985 blinded_secret: pubkey,
986 keyset_id: Id::from_str("009a1f293253e41e").unwrap(),
987 witness: None,
988 }
989 }
990
991 #[test]
992 fn test_melt_sig_all_basic_signing() {
993 let secret_key =
994 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
995 .unwrap();
996 let pubkey = secret_key.public_key();
997
998 let conditions = Conditions {
1000 sig_flag: SigFlag::SigAll,
1001 ..Default::default()
1002 };
1003
1004 let secret = create_test_secret(pubkey, conditions);
1005 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
1006 let blinded_msg = create_test_blinded_msg(pubkey);
1007
1008 let mut melt = MeltRequest::new(
1010 QuoteId::UUID(Uuid::new_v4()),
1011 vec![proof],
1012 Some(vec![blinded_msg]),
1013 );
1014
1015 assert!(
1017 melt.verify_spending_conditions().is_err(),
1018 "Unsigned melt request should fail verification"
1019 );
1020
1021 assert!(
1023 melt.sign_sig_all(secret_key).is_ok(),
1024 "Signing should succeed"
1025 );
1026
1027 assert!(
1029 melt.verify_spending_conditions().is_ok(),
1030 "Signed melt request should pass verification"
1031 );
1032 }
1033
1034 #[test]
1035 fn test_melt_sig_all_unauthorized_key() {
1036 let secret_key =
1037 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1038 .unwrap();
1039 let pubkey = secret_key.public_key();
1040
1041 let conditions = Conditions {
1043 sig_flag: SigFlag::SigAll,
1044 pubkeys: Some(vec![pubkey]),
1045 ..Default::default()
1046 };
1047
1048 let secret = create_test_secret(pubkey, conditions);
1049 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
1050
1051 let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof], None);
1052
1053 let unauthorized_key =
1055 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
1056 .unwrap();
1057 melt.sign_sig_all(unauthorized_key).unwrap();
1058
1059 assert!(
1061 melt.verify_spending_conditions().is_err(),
1062 "Verification should fail with unauthorized key signature"
1063 );
1064 }
1065
1066 #[test]
1067 fn test_melt_sig_all_wrong_flag() {
1068 let secret_key =
1069 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1070 .unwrap();
1071 let pubkey = secret_key.public_key();
1072
1073 let conditions = Conditions {
1075 sig_flag: SigFlag::SigInputs,
1076 pubkeys: Some(vec![pubkey]),
1077 ..Default::default()
1078 };
1079
1080 let secret = create_test_secret(pubkey, conditions);
1081 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
1082
1083 let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof], None);
1084
1085 melt.sign_sig_all(secret_key).unwrap();
1087
1088 assert!(
1090 melt.verify_spending_conditions().is_err(),
1091 "Verification should fail with SIG_INPUTS flag when expecting SIG_ALL"
1092 );
1093 }
1094
1095 #[test]
1096 fn test_melt_sig_all_multiple_inputs() {
1097 let secret_key =
1098 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1099 .unwrap();
1100 let pubkey = secret_key.public_key();
1101
1102 let conditions = Conditions {
1104 sig_flag: SigFlag::SigAll,
1105 ..Default::default()
1106 };
1107
1108 let secret = create_test_secret(pubkey, conditions);
1109
1110 let proof1 = create_test_proof(secret.clone(), pubkey, "009a1f293253e41e");
1112 let proof2 = create_test_proof(secret, pubkey, "009a1f293253e41f");
1113
1114 let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof1, proof2], None);
1115
1116 assert!(
1118 melt.sign_sig_all(secret_key).is_ok(),
1119 "Signing with multiple matching inputs should succeed"
1120 );
1121 assert!(
1122 melt.verify_spending_conditions().is_ok(),
1123 "Verification should succeed with multiple matching inputs"
1124 );
1125 }
1126
1127 #[test]
1128 fn test_melt_sig_all_mismatched_inputs() {
1129 let secret_key =
1130 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1131 .unwrap();
1132 let pubkey = secret_key.public_key();
1133
1134 let conditions1 = Conditions {
1136 sig_flag: SigFlag::SigAll,
1137 ..Default::default()
1138 };
1139 let secret1 = create_test_secret(pubkey, conditions1.clone());
1140 let proof1 = create_test_proof(secret1, pubkey, "009a1f293253e41e");
1141
1142 let conditions2 = conditions1.clone();
1144 let secret2: Secret = SpendingConditions::new_p2pk(
1145 PublicKey::from_str(
1146 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
1147 )
1148 .unwrap(),
1149 Some(conditions2),
1150 )
1151 .try_into()
1152 .unwrap();
1153 let proof2 = create_test_proof(secret2, pubkey, "009a1f293253e41f");
1154
1155 let mut melt = MeltRequest::new(Uuid::new_v4(), vec![proof1, proof2], None);
1156
1157 melt.sign_sig_all(secret_key).unwrap();
1159
1160 assert!(
1162 melt.verify_spending_conditions().is_err(),
1163 "Verification should fail with mismatched input secrets"
1164 );
1165 }
1166
1167 #[test]
1168 fn test_melt_sig_all_multiple_signatures() {
1169 let secret_key1 =
1170 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1171 .unwrap();
1172 let pubkey1 = secret_key1.public_key();
1173
1174 let secret_key2 =
1175 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
1176 .unwrap();
1177 let pubkey2 = secret_key2.public_key();
1178
1179 let conditions = Conditions {
1181 sig_flag: SigFlag::SigAll,
1182 num_sigs: Some(2),
1183 pubkeys: Some(vec![pubkey2]),
1184 ..Default::default()
1185 };
1186
1187 let secret = create_test_secret(pubkey1, conditions);
1188 let proof = create_test_proof(secret, pubkey1, "009a1f293253e41e");
1189
1190 let mut melt = MeltRequest::new(
1191 Uuid::new_v4(),
1192 vec![proof],
1193 Some(vec![create_test_blinded_msg(
1194 SecretKey::generate().public_key(),
1195 )]),
1196 );
1197
1198 assert!(
1200 melt.sign_sig_all(secret_key1).is_ok(),
1201 "First signature should succeed"
1202 );
1203 assert!(
1204 melt.verify_spending_conditions().is_err(),
1205 "Single signature should not verify when two required"
1206 );
1207
1208 assert!(
1210 melt.sign_sig_all(secret_key2).is_ok(),
1211 "Second signature should succeed"
1212 );
1213
1214 assert!(
1215 melt.verify_spending_conditions().is_ok(),
1216 "Both signatures should verify successfully"
1217 );
1218 }
1219
1220 #[test]
1221 fn test_melt_sig_all_message_components() {
1222 let secret_key =
1223 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1224 .unwrap();
1225 let pubkey = secret_key.public_key();
1226
1227 let conditions = Conditions {
1228 sig_flag: SigFlag::SigAll,
1229 pubkeys: Some(vec![pubkey]),
1230 ..Default::default()
1231 };
1232
1233 let secret = create_test_secret(pubkey, conditions);
1234 let proof = create_test_proof(secret.clone(), pubkey, "009a1f293253e41e");
1235 let blinded_msg = create_test_blinded_msg(pubkey);
1236 let quote_id = Uuid::new_v4();
1237
1238 let melt = MeltRequest::new(quote_id, vec![proof], Some(vec![blinded_msg.clone()]));
1239
1240 let msg = melt.sig_all_msg_to_sign();
1242
1243 assert!(
1245 msg.contains(&secret.to_string()),
1246 "Message should contain secret"
1247 );
1248 assert!(
1249 msg.contains(&blinded_msg.blinded_secret.to_hex()),
1250 "Message should contain blinded message in hex format"
1251 );
1252 assert!(
1253 msg.contains("e_id.to_string()),
1254 "Message should contain quote ID"
1255 );
1256 }
1257
1258 #[test]
1260 fn test_sig_all_swap_single_sig() {
1261 let valid_swap = r#"{
1263 "inputs": [
1264 {
1265 "amount": 2,
1266 "id": "00bfa73302d12ffd",
1267 "secret": "[\"P2PK\",{\"nonce\":\"c7f280eb55c1e8564e03db06973e94bc9b666d9e1ca42ad278408fe625950303\",\"data\":\"030d8acedfe072c9fa449a1efe0817157403fbec460d8e79f957966056e5dd76c1\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1268 "C": "02c97ee3d1db41cf0a3ddb601724be8711a032950811bf326f8219c50c4808d3cd",
1269 "witness": "{\"signatures\":[\"ce017ca25b1b97df2f72e4b49f69ac26a240ce14b3690a8fe619d41ccc42d3c1282e073f85acd36dc50011638906f35b56615f24e4d03e8effe8257f6a808538\"]}"
1270 }
1271 ],
1272 "outputs": [
1273 {
1274 "amount": 2,
1275 "id": "00bfa73302d12ffd",
1276 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1277 }
1278 ]
1279}"#;
1280
1281 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1282
1283 let msg_to_sign = valid_swap.sig_all_msg_to_sign();
1285 assert_eq!(
1286 msg_to_sign,
1287 "[\"P2PK\",{\"nonce\":\"c7f280eb55c1e8564e03db06973e94bc9b666d9e1ca42ad278408fe625950303\",\"data\":\"030d8acedfe072c9fa449a1efe0817157403fbec460d8e79f957966056e5dd76c1\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]02c97ee3d1db41cf0a3ddb601724be8711a032950811bf326f8219c50c4808d3cd2038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1288 );
1289
1290 use bitcoin::hashes::{sha256, Hash};
1292 let msg_hash = sha256::Hash::hash(msg_to_sign.as_bytes());
1293 assert_eq!(
1294 msg_hash.to_string(),
1295 "de7f9e3ca0fcc5ed3258fcf83dbf1be7fa78a5ed6da7bf2aa60d61e9dc6eb09a"
1296 );
1297
1298 assert!(
1299 valid_swap.verify_spending_conditions().is_ok(),
1300 "Valid SIG_ALL swap request should verify"
1301 );
1302 }
1303
1304 #[test]
1305 fn test_sig_all_swap_single_sig_2() {
1306 let valid_swap = r#"{
1308 "inputs": [
1309 {
1310 "amount": 2,
1311 "id": "00bfa73302d12ffd",
1312 "secret": "[\"P2PK\",{\"nonce\":\"c7f280eb55c1e8564e03db06973e94bc9b666d9e1ca42ad278408fe625950303\",\"data\":\"030d8acedfe072c9fa449a1efe0817157403fbec460d8e79f957966056e5dd76c1\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1313 "C": "02c97ee3d1db41cf0a3ddb601724be8711a032950811bf326f8219c50c4808d3cd",
1314 "witness": "{\"signatures\":[\"ce017ca25b1b97df2f72e4b49f69ac26a240ce14b3690a8fe619d41ccc42d3c1282e073f85acd36dc50011638906f35b56615f24e4d03e8effe8257f6a808538\"]}"
1315 }
1316 ],
1317 "outputs": [
1318 {
1319 "amount": 2,
1320 "id": "00bfa73302d12ffd",
1321 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1322 }
1323 ]
1324}"#;
1325
1326 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1327
1328 assert!(
1329 valid_swap.verify_spending_conditions().is_ok(),
1330 "Valid SIG_ALL swap request should verify"
1331 );
1332 }
1333
1334 #[test]
1335 fn test_sig_all_multiple_secrets() {
1336 let invalid_swap = r#"{
1338 "inputs": [
1339 {
1340 "amount": 1,
1341 "id": "00bfa73302d12ffd",
1342 "secret": "[\"P2PK\",{\"nonce\":\"fa6dd3fac9086c153878dec90b9e37163d38ff2ecf8b37db6470e9d185abbbae\",\"data\":\"033b42b04e659fed13b669f8b16cdaffc3ee5738608810cf97a7631d09bd01399d\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1343 "C": "024d232312bab25af2e73f41d56864d378edca9109ae8f76e1030e02e585847786",
1344 "witness": "{\"signatures\":[\"27b4d260a1186e3b62a26c0d14ffeab3b9f7c3889e78707b8fd3836b473a00601afbd53a2288ad20a624a8bbe3344453215ea075fc0ce479dd8666fd3d9162cc\"]}"
1345 },
1346 {
1347 "amount": 2,
1348 "id": "00bfa73302d12ffd",
1349 "secret": "[\"P2PK\",{\"nonce\":\"4007b21fc5f5b1d4920bc0a08b158d98fd0fb2b0b0262b57ff53c6c5d6c2ae8c\",\"data\":\"033b42b04e659fed13b669f8b16cdaffc3ee5738608810cf97a7631d09bd01399d\",\"tags\":[[\"locktime\",\"122222222222222\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1350 "C": "02417400f2af09772219c831501afcbab4efb3b2e75175635d5474069608deb641"
1351 }
1352 ],
1353 "outputs": [
1354 {
1355 "amount": 1,
1356 "id": "00bfa73302d12ffd",
1357 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1358 },
1359 {
1360 "amount": 1,
1361 "id": "00bfa73302d12ffd",
1362 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1363 },
1364 {
1365 "amount": 1,
1366 "id": "00bfa73302d12ffd",
1367 "B_": "02c0d4fce02a7a0f09e3f1bca952db910b17e81a7ebcbce62cd8dcfb127d21e37b"
1368 }
1369 ]
1370}"#;
1371
1372 let invalid_swap: SwapRequest = serde_json::from_str(invalid_swap).unwrap();
1373
1374 assert!(
1375 invalid_swap.verify_spending_conditions().is_err(),
1376 "Invalid swap with multiple secrets shouldn't be accepted"
1377 );
1378 }
1379
1380 #[test]
1381 fn test_sig_all_multiple_signatures_provided() {
1382 let valid_swap = r#"{
1384 "inputs": [
1385 {
1386 "amount": 2,
1387 "id": "00bfa73302d12ffd",
1388 "secret": "[\"P2PK\",{\"nonce\":\"04bfd885fc982d553711092d037fdceb7320fd8f96b0d4fd6d31a65b83b94272\",\"data\":\"0275e78025b558dbe6cb8fdd032a2e7613ca14fda5c1f4c4e3427f5077a7bd90e4\",\"tags\":[[\"pubkeys\",\"035163650bbd5ed4be7693f40f340346ba548b941074e9138b67ef6c42755f3449\",\"02817d22a8edc44c4141e192995a7976647c335092199f9e076a170c7336e2f5cc\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1389 "C": "03866a09946562482c576ca989d06371e412b221890804c7da8887d321380755be",
1390 "witness": "{\"signatures\":[\"be1d72c5ca16a93c5a34f25ec63ce632ddc3176787dac363321af3fd0f55d1927e07451bc451ffe5c682d76688ea9925d7977dffbb15bd79763b527f474734b0\",\"669d6d10d7ed35395009f222f6c7bdc28a378a1ebb72ee43117be5754648501da3bedf2fd6ff0c7849ac92683538c60af0af504102e40f2d8daca8e08b1ca16b\"]}"
1391 }
1392 ],
1393 "outputs": [
1394 {
1395 "amount": 2,
1396 "id": "00bfa73302d12ffd",
1397 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1398 }
1399 ]
1400}"#;
1401
1402 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1403
1404 assert!(
1405 valid_swap.verify_spending_conditions().is_ok(),
1406 "Valid swap with multiple signatures should be accepted"
1407 );
1408 }
1409
1410 #[test]
1411 fn test_sig_all_mixed_pubkeys_and_refund() {
1412 let invalid_swap = r#"{
1414 "inputs": [
1415 {
1416 "amount": 2,
1417 "id": "00bfa73302d12ffd",
1418 "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\"]]}]",
1419 "C": "026c12ee3bffa5c617debcf823bf1af6a9b47145b699f2737bba3394f0893eb869",
1420 "witness": "{\"signatures\":[\"bfe884145ce6512331324321c3946dfd812428a53656b108b59d26559a186ba2ab45e5be9ce94e2dff0d09078e25ccb82d06a8b3a63cd3dc67065b8f77292776\",\"236e5cc9c30f85a893a29a4302e41e6f2015caef4229f28fa65e2f5c9d55515cc9a1852093a81a5095055d85fd55bf4da124e55354b56e0a39e83b58b0afc197\"]}"
1421 }
1422 ],
1423 "outputs": [
1424 {
1425 "amount": 1,
1426 "id": "00bfa73302d12ffd",
1427 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1428 },
1429 {
1430 "amount": 1,
1431 "id": "00bfa73302d12ffd",
1432 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1433 }
1434 ]
1435}"#;
1436
1437 let invalid_swap: SwapRequest = serde_json::from_str(invalid_swap).unwrap();
1438
1439 assert!(
1440 invalid_swap.verify_spending_conditions().is_err(),
1441 "Invalid swap with mixed refunds and pubkeys shouldn't be accepted"
1442 );
1443 }
1444
1445 #[test]
1446 fn test_sig_all_locktime_passed_with_valid_refund_key_sigs() {
1447 let valid_swap = r#"{
1449 "inputs": [
1450 {
1451 "amount": 2,
1452 "id": "00bfa73302d12ffd",
1453 "secret": "[\"P2PK\",{\"nonce\":\"9ea35553beb18d553d0a53120d0175a0991ca6109370338406eed007b26eacd1\",\"data\":\"02af21e09300af92e7b48c48afdb12e22933738cfb9bba67b27c00c679aae3ec25\",\"tags\":[[\"locktime\",\"1\"],[\"refund\",\"02637c19143c58b2c58bd378400a7b82bdc91d6dedaeb803b28640ef7d28a887ac\",\"0345c7fdf7ec7c8e746cca264bf27509eb4edb9ac421f8fbfab1dec64945a4d797\"],[\"n_sigs_refund\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1454 "C": "03dd83536fbbcbb74ccb3c87147df26753fd499cc2c095f74367fff0fb459c312e",
1455 "witness": "{\"signatures\":[\"23b58ef28cd22f3dff421121240ddd621deee83a3bc229fd67019c2e338d91e2c61577e081e1375dbab369307bba265e887857110ca3b4bd949211a0a298805f\",\"7e75948ef1513564fdcecfcbd389deac67c730f7004f8631ba90c0844d3e8c0cf470b656306877df5141f65fd3b7e85445a8452c3323ab273e6d0d44843817ed\"]}"
1456 }
1457 ],
1458 "outputs": [
1459 {
1460 "amount": 2,
1461 "id": "00bfa73302d12ffd",
1462 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1463 }
1464 ]
1465}"#;
1466
1467 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1468
1469 assert!(
1470 valid_swap.verify_spending_conditions().is_ok(),
1471 "Valid post-locktime swap with refund keys should be accepted"
1472 );
1473 }
1474
1475 #[test]
1476 fn test_sig_all_htlc_and_pubkey() {
1477 let valid_swap = r#"{
1479 "inputs": [
1480 {
1481 "amount": 2,
1482 "id": "00bfa73302d12ffd",
1483 "secret": "[\"HTLC\",{\"nonce\":\"d730dd70cd7ec6e687829857de8e70aab2b970712f4dbe288343eca20e63c28c\",\"data\":\"ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5\",\"tags\":[[\"pubkeys\",\"0350cda8a1d5257dbd6ba8401a9a27384b9ab699e636e986101172167799469b14\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1484 "C": "03ff6567e2e6c31db5cb7189dab2b5121930086791c93899e4eff3dda61cb57273",
1485 "witness": "{\"preimage\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"signatures\":[\"a4c00a9ad07f9936e404494fda99a9b935c82d7c053173b304b8663124c81d4b00f64a225f5acf41043ca52b06382722bd04ded0fbeb0fcc404eed3b24778b88\"]}"
1486 }
1487 ],
1488 "outputs": [
1489 {
1490 "amount": 2,
1491 "id": "00bfa73302d12ffd",
1492 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1493 }
1494 ]
1495}"#;
1496
1497 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1498
1499 assert!(
1500 valid_swap.verify_spending_conditions().is_ok(),
1501 "Valid swap with htlc and pubkey should be accepted"
1502 );
1503 }
1504
1505 #[test]
1506 fn test_sig_all_enforce_locktime_with_only_refund_signed() {
1507 let invalid_swap = r#"{
1509 "inputs": [
1510 {
1511 "amount": 2,
1512 "id": "00bfa73302d12ffd",
1513 "secret": "[\"HTLC\",{\"nonce\":\"512c4045f12fdfd6f55059669c189e040c37c1ce2f8be104ed6aec296acce4e9\",\"data\":\"ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5\",\"tags\":[[\"pubkeys\",\"03ba83defd31c63f8841d188f0d41b5bb3af1bb3c08d0ba46f8f1d26a4d45e8cad\"],[\"locktime\",\"4854185133\"],[\"refund\",\"032f1008a79c722e93a1b4b853f85f38283f9ef74ee4c5c91293eb1cc3c5e46e34\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1514 "C": "02207abeff828146f1fc3909c74613d5605bd057f16791994b3c91f045b39a6939",
1515 "witness": "{\"preimage\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"signatures\":[\"7816d57871bde5be2e4281065dbe5b15f641d8f1ed9437a3ae556464d6f9b8a0a2e6660337a915f2c26dce1453a416daf682b8fb593b67a0750fce071e0759b9\"]}"
1516 }
1517 ],
1518 "outputs": [
1519 {
1520 "amount": 1,
1521 "id": "00bfa73302d12ffd",
1522 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1523 },
1524 {
1525 "amount": 1,
1526 "id": "00bfa73302d12ffd",
1527 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1528 }
1529 ]
1530}"#;
1531
1532 let invalid_swap: SwapRequest = serde_json::from_str(invalid_swap).unwrap();
1533
1534 assert!(
1535 invalid_swap.verify_spending_conditions().is_err(),
1536 "Invalid swap with pre-locktime conditions not met shouldn't be accepted"
1537 );
1538 }
1539
1540 #[test]
1541 fn test_sig_all_htlc_post_locktime() {
1542 let valid_swap = r#"{
1546 "inputs": [
1547 {
1548 "amount": 2,
1549 "id": "00bfa73302d12ffd",
1550 "secret": "[\"HTLC\",{\"nonce\":\"c9b0fabb8007c0db4bef64d5d128cdcf3c79e8bb780c3294adf4c88e96c32647\",\"data\":\"ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5\",\"tags\":[[\"pubkeys\",\"039e6ec7e922abb4162235b3a42965eb11510b07b7461f6b1a17478b1c9c64d100\"],[\"locktime\",\"1\"],[\"refund\",\"02ce1bbd2c9a4be8029c9a6435ad601c45677f5cde81f8a7f0ed535e0039d0eb6c\",\"03c43c00ff57f63cfa9e732f0520c342123e21331d0121139f1b636921eeec095f\"],[\"n_sigs_refund\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1551 "C": "0344b6f1471cf18a8cbae0e624018c816be5e3a9b04dcb7689f64173c1ae90a3a5",
1552 "witness": "{\"preimage\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"signatures\":[\"98e21672d409cc782c720f203d8284f0af0c8713f18167499f9f101b7050c3e657fb0e57478ebd8bd561c31aa6c30f4cd20ec38c73f5755b7b4ddee693bca5a5\",\"693f40129dbf905ed9c8008081c694f72a36de354f9f4fa7a61b389cf781f62a0ae0586612fb2eb504faaf897fefb6742309186117f4743bcebcb8e350e975e2\"]}"
1553 }
1554 ],
1555 "outputs": [
1556 {
1557 "amount": 2,
1558 "id": "00bfa73302d12ffd",
1559 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1560 }
1561 ]
1562}"#;
1563
1564 let valid_swap: SwapRequest = serde_json::from_str(valid_swap).unwrap();
1565
1566 assert!(
1567 valid_swap.verify_spending_conditions().is_ok(),
1568 "Valid post-locktime swap with htlc should be accepted"
1569 );
1570 }
1571
1572 #[test]
1573 fn test_sig_all_swap_mismatched_inputs() {
1574 let invalid_swap = r#"{
1576 "inputs": [
1577 {
1578 "amount": 1,
1579 "id": "00bfa73302d12ffd",
1580 "secret": "[\"P2PK\",{\"nonce\":\"e2a221fe361f19d95c5c3312ccff3ffa075b4fe37beec99de85a6ee70568385b\",\"data\":\"03dad7f9c588f4cbb55c2e1b7b802fa2bbc63a614d9e9ecdf56a8e7ee8ca65be86\",\"tags\":[[\"pubkeys\",\"025f2af63fd65ca97c3bde4070549683e72769d28def2f1cd3d63576cd9c2ffa6c\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1581 "C": "02a79c09b0605f4e7a21976b511cc7be01cdaeac54b29645258c84f2e74bff13f6",
1582 "witness": "{\"signatures\":[\"b42c7af7e98ca4e3bba8b73702120970286196340b340c21299676dbc7b10cafaa7baeb243affc01afce3218616cf8b3f6b4baaf4414fedb31b0c6653912f769\",\"17781910e2d806cae464f8a692929ee31124c0cd7eaf1e0d94292c6cbc122da09076b649080b8de9201f87d83b99fe04e33d701817eb287d1cdd9c4d0410e625\"]}"
1583 },
1584 {
1585 "amount": 2,
1586 "id": "00bfa73302d12ffd",
1587 "secret": "[\"P2PK\",{\"nonce\":\"973c78b5e84c0986209dc14ba57682baf38fa4c1ea60c4c5f6834779a1a13e6d\",\"data\":\"02685df03c777837bc7155bd2d0d8e98eede7e956a4cd8a9edac84532584e68e0f\",\"tags\":[[\"pubkeys\",\"025f2af63fd65ca97c3bde4070549683e72769d28def2f1cd3d63576cd9c2ffa6c\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1588 "C": "02be48c564cf6a7b4d09fbaf3a78a153a79f687ac4623e48ce1788effc3fb1e024"
1589 }
1590 ],
1591 "outputs": [
1592 {
1593 "amount": 1,
1594 "id": "00bfa73302d12ffd",
1595 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1596 },
1597 {
1598 "amount": 1,
1599 "id": "00bfa73302d12ffd",
1600 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1601 },
1602 {
1603 "amount": 1,
1604 "id": "00bfa73302d12ffd",
1605 "B_": "02c0d4fce02a7a0f09e3f1bca952db910b17e81a7ebcbce62cd8dcfb127d21e37b"
1606 }
1607 ]
1608 }"#;
1609
1610 let invalid_swap: SwapRequest = serde_json::from_str(invalid_swap).unwrap();
1611 assert!(
1612 invalid_swap.verify_spending_conditions().is_err(),
1613 "Invalid SIG_ALL swap request should fail verification"
1614 );
1615 }
1616
1617 #[test]
1618 fn test_sig_all_mixed_pubkeys_and_refund_pubkeys() {
1619 let invalidsig_all_swap = r#"{
1621 "inputs": [
1622 {
1623 "amount": 2,
1624 "id": "00bfa73302d12ffd",
1625 "secret": "[\"P2PK\",{\"nonce\":\"cc93775c74df53d7c97eb37f72018d166a45ce4f4c65f11c4014b19acd02bd2f\",\"data\":\"02f515ab63e973e0dadfc284bf2ef330b01aa99c3ff775d88272f9c17afa25568c\",\"tags\":[[\"pubkeys\",\"026925e5bb547a3ec6b2d9b8934e23b882f54f89b2a9f45300bf81fd1b311d9c97\"],[\"n_sigs\",\"2\"],[\"refund\",\"03c8cd46b7e6592c41df38bc54dce2555586e7adbb15cc80a02d1a05829677286d\"],[\"n_sigs_refund\",\"1\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1626 "C": "03f6d40d0ab11f4082ee7e977534a6fcd151394d647cde4ab122157e6d755410fd",
1627 "witness": "{\"signatures\":[\"a9f61c2b7161a50839bf7f3e2e1cb9bd7bdacd2ce62c0d458a5969db44646dad409a282241b412e8b191cc7432bcfebf16ad72339a9fb966ca71c8bd971662cc\",\"aa778ec15fe9408e1989c712c823e833f33d45780b9a25555ea76004b05d495e99fd326914484f92e7e91f919ee575e79add26e9d4bbe4349d7333d7e0021af7\"]}"
1628 }
1629 ],
1630 "outputs": [
1631 {
1632 "amount": 1,
1633 "id": "00bfa73302d12ffd",
1634 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1635 },
1636 {
1637 "amount": 1,
1638 "id": "00bfa73302d12ffd",
1639 "B_": "03afe7c87e32d436f0957f1d70a2bca025822a84a8623e3a33aed0a167016e0ca5"
1640 }
1641 ]
1642}"#;
1643
1644 let invalid_swap: SwapRequest = serde_json::from_str(invalidsig_all_swap).unwrap();
1645 assert!(
1646 invalid_swap.verify_spending_conditions().is_err(),
1647 "Invalid SIG_ALL swap request should fail verification"
1648 );
1649 }
1650
1651 #[test]
1652 fn test_sig_all_htlc_unexpired_timelock_refund_signature() {
1653 let invalidsig_all_swap = r#"{
1655 "inputs": [
1656 {
1657 "amount": 2,
1658 "id": "00bfa73302d12ffd",
1659 "secret": "[\"HTLC\",{\"nonce\":\"b6f0c59ea4084369d4196e1318477121c2451d59ae767060e083cb6846e6bbe0\",\"data\":\"ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5\",\"tags\":[[\"pubkeys\",\"0329fdfde4becf9ff871129653ff6464bb2c922fbcba442e6166a8b5849599604f\"],[\"locktime\",\"4854185133\"],[\"refund\",\"035fcf4a5393e4bdef0567aa0b8a9555edba36e5fcb283f3bbce52d86a687817d3\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1660 "C": "024fbbee3f3cc306a48841ba327435b64de20b8b172b98296a3e573c673d52562b",
1661 "witness": "{\"preimage\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"signatures\":[\"7526819070a291f731e77acfbe9da71ddc0f748fd2a3e6c2510bc83c61daaa656df345afa3832fe7cb94352c8835a4794ad499760729c0be29417387d1fc3cd1\"]}"
1662 }
1663 ],
1664 "outputs": [
1665 {
1666 "amount": 2,
1667 "id": "00bfa73302d12ffd",
1668 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1669 }
1670 ]
1671}"#;
1672
1673 let invalid_swap: SwapRequest = serde_json::from_str(invalidsig_all_swap).unwrap();
1674 assert!(
1675 invalid_swap.verify_spending_conditions().is_err(),
1676 "Invalid SIG_ALL swap request should fail verification"
1677 );
1678 }
1679
1680 #[test]
1682 fn test_sig_all_melt() {
1683 let valid_melt = r#"{
1686 "quote": "cF8911fzT88aEi1d-6boZZkq5lYxbUSVs-HbJxK0",
1687 "inputs": [
1688 {
1689 "amount": 2,
1690 "id": "00bfa73302d12ffd",
1691 "secret": "[\"P2PK\",{\"nonce\":\"bbf9edf441d17097e39f5095a3313ba24d3055ab8a32f758ff41c10d45c4f3de\",\"data\":\"029116d32e7da635c8feeb9f1f4559eb3d9b42d400f9d22a64834d89cde0eb6835\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1692 "C": "02a9d461ff36448469dccf828fa143833ae71c689886ac51b62c8d61ddaa10028b",
1693 "witness": "{\"signatures\":[\"478224fbe715e34f78cb33451db6fcf8ab948afb8bd04ff1a952c92e562ac0f7c1cb5e61809410635be0aa94d0448f7f7959bd5762cc3802b0a00ff58b2da747\"]}"
1694 }
1695 ],
1696 "outputs": [
1697 {
1698 "amount": 0,
1699 "id": "00bfa73302d12ffd",
1700 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1701 }
1702 ]
1703}"#;
1704
1705 let valid_melt: MeltRequest<String> = serde_json::from_str(valid_melt).unwrap();
1706
1707 let msg_to_sign = valid_melt.sig_all_msg_to_sign();
1709 assert_eq!(
1710 msg_to_sign,
1711 r#"["P2PK",{"nonce":"bbf9edf441d17097e39f5095a3313ba24d3055ab8a32f758ff41c10d45c4f3de","data":"029116d32e7da635c8feeb9f1f4559eb3d9b42d400f9d22a64834d89cde0eb6835","tags":[["sigflag","SIG_ALL"]]}]02a9d461ff36448469dccf828fa143833ae71c689886ac51b62c8d61ddaa10028b0038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39cF8911fzT88aEi1d-6boZZkq5lYxbUSVs-HbJxK0"#
1712 );
1713
1714 use bitcoin::hashes::{sha256, Hash};
1716 let msg_hash = sha256::Hash::hash(msg_to_sign.as_bytes());
1717 assert_eq!(
1718 msg_hash.to_string(),
1719 "9efa1067cc7dc870f4074f695115829c3cd817a6866c3b84e9814adf3c3cf262"
1720 );
1721
1722 assert!(
1723 valid_melt.verify_spending_conditions().is_ok(),
1724 "Valid SIG_ALL melt request should verify"
1725 );
1726 }
1727
1728 #[test]
1729 fn test_sig_all_valid_melt() {
1730 let valid_melt = r#"{
1732 "quote": "cF8911fzT88aEi1d-6boZZkq5lYxbUSVs-HbJxK0",
1733 "inputs": [
1734 {
1735 "amount": 2,
1736 "id": "00bfa73302d12ffd",
1737 "secret": "[\"P2PK\",{\"nonce\":\"bbf9edf441d17097e39f5095a3313ba24d3055ab8a32f758ff41c10d45c4f3de\",\"data\":\"029116d32e7da635c8feeb9f1f4559eb3d9b42d400f9d22a64834d89cde0eb6835\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1738 "C": "02a9d461ff36448469dccf828fa143833ae71c689886ac51b62c8d61ddaa10028b",
1739 "witness": "{\"signatures\":[\"478224fbe715e34f78cb33451db6fcf8ab948afb8bd04ff1a952c92e562ac0f7c1cb5e61809410635be0aa94d0448f7f7959bd5762cc3802b0a00ff58b2da747\"]}"
1740 }
1741 ],
1742 "outputs": [
1743 {
1744 "amount": 0,
1745 "id": "00bfa73302d12ffd",
1746 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1747 }
1748 ]
1749}"#;
1750
1751 let valid_melt: MeltRequest<String> = serde_json::from_str(valid_melt).unwrap();
1752
1753 assert!(
1754 valid_melt.verify_spending_conditions().is_ok(),
1755 "Valid SIG_ALL melt request should verify"
1756 );
1757 }
1758
1759 #[test]
1760 fn test_sig_all_valid_multisig_melt() {
1761 let valid_melt = r#"{
1763 "quote": "Db3qEMVwFN2tf_1JxbZp29aL5cVXpSMIwpYfyOVF",
1764 "inputs": [
1765 {
1766 "amount": 2,
1767 "id": "00bfa73302d12ffd",
1768 "secret": "[\"P2PK\",{\"nonce\":\"68d7822538740e4f9c9ebf5183ef6c4501c7a9bca4e509ce2e41e1d62e7b8a99\",\"data\":\"0394e841bd59aeadce16380df6174cb29c9fea83b0b65b226575e6d73cc5a1bd59\",\"tags\":[[\"pubkeys\",\"033d892d7ad2a7d53708b7a5a2af101cbcef69522bd368eacf55fcb4f1b0494058\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1769 "C": "03a70c42ec9d7192422c7f7a3ad017deda309fb4a2453fcf9357795ea706cc87a9",
1770 "witness": "{\"signatures\":[\"ed739970d003f703da2f101a51767b63858f4894468cc334be04aa3befab1617a81e3eef093441afb499974152d279e59d9582a31dc68adbc17ffc22a2516086\",\"f9efe1c70eb61e7ad8bd615c50ff850410a4135ea73ba5fd8e12a734743ad045e575e9e76ea5c52c8e7908d3ad5c0eaae93337e5c11109e52848dc328d6757a2\"]}"
1771 }
1772 ],
1773 "outputs": [
1774 {
1775 "amount": 0,
1776 "id": "00bfa73302d12ffd",
1777 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1778 }
1779 ]
1780}"#;
1781
1782 let valid_melt: MeltRequest<String> = serde_json::from_str(valid_melt).unwrap();
1783
1784 assert!(
1785 valid_melt.verify_spending_conditions().is_ok(),
1786 "Valid SIG_ALL melt request should verify"
1787 );
1788 }
1789
1790 #[test]
1791 fn test_sig_all_melt_wrong_sig() {
1792 let invalid_melt = r#"{
1794 "inputs": [{
1795 "amount": 1,
1796 "secret": "[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_ALL\"]]}]",
1797 "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
1798 "id": "009a1f293253e41e",
1799 "witness": "{\"signatures\":[\"3426df9730d365a9d18d79bed2f3e78e9172d7107c55306ac5ddd1b2d065893366cfa24ff3c874ebf1fc22360ba5888ddf6ff5dbcb9e5f2f5a1368f7afc64f15\"]}"
1800 }],
1801 "quote": "test_quote_123",
1802 "outputs": null
1803 }"#;
1804
1805 let invalid_melt: MeltRequest<String> = serde_json::from_str(invalid_melt).unwrap();
1806 assert!(
1807 invalid_melt.verify_spending_conditions().is_err(),
1808 "Invalid SIG_ALL melt request should fail verification"
1809 );
1810 }
1811
1812 #[test]
1813 #[ignore]
1814 fn test_sig_all_melt_msg_to_sign() {
1815 let multisig_melt = r#"{
1816 "quote": "uHwJ-f6HFAC-lU2dMw0KOu6gd5S571FXQQHioYMD",
1817 "inputs": [
1818 {
1819 "amount": 4,
1820 "id": "00bfa73302d12ffd",
1821 "secret": "[\"P2PK\",{\"nonce\":\"f5c26c928fb4433131780105eac330338bb9c0af2b2fd29fad9e4f18c4a96d84\",\"data\":\"03c4840e19277822bfeecf104dcd3f38d95b33249983ac6fed755869f23484fb2a\",\"tags\":[[\"pubkeys\",\"0256dcc53d9330e0bc6e9b3d47c26287695aba9fe55cafdde6f46ef56e09582bfb\"],[\"n_sigs\",\"1\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1822 "C": "02174667f98114abeb741f4964bdc88a3b86efde0afa38f791094c1e07e5df3beb",
1823 "witness": "{\"signatures\":[\"abeeceba92bc7d1c514844ddb354d1e88a9776dfb55d3cdc5c289240386e401c3d983b68371ce5530e86c8fc4ff90195982a262f83fa8a5335b43e75af5f5fc7\"]}"
1824 }
1825 ],
1826 "outputs": [
1827 {
1828 "amount": 0,
1829 "id": "00bfa73302d12ffd",
1830 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1831 }
1832 ]
1833}"#;
1834
1835 let multisig_melt: MeltRequest<String> = serde_json::from_str(multisig_melt).unwrap();
1836
1837 assert!(
1838 multisig_melt.verify_spending_conditions().is_ok(),
1839 "melt request with SIG_ALL should succeed"
1840 );
1841
1842 let msg_to_sign = multisig_melt.sig_all_msg_to_sign();
1843
1844 assert_eq!(
1845 msg_to_sign,
1846 r#"["P2PK",{"nonce":"f5c26c928fb4433131780105eac330338bb9c0af2b2fd29fad9e4f18c4a96d84","data":"03c4840e19277822bfeecf104dcd3f38d95b33249983ac6fed755869f23484fb2a","tags":[["pubkeys","0256dcc53d9330e0bc6e9b3d47c26287695aba9fe55cafdde6f46ef56e09582bfb"],["n_sigs","1"],["sigflag","SIG_ALL"]]}]02174667f98114abeb741f4964bdc88a3b86efde0afa38f791094c1e07e5df3beb000bfa73302d12ffd038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39uHwJ-f6HFAC-lU2dMw0KOu6gd5S571FXQQHioYMD"#
1847 );
1848 }
1849
1850 #[test]
1851 #[ignore]
1852 fn test_sig_all_melt_multi_sig() {
1853 let multisig_melt = r#"{
1855 "quote": "wYHbJm5S1GTL28tDHoUAwcvb-31vu5kfDhnLxV9D",
1856 "inputs": [
1857 {
1858 "amount": 4,
1859 "id": "00bfa73302d12ffd",
1860 "secret": "[\"P2PK\",{\"nonce\":\"1705e988054354b703bc9103472cc5646ec76ed557517410186fa827c19c444d\",\"data\":\"024c8b5ec0e560f1fc77d7872ab75dd10a00af73a8ba715b81093b800849cb21fb\",\"tags\":[[\"pubkeys\",\"028d32bc906b3724724244812c450f688c548020f5d5a8c1d6cd1075650933d1a3\"],[\"n_sigs\",\"2\"],[\"sigflag\",\"SIG_ALL\"]]}]",
1861 "C": "02f2a0ff12c4dd95f2476662f1df49e5126f09a5ea1f3ce13b985db57661953072",
1862 "witness": "{\"signatures\":[\"a98a2616716d7813394a54ddc82234e5c47f0ddbddb98ccd1cad25236758fa235c8ae64d9fccd15efbe0ad5eba52a3df8433e9f1c05bc50defcb9161a5bd4bc4\",\"dd418cbbb23276dab8d72632ee77de730b932a3c6e8e15bc8802cef13db0b346915fe6e04e7fae03c3b5af026e25f71a24dc05b28135f0a9b69bc6c7289b6b8d\"]}"
1863 }
1864 ],
1865 "outputs": [
1866 {
1867 "amount": 0,
1868 "id": "00bfa73302d12ffd",
1869 "B_": "038ec853d65ae1b79b5cdbc2774150b2cb288d6d26e12958a16fb33c32d9a86c39"
1870 }
1871 ]
1872}"#;
1873
1874 let multisig_melt: MeltRequest<String> = serde_json::from_str(multisig_melt).unwrap();
1875 assert!(
1876 multisig_melt.verify_spending_conditions().is_ok(),
1877 "Multi-sig SIG_ALL melt request should verify with both signatures"
1878 );
1879
1880 let insufficient_sigs_melt = r#"{
1882 "inputs": [{
1883 "amount": 1,
1884 "secret": "[\"P2PK\",{\"nonce\":\"859d4935c4907062a6297cf4e663e2835d90d97ecdd510745d32f6816323a41f\",\"data\":\"0249098aa8b9d2fbec49ff8598feb17b592b986e62319a4fa488a3dc36387157a7\",\"tags\":[[\"sigflag\",\"SIG_ALL\"],[\"pubkeys\",\"0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798\",\"02142715675faf8da1ecc4d51e0b9e539fa0d52fdd96ed60dbe99adb15d6b05ad9\"],[\"n_sigs\",\"2\"]]}]",
1885 "C": "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
1886 "id": "009a1f293253e41e",
1887 "witness": "{\"signatures\":[\"83564aca48c668f50d022a426ce0ed19d3a9bdcffeeaee0dc1e7ea7e98e9eff1840fcc821724f623468c94f72a8b0a7280fa9ef5a54a1b130ef3055217f467b3\"]}"
1888 }],
1889 "quote": "test_quote_123",
1890 "outputs": null
1891 }"#;
1892
1893 let insufficient_sigs_melt: MeltRequest<String> =
1894 serde_json::from_str(insufficient_sigs_melt).unwrap();
1895 assert!(
1896 insufficient_sigs_melt.verify_spending_conditions().is_err(),
1897 "Multi-sig SIG_ALL melt request should fail with insufficient signatures"
1898 );
1899 }
1900
1901 fn create_test_keys() -> (SecretKey, PublicKey) {
1903 let secret_key =
1904 SecretKey::from_str("99590802251e78ee1051648439eedb003dc539093a48a44e7b8f2642c909ea37")
1905 .unwrap();
1906 let pubkey = secret_key.public_key();
1907 (secret_key, pubkey)
1908 }
1909
1910 #[test]
1911 fn test_sig_all_basic_signing_verification() {
1912 let (secret_key, pubkey) = create_test_keys();
1913
1914 let conditions = Conditions {
1916 sig_flag: SigFlag::SigAll,
1917 ..Default::default()
1918 };
1919
1920 let secret = create_test_secret(pubkey, conditions);
1921 let proof1 = create_test_proof(secret.clone(), pubkey, "009a1f293253e41e");
1922 let proof2 = create_test_proof(secret, pubkey, "009a1f293253e41f");
1923 let blinded_msg = create_test_blinded_msg(pubkey);
1924
1925 let mut swap = SwapRequest::new(vec![proof1, proof2], vec![blinded_msg]);
1927 assert!(
1928 swap.verify_spending_conditions().is_err(),
1929 "Unsigned swap should fail verification"
1930 );
1931
1932 assert!(
1933 swap.sign_sig_all(secret_key).is_ok(),
1934 "Signing should succeed"
1935 );
1936
1937 println!("{}", serde_json::to_string(&swap).unwrap());
1938
1939 assert!(
1940 swap.verify_spending_conditions().is_ok(),
1941 "Signed swap should pass verification"
1942 );
1943 }
1944
1945 #[test]
1946 fn test_sig_all_unauthorized_key() {
1947 let (_secret_key, pubkey) = create_test_keys();
1948
1949 let conditions = Conditions {
1950 sig_flag: SigFlag::SigAll,
1951 ..Default::default()
1952 };
1953
1954 let secret = create_test_secret(pubkey, conditions);
1955 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
1956 let blinded_msg = create_test_blinded_msg(pubkey);
1957
1958 let unauthorized_key =
1960 SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
1961 .unwrap();
1962
1963 let mut swap = SwapRequest::new(vec![proof], vec![blinded_msg]);
1964
1965 swap.sign_sig_all(unauthorized_key).unwrap();
1967
1968 assert!(
1970 swap.verify_spending_conditions().is_err(),
1971 "Verification should fail with unauthorized key signature"
1972 );
1973 }
1974
1975 #[test]
1976 fn test_sig_all_mismatched_secrets() {
1977 let (secret_key, pubkey) = create_test_keys();
1978
1979 let conditions = Conditions {
1980 sig_flag: SigFlag::SigAll,
1981 ..Default::default()
1982 };
1983
1984 let secret1 = create_test_secret(pubkey, conditions.clone());
1986
1987 let different_secret: Secret = SpendingConditions::new_p2pk(
1989 PublicKey::from_str(
1990 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
1991 )
1992 .unwrap(),
1993 Some(conditions),
1994 )
1995 .try_into()
1996 .unwrap();
1997
1998 let proof1 = create_test_proof(secret1, pubkey, "009a1f293253e41e");
1999 let proof2 = create_test_proof(different_secret, pubkey, "009a1f293253e41f");
2000 let blinded_msg = create_test_blinded_msg(pubkey);
2001
2002 let mut swap = SwapRequest::new(vec![proof1, proof2], vec![blinded_msg]);
2003
2004 swap.sign_sig_all(secret_key).unwrap();
2006
2007 assert!(
2009 swap.verify_spending_conditions().is_err(),
2010 "Verification should fail with mismatched secrets"
2011 );
2012 }
2013
2014 #[test]
2015 fn test_sig_all_wrong_flag() {
2016 let (secret_key, pubkey) = create_test_keys();
2017
2018 let sig_inputs_conditions = Conditions {
2020 sig_flag: SigFlag::SigInputs,
2021 ..Default::default()
2022 };
2023
2024 let secret = create_test_secret(pubkey, sig_inputs_conditions);
2025 let proof = create_test_proof(secret, pubkey, "009a1f293253e41e");
2026 let blinded_msg = create_test_blinded_msg(pubkey);
2027
2028 let mut swap = SwapRequest::new(vec![proof], vec![blinded_msg]);
2029
2030 swap.sign_sig_all(secret_key).unwrap();
2032
2033 assert!(
2035 swap.verify_spending_conditions().is_err(),
2036 "Verification should fail with SIG_INPUTS flag when sign_sig_all was used"
2037 );
2038 }
2039
2040 #[test]
2041 fn test_sig_all_multiple_signatures() {
2042 let (secret_key1, pubkey1) = create_test_keys();
2043 let secret_key2 =
2044 SecretKey::from_str("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
2045 .unwrap();
2046 let pubkey2 = secret_key2.public_key();
2047
2048 let conditions = Conditions {
2050 num_sigs: Some(2),
2051 sig_flag: SigFlag::SigAll,
2052 pubkeys: Some(vec![pubkey2]),
2053 ..Default::default()
2054 };
2055
2056 let secret = create_test_secret(pubkey1, conditions);
2057 let proof = create_test_proof(secret, pubkey1, "009a1f293253e41e");
2058 let blinded_msg = create_test_blinded_msg(pubkey1);
2059
2060 let mut swap = SwapRequest::new(vec![proof], vec![blinded_msg]);
2061
2062 assert!(
2064 swap.sign_sig_all(secret_key1).is_ok(),
2065 "First signature should succeed"
2066 );
2067 assert!(
2068 swap.verify_spending_conditions().is_err(),
2069 "Single signature should not verify when two required"
2070 );
2071
2072 assert!(
2074 swap.sign_sig_all(secret_key2).is_ok(),
2075 "Second signature should succeed"
2076 );
2077
2078 assert!(
2079 swap.verify_spending_conditions().is_ok(),
2080 "Both signatures should verify"
2081 );
2082 }
2083
2084 #[test]
2085 fn test_conditions_valid_multisig() {
2086 let pubkey1 = PublicKey::from_str(
2087 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2088 )
2089 .unwrap();
2090 let pubkey2 = PublicKey::from_str(
2091 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2092 )
2093 .unwrap();
2094
2095 let result = Conditions::new(
2097 None,
2098 Some(vec![pubkey1, pubkey2]),
2099 None,
2100 Some(2),
2101 None,
2102 None,
2103 );
2104 assert!(result.is_ok(), "2-of-3 multisig should be valid");
2105 }
2106
2107 #[test]
2108 fn test_conditions_impossible_multisig() {
2109 let pubkey1 = PublicKey::from_str(
2110 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2111 )
2112 .unwrap();
2113 let pubkey2 = PublicKey::from_str(
2114 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2115 )
2116 .unwrap();
2117
2118 let result = Conditions::new(
2120 None,
2121 Some(vec![pubkey1, pubkey2]),
2122 None,
2123 Some(5),
2124 None,
2125 None,
2126 );
2127 assert!(result.is_err(), "5-of-3 multisig should be impossible");
2128 let err = result.unwrap_err();
2129 assert!(
2130 matches!(
2131 err,
2132 crate::nut10::Error::NUT11(Error::ImpossibleMultisigConfiguration {
2133 required: 5,
2134 available: 3,
2135 })
2136 ),
2137 "Expected ImpossibleMultisigConfiguration, got: {err:?}"
2138 );
2139 }
2140
2141 #[test]
2142 fn test_conditions_valid_single_sig_no_pubkeys() {
2143 let result = Conditions::new(None, None, None, Some(1), None, None);
2145 assert!(result.is_ok(), "1-of-1 (data key only) should be valid");
2146 }
2147
2148 #[test]
2149 fn test_conditions_impossible_no_pubkeys() {
2150 let result = Conditions::new(None, None, None, Some(2), None, None);
2152 assert!(result.is_err(), "2-of-1 multisig should be impossible");
2153 let err = result.unwrap_err();
2154 assert!(
2155 matches!(
2156 err,
2157 crate::nut10::Error::NUT11(Error::ImpossibleMultisigConfiguration {
2158 required: 2,
2159 available: 1,
2160 })
2161 ),
2162 "Expected ImpossibleMultisigConfiguration, got: {err:?}"
2163 );
2164 }
2165
2166 #[test]
2167 fn test_conditions_impossible_refund_multisig() {
2168 let refund_key1 = PublicKey::from_str(
2169 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2170 )
2171 .unwrap();
2172
2173 let result = Conditions::new(None, None, Some(vec![refund_key1]), None, None, Some(3));
2175 assert!(
2176 result.is_err(),
2177 "3-of-1 refund multisig should be impossible"
2178 );
2179 let err = result.unwrap_err();
2180 assert!(
2181 matches!(
2182 err,
2183 crate::nut10::Error::NUT11(Error::ImpossibleRefundMultisigConfiguration {
2184 required: 3,
2185 available: 1,
2186 })
2187 ),
2188 "Expected ImpossibleRefundMultisigConfiguration, got: {err:?}"
2189 );
2190 }
2191
2192 #[test]
2193 fn test_conditions_valid_refund_multisig() {
2194 let refund_key1 = PublicKey::from_str(
2195 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2196 )
2197 .unwrap();
2198 let refund_key2 = PublicKey::from_str(
2199 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2200 )
2201 .unwrap();
2202
2203 let result = Conditions::new(
2205 None,
2206 None,
2207 Some(vec![refund_key1, refund_key2]),
2208 None,
2209 None,
2210 Some(2),
2211 );
2212 assert!(result.is_ok(), "2-of-2 refund multisig should be valid");
2213 }
2214
2215 #[test]
2216 fn test_conditions_impossible_refund_no_keys() {
2217 let result = Conditions::new(None, None, None, None, None, Some(1));
2219 assert!(
2220 result.is_err(),
2221 "1-of-0 refund multisig should be impossible"
2222 );
2223 let err = result.unwrap_err();
2224 assert!(
2225 matches!(
2226 err,
2227 crate::nut10::Error::NUT11(Error::ImpossibleRefundMultisigConfiguration {
2228 required: 1,
2229 available: 0,
2230 })
2231 ),
2232 "Expected ImpossibleRefundMultisigConfiguration, got: {err:?}"
2233 );
2234 }
2235
2236 #[test]
2237 fn test_conditions_num_sigs_exact_boundary() {
2238 let pubkey1 = PublicKey::from_str(
2239 "033281c37677ea273eb7183b783067f5244933ef78d8c3f15b1a77cb246099c26e",
2240 )
2241 .unwrap();
2242 let pubkey2 = PublicKey::from_str(
2243 "02698c4e2b5f9534cd0687d87513c759790cf829aa5739184a3e3735471fbda904",
2244 )
2245 .unwrap();
2246
2247 let result = Conditions::new(
2249 None,
2250 Some(vec![pubkey1, pubkey2]),
2251 None,
2252 Some(3),
2253 None,
2254 None,
2255 );
2256 assert!(result.is_ok(), "3-of-3 (exact keys count) should be valid");
2257
2258 let result = Conditions::new(
2260 None,
2261 Some(vec![pubkey1, pubkey2]),
2262 None,
2263 Some(4),
2264 None,
2265 None,
2266 );
2267 assert!(result.is_err(), "4-of-3 should be impossible");
2268 }
2269
2270 #[test]
2271 fn test_p2pk_num_sigs_zero_bypasses_signature_requirement() {
2272 let data_pubkey = PublicKey::from_str(
2281 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198",
2282 )
2283 .unwrap();
2284
2285 let conditions = Conditions {
2286 locktime: None,
2287 pubkeys: None,
2288 refund_keys: None,
2289 num_sigs: Some(0), sig_flag: SigFlag::SigInputs,
2291 num_sigs_refund: None,
2292 };
2293
2294 let result: Result<Secret, _> =
2295 SpendingConditions::new_p2pk(data_pubkey, Some(conditions)).try_into();
2296
2297 assert!(matches!(
2298 result,
2299 Err(crate::nut10::Error::NUT11(Error::ZeroSignaturesRequired))
2300 ));
2301 }
2302
2303 #[test]
2304 fn test_sig_all_p2pk_nsigs_zero_bypasses_sig_check() {
2305 let data_pubkey = PublicKey::from_str(
2306 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198",
2307 )
2308 .unwrap();
2309 let extra_pubkey = PublicKey::from_str(
2310 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
2311 )
2312 .unwrap();
2313
2314 let nut10_secret = Nut10Secret::new(
2322 crate::nuts::nut10::Kind::P2PK,
2323 crate::nuts::nut10::SecretData::new(
2324 data_pubkey.to_string(),
2325 Some(vec![
2326 vec!["pubkeys".to_string(), extra_pubkey.to_string()],
2327 vec!["n_sigs".to_string(), "0".to_string()],
2328 vec!["sigflag".to_string(), "SIG_ALL".to_string()],
2329 ]),
2330 ),
2331 );
2332 let conditions_res = crate::nuts::nut10::Conditions::try_from(
2333 nut10_secret.secret_data().tags().cloned().unwrap(),
2334 );
2335 assert!(
2336 conditions_res.is_err(),
2337 "Conditions should fail to parse due to n_sigs=0"
2338 );
2339 }
2340
2341 #[test]
2342 fn test_refund_path_num_sigs_refund_zero_bypasses_signature_check() {
2343 let data_pubkey = PublicKey::from_str(
2353 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198",
2354 )
2355 .unwrap();
2356
2357 let refund_pubkey = PublicKey::from_str(
2358 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
2359 )
2360 .unwrap();
2361
2362 let nut10_secret = Nut10Secret::new(
2363 crate::nuts::nut10::Kind::P2PK,
2364 crate::nuts::nut10::SecretData::new(
2365 data_pubkey.to_string(),
2366 Some(vec![
2367 vec!["locktime".to_string(), "1".to_string()],
2368 vec!["refund".to_string(), refund_pubkey.to_string()],
2369 vec!["n_sigs".to_string(), "1".to_string()],
2370 vec!["n_sigs_refund".to_string(), "0".to_string()],
2371 ]),
2372 ),
2373 );
2374 let conditions_res = crate::nuts::nut10::Conditions::try_from(
2375 nut10_secret.secret_data().tags().cloned().unwrap(),
2376 );
2377 assert!(
2378 conditions_res.is_err(),
2379 "Conditions should fail to parse due to n_sigs=0"
2380 );
2381 }
2382
2383 #[test]
2384 fn test_sig_all_p2pk_nsigs_refund_zero_bypasses_refund_sig_check() {
2385 let data_pubkey = PublicKey::from_str(
2386 "026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198",
2387 )
2388 .unwrap();
2389 let refund_pubkey = PublicKey::from_str(
2390 "02a9acc1e48c25eeeb9289b5031cc57da9fe72f3fe2861d264bdc074209b107ba2",
2391 )
2392 .unwrap();
2393
2394 let nut10_secret = Nut10Secret::new(
2395 crate::nuts::nut10::Kind::P2PK,
2396 crate::nuts::nut10::SecretData::new(
2397 data_pubkey.to_string(),
2398 Some(vec![
2399 vec!["locktime".to_string(), "1".to_string()],
2400 vec!["refund".to_string(), refund_pubkey.to_string()],
2401 vec!["n_sigs".to_string(), "1".to_string()],
2402 vec!["n_sigs_refund".to_string(), "0".to_string()],
2403 vec!["sigflag".to_string(), "SIG_ALL".to_string()],
2404 ]),
2405 ),
2406 );
2407 let conditions_res = crate::nuts::nut10::Conditions::try_from(
2408 nut10_secret.secret_data().tags().cloned().unwrap(),
2409 );
2410 assert!(
2411 conditions_res.is_err(),
2412 "Conditions should fail to parse due to n_sigs_refund=0"
2413 );
2414 }
2415}