Просмотр исходного кода

Modify WalletDatabase trait to better support db transactions

David Caseria 6 месяцев назад
Родитель
Сommit
d0d7281c77

+ 69 - 60
crates/cdk-redb/src/wallet/mod.rs

@@ -10,7 +10,7 @@ use async_trait::async_trait;
 use cdk::cdk_database::WalletDatabase;
 use cdk::mint_url::MintUrl;
 use cdk::nuts::{
-    CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State,
+    CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State,
 };
 use cdk::types::ProofInfo;
 use cdk::util::unix_time;
@@ -151,6 +151,47 @@ impl WalletRedbDatabase {
             db: Arc::new(Mutex::new(db)),
         })
     }
+
+    async fn update_proof_states(
+        &self,
+        ys: Vec<PublicKey>,
+        state: State,
+    ) -> Result<(), cdk_database::Error> {
+        let db = self.db.lock().await;
+        let read_txn = db.begin_read().map_err(Error::from)?;
+        let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
+
+        let write_txn = db.begin_write().map_err(Error::from)?;
+
+        for y in ys {
+            let y_slice = y.to_bytes();
+            let proof = table
+                .get(y_slice.as_slice())
+                .map_err(Error::from)?
+                .ok_or(Error::UnknownY)?;
+
+            let mut proof_info =
+                serde_json::from_str::<ProofInfo>(proof.value()).map_err(Error::from)?;
+
+            proof_info.state = state;
+
+            {
+                let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
+                table
+                    .insert(
+                        y_slice.as_slice(),
+                        serde_json::to_string(&proof_info)
+                            .map_err(Error::from)?
+                            .as_str(),
+                    )
+                    .map_err(Error::from)?;
+            }
+        }
+
+        write_txn.commit().map_err(Error::from)?;
+
+        Ok(())
+    }
 }
 
 #[async_trait]
@@ -260,7 +301,7 @@ impl WalletDatabase for WalletRedbDatabase {
                 .collect();
 
             if !updated_proofs.is_empty() {
-                self.add_proofs(updated_proofs).await?;
+                self.update_proofs(updated_proofs, vec![]).await?;
             }
         }
 
@@ -566,8 +607,12 @@ impl WalletDatabase for WalletRedbDatabase {
         Ok(())
     }
 
