fix(ecash): stop replayed Minibits claims retrying forever, reduce relay churn
claim_and_redeem retried every redeem failure indefinitely, including a terminal one: mint error 11001 "Token Already Spent" (a claim replayed by a relay-watermark edge case, or already redeemed by an earlier run). On archy-x250-pa3 this pinned pending_claims at 1 forever and hammered mint.minibits.cash's swap endpoint every ~6s, with the UI permanently showing "a payment arrived but couldn't be redeemed yet". - mint_client: expose the NUT error-code-11001 message as ALREADY_REDEEMED_MSG so callers can recognize it without duplicating the string. - minibits: drop (not retry) a redeem failure that matches is_already_redeemed — the value was already swept, so retrying can never succeed. - fetch_relay_dms: query the primary relay.minibits.cash alone first, falling back to the public relay.damus.io/nos.lol only if it's unreachable, and page past a 200-DM backlog instead of silently stranding older DMs behind an un-advanced watermark. This fix already existed on feat/minibits-lnurl-receive (4e410d7,489995c, 2026-09-09) but that branch was never merged into main, which has its own independently-diverged minibits.rs — so the bug shipped again in 1.8.16-alpha. Ported directly onto main's current implementation this time. Immediate unblock on archy-x250-pa3: cleared the one poisoned pending_claims entry from wallet/minibits.json by hand (already-redeemed, zero value at risk) and restarted archipelago.service; confirmed via journalctl that polling is quiet again. See docs/incident-2026-09-15-minibits-already-redeemed.md for the full writeup. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -71,10 +71,18 @@ pub struct MintResult {
|
||||
/// keyset codes shared by NUT-02/03/04/05 — the codes a swap/melt/mint call
|
||||
/// can actually hit. Returns `None` for anything else (e.g. Lightning/quote
|
||||
/// codes in the 20000s) so the caller falls back to the mint's own `detail`.
|
||||
///
|
||||
/// Text of the NUT error-code-11001 translation, exposed so callers that
|
||||
/// received an `anyhow::Error` from a receive/redeem path (e.g. a replayed
|
||||
/// Minibits claim) can recognize an already-spent token as terminal rather
|
||||
/// than retrying it forever.
|
||||
pub const ALREADY_REDEEMED_MSG: &str =
|
||||
"This ecash has already been redeemed — it can't be claimed twice.";
|
||||
|
||||
fn describe_mint_error_code(code: i64) -> Option<&'static str> {
|
||||
Some(match code {
|
||||
10001 => "The mint rejected these coins as invalid.",
|
||||
11001 => "This ecash has already been redeemed — it can't be claimed twice.",
|
||||
11001 => ALREADY_REDEEMED_MSG,
|
||||
11002 => "This ecash is already being redeemed elsewhere — try again in a moment.",
|
||||
11003 => "The mint already issued new coins for this exact request — there's nothing left to redeem.",
|
||||
11004 => "This request is still being processed by the mint — try again in a moment.",
|
||||
|
||||
Reference in New Issue
Block a user