Skip to content

Commit

Permalink
feat: browser chunking (#11102)
Browse files Browse the repository at this point in the history
Further modularises `noir-protocol-circuits-types`, exposing a
`client/lazy` API that allow browser bundles to not import every single
JSON artifact at once, but rather pull them on demand. `/server` and
`/vks` entrypoints are also provided to aid in startup CLI times, jest
cache and general cleanup.

This is very important due to the sheer amount of different reset
variants we currently have. However, a regular `/client/bundle` is still
exposed in case dynamic imports are not supported in the target
platform.

This takes our bundle sizes in the example vite box (with PXE in the
browser and proving) from 50MB (30 compressed) to ~3MB before meaningful
content is displayed. After that, `bb.js` is an additional 8-9 and each
circuit hovers around 0.3-1, but only the ones used in the transactions
are pulled when required.

Also removed the `TestPrivateKernelProver`, and replaced it by each of
our prover implementations providing a cleaner simulation vs.
witgen+proving interface. This way, the prover is now 100% a PXE plugin
(which will allow us to even delegate proving!)
  • Loading branch information
Thunkar authored Jan 9, 2025
1 parent 23e642f commit 393e843
Show file tree
Hide file tree
Showing 69 changed files with 870 additions and 973 deletions.
5 changes: 3 additions & 2 deletions boxes/boxes/vite/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { KeyStore } from "@aztec/key-store";
import { PrivateKernelProver } from "@aztec/circuit-types";
import { L2TipsStore } from "@aztec/kv-store/stores";
import { createStore } from "@aztec/kv-store/indexeddb";
import { BBWasmPrivateKernelProver } from "@aztec/bb-prover/wasm";
import { BBWASMLazyPrivateKernelProver } from "@aztec/bb-prover/wasm/lazy";

process.env = Object.keys(import.meta.env).reduce((acc, key) => {
acc[key.replace("VITE_", "")] = import.meta.env[key];
Expand All @@ -37,8 +37,9 @@ export class PrivateEnv {
async init() {
const config = getPXEServiceConfig();
config.dataDirectory = "pxe";
config.proverEnabled = true;
const aztecNode = await createAztecNodeClient(this.nodeURL);
const proofCreator = new BBWasmPrivateKernelProver(16);
const proofCreator = new BBWASMLazyPrivateKernelProver(16);
this.pxe = await this.createPXEService(aztecNode, config, proofCreator);
const encryptionPrivateKey = deriveMasterIncomingViewingSecretKey(
this.secretKey,
Expand Down
19 changes: 18 additions & 1 deletion boxes/boxes/vite/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineConfig } from "vite";
import { defineConfig, searchForWorkspaceRoot } from "vite";
import react from "@vitejs/plugin-react-swc";
import { PolyfillOptions, nodePolyfills } from "vite-plugin-node-polyfills";
import topLevelAwait from "vite-plugin-top-level-await";
Expand Down Expand Up @@ -28,12 +28,29 @@ export default defineConfig({
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp",
},
fs: {
allow: [
searchForWorkspaceRoot(process.cwd()),
"../../../yarn-project/noir-protocol-circuits-types/artifacts",
],
},
},
plugins: [
react(),
nodePolyfillsFix({ include: ["buffer", "process", "path"] }),
topLevelAwait(),
],
build: {
rollupOptions: {
output: {
manualChunks(id: string) {
if (id.includes("bb-prover")) {
return "@aztec/bb-prover";
}
},
},
},
},
optimizeDeps: {
exclude: ["@noir-lang/acvm_js", "@noir-lang/noirc_abi", "@aztec/bb-prover"],
},
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ builder/src/crs
builder/src/types
noir-protocol-circuits-types/artifacts
noir-protocol-circuits-types/src/private_kernel_reset_data.ts
noir-protocol-circuits-types/src/private_kernel_reset_vks.ts
noir-protocol-circuits-types/src/private_kernel_reset_types.ts
noir-protocol-circuits-types/src/client_artifacts_helper.ts
noir-protocol-circuits-types/src/types/
ivc-integration/artifacts
ivc-integration/src/types/
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec/src/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
getL1ContractsConfigEnvVars,
} from '@aztec/ethereum';
import { createLogger } from '@aztec/foundation/log';
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types';
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vks';
import { ProtocolContractAddress, protocolContractTreeRoot } from '@aztec/protocol-contracts';
import { type PXEServiceConfig, createPXEService, getPXEServiceConfig } from '@aztec/pxe';
import { type TelemetryClient } from '@aztec/telemetry-client';
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/bb-prover/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"type": "module",
"exports": {
".": "./dest/index.js",
"./wasm": "./dest/wasm/index.js",
"./wasm/bundle": "./dest/wasm/bundle.js",
"./wasm/lazy": "./dest/wasm/lazy.js",
"./prover": "./dest/prover/index.js",
"./verifier": "./dest/verifier/index.js",
"./test": "./dest/test/index.js",
Expand Down
10 changes: 9 additions & 1 deletion yarn-project/bb-prover/src/bb/cli.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { type LogFn } from '@aztec/foundation/log';
import { type ProtocolArtifact, ProtocolCircuitArtifacts } from '@aztec/noir-protocol-circuits-types';
import { ClientCircuitArtifacts } from '@aztec/noir-protocol-circuits-types/client/bundle';
import { ServerCircuitArtifacts } from '@aztec/noir-protocol-circuits-types/server';
import { type ProtocolArtifact } from '@aztec/noir-protocol-circuits-types/types';
import { type NoirCompiledCircuit } from '@aztec/types/noir';

import { Command } from 'commander';
import { promises as fs } from 'fs';
Expand All @@ -8,6 +11,11 @@ import { generateContractForCircuit, generateKeyForNoirCircuit } from './execute

const { BB_WORKING_DIRECTORY, BB_BINARY_PATH } = process.env;

export const ProtocolCircuitArtifacts: Record<ProtocolArtifact, NoirCompiledCircuit> = {
...ClientCircuitArtifacts,
...ServerCircuitArtifacts,
};

/**
* Returns commander program that defines the CLI.
* @param log - Console logger.
Expand Down
32 changes: 16 additions & 16 deletions yarn-project/bb-prover/src/honk.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
import { type ProtocolArtifact } from '@aztec/noir-protocol-circuits-types';
import { type ServerProtocolArtifact } from '@aztec/noir-protocol-circuits-types/server';

export type UltraHonkFlavor = 'ultra_honk' | 'ultra_keccak_honk' | 'ultra_rollup_honk';

const UltraKeccakHonkCircuits = ['RootRollupArtifact'] as const satisfies ProtocolArtifact[];
const UltraKeccakHonkCircuits = ['RootRollupArtifact'] as const satisfies ServerProtocolArtifact[];
const UltraHonkCircuits = [
// 'EmptyNestedArtifact',
// 'PrivateKernelEmptyArtifact',
'BaseParityArtifact',
'RootParityArtifact',
] as const satisfies ProtocolArtifact[];
] as const satisfies ServerProtocolArtifact[];

export type UltraKeccakHonkProtocolArtifact = (typeof UltraKeccakHonkCircuits)[number];
export type UltraHonkProtocolArtifact = (typeof UltraHonkCircuits)[number];
export type UltraRollupHonkProtocolArtifact = Exclude<
Exclude<ProtocolArtifact, UltraKeccakHonkProtocolArtifact>,
UltraHonkProtocolArtifact
export type UltraKeccakHonkServerProtocolArtifact = (typeof UltraKeccakHonkCircuits)[number];
export type UltraHonkServerProtocolArtifact = (typeof UltraHonkCircuits)[number];
export type UltraRollupHonkServerProtocolArtifact = Exclude<
Exclude<ServerProtocolArtifact, UltraKeccakHonkServerProtocolArtifact>,
UltraHonkServerProtocolArtifact
>;

export function getUltraHonkFlavorForCircuit(artifact: UltraKeccakHonkProtocolArtifact): 'ultra_keccak_honk';
export function getUltraHonkFlavorForCircuit(artifact: UltraHonkProtocolArtifact): 'ultra_honk';
export function getUltraHonkFlavorForCircuit(artifact: UltraRollupHonkProtocolArtifact): 'ultra_rollup_honk';
export function getUltraHonkFlavorForCircuit(artifact: ProtocolArtifact): UltraHonkFlavor;
export function getUltraHonkFlavorForCircuit(artifact: ProtocolArtifact): UltraHonkFlavor {
export function getUltraHonkFlavorForCircuit(artifact: UltraKeccakHonkServerProtocolArtifact): 'ultra_keccak_honk';
export function getUltraHonkFlavorForCircuit(artifact: UltraHonkServerProtocolArtifact): 'ultra_honk';
export function getUltraHonkFlavorForCircuit(artifact: UltraRollupHonkServerProtocolArtifact): 'ultra_rollup_honk';
export function getUltraHonkFlavorForCircuit(artifact: ServerProtocolArtifact): UltraHonkFlavor;
export function getUltraHonkFlavorForCircuit(artifact: ServerProtocolArtifact): UltraHonkFlavor {
if (isUltraKeccakHonkCircuit(artifact)) {
return 'ultra_keccak_honk';
} else if (UltraHonkCircuits.includes(artifact as UltraHonkProtocolArtifact)) {
} else if (UltraHonkCircuits.includes(artifact as UltraHonkServerProtocolArtifact)) {
return 'ultra_honk';
}
return 'ultra_rollup_honk';
}

function isUltraKeccakHonkCircuit(artifact: ProtocolArtifact): artifact is UltraKeccakHonkProtocolArtifact {
return UltraKeccakHonkCircuits.includes(artifact as UltraKeccakHonkProtocolArtifact);
function isUltraKeccakHonkCircuit(artifact: ServerProtocolArtifact): artifact is UltraKeccakHonkServerProtocolArtifact {
return UltraKeccakHonkCircuits.includes(artifact as UltraKeccakHonkServerProtocolArtifact);
}
110 changes: 110 additions & 0 deletions yarn-project/bb-prover/src/prover/bb_native_private_kernel_prover.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { type ClientIvcProof } from '@aztec/circuits.js';
import { runInDirectory } from '@aztec/foundation/fs';
import { type Logger, createLogger } from '@aztec/foundation/log';
import { BundleArtifactProvider } from '@aztec/noir-protocol-circuits-types/client/bundle';

import { encode } from '@msgpack/msgpack';
import { serializeWitness } from '@noir-lang/noirc_abi';
import { type WitnessMap } from '@noir-lang/types';
import { promises as fs } from 'fs';
import path from 'path';

import { BB_RESULT, computeGateCountForCircuit, executeBbClientIvcProof } from '../bb/execute.js';
import { type BBConfig } from '../config.js';
import { BBPrivateKernelProver } from './bb_private_kernel_prover.js';
import { readFromOutputDirectory } from './client_ivc_proof_utils.js';

/**
* This proof creator implementation uses the native bb binary.
*/
export class BBNativePrivateKernelProver extends BBPrivateKernelProver {
private constructor(
private bbBinaryPath: string,
private bbWorkingDirectory: string,
private skipCleanup: boolean,
protected override log = createLogger('bb-prover:native'),
) {
super(new BundleArtifactProvider(), log);
}

public static async new(config: BBConfig, log?: Logger) {
await fs.mkdir(config.bbWorkingDirectory, { recursive: true });
return new BBNativePrivateKernelProver(config.bbBinaryPath, config.bbWorkingDirectory, !!config.bbSkipCleanup, log);
}

private async _createClientIvcProof(
directory: string,
acirs: Buffer[],
witnessStack: WitnessMap[],
): Promise<ClientIvcProof> {
// TODO(#7371): Longer term we won't use this hacked together msgpack format
// and instead properly create the bincode serialization from rust
await fs.writeFile(path.join(directory, 'acir.msgpack'), encode(acirs));
await fs.writeFile(
path.join(directory, 'witnesses.msgpack'),
encode(witnessStack.map(map => serializeWitness(map))),
);
const provingResult = await executeBbClientIvcProof(
this.bbBinaryPath,
directory,
path.join(directory, 'acir.msgpack'),
path.join(directory, 'witnesses.msgpack'),
this.log.info,
);

if (provingResult.status === BB_RESULT.FAILURE) {
this.log.error(`Failed to generate client ivc proof`);
throw new Error(provingResult.reason);
}

const proof = await readFromOutputDirectory(directory);

this.log.info(`Generated IVC proof`, {
duration: provingResult.durationMs,
eventName: 'circuit-proving',
});

return proof;
}

public override async createClientIvcProof(acirs: Buffer[], witnessStack: WitnessMap[]): Promise<ClientIvcProof> {
this.log.info(`Generating Client IVC proof`);
const operation = async (directory: string) => {
return await this._createClientIvcProof(directory, acirs, witnessStack);
};
return await this.runInDirectory(operation);
}

public override async computeGateCountForCircuit(bytecode: Buffer, circuitName: string): Promise<number> {
const logFunction = (message: string) => {
this.log.debug(`$bb gates ${circuitName} - ${message}`);
};

const result = await computeGateCountForCircuit(
this.bbBinaryPath,
this.bbWorkingDirectory,
circuitName,
bytecode,
'mega_honk',
logFunction,
);
if (result.status === BB_RESULT.FAILURE) {
throw new Error(result.reason);
}

return result.circuitSize as number;
}

private runInDirectory<T>(fn: (dir: string) => Promise<T>) {
const log = this.log;
return runInDirectory(
this.bbWorkingDirectory,
(dir: string) =>
fn(dir).catch(err => {
log.error(`Error running operation at ${dir}: ${err}`);
throw err;
}),
this.skipCleanup,
);
}
}
Loading

0 comments on commit 393e843

Please sign in to comment.