The three credit_balance unit tests mock the DB session but let
credit_balance call the real store_cashu_transaction_with_retry, which
opens its own session against the global engine. In CI that database has
no cashu_transactions table; since storage failures now propagate
(a60b04ae) instead of being silently swallowed, the tests failed with
sqlite3.OperationalError. Patch the audit store like the existing
propagation test already does.
Comprehensive audit of all money-moving code paths on current main.
Found 8 live vulnerabilities where users, providers, or node runners
can lose funds, plus 1 false-green in the existing emergency refund
test suite.
Live vulnerabilities (all RED — tests assert correct/safe behaviour):
V-E1 send_refund() swallows DB failure after minting a refund token
base.py ~line 3625 — except Exception: pass
V-E2 Emergency refund (chat) — same except: pass
base.py ~line 3992 (existing test is a false green — 500-char
window too short)
V-E3 Emergency refund (responses API) — identical pattern
base.py ~line 4972
V-E4 Balance refund endpoint swallows DB failure
balance.py ~line 628
V-E5 credit_balance() swallows 'in' transaction DB failure
wallet.py ~line 1715
V-E6 EHBP refund token — except: pass after store
ehbp.py ~line 762
V-E7 EHBP 'in' transaction — except: pass after store
ehbp.py ~line 1028
V-E8 Admin withdraw returns token even when DB store fails
admin.py ~line 475
V-E9 Window regression guard (GREEN) — documents the false-green in
the existing test_emergency_refund_no_try_except_pass
Test results: 8 failed, 1 passed.
When the mint no longer has a Lightning quote (e.g. after TTL purge or
restart), check_invoice_payment() was logging an ERROR and returning False.
This caused the periodic_invoice_watcher to keep polling the same dead
quote every 10s forever, producing infinite log spam.
Now _is_quote_not_found() detects 'Mint Error: quote not found (Code: 0)'
and returns True, allowing _expire_invoice_if_authoritatively_unpaid()
to mark the invoice as expired so the watcher stops polling it.
The check is case-insensitive and requires code 0 to avoid false positives
from other quote-related errors.