cashu/nuts/nut00/
mod.rs

1//! NUT-00: Notation and Models
2//!
3//! <https://github.com/cashubtc/nuts/blob/main/00.md>
4
5use std::cmp::Ordering;
6use std::collections::{HashMap, HashSet};
7use std::fmt;
8use std::hash::{Hash, Hasher};
9use std::str::FromStr;
10use std::string::FromUtf8Error;
11
12use serde::{de, Deserialize, Deserializer, Serialize};
13use thiserror::Error;
14
15use super::nut02::ShortKeysetId;
16#[cfg(feature = "wallet")]
17use super::nut10;
18#[cfg(feature = "wallet")]
19use super::nut11::SpendingConditions;
20#[cfg(feature = "wallet")]
21use crate::amount::FeeAndAmounts;
22#[cfg(feature = "wallet")]
23use crate::amount::SplitTarget;
24#[cfg(feature = "wallet")]
25use crate::dhke::blind_message;
26use crate::dhke::hash_to_curve;
27use crate::nuts::nut01::PublicKey;
28#[cfg(feature = "wallet")]
29use crate::nuts::nut01::SecretKey;
30use crate::nuts::nut11::{serde_p2pk_witness, P2PKWitness};
31use crate::nuts::nut12::BlindSignatureDleq;
32use crate::nuts::nut14::{serde_htlc_witness, HTLCWitness};
33use crate::nuts::{Id, ProofDleq};
34use crate::secret::Secret;
35use crate::Amount;
36
37pub mod token;
38pub use token::{Token, TokenV3, TokenV4};
39
40/// List of [Proof]
41pub type Proofs = Vec<Proof>;
42
43/// Utility methods for [Proofs]
44pub trait ProofsMethods {
45    /// Count proofs by keyset
46    fn count_by_keyset(&self) -> HashMap<Id, u64>;
47
48    /// Sum proofs by keyset
49    fn sum_by_keyset(&self) -> HashMap<Id, Amount>;
50
51    /// Try to sum up the amounts of all [Proof]s
52    fn total_amount(&self) -> Result<Amount, Error>;
53
54    /// Try to fetch the pubkeys of all [Proof]s
55    fn ys(&self) -> Result<Vec<PublicKey>, Error>;
56
57    /// Create a copy of proofs without dleqs
58    fn without_dleqs(&self) -> Proofs;
59}
60
61impl ProofsMethods for Proofs {
62    fn count_by_keyset(&self) -> HashMap<Id, u64> {
63        count_by_keyset(self.iter())
64    }
65
66    fn sum_by_keyset(&self) -> HashMap<Id, Amount> {
67        sum_by_keyset(self.iter())
68    }
69
70    fn total_amount(&self) -> Result<Amount, Error> {
71        total_amount(self.iter())
72    }
73
74    fn ys(&self) -> Result<Vec<PublicKey>, Error> {
75        ys(self.iter())
76    }
77
78    fn without_dleqs(&self) -> Proofs {
79        self.iter()
80            .map(|p| {
81                let mut p = p.clone();
82                p.dleq = None;
83                p
84            })
85            .collect()
86    }
87}
88
89impl ProofsMethods for HashSet<Proof> {
90    fn count_by_keyset(&self) -> HashMap<Id, u64> {
91        count_by_keyset(self.iter())
92    }
93
94    fn sum_by_keyset(&self) -> HashMap<Id, Amount> {
95        sum_by_keyset(self.iter())
96    }
97
98    fn total_amount(&self) -> Result<Amount, Error> {
99        total_amount(self.iter())
100    }
101
102    fn ys(&self) -> Result<Vec<PublicKey>, Error> {
103        ys(self.iter())
104    }
105
106    fn without_dleqs(&self) -> Proofs {
107        self.iter()
108            .map(|p| {
109                let mut p = p.clone();
110                p.dleq = None;
111                p
112            })
113            .collect()
114    }
115}
116
117fn count_by_keyset<'a, I: Iterator<Item = &'a Proof>>(proofs: I) -> HashMap<Id, u64> {
118    let mut counts = HashMap::new();
119    for proof in proofs {
120        *counts.entry(proof.keyset_id).or_insert(0) += 1;
121    }
122    counts
123}
124
125fn sum_by_keyset<'a, I: Iterator<Item = &'a Proof>>(proofs: I) -> HashMap<Id, Amount> {
126    let mut sums = HashMap::new();
127    for proof in proofs {
128        *sums.entry(proof.keyset_id).or_insert(Amount::ZERO) += proof.amount;
129    }
130    sums
131}
132
133fn total_amount<'a, I: Iterator<Item = &'a Proof>>(proofs: I) -> Result<Amount, Error> {
134    Amount::try_sum(proofs.map(|p| p.amount)).map_err(Into::into)
135}
136
137fn ys<'a, I: Iterator<Item = &'a Proof>>(proofs: I) -> Result<Vec<PublicKey>, Error> {
138    proofs.map(|p| p.y()).collect::<Result<Vec<PublicKey>, _>>()
139}
140
141/// NUT00 Error
142#[derive(Debug, Error)]
143pub enum Error {
144    /// Proofs required
145    #[error("Proofs required in token")]
146    ProofsRequired,
147    /// Unsupported token
148    #[error("Unsupported token")]
149    UnsupportedToken,
150    /// Unsupported token
151    #[error("Unsupported unit")]
152    UnsupportedUnit,
153    /// Unsupported token
154    #[error("Unsupported payment method")]
155    UnsupportedPaymentMethod,
156    /// Duplicate proofs in token
157    #[error("Duplicate proofs in token")]
158    DuplicateProofs,
159    /// Serde Json error
160    #[error(transparent)]
161    SerdeJsonError(#[from] serde_json::Error),
162    /// Utf8 parse error
163    #[error(transparent)]
164    Utf8ParseError(#[from] FromUtf8Error),
165    /// Base64 error
166    #[error(transparent)]
167    Base64Error(#[from] bitcoin::base64::DecodeError),
168    /// Ciborium deserialization error
169    #[error(transparent)]
170    CiboriumError(#[from] ciborium::de::Error<std::io::Error>),
171    /// Ciborium serialization error
172    #[error(transparent)]
173    CiboriumSerError(#[from] ciborium::ser::Error<std::io::Error>),
174    /// Amount Error
175    #[error(transparent)]
176    Amount(#[from] crate::amount::Error),
177    /// Secret error
178    #[error(transparent)]
179    Secret(#[from] crate::secret::Error),
180    /// DHKE error
181    #[error(transparent)]
182    DHKE(#[from] crate::dhke::Error),
183    /// NUT10 error
184    #[error(transparent)]
185    NUT10(#[from] crate::nuts::nut10::Error),
186    /// NUT11 error
187    #[error(transparent)]
188    NUT11(#[from] crate::nuts::nut11::Error),
189    /// Short keyset id -> id error
190    #[error(transparent)]
191    NUT02(#[from] crate::nuts::nut02::Error),
192}
193
194/// Blinded Message (also called `output`)
195#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
196#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
197pub struct BlindedMessage {
198    /// Amount
199    ///
200    /// The value for the requested [BlindSignature]
201    pub amount: Amount,
202    /// Keyset ID
203    ///
204    /// ID from which we expect a signature.
205    #[serde(rename = "id")]
206    #[cfg_attr(feature = "swagger", schema(value_type = String))]
207    pub keyset_id: Id,
208    /// Blinded secret message (B_)
209    ///
210    /// The blinded secret message generated by the sender.
211    #[serde(rename = "B_")]
212    #[cfg_attr(feature = "swagger", schema(value_type = String))]
213    pub blinded_secret: PublicKey,
214    /// Witness
215    ///
216    /// <https://github.com/cashubtc/nuts/blob/main/11.md>
217    #[serde(skip_serializing_if = "Option::is_none")]
218    pub witness: Option<Witness>,
219}
220
221impl BlindedMessage {
222    /// Compose new blinded message
223    #[inline]
224    pub fn new(amount: Amount, keyset_id: Id, blinded_secret: PublicKey) -> Self {
225        Self {
226            amount,
227            keyset_id,
228            blinded_secret,
229            witness: None,
230        }
231    }
232
233    /// Add witness
234    #[inline]
235    pub fn witness(&mut self, witness: Witness) {
236        self.witness = Some(witness);
237    }
238}
239
240/// Blind Signature (also called `promise`)
241#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
242#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
243pub struct BlindSignature {
244    /// Amount
245    ///
246    /// The value of the blinded token.
247    pub amount: Amount,
248    /// Keyset ID
249    ///
250    /// ID of the mint keys that signed the token.
251    #[serde(rename = "id")]
252    #[cfg_attr(feature = "swagger", schema(value_type = String))]
253    pub keyset_id: Id,
254    /// Blinded signature (C_)
255    ///
256    /// The blinded signature on the secret message `B_` of [BlindedMessage].
257    #[serde(rename = "C_")]
258    #[cfg_attr(feature = "swagger", schema(value_type = String))]
259    pub c: PublicKey,
260    /// DLEQ Proof
261    ///
262    /// <https://github.com/cashubtc/nuts/blob/main/12.md>
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub dleq: Option<BlindSignatureDleq>,
265}
266
267impl Ord for BlindSignature {
268    fn cmp(&self, other: &Self) -> Ordering {
269        self.amount.cmp(&other.amount)
270    }
271}
272
273impl PartialOrd for BlindSignature {
274    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
275        Some(self.cmp(other))
276    }
277}
278
279/// Witness
280#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
281#[serde(untagged)]
282#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
283pub enum Witness {
284    /// HTLC Witness
285    #[serde(with = "serde_htlc_witness")]
286    HTLCWitness(HTLCWitness),
287    /// P2PK Witness
288    #[serde(with = "serde_p2pk_witness")]
289    P2PKWitness(P2PKWitness),
290}
291
292impl From<P2PKWitness> for Witness {
293    fn from(witness: P2PKWitness) -> Self {
294        Self::P2PKWitness(witness)
295    }
296}
297
298impl From<HTLCWitness> for Witness {
299    fn from(witness: HTLCWitness) -> Self {
300        Self::HTLCWitness(witness)
301    }
302}
303
304impl Witness {
305    /// Add signatures to [`Witness`]
306    pub fn add_signatures(&mut self, signatues: Vec<String>) {
307        match self {
308            Self::P2PKWitness(p2pk_witness) => p2pk_witness.signatures.extend(signatues),
309            Self::HTLCWitness(htlc_witness) => match &mut htlc_witness.signatures {
310                Some(sigs) => sigs.extend(signatues),
311                None => htlc_witness.signatures = Some(signatues),
312            },
313        }
314    }
315
316    /// Get signatures on [`Witness`]
317    pub fn signatures(&self) -> Option<Vec<String>> {
318        match self {
319            Self::P2PKWitness(witness) => Some(witness.signatures.clone()),
320            Self::HTLCWitness(witness) => witness.signatures.clone(),
321        }
322    }
323
324    /// Get preimage from [`Witness`]
325    pub fn preimage(&self) -> Option<String> {
326        match self {
327            Self::P2PKWitness(_witness) => None,
328            Self::HTLCWitness(witness) => Some(witness.preimage.clone()),
329        }
330    }
331}
332
333/// Proofs
334#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
335#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
336pub struct Proof {
337    /// Amount
338    pub amount: Amount,
339    /// `Keyset id`
340    #[serde(rename = "id")]
341    #[cfg_attr(feature = "swagger", schema(value_type = String))]
342    pub keyset_id: Id,
343    /// Secret message
344    #[cfg_attr(feature = "swagger", schema(value_type = String))]
345    pub secret: Secret,
346    /// Unblinded signature
347    #[serde(rename = "C")]
348    #[cfg_attr(feature = "swagger", schema(value_type = String))]
349    pub c: PublicKey,
350    /// Witness
351    #[serde(skip_serializing_if = "Option::is_none")]
352    pub witness: Option<Witness>,
353    /// DLEQ Proof
354    #[serde(skip_serializing_if = "Option::is_none")]
355    pub dleq: Option<ProofDleq>,
356}
357
358impl Proof {
359    /// Create new [`Proof`]
360    pub fn new(amount: Amount, keyset_id: Id, secret: Secret, c: PublicKey) -> Self {
361        Proof {
362            amount,
363            keyset_id,
364            secret,
365            c,
366            witness: None,
367            dleq: None,
368        }
369    }
370
371    /// Check if proof is in active keyset `Id`s
372    pub fn is_active(&self, active_keyset_ids: &[Id]) -> bool {
373        active_keyset_ids.contains(&self.keyset_id)
374    }
375
376    /// Get y from proof
377    ///
378    /// Where y is `hash_to_curve(secret)`
379    pub fn y(&self) -> Result<PublicKey, Error> {
380        Ok(hash_to_curve(self.secret.as_bytes())?)
381    }
382}
383
384impl Hash for Proof {
385    fn hash<H: Hasher>(&self, state: &mut H) {
386        self.secret.hash(state);
387    }
388}
389
390impl Ord for Proof {
391    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
392        self.amount.cmp(&other.amount)
393    }
394}
395
396impl PartialOrd for Proof {
397    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
398        Some(self.cmp(other))
399    }
400}
401
402/// Proof V4
403#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
404pub struct ProofV4 {
405    /// Amount in satoshi
406    #[serde(rename = "a")]
407    pub amount: Amount,
408    /// Secret message
409    #[serde(rename = "s")]
410    pub secret: Secret,
411    /// Unblinded signature
412    #[serde(
413        serialize_with = "serialize_v4_pubkey",
414        deserialize_with = "deserialize_v4_pubkey"
415    )]
416    pub c: PublicKey,
417    /// Witness
418    #[serde(default)]
419    #[serde(skip_serializing_if = "Option::is_none")]
420    pub witness: Option<Witness>,
421    /// DLEQ Proof
422    #[serde(rename = "d")]
423    pub dleq: Option<ProofDleq>,
424}
425
426impl ProofV4 {
427    /// [`ProofV4`] into [`Proof`]
428    pub fn into_proof(&self, keyset_id: &Id) -> Proof {
429        Proof {
430            amount: self.amount,
431            keyset_id: *keyset_id,
432            secret: self.secret.clone(),
433            c: self.c,
434            witness: self.witness.clone(),
435            dleq: self.dleq.clone(),
436        }
437    }
438}
439
440impl Hash for ProofV4 {
441    fn hash<H: Hasher>(&self, state: &mut H) {
442        self.secret.hash(state);
443    }
444}
445
446impl From<Proof> for ProofV4 {
447    fn from(proof: Proof) -> ProofV4 {
448        let Proof {
449            amount,
450            keyset_id: _,
451            secret,
452            c,
453            witness,
454            dleq,
455        } = proof;
456        ProofV4 {
457            amount,
458            secret,
459            c,
460            witness,
461            dleq,
462        }
463    }
464}
465
466impl From<ProofV3> for ProofV4 {
467    fn from(proof: ProofV3) -> Self {
468        Self {
469            amount: proof.amount,
470            secret: proof.secret,
471            c: proof.c,
472            witness: proof.witness,
473            dleq: proof.dleq,
474        }
475    }
476}
477
478/// Proof v3 with short keyset id
479#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
480pub struct ProofV3 {
481    /// Amount
482    pub amount: Amount,
483    /// Short keyset id
484    #[serde(rename = "id")]
485    pub keyset_id: ShortKeysetId,
486    /// Secret message
487    pub secret: Secret,
488    /// Unblinded signature
489    #[serde(rename = "C")]
490    pub c: PublicKey,
491    /// Witness
492    #[serde(skip_serializing_if = "Option::is_none")]
493    pub witness: Option<Witness>,
494    /// DLEQ Proof
495    #[serde(skip_serializing_if = "Option::is_none")]
496    pub dleq: Option<ProofDleq>,
497}
498
499impl ProofV3 {
500    /// [`ProofV3`] into [`Proof`]
501    pub fn into_proof(&self, keyset_id: &Id) -> Proof {
502        Proof {
503            amount: self.amount,
504            keyset_id: *keyset_id,
505            secret: self.secret.clone(),
506            c: self.c,
507            witness: self.witness.clone(),
508            dleq: self.dleq.clone(),
509        }
510    }
511}
512
513impl From<Proof> for ProofV3 {
514    fn from(proof: Proof) -> ProofV3 {
515        let Proof {
516            amount,
517            keyset_id,
518            secret,
519            c,
520            witness,
521            dleq,
522        } = proof;
523        ProofV3 {
524            amount,
525            secret,
526            c,
527            witness,
528            dleq,
529            keyset_id: keyset_id.into(),
530        }
531    }
532}
533
534impl Hash for ProofV3 {
535    fn hash<H: Hasher>(&self, state: &mut H) {
536        self.secret.hash(state);
537    }
538}
539
540fn serialize_v4_pubkey<S>(key: &PublicKey, serializer: S) -> Result<S::Ok, S::Error>
541where
542    S: serde::Serializer,
543{
544    serializer.serialize_bytes(&key.to_bytes())
545}
546
547fn deserialize_v4_pubkey<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
548where
549    D: serde::Deserializer<'de>,
550{
551    let bytes = Vec::<u8>::deserialize(deserializer)?;
552    PublicKey::from_slice(&bytes).map_err(serde::de::Error::custom)
553}
554
555/// Currency Unit
556#[non_exhaustive]
557#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
558#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
559pub enum CurrencyUnit {
560    /// Sat
561    #[default]
562    Sat,
563    /// Msat
564    Msat,
565    /// Usd
566    Usd,
567    /// Euro
568    Eur,
569    /// Auth
570    Auth,
571    /// Custom currency unit
572    Custom(String),
573}
574
575#[cfg(feature = "mint")]
576impl CurrencyUnit {
577    /// Derivation index mint will use for unit
578    pub fn derivation_index(&self) -> Option<u32> {
579        match self {
580            Self::Sat => Some(0),
581            Self::Msat => Some(1),
582            Self::Usd => Some(2),
583            Self::Eur => Some(3),
584            Self::Auth => Some(4),
585            _ => None,
586        }
587    }
588}
589
590impl FromStr for CurrencyUnit {
591    type Err = Error;
592    fn from_str(value: &str) -> Result<Self, Self::Err> {
593        let upper_value = value.to_uppercase();
594        match upper_value.as_str() {
595            "SAT" => Ok(Self::Sat),
596            "MSAT" => Ok(Self::Msat),
597            "USD" => Ok(Self::Usd),
598            "EUR" => Ok(Self::Eur),
599            "AUTH" => Ok(Self::Auth),
600            _ => Ok(Self::Custom(value.to_string())),
601        }
602    }
603}
604
605impl fmt::Display for CurrencyUnit {
606    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607        let s = match self {
608            CurrencyUnit::Sat => "SAT",
609            CurrencyUnit::Msat => "MSAT",
610            CurrencyUnit::Usd => "USD",
611            CurrencyUnit::Eur => "EUR",
612            CurrencyUnit::Auth => "AUTH",
613            CurrencyUnit::Custom(unit) => unit,
614        };
615        if let Some(width) = f.width() {
616            write!(f, "{:width$}", s.to_lowercase(), width = width)
617        } else {
618            write!(f, "{}", s.to_lowercase())
619        }
620    }
621}
622
623impl Serialize for CurrencyUnit {
624    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
625    where
626        S: serde::Serializer,
627    {
628        serializer.serialize_str(&self.to_string())
629    }
630}
631
632impl<'de> Deserialize<'de> for CurrencyUnit {
633    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
634    where
635        D: Deserializer<'de>,
636    {
637        let currency: String = String::deserialize(deserializer)?;
638        Self::from_str(&currency).map_err(|_| serde::de::Error::custom("Unsupported unit"))
639    }
640}
641
642/// Payment Method
643#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
644#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
645pub enum PaymentMethod {
646    /// Bolt11 payment type
647    #[default]
648    Bolt11,
649    /// Bolt12
650    Bolt12,
651    /// Custom
652    Custom(String),
653}
654
655impl FromStr for PaymentMethod {
656    type Err = Error;
657    fn from_str(value: &str) -> Result<Self, Self::Err> {
658        match value.to_lowercase().as_str() {
659            "bolt11" => Ok(Self::Bolt11),
660            "bolt12" => Ok(Self::Bolt12),
661            c => Ok(Self::Custom(c.to_string())),
662        }
663    }
664}
665
666impl fmt::Display for PaymentMethod {
667    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
668        match self {
669            PaymentMethod::Bolt11 => write!(f, "bolt11"),
670            PaymentMethod::Bolt12 => write!(f, "bolt12"),
671            PaymentMethod::Custom(p) => write!(f, "{p}"),
672        }
673    }
674}
675
676impl Serialize for PaymentMethod {
677    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
678    where
679        S: serde::Serializer,
680    {
681        serializer.serialize_str(&self.to_string())
682    }
683}
684
685impl<'de> Deserialize<'de> for PaymentMethod {
686    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
687    where
688        D: Deserializer<'de>,
689    {
690        let payment_method: String = String::deserialize(deserializer)?;
691        Self::from_str(&payment_method).map_err(|_| de::Error::custom("Unsupported payment method"))
692    }
693}
694
695/// PreMint
696#[cfg(feature = "wallet")]
697#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
698pub struct PreMint {
699    /// Blinded message
700    pub blinded_message: BlindedMessage,
701    /// Secret
702    pub secret: Secret,
703    /// R
704    pub r: SecretKey,
705    /// Amount
706    pub amount: Amount,
707}
708
709#[cfg(feature = "wallet")]
710impl Ord for PreMint {
711    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
712        self.amount.cmp(&other.amount)
713    }
714}
715
716#[cfg(feature = "wallet")]
717impl PartialOrd for PreMint {
718    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
719        Some(self.cmp(other))
720    }
721}
722
723/// Premint Secrets
724#[cfg(feature = "wallet")]
725#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
726pub struct PreMintSecrets {
727    /// Secrets
728    pub secrets: Vec<PreMint>,
729    /// Keyset Id
730    pub keyset_id: Id,
731}
732
733#[cfg(feature = "wallet")]
734impl PreMintSecrets {
735    /// Create new [`PreMintSecrets`]
736    pub fn new(keyset_id: Id) -> Self {
737        Self {
738            secrets: Vec::new(),
739            keyset_id,
740        }
741    }
742
743    /// Outputs for speceifed amount with random secret
744    pub fn random(
745        keyset_id: Id,
746        amount: Amount,
747        amount_split_target: &SplitTarget,
748        fee_and_amounts: &FeeAndAmounts,
749    ) -> Result<Self, Error> {
750        let amount_split = amount.split_targeted(amount_split_target, fee_and_amounts)?;
751
752        let mut output = Vec::with_capacity(amount_split.len());
753
754        for amount in amount_split {
755            let secret = Secret::generate();
756            let (blinded, r) = blind_message(&secret.to_bytes(), None)?;
757
758            let blinded_message = BlindedMessage::new(amount, keyset_id, blinded);
759
760            output.push(PreMint {
761                secret,
762                blinded_message,
763                r,
764                amount,
765            });
766        }
767
768        Ok(PreMintSecrets {
769            secrets: output,
770            keyset_id,
771        })
772    }
773
774    /// Outputs from pre defined secrets
775    pub fn from_secrets(
776        keyset_id: Id,
777        amounts: Vec<Amount>,
778        secrets: Vec<Secret>,
779    ) -> Result<Self, Error> {
780        let mut output = Vec::with_capacity(secrets.len());
781
782        for (secret, amount) in secrets.into_iter().zip(amounts) {
783            let (blinded, r) = blind_message(&secret.to_bytes(), None)?;
784
785            let blinded_message = BlindedMessage::new(amount, keyset_id, blinded);
786
787            output.push(PreMint {
788                secret,
789                blinded_message,
790                r,
791                amount,
792            });
793        }
794
795        Ok(PreMintSecrets {
796            secrets: output,
797            keyset_id,
798        })
799    }
800
801    /// Blank Outputs used for NUT-08 change
802    pub fn blank(keyset_id: Id, fee_reserve: Amount) -> Result<Self, Error> {
803        let count = ((u64::from(fee_reserve) as f64).log2().ceil() as u64).max(1);
804
805        let mut output = Vec::with_capacity(count as usize);
806
807        for _i in 0..count {
808            let secret = Secret::generate();
809            let (blinded, r) = blind_message(&secret.to_bytes(), None)?;
810
811            let blinded_message = BlindedMessage::new(Amount::ZERO, keyset_id, blinded);
812
813            output.push(PreMint {
814                secret,
815                blinded_message,
816                r,
817                amount: Amount::ZERO,
818            })
819        }
820
821        Ok(PreMintSecrets {
822            secrets: output,
823            keyset_id,
824        })
825    }
826
827    /// Outputs with specific spending conditions
828    pub fn with_conditions(
829        keyset_id: Id,
830        amount: Amount,
831        amount_split_target: &SplitTarget,
832        conditions: &SpendingConditions,
833        fee_and_amounts: &FeeAndAmounts,
834    ) -> Result<Self, Error> {
835        let amount_split = amount.split_targeted(amount_split_target, fee_and_amounts)?;
836
837        let mut output = Vec::with_capacity(amount_split.len());
838
839        for amount in amount_split {
840            let secret: nut10::Secret = conditions.clone().into();
841
842            let secret: Secret = secret.try_into()?;
843            let (blinded, r) = blind_message(&secret.to_bytes(), None)?;
844
845            let blinded_message = BlindedMessage::new(amount, keyset_id, blinded);
846
847            output.push(PreMint {
848                secret,
849                blinded_message,
850                r,
851                amount,
852            });
853        }
854
855        Ok(PreMintSecrets {
856            secrets: output,
857            keyset_id,
858        })
859    }
860
861    /// Iterate over secrets
862    #[inline]
863    pub fn iter(&self) -> impl Iterator<Item = &PreMint> {
864        self.secrets.iter()
865    }
866
867    /// Length of secrets
868    #[inline]
869    pub fn len(&self) -> usize {
870        self.secrets.len()
871    }
872
873    /// If secrets is empty
874    #[inline]
875    pub fn is_empty(&self) -> bool {
876        self.secrets.is_empty()
877    }
878
879    /// Totoal amount of secrets
880    pub fn total_amount(&self) -> Result<Amount, Error> {
881        Ok(Amount::try_sum(
882            self.secrets.iter().map(|PreMint { amount, .. }| *amount),
883        )?)
884    }
885
886    /// [`BlindedMessage`]s from [`PreMintSecrets`]
887    #[inline]
888    pub fn blinded_messages(&self) -> Vec<BlindedMessage> {
889        self.iter().map(|pm| pm.blinded_message.clone()).collect()
890    }
891
892    /// [`Secret`]s from [`PreMintSecrets`]
893    #[inline]
894    pub fn secrets(&self) -> Vec<Secret> {
895        self.iter().map(|pm| pm.secret.clone()).collect()
896    }
897
898    /// Blinding factor from [`PreMintSecrets`]
899    #[inline]
900    pub fn rs(&self) -> Vec<SecretKey> {
901        self.iter().map(|pm| pm.r.clone()).collect()
902    }
903
904    /// Amounts from [`PreMintSecrets`]
905    #[inline]
906    pub fn amounts(&self) -> Vec<Amount> {
907        self.iter().map(|pm| pm.amount).collect()
908    }
909
910    /// Combine [`PreMintSecrets`]
911    #[inline]
912    pub fn combine(&mut self, mut other: Self) {
913        self.secrets.append(&mut other.secrets)
914    }
915
916    /// Sort [`PreMintSecrets`] by [`Amount`]
917    #[inline]
918    pub fn sort_secrets(&mut self) {
919        self.secrets.sort();
920    }
921}
922
923// Implement Iterator for PreMintSecrets
924#[cfg(feature = "wallet")]
925impl Iterator for PreMintSecrets {
926    type Item = PreMint;
927
928    fn next(&mut self) -> Option<Self::Item> {
929        // Use the iterator of the vector
930        if self.secrets.is_empty() {
931            return None;
932        }
933        Some(self.secrets.remove(0))
934    }
935}
936
937#[cfg(feature = "wallet")]
938impl Ord for PreMintSecrets {
939    fn cmp(&self, other: &Self) -> Ordering {
940        self.secrets.cmp(&other.secrets)
941    }
942}
943
944#[cfg(feature = "wallet")]
945impl PartialOrd for PreMintSecrets {
946    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
947        Some(self.cmp(other))
948    }
949}
950
951#[cfg(test)]
952mod tests {
953    use std::str::FromStr;
954
955    use super::*;
956
957    #[test]
958    fn test_proof_serialize() {
959        let proof = "[{\"id\":\"009a1f293253e41e\",\"amount\":2,\"secret\":\"407915bc212be61a77e3e6d2aeb4c727980bda51cd06a6afc29e2861768a7837\",\"C\":\"02bc9097997d81afb2cc7346b5e4345a9346bd2a506eb7958598a72f0cf85163ea\"},{\"id\":\"009a1f293253e41e\",\"amount\":8,\"secret\":\"fe15109314e61d7756b0f8ee0f23a624acaa3f4e042f61433c728c7057b931be\",\"C\":\"029e8e5050b890a7d6c0968db16bc1d5d5fa040ea1de284f6ec69d61299f671059\"}]";
960        let proof: Proofs = serde_json::from_str(proof).unwrap();
961
962        assert_eq!(
963            proof[0].clone().keyset_id,
964            Id::from_str("009a1f293253e41e").unwrap()
965        );
966
967        assert_eq!(proof.len(), 2);
968    }
969
970    #[test]
971    #[cfg(feature = "wallet")]
972    fn test_blank_blinded_messages() {
973        let b = PreMintSecrets::blank(
974            Id::from_str("009a1f293253e41e").unwrap(),
975            Amount::from(1000),
976        )
977        .unwrap();
978        assert_eq!(b.len(), 10);
979
980        let b = PreMintSecrets::blank(Id::from_str("009a1f293253e41e").unwrap(), Amount::from(1))
981            .unwrap();
982        assert_eq!(b.len(), 1);
983    }
984
985    #[test]
986    fn custom_unit_ser_der() {
987        let unit = CurrencyUnit::Custom(String::from("test"));
988        let serialized = serde_json::to_string(&unit).unwrap();
989        let deserialized: CurrencyUnit = serde_json::from_str(&serialized).unwrap();
990        assert_eq!(unit, deserialized)
991    }
992
993    #[test]
994    fn test_payment_method_parsing() {
995        // Test standard variants
996        assert_eq!(
997            PaymentMethod::from_str("bolt11").unwrap(),
998            PaymentMethod::Bolt11
999        );
1000        assert_eq!(
1001            PaymentMethod::from_str("BOLT11").unwrap(),
1002            PaymentMethod::Bolt11
1003        );
1004        assert_eq!(
1005            PaymentMethod::from_str("Bolt11").unwrap(),
1006            PaymentMethod::Bolt11
1007        );
1008
1009        assert_eq!(
1010            PaymentMethod::from_str("bolt12").unwrap(),
1011            PaymentMethod::Bolt12
1012        );
1013        assert_eq!(
1014            PaymentMethod::from_str("BOLT12").unwrap(),
1015            PaymentMethod::Bolt12
1016        );
1017        assert_eq!(
1018            PaymentMethod::from_str("Bolt12").unwrap(),
1019            PaymentMethod::Bolt12
1020        );
1021
1022        // Test custom variants
1023        assert_eq!(
1024            PaymentMethod::from_str("custom").unwrap(),
1025            PaymentMethod::Custom("custom".to_string())
1026        );
1027        assert_eq!(
1028            PaymentMethod::from_str("CUSTOM").unwrap(),
1029            PaymentMethod::Custom("custom".to_string())
1030        );
1031
1032        // Test serialization/deserialization consistency
1033        let methods = vec![
1034            PaymentMethod::Bolt11,
1035            PaymentMethod::Bolt12,
1036            PaymentMethod::Custom("test".to_string()),
1037        ];
1038
1039        for method in methods {
1040            let serialized = serde_json::to_string(&method).unwrap();
1041            let deserialized: PaymentMethod = serde_json::from_str(&serialized).unwrap();
1042            assert_eq!(method, deserialized);
1043        }
1044    }
1045
1046    #[test]
1047    fn test_witness_serialization() {
1048        let htlc_witness = HTLCWitness {
1049            preimage: "preimage".to_string(),
1050            signatures: Some(vec!["sig1".to_string()]),
1051        };
1052        let witness = Witness::HTLCWitness(htlc_witness);
1053
1054        let serialized = serde_json::to_string(&witness).unwrap();
1055        let deserialized: Witness = serde_json::from_str(&serialized).unwrap();
1056
1057        assert!(matches!(deserialized, Witness::HTLCWitness(_)));
1058
1059        let p2pk_witness = P2PKWitness {
1060            signatures: vec!["sig1".to_string(), "sig2".to_string()],
1061        };
1062        let witness = Witness::P2PKWitness(p2pk_witness);
1063
1064        let serialized = serde_json::to_string(&witness).unwrap();
1065        let deserialized: Witness = serde_json::from_str(&serialized).unwrap();
1066
1067        assert!(matches!(deserialized, Witness::P2PKWitness(_)));
1068    }
1069}