Przeglądaj źródła

fix: send: split amount

The create split function used in the send of cashu-sdk
was not ensureing that the correct combination of token amounts
is avaliable for the send. Adding an optinal split amount enables this.

TODO: the proofs in split are not sorted in accending order to avoid fingerprinting
thesimplekid 1 rok temu
rodzic
commit
6400624e1e

+ 8 - 6
crates/cashu-sdk/src/client/minreq_client.rs

@@ -1,5 +1,7 @@
 //! Minreq http Client
 //! Minreq http Client
 
 
+use std::println;
+
 use async_trait::async_trait;
 use async_trait::async_trait;
 use cashu::nuts::nut00::wallet::BlindedMessages;
 use cashu::nuts::nut00::wallet::BlindedMessages;
 use cashu::nuts::nut00::{BlindedMessage, Proof};
 use cashu::nuts::nut00::{BlindedMessage, Proof};
@@ -16,6 +18,7 @@ use cashu::nuts::MintInfo;
 use cashu::nuts::*;
 use cashu::nuts::*;
 use cashu::{Amount, Bolt11Invoice};
 use cashu::{Amount, Bolt11Invoice};
 use serde_json::Value;
 use serde_json::Value;
+use tracing::debug;
 use url::Url;
 use url::Url;
 
 
 use super::join_url;
 use super::join_url;
@@ -161,17 +164,16 @@ impl Client for HttpClient {
     ) -> Result<SplitResponse, Error> {
     ) -> Result<SplitResponse, Error> {
         let url = join_url(mint_url, "split")?;
         let url = join_url(mint_url, "split")?;
 
 
-        let res = minreq::post(url)
-            .with_json(&split_request)?
-            .send()?
-            .json::<Value>()?;
+        let res = minreq::post(url).with_json(&split_request)?.send()?;
+
+        println!("{:?}", res);
 
 
         let response: Result<SplitResponse, serde_json::Error> =
         let response: Result<SplitResponse, serde_json::Error> =
-            serde_json::from_value(res.clone());
+            serde_json::from_value(res.json::<Value>()?.clone());
 
 
         match response {
         match response {
             Ok(res) if res.promises.is_some() => Ok(res),
             Ok(res) if res.promises.is_some() => Ok(res),
-            _ => Err(Error::from_json(&res.to_string())?),
+            _ => Err(Error::from_json(&res.json::<Value>()?.to_string())?),
         }
         }
     }
     }
 
 

+ 21 - 17
crates/cashu-sdk/src/client/mod.rs

@@ -52,23 +52,27 @@ pub enum Error {
 
 
 impl Error {
 impl Error {
     pub fn from_json(json: &str) -> Result<Self, Error> {
     pub fn from_json(json: &str) -> Result<Self, Error> {
-        let mint_res: MintErrorResponse = serde_json::from_str(json)?;
-
-        let err = mint_res
-            .error
-            .as_deref()
-            .or(mint_res.detail.as_deref())
-            .unwrap_or_default();
-
-        let mint_error = match err {
-            error if error.starts_with("Lightning invoice not paid yet.") => Error::InvoiceNotPaid,
-            error if error.starts_with("Lightning wallet not responding") => {
-                let mint = utils::extract_url_from_error(error);
-                Error::LightingWalletNotResponding(mint)
-            }
-            error => Error::Custom(error.to_owned()),
-        };
-        Ok(mint_error)
+        if let Ok(mint_res) = serde_json::from_str::<MintErrorResponse>(json) {
+            let err = mint_res
+                .error
+                .as_deref()
+                .or(mint_res.detail.as_deref())
+                .unwrap_or_default();
+
+            let mint_error = match err {
+                error if error.starts_with("Lightning invoice not paid yet.") => {
+                    Error::InvoiceNotPaid
+                }
+                error if error.starts_with("Lightning wallet not responding") => {
+                    let mint = utils::extract_url_from_error(error);
+                    Error::LightingWalletNotResponding(mint)
+                }
+                error => Error::Custom(error.to_owned()),
+            };
+            Ok(mint_error)
+        } else {
+            Ok(Error::Custom(json.to_string()))
+        }
     }
     }
 }
 }
 
 

+ 19 - 8
crates/cashu-sdk/src/wallet.rs

@@ -147,7 +147,7 @@ impl<C: Client> Wallet<C> {
             // Sum amount of all proofs
             // Sum amount of all proofs
             let _amount: Amount = token.proofs.iter().map(|p| p.amount).sum();
             let _amount: Amount = token.proofs.iter().map(|p| p.amount).sum();
 
 
-            let split_payload = self.create_split(token.proofs)?;
+            let split_payload = self.create_split(None, token.proofs)?;
 
 
             let split_response = self
             let split_response = self
                 .client
                 .client
@@ -175,15 +175,26 @@ impl<C: Client> Wallet<C> {
     }
     }
 
 
     /// Create Split Payload
     /// Create Split Payload
-    fn create_split(&self, proofs: Proofs) -> Result<SplitPayload, Error> {
-        let mut proofs = proofs;
+    /// TODO: This needs to sort to avoid finer printing
+    fn create_split(&self, amount: Option<Amount>, proofs: Proofs) -> Result<SplitPayload, Error> {
+        let proofs = proofs;
 
 
-        // Sort proofs in ascending order to avoid fingerprinting
-        proofs.sort();
+        // Since split is used to get the needed combination of tokens for a specific
+        // amount first blinded messages are created for the amount
 
 
-        let value = proofs.iter().map(|p| p.amount).sum();
+        let blinded_messages = if let Some(amount) = amount {
+            let mut desired_messages = BlindedMessages::random(amount)?;
 
 
-        let blinded_messages = BlindedMessages::random(value)?;
+            let change_amount = proofs.iter().map(|p| p.amount).sum::<Amount>() - amount;
+
+            let change_messages = BlindedMessages::random(change_amount)?;
+            desired_messages.combine(change_messages);
+            desired_messages
+        } else {
+            let value = proofs.iter().map(|p| p.amount).sum();
+
+            BlindedMessages::random(value)?
+        };
 
 
         let split_payload = SplitRequest::new(proofs, blinded_messages.blinded_messages.clone());
         let split_payload = SplitRequest::new(proofs, blinded_messages.blinded_messages.clone());
 
 
@@ -240,7 +251,7 @@ impl<C: Client> Wallet<C> {
             return Err(Error::InsufficientFunds);
             return Err(Error::InsufficientFunds);
         }
         }
 
 
-        let split_payload = self.create_split(proofs)?;
+        let split_payload = self.create_split(Some(amount), proofs)?;
 
 
         let split_response = self
         let split_response = self
             .client
             .client

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

@@ -97,6 +97,13 @@ pub mod wallet {
 
 
             Ok(blinded_messages)
             Ok(blinded_messages)
         }
         }
+
+        pub fn combine(&mut self, mut other: Self) {
+            self.blinded_messages.append(&mut other.blinded_messages);
+            self.secrets.append(&mut other.secrets);
+            self.rs.append(&mut other.rs);
+            self.amounts.append(&mut other.amounts);
+        }
     }
     }
 
 
     #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
     #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]