-    #[instrument(skip(self, proofs_info))]
-    async fn add_proofs(&self, proofs_info: Vec<ProofInfo>) -> Result<(), Self::Err> {
+    #[instrument(skip(self, added, deleted_ys))]
+    async fn update_proofs(
+        &self,
+        added: Vec<ProofInfo>,
+        deleted_ys: Vec<PublicKey>,
+    ) -> Result<(), Self::Err> {
         let db = self.db.lock().await;
 
         let write_txn = db.begin_write().map_err(Error::from)?;
@@ -575,7 +620,7 @@ impl WalletDatabase for WalletRedbDatabase {
         {
             let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
 
-            for proof_info in proofs_info.iter() {
+            for proof_info in added.iter() {
                 table
                     .insert(
                         proof_info.y.to_bytes().as_slice(),
@@ -585,12 +630,31 @@ impl WalletDatabase for WalletRedbDatabase {
                     )
                     .map_err(Error::from)?;
             }
+
+            for y in deleted_ys.iter() {
+                table.remove(y.to_bytes().as_slice()).map_err(Error::from)?;
+            }
         }
         write_txn.commit().map_err(Error::from)?;
 
         Ok(())
     }
 
+    #[instrument(skip(self, ys))]
+    async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        self.update_proof_states(ys, State::Pending).await
+    }
+
+    #[instrument(skip(self, ys))]
+    async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        self.update_proof_states(ys, State::Reserved).await
+    }
+
+    #[instrument(skip(self, ys))]
+    async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        self.update_proof_states(ys, State::Unspent).await
+    }
+
     #[instrument(skip_all)]
     async fn get_proofs(
         &self,
@@ -630,61 +694,6 @@ impl WalletDatabase for WalletRedbDatabase {
         Ok(proofs)
     }
 
-    #[instrument(skip(self, proofs))]
-    async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err> {
-        let db = self.db.lock().await;
-
-        let write_txn = db.begin_write().map_err(Error::from)?;
-
-        {
-            let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
-
-            for proof in proofs {
-                let y_slice = proof.y().map_err(Error::from)?.to_bytes();
-                table.remove(y_slice.as_slice()).map_err(Error::from)?;
-            }
-        }
-        write_txn.commit().map_err(Error::from)?;
-
-        Ok(())
-    }
-
-    #[instrument(skip(self, y))]
-    async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), Self::Err> {
-        let db = self.db.lock().await;
-        let read_txn = db.begin_read().map_err(Error::from)?;
-        let table = read_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
-
-        let y_slice = y.to_bytes();
-        let proof = table
-            .get(y_slice.as_slice())
-            .map_err(Error::from)?
-            .ok_or(Error::UnknownY)?;
-
-        let write_txn = db.begin_write().map_err(Error::from)?;
-
-        let mut proof_info =
-            serde_json::from_str::<ProofInfo>(proof.value()).map_err(Error::from)?;
-
-        proof_info.state = state;
-
-        {
-            let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
-            table
-                .insert(
-                    y_slice.as_slice(),
-                    serde_json::to_string(&proof_info)
-                        .map_err(Error::from)?
-                        .as_str(),
-                )
-                .map_err(Error::from)?;
-        }
-
-        write_txn.commit().map_err(Error::from)?;
-
-        Ok(())
-    }
-
     #[instrument(skip(self), fields(keyset_id = %keyset_id))]
     async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err> {
         let db = self.db.lock().await;

+ 66 - 59
crates/cdk-rexie/src/wallet.rs

@@ -5,10 +5,10 @@ use std::rc::Rc;
 use std::result::Result;
 
 use async_trait::async_trait;
-use cdk::cdk_database::WalletDatabase;
+use cdk::cdk_database::{self, WalletDatabase};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::{
-    CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State,
+    CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State,
 };
 use cdk::types::ProofInfo;
 use cdk::util::unix_time;
@@ -108,6 +108,44 @@ impl WalletRexieDatabase {
             db: Rc::new(Mutex::new(rexie)),
         })
     }
+
+    async fn set_proof_states(
+        &self,
+        ys: Vec<PublicKey>,
+        state: State,
+    ) -> Result<(), cdk_database::Error> {
+        let rexie = self.db.lock().await;
+
+        let transaction = rexie
+            .transaction(&[PROOFS], TransactionMode::ReadWrite)
+            .map_err(Error::from)?;
+
+        let proofs_store = transaction.store(PROOFS).map_err(Error::from)?;
+
+        for y in ys {
+            let y = serde_wasm_bindgen::to_value(&y).map_err(Error::from)?;
+
+            let mut proof: ProofInfo = proofs_store
+                .get(y.clone())
+                .await
+                .map_err(Error::from)?
+                .and_then(|p| serde_wasm_bindgen::from_value(p).ok())
+                .ok_or(Error::NotFound)?;
+
+            proof.state = state;
+
+            let proof = serde_wasm_bindgen::to_value(&proof).map_err(Error::from)?;
+
+            proofs_store
+                .put(&proof, Some(&y))
+                .await
+                .map_err(Error::from)?;
+        }
+
+        transaction.done().await.map_err(Error::from)?;
+
+        Ok(())
+    }
 }
 
 #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -224,7 +262,7 @@ impl WalletDatabase for WalletRexieDatabase {
             .collect();
 
         if !updated_proofs.is_empty() {
-            self.add_proofs(updated_proofs).await?;
+            self.update_proofs(updated_proofs, vec![]).await?;
         }
 
         // Update mint quotes
@@ -567,7 +605,11 @@ impl WalletDatabase for WalletRexieDatabase {
         Ok(())
     }
 
