Преглед изворни кода

feat: check pending proofs

thesimplekid пре 11 месеци
родитељ
комит
34e2e15378
3 измењених фајлова са 80 додато и 1 уклоњено
  1. 20 0
      bindings/cdk-js/src/wallet.rs
  2. 5 1
      crates/cdk/src/nuts/nut00.rs
  3. 55 0
      crates/cdk/src/wallet.rs

+ 20 - 0
bindings/cdk-js/src/wallet.rs

@@ -51,6 +51,26 @@ impl JsWallet {
         Ok(self.inner.total_balance().await.map_err(into_err)?.into())
     }
 
+    #[wasm_bindgen(js_name = totalPendingBalance)]
+    pub async fn total_pending_balance(&self) -> Result<JsAmount> {
+        Ok(self
+            .inner
+            .total_pending_balance()
+            .await
+            .map_err(into_err)?
+            .into())
+    }
+
+    #[wasm_bindgen(js_name = checkAllPendingProofs)]
+    pub async fn check_all_pending_proofs(&self) -> Result<JsAmount> {
+        Ok(self
+            .inner
+            .check_all_pending_proofs
+            .await
+            .map_err(into_err)?
+            .into())
+    }
+
     #[wasm_bindgen(js_name = mintBalances)]
     pub async fn mint_balances(&self) -> Result<JsValue> {
         let mint_balances = self.inner.mint_balances().await.map_err(into_err)?;

+ 5 - 1
crates/cdk/src/nuts/nut00.rs

@@ -16,7 +16,7 @@ use url::Url;
 
 use super::nut10;
 use super::nut11::SpendingConditions;
-use crate::dhke::blind_message;
+use crate::dhke::{blind_message, hash_to_curve};
 use crate::nuts::nut01::{PublicKey, SecretKey};
 use crate::nuts::nut11::{serde_p2pk_witness, P2PKWitness};
 use crate::nuts::nut12::BlindSignatureDleq;
@@ -200,6 +200,10 @@ impl Proof {
             dleq: None,
         }
     }
+
+    pub fn y(&self) -> Result<PublicKey, Error> {
+        Ok(hash_to_curve(self.secret.as_bytes())?)
+    }
 }
 
 impl Hash for Proof {

+ 55 - 0
crates/cdk/src/wallet.rs

@@ -129,6 +129,23 @@ impl Wallet {
         Ok(balance)
     }
 
+    /// Total Balance of wallet
+    #[instrument(skip(self))]
+    pub async fn total_pending_balance(&self) -> Result<Amount, Error> {
+        let mints = self.localstore.get_mints().await?;
+        let mut balance = Amount::ZERO;
+
+        for (mint, _) in mints {
+            if let Some(proofs) = self.localstore.get_pending_proofs(mint.clone()).await? {
+                let amount = proofs.iter().map(|p| p.amount).sum();
+
+                balance += amount;
+            }
+        }
+
+        Ok(balance)
+    }
+
     #[instrument(skip(self))]
     pub async fn mint_balances(&self) -> Result<HashMap<UncheckedUrl, Amount>, Error> {
         let mints = self.localstore.get_mints().await?;
@@ -292,6 +309,44 @@ impl Wallet {
         Ok(spendable.states)
     }
 
+    /// Checks pending proofs for spent status
+    #[instrument(skip(self))]
+    pub async fn check_all_pending_proofs(&self) -> Result<Amount, Error> {
+        let mints = self.localstore.get_mints().await?;
+        let mut balance = Amount::ZERO;
+
+        for (mint, _) in mints {
+            if let Some(proofs) = self.localstore.get_pending_proofs(mint.clone()).await? {
+                let states = self
+                    .check_proofs_spent(mint.clone(), proofs.clone())
+                    .await?;
+
+                // Both `State::Pending` and `State::Unspent` should be included in the pending table.
+                // This is because a proof that has been crated to send will be stored in the pending table
+                // in order to avoid accidentally double spending but to allow it to be explicitly reclaimed
+                let pending_states: HashSet<PublicKey> = states
+                    .into_iter()
+                    .filter(|s| s.state.ne(&State::Spent))
+                    .map(|s| s.y)
+                    .collect();
+
+                let (pending_proofs, non_pending_proofs): (Proofs, Proofs) = proofs
+                    .into_iter()
+                    .partition(|p| p.y().map(|y| pending_states.contains(&y)).unwrap_or(false));
+
+                let amount = pending_proofs.iter().map(|p| p.amount).sum();
+
+                self.localstore
+                    .remove_pending_proofs(mint, &non_pending_proofs)
+                    .await?;
+
+                balance += amount;
+            }
+        }
+
+        Ok(balance)
+    }
+
     /// Mint Quote
     #[instrument(skip(self), fields(mint_url = %mint_url))]
     pub async fn mint_quote(