Skip to main content

cashu/nuts/nut11/
mod.rs

1//! NUT-11: Pay to Public Key (P2PK)
2//!
3//! <https://github.com/cashubtc/nuts/blob/main/11.md>
4
5use 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/// Nut11 Error
26#[derive(Debug, Error)]
27pub enum Error {
28    /// Incorrect secret kind
29    #[error("Secret is not a p2pk secret")]
30    IncorrectSecretKind,
31    /// Incorrect secret kind
32    #[error("Witness is not a p2pk witness")]
33    IncorrectWitnessKind,
34    /// P2PK locktime has already passed
35    #[error("Locktime in past")]
36    LocktimeInPast,
37    /// Witness signature is not valid
38    #[error("Invalid signature")]
39    InvalidSignature,
40    /// Unknown tag in P2PK secret
41    #[error("Unknown tag P2PK secret")]
42    UnknownTag,
43    /// Unknown Sigflag
44    #[error("Unknown sigflag")]
45    UnknownSigFlag,
46    /// P2PK Spend conditions not meet
47    #[error("P2PK spend conditions are not met")]
48    SpendConditionsNotMet,
49    /// Pubkey must be in data field of P2PK
50    #[error("P2PK required in secret data")]
51    P2PKPubkeyRequired,
52    /// Witness Signatures not provided
53    #[error("Witness signatures not provided")]
54    SignaturesNotProvided,
55    /// Duplicate signature from same pubkey
56    #[error("Duplicate signature from the same pubkey detected")]
57    DuplicateSignature,
58    /// Duplicate public key in multisig (same x-coordinate)
59    #[error("Duplicate public key in multisig (same x-coordinate)")]
60    DuplicatePubkey,
61    /// Impossible multisig configuration: num_sigs exceeds available pubkeys
62    #[error(
63        "Impossible multisig: required {required} signatures but only {available} keys available"
64    )]
65    ImpossibleMultisigConfiguration {
66        /// Number of signatures required
67        required: u64,
68        /// Number of available keys
69        available: u64,
70    },
71    /// Impossible refund multisig configuration: num_sigs_refund exceeds refund keys
72    #[error("Impossible refund multisig: required {required} signatures but only {available} refund keys available")]
73    ImpossibleRefundMultisigConfiguration {
74        /// Number of refund signatures required
75        required: u64,
76        /// Number of available refund keys
77        available: u64,
78    },
79    /// Preimage not supported in P2PK
80    #[error("P2PK does not support preimage requirements")]
81    PreimageNotSupportedInP2PK,
82    /// SIG_ALL not supported in this context
83    #[error("SIG_ALL proofs must be verified using a different method")]
84    SigAllNotSupportedHere,
85    /// Number of required signatures cannot be zero
86    #[error("Number of required signatures must be 1 or greater")]
87    ZeroSignaturesRequired,
88    /// Serde Json error
89    #[error(transparent)]
90    SerdeJsonError(#[from] serde_json::Error),
91    /// Secp256k1 error
92    #[error(transparent)]
93    Secp256k1(#[from] bitcoin::secp256k1::Error),
94    /// NUT01 Error
95    #[error(transparent)]
96    NUT01(#[from] crate::nuts::nut01::Error),
97}
98
99/// P2Pk Witness
100#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
101pub struct P2PKWitness {
102    /// Signatures
103    pub signatures: Vec<String>,
104}
105
106impl P2PKWitness {
107    #[inline]
108    /// Check id Witness is empty
109    pub fn is_empty(&self) -> bool {
110        self.signatures.is_empty()
111    }
112}
113
114impl Proof {
115    /// Sign [Proof]
116    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    /// Verify P2PK signature on [Proof]
137    ///
138    /// Per NUT-11, there are two spending pathways after locktime:
139    /// 1. Primary path (data + pubkeys): ALWAYS available
140    /// 2. Refund path (refund keys): available AFTER locktime
141    ///
142    /// The verification tries both paths - if either succeeds, the proof is valid.
143    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        // Get spending requirements (includes both primary and refund paths)
162        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        // Extract witness signatures
174        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        // Try primary path first (data + pubkeys)
182        // Per NUT-11: "Locktime Multisig conditions continue to apply"
183        {
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        // Primary path failed or no signatures - try refund path if available
201        {
202            if let Some(refund_path) = &requirements.refund_path {
203                // Anyone can spend (locktime passed, no refund keys)
204                if refund_path.required_sigs == 0 {
205                    return Ok(());
206                }
207
208                // Need signatures for refund path
209                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        // Neither path succeeded
227        if witness_signatures.is_none() {
228            Err(Error::SignaturesNotProvided)
229        } else {
230            Err(Error::SpendConditionsNotMet)
231        }
232    }
233}
234
235impl SpendingConditions {
236    /// New P2PK [SpendingConditions]
237    pub fn new_p2pk(pubkey: PublicKey, conditions: Option<Conditions>) -> Self {
238        Self::P2PKConditions {
239            data: pubkey,
240            conditions,
241        }
242    }
243}
244
245/// Extract and parse Schnorr signatures from a witness
246///
247/// This helper function extracts signature strings from a witness and parses them
248/// into bitcoin secp256k1 Schnorr signatures.
249pub(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
263/// Verify P2PK SIG_ALL signatures
264///
265/// Do NOT call this directly. This is called only from 'verify_full_sig_all_check',
266/// which has already done many important SIG_ALL checks. This performs the final
267/// signature verification for SIG_ALL+P2PK transactions.
268///
269/// Per NUT-11, there are two spending pathways after locktime:
270/// 1. Primary path (data + pubkeys): ALWAYS available
271/// 2. Refund path (refund keys): available AFTER locktime
272pub(crate) fn verify_sig_all_p2pk(first_input: &Proof, msg_to_sign: String) -> Result<(), Error> {
273    // Get the first input, as it's the one with the signatures
274    let first_secret =
275        Nut10Secret::try_from(&first_input.secret).map_err(|_| Error::IncorrectSecretKind)?;
276
277    // Record current time for locktime evaluation
278    let current_time = crate::util::unix_time();
279
280    // Get spending requirements (includes both primary and refund paths)
281    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    // Check for "anyone can spend" case first (locktime passed, no refund keys)
290    // This doesn't require any signatures
291    if let Some(refund_path) = &requirements.refund_path {
292        if refund_path.required_sigs == 0 {
293            return Ok(());
294        }
295    }
296
297    // Get the witness (needed for signature extraction)
298    let first_witness = first_input
299        .witness
300        .as_ref()
301        .ok_or(Error::SignaturesNotProvided)?;
302
303    // Try primary path first (data + pubkeys)
304    // Per NUT-11: "Locktime Multisig conditions continue to apply"
305    {
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    // Primary path failed - try refund path if available
319    {
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    // Neither path succeeded
333    Err(Error::SpendConditionsNotMet)
334}
335
336/// Returns count of valid signatures (each public key is only counted once)
337/// Returns error if the same pubkey has multiple valid signatures
338pub(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                // Insert the x-only coordinate to prevent double counting
349                let x_only = pubkey.x_only_public_key();
350                // If the pubkey's x-coordinate is already verified, return a duplicate signature error
351                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    /// Sign [BlindedMessage]
363    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    /// Verify P2PK conditions on [BlindedMessage]
384    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/// Signature flag
422///
423/// Defined in [NUT11](https://github.com/cashubtc/nuts/blob/main/11.md)
424#[derive(
425    Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, PartialOrd, Ord, Hash,
426)]
427pub enum SigFlag {
428    #[default]
429    /// Requires valid signatures on all inputs.
430    /// It is the default signature flag and will be applied even if the
431    /// `sigflag` tag is absent.
432    SigInputs,
433    /// Requires valid signatures on all inputs and on all outputs.
434    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
457/// Get the signature flag that should be enforced for a set of proofs and the
458/// public keys that signatures are valid for
459pub 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/// Enforce Sigflag info
499#[derive(Debug, Clone, PartialEq, Eq)]
500pub struct EnforceSigFlag {
501    /// Sigflag required for proofs
502    pub sig_flag: SigFlag,
503    /// Pubkeys that can sign for proofs
504    pub pubkeys: HashSet<PublicKey>,
505    /// Number of sigs required for proofs
506    pub sigs_required: u64,
507}
508
509impl SwapRequest {
510    /// Sign swap request with SIG_ALL
511    pub fn sign_sig_all(&mut self, secret_key: SecretKey) -> Result<(), Error> {
512        // Get message to sign
513        let msg = self.sig_all_msg_to_sign();
514        let signature = secret_key.sign(msg.as_bytes())?;
515
516        // Add signature to first input witness
517        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    /// Sign melt request with SIG_ALL
542    pub fn sign_sig_all(&mut self, secret_key: SecretKey) -> Result<(), Error> {
543        // Get message to sign
544        let msg = self.sig_all_msg_to_sign();
545        let signature = secret_key.sign(msg.as_bytes())?;
546
547        // Add signature to first input witness
548        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        // Proof with a valid signature
670        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        // Proof with a signature that is in a different secret
683        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        // Proof with 2 valid signatures to satifiy the condition
693        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        // Proof with only one of the required signatures
700        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        // Verification should fail without the requires signatures
705        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        // One signature, one pubkey - OK
716        assert_eq!(valid_signatures(msg, &[pubkey], &[sig]).unwrap(), 1);
717
718        // Two identical signatures, one pubkey - Error
719        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        // Sign once
737        blinded_message.sign_p2pk(secret_key).unwrap();
738
739        // Add the same signature again to the witness
740        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        // Verification should fail with DuplicateSignature error
746        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        // Create another pubkey with same x-coordinate but different parity
760        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        // Sign once
772        blinded_message.sign_p2pk(secret_key).unwrap();
773
774        // Verification should fail if both pubkeys are provided
775        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        // data is pk_02, pubkeys contains pk_03. They are duplicates in the primary path.
794        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        // We sign twice (since we need 2 sigs). It can even be the same signature or different (if we had different messages, but here it's the same message).
821        proof.sign_p2pk(secret_key.clone()).unwrap();
822        // Just duplicate the signature in the witness
823        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's modify the test to put a duplicate key inside the refund path.
848        let conditions = Conditions {
849            locktime: Some(0),
850            pubkeys: None,
851            refund_keys: Some(vec![pk_03, pk_02]), // Duplicate x-coord in refund path
852            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        // Per NUT-11: primary path (pubkeys) is ALWAYS available, even after locktime
945        // Signing with a key from pubkeys should succeed
946        assert!(proof.verify_p2pk().is_ok());
947
948        proof.witness = None;
949
950        // Sign with secret_key (pubkey = v_key, which is the data key and part of primary path)
951        // Per NUT-11: primary path is always available, and data key is part of primary path
952        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        // Adding more signatures still works (but wasn't needed for primary path)
959        proof.sign_p2pk(signing_key_two).unwrap();
960        assert!(proof.verify_p2pk().is_ok());
961    }
962
963    // Helper functions for melt request tests
964    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        // Create conditions with SIG_ALL
999        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        // Create melt request
1009        let mut melt = MeltRequest::new(
1010            QuoteId::UUID(Uuid::new_v4()),
1011            vec![proof],
1012            Some(vec![blinded_msg]),
1013        );
1014
1015        // Before signing, should fail verification
1016        assert!(
1017            melt.verify_spending_conditions().is_err(),
1018            "Unsigned melt request should fail verification"
1019        );
1020
1021        // Sign the request
1022        assert!(
1023            melt.sign_sig_all(secret_key).is_ok(),
1024            "Signing should succeed"
1025        );
1026
1027        // After signing, should pass verification
1028        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        // Create conditions with explicit authorized pubkey
1042        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        // Sign with unauthorized key
1054        let unauthorized_key =
1055            SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
1056                .unwrap();
1057        melt.sign_sig_all(unauthorized_key).unwrap();
1058
1059        // Verification should fail (unauthorized signature)
1060        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        // Create conditions with SIG_INPUTS instead of SIG_ALL
1074        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        // Signing
1086        melt.sign_sig_all(secret_key).unwrap();
1087
1088        // Verification should fail (wrong flag - expected SIG_ALL)
1089        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        // Create conditions
1103        let conditions = Conditions {
1104            sig_flag: SigFlag::SigAll,
1105            ..Default::default()
1106        };
1107
1108        let secret = create_test_secret(pubkey, conditions);
1109
1110        // Create two proofs with same secret
1111        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        // Signing should work with multiple matching inputs
1117        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        // Create first secret and proof
1135        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        // Create second secret with different data
1143        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        // Signing should succeed (no validation during signing)
1158        melt.sign_sig_all(secret_key).unwrap();
1159
1160        // Verification should fail (catches mismatched inputs)
1161        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        // Create conditions requiring 2 signatures
1180        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        // First signature
1199        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        // Second signature
1209        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        // Get message to sign
1241        let msg = melt.sig_all_msg_to_sign();
1242
1243        // Verify all components are present in the message
1244        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(&quote_id.to_string()),
1254            "Message should contain quote ID"
1255        );
1256    }
1257
1258    // "SIG_ALL Test Vectors", starting with swaps : https://github.com/cashubtc/nuts/blob/5b050c7960607cca0481c28517cab5cc091b5d2e/tests/11-test.md#sig_all-test-vectors
1259    #[test]
1260    fn test_sig_all_swap_single_sig() {
1261        // Valid SwapRequest with SIG_ALL signature
1262        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        // Verify the message format
1284        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        // Verify the SHA256 hash of the message
1291        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        // The following is a SwapRequest with a valid sig_all signature.
1307        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        // The following is a SwapRequest that is invalid as there are multiple secrets.
1337        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        // The following is a SwapRequest multiple valid signatures are provided and required.
1383        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        // The following is an invalid SwapRequest with pubkeys and refund mixed.
1413        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        // The following is a SwapRequest with locktime passed and refund keys signatures are valid
1448        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        // The following is a valid `SwapRequest` with an HTLC also locked to a public key
1478        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        // The following is an invalid SwapRequest with an HTLC also locked to a public key, locktime and refund key. locktime is not expired but proof is signed with refund key.
1508        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        // The following is a valid SwapRequest with a multisig HTLC using the refund path.
1543        // Per NUT-14: After locktime, the refund path is available when preimage is invalid/missing.
1544        // The preimage is intentionally invalid (all zeros) to test the refund path.
1545        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        // Invalid SwapRequest - mismatched inputs with SIG_ALL
1575        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        // SwapRequest with mixed up signatures from pubkey and refund_pubkeys
1620        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        // SwapRequest signed with refund_pubkey without expiration
1654        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    // Now the Melt examples at the end of 11-test.md
1681    #[test]
1682    fn test_sig_all_melt() {
1683        // Valid MeltRequest with SIG_ALL signature
1684        // Example MeltRequest:
1685        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        // Verify the message format
1708        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        // Verify the SHA256 hash of the message
1715        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        // The following is a valid SIG_ALL MeltRequest.
1731        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        // The following is a valid multi-sig SIG_ALL MeltRequest.
1762        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        // Invalid MeltRequest - wrong signature for SIG_ALL
1793        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        // MeltRequest with multi-sig SIG_ALL requiring 2 signatures
1854        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        // MeltRequest with insufficient signatures for multi-sig SIG_ALL
1881        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    // Helper functions for tests
1902    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        // Create basic SIG_ALL conditions
1915        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        // Test basic signing flow
1926        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        // Create unauthorized key
1959        let unauthorized_key =
1960            SecretKey::from_str("0000000000000000000000000000000000000000000000000000000000000001")
1961                .unwrap();
1962
1963        let mut swap = SwapRequest::new(vec![proof], vec![blinded_msg]);
1964
1965        // Signing should succeed (no validation)
1966        swap.sign_sig_all(unauthorized_key).unwrap();
1967
1968        // Verification should fail (unauthorized signature)
1969        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        // Create first proof with original secret
1985        let secret1 = create_test_secret(pubkey, conditions.clone());
1986
1987        // Create second proof with different secret data
1988        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        // Signing should succeed (no validation)
2005        swap.sign_sig_all(secret_key).unwrap();
2006
2007        // Verification should fail (mismatched secrets)
2008        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        // Create conditions with SIG_INPUTS instead of SIG_ALL
2019        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        // Signing should succeed (no validation)
2031        swap.sign_sig_all(secret_key).unwrap();
2032
2033        // Verification should fail (wrong flag - has SIG_INPUTS but sign_sig_all expects SIG_ALL)
2034        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        // Create conditions requiring 2 signatures
2049        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        // Sign with first key
2063        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        // Sign with second key
2073        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        // 2 additional pubkeys + 1 data key = 3 available, requiring 2 should succeed
2096        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        // 2 additional pubkeys + 1 data key = 3 available, requiring 5 is impossible
2119        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        // No additional pubkeys, only data key (1 available), requiring 1 should succeed
2144        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        // No additional pubkeys, only data key (1 available), requiring 2 is impossible
2151        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        // 1 refund key, requiring 3 refund sigs is impossible
2174        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        // 2 refund keys, requiring 2 should succeed
2204        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        // No refund keys, requiring 1 refund sig is impossible
2218        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        // 2 additional pubkeys + 1 data key = 3, requiring exactly 3 should succeed
2248        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        // Requiring 4 should fail
2259        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        // n_sigs=0 makes verify_p2pk() evaluate `valid_sig_count >= required_sigs`
2273        // as `0 >= 0` which is trivially true. An empty P2PKWitness therefore
2274        // passes verification with no cryptographic check at all.
2275        //
2276        // Per bkb's record of NUT-11: n_sigs must be "a positive integer".
2277        // n_sigs=0 is out-of-spec and produces an anyone-can-spend P2PK proof.
2278        //
2279        // FAILS on HEAD (verify_p2pk returns Ok). PASSES once fixed.
2280        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), // invalid: not a positive integer per NUT-11
2290            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        // Note: We bypass Conditions::new() by constructing it directly,
2315        // to test the low-level verify routine. But because we implemented
2316        // ZeroSignaturesRequired inside TryFrom for Conditions too,
2317        // Secret::try_into() will fail on building the conditions!
2318        // To make the test actually exercise verify_sig_all_p2pk on a "bad" Secret,
2319        // we have to assert that either creation fails or verify_sig_all_p2pk fails.
2320        // Given our fix prevents creation entirely, the test should check that.
2321        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        // When n_sigs_refund=Some(0) and refund_keys are present,
2344        // get_pubkeys_and_required_sigs sets required_sigs=0 for the RefundPath.
2345        // In verify_p2pk the guard
2346        //   if refund_path.required_sigs == 0 { return Ok(()); }
2347        // fires once locktime has passed, letting anyone spend with an empty
2348        // witness and no valid refund-key signature.
2349        //
2350        // FAILS on HEAD (verify_p2pk returns Ok). PASSES once the fix is applied.
2351
2352        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}