Close the smaller state inconsistencies (B-96, BET-02, BET-07, WDR-01, WDR-15, TX-16, TX-18, TX-27, TX-29, DEP-15, DEP-16, AUD-30, AUD-46, DAT-22)

- bet/withdrawal routes read listener.client after taking the user lock
- a reduced full-balance withdrawal is checked against the minimum again
- a bet covered only by pending balance answers balance_pending_confirmation
- a recovered bet/withdrawal/payout writes bet_placed/withdrawal_sent/payout_sent
- an abandoned payout clears winner/fee/reserve amounts with payout_txid
- the reconciler pings SSE only for rows it changed
- closing_stalled every 30 minutes past the deadline; closing_waiting_since on /rounds/current
- one refresh per user at a time; a failed refresh no longer ends the Electrum session

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-18 01:08:14 +02:00
1 parent 24e5fc3d8b
commit 9520c80dcf
23 files changed
+445 -151

No files matched your search

+4 -11
View File
@@ -38,9 +38,7 @@ Prima dei controlli valgono due condizioni di contorno:
un'attesa sul blocco durante una riconnessione non fa usare una connessione
già chiusa; il controllo di [BET-01] si fa su quel valore.
*Codice:* `app/api/routes/bets.py:create_bet`, `app/tx/locks.py:UserLocks.acquire` ·
*Test:* —
*Stato:* da implementare (B-96) · *Oggi:* `listener.client` viene letto e
controllato prima di acquisire il blocco.
*Test:* `tests/unit/test_state_consistency.py`
Una scommessa (`POST /bets`, senza corpo) passa solo se **tutte** queste
condizioni sono vere, verificate nell'ordine della tabella; altrimenti viene
@@ -54,7 +52,7 @@ cifra. Il **costo** è quello del round ([BET-21]):
| **[BET-05]** | L'utente non ha già una scommessa in questo round (in qualunque stato) | `already_betting` |
| **[BET-06]** | Il round ha meno di 400 partecipanti (`MAX_PARTICIPANTS_PER_ROUND`), contando anche quelli non ancora confermati | `round_full` (parametro `max_participants`) |
| **[BET-20]** | Se il round ha un tetto al montepremi (`max_round_pool_sats` fotografato all'apertura, [RND-05](ciclo-di-vita-del-round.md#0--apertura)) diverso da 0, la somma degli importi netti dei partecipanti del round, contando anche quelli non ancora confermati, è minore del tetto. | `round_full` |
| **[BET-07]** | La somma delle monete confermate e non spese copre il costo della scommessa | `balance_pending_confirmation` se lo coprirebbe il saldo comprensivo di quanto è in corso (parametro `pending_sats`), altrimenti `insufficient_balance` (parametro `required_sats`), come per il prelievo ([WDR-05](#prelievo)). *Stato:* da implementare (B-96) · *Oggi:* sempre `insufficient_balance`, anche quando il saldo mostrato all'utente copre la scommessa |
| **[BET-07]** | La somma delle monete confermate e non spese copre il costo della scommessa | `balance_pending_confirmation` se lo coprirebbe il saldo comprensivo di quanto è in corso (parametro `pending_sats`), altrimenti `insufficient_balance` (parametro `required_sats`), come per il prelievo ([WDR-05](#prelievo)). |
| **[BET-08]** | Il costo si copre con al massimo 50 monete (`MAX_TX_INPUTS`), scelte dalla più grande fino a coprirlo | `too_many_inputs` (parametro `max_inputs`) |
| **[BET-09]** | Le monete scelte ([BET-08]) superano il costo di almeno 294 sat (`DUST_LIMIT_SATS`), che restano come resto | `balance_leaves_no_change` (parametro `required_extra_sats`) |
| **[BET-10]** | Il costo meno la commissione di rete resta positivo e di almeno 294 sat | `amount_below_network_fee`, `amount_below_dust_limit` |
@@ -241,9 +239,7 @@ esterno scelto dall'utente.
avviene tenendo il **blocco per-utente**; il client della rete viene letto
e controllato dopo aver acquisito il blocco, come in [BET-02].
*Codice:* `app/api/routes/withdrawals.py:create_withdrawal`, `app/tx/locks.py:UserLocks.acquire` ·
*Test:* — (nessun test verifica `network_unavailable`)
*Stato:* da implementare (B-96) · *Oggi:* `listener.client` viene letto e
controllato prima di acquisire il blocco.
*Test:* `tests/unit/test_state_consistency.py`
Le condizioni sono verificate nell'ordine della tabella; un rifiuto risponde
HTTP 400 prima che si muova qualunque cifra:
@@ -290,10 +286,7 @@ solo se la rete rifiuta esplicitamente, RBF se non conferma in tempo.
prelievo è rifiutato con `amount_below_minimum` (parametro `minimum_sats`)
prima che si muova qualunque cifra.
*Codice:* `app/withdrawals/service.py:request_withdrawal` ·
*Test:* —
*Stato:* da implementare (B-96) · *Oggi:* il minimo è verificato solo
sull'importo richiesto, quindi un prelievo dell'intero saldo può inviare
fino a 294 sat meno del minimo.
*Test:* `tests/unit/test_state_consistency.py`
- **[WDR-10]** Prima dell'invio, in un'unica transazione del database
([TX-28](transazioni-e-riconciliazione.md#il-principio-prima-si-scrive-poi-si-invia)),
le monete spese vengono segnate come tali, il saldo ricalcolato, e si