diff --git a/crates/cashu-sdk/Cargo.toml b/crates/cashu-sdk/Cargo.toml index 4c5e1028..ced5fbcf 100644 --- a/crates/cashu-sdk/Cargo.toml +++ b/crates/cashu-sdk/Cargo.toml @@ -17,7 +17,7 @@ gloo = ["dep:gloo"] all-nuts = ["nut07", "nut08", "nut09", "nut10", "nut11", "nut13"] nut07 = ["cashu/nut07"] nut08 = ["cashu/nut08"] -nut09 = ["cashu/nut09"] +nut09 = ["cashu/nut07", "cashu/nut09"] nut10 = ["cashu/nut10"] nut11 = ["cashu/nut11"] nut13 = ["cashu/nut13"] diff --git a/crates/cashu-sdk/src/mint/localstore/redb_store.rs b/crates/cashu-sdk/src/mint/localstore/redb_store.rs index 47b00384..9cbd3aab 100644 --- a/crates/cashu-sdk/src/mint/localstore/redb_store.rs +++ b/crates/cashu-sdk/src/mint/localstore/redb_store.rs @@ -288,10 +288,9 @@ impl LocalStore for RedbLocalStore { { let mut table = write_txn.open_table(SPENT_PROOFS_TABLE)?; + let y: PublicKey = hash_to_curve(&proof.secret.to_bytes())?.into(); table.insert( - hash_to_curve(&proof.secret.to_bytes())? - .to_sec1_bytes() - .as_ref(), + y.to_bytes().as_ref(), serde_json::to_string(&proof)?.as_str(), )?; } @@ -320,9 +319,9 @@ impl LocalStore for RedbLocalStore { 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 y: PublicKey = hash_to_curve(&secret.to_bytes())?.into(); - let proof = table.get(secret_hash.to_sec1_bytes().as_ref())?; + let proof = table.get(y.to_bytes().as_ref())?; debug!("Checking secret: {}", secret.to_string()); @@ -446,6 +445,8 @@ impl LocalStore for RedbLocalStore { for blinded_message in blinded_messages { if let Some(blinded_signature) = table.get(blinded_message.to_bytes().as_ref())? { signatures.push(Some(serde_json::from_str(blinded_signature.value())?)) + } else { + signatures.push(None); } } diff --git a/crates/cashu-sdk/src/mint/mod.rs b/crates/cashu-sdk/src/mint/mod.rs index 22688aed..363391d3 100644 --- a/crates/cashu-sdk/src/mint/mod.rs +++ b/crates/cashu-sdk/src/mint/mod.rs @@ -11,6 +11,8 @@ use cashu::nuts::{ }; #[cfg(feature = "nut07")] use cashu::nuts::{CheckStateRequest, CheckStateResponse}; +#[cfg(feature = "nut09")] +use cashu::nuts::{RestoreRequest, RestoreResponse}; use cashu::types::{MeltQuote, MintQuote}; use cashu::Amount; use http::StatusCode; @@ -291,6 +293,7 @@ impl Mint { .await? .is_some() { + error!("Output has already been signed: {}", blinded_message.b); return Err(Error::BlindedMessageAlreadySigned); } } @@ -373,6 +376,7 @@ impl Mint { .await? .is_some() { + error!("Output has already been signed: {}", blinded_message.b); return Err(Error::BlindedMessageAlreadySigned); } } @@ -658,6 +662,7 @@ impl Mint { .await? .is_some() { + error!("Output has already been signed: {}", blinded_message.b); return Err(Error::BlindedMessageAlreadySigned); } } @@ -740,13 +745,15 @@ impl Mint { let mut outputs = Vec::with_capacity(output_len); let mut signatures = Vec::with_capacity(output_len); - let blinded_message = request.outputs.iter().map(|b| b.b.clone()).collect(); + let blinded_message: Vec = request.outputs.iter().map(|b| b.b.clone()).collect(); let blinded_signatures = self .localstore .get_blinded_signatures(blinded_message) .await?; + assert_eq!(blinded_signatures.len(), output_len); + for (blinded_message, blinded_signature) in request.outputs.into_iter().zip(blinded_signatures) { diff --git a/crates/cashu-sdk/src/wallet/localstore/memory.rs b/crates/cashu-sdk/src/wallet/localstore/memory.rs index 21d75ff6..014f8ce6 100644 --- a/crates/cashu-sdk/src/wallet/localstore/memory.rs +++ b/crates/cashu-sdk/src/wallet/localstore/memory.rs @@ -211,8 +211,13 @@ impl LocalStore for MemoryLocalStore { Ok(()) } - async fn add_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error> { - self.keyset_counter.lock().await.insert(*keyset_id, count); + async fn increment_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error> { + let keyset_counter = self.keyset_counter.lock().await; + let current_counter = keyset_counter.get(keyset_id).unwrap_or(&0); + self.keyset_counter + .lock() + .await + .insert(*keyset_id, current_counter + count); Ok(()) } diff --git a/crates/cashu-sdk/src/wallet/localstore/mod.rs b/crates/cashu-sdk/src/wallet/localstore/mod.rs index afe5b95e..a300c53e 100644 --- a/crates/cashu-sdk/src/wallet/localstore/mod.rs +++ b/crates/cashu-sdk/src/wallet/localstore/mod.rs @@ -84,7 +84,7 @@ pub trait LocalStore { ) -> Result<(), Error>; #[cfg(feature = "nut13")] - async fn add_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error>; + async fn increment_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error>; #[cfg(feature = "nut13")] async fn get_keyset_counter(&self, keyset_id: &Id) -> Result, Error>; } diff --git a/crates/cashu-sdk/src/wallet/localstore/redb_store.rs b/crates/cashu-sdk/src/wallet/localstore/redb_store.rs index 73c15413..f417690e 100644 --- a/crates/cashu-sdk/src/wallet/localstore/redb_store.rs +++ b/crates/cashu-sdk/src/wallet/localstore/redb_store.rs @@ -389,15 +389,27 @@ impl LocalStore for RedbLocalStore { } #[cfg(feature = "nut13")] - async fn add_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error> { + async fn increment_keyset_counter(&self, keyset_id: &Id, count: u64) -> Result<(), Error> { let db = self.db.lock().await; - let write_txn = db.begin_write()?; + let current_counter; + { + let read_txn = db.begin_read()?; + let table = read_txn.open_table(KEYSET_COUNTER)?; + let counter = table.get(keyset_id.to_string().as_str())?; + current_counter = if let Some(counter) = counter { + counter.value() + } else { + 0 + }; + } + let write_txn = db.begin_write()?; { let mut table = write_txn.open_table(KEYSET_COUNTER)?; + let new_counter = current_counter + count; - table.insert(keyset_id.to_string().as_str(), count)?; + table.insert(keyset_id.to_string().as_str(), new_counter)?; } write_txn.commit()?; diff --git a/crates/cashu-sdk/src/wallet/mod.rs b/crates/cashu-sdk/src/wallet/mod.rs index 8d7b594d..98549b3f 100644 --- a/crates/cashu-sdk/src/wallet/mod.rs +++ b/crates/cashu-sdk/src/wallet/mod.rs @@ -351,10 +351,9 @@ impl Wallet { self.localstore.remove_mint_quote("e_info.id).await?; // Update counter for keyset - if let Some(counter) = counter { - let count = counter + proofs.len() as u64; + if counter.is_some() { self.localstore - .add_keyset_counter(&active_keyset_id, count) + .increment_keyset_counter(&active_keyset_id, proofs.len() as u64) .await?; } @@ -406,6 +405,13 @@ impl Wallet { pre_swap.pre_mint_secrets.secrets(), &keys, )?; + + if self.mnemonic.is_some() { + self.localstore + .increment_keyset_counter(&active_keyset_id, p.len() as u64) + .await?; + } + let mint_proofs = proofs.entry(token.mint).or_default(); mint_proofs.extend(p); @@ -523,14 +529,8 @@ impl Wallet { if self.mnemonic.is_some() { for (keyset_id, count) in proof_count { - let counter = self - .localstore - .get_keyset_counter(&keyset_id) - .await? - .unwrap_or(0); - self.localstore - .add_keyset_counter(&keyset_id, counter + count) + .increment_keyset_counter(&keyset_id, count) .await?; } } @@ -569,16 +569,8 @@ impl Wallet { let active_keyset = self.active_mint_keyset(mint_url, unit).await?; if self.mnemonic.is_some() { - let count = self - .localstore - .get_keyset_counter(&active_keyset) - .await? - .unwrap_or(0); - - let new_count = count + post_swap_proofs.len() as u64; - self.localstore - .add_keyset_counter(&active_keyset, new_count) + .increment_keyset_counter(&active_keyset, post_swap_proofs.len() as u64) .await?; } @@ -776,11 +768,14 @@ impl Wallet { }; if let Some(change_proofs) = change_proofs { + debug!( + "Change amount returned from melt: {}", + change_proofs.iter().map(|p| p.amount).sum::() + ); // Update counter for keyset - if let Some(counter) = counter { - let count = counter + change_proofs.len() as u64; + if counter.is_some() { self.localstore - .add_keyset_counter(&active_keyset_id, count) + .increment_keyset_counter(&active_keyset_id, change_proofs.len() as u64) .await?; } @@ -951,6 +946,12 @@ impl Wallet { )?; let mint_proofs = received_proofs.entry(token.mint).or_default(); + if self.mnemonic.is_some() { + self.localstore + .increment_keyset_counter(&active_keyset_id, p.len() as u64) + .await?; + } + mint_proofs.extend(p); } @@ -1014,6 +1015,7 @@ impl Wallet { if response.signatures.is_empty() { empty_batch += 1; + start_counter += 100; continue; } @@ -1039,8 +1041,10 @@ impl Wallet { &keys, )?; + debug!("Restored {} proofs", proofs.len()); + self.localstore - .add_keyset_counter(&keyset.id, start_counter + proofs.len() as u64) + .increment_keyset_counter(&keyset.id, proofs.len() as u64) .await?; let states = self diff --git a/crates/cashu/src/dhke.rs b/crates/cashu/src/dhke.rs index 9d2c08bd..fffe3925 100644 --- a/crates/cashu/src/dhke.rs +++ b/crates/cashu/src/dhke.rs @@ -123,6 +123,7 @@ mod mint { use std::ops::Mul; use k256::{Scalar, SecretKey}; + use log::warn; use super::hash_to_curve; use crate::error; @@ -154,6 +155,8 @@ mod mint { return Ok(()); } + warn!("Message not verifed"); + Err(error::mint::Error::TokenNotVerifed) } } diff --git a/crates/cashu/src/nuts/nut13.rs b/crates/cashu/src/nuts/nut13.rs index 7e0a1a3d..9f0da6aa 100644 --- a/crates/cashu/src/nuts/nut13.rs +++ b/crates/cashu/src/nuts/nut13.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use bip32::{DerivationPath, XPrv}; use bip39::Mnemonic; +use log::debug; use super::{Id, SecretKey}; use crate::error::Error; @@ -9,6 +10,11 @@ use crate::secret::Secret; impl Secret { pub fn from_seed(mnemonic: &Mnemonic, keyset_id: Id, counter: u64) -> Result { + debug!( + "Deriving secret for {} with count {}", + keyset_id.to_string(), + counter.to_string() + ); let path = DerivationPath::from_str(&format!( "m/129372'/0'/{}'/{}'/0", u64::try_from(keyset_id)?, @@ -23,6 +29,11 @@ impl Secret { impl SecretKey { pub fn from_seed(mnemonic: &Mnemonic, keyset_id: Id, counter: u64) -> Result { + debug!( + "Deriving key for {} with count {}", + keyset_id.to_string(), + counter.to_string() + ); let path = DerivationPath::from_str(&format!( "m/129372'/0'/{}'/{}'/1", u64::try_from(keyset_id)?, @@ -95,7 +106,7 @@ mod wallet { ) -> Result { let mut pre_mint_secrets = PreMintSecrets::default(); - for i in start_count..end_count { + for i in start_count..=end_count { let secret = Secret::from_seed(mnemonic, keyset_id, i)?; let blinding_factor = SecretKey::from_seed(mnemonic, keyset_id, i)?;