diff --git a/crates/cdk-redb/src/wallet/mod.rs b/crates/cdk-redb/src/wallet/mod.rs index 6c498331..a441f71a 100644 --- a/crates/cdk-redb/src/wallet/mod.rs +++ b/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, + 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::(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) -> Result<(), Self::Err> { + #[instrument(skip(self, added, deleted_ys))] + async fn update_proofs( + &self, + added: Vec, + deleted_ys: Vec, + ) -> 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) -> Result<(), Self::Err> { + self.update_proof_states(ys, State::Pending).await + } + + #[instrument(skip(self, ys))] + async fn reserve_proofs(&self, ys: Vec) -> Result<(), Self::Err> { + self.update_proof_states(ys, State::Reserved).await + } + + #[instrument(skip(self, ys))] + async fn set_unspent_proofs(&self, ys: Vec) -> 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::(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; diff --git a/crates/cdk-rexie/src/wallet.rs b/crates/cdk-rexie/src/wallet.rs index 7a5a16de..436ad327 100644 --- a/crates/cdk-rexie/src/wallet.rs +++ b/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, + 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) -> Result<(), Self::Err> { + async fn update_proofs( + &self, + added: Vec, + removed_ys: Vec, + ) -> 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) -> Result<(), Self::Err> { + self.set_proof_states(ys, State::Pending).await + } + + async fn reserve_proofs(&self, ys: Vec) -> Result<(), Self::Err> { + self.set_proof_states(ys, State::Reserved).await + } + + async fn set_unspent_proofs(&self, ys: Vec) -> Result<(), Self::Err> { + self.set_proof_states(ys, State::Unspent).await + } + async fn get_proofs( &self, mint_url: Option, @@ -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; diff --git a/crates/cdk-sqlite/src/wallet/mod.rs b/crates/cdk-sqlite/src/wallet/mod.rs index 2758cea4..935236b8 100644 --- a/crates/cdk-sqlite/src/wallet/mod.rs +++ b/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) -> Result<(), Self::Err> { - for proof in proof_info { + async fn update_proofs( + &self, + added: Vec, + removed_ys: Vec, + ) -> 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) -> Result<(), Self::Err> { + for y in ys { + self.set_proof_state(y, State::Pending).await?; + } + + Ok(()) + } + + async fn reserve_proofs(&self, ys: Vec) -> Result<(), Self::Err> { + for y in ys { + self.set_proof_state(y, State::Reserved).await?; + } + + Ok(()) + } + + async fn set_unspent_proofs(&self, ys: Vec) -> 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( diff --git a/crates/cdk/src/cdk_database/mod.rs b/crates/cdk/src/cdk_database/mod.rs index 7823ad69..8c20ea84 100644 --- a/crates/cdk/src/cdk_database/mod.rs +++ b/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) -> 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, + removed_ys: Vec, + ) -> Result<(), Self::Err>; + /// Set proofs as pending in storage. Proofs are identified by their Y value. + async fn set_pending_proofs(&self, ys: Vec) -> Result<(), Self::Err>; + /// Reserve proofs in storage. Proofs are identified by their Y value. + async fn reserve_proofs(&self, ys: Vec) -> Result<(), Self::Err>; + /// Set proofs as unspent in storage. Proofs are identified by their Y value. + async fn set_unspent_proofs(&self, ys: Vec) -> Result<(), Self::Err>; /// Get proofs from storage async fn get_proofs( &self, @@ -134,11 +144,6 @@ pub trait WalletDatabase: Debug { state: Option>, spending_conditions: Option>, ) -> Result, 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>; diff --git a/crates/cdk/src/cdk_database/wallet_memory.rs b/crates/cdk/src/cdk_database/wallet_memory.rs index 3299ef3a..1e56f19e 100644 --- a/crates/cdk/src/cdk_database/wallet_memory.rs +++ b/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) -> Result<(), Error> { + async fn update_proofs( + &self, + added: Vec, + removed_ys: Vec, + ) -> 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) -> 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) -> 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) -> 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); diff --git a/crates/cdk/src/wallet/mod.rs b/crates/cdk/src/wallet/mod.rs index ec15a618..73eb4f18 100644 --- a/crates/cdk/src/wallet/mod.rs +++ b/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) -> 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::, _>>()?; // 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 = proofs.iter().map(|p| p.y()).collect::>()?; + 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::, _>>()?; - - 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::, _>>()?; - - 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::, _>>()?; + 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, proofs: Proofs) -> Result { - for proof in proofs.iter() { - self.localstore - .set_proof_state(proof.y()?, State::Reserved) - .await?; - } + let ys = proofs + .iter() + .map(|p| p.y()) + .collect::, _>>()?; + 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::, _>>()?; + 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::() - ); + 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::() + ); - // Update counter for keyset - self.localstore - .increment_keyset_counter(&active_keyset_id, change_proofs.len() as u32) - .await?; + // Update counter for keyset + self.localstore + .increment_keyset_counter(&active_keyset_id, change_proofs.len() as u32) + .await?; - let change_proofs_info = change_proofs - .into_iter() - .map(|proof| { - ProofInfo::new( - proof, - self.mint_url.clone(), - State::Unspent, - quote_info.unit, - ) - }) - .collect::, _>>()?; - - 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::, _>>()? + } + None => Vec::new(), + }; self.localstore.remove_melt_quote("e_info.id).await?; - self.localstore.remove_proofs(&proofs).await?; + let deleted_ys = proofs + .iter() + .map(|p| p.y()) + .collect::, _>>()?; + 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::, _>>()?; + 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::, _>>()?; - self.localstore.add_proofs(proofs).await?; + self.localstore.update_proofs(proofs, vec![]).await?; } Ok(total_amount) @@ -1919,7 +1933,9 @@ impl Wallet { }) .collect::, _>>()?; - self.localstore.add_proofs(unspent_proofs).await?; + self.localstore + .update_proofs(unspent_proofs, vec![]) + .await?; empty_batch = 0; start_counter += 100;