From 8b74803290c5b1657ba9c6778d0b717c8e9c647e Mon Sep 17 00:00:00 2001 From: ssmithx Date: Tue, 29 Sep 2026 15:47:23 +0000 Subject: [PATCH] fix(ecash): repair short v2 keyset ids on every swap, not just receive Paid cloud downloads paid with Minibits ecash were always rejected. The buyer sends a cashuB token, which carries NUT-02 v2 keyset ids in their 8-byte short form. Minibits rotated its active keyset to a v2 id, and the seller's verify_and_receive_payment called MintClient::swap directly, skipping the short->full id repair that only receive_token applied. The mint answered 422 ("ID length invalid"). The buyer then showed the misleading "seller doesn't accept your Cashu mint" hint. Move the repair into swap() so every caller is covered: payment verify, streaming gate, send change, and cross-mint swaps. Co-Authored-By: Claude Opus 5.5 --- core/archipelago/src/wallet/mint_client.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/archipelago/src/wallet/mint_client.rs b/core/archipelago/src/wallet/mint_client.rs index 83950898..b7979fc7 100644 --- a/core/archipelago/src/wallet/mint_client.rs +++ b/core/archipelago/src/wallet/mint_client.rs @@ -514,6 +514,13 @@ impl MintClient { pub async fn swap(&self, inputs: &[Proof], target_amounts: &[u64]) -> Result { let keyset = self.get_active_sat_keyset().await?; + // cashuB tokens carry NUT-02 v2 keyset ids in their 8-byte short form, + // which the mint rejects (bare 422). Repair here, not at each caller: + // the paid-download seller path swapped directly and every Minibits + // payment failed once the mint rotated to a v2 keyset. + let repaired = self.resolve_truncated_keyset_ids(inputs).await; + let inputs: &[Proof] = &repaired; + // NUT-02: a mint may charge a per-input fee, and it rejects the swap // outright unless outputs == inputs - fee (`11005 Transaction inputs // should equal outputs less fee`). Applied here rather than at each @@ -813,8 +820,7 @@ impl MintClient { let total: u64 = entry.proofs.iter().map(|p| p.amount).sum(); let target_amounts = amount_to_denominations(total); - let proofs = self.resolve_truncated_keyset_ids(&entry.proofs).await; - let result = self.swap(&proofs, &target_amounts).await?; + let result = self.swap(&entry.proofs, &target_amounts).await?; all_new_proofs.extend(result.new_proofs); }