diff --git a/crates/cashu-sdk/src/client/gloo_client.rs b/crates/cashu-sdk/src/client/gloo_client.rs index 2db6626e5..fed63a5a7 100644 --- a/crates/cashu-sdk/src/client/gloo_client.rs +++ b/crates/cashu-sdk/src/client/gloo_client.rs @@ -182,7 +182,7 @@ impl Client for HttpClient { } /// Split Token [NUT-06] - async fn post_split( + async fn post_swap( &self, mint_url: Url, split_request: SwapRequest, diff --git a/crates/cashu-sdk/src/client/minreq_client.rs b/crates/cashu-sdk/src/client/minreq_client.rs index 7e58fc3e9..71dfb776d 100644 --- a/crates/cashu-sdk/src/client/minreq_client.rs +++ b/crates/cashu-sdk/src/client/minreq_client.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use cashu::nuts::{ - BlindedMessage, CurrencyUnit, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request, + BlindedMessage, CurrencyUnit, Id, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request, MeltBolt11Response, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request, MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse, @@ -33,6 +33,19 @@ impl Client for HttpClient { Ok(keys.keysets) } + /// Get Keyset Keys [NUT-01] + async fn get_mint_keyset(&self, mint_url: Url, keyset_id: Id) -> Result { + let url = join_url(mint_url, &["v1", "keys", &keyset_id.to_string()])?; + println!("{url}"); + let keys = minreq::get(url).send()?.json::()?; + println!("{keys:?}"); + + // let keys: KeysResponse = serde_json::from_value(keys)?; // + // serde_json::from_str(&keys.to_string())?; + println!("{keys:?}"); + Ok(keys.keysets[0].clone()) + } + /// Get Keysets [NUT-02] async fn get_mint_keysets(&self, mint_url: Url) -> Result { let url = join_url(mint_url, &["v1", "keysets"])?; @@ -60,8 +73,6 @@ impl Client for HttpClient { let res = minreq::post(url).with_json(&request)?.send()?; - print!("r: {:?}", res); - let response: Result = serde_json::from_value(res.json()?); @@ -159,7 +170,7 @@ impl Client for HttpClient { } /// Split Token [NUT-06] - async fn post_split( + async fn post_swap( &self, mint_url: Url, split_request: SwapRequest, @@ -169,7 +180,7 @@ impl Client for HttpClient { let res = minreq::post(url).with_json(&split_request)?.send()?; - println!("{:?}", res); + println!("{:?}", res.json::()); let response: Result = serde_json::from_value(res.json::()?.clone()); diff --git a/crates/cashu-sdk/src/client/mod.rs b/crates/cashu-sdk/src/client/mod.rs index 9f59c2945..b5b32bcaf 100644 --- a/crates/cashu-sdk/src/client/mod.rs +++ b/crates/cashu-sdk/src/client/mod.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; #[cfg(feature = "nut07")] use cashu::nuts::CheckStateResponse; use cashu::nuts::{ - BlindedMessage, CurrencyUnit, KeySet, KeysetResponse, MeltBolt11Response, + BlindedMessage, CurrencyUnit, Id, KeySet, KeysetResponse, MeltBolt11Response, MeltQuoteBolt11Response, MintBolt11Response, MintInfo, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse, }; @@ -88,6 +88,8 @@ pub trait Client { async fn get_mint_keysets(&self, mint_url: Url) -> Result; + async fn get_mint_keyset(&self, mint_url: Url, keyset_id: Id) -> Result; + async fn post_mint_quote( &self, mint_url: Url, @@ -120,7 +122,7 @@ pub trait Client { // REVIEW: Should be consistent aboue passing in the Request struct or the // compnatants and making it within the function. Here the struct is passed // in but in check spendable and melt the compants are passed in - async fn post_split( + async fn post_swap( &self, mint_url: Url, split_request: SwapRequest, diff --git a/crates/cashu-sdk/src/wallet/localstore/redb_store.rs b/crates/cashu-sdk/src/wallet/localstore/redb_store.rs index 6e6604cb2..acbdee029 100644 --- a/crates/cashu-sdk/src/wallet/localstore/redb_store.rs +++ b/crates/cashu-sdk/src/wallet/localstore/redb_store.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::str::FromStr; use std::sync::Arc; use async_trait::async_trait; @@ -91,8 +92,8 @@ impl LocalStore for RedbLocalStore { .flatten() .map(|(mint, mint_info)| { ( - serde_json::from_str(mint.value()).unwrap(), - serde_json::from_str(mint_info.value()).unwrap(), + UncheckedUrl::from_str(mint.value()).unwrap(), + serde_json::from_str(mint_info.value()).ok(), ) }) .collect(); diff --git a/crates/cashu-sdk/src/wallet/mod.rs b/crates/cashu-sdk/src/wallet/mod.rs index 5e1288a54..c243f7b31 100644 --- a/crates/cashu-sdk/src/wallet/mod.rs +++ b/crates/cashu-sdk/src/wallet/mod.rs @@ -7,8 +7,8 @@ use cashu::dhke::{construct_proofs, unblind_message}; #[cfg(feature = "nut07")] use cashu::nuts::nut07::ProofState; use cashu::nuts::{ - BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, PreMintSecrets, PreSwap, Proof, Proofs, - SwapRequest, Token, + BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, PreMintSecrets, PreSwap, Proof, + Proofs, SwapRequest, Token, }; #[cfg(feature = "nut07")] use cashu::secret::Secret; @@ -119,6 +119,47 @@ impl Wallet { Ok(self.localstore.get_proofs(mint_url).await?) } + pub async fn add_mint(&self, mint_url: UncheckedUrl) -> Result, Error> { + let mint_info = match self + .client + .get_mint_info(mint_url.clone().try_into()?) + .await + { + Ok(mint_info) => Some(mint_info), + Err(err) => { + warn!("Could not get mint info {}", err); + None + } + }; + + self.localstore + .add_mint(mint_url, mint_info.clone()) + .await?; + + Ok(mint_info) + } + + pub async fn get_mint_keys( + &self, + mint_url: &UncheckedUrl, + keyset_id: Id, + ) -> Result { + let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? { + keys + } else { + let keys = self + .client + .get_mint_keyset(mint_url.try_into()?, keyset_id) + .await?; + + self.localstore.add_keys(keys.keys.clone()).await?; + + keys.keys + }; + + Ok(keys) + } + /// Check if a proof is spent #[cfg(feature = "nut07")] pub async fn check_proofs_spent( @@ -208,12 +249,20 @@ impl Wallet { return Ok(Some(keyset.id)); } } - } else { - let keysets = self.client.get_mint_keysets(mint_url.try_into()?).await?; + } - self.localstore - .add_mint_keysets(mint_url.clone(), keysets.keysets.into_iter().collect()) - .await?; + let keysets = self.client.get_mint_keysets(mint_url.try_into()?).await?; + + self.localstore + .add_mint_keysets( + mint_url.clone(), + keysets.keysets.clone().into_iter().collect(), + ) + .await?; + for keyset in &keysets.keysets { + if keyset.unit.eq(unit) && keyset.active { + return Ok(Some(keyset.id)); + } } Ok(None) @@ -226,19 +275,18 @@ impl Wallet { ) -> Result, Error> { let active_keyset_id = self.active_mint_keyset(mint_url, unit).await?.unwrap(); - let mut keys = None; + let keys; if let Some(k) = self.localstore.get_keys(&active_keyset_id).await? { keys = Some(k.clone()) } else { - let keysets = self.client.get_mint_keys(mint_url.try_into()?).await?; + let keyset = self + .client + .get_mint_keyset(mint_url.try_into()?, active_keyset_id) + .await?; - for keyset in keysets { - if keyset.id.eq(&active_keyset_id) { - keys = Some(keyset.keys.clone()) - } - self.localstore.add_keys(keyset.keys).await?; - } + self.localstore.add_keys(keyset.keys.clone()).await?; + keys = Some(keyset.keys); } Ok(keys) @@ -246,10 +294,15 @@ impl Wallet { /// Mint pub async fn mint(&mut 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?; + } + let quote_info = self.localstore.get_mint_quote(quote_id).await?; let quote_info = if let Some(quote) = quote_info { - if quote.expiry.le(&unix_time()) { + if quote.expiry.le(&unix_time()) && quote.expiry.ne(&0) { return Err(Error::QuoteExpired); } @@ -282,7 +335,7 @@ impl Wallet { ) .await?; - let keys = self.localstore.get_keys(&active_keyset_id).await?.unwrap(); + let keys = self.get_mint_keys(&mint_url, active_keyset_id).await?; let proofs = construct_proofs( mint_res.signatures, @@ -329,7 +382,7 @@ impl Wallet { let swap_response = self .client - .post_split(token.mint.clone().try_into()?, pre_swap.split_request) + .post_swap(token.mint.clone().try_into()?, pre_swap.split_request) .await?; // Proof to keep @@ -437,7 +490,7 @@ impl Wallet { let swap_response = self .client - .post_split(mint_url.clone().try_into()?, pre_swap.split_request) + .post_swap(mint_url.clone().try_into()?, pre_swap.split_request) .await?; let mut keep_proofs = Proofs::new(); diff --git a/crates/cashu/src/nuts/nut01.rs b/crates/cashu/src/nuts/nut01.rs index ec8a2e04f..6774e3e7c 100644 --- a/crates/cashu/src/nuts/nut01.rs +++ b/crates/cashu/src/nuts/nut01.rs @@ -147,7 +147,7 @@ impl Keys { #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct KeysResponse { - #[serde_as(as = "VecSkipError<_>")] + // #[serde_as(as = "VecSkipError<_>")] pub keysets: Vec, }