-    async fn add_proofs(&self, proofs: Vec<ProofInfo>) -> Result<(), Self::Err> {
+    async fn update_proofs(
+        &self,
+        added: Vec<ProofInfo>,
+        removed_ys: Vec<PublicKey>,
+    ) -> Result<(), Self::Err> {
         let rexie = self.db.lock().await;
 
         let transaction = rexie
@@ -576,9 +618,8 @@ impl WalletDatabase for WalletRexieDatabase {
 
         let proofs_store = transaction.store(PROOFS).map_err(Error::from)?;
 
-        for proof in proofs {
-            let y = proof.y;
-            let y = serde_wasm_bindgen::to_value(&y).map_err(Error::from)?;
+        for proof in added {
+            let y = serde_wasm_bindgen::to_value(&proof.y).map_err(Error::from)?;
             let proof = serde_wasm_bindgen::to_value(&proof).map_err(Error::from)?;
 
             proofs_store
@@ -587,11 +628,29 @@ impl WalletDatabase for WalletRexieDatabase {
                 .map_err(Error::from)?;
         }
 
+        for y in removed_ys {
+            let y = serde_wasm_bindgen::to_value(&y).map_err(Error::from)?;
+
+            proofs_store.delete(y).await.map_err(Error::from)?;
+        }
+
         transaction.done().await.map_err(Error::from)?;
 
         Ok(())
     }
 
+    async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        self.set_proof_states(ys, State::Pending).await
+    }
+
+    async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        self.set_proof_states(ys, State::Reserved).await
+    }
+
+    async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        self.set_proof_states(ys, State::Unspent).await
+    }
+
     async fn get_proofs(
         &self,
         mint_url: Option<MintUrl>,
@@ -638,58 +697,6 @@ impl WalletDatabase for WalletRexieDatabase {
         Ok(proofs)
     }
 
-    async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err> {
-        let rexie = self.db.lock().await;
-
-        let transaction = rexie
-            .transaction(&[PROOFS], TransactionMode::ReadWrite)
-            .map_err(Error::from)?;
-
-        let proofs_store = transaction.store(PROOFS).map_err(Error::from)?;
-
-        for proof in proofs {
-            let y = serde_wasm_bindgen::to_value(&proof.y()?).map_err(Error::from)?;
-
-            proofs_store.delete(y).await.map_err(Error::from)?;
-        }
-
-        transaction.done().await.map_err(Error::from)?;
-
-        Ok(())
-    }
-
-    async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), Self::Err> {
-        let rexie = self.db.lock().await;
-
-        let transaction = rexie
-            .transaction(&[PROOFS], TransactionMode::ReadWrite)
-            .map_err(Error::from)?;
-
-        let proofs_store = transaction.store(PROOFS).map_err(Error::from)?;
-
-        let y = serde_wasm_bindgen::to_value(&y).map_err(Error::from)?;
-
-        let mut proof: ProofInfo = proofs_store
-            .get(y.clone())
-            .await
-            .map_err(Error::from)?
-            .and_then(|p| serde_wasm_bindgen::from_value(p).ok())
-            .ok_or(Error::NotFound)?;
-
-        proof.state = state;
-
-        let proof = serde_wasm_bindgen::to_value(&proof).map_err(Error::from)?;
-
-        proofs_store
-            .put(&proof, Some(&y))
-            .await
-            .map_err(Error::from)?;
-
-        transaction.done().await.map_err(Error::from)?;
-
-        Ok(())
-    }
-
     async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err> {
         let rexie = self.db.lock().await;
 

+ 67 - 46
crates/cdk-sqlite/src/wallet/mod.rs

@@ -9,8 +9,8 @@ use cdk::amount::Amount;
 use cdk::cdk_database::{self, WalletDatabase};
 use cdk::mint_url::MintUrl;
 use cdk::nuts::{
-    CurrencyUnit, Id, KeySetInfo, Keys, MeltQuoteState, MintInfo, MintQuoteState, Proof, Proofs,
-    PublicKey, SpendingConditions, State,
+    CurrencyUnit, Id, KeySetInfo, Keys, MeltQuoteState, MintInfo, MintQuoteState, Proof, PublicKey,
+    SpendingConditions, State,
 };
 use cdk::secret::Secret;
 use cdk::types::ProofInfo;
@@ -53,6 +53,23 @@ impl WalletSqliteDatabase {
             .await
             .expect("Could not run migrations");
     }
+
+    async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), cdk_database::Error> {
+        sqlx::query(
+            r#"
+    UPDATE proof
+    SET state=?
+    WHERE y IS ?;
+            "#,
+        )
+        .bind(state.to_string())
+        .bind(y.to_bytes().to_vec())
+        .execute(&self.pool)
+        .await
+        .map_err(Error::from)?;
+
+        Ok(())
+    }
 }
 
 #[async_trait]
