The two Mermaid diagrams under flowchart/ described the intended flow but had drifted from the code and carried no link back to it. docs/ now holds the specification instead: 15 Italian documents (flows, architecture, data model, API, security, audit log, operations, decisions and limits, bug history) with 472 numbered requirements, each pointing at the function that implements it and the test that covers it. docs/processo-di-modifica.md defines the ID convention and the spec -> code -> test change workflow, and CLAUDE.md now names docs/ as authoritative. Writing the spec from the code also corrected stale claims in CLAUDE.md (B-41's get_history check, BUGS.md still tracking B-71/B-72, Cache-Control scope, the error contract's bare-string exceptions) and in the operator guides (payouts do retry, only `drawing` fails to resume, rounds do not open without fee_address). The B-70 doc test now pins the prose in docs/scommesse-e-prelievi.md instead of the deleted diagram. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
26 KiB
Registro di audit
Il registro di audit è la traccia, nel database, di ciò che ha mosso denaro o cambiato la configurazione: scommesse, prelievi, depositi accreditati, estrazioni, pagamenti, interventi dell'operatore. È il primo posto da guardare per ricostruire cosa è successo dopo un problema, ed è l'unica forma di responsabilità che esiste per le azioni dal pannello admin, che non ha un'identità per singolo operatore (vedi sicurezza.md).
Questo documento descrive come il registro viene scritto e letto, e cataloga
ogni tipo di evento. Le colonne della tabella audit_log sono descritte in
modello-dati.md; le procedure che partono da un evento
(per esempio un payout_failed) sono in operativita.md.
Come si scrive
-
[AUD-01] Ogni riga di audit nasce da un'unica funzione,
write_audit_log(session, event_type, payload, user_id=None, round_id=None). La funzione non fa commit: aggiunge la riga alla sessione del chiamante, che la salva insieme al resto delle sue modifiche. Nessun altro punto del codice crea righeAuditLog. Codice:app/audit/log.py:write_audit_log -
[AUD-02] Per gli eventi che accompagnano un cambiamento di stato, la riga di audit è salvata nella stessa transazione del cambiamento che descrive: o si salvano entrambi, o nessuno dei due. Vale per tutti gli eventi del catalogo tranne quelli elencati in [AUD-03]. Codice: i chiamanti elencati nel catalogo, ognuno dei quali chiama
write_audit_loge poisession.commit()sulla stessa sessione. -
[AUD-03] Quattro eventi sono solo osservativi: non accompagnano un cambiamento di stato e vengono salvati in una transazione propria, aperta apposta. Sono
draw_baseline_tip_unknown,draw_header_corroboration_failed,draw_stalledepayout_failed. Perpayout_failedla scrittura è anche best effort: se fallisce, l'errore viene registrato nel log applicativo (could not record the payout failure of round …) e ignorato. Codice:app/rounds/scheduler.py:RoundScheduler._wait_for_next_block,app/rounds/scheduler.py:RoundScheduler._adopt_baseline_tip,app/rounds/scheduler.py:RoundScheduler._log_payout_failure -
[AUD-04] Il
payloadè un dizionario serializzato in JSON nella colonnapayload_json;created_atè impostato dal database all'inserimento, in UTC.user_ideround_idsono chiavi esterne opzionali, valorizzate come indicato evento per evento nel catalogo. Codice:app/audit/log.py:write_audit_log,app/db/models.py:AuditLog -
[AUD-05] Il registro è in sola aggiunta: nessuna parte del codice modifica o cancella una riga di
audit_log, e non c'è alcuna rotazione o scadenza. La tabella cresce per sempre. Codice:app/audit/log.py:write_audit_log(unico punto di scrittura) -
[AUD-06]
event_typeè una colonnaString(32). Il nome più lungo oggi in uso,draw_header_corroboration_failed, occupa esattamente 32 caratteri: un nuovo tipo di evento deve restare entro questo limite. SQLite non lo fa rispettare, un altro database lo farebbe. Codice:app/db/models.py:AuditLog
Come si legge
-
[AUD-07]
GET /admin/audit-log(protetto daX-Admin-Token) restituisce le righe dalla più recente (ordine periddecrescente), con il parametrolimit(predefinito 200, minimo 1, massimo 500). Ogni elemento contieneid,event_type,payload(già decodificato da JSON),user_id,round_idecreated_atin ISO 8601 UTC. Non esistono filtri per tipo di evento, utente o round, né paginazione: le righe più vecchie delle ultime 500 si leggono solo direttamente dal database. Codice:app/api/routes/admin.py:list_audit_log· Test:tests/unit/test_admin.py -
[AUD-08] La sezione "Audit log" di
/adminchiama l'endpoint senza parametri (quindi le ultime 200 righe) al login e ogni volta che si apre la sezione, e mostra id, tipo, payload JSON grezzo, utente, round e data. Non ha filtri né ricerca. Codice:app/static/admin.js:loadAuditLog -
[AUD-09] Una sola logica del programma rilegge il registro:
_retry_payout_if_dueusa la rigapayout_failedpiù recente del round come orologio per decidere se ritentare il pagamento. Se è più recente di 60 secondi (_PAYOUT_RETRY_INTERVAL_SECONDS) il tentativo viene saltato; se non esiste, il tentativo parte subito. Nessun'altra decisione dipende dal registro. Codice:app/rounds/scheduler.py:RoundScheduler._retry_payout_if_due· Test:tests/unit/test_scheduler.py
Catalogo degli eventi
Legenda delle colonne user_id / round_id: sì = sempre valorizzato,
no = sempre vuoto, dipende = come spiegato nel testo. Gli importi sono
sempre in satoshi.
flowchart LR
subgraph Operatore["Pannello admin"]
config_updated
lottery_paused
lottery_resumed
admin_privkey_accessed
admin_password_reset
bug_report_status_changed
end
subgraph Fondi["Denaro degli utenti"]
deposit_credited
utxo_spent_externally
utxo_external_spend_reinstated
bet_placed
bet_broadcast_failed
withdrawal_sent
withdrawal_broadcast_failed
end
subgraph Round["Round"]
round_closed
winner_drawn
draw_baseline_tip_unknown
draw_header_corroboration_failed
draw_stalled
payout_failed
payout_sent
end
subgraph Ricon["Riconciliazione"]
pending_tx_recovered
pending_tx_abandoned
end
Pannello admin
-
[AUD-10]
config_updated— unPUT /admin/configha cambiato almeno un parametro. Payload: un oggetto con una chiave per ogni campo effettivamente cambiato, ciascuna{"from": <valore precedente>, "to": <valore nuovo>}(i campi sono quelli di_CONFIG_FIELDS:fee_address,bet_amount_sats,round_duration_seconds,round_cooldown_seconds,fee_rate_sat_vb,rbf_timeout_seconds,draw_animation_seconds). Un campo inviato con lo stesso valore che aveva non compare; se nulla cambia, non si scrive nessuna riga.user_idno,round_idno. Stessa transazione della modifica. Codice:app/api/routes/admin.py:update_config· Test:tests/unit/test_admin.py -
[AUD-11]
lottery_paused—POST /admin/pause. Payload vuoto{}.user_idno,round_idno. Viene scritto a ogni chiamata, anche se la lotteria era già in pausa. Stessa transazione dipaused = true. Codice:app/api/routes/admin.py:pause_lottery· Test: nessun test verifica la riga di audit. -
[AUD-12]
lottery_resumed—POST /admin/resume. Payload vuoto{}.user_idno,round_idno. Scritto a ogni chiamata, anche se la lotteria non era in pausa. Stessa transazione dipaused = false. Codice:app/api/routes/admin.py:resume_lottery· Test: nessun test verifica la riga di audit. -
[AUD-13]
admin_privkey_accessed—GET /admin/users/{id}/privkeyha esportato la chiave privata (WIF) di un utente. Payload{"user_id"}.user_idsì (l'utente la cui chiave è stata esportata),round_idno. La riga è salvata prima che la risposta con la chiave venga restituita: se il salvataggio fallisce, la chiave non esce. Per un utente inesistente (404) non si scrive nulla. Questa registrazione non va mai rimossa (vedi decisioni-e-limiti.md). Codice:app/api/routes/admin.py:user_privkey· Test: nessun test verifica la riga di audit (tests/unit/test_admin.pycopre solo l'esportazione). -
[AUD-14]
admin_password_reset—POST /admin/users/{id}/reset-passwordha impostato una nuova password casuale. Payload{"user_id"}; la nuova password non compare.user_idsì,round_idno. Stessa transazione del nuovo hash e dell'incremento ditoken_version(che invalida le sessioni esistenti). Codice:app/api/routes/admin.py:reset_user_password· Test: nessun test verifica la riga di audit. -
[AUD-15]
bug_report_status_changed—POST /admin/bug-reports/{id}/statusha cambiato lo stato di una segnalazione. Payload{"report_id", "from", "to"}con gli statiopen/read/resolved.user_iddipende: è l'autore della segnalazione, vuoto se era anonima;round_idno. Se lo stato richiesto è quello già presente non si scrive nulla. Stessa transazione del cambio di stato. Codice:app/api/routes/admin.py:update_bug_report_status· Test:tests/unit/test_bug_reports.py
Denaro degli utenti
-
[AUD-16]
deposit_credited— un nuovo UTXO confermato sull'indirizzo di un utente è stato accreditato, dopo la corroborazione degli altri server (vedi registrazione-e-depositi.md). Una riga per UTXO. Payload{"txid", "vout", "amount_sats"}.user_idsì,round_idno. Stessa transazione dell'inserimento inutxo_eventse del ricalcolo del saldo. Attenzione: non riguarda solo i depositi dall'esterno. Anche le vincite (l'uscita del pagamento verso il vincitore) e i resti di scommesse e prelievi arrivano all'indirizzo dell'utente e vengono accreditati per la stessa strada, quindi producono anche loro undeposit_credited. Codice:app/deposits/service.py:credit_confirmed_utxos· Test: nessun test verifica la riga di audit. -
[AUD-17]
utxo_spent_externally— un UTXO che il database considerava non speso non è più tra quelli non spesi, secondo il server attivo e la maggioranza degli altri: è stato speso fuori dalla piattaforma (per esempio con la chiave esportata). Una riga per UTXO. Payload{"txid", "vout", "amount_sats"}.user_idsì,round_idno. Stessa transazione della marcatura (spent_txid = "external-spend") e del ricalcolo del saldo. Codice:app/deposits/service.py:mark_utxos_spent_externally· Test:tests/unit/test_deposits.py -
[AUD-18]
utxo_external_spend_reinstated— un UTXO segnato come speso all'esterno è ricomparso tra i non spesi e la marcatura è stata annullata. Payload{"txid", "vout", "amount_sats"}.user_idsì,round_idno. Stessa transazione dell'annullamento e del ricalcolo del saldo. Codice:app/deposits/service.py:reinstate_reappeared_utxos· Test:tests/unit/test_deposits.py -
[AUD-19]
bet_placed— la rete ha accettato la transazione di una scommessa. Payload{"txid", "amount_sats"}:txidè quello del primo invio (un successivo RBF non aggiorna la riga),amount_satsè l'importo che arriva al montepremi, cioè la puntata al netto della commissione di rete.user_idsì,round_idsì. Stessa transazione che porta il partecipante abroadcaste la transazione pendente apending. Codice:app/bets/service.py:place_bet· Test:tests/unit/test_bets.py -
[AUD-20]
bet_broadcast_failed— la rete ha rifiutato la transazione di una scommessa; gli UTXO sono stati liberati e il partecipante rimosso. Payload{"txid", "reason"}, conreasonil testo dell'errore troncato a 200 caratteri.user_idsì,round_idno (a differenza dibet_placed). Stessa transazione del ripristino. Codice:app/bets/service.py:_release_failed_bet· Test:tests/unit/test_bets.py -
[AUD-21]
withdrawal_sent— la rete ha accettato la transazione di un prelievo. Payload{"txid", "amount_sent_sats", "external_address"}:amount_sent_satsè quanto arriva davvero all'indirizzo esterno (al netto della commissione e dell'eventuale limite di polvere, vedi scommesse-e-prelievi.md). L'id del prelievo non è nel payload.user_idsì,round_idno. Stessa transazione che porta il prelievo abroadcast. Codice:app/withdrawals/service.py:request_withdrawal· Test: nessun test verifica la riga di audit. -
[AUD-22]
withdrawal_broadcast_failed— la rete ha rifiutato la transazione di un prelievo; gli UTXO sono stati liberati e il prelievo segnatofailed. Payload{"txid", "reason"}(reasontroncato a 200 caratteri).user_idsì,round_idno. Stessa transazione del ripristino. Codice:app/withdrawals/service.py:_release_failed_withdrawal· Test: nessun test verifica la riga di audit.
Round
-
[AUD-23]
round_closed— un round è stato chiuso senza partecipanti confermati. Payload{"participants": 0}.user_idno,round_idsì. Stessa transazione del passaggio aclosed. È l'unico caso in cui la chiusura di un round viene registrata: un round chiuso dopo la conferma del pagamento non produce alcuna riga (vedi [AUD-32]). Codice:app/rounds/scheduler.py:RoundScheduler._close_and_draw· Test:tests/unit/test_scheduler.py -
[AUD-24]
winner_drawn— il vincitore è stato estratto. Payload{"winner_address", "pool_amount_sats", "block_height", "block_hash", "participants"}: indirizzo del vincitore, montepremi totale (somma delle puntate nette), altezza e hash del blocco usato come seme, numero di partecipanti.user_idsì (il vincitore),round_idsì. Stessa transazione che salva i dati dell'estrazione sul round e lo porta apaying_out. Codice:app/rounds/scheduler.py:RoundScheduler._close_and_draw· Test:tests/unit/test_scheduler.py -
[AUD-25]
draw_baseline_tip_unknown— al momento della chiusura l'altezza della catena non era nota, quindi il primo blocco ricevuto è stato preso come base e l'estrazione aspetta il blocco successivo. Payload{"baseline_height"}.user_idno,round_idsì. Transazione propria ([AUD-03]). Dovrebbe essere irraggiungibile (lo scheduler non lavora senza un'altezza nota), vedi ciclo-di-vita-del-round.md. Codice:app/rounds/scheduler.py:RoundScheduler._adopt_baseline_tip· Test:tests/unit/test_scheduler.py -
[AUD-26]
draw_header_corroboration_failed— il blocco candidato per l'estrazione non è stato confermato dalla maggioranza degli altri server Electrum (o nessuno ha risposto); l'estrazione aspetta un blocco ulteriore. Una riga per ogni blocco candidato rifiutato. Payload{"height", "reported_hash"}.user_idno,round_idsì. Transazione propria ([AUD-03]). Codice:app/rounds/scheduler.py:RoundScheduler._wait_for_next_block· Test:tests/unit/test_scheduler.py -
[AUD-27]
draw_stalled— l'estrazione aspetta un nuovo blocco da più di 360 secondi (_DRAW_STALL_THRESHOLD_SECONDS), contati dadrawing_started_at; la riga si ripete ogni 360 secondi finché l'attesa continua. Payload{"tip_at_close", "current_tip_height", "elapsed_seconds"}. Nota:tip_at_closeè la base attuale dell'attesa, che dopo undraw_header_corroboration_faileddiventa l'altezza del candidato rifiutato, non più quella della chiusura.user_idno,round_idsì. Transazione propria ([AUD-03]). Non viene scritta mentre è in corso una corroborazione, solo mentre si aspetta un blocco. Codice:app/rounds/scheduler.py:RoundScheduler._wait_for_next_block· Test:tests/unit/test_scheduler.py -
[AUD-28]
payout_failed— un tentativo di pagamento non è partito. Payload{"round_id", "reason"}.user_iddipende: il vincitore, tranne quando il motivo è l'assenza di connessione (vuoto);round_idsì. Transazione propria e best effort ([AUD-03]); è l'orologio del nuovo tentativo ([AUD-09]). Valori possibili direason:reasonCausa electrum client not connectedNessuna connessione Electrum al momento del tentativo no fee_address configuredfee_addressvuoto nella configurazionewinner user not foundL'utente vincitore non esiste più nel database insufficient pool UTXOsGli UTXO confermati e non riservati dell'indirizzo del montepremi non coprono l'importo too_many_inputsServirebbero più di MAX_PAYOUT_TX_INPUTS(500) inputwinner_share_below_network_feeLa quota del vincitore non copre la commissione di rete commission_below_dust_limitLa quota della piattaforma è sotto il limite di polvere payout build failedQualunque altro errore nella costruzione (per esempio un fee_addressmalformato)broadcast rejectedLa rete ha rifiutato la transazione; la riga buildingresta al riconciliatoreUn tentativo saltato perché esiste già un pagamento
buildingopendingper il round non scrivepayout_failed. Cosa fare per ogni motivo è descritto in operativita.md. Codice:app/rounds/scheduler.py:RoundScheduler._trigger_payout,app/rounds/scheduler.py:RoundScheduler._log_payout_failure· Test:tests/unit/test_scheduler.py -
[AUD-29]
payout_sent— la rete ha accettato la transazione di pagamento. Payload{"txid", "winner_sats", "commission_sats"}: quanto riceve il vincitore (già al netto della commissione di rete) e quanto va afee_address.user_idsì (il vincitore),round_idsì. Stessa transazione che porta la transazione pendente dabuildingapending. Codice:app/rounds/scheduler.py:RoundScheduler._trigger_payout· Test:tests/unit/test_scheduler.py
Riconciliazione
-
[AUD-30]
pending_tx_recovered— una transazione rimastabuilding(tipicamente per un riavvio a metà invio) risulta invece presente in catena ed è stata promossa apending. Payload{"pending_transaction_id", "kind", "txid"}.user_ideround_iddipendono dal tipo: sono quelli della rigapending_transactions(per un pagamentouser_idè vuoto, per un prelievoround_idè vuoto). Stessa transazione della promozione. Vedi transazioni-e-riconciliazione.md. Codice:app/tx/reconcile.py:_promote· Test: nessun test verifica la riga di audit. -
[AUD-31]
pending_tx_abandoned— una transazionebuildingopendingnon compare nella cronologia del proprio indirizzo ed è stata dichiarata persa: statofailed, UTXO liberati, partecipante rimosso o prelievofailed,payout_txiddel round azzerato. Payload{"pending_transaction_id", "kind", "txid", "reason", "utxos_released"}, conreasonoggi sempre"not found on chain"eutxos_releasedil numero di UTXO liberati (sempre 0 per un pagamento, perché l'indirizzo del montepremi non è tracciato inutxo_events).user_ideround_idcome in [AUD-30]. Stessa transazione dell'abbandono. Codice:app/tx/reconcile.py:_abandon· Test:tests/unit/test_reconcile.py
Cosa non viene registrato
- [AUD-32] Questi fatti non producono una riga di audit; restano
ricostruibili solo dallo stato delle tabelle o dal log applicativo:
- apertura di un round;
- passaggi di stato del round a
closingedrawing, e la chiusura dopo la conferma del pagamento (_on_payout_confirmed); - conferma di una scommessa, di un prelievo o di un pagamento
(
ConfirmationPoller); - ogni aumento di commissione RBF e ogni RBF impossibile (solo log);
- registrazione, login, login falliti e blocchi per troppi tentativi;
- cambio password fatto dall'utente (
POST /users/me/change-password); - invio di una segnalazione di bug;
- accesso in lettura al pannello admin (elenchi utenti, round, audit).
Codice:
app/rounds/service.py:open_new_round_if_needed,app/rounds/scheduler.py:RoundScheduler._tick,app/rounds/confirmation.py:_on_payout_confirmed,app/tx/confirmation.py:poll_once,app/tx/broadcast.py:bump_fee
Log applicativo
Il registro di audit è nel database e si vede da /admin; il log applicativo
è un file di testo e contiene molto di più (errori, riconnessioni, attese), ma
va letto sul server.
-
[AUD-33] All'importazione di
app.main,setup_loggingaggiunge al logger radice (livelloINFO) unRotatingFileHandlersulogs/app.log, relativo alla radice del progetto: 10 MB per file, 5 file di rotazione (app.log.1…app.log.5), formatodata ora LIVELLO [logger] messaggio. Lo stesso handler è aggiunto auvicorn.erroreuvicorn.access, che non propagano al radice. Nel container la radice del progetto è/app, e/app/logsè montato su./data/logsdell'host. Codice:app/logging_config.py:setup_logging,docker-compose.yml -
[AUD-34] Il logger radice ha solo l'handler su file: i messaggi dei moduli dell'applicazione (scheduler, listener, riconciliatori) finiscono in
data/logs/app.loge non sull'output del container.docker compose logs appmostra solo ciò che uvicorn scrive con la propria configurazione (avvio, richieste) e l'output di avvio del container (controllo della chiave, migrazioni Alembic). Codice:app/logging_config.py:setup_logging -
[AUD-35] Un'eccezione non gestita in una richiesta HTTP viene registrata per intero nel log (
Unhandled error on <metodo> <percorso>) e al client arriva solo un errore genericointernal_errorcon stato 500, senza il testo dell'eccezione. Codice:app/main.py:log_unhandled_exception -
[AUD-36] Le richieste di polling delle due interfacce (per esempio
/rounds/currentogni 15 secondi per ogni pagina aperta, più un aggiornamento a ogni evento SSE) vengono tutte registrate dauvicorn.access. Con 60 MB di spazio complessivo, la rotazione può cancellare in poche ore o giorni i messaggi più vecchi: il log non è un archivio, il registro di audit sì. Codice:app/logging_config.py:setup_logging
Messaggi di log che contano
Messaggi non accompagnati da una riga di audit ma utili in esercizio. Il
nome tra parentesi quadre nel log è il modulo (app.rounds.scheduler, ecc.).
| Livello | Modulo | Messaggio (inizio) | Significato |
|---|---|---|---|
| WARNING | app.electrum.listener |
all N Electrum server(s) unreachable, retrying in Ns |
Tutti i server provati hanno fallito; attesa da 1 a 30s prima del giro successivo |
| ERROR (con traccia) | app.electrum.listener |
Electrum session on … failed |
Errore nell'aprire la sessione: connessione, primo header, primo header rifiutato |
| INFO | app.electrum.listener |
Electrum connected to … / Electrum connection to … ended, reconnecting |
Connessione aperta / chiusa normalmente |
| WARNING | app.electrum.listener |
Electrum session on … ending: … |
Motivo della fine di una sessione già aperta (ping scaduto, socket chiuso, header successivo rifiutato con HeaderValidationError) |
| WARNING | app.electrum.listener |
ignoring Electrum header at height … / ignoring a competing header … |
Header scartato senza chiudere la sessione (senza hex, più basso, concorrente) |
| WARNING | app.electrum.listener |
could not corroborate … with any of N other configured server(s) |
Nessun altro server ha risposto: la corroborazione fallisce |
| WARNING | app.electrum.listener |
N new UTXO(s) for user_id=… not corroborated by the other servers |
Deposito non ancora accreditato; si riprova al prossimo aggiornamento |
| WARNING | app.electrum.listener |
N UTXO(s) spent outside the platform for user_id=… |
Accompagna utxo_spent_externally |
| WARNING | app.electrum.listener |
could not subscribe a newly-registered user's address / resubscribing all users failed partway through |
Sottoscrizione persa; la recupera DepositReconciler entro 300s |
| ERROR (con traccia) | app.electrum.listener |
failed to resubscribe user_id=… |
Un singolo utente non risottoscritto alla riconnessione |
| WARNING | app.deposits.service |
listunspent for user_id=… returned no entries at all … |
Risposta vuota per un indirizzo con fondi: ignorata come incompleta |
| ERROR (con traccia) | app.deposits.reconcile |
deposit reconciliation failed for user_id=… |
Il controllo periodico di un utente è fallito |
| ERROR (con traccia) | app.rounds.scheduler |
round scheduler tick failed |
Eccezione in un ciclo dello scheduler. Se avviene durante drawing, il round resta bloccato (vedi operativita.md) |
| INFO | app.rounds.scheduler |
round N: N bet(s) still in flight at close time, waiting |
Chiusura rinviata per scommesse ancora in volo |
| INFO | app.rounds.scheduler |
round N: still waiting for a block past height … |
Estrazione in attesa, ogni 60s |
| ERROR | app.rounds.scheduler |
round N: block … header … could not be corroborated … |
Accompagna draw_header_corroboration_failed |
| WARNING | app.rounds.scheduler |
round N: chain tip was unknown at closing time … |
Accompagna draw_baseline_tip_unknown |
| ERROR | app.rounds.scheduler |
round N payout blocked: … / payout deferred: not connected / payout failed: … / payout build failed / payout broadcast failed |
Accompagnano i vari payout_failed |
| INFO | app.rounds.scheduler |
round N payout already in flight (pending_transaction N), skipping |
Pagamento già in volo. Mentre aspetta la conferma, ricompare a ogni ciclo (ogni 5s) |
| ERROR (con traccia) | app.rounds.scheduler |
could not record the payout failure of round N |
Il payout_failed stesso non è stato salvato |
| ERROR | app.rounds.service |
invariant violated: N rounds are active at once |
Più di un round attivo: non dovrebbe mai accadere |
| ERROR | app.rounds.service |
could not open a round after 3 attempts |
Conflitti ripetuti all'apertura di un round |
| ERROR (con traccia) | app.tx.broadcast |
could not bump pending_transaction N |
RBF impossibile (resto insufficiente o commissione già al massimo). Si ripete ogni 30s finché la transazione conferma o viene abbandonata |
| INFO | app.tx.broadcast |
bumped <tipo> pending_transaction N: <vecchio> -> <nuovo> |
RBF riuscito: l'unica traccia di un aumento di commissione |
| WARNING | app.tx.confirmation |
could not fetch history for pending_transaction … |
Controllo di conferma non riuscito, si riprova tra 10s |
| WARNING | app.tx.reconcile |
could not check pending_transaction … against the chain |
Il riconciliatore non ha potuto decidere; non abbandona nulla |
| WARNING | app.tx.reconcile |
abandoned <tipo> pending_transaction … |
Accompagna pending_tx_abandoned |
| ERROR | app.tx.reconcile |
round N payout tx … vanished — round needs operator attention |
Pagamento abbandonato; il nuovo tentativo automatico parte da solo |
| ERROR | app.auth.security |
stored password hash is unparseable … |
Hash di password corrotto nel database: quell'utente non può accedere |
| ERROR (con traccia) | app.main |
Unhandled error on … |
Errore 500 su una richiesta ([AUD-35]) |