Skip to main content

cdk_sqlite/mint/
mod.rs

1//! SQLite Mint
2
3use cdk_sql_common::mint::SQLMintAuthDatabase;
4use cdk_sql_common::SQLMintDatabase;
5
6use crate::common::SqliteConnectionManager;
7
8pub mod memory;
9
10/// Mint SQLite implementation with rusqlite
11pub type MintSqliteDatabase = SQLMintDatabase<SqliteConnectionManager>;
12
13/// Mint Auth database with rusqlite
14pub type MintSqliteAuthDatabase = SQLMintAuthDatabase<SqliteConnectionManager>;
15
16#[cfg(test)]
17mod test {
18    use std::fs::remove_file;
19    use std::str::FromStr;
20    use std::sync::Arc;
21    use std::time::Duration;
22
23    use cdk_common::database::{self, MintAuthDatabase};
24    use cdk_common::secret::Secret;
25    use cdk_common::{mint_db_test, AuthProof, Id, SecretKey, State};
26    use cdk_sql_common::pool::Pool;
27    use cdk_sql_common::stmt::query;
28
29    use super::*;
30    use crate::common::Config;
31
32    async fn provide_db(_test_name: String) -> MintSqliteDatabase {
33        memory::empty().await.unwrap()
34    }
35
36    mint_db_test!(provide_db);
37
38    #[tokio::test]
39    async fn kvstore_compare_and_swap() {
40        cdk_common::database::mint::test::kvstore_compare_and_swap(
41            provide_db("test_kvstore_compare_and_swap".to_owned()).await,
42        )
43        .await;
44    }
45
46    #[tokio::test]
47    async fn bug_opening_relative_path() {
48        let config: Config = "test.db".into();
49
50        let pool = Pool::<SqliteConnectionManager>::new(config);
51        let db = pool.get().await;
52        assert!(db.is_ok());
53        let _ = remove_file("test.db");
54    }
55
56    #[tokio::test]
57    async fn exhausted_in_memory_pool_times_out() {
58        let config: Config = ":memory:".into();
59        let pool = Pool::<SqliteConnectionManager>::new(config);
60
61        let _conn = pool.get().await.expect("valid connection");
62        let result = pool.get_timeout(Duration::from_millis(10)).await;
63
64        assert!(matches!(result, Err(cdk_sql_common::pool::Error::Timeout)));
65    }
66
67    async fn spend_auth_proof(
68        db: Arc<MintSqliteAuthDatabase>,
69        proof: AuthProof,
70    ) -> Result<(), database::Error> {
71        let mut tx = db.as_ref().begin_transaction().await?;
72        tx.add_proof(proof).await?;
73        tx.commit().await
74    }
75
76    #[tokio::test]
77    async fn concurrent_blind_auth_proof_spend_allows_one_request() {
78        let path = std::env::temp_dir().join(format!(
79            "cdk-blind-auth-replay-{}.sqlite",
80            uuid::Uuid::new_v4()
81        ));
82
83        #[cfg(not(feature = "sqlcipher"))]
84        let db = Arc::new(
85            MintSqliteAuthDatabase::new(&path)
86                .await
87                .expect("auth database"),
88        );
89        #[cfg(feature = "sqlcipher")]
90        let db = Arc::new(
91            MintSqliteAuthDatabase::new((path.clone(), "test".to_owned()))
92                .await
93                .expect("auth database"),
94        );
95
96        let proof = AuthProof {
97            keyset_id: Id::from_str("00916bbf7ef91a36").expect("valid keyset id"),
98            secret: Secret::generate(),
99            c: SecretKey::generate().public_key(),
100            dleq: None,
101        };
102        let y = proof.y().expect("proof y");
103
104        let (first, second) = tokio::join!(
105            spend_auth_proof(db.clone(), proof.clone()),
106            spend_auth_proof(db.clone(), proof)
107        );
108
109        assert!(matches!(
110            (&first, &second),
111            (Ok(()), Err(database::Error::Duplicate)) | (Err(database::Error::Duplicate), Ok(()))
112        ));
113        assert_eq!(
114            db.get_proofs_states(&[y]).await.expect("proof state"),
115            vec![Some(State::Spent)]
116        );
117
118        drop(db);
119        remove_file(path).expect("remove auth database");
120    }
121
122    #[tokio::test]
123    async fn open_legacy_and_migrate() {
124        let file = format!(
125            "{}/db.sqlite",
126            std::env::temp_dir().to_str().unwrap_or_default()
127        );
128
129        {
130            let _ = remove_file(&file);
131            #[cfg(not(feature = "sqlcipher"))]
132            let config: Config = file.as_str().into();
133            #[cfg(feature = "sqlcipher")]
134            let config: Config = (file.as_str(), "test").into();
135
136            let pool = Pool::<SqliteConnectionManager>::new(config);
137
138            let conn = pool.get().await.expect("valid connection");
139
140            query(include_str!("../../tests/legacy-sqlx.sql"))
141                .expect("query")
142                .execute(&*conn)
143                .await
144                .expect("create former db failed");
145        }
146
147        #[cfg(not(feature = "sqlcipher"))]
148        let conn = MintSqliteDatabase::new(file.as_str()).await;
149
150        #[cfg(feature = "sqlcipher")]
151        let conn = MintSqliteDatabase::new((file.as_str(), "test")).await;
152
153        assert!(conn.is_ok(), "Failed with {:?}", conn.unwrap_err());
154
155        let _ = remove_file(&file);
156    }
157}