Protocol-Circuit Gate Counts & Variant Regeneration
When to Use
Use this skill when asked to:
- Compile protocol circuits (private kernels, rollup circuits, etc.) with
nargo - Regenerate circuit variants (e.g. after adding
init-N/inner-N/ reset crates) - Measure gate counts / circuit sizes with
bb gates, for either:--scheme chonk— client-IVC circuits (private kernels)--scheme ultra_honk— standalone UltraHonk circuits (rollup circuits)
Key Paths
All paths relative to the git root.
- Protocol circuits workspace:
noir-projects/fnd/noir-protocol-circuits/ - Circuit crates:
noir-projects/fnd/noir-protocol-circuits/crates/<crate-name>/ - Variant generator:
noir-projects/fnd/noir-protocol-circuits/scripts/generate_variants.js - Workspace manifest (generated):
noir-projects/fnd/noir-protocol-circuits/Nargo.toml - Workspace template (source of truth):
noir-projects/fnd/noir-protocol-circuits/Nargo.template.toml nargobinary:noir/noir-repo/target/release/nargo— do NOT use a globally installed nargo; version mismatches produce opaque bytecode failures.bbbinary:barretenberg/cpp/build/bin/bbbb-avmbinary:barretenberg/cpp/build/bin/bb-avm— required for circuits that recurse the AVM (see Gotchas).- Compiled artifacts:
noir-projects/fnd/noir-protocol-circuits/target/<package_name>.json
Step 1 — Regenerate Variants (only when crates were added/removed)
generate_variants.js regenerates the reset-circuit variants under
crates/autogenerated/ AND rewrites the workspace Nargo.toml from
Nargo.template.toml. Run it after adding or removing crates so the new crates
are actually included in the workspace — otherwise nargo won't see them.
cd noir-projects/fnd/noir-protocol-circuits
node scripts/generate_variants.js
Verify the crates you expect are now listed, e.g.:
grep -nE "inner|init" Nargo.toml
Note: numbered kernel variants (init-N / inner-N) are hand-written crates
committed under crates/, not auto-generated. The generator only (re)writes
Nargo.toml and the crates/autogenerated/ reset variants. Running node
before compiling newly-added crates is needed purely to refresh Nargo.toml
from the template. If no crates changed, you can skip this step.
Step 2 — Compile
Package names use underscores, not dashes. A directory like
crates/private-kernel-inner-N has package name private_kernel_inner_N
(check the name = field in <crate>/Nargo.toml). Passing the dashed form to
--package fails with "invalid character '-' in package name".
cd noir-projects/fnd/noir-protocol-circuits
NARGO=../../noir/noir-repo/target/release/nargo
$NARGO compile --package <package_name> --silence-warnings
Discover package names for a family of crates automatically:
for d in crates/private-kernel-init crates/private-kernel-init-* \
crates/private-kernel-inner crates/private-kernel-inner-*; do
[ -d "$d" ] && grep -h '^name' "$d/Nargo.toml"
done
The *_simulated crates are unconstrained witness-generation variants, NOT
proving circuits — exclude them from gate counting unless explicitly asked.
Step 3 — Gate Counts
Pass the compiled .json artifact as the bytecode path:
bb gates --scheme <chonk|ultra_honk> -b target/<package_name>.json
Reading the result (works for both schemes): bb gates prints a JSON
object to stdout with the gate count in functions[].circuit_size
(acir_opcodes is the ACIR opcode count):
{"functions": [
{
"acir_opcodes": 8653,
"circuit_size": 32209
}
]}
Extract it robustly with jq (ignores any warnings on stderr):
bb gates --scheme chonk -b target/private_kernel_init.json 2>/dev/null \
| jq '.functions[].circuit_size'
The chonk scheme additionally prints a human-readable line to stderr
(capture with 2>&1, not 2>/dev/null):
ChonkStats - circuit: ivc_circuit, acir_opcodes: 8653, circuit_size: 32209 (mem: 30.22 MiB)
Example A — chonk circuit: private kernel init
cd noir-projects/fnd/noir-protocol-circuits
NARGO=../../noir/noir-repo/target/release/nargo
BB=../../barretenberg/cpp/build/bin/bb
$NARGO compile --package private_kernel_init --silence-warnings
$BB gates --scheme chonk -b target/private_kernel_init.json 2>/dev/null \
| jq '.functions[].circuit_size'
Example B — ultra_honk circuit: private base rollup
Rollup circuits recursively verify proofs that carry IPA (the rollup uses IPA
for the inner proofs), so you must pass --verifier_target noir-rollup.
Without it the construction aborts with IPA proofs present when not expected.
cd noir-projects/fnd/noir-protocol-circuits
NARGO=../../noir/noir-repo/target/release/nargo
BB=../../barretenberg/cpp/build/bin/bb
$NARGO compile --package rollup_tx_base_private --silence-warnings
$BB gates --scheme ultra_honk -t noir-rollup -b target/rollup_tx_base_private.json 2>/dev/null \
| jq '.functions[].circuit_size'
Recursive circuits print warnings during construction (e.g.
ChonkRecursiveVerifier: Databus Consistency check failure,
bigfield: remainder not zero!). These are expected when no real proof inputs
are supplied — bb gates only builds the constraint system to count gates, so
the consistency checks have nothing to satisfy. The circuit_size on stdout is
still valid. Discard stderr (2>/dev/null) to keep output clean.
bb gates reference
-b, --bytecode_path— path to the compiled.jsonartifact.-s, --scheme—{chonk, avm, ultra_honk}. Usechonkfor client-IVC kernels,ultra_honkfor standalone circuits like the rollup circuits.-t, --verifier_target— hash/ZK settings. Relevant values:noir-recursive(poseidon2, ZK; default-ish for recursive noir circuits),noir-rollup(IPA + poseidon2, ZK — required for rollup circuits), plusevm,starknet, and-no-zkvariants of each.--include_gates_per_opcode— adds a per-opcode breakdown.
Proof-Size Assertion Failures
bb gates on a recursive circuit may abort with a proof-length assertion, e.g.:
create_honk_recursion_constraints: ACIR proof size mismatch. Actual: 480, Expected: 481
Assertion failed: (proof_size == ChonkProof::PROOF_LENGTH_WITHOUT_PUB_INPUTS)
Actual : 1243
Expected: 1244
The first is the Honk recursion path (e.g. rollup_tx_merge, which recurses
child rollup Honk proofs); the second is the Chonk recursion path (e.g.
rollup_tx_base_private, which recurses the tx/kernel Chonk proof). In both
cases the constraint system encodes one proof length while bb expects
another. The proof length must agree across three places:
- barretenberg C++ — the
bbbinary. - the noir stdlib — the pinned
noir/noir-reposubmodule, baked into the compiled circuit bytecode atnargo compiletime. - the TS constants in
labs/yarn-project/constants/src/constants.gen.ts—RECURSIVE_ROLLUP_HONK_PROOF_LENGTH/NESTED_RECURSIVE_ROLLUP_HONK_PROOF_LENGTH(Honk, e.g. 480) andCHONK_PROOF_LENGTH(e.g. 1271). These fill the fake recursive-proof arrays in the committedProver.tomlfixtures.
There are two causes. Diagnose by comparing the assertion's Actual (what the
bytecode/Prover.toml encodes) against Expected (what bb wants).
Cause 1 — stale bb binary (try this first)
Common when proof-format changes land in bb before the noir submodule is synced
to match. The on-disk bb may have been built from a commit with a different
proof length. Rebuild it:
cd barretenberg/cpp
cmake --build build --target bb # add: --target bb-avm for AVM-recursing circuits
Then retry bb gates.
Cause 2 — stale Prover.toml fixtures
The committed Prover.toml files carry fake recursive proofs whose length is
frozen at the value present when they were last generated. After a proof-length
change they must be regenerated (or hacked — see below). Two commands cover all
protocol circuits, split by whether the sample needs a real client-proved
transaction.
Both commands below run from labs/yarn-project, the TypeScript node's git
submodule; see the update-prover-toml skill for the directory the captured
tomls actually land in, which is not this repository.
Block-root and above rollup circuits — prover-client suite (simulated orchestrator, no L1 sandbox):
AZTEC_GENERATE_TEST_DATA=1 \
yarn workspace @aztec-labs/prover-client test regenerate_rollup_sample_inputs
Regenerates the Prover.toml of every block-root variant plus the block-merge,
checkpoint-root, checkpoint-merge, tx-merge and root circuits. Read the exact
inventory off the dump fields of the scenarios array in
labs/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts
rather than from a list here: each scenario declares which circuits it is
responsible for, and the set changes whenever a block-root variant is added or
removed.
Private-kernel + transaction-base circuits — e2e prover full test (spins up an L1/anvil sandbox):
AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 \
yarn workspace @aztec-labs/end-to-end test:e2e single-node/prover/server/full.test
Regenerates Prover.toml for: private-kernel-init and its private-kernel-init-N
variants, private-kernel-inner and its private-kernel-inner-N variants,
private-kernel-reset-tail, private-kernel-reset-tail-to-public,
rollup-tx-base-private, rollup-tx-base-public. These need real client-proved
transactions the simulated orchestrator cannot produce. (private-kernel-reset,
the inner reset, is commented out and hand-maintained — not regenerated.)
The circuit lists live in the updateProtocolCircuitSampleInputs(...) loops in
labs/yarn-project/end-to-end/src/single-node/prover/server/full.test.ts and the
scenarios array in
labs/yarn-project/prover-client/src/test/regenerate_rollup_sample_inputs.test.ts;
re-read them if a circuit seems missing (entries get commented in/out).
Build prerequisites for regeneration
The fake-proof lengths come from @aztec-labs/constants, so the TS build must be
current after any proof-length change — otherwise regeneration re-emits the
old length. Build in dependency order (simplest: make labs-yarn-project from
the git root, which does bb → noir → l1-contracts → protocol circuits → the
submodule's yarn-project):
- bb / bb-avm native at
barretenberg/cpp/build/bin/{bb,bb-avm}. The prover-client test looks forbb-avmand the e2e test forbb; both fall back to WASM if absent, but you want a current native build. - noir-execute at
noir/noir-repo/target/release/noir-execute(witness generation; WASM fallback if absent). - Protocol circuits compiled under
noir-projects/fnd/noir-protocol-circuits(artifacts are staged into@aztec-foundation/protocol-circuits-artifacts, which@aztec-labs/noir-protocol-circuits-typesconsumes). - TS build current, especially
@aztec-labs/constants(regenerated fromnoir-projects/.../constants.nr), then@aztec-labs/noir-protocol-circuits-types,@aztec-labs/bb-prover,@aztec-labs/simulator, and the test's own package. - The e2e command additionally needs
l1-contractsbuilt (it runs anvil).
After regenerating, recompile the affected circuit (Step 2) and re-run
bb gates.
Hack path — trim/extend the proof field by hand
(Note: bb gates reads only the compiled bytecode, not Prover.toml, so a
stale fixture cannot break gate counting — that's purely Cause 1. The
Prover.toml length only bites when you actually prove the circuit, e.g. via
the prover-client/e2e tests above or bb prove.)
If you just need to prove the circuit once and don't want to stand up the full
regeneration pipeline, edit the affected Prover.toml directly instead: the
assertion tells you the delta (Actual vs Expected). Trim or pad the
fake-proof array(s) in that file to the expected length — drop the extra
trailing elements, or append zero entries ("0x00") — so the field matches what
bb wants. The proof values are dummy under FAKE_PROOFS, so only the array
length matters. This is a stopgap; the proper fix is regeneration.
Gotchas
- Dashed dir name vs underscored package name —
--package private_kernel_inner_N, notprivate-kernel-inner-N. - Gate count is
functions[].circuit_sizein the stdout JSON for both schemes;chonkalso prints aChonkStatsline to stderr. - Rollup circuits need
-t noir-rollup(IPA), or construction aborts withIPA proofs present when not expected. - AVM-recursing circuits need
bb-avm, notbb. The public base rollup (rollup_tx_base_public) recursively verifies the AVM and fails under plainbbwithAVM recursion is not supported in this build. Please use the 'bb-avm' binary. Usebarretenberg/cpp/build/bin/bb-avmfor those. The private base rollup (rollup_tx_base_private) does not recurse the AVM and works with plainbb. - Run
node scripts/generate_variants.jsbefore compiling newly-added crates, or the workspaceNargo.tomlwon't include them. - Always use the repo-local
nargoatnoir/noir-repo/target/release/nargo. - Warnings on stderr from recursive circuits are expected; the stdout
circuit_sizeis still authoritative.