mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-05 05:06:14 +01:00
cashu add UncheckedUrl type
This is needed to track if the token mint url has a traling "/". This is needed to round trip serialize a token. Though it would not actually effect being able to redeam the token or now.
This commit is contained in:
@@ -56,3 +56,11 @@ impl From<cashu::nuts::nut02::Error> for CashuError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cashu::url::Error> for CashuError {
|
||||
fn from(err: cashu::url::Error) -> Self {
|
||||
Self::Generic {
|
||||
err: err.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use cashu::nuts::nut00::MintProofs as MintProofsSdk;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cashu::nuts::nut00::MintProofs as MintProofsSdk;
|
||||
use cashu::url::UncheckedUrl;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::Proof;
|
||||
|
||||
@@ -19,7 +21,7 @@ impl Deref for MintProofs {
|
||||
|
||||
impl MintProofs {
|
||||
pub fn new(mint: String, proofs: Vec<Arc<Proof>>) -> Result<Self> {
|
||||
let mint = url::Url::from_str(&mint)?;
|
||||
let mint = UncheckedUrl::from_str(&mint)?;
|
||||
let proofs = proofs.iter().map(|p| p.as_ref().deref().clone()).collect();
|
||||
|
||||
Ok(Self {
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cashu::nuts::nut00::wallet::Token as TokenSdk;
|
||||
use cashu::url::UncheckedUrl;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::MintProofs;
|
||||
@@ -13,7 +14,7 @@ pub struct Token {
|
||||
|
||||
impl Token {
|
||||
pub fn new(mint: String, proofs: Vec<Arc<Proof>>, memo: Option<String>) -> Result<Self> {
|
||||
let mint = url::Url::from_str(&mint)?;
|
||||
let mint = UncheckedUrl::from_str(&mint)?;
|
||||
let proofs = proofs.into_iter().map(|p| p.as_ref().into()).collect();
|
||||
Ok(Self {
|
||||
inner: TokenSdk::new(mint, proofs, memo)?,
|
||||
|
||||
@@ -134,7 +134,7 @@ impl Wallet {
|
||||
pub fn mint_token(&self, amount: Amount, hash: &str) -> Result<Token, Error> {
|
||||
let proofs = self.mint(amount, hash)?;
|
||||
|
||||
let token = Token::new(self.client.client.mint_url.clone(), proofs, None);
|
||||
let token = Token::new(self.client.client.mint_url.clone().into(), proofs, None);
|
||||
Ok(token?)
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ impl Wallet {
|
||||
{
|
||||
self.mint_keys.clone()
|
||||
} else {
|
||||
Client::new(token.mint.as_str())?.get_keys()?
|
||||
Client::new(&token.mint.to_string())?.get_keys()?
|
||||
};
|
||||
|
||||
// Sum amount of all proofs
|
||||
@@ -520,7 +520,10 @@ impl Wallet {
|
||||
|
||||
#[cfg(feature = "blocking")]
|
||||
pub fn proofs_to_token(&self, proofs: Proofs, memo: Option<String>) -> Result<String, Error> {
|
||||
Ok(Token::new(self.client.client.mint_url.clone(), proofs, memo)?.convert_to_string()?)
|
||||
Ok(
|
||||
Token::new(self.client.client.mint_url.clone().into(), proofs, memo)?
|
||||
.convert_to_string()?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,8 @@ pub mod wallet {
|
||||
UnsupportedToken,
|
||||
/// Token Requires proofs
|
||||
ProofsRequired,
|
||||
/// Url Parse error
|
||||
UrlParse,
|
||||
/// Custom Error message
|
||||
CustomError(String),
|
||||
}
|
||||
@@ -118,6 +120,7 @@ pub mod wallet {
|
||||
Error::UnsupportedToken => write!(f, "Unsuppported Token"),
|
||||
Error::EllipticError(err) => write!(f, "{}", err),
|
||||
Error::SerdeJsonError(err) => write!(f, "{}", err),
|
||||
Error::UrlParse => write!(f, "Could not parse url"),
|
||||
Error::ProofsRequired => write!(f, "Token must have at least one proof",),
|
||||
}
|
||||
}
|
||||
@@ -146,6 +149,12 @@ pub mod wallet {
|
||||
Error::Base64Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::url::Error> for Error {
|
||||
fn from(_err: crate::url::Error) -> Error {
|
||||
Error::UrlParse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
|
||||
@@ -6,6 +6,7 @@ pub mod nuts;
|
||||
pub mod secret;
|
||||
pub mod serde_utils;
|
||||
pub mod types;
|
||||
pub mod url;
|
||||
pub mod utils;
|
||||
|
||||
pub use amount::Amount;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
//! Notation and Models
|
||||
// https://github.com/cashubtc/nuts/blob/main/00.md
|
||||
|
||||
use url::Url;
|
||||
|
||||
use crate::Amount;
|
||||
use crate::{secret::Secret, serde_utils::serde_url};
|
||||
use crate::{secret::Secret, url::UncheckedUrl, Amount};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::nut01::PublicKey;
|
||||
@@ -34,6 +31,7 @@ pub mod wallet {
|
||||
use crate::nuts::nut00::Proofs;
|
||||
use crate::nuts::nut01;
|
||||
use crate::secret::Secret;
|
||||
use crate::url::UncheckedUrl;
|
||||
use crate::Amount;
|
||||
use crate::{dhke::blind_message, utils::split_amount};
|
||||
|
||||
@@ -111,7 +109,7 @@ pub mod wallet {
|
||||
|
||||
impl Token {
|
||||
pub fn new(
|
||||
mint_url: Url,
|
||||
mint_url: UncheckedUrl,
|
||||
proofs: Proofs,
|
||||
memo: Option<String>,
|
||||
) -> Result<Self, wallet::Error> {
|
||||
@@ -119,8 +117,11 @@ pub mod wallet {
|
||||
return Err(wallet::Error::ProofsRequired);
|
||||
}
|
||||
|
||||
// Check Url is valid
|
||||
let _: Url = (&mint_url).try_into()?;
|
||||
|
||||
Ok(Self {
|
||||
token: vec![MintProofs::new(mint_url, proofs)],
|
||||
token: vec![MintProofs::new(mint_url.into(), proofs)],
|
||||
memo,
|
||||
})
|
||||
}
|
||||
@@ -165,14 +166,13 @@ pub mod wallet {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct MintProofs {
|
||||
#[serde(with = "serde_url")]
|
||||
pub mint: Url,
|
||||
pub mint: UncheckedUrl,
|
||||
pub proofs: Proofs,
|
||||
}
|
||||
|
||||
#[cfg(feature = "wallet")]
|
||||
impl MintProofs {
|
||||
fn new(mint_url: Url, proofs: Proofs) -> Self {
|
||||
fn new(mint_url: UncheckedUrl, proofs: Proofs) -> Self {
|
||||
Self {
|
||||
mint: mint_url,
|
||||
proofs,
|
||||
@@ -250,7 +250,6 @@ pub mod mint {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
use super::wallet::*;
|
||||
use super::*;
|
||||
@@ -268,12 +267,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_token_str_round_trip() {
|
||||
let token_str = "cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJpZCI6IkRTQWw5bnZ2eWZ2YSIsImFtb3VudCI6Miwic2VjcmV0IjoiRWhwZW5uQzlxQjNpRmxXOEZaX3BadyIsIkMiOiIwMmMwMjAwNjdkYjcyN2Q1ODZiYzMxODNhZWNmOTdmY2I4MDBjM2Y0Y2M0NzU5ZjY5YzYyNmM5ZGI1ZDhmNWI1ZDQifSx7ImlkIjoiRFNBbDludnZ5ZnZhIiwiYW1vdW50Ijo4LCJzZWNyZXQiOiJUbVM2Q3YwWVQ1UFVfNUFUVktudWt3IiwiQyI6IjAyYWM5MTBiZWYyOGNiZTVkNzMyNTQxNWQ1YzI2MzAyNmYxNWY5Yjk2N2EwNzljYTk3NzlhYjZlNWMyZGIxMzNhNyJ9XX1dLCJtZW1vIjoiVGhhbmt5b3UuIn0=";
|
||||
let token_str = "cashuAeyJ0b2tlbiI6W3sibWludCI6Imh0dHBzOi8vODMzMy5zcGFjZTozMzM4IiwicHJvb2ZzIjpbeyJpZCI6IkRTQWw5bnZ2eWZ2YSIsImFtb3VudCI6Miwic2VjcmV0IjoiRWhwZW5uQzlxQjNpRmxXOEZaX3BadyIsIkMiOiIwMmMwMjAwNjdkYjcyN2Q1ODZiYzMxODNhZWNmOTdmY2I4MDBjM2Y0Y2M0NzU5ZjY5YzYyNmM5ZGI1ZDhmNWI1ZDQifSx7ImlkIjoiRFNBbDludnZ5ZnZhIiwiYW1vdW50Ijo4LCJzZWNyZXQiOiJUbVM2Q3YwWVQ1UFVfNUFUVktudWt3IiwiQyI6IjAyYWM5MTBiZWYyOGNiZTVkNzMyNTQxNWQ1YzI2MzAyNmYxNWY5Yjk2N2EwNzljYTk3NzlhYjZlNWMyZGIxMzNhNyJ9XX1dLCJtZW1vIjoiVGhhbmsgeW91LiJ9";
|
||||
|
||||
let token = Token::from_str(token_str).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
token.token[0].mint,
|
||||
Url::from_str("https://8333.space:3338").unwrap()
|
||||
UncheckedUrl::from_str("https://8333.space:3338")
|
||||
.unwrap()
|
||||
.into()
|
||||
);
|
||||
assert_eq!(
|
||||
token.token[0].proofs[0].clone().id.unwrap(),
|
||||
|
||||
@@ -78,7 +78,6 @@ impl SecretKey {
|
||||
}
|
||||
|
||||
/// Mint Keys [NUT-01]
|
||||
// TODO: CHange this to Amount type
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct Keys(BTreeMap<Amount, PublicKey>);
|
||||
|
||||
|
||||
113
crates/cashu/src/url.rs
Normal file
113
crates/cashu/src/url.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2022-2023 Yuki Kishimoto
|
||||
// Distributed under the MIT software license
|
||||
|
||||
//! Url
|
||||
|
||||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::{ParseError, Url};
|
||||
|
||||
/// Url Error
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// Url error
|
||||
Url(ParseError),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Url(e) => write!(f, "Url: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for Error {
|
||||
fn from(e: ParseError) -> Self {
|
||||
Self::Url(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Unchecked Url
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
pub struct UncheckedUrl(String);
|
||||
|
||||
impl UncheckedUrl {
|
||||
/// New unchecked url
|
||||
pub fn new<S>(url: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self(url.into())
|
||||
}
|
||||
|
||||
/// Empty unchecked url
|
||||
pub fn empty() -> Self {
|
||||
Self(String::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> From<S> for UncheckedUrl
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
fn from(url: S) -> Self {
|
||||
Self(url.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UncheckedUrl {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(url: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self::from(url))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<UncheckedUrl> for Url {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(unchecked_url: UncheckedUrl) -> Result<Url, Self::Error> {
|
||||
Ok(Self::parse(&unchecked_url.0)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&UncheckedUrl> for Url {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(unchecked_url: &UncheckedUrl) -> Result<Url, Self::Error> {
|
||||
Ok(Self::parse(unchecked_url.0.as_str())?)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for UncheckedUrl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_unchecked_relay_url() {
|
||||
let relay = "wss://relay.damus.io/";
|
||||
let relay_url = Url::from_str(relay).unwrap();
|
||||
|
||||
let unchecked_relay_url = UncheckedUrl::from(relay_url.clone());
|
||||
|
||||
assert_eq!(unchecked_relay_url, UncheckedUrl::from(relay));
|
||||
|
||||
assert_eq!(
|
||||
Url::try_from(unchecked_relay_url.clone()).unwrap(),
|
||||
relay_url
|
||||
);
|
||||
|
||||
assert_eq!(relay, unchecked_relay_url.to_string());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user