feat(machine): record failed dispenses and per-cassette tracking

Failed dispenses (sats debited, cash not dispensed) were invisible —
transactions only recorded on 'complete'. Now records on 'dispenseError'
with status ('dispense_error'|'partial'|'complete'), error message, and
per-cassette detail.

Also fixes a bug in both HAL services where dispense results were mapped
by amounts-array index instead of cassette position, causing swapped
denomination counts when cassette order differs from request order.

Schema v3→v4: adds status/error columns to transactions, new
cassette_bills table for per-cassette provisioned/dispensed/rejected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-03-22 17:53:10 -04:00
commit a21865ff20
10 changed files with 287 additions and 53 deletions

View file

@ -149,16 +149,16 @@ alice-pay <invoice>
For advanced testing with multiple Lightning implementations, use the regtest environment at `~/dev/local/docker/regtest`. This provides:
| Service | Description |
| ----------------- | ------------------------------------- |
| 4 LND nodes | lnd-1 (hub), lnd-2 (Boltz), lnd-3 (LNbits), lnd-4 (standalone) |
| 3 CLN nodes | Core Lightning with REST/gRPC |
| 1 Eclair node | ACINQ Eclair implementation |
| LNbits | Lightning wallet platform (port 5001) |
| Boltz | Submarine swaps (port 9001) |
| Electrs | Electrum server (port 3002) |
| Lightning Terminal| Web UI for lnd-1 (port 8443) |
| Elements/Liquid | Sidechain (port 18884) |
| Service | Description |
| ------------------ | -------------------------------------------------------------- |
| 4 LND nodes | lnd-1 (hub), lnd-2 (Boltz), lnd-3 (LNbits), lnd-4 (standalone) |
| 3 CLN nodes | Core Lightning with REST/gRPC |
| 1 Eclair node | ACINQ Eclair implementation |
| LNbits | Lightning wallet platform (port 5001) |
| Boltz | Submarine swaps (port 9001) |
| Electrs | Electrum server (port 3002) |
| Lightning Terminal | Web UI for lnd-1 (port 8443) |
| Elements/Liquid | Sidechain (port 18884) |
```bash
# Start regtest environment
@ -289,13 +289,26 @@ Drivers are ported from `lamassu-machine/lib/`:
| ---------- | ------------------------------------------------------------ |
| Validators | id003, ccnet, cashflow_sc, bnr_advance, genmega, hcm2, gsr50 |
| Dispensers | puloon, f56, genmega, hcm2, gsr50 |
| Recyclers | MEI SCR (planned — hardware exists in BATM3, no driver yet) |
| Printers | nippon, zebra, genmega |
### BATM3 Hardware Topology
The GeneralBytes BATM3 has two separate cash-handling units:
| Unit | Hardware | Role | Cassettes |
| ----------------- | ---------- | ---------------------------------- | ------------------------------------ |
| MEI Cash Recycler | MEI SCR | Cash-in (accept bills) + recycling | 2 recycler (60 each) + cashbox (600) |
| F56 Dispenser | Puloon F56 | Cash-out (dispense only) | 2 dispenser cassettes |
**Current status**: MEI used as validator-only (`cashflowSc` driver), F56 for dispensing.
The MEI's recycler cassettes are untapped — accepted bills go to cashbox, not recycled.
When porting:
1. Read JS driver thoroughly
2. Document protocol from JS code
3. Implement Rust version
3. Implement TypeScript version
4. Test against same hardware
5. Use `/hal-check port <driver>` to validate