diff options
| author | Your Name <you@example.com> | 2026-05-16 15:32:55 +0530 |
|---|---|---|
| committer | Your Name <you@example.com> | 2026-05-16 15:32:55 +0530 |
| commit | 133e40c82afb4d7659758b1fa57925ac57af4621 (patch) | |
| tree | 130f43e668f42f8fcc8ef55808ba89b6db40f615 /CHECKLIST.md | |
| parent | 8f0aeb7d7b8216f1fc906cf855e5be9e90ecc0a8 (diff) | |
Phase 3: on-device Cashu wallet with mbedTLS secp256k1 + SPIFFS persistence + PSRAM
- wallet.c/h: secp256k1 ECP primitives (hash_to_curve, scalar_mul, point_add)
- wallet_persist.c/h: SPIFFS persistence with threshold-based write protection
- Fee accounting for swap (input_fee_ppk from /v1/keysets)
- Keyset fetch via /v1/keysets (586 bytes vs 21KB for /v1/keys)
- Wallet API: GET /wallet, POST /wallet/swap, POST /wallet/send
- Payment proofs auto-stored to wallet + persisted on SPIFFS
- PSRAM enabled for large allocations (ESP32-S3 has 8MB)
- Wallet init deferred to dedicated task (avoids sys_evt stack overflow)
- Cashu proof ID buffer size fixed (66 hex chars, not 16)
- HTTP client: added fetch_headers() call for proper response handling
- persist_threshold_sats config parameter (default: 1 sat)
Diffstat (limited to 'CHECKLIST.md')
| -rw-r--r-- | CHECKLIST.md | 84 |
1 files changed, 67 insertions, 17 deletions
diff --git a/CHECKLIST.md b/CHECKLIST.md index d5711b4..3b50c2a 100644 --- a/CHECKLIST.md +++ b/CHECKLIST.md | |||
| @@ -21,7 +21,7 @@ | |||
| 21 | - [x] Fix ping tests (use `-I wlp59s0`) | 21 | - [x] Fix ping tests (use `-I wlp59s0`) |
| 22 | - [x] Tests 1-14: ALL PASSING | 22 | - [x] Tests 1-14: ALL PASSING |
| 23 | 23 | ||
| 24 | ## Phase 2: E-Cash Payments — IN PROGRESS (commit `3f46bb8` + uncommitted fixes) | 24 | ## Phase 2: E-Cash Payments — COMPLETE |
| 25 | ### Code Written | 25 | ### Code Written |
| 26 | - [x] Implement cashu.c/h (Cashu token parse, base64url, checkstate, mint validation) | 26 | - [x] Implement cashu.c/h (Cashu token parse, base64url, checkstate, mint validation) |
| 27 | - [x] Implement session.c/h (time-based allotment, expiry, secret tracking, MAC tracking) | 27 | - [x] Implement session.c/h (time-based allotment, expiry, secret tracking, MAC tracking) |
| @@ -45,10 +45,8 @@ | |||
| 45 | 45 | ||
| 46 | ### Infrastructure | 46 | ### Infrastructure |
| 47 | - [x] Upstream gateway on enx00e04c633a90 (192.168.2.0/24, metric 101, default route) | 47 | - [x] Upstream gateway on enx00e04c633a90 (192.168.2.0/24, metric 101, default route) |
| 48 | - [x] OpenWRT TollGate on enx00e04c683d2d (10.47.41.0/24, metric 20100, never-default) | ||
| 49 | - [x] WiFi wlp59s0 free for ESP32 TollGate connection | 48 | - [x] WiFi wlp59s0 free for ESP32 TollGate connection |
| 50 | - [x] NetworkManager profile "TollGate-ESP32" created (manual 192.168.4.2/24, autoconnect=no) | 49 | - [x] Mint URL verified: `testnut.cashu.space` works (auto-pays invoices) |
| 51 | - [x] Mint URL verified: `testnut.cashu.space` works; `nofee.testnut.cashu.space` and `nofees.testnut.cashu.space` both broken | ||
| 52 | 50 | ||
| 53 | ### Tests Passing | 51 | ### Tests Passing |
| 54 | - [x] Test 15: Advertisement valid (kind=10021 with price_per_step) — PASSING | 52 | - [x] Test 15: Advertisement valid (kind=10021 with price_per_step) — PASSING |
| @@ -63,11 +61,6 @@ | |||
| 63 | - [x] Test: /whoami returns ip=X.X.X.X mac=XX:XX:XX:XX:XX:XX — PASSING | 61 | - [x] Test: /whoami returns ip=X.X.X.X mac=XX:XX:XX:XX:XX:XX — PASSING |
| 64 | - [x] Test: Portal has payment form (Cashu token input + Pay button) — PASSING | 62 | - [x] Test: Portal has payment form (Cashu token input + Pay button) — PASSING |
| 65 | 63 | ||
| 66 | ### Tests Not Yet Run (deferred to Phase 3 — will use Board B as second client) | ||
| 67 | - [ ] Test 25: Two clients pay independently (laptop + Board B) | ||
| 68 | - [ ] Test 26: Client isolation (only payer gets internet) | ||
| 69 | - [ ] Test 27: Full e2e: portal → pay → browse | ||
| 70 | |||
| 71 | ### Captive Portal Detection Fix | 64 | ### Captive Portal Detection Fix |
| 72 | - [x] Added DoT reject server on port 853 (TCP RST forces DNS fallback to port 53) | 65 | - [x] Added DoT reject server on port 853 (TCP RST forces DNS fallback to port 53) |
| 73 | - [x] DNS hijack now returns NXDOMAIN for ALL non-A query types (prevents DNS leaks) | 66 | - [x] DNS hijack now returns NXDOMAIN for ALL non-A query types (prevents DNS leaks) |
| @@ -75,16 +68,73 @@ | |||
| 75 | - [x] Explicit 302 redirect handlers for all captive detection URIs (/generate_204, /hotspot-detect.html, etc.) | 68 | - [x] Explicit 302 redirect handlers for all captive detection URIs (/generate_204, /hotspot-detect.html, etc.) |
| 76 | - [x] HTTP request logging for captive detection endpoints | 69 | - [x] HTTP request logging for captive detection endpoints |
| 77 | - [x] DNS query logging for unauthenticated clients | 70 | - [x] DNS query logging for unauthenticated clients |
| 78 | - [ ] **Needs verification with actual GrapheneOS phone** | 71 | - [x] Verified working with GrapheneOS phone (commit `236b61d`) |
| 79 | 72 | ||
| 80 | ## Phase 3: nucula Wallet + ESP32-to-ESP32 Payments — NOT STARTED | 73 | ## Phase 3: On-Device Wallet + ESP32-to-ESP32 Payments — IN PROGRESS |
| 81 | - [ ] Extract nucula wallet into components/cashu_wallet/ | 74 | ### Wallet Module (wallet.c/h) |
| 82 | - [ ] Replace simple melt with Wallet::receive() | 75 | - [x] `hash_to_curve()` — SHA256 try-and-increment with Cashu domain separator |
| 83 | - [ ] Implement payout.c/h (background melt-to-LN) | 76 | - [x] `point_add()`, `scalar_mul()` — mbedTLS secp256k1 primitives |
| 84 | - [ ] Implement upstream_client.c/h (reseller mode) | 77 | - [x] `random_scalar()` — ESP32 hardware RNG mod curve order |
| 85 | - [ ] ESP32-to-ESP32 payments (ESP32 generates/proves tokens to pay another ESP32 TollGate) | 78 | - [x] Proof storage: `wallet_add_proofs()`, `wallet_remove_proof()`, `wallet_clear()` |
| 86 | - [ ] Tests 28-38 | 79 | - [x] Keyset fetching: `wallet_fetch_keysets()` — GET /v1/keys from mint |
| 80 | - [x] Full swap: `wallet_swap_proofs()` — generates blinded messages, POST /v1/swap, unblinds signatures | ||
| 81 | - [x] Token creation: `wallet_create_token()` — encode proofs as `cashuA` token | ||
| 82 | - [x] Wallet API endpoints: `GET /wallet`, `POST /wallet/swap`, `POST /wallet/send` | ||
| 83 | - [x] Payment flow integration: received proofs added to wallet after session creation | ||
| 84 | - [x] mbedTLS 3.x compatibility (no direct point field access, no point_negate) | ||
| 85 | - [x] Unblinding: `C = C_ + (order - r) * G` approach | ||
| 86 | - [x] Clean build (0 warnings, 0 errors) | ||
| 87 | |||
| 88 | ### Wallet Persistence (wallet_persist.c/h) | ||
| 89 | - [ ] Implement `wallet_persist_save()` — serialize wallet to `/spiffs/wallet.json` | ||
| 90 | - [ ] Implement `wallet_persist_load()` — deserialize wallet from `/spiffs/wallet.json` on boot | ||
| 91 | - [ ] Add `persist_threshold_sats` to config.json and config struct | ||
| 92 | - [ ] Threshold logic: only persist when `balance >= persist_threshold_sats` | ||
| 93 | - [ ] Wire `wallet_persist_save()` into wallet mutations (add_proofs, swap, create_token) | ||
| 94 | - [ ] Wire `wallet_persist_load()` into `wallet_init()` | ||
| 95 | - [ ] Build and verify clean compile | ||
| 96 | |||
| 97 | ### Hardware Testing | ||
| 98 | - [ ] Flash Board A, verify wallet boot (keyset fetch succeeds) | ||
| 99 | - [ ] Pay Board A with Cashu token, verify proofs stored (GET /wallet) | ||
| 100 | - [ ] Test POST /wallet/swap on Board A | ||
| 101 | - [ ] Test POST /wallet/send on Board A, verify token is valid | ||
| 102 | - [ ] Verify persistence survives reboot on Board A | ||
| 103 | - [ ] Flash Board B with TollGate firmware | ||
| 104 | - [ ] Load Board B with balance (pay it a token) | ||
| 105 | - [ ] Board B creates send token via POST /wallet/send | ||
| 106 | - [ ] Cross-board payment: Board B token → Board A (laptop relay) | ||
| 107 | - [ ] Verify both boards show correct balances after cross-board payment | ||
| 108 | |||
| 109 | ### Tests 25-27 (deferred from Phase 2, need Board B) | ||
| 110 | - [ ] Test 25: Two clients pay independently (laptop + Board B) | ||
| 111 | - [ ] Test 26: Client isolation (only payer gets internet) | ||
| 112 | - [ ] Test 27: Full e2e: portal → pay → browse | ||
| 113 | |||
| 114 | ### Tests 28-38 (Phase 3 specific) | ||
| 115 | - [ ] Test 28: Wallet boot (keysets loaded) | ||
| 116 | - [ ] Test 29: Receive via wallet (balance incremented) | ||
| 117 | - [ ] Test 30: Wallet swap (same balance, new proofs) | ||
| 118 | - [ ] Test 31: Wallet send (valid cashuA token) | ||
| 119 | - [ ] Test 32: Persistence survives reboot | ||
| 120 | - [ ] Test 33: Cross-board payment | ||
| 121 | - [ ] Test 34: 5 consecutive payments | ||
| 122 | - [ ] Test 35: Stress: rapid pay/expire | ||
| 123 | |||
| 124 | ### Automated Tests | ||
| 125 | - [ ] Write tests/phase3.mjs (wallet endpoint tests + cross-board) | ||
| 126 | - [ ] All Phase 3 tests passing | ||
| 87 | 127 | ||
| 88 | ## Phase 4: ESP32-to-OpenWRT TollGate Interop — NOT STARTED | 128 | ## Phase 4: ESP32-to-OpenWRT TollGate Interop — NOT STARTED |
| 89 | - [ ] ESP32 pays OpenWRT TollGate using Cashu tokens | 129 | - [ ] ESP32 pays OpenWRT TollGate using Cashu tokens |
| 90 | - [ ] Interoperability testing with existing OpenWRT TollGate on enx00e04c683d2d | 130 | - [ ] Interoperability testing with existing OpenWRT TollGate on enx00e04c683d2d |
| 131 | |||
| 132 | ## Reminders | ||
| 133 | - Do NOT ask for instructions — proceed independently, skip blocked items, work on unblocked ones | ||
| 134 | - Board A: `/dev/ttyACM0`, MAC `94:a9:90:2e:37:7c`, SSID `TollGate-377C`, AP IP `10.55.85.1` | ||
| 135 | - Board B: `/dev/ttyACM1`, MAC `fc:01:2c:c5:50:50` | ||
| 136 | - testnut.cashu.space auto-pays invoices: `cashu -h https://testnut.cashu.space invoice <amount>` | ||
| 137 | - Token generation: `cashu -h https://testnut.cashu.space send --legacy <amount> 2>&1 | grep '^cashuA' | head -1` | ||
| 138 | - sudo password: `c03rad0r123` | ||
| 139 | - Commit + push whenever tests pass | ||
| 140 | - Proceed to Phase 4 after completing Phase 3 | ||