diff --git a/crates/cashu-sdk/src/mint/localstore/memory.rs b/crates/cashu-sdk/src/mint/localstore/memory.rs index 371d8748..0ffdff67 100644 --- a/crates/cashu-sdk/src/mint/localstore/memory.rs +++ b/crates/cashu-sdk/src/mint/localstore/memory.rs @@ -2,6 +2,8 @@ use std::collections::HashMap; 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::secret::Secret; @@ -17,8 +19,8 @@ pub struct MemoryLocalStore { keysets: Arc>>, mint_quotes: Arc>>, melt_quotes: Arc>>, - pending_proofs: Arc>>, - spent_proofs: Arc>>, + pending_proofs: Arc, Proof>>>, + spent_proofs: Arc, Proof>>>, } impl MemoryLocalStore { @@ -30,8 +32,8 @@ impl MemoryLocalStore { melt_quotes: Vec, pending_proofs: Proofs, spent_proofs: Proofs, - ) -> Self { - Self { + ) -> Result { + Ok(Self { mint_info: Arc::new(Mutex::new(mint_info)), active_keysets: Arc::new(Mutex::new(active_keysets)), keysets: Arc::new(Mutex::new(keysets.into_iter().map(|k| (k.id, k)).collect())), @@ -44,16 +46,30 @@ impl MemoryLocalStore { pending_proofs: Arc::new(Mutex::new( pending_proofs .into_iter() - .map(|p| (p.secret.clone(), p)) + .map(|p| { + ( + hash_to_curve(&p.secret.to_bytes().unwrap()) + .to_sec1_bytes() + .to_vec(), + p, + ) + }) .collect(), )), spent_proofs: Arc::new(Mutex::new( spent_proofs .into_iter() - .map(|p| (p.secret.clone(), p)) + .map(|p| { + ( + hash_to_curve(&p.secret.to_bytes().unwrap()) + .to_sec1_bytes() + .to_vec(), + p, + ) + }) .collect(), )), - } + }) } } @@ -138,31 +154,73 @@ impl LocalStore for MemoryLocalStore { } async fn add_spent_proof(&self, proof: Proof) -> Result<(), Error> { + let secret_point = hash_to_curve(&proof.secret.to_bytes()?); self.spent_proofs .lock() .await - .insert(proof.secret.clone(), proof); + .insert(secret_point.to_sec1_bytes().to_vec(), proof); Ok(()) } - async fn get_spent_proof(&self, secret: &Secret) -> Result, Error> { - Ok(self.spent_proofs.lock().await.get(secret).cloned()) + async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result, Error> { + Ok(self + .spent_proofs + .lock() + .await + .get(&hash_to_curve(&secret.to_bytes()?).to_sec1_bytes().to_vec()) + .cloned()) + } + + async fn get_spent_proof_by_hash( + &self, + secret: &k256::PublicKey, + ) -> Result, Error> { + Ok(self + .spent_proofs + .lock() + .await + .get(&secret.to_sec1_bytes().to_vec()) + .cloned()) } async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error> { - self.pending_proofs - .lock() - .await - .insert(proof.secret.clone(), proof); + self.pending_proofs.lock().await.insert( + hash_to_curve(&proof.secret.to_bytes()?) + .to_sec1_bytes() + .to_vec(), + proof, + ); Ok(()) } - async fn get_pending_proof(&self, secret: &Secret) -> Result, Error> { - Ok(self.pending_proofs.lock().await.get(secret).cloned()) + async fn get_pending_proof_by_secret(&self, secret: &Secret) -> Result, Error> { + let secret_point = hash_to_curve(&secret.to_bytes()?); + Ok(self + .pending_proofs + .lock() + .await + .get(&secret_point.to_sec1_bytes().to_vec()) + .cloned()) + } + + async fn get_pending_proof_by_hash( + &self, + secret: &k256::PublicKey, + ) -> Result, Error> { + Ok(self + .pending_proofs + .lock() + .await + .get(&secret.to_sec1_bytes().to_vec()) + .cloned()) } async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Error> { - self.pending_proofs.lock().await.remove(secret); + let secret_point = hash_to_curve(&secret.to_bytes()?); + self.pending_proofs + .lock() + .await + .remove(&secret_point.to_sec1_bytes().to_vec()); Ok(()) } } diff --git a/crates/cashu-sdk/src/mint/localstore/mod.rs b/crates/cashu-sdk/src/mint/localstore/mod.rs index 2c406532..7620fbed 100644 --- a/crates/cashu-sdk/src/mint/localstore/mod.rs +++ b/crates/cashu-sdk/src/mint/localstore/mod.rs @@ -5,6 +5,7 @@ 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::secret::Secret; @@ -43,6 +44,8 @@ pub enum Error { Cashu(#[from] cashu::error::Error), #[error("`{0}`")] CashuNut02(#[from] cashu::nuts::nut02::Error), + #[error("`{0}`")] + Secret(#[from] cashu::secret::Error), } #[async_trait] @@ -69,9 +72,17 @@ pub trait LocalStore { async fn get_keysets(&self) -> Result, Error>; async fn add_spent_proof(&self, proof: Proof) -> Result<(), Error>; - async fn get_spent_proof(&self, secret: &Secret) -> 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 add_pending_proof(&self, proof: Proof) -> Result<(), Error>; - async fn get_pending_proof(&self, secret: &Secret) -> 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 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 63fbe15f..69507fb8 100644 --- a/crates/cashu-sdk/src/mint/localstore/redb_store.rs +++ b/crates/cashu-sdk/src/mint/localstore/redb_store.rs @@ -3,6 +3,8 @@ use std::str::FromStr; 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::secret::Secret; use cashu::types::{MeltQuote, MintQuote}; @@ -16,8 +18,8 @@ const ACTIVE_KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new(" const KEYSETS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("keysets"); const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes"); const MELT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("melt_quotes"); -const PENDING_PROOFS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("pending_proofs"); -const SPENT_PROOFS_TABLE: TableDefinition<&str, &str> = TableDefinition::new("spent_proofs"); +const PENDING_PROOFS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("pending_proofs"); +const SPENT_PROOFS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("spent_proofs"); const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config"); #[derive(Debug, Clone)] @@ -275,7 +277,9 @@ impl LocalStore for RedbLocalStore { { let mut table = write_txn.open_table(SPENT_PROOFS_TABLE)?; table.insert( - proof.secret.to_string().as_str(), + hash_to_curve(&proof.secret.to_bytes()?) + .to_sec1_bytes() + .as_ref(), serde_json::to_string(&proof)?.as_str(), )?; } @@ -286,16 +290,31 @@ impl LocalStore for RedbLocalStore { Ok(()) } - async fn get_spent_proof(&self, secret: &Secret) -> Result, Error> { + async fn get_spent_proof_by_hash( + &self, + secret_point: &k256::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 quote = table.get(secret.to_string().as_str())?; + let proof = table.get(secret_point.to_sec1_bytes().as_ref())?; + + Ok(proof.map(|p| serde_json::from_str(p.value()).unwrap())) + } + + async fn get_spent_proof_by_secret(&self, secret: &Secret) -> Result, Error> { + let db = self.db.lock().await; + let read_txn = db.begin_read()?; + let table = read_txn.open_table(SPENT_PROOFS_TABLE)?; + + let secret_hash = hash_to_curve(&secret.to_bytes()?); + + let proof = table.get(secret_hash.to_sec1_bytes().as_ref())?; debug!("Checking secret: {}", secret.to_string()); - Ok(quote.map(|q| serde_json::from_str(q.value()).unwrap())) + Ok(proof.map(|p| serde_json::from_str(p.value()).unwrap())) } async fn add_pending_proof(&self, proof: Proof) -> Result<(), Error> { @@ -306,7 +325,9 @@ impl LocalStore for RedbLocalStore { { let mut table = write_txn.open_table(PENDING_PROOFS_TABLE)?; table.insert( - proof.secret.to_string().as_str(), + hash_to_curve(&proof.secret.to_bytes()?) + .to_sec1_bytes() + .as_ref(), serde_json::to_string(&proof)?.as_str(), )?; } @@ -315,14 +336,29 @@ impl LocalStore for RedbLocalStore { Ok(()) } - async fn get_pending_proof(&self, secret: &Secret) -> Result, Error> { + async fn get_pending_proof_by_hash( + &self, + secret_point: &k256::PublicKey, + ) -> Result, Error> { let db = self.db.lock().await; let read_txn = db.begin_read()?; - let table = read_txn.open_table(MELT_QUOTES_TABLE)?; + let table = read_txn.open_table(PENDING_PROOFS_TABLE)?; - let quote = table.get(secret.to_string().as_str())?; + let proof = table.get(secret_point.to_sec1_bytes().as_ref())?; - Ok(quote.map(|q| serde_json::from_str(q.value()).unwrap())) + Ok(proof.map(|p| serde_json::from_str(p.value()).unwrap())) + } + + async fn get_pending_proof_by_secret(&self, secret: &Secret) -> Result, Error> { + let db = self.db.lock().await; + let read_txn = db.begin_read()?; + let table = read_txn.open_table(PENDING_PROOFS_TABLE)?; + + let secret_hash = hash_to_curve(&secret.to_bytes()?); + + let proof = table.get(secret_hash.to_sec1_bytes().as_ref())?; + + Ok(proof.map(|p| serde_json::from_str(p.value()).unwrap())) } async fn remove_pending_proof(&self, secret: &Secret) -> Result<(), Error> { @@ -332,7 +368,8 @@ impl LocalStore for RedbLocalStore { { let mut table = write_txn.open_table(PENDING_PROOFS_TABLE)?; - table.remove(secret.to_string().as_str())?; + let secret_hash = hash_to_curve(&secret.to_bytes()?); + table.remove(secret_hash.to_sec1_bytes().as_ref())?; } write_txn.commit()?; diff --git a/crates/cashu-sdk/src/mint/mod.rs b/crates/cashu-sdk/src/mint/mod.rs index fc8b862c..bfe4b07d 100644 --- a/crates/cashu-sdk/src/mint/mod.rs +++ b/crates/cashu-sdk/src/mint/mod.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use std::sync::Arc; -use cashu::dhke::{sign_message, verify_message}; +use cashu::dhke::{hash_to_curve, sign_message, verify_message}; use cashu::error::ErrorResponse; #[cfg(feature = "nut07")] use cashu::nuts::nut07::{ProofState, State}; @@ -11,7 +11,6 @@ use cashu::nuts::{ }; #[cfg(feature = "nut07")] use cashu::nuts::{CheckStateRequest, CheckStateResponse}; -use cashu::secret::Secret; use cashu::types::{MeltQuote, MintQuote}; use cashu::Amount; use http::StatusCode; @@ -52,6 +51,8 @@ pub enum Error { Cashu(#[from] cashu::error::mint::Error), #[error("`{0}`")] Localstore(#[from] localstore::Error), + #[error("`{0}`")] + Secret(#[from] cashu::secret::Error), #[error("Unknown quote")] UnknownQuote, #[error("Cannot have multiple units")] @@ -353,10 +354,11 @@ impl Mint { let proof_count = swap_request.inputs.len(); - let secrets: HashSet = swap_request + let secrets: HashSet> = swap_request .inputs .iter() - .map(|p| p.secret.clone()) + .flat_map(|p| p.secret.to_bytes()) + .map(|p| hash_to_curve(&p).to_sec1_bytes().to_vec()) .collect(); // Check that there are no duplicate proofs in request @@ -420,7 +422,7 @@ impl Mint { async fn verify_proof(&self, proof: &Proof) -> Result<(), Error> { if self .localstore - .get_spent_proof(&proof.secret) + .get_spent_proof_by_secret(&proof.secret) .await? .is_some() { @@ -429,7 +431,7 @@ impl Mint { if self .localstore - .get_pending_proof(&proof.secret) + .get_pending_proof_by_secret(&proof.secret) .await? .is_some() { @@ -463,9 +465,19 @@ impl Mint { let mut states = Vec::with_capacity(check_state.secrets.len()); for secret in &check_state.secrets { - let state = if self.localstore.get_spent_proof(secret).await?.is_some() { + let state = if self + .localstore + .get_spent_proof_by_secret(secret) + .await? + .is_some() + { State::Spent - } else if self.localstore.get_pending_proof(secret).await?.is_some() { + } else if self + .localstore + .get_pending_proof_by_secret(secret) + .await? + .is_some() + { State::Pending } else { State::Unspent @@ -545,7 +557,12 @@ impl Mint { return Err(Error::MultipleUnits); } - let secrets: HashSet<&Secret> = melt_request.inputs.iter().map(|p| &p.secret).collect(); + let secrets: HashSet> = melt_request + .inputs + .iter() + .flat_map(|p| p.secret.to_bytes()) + .map(|p| hash_to_curve(&p).to_sec1_bytes().to_vec()) + .collect(); // Ensure proofs are unique and not being double spent if melt_request.inputs.len().ne(&secrets.len()) { diff --git a/crates/cashu/src/lib.rs b/crates/cashu/src/lib.rs index 4997db01..2af5ffbb 100644 --- a/crates/cashu/src/lib.rs +++ b/crates/cashu/src/lib.rs @@ -11,7 +11,6 @@ pub mod utils; pub use amount::Amount; pub use bitcoin::hashes::sha256::Hash as Sha256; -pub use lightning_invoice; pub use lightning_invoice::Bolt11Invoice; - +pub use {k256, lightning_invoice}; pub type Result> = std::result::Result;