Cesar Rodas 5 miesięcy temu
rodzic
commit
4f7f9caf05

+ 41 - 4
crates/cdk-common/src/database/mint/mod.rs

@@ -23,22 +23,36 @@ pub mod test;
 #[cfg(feature = "auth")]
 pub use auth::MintAuthDatabase;
 
+/// KeysDatabaseWriter
+#[async_trait]
+pub trait KeysDatabaseWriter<'a, Error>: DbWriterFinalizer<Err = Error> {
+    /// Add Active Keyset
+    async fn set_active_keyset(&mut self, unit: CurrencyUnit, id: Id) -> Result<(), Error>;
+
+    /// Add [`MintKeySetInfo`]
+    async fn add_keyset_info(&mut self, keyset: MintKeySetInfo) -> Result<(), Error>;
+}
+
 /// Mint Keys Database trait
 #[async_trait]
 pub trait KeysDatabase {
     /// Mint Keys Database Error
     type Err: Into<Error> + From<Error>;
 
-    /// Add Active Keyset
-    async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err>;
+    /// Beings a transaction
+    async fn begin_transaction<'a>(
+        &'a self,
+    ) -> Result<Box<dyn KeysDatabaseWriter<'a, Self::Err> + Send + Sync + 'a>, Error>;
+
     /// Get Active Keyset
     async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Self::Err>;
+
     /// Get all Active Keyset
     async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Self::Err>;
-    /// Add [`MintKeySetInfo`]
-    async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err>;
+
     /// Get [`MintKeySetInfo`]
     async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err>;
+
     /// Get [`MintKeySetInfo`]s
     async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>;
 }
@@ -163,13 +177,36 @@ pub trait SignaturesDatabase {
     ) -> Result<Vec<BlindSignature>, Self::Err>;
 }
 
+#[async_trait]
+/// Commit and Rollback
+pub trait DbWriterFinalizer {
+    /// Mint Signature Database Error
+    type Err: Into<Error> + From<Error>;
+
+    /// Commits all the changes into the database
+    async fn commit(self: Box<Self>) -> Result<(), Self::Err>;
+
+    /// Rollbacks the write transaction
+    async fn rollback(self: Box<Self>) -> Result<(), Self::Err>;
+}
+
+/// Base database writer
+#[async_trait]
+pub trait DbWriter<'a, Error>: DbWriterFinalizer<Err = Error> {}
+
 /// Mint Database trait
 #[async_trait]
 pub trait Database<Error>:
     QuotesDatabase<Err = Error> + ProofsDatabase<Err = Error> + SignaturesDatabase<Err = Error>
 {
+    /// Beings a transaction
+    async fn begin_transaction<'a>(
+        &'a self,
+    ) -> Result<Box<dyn DbWriter<'a, Error> + Send + Sync + 'a>, Error>;
+
     /// Set [`MintInfo`]
     async fn set_mint_info(&self, mint_info: MintInfo) -> Result<(), Error>;
+
     /// Get [`MintInfo`]
     async fn get_mint_info(&self) -> Result<MintInfo, Error>;
 

+ 13 - 4
crates/cdk-common/src/database/mint/test.rs

@@ -2,17 +2,21 @@
 //!
 //! This set is generic and checks the default and expected behaviour for a mint database
 //! implementation
-use std::fmt::Debug;
 use std::str::FromStr;
 
 use cashu::secret::Secret;
 use cashu::{Amount, CurrencyUnit, SecretKey};
 
 use super::*;
+use crate::database;
 use crate::mint::MintKeySetInfo;
 
 #[inline]
