fix(ecash): stop Minibits LN-address claims from being silently lost
A Minibits /claim response consumes the payment server-side the instant it's returned — it can never be re-fetched. claim_and_redeem previously decrypted/redeemed each claim inline and just warn!-logged any failure, so a mint-unreachable blip, a stale cached server key, or an operator who'd edited their accepted-mints list to drop the default mint (via streaming.configure-mints) could make a real payment vanish with nothing but a log line to show for it — claimed_count/received_sats still came back as a clean 0, identical to "nothing arrived." Now: every fetched claim is persisted to MinibitsState.pending_claims before decrypt/redeem is attempted, survives failures across polls instead of being dropped, and claim_and_redeem no longer bails out on a fetch error without first retrying whatever was already pending. ensure_mint_accepted self-heals the accepted-mints allow-list so the Minibits mint (the address is inherently backed by it) can't be excluded out from under a claim. ClaimOutcome gains failed_count, threaded through wallet.ecash-lnaddress-claim and shown in ReceiveBitcoinModal so a stuck claim is visible instead of silent. Also fixes the server_nostur_pubkey field-name typo (no live state to migrate — this feature hasn't shipped yet). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EawZPP9iidXj6Tvg3EpG3a
This commit is contained in:
committed by
archipelago
co-authored by
Claude Sonnet 5
parent
ce9fca1c38
commit
ce5c04d49d
@@ -119,6 +119,9 @@
|
||||
<p v-if="lnClaimedSats > 0" class="text-green-400 text-sm mt-2">
|
||||
{{ t('receiveBitcoin.lnAddressReceived', { amount: lnClaimedSats.toLocaleString() }) }}
|
||||
</p>
|
||||
<p v-if="lnPendingClaims > 0" class="text-orange-400 text-sm mt-2">
|
||||
{{ t('receiveBitcoin.lnAddressPendingRetry', { count: lnPendingClaims }) }}
|
||||
</p>
|
||||
</div>
|
||||
<div v-else-if="lnAddressLoading" class="mb-4 text-center text-white/50 text-sm py-4">
|
||||
{{ t('receiveBitcoin.lnAddressLoading') }}
|
||||
@@ -201,6 +204,7 @@ watch(() => props.show, (open) => {
|
||||
lnAddressLoading.value = false
|
||||
lnAddressError.value = false
|
||||
lnClaimedSats.value = 0
|
||||
lnPendingClaims.value = 0
|
||||
error.value = ''
|
||||
processing.value = false
|
||||
if (props.autoGenerate && receiveMethod.value === 'onchain') {
|
||||
@@ -233,6 +237,10 @@ const lnAddress = ref('')
|
||||
const lnAddressLoading = ref(false)
|
||||
const lnAddressError = ref(false)
|
||||
const lnClaimedSats = ref(0)
|
||||
// A payment the backend fetched (and so already consumed at Minibits) but
|
||||
// couldn't redeem yet — it's queued for automatic retry, not lost, but the
|
||||
// operator should see it rather than have it be a silent, unbounded wait.
|
||||
const lnPendingClaims = ref(0)
|
||||
let lnClaimTimer: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
async function loadLnAddress() {
|
||||
@@ -274,13 +282,14 @@ async function pollLnClaims() {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await rpcClient.call<{ received_sats?: number }>({
|
||||
const res = await rpcClient.call<{ received_sats?: number; failed_count?: number }>({
|
||||
method: 'wallet.ecash-lnaddress-claim',
|
||||
})
|
||||
if (res?.received_sats && res.received_sats > 0) {
|
||||
lnClaimedSats.value += res.received_sats
|
||||
emit('received')
|
||||
}
|
||||
lnPendingClaims.value = res?.failed_count || 0
|
||||
} catch {
|
||||
// Transient poll failure (offline, mint busy) — keep polling.
|
||||
}
|
||||
@@ -415,6 +424,7 @@ function close() {
|
||||
ecashResult.value = ''
|
||||
lnAddress.value = ''
|
||||
lnClaimedSats.value = 0
|
||||
lnPendingClaims.value = 0
|
||||
error.value = ''
|
||||
emit('close')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user