Skip to content

PLT-456: Seed → deterministic PRNG sub-stream derivation#44

Merged
bdchatham merged 3 commits into
mainfrom
brandon2/plt-456-seed-prng-substreams
Jun 11, 2026
Merged

PLT-456: Seed → deterministic PRNG sub-stream derivation#44
bdchatham merged 3 commits into
mainfrom
brandon2/plt-456-seed-prng-substreams

Conversation

@bdchatham

Copy link
Copy Markdown
Contributor

Implements PLT-456 — pins how a single run seed derives the independent per-consumer PRNG sub-streams, making load runs reproducible. The derivation is a one-way door (frozen).

What

  • New utils/rng package: substream(seed, streamID) = math/rand/v2.NewPCG(seed, splitmix64(fnv1a64(streamID))), keyed by a logical stream id (string), so the draw a consumer sees is independent of worker count. SourceStream (mutex-guarded). Unseeded runs get a crypto-random seed, recorded for after-the-fact replay.
  • Replaces the three unseeded global-rand call-sites (config/gas.go, types/account_pool.go, generator/weighted.go) with derived sub-streams; falls back to the global RNG when no seed is set (behaviour-preserving).
  • Seed *uint64 on LoadConfig (nil = unseeded). Stream ids centralized in utils/rng/streams.go.

Reproducibility contract (stated honestly)

Same seed + config ⇒ identical per-stream draw multiset — the workload (distribution of keys/sizes/gas/accounts) is statistically reproducible, which is what fair A/B comparison needs. Per-tx ordering is reproducible only at one worker (concurrent workers interleave draws); on-chain arrival order is concurrent regardless; individual txs are not byte-identical. The FROZEN block freezes four inputs: the formula, the stream-id set, the per-stream draw order, and the per-tx account draw cadence — changing any requires a config_sha256 version bump.

Tests

go test -race -count=10 ./... green. TestWorkerCountMultisetInvariant runs generation concurrently and asserts per-stream column multisets; TestSingleWorkerOrderedReplay pins ordered replay at 1 worker; aliasing + tip/feecap + SetStream-no-op covered. TestAccountPoolMixedRate is now deterministic (seeded).

Review

Cross-reviewed: idiom + two independent systems rounds — which caught and corrected an overclaiming reproducibility contract (byte-identical → honest per-stream-multiset) before merge.

Decision brief: designs/sei-load-workload-modeler/PLT-456-seed-prng-substreams.md (sei-protocol/bdchatham-designs).

🤖 Generated with Claude Code

bdchatham and others added 3 commits June 11, 2026 15:47
Pin a frozen derivation substream(seed, streamID) = NewPCG(seed,
splitmix64(fnv1a64(streamID))) so same seed + config replays
byte-identically. Sub-streams are keyed by a logical consumer id, never a
live-goroutine counter, so the draw sequence is independent of --workers.

Add utils/rng (Source + per-consumer Stream handle, mutex-guarded for
concurrent draws). Thread the resolved seed through NewConfigBasedGenerator;
unseeded runs get a random seed that is recorded on cfg.Seed and logged for
after-the-fact replay (PLT-467 reads it there).

Convert the three workload RNG call-sites to derived streams, falling back to
the unseeded global RNG when no stream is bound:
- config/gas.go RandomGasGenerator.GenerateGas
- types/account_pool.go accountPool.NextAccount
- generator/weighted.go NewWeightedGenerator startup shuffle

Seed TestAccountPoolMixedRate so it asserts an exact reproducible split
instead of a probabilistic tolerance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review: the seed guarantees a reproducible per-stream draw
multiset (statistically reproducible workload for A/B), not byte-identical
ordered replay above one worker. Restate the contract honestly in the rng
package doc and the config Seed comment; drop byte-identical claims.

Freeze stream-ids and per-stream draw order alongside the formula and
centralize the stream-id strings in utils/rng/streams.go so the frozen
surface is reviewable in one place. Document the SetStream fixed/empty
no-op. Tests: rename to TestWorkerCountMultisetInvariant (asserts the
multiset), add TestSingleWorkerOrderedReplay (pins ordered replay at one
worker), cover tip/feecap streams in the seeded config, and add a
config-level guard that a bound random picker draws seed-determined values
so a refactor breaking the pointer aliasing fails loudly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The worker-count invariant test held the mutex across gen.Generate(), so
workers never drew concurrently, and it asserted per-tx gasDraw tuples — which
are not worker-invariant: concurrent txs interleave their base/tip/feecap draws
across three independently-locked streams, so tuples reassemble differently
while each stream's multiset is unchanged.

Run Generate() outside the work-claim lock so workers genuinely interleave, and
assert each gas-stream column multiset independently rather than the tuple,
matching the per-stream guarantee the contract states. Passes -race -count=10.

Also freeze the per-tx account draw cadence (sender then receiver NextAccount)
as FROZEN input #4, and correct the bindGasStreams aliasing comment: a shallow
copy is safe; only one that also clones the gas delegate breaks it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Jun 11, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches core tx generation randomness and freezes a one-way derivation contract; mistakes could skew workloads or break replay archives, but behavior is gated on seed and falls back to prior global RNG when unseeded.

Overview
Adds deterministic, seed-driven PRNG sub-streams so load runs can be replayed for fair A/B comparison. LoadConfig gains optional seed; when omitted, the generator picks a crypto-random seed, writes it back to config, and logs it for later replay.

New utils/rng derives mutex-safe sub-streams from one run seed via a frozen PCG formula keyed by logical stream ids (gas, accounts, weighted shuffle), documented so worker count does not change each stream’s draw multiset.

Gas pickers, account pools (new-account rate), and weighted scenario shuffle stop using the global rand when seeded; unseeded behavior still uses the global RNG. The config-based generator binds per-scenario gas and account streams at startup.

Tests cover stream derivation, gas SetStream aliasing, single-worker ordered replay, and per-stream column multisets under concurrent workers (not full tx ordering).

Reviewed by Cursor Bugbot for commit d0605a0. Bugbot is set up for automated code reviews on this repo. Configure here.

@bdchatham bdchatham requested review from amir-deris and masih June 11, 2026 23:11
@bdchatham bdchatham merged commit 8530f17 into main Jun 11, 2026
4 checks passed
@bdchatham bdchatham deleted the brandon2/plt-456-seed-prng-substreams branch June 11, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants