diff --git a/crates/cdk-redb/src/mint/mod.rs b/crates/cdk-redb/src/mint/mod.rs index 1efbb2d2..9c0a615e 100644 --- a/crates/cdk-redb/src/mint/mod.rs +++ b/crates/cdk-redb/src/mint/mod.rs @@ -453,7 +453,7 @@ impl MintDatabase for MintRedbDatabase { Ok(()) } - async fn add_spent_proof(&self, proof: Proof) -> Result<(), Self::Err> { + async fn add_spent_proofs(&self, proofs: Vec) -> Result<(), Self::Err> { let db = self.db.lock().await; let write_txn = db.begin_write().map_err(Error::from)?; @@ -462,13 +462,15 @@ impl MintDatabase for MintRedbDatabase { let mut table = write_txn .open_table(SPENT_PROOFS_TABLE) .map_err(Error::from)?; - let y: PublicKey = hash_to_curve(&proof.secret.to_bytes()).map_err(Error::from)?; - table - .insert( - y.to_bytes(), - serde_json::to_string(&proof).map_err(Error::from)?.as_str(), - ) - .map_err(Error::from)?; + for proof in proofs { + let y: PublicKey = hash_to_curve(&proof.secret.to_bytes()).map_err(Error::from)?; + table + .insert( + y.to_bytes(), + serde_json::to_string(&proof).map_err(Error::from)?.as_str(), + ) + .map_err(Error::from)?; + } } write_txn.commit().map_err(Error::from)?; @@ -503,7 +505,7 @@ impl MintDatabase for MintRedbDatabase { } } - async fn add_pending_proof(&self, proof: Proof) -> Result<(), Self::Err> { + async fn add_pending_proofs(&self, proofs: Vec) -> Result<(), Self::Err> { let db = self.db.lock().await; let write_txn = db.begin_write().map_err(Error::from)?; @@ -512,12 +514,14 @@ impl MintDatabase for MintRedbDatabase { let mut table = write_txn .open_table(PENDING_PROOFS_TABLE) .map_err(Error::from)?; - table - .insert( - hash_to_curve(&proof.secret.to_bytes())?.to_bytes(), - serde_json::to_string(&proof).map_err(Error::from)?.as_str(), - ) - .map_err(Error::from)?; + for proof in proofs { + table + .insert( + hash_to_curve(&proof.secret.to_bytes())?.to_bytes(), + serde_json::to_string(&proof).map_err(Error::from)?.as_str(), + ) + .map_err(Error::from)?; + } } write_txn.commit().map_err(Error::from)?; @@ -555,7 +559,7 @@ impl MintDatabase for MintRedbDatabase { } } - async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Self::Err> { + async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> { let db = self.db.lock().await; let write_txn = db.begin_write().map_err(Error::from)?; @@ -564,8 +568,10 @@ impl MintDatabase for MintRedbDatabase { let mut table = write_txn .open_table(PENDING_PROOFS_TABLE) .map_err(Error::from)?; - let secret_hash = hash_to_curve(&secret.to_bytes()).map_err(Error::from)?; - table.remove(secret_hash.to_bytes()).map_err(Error::from)?; + for secret in secrets { + let secret_hash = hash_to_curve(&secret.to_bytes()).map_err(Error::from)?; + table.remove(secret_hash.to_bytes()).map_err(Error::from)?; + } } write_txn.commit().map_err(Error::from)?; diff --git a/crates/cdk-sqlite/src/mint/mod.rs b/crates/cdk-sqlite/src/mint/mod.rs index 7e2796dd..11adf7a8 100644 --- a/crates/cdk-sqlite/src/mint/mod.rs +++ b/crates/cdk-sqlite/src/mint/mod.rs @@ -10,7 +10,7 @@ use cdk::cdk_database::{self, MintDatabase}; use cdk::mint::MintKeySetInfo; use cdk::nuts::nut05::QuoteState; use cdk::nuts::{ - BlindSignature, CurrencyUnit, Id, MeltQuoteState, MintQuoteState, Proof, PublicKey, + BlindSignature, CurrencyUnit, Id, MeltQuoteState, MintQuoteState, Proof, Proofs, PublicKey, }; use cdk::secret::Secret; use cdk::types::{MeltQuote, MintQuote}; @@ -405,25 +405,29 @@ FROM keyset; .collect()) } - async fn add_spent_proof(&self, proof: Proof) -> Result<(), Self::Err> { - sqlx::query( - r#" + async fn add_spent_proofs(&self, proofs: Proofs) -> Result<(), Self::Err> { + let mut transaction = self.pool.begin().await.map_err(Error::from)?; + + for proof in proofs { + sqlx::query( + r#" INSERT OR REPLACE INTO proof (y, amount, keyset_id, secret, c, witness, state) VALUES (?, ?, ?, ?, ?, ?, ?); "#, - ) - .bind(proof.y()?.to_bytes().to_vec()) - .bind(u64::from(proof.amount) as i64) - .bind(proof.keyset_id.to_string()) - .bind(proof.secret.to_string()) - .bind(proof.c.to_bytes().to_vec()) - .bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap())) - .bind("SPENT") - .execute(&self.pool) - .await - .map_err(Error::from)?; - + ) + .bind(proof.y()?.to_bytes().to_vec()) + .bind(u64::from(proof.amount) as i64) + .bind(proof.keyset_id.to_string()) + .bind(proof.secret.to_string()) + .bind(proof.c.to_bytes().to_vec()) + .bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap())) + .bind("SPENT") + .execute(&mut transaction) + .await + .map_err(Error::from)?; + } + transaction.commit().await.map_err(Error::from)?; Ok(()) } async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result, Self::Err> { @@ -473,24 +477,28 @@ AND state="SPENT"; Ok(Some(sqlite_row_to_proof(rec)?)) } - async fn add_pending_proof(&self, proof: Proof) -> Result<(), Self::Err> { - sqlx::query( - r#" + async fn add_pending_proofs(&self, proofs: Proofs) -> Result<(), Self::Err> { + let mut transaction = self.pool.begin().await.map_err(Error::from)?; + for proof in proofs { + sqlx::query( + r#" INSERT OR REPLACE INTO proof (y, amount, keyset_id, secret, c, witness, spent, pending) VALUES (?, ?, ?, ?, ?, ?, ?); "#, - ) - .bind(proof.y()?.to_bytes().to_vec()) - .bind(u64::from(proof.amount) as i64) - .bind(proof.keyset_id.to_string()) - .bind(proof.secret.to_string()) - .bind(proof.c.to_bytes().to_vec()) - .bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap())) - .bind("PENDING") - .execute(&self.pool) - .await - .map_err(Error::from)?; + ) + .bind(proof.y()?.to_bytes().to_vec()) + .bind(u64::from(proof.amount) as i64) + .bind(proof.keyset_id.to_string()) + .bind(proof.secret.to_string()) + .bind(proof.c.to_bytes().to_vec()) + .bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap())) + .bind("PENDING") + .execute(&mut transaction) + .await + .map_err(Error::from)?; + } + transaction.commit().await.map_err(Error::from)?; Ok(()) } @@ -542,18 +550,22 @@ AND state="PENDING"; }; Ok(Some(sqlite_row_to_proof(rec)?)) } - async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Self::Err> { - sqlx::query( - r#" + async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> { + let mut transaction = self.pool.begin().await.map_err(Error::from)?; + for secret in secrets { + sqlx::query( + r#" DELETE FROM proof WHERE secret=? AND state="PENDING"; "#, - ) - .bind(secret.to_string()) - .execute(&self.pool) - .await - .map_err(Error::from)?; + ) + .bind(secret.to_string()) + .execute(&mut transaction) + .await + .map_err(Error::from)?; + } + transaction.commit().await.map_err(Error::from)?; Ok(()) } diff --git a/crates/cdk/src/cdk_database/mint_memory.rs b/crates/cdk/src/cdk_database/mint_memory.rs index cb7be6a0..0efcfa93 100644 --- a/crates/cdk/src/cdk_database/mint_memory.rs +++ b/crates/cdk/src/cdk_database/mint_memory.rs @@ -179,12 +179,13 @@ impl MintDatabase for MintMemoryDatabase { Ok(()) } - async fn add_spent_proof(&self, proof: Proof) -> Result<(), Self::Err> { - let secret_point = hash_to_curve(&proof.secret.to_bytes())?; - self.spent_proofs - .write() - .await - .insert(secret_point.to_bytes(), proof); + async fn add_spent_proofs(&self, spent_proofs: Proofs) -> Result<(), Self::Err> { + let mut proofs = self.spent_proofs.write().await; + + for proof in spent_proofs { + let secret_point = hash_to_curve(&proof.secret.to_bytes())?; + proofs.insert(secret_point.to_bytes(), proof); + } Ok(()) } @@ -201,11 +202,12 @@ impl MintDatabase for MintMemoryDatabase { Ok(self.spent_proofs.read().await.get(&y.to_bytes()).cloned()) } - async fn add_pending_proof(&self, proof: Proof) -> Result<(), Self::Err> { - self.pending_proofs - .write() - .await - .insert(hash_to_curve(&proof.secret.to_bytes())?.to_bytes(), proof); + async fn add_pending_proofs(&self, pending_proofs: Proofs) -> Result<(), Self::Err> { + let mut proofs = self.pending_proofs.write().await; + + for proof in pending_proofs { + proofs.insert(hash_to_curve(&proof.secret.to_bytes())?.to_bytes(), proof); + } Ok(()) } @@ -226,12 +228,14 @@ impl MintDatabase for MintMemoryDatabase { Ok(self.pending_proofs.read().await.get(&y.to_bytes()).cloned()) } - async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Self::Err> { - let secret_point = hash_to_curve(&secret.to_bytes())?; - self.pending_proofs - .write() - .await - .remove(&secret_point.to_bytes()); + async fn remove_pending_proofs(&self, secrets: Vec<&Secret>) -> Result<(), Self::Err> { + let mut proofs = self.pending_proofs.write().await; + + for secret in secrets { + let secret_point = hash_to_curve(&secret.to_bytes())?; + proofs.remove(&secret_point.to_bytes()); + } + Ok(()) } diff --git a/crates/cdk/src/cdk_database/mod.rs b/crates/cdk/src/cdk_database/mod.rs index 5de640ea..6c933dfe 100644 --- a/crates/cdk/src/cdk_database/mod.rs +++ b/crates/cdk/src/cdk_database/mod.rs @@ -15,9 +15,9 @@ use crate::nuts::State; #[cfg(feature = "mint")] use crate::nuts::{BlindSignature, MeltQuoteState, MintQuoteState, Proof}; #[cfg(any(feature = "wallet", feature = "mint"))] -use crate::nuts::{CurrencyUnit, Id, PublicKey}; +use crate::nuts::{CurrencyUnit, Id, Proofs, PublicKey}; #[cfg(feature = "wallet")] -use crate::nuts::{KeySetInfo, Keys, MintInfo, Proofs, SpendingConditions}; +use crate::nuts::{KeySetInfo, Keys, MintInfo, SpendingConditions}; #[cfg(feature = "mint")] use crate::secret::Secret; #[cfg(feature = "wallet")] @@ -150,17 +150,17 @@ pub trait MintDatabase { async fn get_keyset_info(&self, id: &Id) -> Result, Self::Err>; async fn get_keyset_infos(&self) -> Result, Self::Err>; - async fn add_spent_proof(&self, proof: Proof) -> Result<(), Self::Err>; + async fn add_spent_proofs(&self, proof: Proofs) -> Result<(), Self::Err>; async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result, Self::Err>; async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result, Self::Err>; - async fn add_pending_proof(&self, proof: Proof) -> Result<(), Self::Err>; + async fn add_pending_proofs(&self, proof: Proofs) -> Result<(), Self::Err>; async fn get_pending_proof_by_secret( &self, secret: &Secret, ) -> Result, Self::Err>; async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result, Self::Err>; - async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Self::Err>; + async fn remove_pending_proofs(&self, secret: Vec<&Secret>) -> Result<(), Self::Err>; async fn add_blinded_signature( &self, diff --git a/crates/cdk/src/mint/mod.rs b/crates/cdk/src/mint/mod.rs index edcbe1dc..7145d583 100644 --- a/crates/cdk/src/mint/mod.rs +++ b/crates/cdk/src/mint/mod.rs @@ -493,9 +493,9 @@ impl Mint { } } - for proof in swap_request.inputs { - self.localstore.add_spent_proof(proof).await?; - } + self.localstore + .add_spent_proofs(swap_request.inputs) + .await?; let mut promises = Vec::with_capacity(swap_request.outputs.len()); @@ -583,6 +583,14 @@ impl Mint { &self, melt_request: &MeltBolt11Request, ) -> Result { + for proof in &melt_request.inputs { + self.verify_proof(proof).await?; + } + + self.localstore + .add_pending_proofs(melt_request.inputs.clone()) + .await?; + let state = self .localstore .update_melt_quote_state(&melt_request.quote, MeltQuoteState::Pending) @@ -681,10 +689,6 @@ impl Mint { return Err(Error::DuplicateProofs); } - for proof in &melt_request.inputs { - self.verify_proof(proof).await?; - } - Ok(quote) } @@ -714,9 +718,9 @@ impl Mint { } } - for input in &melt_request.inputs { - self.localstore.add_spent_proof(input.clone()).await?; - } + self.localstore + .add_spent_proofs(melt_request.inputs.clone()) + .await?; let mut change = None; @@ -760,6 +764,10 @@ impl Mint { ); } + self.localstore + .remove_pending_proofs(melt_request.inputs.iter().map(|p| &p.secret).collect()) + .await?; + self.localstore .update_melt_quote_state(&melt_request.quote, MeltQuoteState::Paid) .await?;