From 7076c4db5fdb67bfdf35a8fb99483250892d490a Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Fri, 22 Mar 2024 15:01:48 +0000 Subject: [PATCH] feat(nut07): check state on y --- crates/cashu-sdk/src/client/gloo_client.rs | 6 ++-- crates/cashu-sdk/src/client/minreq_client.rs | 8 ++--- crates/cashu-sdk/src/client/mod.rs | 6 ++-- .../cashu-sdk/src/mint/localstore/memory.rs | 17 +++------- crates/cashu-sdk/src/mint/localstore/mod.rs | 13 ++------ .../src/mint/localstore/redb_store.rs | 17 +++------- crates/cashu-sdk/src/mint/mod.rs | 31 +++++-------------- crates/cashu-sdk/src/wallet/mod.rs | 12 ++++--- crates/cashu/src/nuts/nut07.rs | 11 ++++--- 9 files changed, 46 insertions(+), 75 deletions(-) diff --git a/crates/cashu-sdk/src/client/gloo_client.rs b/crates/cashu-sdk/src/client/gloo_client.rs index fed63a5a..fc19a5a7 100644 --- a/crates/cashu-sdk/src/client/gloo_client.rs +++ b/crates/cashu-sdk/src/client/gloo_client.rs @@ -6,7 +6,7 @@ use cashu::nuts::{ MintInfo, PreMintSecrets, Proof, SwapRequest, SwapResponse, *, }; #[cfg(feature = "nut07")] -use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse}; +use cashu::nuts::{CheckSpendableRequest, CheckSpendableResponse, PublicKey}; use cashu::{Amount, Bolt11Invoice}; use gloo::net::http::Request; use serde_json::Value; @@ -212,10 +212,10 @@ impl Client for HttpClient { async fn post_check_state( &self, mint_url: Url, - secrets: Vec, + ys: Vec, ) -> Result { let url = join_url(mint_url, &["v1", "check"])?; - let request = CheckSpendableRequest { secrets }; + let request = CheckSpendableRequest { ys }; 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 154e3e85..e4fb858f 100644 --- a/crates/cashu-sdk/src/client/minreq_client.rs +++ b/crates/cashu-sdk/src/client/minreq_client.rs @@ -2,6 +2,8 @@ use async_trait::async_trait; use cashu::error::ErrorResponse; +#[cfg(feature = "nut07")] +use cashu::nuts::PublicKey; use cashu::nuts::{ BlindedMessage, CurrencyUnit, Id, KeySet, KeysResponse, KeysetResponse, MeltBolt11Request, MeltBolt11Response, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request, @@ -10,8 +12,6 @@ use cashu::nuts::{ }; #[cfg(feature = "nut07")] use cashu::nuts::{CheckStateRequest, CheckStateResponse}; -#[cfg(feature = "nut07")] -use cashu::secret::Secret; use cashu::{Amount, Bolt11Invoice}; use serde_json::Value; use tracing::warn; @@ -189,10 +189,10 @@ impl Client for HttpClient { async fn post_check_state( &self, mint_url: Url, - secrets: Vec, + ys: Vec, ) -> Result { let url = join_url(mint_url, &["v1", "checkstate"])?; - let request = CheckStateRequest { secrets }; + let request = CheckStateRequest { ys }; let res = minreq::post(url) .with_json(&request)? diff --git a/crates/cashu-sdk/src/client/mod.rs b/crates/cashu-sdk/src/client/mod.rs index 55031035..3d72293a 100644 --- a/crates/cashu-sdk/src/client/mod.rs +++ b/crates/cashu-sdk/src/client/mod.rs @@ -4,13 +4,13 @@ use async_trait::async_trait; use cashu::error::ErrorResponse; #[cfg(feature = "nut07")] use cashu::nuts::CheckStateResponse; +#[cfg(feature = "nut07")] +use cashu::nuts::PublicKey; use cashu::nuts::{ BlindedMessage, CurrencyUnit, Id, KeySet, KeysetResponse, MeltBolt11Response, MeltQuoteBolt11Response, MintBolt11Response, MintInfo, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse, }; -#[cfg(feature = "nut07")] -use cashu::secret::Secret; use cashu::Amount; use thiserror::Error; use url::Url; @@ -107,7 +107,7 @@ pub trait Client { async fn post_check_state( &self, mint_url: Url, - secrets: Vec, + ys: Vec, ) -> Result; async fn get_mint_info(&self, mint_url: Url) -> Result; diff --git a/crates/cashu-sdk/src/mint/localstore/memory.rs b/crates/cashu-sdk/src/mint/localstore/memory.rs index b4ccd1d8..cf70c574 100644 --- a/crates/cashu-sdk/src/mint/localstore/memory.rs +++ b/crates/cashu-sdk/src/mint/localstore/memory.rs @@ -3,9 +3,8 @@ use std::sync::Arc; use async_trait::async_trait; use cashu::dhke::hash_to_curve; -use cashu::k256; use cashu::nuts::nut02::mint::KeySet; -use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof, Proofs}; +use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof, Proofs, PublicKey}; use cashu::secret::Secret; use cashu::types::{MeltQuote, MintQuote}; use tokio::sync::Mutex; @@ -173,15 +172,12 @@ impl LocalStore for MemoryLocalStore { .cloned()) } - async fn get_spent_proof_by_hash( - &self, - secret: &k256::PublicKey, - ) -> Result, Error> { + async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result, Error> { Ok(self .spent_proofs .lock() .await - .get(&secret.to_sec1_bytes().to_vec()) + .get(&y.to_bytes().to_vec()) .cloned()) } @@ -205,15 +201,12 @@ impl LocalStore for MemoryLocalStore { .cloned()) } - async fn get_pending_proof_by_hash( - &self, - secret: &k256::PublicKey, - ) -> Result, Error> { + async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result, Error> { Ok(self .pending_proofs .lock() .await - .get(&secret.to_sec1_bytes().to_vec()) + .get(&y.to_bytes().to_vec()) .cloned()) } diff --git a/crates/cashu-sdk/src/mint/localstore/mod.rs b/crates/cashu-sdk/src/mint/localstore/mod.rs index 7620fbed..5f9463fa 100644 --- a/crates/cashu-sdk/src/mint/localstore/mod.rs +++ b/crates/cashu-sdk/src/mint/localstore/mod.rs @@ -5,9 +5,8 @@ pub mod redb_store; use std::collections::HashMap; use async_trait::async_trait; -use cashu::k256; use cashu::nuts::nut02::mint::KeySet; -use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof}; +use cashu::nuts::{CurrencyUnit, Id, MintInfo, Proof, PublicKey}; use cashu::secret::Secret; use cashu::types::{MeltQuote, MintQuote}; pub use memory::MemoryLocalStore; @@ -73,16 +72,10 @@ pub trait LocalStore { async fn add_spent_proof(&self, proof: Proof) -> Result<(), Error>; async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result, Error>; - async fn get_spent_proof_by_hash( - &self, - secret: &k256::PublicKey, - ) -> Result, Error>; + async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result, Error>; async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error>; async fn get_pending_proof_by_secret(&self, secret: &Secret) -> Result, Error>; - async fn get_pending_proof_by_hash( - &self, - secret: &k256::PublicKey, - ) -> Result, Error>; + async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result, Error>; async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Error>; } diff --git a/crates/cashu-sdk/src/mint/localstore/redb_store.rs b/crates/cashu-sdk/src/mint/localstore/redb_store.rs index 0646e987..ba8df57d 100644 --- a/crates/cashu-sdk/src/mint/localstore/redb_store.rs +++ b/crates/cashu-sdk/src/mint/localstore/redb_store.rs @@ -4,8 +4,7 @@ use std::sync::Arc; use async_trait::async_trait; use cashu::dhke::hash_to_curve; -use cashu::k256; -use cashu::nuts::{CurrencyUnit, Id, MintInfo, MintKeySet as KeySet, Proof}; +use cashu::nuts::{CurrencyUnit, Id, MintInfo, MintKeySet as KeySet, Proof, PublicKey}; use cashu::secret::Secret; use cashu::types::{MeltQuote, MintQuote}; use redb::{Database, ReadableTable, TableDefinition}; @@ -297,15 +296,12 @@ impl LocalStore for RedbLocalStore { Ok(()) } - async fn get_spent_proof_by_hash( - &self, - secret_point: &k256::PublicKey, - ) -> Result, Error> { + async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result, Error> { let db = self.db.lock().await; let read_txn = db.begin_read()?; let table = read_txn.open_table(SPENT_PROOFS_TABLE)?; - let proof = table.get(secret_point.to_sec1_bytes().as_ref())?; + let proof = table.get(y.to_bytes().as_ref())?; if let Some(proof) = proof { Ok(serde_json::from_str(proof.value())?) @@ -351,15 +347,12 @@ impl LocalStore for RedbLocalStore { Ok(()) } - async fn get_pending_proof_by_hash( - &self, - secret_point: &k256::PublicKey, - ) -> Result, Error> { + async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result, Error> { let db = self.db.lock().await; let read_txn = db.begin_read()?; let table = read_txn.open_table(PENDING_PROOFS_TABLE)?; - let proof = table.get(secret_point.to_sec1_bytes().as_ref())?; + let proof = table.get(y.to_bytes().as_ref())?; if let Some(proof) = proof { Ok(serde_json::from_str(proof.value())?) diff --git a/crates/cashu-sdk/src/mint/mod.rs b/crates/cashu-sdk/src/mint/mod.rs index c73d0a77..e97a29b1 100644 --- a/crates/cashu-sdk/src/mint/mod.rs +++ b/crates/cashu-sdk/src/mint/mod.rs @@ -473,18 +473,13 @@ impl Mint { } } - let y = hash_to_curve(&proof.secret.to_bytes())?; + let y: PublicKey = hash_to_curve(&proof.secret.to_bytes())?.into(); - if self.localstore.get_spent_proof_by_hash(&y).await?.is_some() { + if self.localstore.get_spent_proof_by_y(&y).await?.is_some() { return Err(Error::TokenSpent); } - if self - .localstore - .get_pending_proof_by_hash(&y) - .await? - .is_some() - { + if self.localstore.get_pending_proof_by_y(&y).await?.is_some() { return Err(Error::TokenPending); } @@ -512,29 +507,19 @@ impl Mint { &self, check_state: &CheckStateRequest, ) -> Result { - let mut states = Vec::with_capacity(check_state.secrets.len()); + let mut states = Vec::with_capacity(check_state.ys.len()); - for secret in &check_state.secrets { - let state = if self - .localstore - .get_spent_proof_by_secret(secret) - .await? - .is_some() - { + for y in &check_state.ys { + let state = if self.localstore.get_spent_proof_by_y(y).await?.is_some() { State::Spent - } else if self - .localstore - .get_pending_proof_by_secret(secret) - .await? - .is_some() - { + } else if self.localstore.get_pending_proof_by_y(y).await?.is_some() { State::Pending } else { State::Unspent }; states.push(ProofState { - secret: secret.clone(), + y: y.clone(), state, witness: None, }) diff --git a/crates/cashu-sdk/src/wallet/mod.rs b/crates/cashu-sdk/src/wallet/mod.rs index f550460d..cb179cc3 100644 --- a/crates/cashu-sdk/src/wallet/mod.rs +++ b/crates/cashu-sdk/src/wallet/mod.rs @@ -7,12 +7,12 @@ use cashu::dhke::{construct_proofs, unblind_message}; #[cfg(feature = "nut07")] use cashu::nuts::nut07::ProofState; use cashu::nuts::nut11::SigningKey; +#[cfg(feature = "nut07")] +use cashu::nuts::PublicKey; use cashu::nuts::{ BlindedSignature, CurrencyUnit, Id, KeySetInfo, Keys, MintInfo, P2PKConditions, PreMintSecrets, PreSwap, Proof, Proofs, SigFlag, SwapRequest, Token, }; -#[cfg(feature = "nut07")] -use cashu::secret::Secret; use cashu::types::{MeltQuote, Melted, MintQuote}; use cashu::url::UncheckedUrl; use cashu::{Amount, Bolt11Invoice}; @@ -153,6 +153,8 @@ impl Wallet { mint_url: UncheckedUrl, proofs: Proofs, ) -> Result, Error> { + use cashu::dhke::hash_to_curve; + let spendable = self .client .post_check_state( @@ -160,8 +162,10 @@ impl Wallet { proofs .clone() .into_iter() - .map(|p| p.secret) - .collect::>() + // Find Y for the secret + .flat_map(|p| hash_to_curve(&p.secret.to_bytes())) + .map(|y| y.into()) + .collect::>() .clone(), ) .await?; diff --git a/crates/cashu/src/nuts/nut07.rs b/crates/cashu/src/nuts/nut07.rs index b0794090..889b6db4 100644 --- a/crates/cashu/src/nuts/nut07.rs +++ b/crates/cashu/src/nuts/nut07.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; -use crate::secret::Secret; +use super::PublicKey; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "UPPERCASE")] @@ -16,14 +16,17 @@ pub enum State { /// Check spendabale request [NUT-07] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct CheckStateRequest { - pub secrets: Vec, + /// Y's of the proofs to check + #[serde(rename = "Ys")] + pub ys: Vec, } /// Proof state [NUT-07] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ProofState { - /// Secret of proof - pub secret: Secret, + /// Y of proof + #[serde(rename = "Y")] + pub y: PublicKey, /// State of proof pub state: State, /// Witness data if it is supplied