fix(ci): commit-independent toolchain hash; generate-once VK cache - #25111
Merged
Merged
Conversation
The labs-aztec-toolchain hash hashed the bytes of the built binaries, but inject_version stamps the current commit into bb/bb-avm on non-release builds, so byte-identical trees produced a different toolchain hash on every commit. Every noir-contracts cache key mixes that hash in, so every commit recompiled all ~84 contracts and mass-regenerated their VKs in parallel into a fresh shared vk_cache. The toolchain hash now composes the providers' source content hashes (the same inputs that decide whether the binaries rebuild) plus the presence of the optional binaries. That mass regeneration exposed a latent race: get_or_generate_cached_app_vk checked existence then wrote the final path directly, with no locking — and distinct contracts can carry byte-identical functions (the simulated account contracts), so concurrent bb processes double-generated the same entry and could read a partially written VK. One corrupted account-contract VK baked into the yarn-project artifact and the aztec-up test image caches, making bridge_and_claim fail deterministically on every retry of PR #24306. VK cache entries now take a per-entry flock (generate-once: one process generates, the rest block then read) and are written via temp file + rename.
nchamo
self-requested a review
August 5, 2026 15:01
charlielye
added this pull request to the merge queue
Aug 5, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Aug 5, 2026
fcarreiro
reviewed
Aug 5, 2026
| done | ||
| hash_str $(git hash-object "${files[@]}") | ||
| hash_str \ | ||
| $("$ROOT"/barretenberg/cpp/bootstrap.sh hash) \ |
Contributor
There was a problem hiding this comment.
I will come back to this PR when I get back from OOO next week, but this part is wrong. It makes the labs repo depend on the foundation repo.
fcarreiro
added this pull request to the merge queue
Aug 5, 2026
fcarreiro
added a commit
that referenced
this pull request
Aug 7, 2026
## Summary - Merges `next` into `monorepo-split/labs`. Three conflicts, kept as separate commits (needs `ci-no-squash`): the merge itself, then the labs-side resolutions. - Keeps the `labs-aztec-toolchain` hash exactly as it is on this branch. #25111 changed it to compose `barretenberg/cpp/bootstrap.sh hash` and `noir/bootstrap.sh hash`, which makes the labs toolchain depend on foundation source trees that will not exist in the labs repo - see #25111 (comment). Identity there comes from the pinned binaries instead. The `hash` function is byte-identical to this branch's. - Drops `boxes` from the root bootstrap project list, deleted on `next` by #25118, alongside the `aztec-up` omission this branch already carries. - Bumps the pinned foundation release to `6.0.0-nightly.20260807`, matching #25131. The merge brings `next`'s migration to the published `@aztec/cdb`, which does not exist at the nightly this branch was pinned to, and `check_pin_drift` requires every pin to track `BB_VERSION`, so all six sites move together. The noir submodule and both pinned Noir crates (`protocol_types`, `bb_proof_verification`) are byte-identical across the bump. Overlaps #25131 deliberately: the merge independently brings most of it (the generated cdb sources, the `generate` script and its ignore/eslint entries are gone, and `cdb_ipc_server.ts` matches), and both branches now set the same pin value, so only the lockfiles conflict. #25131 additionally removes the now-inputless `ipc-codegen`/`cdb_schema.json` cache patterns in `yarn-project/bootstrap.sh`, which this PR leaves alone. ## Catch-up merge with the base branch `monorepo-split/labs` moved on while this was open - #25133 dropped `l1-contracts` and #25140 deleted `noir-projects/fnd` and `protocol/constants-codegen` - so the base is merged back in as a third commit. All four conflicts are lists that both sides shortened, so each resolution is the union of the two removals: - `Makefile`: `.PHONY` loses both `boxes` (deleted on `next` by #25118) and `l1-contracts`. The `barretenberg` aggregate keeps this branch's new `bb-cdb` and drops `bb-sol`. The extra `yarn-project` prerequisites become `bb-ts wsdb bb-avm-sim bb-cdb`. - `scripts/socket-fix.sh`: the `WORKSPACES` default and its doc comment become `"yarn-project docs playground"`. - `boxes/bootstrap.sh`, `boxes/package.json`: modify/delete. The deletion wins - the base had edited both, but the whole directory is gone on `next`. Neither the pin bump nor the lockfiles conflicted here: the base never touched `yarn-project/package.json` or either lockfile, so `6.0.0-nightly.20260807` carries over intact and `check_pin_drift` passes. `make -n yarn-project` and `make -n barretenberg` both resolve with `bb-cdb` present and no dangling `bb-sol`, `l1-contracts`, `constants-codegen`, or `noir-projects-fnd` references. A fourth commit drops two comments that the deletions left describing things that no longer exist: `ci3/dist_tag` credited a git-branch-push consumer to `l1-contracts` (nothing pushes branches off `dist_tag` anymore), and `yarn-project/bootstrap.sh` claimed `warm_solc_cache` is normally a no-op because `l1-contracts-solc` has already populated `~/.svm`. No behavior change.
fcarreiro
added a commit
that referenced
this pull request
Aug 10, 2026
…25151) Unifies `labs-aztec-toolchain/bootstrap.sh` so one file serves the three contexts that need it: the monorepo today, the standalone labs repo after the split, and the foundation repo after the split (which deletes the labs components, consumes them as a submodule, and runs labs e2e against its own locally built bb/noir — the pre-split flow). Today the next and monorepo-split/labs lines carry divergent copies of this file that conflict on every sync merge; this converges them so the only intended difference is one committed default. ## The mode protocol A single variable selects the provisioning mode: - **Foundation mode** (`FND_ROOT` non-empty): symlink the binaries built inside the checkout at `FND_ROOT` (`barretenberg/cpp`, the `noir` submodule), and derive the toolchain identity from that tree's source hashes. This is `build_monorepo` + the #25111 hash, with the root parameterized. - **Pinned mode** (`FND_ROOT` empty): download released binaries at the pinned `BB_VERSION`/`NOIR_VERSION` (the monorepo-split/labs flow: bbup/noirup, cached acvm source build, `.pin` provenance record, `check_pin_drift`), and derive the identity from this directory's committed content. The committed default on this line is `FND_ROOT=$(git rev-parse --show-toplevel)` — foundation mode, today's behavior — so a bare invocation keeps linking the local build. `AZTEC_TOOLCHAIN_FND_ROOT` overrides either way: export it empty to force pinned mode, or point it at a foundation checkout root (how the post-split foundation repo will drive its labs submodule). The labs line will carry the same file with an empty default (follow-up PR against monorepo-split/labs). ## Hash semantics - **Foundation mode**: byte-identical inputs to the current (post-#25111) hash — providers' source hashes plus observed optional binaries. Verified the value is unchanged on this tree (`ce12057b4ea229fb` before and after), so **no cache invalidation on this line**. - **Pinned mode**: `cache_content_hash "^labs-aztec-toolchain/"` plus the declaratively expected optionals (bb-avm iff released for this platform, acvm iff cargo exists). This replaces the labs line's current byte-hashing of `bin/`, completing the identity/verification separation #25111 started: the hash is a pure function of the committed tree (computable on a fresh checkout — today `hash` fails until `build` has run, which downstream hash compositions trip over), a corrupted `bin/` can no longer mint a fresh valid-looking cache key (byte verification stays in the `.pin` record at provision time), a pin bump still moves the hash before any binary is refreshed, and a dirty toolchain dir propagates `disabled-cache` instead of laundering it into a stable-looking key. ## Other behavior notes - Foundation-mode `build` now starts from an empty `bin/` and writes the `.pin` record (previously labs-line-only), so switching between modes in one checkout fully re-provisions, and `noir_version` can report the exact submodule tag (e.g. `nightly-2026-07-31`) instead of nargo's base cargo version; it falls back to the binary when no record exists. - Pinned mode on this line is exercisable via the override but `check_pin_drift` will legitimately fail while next's Nargo.toml/docs/yarn-project pins differ from the labs line's — expected until the split content converges. - Call-site interface (`hash`, `build`, `noir_version`, `bin/*` paths) is unchanged. ## Validation - `bash -n` clean; fnd-mode `hash` byte-identical to the old script on the same tree (`ce12057b4ea229fb`). - Pinned-mode `hash` identical with `bin/` present and absent (`bd71460d78038ae5`), returns `disabled-cache` on a dirty toolchain dir locally, and fails (exit 1) under `CI=1` instead of hashing an empty string. - Foundation `build` run against this checkout's local bb/noir builds: symlinks all five binaries, writes the pin record, hash stable across re-provisioning. - Invalid `AZTEC_TOOLCHAIN_FND_ROOT` fails with a clear message. ## Follow-up - Apply the same file to `monorepo-split/labs` with an empty committed `FND_ROOT` default (its toolchain hash value changes once: contract/yarn-project caches on that line rebuild one time). - After the split, the foundation repo's labs-e2e driver exports `AZTEC_TOOLCHAIN_FND_ROOT=$(git rev-parse --show-toplevel)` before entering the submodule; the committed default divergence disappears.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The incident
PR #24306 failed
aztec-up/scripts/run_test.sh bridge_and_claimdeterministically — the identicalBBApiException: Failed to verify the generated proof!~2.8s into the only ClientIVC prove of the test, across multiple "retries", while the same test was 60/60 green on next. The retries could never help: the failure was frozen inside cached build artifacts (aztec-up-test-image-…andyarn-project-….tar.gz), which are content-addressed by tree hash and therefore reused by every run of the same tree.Tracing how a wrong artifact could sit under a correct content key led to two independent defects that compose:
Defect 1: the toolchain hash changes on every commit (regression, #25078 / #25057 / #25047)
labs-aztec-toolchain/bootstrap.sh hashcomputed the toolchain identity ashash_str $(git hash-object <built binaries>). Butinject_version(barretenberg/cpp/bootstrap.sh) stampsgit rev-parse --short HEADinto bb/bb-avm bytes on any non-release build — so the binary bytes encode the commit, and byte-identical source trees produce a different toolchain hash on every commit.Observed directly: two CI runs of byte-identical trees (commits
5041b6bb/fafdd723) had toolchain hashesf2e3f37015b9cdfavsd714a9fc28a62cff, zero overlap between their 84contract-*.tar.gzcache names, and bb logged~/.bb/5041b6b/vk_cachevs~/.bb/fafdd72/vk_cache— the injected commit even keys the runtime VK cache directory.Since every noir-contracts cache key mixes in
AZTEC_TOOLCHAIN_HASH, the practical effect is: every commit of every PR recompiles all ~84 contracts and mass-regenerates their VKs in parallel into a brand-new, empty, shared VK cache. Before this scheme (pre-Aug-3), contract keys were built from source rebuild patterns over bb/noir/transpiler — identical trees reused artifacts.It is also conceptually circular: the binaries themselves rebuild if and only if their source hash changes (their artifact names are the source hash — both runs above shared
barretenberg-clang20-11cccdad4e78d0a2.zst), so deriving downstream rebuild keys from the binary bytes answers "did the toolchain change?" with information that is only available after deciding exactly that.Fix: the toolchain hash now composes the providers' source content hashes —
barretenberg/cpp/bootstrap.sh hash+noir/bootstrap.sh hash— plus the presence of the optional binaries (bb-avm/acvm, whose availability is part of the toolchain's identity but whose content is already covered by the provider hashes). Rebuild decisions at every level of the graph now key on the same inputs. A future labs-repo provisioning mode (build_labs, currently stubbed) should use the released toolchain's version string as its fixed identity.Defect 2: the shared VK cache is not generate-once (latent since ~June)
get_or_generate_cached_app_vk(barretenberg/cpp/src/barretenberg/api/aztec_process.cpp) didexists() → read_file, and on miss wrote the VK directly to the final path — no locking, no temp-file+rename. The cache directory is shared by all concurrent bb processes (the noir-projects builds run one per contract under GNU parallel), and distinct contracts can carry byte-identical functions: the simulated account contracts stub out signature verification, sosimulated_schnorr_accountandsimulated_ecdsa_accountproduce the same bytecode hash → the same cache path, wanted simultaneously by two unrelated processes.The failing run's logs show the race firing three times (same VK hash "Generating…" concurrently from two contracts), plus other processes reading entries moments after a different process generated them. Two simultaneous writers on one path — or a reader consuming a half-written file — silently embeds a truncated VK into a contract artifact. Account-contract VKs are exactly what ClientIVC private-kernel recursion verifies, matching the observed deterministic proof-verification failure; the corrupted artifact then flowed into the yarn-project tarball and the aztec-up test image, both content-keyed, freezing the failure for every subsequent run of that tree.
Fix: each cache entry now takes an exclusive
flockon a<entry>.locksidecar for the duration of check-generate-write — one process generates while the rest block, then read the completed entry (generate-once, which is also the economical behavior: previously colliding processes each did the full ChonkComputeVk). Writes go via temp file +rename, so even a reader without the advisory lock (the Windows fallback, whereflockis unavailable) can never observe a partial entry; a lost rename race there is benign and resolved in favor of the completed equivalent entry. The file is already excluded from WASM builds.Why the fixes fix it
Validation
bash -non the bootstrap change.aztec-up-test-image-3747b48e27303118.zst) were separately rebuilt from a from-source local build and force-uploaded; this PR prevents recurrence.