Skip to content

validation/detsample: Go chain-validator for reproducible sampling (Stage-1 replay)#1448

Draft
bonujel wants to merge 11 commits into
gonka-ai:mainfrom
bonujel:bonujel/deterministic_sampling_merged
Draft

validation/detsample: Go chain-validator for reproducible sampling (Stage-1 replay)#1448
bonujel wants to merge 11 commits into
gonka-ai:mainfrom
bonujel:bonujel/deterministic_sampling_merged

Conversation

@bonujel

@bonujel bonujel commented Jul 14, 2026

Copy link
Copy Markdown

What this is

The gonka half of the two-stage inference-validation design in #1199: the Go chain-validator that replays reproducible sampling artifacts, plus the shared cross-language conformance vectors that prove it is byte-for-byte identical to the vLLM-side Python validator.

This PR is:

It is the gonka half of a symmetric pair: vLLM produces reproducible sampling artifacts and a Python validator; this package is the Go validator the chain runs. Both reduce the same signed logprobs to the same sampled token from the same seed — verified by a common vector file.

How it works

The chain validates an inference in two stages.

  • Stage 1 — Sampling-Replay (this package, full). The cheap, exact check: the validator re-derives the chain-bound RNG seed and replays the signed per-position logprobs through a deterministic decimal pipeline, checking that the same seed reduces to the same sampled token, bit for bit.
    • It never recomputes logprobs — batch composition alone flips 0–8% of honest positions, even after the executor weight fix — it replays the signed ones.
  • Stage 2 — distance (metric only here). Covers the positions Stage 1 structurally can't: greedy / temperature-0 and unbounded support.
    • This package provides the metric — MAE over the top-K support — which separates honest from a wrong model by ~40–65×.
    • The threshold that turns that metric into a verdict is a policy call (Decision D) and is not in this package.

Scope of this PR. Stage 1 is complete; Stage 2 contributes the distance metric only. Signature verification of the artifact (Decision A) and wiring into consensus are out of scope. In short: this makes honest executor output replayable and provably consistent across languages, not yet enforceable.

What's in the diff

RNG and pipeline

rng.go

  • Sha256CounterRNG: portable counter-mode RNG, u64 = first_8_bytes(SHA256(seed ‖ counter_be_u64)).
  • Uint64Below (unbiased [0,n) via rejection sampling) and SampleCategoricalWeights over integer weights, which raises on an all-zero weight vector instead of a silent fallback.

pipeline.go

  • LogprobsToWeights / DecimalSampleFromLogprobs: the decimal pipeline via cockroachdb/apd/v2.
  • Fixed lexicographic token-ID string order; temperature → softmax → top_k/top_p/min_p filter → renormalize → integer weights summing to exactly 2^16 (65536).
  • Exactness is what lets executor and validator reduce identically: the float path sums to [65530, 65541] and false-rejects 14–77% of honest positions; the decimal total is exactly 2^16, giving 0/128 false-rejects (E1).

Seed derivation

seed.go

  • DeriveChainBoundSeed(userSeed int64, inferenceID string): Go side of fix: bind deterministic sampling seed to chain inference id vllm#56, mirroring the Python derive_chain_bound_seed byte-for-byte.
  • Domain-tagged (gonka-deterministic-sampling-v1), byte-length-prefixed framing, fail-closed: rejects empty, >256-char, and non-printable-ASCII inference IDs.
  • The validator derives the seed and never trusts request-controlled material.

Verdict — single position and sequence

verify.go

  • VerifyPosition(Position) Result: single-position replay with a three-valued Verdict {honest, fraud, inconclusive}.
  • A mismatch is fraud only when the validator could faithfully replay. An unsupported contract version or seed domain, a greedy (temp-0) position, a non-positive or unparseable temperature, or the validator's own replay error is inconclusive, never fraud — in a slashing system a false-fraud is far costlier than a missed detection.

sequence.go

  • VerifySequence(SequenceRequest, []SequencePosition) SequenceResult: sequence-level Stage-1 orchestration.
  • Composes the per-position seed base|pos, loops VerifyPosition, and aggregates: any Fraud → Fraud; else at least one Honest → Honest; else Inconclusive.
  • Version, seed-domain, greedy, and unbounded-support gates run before any replay. Mirrors the Python verify_sequence.

Stage-2 distance metric

distance.go

  • MAEDistance(executor, validator): Stage-2 distance as MAE over the signed top-K support, with sorted-key summation for byte-exact cross-language equality.
  • Stage2MAEFraudThreshold is a documented placeholder (Decision D); nothing here enforces. Mirrors the Python mae_distance.

