Skip to content

feat: multi-app kernel circuits#23076

Merged
ledwards2225 merged 30 commits into
merge-train/barretenbergfrom
lde/integrate-init-3
May 11, 2026
Merged

feat: multi-app kernel circuits#23076
ledwards2225 merged 30 commits into
merge-train/barretenbergfrom
lde/integrate-init-3

Conversation

@ledwards2225
Copy link
Copy Markdown
Contributor

@ledwards2225 ledwards2225 commented May 8, 2026

Integrates init/inner kernels that process up to 3 apps into the PXE

@ledwards2225 ledwards2225 marked this pull request as ready for review May 8, 2026 03:36
@ledwards2225 ledwards2225 requested a review from LeilaWang as a code owner May 8, 2026 03:36
ledwards2225 and others added 15 commits May 8, 2026 19:43
…accept it as a previous kernel

Introduces VK tree index 65 for the batched init_3 kernel and adds it to
the ALLOWED_PREVIOUS_CIRCUITS lists for inner, reset, tail, and
tail-to-public so an init_3 kernel may precede them. Index 65 is
appended after the 23..64 reset variants; comment in constants.nr
updated to reserve indices 24..64 for reset and reopen 65+ for new
non-reset kernel circuits.

Regenerated TS-side constants via `yarn remake-constants`.
Most downstream scripts (e.g. noir-protocol-circuits/bootstrap.sh) call
find-bb, which returns bb-avm by default. Rebuilding only the non-AVM
`bb` target therefore leaves a stale bb-avm and downstream bootstraps
keep failing with the same error after the supposed fix.
…nterface methods

Concrete inputs class for the batched init_3 kernel (three PrivateCallData
fields), exported from @aztec/stdlib/kernel. PrivateKernelProver gains
generateInit3Output / simulateInit3 mirroring the existing init pair.
Generic over N is deferred until a second variant lands.
…ct and VK

Adds PrivateKernelInit3Artifact to the ClientProtocolArtifact union and
to the artifact / VK / VK-index registries. The simulated lookup reuses
the constrained init_3 artifact, since no private-kernel-init-3-simulated
crate exists.

