Browse Source

feat: reclaim proofs if melt fails

thesimplekid 8 months ago
parent
commit
b7208a584b
1 changed files with 64 additions and 3 deletions
  1. 64 3
      crates/cdk/src/wallet/mod.rs

+ 64 - 3
crates/cdk/src/wallet/mod.rs

@@ -378,6 +378,33 @@ impl Wallet {
         Ok(keys)
     }
 
+    /// Reclaim unspent proofs
+    #[instrument(skip(self, proofs))]
+    pub async fn reclaim_unspent(&self, proofs: Proofs) -> Result<(), Error> {
+        let proof_ys = proofs
+            .iter()
+            // Find Y for the secret
+            .flat_map(|p| hash_to_curve(p.secret.as_bytes()))
+            .collect::<Vec<PublicKey>>();
+
+        let spendable = self
+            .client
+            .post_check_state(self.mint_url.clone().try_into()?, proof_ys)
+            .await?
+            .states;
+
+        let unspent: Proofs = proofs
+            .into_iter()
+            .zip(spendable)
+            .filter_map(|(p, s)| (s.state == State::Unspent).then_some(p))
+            .collect();
+
+        self.swap(None, &SplitTarget::default(), unspent, None)
+            .await?;
+
+        Ok(())
+    }
+
     /// Check if a proof is spent
     #[instrument(skip(self, proofs))]
     pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> {
@@ -386,7 +413,7 @@ impl Wallet {
             .post_check_state(
                 self.mint_url.clone().try_into()?,
                 proofs
-                    .into_iter()
+                    .iter()
                     // Find Y for the secret
                     .flat_map(|p| hash_to_curve(p.secret.as_bytes()))
                     .collect::<Vec<PublicKey>>(),
@@ -726,6 +753,17 @@ impl Wallet {
                 .await?;
         }
 
+        if let Some(proofs) = proofs_to_send.clone() {
+            let send_proofs = proofs
+                .into_iter()
+                .flat_map(|proof| {
+                    ProofInfo::new(proof, mint_url.clone(), State::Reserved, unit.clone())
+                })
+                .collect();
+
+            self.localstore.add_proofs(send_proofs).await?;
+        }
+
         let keep_proofs = keep_proofs
             .into_iter()
             .flat_map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Unspent, unit.clone()))
@@ -1006,11 +1044,22 @@ impl Wallet {
                     )
                     .await?;
 
-                proofs.ok_or(Error::InsufficientFunds)?
+                match proofs {
+                    Some(proofs) => proofs,
+                    None => {
+                        return Err(Error::InsufficientFunds);
+                    }
+                }
             }
             false => proofs,
         };
 
+        for proof in input_proofs.iter() {
+            self.localstore
+                .set_proof_state(proof.y()?, State::Pending)
+                .await?;
+        }
+
         let active_keyset_id = self.active_mint_keyset().await?;
 
         let count = self
@@ -1035,7 +1084,19 @@ impl Wallet {
                 input_proofs.clone(),
                 Some(premint_secrets.blinded_messages()),
             )
-            .await?;
+            .await;
+
+        let melt_response = match melt_response {
+            Ok(melt_response) => melt_response,
+            Err(err) => {
+                tracing::error!("Could not melt: {}", err);
+                tracing::info!("Checking status of input proofs.");
+
+                self.reclaim_unspent(input_proofs).await?;
+
+                return Err(err);
+            }
+        };
 
         let change_proofs = match melt_response.change {
             Some(change) => Some(construct_proofs(