cdk_common/
common.rs

1//! Types
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::Error;
6use crate::mint_url::MintUrl;
7use crate::nuts::nut00::ProofsMethods;
8use crate::nuts::{
9    CurrencyUnit, MeltQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SpendingConditions,
10    State,
11};
12use crate::Amount;
13
14/// Melt response with proofs
15#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
16pub struct Melted {
17    /// State of quote
18    pub state: MeltQuoteState,
19    /// Preimage of melt payment
20    pub preimage: Option<String>,
21    /// Melt change
22    pub change: Option<Proofs>,
23    /// Melt amount
24    pub amount: Amount,
25    /// Fee paid
26    pub fee_paid: Amount,
27}
28
29impl Melted {
30    /// Create new [`Melted`]
31    pub fn from_proofs(
32        state: MeltQuoteState,
33        preimage: Option<String>,
34        amount: Amount,
35        proofs: Proofs,
36        change_proofs: Option<Proofs>,
37    ) -> Result<Self, Error> {
38        let proofs_amount = proofs.total_amount()?;
39        let change_amount = match &change_proofs {
40            Some(change_proofs) => change_proofs.total_amount()?,
41            None => Amount::ZERO,
42        };
43
44        let fee_paid = proofs_amount
45            .checked_sub(amount + change_amount)
46            .ok_or(Error::AmountOverflow)?;
47
48        Ok(Self {
49            state,
50            preimage,
51            change: change_proofs,
52            amount,
53            fee_paid,
54        })
55    }
56
57    /// Total amount melted
58    pub fn total_amount(&self) -> Amount {
59        self.amount + self.fee_paid
60    }
61}
62
63/// Prooinfo
64#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
65pub struct ProofInfo {
66    /// Proof
67    pub proof: Proof,
68    /// y
69    pub y: PublicKey,
70    /// Mint Url
71    pub mint_url: MintUrl,
72    /// Proof State
73    pub state: State,
74    /// Proof Spending Conditions
75    pub spending_condition: Option<SpendingConditions>,
76    /// Unit
77    pub unit: CurrencyUnit,
78}
79
80impl ProofInfo {
81    /// Create new [`ProofInfo`]
82    pub fn new(
83        proof: Proof,
84        mint_url: MintUrl,
85        state: State,
86        unit: CurrencyUnit,
87    ) -> Result<Self, Error> {
88        let y = proof.y()?;
89
90        let spending_condition: Option<SpendingConditions> = (&proof.secret).try_into().ok();
91
92        Ok(Self {
93            proof,
94            y,
95            mint_url,
96            state,
97            spending_condition,
98            unit,
99        })
100    }
101
102    /// Check if [`Proof`] matches conditions
103    pub fn matches_conditions(
104        &self,
105        mint_url: &Option<MintUrl>,
106        unit: &Option<CurrencyUnit>,
107        state: &Option<Vec<State>>,
108        spending_conditions: &Option<Vec<SpendingConditions>>,
109    ) -> bool {
110        if let Some(mint_url) = mint_url {
111            if mint_url.ne(&self.mint_url) {
112                return false;
113            }
114        }
115
116        if let Some(unit) = unit {
117            if unit.ne(&self.unit) {
118                return false;
119            }
120        }
121
122        if let Some(state) = state {
123            if !state.contains(&self.state) {
124                return false;
125            }
126        }
127
128        if let Some(spending_conditions) = spending_conditions {
129            match &self.spending_condition {
130                None => return false,
131                Some(s) => {
132                    if !spending_conditions.contains(s) {
133                        return false;
134                    }
135                }
136            }
137        }
138
139        true
140    }
141}
142
143/// Key used in hashmap of ln backends to identify what unit and payment method
144/// it is for
145#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
146pub struct LnKey {
147    /// Unit of Payment backend
148    pub unit: CurrencyUnit,
149    /// Method of payment backend
150    pub method: PaymentMethod,
151}
152
153impl LnKey {
154    /// Create new [`LnKey`]
155    pub fn new(unit: CurrencyUnit, method: PaymentMethod) -> Self {
156        Self { unit, method }
157    }
158}
159
160/// Secs wuotes are valid
161#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
162pub struct QuoteTTL {
163    /// Seconds mint quote is valid
164    pub mint_ttl: u64,
165    /// Seconds melt quote is valid
166    pub melt_ttl: u64,
167}
168
169impl QuoteTTL {
170    /// Create new [`QuoteTTL`]
171    pub fn new(mint_ttl: u64, melt_ttl: u64) -> QuoteTTL {
172        Self { mint_ttl, melt_ttl }
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use std::str::FromStr;
179
180    use super::Melted;
181    use crate::nuts::{Id, Proof, PublicKey};
182    use crate::secret::Secret;
183    use crate::Amount;
184
185    #[test]
186    fn test_melted() {
187        let keyset_id = Id::from_str("00deadbeef123456").unwrap();
188        let proof = Proof::new(
189            Amount::from(64),
190            keyset_id,
191            Secret::generate(),
192            PublicKey::from_hex(
193                "02deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
194            )
195            .unwrap(),
196        );
197        let melted = Melted::from_proofs(
198            super::MeltQuoteState::Paid,
199            Some("preimage".to_string()),
200            Amount::from(64),
201            vec![proof.clone()],
202            None,
203        )
204        .unwrap();
205        assert_eq!(melted.amount, Amount::from(64));
206        assert_eq!(melted.fee_paid, Amount::ZERO);
207        assert_eq!(melted.total_amount(), Amount::from(64));
208    }
209
210    #[test]
211    fn test_melted_with_change() {
212        let keyset_id = Id::from_str("00deadbeef123456").unwrap();
213        let proof = Proof::new(
214            Amount::from(64),
215            keyset_id,
216            Secret::generate(),
217            PublicKey::from_hex(
218                "02deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
219            )
220            .unwrap(),
221        );
222        let change_proof = Proof::new(
223            Amount::from(32),
224            keyset_id,
225            Secret::generate(),
226            PublicKey::from_hex(
227                "03deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
228            )
229            .unwrap(),
230        );
231        let melted = Melted::from_proofs(
232            super::MeltQuoteState::Paid,
233            Some("preimage".to_string()),
234            Amount::from(31),
235            vec![proof.clone()],
236            Some(vec![change_proof.clone()]),
237        )
238        .unwrap();
239        assert_eq!(melted.amount, Amount::from(31));
240        assert_eq!(melted.fee_paid, Amount::from(1));
241        assert_eq!(melted.total_amount(), Amount::from(32));
242    }
243}