The seed was the hash of the first block seen after the close, so a miner holding tickets
could withhold blocks until one made them win, the active Electrum server could pick which
real block was used by not announcing another (a failed corroboration even moved the draw to
the next block), and the participant order was the server-only `broadcast_at` with no
tie-break, so nobody could recompute the result.
- Open: every round gets a 32-byte `server_seed`, stored only Fernet-encrypted with the
master-xprv key (`server_seed_enc`), with its published `server_seed_hash`;
`draw_confirmations` is copied from round_config. The plaintext column is written only by
the reveal.
- Entering `drawing` (one transaction, `draw_committed` audit row): participants ordered by
bet txid, each `draw_position`, `participants_hash` (sha256 of `txid:vout:address\n`
lines; new `bet_vout`), `draw_height = tip_at_close + 1`. A tip of 0 keeps the round
`closing` (`draw_baseline_tip_unknown`, once per round). All persisted, so a resumed draw
reads rather than recomputes (the `_tick` branch itself is B-81).
- `_complete_draw` / `_wait_for_draw_block` replace `_wait_for_next_block`: wait for
`draw_confirmations` blocks from `draw_height`, then `ElectrumListener.agreed_block_hash`
asks every server, the active one included, for the header at that exact height (valid
work, >= 2 other answers, strict majority of all); no agreement means the same height is
asked again and `draw_header_corroboration_failed` is written once per distinct reported
hash. `draw_stalled` now counts from the last tip advance, or from when the quorum started
to fail. Seed = sha256(server_seed || block_hash || participants_hash), index = seed mod n.
- The winner, the seed and the reveal (`server_seed`, `winner_drawn` payload) are written in
one transaction; a round closing with no participants reveals too. A round opened before
this change (no commitment) is given a secret as it enters `drawing`.
- `GET /rounds/{id}/proof` (public, 404 `round_not_found`, translated in all 7 languages) and
the new fields on `GET /rounds/current`; `draw_confirmations` in `/admin/config` and the
/admin form; `server_seed_hash`/`draw_height` in `/admin/rounds`, never the secret.
- Alembic d3e8b15a7c42 (plain add_column / create_index, expression indexes untouched).
- Docs: the B-73 `*Stato:*`/`*Oggi:*` lines are gone, guides and CLAUDE.md describe the new
draw. Ambiguities resolved in api.md: the proof's participants also carry `amount_sats`
(RND-37 lists it); `seed` is decimal in the proof and hex in the audit row, as specified.
Tests changed because the spec changed: ElectrumListener.corroborate_header (asked only the
other servers about the block the active one announced) is replaced by agreed_block_hash and
its quorum tests ported; draw_winner takes the secret and the list fingerprint; the four
_wait_for_next_block scheduler tests are replaced by _wait_for_draw_block ones (fixed height,
confirmations, same-height retry, stall clock). New: test_draw_proof.py recomputes the winner
independently from the public proof, checks the plaintext secret is nowhere in the database,
the API or the log before the reveal, and that a second pass over a `drawing` round yields
the same winner. Mutation-checked: ordering by broadcast_at, an early reveal and asking the
tip instead of the draw height each fail tests.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
154 lines
8.1 KiB
Markdown
154 lines
8.1 KiB
Markdown
# Processo di modifica: dalla documentazione al codice
|
|
|
|
L'indice di tutta la documentazione è [README.md](README.md). Questo documento
|
|
ne fissa le regole di lavoro.
|
|
|
|
## Principi: docs-first, SSOT, ADR
|
|
|
|
- **Docs-first.** Si cambia prima la documentazione, poi il codice; spec,
|
|
codice e test stanno nello stesso commit.
|
|
- **SSOT.** Ogni informazione ha un solo proprietario e gli altri documenti
|
|
rimandano a lei per ID senza ripeterla:
|
|
|
|
| Informazione | Proprietario |
|
|
|---|---|
|
|
| Perché una scelta (contesto, alternative, conseguenze) | un ADR in [adr/](adr/README.md) |
|
|
| Che cosa deve fare il software (regola verificabile) | il requisito `PREFISSO-NN` del documento di dominio |
|
|
| Schema, stati, vincoli | `DAT` in [modello-dati.md](modello-dati.md) |
|
|
| Soglie e rischi di sicurezza | `SEC` in [sicurezza.md](sicurezza.md) |
|
|
| Eventi di audit | `AUD` in [audit-log.md](audit-log.md) |
|
|
| Come lo fa | il codice (riferimento `percorso:funzione`) |
|
|
| Limiti accettati | `LIM` in [limiti-noti.md](limiti-noti.md) |
|
|
| Difetti trovati e corretti | [storico-bug.md](storico-bug.md) |
|
|
|
|
- **Un ADR per ogni funzionalità.** Una funzionalità nuova, o un cambio di
|
|
scelta in una esistente, parte da un ADR scritto da [adr/TEMPLATE.md](adr/TEMPLATE.md):
|
|
prossimo numero libero, stato *Proposta* finché non è approvato, poi
|
|
*Accettata*. L'ADR elenca i requisiti che lo attuano; i requisiti citano
|
|
l'ADR. Un ADR non si riscrive: se la scelta cambia se ne scrive uno nuovo e
|
|
il vecchio passa a *Sostituita da ADR-MM*. Un nuovo ADR va aggiunto
|
|
all'indice per area e alla tabella funzionalità → ADR di
|
|
[adr/README.md](adr/README.md).
|
|
|
|
La cartella `docs/` è la **specifica** di PLM Lottery. Descrive cosa il
|
|
software fa e perché; il codice è l'implementazione di questa specifica.
|
|
Ogni modifica di comportamento parte da qui.
|
|
|
|
## Convenzione dei requisiti
|
|
|
|
Ogni regola verificabile ha un **identificativo stabile**, nella forma
|
|
`PREFISSO-NN`, scritto in grassetto all'inizio del punto che la descrive,
|
|
seguito dai riferimenti all'implementazione e, dove esistono, ai test:
|
|
|
|
```markdown
|
|
- **[RND-11]** Al primo passo dello scheduler successivo alla scadenza il round
|
|
passa a `closing`; le scommesse sono però rifiutate già dall'istante della
|
|
scadenza ([BET-04]).
|
|
*Codice:* `app/rounds/scheduler.py:RoundScheduler._tick` ·
|
|
*Test:* `tests/unit/test_scheduler.py`
|
|
```
|
|
|
|
Regole della convenzione:
|
|
|
|
- **Un identificativo non si riusa mai.** Un requisito eliminato resta nel
|
|
documento come `~~[RND-11]~~ Rimosso: <motivo>`, così i riferimenti già
|
|
scritti (commenti, commit, test) non puntano a una regola diversa.
|
|
- **I numeri non si rinumerano.** Un requisito nuovo prende il numero
|
|
successivo al più alto del suo prefisso, anche se finisce in mezzo al testo.
|
|
- **I riferimenti al codice** sono `percorso:funzione` o `percorso:Classe.metodo`,
|
|
mai numeri di riga (cambiano a ogni modifica).
|
|
- Il testo descrittivo che non contiene regole (introduzioni, spiegazioni,
|
|
esempi) non ha identificativo.
|
|
- **Un solo proprietario per regola.** Ogni regola ha l'identificativo in un
|
|
solo documento; gli altri documenti che la citano rimandano a quell'ID
|
|
invece di ripeterla con un ID proprio. Il documento di dominio (round,
|
|
scommesse, transazioni…) è proprietario del comportamento, `DAT` dello
|
|
schema, `SEC` delle soglie di sicurezza.
|
|
- **Niente formulazioni vaghe nelle regole.** "Circa", "normalmente", "prima o
|
|
poi" non compaiono in un requisito: si scrive la condizione esatta, con il
|
|
nome della costante.
|
|
|
|
### Requisiti non ancora implementati
|
|
|
|
La specifica può essere più avanti del codice: è il caso normale fra il passo 1
|
|
e il passo 3 del ciclo qui sotto. Un requisito che il codice non soddisfa
|
|
ancora porta, subito dopo i riferimenti, una riga di stato e una che dice cosa
|
|
fa il codice oggi:
|
|
|
|
```markdown
|
|
- **[RND-33]** L'altezza del blocco di estrazione è fissata all'ingresso in
|
|
`drawing` come `tip_at_close + 1` e non cambia più.
|
|
*Codice:* `app/rounds/scheduler.py:RoundScheduler._close_and_draw` ·
|
|
*Test:* —
|
|
*Stato:* da implementare (B-nn) · *Oggi:* si usa la punta vista al primo
|
|
controllo utile, che può essere un blocco successivo.
|
|
```
|
|
|
|
- *Codice* indica dove andrà l'implementazione (il file o la funzione
|
|
esistente più vicina); *Test* è `—` finché il test non esiste.
|
|
- Il B-nn rimanda alla voce di [BUGS.md](../BUGS.md) che descrive la
|
|
correzione; l'ordine di implementazione è in
|
|
[piano-correzioni.md](piano-correzioni.md).
|
|
- Quando il codice viene implementato, nello stesso commit si tolgono le righe
|
|
*Stato* e *Oggi* e si completano *Codice* e *Test*. `git grep "da
|
|
implementare"` elenca in ogni momento tutto ciò che manca.
|
|
- Un requisito modificato (non nuovo) mantiene il suo identificativo: il testo
|
|
descrive il comportamento atteso, *Oggi* quello attuale.
|
|
|
|
### Prefissi
|
|
|
|
| Prefisso | Documento | Ambito |
|
|
|---|---|---|
|
|
| `ARC` | [architettura.md](architettura.md) | Struttura del codice, processi, frontend, test |
|
|
| `DAT` | [modello-dati.md](modello-dati.md) | Tabelle, campi, stati, vincoli |
|
|
| `API` | [api.md](api.md) | Endpoint, formato delle risposte, codici di errore |
|
|
| `SEC` | [sicurezza.md](sicurezza.md) | Chiavi, autenticazione, limiti, rischi accettati |
|
|
| `REG` | [registrazione-e-depositi.md](registrazione-e-depositi.md) | Registrazione e account |
|
|
| `DEP` | [registrazione-e-depositi.md](registrazione-e-depositi.md) | Depositi e saldo |
|
|
| `BET` | [scommesse-e-prelievi.md](scommesse-e-prelievi.md) | Scommesse |
|
|
| `WDR` | [scommesse-e-prelievi.md](scommesse-e-prelievi.md) | Prelievi |
|
|
| `RND` | [ciclo-di-vita-del-round.md](ciclo-di-vita-del-round.md) | Round, estrazione, pagamento |
|
|
| `TX` | [transazioni-e-riconciliazione.md](transazioni-e-riconciliazione.md) | Ciclo di vita delle transazioni, RBF, riconciliazione |
|
|
| `ELC` | [connessione-electrum.md](connessione-electrum.md) | Connessione alla rete |
|
|
| `AUD` | [audit-log.md](audit-log.md) | Eventi del registro di audit |
|
|
| `OPS` | [operativita.md](operativita.md) | Procedure operative |
|
|
| `ADR` | [adr/](adr/README.md) | Decisioni di progetto (un file per decisione) |
|
|
| `LIM` | [limiti-noti.md](limiti-noti.md) | Limiti noti accettati |
|
|
|
|
## Il ciclo di una modifica
|
|
|
|
0. **Scegliere.** Se la modifica è una funzionalità o una scelta nuova, si
|
|
scrive o aggiorna l'ADR (e si approva) prima di toccare i requisiti.
|
|
1. **Modificare la specifica.** Si cambia il testo del requisito in `docs/`
|
|
(o se ne aggiunge uno nuovo con il prossimo numero libero). Se la modifica
|
|
tocca una decisione (`ADR-*`) o un limite (`LIM-*`), si aggiorna anche
|
|
quello; se introduce una funzionalità o una scelta nuova, si scrive prima
|
|
il relativo ADR (vedi sotto).
|
|
2. **Individuare l'impatto.** I riferimenti *Codice* e *Test* del requisito
|
|
dicono dove intervenire. Si cercano anche le altre occorrenze
|
|
dell'identificativo nel repository (`git grep RND-11`) e i requisiti di
|
|
altri documenti che dipendono dallo stesso codice.
|
|
3. **Implementare e testare.** Si modifica il codice e si aggiunge o aggiorna
|
|
un test che verifica il nuovo comportamento. Il test cita l'identificativo
|
|
nella sua docstring.
|
|
4. **Allineare i riferimenti.** Se il codice si è spostato (funzione
|
|
rinominata, file diviso), si aggiornano i riferimenti *Codice* del
|
|
requisito.
|
|
5. **Commit unico.** Specifica, codice e test entrano nello stesso commit,
|
|
con l'identificativo nel messaggio (ad esempio
|
|
`Close the round on its own deadline (RND-11)`).
|
|
|
|
Una correzione di bug che non cambia la specifica (il codice non faceva ciò
|
|
che il documento già diceva) segue la convenzione `B-nn` descritta in
|
|
[storico-bug.md](storico-bug.md): numero successivo, commit dedicato, test di
|
|
regressione. Se invece il bug rivela che la specifica era sbagliata o
|
|
incompleta, si corregge prima la specifica, come per qualunque altra modifica.
|
|
|
|
## Documenti della specifica
|
|
|
|
Il punto d'ingresso è [come-funziona.md](come-funziona.md). Le guide
|
|
operative ([setup.md](setup.md), [running-the-server.md](running-the-server.md),
|
|
[guida-utente.md](guida-utente.md), [guida-admin.md](guida-admin.md)) non
|
|
contengono requisiti: descrivono come usare il software, e si aggiornano
|
|
quando la specifica cambia qualcosa di visibile.
|