Browse Source

Merge pull request #410 from ok300/ok300-simplify-minturl-handling

Simplify `MintUrl`, use it directly in `wallet/client.rs`
thesimplekid 4 months ago
parent
commit
103574bf71

+ 1 - 2
crates/cdk-cli/src/sub_commands/mint_info.rs

@@ -6,7 +6,6 @@ use url::Url;
 
 #[derive(Args)]
 pub struct MintInfoSubcommand {
-    /// Cashu Token
     mint_url: MintUrl,
 }
 
@@ -17,7 +16,7 @@ pub async fn mint_info(proxy: Option<Url>, sub_command_args: &MintInfoSubcommand
     };
 
     let info = client
-        .get_mint_info(sub_command_args.mint_url.clone().try_into()?)
+        .get_mint_info(sub_command_args.mint_url.clone())
         .await?;
 
     println!("{:#?}", info);

+ 8 - 33
crates/cdk/src/mint_url.rs

@@ -6,7 +6,7 @@
 use core::fmt;
 use core::str::FromStr;
 
-use serde::{Deserialize, Deserializer, Serialize};
+use serde::{Deserialize, Serialize};
 use thiserror::Error;
 use url::{ParseError, Url};
 
@@ -22,7 +22,7 @@ pub enum Error {
 }
 
 /// MintUrl Url
-#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
 pub struct MintUrl(String);
 
 impl MintUrl {
@@ -60,25 +60,16 @@ impl MintUrl {
         Ok(formatted_url)
     }
 
-    /// Empty mint url
-    pub fn empty() -> Self {
-        Self(String::new())
-    }
-
     /// Join onto url
     pub fn join(&self, path: &str) -> Result<Url, Error> {
-        let url: Url = self.try_into()?;
-        Ok(url.join(path)?)
+        Url::parse(&self.0)
+            .and_then(|url| url.join(path))
+            .map_err(Into::into)
     }
-}
 
-impl<'de> Deserialize<'de> for MintUrl {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        let s = String::deserialize(deserializer)?;
-        MintUrl::from_str(&s).map_err(serde::de::Error::custom)
+    /// Append path elements onto the URL
+    pub fn join_paths(&self, path_elements: &[&str]) -> Result<Url, Error> {
+        self.join(&path_elements.join("/"))
     }
 }
 
@@ -94,22 +85,6 @@ impl FromStr for MintUrl {
     }
 }
 
