Merge pull request 'fix(ecash): change proofs no longer ride along inside sent tokens — double-credit bug' (#122) from fix/cashu-double-credit into main

This commit was merged in pull request #122.
This commit is contained in:
2026-07-24 12:34:56 +00:00
+24 -5
View File
@@ -632,11 +632,30 @@ pub async fn send_token_at(data_dir: &Path, mint_url: &str, amount_sats: u64) ->
// Mark original proofs as spent // Mark original proofs as spent
wallet.mark_spent(&indices); wallet.mark_spent(&indices);
// Separate send proofs from change proofs // Separate send proofs from change proofs BY COUNT, not membership:
let (send, change): (Vec<_>, Vec<_>) = swap_result // partition(contains) put EVERY proof whose denomination appeared in
.new_proofs // send_denoms into the token — when change shared a denomination with
.into_iter() // the send (worst case: spending from a proof worth exactly 2× the
.partition(|p| send_denoms.contains(&p.amount)); // amount, send [n] + change [n]), the change proofs rode along and the
// receiver was credited double. Consume exactly one proof per needed
// send denomination; everything else is change.
let mut send_needed = send_denoms.clone();
let mut send: Vec<Proof> = Vec::new();
let mut change: Vec<Proof> = Vec::new();
for p in swap_result.new_proofs {
if let Some(pos) = send_needed.iter().position(|&d| d == p.amount) {
send_needed.swap_remove(pos);
send.push(p);
} else {
change.push(p);
}
}
if !send_needed.is_empty() {
anyhow::bail!(
"Mint swap returned incomplete send denominations (missing {:?})",
send_needed
);
}
// Add change proofs back to wallet // Add change proofs back to wallet
if !change.is_empty() { if !change.is_empty() {