mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-05 05:06:14 +01:00
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:
@@ -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())?),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user