Document the draw animation and win/lose reveal
CLAUDE.md's DRAW architecture bullet now explains draw_animation_seconds and its decoupling from the real block-wait timing. guida-utente.md gets a new "Estrazione del vincitore" section describing what a player sees and when. guida-admin.md's Parametri table and hardcoded-defaults note include the new field. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -62,7 +62,7 @@ Known risk: `docker-compose.yml` sets `restart: unless-stopped` on `app`, so a c
|
||||
- **PLM node access**: Electrum protocol only (no full node/P2P). Bootstrap server for development: `santantonio.sytes.net:50002` (SSL).
|
||||
- **Auth**: Argon2 password hashing + JWT sessions.
|
||||
- **Secrets**: master xprv encrypted at rest with a symmetric scheme (AES-GCM/Fernet); the encryption key itself lives in an env var, never in the DB or in git.
|
||||
- **Operational config**: every business/round parameter (fee address, bet amount, round duration, round cooldown, minimum amount, network fee rate, RBF timeout) lives in the `round_config` DB table (single row, `app/rounds/config.py`) and is only editable live via the admin dashboard (`/admin`) or its API — no env var involved at all, no redeploy or restart needed. Defaults for a brand-new instance are hardcoded column defaults on the `RoundConfig` model (`app/db/models.py`), not `app/config.py`. Secrets and infra wiring (master key, JWT secret, Electrum host, admin token, database URL) stay env-var-driven in `.env` since those genuinely need a restart.
|
||||
- **Operational config**: every business/round parameter (fee address, bet amount, round duration, round cooldown, draw animation duration, minimum amount, network fee rate, RBF timeout) lives in the `round_config` DB table (single row, `app/rounds/config.py`) and is only editable live via the admin dashboard (`/admin`) or its API — no env var involved at all, no redeploy or restart needed. Defaults for a brand-new instance are hardcoded column defaults on the `RoundConfig` model (`app/db/models.py`), not `app/config.py`. Secrets and infra wiring (master key, JWT secret, Electrum host, admin token, database URL) stay env-var-driven in `.env` since those genuinely need a restart.
|
||||
- **Round cooldown**: `round_cooldown_seconds` — gap after a round closes before the next one opens, so players have time to see the outcome (default 30s). Not in the original flowchart; added afterwards as an explicit design decision.
|
||||
|
||||
## PLM network parameters
|
||||
@@ -96,7 +96,7 @@ The flow is organized into 5 phases, each a subgraph in [flowchart.mmd](flowchar
|
||||
- **REG (Registration)**: on signup the server derives a new P2WPKH address via BIP84 (`m/84'/coin'/0'/0/index`, one index per user) from a master xprv **encrypted at rest**. This address is permanent and serves as both the deposit address and the address that receives winnings and withdrawals.
|
||||
- **DEP (Balance top-up)**: an ElectrumClient/SPV subscribes to the user's address scripthash. Internal balance (DB) is credited after **1 confirmation only** — the reorg risk at 1-conf is knowingly accepted in v1, with no rollback logic.
|
||||
- **PLAY (Bet)**: fixed cost per round, **at most one active bet per user at a time** in v1. The server builds a PSBT user-address → pool-address for the fixed amount, with a **change output back to the same user address** (the user's balance must never exactly equal the bet amount). Fee minimized (~1 sat/vB), **deducted from the bet amount**. If the tx doesn't confirm within a timeout, fee-bump (RBF) and rebroadcast.
|
||||
- **DRAW (Periodic draw)**: configurable timer (default 10 minutes). Round closing **waits for all already-broadcast bets to confirm** before proceeding (avoids losing bets at the round boundary). The **next round only opens once the previous round's payout tx is confirmed** — rounds never overlap in v1. v1 draw algorithm (deliberately simple, meant to be replaced later): wait for the first block confirmed after round closing, use its hash as seed, `index = seed mod participant_count` over the participant list ordered by **broadcast timestamp** (this is also the tie-break when two bets confirm in the same block). Every participant has **equal probability regardless of bet amount** (consistent with the fixed bet amount). The payout (70% winner / 30% fees) is signed with the pool address key; the **payout fee is deducted from the winner's 70%**, the 30% fee share stays intact. Same timeout → RBF → rebroadcast pattern here too.
|
||||
- **DRAW (Periodic draw)**: configurable timer (default 10 minutes). Once a round leaves `open` (closing/drawing/paying_out), **no new bets are accepted** for it — `place_bet` checks `status == "open"` and a new round can't open until the current one is fully `closed` (see round cooldown below). Round closing **waits for all already-broadcast bets to confirm** before proceeding (avoids losing bets at the round boundary). The **next round only opens once the previous round's payout tx is confirmed** — rounds never overlap in v1. v1 draw algorithm (deliberately simple, meant to be replaced later): wait for the first block confirmed after round closing, use its hash as seed, `index = seed mod participant_count` over the participant list ordered by **broadcast timestamp** (this is also the tie-break when two bets confirm in the same block). Every participant has **equal probability regardless of bet amount** (consistent with the fixed bet amount). The payout (70% winner / 30% fees) is signed with the pool address key; the **payout fee is deducted from the winner's 70%**, the 30% fee share stays intact. Same timeout → RBF → rebroadcast pattern here too. The frontend shows a "drawing" animation on every user's dashboard for at least `draw_animation_seconds` (admin-configurable, default 20s) once the round starts closing — purely cosmetic, decoupled from the real (and much longer, ~block-time) wait for `winner_user_id` to actually be set; see `GET /rounds/current`'s `winner_user_id`/`winner_amount_sats` and `app/static/index.html`'s reveal logic.
|
||||
- **WITHDRAW (Withdrawal)**: the only way to move funds out of the platform to an external address. PSBT user-address → external-address + change back to the user address, fee deducted from the withdrawn amount, same RBF retry pattern.
|
||||
|
||||
PLAY and WITHDRAW share a **per-user DB lock**: a user can never have a bet-build and a withdrawal-build in flight at the same time, since both would otherwise spend from the same UTXO set on the user's dedicated address.
|
||||
|
||||
+3
-1
@@ -40,6 +40,7 @@ business — quelli si toccano solo da qui.
|
||||
| **Bet amount (PLM)** | Il costo fisso d'ingresso per round. |
|
||||
| **Durata round (secondi)** | Quanto resta aperto un round prima di chiudersi ed estrarre il vincitore. |
|
||||
| **Pausa tra un round e il successivo (secondi)** | Cooldown dopo la chiusura di un round, prima che il successivo si apra — dà tempo ai giocatori di vedere l'esito. |
|
||||
| **Durata animazione estrazione (secondi)** | Tempo minimo per cui la dashboard di ogni utente mostra l'animazione "Estrazione in corso" dopo la chiusura del round, prima di rivelare il vincitore. È solo un minimo: l'estrazione reale aspetta un blocco confermato (~2 minuti in media), quindi l'animazione può durare più a lungo di questo valore, mai meno. |
|
||||
| **Importo minimo deposito/prelievo (PLM)** | Soglia minima per un prelievo (i depositi non hanno un controllo minimo lato server, solo un floor consigliato). |
|
||||
| **Fee rate di rete (sat/vB)** | Fee per byte usata per costruire bet, payout e prelievi. |
|
||||
| **Timeout prima del fee-bump RBF (secondi)** | Dopo quanto tempo senza conferma una transazione viene ritrasmessa con fee più alta. |
|
||||
@@ -49,7 +50,8 @@ solo nella chiamata API — il backend lavora sempre in sats.
|
||||
|
||||
Su un'istanza nuova (mai avviata), questi campi partono con dei default
|
||||
hardcoded nel codice (`RoundConfig` in `app/db/models.py`: bet 10 PLM, round
|
||||
10 minuti, cooldown 30s, minimo 1 PLM, fee 1 sat/vB, RBF timeout 900s) — vanno
|
||||
10 minuti, cooldown 30s, animazione estrazione 20s, minimo 1 PLM, fee 1
|
||||
sat/vB, RBF timeout 900s) — vanno
|
||||
comunque rivisti e confermati dal pannello prima del primo utilizzo reale.
|
||||
|
||||
**Chi paga il fee-bump RBF?** Quando una bet, un payout o un prelievo resta
|
||||
|
||||
@@ -32,6 +32,26 @@ Dopo l'accesso vedi, in ordine:
|
||||
30% in fee)
|
||||
3. **Menu di navigazione** con tre sezioni:
|
||||
|
||||
### Estrazione del vincitore
|
||||
|
||||
Quando il round chiude, **le bet non sono più accettate** e la card del
|
||||
round mostra un'animazione ("Estrazione del vincitore in corso…") al posto
|
||||
del timer — la stessa cosa compare nella dashboard di ogni giocatore, non
|
||||
solo la tua. L'animazione resta visibile per almeno un tempo minimo
|
||||
configurabile dall'admin (default 20s), ma può durare più a lungo: il
|
||||
vincitore viene scelto usando l'hash del primo blocco confermato dopo la
|
||||
chiusura, quindi il tempo reale dipende dalla rete (mediamente ~2 minuti,
|
||||
il block time di PLM).
|
||||
|
||||
Appena il vincitore è determinato, l'animazione lascia spazio a un
|
||||
messaggio:
|
||||
- **"🎉 Hai vinto! +N PLM"** se sei tu il vincitore — l'importo ti verrà
|
||||
accreditato non appena la transazione di payout viene confermata (il
|
||||
round successivo non si apre finché questo non accade)
|
||||
- **"Non hai vinto questa volta."** altrimenti
|
||||
|
||||
Il messaggio resta visibile fino all'apertura del round successivo.
|
||||
|
||||
### Deposito
|
||||
|
||||
- Il tuo **saldo interno** (accreditato dopo 1 conferma di rete) con bottone
|
||||
|
||||
Reference in New Issue
Block a user