Explorar o código

fix: send in wallet proof totals

thesimplekid hai 1 ano
pai
achega
dd3e96243a
Modificáronse 2 ficheiros con 40 adicións e 28 borrados
  1. 28 28
      crates/cashu-sdk/src/wallet.rs
  2. 12 0
      crates/cashu/src/nuts/nut00.rs

+ 28 - 28
crates/cashu-sdk/src/wallet.rs

@@ -171,6 +171,11 @@ impl<C: Client> Wallet<C> {
 
     /// Create Split Payload
     fn create_split(&self, proofs: Proofs) -> Result<SplitPayload, Error> {
+        let mut proofs = proofs;
+
+        // Sort proofs in ascending order to avoid fingerprinting
+        proofs.sort();
+
         let value = proofs.iter().map(|p| p.amount).sum();
 
         let blinded_messages = BlindedMessages::random(value)?;
@@ -223,33 +228,14 @@ impl<C: Client> Wallet<C> {
 
     /// Send
     pub async fn send(&self, amount: Amount, proofs: Proofs) -> Result<SendProofs, Error> {
-        let mut amount_available = Amount::ZERO;
-        let mut send_proofs = SendProofs::default();
-
-        for proof in proofs {
-            let proof_value = proof.amount;
-            if amount_available > amount {
-                send_proofs.change_proofs.push(proof);
-            } else {
-                send_proofs.send_proofs.push(proof);
-            }
-            amount_available += proof_value;
-        }
+        let amount_available: Amount = proofs.iter().map(|p| p.amount).sum();
 
         if amount_available.lt(&amount) {
             println!("Not enough funds");
             return Err(Error::InsufficientFunds);
         }
 
-        // If amount available is EQUAL to send amount no need to split
-        if amount_available.eq(&amount) {
-            return Ok(send_proofs);
-        }
-
-        let _amount_to_keep = amount_available - amount;
-        let amount_to_send = amount;
-
-        let split_payload = self.create_split(send_proofs.send_proofs)?;
+        let split_payload = self.create_split(proofs)?;
 
         let split_response = self
             .client
@@ -259,22 +245,26 @@ impl<C: Client> Wallet<C> {
             )
             .await?;
 
-        // If only promises assemble proofs needed for amount
-        let keep_proofs;
-        let send_proofs;
+        let mut keep_proofs = Proofs::new();
+        let mut send_proofs = Proofs::new();
 
         if let Some(promises) = split_response.promises {
-            let proofs = construct_proofs(
+            let mut proofs = construct_proofs(
                 promises,
                 split_payload.blinded_messages.rs,
                 split_payload.blinded_messages.secrets,
                 &self.mint_keys,
             )?;
 
-            let split = amount_to_send.split();
+            proofs.reverse();
 
-            keep_proofs = proofs[0..split.len()].to_vec();
-            send_proofs = proofs[split.len()..].to_vec();
+            for proof in proofs {
+                if (proof.amount + send_proofs.iter().map(|p| p.amount).sum()).gt(&amount) {
+                    keep_proofs.push(proof);
+                } else {
+                    send_proofs.push(proof);
+                }
+            }
         } else {
             return Err(Error::Custom("Invalid split response".to_string()));
         }
@@ -282,6 +272,16 @@ impl<C: Client> Wallet<C> {
         // println!("Send Proofs: {:#?}", send_proofs);
         // println!("Keep Proofs: {:#?}", keep_proofs);
 
+        let send_amount: Amount = send_proofs.iter().map(|p| p.amount).sum();
+
+        if send_amount.ne(&amount) {
+            warn!(
+                "Send amount proofs is {} expected {}",
+                send_amount.to_sat(),
+                amount.to_sat()
+            );
+        }
+
         Ok(SendProofs {
             change_proofs: keep_proofs,
             send_proofs,

+ 12 - 0
crates/cashu/src/nuts/nut00.rs

@@ -204,6 +204,18 @@ pub struct Proof {
     pub id: Option<Id>,
 }
 
+impl Ord for Proof {
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.amount.cmp(&other.amount)
+    }
+}
+
+impl PartialOrd for Proof {
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
 impl From<Proof> for mint::Proof {
     fn from(proof: Proof) -> Self {
         Self {