STATE-MODIFICATION — GLI-19 §3.3.2(C) EXTERNAL ENTROPY
sdk-v2/lab/STATE-MODIFICATION-3.3.2c.md · commit 96368332 · GLI CSR §2.2: Independence / unpredictability · ← package index · raw on GitHub ↗RAIN RNG v2.1 — Periodic state modification with external entropy (GLI-19 §3.3.2(c)) — one page
GLI-19 v3.0 §3.3.2(c): the RNG "shall periodically modify its state through the use of external entropy, limiting the effective duration of any potential exploit". Extracted from RNG-DESCRIPTION.md §2–3 and Appendix A so the answer is in one place. Code references: packages/rng-core/src/rng.ts (outcome, randomBytes32), packages/rng-session/src/index.ts (RainRngHouse.rotate, localRotate, lines 117/242), packages/rng-core/src/drbg.ts (drbg(r_k, gameId)).
1. What "the state" is
RAIN is a two-party commit-reveal generator. The house (party B) holds a 256-bit CSPRNG-generated houseSeed and a hash chain c_B[0..L] (root published before the session); the player/operator (party A) holds playerSeed and its own chain c_A[0..L] (root published before the session). The house state at time t is {houseSeed, chainSecret_B, all unrevealed c_B[k], sessionSeed, sessionId, the round counter}. The generator seed of round k is
r_k = keccak256(abi.encode(pRev_k, hRev_k, sessionSeed, sessionId, k)) pRev_k = c_A[k], hRev_k = c_B[k]
and the draw layer drbg(r_k, gameId) is a pure function of r_k — it holds no state across rounds.
2. Three layers of state modification
| layer | mechanism | cadence | entropy source | what it limits |
|---|---|---|---|---|
| A. Per-round external entropy | r_k mixes the player's fresh reveal pRev_k — a value the house never possessed and cannot influence (Appendix A Claim 2, Claim 4 binding). From the house's point of view it is 256 bits of external entropy injected into the generator seed every round |
every round (continuous, not merely "periodic") | party A's OS CSPRNG (randomBytes32() → crypto.getRandomValues), or a wallet signature on-chain; never time, never a counter |
an attacker holding the entire house state still cannot predict or bias r_k: for any fixed hRev_k, r_k is a uniform function of the unknown pRev_k |
| B. Chain rotation / re-commit | at 90 % consumption of the chain (default L = 65 536; on-chain 4 096) both parties draw fresh houseSeed / chainSecret / playerSeed / chainSecret from the CSPRNG and run a new commit-reveal (localRotate, RainRngHouse.rotate) |
≤ 58 982 rounds (local) / ≤ 3 686 (on-chain), or on any session re-open | both parties' OS CSPRNGs | the lifetime of any one party's secret material is bounded to one session; a snapshot of a chain expires with it |
| C. Per-round DRBG re-instantiation | drbg(r_k, gameId, {mechanism}) — HMAC_DRBG (SP 800-90A) or ChaCha20 is instantiated from r_k for one round and discarded; no Generate state survives the round |
every round | r_k (layer A) |
a compromise of the DRBG's internal working state (e.g. via a memory read) exposes at most the remaining draws of that round |
3. Attacker with a full house-state snapshot at time t — what they can and cannot do
Assume the attacker copies the complete house state at time t (all of houseSeed, chainSecret_B, every c_B[k], sessionSeed, sessionId) and is never detected.
| capability | answer | why |
|---|---|---|
| reproduce past outcomes r_1 … r_t | yes — but they are already public | every past reveal of both parties is public after settlement; anyone can (and verifiers do) recompute them |
| predict the next outcome r_{t+1} before the player reveals | no | requires pRev_{t+1} = c_A[t+1], held only by the player; a pre-image of the public c_A[t] → 2²⁵⁵ expected keccak evaluations (Claim 1) |
| bias r_{t+1} by choosing/altering the house reveal | no | hRev_{t+1} is bound by keccak(abi.encode(c_B[t+1])) == c_B[t] (Claim 4); any other value is rejected by the player and by the contract in a dispute |
| predict r_{t+1} after the player has revealed but before the house reveals | yes, for that one round | this is the ordinary reveal ordering: the player reveals first, and the house — honest or compromised — then learns r_{t+1}; the house cannot change it (row above) and the player has already fixed their bet before revealing (RTS 7B, no re-draw §8). The exploit window is one round, and it is the same window an uncompromised house has |
| extend the exploit beyond the current session | no | layer B: the house's secrets are replaced from the CSPRNG at rotation; the snapshot contains nothing about the new secrets |
| learn anything about the player's secret from the house state | no | the house state contains only c_A[0..t] (public) — the player's chainSecret_A never leaves party A |
| compromise of both parties' state | out of any RNG's threat model — equivalent to stealing the seed of a conventional RNG and the game client | Appendix A, "What is not claimed" |
Effective duration of any potential exploit (the quantity §3.3.2(c) asks to limit): zero rounds of prediction or bias against the player from a house-side compromise (layer A), at most the current round's draws from a DRBG-working-state compromise (layer C), at most one session for any secret material (layer B).
4. Wording for the certificate
The RNG state is re-keyed on every draw round from a 256-bit value that depends on fresh secret input from each of two independent parties (the counter-party's reveal is external entropy on every round); the draw-layer DRBG is instantiated per round and holds no cross-round state; the secret material of each party is replaced from the OS CSPRNG at least every 65 536 rounds (4 096 on-chain).
5. Evidence
| claim | evidence |
|---|---|
| the player's reveal changes every round and is external | ceremony transcripts in results/outcomes/<mech>/<set>.csv.meta.json (sessions[], per-session playerChainRoot, houseChainRoot), verified by @rain/rng-session on both sides |
| rotation happens at 90 % | packages/rng-session/test/ rotation tests (npm test); sessions count in every outcomes meta (sessions: 340 for 2·10⁷ rounds at L = 65 536 → rotation every 58 982 rounds) |
| a player with degenerate entropy cannot alter the distribution (Claim 2, the mirror image of a house compromise) | adversarial data sets dice-adv-{zero,fixed,grind} in REPORT-STATISTICAL.md §5.1 — indistinguishable from honest |
| no cross-round DRBG state | drbg() is a pure function; packages/rng-core/src/drbg.ts; unit tests for determinism from r_k |
← Back to the package index · Rendered 2026-09-14 09:34 UTC from the repository copy; the markdown in the zip / repo is the document of record.