# Kernel Gate Counts

> Compile noir-protocol-circuits (kernels, rollup, etc.) and compute their gate counts with `bb gates`, for both the chonk (client IVC) and ultra_honk schemes. Use when asked to regenerate circuit variants, compile protocol circuits, or measure gate/circuit sizes.

- Skill: `aztecprotocol/kernel-gate-counts` (Agent Skill)
- Install (CLI): `npx skillmds@latest add aztecprotocol/kernel-gate-counts`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aztecprotocol/kernel-gate-counts/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: aztecprotocol (https://skillmd.com/u/aztecprotocol)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aztecprotocol/kernel-gate-counts

---


# 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`
- `nargo` binary: `noir/noir-repo/target/release/nargo` — do NOT use a globally
  installed nargo; version mismatches produce opaque bytecode failures.
- `bb` binary: `barretenberg/cpp/build/bin/bb`
- `bb-avm` binary: `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.

```bash
cd noir-projects/fnd/noir-protocol-circuits
node scripts/generate_variants.js
```

Verify the crates you expect are now listed, e.g.:

```bash
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".

```bash
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:

```bash
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:

```bash
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):

```json
{"functions": [
  {
        "acir_opcodes": 8653,
        "circuit_size": 32209
  }
]}
```

Extract it robustly with `jq` (ignores any warnings on stderr):

```bash
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

```bash
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`.

```bash
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 `.json` artifact.
- `-s, --scheme` — `{chonk, avm, ultra_honk}`. Use `chonk` for client-IVC
  kernels, `ultra_honk` for 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),
  plus `evm`, `starknet`, and `-no-zk` variants 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:

1. barretenberg C++ — the `bb` binary.
2. the noir stdlib — the pinned `noir/noir-repo` submodule, baked into the
   compiled circuit bytecode at `nargo compile` time.
3. 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) and `CHONK_PROOF_LENGTH` (e.g. 1271). These fill the
   **fake recursive-proof arrays** in the committed `Prover.toml` fixtures.

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:

```bash
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):

```bash
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):

```bash
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 for `bb-avm` and the e2e test for `bb`; 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-types` consumes).
- **TS build current**, especially `@aztec-labs/constants` (regenerated from
  `noir-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-contracts` built (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`,
  not `private-kernel-inner-N`.
- **Gate count is `functions[].circuit_size` in the stdout JSON** for both
  schemes; `chonk` also prints a `ChonkStats` line to stderr.
- **Rollup circuits need `-t noir-rollup`** (IPA), or construction aborts with
  `IPA proofs present when not expected`.
- **AVM-recursing circuits need `bb-avm`, not `bb`.** The public base rollup
  (`rollup_tx_base_public`) recursively verifies the AVM and fails under plain
  `bb` with `AVM recursion is not supported in this build. Please use the
  'bb-avm' binary`. Use `barretenberg/cpp/build/bin/bb-avm` for those. The
  private base rollup (`rollup_tx_base_private`) does not recurse the AVM and
  works with plain `bb`.
- **Run `node scripts/generate_variants.js` before compiling newly-added
  crates**, or the workspace `Nargo.toml` won't include them.
- **Always use the repo-local `nargo`** at `noir/noir-repo/target/release/nargo`.
- Warnings on stderr from recursive circuits are expected; the stdout
  `circuit_size` is still authoritative.

