Repository navigation
feat: token recovery tool + AI audit prompts + speccheck - #340
Amperstrand wants to merge 5 commits into
Conversation
Update example from old 'TollGate-ABCD-2.4GHz-1' (band suffix) to simplified 'TollGate-A1B2' (random chars only).
Token Recovery Tool: Standalone Go tool that recovers Cashu tokens from tokens-to-recover.txt (written when autopay fails with NDS gate errors). Checks proof state at mint via NUT-07 checkstate, recovers unspent tokens via Wallet.Receive(). Supports --dry-run. Also includes: - docs/ai-audit/ — 3 AI audit prompt templates (NUT compliance, security, deps) - make/speccheck.sh — one-command greatspectations spec checking Tested against testnut.cashu.exchange with real tokens.
Under set -e, a non-zero exit from 'spectate check' (drift found) aborted the script before the coverage report and propagated as the script's exit status. Drift is this script's report, not its failure mode — report and exit 0; infra errors (install/clone) still abort. CI gating can invert this deliberately when wired in. Review findings: cold-code review 2026-08-17 item 3.
|
Cold-review item 3 + housekeeping: resolved ( speccheck exit semantics — fixed here, with a corrected diagnosis. The script had no Consolidation: this PR is now the single home of the audit tooling — |
felixfelix-bot
left a comment
There was a problem hiding this comment.
Cold review — new findings only (1 issue previously raised, not repeated).
2 major:
-
PENDING proofs are treated as recoverable. The code only checks s.State != nut07.Spent to set allSpent=false, so PENDING (in-flight) proofs are treated as UNSPENT and Wallet.Receive() is called on them. This can cause failed receives or double-spend attempts. Should check s.State == nut07.Unspent explicitly instead
-
Wallet is loaded per-token inside processLine via wallet.LoadWallet(config). For multiple recoverable tokens, this opens/closes the wallet database repeatedly, risking lock contention or data loss. Wallet should be loaded once before the loop and reused
4 minor (non-blocking):
- Dry-run returns "failed" for recoverable tokens — summary counts them as ❌ failed instead of a distinct "recoverable" status
- p.Secret[:16] printed in error output — leaks partial proof secret to stdout/logs
- No HTTP timeout on client.PostCheckProofState call — unresponsive mint hangs the tool indefinitely
- p.Secret[:16] will panic with slice out of bounds if a malformed token has a secret shorter than 16 bytes
The NUT-07 Ys list format is correct. The speccheck.sh fix (set -e + non-zero from spectate check) is verified correct.
…leak - PENDING proofs were treated as recoverable (state != Spent), so the tool could receive in-flight proofs — double-spend attempt or failed receive. Recoverable now requires State == Unspent for every proof; PENDING gets its own status and a retry-later message. - Wallet loads once per run (lazy, first recoverable token) instead of per line — LoadWallet locks the bolt store, per-token loads contend. - hash_to_curve error no longer prints p.Secret[:16] — proof secrets spend tokens; the slice would also panic on short secrets. - Dry-run counts recoverable tokens separately instead of as ❌ failed. - No timeout change needed: gonuts client already uses a 30s http.Client timeout (wallet/client/client.go:34). Review findings: cold review 2026-08-18 (majors 1-2 + minors 1,2,3*4).
|
Cold-review response — both majors + minors addressed in
|
…o a hung mint cannot stall recovery
felixfelix-bot
left a comment
There was a problem hiding this comment.
Re-reviewed: 03ccf14 verified earlier (State == Unspent with retry-later for PENDING, single wallet per run, secret fragment removed, dry-run separate counting) + 3ca91f5 adds a belt-and-braces 30s bound on checkstate on top. Accepting the stale-timeout note (gonuts client already wraps 30s) — harmless either way. Good to merge.
|
Closing — rebased onto main as #354. Conflicts resolved, build verified. |
Token Recovery Tool
Standalone Go tool (
scripts/token-recovery/) that recovers Cashu tokens from thetokens-to-recover.txtfile written when autopay fails (e.g., NDS #88 gate-open bug).How it works
timestamp | mint_url | token | errorPOST /v1/checkstate(NUT-07)Wallet.Receive(token, swapToTrusted=true)Usage