Cross-language conformance (S3)

testdata/conformance_vectors.json + conformance_vectors_test.go + sequence_conformance_test.go

  • The shared vector file: RNG reference, Uint64Below sequences, float→string canonicalization, seed-derivation reference, pipeline cases, and now sequence-level and distance cases too.
  • The vLLM-side PR generates and replays the same JSON. This proves Python↔Go byte-parity from the primitives up to the sequence verdict and the distance value — for example, an MAE of 0.5000000000000001 matches exactly — closing the previously-untested cross-language gap (S3).

Testing

go test ./internal/validation/detsample/ is green. Coverage:

  • rng_test.go, pipeline_test.go
  • seed_test.go — domain-separation and fail-closed invariants
  • verify_test.go — three-valued verdict paths
  • sequence_test.go — aggregation across honest / fraud / inconclusive / unbounded / greedy / version / missing-logprobs
  • distance_test.go
  • reject_test.go — rejection-sampling paths
  • conformance_vectors_test.go, sequence_conformance_test.go — the shared vectors

Still open — do not merge for enforcement

  1. Decision A — signed-artifact contract (keystone). Which fields bind into responseHash, which tokens' logprobs are signed (the filter's kept set, not a fixed K), canonicalization, filter-edge rules, and a version descriptor. SupportedContractVersion = "1.0.0" is a placeholder pending A; everything downstream depends on it.
  2. Wiring into consensus. inference_validation.go does not call detsample yet. Turning it on is gated on Decision A and enforcement gating (Decision D).
  3. Seed hardening (Decision C). DeriveChainBoundSeed binds (user_seed, inference_id). inference_id (via fix: bind deterministic sampling seed to chain inference id vllm#56) removes the executor-choosable hole, but user_seed stays request-controlled, leaving residual offline grinding. Bind inputs the executor cannot pick (executor address / block hash) or commit user_seed on-chain.
  4. Stage-2 threshold (Decision D). The metric (MAEDistance) is settled; the threshold value, epochs, and cross-architecture CI gate are policy.
  5. Tokenizer determinism. If the seed binds tokenized prompt material, tokenizer or engine drift changes the seed and false-rejects. Decide: pin the tokenizer/engine on-chain, or bind prompt_hash.

Note: RNG semantics (Decision B) are resolved to per-position independent seeding (base|pos), matched by the executor on the vLLM side.

Companion PR

The vLLM side — the executor sampling fix, the Python validator, and the GPU evidence — is gonka-ai/vllm#62.

Refs: #1199

bonujel added 11 commits July 14, 2026 17:11
Land the cross-language contract fixtures at the gonka<->vLLM boundary:
- testdata/conformance_vectors.json: golden vectors generated from the vLLM
  reference impl (contract v1.0.0), the executable form of the contract.
- DETERMINISTIC_SAMPLING_CONTRACT.md: mirror of the authoritative vLLM contract.
- conformance_vectors_test.go: TestConformanceVectorsWellFormed validates the
  fixture invariants today (weight_scale=2^16, per-case sum, RNG reference).
  TestConformanceReplay skips pending the Go decimal pipeline + Sha256CounterRNG
  (item 2), at which point it becomes the cross-language parity gate.
…§5/§6)

First cross-language parity result for the deterministic-sampling scheme. The Go
Sha256CounterRNG and integer categorical sampler reproduce the Python reference
bit-for-bit: TestRNGReference matches the pinned RNG stream, and
TestCategoricalReplayFromExpectedWeights feeds every conformance vector's
committed expected_weights through the Go sampler and gets the exact
expected_token (all 10 cases).

Isolated in a stdlib-only sub-package so it compiles and tests offline, free of
the parent package's chain deps. This closes the §5/§6 half of cross-language
reproducibility; the decimal pipeline (§4, logprobs -> weights) lands next and
completes the parity gate (TestConformanceReplay).
…y proven

Port the reference logprobs->integer-weights pipeline (contract §4) to Go using
cockroachdb/apd (General Decimal Arithmetic, same spec as CPython libmpdec) under
prec=10 / ROUND_HALF_EVEN, line-for-line with the Python reference.

Result: apd reproduces libmpdec bit-for-bit at prec=10 across all 10 conformance
cases — INCLUDING the softmax exp(), the biggest unknown. TestPipelineWeightsMatch
asserts identical integer weights; TestPipelineTokenMatch asserts identical
sampled tokens (full §4+§5+§6 replay). This empirically closes the S3
cross-language-reproducibility gap the review flagged as untested on either side.

Note: apd/v2 is currently an indirect dep (via cosmos-sdk); run 'go mod tidy' on a
networked machine to promote it to direct.
… parity

