1use 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
40pub type Proofs = Vec<Proof>;
42
43pub trait ProofsMethods {
45 fn count_by_keyset(&self) -> HashMap<Id, u64>;
47
48 fn sum_by_keyset(&self) -> HashMap<Id, Amount>;
50
51 fn total_amount(&self) -> Result<Amount, Error>;
53
54 fn ys(&self) -> Result<Vec<PublicKey>, Error>;
56
57 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#[derive(Debug, Error)]
143pub enum Error {
144 #[error("Proofs required in token")]
146 ProofsRequired,
147 #[error("Unsupported token")]
149 UnsupportedToken,
150 #[error("Unsupported unit")]
152 UnsupportedUnit,
153 #[error("Unsupported payment method")]
155 UnsupportedPaymentMethod,
156 #[error("Duplicate proofs in token")]
158 DuplicateProofs,
159 #[error(transparent)]
161 SerdeJsonError(#[from] serde_json::Error),
162 #[error(transparent)]
164 Utf8ParseError(#[from] FromUtf8Error),
165 #[error(transparent)]
167 Base64Error(#[from] bitcoin::base64::DecodeError),
168 #[error(transparent)]
170 CiboriumError(#[from] ciborium::de::Error<std::io::Error>),
171 #[error(transparent)]
173 CiboriumSerError(#[from] ciborium::ser::Error<std::io::Error>),
174 #[error(transparent)]
176 Amount(#[from] crate::amount::Error),
177 #[error(transparent)]
179 Secret(#[from] crate::secret::Error),
180 #[error(transparent)]
182 DHKE(#[from] crate::dhke::Error),
183 #[error(transparent)]
185 NUT10(#[from] crate::nuts::nut10::Error),
186 #[error(transparent)]
188 NUT11(#[from] crate::nuts::nut11::Error),
189 #[error(transparent)]
191 NUT02(#[from] crate::nuts::nut02::Error),
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
196#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
197pub struct BlindedMessage {
198 pub amount: Amount,
202 #[serde(rename = "id")]
206 #[cfg_attr(feature = "swagger", schema(value_type = String))]
207 pub keyset_id: Id,
208 #[serde(rename = "B_")]
212 #[cfg_attr(feature = "swagger", schema(value_type = String))]
213 pub blinded_secret: PublicKey,
214 #[serde(skip_serializing_if = "Option::is_none")]
218 pub witness: Option<Witness>,
219}
220
221impl BlindedMessage {
222 #[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 #[inline]
235 pub fn witness(&mut self, witness: Witness) {
236 self.witness = Some(witness);
237 }
238}
239
240#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
242#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
243pub struct BlindSignature {
244 pub amount: Amount,
248 #[serde(rename = "id")]
252 #[cfg_attr(feature = "swagger", schema(value_type = String))]
253 pub keyset_id: Id,
254 #[serde(rename = "C_")]
258 #[cfg_attr(feature = "swagger", schema(value_type = String))]
259 pub c: PublicKey,
260 #[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#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
281#[serde(untagged)]
282#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
283pub enum Witness {
284 #[serde(with = "serde_htlc_witness")]
286 HTLCWitness(HTLCWitness),
287 #[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 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 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 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
335#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
336pub struct Proof {
337 pub amount: Amount,
339 #[serde(rename = "id")]
341 #[cfg_attr(feature = "swagger", schema(value_type = String))]
342 pub keyset_id: Id,
343 #[cfg_attr(feature = "swagger", schema(value_type = String))]
345 pub secret: Secret,
346 #[serde(rename = "C")]
348 #[cfg_attr(feature = "swagger", schema(value_type = String))]
349 pub c: PublicKey,
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub witness: Option<Witness>,
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub dleq: Option<ProofDleq>,
356}
357
358impl Proof {
359 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 pub fn is_active(&self, active_keyset_ids: &[Id]) -> bool {
373 active_keyset_ids.contains(&self.keyset_id)
374 }
375
376 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
404pub struct ProofV4 {
405 #[serde(rename = "a")]
407 pub amount: Amount,
408 #[serde(rename = "s")]
410 pub secret: Secret,
411 #[serde(
413 serialize_with = "serialize_v4_pubkey",
414 deserialize_with = "deserialize_v4_pubkey"
415 )]
416 pub c: PublicKey,
417 #[serde(default)]
419 #[serde(skip_serializing_if = "Option::is_none")]
420 pub witness: Option<Witness>,
421 #[serde(rename = "d")]
423 pub dleq: Option<ProofDleq>,
424}
425
426impl ProofV4 {
427 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#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
480pub struct ProofV3 {
481 pub amount: Amount,
483 #[serde(rename = "id")]
485 pub keyset_id: ShortKeysetId,
486 pub secret: Secret,
488 #[serde(rename = "C")]
490 pub c: PublicKey,
491 #[serde(skip_serializing_if = "Option::is_none")]
493 pub witness: Option<Witness>,
494 #[serde(skip_serializing_if = "Option::is_none")]
496 pub dleq: Option<ProofDleq>,
497}
498
499impl ProofV3 {
500 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#[non_exhaustive]
557#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
558#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
559pub enum CurrencyUnit {
560 #[default]
562 Sat,
563 Msat,
565 Usd,
567 Eur,
569 Auth,
571 Custom(String),
573}
574
575#[cfg(feature = "mint")]
576impl CurrencyUnit {
577 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(¤cy).map_err(|_| serde::de::Error::custom("Unsupported unit"))
639 }
640}
641
642#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
644#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
645pub enum PaymentMethod {
646 #[default]
648 Bolt11,
649 Bolt12,
651 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#[cfg(feature = "wallet")]
697#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
698pub struct PreMint {
699 pub blinded_message: BlindedMessage,
701 pub secret: Secret,
703 pub r: SecretKey,
705 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#[cfg(feature = "wallet")]
725#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
726pub struct PreMintSecrets {
727 pub secrets: Vec<PreMint>,
729 pub keyset_id: Id,
731}
732
733#[cfg(feature = "wallet")]
734impl PreMintSecrets {
735 pub fn new(keyset_id: Id) -> Self {
737 Self {
738 secrets: Vec::new(),
739 keyset_id,
740 }
741 }
742
743 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 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 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 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 #[inline]
863 pub fn iter(&self) -> impl Iterator<Item = &PreMint> {
864 self.secrets.iter()
865 }
866
867 #[inline]
869 pub fn len(&self) -> usize {
870 self.secrets.len()
871 }
872
873 #[inline]
875 pub fn is_empty(&self) -> bool {
876 self.secrets.is_empty()
877 }
878
879 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 #[inline]
888 pub fn blinded_messages(&self) -> Vec<BlindedMessage> {
889 self.iter().map(|pm| pm.blinded_message.clone()).collect()
890 }
891
892 #[inline]
894 pub fn secrets(&self) -> Vec<Secret> {
895 self.iter().map(|pm| pm.secret.clone()).collect()
896 }
897
898 #[inline]
900 pub fn rs(&self) -> Vec<SecretKey> {
901 self.iter().map(|pm| pm.r.clone()).collect()
902 }
903
904 #[inline]
906 pub fn amounts(&self) -> Vec<Amount> {
907 self.iter().map(|pm| pm.amount).collect()
908 }
909
910 #[inline]
912 pub fn combine(&mut self, mut other: Self) {
913 self.secrets.append(&mut other.secrets)
914 }
915
916 #[inline]
918 pub fn sort_secrets(&mut self) {
919 self.secrets.sort();
920 }
921}
922
923#[cfg(feature = "wallet")]
925impl Iterator for PreMintSecrets {
926 type Item = PreMint;
927
928 fn next(&mut self) -> Option<Self::Item> {
929 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 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 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 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}