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>
8.1 KiB
Processo di modifica: dalla documentazione al codice
L'indice di tutta la documentazione è 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/ Che cosa deve fare il software (regola verificabile) il requisito PREFISSO-NNdel documento di dominioSchema, stati, vincoli DATin modello-dati.mdSoglie e rischi di sicurezza SECin sicurezza.mdEventi di audit AUDin audit-log.mdCome lo fa il codice (riferimento percorso:funzione)Limiti accettati LIMin limiti-noti.mdDifetti trovati e corretti 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: 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.
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:
- **[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:funzioneopercorso: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,
DATdello schema,SECdelle 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:
- **[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 che descrive la correzione; l'ordine di implementazione è in 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 | Struttura del codice, processi, frontend, test |
DAT |
modello-dati.md | Tabelle, campi, stati, vincoli |
API |
api.md | Endpoint, formato delle risposte, codici di errore |
SEC |
sicurezza.md | Chiavi, autenticazione, limiti, rischi accettati |
REG |
registrazione-e-depositi.md | Registrazione e account |
DEP |
registrazione-e-depositi.md | Depositi e saldo |
BET |
scommesse-e-prelievi.md | Scommesse |
WDR |
scommesse-e-prelievi.md | Prelievi |
RND |
ciclo-di-vita-del-round.md | Round, estrazione, pagamento |
TX |
transazioni-e-riconciliazione.md | Ciclo di vita delle transazioni, RBF, riconciliazione |
ELC |
connessione-electrum.md | Connessione alla rete |
AUD |
audit-log.md | Eventi del registro di audit |
OPS |
operativita.md | Procedure operative |
ADR |
adr/ | Decisioni di progetto (un file per decisione) |
LIM |
limiti-noti.md | Limiti noti accettati |
Il ciclo di una modifica
- Scegliere. Se la modifica è una funzionalità o una scelta nuova, si scrive o aggiorna l'ADR (e si approva) prima di toccare i requisiti.
- 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). - 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. - 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.
- Allineare i riferimenti. Se il codice si è spostato (funzione rinominata, file diviso), si aggiornano i riferimenti Codice del requisito.
- 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: 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. Le guide operative (setup.md, running-the-server.md, guida-utente.md, guida-admin.md) non contengono requisiti: descrivono come usare il software, e si aggiornano quando la specifica cambia qualcosa di visibile.