Simplify MintUrl, use it directly in wallet/client.rs

This commit is contained in:
ok300
2024-10-20 19:33:22 +02:00
parent f6533a08de
commit 98c94e28bb
12 changed files with 52 additions and 122 deletions

View File

@@ -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);

View File

@@ -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)

View File

@@ -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>),

View File

@@ -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,

View File

@@ -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

View File

@@ -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?;
self.localstore.add_keys(keys.keys.clone()).await?;
@@ -34,10 +34,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())
@@ -52,10 +49,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

View File

@@ -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()),

View File

@@ -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?;

View File

@@ -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() {

View File

@@ -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

View File

@@ -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

View File

@@ -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;