diff --git a/.claude/README.md b/.claude/README.md index ac93612b1963..c68bf7a373c4 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -7,7 +7,8 @@ Project-level Claude Code configuration for aztec-packages. This file documents 1. Universal content lives at repository root: `CLAUDE.md`, `.claude/agents/`, `.claude/scripts/`, and the root `settings.json` hooks. 2. Subdirectory `.claude/` directories hold only component-specific skills, permission allowlists, and settings. Content is not merged upward. 3. A subdirectory that has its own `.claude/` must symlink `agents/` to the repository root's `.claude/agents/`. Claude Code's ancestor walk stops at the nearest `.claude/` and does not merge ancestors, so subdirectories otherwise shadow the root agents. Subdirectories without their own `.claude/` inherit the root automatically. `skills/` is intentionally *not* symlinked — skills are scoped to their subdir (root skills are repo-wide workflows; `yarn-project/.claude/skills/` are TS-specific; etc.). -4. Prefer XML-tagged sections inside `CLAUDE.md` for prose guidance. A `.claude/rules/` directory is only warranted when a rule needs YAML frontmatter (e.g. path-scoped `paths:` metadata) that `CLAUDE.md` cannot express. `.claude/rules/` without frontmatter does not auto-load when Claude Code is started from a subdirectory, so plain rules files are strictly worse than inlining into `CLAUDE.md`. +4. Tracked `.codex` symlinks point at the nearest `.claude/` directory. Keep `.claude/` canonical and do not duplicate agent or skill indexes in `AGENTS.md`. +5. Prefer XML-tagged sections inside `CLAUDE.md` for prose guidance. A `.claude/rules/` directory is only warranted when a rule needs YAML frontmatter (e.g. path-scoped `paths:` metadata) that `CLAUDE.md` cannot express. `.claude/rules/` without frontmatter does not auto-load when Claude Code is started from a subdirectory, so plain rules files are strictly worse than inlining into `CLAUDE.md`. ## Tests diff --git a/.claude/skills/chonk-inputs/SKILL.md b/.claude/skills/chonk-inputs/SKILL.md new file mode 100644 index 000000000000..b0950560ac25 --- /dev/null +++ b/.claude/skills/chonk-inputs/SKILL.md @@ -0,0 +1,62 @@ +--- +name: chonk-inputs +description: Manage pinned Chonk IVC inputs and the Chonk/rollup UltraHonk proving checks. Use when updating, testing, benchmarking, or reviewing the CI flow for Chonk input refreshes. +argument-hint: e.g. "download", "check", "refresh-pr", "benchmark", "ultrahonk" +--- + +# Chonk Inputs + +Pinned Chonk IVC inputs live in an S3 tarball keyed by `barretenberg/cpp/scripts/chonk-inputs.hash`. The owner scripts are: + +- `barretenberg/cpp/scripts/pinned_chonk_inputs.sh` for shared shell helpers. +- `barretenberg/cpp/scripts/chonk_inputs.sh` for download, check, and update. +- `barretenberg/cpp/scripts/ci_update_chonk_inputs.sh` for the PR push-back step. + +Use the scripts instead of open-coding URLs, hashes, temp paths, or bucket listings. + +## Common Commands + +Download or repair the local fixture directory: + +```bash +barretenberg/cpp/scripts/chonk_inputs.sh download +``` + +Check pinned VK compatibility without updating: + +```bash +barretenberg/cpp/scripts/chonk_inputs.sh check +``` + +Refresh the pin locally: + +```bash +barretenberg/cpp/scripts/chonk_inputs.sh update +``` + +In PR CI, refresh via the `ci-refresh-chonk` label or a `--ci-refresh-chonk` head-commit marker. The follow-up refresh commit includes `--ci-skip`. +Proving is handled by the pinned-input tests; the CI push-back step runs one small pinned flow before committing a refreshed hash. + +## Benchmark and Proving Checks + +Barretenberg owns the Chonk and rollup UltraHonk benchmark commands. Use `barretenberg/cpp/bootstrap.sh bench_cmds` to inspect the CI command list, or `bench_ivc` for a focused Chonk run. + +The benchmark scripts restore or regenerate their inputs when missing: + +```bash +barretenberg/cpp/scripts/chonk_inputs.sh download +barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh native + +barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh parity_base \ + ../../yarn-project/end-to-end/ultrahonk-bench-inputs 8 +``` + +Keep Chonk benchmark command enumeration in barretenberg; `yarn-project/end-to-end/bootstrap.sh build_bench_capture` is only for live input capture during pin refresh. + +## Review Checklist + +- Do not use `aws s3 ls` or depend on bucket listing. Verify exact object URLs. +- Use `.cache/` for scratch state and clean job-specific subdirectories. +- Keep refresh commits scoped to `barretenberg/cpp/scripts/chonk-inputs.hash`. +- Run `bash -n` on edited shell scripts and `yq e . .github/workflows/ci3.yml` after workflow edits. +- For update-step changes, run `.cache/chonk-ux-harness/run.sh` when available. diff --git a/.claude/tests/agents_symlink_test b/.claude/tests/agents_symlink_test index 3c0ef5f26540..48a13ff77f16 100755 --- a/.claude/tests/agents_symlink_test +++ b/.claude/tests/agents_symlink_test @@ -28,6 +28,15 @@ pass() { echo "${GREEN}✓${NC} $1"; ((CHECKED++)) || true; } ROOT_AGENTS="$ROOT/.claude/agents" [[ -d "$ROOT_AGENTS" ]] || { fail "root .claude/agents missing at $ROOT_AGENTS"; exit 1; } +ROOT_AGENTS_MD="$ROOT/AGENTS.md" +if [[ ! -L "$ROOT_AGENTS_MD" ]]; then + fail "AGENTS.md is not a symlink to CLAUDE.md" +elif [[ "$(readlink "$ROOT_AGENTS_MD")" != "CLAUDE.md" ]]; then + fail "AGENTS.md points at $(readlink "$ROOT_AGENTS_MD"), expected CLAUDE.md" +else + pass "AGENTS.md -> CLAUDE.md" +fi + # Every subdir .claude/ (excluding the root one) must expose an agents entry # that resolves to the same inode as the root agents/ directory. while IFS= read -r dir; do @@ -62,8 +71,35 @@ done < <(find "$ROOT" -type d -name .claude -not -path "$ROOT/noir/*" -not -path echo if (( FAIL == 0 )); then echo "${GREEN}All $CHECKED subdir .claude/ directories expose agents/ correctly.${NC}" - exit 0 else echo "${RED}$FAIL subdir .claude/ directories are missing or have broken agents symlinks.${NC}" exit 1 fi + +while IFS= read -r dir; do + case "$dir" in + "$ROOT"/node_modules/*|*"/node_modules/"*) continue;; + "$ROOT"/noir/noir-repo/*) continue;; + esac + codex="$(dirname "$dir")/.codex" + if [[ ! -L "$codex" ]]; then + fail "${codex#$ROOT/} is missing or is not a symlink to .claude" + continue + fi + resolved=$(cd "$(dirname "$codex")" && cd "$(readlink "$codex")" 2>/dev/null && pwd -P) || resolved="" + claude_resolved=$(cd "$dir" && pwd -P) + if [[ "$resolved" != "$claude_resolved" ]]; then + fail "${codex#$ROOT/} resolves to $resolved, expected $claude_resolved" + continue + fi + pass "${codex#$ROOT/}" +done < <(find "$ROOT" -type d -name .claude -not -path "$ROOT/noir/*" -not -path "$ROOT/**/node_modules/*") + +echo +if (( FAIL == 0 )); then + echo "${GREEN}All checked .codex symlinks point at their sibling .claude directories.${NC}" + exit 0 +else + echo "${RED}$FAIL .claude/.codex layout checks failed.${NC}" + exit 1 +fi diff --git a/.codex b/.codex new file mode 120000 index 000000000000..c8161850a43d --- /dev/null +++ b/.codex @@ -0,0 +1 @@ +.claude \ No newline at end of file diff --git a/.github/ci3_labels_to_env.sh b/.github/ci3_labels_to_env.sh index 476a4f201f05..dfc11d838f84 100755 --- a/.github/ci3_labels_to_env.sh +++ b/.github/ci3_labels_to_env.sh @@ -8,13 +8,33 @@ function has_label { local label="$1" for l in "${LABELS[@]}"; do if [[ "$l" == "$label" ]]; then - echo "Label '$label' found" >&2 return 0 fi done return 1 } +function join_by { + local delimiter="$1" + shift + local result="" + local value + for value in "$@"; do + if [[ -n "$result" ]]; then + result+="$delimiter" + fi + result+="$value" + done + echo "$result" +} + +function head_commit_has_marker { + local marker="$1" + local message + message="$(git log -1 --pretty=%B 2>/dev/null || true)" + grep -Eq "(^|[[:space:]])${marker}([[:space:]]|$)" <<< "$message" +} + function main { LABELS=("$@") echo "Labels: ${LABELS[*]}" @@ -42,9 +62,50 @@ function main { echo "SKIP_COMPAT_E2E=1" >> $GITHUB_ENV fi + local chonk_input_update=0 + local chonk_input_update_requested=0 + if [ "${GITHUB_EVENT_NAME:-}" = "pull_request" ] && { has_label "ci-refresh-chonk" || head_commit_has_marker "--ci-refresh-chonk"; }; then + chonk_input_update_requested=1 + fi + local ci_skip_requested=0 + if has_label "ci-skip" || head_commit_has_marker "--ci-skip"; then + ci_skip_requested=1 + fi + + local explicit_ci_mode_labels=() + local mode_label + for mode_label in ci-merge-queue ci-release-pr ci-full ci-full-no-test-cache ci-docs ci-barretenberg-full ci-barretenberg; do + if has_label "$mode_label"; then + explicit_ci_mode_labels+=("$mode_label") + fi + done + + if [ "$ci_skip_requested" -eq 0 ] && [ "${#explicit_ci_mode_labels[@]}" -gt 1 ]; then + echo "ERROR: Conflicting CI mode labels: $(join_by ', ' "${explicit_ci_mode_labels[@]}"). Remove all but one mode label, or use ci-skip/--ci-skip to skip CI intentionally." >&2 + exit 1 + fi + + if [ "$ci_skip_requested" -eq 0 ] && [ "$chonk_input_update_requested" -eq 1 ] && [ "${#explicit_ci_mode_labels[@]}" -gt 0 ]; then + echo "ERROR: ci-refresh-chonk cannot be combined with explicit CI mode labels: $(join_by ', ' "${explicit_ci_mode_labels[@]}"). Remove the mode label, or use ci-skip/--ci-skip to skip without refreshing inputs." >&2 + exit 1 + fi + + # Chonk input updates are side-effecting internal PR-only work. The main CI + # run behaves like ci-skip until post-actions regenerate and push the diff. + if [ "$chonk_input_update_requested" -eq 1 ] && [ "$ci_skip_requested" -eq 0 ]; then + chonk_input_update=1 + fi + echo "CHONK_INPUT_UPDATE_REQUESTED=$chonk_input_update" >> $GITHUB_ENV + # Determine CI mode based on event, labels, and target branch local ci_mode - if [ "${GITHUB_EVENT_NAME:-}" == "merge_group" ] || has_label "ci-merge-queue"; then + if [ "$ci_skip_requested" -eq 1 ]; then + echo "WARNING: Skipping CI because a ci-skip label or --ci-skip commit marker was present. Skip takes precedence over other CI signals." >&2 + if [ "$chonk_input_update_requested" -eq 1 ]; then + echo "WARNING: Chonk input refresh was requested but ignored because CI skip was also requested." >&2 + fi + ci_mode="skip" + elif [ "${GITHUB_EVENT_NAME:-}" == "merge_group" ] || has_label "ci-merge-queue"; then ci_mode="merge-queue" # Check if this is a merge-train/spartan PR entering the merge queue. # If so, use the heavier merge-queue-heavy mode (10 grind runs). @@ -62,8 +123,8 @@ function main { fi fi fi - elif has_label "ci-skip"; then - echo "WARNING: Skipping CI due to the ci-skipok label! Make sure this is intended!" >&2 + elif [ "$chonk_input_update" -eq 1 ]; then + echo "WARNING: Skipping main CI because Chonk input refresh was requested; the update step will run after this step succeeds." >&2 ci_mode="skip" elif has_label "ci-release-pr"; then ci_mode="release-pr" @@ -84,6 +145,7 @@ function main { else ci_mode="fast" fi + echo "CI_MODE=$ci_mode" >> $GITHUB_ENV echo "CI mode: $ci_mode" diff --git a/.github/ci3_success.sh b/.github/ci3_success.sh index 2d26bfa7ee13..7684cdd71410 100755 --- a/.github/ci3_success.sh +++ b/.github/ci3_success.sh @@ -56,10 +56,22 @@ function handle_benchmarks { fi } +function handle_chonk_input_update { + local github_repository="$1" + echo_header "Chonk Input Update" + export GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-$github_repository}" + ./.github/ci3.sh chonk-input-update +} + function main { echo_header "CI3 Post-Actions" # Get repository from git remote local github_repository=$(git remote get-url origin | sed -E 's|.*github\.com[/:]([^/]+/[^/]+)(\.git)?$|\1|') + if [ "${CHONK_INPUT_UPDATE_REQUESTED:-0}" -eq 1 ]; then + handle_chonk_input_update "${github_repository}" + echo_header "Post-Actions Complete" + return + fi save_cache "${github_repository}" handle_squash_merge "${github_repository}" handle_benchmarks diff --git a/.github/workflows/ci3.yml b/.github/workflows/ci3.yml index ea3c1c1d901e..01613cb728f4 100644 --- a/.github/workflows/ci3.yml +++ b/.github/workflows/ci3.yml @@ -45,7 +45,8 @@ jobs: environment: ${{ startsWith(github.ref, 'refs/tags/v') && 'master' || '' }} permissions: id-token: write # required for OIDC assume-role with AWS - contents: read + contents: read # checkout/fetch with the default github.token + pull-requests: read # merge_group mode looks up the source PR steps: - name: Remove wakeup label if: contains(github.event.pull_request.labels.*.name, 'ci-wakeup-pr-after-merge') diff --git a/.github/workflows/pull-request-title.yml b/.github/workflows/pull-request-title.yml index 161a2a396a4a..52059d0a27e4 100644 --- a/.github/workflows/pull-request-title.yml +++ b/.github/workflows/pull-request-title.yml @@ -11,6 +11,7 @@ on: - synchronize permissions: + contents: read pull-requests: read jobs: diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 7c665258297b..000000000000 --- a/AGENTS.md +++ /dev/null @@ -1,54 +0,0 @@ -This repo uses CLAUDE.md files. When working in a module, look for the CLAUDE.md. *Immediately read the root ./CLAUDE.md here*. - -## Specialist subagents - -Domain-expert references under `.claude/agents/`: - -- `analyze-logs` — `.claude/agents/analyze-logs.md` — Deep-read test logs and extract relevant info; runs in a separate context to avoid polluting the main conversation. -- `aztec-wallet` — `.claude/agents/aztec-wallet.md` — Execute cli-wallet commands against live Aztec networks (account setup, contract deployment, function calls, fee juice bridging). -- `identify-ci-failures` — `.claude/agents/identify-ci-failures.md` — Identify CI failures from a PR number, CI URL, or log hash; returns a structured list of failures with downloaded log paths. -- `network-logs` — `.claude/agents/network-logs.md` — Query GCP Cloud Logging for live Aztec network deployments (block production, proving status, errors). - -## Skills - -Task-specific workflows. When the user asks for one of these, read the matching `SKILL.md` (or `*.md` under `commands/`) and follow its instructions. - -Repo-wide (`.claude/skills//SKILL.md`): - -- `acir-formal-proofs` — Build/run ACIR formal proof tests with SMT verification; updates the README results table. -- `adding-benchmarks` — Add new benchmarks to the CI pipeline (JSON files, `bootstrap.sh` wiring, `ci3.yml` upload). -- `aztec-wallet` — Run `cli-wallet` against a live Aztec network: deploy contracts, send transactions, query state, bridge funds, manage accounts. -- `backport` — Backport a merged PR to a release branch (e.g. `v4`, `v4-devnet-2`), resolving conflicts if needed. -- `ci-logs` — Analyze CI logs from `ci.aztec-labs.com`. Use instead of `WebFetch` for CI URLs. -- `cycle` — Show Linear issues for the current cycle, grouped by status. -- `fix` — Analyze Linear issues, validate them against the codebase, and open draft fix PRs. -- `merge-train-infra` — Reference for merge-train automation internals (workflows, scripts, CI integration). -- `merge-trains` — Guide for working with merge-train branches: PR creation, base branch choice, labels, failure handling. -- `network-logs` — Query and analyze logs from live Aztec network deployments on GCP Cloud Logging. -- `noir-sync-update` — Follow-on updates after bumping the `noir/noir-repo` submodule (`Cargo.lock`, `yarn.lock`, etc.). -- `release-docs` — Build and update the developer documentation site for a new release. -- `release-network-docs` — Update network/operator documentation for a mainnet/testnet release without touching developer docs. -- `update-doc-references` — Update documentation when source files it references change in a PR. -- `updating-changelog` — Update changelog/migration notes for contract developers and node operators based on branch changes. - -Barretenberg-scoped (`barretenberg/.claude/skills//SKILL.md`): - -- `benchmark-chonk` — Run realistic Chonk (client IVC) benchmarks; native + WASM, per-circuit breakdowns, `BB_BENCH` instrumentation. -- `profile-chonk` — Run Chonk prover on the remote EC2 and collect Perfetto-compatible traces with a one-click UI link. -- `remote-bench` — Run benchmarks on the dedicated remote EC2 benchmarking machine (noise-free, single-run). -- `stdlib-point-at-infinity` — Guidelines for handling point-at-infinity in stdlib circuit types (serialization, public inputs, `cycle_group`/`biggroup`). -- `sumcheck` — Comprehensive reference for the Sumcheck protocol implementation in barretenberg (prover/verifier, relations, ZK sumcheck, ECCVM committed sumcheck). - -Yarn-project-scoped (`yarn-project/.claude/skills//SKILL.md`): - -- `debug-e2e` — Interactive ping-pong debugging for failed e2e tests; delegates log reading to subagents. -- `fix-pr` — Autonomous workflow that fixes a failing PR by analyzing CI logs, rebasing, fixing, and pushing. -- `read-gist` — Fetch and display a GitHub gist. -- `readme-writer` — Guidelines for writing module READMEs that explain how a module works. -- `rebase-pr` — Rebase a PR on its base branch, fix conflicts, and verify the build. -- `unit-test-implementation` — Best practices for unit tests in the TS monorepo (mocking, organization, helpers, assertions). -- `worktree-spawn` — Spawn an independent Claude instance in a git worktree to work on a task in parallel. - -Docs slash-commands (`docs/.claude/commands/.md`): - -- `review-docs` — Review documentation for correctness, accuracy, and adherence to conventions. diff --git a/AGENTS.md b/AGENTS.md new file mode 120000 index 000000000000..681311eb9cf4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 0d41fa63d64e..0da2bd2f72db 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -83,6 +83,8 @@ Before writing a new helper, utility, or component, search for an existing one w Preserve existing `// TODO`, `// TODO(name)`, and `// NOTE:` comments unless the current task is to resolve them. A "tidy up" refactor that deletes another author's deferred-work markers destroys context that is not recoverable from git history. + +During cleanup or review passes, do not delete useful explanatory comments merely to reduce diff size. Remove or rewrite a comment only when it is incorrect, obsolete, noise, or directly resolved by the current task. diff --git a/Makefile b/Makefile index 79de2fa421a5..f616809a0c05 100644 --- a/Makefile +++ b/Makefile @@ -126,6 +126,9 @@ bb-cpp-native-objects: bb-cpp-yarn bb-cpp-native: bb-cpp-native-objects avm-transpiler-native bb-cpp-yarn bb-cpp-format-check $(call build,$@,barretenberg/cpp,build_native) +bb-cpp-chonk-inputs: + $(call build,$@,barretenberg/cpp,download_chonk_inputs) + # BB C++ WASM - Single-threaded WebAssembly build bb-cpp-wasm: $(call build,$@,barretenberg/cpp,build_preset wasm) @@ -238,7 +241,7 @@ bb-sol: bb-cpp-native bb-crs l1-contracts-solc # Barretenberg Tests #============================================================================== -bb-cpp-native-tests: bb-cpp-native +bb-cpp-native-tests: bb-cpp-native bb-cpp-chonk-inputs $(call test,$@,barretenberg/cpp,native) bb-cpp-wasm-threads-tests: bb-cpp-wasm-threads diff --git a/barretenberg/.claude/skills/benchmark-chonk/SKILL.md b/barretenberg/.claude/skills/benchmark-chonk/SKILL.md index 07afa48c2bbe..f2be977bbb14 100644 --- a/barretenberg/.claude/skills/benchmark-chonk/SKILL.md +++ b/barretenberg/.claude/skills/benchmark-chonk/SKILL.md @@ -8,15 +8,14 @@ argument-hint: e.g. "run", "compare", "wasm", "instrument ", "per Run realistic Chonk IVC benchmarks using **pinned protocol inputs** (real transaction flows captured from end-to-end tests). -**Chonk has no synthetic micro-benchmark.** Past attempts (`chonk_bench`) used trivially small mock circuits and produced misleading numbers — the target was deleted to prevent regression of that mistake. Always benchmark Chonk via `bb prove --scheme chonk` against pinned `ivc-inputs.msgpack` for real transaction flows. If a Chonk proving question seems to call for a micro-benchmark, the answer is still `bb prove` on a real flow. +**Chonk has no synthetic micro-benchmark.** Always benchmark Chonk via `bb prove --scheme chonk` against pinned `ivc-inputs.msgpack` for real transaction flows. If a Chonk proving question seems to call for a micro-benchmark, the answer is still `bb prove` on a real flow. ## Step 1: Get pinned IVC inputs -The real benchmark inputs are pinned to an S3 artifact keyed by a short hash. Download them: +The real benchmark inputs are pinned to an S3 artifact keyed by a 16-character hash prefix. Download them: ```bash -cd barretenberg/cpp/scripts -./test_chonk_standalone_vks_havent_changed.sh --download_pinned_inputs +barretenberg/cpp/scripts/chonk_inputs.sh download ``` This populates `yarn-project/end-to-end/example-app-ivc-inputs-out//ivc-inputs.msgpack`. @@ -26,27 +25,27 @@ Available flows (typical): - `schnorr+deploy_tokenContract_with_registration+sponsored_fpc` - `ecdsar1+amm_add_liquidity_1_recursions+sponsored_fpc` - `ecdsar1+transfer_1_recursions+private_fpc` -- and more — run `ls yarn-project/end-to-end/example-app-ivc-inputs-out/` after downloading +- and more under `yarn-project/end-to-end/example-app-ivc-inputs-out/` after downloading -The pinned hash is maintained in `barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh` (variable `pinned_short_hash`). The S3 URL is: +The pinned hash prefix is maintained in `barretenberg/cpp/scripts/chonk-inputs.hash`. The S3 URL is: ``` https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-.tar.gz ``` To update the pinned inputs (after protocol changes that affect VKs): ```bash -./test_chonk_standalone_vks_havent_changed.sh --update_inputs +barretenberg/cpp/scripts/chonk_inputs.sh update ``` -## Step 2: Build bb in release mode +## Step 2: Build bb ```bash cd barretenberg/cpp -cmake --preset clang20-no-avm # AVM not needed for Chonk -cmake --build --preset clang20-no-avm --target bb +cmake --preset clang20 +cmake --build --preset clang20 --target bb ``` -Build dir: `build-no-avm` (or `build` if using the `clang20` preset). +Build dir: `build`. To use a different native preset, build with that preset and set `NATIVE_PRESET` when running the benchmark script. ## Step 3: Run the benchmark @@ -54,6 +53,24 @@ Build dir: `build-no-avm` (or `build` if using the `clang20` preset). ### Native +For the standard local path, download the pinned inputs and use the same benchmark script CI uses: + +```bash +barretenberg/cpp/scripts/chonk_inputs.sh download +HARDWARE_CONCURRENCY=8 barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh native +``` + +The benchmark script defaults to `ecdsar1+transfer_0_recursions+sponsored_fpc`. If a flow fails, the pinned inputs remain in `yarn-project/end-to-end/example-app-ivc-inputs-out//ivc-inputs.msgpack`; rerun the same case by passing the flow directory: + +```bash +HARDWARE_CONCURRENCY=8 barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh native \ + yarn-project/end-to-end/example-app-ivc-inputs-out/ +``` + +The CI benchmark also cross-checks the proof against the generated protocol VK artifacts under `noir-projects/noir-protocol-circuits/target`. That is a post-prove check, not part of the pinned input download. + +For manual runs: + ```bash cd barretenberg/cpp @@ -61,7 +78,7 @@ FLOW="schnorr+deploy_tokenContract_with_registration+sponsored_fpc" OUTPUT_DIR="/tmp/chonk-bench-out" mkdir -p $OUTPUT_DIR -HARDWARE_CONCURRENCY=8 ./build-no-avm/bin/bb prove \ +HARDWARE_CONCURRENCY=8 ./build/bin/bb prove \ -o $OUTPUT_DIR \ --ivc_inputs_path ../../yarn-project/end-to-end/example-app-ivc-inputs-out/$FLOW/ivc-inputs.msgpack \ --scheme chonk \ @@ -72,6 +89,15 @@ HARDWARE_CONCURRENCY=8 ./build-no-avm/bin/bb prove \ ### WASM (via wasmtime) +Use the CI benchmark script with the WASM runtime: + +```bash +barretenberg/cpp/scripts/chonk_inputs.sh download +HARDWARE_CONCURRENCY=8 barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh wasm +``` + +For manual runs: + Build the WASM binary with threads enabled: ```bash @@ -283,7 +309,7 @@ The generic Google-Benchmark A/B scripts still exist for non-Chonk targets: | Script | Purpose | |--------|---------| -| `scripts/test_chonk_standalone_vks_havent_changed.sh` | Download/update/verify pinned inputs | +| `scripts/chonk_inputs.sh` | Download, check, and update pinned inputs | | `scripts/ci_benchmark_ivc_flows.sh` | CI: proves a flow, extracts components, uploads to dashboard | | `scripts/benchmark_example_ivc_flow_remote.sh` | Proves a pinned flow on the remote machine (uses `/remote-bench`) | | `scripts/wasmtime.sh` | wasmtime wrapper with standard flags | @@ -305,7 +331,7 @@ The generic Google-Benchmark A/B scripts still exist for non-Chonk targets: ## Presenting results -When sharing benchmark results, create an **HTML gist** with an interactive visualization. Include: +When sharing benchmark results, create an **HTML report** with an interactive visualization. Include: - **Native vs WASM tabs** with per-circuit comparison table - **Stacked bar charts** showing time distribution across circuits @@ -313,4 +339,4 @@ When sharing benchmark results, create an **HTML gist** with an interactive visu - **Summary cards** with total time, slowdown ratio, and heaviest circuit - **Color-coded circuit types**: kernel (blue), app (red), infra (gray) -Use `create_gist` / `update_gist` with a `.html` file. GitHub renders HTML gists — viewers can open the raw HTML to interact with tabs and tooltips. This is much more useful than plain markdown tables for benchmark data. +Prefer the benchmark dashboard for CI results. For local comparisons, keep the HTML report as a local artifact unless the reviewer asks for a shared upload. diff --git a/barretenberg/.claude/skills/profile-chonk/SKILL.md b/barretenberg/.claude/skills/profile-chonk/SKILL.md index 0294f7dbdfb3..b1e43961aae2 100644 --- a/barretenberg/.claude/skills/profile-chonk/SKILL.md +++ b/barretenberg/.claude/skills/profile-chonk/SKILL.md @@ -51,7 +51,8 @@ binary, not to mutate the shared bencher. ## Available flows ``` -ecdsar1+transfer_1_recursions+sponsored_fpc (default / most representative) +ecdsar1+transfer_0_recursions+sponsored_fpc (small smoke flow) +ecdsar1+transfer_1_recursions+sponsored_fpc ecdsar1+transfer_1_recursions+private_fpc ecdsar1+storage_proof_7_layers+sponsored_fpc ``` @@ -62,10 +63,10 @@ Always re-download so that stale inputs (e.g. from before a VK-breaking change l ```bash cd barretenberg/cpp -FLOW="ecdsar1+transfer_1_recursions+sponsored_fpc" +FLOW="ecdsar1+transfer_0_recursions+sponsored_fpc" INPUTS_ROOT="../../yarn-project/end-to-end/example-app-ivc-inputs-out" -./scripts/test_chonk_standalone_vks_havent_changed.sh --download_pinned_inputs +./scripts/chonk_inputs.sh download ``` ## Step 2: Build bb @@ -89,7 +90,7 @@ cmake --build --preset wasm-threads --target bb Set these variables first: ```bash -FLOW="ecdsar1+transfer_1_recursions+sponsored_fpc" +FLOW="ecdsar1+transfer_0_recursions+sponsored_fpc" HARDWARE_CONCURRENCY=${HARDWARE_CONCURRENCY:-16} INPUTS_ROOT="../../yarn-project/end-to-end/example-app-ivc-inputs-out" @@ -159,7 +160,7 @@ done echo "Results in: $LOCAL_OUT/" ``` -To profile all three flows, loop `FLOW` over the three values in the Available flows section above. +To profile multiple flows, loop `FLOW` over the values in the Available flows section above. ## Step 4: Generate a Perfetto link diff --git a/barretenberg/.codex b/barretenberg/.codex new file mode 120000 index 000000000000..c8161850a43d --- /dev/null +++ b/barretenberg/.codex @@ -0,0 +1 @@ +.claude \ No newline at end of file diff --git a/barretenberg/README.md b/barretenberg/README.md index fb1f4571c09b..3d717969060d 100644 --- a/barretenberg/README.md +++ b/barretenberg/README.md @@ -407,15 +407,16 @@ Now when you `print` things with e.g. `print bigfield_t.get_value()` or inspect #### Running Realistic Chonk from barretenberg folder -Realistic IVC inputs pose a problem as the only code to sequence them requires a full end to end run. -One can run the fourth newest master commit for example (any master commit that has finished benchmarking can be used): -`barretenberg/cpp/bootstrap.sh bench_ivc origin/master~3` +Realistic IVC inputs are pinned in CI and downloaded into +`yarn-project/end-to-end/example-app-ivc-inputs-out`. +Run the benchmark flow with: +`barretenberg/cpp/bootstrap.sh bench_ivc` To do a single benchmark you can do e.g. -`IVC_BENCH=ecdsar1+transfer_0_recursions+sponsored_fpc ./bootstrap.sh bench_ivc origin/master~3` +`barretenberg/cpp/bootstrap.sh bench_ivc transfer_0_recursions+sponsored_fpc` -If one doesn't provide the commit, it generates these IVC inputs on the fly (depends on yarn-project having been bootstrapped). -To use these inputs manually, just abort after input download and run Chonk proving on those inputs (stored in `yarn-project/end-to-end/example-app-ivc-inputs-out`). +To use these inputs manually, run `barretenberg/cpp/scripts/chonk_inputs.sh download` and then point +`bb prove --scheme chonk` at the downloaded flow's `ivc-inputs.msgpack`. #### Using Tracy to Profile Memory/CPU/Gate Counts diff --git a/barretenberg/cpp/.codex b/barretenberg/cpp/.codex new file mode 120000 index 000000000000..c8161850a43d --- /dev/null +++ b/barretenberg/cpp/.codex @@ -0,0 +1 @@ +.claude \ No newline at end of file diff --git a/barretenberg/cpp/CLAUDE.md b/barretenberg/cpp/CLAUDE.md index c81ba242e4f5..57ab22926b83 100644 --- a/barretenberg/cpp/CLAUDE.md +++ b/barretenberg/cpp/CLAUDE.md @@ -160,8 +160,7 @@ cd barretenberg/cpp Run the VK check script from barretenberg/cpp/scripts: ```bash -cd barretenberg/cpp/scripts -./test_chonk_standalone_vks_havent_changed.sh +barretenberg/cpp/scripts/chonk_inputs.sh check ``` Expected result: Script exits successfully if VKs are unchanged, or shows that VKs have changed. @@ -173,40 +172,41 @@ Expected result: Script exits successfully if VKs are unchanged, or shows that V If VKs have changed and this is expected due to your modifications, update the stored VKs: ```bash -cd barretenberg/cpp/scripts -./test_chonk_standalone_vks_havent_changed.sh --update_inputs +barretenberg/cpp/scripts/chonk_inputs.sh update ``` ### Verifying VK validity (proving the updated inputs) -**IMPORTANT**: There is no need to verify the validity of the inputs after having updated them. The flag `update_inputs` verifies the new inputs. - -To verify the validity of the inputs pinned by the script `./test_chonk_standalone_vks_havent_changed.sh`, run: +Proving the pinned inputs is handled by tests, not by `chonk_inputs.sh`. To verify the C++ pinned input path locally, run: ```bash -cd barretenberg/cpp/scripts -./test_chonk_standalone_vks_havent_changed.sh --prove_and_verify +barretenberg/cpp/scripts/chonk_inputs.sh download +barretenberg/cpp/scripts/run_test.sh bbapi_tests ChonkPinnedIvcInputsTest.AllPinnedFlows ``` -Note: If a proof test fails for a specific flow, the inputs are saved to: -`yarn-project/end-to-end/example-app-ivc-inputs-out/` +For bb.js, run: + +```bash +barretenberg/cpp/scripts/chonk_inputs.sh download +barretenberg/ts/scripts/run_test.sh bbapi/chonk_pinned_inputs.test.js +``` Typical workflow 1. Make barretenberg changes 2. Build native code: `cd barretenberg/cpp && ./bootstrap.sh build_native` -3. Check VKs: `cd scripts && ./test_chonk_standalone_vks_havent_changed.sh` -4. If VKs changed intentionally: `./test_chonk_standalone_vks_havent_changed.sh --update_inputs` +3. Check VKs: `barretenberg/cpp/scripts/chonk_inputs.sh check` +4. If VKs changed intentionally: `barretenberg/cpp/scripts/chonk_inputs.sh update` ## Example IVC inputs -Example IVC inputs (msgpack files) for `bb prove --scheme chonk` are generated by e2e benchmark tests. Run the full bootstrap from the repo root to populate them: +Example IVC inputs (msgpack files) for `bb prove --scheme chonk` are pinned to a fixed CI tarball. Download them from the repo root with: ```bash -cd $(git rev-parse --show-toplevel) && ./bootstrap.sh +barretenberg/cpp/scripts/chonk_inputs.sh download ``` -This creates `yarn-project/end-to-end/example-app-ivc-inputs-out//ivc-inputs.msgpack`. The inputs are generated by the `build_bench` function in `yarn-project/end-to-end/bootstrap.sh`, which runs client flow tests with `CAPTURE_IVC_FOLDER` set. In CI, these are cached as `bb-chonk-captures-.tar.gz`. +This creates `yarn-project/end-to-end/example-app-ivc-inputs-out//ivc-inputs.msgpack`. To intentionally refresh the pinned tarball, use the PR `ci-refresh-chonk` label or put `--ci-refresh-chonk` in the head commit message. ## Memory profiling diff --git a/barretenberg/cpp/bootstrap.sh b/barretenberg/cpp/bootstrap.sh index cf107937bdea..accf8ceb4baf 100755 --- a/barretenberg/cpp/bootstrap.sh +++ b/barretenberg/cpp/bootstrap.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash source $(git rev-parse --show-toplevel)/ci3/source_bootstrap +source "$root/barretenberg/cpp/scripts/pinned_chonk_inputs.sh" if [ "${AVM:-1}" -eq "1" ]; then export native_preset=${NATIVE_PRESET:-clang20} @@ -125,6 +126,10 @@ function build_native_objects { function build_native { build_preset $native_preset; } +function download_chonk_inputs { + scripts/chonk_inputs.sh download +} + # Builds object files early for cross compilation (parallel with avm-transpiler). function build_cross_objects { set -eu @@ -220,7 +225,7 @@ function build_release_dir { tar -czf build-release/barretenberg-static-x86_64-android.tar.gz -C build-x86_64-android/lib libbb-external.a } -export -f cmake_build preset_cache_paths build_preset build_format_check build_native_objects build_cross_objects build_native build_gcc_syntax_check_only build_fuzzing_syntax_check_only build_smt_verification inject_version inject_bb_versions +export -f cmake_build preset_cache_paths build_preset build_format_check build_native_objects build_cross_objects build_native download_chonk_inputs build_gcc_syntax_check_only build_fuzzing_syntax_check_only build_smt_verification inject_version inject_bb_versions function build { echo_header "bb cpp build" @@ -245,6 +250,9 @@ function test_cmds_native { awk '/^[a-zA-Z]/ {suite=$1} /^[ ]/ {print suite$1}' | \ grep -v 'DISABLED_' | \ while read -r test; do + if [[ "$test" == "ChonkPinnedIvcInputsTest.AllPinnedFlows" ]]; then + continue + fi # Skip heavy recursion tests in debug builds — they take 400-600s+ and the same # code paths are already exercised (with assertions) by faster tests in the suite. # Keep WithoutPredicate/1.GenerateVKFromConstraints (241s) so that the debug-only @@ -265,12 +273,8 @@ function test_cmds_native { done || (echo "Failed to list tests in $bin" && exit 1) done - # The pinned IVC inputs / VKs live in the public repo; the private fork - # carries divergent circuits so the check is expected to fail there. - local github_repository="${GITHUB_REPOSITORY:-}" - if [[ "${github_repository,,}" != "aztecprotocol/aztec-packages-private" ]]; then - echo "$hash barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh" - fi + echo "$hash:CPUS=8:MEM=32g:TIMEOUT=20m barretenberg/cpp/scripts/run_test.sh bbapi_tests ChonkPinnedIvcInputsTest.AllPinnedFlows" + echo "$hash barretenberg/cpp/scripts/chonk_inputs.sh check" } function test_cmds_wasm_threads { @@ -321,12 +325,51 @@ function test { test_cmds | filter_test_cmds | parallelize } +function pinned_chonk_bench_flow_names { + ensure_pinned_chonk_inputs "$(pinned_chonk_inputs_dir)" >&2 + list_pinned_chonk_input_flows "$(pinned_chonk_inputs_dir)" +} + +function chonk_ivc_bench_cmds { + local flow_filter="${1:-}" + local flow flow_dir + while IFS= read -r flow; do + [[ -n "$flow" ]] || continue + [[ -z "$flow_filter" || "$flow" == *"$flow_filter"* ]] || continue + flow_dir="../../yarn-project/end-to-end/example-app-ivc-inputs-out/$flow" + echo "$hash:CPUS=8 barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh native $flow_dir" + if [[ "${NO_WASM:-}" != "1" ]]; then + echo "$hash:CPUS=8 barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh wasm $flow_dir" + fi + done < <(pinned_chonk_bench_flow_names) +} + +function chonk_browser_bench_cmds { + local flow flow_dir + while IFS= read -r flow; do + [[ -n "$flow" ]] || continue + flow_dir="../../yarn-project/end-to-end/example-app-ivc-inputs-out/$flow" + echo "$hash:ISOLATE=1:NET=1:CPUS=8:PARALLEL=0 barretenberg/cpp/scripts/ci_benchmark_browser_memory.sh $flow_dir" + done < <(pinned_chonk_bench_flow_names) +} + +function ultrahonk_rollup_bench_cmds { + local inputs_dir="../../yarn-project/end-to-end/ultrahonk-bench-inputs" + local cpus + for cpus in 8 16 32; do + echo "$hash:CPUS=$cpus:PARALLEL=0 barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh parity_base $inputs_dir $cpus" + done +} + function bench_cmds { prefix="$hash:CPUS=8" echo "$prefix barretenberg/cpp/scripts/run_bench.sh native bb-micro-bench/native/ultra_honk $native_build_dir/bin/ultra_honk_bench construct_proof_ultrahonk_power_of_2/20$" echo "$prefix barretenberg/cpp/scripts/run_bench.sh native bb-micro-bench/native/ultra_honk_zk $native_build_dir/bin/ultra_honk_bench construct_proof_ultrahonk_zk_power_of_2/20$" echo "$prefix barretenberg/cpp/scripts/run_bench.sh wasm bb-micro-bench/wasm/ultra_honk build-wasm-threads/bin/ultra_honk_bench construct_proof_ultrahonk_power_of_2/20$" echo "$prefix barretenberg/cpp/scripts/run_bench.sh wasm bb-micro-bench/wasm/ultra_honk_zk build-wasm-threads/bin/ultra_honk_bench construct_proof_ultrahonk_zk_power_of_2/20$" + chonk_ivc_bench_cmds + chonk_browser_bench_cmds + ultrahonk_rollup_bench_cmds } # Runs benchmarks sharded over machine cores. @@ -343,13 +386,11 @@ function release { } function bench_ivc { - # Intended only for dev usage. For CI usage, we run yarn-project/end-to-end/bootstrap.sh bench. + # Intended only for dev usage. CI gets the same commands from bench_cmds. # Sample usage (CI=1 required for bench results to be visible; exclude NO_WASM=1 to run wasm benchmarks): # CI=1 NO_WASM=1 ./barretenberg/cpp/bootstrap.sh bench_ivc transfer_0_recursions+sponsored_fpc - git fetch origin next - flow_filter="${1:-}" # optional string-match filter for flow names - commit_hash="${2:-origin/next~3}" # commit from which to download flow inputs + flow_filter="${1:-}" # optional string-match filter for flow names # Build both native and wasm benchmark binaries builds=( @@ -360,32 +401,20 @@ function bench_ivc { fi parallel --line-buffered --tag -v denoise ::: "${builds[@]}" - # Download cached flow inputs from the specified commit - export AZTEC_CACHE_COMMIT=$commit_hash - # TODO currently does nothing! to reinstate in cache_download - export FORCE_CACHE_DOWNLOAD=${FORCE_CACHE_DOWNLOAD:-1} - # make sure that disabling the aztec VM does not interfere with cache results from CI. + # Make sure disabling the aztec VM does not interfere with cache results from CI. BOOTSTRAP_AFTER=barretenberg BOOSTRAP_TO=yarn-project ../../bootstrap.sh rm -rf bench-out - # Recreation of logic from bench. - ../../yarn-project/end-to-end/bootstrap.sh build_bench - - # Extract and filter benchmark commands by flow name and wasm/no-wasm - function ivc_bench_cmds { - local flow_filter="$1" # select only flows containing this string - - ../../yarn-project/end-to-end/bootstrap.sh bench_cmds | - grep barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh | - { [[ "${NO_WASM:-}" == "1" ]] && grep -v wasm || cat; } | - { [[ -n "$flow_filter" ]] && grep -F "$flow_filter" || cat; } - } - echo "Running commands:" - ivc_bench_cmds "$flow_filter" + chonk_ivc_bench_cmds "$flow_filter" + + chonk_ivc_bench_cmds "$flow_filter" | STRICT_SCHEDULING=1 parallelize +} - ivc_bench_cmds "$flow_filter" | STRICT_SCHEDULING=1 parallelize +function chonk_input_update { + echo_header "bb chonk input update" + scripts/ci_update_chonk_inputs.sh } case "$cmd" in diff --git a/barretenberg/cpp/scripts/README.md b/barretenberg/cpp/scripts/README.md index 378c8562da47..c81ff6baf7b3 100644 --- a/barretenberg/cpp/scripts/README.md +++ b/barretenberg/cpp/scripts/README.md @@ -28,8 +28,9 @@ There are scripts that: 4. If `ssh` worked, the setup is complete. ## How +- `./scripts/chonk_inputs.sh download && ./scripts/ci_benchmark_ivc_flows.sh native` is the shortest local path for a pinned Chonk flow benchmark. The benchmark script defaults to the sponsored `transfer_0` flow and accepts an explicit flow directory as its second argument. - `./scripts/benchmark_example_ivc_flow_remote.sh` copies the example flow input you'd like to run to the remote machine, runs `bb prove`, and analyze the results. - - For the script to work you need to have the example flows downloaded locally, by `AZTEC_CACHE_COMMIT=origin/next~3 FORCE_CACHE_DOWNLOAD=1 yarn-project/end-to-end/bootstrap.sh build_bench` + - The script downloads the pinned Chonk example flows into `yarn-project/end-to-end/example-app-ivc-inputs-out` when they are missing or stale. - If you have other special needs, look inside the above scripts and see what parameters you can give, or use `./scripts/benchmark_remote.sh`. Chonk proving must always be measured on real example app flows via `benchmark_example_ivc_flow_remote.sh` — there is no synthetic chonk benchmark, and there should not be one. Running synthetic Chonk benchmarks gives misleading numbers because the mock circuits do not reflect production proving costs. diff --git a/barretenberg/cpp/scripts/bench_hardware_concurrency.sh b/barretenberg/cpp/scripts/bench_hardware_concurrency.sh index 49eb75879812..587af7e81b6e 100755 --- a/barretenberg/cpp/scripts/bench_hardware_concurrency.sh +++ b/barretenberg/cpp/scripts/bench_hardware_concurrency.sh @@ -5,8 +5,7 @@ # Example: ./bench_hardware_concurrency.sh 1 2 4 8 16 32 # # To run on a remote machine with ci.sh shell-new: -# ./ci.sh shell-new "./ci3/cache_download bb-chonk-captures-ba1369853ed8670e.tar.gz ; \ -# mv example-app-ivc-inputs-out yarn-project/end-to-end 2>/dev/null ; \ +# ./ci.sh shell-new "./barretenberg/cpp/scripts/chonk_inputs.sh download ; \ # DENOISE=1 ./barretenberg/cpp/bootstrap.sh build_native ; \ # DENOISE=1 ./barretenberg/cpp/scripts/bench_hardware_concurrency.sh" # @@ -19,7 +18,8 @@ REPO_ROOT=$(git rev-parse --show-toplevel) # Use ci3 script base. -source $REPO_ROOT/ci3/source_bootstrap +source $REPO_ROOT/ci3/source +source $REPO_ROOT/barretenberg/cpp/scripts/pinned_chonk_inputs.sh # Use provided arguments or default values if [ $# -eq 0 ]; then @@ -37,6 +37,9 @@ TEST_CASES=( "ecdsar1+transfer_1_recursions+private_fpc" ) +echo "Ensuring pinned Chonk inputs are present..." >&2 +ensure_pinned_chonk_inputs "$(pinned_chonk_inputs_dir)" + # Function to run benchmark for a specific test case run_benchmark() { local test_case=$1 diff --git a/barretenberg/cpp/scripts/benchmark_example_ivc_flow_remote.sh b/barretenberg/cpp/scripts/benchmark_example_ivc_flow_remote.sh index 514240c14af3..80fa024713ec 100755 --- a/barretenberg/cpp/scripts/benchmark_example_ivc_flow_remote.sh +++ b/barretenberg/cpp/scripts/benchmark_example_ivc_flow_remote.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -eu +REPO_ROOT=$(git rev-parse --show-toplevel) +NO_CD=1 source "$REPO_ROOT/ci3/source" +source "$REPO_ROOT/barretenberg/cpp/scripts/pinned_chonk_inputs.sh" + TARGET=${1:-"bb"} #FLOW=${2:-"ecdsar1+amm_add_liquidity_1_recursions+sponsored_fpc"} #FLOW=${2:-"ecdsar1+transfer_1_recursions+private_fpc"} @@ -8,11 +12,13 @@ TARGET=${1:-"bb"} #FLOW=${2:-"ecdsar1+transfer_1_recursions+sponsored_fpc"} FLOW=${2:-"schnorr+deploy_tokenContract_with_registration+sponsored_fpc"} BUILD_DIR="build-no-avm" +INPUT_PATH="$(pinned_chonk_inputs_dir)/$FLOW/ivc-inputs.msgpack" + +ensure_pinned_chonk_inputs "$(pinned_chonk_inputs_dir)" -# Move above script dir. -cd $(dirname $0)/.. +cd "$REPO_ROOT/barretenberg/cpp" -scp $BB_SSH_KEY ../../yarn-project/end-to-end/example-app-ivc-inputs-out/$FLOW/ivc-inputs.msgpack $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/build/ +scp $BB_SSH_KEY "$INPUT_PATH" $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/build/ # Measure the benchmarks with ops time counting diff --git a/barretenberg/cpp/scripts/chonk-inputs.hash b/barretenberg/cpp/scripts/chonk-inputs.hash new file mode 100644 index 000000000000..36bfba515855 --- /dev/null +++ b/barretenberg/cpp/scripts/chonk-inputs.hash @@ -0,0 +1 @@ +5d17cc1cfa051b60 diff --git a/barretenberg/cpp/scripts/chonk_inputs.sh b/barretenberg/cpp/scripts/chonk_inputs.sh new file mode 100755 index 000000000000..1ffd63248c35 --- /dev/null +++ b/barretenberg/cpp/scripts/chonk_inputs.sh @@ -0,0 +1,159 @@ +#!/usr/bin/env bash +# Single owner of the pinned Chonk IVC inputs lifecycle. +# +# The current pin lives in chonk-inputs.hash next to this script. The tarball is +# stored as bb-chonk-inputs-.tar.gz under the protocol artifact +# bucket. +# +# Subcommands: +# download [dir] Download and extract pinned inputs. Defaults to the canonical e2e fixture dir. +# update Capture generated flows, then upload and pin them. +# check Download pinned inputs to a scratch dir and run bb check over every flow. +# help Show this help. +set -euo pipefail + +own_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NO_CD=1 source "$(git rev-parse --show-toplevel)/ci3/source" +source "$own_dir/pinned_chonk_inputs.sh" + +script_dir="$root/barretenberg/cpp/scripts" +bb_preset="${BB_BUILD_PRESET:-${NATIVE_PRESET:-clang20}}" +bb="$root/barretenberg/cpp/$($script_dir/preset-build-dir "$bb_preset")/bin/bb" +export bb_preset +export bb + +function usage { + awk ' + NR == 1 { next } + /^set -euo pipefail$/ { exit } + /^#/ { sub(/^# ?/, ""); print; next } + /^$/ { print } + ' "$0" +} + +function require_bb { + if [[ ! -x "$bb" ]]; then + echo_stderr "ERROR: bb binary not found at $bb" + echo_stderr "Build it first, or set BB_BUILD_PRESET/NATIVE_PRESET to the preset containing bin/bb." + return 1 + fi +} + +function cmd_download { + local dest=${1:-$(pinned_chonk_inputs_dir)} + ensure_pinned_chonk_inputs "$dest" + check_pinned_chonk_inputs "$dest" + echo "Pinned Chonk inputs ${pinned_chonk_inputs_hash} ready at: $dest" +} + +function capture_inputs { + if [[ -n "${CHONK_INPUTS_CAPTURE_FROM:-}" ]]; then + local inputs_dir + inputs_dir="$(pinned_chonk_inputs_dir)" + echo "Using pre-captured Chonk inputs from ${CHONK_INPUTS_CAPTURE_FROM}" + rm -rf "$inputs_dir" + mkdir -p "$inputs_dir" + cp -a "${CHONK_INPUTS_CAPTURE_FROM}/." "$inputs_dir/" + check_chonk_inputs_shape "$inputs_dir" + return + fi + + echo "Running live Chonk input capture via yarn-project/end-to-end build_bench_capture..." + cd "$root" + ./bootstrap.sh pull_submodules + make yarn-project + cd "$root/yarn-project/end-to-end" + ./bootstrap.sh build_bench_capture +} + +function check_circuit_vks { + set -eu + local flow_folder="$work_dir/$1" + local output + local exit_code=0 + local -a bb_check_args=(check --scheme chonk --ivc_inputs_path "$flow_folder/ivc-inputs.msgpack") + + if [[ "$bb_preset" == "debug" ]]; then + bb_check_args+=(--disable_asserts) + fi + + output="$("$bb" "${bb_check_args[@]}" 2>&1)" || exit_code=$? + + if [[ $exit_code -ne 0 ]]; then + if echo "$output" | grep -q "VK mismatch detected\|Expected precomputed vk"; then + echo "Error: VK change detected in $flow_folder" >&2 + echo "$output" >&2 + exit 1 + fi + + echo "Error: bb check failed in $flow_folder (not a VK change)" >&2 + echo "$output" >&2 + exit 2 + fi +} +export -f check_circuit_vks + +function extract_parallel_exit_code { + local log_file="$1" + local exit_code=0 + + awk 'NR>1 { codes[$7]=1 } END { + has_other = 0; + has_one = 0; + for (code in codes) { + if (code != 0 && code != 1) has_other = 1; + if (code == 1) has_one = 1; + } + if (has_other) exit 2; + if (has_one) exit 1; + exit 0; + }' "$log_file" || exit_code=$? + + case "$exit_code" in + 0) return 0 ;; + 1) return 1 ;; + *) return 2 ;; + esac +} + +function cmd_check { + require_bb + local exit_code=0 + export work_dir + work_dir="$(reset_pinned_chonk_state_subdir downloaded)" + trap "rm -rf '$work_dir'" RETURN + download_pinned_chonk_inputs "$work_dir" + check_pinned_chonk_inputs "$work_dir" + + mapfile -t flows < <(list_pinned_chonk_input_flows "$work_dir") + parallel --joblog "$work_dir/joblog.log" -v --line-buffer --tag check_circuit_vks {} ::: "${flows[@]}" || true + extract_parallel_exit_code "$work_dir/joblog.log" || exit_code=$? + + if [[ $exit_code -eq 0 ]]; then + echo "No VK changes detected. Pinned hash is: ${pinned_chonk_inputs_hash}" + elif [[ $exit_code -eq 1 ]]; then + echo_stderr "VK changes detected. Add the ci-refresh-chonk label to the PR, put --ci-refresh-chonk in the head commit message, or run this script with update locally." + exit 1 + else + echo_stderr "Real error detected, please investigate." + exit "$exit_code" + fi +} + +function cmd_update { + capture_inputs + local new_hash + new_hash="$(upload_and_pin_chonk_inputs "$(pinned_chonk_inputs_dir)" | tail -1)" + echo "Pinned Chonk inputs refreshed to ${new_hash}." +} + +cmd="${1:-help}" +shift || true + +case "$cmd" in + download) cmd_download "$@" ;; + update) cmd_update "$@" ;; + check) cmd_check "$@" ;; + help|-h|--help) usage ;; + *) echo_stderr "Unknown subcommand: $cmd"; usage; exit 2 ;; +esac diff --git a/barretenberg/cpp/scripts/ci_benchmark_browser_memory.sh b/barretenberg/cpp/scripts/ci_benchmark_browser_memory.sh index a14769d9cc23..f2889568d171 100755 --- a/barretenberg/cpp/scripts/ci_benchmark_browser_memory.sh +++ b/barretenberg/cpp/scripts/ci_benchmark_browser_memory.sh @@ -2,10 +2,17 @@ # Runs a in-browser memory benchmark for Chonk proving. # This is used to get as realistic as possible memory usage for the proving of a private transaction in a browser setting. -source $(git rev-parse --show-toplevel)/ci3/source -cd .. - -flow_folder=${1:-"ecdsar1+transfer_0_recursions+sponsored_fpc"} +own_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NO_CD=1 source "$(git rev-parse --show-toplevel)/ci3/source" +source "$root/barretenberg/cpp/scripts/pinned_chonk_inputs.sh" +cd "$own_dir/.." + +if [[ $# -eq 0 ]]; then + ensure_pinned_chonk_inputs "$(pinned_chonk_inputs_dir)" + flow_folder="../../yarn-project/end-to-end/example-app-ivc-inputs-out/ecdsar1+transfer_0_recursions+sponsored_fpc" +else + flow_folder=$1 +fi flow="$(basename $flow_folder)" mkdir -p bench-out/ diff --git a/barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh b/barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh index 01e735c67c57..5edbbd282300 100755 --- a/barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh +++ b/barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh @@ -1,25 +1,102 @@ #!/usr/bin/env bash # Performs the chonk private transaction proving benchmarks for our 'realistic apps'. -# This is called by yarn-project/end-to-end/bootstrap.sh bench, which creates these inputs from end-to-end tests. -source $(git rev-parse --show-toplevel)/ci3/source -source $(git rev-parse --show-toplevel)/ci3/source_redis -source $(git rev-parse --show-toplevel)/ci3/source_cache +# Downloads the pinned inputs itself when the requested flow is missing or stale. +REPO_ROOT=$(git rev-parse --show-toplevel) +NO_CD=1 source "$REPO_ROOT/ci3/source" +source "$REPO_ROOT/ci3/source_redis" +source "$REPO_ROOT/ci3/source_cache" +source "$REPO_ROOT/barretenberg/cpp/scripts/pinned_chonk_inputs.sh" -if [[ $# -ne 2 ]]; then - echo "Usage: $0 " - exit 1 -fi -cd .. +default_chonk_flow="ecdsar1+transfer_0_recursions+sponsored_fpc" + +function usage { + echo "Usage: $0 [native|wasm] [benchmark_folder]" + echo "Defaults to native ${default_chonk_flow} from the pinned Chonk inputs." +} + +case $# in + 0) + runtime_arg="native" + flow_folder_arg="$(pinned_chonk_inputs_dir)/$default_chonk_flow" + ;; + 1) + runtime_arg="$1" + flow_folder_arg="$(pinned_chonk_inputs_dir)/$default_chonk_flow" + ;; + 2) + runtime_arg="$1" + flow_folder_arg="${2%/}" + ;; + *) + usage >&2 + exit 1 + ;; +esac +case "$runtime_arg" in + native|wasm) ;; + *) usage >&2; exit 1 ;; +esac +caller_dir="$PWD" + +function resolve_flow_folder_arg { + local requested="${1%/}" + if [[ "$requested" == /* ]]; then + realpath -m "$requested" + return + fi + + local caller_path="$caller_dir/$requested" + if [[ -e "$caller_path" ]]; then + realpath -m "$caller_path" + return + fi + + realpath -m "$REPO_ROOT/barretenberg/cpp/$requested" +} + +flow_folder_arg="$(resolve_flow_folder_arg "$flow_folder_arg")" + +cd "$REPO_ROOT/barretenberg/cpp" echo_header "bb ivc flow bench" export HARDWARE_CONCURRENCY=${CPUS:-8} # E.g. build, build-debug or build-coverage export native_build_dir=$(scripts/preset-build-dir) +resolved_chonk_flow_folder="" + +function ensure_chonk_flow_folder { + local requested="$1" + local flow + flow="$(basename "$requested")" + + ensure_pinned_chonk_inputs "$(pinned_chonk_inputs_dir)" + + local canonical_flow_folder="" + if canonical_flow_folder="$(pinned_chonk_input_flow_dir "$flow" "$(pinned_chonk_inputs_dir)")"; then + if [[ -f "$requested/ivc-inputs.msgpack" ]]; then + if [[ "$(realpath -m "$requested")" != "$(realpath -m "$canonical_flow_folder")" ]]; then + if [[ "${ALLOW_UNPINNED_CHONK_INPUTS:-0}" != "1" ]]; then + echo_stderr "ERROR: $requested is not the pinned Chonk input flow for '$flow'." + echo_stderr "Use the pinned flow at $canonical_flow_folder, or set ALLOW_UNPINNED_CHONK_INPUTS=1 for a local override." + exit 1 + fi + resolved_chonk_flow_folder="$requested" + return + fi + fi + resolved_chonk_flow_folder="$canonical_flow_folder" + return + fi + + echo_stderr "ERROR: pinned Chonk input flow '$flow' was not found at $requested or $(pinned_chonk_inputs_dir)/$flow" + exit 1 +} function verify_ivc_flow { - local flow="$1" - local proof="$2" + local runtime="$1" + local flow="$2" + local proof="$3" # Check that this verifies with one of our verification keys and fails with the other. # NOTE: This is effectively a test. # TODO(AD): Checking which one would be good, but there isn't too much that can go wrong here. @@ -27,10 +104,24 @@ function verify_ivc_flow { # Extract VK bytes from JSON artifacts and convert to binary local circuits_target="../../noir-projects/noir-protocol-circuits/target" + local rollup_vk_json="$circuits_target/hiding_kernel_to_rollup.json" + local public_vk_json="$circuits_target/hiding_kernel_to_public.json" + if [[ ! -f "$rollup_vk_json" || ! -f "$public_vk_json" ]]; then + echo_stderr "Pinned inputs for this flow remain at $(pinned_chonk_inputs_dir)/$flow/ivc-inputs.msgpack" + echo_stderr "Rerun with: barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh $runtime '$(pinned_chonk_inputs_dir)/$flow'" + if [[ "${CI:-0}" == "1" || "${REQUIRE_IVC_FLOW_KEY_VERIFY:-0}" == "1" ]]; then + echo_stderr "Verification key artifacts are missing under $circuits_target." + echo_stderr "Build noir-projects/noir-protocol-circuits before running this benchmark." + exit 1 + fi + echo "proof completed; protocol VK check skipped because $circuits_target has not been built." + return 0 + fi + local rollup_vk=$(mktemp) local public_vk=$(mktemp) - jq -r '.verificationKey.bytes' "$circuits_target/hiding_kernel_to_rollup.json" | xxd -r -p > "$rollup_vk" - jq -r '.verificationKey.bytes' "$circuits_target/hiding_kernel_to_public.json" | xxd -r -p > "$public_vk" + jq -r '.verificationKey.bytes' "$rollup_vk_json" | xxd -r -p > "$rollup_vk" + jq -r '.verificationKey.bytes' "$public_vk_json" | xxd -r -p > "$public_vk" echo_stderr "Private verify." "./$native_build_dir/bin/bb" verify --scheme chonk -p "$proof" -k "$rollup_vk" 1>&2 @@ -50,6 +141,16 @@ function verify_ivc_flow { echo_stderr "Verification failed for $flow. Did not verify with precalculated verification key - we may need to revisit how it is generated in noir-projects/noir-protocol-circuits." exit 1 fi + echo "protocol VK check passed." +} + +function print_chonk_flow_reuse_hint { + local runtime="$1" + local flow_folder="$2" + local flow + flow="$(basename "$flow_folder")" + echo_stderr "Pinned inputs for this flow remain at $flow_folder/ivc-inputs.msgpack" + echo_stderr "Rerun with: barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh $runtime '$(pinned_chonk_inputs_dir)/$flow'" } function run_bb_cli_bench { @@ -61,6 +162,7 @@ function run_bb_cli_bench { # Add --bench_out_hierarchical flag for native builds to capture hierarchical op counts and timings memusage "./$native_build_dir/bin/bb" "$@" "--bench_out_hierarchical" "$output/benchmark_breakdown.json" "--memory_profile_out" "$output/memory_profile.json" || { echo "bb native failed with args: $@ --bench_out_hierarchical $output/benchmark_breakdown.json --memory_profile_out $output/memory_profile.json" + print_chonk_flow_reuse_hint "$runtime" "$flow_folder" exit 1 } else # wasm @@ -69,6 +171,7 @@ function run_bb_cli_bench { # Note: --memory_profile_out is native-only (getrusage not available in wasm) memusage scripts/wasmtime.sh $WASMTIME_ALLOWED_DIRS ./build-wasm-threads/bin/bb "$@" "--bench_out_hierarchical" "$output/benchmark_breakdown.json" || { echo "bb wasm failed with args: $@ --bench_out_hierarchical $output/benchmark_breakdown.json" + print_chonk_flow_reuse_hint "$runtime" "$flow_folder" exit 1 } fi @@ -95,8 +198,9 @@ function chonk_flow { local memory_taken_mb=$(cat "$MEMUSAGE_OUT") echo "$flow ($runtime) has proven in $((elapsed_ms / 1000))s and peak memory of ${memory_taken_mb}MB." - dump_fail "verify_ivc_flow $flow $output/proof" - echo "$flow ($runtime) has verified." + local verification_status + verification_status="$(dump_fail "verify_ivc_flow $runtime $flow $output/proof")" + echo "$flow ($runtime): $verification_status" # Get proof size after compression tar -czf "$output/proof.tar.gz" -C "$output" proof local proof_size_bytes=$(stat -c%s "$output/proof.tar.gz" 2>/dev/null || stat -f%z "$output/proof.tar.gz") @@ -148,28 +252,29 @@ EOF fi } -export -f verify_ivc_flow run_bb_cli_bench +export -f verify_ivc_flow print_chonk_flow_reuse_hint run_bb_cli_bench -chonk_flow $1 $2 +ensure_chonk_flow_folder "$flow_folder_arg" +chonk_flow "$runtime_arg" "$resolved_chonk_flow_folder" # Upload benchmark breakdown (op counts and timings) to disk if running in CI # Now uploads all flows via disk transfer only (no git uploads) -runtime="$1" -flow_name="$(basename $2)" +runtime="$runtime_arg" +flow_name="$(basename "$resolved_chonk_flow_folder")" if [[ "${CI:-}" == "1" ]] && [[ "${CI_USE_BUILD_INSTANCE_KEY:-0}" == "1" ]]; then echo_header "Uploading Barretenberg benchmark breakdowns for $flow_name" + current_sha=$(git rev-parse HEAD) benchmark_breakdown_file="bench-out/app-proving/$flow_name/$runtime/benchmark_breakdown.json" if [[ -f "$benchmark_breakdown_file" ]]; then # TODO(AD): make this robust. not erroring if this breaks is not great. set +e - current_sha=$(git rev-parse HEAD) - # Copy to /tmp with unique name to avoid race conditions with concurrent flows + upload_state_dir="$(make_pinned_chonk_state_tmpdir benchmark-upload)" + tmp_breakdown_file="$upload_state_dir/benchmark_breakdown_${runtime}_${flow_name}.json" # Other flows might delete bench-out before we finish uploading - tmp_breakdown_file="/tmp/benchmark_breakdown_${runtime}_${flow_name}_$$.json" cp "$benchmark_breakdown_file" "$tmp_breakdown_file" # Upload to S3 (bench/bb-breakdown subfolder) in background @@ -177,8 +282,7 @@ if [[ "${CI:-}" == "1" ]] && [[ "${CI_USE_BUILD_INSTANCE_KEY:-0}" == "1" ]]; the disk_key="${runtime}-${flow_name}-${current_sha}" { cat "$tmp_breakdown_file" | gzip | cache_s3_transfer_to "bench/bb-breakdown" "$disk_key" - # Clean up tmp file after upload completes - rm -f "$tmp_breakdown_file" + rm -rf "$upload_state_dir" } & echo "Uploaded benchmark breakdown to S3: bench/bb-breakdown/$disk_key" @@ -189,12 +293,13 @@ if [[ "${CI:-}" == "1" ]] && [[ "${CI_USE_BUILD_INSTANCE_KEY:-0}" == "1" ]]; the # Upload memory profile to S3 memory_profile_file="bench-out/app-proving/$flow_name/$runtime/memory_profile.json" if [[ -f "$memory_profile_file" ]]; then - tmp_memory_file="/tmp/memory_profile_${runtime}_${flow_name}_$$.json" + upload_state_dir="$(make_pinned_chonk_state_tmpdir memory-upload)" + tmp_memory_file="$upload_state_dir/memory_profile_${runtime}_${flow_name}.json" cp "$memory_profile_file" "$tmp_memory_file" memory_disk_key="memory-${runtime}-${flow_name}-${current_sha}" { cat "$tmp_memory_file" | gzip | cache_s3_transfer_to "bench/bb-breakdown" "$memory_disk_key" - rm -f "$tmp_memory_file" + rm -rf "$upload_state_dir" } & echo "Uploaded memory profile to S3: bench/bb-breakdown/$memory_disk_key" fi diff --git a/barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh b/barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh index a40022d66a2d..2b5b31ba1b81 100755 --- a/barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh +++ b/barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh @@ -9,9 +9,10 @@ # - .json (the circuit artifact with bytecode) # - witness.gz (the compressed witness) -source $(git rev-parse --show-toplevel)/ci3/source -source $(git rev-parse --show-toplevel)/ci3/source_redis -source $(git rev-parse --show-toplevel)/ci3/source_cache +own_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NO_CD=1 source "$(git rev-parse --show-toplevel)/ci3/source" +source "$root/ci3/source_redis" +source "$root/ci3/source_cache" if [[ $# -ne 3 ]]; then echo "Usage: $0 " @@ -19,7 +20,7 @@ if [[ $# -ne 3 ]]; then exit 1 fi -cd .. +cd "$own_dir/.." circuit_name="$1" inputs_folder="$2" @@ -30,6 +31,105 @@ echo_header "UltraHonk benchmark: $circuit_name (CPUS=$cpus)" export HARDWARE_CONCURRENCY="$cpus" export native_build_dir=$(scripts/preset-build-dir) +function ultrahonk_inputs_present { + local dir="$1" + local expected_hash="${2:-}" + local marker="$dir/.ultrahonk-bench-inputs.hash" + if [[ -n "$expected_hash" ]] && [[ ! -f "$marker" || "$(<"$marker")" != "$expected_hash" ]]; then + return 1 + fi + [[ -f "$dir/${circuit_name}.json" && -f "$dir/witness.gz" ]] +} + +function generate_ultrahonk_inputs { + local dir="$1" + local state_dir + if [[ "$circuit_name" != "parity_base" ]]; then + echo_stderr "Error: automatic UltraHonk input generation currently supports parity_base only." + exit 1 + fi + + state_dir="$root/.cache/ultrahonk-bench-inputs/generate-$$" + rm -rf "$state_dir" + mkdir -p "$state_dir" + if ! ( + cd "$root/yarn-project" + BASE_PARITY_BENCH_DIR="$state_dir" yarn workspace @aztec/ivc-integration test src/base_parity_inputs.test.ts + ); then + rm -rf "$state_dir" + return 1 + fi + + rm -rf "$dir" + mkdir -p "$(dirname "$dir")" + mv "$state_dir" "$dir" +} + +function restore_cached_ultrahonk_inputs { + local dir="$1" + local cache_name="$2" + local state_dir cached_dir + if [[ "$(basename "$dir")" != "ultrahonk-bench-inputs" ]]; then + return 1 + fi + + state_dir="$root/.cache/ultrahonk-bench-inputs/cache-$$" + cached_dir="$state_dir/ultrahonk-bench-inputs" + rm -rf "$state_dir" + mkdir -p "$state_dir" + if cache_download "$cache_name" "$state_dir" && ultrahonk_inputs_present "$cached_dir"; then + rm -rf "$dir" + mkdir -p "$(dirname "$dir")" + mv "$cached_dir" "$dir" + rm -rf "$state_dir" + return 0 + fi + + rm -rf "$state_dir" + return 1 +} + +function ensure_ultrahonk_inputs { + local requested="$1" + local abs_inputs cache_hash cache_name lock_dir lock_file + abs_inputs="$(realpath -m "$requested")" + + cache_hash="$(cd "$root/yarn-project" && ./bootstrap.sh hash)" + cache_name="bb-ultrahonk-bench-inputs-${cache_hash}.tar.gz" + + if ultrahonk_inputs_present "$abs_inputs" "$cache_hash"; then + inputs_folder="$abs_inputs" + return + fi + + lock_dir="$root/.cache/ultrahonk-bench-inputs/locks" + mkdir -p "$lock_dir" + lock_file="$lock_dir/$(printf '%s' "$abs_inputs" | sha256sum | cut -c1-16).lock" + + ( + flock 9 + if ultrahonk_inputs_present "$abs_inputs" "$cache_hash"; then + return + fi + + restore_cached_ultrahonk_inputs "$abs_inputs" "$cache_name" || true + + if ! ultrahonk_inputs_present "$abs_inputs"; then + echo "Generating UltraHonk benchmark inputs at $abs_inputs" + generate_ultrahonk_inputs "$abs_inputs" + if [[ "$abs_inputs" == "$root/yarn-project/end-to-end/ultrahonk-bench-inputs" ]]; then + (cd "$root/yarn-project/end-to-end" && cache_upload "$cache_name" ultrahonk-bench-inputs) + fi + fi + + printf '%s\n' "$cache_hash" > "$abs_inputs/.ultrahonk-bench-inputs.hash" + ) 9>"$lock_file" + + inputs_folder="$abs_inputs" +} + +ensure_ultrahonk_inputs "$inputs_folder" + # Verify inputs exist bytecode_path="$inputs_folder/${circuit_name}.json" witness_path="$inputs_folder/witness.gz" diff --git a/barretenberg/cpp/scripts/ci_update_chonk_inputs.sh b/barretenberg/cpp/scripts/ci_update_chonk_inputs.sh new file mode 100755 index 000000000000..c265fd431b4d --- /dev/null +++ b/barretenberg/cpp/scripts/ci_update_chonk_inputs.sh @@ -0,0 +1,192 @@ +#!/usr/bin/env bash +set -euo pipefail + +root="$(git rev-parse --show-toplevel)" +cd "$root" +NO_CD=1 source "$root/ci3/source" +source "$root/barretenberg/cpp/scripts/pinned_chonk_inputs.sh" + +hash_file="barretenberg/cpp/scripts/chonk-inputs.hash" +refresh_label="${CHONK_INPUT_REFRESH_LABEL:-ci-refresh-chonk}" +github_token="${GITHUB_TOKEN:-${GH_TOKEN:-}}" +github_repository="${GITHUB_REPOSITORY:-AztecProtocol/aztec-packages}" +state_dir="$root/.cache/chonk-inputs/ci-update" +diff_file="$state_dir/chonk-input-update.diff" + +function echo_stderr { + echo "$@" >&2 +} + +function gh_pr { + [[ -n "$github_token" ]] || return 1 + GH_TOKEN="$github_token" gh pr "$@" +} + +function remove_pr_label { + local label=${1:?label required} + [[ -n "${PR_NUMBER:-}" ]] || return 0 + gh_pr edit "$PR_NUMBER" --repo "$github_repository" --remove-label "$label" >/dev/null 2>&1 || true +} + +function comment_pr { + local body=${1:?body required} + [[ -n "${PR_NUMBER:-}" ]] || return 0 + gh_pr comment "$PR_NUMBER" --repo "$github_repository" --body "$body" >/dev/null 2>&1 || true +} + +function write_diff_file { + mkdir -p "$state_dir" + : > "$diff_file" + if ! git diff --cached --quiet; then + { + echo "# staged diff" + git diff --cached --binary + } >> "$diff_file" + fi + if ! git diff --quiet; then + { + echo "# unstaged diff" + git diff --binary + } >> "$diff_file" + fi + if [[ ! -s "$diff_file" ]]; then + git show --binary --format=fuller --patch HEAD > "$diff_file" + fi +} + +function upload_diff_file { + write_diff_file + local key run_id + run_id="${GITHUB_RUN_ID:-manual-$(date +%s)}" + key="chonk-input-update-diffs/${PR_NUMBER:-unknown}-${run_id}-$(git rev-parse --short HEAD).diff" + aws s3 cp "$diff_file" "${PINNED_CHONK_S3_BUCKET}/${key}" >/dev/null + echo "${PINNED_CHONK_BASE_URL}/${key}" +} + +function comment_diff_failure { + local reason=${1:?reason required} + local diff_url="" + if diff_url="$(upload_diff_file 2>/dev/null)"; then + comment_pr "Chonk input update produced a diff, but ${reason}. Apply the diff from this artifact or rerun the update: ${diff_url}" + else + comment_pr "Chonk input update produced a diff, but ${reason}. I could not upload the diff artifact from CI; rerun the update locally." + fi +} + +function stage_expected_changes { + if [[ ! -f "$hash_file" ]]; then + echo_stderr "ERROR: Chonk input update did not produce ${hash_file}." + return 1 + fi + git add "$hash_file" +} + +function fail_on_unstaged_changes { + if git diff --quiet; then + return 0 + fi + + echo_stderr "ERROR: Chonk input update produced tracked changes outside the allowed scope:" + git diff --name-only >&2 + if [[ -n "${PR_NUMBER:-}" ]]; then + comment_diff_failure "tracked files outside the pinned Chonk input hash scope also changed" + fi + return 1 +} + +function select_smallest_pinned_flow { + local inputs_dir + inputs_dir="$(pinned_chonk_inputs_dir)" + local flow_dir size best_size="" best_flow="" + + for flow_dir in "$inputs_dir"/*/; do + [[ -f "${flow_dir}ivc-inputs.msgpack" ]] || continue + size="$(stat -c%s "${flow_dir}ivc-inputs.msgpack" 2>/dev/null || stat -f%z "${flow_dir}ivc-inputs.msgpack")" + if [[ -z "$best_size" || "$size" -lt "$best_size" ]]; then + best_size="$size" + best_flow="$(basename "$flow_dir")" + fi + done + + if [[ -z "$best_flow" ]]; then + echo_stderr "ERROR: no pinned Chonk input flows found under $inputs_dir" + return 1 + fi + + echo "$best_flow" +} + +function verify_refreshed_inputs { + echo "Verifying refreshed Chonk inputs before push..." + barretenberg/cpp/scripts/chonk_inputs.sh download + local flow + flow="$(select_smallest_pinned_flow)" + echo "Using smallest pinned Chonk smoke-test flow: $flow" + CHONK_PINNED_IVC_FLOW="$flow" CHONK_PINNED_IVC_FLOW_LIMIT=1 \ + barretenberg/cpp/scripts/run_test.sh bbapi_tests ChonkPinnedIvcInputsTest.AllPinnedFlows + CHONK_PINNED_IVC_FLOW="$flow" CHONK_PINNED_IVC_FLOW_LIMIT=1 CHONK_PINNED_IVC_WASM_FLOW_LIMIT=1 \ + barretenberg/ts/scripts/run_test.sh bbapi/chonk_pinned_inputs.test.js +} + +remove_pr_label "$refresh_label" + +echo "Updating Chonk inputs..." +barretenberg/cpp/scripts/chonk_inputs.sh update + +verify_refreshed_inputs + +stage_expected_changes +fail_on_unstaged_changes + +if git diff --cached --quiet; then + echo "Pinned Chonk input hash is already up to date." + remove_pr_label "$refresh_label" + exit 0 +fi + +new_hash="$(tr -d '[:space:]' < "$hash_file")" + +if [[ -z "${PR_NUMBER:-}" || -z "${PR_HEAD_REF:-}" ]]; then + git diff --cached --stat + echo_stderr "ERROR: Chonk input update would change files, but this run is not attached to a PR head branch." + exit 1 +fi + +pr_info="$(GH_TOKEN="$github_token" gh pr view "$PR_NUMBER" --repo "$github_repository" --json headRefName,isCrossRepository)" +head_ref="$(jq -r '.headRefName' <<< "$pr_info")" +is_fork="$(jq -r '.isCrossRepository' <<< "$pr_info")" + +if [[ "$is_fork" == "true" ]]; then + comment_diff_failure "push-back is unavailable for fork PRs" + remove_pr_label "$refresh_label" + exit 1 +fi + +if [[ "$head_ref" != "$PR_HEAD_REF" ]]; then + comment_diff_failure "the PR head branch changed from ${PR_HEAD_REF} to ${head_ref}" + remove_pr_label "$refresh_label" + exit 1 +fi + +git config user.name "AztecBot" +git config user.email "tech@aztecprotocol.com" +git remote set-url origin "https://x-access-token:${github_token}@github.com/${github_repository}.git" +git config --unset-all http.https://github.com/.extraheader || true + +write_diff_file +git commit -m "chore(bb): refresh pinned Chonk IVC inputs to ${new_hash}" \ + -m "Generated by ci-refresh-chonk." \ + -m "Only the pinned Chonk input hash is committed here; the immediate follow-up CI run is skipped intentionally." \ + -m "--ci-skip" \ + --no-verify + +if git push origin "HEAD:refs/heads/${PR_HEAD_REF}"; then + echo "Pushed Chonk input update to ${PR_HEAD_REF}." + comment_pr "Pinned Chonk inputs refreshed to \`${new_hash}\`. The update commit includes a head-commit \`--ci-skip\` marker so the automatic follow-up run does not repeat CI." + exit 0 +fi + +echo_stderr "ERROR: failed to push Chonk input update to ${PR_HEAD_REF}." +comment_diff_failure "the push to ${PR_HEAD_REF} was rejected" +remove_pr_label "$refresh_label" +exit 1 diff --git a/barretenberg/cpp/scripts/pinned_chonk_inputs.sh b/barretenberg/cpp/scripts/pinned_chonk_inputs.sh new file mode 100755 index 000000000000..a37a5ca24f4c --- /dev/null +++ b/barretenberg/cpp/scripts/pinned_chonk_inputs.sh @@ -0,0 +1,296 @@ +#!/usr/bin/env bash +# Shared helpers for the pinned chonk IVC inputs S3 hash. +# +# All scripts that download or update the pinned chonk inputs MUST source +# this file rather than holding their own copy of the hash. The hash file is +# updated automatically by PR CI when an input update is requested. +if [[ -z "${root:-}" ]]; then + NO_CD=1 source "$(git rev-parse --show-toplevel)/ci3/source" +fi + +PINNED_CHONK_INPUTS_HASH_FILE="${PINNED_CHONK_INPUTS_HASH_FILE:-$root/barretenberg/cpp/scripts/chonk-inputs.hash}" +PINNED_CHONK_S3_BUCKET="${PINNED_CHONK_S3_BUCKET:-s3://aztec-ci-artifacts/protocol}" +PINNED_CHONK_BASE_URL="${PINNED_CHONK_BASE_URL:-https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol}" +PINNED_CHONK_STATE_DIR="${CHONK_INPUTS_STATE_DIR:-$root/.cache/chonk-inputs}" +PINNED_CHONK_MARKER_FILE=".chonk-inputs.hash" +PINNED_CHONK_HASH_LENGTH=16 +export PINNED_CHONK_HASH_LENGTH +export PINNED_CHONK_MARKER_FILE + +function read_pinned_chonk_inputs_hash { + local hash + hash=$(<"$PINNED_CHONK_INPUTS_HASH_FILE") + hash="${hash//$'\n'/}" + hash="${hash//$'\r'/}" + hash="${hash// /}" + if ! is_pinned_chonk_hash "$hash"; then + echo_stderr "ERROR: invalid pinned chonk inputs hash '$hash' in $PINNED_CHONK_INPUTS_HASH_FILE" + return 1 + fi + echo "$hash" +} + +function is_pinned_chonk_hash { + local hash=${1:-} + local hash_regex="^[a-f0-9]{${PINNED_CHONK_HASH_LENGTH}}$" + [[ "$hash" =~ $hash_regex ]] +} + +if ! pinned_chonk_inputs_hash="$(read_pinned_chonk_inputs_hash)"; then + return 1 2>/dev/null || exit 1 +fi + +function update_pinned_chonk_inputs_hash { + local new_hash=${1:?new hash required} + if ! is_pinned_chonk_hash "$new_hash"; then + echo_stderr "ERROR: invalid pinned chonk inputs hash '$new_hash'" + return 1 + fi + printf '%s\n' "$new_hash" > "$PINNED_CHONK_INPUTS_HASH_FILE" + pinned_chonk_inputs_hash="$new_hash" +} + +function pinned_chonk_inputs_url { + local hash=${1:-$pinned_chonk_inputs_hash} + echo "${PINNED_CHONK_BASE_URL}/bb-chonk-inputs-${hash}.tar.gz" +} + +function pinned_chonk_inputs_s3_uri { + local hash=${1:?hash required} + echo "${PINNED_CHONK_S3_BUCKET}/bb-chonk-inputs-${hash}.tar.gz" +} + +function reset_pinned_chonk_state_subdir { + local name=${1:?state subdir required} + local dir="$PINNED_CHONK_STATE_DIR/$name" + rm -rf "$dir" + mkdir -p "$dir" + echo "$dir" +} + +function make_pinned_chonk_state_tmpdir { + local prefix=${1:?state prefix required} + mkdir -p "$PINNED_CHONK_STATE_DIR" + mktemp -d "$PINNED_CHONK_STATE_DIR/${prefix}.XXXXXXXX" +} + +# Canonical extraction directory used by downstream Chonk scripts. +function pinned_chonk_inputs_dir { + echo "$(git rev-parse --show-toplevel)/yarn-project/end-to-end/example-app-ivc-inputs-out" +} + +function list_chonk_input_flow_dirs { + local dir=${1:?dir required} + find "$dir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort +} + +function list_pinned_chonk_input_flows { + local dir=${1:-$(pinned_chonk_inputs_dir)} + check_pinned_chonk_inputs "$dir" + list_chonk_input_flow_dirs "$dir" +} + +function pinned_chonk_input_flow_dir { + local flow=${1:?flow required} + local dir=${2:-$(pinned_chonk_inputs_dir)} + local flow_dir="$dir/$flow" + if [[ ! -f "$flow_dir/ivc-inputs.msgpack" ]]; then + echo_stderr "ERROR: pinned Chonk input flow '$flow' not found under $dir" + return 1 + fi + echo "$flow_dir" +} + +function write_pinned_chonk_inputs_marker { + local dest=${1:?dest dir required} + printf '%s\n' "$pinned_chonk_inputs_hash" > "$dest/$PINNED_CHONK_MARKER_FILE" +} + +# Downloads and extracts the pinned tarball into $dest. Wipes $dest first. +# Fails noisily on download or extract errors. +function download_pinned_chonk_inputs { + local dest=${1:?dest dir required} + local url + url=$(pinned_chonk_inputs_url) + echo_stderr "Downloading pinned chonk inputs ${pinned_chonk_inputs_hash} from ${url}" + rm -rf "$dest" + mkdir -p "$dest" + local state_dir tarball + state_dir="$(make_pinned_chonk_state_tmpdir download)" + tarball="$state_dir/bb-chonk-inputs-${pinned_chonk_inputs_hash}.tar.gz" + if ! curl -sSf "$url" -o "$tarball"; then + echo_stderr "ERROR: failed to download pinned chonk inputs from $url" + echo_stderr "pinned_chonk_inputs_hash='${pinned_chonk_inputs_hash}' may be stale." + echo_stderr "Add the ci-refresh-chonk label to the PR, or put --ci-refresh-chonk in the head commit message, to regenerate." + rm -rf "$state_dir" + return 1 + fi + if ! tar -xzf "$tarball" -C "$dest"; then + echo_stderr "ERROR: failed to extract pinned chonk inputs tarball" + rm -rf "$state_dir" + return 1 + fi + write_pinned_chonk_inputs_marker "$dest" + rm -rf "$state_dir" +} + +function check_chonk_inputs_shape { + local dir=${1:?dir required} + if [[ ! -d "$dir" ]] || [[ -z "$(ls -A "$dir" 2>/dev/null)" ]]; then + echo_stderr "ERROR: pinned chonk inputs not present at $dir" + return 1 + fi + local missing=() + local flow_count=0 + for flow_dir in "$dir"/*/; do + [[ -d "$flow_dir" ]] || continue + flow_count=$((flow_count + 1)) + [[ -f "$flow_dir/ivc-inputs.msgpack" ]] || missing+=("${flow_dir}ivc-inputs.msgpack") + done + if (( flow_count == 0 )); then + echo_stderr "ERROR: pinned chonk inputs contain no flow directories at $dir" + return 1 + fi + if (( ${#missing[@]} > 0 )); then + echo_stderr "ERROR: pinned chonk inputs missing files:" + printf ' %s\n' "${missing[@]}" >&2 + return 1 + fi +} + +# Validates that $dir is a pinned chonk inputs tree for the current hash: +# at least one flow folder, each containing ivc-inputs.msgpack, plus marker. +function check_pinned_chonk_inputs { + local dir=${1:-$(pinned_chonk_inputs_dir)} + check_chonk_inputs_shape "$dir" + local marker="$dir/$PINNED_CHONK_MARKER_FILE" + if [[ -f "$marker" ]] && [[ "$(<"$marker")" != "$pinned_chonk_inputs_hash" ]]; then + echo_stderr "ERROR: pinned chonk inputs at $dir were downloaded for hash $(<"$marker"), expected ${pinned_chonk_inputs_hash}" + return 1 + fi +} + +function ensure_pinned_chonk_inputs { + local dest=${1:-$(pinned_chonk_inputs_dir)} + local lock_dir lock_file + lock_dir="$PINNED_CHONK_STATE_DIR/locks" + mkdir -p "$lock_dir" + lock_file="$lock_dir/$(printf '%s' "$dest" | sha256sum | cut -c1-16).lock" + + ( + flock 9 + if [[ -f "$dest/$PINNED_CHONK_MARKER_FILE" ]] && check_pinned_chonk_inputs "$dest" >/dev/null 2>&1; then + return 0 + fi + download_pinned_chonk_inputs "$dest" + check_pinned_chonk_inputs "$dest" + ) 9>"$lock_file" +} + +function create_normalized_chonk_inputs_tarball { + local src=${1:?src dir required} + local tarball=${2:?tarball path required} + local state_dir plain_tar + mkdir -p "$(dirname "$tarball")" + state_dir="$(make_pinned_chonk_state_tmpdir package)" + plain_tar="$state_dir/bb-chonk-inputs.tar" + + if ! tar --sort=name \ + --mtime='UTC 1970-01-01' \ + --owner=0 \ + --group=0 \ + --numeric-owner \ + --mode='u+rw,go+r-w,a+X' \ + --exclude='./chonk-inputs-manifest.json' \ + --exclude="./$PINNED_CHONK_MARKER_FILE" \ + -cf "$plain_tar" \ + -C "$src" .; then + rm -rf "$state_dir" + return 1 + fi + if ! gzip -n -c "$plain_tar" > "$tarball"; then + rm -rf "$state_dir" + return 1 + fi + rm -rf "$state_dir" +} + +function assert_chonk_inputs_object_exists { + local hash=${1:?hash required} + if [[ "${CHONK_INPUTS_UPLOAD_DRY_RUN:-0}" == "1" ]]; then + return 0 + fi + local url + url="$(pinned_chonk_inputs_url "$hash")" + if ! curl -sSfI "$url" >/dev/null; then + echo_stderr "ERROR: uploaded chonk inputs artifact is not visible at ${url}" + return 1 + fi +} + +# Compresses $src into a tarball, uploads to S3 under the new hash prefix, +# rewrites the hash file. No-op when +# the regenerated tarball hashes to the existing pinned value. +# Echoes the resulting hash prefix to stdout. +function upload_and_pin_chonk_inputs { + local src=${1:?src dir required} + local state_dir tarball + check_chonk_inputs_shape "$src" + state_dir="$(make_pinned_chonk_state_tmpdir upload)" + tarball="$state_dir/bb-chonk-inputs.tar.gz" + + echo_stderr "Packaging chonk inputs from $src ..." + if ! create_normalized_chonk_inputs_tarball "$src" "$tarball"; then + rm -rf "$state_dir" + return 1 + fi + + local full_hash hash_prefix + if ! full_hash=$(sha256sum "$tarball" | awk '{print $1}'); then + rm -rf "$state_dir" + return 1 + fi + hash_prefix="${full_hash:0:$PINNED_CHONK_HASH_LENGTH}" + + if [[ "$hash_prefix" == "${pinned_chonk_inputs_hash}" ]]; then + echo_stderr "Regenerated inputs hash to ${hash_prefix}; matches current pin. No upload." + echo "$hash_prefix" + rm -rf "$state_dir" + return 0 + fi + + local s3_uri + s3_uri="$(pinned_chonk_inputs_s3_uri "$hash_prefix")" + if [[ "${CHONK_INPUTS_UPLOAD_DRY_RUN:-0}" == "1" ]]; then + echo_stderr "Dry-run: would upload new chonk inputs to ${s3_uri}" + else + echo_stderr "Uploading new chonk inputs to ${s3_uri} ..." + if ! aws s3 cp "$tarball" "$s3_uri"; then + rm -rf "$state_dir" + return 1 + fi + fi + if ! assert_chonk_inputs_object_exists "$hash_prefix"; then + rm -rf "$state_dir" + return 1 + fi + + echo_stderr "Pinned chonk inputs: ${pinned_chonk_inputs_hash} -> ${hash_prefix}" + if ! update_pinned_chonk_inputs_hash "$hash_prefix"; then + rm -rf "$state_dir" + return 1 + fi + echo "$hash_prefix" + rm -rf "$state_dir" +} + +export -f pinned_chonk_inputs_url pinned_chonk_inputs_s3_uri pinned_chonk_inputs_dir \ + make_pinned_chonk_state_tmpdir list_chonk_input_flow_dirs \ + list_pinned_chonk_input_flows pinned_chonk_input_flow_dir \ + write_pinned_chonk_inputs_marker \ + reset_pinned_chonk_state_subdir create_normalized_chonk_inputs_tarball \ + assert_chonk_inputs_object_exists check_chonk_inputs_shape \ + is_pinned_chonk_hash read_pinned_chonk_inputs_hash update_pinned_chonk_inputs_hash \ + download_pinned_chonk_inputs check_pinned_chonk_inputs \ + ensure_pinned_chonk_inputs \ + upload_and_pin_chonk_inputs diff --git a/barretenberg/cpp/scripts/run_test.sh b/barretenberg/cpp/scripts/run_test.sh index 4f5ad1923c25..3dec572b6abe 100755 --- a/barretenberg/cpp/scripts/run_test.sh +++ b/barretenberg/cpp/scripts/run_test.sh @@ -5,8 +5,15 @@ set -eu export native_preset=${NATIVE_PRESET:-clang20} +test_bin=${1:?test binary required} +test_filter=${2:?gtest filter required} cd $(dirname $0)/.. + +if [[ "$test_bin" == "bbapi_tests" && "$test_filter" == "ChonkPinnedIvcInputsTest.AllPinnedFlows" ]]; then + scripts/chonk_inputs.sh download +fi + # E.g. build, build-debug or build-coverage cd $(scripts/preset-build-dir) @@ -14,4 +21,4 @@ export GTEST_COLOR=1 export HARDWARE_CONCURRENCY=${CPUS:-8} export BB_VERBOSE=1 -exec ./bin/$1 --gtest_filter=$2 +exec ./bin/$test_bin --gtest_filter=$test_filter diff --git a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh b/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh deleted file mode 100755 index 8308eea435a5..000000000000 --- a/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh +++ /dev/null @@ -1,255 +0,0 @@ -#!/usr/bin/env bash -source $(git rev-parse --show-toplevel)/ci3/source - -# Resolve bb from an explicit preset when provided, and fall back to known build dirs. -script_dir="$root/barretenberg/cpp/scripts" -bb_preset="${BB_BUILD_PRESET:-${NATIVE_PRESET:-clang20}}" -bb="$root/barretenberg/cpp/$($script_dir/preset-build-dir "$bb_preset")/bin/bb" - -export bb_preset -export bb - -# script path to auto update short hash -script_path="$root/barretenberg/cpp/scripts/test_chonk_standalone_vks_havent_changed.sh" - - -# NOTE: We pin the captured IVC inputs to a known master commit, exploiting that there won't be frequent changes. -# This allows us to compare the generated VKs here with ones we compute freshly, detecting breaking protocol changes. -# IF A VK CHANGE IS EXPECTED - we need to redo this: -# - Generate inputs: $root/yarn-project/end-to-end/bootstrap.sh build_bench -# - Compress the results: tar -czf bb-chonk-inputs.tar.gz -C example-app-ivc-inputs-out . -# - Generate a hash for versioning: sha256sum bb-chonk-inputs.tar.gz -# - Upload the compressed results: aws s3 cp bb-chonk-inputs.tar.gz s3://aztec-ci-artifacts/protocol/bb-chonk-inputs-[hash(0:8)].tar.gz -# Note: In case of the "Test suite failed to run ... Unexpected token 'with' " error, need to run: docker pull aztecprotocol/build:3.0 -pinned_short_hash="63d1c3f0" -pinned_chonk_inputs_url="https://aztec-ci-artifacts.s3.us-east-2.amazonaws.com/protocol/bb-chonk-inputs-${pinned_short_hash}.tar.gz" - -function update_pinned_hash_in_script { - local new_hash=$1 - echo "Updating pinned_short_hash in script to: $new_hash" - sed -i "s/^pinned_short_hash=\"[^\"]*\"/pinned_short_hash=\"$new_hash\"/" "$script_path" -} - -function compress_and_upload { - # 1) Compress the results - echo "Compressing the generated inputs..." - tar -czf bb-chonk-inputs.tar.gz -C $1 . - - # 2) Compute a short hash for versioning - echo "Computing SHA256 hash for versioning..." - full_hash=$(sha256sum bb-chonk-inputs.tar.gz | awk '{ print $1 }') - short_hash=${full_hash:0:8} - echo "Short hash is: $short_hash" - - # 3) Upload to S3 - s3_key="bb-chonk-inputs-${short_hash}.tar.gz" - s3_uri="s3://aztec-ci-artifacts/protocol/${s3_key}" - echo "Uploading bb-chonk-inputs.tar.gz to ${s3_uri}..." - aws s3 cp bb-chonk-inputs.tar.gz "${s3_uri}" - - # 4) Update the pinned hash in this script - update_pinned_hash_in_script "$short_hash" - - echo "Done. New inputs available at:" - echo " ${s3_uri}" - echo "Script updated with new pinned_short_hash: $short_hash" -} - -function check_circuit_vks { - set -eu - local flow_folder="$inputs_dir/$1" - local output - local exit_code=0 - local -a bb_check_args=(check --scheme chonk --ivc_inputs_path "$flow_folder/ivc-inputs.msgpack") - - if [[ "$bb_preset" == "debug" ]]; then - bb_check_args+=(--disable_asserts) - fi - - output=$($bb "${bb_check_args[@]}" 2>&1) || exit_code=$? - - if [[ $exit_code -ne 0 ]]; then - # Check if this is actually a VK change - if echo "$output" | grep -q "VK mismatch detected\|Expected precomputed vk"; then - echo_stderr "Error: VK change detected in $flow_folder!" - echo_stderr "$output" - exit 1 - else - # Some other error occurred (file corruption, crash, etc.) - echo_stderr "Error: bb check failed in $flow_folder (not a VK change):" - echo_stderr "$output" - echo_stderr "" - echo_stderr "This indicates a bug or regression that is not related to VK changes." - echo_stderr "If this failure wasn't caught by other tests, please add a test case to prevent this regression." - exit 2 - fi - fi -} - -export -f check_circuit_vks - -function prove_and_verify_inputs { - set -eu - local flow_folder="$inputs_dir/$1" - local prove_exit_code=0 - - echo "Running proof test for $1..." - $bb prove --scheme chonk --ivc_inputs_path "$flow_folder/ivc-inputs.msgpack" > /dev/null 2>&1 || prove_exit_code=$? - - if [[ $prove_exit_code -ne 0 ]]; then - echo "Proof test failed for flow $1. Please re-run the script with flag --update_inputs." - - cp "$flow_folder/ivc-inputs.msgpack" "$root/yarn-project/end-to-end/example-app-ivc-inputs-out/$1/ivc-inputs.msgpack" - echo "Inputs copied in yarn-project for debugging" - exit 1 - fi -} - -export -f prove_and_verify_inputs - -# Extract exit code from job logs of parallel execution -function extract_exit_code { - local log_file="$1" - local exit_code=0 - - awk 'NR>1 { codes[$7]=1 } END { - has_other = 0; - has_one = 0; - for (code in codes) { - if (code != 0 && code != 1) has_other = 1; - if (code == 1) has_one = 1; - } - if (has_other) exit 2; - if (has_one) exit 1; - exit 0; - }' "$log_file" || exit_code=$? - - if [[ $exit_code -eq 0 ]]; then - return 0 - elif [[ $exit_code -eq 1 ]]; then - return 1 - else - return 2 - fi -} - -if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then - cat << EOF - Usage: $(basename "$0") [OPTIONS] - - Options: - none Test that Chonk standalone VKs haven't changed - --update_inputs Generate new IVC inputs and upload to S3 - --prove_and_verify Prove and verify current pinned inputs - --download_pinned_inputs Download pinned inputs to yarn-project for local debugging - -h, --help Show this help message - - Description: - Tests that Chonk standalone VKs haven't changed by comparing - generated VKs with pinned reference inputs. -EOF - exit 0 -elif [[ "${1:-}" == "--update_inputs" ]]; then - export inputs_dir="$root/yarn-project/end-to-end/example-app-ivc-inputs-out" - - # For easily rerunning the inputs generation - set -eu - trap 'rm -f bb-chonk-inputs.tar.gz' EXIT SIGINT - echo "Updating pinned IVC inputs..." - - # Generate new inputs - echo "Running bootstrap to generate new IVC inputs..." - - cd "$root" - ./bootstrap.sh pull_submodules - make yarn-project - cd yarn-project/end-to-end - ./bootstrap.sh build_bench # build bench to generate IVC inputs - cd "$root/barretenberg/cpp/scripts" - - compress_and_upload "$inputs_dir" - - prove_exit_code=0 - parallel -v --line-buffer --tag prove_and_verify_inputs {} ::: $(ls "$inputs_dir") || prove_exit_code=$? - - if [[ $prove_exit_code -eq 1 ]]; then - echo "One or more flows failed the proof test after updating inputs. Please investigate." - exit 1 - fi - - echo "Inputs successfully updated." - exit 0 -elif [[ "${1:-}" == "--download_pinned_inputs" ]]; then - # Download pinned inputs to yarn-project for local debugging - set -eu - local_output_dir="$root/yarn-project/end-to-end/example-app-ivc-inputs-out" - - echo "Downloading pinned IVC inputs (hash: $pinned_short_hash) to $local_output_dir..." - - mkdir -p "$local_output_dir" - cd "$local_output_dir" - - # Clean existing contents - rm -rf ./* - - if ! curl -s -f "$pinned_chonk_inputs_url" -o bb-chonk-inputs.tar.gz; then - echo "Error: Failed to download pinned IVC inputs from $pinned_chonk_inputs_url" - exit 1 - fi - - tar -xzf bb-chonk-inputs.tar.gz -C . - rm -f bb-chonk-inputs.tar.gz - - echo "Done. Inputs downloaded to: $local_output_dir" - ls -la "$local_output_dir" - exit 0 -else - export inputs_dir=$(mktemp -d) - trap 'rm -rf "$inputs_dir" bb-chonk-inputs.tar.gz' EXIT SIGINT - - echo "Downloading pinned IVC inputs from: $pinned_chonk_inputs_url" - if ! curl -s -f "$pinned_chonk_inputs_url" -o bb-chonk-inputs.tar.gz; then - echo_stderr "Error: Failed to download pinned IVC inputs from $pinned_chonk_inputs_url" - echo_stderr "The pinned short hash '$pinned_short_hash' may be invalid or the file may not exist in S3." - exit 1 - fi - - echo "Extracting IVC inputs..." - if ! tar -xzf bb-chonk-inputs.tar.gz -C "$inputs_dir"; then - echo_stderr "Error: Failed to extract IVC inputs archive" - exit 1 - fi - - ls "$inputs_dir" - - if [[ "${1:-}" == "--prove_and_verify" ]]; then - # Prove and verify the current pinned inputs - prove_exit_code=0 - parallel -v --line-buffer --tag prove_and_verify_inputs {} ::: $(ls "$inputs_dir") || prove_exit_code=$? - - if [[ $prove_exit_code -ne 0 ]]; then - echo "One or more flows failed the proof test after updating inputs. Please investigate." - exit 1 - else - echo "All inputs were successfully proven and verified." - fi - exit 0 - else - exit_code=0 - parallel --joblog "$inputs_dir/joblog.log" -v --line-buffer --tag check_circuit_vks {} ::: $(ls "$inputs_dir") || true - - extract_exit_code "$inputs_dir/joblog.log" || exit_code=$? - - if [[ $exit_code -eq 0 ]]; then - echo "No VK changes detected. Short hash is: ${pinned_short_hash}" - elif [[ $exit_code -eq 1 ]]; then - # All flows had VK changes - echo "VK changes detected. Please re-run the script with --update_inputs" - exit 1 - else - # At least one real error - echo "Real error detected, please investigate." - exit $exit_code - fi - fi -fi diff --git a/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk_pinned_inputs.test.cpp b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk_pinned_inputs.test.cpp new file mode 100644 index 000000000000..305eb0d651a9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk_pinned_inputs.test.cpp @@ -0,0 +1,131 @@ +#include "barretenberg/bbapi/bbapi_chonk.hpp" +#include "barretenberg/bbapi/bbapi_shared.hpp" +#include "barretenberg/chonk/chonk_proof.hpp" +#include "barretenberg/chonk/private_execution_steps.hpp" +#include "barretenberg/common/log.hpp" +#include "barretenberg/srs/global_crs.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +class ChonkPinnedIvcInputsTest : public ::testing::Test { + protected: + static void SetUpTestSuite() { bb::srs::init_file_crs_factory(bb::srs::bb_crs_path()); } + + static std::filesystem::path find_repo_root() + { + if (const char* env = std::getenv("AZTEC_REPO_ROOT"); env != nullptr && *env != '\0') { + return std::filesystem::path{ env }; + } + return std::filesystem::weakly_canonical(std::filesystem::current_path() / "../../.."); + } + + static std::filesystem::path pinned_inputs_root() + { + if (const char* env = std::getenv("CHONK_PINNED_IVC_INPUTS_DIR"); env != nullptr && *env != '\0') { + return std::filesystem::path{ env }; + } + return find_repo_root() / "yarn-project/end-to-end/example-app-ivc-inputs-out"; + } + + static std::vector find_flow_dirs(const std::filesystem::path& inputs_root) + { + std::vector flows; + if (!std::filesystem::is_directory(inputs_root)) { + return flows; + } + for (const auto& entry : std::filesystem::directory_iterator(inputs_root)) { + if (entry.is_directory() && std::filesystem::exists(entry.path() / "ivc-inputs.msgpack")) { + flows.push_back(entry.path()); + } + } + std::sort(flows.begin(), flows.end()); + return flows; + } + + static void apply_flow_selection(std::vector& flows) + { + if (const char* filter = std::getenv("CHONK_PINNED_IVC_FLOW"); filter != nullptr && *filter != '\0') { + flows.erase(std::remove_if(flows.begin(), + flows.end(), + [filter](const auto& flow) { + return flow.filename().string().find(filter) == std::string::npos; + }), + flows.end()); + } + + const char* limit_env = std::getenv("CHONK_PINNED_IVC_FLOW_LIMIT"); + if (limit_env == nullptr || *limit_env == '\0') { + return; + } + char* end = nullptr; + const long limit = std::strtol(limit_env, &end, 10); + if (end != limit_env && *end == '\0' && limit > 0 && static_cast(limit) < flows.size()) { + flows.resize(static_cast(limit)); + } + } + + static void run_flow(const std::filesystem::path& flow_dir) + { + const std::filesystem::path inputs_path = flow_dir / "ivc-inputs.msgpack"; + info("ChonkPinnedIvcInputs: loading ", inputs_path.string()); + + auto raw_steps = bb::PrivateExecutionStepRaw::load_and_decompress(inputs_path); + ASSERT_FALSE(raw_steps.empty()) << "no execution steps in " << inputs_path; + + const auto hiding_bytecode = raw_steps.back().bytecode; + + bb::bbapi::BBApiRequest request; + request.vk_policy = bb::bbapi::VkPolicy::DEFAULT; + + bb::bbapi::ChonkStart{ .num_circuits = static_cast(raw_steps.size()) }.execute(request); + + for (auto& step : raw_steps) { + bb::bbapi::ChonkLoad{ + .circuit = { .name = std::move(step.function_name), + .bytecode = std::move(step.bytecode), + .verification_key = std::move(step.vk) } + }.execute(request); + bb::bbapi::ChonkAccumulate{ .witness = std::move(step.witness) }.execute(request); + } + + auto prove_response = bb::bbapi::ChonkProve{}.execute(request); + auto vk_response = + bb::bbapi::ChonkComputeVk{ .circuit = { .bytecode = hiding_bytecode }, .use_zk_flavor = true }.execute(); + + auto verify_response = + bb::bbapi::ChonkVerify{ .proof = std::move(prove_response.proof), .vk = std::move(vk_response.bytes) } + .execute(); + EXPECT_TRUE(verify_response.valid) << "ChonkVerify rejected " << flow_dir.filename(); + } +}; + +TEST_F(ChonkPinnedIvcInputsTest, AllPinnedFlows) +{ + const auto inputs_root = pinned_inputs_root(); + auto flows = find_flow_dirs(inputs_root); + ASSERT_FALSE(flows.empty()) << "no pinned Chonk flows under " << inputs_root + << ". Run `barretenberg/cpp/scripts/chonk_inputs.sh download` first."; + + apply_flow_selection(flows); + const char* flow_filter = std::getenv("CHONK_PINNED_IVC_FLOW"); + ASSERT_FALSE(flows.empty() && flow_filter != nullptr && *flow_filter != '\0') + << "CHONK_PINNED_IVC_FLOW='" << flow_filter << "' matched no pinned flows under " << inputs_root; + ASSERT_FALSE(flows.empty()) << "no pinned Chonk flows found under " << inputs_root; + + for (const auto& flow : flows) { + SCOPED_TRACE("flow: " + flow.filename().string()); + run_flow(flow); + } +} + +} // namespace diff --git a/barretenberg/ts/CHANGELOG.md b/barretenberg/ts/CHANGELOG.md index 1c99d7a36e09..7995feb362bf 100644 --- a/barretenberg/ts/CHANGELOG.md +++ b/barretenberg/ts/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Miscellaneous + +* Lower the default bb.js CRS download size from `2 ** 20` to `2 ** 19` for non-iOS browsers. iOS remains at `2 ** 18`. Callers that need a larger CRS can still pass an explicit SRS size. + ## [0.77.0](https://github.com/AztecProtocol/aztec-packages/compare/barretenberg.js-v0.76.4...barretenberg.js-v0.77.0) (2025-02-14) diff --git a/barretenberg/ts/bootstrap.sh b/barretenberg/ts/bootstrap.sh index 60448f66e79f..7a8fb3be4f5b 100755 --- a/barretenberg/ts/bootstrap.sh +++ b/barretenberg/ts/bootstrap.sh @@ -43,6 +43,7 @@ function test_cmds { for test in **/*.test.js; do # Skip benchmarks here. [[ "$test" =~ \.bench\.test\.js$ ]] && continue + [[ "$test" == "bbapi/chonk_pinned_inputs.test.js" ]] && continue local prefix=$hash # Extra resource. @@ -51,6 +52,7 @@ function test_cmds { fi echo "$prefix barretenberg/ts/scripts/run_test.sh $test" done + echo "$hash:CPUS=8:MEM=32g:TIMEOUT=20m barretenberg/cpp/scripts/chonk_inputs.sh download && barretenberg/ts/scripts/run_test.sh bbapi/chonk_pinned_inputs.test.js" } function bench_cmds { diff --git a/barretenberg/ts/package.json b/barretenberg/ts/package.json index c6118f0d423e..8f537d1f3750 100644 --- a/barretenberg/ts/package.json +++ b/barretenberg/ts/package.json @@ -40,6 +40,7 @@ "formatting": "prettier --check ./src && eslint --max-warnings 0 ./src", "formatting:fix": "prettier -w ./src", "test": "NODE_OPTIONS='--loader ts-node/esm' NODE_NO_WARNINGS=1 node --experimental-vm-modules $(yarn bin jest) --no-cache --passWithNoTests", + "test:chonk-inputs": "yarn test chonk_pinned_inputs", "test:debug": "NODE_NO_WARNINGS=1 node --inspect-brk=0.0.0.0 --experimental-vm-modules ./node_modules/.bin/jest --no-cache --passWithNoTests --runInBand", "simple_test": "NODE_NO_WARNINGS=1 node ./src/examples/simple.rawtest.ts", "deploy": "npm publish --access public" diff --git a/barretenberg/ts/src/barretenberg/index.ts b/barretenberg/ts/src/barretenberg/index.ts index 96c34a5b2bc3..90ad65642a27 100644 --- a/barretenberg/ts/src/barretenberg/index.ts +++ b/barretenberg/ts/src/barretenberg/index.ts @@ -5,6 +5,10 @@ import { IMsgpackBackendSync, IMsgpackBackendAsync } from '../bb_backends/interf import { BackendOptions, BackendType } from '../bb_backends/index.js'; import { createAsyncBackend, createSyncBackend } from '../bb_backends/node/index.js'; +const DEFAULT_BB_CRS_SIZE = 2 ** 19; +// Keep the iOS default separate so it can diverge when mobile memory limits require it. +const IOS_BB_CRS_SIZE = 2 ** 18; + export { UltraHonkBackend, UltraHonkVerifierBackend, @@ -50,7 +54,7 @@ export class Barretenberg extends AsyncApi { // Explicit backend required - no fallback const backend = await createAsyncBackend(options.backend, options, logger); if (!options.skipSrsInit && (options.backend === BackendType.Wasm || options.backend === BackendType.WasmWorker)) { - await backend.initSRSChonk(); + await backend.initSRSChonk(options.srsSize); } return backend; } @@ -62,7 +66,7 @@ export class Barretenberg extends AsyncApi { logger(`Unix socket unavailable (${err.message}), falling back to WASM`); const backend = await createAsyncBackend(BackendType.Wasm, options, logger); if (!options.skipSrsInit) { - await backend.initSRSChonk(); + await backend.initSRSChonk(options.srsSize); } return backend; } @@ -70,7 +74,7 @@ export class Barretenberg extends AsyncApi { logger(`In browser, using WASM over worker backend.`); const backend = await createAsyncBackend(BackendType.WasmWorker, options, logger); if (!options.skipSrsInit) { - await backend.initSRSChonk(); + await backend.initSRSChonk(options.srsSize); } return backend; } @@ -100,9 +104,9 @@ export class Barretenberg extends AsyncApi { // We expect the mobile iOS browser to kill us >=1GB, so no real use in using a larger SRS. // Use `self` instead of `window` so this check also works inside Web Workers. if (typeof self !== 'undefined' && typeof self.navigator !== 'undefined' && /iPad|iPhone/.test(self.navigator.userAgent)) { - return 2 ** 18; + return IOS_BB_CRS_SIZE; } - return 2 ** 20; + return DEFAULT_BB_CRS_SIZE; } async acirGetCircuitSizes( diff --git a/barretenberg/ts/src/bb_backends/index.ts b/barretenberg/ts/src/bb_backends/index.ts index 18e17ea68407..760f51fe9da0 100644 --- a/barretenberg/ts/src/bb_backends/index.ts +++ b/barretenberg/ts/src/bb_backends/index.ts @@ -22,6 +22,9 @@ export type BackendOptions = { /** @description Path to download CRS files */ crsPath?: string; + /** @description Number of G1 points to download when initializing the CRS/SRS for WASM backends */ + srsSize?: number; + /** @description Path to download WASM files */ wasmPath?: string; diff --git a/barretenberg/ts/src/bbapi/chonk_pinned_inputs.test.ts b/barretenberg/ts/src/bbapi/chonk_pinned_inputs.test.ts new file mode 100644 index 000000000000..a2611c18a1fb --- /dev/null +++ b/barretenberg/ts/src/bbapi/chonk_pinned_inputs.test.ts @@ -0,0 +1,130 @@ +import { Decoder } from 'msgpackr'; +import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'; +import { basename, join, resolve } from 'node:path'; +import { ungzip } from 'pako'; + +import { AztecClientBackend, BackendType, Barretenberg } from '../index.js'; + +interface RawStep { + bytecode: Buffer; + witness: Buffer; + vk: Buffer; + functionName: string; +} + +const TEST_TIMEOUT_MS = 30 * 60 * 1000; +const DEFAULT_WASM_FLOW_LIMIT = 1; +function findRepoRoot(): string { + return process.env.AZTEC_REPO_ROOT ?? resolve(process.cwd(), '../..'); +} + +function ensurePinnedInputsRoot(): string { + const explicit = process.env.CHONK_PINNED_IVC_INPUTS_DIR; + if (explicit) { + return explicit; + } + + const repoRoot = findRepoRoot(); + return join(repoRoot, 'yarn-project/end-to-end/example-app-ivc-inputs-out'); +} + +function discoverFlows(root: string): string[] { + if (!existsSync(root)) { + return []; + } + return readdirSync(root) + .map(name => join(root, name)) + .filter(path => statSync(path).isDirectory() && existsSync(join(path, 'ivc-inputs.msgpack'))) + .sort(); +} + +function selectFlows(flows: string[]): string[] { + const filter = process.env.CHONK_PINNED_IVC_FLOW; + const filtered = filter ? flows.filter(flow => basename(flow).includes(filter)) : flows; + const limit = Number(process.env.CHONK_PINNED_IVC_FLOW_LIMIT); + return Number.isInteger(limit) && limit > 0 ? filtered.slice(0, limit) : filtered; +} + +function loadPinnedFlow(flowDir: string) { + const buf = readFileSync(join(flowDir, 'ivc-inputs.msgpack')); + const steps = new Decoder({ useRecords: false }).unpack(buf) as RawStep[]; + if (steps.length === 0) { + throw new Error(`No execution steps in ${join(flowDir, 'ivc-inputs.msgpack')}`); + } + return { + bytecodes: steps.map(step => ungzip(step.bytecode)), + witnesses: steps.map(step => ungzip(step.witness)), + vks: steps.map(step => new Uint8Array(step.vk)), + names: steps.map(step => step.functionName), + }; +} + +function getWasmFlowLimit(): number { + const parsed = Number(process.env.CHONK_PINNED_IVC_WASM_FLOW_LIMIT ?? DEFAULT_WASM_FLOW_LIMIT); + return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_WASM_FLOW_LIMIT; +} + +const wasmFlowLimit = getWasmFlowLimit(); +const backendCases = [ + { + label: 'native', + backendType: BackendType.NativeUnixSocket, + threads: 16, + selectFlows: (flows: string[]) => flows, + }, + { + label: 'wasm', + backendType: BackendType.Wasm, + threads: 1, + selectFlows: (flows: string[]) => flows.slice(0, Math.max(1, wasmFlowLimit)), + }, +] as const; + +describe('Chonk pinned IVC inputs through bb.js', () => { + let flows: string[]; + let bbPath: string | undefined; + + beforeAll(() => { + const repoRoot = findRepoRoot(); + const defaultBbPath = join(repoRoot, 'barretenberg/cpp/build/bin/bb'); + bbPath = process.env.BB_BINARY_PATH ?? (existsSync(defaultBbPath) ? defaultBbPath : undefined); + const pinnedRoot = ensurePinnedInputsRoot(); + const discoveredFlows = discoverFlows(pinnedRoot); + if (discoveredFlows.length === 0) { + throw new Error( + `No pinned ivc-inputs.msgpack files found under ${pinnedRoot}. Run barretenberg/cpp/scripts/chonk_inputs.sh download first.`, + ); + } + flows = selectFlows(discoveredFlows); + if (flows.length === 0 && process.env.CHONK_PINNED_IVC_FLOW) { + throw new Error(`CHONK_PINNED_IVC_FLOW='${process.env.CHONK_PINNED_IVC_FLOW}' matched no pinned flows`); + } + if (flows.length === 0) { + throw new Error(`No pinned ivc-inputs.msgpack files found under ${pinnedRoot}`); + } + }, TEST_TIMEOUT_MS); + + it.each(backendCases)( + 'proves and verifies pinned flows with $label backend', + async backendCase => { + const barretenberg = await Barretenberg.initSingleton({ + backend: backendCase.backendType, + threads: backendCase.threads, + ...(backendCase.backendType === BackendType.NativeUnixSocket && bbPath ? { bbPath } : {}), + }); + + try { + for (const flowDir of backendCase.selectFlows(flows)) { + const { bytecodes, witnesses, vks, names } = loadPinnedFlow(flowDir); + const backend = new AztecClientBackend(bytecodes, barretenberg, names); + const { proof, vk } = await backend.prove(witnesses, vks); + const verified = await backend.verify(proof, vk); + expect(verified).toBe(true); + } + } finally { + await Barretenberg.destroySingleton(); + } + }, + TEST_TIMEOUT_MS, + ); +}); diff --git a/bootstrap.sh b/bootstrap.sh index 6b9c3f74b058..14fbebbf1f49 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -466,6 +466,7 @@ function bench { mkdir -p bench-out bench_merge cache_upload bench-$(git rev-parse HEAD^{tree}).tar.gz bench-out/bench.json + } ### RELEASING ########################################################################################################## @@ -653,6 +654,13 @@ case "$cmd" in build_and_test full bench ;; + "ci-chonk-input-update") + export CI=1 + export USE_TEST_CACHE=1 + export CI_FULL=0 + prep + barretenberg/cpp/bootstrap.sh chonk_input_update + ;; "ci-grind-test") export CI=1 export USE_TEST_CACHE=0 diff --git a/ci.sh b/ci.sh index 0a48e1b99ee1..c1230d142a9e 100755 --- a/ci.sh +++ b/ci.sh @@ -36,6 +36,7 @@ function print_usage { echo_cmd "network-tests-kind" "Spin up an EC2 instance to run a KIND-based spartan test." echo_cmd "deploy-rollup-upgrade" "Spin up an EC2 instance to deploy a rollup upgrade." echo_cmd "compat-e2e" "Spin up an EC2 instance and run backwards compat e2e tests." + echo_cmd "chonk-input-update" "Spin up an EC2 instance to update pinned Chonk IVC inputs and push the diff." echo_cmd "release" "Spin up an EC2 instance and run bootstrap release." echo_cmd "shell-new" "Spin up an EC2 instance, clone the repo, and drop into a shell." echo_cmd "shell" "Drop into a shell in the current running build instance container." @@ -127,6 +128,12 @@ case "$cmd" in export AWS_SHUTDOWN_TIME=75 bootstrap_ec2 "./bootstrap.sh ci-$cmd" ;; + chonk-input-update) + export CI_DASHBOARD="prs" + export JOB_ID="x-$cmd" + export AWS_SHUTDOWN_TIME=90 + bootstrap_ec2 "./bootstrap.sh ci-chonk-input-update" + ;; barretenberg-debug) export CI_DASHBOARD="nightly" export JOB_ID="x-$cmd" diff --git a/ci3/bootstrap_ec2 b/ci3/bootstrap_ec2 index 2735bf484649..117ee3e1e321 100755 --- a/ci3/bootstrap_ec2 +++ b/ci3/bootstrap_ec2 @@ -337,6 +337,10 @@ start_build() { -e BUILD_SYSTEM_DEBUG=${BUILD_SYSTEM_DEBUG:-} \ -e GITHUB_TOKEN=${GITHUB_TOKEN:-} \ -e GITHUB_REPOSITORY=${GITHUB_REPOSITORY:-aztecprotocol/aztec-packages} \ + -e PR_NUMBER=${PR_NUMBER:-} \ + -e PR_HEAD_REF=${PR_HEAD_REF:-} \ + -e PR_BASE_REF=${PR_BASE_REF:-} \ + -e CHONK_INPUTS_STATE_DIR=${CHONK_INPUTS_STATE_DIR:-} \ -e NETLIFY_SITE_ID=${NETLIFY_SITE_ID:-} \ -e NETLIFY_AUTH_TOKEN=${NETLIFY_AUTH_TOKEN:-} \ -e NPM_TOKEN=${NPM_TOKEN:-} \ diff --git a/docs/.codex b/docs/.codex new file mode 120000 index 000000000000..c8161850a43d --- /dev/null +++ b/docs/.codex @@ -0,0 +1 @@ +.claude \ No newline at end of file diff --git a/yarn-project/.codex b/yarn-project/.codex new file mode 120000 index 000000000000..c8161850a43d --- /dev/null +++ b/yarn-project/.codex @@ -0,0 +1 @@ +.claude \ No newline at end of file diff --git a/yarn-project/end-to-end/bootstrap.sh b/yarn-project/end-to-end/bootstrap.sh index e1f55e3090eb..e0ec4356830e 100755 --- a/yarn-project/end-to-end/bootstrap.sh +++ b/yarn-project/end-to-end/bootstrap.sh @@ -117,48 +117,35 @@ function bench_cmds { for client_flow in client_flows/bridging client_flows/deployments client_flows/amm client_flows/account_deployments client_flows/transfers client_flows/storage_proof; do echo "$hash:ISOLATE=1:CPUS=8:NAME=$client_flow BENCHMARK_CONFIG=key_flows LOG_LEVEL=error BENCH_OUTPUT=bench-out/ yarn-project/end-to-end/scripts/run_test.sh simple $client_flow" done - - for dir in $bench_fixtures_dir/*; do - for runtime in native wasm; do - echo "$hash:CPUS=8 barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh $runtime ../../yarn-project/end-to-end/$dir" - done - done - echo "$hash:ISOLATE=1:NET=1:CPUS=8 barretenberg/cpp/scripts/ci_benchmark_browser_memory.sh ../../yarn-project/end-to-end/example-app-ivc-inputs-out/ecdsar1+transfer_0_recursions+sponsored_fpc" - - # UltraHonk circuit benchmarks at different CPU counts (run serially for cache/bandwidth isolation) - for cpus in 8 16 32; do - echo "$hash:CPUS=$cpus:PARALLEL=0 barretenberg/cpp/scripts/ci_benchmark_ultrahonk_circuits.sh parity_base ../../yarn-project/end-to-end/$ultrahonk_bench_dir $cpus" - done } -# Builds all benchmark fixtures (chonk IVC captures + UltraHonk circuit inputs). -function build_bench { - rm -rf bench-out && mkdir -p bench-out - - # Build chonk IVC captures - export CAPTURE_IVC_FOLDER=$bench_fixtures_dir +# Live-capture Chonk IVC inputs from the e2e stack into $bench_fixtures_dir. +# Slow: used only when explicitly refreshing the pinned tarball. +function build_bench_capture { + export CAPTURE_IVC_FOLDER=${CAPTURE_IVC_FOLDER:-$bench_fixtures_dir} export BENCHMARK_CONFIG=key_flows export LOG_LEVEL=error export ENV_VARS_TO_INJECT="BENCHMARK_CONFIG CAPTURE_IVC_FOLDER LOG_LEVEL" - rm -rf $CAPTURE_IVC_FOLDER && mkdir -p $CAPTURE_IVC_FOLDER - if ! cache_download bb-chonk-captures-$hash.tar.gz; then - parallel --tag --line-buffer --halt now,fail=1 'docker_isolate "scripts/run_test.sh simple {}"' ::: \ - client_flows/account_deployments \ - client_flows/deployments \ - client_flows/bridging \ - client_flows/transfers \ - client_flows/amm \ - client_flows/storage_proof - cache_upload bb-chonk-captures-$hash.tar.gz $CAPTURE_IVC_FOLDER - fi + rm -rf "$CAPTURE_IVC_FOLDER" && mkdir -p "$CAPTURE_IVC_FOLDER" + parallel --tag --line-buffer --halt now,fail=1 'docker_isolate "scripts/run_test.sh simple {}"' ::: \ + client_flows/account_deployments \ + client_flows/deployments \ + client_flows/bridging \ + client_flows/transfers \ + client_flows/amm \ + client_flows/storage_proof +} + +# Builds benchmark fixtures that are still owned by yarn-project. +# Chonk benchmark inputs are pinned and managed by barretenberg/cpp. +function build_bench { + rm -rf bench-out && mkdir -p bench-out - # Build UltraHonk circuit benchmark inputs (bytecode + witness pairs) - rm -rf $ultrahonk_bench_dir && mkdir -p $ultrahonk_bench_dir - if ! cache_download bb-ultrahonk-bench-inputs-$hash.tar.gz; then - # Generate base parity circuit inputs (use absolute path since test runs from ivc-integration) - export BASE_PARITY_BENCH_DIR=$(pwd)/$ultrahonk_bench_dir + rm -rf "$ultrahonk_bench_dir" && mkdir -p "$ultrahonk_bench_dir" + if ! cache_download "bb-ultrahonk-bench-inputs-$hash.tar.gz"; then + export BASE_PARITY_BENCH_DIR="$(pwd)/$ultrahonk_bench_dir" yarn workspace @aztec/ivc-integration test src/base_parity_inputs.test.ts - cache_upload bb-ultrahonk-bench-inputs-$hash.tar.gz $ultrahonk_bench_dir + cache_upload "bb-ultrahonk-bench-inputs-$hash.tar.gz" "$ultrahonk_bench_dir" fi } diff --git a/yarn-project/ivc-integration/src/base_parity_inputs.test.ts b/yarn-project/ivc-integration/src/base_parity_inputs.test.ts index 8337eab76e34..523e12618bad 100644 --- a/yarn-project/ivc-integration/src/base_parity_inputs.test.ts +++ b/yarn-project/ivc-integration/src/base_parity_inputs.test.ts @@ -1,6 +1,6 @@ /** * Generates base parity circuit inputs (bytecode + witness) for UltraHonk benchmarks. - * Only runs when BASE_PARITY_BENCH_DIR env var is set (during build_bench). + * Only runs when BASE_PARITY_BENCH_DIR env var is set by the UltraHonk benchmark input generator. * * Run with: BASE_PARITY_BENCH_DIR=./bench-out yarn workspace @aztec/ivc-integration test src/base_parity_inputs.test.ts */ diff --git a/yarn-project/ivc-integration/src/batch_verifier.bench.test.ts b/yarn-project/ivc-integration/src/batch_verifier.bench.test.ts index 8282aa50b54e..9058f8288864 100644 --- a/yarn-project/ivc-integration/src/batch_verifier.bench.test.ts +++ b/yarn-project/ivc-integration/src/batch_verifier.bench.test.ts @@ -1,8 +1,7 @@ /** * Batch chonk verifier benchmarks using real protocol proofs. * - * Uses pre-generated IVC inputs from example-app-ivc-inputs-out (built by - * end-to-end/bootstrap.sh build_bench), proves a representative transaction, + * Uses pinned IVC inputs from example-app-ivc-inputs-out, proves a representative transaction, * then benchmarks batch verification throughput at various configurations. */ import { BatchChonkVerifier } from '@aztec/bb-prover'; @@ -11,8 +10,8 @@ import { ProtocolCircuitVks } from '@aztec/noir-protocol-circuits-types/server/v import { jest } from '@jest/globals'; import { execFile } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; import { dirname, resolve } from 'node:path'; import { promisify } from 'node:util'; @@ -21,8 +20,13 @@ import { corruptProofFields } from './batch_verifier_test_helpers.js'; const execFileAsync = promisify(execFile); const logger = createLogger('ivc-integration:bench:batch-verifier'); +const REPO_ROOT = resolve('../..'); const INPUTS_DIR = resolve('../end-to-end/example-app-ivc-inputs-out'); const BB_PATH = process.env.BB_BINARY_PATH ?? resolve('../../barretenberg/cpp/build/bin/bb'); +const CHONK_INPUTS_SCRIPT = resolve(REPO_ROOT, 'barretenberg/cpp/scripts/chonk_inputs.sh'); +const CHONK_INPUTS_HASH_FILE = resolve(REPO_ROOT, 'barretenberg/cpp/scripts/chonk-inputs.hash'); +const CHONK_INPUTS_MARKER_FILE = resolve(INPUTS_DIR, '.chonk-inputs.hash'); +const CHONK_INPUTS_STATE_DIR = resolve(REPO_ROOT, '.cache/chonk-inputs'); jest.setTimeout(1_200_000); // 20 min — proving is slow @@ -37,6 +41,20 @@ function proofToFields(proofBuf: Buffer): Uint8Array[] { return fields; } +async function ensurePinnedInputs(): Promise { + const expectedHash = readFileSync(CHONK_INPUTS_HASH_FILE, 'utf8').trim(); + const currentHash = existsSync(CHONK_INPUTS_MARKER_FILE) + ? readFileSync(CHONK_INPUTS_MARKER_FILE, 'utf8').trim() + : undefined; + + if (currentHash === expectedHash) { + return; + } + + logger.info(`Downloading pinned Chonk inputs ${expectedHash}...`); + await execFileAsync(CHONK_INPUTS_SCRIPT, ['download'], { cwd: REPO_ROOT, timeout: 180_000 }); +} + describe('Batch Chonk Verifier Benchmarks (Real Proofs)', () => { let validProofFields: Uint8Array[]; let invalidProofFields: Uint8Array[]; @@ -45,7 +63,9 @@ describe('Batch Chonk Verifier Benchmarks (Real Proofs)', () => { const benchResults: BenchEntry[] = []; beforeAll(async () => { - // Use pre-generated IVC inputs from example-app-ivc-inputs-out + await ensurePinnedInputs(); + + // Use pinned IVC inputs from example-app-ivc-inputs-out logger.info(`Using local IVC inputs from ${INPUTS_DIR}...`); // Pick the largest flow for a realistic proof @@ -56,7 +76,7 @@ describe('Batch Chonk Verifier Benchmarks (Real Proofs)', () => { // Use a transfer flow (representative of typical txs) const flow = flowDirs.find(f => f.includes('transfer_0_recursions+sponsored')) ?? flowDirs[0]; const ivcInputsPath = resolve(INPUTS_DIR, flow, 'ivc-inputs.msgpack'); - proofDir = resolve(tmpdir(), `bb-bench-proof-${Date.now()}`); + proofDir = resolve(CHONK_INPUTS_STATE_DIR, `batch-verifier-proof-${Date.now()}`); await mkdir(proofDir, { recursive: true }); // Prove the flow