Give every payout a bump reserve output and refuse configs that cannot pay a round (B-79, RND-21, RND-23, RND-24, RND-27, RND-38, RND-40, DAT-73, API-31, API-36, TX-11, ADR-17, ADR-29)
The pool normally holds exactly the round's bet outputs and the payout spent them all for exactly the pool amount, so it had no change output: bump_fee could never accelerate it, and a payout stuck in a mempool (never abandoned while a server knows it) kept the round in `paying_out` and the whole lottery stopped. - build_payout_transaction always has three outputs: winner, fee address, and a bump reserve of max(DUST_LIMIT_SATS, estimate_vsize(inputs, 3) * 10 sat/vB) paid to the reserve address m/84'/746'/0'/1/0. The reserve comes out of the platform's 30%, the network fee still out of the winner's 70%. Input value beyond winner share + commission (only rounds opened before the per-round pool address) is folded into the reserve output if >= dust, left to the network fee otherwise. `change_address` is replaced by `reserve_address`. - `rounds.bump_reserve_sats` (Alembic e5a91c3d7b26, plain add_column) is written with the payout intent before the broadcast; /admin/rounds returns it. - bump_fee takes the increase from the reserve output for a payout (_find_reducible_output; a payout already in flight with a change to the pool still falls back to that), never from the winner or fee-address outputs. - payout_config_problem / rounds_can_open (RND-40): a bet amount and fee rate under which a round of 1 or of MAX_PARTICIPANTS_PER_ROUND participants could not cover the network fee, the reserve and dust outputs opens no round (lottery_configured=false); PUT /admin/config refuses such a bet amount or fee rate with 422 config_not_payable, judged on the resulting values, since the payout reads the fee rate live. Ambiguity resolved: the reserve output and any excess input are one output (RND-23 says one output to the reserve address), and bump_reserve_sats records that output's initial value. DAT-22's "abandon clears all amounts" stays with B-96. Tests changed because the spec changed: the four test_payout_builder tests asserted the old two/three-output shape with change to the pool and the fee address receiving the whole 30%; the whitespace fee_address test now spells out the amounts rounds_can_open reads. New: reserve sizing, payout with 1 and with 400 participants (inputs = outputs + fee to the satoshi), exact commission/reserve/dust boundary, excess folded or left to the fee, a real payout bumped out of its reserve with the other outputs byte-identical and bumped five times, the old reserve-less shape still raising RbfError, the scheduler output addresses and recorded amounts, payability boundaries found independently by bisection, no round / unconfigured flag for an unpayable config, a round in progress surviving it, PUT /admin/config refusals and the migration column. Mutation-checked: dropping the reserve lookup in bump_fee and the payability check in rounds_can_open each fail tests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
473d541ab5
commit
daa85cf4f0
27 files changed
+684
-139
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# ADR-17 — La commissione del pagamento esce dalla quota del vincitore; la riserva per l'RBF esce dalla quota della piattaforma
|
||||
|
||||
- **Stato:** Accettata — implementazione da completare (B-79)
|
||||
- **Stato:** Accettata
|
||||
- **Area:** Economia del gioco
|
||||
- **Fonte di verità per:** la scelta di progetto descritta qui sotto; i requisiti (`PREFISSO-NN`) che la attuano stanno nei documenti di dominio, indicati nelle *Conseguenze*.
|
||||
|
||||
@@ -30,6 +30,4 @@ commissione verso `fee_address` è quindi il 30% meno la riserva.
|
||||
[ciclo-di-vita-del-round.md](../ciclo-di-vita-del-round.md#4--pagamento) (B-79).
|
||||
|
||||
*Codice:* `app/wallet/psbt_builder.py:build_payout_transaction` ·
|
||||
*Test:* `tests/unit/test_payout_builder.py::test_payout_deducts_fee_only_from_winner_share`
|
||||
*Stato:* da implementare (B-79) · *Oggi:* nessuna riserva: il 30% va intero a
|
||||
`fee_address` e, nel caso normale, il pagamento non ha resto.
|
||||
*Test:* `tests/unit/test_payout_builder.py::test_payout_deducts_fee_only_from_winner_share`, `tests/unit/test_payout_builder.py::test_payout_at_the_participant_cap_pays_the_reserve_out_of_the_platform_share`, `tests/unit/test_broadcast.py::test_a_payout_is_bumped_out_of_its_reserve_output_and_nothing_else`
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-29 — Senza `fee_address` non si apre nessun round
|
||||
|
||||
- **Stato:** Accettata — implementazione da completare (B-79)
|
||||
- **Stato:** Accettata
|
||||
- **Area:** Round ed estrazione
|
||||
- **Fonte di verità per:** la scelta di progetto descritta qui sotto; i requisiti (`PREFISSO-NN`) che la attuano stanno nei documenti di dominio, indicati nelle *Conseguenze*.
|
||||
|
||||
@@ -19,6 +19,4 @@ round impagabile va aggiunta nello stesso punto.
|
||||
l'RBF e una commissione sopra la soglia di polvere ([ADR-17](ADR-17-commissione-pagamento-esce-dalla-quota-vincitore.md), B-79).
|
||||
|
||||
*Codice:* `app/rounds/service.py:rounds_can_open` · `app/rounds/config.py:get_round_config` ·
|
||||
*Test:* `tests/unit/test_rounds_service.py::test_withholds_new_round_while_no_fee_address_is_configured`, `tests/unit/test_rounds_service.py::test_a_round_in_progress_survives_the_fee_address_being_cleared`, `tests/unit/test_rounds_route.py::test_lottery_configured_flags_a_missing_fee_address`
|
||||
*Stato:* da implementare (B-79) · *Oggi:* `rounds_can_open` controlla solo
|
||||
`fee_address`.
|
||||
*Test:* `tests/unit/test_rounds_service.py::test_withholds_new_round_while_no_fee_address_is_configured`, `tests/unit/test_rounds_service.py::test_a_round_in_progress_survives_the_fee_address_being_cleared`, `tests/unit/test_rounds_route.py::test_lottery_configured_flags_a_missing_fee_address`, `tests/unit/test_rounds_service.py::test_no_round_opens_while_the_config_could_not_pay_one`, `tests/unit/test_rounds_route.py::test_lottery_configured_is_false_when_the_amounts_could_not_pay_a_round`
|
||||
+8
-3
@@ -789,7 +789,12 @@ dei parametri operativi in
|
||||
- **Risposta `200`:** la configurazione completa, come
|
||||
[API-30](#get-adminconfig).
|
||||
- **Errori:** `422` per qualunque vincolo violato (nessun campo viene
|
||||
applicato).
|
||||
applicato). Una modifica di `bet_amount_sats` o `fee_rate_sat_vb` con cui
|
||||
un round non si potrebbe pagare ([RND-40](ciclo-di-vita-del-round.md#4--pagamento))
|
||||
dà `422` con `detail = {"code": "config_not_payable", "message", "params":
|
||||
{"reason"}}`, dove `reason` è `winner_share_below_network_fee` o
|
||||
`commission_below_dust_limit`; il controllo usa i valori risultanti dopo
|
||||
la modifica.
|
||||
- **Effetti:** scrive `config_updated` con i valori prima/dopo dei soli
|
||||
campi davvero cambiati; nessuna scrittura se nulla cambia. Durata, pausa
|
||||
tra round, costo della scommessa, `fee_address` e `draw_confirmations` si
|
||||
@@ -888,8 +893,8 @@ dei parametri operativi in
|
||||
completa di un round è in [API-45](#get-roundsround_idproof).
|
||||
*Codice:* `app/api/routes/admin.py:list_rounds` ·
|
||||
*Test:* `tests/unit/test_admin.py`
|
||||
*Stato:* da implementare (B-79, B-82), per gli ultimi due campi · *Oggi:*
|
||||
la lista si ferma a `draw_height`.
|
||||
*Stato:* da implementare (B-82), per `payout_blocked_at` · *Oggi:*
|
||||
la lista si ferma a `bump_reserve_sats`.
|
||||
|
||||
#### GET /admin/audit-log
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ sono vuoti e non sono elencati.
|
||||
| `app/wallet/hd.py` | Generazione della master key, derivazione di indirizzi/chiavi utente (`0/i`), del montepremi di ogni round (`2/<round_id>`, B-80) e dell'indirizzo di riserva (`1/0`), `derive_round_pool_key` che sceglie la chiave che controlla l'indirizzo salvato sul round, export WIF; la chiave d'account decifrata resta in cache nel processo |
|
||||
| `app/wallet/address.py` | `is_valid_plm_address`: checksum bech32 + HRP `plm` |
|
||||
| `app/wallet/balance.py` | `recompute_balance` (saldo confermato), `compute_pending_balance` (saldo mostrato) — vedi [registrazione-e-depositi.md](registrazione-e-depositi.md) |
|
||||
| `app/wallet/psbt_builder.py` | Selezione UTXO, costruzione e firma di scommessa, prelievo e pagamento; limiti `MAX_TX_INPUTS`, `MAX_PAYOUT_TX_INPUTS`, `MAX_PARTICIPANTS_PER_ROUND`, `DUST_LIMIT_SATS`. *Da implementare:* uscita di riserva per l'RBF sul pagamento (B-79) |
|
||||
| `app/wallet/psbt_builder.py` | Selezione UTXO, costruzione e firma di scommessa, prelievo e pagamento; limiti `MAX_TX_INPUTS`, `MAX_PAYOUT_TX_INPUTS`, `MAX_PARTICIPANTS_PER_ROUND`, `DUST_LIMIT_SATS`; l'uscita di riserva per l'RBF sul pagamento (`payout_bump_reserve`, B-79) |
|
||||
|
||||
### Rete Electrum
|
||||
|
||||
|
||||
@@ -94,8 +94,6 @@ richiesta di scommessa (vedi
|
||||
|
||||
*Codice:* `app/rounds/service.py:open_new_round_if_needed`, `app/rounds/service.py:rounds_can_open` ·
|
||||
*Test:* `tests/unit/test_rounds_service.py`
|
||||
*Stato:* da implementare (B-79) · *Oggi:* `rounds_can_open` controlla solo
|
||||
che `fee_address` non sia vuoto.
|
||||
- **[RND-05]** All'apertura il round **fotografa** i parametri correnti che lo
|
||||
governano: durata (`round_duration_seconds`, 600s predefiniti), pausa
|
||||
successiva (`round_cooldown_seconds`, 30s predefiniti), costo della
|
||||
@@ -413,9 +411,10 @@ del montepremi del round.
|
||||
`/admin`.
|
||||
*Codice:* `app/rounds/service.py:winner_share`, `app/rounds/scheduler.py:RoundScheduler._trigger_payout` ·
|
||||
*Test:* `tests/unit/test_payout_builder.py`
|
||||
*Stato:* da implementare (B-79, B-82) · *Oggi:* la ripartizione si calcola
|
||||
su `pool_amount_sats` fissato all'estrazione e l'indirizzo delle
|
||||
commissioni riceve l'intera quota del 30%.
|
||||
*Stato:* da implementare (B-82) · *Oggi:* la ripartizione si calcola
|
||||
su `pool_amount_sats` fissato all'estrazione, senza la verifica di
|
||||
[RND-39]; la riserva per l'RBF è già tolta dalla quota delle commissioni
|
||||
(B-79).
|
||||
- **[RND-22]** Il pagamento spende le uscite delle scommesse dei partecipanti
|
||||
del round **verificate** ([RND-39]), all'indirizzo del montepremi del round
|
||||
([RND-35]), e nient'altro: altre monete arrivate a quell'indirizzo non
|
||||
@@ -447,11 +446,10 @@ del montepremi del round.
|
||||
si costruisce (`winner_share_below_network_fee`,
|
||||
`commission_below_dust_limit`).
|
||||
*Codice:* `app/wallet/psbt_builder.py:build_payout_transaction` ·
|
||||
*Test:* `tests/unit/test_payout_builder.py` (nessun test copre `commission_below_dust_limit`)
|
||||
*Stato:* da implementare (B-79, B-95) · *Oggi:* la terza uscita è un resto
|
||||
verso l'indirizzo del montepremi, presente solo se è di almeno 294 sat e
|
||||
assente quando le monete spese sono esattamente le scommesse del round; non c'è riserva, la soglia di 294 sat si applica all'intera
|
||||
quota delle commissioni e `fee_address` è letto dalla configurazione.
|
||||
*Test:* `tests/unit/test_payout_builder.py::test_payout_deducts_fee_only_from_winner_share`, `tests/unit/test_payout_builder.py::test_payout_folds_excess_input_value_into_the_reserve_output`, `tests/unit/test_payout_builder.py::test_payout_leaves_a_dust_sized_excess_to_the_network_fee`, `tests/unit/test_payout_builder.py::test_payout_with_one_participant_at_the_smallest_viable_pool`
|
||||
*Stato:* da implementare (B-95) · *Oggi:* `fee_address` è letto dalla
|
||||
configurazione corrente al momento del pagamento, non fotografato
|
||||
all'apertura.
|
||||
- **[RND-24]** **Prima si scrive, poi si invia**: prima dell'invio il round
|
||||
registra importo del vincitore (`winner_amount_sats`), commissioni
|
||||
(`fee_amount_sats`), riserva per l'RBF (`bump_reserve_sats`, [RND-38]) e
|
||||
@@ -464,9 +462,7 @@ del montepremi del round.
|
||||
(TX-18, B-76, B-78); solo un abbandono libera il round per un nuovo
|
||||
tentativo.
|
||||
*Codice:* `app/rounds/scheduler.py:RoundScheduler._trigger_payout` ·
|
||||
*Test:* `tests/unit/test_scheduler.py`
|
||||
*Stato:* da implementare (B-79) · *Oggi:* tutto come descritto, tranne
|
||||
`bump_reserve_sats`, che non esiste.
|
||||
*Test:* `tests/unit/test_scheduler.py`, `tests/unit/test_scheduler.py::test_trigger_payout_always_carries_a_bump_reserve_to_the_reserve_address`
|
||||
- **[RND-25]** Ogni tentativo che si ferma prima di un invio riuscito scrive
|
||||
un evento `payout_failed` con il motivo: `electrum client not connected`,
|
||||
`no fee_address configured`, `winner user not found`,
|
||||
@@ -496,13 +492,8 @@ del montepremi del round.
|
||||
riducendo l'uscita di riserva ([RND-38]); le uscite del vincitore e delle
|
||||
commissioni non cambiano
|
||||
([TX-11](transazioni-e-riconciliazione.md#rbf-accelerare-una-transazione-lenta)).
|
||||
*Codice:* `app/tx/broadcast.py:bump_fee`
|
||||
*Stato:* da implementare (B-79) · *Oggi:* l'RBF può ridurre solo il resto
|
||||
verso il montepremi, che non esiste quando le monete spese sono
|
||||
esattamente le scommesse del round: un pagamento bloccato nel
|
||||
mempool non si può accelerare, e poiché il server lo conosce non viene
|
||||
nemmeno abbandonato, quindi il round resta in `paying_out` e nessun nuovo
|
||||
round si apre.
|
||||
*Codice:* `app/tx/broadcast.py:bump_fee`, `app/tx/broadcast.py:_find_reducible_output` ·
|
||||
*Test:* `tests/unit/test_broadcast.py::test_a_payout_is_bumped_out_of_its_reserve_output_and_nothing_else`, `tests/unit/test_broadcast.py::test_a_payout_can_be_bumped_repeatedly_while_the_reserve_lasts`
|
||||
- **[RND-28]** Quando il pagamento è confermato il round passa a `closed` e
|
||||
`closed_at` viene riscritto con l'orario della conferma: da lì parte la
|
||||
pausa prima del round successivo. Il vincitore si vede accreditare la
|
||||
@@ -516,9 +507,8 @@ del montepremi del round.
|
||||
sat/vB è la costante `PAYOUT_BUMP_RESERVE_SAT_VB`. La riserva è tolta
|
||||
dalla quota delle commissioni, mai da quella del vincitore; è ciò che
|
||||
l'RBF riduce ([RND-27]).
|
||||
*Codice:* `app/wallet/psbt_builder.py:build_payout_transaction`, `app/wallet/hd.py:derive_pool_key` ·
|
||||
*Test:* —
|
||||
*Stato:* da implementare (B-79) · *Oggi:* non esiste alcuna riserva.
|
||||
*Codice:* `app/wallet/psbt_builder.py:build_payout_transaction`, `app/wallet/psbt_builder.py:payout_bump_reserve`, `app/wallet/hd.py:derive_pool_key` ·
|
||||
*Test:* `tests/unit/test_payout_builder.py`, `tests/unit/test_scheduler.py::test_trigger_payout_always_carries_a_bump_reserve_to_the_reserve_address`
|
||||
- **[RND-39]** **Verifica prima del pagamento**: prima di costruire il
|
||||
pagamento, le uscite delle scommesse dei partecipanti vengono verificate
|
||||
all'indirizzo del montepremi del round con il quorum dei server
|
||||
@@ -564,11 +554,8 @@ del montepremi del round.
|
||||
così un aumento della commissione di rete a round in corso non può rendere
|
||||
impagabile il round (il pagamento legge `fee_rate_sat_vb` al momento della
|
||||
costruzione).
|
||||
*Codice:* `app/rounds/service.py:rounds_can_open`, `app/api/routes/admin.py` ·
|
||||
*Test:* —
|
||||
*Stato:* da implementare (B-79) · *Oggi:* `rounds_can_open` controlla solo
|
||||
che `fee_address` non sia vuoto, e `PUT /admin/config` non fa verifiche di
|
||||
pagabilità.
|
||||
*Codice:* `app/rounds/service.py:payout_config_problem`, `app/rounds/service.py:rounds_can_open`, `app/api/routes/admin.py:update_config` ·
|
||||
*Test:* `tests/unit/test_rounds_service.py::test_payout_config_problem_at_both_extremes`, `tests/unit/test_rounds_service.py::test_no_round_opens_while_the_config_could_not_pay_one`, `tests/unit/test_admin.py::test_config_refuses_a_change_that_would_make_a_round_unpayable`, `tests/unit/test_admin.py::test_a_fee_rate_raised_mid_round_cannot_make_the_round_unpayable`
|
||||
|
||||
## Tempi reali
|
||||
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ implementato il [piano delle correzioni](piano-correzioni.md).
|
||||
tetto al fee rate (`MAX_FEE_RATE_SAT_VB`); abbandono dopo 6 ore per ciò che
|
||||
sparisce dalla rete; ritentativo automatico del pagamento dopo un
|
||||
abbandono.
|
||||
- *Stato:* da chiudere con B-79 e B-87.
|
||||
- *Stato:* da chiudere con B-87 (la riserva del pagamento, B-79, c'è).
|
||||
- *Per chiuderlo:* ogni pagamento porta un'uscita di riserva che
|
||||
l'accelerazione riduce ([ADR-17](adr/ADR-17-commissione-pagamento-esce-dalla-quota-vincitore.md), B-79); un aumento che lascerebbe un
|
||||
resto sotto `DUST_LIMIT_SATS` elimina il resto e ne versa il valore in
|
||||
|
||||
+13
-15
@@ -115,7 +115,7 @@ erDiagram
|
||||
bigint pool_amount_sats
|
||||
bigint winner_amount_sats
|
||||
bigint fee_amount_sats
|
||||
bigint bump_reserve_sats "da implementare"
|
||||
bigint bump_reserve_sats
|
||||
string payout_txid
|
||||
datetime payout_blocked_at "da implementare"
|
||||
}
|
||||
@@ -446,7 +446,7 @@ restano nella tabella come storico.
|
||||
| `pool_amount_sats` | BigInteger | sì | `NULL` | Montepremi: somma delle scommesse confermate e ancora sulla catena ([DAT-21], [DAT-67]) | `_close_and_draw`, `_complete_draw`; `RoundScheduler._trigger_payout` (atteso, B-82) |
|
||||
| `winner_amount_sats` | BigInteger | sì | `NULL` | Quanto riceve il vincitore (70% meno la commissione di rete) | `RoundScheduler._trigger_payout` |
|
||||
| `fee_amount_sats` | BigInteger | sì | `NULL` | Quanto va a `fee_address`: 30% più il resto dell'arrotondamento, meno `bump_reserve_sats` nello schema atteso | `_trigger_payout` |
|
||||
| `bump_reserve_sats` | BigInteger | sì | `NULL` | Output di riserva per l'RBF del pagamento, verso l'indirizzo di riserva ([DAT-73]) — *da implementare (B-79)* | `_trigger_payout`; `app/tx/broadcast.py:bump_fee` |
|
||||
| `bump_reserve_sats` | BigInteger | sì | `NULL` | Output di riserva per l'RBF del pagamento, verso l'indirizzo di riserva ([DAT-73]) | `_trigger_payout`; `app/tx/broadcast.py:bump_fee` |
|
||||
| `payout_txid` | String(64) | sì | `NULL` | Txid corrente della transazione di pagamento | `_trigger_payout`; `_retarget_txid_references`; `app/tx/reconcile.py:_abandon` |
|
||||
| `payout_blocked_at` | DateTime | sì | `NULL` | Il pagamento è trattenuto perché la scommessa del vincitore non è più sulla catena ([DAT-68]) — *da implementare (B-82)* | `_trigger_payout`; `app/api/routes/admin.py:resume_lottery` |
|
||||
|
||||
@@ -537,10 +537,10 @@ restano nella tabella come storico.
|
||||
`app/tx/broadcast.py:_retarget_txid_references` · `app/tx/reconcile.py:_abandon` ·
|
||||
*Test:* `tests/unit/test_scheduler.py`, `tests/unit/test_broadcast.py`,
|
||||
`tests/unit/test_reconcile.py`
|
||||
*Stato:* da implementare (B-96, B-79) · *Oggi:* l'abbandono azzera solo
|
||||
`payout_txid` e lascia `winner_amount_sats`/`fee_amount_sats` del tentativo
|
||||
abbandonato; non esiste un output di riserva e il bump riduce un resto del
|
||||
pool che normalmente non c'è.
|
||||
*Stato:* da implementare (B-96) · *Oggi:* l'abbandono azzera solo
|
||||
`payout_txid` e lascia `winner_amount_sats`, `fee_amount_sats` e
|
||||
`bump_reserve_sats` del tentativo abbandonato (le riscrive il tentativo
|
||||
successivo).
|
||||
|
||||
- **[DAT-60]** Ogni round ha il proprio indirizzo del montepremi,
|
||||
`pool_address`, derivato all'apertura da `m/84'/746'/0'/2/<id>` e scritto
|
||||
@@ -646,9 +646,8 @@ restano nella tabella come storico.
|
||||
è in [ciclo-di-vita-del-round.md](ciclo-di-vita-del-round.md) (B-79).
|
||||
*Codice:* `app/rounds/scheduler.py:RoundScheduler._trigger_payout` ·
|
||||
`app/wallet/psbt_builder.py:build_payout_transaction` ·
|
||||
`app/tx/broadcast.py:bump_fee` · *Test:* —
|
||||
*Stato:* da implementare (B-79) · *Oggi:* la colonna non esiste; il
|
||||
pagamento non ha di norma alcun resto e quindi non si può accelerare.
|
||||
`app/tx/broadcast.py:bump_fee` ·
|
||||
*Test:* `tests/unit/test_scheduler.py::test_trigger_payout_always_carries_a_bump_reserve_to_the_reserve_address`, `tests/unit/test_broadcast.py::test_a_payout_is_bumped_out_of_its_reserve_output_and_nothing_else`, `tests/unit/test_migrations.py::test_the_bump_reserve_column_exists_at_head_and_is_nullable`
|
||||
|
||||
### `round_participants`
|
||||
|
||||
@@ -1276,11 +1275,10 @@ letti con una scansione completa.
|
||||
`app/rounds/scheduler.py:RoundScheduler._trigger_payout` ·
|
||||
`app/wallet/psbt_builder.py:build_payout_transaction` ·
|
||||
*Test:* `tests/unit/test_payout_builder.py`, `tests/unit/test_scheduler.py`
|
||||
*Stato:* da implementare (B-79, B-82) · *Oggi:* il pagamento sceglie fra
|
||||
tutti gli UTXO confermati dell'indirizzo del round e può avere un resto
|
||||
verso quello stesso indirizzo; non c'è
|
||||
riserva, e vale `pool_amount_sats` = `winner_amount_sats` +
|
||||
`fee_amount_sats` + commissione di rete.
|
||||
*Stato:* da implementare (B-82) · *Oggi:* il pagamento sceglie fra
|
||||
tutti gli UTXO confermati dell'indirizzo del round, senza la verifica di
|
||||
[DAT-67]; l'identità con la riserva vale già per i round pagati con tutti
|
||||
e soli i loro ingressi.
|
||||
|
||||
- ~~**[DAT-47]**~~ Assorbito da [RND-12](ciclo-di-vita-del-round.md): l'attesa
|
||||
delle scommesse in volo prima dell'estrazione è comportamento del round. Il
|
||||
@@ -1467,7 +1465,7 @@ In ordine di applicazione (ogni riga rivede la precedente):
|
||||
Revisioni attese, non ancora scritte (ciascuna nel commit della sua voce,
|
||||
con i backfill di [DAT-55]): `rounds` (`bet_amount_sats`,
|
||||
`fee_address`, `draw_confirmations`, `server_seed`, `server_seed_hash`,
|
||||
`participants_hash`, `draw_height`, `bump_reserve_sats`, `payout_blocked_at`),
|
||||
`participants_hash`, `draw_height`, `payout_blocked_at`),
|
||||
`round_participants` (`bet_vout`, `draw_position` e il vincolo unico),
|
||||
`round_config` (`deposit_confirmations`, `draw_confirmations`,
|
||||
`max_round_pool_sats`, `paused_reason`, con `paused_reason = 'operator'` per
|
||||
|
||||
+5
-5
@@ -616,9 +616,10 @@ con `docker compose logs app` ([architettura.md](architettura.md) ARC-09).
|
||||
polvere) compare invece come errore di invio nello stesso messaggio.
|
||||
**Causa.** L'RBF riduce solo il resto che torna al mittente; se il resto non
|
||||
basta a coprire l'aumento, o la commissione è già al massimo (10.000 sat/vB),
|
||||
non si può fare. **Un pagamento di norma non ha resto** (spende esattamente
|
||||
gli UTXO del montepremi), quindi oggi un pagamento fermo nel mempool non si
|
||||
può accelerare in nessun caso.
|
||||
non si può fare. Un pagamento ha un'uscita di riserva
|
||||
(B-79) che l'aumento riduce; la riserva è dimensionata per arrivare a circa
|
||||
10 sat/vB, oltre la quale (o per un pagamento costruito prima di B-79, che
|
||||
non ce l'ha) la transazione non si può accelerare.
|
||||
**Cosa fare.** Nessuna procedura supportata. La transazione resta in attesa
|
||||
alla commissione che ha: conferma quando il mempool la accetta, oppure, se
|
||||
sparisce dal mempool, viene abbandonata dopo 6 ore ([OPS-16]). Se blocca un
|
||||
@@ -627,8 +628,7 @@ con `docker compose logs app` ([architettura.md](architettura.md) ARC-09).
|
||||
([OPS-23]); il sistema accrediterà l'uscita della nuova transazione come un
|
||||
normale deposito. Per un pagamento non c'è un'alternativa supportata: la
|
||||
chiave del montepremi non è esportabile ([OPS-24]).
|
||||
Dopo B-79 ogni pagamento ha un'uscita di riserva che l'RBF può ridurre;
|
||||
dopo B-87 un aumento che lascerebbe un resto sotto la soglia di polvere
|
||||
Dopo B-87 un aumento che lascerebbe un resto sotto la soglia di polvere
|
||||
toglie il resto invece di fallire, e ogni fallimento scrive
|
||||
`rbf_bump_failed`.
|
||||
*Codice:* `app/tx/broadcast.py:bump_fee`, `app/tx/broadcast.py:RbfBumper._tick` ·
|
||||
|
||||
+3
-3
@@ -227,7 +227,7 @@ cifrava di nuovo il file sbagliato (vedi [operativita.md](operativita.md)).
|
||||
| B-76 | Un invio ambiguo (timeout, connessione caduta) viene annullato come se fosse rifiutato | Critica | *da registrare* | `test_broadcast_outcome.py`, `test_electrum_client.py` | TX, BET, WDR |
|
||||
| B-77 | Si ricorda solo l'ultimo txid di una transazione accelerata, e la sostituzione viene inviata prima di essere salvata | Critica | *da registrare* | `test_tx_versions.py`, `test_migrations.py`, `test_broadcast.py` | TX |
|
||||
| B-78 | Una transazione viene abbandonata sulla base del silenzio di un solo server | Critica | *da registrare* | `test_reconcile_proof.py`, `test_reconcile.py`, `test_pause_reason.py`, `test_migrations.py` | TX, ELC |
|
||||
| B-79 | Il pagamento di norma non ha resto, quindi non si può accelerare e può bloccare la lotteria | Critica | **aperto** | — | RND, TX |
|
||||
| B-79 | Il pagamento di norma non ha resto, quindi non si può accelerare e può bloccare la lotteria | Critica | *da registrare* | `test_payout_builder.py`, `test_broadcast.py`, `test_scheduler.py`, `test_rounds_service.py`, `test_admin.py`, `test_migrations.py` | RND, TX |
|
||||
| B-80 | Un solo indirizzo del montepremi per tutti i round | Critica | *da registrare* | `test_round_pool_address.py`, `test_pending_address.py`, `test_scheduler.py` | RND, BET, WDR |
|
||||
| B-81 | `drawing` resta bloccato dopo qualsiasi eccezione, non solo dopo un riavvio | Critica | *da registrare* | `test_draw_proof.py` | RND |
|
||||
| B-82 | Un deposito annullato da una riorganizzazione dopo la scommessa rende il round impagabile; coinbase accreditate a 1 conferma | Critica | **aperto** | — | DEP, RND |
|
||||
@@ -252,9 +252,9 @@ cifrava di nuovo il file sbagliato (vedi [operativita.md](operativita.md)).
|
||||
|
||||
- **Corretti:** B-01 … B-70, tranne B-16, che è chiuso nella sostanza (il
|
||||
crash è eliminato e `/guida` serve un segnaposto) ma lascia la guida vera
|
||||
come limite accettato; dalla revisione del 2026-09-28: B-88, B-75, B-97, B-80, B-84, B-76, B-77, B-78, B-83, B-74, B-85, B-86, B-73, B-81.
|
||||
come limite accettato; dalla revisione del 2026-09-28: B-88, B-75, B-97, B-80, B-84, B-76, B-77, B-78, B-83, B-74, B-85, B-86, B-73, B-81, B-79.
|
||||
- **Aperti:** B-71, B-72 (gravità bassa, operativi o di documentazione) e
|
||||
B-73 … B-98 dalla revisione del 2026-09-28 tranne i corretti (2 critici,
|
||||
B-73 … B-98 dalla revisione del 2026-09-28 tranne i corretti (1 critico,
|
||||
4 alti, 6 medi), da implementare nell'ordine di
|
||||
[piano-correzioni.md](piano-correzioni.md).
|
||||
- **Prossimo numero libero:** `B-99`.
|
||||
@@ -244,13 +244,9 @@ sostituibilità fin dal primo invio.
|
||||
mai abbandonata.
|
||||
*Codice:* `app/tx/broadcast.py:bump_fee`, `app/tx/broadcast.py:_find_change_output` ·
|
||||
*Test:* `tests/unit/test_broadcast.py`
|
||||
*Stato:* da implementare (B-79, B-87) · *Oggi:* per il pagamento si cerca
|
||||
un resto verso l'indirizzo del montepremi, che esiste solo se le monete
|
||||
scelte superano vincitore più commissioni di almeno 294 sat: quando il
|
||||
montepremi contiene esattamente le scommesse del round il pagamento le
|
||||
spende tutte per esattamente `pool_amount_sats`, non ha resto e non è
|
||||
accelerabile; per scommesse e prelievi basta che il resto superi
|
||||
l'aumento, così un resto di 294 sat diventa 153 sat, sotto la soglia di
|
||||
*Stato:* da implementare (B-87) · *Oggi:* il pagamento si accelera già
|
||||
dall'uscita di riserva (B-79), ma per ogni transazione basta che l'uscita
|
||||
superi l'aumento: un resto di 294 sat diventa 153 sat, sotto la soglia di
|
||||
dust, e la rete rifiuta la sostituzione a ogni giro.
|
||||
- **[TX-12]** **Prima si scrive la sostituzione, poi si invia.** La
|
||||
sostituzione firmata viene registrata come candidata in
|
||||
|
||||
Reference in new issue
Block a user