A user could bet and then double-spend the 1-conference deposit that funded the bet with a private chain: the bet left the chain but its participant stayed `confirmed`, and the payout failed "insufficient pool UTXOs" for ever with everyone else's money stuck. Coinbase outputs were also credited at 1 confirmation (maturity is 120). Deposits - `round_config.deposit_confirmations` (default 6, 1-100, editable from /admin) is the threshold for a deposit; an output of a platform transaction (any txid of a pending_transactions row, RBF versions included) keeps 1; a coinbase output needs 120, recognised from the raw transaction, which must hash to the txid (`is_coinbase`, cached). `read_deposit_policy` / `classify_new_deposits` run between the read and the write, so no session is open across the lookup. - The quorum must also place the coin deep enough on our own tip (ELC-17). - Confirmed outputs short of their threshold are pending deposits (memory only, replaced at every refresh): shown in `pending_balance_sats` / `has_pending` (also for DEP-13's withdrawal message), never spendable. No scripthash notification announces a maturing deposit, so each new block refreshes only the users that have one, one task per user (DEP-09 now says so). Payout - `_trigger_payout` verifies the round's confirmed participants' bet outputs with the other servers (`verify_pool_outpoints`: strict majority of >= 2 answers per outpoint, a mempool-only coin or a tie gives no verdict) and builds from exactly those outpoints (RND-22), never from whatever else sits at the address. No quorum: `payout_failed`, `pool verification unavailable`, nothing changes. Bets that are gone become `reorged_out`, one `participant_reorged_out` row each, `pool_amount_sats` rewritten (only downward) in the same transaction, and the 70/30 split is computed on what is left. - Winner's own bet gone: nothing is built; `rounds.payout_blocked_at` (Alembic f2c64a8d1e93, with `deposit_confirmations`, plain add_column) is set, `payout_blocked` and the pause with `paused_reason = 'payout_blocked'` are written in one transaction; `POST /admin/resume` clears the mark and the next attempt verifies again. /admin/rounds returns it. - Rounds opened before B-80 (pool = the shared reserve address) keep the old active-server `listunspent` choice, since that address holds no single round's bets. Ambiguities resolved: the winner's row stays `confirmed` while held (AUD-45 says every participant whose bet is gone becomes `reorged_out`, but OPS-30/DAT-68 need the verification to repeat after the resume, which only a `confirmed` row allows); `/`'s deposit hint no longer says "1 confirmation" (7 languages, same keys); guides updated. Tests changed because the spec changed: listener tests that credited height-100 coins at tip 0 now set a tip past every threshold; the payout fixtures seed confirmed participants (the new source of the inputs) instead of an active-server `listunspent`, FakeNetwork answers the verification, and test_round_pool_address asserts the quorum is asked about the round's address. New: thresholds at exactly 6/5, configured value, coinbase at 119/120 and a mismatching or missing raw tx, platform outputs at 1, dust, quorum height, waiting-deposit display and disappearance, block-triggered refresh, resume, verification verdicts, gone bets excluded with the split recomputed, winner gone held and paused, held payouts untouched until the resume, re-verification after it, no quorum, a payout with 1 and with 400 participants against reserve+fee+dust, admin config bounds and migration. Mutation-checked: threshold 1, ignored coinbase, no height bound, no verification and no winner hold 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 |