From 0cc5e153a8a9158126ebf44ec77a296e4a1c16b6 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sun, 19 May 2024 00:34:18 +0100 Subject: [PATCH] feat: check mint quotes --- crates/cdk-redb/src/wallet.rs | 16 ++++++++++++ crates/cdk-rexie/src/wallet.rs | 24 +++++++++++++++-- crates/cdk/src/cdk_database/mod.rs | 1 + crates/cdk/src/cdk_database/wallet_memory.rs | 5 ++++ crates/cdk/src/mint.rs | 4 ++- crates/cdk/src/types.rs | 11 +++++++- crates/cdk/src/wallet.rs | 27 +++++++++++++++++--- 7 files changed, 81 insertions(+), 7 deletions(-) diff --git a/crates/cdk-redb/src/wallet.rs b/crates/cdk-redb/src/wallet.rs index 321f8e45..c36f0e84 100644 --- a/crates/cdk-redb/src/wallet.rs +++ b/crates/cdk-redb/src/wallet.rs @@ -231,6 +231,22 @@ impl WalletDatabase for RedbWalletDatabase { Ok(None) } + #[instrument(skip_all)] + async fn get_mint_quotes(&self) -> Result, Self::Err> { + let db = self.db.lock().await; + let read_txn = db.begin_read().map_err(Into::::into)?; + let table = read_txn + .open_table(MINT_QUOTES_TABLE) + .map_err(Error::from)?; + + Ok(table + .iter() + .map_err(Error::from)? + .flatten() + .flat_map(|(_id, quote)| serde_json::from_str(quote.value())) + .collect()) + } + #[instrument(skip_all)] async fn remove_mint_quote(&self, quote_id: &str) -> 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 edb10a9b..44ae33f9 100644 --- a/crates/cdk-rexie/src/wallet.rs +++ b/crates/cdk-rexie/src/wallet.rs @@ -265,14 +265,34 @@ impl WalletDatabase for RexieWalletDatabase { let quotes_store = transaction.store(MINT_QUOTES).map_err(Error::from)?; let quote_id = serde_wasm_bindgen::to_value("e_id).map_err(Error::from)?; - let keysets = quotes_store.get("e_id).await.map_err(Error::from)?; + let quote = quotes_store.get("e_id).await.map_err(Error::from)?; let quote: Option = - serde_wasm_bindgen::from_value(keysets).map_err(Error::from)?; + serde_wasm_bindgen::from_value(quote).map_err(Error::from)?; Ok(quote) } + async fn get_mint_quotes(&self) -> Result, Self::Err> { + let rexie = self.db.lock().await; + + let transaction = rexie + .transaction(&[MINT_QUOTES], TransactionMode::ReadOnly) + .map_err(Error::from)?; + + let quotes_store = transaction.store(MINT_QUOTES).map_err(Error::from)?; + + let quotes = quotes_store + .get_all(None, None, None, None) + .await + .map_err(Error::from)?; + + Ok(quotes + .into_iter() + .flat_map(|(_id, q)| serde_wasm_bindgen::from_value(q)) + .collect()) + } + async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err> { let rexie = self.db.lock().await; diff --git a/crates/cdk/src/cdk_database/mod.rs b/crates/cdk/src/cdk_database/mod.rs index ba4bc533..8bb0ef52 100644 --- a/crates/cdk/src/cdk_database/mod.rs +++ b/crates/cdk/src/cdk_database/mod.rs @@ -60,6 +60,7 @@ pub trait WalletDatabase { ) -> Result>, Self::Err>; async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), Self::Err>; async fn get_mint_quote(&self, quote_id: &str) -> Result, Self::Err>; + async fn get_mint_quotes(&self) -> Result, Self::Err>; async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Self::Err>; async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), Self::Err>; diff --git a/crates/cdk/src/cdk_database/wallet_memory.rs b/crates/cdk/src/cdk_database/wallet_memory.rs index fcbed567..4bee0967 100644 --- a/crates/cdk/src/cdk_database/wallet_memory.rs +++ b/crates/cdk/src/cdk_database/wallet_memory.rs @@ -109,6 +109,11 @@ impl WalletDatabase for WalletMemoryDatabase { Ok(self.mint_quotes.lock().await.get(quote_id).cloned()) } + async fn get_mint_quotes(&self) -> Result, Error> { + let quotes = self.mint_quotes.lock().await; + Ok(quotes.values().cloned().collect()) + } + async fn remove_mint_quote(&self, quote_id: &str) -> Result<(), Error> { self.mint_quotes.lock().await.remove(quote_id); diff --git a/crates/cdk/src/mint.rs b/crates/cdk/src/mint.rs index 9ba94ab7..36891bd3 100644 --- a/crates/cdk/src/mint.rs +++ b/crates/cdk/src/mint.rs @@ -15,6 +15,7 @@ use crate::dhke::{hash_to_curve, sign_message, verify_message}; use crate::error::ErrorResponse; use crate::nuts::*; use crate::types::{MeltQuote, MintQuote}; +use crate::url::UncheckedUrl; use crate::util::unix_time; use crate::Amount; @@ -135,12 +136,13 @@ impl Mint { pub async fn new_mint_quote( &self, + mint_url: UncheckedUrl, request: String, unit: CurrencyUnit, amount: Amount, expiry: u64, ) -> Result { - let quote = MintQuote::new(request, unit, amount, expiry); + let quote = MintQuote::new(mint_url, request, unit, amount, expiry); self.localstore.add_mint_quote(quote.clone()).await?; diff --git a/crates/cdk/src/types.rs b/crates/cdk/src/types.rs index 5f756bf9..7659095f 100644 --- a/crates/cdk/src/types.rs +++ b/crates/cdk/src/types.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::nuts::{CurrencyUnit, Proofs}; +use crate::url::UncheckedUrl; use crate::Amount; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -33,6 +34,7 @@ pub enum InvoiceStatus { #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct MintQuote { pub id: String, + pub mint_url: UncheckedUrl, pub amount: Amount, pub unit: CurrencyUnit, pub request: String, @@ -41,10 +43,17 @@ pub struct MintQuote { } impl MintQuote { - pub fn new(request: String, unit: CurrencyUnit, amount: Amount, expiry: u64) -> Self { + pub fn new( + mint_url: UncheckedUrl, + request: String, + unit: CurrencyUnit, + amount: Amount, + expiry: u64, + ) -> Self { let id = Uuid::new_v4(); Self { + mint_url, id: id.to_string(), amount, unit, diff --git a/crates/cdk/src/wallet.rs b/crates/cdk/src/wallet.rs index 6ea05ae0..ca7a2a54 100644 --- a/crates/cdk/src/wallet.rs +++ b/crates/cdk/src/wallet.rs @@ -364,10 +364,11 @@ impl Wallet { ) -> Result { let quote_res = self .client - .post_mint_quote(mint_url.try_into()?, amount, unit.clone()) + .post_mint_quote(mint_url.clone().try_into()?, amount, unit.clone()) .await?; let quote = MintQuote { + mint_url, id: quote_res.quote.clone(), amount, unit: unit.clone(), @@ -408,9 +409,29 @@ impl Wallet { Ok(response) } + /// Check status of pending mint quotes + pub async fn check_all_mint_quotes(&self) -> Result { + let mint_quotes = self.localstore.get_mint_quotes().await?; + let mut total_amount = Amount::ZERO; + + for mint_quote in mint_quotes { + let mint_quote_response = self + .mint_quote_status(mint_quote.mint_url.clone(), &mint_quote.id) + .await?; + + if mint_quote_response.paid { + let amount = self.mint(mint_quote.mint_url, &mint_quote.id).await?; + total_amount += amount; + } else if mint_quote.expiry.le(&unix_time()) { + self.localstore.remove_mint_quote(&mint_quote.id).await?; + } + } + Ok(total_amount) + } + #[instrument(skip(self), fields(mint_url = %mint_url))] async fn active_mint_keyset( - &mut self, + &self, mint_url: &UncheckedUrl, unit: &CurrencyUnit, ) -> Result { @@ -466,7 +487,7 @@ impl Wallet { /// Mint #[instrument(skip(self, quote_id), fields(mint_url = %mint_url))] - pub async fn mint(&mut self, mint_url: UncheckedUrl, quote_id: &str) -> Result { + pub async fn mint(&self, mint_url: UncheckedUrl, quote_id: &str) -> Result { // Check that mint is in store of mints if self.localstore.get_mint(mint_url.clone()).await?.is_none() { self.add_mint(mint_url.clone()).await?;