@@ -513,15 +530,18 @@ WHERE id=?
         Ok(())
     }
 
-    #[instrument(skip_all)]
-    async fn add_proofs(&self, proof_info: Vec<ProofInfo>) -> Result<(), Self::Err> {
-        for proof in proof_info {
+    async fn update_proofs(
+        &self,
+        added: Vec<ProofInfo>,
+        removed_ys: Vec<PublicKey>,
+    ) -> Result<(), Self::Err> {
+        for proof in added {
             sqlx::query(
                 r#"
-INSERT OR REPLACE INTO proof
-(y, mint_url, state, spending_condition, unit, amount, keyset_id, secret, c, witness)
-VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
-        "#,
+    INSERT OR REPLACE INTO proof
+    (y, mint_url, state, spending_condition, unit, amount, keyset_id, secret, c, witness)
+    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
+            "#,
             )
             .bind(proof.y.to_bytes().to_vec())
             .bind(proof.mint_url.to_string())
@@ -547,6 +567,44 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
             .map_err(Error::from)?;
         }
 
+        // TODO: Generate a IN clause
+        for y in removed_ys {
+            sqlx::query(
+                r#"
+    DELETE FROM proof
+    WHERE y = ?
+            "#,
+            )
+            .bind(y.to_bytes().to_vec())
+            .execute(&self.pool)
+            .await
+            .map_err(Error::from)?;
+        }
+
+        Ok(())
+    }
+
+    async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        for y in ys {
+            self.set_proof_state(y, State::Pending).await?;
+        }
+
+        Ok(())
+    }
+
+    async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        for y in ys {
+            self.set_proof_state(y, State::Reserved).await?;
+        }
+
+        Ok(())
+    }
+
+    async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
+        for y in ys {
+            self.set_proof_state(y, State::Unspent).await?;
+        }
+
         Ok(())
     }
 
@@ -602,43 +660,6 @@ FROM proof;
         }
     }
 
