Browse Source

feat(nut07): check state on y

thesimplekid 1 year ago
parent
commit
7076c4db5f

+ 3 - 3
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<Secret>,
+        ys: Vec<PublicKey>,
     ) -> Result<CheckStateResponse, Error> {
         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)

+ 4 - 4
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<Secret>,
+        ys: Vec<PublicKey>,
     ) -> Result<CheckStateResponse, Error> {
         let url = join_url(mint_url, &["v1", "checkstate"])?;
-        let request = CheckStateRequest { secrets };
+        let request = CheckStateRequest { ys };
 
         let res = minreq::post(url)
             .with_json(&request)?

+ 3 - 3
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<Secret>,
+        ys: Vec<PublicKey>,
     ) -> Result<CheckStateResponse, Error>;
 
     async fn get_mint_info(&self, mint_url: Url) -> Result<MintInfo, Error>;

+ 5 - 12
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<Option<Proof>, Error> {
+    async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, 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<Option<Proof>, Error> {
+    async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error> {
         Ok(self
             .pending_proofs
             .lock()
             .await
-            .get(&secret.to_sec1_bytes().to_vec())
+            .get(&y.to_bytes().to_vec())
             .cloned())
     }
 

+ 3 - 10
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<Option<Proof>, Error>;
-    async fn get_spent_proof_by_hash(
-        &self,
-        secret: &k256::PublicKey,
-    ) -> Result<Option<Proof>, Error>;
+    async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error>;
 
     async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error>;
     async fn get_pending_proof_by_secret(&self, secret: &Secret) -> Result<Option<Proof>, Error>;
-    async fn get_pending_proof_by_hash(
-        &self,
-        secret: &k256::PublicKey,
-    ) -> Result<Option<Proof>, Error>;
+    async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, Error>;
     async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Error>;
 }

+ 5 - 12
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<Option<Proof>, Error> {
+    async fn get_spent_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, 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<Option<Proof>, Error> {
+    async fn get_pending_proof_by_y(&self, y: &PublicKey) -> Result<Option<Proof>, 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())?)

+ 8 - 23
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<CheckStateResponse, Error> {
-        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,
             })

+ 8 - 4
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<C: Client, L: LocalStore> Wallet<C, L> {
         mint_url: UncheckedUrl,
         proofs: Proofs,
     ) -> Result<Vec<ProofState>, Error> {
+        use cashu::dhke::hash_to_curve;
+
         let spendable = self
             .client
             .post_check_state(
@@ -160,8 +162,10 @@ impl<C: Client, L: LocalStore> Wallet<C, L> {
                 proofs
                     .clone()
                     .into_iter()
-                    .map(|p| p.secret)
-                    .collect::<Vec<Secret>>()
+                    // Find Y for the secret
+                    .flat_map(|p| hash_to_curve(&p.secret.to_bytes()))
+                    .map(|y| y.into())
+                    .collect::<Vec<PublicKey>>()
                     .clone(),
             )
             .await?;

+ 7 - 4
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<Secret>,
+    /// Y's of the proofs to check
+    #[serde(rename = "Ys")]
+    pub ys: Vec<PublicKey>,
 }
 
 /// 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