S1 step 2/2 (Go side of gonka-ai/vllm#56's derive_chain_bound_seed).

DeriveChainBoundSeed reproduces the Python digests bit-for-bit and fails closed
on the identical boundary (printable-ASCII 0x21..0x7E, <=256, non-empty id;
int64 seed). Verified against the shared seed_derivation vectors:
TestChainBoundSeedAccept (5 digests incl. the pinned golden vector, negative
seed, int64-max, devshard-style id), TestChainBoundSeedReject (empty /
whitespace / control / non-ASCII / too-long), TestChainBoundSeedDomainSeparation.
Full detsample suite (sampling + seed) green offline.

Seed parity now joins the sampling parity proven earlier; the whole replay
surface (seed -> weights -> token) is cross-language bit-identical.
…erdict

Add the layer the chain validator calls: VerifyPosition(Position) -> Result,
built on the bit-verified primitives. The verdict is Honest / Fraud /
Inconclusive — version gating and the greedy (temperature 0) exemption run
before any fraud verdict, so a version skew or a validator-side replay error is
never punished as executor fraud (the failure-taxonomy gap from the review).

Tests over the conformance vectors: every honest case -> Honest; every tampered
token -> Fraud; unknown contract/seed version and greedy -> Inconclusive (and a
tamper under an unsupported version stays Inconclusive — gating wins). Offline,
dependency-free. Wiring this into inference_validation.go is the GPU/networked
step; sequence replay waits on the open RNG-semantics item.
…p guard)

- Ryanchen911#1 Split Uint64Below into pure rejectionLimit + reduceUnbiased so the
  reject-and-retry branch is directly testable. Previously the pipeline only
  ever passed n=2^16 (power of two) so the branch was dead in tests.
  TestReduceUnbiasedRejects forces it via an injected draw source;
  TestUint64BelowParity replays the non-power-of-2 vectors vs the Python
  reference; TestRejectionLimit / TestSampleCategoricalWeightsErrors cover the
  boundary math and guard paths.
- Ryanchen911#2 VerifyPosition now guards temperature <= 0 (or unparseable) explicitly and
  returns Inconclusive, instead of relying on a downstream divide-by-zero.

Full detsample suite green offline.

Note (Ryanchen911#3): apd/v2 is still an indirect dep; run 'go mod tidy' on a networked
machine to promote it (can't fetch the module graph offline here).
…nchen911#3)

detsample imports apd/v2 directly for the decimal pipeline; it was carried as an
indirect dep via cosmos-sdk. Promote it so the dependency is declared honestly.
Mirrors the Python serving-side additions (validation_sampling.verify_sequence /
mae_distance) so the Go chain validator and the vLLM Python validator reach the
same verdict from the same artifact — restoring Python<->Go parity at the
sequence level, not just the primitives (gonka-ai#1199 follow-up).

- VerifySequence: replays a whole response over VerifyPosition, composing each
  position's seed as "<base>|<pos>" (Decision B = per-position, byte-identical to
  the Python side), and aggregates three-valued: any Fraud -> Fraud (first
  position); else >=1 Honest -> Honest (Inconclusive positions defer to Stage-2);
  else all Inconclusive. Version/seed-domain/greedy gating and the unbounded-
  support gate (supportIsBounded: bounded iff top_k or min_p active) run before
  any per-position replay, so a request the validator cannot cover never yields
  Fraud.
- MAEDistance: Stage-2 metric as mean-over-positions MAE over the reported top-K
  support (Experiment 4), replacing the relative-diff ratio. Stage2MAEFraud
  Threshold is a documented Decision-D placeholder, non-enforcing.

11 table tests (honest/fraud/inconclusive/unbounded/greedy/version/missing-
logprobs aggregation + distance). go test ./internal/validation/detsample/ green.

Refs: gonka-ai#1199
…byte-parity

Consumes the sequence_cases / distance_cases the vLLM reference added to the
shared conformance_vectors.json (byte-identical file), extending Python<->Go
parity from the primitives to the sequence layer: VerifySequence reproduces the
per-position-seed aggregation and MAEDistance reproduces the Stage-2 distance
bit-for-bit (gonka-ai#1199 follow-up).

- MAEDistance: sum over sorted token IDs so the float64 accumulation order matches
  the Python reference exactly; the conformance test asserts exact equality
  (e.g. uniform_shift = 0.5000000000000001).
- sequence_conformance_test.go: TestSequenceConformance / TestDistanceConformance
  drive VerifySequence / MAEDistance over every committed case.

Refs: gonka-ai#1199
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.

1 participant