-impl TryFrom<MintUrl> for Url {
-    type Error = Error;
-
-    fn try_from(mint_url: MintUrl) -> Result<Url, Self::Error> {
-        Ok(Self::parse(&mint_url.0)?)
-    }
-}
-
-impl TryFrom<&MintUrl> for Url {
-    type Error = Error;
-
-    fn try_from(mint_url: &MintUrl) -> Result<Url, Self::Error> {
-        Ok(Self::parse(mint_url.0.as_str())?)
-    }
-}
-
 impl fmt::Display for MintUrl {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{}", self.0)

+ 0 - 6
crates/cdk/src/nuts/nut00/mod.rs

@@ -66,9 +66,6 @@ pub enum Error {
     /// Unsupported token
     #[error("Unsupported payment method")]
     UnsupportedPaymentMethod,
-    /// Invalid Url
-    #[error("Invalid URL")]
-    InvalidUrl,
     /// Serde Json error
     #[error(transparent)]
     SerdeJsonError(#[from] serde_json::Error),
@@ -78,9 +75,6 @@ pub enum Error {
     /// Base64 error
     #[error(transparent)]
     Base64Error(#[from] bitcoin::base64::DecodeError),
-    /// Parse Url Error
-    #[error(transparent)]
-    UrlParseError(#[from] url::ParseError),
     /// Ciborium error
     #[error(transparent)]
     CiboriumError(#[from] ciborium::de::Error<std::io::Error>),

+ 0 - 4
crates/cdk/src/nuts/nut00/token.rs

@@ -9,7 +9,6 @@ use std::str::FromStr;
 use bitcoin::base64::engine::{general_purpose, GeneralPurpose};
 use bitcoin::base64::{alphabet, Engine as _};
 use serde::{Deserialize, Serialize};
-use url::Url;
 
 use super::{Error, Proof, ProofV4, Proofs};
 use crate::mint_url::MintUrl;
@@ -181,9 +180,6 @@ impl TokenV3 {
             return Err(Error::ProofsRequired);
         }
 
-        // Check Url is valid
-        let _: Url = (&mint_url).try_into().map_err(|_| Error::InvalidUrl)?;
-
         Ok(Self {
             token: vec![TokenV3Token::new(mint_url, proofs)],
             memo,

+ 27 - 44
crates/cdk/src/wallet/client.rs

@@ -7,6 +7,7 @@ use url::Url;
 
 use super::Error;
 use crate::error::ErrorResponse;
+use crate::mint_url::MintUrl;
 use crate::nuts::nut15::Mpp;
 use crate::nuts::{
     BlindedMessage, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysResponse,
@@ -17,24 +18,6 @@ use crate::nuts::{
 };
 use crate::{Amount, Bolt11Invoice};
 
-fn join_url(url: Url, paths: &[&str]) -> Result<Url, Error> {
-    let mut url = url;
-    for path in paths {
-        if !url.path().ends_with('/') {
-            url.path_segments_mut()
-                .map_err(|_| Error::UrlPathSegments)?
-                .push(path);
-        } else {
-            url.path_segments_mut()
-                .map_err(|_| Error::UrlPathSegments)?
-                .pop()
-                .push(path);
-        }
-    }
-
-    Ok(url)
-}
-
 /// Http Client
 #[derive(Debug, Clone)]
 pub struct HttpClient {
@@ -87,8 +70,8 @@ impl HttpClient {
 
     /// Get Active Mint Keys [NUT-01]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_keys(&self, mint_url: Url) -> Result<Vec<KeySet>, Error> {
-        let url = join_url(mint_url, &["v1", "keys"])?;
+    pub async fn get_mint_keys(&self, mint_url: MintUrl) -> Result<Vec<KeySet>, Error> {
+        let url = mint_url.join_paths(&["v1", "keys"])?;
         let keys = self.inner.get(url).send().await?.json::<Value>().await?;
 
         match serde_json::from_value::<KeysResponse>(keys.clone()) {
@@ -99,8 +82,8 @@ impl HttpClient {
 
     /// Get Keyset Keys [NUT-01]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_keyset(&self, mint_url: Url, keyset_id: Id) -> Result<KeySet, Error> {
-        let url = join_url(mint_url, &["v1", "keys", &keyset_id.to_string()])?;
+    pub async fn get_mint_keyset(&self, mint_url: MintUrl, keyset_id: Id) -> Result<KeySet, Error> {
+        let url = mint_url.join_paths(&["v1", "keys", &keyset_id.to_string()])?;
         let keys = self.inner.get(url).send().await?.json::<Value>().await?;
 
         match serde_json::from_value::<KeysResponse>(keys.clone()) {
@@ -111,8 +94,8 @@ impl HttpClient {
 
     /// Get Keysets [NUT-02]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_keysets(&self, mint_url: Url) -> Result<KeysetResponse, Error> {
-        let url = join_url(mint_url, &["v1", "keysets"])?;
+    pub async fn get_mint_keysets(&self, mint_url: MintUrl) -> Result<KeysetResponse, Error> {
+        let url = mint_url.join_paths(&["v1", "keysets"])?;
         let res = self.inner.get(url).send().await?.json::<Value>().await?;
 
         match serde_json::from_value::<KeysetResponse>(res.clone()) {
@@ -125,12 +108,12 @@ impl HttpClient {
     #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn post_mint_quote(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         amount: Amount,
         unit: CurrencyUnit,
         description: Option<String>,
     ) -> Result<MintQuoteBolt11Response, Error> {
-        let url = join_url(mint_url, &["v1", "mint", "quote", "bolt11"])?;
+        let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11"])?;
 
         let request = MintQuoteBolt11Request {
             amount,
@@ -160,10 +143,10 @@ impl HttpClient {
     #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_mint_quote_status(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         quote_id: &str,
     ) -> Result<MintQuoteBolt11Response, Error> {
-        let url = join_url(mint_url, &["v1", "mint", "quote", "bolt11", quote_id])?;
+        let url = mint_url.join_paths(&["v1", "mint", "quote", "bolt11", quote_id])?;
 
         let res = self.inner.get(url).send().await?.json::<Value>().await?;
 
@@ -180,11 +163,11 @@ impl HttpClient {
     #[instrument(skip(self, quote, premint_secrets), fields(mint_url = %mint_url))]
     pub async fn post_mint(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         quote: &str,
         premint_secrets: PreMintSecrets,
     ) -> Result<MintBolt11Response, Error> {
-        let url = join_url(mint_url, &["v1", "mint", "bolt11"])?;
+        let url = mint_url.join_paths(&["v1", "mint", "bolt11"])?;
 
         let request = MintBolt11Request {
             quote: quote.to_string(),
@@ -210,12 +193,12 @@ impl HttpClient {
     #[instrument(skip(self, request), fields(mint_url = %mint_url))]
     pub async fn post_melt_quote(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         unit: CurrencyUnit,
         request: Bolt11Invoice,
         mpp_amount: Option<Amount>,
     ) -> Result<MeltQuoteBolt11Response, Error> {
-        let url = join_url(mint_url, &["v1", "melt", "quote", "bolt11"])?;
+        let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11"])?;
 
         let options = mpp_amount.map(|amount| Mpp { amount });
 
@@ -244,10 +227,10 @@ impl HttpClient {
     #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn get_melt_quote_status(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         quote_id: &str,
     ) -> Result<MeltQuoteBolt11Response, Error> {
-        let url = join_url(mint_url, &["v1", "melt", "quote", "bolt11", quote_id])?;
+        let url = mint_url.join_paths(&["v1", "melt", "quote", "bolt11", quote_id])?;
 
         let res = self.inner.get(url).send().await?.json::<Value>().await?;
 
@@ -262,12 +245,12 @@ impl HttpClient {
     #[instrument(skip(self, quote, inputs, outputs), fields(mint_url = %mint_url))]
     pub async fn post_melt(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         quote: String,
         inputs: Vec<Proof>,
         outputs: Option<Vec<BlindedMessage>>,
     ) -> Result<MeltQuoteBolt11Response, Error> {
-        let url = join_url(mint_url, &["v1", "melt", "bolt11"])?;
+        let url = mint_url.join_paths(&["v1", "melt", "bolt11"])?;
 
         let request = MeltBolt11Request {
             quote,
@@ -299,10 +282,10 @@ impl HttpClient {
     #[instrument(skip(self, swap_request), fields(mint_url = %mint_url))]
     pub async fn post_swap(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         swap_request: SwapRequest,
     ) -> Result<SwapResponse, Error> {
-        let url = join_url(mint_url, &["v1", "swap"])?;
+        let url = mint_url.join_paths(&["v1", "swap"])?;
 
         let res = self
             .inner
@@ -321,8 +304,8 @@ impl HttpClient {
 
     /// Get Mint Info [NUT-06]
     #[instrument(skip(self), fields(mint_url = %mint_url))]
-    pub async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error> {
-        let url = join_url(mint_url, &["v1", "info"])?;
+    pub async fn get_mint_info(&self, mint_url: MintUrl) -> Result<MintInfo, Error> {
+        let url = mint_url.join_paths(&["v1", "info"])?;
 
         let res = self.inner.get(url).send().await?.json::<Value>().await?;
 
@@ -339,10 +322,10 @@ impl HttpClient {
     #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn post_check_state(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         ys: Vec<PublicKey>,
     ) -> Result<CheckStateResponse, Error> {
-        let url = join_url(mint_url, &["v1", "checkstate"])?;
+        let url = mint_url.join_paths(&["v1", "checkstate"])?;
         let request = CheckStateRequest { ys };
 
         let res = self
@@ -364,10 +347,10 @@ impl HttpClient {
     #[instrument(skip(self, request), fields(mint_url = %mint_url))]
     pub async fn post_restore(
         &self,
-        mint_url: Url,
+        mint_url: MintUrl,
         request: RestoreRequest,
     ) -> Result<RestoreResponse, Error> {
-        let url = join_url(mint_url, &["v1", "restore"])?;
+        let url = mint_url.join_paths(&["v1", "restore"])?;
 
         let res = self
             .inner

+ 3 - 9
crates/cdk/src/wallet/keysets.rs

@@ -18,7 +18,7 @@ impl Wallet {
         } else {
             let keys = self
                 .client
-                .get_mint_keyset(self.mint_url.clone().try_into()?, keyset_id)
+                .get_mint_keyset(self.mint_url.clone(), keyset_id)
                 .await?;
 
             keys.verify_id()?;
@@ -36,10 +36,7 @@ impl Wallet {
     /// Queries mint for all keysets
     #[instrument(skip(self))]
     pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
-        let keysets = self
-            .client
-            .get_mint_keysets(self.mint_url.clone().try_into()?)
-            .await?;
+        let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?;
 
         self.localstore
             .add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone())
@@ -54,10 +51,7 @@ impl Wallet {
     /// keysets
     #[instrument(skip(self))]
     pub async fn get_active_mint_keyset(&self) -> Result<KeySetInfo, Error> {
-        let keysets = self
-            .client
-            .get_mint_keysets(self.mint_url.clone().try_into()?)
-            .await?;
+        let keysets = self.client.get_mint_keysets(self.mint_url.clone()).await?;
         let keysets = keysets.keysets;
 
         self.localstore

+ 3 - 3
crates/cdk/src/wallet/melt.rs

@@ -59,7 +59,7 @@ impl Wallet {
 
         let quote_res = self
             .client
-            .post_melt_quote(self.mint_url.clone().try_into()?, self.unit, invoice, mpp)
+            .post_melt_quote(self.mint_url.clone(), self.unit, invoice, mpp)
             .await?;
 
         if quote_res.amount != amount {
@@ -90,7 +90,7 @@ impl Wallet {
     ) -> Result<MeltQuoteBolt11Response, Error> {
         let response = self
             .client
-            .get_melt_quote_status(self.mint_url.clone().try_into()?, quote_id)
+            .get_melt_quote_status(self.mint_url.clone(), quote_id)
             .await?;
 
         match self.localstore.get_melt_quote(quote_id).await? {
@@ -149,7 +149,7 @@ impl Wallet {
         let melt_response = self
             .client
             .post_melt(
-                self.mint_url.clone().try_into()?,
+                self.mint_url.clone(),
                 quote_id.to_string(),
                 proofs.clone(),
                 Some(premint_secrets.blinded_messages()),

+ 3 - 7
crates/cdk/src/wallet/mint.rs

@@ -67,7 +67,7 @@ impl Wallet {
 
         let quote_res = self
             .client
-            .post_mint_quote(mint_url.clone().try_into()?, amount, unit, description)
+            .post_mint_quote(mint_url.clone(), amount, unit, description)
             .await?;
 
         let quote = MintQuote {
@@ -90,7 +90,7 @@ impl Wallet {
     pub async fn mint_quote_state(&self, quote_id: &str) -> Result<MintQuoteBolt11Response, Error> {
         let response = self
             .client
-            .get_mint_quote_status(self.mint_url.clone().try_into()?, quote_id)
+            .get_mint_quote_status(self.mint_url.clone(), quote_id)
             .await?;
 
         match self.localstore.get_mint_quote(quote_id).await? {
@@ -215,11 +215,7 @@ impl Wallet {
 
         let mint_res = self
             .client
-            .post_mint(
-                self.mint_url.clone().try_into()?,
-                quote_id,
-                premint_secrets.clone(),
-            )
+            .post_mint(self.mint_url.clone(), quote_id, premint_secrets.clone())
             .await?;
 
         let keys = self.get_keyset_keys(active_keyset_id).await?;

+ 2 - 6
crates/cdk/src/wallet/mod.rs

@@ -161,11 +161,7 @@ impl Wallet {
     /// Qeury mint for current mint information
     #[instrument(skip(self))]
     pub async fn get_mint_info(&self) -> Result<Option<MintInfo>, Error> {
-        let mint_info = match self
-            .client
-            .get_mint_info(self.mint_url.clone().try_into()?)
-            .await
-        {
+        let mint_info = match self.client.get_mint_info(self.mint_url.clone()).await {
             Ok(mint_info) => Some(mint_info),
             Err(err) => {
                 tracing::warn!("Could not get mint info {}", err);
@@ -281,7 +277,7 @@ impl Wallet {
 
                 let response = self
                     .client
-                    .post_restore(self.mint_url.clone().try_into()?, restore_request)
+                    .post_restore(self.mint_url.clone(), restore_request)
                     .await?;
 
                 if response.signatures.is_empty() {

+ 2 - 2
crates/cdk/src/wallet/proofs.rs

@@ -77,7 +77,7 @@ impl Wallet {
 
         let spendable = self
             .client
-            .post_check_state(self.mint_url.clone().try_into()?, proof_ys)
+            .post_check_state(self.mint_url.clone(), proof_ys)
             .await?
             .states;
 
@@ -98,7 +98,7 @@ impl Wallet {
     pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> {
         let spendable = self
             .client
-            .post_check_state(self.mint_url.clone().try_into()?, proofs.ys()?)
+            .post_check_state(self.mint_url.clone(), proofs.ys()?)
             .await?;
         let spent_ys: Vec<_> = spendable
             .states

+ 2 - 5
crates/cdk/src/wallet/receive.rs

@@ -34,10 +34,7 @@ impl Wallet {
             .await?
             .is_none()
         {
-            tracing::debug!(
-                "Mint not in localstore fetching info for: {}",
-                self.mint_url
-            );
+            tracing::debug!("Mint not in localstore fetching info for: {mint_url}");
             self.get_mint_info().await?;
         }
 
@@ -134,7 +131,7 @@ impl Wallet {
 
         let swap_response = self
             .client
-            .post_swap(mint_url.clone().try_into()?, pre_swap.swap_request)
+            .post_swap(mint_url.clone(), pre_swap.swap_request)
             .await?;
 
         // Proof to keep

+ 1 - 1
crates/cdk/src/wallet/swap.rs

@@ -42,7 +42,7 @@ impl Wallet {
 
         let swap_response = self
             .client
-            .post_swap(mint_url.clone().try_into()?, pre_swap.swap_request)
+            .post_swap(mint_url.clone(), pre_swap.swap_request)
             .await?;
 
         let active_keyset_id = pre_swap.pre_mint_secrets.keyset_id;