The dynamic-import generator gains a small additive pass that emits
fallback cases mapping <name>_simulated to the constrained <name>.json
for any artifact in artifactsWithoutSimulatedVersions, so lazy and
bundle providers stay in sync (the bundle provider has reused the
constrained artifact for hiding kernels' simulated slot all along).

Adds 'private-kernel-init-3' to ClientCircuitName for stats plumbing.
Adds private_kernel_init_3 to the codegen list so the Noir ABI type
flows through.

Generated files (client_artifacts_helper.ts, types/index.ts, vk_tree.ts)
are gitignored and produced by `yarn generate`.
Adds witness-map converters for the batched private_kernel_init_3
circuit and wires them into BBPrivateKernelProver. Mirrors the existing
init pair; the input mapper matches the Noir ABI for
private_kernel_init_3/main.nr (three private_call_N + three
app_public_inputs_N fields).
When PXE_USE_INIT_3=1 is set and the tx has at least three private app
calls, the first kernel iteration verifies three apps in a single
batched private_kernel_init_3 circuit instead of one private_kernel_init
followed by inners. Saves N-1 prev-kernel HN verifications across the
chain. Falls back to the standard init for txs with fewer than three
calls so the flag can be left on permanently.

Refactors the loop's pop-stack / push-nested / record-step /
build-call-data sequence into a consumeNextApp helper used at every
app-consumption site (top of loop + the two follow-up apps in the
init_3 branch). Total app count is computed once via the existing
collectNested helper.

Leaves a WORKTODO(luke) for the case where init_3 consumed every app
in the tx and the chain can skip directly to reset+tail without an
inner kernel chain.
scripts/reproduce_init3_canary.sh runs a real client_flow with
PXE_USE_INIT_3=1, captures the IVC inputs, proves them with bb (Chonk),
and verifies the proof. Asserts the capture references
PrivateKernelInit3Artifact so a regression in the orchestrator's flag
plumbing is caught at capture time.

Gates the captureProfile expectedSteps check on SKIP_STEP_COUNT_CHECK=1
so the test doesn't abort before writing the msgpack when kernel
batching collapses steps.
@ledwards2225 ledwards2225 force-pushed the lde/integrate-init-3 branch from e6d8db4 to 755b955 Compare May 8, 2026 21:26
@ledwards2225 ledwards2225 added the ci-full Run all master checks. label May 8, 2026
The Private-Kernel-Sequencer suite asserts call counts on
proofCreator.simulateInit / .simulateInner with the single-app dispatch
shape (e.g. expect(simulateInit).toHaveBeenCalledTimes(1) and
expect(simulateInner).toHaveBeenCalledTimes(3) for nested calls).

With the new default PXE_KERNEL_BATCH_SIZE=3 the planner routes through
simulateInit2 / .simulateInit3 / .simulateInner2 / .simulateInner3, none
of which have mock return values configured here, so the orchestrator
crashes reading bytecode off undefined. Multi-app dispatch is
integration-tested via the bench captures; this unit suite is about the
single-app orchestration path, so pin the env var locally and restore
it on teardown.
Multi-app kernel batching added 4 new stdlib classes
(PrivateKernelInit{2,3}CircuitPrivateInputs,
PrivateKernelInner{2,3}CircuitPrivateInputs), 4 corresponding
witness-map converters in noir-protocol-circuits-types, and the
BatchPlanner orchestrator helper. Main entrypoint grew from ~1733 KB
to 1760 KB, exceeding the 1750 KB hard limit by 10.72 KB.

Bump to 1800 KB to clear the limit and leave ~40 KB headroom for the
inevitable init_4 / inner_4 work.
@ledwards2225 ledwards2225 requested a review from Thunkar as a code owner May 8, 2026 23:36
The test asserts that a `PrivateKernelInit_main` recording exists and
that its content has `circuitName: 'PrivateKernelInit'`. With the new
default batch size of 3 the orchestrator routes the first kernel
through `private_kernel_init_3` instead, producing a
`PrivateKernelInit3_main` recording — so the lookup returns undefined.

The test is about the recorder mechanism, not kernel batching, so pin
the batch size to 1 for the duration of the test and restore on
teardown.
@ledwards2225 ledwards2225 changed the title feat: init_3 kernel feat: multi-app kernel circuits May 11, 2026
// kernel). With the default batch size of 3 the orchestrator picks `private_kernel_init_3`
// instead. This test is about the recorder mechanism, not kernel batching, so pin the batch
// size to 1 for the duration of the test.
const originalBatchSize = process.env.PXE_KERNEL_BATCH_SIZE;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be fine to run this test with the default batching and redo the assertions, they're not that exhaustive anyways

// Multi-app behaviour is integration-tested via the bench capture flows.
const originalBatchSize = process.env.PXE_KERNEL_BATCH_SIZE;
beforeAll(async () => {
process.env.PXE_KERNEL_BATCH_SIZE = '1';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably test multiple batch sizes here.

@Thunkar
Copy link
Copy Markdown
Contributor

Thunkar commented May 11, 2026

Just amazing, I'm so eager to try this!

A nit though: I'd prefer if PXE_KERNEL_BATCH_SIZE was wired in PXE's config rather than being a plain env var. In browser environments process.env has to be mocked and is a lot better to configure the wallet/pxe explicitly at init time

federicobarbacovi and others added 8 commits May 11, 2026 12:05
Cherry-pick of #23092 onto merge-train/fairies. Same flake (`verifies
transactions at 10 TPS` in `tx_stats_bench.test.ts:268`) is now blocking
PRs based on this train; see #23092 for the full analysis (bb.js
NativeUnixSocket backend under 8x parallel IVC verifications
intermittently returning `valid:false`).

Skipping at sub-test granularity keeps the other three serial sub-tests
emitting their compression and single-tx verification metrics; only the
IVC-verifier-under-concurrency metrics are dropped. The
`.test_patterns.yml` flake-retry entry from #23083 stays in place.
pub global PARITY_ROOT_VK_INDEX: u32 = 22;
pub global PRIVATE_KERNEL_RESET_VK_INDEX: u32 = 23;
// Important: Do not define indexes after the PRIVATE_KERNEL_RESET_VK_INDEX. They are allocated for the variants of private kernel reset.
// Important: Do not define indexes between 24 and 64. They are allocated for the variants of private kernel reset. Add new non-reset kernel indices at 65+.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should've done this before. In generate_vk_tree.ts, can we add some checks:

const vkHashes = new Array(2 ** VK_TREE_HEIGHT).fill(Buffer.alloc(32));
  for (const [key, value] of Object.entries(allVks)) {
    const index = ProtocolCircuitVkIndexes[key as ProtocolArtifact];
    if (index >= vkHashes.length) { // <- new
      throw new Error(`VK index ${index} is out of bounds. VK tree size: ${vkHashes.length}`);
    }
    if (!vkHashes[index].equals(Buffer.alloc(32))) { // <- new
      throw new Error(`VK hash for ${key} already exists at index ${index}`);
    }
    vkHashes[index] = value.keyAsFields.hash.toBuffer();
  }

Just in case we have more reset circuits one day and the last one's index is larger than 64.

Copy link
Copy Markdown
Contributor

@federicobarbacovi federicobarbacovi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed via active collaboration

@ledwards2225 ledwards2225 merged commit f5e77a9 into merge-train/barretenberg May 11, 2026
14 checks passed
@ledwards2225 ledwards2225 deleted the lde/integrate-init-3 branch May 11, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-full Run all master checks.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants