From f9f88edfd8b623d07195189d6d7e78e89d319280 Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Sat, 6 Jan 2024 14:49:49 +0000 Subject: [PATCH] refactor: nut-07 state --- crates/cashu-sdk/src/client/gloo_client.rs | 10 ++-- crates/cashu-sdk/src/client/minreq_client.rs | 17 +++--- crates/cashu-sdk/src/client/mod.rs | 11 ++-- crates/cashu-sdk/src/mint/mod.rs | 56 ++++++++++++-------- crates/cashu-sdk/src/wallet/mod.rs | 21 +++----- crates/cashu/src/nuts/mod.rs | 2 +- crates/cashu/src/nuts/nut07.rs | 32 ++++++++--- 7 files changed, 85 insertions(+), 64 deletions(-) diff --git a/crates/cashu-sdk/src/client/gloo_client.rs b/crates/cashu-sdk/src/client/gloo_client.rs index 7d29b390..2db6626e 100644 --- a/crates/cashu-sdk/src/client/gloo_client.rs +++ b/crates/cashu-sdk/src/client/gloo_client.rs @@ -209,15 +209,13 @@ impl Client for HttpClient { /// Spendable check [NUT-07] #[cfg(feature = "nut07")] - async fn post_check_spendable( + async fn post_check_state( &self, mint_url: Url, - proofs: Vec, - ) -> Result { + secrets: Vec, + ) -> Result { let url = join_url(mint_url, &["v1", "check"])?; - let request = CheckSpendableRequest { - proofs: proofs.to_owned(), - }; + let request = CheckSpendableRequest { secrets }; let res = Request::post(url.as_str()) .json(&request) diff --git a/crates/cashu-sdk/src/client/minreq_client.rs b/crates/cashu-sdk/src/client/minreq_client.rs index 959e3e45..b2ce0421 100644 --- a/crates/cashu-sdk/src/client/minreq_client.rs +++ b/crates/cashu-sdk/src/client/minreq_client.rs @@ -4,13 +4,14 @@ use std::println; use async_trait::async_trait; use cashu::nuts::{ - nut00, BlindedMessage, CurrencyUnit, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request, + BlindedMessage, CurrencyUnit, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request, MeltBolt11Response, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request, MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse, }; #[cfg(feature = "nut07")] -use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse}; +use cashu::nuts::{CheckStateRequest, CheckStateResponse}; +use cashu::secret::Secret; use cashu::{Amount, Bolt11Invoice}; use serde_json::Value; use tracing::warn; @@ -179,20 +180,20 @@ impl Client for HttpClient { /// Spendable check [NUT-07] #[cfg(feature = "nut07")] - async fn post_check_spendable( + async fn post_check_state( &self, mint_url: Url, - proofs: Vec, - ) -> Result { - let url = join_url(mint_url, &["v1", "check"])?; - let request = CheckSpendableRequest { proofs }; + secrets: Vec, + ) -> Result { + let url = join_url(mint_url, &["v1", "checkstate"])?; + let request = CheckStateRequest { secrets }; let res = minreq::post(url) .with_json(&request)? .send()? .json::()?; - let response: Result = + let response: Result = serde_json::from_value(res.clone()); match response { diff --git a/crates/cashu-sdk/src/client/mod.rs b/crates/cashu-sdk/src/client/mod.rs index 4194a14c..a255857e 100644 --- a/crates/cashu-sdk/src/client/mod.rs +++ b/crates/cashu-sdk/src/client/mod.rs @@ -1,15 +1,14 @@ //! Client to connet to mint use async_trait::async_trait; -#[cfg(feature = "mint")] -use cashu::nuts::nut00; #[cfg(feature = "nut07")] -use cashu::nuts::CheckSpendableResponse; +use cashu::nuts::CheckStateResponse; use cashu::nuts::{ BlindedMessage, CurrencyUnit, KeySet, KeysetResponse, MeltBolt11Response, MeltQuoteBolt11Response, MintBolt11Response, MintInfo, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse, }; +use cashu::secret::Secret; use cashu::{utils, Amount}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -127,11 +126,11 @@ pub trait Client { ) -> Result; #[cfg(feature = "nut07")] - async fn post_check_spendable( + async fn post_check_state( &self, mint_url: Url, - proofs: Vec, - ) -> Result; + secrets: Vec, + ) -> Result; async fn get_mint_info(&self, mint_url: Url) -> Result; } diff --git a/crates/cashu-sdk/src/mint/mod.rs b/crates/cashu-sdk/src/mint/mod.rs index 2d953ce8..8ec077db 100644 --- a/crates/cashu-sdk/src/mint/mod.rs +++ b/crates/cashu-sdk/src/mint/mod.rs @@ -1,12 +1,14 @@ use std::collections::HashSet; use cashu::dhke::{sign_message, verify_message}; +#[cfg(feature = "nut07")] +use cashu::nuts::nut07::State; use cashu::nuts::{ BlindedMessage, BlindedSignature, MeltBolt11Request, MeltBolt11Response, Proof, SwapRequest, SwapResponse, *, }; #[cfg(feature = "nut07")] -use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse}; +use cashu::nuts::{CheckStateRequest, CheckStateResponse}; use cashu::secret::Secret; use cashu::types::{MeltQuote, MintQuote}; use cashu::Amount; @@ -369,29 +371,41 @@ impl Mint { #[cfg(feature = "nut07")] pub async fn check_spendable( &self, - check_spendable: &CheckSpendableRequest, - ) -> Result { - let mut spendable = Vec::with_capacity(check_spendable.proofs.len()); - let mut pending = Vec::with_capacity(check_spendable.proofs.len()); + check_spendable: &CheckStateRequest, + ) -> Result { + use cashu::nuts::nut07::ProofState; - for proof in &check_spendable.proofs { - spendable.push( - self.localstore - .get_spent_proof(&proof.secret) - .await - .unwrap() - .is_none(), - ); - pending.push( - self.localstore - .get_pending_proof(&proof.secret) - .await - .unwrap() - .is_some(), - ); + let mut states = Vec::with_capacity(check_spendable.secrets.len()); + + for secret in &check_spendable.secrets { + let state = if self + .localstore + .get_spent_proof(secret) + .await + .unwrap() + .is_some() + { + State::Spent + } else if self + .localstore + .get_pending_proof(secret) + .await + .unwrap() + .is_some() + { + State::Pending + } else { + State::Unspent + }; + + states.push(ProofState { + secret: secret.clone(), + state, + witness: None, + }) } - Ok(CheckSpendableResponse { spendable, pending }) + Ok(CheckStateResponse { states }) } pub async fn verify_melt_request( diff --git a/crates/cashu-sdk/src/wallet/mod.rs b/crates/cashu-sdk/src/wallet/mod.rs index f027ee52..5e1288a5 100644 --- a/crates/cashu-sdk/src/wallet/mod.rs +++ b/crates/cashu-sdk/src/wallet/mod.rs @@ -5,13 +5,13 @@ use std::str::FromStr; use bip39::Mnemonic; use cashu::dhke::{construct_proofs, unblind_message}; #[cfg(feature = "nut07")] -use cashu::nuts::nut00::mint; +use cashu::nuts::nut07::ProofState; use cashu::nuts::{ BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, PreMintSecrets, PreSwap, Proof, Proofs, SwapRequest, Token, }; #[cfg(feature = "nut07")] -use cashu::types::ProofsStatus; +use cashu::secret::Secret; use cashu::types::{MeltQuote, Melted, MintQuote}; use cashu::url::UncheckedUrl; use cashu::{Amount, Bolt11Invoice}; @@ -125,30 +125,23 @@ impl Wallet { &self, mint_url: UncheckedUrl, proofs: Proofs, - ) -> Result { + ) -> Result, Error> { let spendable = self .client - .post_check_spendable( + .post_check_state( mint_url.try_into()?, proofs .clone() .into_iter() - .map(|p| p.into()) - .collect::() + .map(|p| p.secret) + .collect::>() .clone(), ) .await?; // Separate proofs in spent and unspent based on mint response - let (spendable, spent): (Vec<_>, Vec<_>) = proofs - .iter() - .zip(spendable.spendable.iter()) - .partition(|(_, &b)| b); - Ok(ProofsStatus { - spendable: spendable.into_iter().map(|(s, _)| s).cloned().collect(), - spent: spent.into_iter().map(|(s, _)| s).cloned().collect(), - }) + Ok(spendable.states) } /* diff --git a/crates/cashu/src/nuts/mod.rs b/crates/cashu/src/nuts/mod.rs index b8aa512d..660d8d76 100644 --- a/crates/cashu/src/nuts/mod.rs +++ b/crates/cashu/src/nuts/mod.rs @@ -28,7 +28,7 @@ pub use nut05::{MeltQuoteBolt11Request, MeltQuoteBolt11Response}; pub use nut06::{MintInfo, MintVersion, Nuts}; #[cfg(feature = "wallet")] #[cfg(feature = "nut07")] -pub use nut07::{CheckSpendableRequest, CheckSpendableResponse}; +pub use nut07::{CheckStateRequest, CheckStateResponse}; #[cfg(feature = "nut08")] pub use nut08::{MeltBolt11Request, MeltBolt11Response}; diff --git a/crates/cashu/src/nuts/nut07.rs b/crates/cashu/src/nuts/nut07.rs index 39ddd3ef..b0794090 100644 --- a/crates/cashu/src/nuts/nut07.rs +++ b/crates/cashu/src/nuts/nut07.rs @@ -3,21 +3,37 @@ use serde::{Deserialize, Serialize}; -use super::nut00::mint; +use crate::secret::Secret; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum State { + Spent, + Unspent, + Pending, +} /// Check spendabale request [NUT-07] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct CheckSpendableRequest { - pub proofs: mint::Proofs, +pub struct CheckStateRequest { + pub secrets: Vec, +} + +/// Proof state [NUT-07] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ProofState { + /// Secret of proof + pub secret: Secret, + /// State of proof + pub state: State, + /// Witness data if it is supplied + pub witness: Option, } /// Check Spendable Response [NUT-07] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct CheckSpendableResponse { - /// booleans indicating whether the provided Proof is still spendable. - /// In same order as provided proofs - pub spendable: Vec, - pub pending: Vec, +pub struct CheckStateResponse { + pub states: Vec, } /// Spendable Settings