소스 검색

feat(mint): restore function

thesimplekid 1 년 전
부모
커밋
65b0f66729

+ 22 - 4
crates/cashu-sdk/src/mint/localstore/memory.rs

@@ -20,7 +20,7 @@ pub struct MemoryLocalStore {
     melt_quotes: Arc<Mutex<HashMap<String, MeltQuote>>>,
     pending_proofs: Arc<Mutex<HashMap<Vec<u8>, Proof>>>,
     spent_proofs: Arc<Mutex<HashMap<Vec<u8>, Proof>>>,
-    blinded_signatures: Arc<Mutex<HashMap<String, BlindedSignature>>>,
+    blinded_signatures: Arc<Mutex<HashMap<Box<[u8]>, BlindedSignature>>>,
 }
 
 impl MemoryLocalStore {
@@ -33,7 +33,7 @@ impl MemoryLocalStore {
         melt_quotes: Vec<MeltQuote>,
         pending_proofs: Proofs,
         spent_proofs: Proofs,
-        blinded_signatures: HashMap<String, BlindedSignature>,
+        blinded_signatures: HashMap<Box<[u8]>, BlindedSignature>,
     ) -> Result<Self, Error> {
         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<PublicKey>,
+    ) -> Result<Vec<Option<BlindedSignature>>, 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)
+    }
 }

+ 4 - 0
crates/cashu-sdk/src/mint/localstore/mod.rs

@@ -88,4 +88,8 @@ pub trait LocalStore {
         &self,
         blinded_message: &PublicKey,
     ) -> Result<Option<BlindedSignature>, Error>;
+    async fn get_blinded_signatures(
+        &self,
+        blinded_messages: Vec<PublicKey>,
+    ) -> Result<Vec<Option<BlindedSignature>>, Error>;
 }

+ 22 - 3
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<PublicKey>,
+    ) -> Result<Vec<Option<BlindedSignature>>, 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)
+    }
 }

+ 29 - 0
crates/cashu-sdk/src/mint/mod.rs

@@ -732,6 +732,35 @@ impl Mint {
     pub async fn mint_info(&self) -> Result<MintInfo, Error> {
         Ok(self.localstore.get_mint_info().await?)
     }
+
+    #[cfg(feature = "nut09")]
+    pub async fn restore(&self, request: RestoreRequest) -> Result<RestoreResponse, Error> {
+        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)]

+ 2 - 0
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")]

+ 6 - 0
crates/cashu/src/nuts/nut01.rs

@@ -79,6 +79,12 @@ impl PublicKey {
     }
 }
 
+impl From<PublicKey> for Box<[u8]> {
+    fn from(pubkey: PublicKey) -> Box<[u8]> {
+        pubkey.to_bytes()
+    }
+}
+
 impl FromStr for PublicKey {
     type Err = Error;
 

+ 1 - 0
crates/cashu/src/nuts/nut09.rs

@@ -17,6 +17,7 @@ pub struct RestoreResponse {
     /// Outputs
     pub outputs: Vec<BlindedMessage>,
     /// Signatures
+    // TODO: remove rename just for temp compatanlite with nutshell
     #[serde(rename = "promises")]
     pub signatures: Vec<BlindedSignature>,
 }