Reproducibility
What it means for a proof to be reproducible in this repository, and every pin that makes it so. Reproducibility is not just determinism of one artifact — it is a chain: same sources + same toolchains + same inputs ⇒ same bytecode, same manifest, same fixtures.
The pins
| Pin | Where | Enforced by |
|---|---|---|
| Rust toolchain 1.98 | rust-toolchain.toml | rustup |
nargo 1.0.0-beta.26 | TESTED_NARGO_VERSION in crates/noir/src/lib.rs, scripts/setup-noir.sh, CI | version gate in crucible-noir, CI install |
bb 6.0.0-nightly.20260903 | TESTED_BB_VERSION in crates/ultrahonk/src/lib.rs, CI | version gate in crucible-ultrahonk, CI install |
| nargo × bb pairing + circuit versions | BACKEND_COMPAT in crates/ultrahonk/src/backend.rs | provider supports/check_supported |
| Canonical value encoding | FieldValue/SecretValue canonical lowercase hex (no 0x, no leading zeros) | constructors reject non-canonical forms |
| Envelope format v1 + deterministic JSON | crates/proof-types | serialization is field-ordered; parsing rejects future versions |
Deterministic artifacts
Compiled bytecode is pinned with byte-for-byte reproducibility:
- Artifacts are generated by a deterministic path: per-package
nargo execute(seescripts/generate-test-vectors.sh) — a whole-workspacenargo compileproduces different debug-metadata ordering, so the generate path matters as much as the compiler version. crucible-prover artifacts generatewrites bytecode + a canonicalmanifest.json(sorted file list, ordered JSON); identical bytecode reproduces byte-identical manifests.- CI runs the fresh-compile determinism gate: generate to a fresh
root and
diff -ragainst the committedartifacts/circuits/— a circuit change that forgets to re-pin, or a toolchain drift that changes bytecode, fails CI. - The strict loader rejects any committed artifact that no longer matches its manifest (docs/artifacts.md).
Deterministic fixtures
- Catalog vectors: expected public outputs are captured from real
nargo executeruns, and the circuit tier re-checks them on every test run (docs/test-vectors.md). - Mock proofs are deterministic (same vector + same mock key ⇒ same
bytes), so
proofs/fixtures/regenerate as a no-op diff unless the envelope format or a vector changed;scripts/generate-proof-fixtures.shreproduces them exactly.
What is not claimed reproducible
Real UltraHonk proof bytes are produced by bb and are treated as opaque
backend output; reproducibility guarantees apply to the context around
them (artifact checksums, verification-key ids, public outputs, state
binding), not to byte-identity of proofs across backend versions — which is
precisely why backend version identity is pinned and carried on every
envelope.
Regen workflow
| Material | Command |
|---|---|
| Circuit vectors | bash scripts/generate-test-vectors.sh |
| Pinned artifacts | cargo run -q -p crucible-cli -- artifacts generate |
| Proof fixtures | bash scripts/generate-proof-fixtures.sh |
Each produces a no-op git diff when nothing changed — drift is loud by
design.