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>
Architecture Decision Records (ADR)
Una decisione di progetto = un file. Ogni ADR spiega perché una scelta è stata fatta (contesto, alternative scartate, conseguenze); le regole verificabili che la attuano restano nei documenti di dominio con il loro PREFISSO-NN. Così ogni informazione ha un solo proprietario: l'ADR possiede il perché, il requisito possiede il cosa, il codice il come. Regole: processo-di-modifica.md. Modello per i nuovi: TEMPLATE.md.
Gli identificativi ADR-NN sono stabili e non si riusano (i primi 52 coincidono con i vecchi DEC-NN). Un ADR superato non si cancella: passa a Sostituita da ADR-MM.
Indice per area
Custodia, chiavi e indirizzi
| ADR | Decisione |
|---|---|
| ADR-01 | La piattaforma è custodiale: tutte le chiavi sono generate e tenute dal server |
| ADR-02 | Un indirizzo per utente, permanente, per tutti gli usi |
| ADR-03 | Ogni round ha il proprio indirizzo del montepremi |
| ADR-04 | Accesso alla rete solo via protocollo Electrum |
| ADR-05 | Solo indirizzi bech32 PLM (plm1…) come destinazione |
Conferme e fiducia nella rete
| ADR | Decisione |
|---|---|
| ADR-06 | I depositi richiedono deposit_confirmations conferme (6 per default, configurabile in round_config), le uscite coinbase 120; ogni altra transazione ne richiede 1 |
| ADR-07 | Ogni decisione che sposta denaro in base a una risposta di rete è corroborata da un quorum degli altri server configurati |
| ADR-52 | Le riorganizzazioni della catena si contengono invece di ignorarle |
Configurazione e ambiente
| ADR | Decisione |
|---|---|
| ADR-08 | I parametri operativi stanno nel database, i segreti nell'ambiente |
| ADR-09 | Le modifiche di configurazione sono validate, non fidate |
| ADR-10 | I parametri del round sono fotografati all'apertura |
| ADR-11 | Il server rifiuta di partire con segreti inadeguati |
| ADR-12 | Il server gira solo in Docker, in sviluppo come in produzione |
| ADR-13 | Un solo processo, un solo file SQLite |
Economia del gioco
| ADR | Decisione |
|---|---|
| ADR-14 | Ripartizione fissa 70% al vincitore / 30% alla piattaforma |
| ADR-15 | Biglietto a prezzo fisso, una scommessa per utente per round, probabilità uguale per tutti |
| ADR-16 | La commissione di rete si toglie dall'importo che si muove |
| ADR-17 | La commissione del pagamento esce dalla quota del vincitore; la riserva per l'RBF esce dalla quota della piattaforma |
| ADR-18 | L'accelerazione RBF la paga chi riceve il resto |
| ADR-19 | Ogni transazione utente conserva un'uscita di resto verso l'utente, e un'accelerazione che porterebbe quel resto sotto DUST_LIMIT_SATS (294 sat) lo elimina |
| ADR-20 | Il prelievo minimo è il costo corrente della scommessa |
| ADR-21 | Tre limiti distinti sul numero di monete e di giocatori: |
Round ed estrazione
| ADR | Decisione |
|---|---|
| ADR-22 | Al più un round attivo, come invariante del database |
| ADR-23 | Il limite per scommettere è la scadenza del round stesso, non il suo stato nel database |
| ADR-24 | L'estrazione è un commit-reveal fra il server e un blocco futuro |
| ADR-25 | Il blocco di estrazione è fissato per altezza, è successivo alla chiusura, e si usa solo quando è confermato e corroborato |
| ADR-26 | Validazione degli header concatenata, senza ricalcolo della difficoltà |
| ADR-27 | Pausa di cortesia fra un round e l'altro |
| ADR-28 | La pausa per manutenzione ferma solo il round successivo |
| ADR-29 | Senza fee_address non si apre nessun round |
| ADR-30 | Il montepremi annunciato è quello che sarà pagato |
Denaro in movimento
| ADR | Decisione |
|---|---|
| ADR-31 | Tutto ciò che spende denaro si scrive prima di inviarlo |
| ADR-32 | Il pagamento si ritenta da solo, mai due volte |
| ADR-33 | Scommessa e prelievo si escludono solo tramite il blocco per-utente |
| ADR-34 | Saldo spendibile e saldo mostrato sono due numeri distinti |
| ADR-35 | Aritmetica solo in satoshi interi |
| ADR-51 | Nel dubbio non si liberano mai i fondi |
Account, amministrazione e sicurezza
| ADR | Decisione |
|---|---|
| ADR-36 | Password con Argon2, sessioni con JWT HS256 da 24 ore, revocabili |
| ADR-37 | Nessun recupero password, solo reset |
| ADR-38 | Gli username sono un unico spazio di nomi senza distinzione fra maiuscole e minuscole |
| ADR-39 | L'esportazione della chiave privata WIF di un utente è voluta, non una vulnerabilità |
| ADR-40 | L'amministrazione usa un unico token condiviso |
| ADR-41 | Limitazione dei tentativi solo su login e registrazione |
| ADR-42 | L'IP del client è l'ultimo elemento di X-Forwarded-For |
| ADR-43 | Header di sicurezza nel proxy, con 'unsafe-inline' accettato |
| ADR-44 | /qr/{address} è pubblico ma limitato |
| ADR-45 | Segnalazioni di bug aperte a tutti, triage in admin |
| ADR-53 | Il registro di audit è in sola aggiunta e si scrive nella transazione che descrive |
Interfacce, API e lingue
| ADR | Decisione |
|---|---|
| ADR-46 | Il canale SSE è un'aggiunta al polling, non un sostituto |
| ADR-47 | L'API parla una sola lingua e risponde con codici |
| ADR-48 | Le pagine per l'utente (/ e /report-bug) sono in 7 lingue |
| ADR-49 | Il pannello /admin e la pagina /guida non sono tradotti |
| ADR-50 | Frontend statici, senza framework né build |
Funzionalità → ADR
Per ogni funzionalità, gli ADR da leggere prima di toccarla.
| Funzionalità | Prefisso | Documento di dominio | ADR |
|---|---|---|---|
| Registrazione e account | REG |
registrazione-e-depositi.md | 01, 02, 36, 37, 38 |
| Depositi e saldo | DEP |
registrazione-e-depositi.md | 06, 07, 34, 35, 52 |
| Scommesse | BET |
scommesse-e-prelievi.md | 15, 16, 19, 21, 23, 33 |
| Prelievi | WDR |
scommesse-e-prelievi.md | 05, 18, 19, 20, 33 |
| Round, estrazione, pagamento | RND |
ciclo-di-vita-del-round.md | 03, 10, 14, 17, 21–30, 32 |
| Transazioni, RBF, riconciliazione | TX |
transazioni-e-riconciliazione.md | 17, 18, 31, 32, 51 |
| Connessione Electrum | ELC |
connessione-electrum.md | 04, 07, 26, 52 |
| Registro di audit | AUD |
audit-log.md | 40, 53 |
| Amministrazione | SEC, API |
sicurezza.md, api.md | 09, 28, 29, 39, 40 |
| Sicurezza (limiti, header, QR) | SEC |
sicurezza.md | 41, 42, 43, 44 |
| Segnalazioni di bug | API |
api.md | 45 |
| Tempo reale (SSE) | ARC |
architettura.md | 46 |
| Interfacce e lingue | ARC, API |
architettura.md | 47, 48, 49, 50 |
| Configurazione e ambiente | ARC, OPS |
architettura.md, operativita.md | 08, 09, 10, 11, 12, 13 |
Le decisioni del 2026-09-28
| Decisione | ADR | B-nn |
|---|---|---|
| Estrazione con commit-reveal | ADR-24, ADR-25 | B-73, B-81 |
| Nel dubbio non si liberano mai i fondi | ADR-51 | B-76, B-77, B-78 |
| Un indirizzo del montepremi per ogni round | ADR-03 | B-80 |
| 6 conferme per i depositi, 1 per il resto | ADR-06 | B-82 |
| Le riorganizzazioni della catena si contengono | ADR-52 | B-82, B-85 |