fix(sim): cap crash point so extreme seeds cannot overflow or bankrupt

At u=1 the unsigned quotient exceeded int64 and wrapped negative, so the
rarest and most valuable outcome silently became an instant 1.00x loss.
At u=2 it produced a 2.1-billion-times payout the house could never
cover, which would have left settlement failing and the player unpaid.
The crash point is now capped at the largest multiplier the curve can
express, which is unreachable anyway since the round hits its tick
ceiling first.

FromInt now panics outside the Q32.32 integer range instead of wrapping
a positive input into a negative value.

Raises coverage to 88% overall; adds a Makefile with db-reset, since the
append-only ledger steadily consumes bridge headroom across test runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
drjones
2026-08-05 15:52:50 +00:00
parent f2c02e2bde
commit dee3becd47
14 changed files with 790 additions and 42 deletions

View File

@@ -185,10 +185,10 @@ func TestFullRoundLifecycleAndVerification(t *testing.T) {
// Wait for the round to settle and expose its proof.
var proof struct {
Commitment string `json:"commitment"`
ServerSeed string `json:"server_seed"`
ClientSeed string `json:"client_seed"`
Nonce int64 `json:"nonce"`
Commitment string `json:"commitment"`
ServerSeed string `json:"server_seed"`
ClientSeed string `json:"client_seed"`
Nonce int64 `json:"nonce"`
Participants []string `json:"participants"`
}
settled := false

View File

@@ -218,8 +218,8 @@ func (s *server) handleHealth(w http.ResponseWriter, r *http.Request) {
status = "ledger_imbalance"
}
writeJSON(w, http.StatusOK, map[string]any{
"status": status,
"ledger_sum_msat": total,
"status": status,
"ledger_sum_msat": total,
})
}
@@ -407,12 +407,12 @@ func (s *server) handleCashout(w http.ResponseWriter, r *http.Request) {
func (s *server) handleScratchCatalog(w http.ResponseWriter, r *http.Request) {
type entry struct {
ID string `json:"id"`
Name string `json:"name"`
Blurb string `json:"blurb"`
Cells int `json:"cells"`
RTPBP uint64 `json:"rtp_bp"`
Odds []scratch.OddsRow `json:"odds"`
ID string `json:"id"`
Name string `json:"name"`
Blurb string `json:"blurb"`
Cells int `json:"cells"`
RTPBP uint64 `json:"rtp_bp"`
Odds []scratch.OddsRow `json:"odds"`
}
out := make([]entry, 0, len(scratch.Catalog))
for _, t := range scratch.Catalog {