v2.8 · RNG PRE-SUBMISSION PACKAGE v1.2
RAIN RNG · PRE-SUBMISSION PACKAGE v1.2 · DOCUMENT

STATE-MODIFICATION — GLI-19 §3.3.2(C) EXTERNAL ENTROPY

Source 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.