From e8db30af3acbbb2c4756140d80f433d0959fc117 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Mon, 1 Jan 2024 18:35:23 +0000 Subject: [PATCH] refactor: proofs in localstore --- crates/cashu-sdk/src/localstore/memory.rs | 37 +++++++++- crates/cashu-sdk/src/localstore/mod.rs | 6 +- crates/cashu-sdk/src/wallet.rs | 89 +++++++++++++---------- crates/cashu/src/nuts/nut00.rs | 7 ++ 4 files changed, 95 insertions(+), 44 deletions(-) diff --git a/crates/cashu-sdk/src/localstore/memory.rs b/crates/cashu-sdk/src/localstore/memory.rs index f2bc14e6..9e5eeae3 100644 --- a/crates/cashu-sdk/src/localstore/memory.rs +++ b/crates/cashu-sdk/src/localstore/memory.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use async_trait::async_trait; -use cashu::nuts::{Id, KeySetInfo, Keys, MintInfo}; +use cashu::nuts::{Id, KeySetInfo, Keys, MintInfo, Proof, Proofs}; use cashu::types::{MeltQuote, MintQuote}; use cashu::url::UncheckedUrl; use tokio::sync::Mutex; @@ -16,6 +16,7 @@ pub struct MemoryLocalStore { mint_quotes: Arc>>, melt_quotes: Arc>>, mint_keys: Arc>>, + proofs: Arc>>>, } #[async_trait(?Send)] @@ -40,8 +41,8 @@ impl LocalStore for MemoryLocalStore { ) -> Result<(), Error> { let mut current_keysets = self.mint_keysets.lock().await; - let current_keysets = current_keysets.entry(mint_url).or_insert(HashSet::new()); - current_keysets.extend(keysets); + let mint_keysets = current_keysets.entry(mint_url).or_insert(HashSet::new()); + mint_keysets.extend(keysets); Ok(()) } @@ -107,4 +108,34 @@ impl LocalStore for MemoryLocalStore { self.mint_keys.lock().await.remove(id); Ok(()) } + + async fn add_proofs(&self, mint_url: UncheckedUrl, proofs: Proofs) -> Result<(), Error> { + let mut all_proofs = self.proofs.lock().await; + + let mint_proofs = all_proofs.entry(mint_url).or_insert(HashSet::new()); + mint_proofs.extend(proofs); + + Ok(()) + } + + async fn get_proofs(&self, mint_url: UncheckedUrl) -> Result, Error> { + Ok(self + .proofs + .lock() + .await + .get(&mint_url) + .map(|p| p.iter().cloned().collect())) + } + + async fn remove_proofs(&self, mint_url: UncheckedUrl, proofs: Proofs) -> Result<(), Error> { + let mut mint_proofs = self.proofs.lock().await; + + if let Some(mint_proofs) = mint_proofs.get_mut(&mint_url) { + for proof in proofs { + mint_proofs.remove(&proof); + } + } + + Ok(()) + } } diff --git a/crates/cashu-sdk/src/localstore/mod.rs b/crates/cashu-sdk/src/localstore/mod.rs index dd60a7e8..4fcc0999 100644 --- a/crates/cashu-sdk/src/localstore/mod.rs +++ b/crates/cashu-sdk/src/localstore/mod.rs @@ -1,6 +1,6 @@ mod memory; use async_trait::async_trait; -use cashu::nuts::{Id, KeySetInfo, Keys, MintInfo}; +use cashu::nuts::{Id, KeySetInfo, Keys, MintInfo, Proofs}; use cashu::types::{MeltQuote, MintQuote}; use cashu::url::UncheckedUrl; use thiserror::Error; @@ -38,4 +38,8 @@ pub trait LocalStore { async fn add_keys(&self, keys: Keys) -> Result<(), Error>; async fn get_keys(&self, id: &Id) -> Result, Error>; async fn remove_keys(&self, id: &Id) -> Result<(), Error>; + + async fn add_proofs(&self, mint_url: UncheckedUrl, proof: Proofs) -> Result<(), Error>; + async fn get_proofs(&self, mint_url: UncheckedUrl) -> Result, Error>; + async fn remove_proofs(&self, mint_url: UncheckedUrl, proofs: Proofs) -> Result<(), Error>; } diff --git a/crates/cashu-sdk/src/wallet.rs b/crates/cashu-sdk/src/wallet.rs index 792aeb26..c18c24c2 100644 --- a/crates/cashu-sdk/src/wallet.rs +++ b/crates/cashu-sdk/src/wallet.rs @@ -53,9 +53,9 @@ pub struct BackupInfo { #[derive(Clone, Debug)] pub struct Wallet { - backup_info: Option, pub client: C, - pub localstore: L, + localstore: L, + backup_info: Option, } impl Wallet { @@ -128,29 +128,32 @@ impl Wallet { }) } - // Mint a token - pub async fn mint_token( - &mut self, - mint_url: UncheckedUrl, - amount: Amount, - memo: Option, - unit: Option, - ) -> Result { - let quote = self - .mint_quote( - mint_url.clone(), - amount, - unit.clone() - .ok_or(Error::Custom("Unit required".to_string()))?, - ) - .await?; + /* + // TODO: This should be create token + // the requited proofs for the token amount may already be in the wallet and mint is not needed + // Mint a token + pub async fn mint_token( + &mut self, + mint_url: UncheckedUrl, + amount: Amount, + memo: Option, + unit: Option, + ) -> Result { + let quote = self + .mint_quote( + mint_url.clone(), + amount, + unit.clone() + .ok_or(Error::Custom("Unit required".to_string()))?, + ) + .await?; - let proofs = self.mint(mint_url.clone(), "e.id).await?; - - let token = Token::new(mint_url.clone(), proofs, memo, unit); - Ok(token?) - } + let proofs = self.mint(mint_url.clone(), "e.id).await?; + let token = Token::new(mint_url.clone(), proofs, memo, unit); + Ok(token?) + } + */ /// Mint Quote pub async fn mint_quote( &mut self, @@ -225,7 +228,7 @@ impl Wallet { } /// Mint - pub async fn mint(&mut self, mint_url: UncheckedUrl, quote_id: &str) -> Result { + pub async fn mint(&mut self, mint_url: UncheckedUrl, quote_id: &str) -> Result { let quote_info = self.localstore.get_mint_quote(quote_id).await?; let quote_info = if let Some(quote) = quote_info { @@ -271,29 +274,28 @@ impl Wallet { &keys, )?; + let minted_amount = proofs.iter().map(|p| p.amount).sum(); + + // Remove filled quote from store self.localstore.remove_mint_quote("e_info.id).await?; - Ok(proofs) + // Add new proofs to store + self.localstore.add_proofs(mint_url, proofs).await?; + + Ok(minted_amount) } /// Receive - pub async fn receive(&mut self, encoded_token: &str) -> Result { + pub async fn receive(&mut self, encoded_token: &str) -> Result<(), Error> { let token_data = Token::from_str(encoded_token)?; let unit = token_data.unit.unwrap_or_default(); - let mut proofs: Vec = vec![vec![]]; + let mut proofs: HashMap = HashMap::new(); for token in token_data.token { if token.proofs.is_empty() { continue; } - /* - let keys = if token.mint.to_string().eq(&self.mint_url.to_string()) { - self.mint_keys.clone() - } else { - self.client.get_mint_keys(token.mint.try_into()?).await? - }; - */ let active_keyset_id = self.active_mint_keyset(&token.mint, &unit).await?; @@ -305,7 +307,7 @@ impl Wallet { let amount: Amount = token.proofs.iter().map(|p| p.amount).sum(); let pre_swap = self - .create_split(&token.mint, &unit, Some(amount), token.proofs) + .create_swap(&token.mint, &unit, Some(amount), token.proofs) .await?; let swap_response = self @@ -320,13 +322,20 @@ impl Wallet { pre_swap.pre_mint_secrets.secrets(), &keys.unwrap(), )?; - proofs.push(p); + let mint_proofs = proofs.entry(token.mint).or_insert(Vec::new()); + + mint_proofs.extend(p); } - Ok(proofs.iter().flatten().cloned().collect()) + + for (mint, proofs) in proofs { + self.localstore.add_proofs(mint, proofs).await?; + } + + Ok(()) } /// Create Split Payload - async fn create_split( + async fn create_swap( &mut self, mint_url: &UncheckedUrl, unit: &CurrencyUnit, @@ -363,7 +372,7 @@ impl Wallet { }) } - pub async fn process_split_response( + pub async fn process_swap_response( &self, blinded_messages: PreMintSecrets, promises: Vec, @@ -412,7 +421,7 @@ impl Wallet { } let pre_swap = self - .create_split(mint_url, unit, Some(amount), proofs) + .create_swap(mint_url, unit, Some(amount), proofs) .await?; let swap_response = self diff --git a/crates/cashu/src/nuts/nut00.rs b/crates/cashu/src/nuts/nut00.rs index 451e7a59..081e1d6b 100644 --- a/crates/cashu/src/nuts/nut00.rs +++ b/crates/cashu/src/nuts/nut00.rs @@ -2,6 +2,7 @@ // https://github.com/cashubtc/nuts/blob/main/00.md use std::fmt; +use std::hash::{Hash, Hasher}; use std::str::FromStr; use serde::{Deserialize, Serialize}; @@ -409,6 +410,12 @@ pub struct Proof { pub c: PublicKey, } +impl Hash for Proof { + fn hash(&self, state: &mut H) { + self.secret.hash(state); + } +} + impl Ord for Proof { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.amount.cmp(&other.amount)