Drop a dust-sized change into the fee on an RBF bump and audit every refused bump (B-87, TX-11, TX-24, AUD-40, AUD-09, ADR-19)
`bump_fee` only required `change > fee delta`. A change at the 294 sat minimum (a full-balance
withdrawal) shrank to ~153 sat after the very first minimum bump, relay policy refuses a
replacement with a sub-dust output, and the bumper tried again every 30 seconds for ever, with
nothing but a log line to show for it.
- When what would be left of the reducible output (the user's change, a payout's bump
reserve) is under DUST_LIMIT_SATS the output is dropped and its whole value goes to the fee:
that replacement is valid and the last one possible. If the output does not exist, or even
its whole value is below BIP125's minimum increase (vsize x 1 sat/vB), RbfError. The
recorded fee rate uses the replacement's own, smaller, size.
- RbfError now carries a `reason` (`change_too_small`, `max_fee_rate`, `replacement_rejected`);
RbfBumper._record_failure writes one `rbf_bump_failed` row {pending_transaction_id, kind,
txid, reason, message[:200]} with the row's user_id/round_id, in a transaction of its own,
once per (current txid, reason) - the audit log is the dedupe clock (AUD-09) - and the pending
row is untouched. A new txid or a new reason writes a new row.
- bump_fee's docstring no longer cites the "bet without change" case that B-62 removed.
No existing test changed (all bumps they make leave a change well above dust). New
tests/unit/test_rbf_dust.py: the 294 sat case end to end (recipient untouched, whole change to
the fee, last bump impossible), the exact boundary (leaves 294 kept / leaves 293 dropped), a
change under the minimum increase, a payout's reserve dropped with the winner and fee outputs
byte-identical, and the audit rows for each reason, once per pair, new row on a new txid or
reason, round/user attribution. Mutation-checked: the dust test of the output and the
once-per-pair check each fail tests.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
54dbffb552
commit
ccd15807ed
11 files changed
+440
-108
No files matched your search
+27
-34
@@ -33,7 +33,7 @@ implementato il [piano delle correzioni](piano-correzioni.md).
|
||||
| Limite | Blocca il non presidiato oggi? | Dopo il piano |
|
||||
|---|---|---|
|
||||
| ~~[LIM-01]~~ `drawing` non riprende dopo un errore o un riavvio | Non più (chiuso da B-81) | — |
|
||||
| [LIM-02] RBF di una sola forma (e pagamento senza resto) | **Sì, in caso di congestione della rete** | Chiuso da B-79 e B-87 |
|
||||
| [LIM-02] RBF di una sola forma | Non più il pagamento senza resto (B-79) né il resto sotto soglia (B-87); resta una transazione che esaurisce resto o riserva | Visibile in `rbf_bump_failed`; resta in attesa senza abbandono |
|
||||
| [LIM-03] Attesa in `closing` senza limite di tempo | **Sì, in caso di congestione** | Resta senza limite di tempo per scelta ([ADR-51](adr/ADR-51-dubbio-liberano-mai-fondi.md)); con B-78 e B-87 finisce quando la rete decide |
|
||||
| [LIM-04] Prelievo e RBF mai provati in rete reale | **Sì, come verifica** | Da chiudere con la prova su rete reale ([operativita.md](operativita.md#prova-su-rete-reale)) prima di aprire al pubblico |
|
||||
| ~~[LIM-10]~~ Riorganizzazioni non gestite | Non più (chiuso da B-82 e B-85) | Resta [LIM-24] |
|
||||
@@ -50,38 +50,31 @@ implementato il [piano delle correzioni](piano-correzioni.md).
|
||||
stesso vincitore ([RND-36](ciclo-di-vita-del-round.md)).
|
||||
|
||||
- **[LIM-02]** **L'RBF gestisce una sola forma di transazione**: un'unica uscita
|
||||
di resto verso il mittente, abbastanza grande da assorbire l'aumento. Non c'è
|
||||
il ripiego «aggiungi un'altra moneta». Ogni aumento costa almeno 1 sat/vB
|
||||
sull'intera dimensione della transazione (141 sat per una transazione
|
||||
con un ingresso e due uscite) e si toglie dal resto; quando l'aumento supera
|
||||
il resto, `bump_fee` solleva `RbfError`. Inoltre `bump_fee` **non controlla
|
||||
che il resto ridotto resti sopra la soglia di polvere**: con un resto vicino
|
||||
al minimo di 294 sat ([ADR-19](adr/ADR-19-ogni-transazione-utente-conserva-uscita-resto.md)) già il primo aumento produce un'uscita sotto
|
||||
soglia, che le regole di inoltro standard rifiutano. **Il pagamento del
|
||||
round, nel caso normale, non ha alcun resto** (spende esattamente il
|
||||
montepremi del round, e un resto sotto soglia va alla commissione), quindi
|
||||
non è accelerabile affatto.
|
||||
- *Impatto:* con la rete congestionata una transazione a 1 sat/vB può
|
||||
restare in mempool indefinitamente. Poiché il riconciliatore abbandona solo
|
||||
ciò che il server **non conosce**, una transazione ferma ma presente in
|
||||
mempool non viene mai abbandonata: una scommessa così blocca il round in
|
||||
`closing`, un pagamento così lo blocca in `paying_out`. `RbfBumper`
|
||||
registra un errore ogni 30s.
|
||||
- *Mitigazione attuale:* resto minimo garantito sulle transazioni utente;
|
||||
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-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
|
||||
commissione, e quella sostituzione è l'ultima ([ADR-19](adr/ADR-19-ogni-transazione-utente-conserva-uscita-resto.md), B-87); ogni
|
||||
`RbfError` e ogni sostituzione rifiutata scrivono `rbf_bump_failed` in
|
||||
audit. Resta accettato, dopo il piano: nessun ripiego «aggiungi un'altra
|
||||
moneta» né CPFP; una transazione che ha esaurito resto o riserva e non
|
||||
conferma nemmeno al tetto del fee rate resta in attesa, visibile, e non
|
||||
viene abbandonata ([ADR-51](adr/ADR-51-dubbio-liberano-mai-fondi.md)). Il docstring di `bump_fee` cita ancora come
|
||||
esempio «una scommessa senza resto», caso che dopo B-62 non esiste più.
|
||||
riducibile — il resto verso il mittente per scommesse e prelievi, l'uscita
|
||||
di riserva per il pagamento ([RND-38](ciclo-di-vita-del-round.md#4--pagamento), B-79).
|
||||
Non c'è il ripiego «aggiungi un'altra moneta» né il CPFP. Ogni aumento costa
|
||||
almeno 1 sat/vB sull'intera dimensione della transazione (141 sat per una
|
||||
transazione con un ingresso e due uscite) e si toglie da quell'uscita; se
|
||||
quanto resta sarebbe sotto la soglia di polvere l'uscita viene eliminata e
|
||||
il suo valore va in commissione, e quella sostituzione è l'ultima (B-87,
|
||||
[ADR-19](adr/ADR-19-ogni-transazione-utente-conserva-uscita-resto.md)); se
|
||||
nemmeno l'intera uscita raggiunge l'aumento minimo, o non esiste, `bump_fee`
|
||||
solleva `RbfError`.
|
||||
- *Impatto:* con la rete congestionata una transazione a 1 sat/vB che ha
|
||||
esaurito resto o riserva può restare in mempool indefinitamente. Poiché il
|
||||
riconciliatore abbandona solo ciò che il server **non conosce**, una
|
||||
transazione ferma ma presente in mempool non viene mai abbandonata: una
|
||||
scommessa così blocca il round in `closing`, un pagamento così lo blocca
|
||||
in `paying_out`.
|
||||
- *Mitigazione attuale:* la riserva del pagamento è dimensionata per circa
|
||||
10 sat/vB; resto minimo garantito sulle transazioni utente; tetto al fee
|
||||
rate (`MAX_FEE_RATE_SAT_VB`); ogni accelerazione impossibile o rifiutata
|
||||
scrive `rbf_bump_failed` (una volta per transazione e motivo); abbandono
|
||||
dopo 6 ore per ciò che sparisce dalla rete; ritentativo automatico del
|
||||
pagamento dopo un abbandono.
|
||||
- *Stato:* accettato dopo B-79 e B-87: una transazione che ha esaurito resto
|
||||
o riserva e non conferma nemmeno al tetto del fee rate resta in attesa,
|
||||
visibile, e non viene abbandonata ([ADR-51](adr/ADR-51-dubbio-liberano-mai-fondi.md)).
|
||||
|
||||
*Codice:* `app/tx/broadcast.py:bump_fee` · `app/wallet/psbt_builder.py:build_payout_transaction` · `app/tx/reconcile.py:reconcile_once`
|
||||
|
||||
@@ -95,7 +88,7 @@ implementato il [piano delle correzioni](piano-correzioni.md).
|
||||
nuovo round si apre nel frattempo.
|
||||
- *Mitigazione attuale:* lo stato è visibile (`closing`) e il pannello
|
||||
«Transazioni pendenti» mostra la scommessa che trattiene il round.
|
||||
- *Stato:* B-78 e B-77 chiusi; resta da chiudere B-87 (resto minimo), e per scelta l'assenza di un limite di tempo.
|
||||
- *Stato:* B-78, B-77 e B-87 chiusi; resta per scelta l'assenza di un limite di tempo.
|
||||
- *Per chiuderlo:* con B-78 l'attesa finisce quando la scommessa conferma
|
||||
(una qualsiasi delle sue versioni, B-77) o quando c'è la prova che non
|
||||
confermerà mai ([ADR-51](adr/ADR-51-dubbio-liberano-mai-fondi.md)), non dopo un tempo fisso; con B-87 la scommessa
|
||||
|
||||
Reference in new issue
Block a user