Files
plm-lottery/docs/processo-di-modifica.md
T
davideandClaude Sonnet 5.5 db24287ea5 Make the draw a commit-reveal at a fixed, confirmed, quorum-agreed height with a public proof (B-73, RND-14 … RND-20, RND-33 … RND-37, DAT-61 … DAT-66, API-25, API-45, AUD-24 … AUD-27, AUD-37, ELC-15, SEC-56, ADR-24, ADR-25)
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>
2026-10-14 16:29:09 +02:00

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-NN del documento di dominio
    Schema, stati, vincoli DAT in modello-dati.md
    Soglie e rischi di sicurezza SEC in sicurezza.md
    Eventi di audit AUD in audit-log.md
    Come lo fa il codice (riferimento percorso:funzione)
    Limiti accettati LIM in limiti-noti.md
    Difetti 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: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:

- **[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

  1. Scegliere. Se la modifica è una funzionalità o una scelta nuova, si scrive o aggiorna l'ADR (e si approva) prima di toccare i requisiti.
  2. 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).
  3. 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.
  4. 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.
  5. Allineare i riferimenti. Se il codice si è spostato (funzione rinominata, file diviso), si aggiornano i riferimenti Codice del requisito.
  6. 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.