-async fn setup_keyset<E: Debug, DB: Database<E> + KeysDatabase<Err = E>>(db: &DB) -> Id {
+
+async fn setup_keyset<DB>(db: &DB) -> Id
+where
+    DB: KeysDatabase<Err = database::Error>,
+{
     let keyset_id = Id::from_str("00916bbf7ef91a36").unwrap();
     let keyset_info = MintKeySetInfo {
         id: keyset_id,
@@ -25,12 +29,17 @@ async fn setup_keyset<E: Debug, DB: Database<E> + KeysDatabase<Err = E>>(db: &DB
         max_order: 32,
         input_fee_ppk: 0,
     };
-    db.add_keyset_info(keyset_info).await.unwrap();
+    let mut writer = db.begin_transaction().await.expect("db.begin()");
+    writer.add_keyset_info(keyset_info).await.unwrap();
+    writer.commit().await.expect("commit()");
     keyset_id
 }
 
 /// State transition test
-pub async fn state_transition<E: Debug, DB: Database<E> + KeysDatabase<Err = E>>(db: DB) {
+pub async fn state_transition<DB>(db: DB)
+where
+    DB: Database<database::Error> + KeysDatabase<Err = database::Error>,
+{
     let keyset_id = setup_keyset(&db).await;
 
     let proofs = vec![

+ 2 - 1
crates/cdk-common/src/database/mod.rs

@@ -9,7 +9,8 @@ mod wallet;
 pub use mint::MintAuthDatabase;
 #[cfg(feature = "mint")]
 pub use mint::{
-    Database as MintDatabase, KeysDatabase as MintKeysDatabase,
+    Database as MintDatabase, DbWriterFinalizer as MintDbWriterFinalizer,
+    KeysDatabase as MintKeysDatabase, KeysDatabaseWriter as MintKeyDatabaseWriter,
     ProofsDatabase as MintProofsDatabase, QuotesDatabase as MintQuotesDatabase,
     SignaturesDatabase as MintSignaturesDatabase,
 };

+ 8 - 5
crates/cdk-signatory/src/common.rs

@@ -25,13 +25,14 @@ pub async fn init_keysets(
     // Get keysets info from DB
     let keysets_infos = localstore.get_keyset_infos().await?;
 
+    let mut tx = localstore.begin_transaction().await.expect("begin");
     if !keysets_infos.is_empty() {
         tracing::debug!("Setting all saved keysets to inactive");
         for keyset in keysets_infos.clone() {
             // Set all to in active
             let mut keyset = keyset;
             keyset.active = false;
-            localstore.add_keyset_info(keyset).await?;
+            tx.add_keyset_info(keyset).await?;
         }
 
         let keysets_by_unit: HashMap<CurrencyUnit, Vec<MintKeySetInfo>> =
@@ -74,9 +75,9 @@ pub async fn init_keysets(
                     active_keysets.insert(id, keyset);
                     let mut keyset_info = highest_index_keyset;
                     keyset_info.active = true;
-                    localstore.add_keyset_info(keyset_info).await?;
+                    tx.add_keyset_info(keyset_info).await?;
                     active_keyset_units.push(unit.clone());
-                    localstore.set_active_keyset(unit, id).await?;
+                    tx.set_active_keyset(unit, id).await?;
                 } else {
                     // Check to see if there are not keysets by this unit
                     let derivation_path_index = if keysets.is_empty() {
@@ -104,8 +105,8 @@ pub async fn init_keysets(
                     );
 
                     let id = keyset_info.id;
-                    localstore.add_keyset_info(keyset_info).await?;
-                    localstore.set_active_keyset(unit.clone(), id).await?;
+                    tx.add_keyset_info(keyset_info).await?;
+                    tx.set_active_keyset(unit.clone(), id).await?;
                     active_keysets.insert(id, keyset);
                     active_keyset_units.push(unit.clone());
                 };
@@ -113,6 +114,8 @@ pub async fn init_keysets(
         }
     }
 
+    tx.commit().await.expect("commit");
+
     Ok((active_keysets, active_keyset_units))
 }
 

+ 10 - 4
crates/cdk-signatory/src/db_signatory.rs

@@ -54,6 +54,8 @@ impl DbSignatory {
 
         supported_units.entry(CurrencyUnit::Auth).or_insert((0, 1));
 
+        let mut tx = localstore.begin_transaction().await?;
+
         // Create new keysets for supported units that aren't covered by the current keysets
         for (unit, (fee, max_order)) in supported_units {
             if !active_keyset_units.contains(&unit) {
@@ -77,12 +79,14 @@ impl DbSignatory {
                 );
 
                 let id = keyset_info.id;
-                localstore.add_keyset_info(keyset_info).await?;
-                localstore.set_active_keyset(unit, id).await?;
+                tx.add_keyset_info(keyset_info).await?;
+                tx.set_active_keyset(unit, id).await?;
                 active_keysets.insert(id, keyset);
             }
         }
 
+        tx.commit().await?;
+
         let keys = Self {
             keysets: Default::default(),
             active_keysets: Default::default(),
@@ -244,8 +248,10 @@ impl Signatory for DbSignatory {
             None,
         );
         let id = info.id;
-        self.localstore.add_keyset_info(info.clone()).await?;
-        self.localstore.set_active_keyset(args.unit, id).await?;
+        let mut tx = self.localstore.begin_transaction().await?;
+        tx.add_keyset_info(info.clone()).await?;
+        tx.set_active_keyset(args.unit, id).await?;
+        tx.commit().await?;
 
         self.reload_keys_from_db().await?;
 

+ 4 - 2
crates/cdk-sqlite/src/mint/memory.rs

@@ -31,15 +31,17 @@ pub async fn new_with_state(
     mint_info: MintInfo,
 ) -> Result<MintSqliteDatabase, database::Error> {
     let db = empty().await?;
+    let mut tx = MintKeysDatabase::begin_transaction(&db).await?;
 
     for active_keyset in active_keysets {
-        db.set_active_keyset(active_keyset.0, active_keyset.1)
+        tx.set_active_keyset(active_keyset.0, active_keyset.1)
             .await?;
     }
 
     for keyset in keysets {
-        db.add_keyset_info(keyset).await?;
+        tx.add_keyset_info(keyset).await?;
     }
+    tx.commit().await?;
 
     for quote in mint_quotes {
         db.add_mint_quote(quote).await?;

+ 100 - 50
crates/cdk-sqlite/src/mint/mod.rs

@@ -5,13 +5,14 @@ use std::ops::DerefMut;
 use std::path::Path;
 use std::str::FromStr;
 
-use async_rusqlite::{query, DatabaseExecutor};
+use async_rusqlite::{query, DatabaseExecutor, Transaction};
 use async_trait::async_trait;
 use bitcoin::bip32::DerivationPath;
 use cdk_common::common::QuoteTTL;
+use cdk_common::database::mint::DbWriter;
 use cdk_common::database::{
-    self, MintDatabase, MintKeysDatabase, MintProofsDatabase, MintQuotesDatabase,
-    MintSignaturesDatabase,
+    self, MintDatabase, MintDbWriterFinalizer, MintKeyDatabaseWriter, MintKeysDatabase,
+    MintProofsDatabase, MintQuotesDatabase, MintSignaturesDatabase,
 };
 use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
 use cdk_common::nut00::ProofsMethods;
@@ -135,28 +136,101 @@ impl MintSqliteDatabase {
     }
 }
 
+/// Sqlite Writer
+pub struct SqliteWriter<'a> {
+    transaction: Transaction<'a>,
+}
+
 #[async_trait]
-impl MintKeysDatabase for MintSqliteDatabase {
+impl<'a> MintDbWriterFinalizer for SqliteWriter<'a> {
     type Err = database::Error;
 
-    async fn set_active_keyset(&self, unit: CurrencyUnit, id: Id) -> Result<(), Self::Err> {
-        let transaction = self.pool.begin().await?;
+    async fn commit(self: Box<Self>) -> Result<(), database::Error> {
+        Ok(self.transaction.commit().await?)
+    }
 
+    async fn rollback(self: Box<Self>) -> Result<(), database::Error> {
+        Ok(self.transaction.rollback().await?)
+    }
+}
+
+#[async_trait]
+impl<'a> MintKeyDatabaseWriter<'a, database::Error> for SqliteWriter<'a> {
+    async fn add_keyset_info(&mut self, keyset: MintKeySetInfo) -> Result<(), database::Error> {
+        query(
+            r#"
+        INSERT INTO
+            keyset (
+                id, unit, active, valid_from, valid_to, derivation_path,
+                max_order, input_fee_ppk, derivation_path_index
+            )
+        VALUES (
+            :id, :unit, :active, :valid_from, :valid_to, :derivation_path,
+            :max_order, :input_fee_ppk, :derivation_path_index
+        )
+        ON CONFLICT(id) DO UPDATE SET
+            unit = excluded.unit,
+            active = excluded.active,
+            valid_from = excluded.valid_from,
+            valid_to = excluded.valid_to,
+            derivation_path = excluded.derivation_path,
+            max_order = excluded.max_order,
+            input_fee_ppk = excluded.input_fee_ppk,
+            derivation_path_index = excluded.derivation_path_index
+        "#,
+        )
+        .bind(":id", keyset.id.to_string())
+        .bind(":unit", keyset.unit.to_string())
+        .bind(":active", keyset.active)
+        .bind(":valid_from", keyset.valid_from as i64)
+        .bind(":valid_to", keyset.final_expiry.map(|v| v as i64))
+        .bind(":derivation_path", keyset.derivation_path.to_string())
+        .bind(":max_order", keyset.max_order)
+        .bind(":input_fee_ppk", keyset.input_fee_ppk as i64)
+        .bind(":derivation_path_index", keyset.derivation_path_index)
+        .execute(&self.transaction)
+        .await?;
+
+        Ok(())
+    }
+
+    async fn set_active_keyset(
+        &mut self,
+        unit: CurrencyUnit,
+        id: Id,
+    ) -> Result<(), database::Error> {
         query(r#"UPDATE keyset SET active=FALSE WHERE unit IS :unit"#)
             .bind(":unit", unit.to_string())
-            .execute(&transaction)
+            .execute(&self.transaction)
             .await?;
 
         query(r#"UPDATE keyset SET active=TRUE WHERE unit IS :unit AND id IS :id"#)
             .bind(":unit", unit.to_string())
             .bind(":id", id.to_string())
-            .execute(&transaction)
+            .execute(&self.transaction)
             .await?;
 
-        transaction.commit().await?;
-
         Ok(())
     }
+}
+
+#[async_trait]
+impl<'a> DbWriter<'a, database::Error> for SqliteWriter<'a> {}
+
+#[async_trait]
+impl MintKeysDatabase for MintSqliteDatabase {
+    type Err = database::Error;
+
+    async fn begin_transaction<'a>(
+        &'a self,
+    ) -> Result<
+        Box<dyn MintKeyDatabaseWriter<'a, database::Error> + Send + Sync + 'a>,
+        database::Error,
+    > {
+        Ok(Box::new(SqliteWriter {
+            transaction: self.pool.begin().await?,
+        }))
+    }
 
     async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Self::Err> {
         Ok(
@@ -187,44 +261,6 @@ impl MintKeysDatabase for MintSqliteDatabase {
             .collect::<Result<HashMap<_, _>, Error>>()?)
     }
 
-    async fn add_keyset_info(&self, keyset: MintKeySetInfo) -> Result<(), Self::Err> {
-        query(
-            r#"
-        INSERT INTO
-            keyset (
-                id, unit, active, valid_from, valid_to, derivation_path,
-                max_order, input_fee_ppk, derivation_path_index
-            )
-        VALUES (
-            :id, :unit, :active, :valid_from, :valid_to, :derivation_path,
-            :max_order, :input_fee_ppk, :derivation_path_index
-        )
-        ON CONFLICT(id) DO UPDATE SET
-            unit = excluded.unit,
-            active = excluded.active,
-            valid_from = excluded.valid_from,
-            valid_to = excluded.valid_to,
-            derivation_path = excluded.derivation_path,
-            max_order = excluded.max_order,
-            input_fee_ppk = excluded.input_fee_ppk,
-            derivation_path_index = excluded.derivation_path_index
-        "#,
-        )
-        .bind(":id", keyset.id.to_string())
-        .bind(":unit", keyset.unit.to_string())
-        .bind(":active", keyset.active)
-        .bind(":valid_from", keyset.valid_from as i64)
-        .bind(":valid_to", keyset.final_expiry.map(|v| v as i64))
-        .bind(":derivation_path", keyset.derivation_path.to_string())
-        .bind(":max_order", keyset.max_order)
-        .bind(":input_fee_ppk", keyset.input_fee_ppk as i64)
-        .bind(":derivation_path_index", keyset.derivation_path_index)
-        .execute(&self.pool)
-        .await?;
-
-        Ok(())
-    }
-
     async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err> {
         Ok(query(
             r#"SELECT
@@ -1097,6 +1133,14 @@ impl MintSignaturesDatabase for MintSqliteDatabase {
 
 #[async_trait]
 impl MintDatabase<database::Error> for MintSqliteDatabase {
+    async fn begin_transaction<'a>(
+        &'a self,
+    ) -> Result<Box<dyn DbWriter<'a, database::Error> + Send + Sync + 'a>, database::Error> {
+        Ok(Box::new(SqliteWriter {
+            transaction: self.pool.begin().await?,
+        }))
+    }
+
     async fn set_mint_info(&self, mint_info: MintInfo) -> Result<(), database::Error> {
         Ok(self.set_to_config("mint_info", &mint_info).await?)
     }
@@ -1325,7 +1369,9 @@ mod tests {
             input_fee_ppk: 0,
             final_expiry: None,
         };
-        db.add_keyset_info(keyset_info).await.unwrap();
+        let mut tx = MintKeysDatabase::begin_transaction(&db).await.unwrap();
+        tx.add_keyset_info(keyset_info).await.unwrap();
+        tx.commit().await.unwrap();
 
         let proofs = vec![
             Proof {
@@ -1393,7 +1439,11 @@ mod tests {
             input_fee_ppk: 0,
             final_expiry: None,
         };
-        db.add_keyset_info(keyset_info).await.unwrap();
+        let mut tx = MintKeysDatabase::begin_transaction(&db)
+            .await
+            .expect("begin");
+        tx.add_keyset_info(keyset_info).await.unwrap();
+        tx.commit().await.expect("commit");
 
         let proofs = vec![
             Proof {