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
This commit is contained in:
thesimplekid
2023-12-11 20:34:15 +00:00
parent fa1d54083e
commit 6400624e1e
4 changed files with 54 additions and 30 deletions

View File

@@ -1,5 +1,7 @@
//! Minreq http Client
use std::println;
use async_trait::async_trait;
use cashu::nuts::nut00::wallet::BlindedMessages;
use cashu::nuts::nut00::{BlindedMessage, Proof};
@@ -16,6 +18,7 @@ use cashu::nuts::MintInfo;
use cashu::nuts::*;
use cashu::{Amount, Bolt11Invoice};
use serde_json::Value;
use tracing::debug;
use url::Url;
use super::join_url;
@@ -161,17 +164,16 @@ impl Client for HttpClient {
) -> Result<SplitResponse, Error> {
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> =
serde_json::from_value(res.clone());
serde_json::from_value(res.json::<Value>()?.clone());
match response {
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())?),
}
}

View File

@@ -52,23 +52,27 @@ pub enum Error {
impl Error {
pub fn from_json(json: &str) -> Result<Self, Error> {
let mint_res: MintErrorResponse = serde_json::from_str(json)?;
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 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)
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()))
}
}
}

View File

@@ -147,7 +147,7 @@ impl<C: Client> Wallet<C> {
// Sum amount of all proofs
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
.client
@@ -175,15 +175,26 @@ impl<C: Client> Wallet<C> {
}
/// 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());
@@ -240,7 +251,7 @@ impl<C: Client> Wallet<C> {
return Err(Error::InsufficientFunds);
}
let split_payload = self.create_split(proofs)?;
let split_payload = self.create_split(Some(amount), proofs)?;
let split_response = self
.client

View File

@@ -97,6 +97,13 @@ pub mod wallet {
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)]