-    #[instrument(skip_all)]
-    async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err> {
-        // TODO: Generate a IN clause
-        for proof in proofs {
-            sqlx::query(
-                r#"
-DELETE FROM proof
-WHERE y = ?
-        "#,
-            )
-            .bind(proof.y()?.to_bytes().to_vec())
-            .execute(&self.pool)
-            .await
-            .map_err(Error::from)?;
-        }
-
-        Ok(())
-    }
-
-    #[instrument(skip(self, y))]
-    async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), Self::Err> {
-        sqlx::query(
-            r#"
-UPDATE proof
-SET state=?
-WHERE y IS ?;
-        "#,
-        )
-        .bind(state.to_string())
-        .bind(y.to_bytes().to_vec())
-        .execute(&self.pool)
-        .await
-        .map_err(Error::from)?;
-
-        Ok(())
-    }
-
     #[instrument(skip(self), fields(keyset_id = %keyset_id))]
     async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err> {
         sqlx::query(

+ 14 - 9
crates/cdk/src/cdk_database/mod.rs

@@ -17,9 +17,9 @@ use crate::mint::MintQuote as MintMintQuote;
 #[cfg(feature = "wallet")]
 use crate::mint_url::MintUrl;
 #[cfg(feature = "mint")]
-use crate::nuts::{BlindSignature, MeltQuoteState, MintQuoteState, Proof};
+use crate::nuts::{BlindSignature, MeltQuoteState, MintQuoteState, Proof, Proofs};
 #[cfg(any(feature = "wallet", feature = "mint"))]
-use crate::nuts::{CurrencyUnit, Id, Proofs, PublicKey, State};
+use crate::nuts::{CurrencyUnit, Id, PublicKey, State};
 #[cfg(feature = "wallet")]
 use crate::nuts::{KeySetInfo, Keys, MintInfo, SpendingConditions};
 #[cfg(feature = "wallet")]
@@ -124,8 +124,18 @@ pub trait WalletDatabase: Debug {
     /// Remove [`Keys`] from storage
     async fn remove_keys(&self, id: &Id) -> Result<(), Self::Err>;
 
-    /// Add [`Proofs`] to storage
-    async fn add_proofs(&self, proof_info: Vec<ProofInfo>) -> Result<(), Self::Err>;
+    /// Update the proofs in storage by adding new proofs or removing proofs by their Y value.
+    async fn update_proofs(
+        &self,
+        added: Vec<ProofInfo>,
+        removed_ys: Vec<PublicKey>,
+    ) -> Result<(), Self::Err>;
+    /// Set proofs as pending in storage. Proofs are identified by their Y value.
+    async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
+    /// Reserve proofs in storage. Proofs are identified by their Y value.
+    async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
+    /// Set proofs as unspent in storage. Proofs are identified by their Y value.
+    async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>;
     /// Get proofs from storage
     async fn get_proofs(
         &self,
@@ -134,11 +144,6 @@ pub trait WalletDatabase: Debug {
         state: Option<Vec<State>>,
         spending_conditions: Option<Vec<SpendingConditions>>,
     ) -> Result<Vec<ProofInfo>, Self::Err>;
-    /// Remove proofs from storage
-    async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Self::Err>;
-
-    /// Set Proof state
-    async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), Self::Err>;
 
     /// Increment Keyset counter
     async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Self::Err>;

+ 48 - 32
crates/cdk/src/cdk_database/wallet_memory.rs

@@ -10,7 +10,7 @@ use super::WalletDatabase;
 use crate::cdk_database::Error;
 use crate::mint_url::MintUrl;
 use crate::nuts::{
-    CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, Proofs, PublicKey, SpendingConditions, State,
+    CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PublicKey, SpendingConditions, State,
 };
 use crate::types::ProofInfo;
 use crate::util::unix_time;
@@ -109,7 +109,7 @@ impl WalletDatabase for WalletMemoryDatabase {
                 })
                 .collect();
 
-            self.add_proofs(updated_proofs).await?;
+            self.update_proofs(updated_proofs, vec![]).await?;
         }
 
         // Update mint quotes
@@ -238,13 +238,57 @@ impl WalletDatabase for WalletMemoryDatabase {
         Ok(())
     }
 
-    async fn add_proofs(&self, proofs_info: Vec<ProofInfo>) -> Result<(), Error> {
+    async fn update_proofs(
+        &self,
+        added: Vec<ProofInfo>,
+        removed_ys: Vec<PublicKey>,
+    ) -> Result<(), Error> {
         let mut all_proofs = self.proofs.write().await;
 
-        for proof_info in proofs_info.into_iter() {
+        for proof_info in added.into_iter() {
             all_proofs.insert(proof_info.y, proof_info);
         }
 
+        for y in removed_ys.into_iter() {
+            all_proofs.remove(&y);
+        }
+
+        Ok(())
+    }
+
+    async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Error> {
+        let mut all_proofs = self.proofs.write().await;
+
+        for y in ys.into_iter() {
+            if let Some(proof_info) = all_proofs.get_mut(&y) {
+                proof_info.state = State::Pending;
+            }
+        }
+
+        Ok(())
+    }
+
+    async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Error> {
+        let mut all_proofs = self.proofs.write().await;
+
+        for y in ys.into_iter() {
+            if let Some(proof_info) = all_proofs.get_mut(&y) {
+                proof_info.state = State::Reserved;
+            }
+        }
+
+        Ok(())
+    }
+
+    async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Error> {
+        let mut all_proofs = self.proofs.write().await;
+
+        for y in ys.into_iter() {
+            if let Some(proof_info) = all_proofs.get_mut(&y) {
+                proof_info.state = State::Unspent;
+            }
+        }
+
         Ok(())
     }
 
@@ -272,34 +316,6 @@ impl WalletDatabase for WalletMemoryDatabase {
         Ok(proofs)
     }
 
-    async fn remove_proofs(&self, proofs: &Proofs) -> Result<(), Error> {
-        let mut mint_proofs = self.proofs.write().await;
-
-        for proof in proofs {
-            mint_proofs.remove(&proof.y().map_err(Error::from)?);
-        }
-
-        Ok(())
-    }
-
-    async fn set_proof_state(&self, y: PublicKey, state: State) -> Result<(), Self::Err> {
-        let mint_proofs = self.proofs.read().await;
-
-        let mint_proof = mint_proofs.get(&y).cloned();
-        drop(mint_proofs);
-
-        let mut mint_proofs = self.proofs.write().await;
-
-        if let Some(proof_info) = mint_proof {
-            let mut proof_info = proof_info.clone();
-
-            proof_info.state = state;
-            mint_proofs.insert(y, proof_info);
-        }
-
-        Ok(())
-    }
-
     async fn increment_keyset_counter(&self, keyset_id: &Id, count: u32) -> Result<(), Error> {
         let keyset_counter = self.keyset_counter.read().await;
         let current_counter = keyset_counter.get(keyset_id).cloned().unwrap_or(0);

+ 71 - 55
crates/cdk/src/wallet/mod.rs

@@ -259,11 +259,7 @@ impl Wallet {
     /// Return proofs to unspent allowing them to be selected and spent
     #[instrument(skip(self))]
     pub async fn unreserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Error> {
-        for y in ys {
-            self.localstore.set_proof_state(y, State::Unspent).await?;
-        }
-
-        Ok(())
+        Ok(self.localstore.set_unspent_proofs(ys).await?)
     }
 
     /// Qeury mint for current mint information
@@ -461,7 +457,10 @@ impl Wallet {
         let amount = pending_proofs.iter().map(|p| p.proof.amount).sum();
 
         self.localstore
-            .remove_proofs(&non_pending_proofs.into_iter().map(|p| p.proof).collect())
+            .update_proofs(
+                vec![],
+                non_pending_proofs.into_iter().map(|p| p.y).collect(),
+            )
             .await?;
 
         balance += amount;
@@ -701,7 +700,7 @@ impl Wallet {
             .collect::<Result<Vec<ProofInfo>, _>>()?;
 
         // Add new proofs to store
-        self.localstore.add_proofs(proofs).await?;
+        self.localstore.update_proofs(proofs, vec![]).await?;
 
         Ok(minted_amount)
     }
@@ -776,12 +775,8 @@ impl Wallet {
         // Desired amount is either amount passed or value of all proof
         let proofs_total: Amount = proofs.iter().map(|p| p.amount).sum();
 
-        for proof in proofs.iter() {
-            self.localstore
-                .set_proof_state(proof.y()?, State::Pending)
-                .await
-                .ok();
-        }
+        let ys: Vec<PublicKey> = proofs.iter().map(|p| p.y()).collect::<Result<_, _>>()?;
+        self.localstore.set_pending_proofs(ys).await?;
 
         let fee = self.get_proofs_fee(&proofs).await?;
 
@@ -932,9 +927,9 @@ impl Wallet {
             .increment_keyset_counter(&active_keyset_id, pre_swap.derived_secret_count)
             .await?;
 
+        let mut added_proofs = Vec::new();
         let change_proofs;
         let send_proofs;
-
         match amount {
             Some(amount) => {
                 let (proofs_with_condition, proofs_without_condition): (Proofs, Proofs) =
@@ -982,8 +977,7 @@ impl Wallet {
                     .into_iter()
                     .map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Reserved, *unit))
                     .collect::<Result<Vec<ProofInfo>, _>>()?;
-
-                self.localstore.add_proofs(send_proofs_info).await?;
+                added_proofs = send_proofs_info;
 
                 change_proofs = proofs_to_keep;
                 send_proofs = Some(proofs_to_send);
@@ -998,12 +992,17 @@ impl Wallet {
             .into_iter()
             .map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Unspent, *unit))
             .collect::<Result<Vec<ProofInfo>, _>>()?;
-
-        self.localstore.add_proofs(keep_proofs).await?;
+        added_proofs.extend(keep_proofs);
 
         // Remove spent proofs used as inputs
-        self.localstore.remove_proofs(&input_proofs).await?;
+        let deleted_ys = input_proofs
+            .into_iter()
+            .map(|proof| proof.y())
+            .collect::<Result<Vec<PublicKey>, _>>()?;
 
+        self.localstore
+            .update_proofs(added_proofs, deleted_ys)
+            .await?;
         Ok(send_proofs)
     }
 
@@ -1053,11 +1052,11 @@ impl Wallet {
     /// Send specific proofs
     #[instrument(skip(self))]
     pub async fn send_proofs(&self, memo: Option<String>, proofs: Proofs) -> Result<Token, Error> {
-        for proof in proofs.iter() {
-            self.localstore
-                .set_proof_state(proof.y()?, State::Reserved)
-                .await?;
-        }
+        let ys = proofs
+            .iter()
+            .map(|p| p.y())
+            .collect::<Result<Vec<PublicKey>, _>>()?;
+        self.localstore.reserve_proofs(ys).await?;
 
         Ok(Token::new(
             self.mint_url.clone(),
@@ -1358,11 +1357,11 @@ impl Wallet {
             return Err(Error::QuoteUnknown);
         };
 
-        for proof in proofs.iter() {
-            self.localstore
-                .set_proof_state(proof.y()?, State::Pending)
-                .await?;
-        }
+        let ys = proofs
+            .iter()
+            .map(|p| p.y())
+            .collect::<Result<Vec<PublicKey>, _>>()?;
+        self.localstore.set_pending_proofs(ys).await?;
 
         let active_keyset_id = self.get_active_mint_keyset().await?.id;
 
@@ -1429,35 +1428,42 @@ impl Wallet {
             change: change_proofs.clone(),
         };
 
-        if let Some(change_proofs) = change_proofs {
-            tracing::debug!(
-                "Change amount returned from melt: {}",
-                change_proofs.iter().map(|p| p.amount).sum::<Amount>()
-            );
-
-            // Update counter for keyset
-            self.localstore
-                .increment_keyset_counter(&active_keyset_id, change_proofs.len() as u32)
-                .await?;
+        let change_proof_infos = match change_proofs {
+            Some(change_proofs) => {
+                tracing::debug!(
+                    "Change amount returned from melt: {}",
+                    change_proofs.iter().map(|p| p.amount).sum::<Amount>()
+                );
 
-            let change_proofs_info = change_proofs
-                .into_iter()
-                .map(|proof| {
-                    ProofInfo::new(
-                        proof,
-                        self.mint_url.clone(),
-                        State::Unspent,
-                        quote_info.unit,
-                    )
-                })
-                .collect::<Result<Vec<ProofInfo>, _>>()?;
+                // Update counter for keyset
+                self.localstore
+                    .increment_keyset_counter(&active_keyset_id, change_proofs.len() as u32)
+                    .await?;
 
-            self.localstore.add_proofs(change_proofs_info).await?;
-        }
+                change_proofs
+                    .into_iter()
+                    .map(|proof| {
+                        ProofInfo::new(
+                            proof,
+                            self.mint_url.clone(),
+                            State::Unspent,
+                            quote_info.unit,
+                        )
+                    })
+                    .collect::<Result<Vec<ProofInfo>, _>>()?
+            }
+            None => Vec::new(),
+        };
 
         self.localstore.remove_melt_quote(&quote_info.id).await?;
 
-        self.localstore.remove_proofs(&proofs).await?;
+        let deleted_ys = proofs
+            .iter()
+            .map(|p| p.y())
+            .collect::<Result<Vec<PublicKey>, _>>()?;
+        self.localstore
+            .update_proofs(change_proof_infos, deleted_ys)
+            .await?;
 
         Ok(melted)
     }
@@ -1716,6 +1722,14 @@ impl Wallet {
             }
         }
 
+        // Since the proofs are unknown they need to be added to the database
+        let proofs_info = proofs
+            .clone()
+            .into_iter()
+            .map(|p| ProofInfo::new(p, self.mint_url.clone(), State::Pending, self.unit))
+            .collect::<Result<Vec<ProofInfo>, _>>()?;
+        self.localstore.update_proofs(proofs_info, vec![]).await?;
+
         let mut pre_swap = self
             .create_swap(None, amount_split_target, proofs, None, false)
             .await?;
@@ -1755,7 +1769,7 @@ impl Wallet {
                 .into_iter()
                 .map(|proof| ProofInfo::new(proof, mint.clone(), State::Unspent, self.unit))
                 .collect::<Result<Vec<ProofInfo>, _>>()?;
-            self.localstore.add_proofs(proofs).await?;
+            self.localstore.update_proofs(proofs, vec![]).await?;
         }
 
         Ok(total_amount)
@@ -1919,7 +1933,9 @@ impl Wallet {
                     })
                     .collect::<Result<Vec<ProofInfo>, _>>()?;
 
-                self.localstore.add_proofs(unspent_proofs).await?;
+                self.localstore
+                    .update_proofs(unspent_proofs, vec![])
+                    .await?;
 
                 empty_batch = 0;
                 start_counter += 100;