Bring docs in sync with recent features (pending balance, SSE, per-player reveal)
CLAUDE.md: bumped the stale test count (54 -> 76), added "Balance display" and "Real-time updates (SSE)" sections, and rewrote the DRAW section's frontend-reveal paragraph to describe the actual current behavior (dual status/result boxes gated by user_played, closes_at-anchored reveal delay, localStorage persistence, the last-round-result backstop) instead of the older single-box design. Refined the "no history endpoints" known gap now that GET /users/me/last-round-result exists (still not general history). README.md: same test count fix, expanded coverage list. docs/: fixed a pre-existing broken link in setup.md (admin-guide.md -> guida-admin.md), added a note in running-the-server.md that editing the bind-mounted Caddyfile needs an explicit `docker compose restart caddy` (discovered while adding the SSE Caddy config in a prior change), and rewrote guida-utente.md's draw/reveal section plus the balance/withdrawal sections to match what the UI actually does now. guida-admin.md was reviewed but needed no changes. app/static/style.css: dropped `.toast.info`, dead since the toast-based loss notification it styled was replaced by the persistent result box. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dda5bd14e1
commit
aae0961c94
6 files changed
+80
-26
No files matched your search
@@ -8,7 +8,7 @@ The user communicates in Italian in chat — reply to them in Italian. Everythin
|
||||
|
||||
## Project status
|
||||
|
||||
All 10 build-order stages from `/home/davide/.claude/plans/scalable-mixing-sloth.md` are code-complete and unit-tested (54 tests green): project skeleton, DB schema + Alembic migrations, auth, HD wallet derivation, Electrum client, deposit detection, bet flow, round/draw engine, payout, withdrawal, RBF fee-bump, admin config + audit log. Beyond the original 10 stages: a Docker + Caddy deployment (see below), a full admin dashboard (`/admin`), and a static test UI for the user-facing flow (`/`).
|
||||
All 10 build-order stages from `/home/davide/.claude/plans/scalable-mixing-sloth.md` are code-complete and unit-tested (76 tests green): project skeleton, DB schema + Alembic migrations, auth, HD wallet derivation, Electrum client, deposit detection, bet flow, round/draw engine, payout, withdrawal, RBF fee-bump, admin config + audit log. Beyond the original 10 stages: a Docker + Caddy deployment (see below), a full admin dashboard (`/admin`), a static test UI for the user-facing flow (`/`), a pending-inclusive balance display (see "Balance display" below), and a Server-Sent Events push channel layered on top of the original polling (see "Real-time updates" below).
|
||||
|
||||
Real-money verification on mainnet, done so far: registration + address derivation, deposit crediting (1-conf), a real 10 PLM bet (broadcast, confirmed, change credited back), and a full round cycle — close → draw (real block hash) → payout (70/30 split, exact sat math verified against the broadcast tx) → confirmation → round closed → next round auto-opened. Withdrawal and the RBF bump path are unit-tested but have never been exercised against a live broadcast. See "Known gaps" below before treating this as production-ready.
|
||||
|
||||
@@ -81,6 +81,25 @@ Mainnet:
|
||||
- Block time: 120s
|
||||
- BIP32 extended key headers (Legacy/native-segwit `zprv`/`zpub` etc.): see `ExtKeyHeaders` in `ChainProfiles.cs`
|
||||
|
||||
## Balance display
|
||||
|
||||
`place_bet`/`request_withdrawal` (`app/bets/service.py`, `app/withdrawals/service.py`) select whole UTXOs to cover the amount (`select_utxos`, largest-first) and mark every selected UTXO `spent_txid` immediately at broadcast time — well before the tx has any confirmations. `User.cached_balance_sats` (`recompute_balance`, `app/wallet/balance.py`) only sums confirmed, unspent UTXOs, so right after a bet/withdrawal it understates the user's real balance by the entire unconfirmed change amount, which is often far larger than the amount actually moving.
|
||||
|
||||
`compute_pending_balance` (`app/wallet/balance.py`) fixes the *displayed* number without touching what's actually spendable: it decodes the raw tx of every in-flight (`status="pending"`) bet/withdrawal `PendingTransaction` belonging to the user and sums whichever outputs pay back to the user's own address, adding that to `cached_balance_sats`. `GET /users/me` returns both `balance_sats` (confirmed-only — still what withdrawal-max and internal spend logic use, since only confirmed UTXOs are actually spendable) and `pending_balance_sats` + `has_pending` (what the frontend displays, colored green when settled and amber while `has_pending` is true).
|
||||
|
||||
## Real-time updates (SSE)
|
||||
|
||||
`GET /rounds/stream` (`app/api/routes/rounds.py`) is a Server-Sent Events channel layered *on top of* the original polling loops in `app/static/index.html`/`admin.html` — polling is the fallback, not replaced, so a blocked/dropped SSE connection just degrades to the pre-existing behavior. The channel carries no payload and needs no auth: it's purely a "something changed, go refetch" ping; personalization (e.g. `user_played` below) still lives entirely in the normal per-user REST endpoints.
|
||||
|
||||
`app/rounds/events.py`'s `RoundEventBroadcaster` (module-level singleton `broadcaster`) is a simple in-process pub/sub — one `asyncio.Queue` (maxsize 1, so redundant notifications coalesce) per connected SSE client. `broadcaster.publish()` is called from every point that changes something a dashboard would want to know about: a new round opening (`rounds/service.py`), every round status transition (`rounds/scheduler.py`: closing/drawing/paying_out/closed), a bet or withdrawal broadcast (`bets/service.py`, `withdrawals/service.py`), any pending tx confirming — bet/withdrawal/payout (`tx/confirmation.py`), a deposit credited (`deposits/service.py`), and a new block tip arriving (`electrum/listener.py` — the exact moment the "drawing" phase is waiting on).
|
||||
|
||||
Deliberate scope decisions, not oversights:
|
||||
- **Single-process only, no cross-worker fan-out.** Fine for the current deployment (one uvicorn process, see `docker-compose.yml`). A multi-worker/multi-container deployment would need a shared channel (e.g. Redis pub/sub) instead — don't add that speculatively before it's actually needed.
|
||||
- **Generic broadcast, not a per-user channel.** Every connected client refetches on every event, even ones irrelevant to them. Acceptable at the expected scale (~100 concurrent users); a targeted per-user channel would need auth on the SSE endpoint and server-side knowledge of who's affected by each event — real engineering work, only worth it well past current expected concurrency.
|
||||
- `MAX_SUBSCRIBERS` (default 500, `app/rounds/events.py`) is a defensive cap only — past it, `GET /rounds/stream` returns 503 instead of opening a stream, and the client's `EventSource` just falls back to polling. Not a substitute for the app-wide "no rate limiting anywhere" gap (see Known gaps).
|
||||
|
||||
Frontend: both `index.html` and `admin.html` open an `EventSource('/rounds/stream')` and, on an `update` message *or* on `open` (which fires on the initial connection and every automatic reconnect), immediately re-run the same refresh calls polling would eventually do — this matters most right after a dropped connection reconnects, closing most of the "missed while disconnected" gap.
|
||||
|
||||
## MVP business parameters
|
||||
|
||||
- Bet cost per round: **10 PLM** by default, admin-configurable (`RoundConfig.bet_amount_sats`) — not a fixed constant.
|
||||
@@ -99,7 +118,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). The round's own deadline (`opened_at + round_duration_seconds`) is the authoritative "yellow light" cutoff for new bets — **not** the DB status transition. `place_bet` (`app/bets/service.py`) calls `rounds/service.round_accepts_bets(round_, round_duration_seconds)`, which rejects the bet once the deadline has passed even if `status` is still `"open"` in the DB (the `RoundScheduler` tick that flips it to `"closing"` runs every `_TICK_INTERVAL_SECONDS` = 5s and can lag a few seconds behind the deadline). This closes the race where a bet placed in that lag window would otherwise still be accepted. Once a round leaves `open` (closing/drawing/paying_out), **no new bets are accepted** for it either, 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) — this is the "yellow light" behavior: no new entries once the timer hits zero, but bets already in flight are still given time to confirm before the round actually closes and draws. 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.
|
||||
- **DRAW (Periodic draw)**: configurable timer (default 10 minutes). The round's own deadline (`opened_at + round_duration_seconds`) is the authoritative "yellow light" cutoff for new bets — **not** the DB status transition. `place_bet` (`app/bets/service.py`) calls `rounds/service.round_accepts_bets(round_, round_duration_seconds)`, which rejects the bet once the deadline has passed even if `status` is still `"open"` in the DB (the `RoundScheduler` tick that flips it to `"closing"` runs every `_TICK_INTERVAL_SECONDS` = 5s and can lag a few seconds behind the deadline). This closes the race where a bet placed in that lag window would otherwise still be accepted. Once a round leaves `open` (closing/drawing/paying_out), **no new bets are accepted** for it either, 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) — this is the "yellow light" behavior: no new entries once the timer hits zero, but bets already in flight are still given time to confirm before the round actually closes and draws. 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 generic "drawing" status box (phase label, e.g. "Pagamento al vincitore in corso…") to **every** viewer on every dashboard for the whole closing/drawing/paying_out phase — this one is purely cosmetic status text, driven directly by `status`, no gating. Independently and *additively* (not instead of it), a personalized "Hai vinto!/Non hai vinto" box appears only for users where `GET /rounds/current`'s `user_played` field is true (computed via `app/auth/dependencies.py:get_optional_user`, since this endpoint is reachable logged-out too) — everyone else has nothing to reveal and never sees it. That reveal is additionally delayed by at least `draw_animation_seconds` (admin-configurable, default 20s) for cosmetic suspense, anchored to the round's server-provided `closes_at` timestamp rather than a client-side "first seen" time (so reloading the page can't reset the countdown), and decoupled from the real (and much longer, ~block-time) wait for `winner_user_id` to actually be set. Once revealed, the result is persisted in the browser's `localStorage` (`plm_persisted_result`) so it survives a page refresh even after the round moves past `paying_out` into `closed` — at which point `get_active_round` stops returning that round at all and `winner_user_id` disappears from `GET /rounds/current` entirely. `GET /users/me/last-round-result` (`app/api/routes/users.py`) is a durable, DB-backed backstop for a user who reloads on a browser/device that missed the live reveal window completely: it looks up the most recent *closed* round the user has a `RoundParticipant` row in. See `app/static/index.html`'s `refreshRound`/`checkLastRoundResult` for the full 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.
|
||||
@@ -113,7 +132,7 @@ So worst case (last bet confirms right at the deadline) is ~3 block times end-to
|
||||
|
||||
## Admin dashboard and test UI
|
||||
|
||||
Two static single-page apps, served directly by FastAPI (`app/main.py` mounts `app/static/` and adds a dedicated `GET /admin` route) — no build step, no framework:
|
||||
Two static single-page apps, served directly by FastAPI (`app/main.py` mounts `app/static/` and adds a dedicated `GET /admin` route) — no build step, no framework. Each page's HTML/CSS/JS are separate files (`index.html`/`style.css`/`app.js`, `admin.html`/`admin.css`/`admin.js`), served as plain static files (no bundler):
|
||||
|
||||
- **`/` (`app/static/index.html`)**: the end-user test UI. Register/login, then a menu-driven dashboard (Deposito with a QR code of the address via `GET /qr/{address}`, Bet, Prelievo) with a persistent round-status card (`GET /rounds/current`: id/status/timer/participant count/jackpot) above the menu.
|
||||
- **`/admin` (`app/static/admin.html`)**: gated by a token screen (not a real login — just checks `X-Admin-Token` against `ADMIN_TOKEN` from `.env`), then a navbar-driven dashboard with five sections, each backed by its own `/admin/*` endpoint (`app/api/routes/admin.py`): Parametri (`RoundConfig` CRUD), Utenti (list + per-user WIF privkey export, audit-logged), Round (history), Transazioni pendenti (in-flight RBF candidates), Audit log. **`/admin` is deliberately not linked from `/`** in either direction — reachable only by knowing the URL.
|
||||
@@ -139,7 +158,7 @@ Not blockers for reading the code, but must be addressed before this is producti
|
||||
- **RBF bump only handles one case**: a single change output, paying back to the tx's own sender address, large enough to absorb the fee increase. No additional-input selection fallback — an exact-amount tx (no change) or a change output too small to absorb the bump raises `RbfError` and needs manual operator intervention. Documented in `tx/broadcast.py`.
|
||||
- **Payout retry**: if `_trigger_payout` fails (e.g. insufficient pool UTXOs, Electrum disconnected), it just logs and returns — the round stays stuck in `paying_out` with no automatic retry.
|
||||
- **Withdrawal and RBF bump have never been exercised against a live broadcast** — only deposit and bet flow are verified end-to-end with real PLM as of this commit.
|
||||
- **No user-facing history endpoints** (list my own bets / withdrawals / past rounds) — a user still only has `/users/me` (balance). The admin side now has this (`/admin/rounds`, `/admin/pending-transactions`, `/admin/audit-log`), but there's no equivalent scoped to "my own history" for a logged-in user.
|
||||
- **No general user-facing history endpoints** (list my own bets / withdrawals / past rounds) — `GET /users/me/last-round-result` covers exactly one case (the outcome of the most recent *closed* round the user played in, as a reveal-persistence backstop; see DRAW above), not a real history. The admin side has more (`/admin/rounds`, `/admin/pending-transactions`, `/admin/audit-log`), but there's still no "my own full history" equivalent for a logged-in user.
|
||||
- **Admin auth is a single shared bearer token** (`ADMIN_TOKEN`, `X-Admin-Token` header) — no per-admin identity or audit trail of *who* changed config (the `audit_log` table records *what* changed, not which operator did it). This token now gates a lot more than config (user list, private key export, round/audit history), so its blast radius if leaked is correspondingly larger.
|
||||
- **No rate limiting / abuse protection** on any endpoint (register, bet, withdrawal, admin).
|
||||
- No automated integration tests against a live Electrum connection — all live-network verification so far has been manual (ad hoc scripts + real mainnet transactions), not part of the `pytest` suite.
|
||||
|
||||
@@ -62,8 +62,8 @@ python -m pytest # all tests
|
||||
python -m pytest tests/unit/test_hd.py # one file
|
||||
```
|
||||
|
||||
54 unit tests cover HD derivation, PSBT building, the Electrum client, bets,
|
||||
deposits, withdrawals, the round/draw engine, RBF fee-bumping, and admin
|
||||
config. No automated integration tests against a live Electrum connection —
|
||||
mainnet verification so far has been manual (see CLAUDE.md's "Project
|
||||
status").
|
||||
76 unit tests cover HD derivation, PSBT building, the Electrum client, bets,
|
||||
deposits, withdrawals, the round/draw engine, RBF fee-bumping, admin config,
|
||||
the pending-inclusive balance calculation, and the SSE push channel. No
|
||||
automated integration tests against a live Electrum connection — mainnet
|
||||
verification so far has been manual (see CLAUDE.md's "Project status").
|
||||
@@ -251,7 +251,6 @@ button.link:hover, a.link:hover { filter: none; color: var(--color-foreground);
|
||||
}
|
||||
.toast.success { background: var(--color-success-bg); color: var(--color-success); }
|
||||
.toast.error { background: var(--color-destructive-bg); color: var(--color-destructive); }
|
||||
.toast.info { background: var(--color-surface-inset); color: var(--color-foreground); }
|
||||
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
/* --- landing hero (shown only when logged out) --- */
|
||||
|
||||
+40
-15
@@ -40,12 +40,13 @@ subito le nuove giocate, ma il round non chiude immediatamente. Se qualcuno
|
||||
aveva già piazzato una bet negli ultimi istanti (transazione trasmessa ma
|
||||
non ancora confermata), il round aspetta che anche quella si confermi prima
|
||||
di procedere, così nessuna giocata già fatta viene persa al confine del
|
||||
round. Solo a quel punto la card 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, perché sotto la copertina servono **fino a tre
|
||||
conferme sulla rete PLM in sequenza**, una diversa dall'altra:
|
||||
round. Solo a quel punto la card mostra un messaggio di stato ("Round
|
||||
chiuso — attesa conferma puntate…", poi "Estrazione in corso…", poi
|
||||
"Pagamento al vincitore in corso…") al posto del timer — la stessa cosa
|
||||
compare nella dashboard di **ogni** utente, anche di chi non ha giocato in
|
||||
questo round. Questo messaggio resta visibile per l'intera durata della fase
|
||||
(chiusura → estrazione → pagamento), perché sotto la copertina servono
|
||||
**fino a tre conferme sulla rete PLM in sequenza**, una diversa dall'altra:
|
||||
|
||||
1. conferma dell'ultima giocata rimasta in sospeso (se ce n'era una proprio
|
||||
allo scadere del timer — altrimenti questo passo è già superato);
|
||||
@@ -59,14 +60,27 @@ c'era una giocata da confermare all'ultimo istante) o **2-4 minuti** (se
|
||||
tutte le giocate erano già confermate prima dello zero) — non pochi
|
||||
secondi, ed è normale.
|
||||
|
||||
Appena il vincitore è determinato, l'animazione lascia spazio a un
|
||||
messaggio:
|
||||
Se **hai giocato in questo round**, appena il vincitore è determinato compare
|
||||
**in aggiunta** (non al posto del messaggio di stato sopra, che resta
|
||||
visibile finché il pagamento non è confermato) un secondo riquadro solo per
|
||||
te:
|
||||
- **"🎉 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.
|
||||
Chi non ha giocato in questo round non vede mai questo secondo riquadro,
|
||||
solo il messaggio di stato generico. Il riquadro personale resta visibile
|
||||
anche **dopo un refresh della pagina** (persiste nel browser), fino
|
||||
all'apertura del round successivo — non serve restare sulla pagina per non
|
||||
perderlo, e se hai perso completamente la finestra in tempo reale (es. tab in
|
||||
background per diversi minuti), lo vedrai comunque comparire non appena
|
||||
riapri la dashboard.
|
||||
|
||||
La dashboard si aggiorna anche **in tempo reale**, non solo a intervalli
|
||||
fissi: appena qualcosa cambia sul server (una giocata, un cambio di fase del
|
||||
round, un nuovo blocco confermato...) la pagina lo recepisce quasi subito,
|
||||
senza bisogno di premere "Aggiorna" o ricaricare.
|
||||
|
||||
### Avviso di manutenzione
|
||||
|
||||
@@ -78,15 +92,22 @@ L'avviso sparisce da solo appena l'operatore riprende la lotteria.
|
||||
|
||||
### Deposito
|
||||
|
||||
- Il tuo **saldo interno** (accreditato dopo 1 conferma di rete) con bottone
|
||||
"Aggiorna" per ricontrollarlo
|
||||
- Il tuo **saldo interno**, con bottone "Aggiorna" per ricontrollarlo. Il
|
||||
numero mostrato include anche il resto di una bet o un prelievo appena
|
||||
inviati (non ancora confermato sulla rete) — non solo la parte già
|
||||
confermata — così non sembra che il saldo sia crollato più del dovuto
|
||||
subito dopo un'operazione. Il colore indica lo stato:
|
||||
- **verde**: tutto confermato, il saldo mostrato è quello definitivo
|
||||
- **arancione**: c'è una bet o un prelievo ancora in attesa di conferma —
|
||||
il numero è corretto, ma non ancora "finale"
|
||||
- Il tuo **indirizzo di deposito**, con bottone per copiarlo negli appunti
|
||||
- Il **QR code** dello stesso indirizzo, comodo per inviare PLM da un altro
|
||||
wallet scansionandolo invece di copiare l'indirizzo a mano
|
||||
|
||||
Per depositare, invia PLM (mainnet reale) a quell'indirizzo da un wallet
|
||||
esterno. Il saldo si aggiorna da solo dopo la prima conferma; premi
|
||||
"Aggiorna" per vederlo comparire.
|
||||
esterno. Il saldo si aggiorna da solo dopo la prima conferma (e quasi subito,
|
||||
grazie all'aggiornamento in tempo reale); premi "Aggiorna" se vuoi comunque
|
||||
ricontrollarlo a mano.
|
||||
|
||||
### Bet
|
||||
|
||||
@@ -98,10 +119,14 @@ scalato dal tuo saldo interno.
|
||||
|
||||
Form con due campi:
|
||||
- **Indirizzo esterno**: dove vuoi ricevere i PLM
|
||||
- **Importo (PLM)**: quanto prelevare
|
||||
- **Importo (PLM)**: quanto prelevare, oppure spunta **"Preleva l'intero
|
||||
importo"** per prelevare tutto il saldo confermato senza doverlo
|
||||
ricopiare a mano (il campo importo si disabilita e si aggiorna da solo)
|
||||
|
||||
Il prelievo viene costruito e trasmesso sulla rete; la fee di rete viene
|
||||
scalata dall'importo richiesto (non si aggiunge separatamente).
|
||||
scalata dall'importo richiesto (non si aggiunge separatamente). L'importo
|
||||
minimo prelevabile è pari alla quota fissa di ingresso al round (mostrata
|
||||
nella sezione Bet).
|
||||
|
||||
> **Nota**: attualmente è supportato solo l'indirizzo esterno in formato
|
||||
> **P2WPKH bech32** (quelli che iniziano con `plm1q...`). Non inserire
|
||||
|
||||
@@ -60,6 +60,17 @@ docker compose stop # ferma senza rimuovere i container
|
||||
docker compose down # ferma e rimuove i container (i dati in ./data/ restano)
|
||||
```
|
||||
|
||||
> **Nota sul `Caddyfile`**: è montato in sola lettura nel container `caddy`
|
||||
> (bind mount). Modificarlo non basta a farlo ripartire con la nuova
|
||||
> configurazione — `docker compose up -d --build` non ricrea `caddy` solo
|
||||
> perché il *contenuto* di un file montato è cambiato. Dopo una modifica al
|
||||
> `Caddyfile` serve un passaggio in più:
|
||||
> ```bash
|
||||
> docker compose restart caddy
|
||||
> ```
|
||||
> (oppure, senza interrompere le connessioni esistenti: `docker compose exec
|
||||
> caddy caddy reload --config /etc/caddy/Caddyfile`).
|
||||
|
||||
### ⚠️ Attenzione: riavvii automatici a metà round
|
||||
|
||||
`docker-compose.yml` imposta `restart: unless-stopped` sul container dell'app:
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ pip install -e ".[dev]"
|
||||
## 6. Impostare l'indirizzo delle fee
|
||||
|
||||
Prima che il primo round possa pagare, un admin deve impostare `fee_address`
|
||||
tramite il pannello admin o l'API — vedi [admin-guide.md](admin-guide.md). I
|
||||
tramite il pannello admin o l'API — vedi [guida-admin.md](guida-admin.md). I
|
||||
payout si rifiutano di partire finché non è impostato.
|
||||
|
||||
A questo punto l'istanza è pronta per essere avviata — continua con
|
||||
|
||||
Reference in new issue
Block a user