Never roll back an ambiguous broadcast as if it had been refused (B-76, TX-04, TX-05, TX-20, TX-21, BET-16, BET-17, BET-23, WDR-11, WDR-12, WDR-16, API-13)
place_bet and request_withdrawal released everything on any exception from the broadcast, including the 15s timeout and a connection dropped after the write. The tx may have propagated: a bet's sats landed in the pool with no participant, a withdrawal left while its row said failed and its coins were spendable again. - client: ElectrumRpcError (subclass of ElectrumError) is raised only for a reply carrying a JSON-RPC `error`; timeouts, drops and write errors stay plain ElectrumError. - tx/outcome.py:broadcast_with_verdict sorts a broadcast into accepted / refused / uncertain. A refusal is verified first: ElectrumListener.transaction_known asks blockchain.transaction.get of the active server and every other one (10s each) — any server returning the tx means accepted; the active server plus MIN_QUORUM_RESPONSES others all denying it means refused (ALLOW_NO_QUORUM keeps the reduced rule); too few answers is uncertain. "already known" wording is only a shortcut that skips the check. With no network to ask, a refusal is uncertain. - place_bet / request_withdrawal / _trigger_payout use it: only a verified refusal releases; uncertain leaves the rows building for the reconciler, publishes SSE and POST /bets and POST /withdrawals answer 202 with status building; "already known" promotes a payout (payout_sent) like any accepted broadcast. - An accepted transaction is fanned out to every other server (ElectrumListener.fan_out_transaction, best effort, off the caller's path). The RBF replacement in bump_fee is left for B-77, which rewrites that function. Tests changed because the spec changed: the existing "refused broadcast" tests raised a bare RuntimeError (now an uncertain outcome) and are made explicit refusals (rpc_error) with a network that denies the tx; direct route calls pass a Response; FakePayoutListener gains the network surface. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
13cb66cc06
commit
2b6ddb019b
27 files changed
+965
-150
No files matched your search
@@ -203,11 +203,7 @@ flowchart TB
|
||||
registro di audit, notifica SSE; la risposta è `broadcast_failed`
|
||||
(HTTP 502).
|
||||
*Codice:* `app/bets/service.py:_release_failed_bet`, `app/api/routes/bets.py:create_bet` ·
|
||||
*Test:* `tests/unit/test_bets.py`
|
||||
*Stato:* da implementare (B-76) · *Oggi:* tutto viene annullato per
|
||||
qualunque errore dell'invio, anche un timeout o una connessione caduta
|
||||
dopo la scrittura, quando la transazione può essere già in rete: i suoi
|
||||
sat arrivano al montepremi senza partecipante.
|
||||
*Test:* `tests/unit/test_bets.py`, `tests/unit/test_broadcast_outcome.py`
|
||||
- **[BET-23]** Se l'invio ha un **esito incerto**
|
||||
([TX-20](transazioni-e-riconciliazione.md#invio-rifiutato)), partecipante e
|
||||
transazione in corso restano `building` con le monete ancora segnate come
|
||||
@@ -216,9 +212,7 @@ flowchart TB
|
||||
limite di [BET-06] e tiene il round in `closing` finché il riconciliatore
|
||||
non lo promuove o lo abbandona.
|
||||
*Codice:* `app/bets/service.py:place_bet`, `app/api/routes/bets.py:create_bet` ·
|
||||
*Test:* —
|
||||
*Stato:* da implementare (B-76) · *Oggi:* un esito incerto è trattato come
|
||||
un rifiuto ([BET-16]).
|
||||
*Test:* `tests/unit/test_broadcast_outcome.py`
|
||||
- **[BET-17]** Se l'invio è accettato — anche con un rifiuto "già nota"
|
||||
([TX-04](transazioni-e-riconciliazione.md#invio-rifiutato)) — il
|
||||
partecipante passa a `broadcast`, la transazione in corso a `pending`, si
|
||||
@@ -229,9 +223,7 @@ flowchart TB
|
||||
scommessa promossa dal riconciliatore scrive `bet_placed` in quel momento
|
||||
([TX-16](transazioni-e-riconciliazione.md#riconciliazione)).
|
||||
*Codice:* `app/bets/service.py:place_bet`, `app/api/routes/bets.py:create_bet` ·
|
||||
*Test:* `tests/unit/test_bets.py`
|
||||
*Stato:* da implementare (B-76) · *Oggi:* un "già nota" è trattato come un
|
||||
rifiuto e la transazione va solo al server attivo.
|
||||
*Test:* `tests/unit/test_bets.py`, `tests/unit/test_broadcast_outcome.py`
|
||||
- **[BET-18]** Alla conferma il partecipante passa a `confirmed`
|
||||
([TX-07](transazioni-e-riconciliazione.md#conferma)). Solo le scommesse
|
||||
**confermate** partecipano all'estrazione; il round, allo scadere, aspetta
|
||||
@@ -324,12 +316,7 @@ solo se la rete rifiuta esplicitamente, RBF se non conferma in tempo.
|
||||
`failure_reason`); si scrive `withdrawal_broadcast_failed`, si invia una
|
||||
notifica SSE e la risposta è `broadcast_failed` (HTTP 502).
|
||||
*Codice:* `app/withdrawals/service.py:_release_failed_withdrawal`, `app/api/routes/withdrawals.py:create_withdrawal` ·
|
||||
*Test:* `tests/unit/test_withdrawals.py` (nessun test verifica l'evento
|
||||
`withdrawal_broadcast_failed`)
|
||||
*Stato:* da implementare (B-76) · *Oggi:* tutto viene annullato per
|
||||
qualunque errore dell'invio, anche un timeout o una connessione caduta
|
||||
dopo la scrittura: la transazione può essere partita mentre il prelievo
|
||||
risulta `failed` e le monete di nuovo spendibili.
|
||||
*Test:* `tests/unit/test_withdrawals.py`, `tests/unit/test_broadcast_outcome.py`
|
||||
- **[WDR-16]** Se l'invio ha un **esito incerto**
|
||||
([TX-20](transazioni-e-riconciliazione.md#invio-rifiutato)), prelievo e
|
||||
transazione in corso restano `building` con le monete ancora segnate come
|
||||
@@ -337,9 +324,7 @@ solo se la rete rifiuta esplicitamente, RBF se non conferma in tempo.
|
||||
`amount_sent_sats` e `status` = `building`; sarà il riconciliatore a
|
||||
promuoverlo o abbandonarlo.
|
||||
*Codice:* `app/withdrawals/service.py:request_withdrawal`, `app/api/routes/withdrawals.py:create_withdrawal` ·
|
||||
*Test:* —
|
||||
*Stato:* da implementare (B-76) · *Oggi:* un esito incerto è trattato come
|
||||
un rifiuto ([WDR-11]).
|
||||
*Test:* `tests/unit/test_broadcast_outcome.py`
|
||||
- **[WDR-12]** Se l'invio è accettato — anche con un rifiuto "già nota"
|
||||
([TX-04](transazioni-e-riconciliazione.md#invio-rifiutato)) — il prelievo
|
||||
passa a `broadcast`, la transazione in corso a `pending`, si scrive
|
||||
@@ -350,9 +335,7 @@ solo se la rete rifiuta esplicitamente, RBF se non conferma in tempo.
|
||||
`status`. Alla conferma il prelievo passa a `confirmed`
|
||||
([TX-07](transazioni-e-riconciliazione.md#conferma)).
|
||||
*Codice:* `app/withdrawals/service.py:request_withdrawal`, `app/withdrawals/confirmation.py:_on_withdrawal_confirmed` ·
|
||||
*Test:* `tests/unit/test_withdrawals.py`
|
||||
*Stato:* da implementare (B-76) · *Oggi:* un "già nota" è trattato come un
|
||||
rifiuto e la transazione va solo al server attivo.
|
||||
*Test:* `tests/unit/test_withdrawals.py`, `tests/unit/test_broadcast_outcome.py`
|
||||
|
||||
## Scommessa e prelievo insieme
|
||||
|
||||
|
||||
Reference in new issue
Block a user