diff --git a/crates/cashu-sdk/src/mint/localstore/memory.rs b/crates/cashu-sdk/src/mint/localstore/memory.rs index ab313a84..33fcf5b6 100644 --- a/crates/cashu-sdk/src/mint/localstore/memory.rs +++ b/crates/cashu-sdk/src/mint/localstore/memory.rs @@ -20,7 +20,7 @@ pub struct MemoryLocalStore { melt_quotes: Arc>>, pending_proofs: Arc, Proof>>>, spent_proofs: Arc, Proof>>>, - blinded_signatures: Arc>>, + blinded_signatures: Arc, BlindedSignature>>>, } impl MemoryLocalStore { @@ -33,7 +33,7 @@ impl MemoryLocalStore { melt_quotes: Vec, pending_proofs: Proofs, spent_proofs: Proofs, - blinded_signatures: HashMap, + blinded_signatures: HashMap, BlindedSignature>, ) -> Result { Ok(Self { mint_info: Arc::new(Mutex::new(mint_info)), @@ -231,9 +231,10 @@ impl LocalStore for MemoryLocalStore { self.blinded_signatures .lock() .await - .insert(blinded_message.to_string(), blinded_signature); + .insert(blinded_message.to_bytes(), blinded_signature); Ok(()) } + async fn get_blinded_signature( &self, blinded_message: &PublicKey, @@ -242,7 +243,24 @@ impl LocalStore for MemoryLocalStore { .blinded_signatures .lock() .await - .get(&blinded_message.to_string()) + .get(&blinded_message.to_bytes()) .cloned()) } + + async fn get_blinded_signatures( + &self, + blinded_messages: Vec, + ) -> Result>, Error> { + let mut signatures = Vec::with_capacity(blinded_messages.len()); + + let blinded_signatures = self.blinded_signatures.lock().await; + + for blinded_message in blinded_messages { + let signature = blinded_signatures.get(&blinded_message.to_bytes()).cloned(); + + signatures.push(signature) + } + + Ok(signatures) + } } diff --git a/crates/cashu-sdk/src/mint/localstore/mod.rs b/crates/cashu-sdk/src/mint/localstore/mod.rs index 396c5c5b..73f07d0e 100644 --- a/crates/cashu-sdk/src/mint/localstore/mod.rs +++ b/crates/cashu-sdk/src/mint/localstore/mod.rs @@ -88,4 +88,8 @@ pub trait LocalStore { &self, blinded_message: &PublicKey, ) -> Result, Error>; + async fn get_blinded_signatures( + &self, + blinded_messages: Vec, + ) -> 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 f2c38d1f..47b00384 100644 --- a/crates/cashu-sdk/src/mint/localstore/redb_store.rs +++ b/crates/cashu-sdk/src/mint/localstore/redb_store.rs @@ -23,7 +23,7 @@ const PENDING_PROOFS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new( const SPENT_PROOFS_TABLE: TableDefinition<&[u8], &str> = TableDefinition::new("spent_proofs"); const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config"); // Key is hex blinded_message B_ value is blinded_signature -const BLINDED_SIGNATURES: TableDefinition<&str, &str> = TableDefinition::new("blinded_signatures"); +const BLINDED_SIGNATURES: TableDefinition<&[u8], &str> = TableDefinition::new("blinded_signatures"); #[derive(Debug, Clone)] pub struct RedbLocalStore { @@ -408,7 +408,7 @@ impl LocalStore for RedbLocalStore { { let mut table = write_txn.open_table(BLINDED_SIGNATURES)?; table.insert( - blinded_message.to_string().as_str(), + blinded_message.to_bytes().as_ref(), serde_json::to_string(&blinded_signature)?.as_str(), )?; } @@ -426,10 +426,29 @@ impl LocalStore for RedbLocalStore { let read_txn = db.begin_read()?; let table = read_txn.open_table(BLINDED_SIGNATURES)?; - if let Some(blinded_signature) = table.get(blinded_message.to_string().as_str())? { + if let Some(blinded_signature) = table.get(blinded_message.to_bytes().as_ref())? { return Ok(serde_json::from_str(blinded_signature.value())?); } Ok(None) } + + async fn get_blinded_signatures( + &self, + blinded_messages: Vec, + ) -> Result>, Error> { + let db = self.db.lock().await; + let read_txn = db.begin_read()?; + let table = read_txn.open_table(BLINDED_SIGNATURES)?; + + let mut signatures = Vec::with_capacity(blinded_messages.len()); + + 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())?)) + } + } + + Ok(signatures) + } } diff --git a/crates/cashu-sdk/src/mint/mod.rs b/crates/cashu-sdk/src/mint/mod.rs index bdd530f6..22688aed 100644 --- a/crates/cashu-sdk/src/mint/mod.rs +++ b/crates/cashu-sdk/src/mint/mod.rs @@ -732,6 +732,35 @@ impl Mint { pub async fn mint_info(&self) -> Result { Ok(self.localstore.get_mint_info().await?) } + + #[cfg(feature = "nut09")] + pub async fn restore(&self, request: RestoreRequest) -> Result { + let output_len = request.outputs.len(); + + 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_signatures = self + .localstore + .get_blinded_signatures(blinded_message) + .await?; + + for (blinded_message, blinded_signature) in + request.outputs.into_iter().zip(blinded_signatures) + { + if let Some(blinded_signature) = blinded_signature { + outputs.push(blinded_message); + signatures.push(blinded_signature); + } + } + + Ok(RestoreResponse { + outputs, + signatures, + }) + } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/crates/cashu/src/nuts/mod.rs b/crates/cashu/src/nuts/mod.rs index 201424d3..aa0582ad 100644 --- a/crates/cashu/src/nuts/mod.rs +++ b/crates/cashu/src/nuts/mod.rs @@ -39,6 +39,8 @@ pub use nut06::{MintInfo, MintVersion, Nuts}; pub use nut07::{CheckStateRequest, CheckStateResponse}; #[cfg(feature = "nut08")] pub use nut08::{MeltBolt11Request, MeltBolt11Response}; +#[cfg(feature = "nut09")] +pub use nut09::{RestoreRequest, RestoreResponse}; #[cfg(feature = "nut10")] pub use nut10::{Kind, Secret as Nut10Secret, SecretData}; #[cfg(feature = "nut11")] diff --git a/crates/cashu/src/nuts/nut01.rs b/crates/cashu/src/nuts/nut01.rs index 0fdf3158..a4c13ab8 100644 --- a/crates/cashu/src/nuts/nut01.rs +++ b/crates/cashu/src/nuts/nut01.rs @@ -79,6 +79,12 @@ impl PublicKey { } } +impl From for Box<[u8]> { + fn from(pubkey: PublicKey) -> Box<[u8]> { + pubkey.to_bytes() + } +} + impl FromStr for PublicKey { type Err = Error; diff --git a/crates/cashu/src/nuts/nut09.rs b/crates/cashu/src/nuts/nut09.rs index 82e78de4..81cb4704 100644 --- a/crates/cashu/src/nuts/nut09.rs +++ b/crates/cashu/src/nuts/nut09.rs @@ -17,6 +17,7 @@ pub struct RestoreResponse { /// Outputs pub outputs: Vec, /// Signatures + // TODO: remove rename just for temp compatanlite with nutshell #[serde(rename = "promises")] pub signatures: Vec, }