Skip to main content

cdk_sqlite/mint/
memory.rs

1//! In-memory database that is provided by the `cdk-sqlite` crate, mainly for testing purposes.
2use std::collections::HashMap;
3
4use cdk_common::database::{self, MintDatabase, MintKeysDatabase};
5use cdk_common::mint::{self, MintKeySetInfo, MintQuote, Operation};
6use cdk_common::nuts::{CurrencyUnit, Id, Proofs, State};
7use cdk_common::MintInfo;
8
9use super::MintSqliteDatabase;
10
11const CDK_MINT_PRIMARY_NAMESPACE: &str = "cdk_mint";
12const CDK_MINT_CONFIG_SECONDARY_NAMESPACE: &str = "config";
13const CDK_MINT_CONFIG_KV_KEY: &str = "mint_info";
14
15/// Creates a new in-memory [`MintSqliteDatabase`] instance
16pub async fn empty() -> Result<MintSqliteDatabase, database::Error> {
17    #[cfg(not(feature = "sqlcipher"))]
18    let path = ":memory:";
19    #[cfg(feature = "sqlcipher")]
20    let path = (":memory:", "memory");
21
22    MintSqliteDatabase::new(path).await
23}
24
25/// Creates a new in-memory [`MintSqliteDatabase`] instance with the given state
26#[allow(clippy::too_many_arguments)]
27pub async fn new_with_state(
28    active_keysets: HashMap<CurrencyUnit, Id>,
29    keysets: Vec<MintKeySetInfo>,
30    mint_quotes: Vec<MintQuote>,
31    melt_quotes: Vec<mint::MeltQuote>,
32    pending_proofs: Proofs,
33    spent_proofs: Proofs,
34    mint_info: MintInfo,
35) -> Result<MintSqliteDatabase, database::Error> {
36    let db = empty().await?;
37    let mut tx = MintKeysDatabase::begin_transaction(&db).await?;
38
39    for active_keyset in active_keysets {
40        tx.set_active_keyset(active_keyset.0, active_keyset.1)
41            .await?;
42    }
43
44    for keyset in keysets {
45        tx.add_keyset_info(keyset).await?;
46    }
47    tx.commit().await?;
48
49    let mut tx = MintDatabase::begin_transaction(&db).await?;
50
51    for quote in mint_quotes {
52        tx.add_mint_quote(quote).await?;
53    }
54
55    for quote in melt_quotes {
56        tx.add_melt_quote(quote).await?;
57    }
58
59    let operation = Operation::new_swap(Default::default(), Default::default(), Default::default());
60
61    if !pending_proofs.is_empty() {
62        let mut proofs = tx.add_proofs(pending_proofs, None, &operation).await?;
63        tx.update_proofs_state(&mut proofs, State::Pending).await?;
64    }
65
66    if !spent_proofs.is_empty() {
67        let mut proofs = tx.add_proofs(spent_proofs, None, &operation).await?;
68        tx.update_proofs_state(&mut proofs, State::Spent).await?;
69    }
70    let mint_info_bytes = serde_json::to_vec(&mint_info)?;
71    tx.kv_write(
72        CDK_MINT_PRIMARY_NAMESPACE,
73        CDK_MINT_CONFIG_SECONDARY_NAMESPACE,
74        CDK_MINT_CONFIG_KV_KEY,
75        &mint_info_bytes,
76    )
77    .await?;
78    tx.commit().await?;
79
80    Ok(db)
81}
82
83#[cfg(test)]
84mod tests {
85    use std::collections::HashMap;
86    use std::str::FromStr;
87
88    use cdk_common::database::MintProofsDatabase;
89    use cdk_common::nuts::{Id, Proof, State};
90    use cdk_common::secret::Secret;
91    use cdk_common::{Amount, MintInfo, SecretKey};
92
93    use super::new_with_state;
94
95    fn make_proof(keyset_id: Id, amount: u64) -> Proof {
96        Proof {
97            amount: Amount::from(amount),
98            keyset_id,
99            secret: Secret::generate(),
100            c: SecretKey::generate().public_key(),
101            witness: None,
102            dleq: None,
103            p2pk_e: None,
104        }
105    }
106
107    #[tokio::test]
108    async fn new_with_state_restores_spent_proofs_as_spent() {
109        let keyset_id = Id::from_str("00916bbf7ef91a36").expect("valid keyset id");
110        let pending_proofs = vec![make_proof(keyset_id, 1)];
111        let spent_proofs = vec![make_proof(keyset_id, 2), make_proof(keyset_id, 4)];
112        let spent_ys = spent_proofs
113            .iter()
114            .map(|proof| proof.y().expect("valid proof y"))
115            .collect::<Vec<_>>();
116
117        let db = new_with_state(
118            HashMap::new(),
119            vec![],
120            vec![],
121            vec![],
122            pending_proofs,
123            spent_proofs,
124            MintInfo::default(),
125        )
126        .await
127        .expect("valid db");
128
129        let states = db.get_proofs_states(&spent_ys).await.expect("proof states");
130
131        assert_eq!(states, vec![Some(State::Spent), Some(State::Spent)]);
132    }
133
134    #[tokio::test]
135    async fn new_with_state_restores_pending_proofs_without_spent_proofs() {
136        let keyset_id = Id::from_str("00916bbf7ef91a36").expect("valid keyset id");
137        let pending_proofs = vec![make_proof(keyset_id, 1), make_proof(keyset_id, 2)];
138        let pending_ys = pending_proofs
139            .iter()
140            .map(|proof| proof.y().expect("valid proof y"))
141            .collect::<Vec<_>>();
142
143        let db = new_with_state(
144            HashMap::new(),
145            vec![],
146            vec![],
147            vec![],
148            pending_proofs,
149            vec![],
150            MintInfo::default(),
151        )
152        .await
153        .expect("valid db");
154
155        let states = db
156            .get_proofs_states(&pending_ys)
157            .await
158            .expect("proof states");
159
160        assert_eq!(states, vec![Some(State::Pending), Some(State::Pending)]);
161    }
162}