From f45a0b19b7f0992dc9de2adb216175d39f54aada Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sun, 31 Dec 2023 20:51:48 +0000 Subject: [PATCH] feat: get mint keysets and keys --- crates/cashu-sdk/src/client/gloo_client.rs | 10 +-- crates/cashu-sdk/src/client/minreq_client.rs | 8 +- crates/cashu-sdk/src/client/mod.rs | 4 +- crates/cashu-sdk/src/wallet.rs | 85 ++++++++++++++------ 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/crates/cashu-sdk/src/client/gloo_client.rs b/crates/cashu-sdk/src/client/gloo_client.rs index c0d67578..7d29b390 100644 --- a/crates/cashu-sdk/src/client/gloo_client.rs +++ b/crates/cashu-sdk/src/client/gloo_client.rs @@ -2,8 +2,8 @@ use async_trait::async_trait; use cashu::nuts::{ - BlindedMessage, Keys, MeltBolt11Request, MeltBolt11Response, MintBolt11Request, - MintBolt11Response, MintInfo, PreMintSecrets, Proof, SwapRequest, SwapResponse, *, + BlindedMessage, MeltBolt11Request, MeltBolt11Response, MintBolt11Request, MintBolt11Response, + MintInfo, PreMintSecrets, Proof, SwapRequest, SwapResponse, *, }; #[cfg(feature = "nut07")] use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse}; @@ -21,7 +21,7 @@ pub struct HttpClient {} #[async_trait(?Send)] impl Client for HttpClient { /// Get Mint Keys [NUT-01] - async fn get_mint_keys(&self, mint_url: Url) -> Result { + async fn get_mint_keys(&self, mint_url: Url) -> Result, Error> { let url = join_url(mint_url, &["v1", "keys"])?; let keys = Request::get(url.as_str()) .send() @@ -31,8 +31,8 @@ impl Client for HttpClient { .await .map_err(|err| Error::Gloo(err.to_string()))?; - let keys: Keys = serde_json::from_str(&keys.to_string())?; - Ok(keys) + let keys: KeysResponse = serde_json::from_str(&keys.to_string())?; + Ok(keys.keysets) } /// Get Keysets [NUT-02] diff --git a/crates/cashu-sdk/src/client/minreq_client.rs b/crates/cashu-sdk/src/client/minreq_client.rs index b39fedc7..959e3e45 100644 --- a/crates/cashu-sdk/src/client/minreq_client.rs +++ b/crates/cashu-sdk/src/client/minreq_client.rs @@ -4,7 +4,7 @@ use std::println; use async_trait::async_trait; use cashu::nuts::{ - nut00, BlindedMessage, CurrencyUnit, Keys, KeysResponse, KeysetResponse, MeltBolt11Request, + nut00, BlindedMessage, CurrencyUnit, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request, MeltBolt11Response, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request, MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse, @@ -25,14 +25,12 @@ pub struct HttpClient {} #[async_trait(?Send)] impl Client for HttpClient { /// Get Mint Keys [NUT-01] - async fn get_mint_keys(&self, mint_url: Url) -> Result { + async fn get_mint_keys(&self, mint_url: Url) -> Result, Error> { let url = join_url(mint_url, &["v1", "keys"])?; let keys = minreq::get(url).send()?.json::()?; - println!("{}", keys); - let keys: KeysResponse = serde_json::from_str(&keys.to_string())?; - Ok(keys.keysets[0].keys.clone()) + Ok(keys.keysets) } /// Get Keysets [NUT-02] diff --git a/crates/cashu-sdk/src/client/mod.rs b/crates/cashu-sdk/src/client/mod.rs index 38b3993d..4194a14c 100644 --- a/crates/cashu-sdk/src/client/mod.rs +++ b/crates/cashu-sdk/src/client/mod.rs @@ -6,7 +6,7 @@ use cashu::nuts::nut00; #[cfg(feature = "nut07")] use cashu::nuts::CheckSpendableResponse; use cashu::nuts::{ - BlindedMessage, CurrencyUnit, Keys, KeysetResponse, MeltBolt11Response, + BlindedMessage, CurrencyUnit, KeySet, KeysetResponse, MeltBolt11Response, MeltQuoteBolt11Response, MintBolt11Response, MintInfo, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse, }; @@ -84,7 +84,7 @@ pub struct MintErrorResponse { #[async_trait(?Send)] pub trait Client { - async fn get_mint_keys(&self, mint_url: Url) -> Result; + async fn get_mint_keys(&self, mint_url: Url) -> Result, Error>; async fn get_mint_keysets(&self, mint_url: Url) -> Result; diff --git a/crates/cashu-sdk/src/wallet.rs b/crates/cashu-sdk/src/wallet.rs index 3a25fd3c..23d2e745 100644 --- a/crates/cashu-sdk/src/wallet.rs +++ b/crates/cashu-sdk/src/wallet.rs @@ -52,7 +52,7 @@ pub struct BackupInfo { pub struct Wallet { backup_info: Option, pub client: C, - pub mints: HashMap, + pub mints: HashMap>, pub mint_keysets: HashMap>, pub mint_quotes: HashMap, pub melt_quotes: HashMap, @@ -63,7 +63,7 @@ pub struct Wallet { impl Wallet { pub fn new( client: C, - mints: HashMap, + mints: HashMap>, mint_keysets: HashMap>, mint_quotes: Vec, melt_quotes: Vec, @@ -164,22 +164,50 @@ impl Wallet { Ok(quote) } - fn active_mint_keyset(&self, mint_url: &UncheckedUrl, unit: &CurrencyUnit) -> Option { + async fn active_mint_keyset( + &mut self, + mint_url: &UncheckedUrl, + unit: &CurrencyUnit, + ) -> Result, Error> { if let Some(keysets) = self.mint_keysets.get(mint_url) { for keyset in keysets { if keyset.unit.eq(unit) && keyset.active { - return Some(keyset.id); + return Ok(Some(keyset.id)); } } + } else { + let keysets = self.client.get_mint_keysets(mint_url.try_into()?).await?; + + self.mint_keysets + .insert(mint_url.clone(), keysets.keysets.into_iter().collect()); + } + + Ok(None) + } + + async fn active_keys( + &mut self, + mint_url: &UncheckedUrl, + unit: &CurrencyUnit, + ) -> Result, Error> { + let active_keyset_id = self.active_mint_keyset(mint_url, unit).await?.unwrap(); + + let mut keys = None; + + if let Some(k) = self.mint_keys.get(&active_keyset_id) { + keys = Some(k.clone()) + } else { + let keysets = self.client.get_mint_keys(mint_url.try_into()?).await?; + + for keyset in keysets { + if keyset.id.eq(&active_keyset_id) { + keys = Some(keyset.keys.clone()) + } + self.mint_keys.insert(keyset.id, keyset.keys); + } } - None - } - - fn active_keys(&self, mint_url: &UncheckedUrl, unit: &CurrencyUnit) -> Option { - self.active_mint_keyset(mint_url, unit) - .and_then(|id| self.mint_keys.get(&id)) - .cloned() + Ok(keys) } /// Mint @@ -198,6 +226,7 @@ impl Wallet { let active_keyset_id = self .active_mint_keyset(&mint_url, "e_info.unit) + .await? .unwrap(); let premint_secrets = match &self.backup_info { @@ -234,7 +263,7 @@ impl Wallet { } /// Receive - pub async fn receive(&self, encoded_token: &str) -> Result { + pub async fn receive(&mut self, encoded_token: &str) -> Result { let token_data = Token::from_str(encoded_token)?; let unit = token_data.unit.unwrap_or_default(); @@ -252,16 +281,18 @@ impl Wallet { }; */ - let active_keyset_id = self.active_mint_keyset(&token.mint, &unit); + let active_keyset_id = self.active_mint_keyset(&token.mint, &unit).await?; // TODO: if none fetch keyset for mint - let keys = self.mint_keys.get(&active_keyset_id.unwrap()); + let keys = self.mint_keys.get(&active_keyset_id.unwrap()).cloned(); // Sum amount of all proofs let amount: Amount = token.proofs.iter().map(|p| p.amount).sum(); - let pre_swap = self.create_split(&token.mint, &unit, Some(amount), token.proofs)?; + let pre_swap = self + .create_split(&token.mint, &unit, Some(amount), token.proofs) + .await?; let swap_response = self .client @@ -273,7 +304,7 @@ impl Wallet { swap_response.signatures, pre_swap.pre_mint_secrets.rs(), pre_swap.pre_mint_secrets.secrets(), - keys.unwrap(), + &keys.unwrap(), )?; proofs.push(p); } @@ -281,8 +312,8 @@ impl Wallet { } /// Create Split Payload - fn create_split( - &self, + async fn create_split( + &mut self, mint_url: &UncheckedUrl, unit: &CurrencyUnit, amount: Option, @@ -291,7 +322,7 @@ impl Wallet { // Since split is used to get the needed combination of tokens for a specific // amount first blinded messages are created for the amount - let active_keyset_id = self.active_mint_keyset(mint_url, unit).unwrap(); + let active_keyset_id = self.active_mint_keyset(mint_url, unit).await?.unwrap(); let pre_mint_secrets = if let Some(amount) = amount { let mut desired_messages = PreMintSecrets::random(active_keyset_id, amount)?; @@ -352,7 +383,7 @@ impl Wallet { /// Send pub async fn send( - &self, + &mut self, mint_url: &UncheckedUrl, unit: &CurrencyUnit, amount: Amount, @@ -365,7 +396,9 @@ impl Wallet { return Err(Error::InsufficientFunds); } - let pre_swap = self.create_split(mint_url, unit, Some(amount), proofs)?; + let pre_swap = self + .create_split(mint_url, unit, Some(amount), proofs) + .await?; let swap_response = self .client @@ -379,7 +412,7 @@ impl Wallet { swap_response.signatures, pre_swap.pre_mint_secrets.rs(), pre_swap.pre_mint_secrets.secrets(), - &self.active_keys(mint_url, unit).unwrap(), + &self.active_keys(mint_url, unit).await?.unwrap(), )?; proofs.reverse(); @@ -439,7 +472,7 @@ impl Wallet { /// Melt pub async fn melt( - &self, + &mut self, mint_url: &UncheckedUrl, quote_id: &str, proofs: Proofs, @@ -457,7 +490,9 @@ impl Wallet { }; let blinded = PreMintSecrets::blank( - self.active_mint_keyset(mint_url, "e_info.unit).unwrap(), + self.active_mint_keyset(mint_url, "e_info.unit) + .await? + .unwrap(), quote_info.fee_reserve, )?; @@ -476,7 +511,7 @@ impl Wallet { change, blinded.rs(), blinded.secrets(), - &self.active_keys(mint_url, "e_info.unit).unwrap(), + &self.active_keys(mint_url, "e_info.unit).await?.unwrap(), )?), None => None, };