diff --git a/scripts/ci/aggregate_e2e_benchmark.js b/scripts/ci/aggregate_e2e_benchmark.js index d9f9e6fc0b89..133c3a32ce9e 100644 --- a/scripts/ci/aggregate_e2e_benchmark.js +++ b/scripts/ci/aggregate_e2e_benchmark.js @@ -2,6 +2,12 @@ // output with the grouped metrics to be published. This script can probably // be replaced by a single call to jq, but I found this easier to write, // and pretty much every CI comes with a working version of node. +// +// To test this locally, first run the benchmark tests from the yarn-project/end-to-end folder +// BENCHMARK=1 ROLLUP_SIZES=8 yarn test bench +// +// And then run this script from the root of the project: +// LOGS_DIR=./yarn-project/end-to-end/log/ node ./scripts/ci/aggregate_e2e_benchmark.js const fs = require("fs"); const path = require("path"); @@ -14,6 +20,10 @@ const { L2_BLOCK_PROCESSING_TIME, L2_BLOCK_SYNCED, L2_BLOCK_PUBLISHED_TO_L1, + CIRCUIT_SIMULATION_TIME, + CIRCUIT_OUTPUT_SIZE, + CIRCUIT_INPUT_SIZE, + CIRCUIT_SIMULATED, ROLLUP_SIZES, BENCHMARK_FILE_JSON, } = require("./benchmark_shared.js"); @@ -55,6 +65,16 @@ function processRollupBlockSynced(entry, results) { append(results, L2_BLOCK_PROCESSING_TIME, bucket, entry.duration); } +// Processes an entry with event name 'circuit-simulated' and updates results +// Buckets are circuit names +function processCircuitSimulation(entry, results) { + const bucket = entry.circuitName; + if (!bucket) return; + append(results, CIRCUIT_SIMULATION_TIME, bucket, entry.duration); + append(results, CIRCUIT_INPUT_SIZE, bucket, entry.inputSize); + append(results, CIRCUIT_OUTPUT_SIZE, bucket, entry.outputSize); +} + // Processes a parsed entry from a logfile and updates results function processEntry(entry, results) { switch (entry.eventName) { @@ -62,6 +82,8 @@ function processEntry(entry, results) { return processRollupPublished(entry, results); case L2_BLOCK_SYNCED: return processRollupBlockSynced(entry, results); + case CIRCUIT_SIMULATED: + return processCircuitSimulation(entry, results); default: return; } diff --git a/scripts/ci/benchmark_shared.js b/scripts/ci/benchmark_shared.js index c903bd473326..d3d32f0404f9 100644 --- a/scripts/ci/benchmark_shared.js +++ b/scripts/ci/benchmark_shared.js @@ -1,13 +1,3 @@ -// Metrics to capture -const L1_ROLLUP_CALLDATA_SIZE_IN_BYTES = "l1_rollup_calldata_size_in_bytes"; -const L1_ROLLUP_CALLDATA_GAS = "l1_rollup_calldata_gas"; -const L1_ROLLUP_EXECUTION_GAS = "l1_rollup_execution_gas"; -const L2_BLOCK_PROCESSING_TIME = "l2_block_processing_time_in_ms"; - -// Events to track -const L2_BLOCK_PUBLISHED_TO_L1 = "rollup-published-to-l1"; -const L2_BLOCK_SYNCED = "l2-block-handled"; - // Rollup sizes to track (duplicated from yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts) const ROLLUP_SIZES = process.env.ROLLUP_SIZES ? process.env.ROLLUP_SIZES.split(",").map(Number) @@ -17,12 +7,19 @@ const ROLLUP_SIZES = process.env.ROLLUP_SIZES const BENCHMARK_FILE_JSON = process.env.BENCHMARK_FILE_JSON ?? "benchmark.json"; module.exports = { - L1_ROLLUP_CALLDATA_SIZE_IN_BYTES, - L1_ROLLUP_CALLDATA_GAS, - L1_ROLLUP_EXECUTION_GAS, - L2_BLOCK_PROCESSING_TIME, - L2_BLOCK_PUBLISHED_TO_L1, - L2_BLOCK_SYNCED, + // Metrics to capture + L1_ROLLUP_CALLDATA_SIZE_IN_BYTES: "l1_rollup_calldata_size_in_bytes", + L1_ROLLUP_CALLDATA_GAS: "l1_rollup_calldata_gas", + L1_ROLLUP_EXECUTION_GAS: "l1_rollup_execution_gas", + L2_BLOCK_PROCESSING_TIME: "l2_block_processing_time_in_ms", + CIRCUIT_SIMULATION_TIME: "circuit_simulation_time_in_ms", + CIRCUIT_INPUT_SIZE: "circuit_input_size_in_bytes", + CIRCUIT_OUTPUT_SIZE: "circuit_output_size_in_bytes", + // Events to track + L2_BLOCK_PUBLISHED_TO_L1: "rollup-published-to-l1", + L2_BLOCK_SYNCED: "l2-block-handled", + CIRCUIT_SIMULATED: "circuit-simulation", + // Other ROLLUP_SIZES, BENCHMARK_FILE_JSON, }; diff --git a/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts index ee792e59f258..8855939931a2 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts @@ -1,8 +1,8 @@ /* eslint-disable camelcase */ import { AztecNodeService } from '@aztec/aztec-node'; -import { AztecAddress } from '@aztec/aztec.js'; +import { AztecAddress, BatchCall } from '@aztec/aztec.js'; import { sleep } from '@aztec/foundation/sleep'; -import { TokenContract } from '@aztec/noir-contracts/types'; +import { BenchmarkingContract } from '@aztec/noir-contracts/types'; import { SequencerClient } from '@aztec/sequencer-client'; import times from 'lodash.times'; @@ -13,29 +13,33 @@ const ROLLUP_SIZES = process.env.ROLLUP_SIZES ? process.env.ROLLUP_SIZES.split(' describe('benchmarks/publish_rollup', () => { let context: Awaited>; - let token: TokenContract; + let contract: BenchmarkingContract; let owner: AztecAddress; - let recipient: AztecAddress; let sequencer: SequencerClient; beforeEach(async () => { context = await setup(2, { maxTxsPerBlock: 1024 }); - - if (!(context.aztecNode instanceof AztecNodeService)) throw new Error('Aztec node is not a service'); - sequencer = context.aztecNode!.getSequencer()!; - - [owner, recipient] = context.accounts.map(a => a.address); - token = await TokenContract.deploy(context.wallet, owner).send().deployed(); - await token.methods.mint_public(owner, 10000n).send().wait(); + [owner] = context.accounts.map(a => a.address); + contract = await BenchmarkingContract.deploy(context.wallet).send().deployed(); + sequencer = (context.aztecNode as AztecNodeService).getSequencer()!; await sequencer.stop(); }, 60_000); + const makeBatchCall = (i: number) => + new BatchCall(context.wallet, [ + contract.methods.create_note(owner, i).request(), + contract.methods.increment_balance(owner, i).request(), + ]); + it.each(ROLLUP_SIZES)( `publishes a rollup with %d txs`, async (txCount: number) => { context.logger(`Assembling rollup with ${txCount} txs`); - // Simulate and simultaneously send ROLLUP_SIZE txs. These should not yet be processed since sequencer is stopped. - const calls = times(txCount, () => token.methods.transfer_public(owner, recipient, 1, 0)); + // Simulate and simultaneously send %d txs. These should not yet be processed since sequencer is stopped. + // Each tx has a private execution (account entrypoint), a nested private call (create_note), + // a public call (increment_balance), and a nested public call (broadcast). These include + // emitting one private note and one unencrypted log, two storage reads and one write. + const calls = times(txCount, makeBatchCall); calls.forEach(call => call.simulate({ skipPublicSimulation: true })); const sentTxs = calls.map(call => call.send()); diff --git a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts index f68b2d11af1e..2f2b9b21c071 100644 --- a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts +++ b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts @@ -94,6 +94,7 @@ Rollup Address: 0x0dcd1bf9a1b36ce34237eeafef220932846bcd82 const docs = ` // docs:start:example-contracts % aztec-cli example-contracts +BenchmarkingContractAbi CardGameContractAbi ChildContractAbi DocsExampleContractAbi diff --git a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts index bd09b24cd74c..adc9c01d7aec 100644 --- a/yarn-project/end-to-end/src/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/integration_l1_publisher.test.ts @@ -126,7 +126,7 @@ describe('L1Publisher integration', () => { builderDb = await MerkleTrees.new(levelup((memdown as any)())).then(t => t.asLatest()); const vks = getVerificationKeys(); - const simulator = await WasmRollupCircuitSimulator.new(); + const simulator = new WasmRollupCircuitSimulator(); const prover = new EmptyRollupProver(); builder = new SoloBlockBuilder(builderDb, vks, simulator, prover); diff --git a/yarn-project/foundation/src/timer/elapsed.ts b/yarn-project/foundation/src/timer/elapsed.ts index bc771117a252..c400816665c6 100644 --- a/yarn-project/foundation/src/timer/elapsed.ts +++ b/yarn-project/foundation/src/timer/elapsed.ts @@ -3,10 +3,21 @@ import { Timer } from './timer.js'; /** * Measures the elapsed execution time of a function call or promise once it is awaited. * @param fn - Function or promise. - * @returns A timer object. + * @returns A timer object and the result. */ export async function elapsed(fn: Promise | (() => T | Promise)): Promise<[Timer, T]> { const timer = new Timer(); const result = await (typeof fn === 'function' ? fn() : fn); return [timer, result]; } + +/** + * Measures the elapsed execution time of a synchronous function call once it is awaited. + * @param fn - Function. + * @returns A timer object and the result. + */ +export function elapsedSync(fn: () => T): [Timer, T] { + const timer = new Timer(); + const result = fn(); + return [timer, result]; +} diff --git a/yarn-project/foundation/src/timer/index.ts b/yarn-project/foundation/src/timer/index.ts index 96ad8fe9854f..a883953799dd 100644 --- a/yarn-project/foundation/src/timer/index.ts +++ b/yarn-project/foundation/src/timer/index.ts @@ -1,3 +1,3 @@ export { TimeoutTask } from './timeout.js'; export { Timer } from './timer.js'; -export { elapsed } from './elapsed.js'; +export { elapsed, elapsedSync } from './elapsed.js'; diff --git a/yarn-project/noir-contracts/Nargo.toml b/yarn-project/noir-contracts/Nargo.toml index fc837a45a5ab..f1e3bc6fe800 100644 --- a/yarn-project/noir-contracts/Nargo.toml +++ b/yarn-project/noir-contracts/Nargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "src/contracts/benchmarking_contract", "src/contracts/card_game_contract", "src/contracts/child_contract", "src/contracts/docs_example_contract", diff --git a/yarn-project/noir-contracts/src/contracts/benchmarking_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/benchmarking_contract/Nargo.toml new file mode 100644 index 000000000000..c96a5f820f25 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/benchmarking_contract/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "benchmarking_contract" +authors = [""] +compiler_version = "0.1" +type = "contract" + +[dependencies] +aztec = { path = "../../../../aztec-nr/aztec" } +value_note = { path = "../../../../aztec-nr/value-note" } diff --git a/yarn-project/noir-contracts/src/contracts/benchmarking_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/benchmarking_contract/src/main.nr new file mode 100644 index 000000000000..184583d19239 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/benchmarking_contract/src/main.nr @@ -0,0 +1,74 @@ +// A contract used for running benchmarks. +// We should try to change this contract as little as possible, since any modification +// would alter the metrics we're capturing in the benchmarks, and we want to keep the +// subject being tested as unmodified as possible so we can detect metric changes that +// arise from code changes. + +contract Benchmarking { + use dep::value_note::{ + utils::{increment, decrement}, + value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, + }; + + use dep::aztec::{ + context::{Context}, + note::note_getter_options::NoteGetterOptions, + oracle::compute_selector::compute_selector, + log::emit_unencrypted_log, + state_vars::{map::Map, public_state::PublicState, set::Set}, + types::type_serialization::field_serialization::{FieldSerializationMethods, FIELD_SERIALIZED_LEN}, + types::address::{AztecAddress}, + }; + + struct Storage { + notes: Map>, + balances: Map>, + } + + impl Storage { + fn init(context: Context) -> pub Self { + Storage { + notes: Map::new(context, 1, |context, slot| { Set::new(context, slot, ValueNoteMethods) }), + balances: Map::new(context, 2, |context, slot| { PublicState::new(context, slot, FieldSerializationMethods) }), + } + } + } + + #[aztec(private)] + fn constructor() {} + + // Creates a new value note for the target owner. Use this method to seed an initial set of notes. + #[aztec(private)] + fn create_note(owner: Field, value: Field) { + increment(storage.notes.at(owner), value, owner); + } + + // Deletes a note at a specific index in the set and creates a new one with the same value. + // We explicitly pass in the note index so we can ensure we consume different notes when sending + // multiple txs that will land on the same block. + // See https://discourse.aztec.network/t/utxo-concurrency-issues-for-private-state/635 + // by @rahul-kothari for a full explanation on why this is needed. + #[aztec(private)] + fn recreate_note(owner: Field, index: u32) { + let owner_notes = storage.notes.at(owner); + let getter_options = NoteGetterOptions::new().set_limit(1).set_offset(index); + let notes = owner_notes.get_notes(getter_options); + let note = notes[0].unwrap_unchecked(); + owner_notes.remove(note); + increment(owner_notes, note.value, owner); + } + + // Reads and writes to public storage and enqueues a call to another public function. + #[aztec(public)] + fn increment_balance(owner: Field, value: Field) { + let current = storage.balances.at(owner).read(); + storage.balances.at(owner).write(current + value); + let _callStackItem1 = context.call_public_function(context.this_address(), compute_selector("broadcast(Field)"), [owner]); + } + + // Emits a public log. + #[aztec(public)] + fn broadcast(owner: Field) { + emit_unencrypted_log(&mut context, storage.balances.at(owner).read()); + } +} diff --git a/yarn-project/pxe/src/kernel_prover/proof_creator.ts b/yarn-project/pxe/src/kernel_prover/proof_creator.ts index cbf02be23d38..016018ba808e 100644 --- a/yarn-project/pxe/src/kernel_prover/proof_creator.ts +++ b/yarn-project/pxe/src/kernel_prover/proof_creator.ts @@ -16,6 +16,7 @@ import { import { siloCommitment } from '@aztec/circuits.js/abis'; import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; +import { elapsed } from '@aztec/foundation/timer'; /** * Represents the output of the proof creation process for init and inner private kernel circuit. @@ -108,15 +109,19 @@ export class KernelProofCreator implements ProofCreator { public async createProofInit(privateInputs: PrivateKernelInputsInit): Promise { const wasm = await CircuitsWasm.get(); - this.log('Executing private kernel simulation init...'); - const result = privateKernelSimInit(wasm, privateInputs); + const [time, result] = await elapsed(() => privateKernelSimInit(wasm, privateInputs)); if (result instanceof CircuitError) { throw new CircuitError(result.code, result.message); } + this.log(`Simulated private kernel init`, { + eventName: 'circuit-simulation', + circuitName: 'private-kernel-init', + duration: time.ms(), + inputSize: privateInputs.toBuffer().length, + outputSize: result.toBuffer().length, + }); this.log('Skipping private kernel init proving...'); - // TODO const proof = makeEmptyProof(); - this.log('Kernel Prover Init Completed!'); return { publicInputs: result, @@ -126,15 +131,19 @@ export class KernelProofCreator implements ProofCreator { public async createProofInner(privateInputs: PrivateKernelInputsInner): Promise { const wasm = await CircuitsWasm.get(); - this.log('Executing private kernel simulation inner...'); - const result = privateKernelSimInner(wasm, privateInputs); + const [time, result] = await elapsed(() => privateKernelSimInner(wasm, privateInputs)); if (result instanceof CircuitError) { throw new CircuitError(result.code, result.message); } + this.log(`Simulated private kernel inner`, { + eventName: 'circuit-simulation', + circuitName: 'private-kernel-inner', + duration: time.ms(), + inputSize: privateInputs.toBuffer().length, + outputSize: result.toBuffer().length, + }); this.log('Skipping private kernel inner proving...'); - // TODO const proof = makeEmptyProof(); - this.log('Kernel Prover Inner Completed!'); return { publicInputs: result, @@ -145,14 +154,19 @@ export class KernelProofCreator implements ProofCreator { public async createProofOrdering(privateInputs: PrivateKernelInputsOrdering): Promise { const wasm = await CircuitsWasm.get(); this.log('Executing private kernel simulation ordering...'); - const result = privateKernelSimOrdering(wasm, privateInputs); + const [time, result] = await elapsed(() => privateKernelSimOrdering(wasm, privateInputs)); if (result instanceof CircuitError) { throw new CircuitError(result.code, result.message); } + this.log(`Simulated private kernel ordering`, { + eventName: 'circuit-simulation', + circuitName: 'private-kernel-ordering', + duration: time.ms(), + inputSize: privateInputs.toBuffer().length, + outputSize: result.toBuffer().length, + }); this.log('Skipping private kernel ordering proving...'); - // TODO const proof = makeEmptyProof(); - this.log('Ordering Kernel Prover Ordering Completed!'); return { publicInputs: result, diff --git a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts index 6f1b23c9c934..7f61ad4e13e7 100644 --- a/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts +++ b/yarn-project/sequencer-client/src/block_builder/solo_block_builder.test.ts @@ -291,8 +291,8 @@ describe('sequencer/solo_block_builder', () => { }); describe('circuits simulator', () => { - beforeEach(async () => { - const simulator = await WasmRollupCircuitSimulator.new(); + beforeEach(() => { + const simulator = new WasmRollupCircuitSimulator(); const prover = new EmptyRollupProver(); builder = new SoloBlockBuilder(builderDb, vks, simulator, prover); }); @@ -388,7 +388,7 @@ describe('sequencer/solo_block_builder', () => { // This test specifically tests nullifier values which previously caused e2e_private_token test to fail it('e2e_private_token edge case regression test on nullifier values', async () => { - const simulator = await WasmRollupCircuitSimulator.new(); + const simulator = new WasmRollupCircuitSimulator(); const prover = new EmptyRollupProver(); builder = new SoloBlockBuilder(builderDb, vks, simulator, prover); // update the starting tree diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 90764a5f369e..22af9491e4e7 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -41,7 +41,7 @@ export class SequencerClient { const blockBuilder = new SoloBlockBuilder( merkleTreeDb, getVerificationKeys(), - await WasmRollupCircuitSimulator.new(), + new WasmRollupCircuitSimulator(), new EmptyRollupProver(), ); diff --git a/yarn-project/sequencer-client/src/simulator/public_kernel.ts b/yarn-project/sequencer-client/src/simulator/public_kernel.ts index ec7c3c522b96..3ba43adf5c20 100644 --- a/yarn-project/sequencer-client/src/simulator/public_kernel.ts +++ b/yarn-project/sequencer-client/src/simulator/public_kernel.ts @@ -1,4 +1,6 @@ import { PublicKernelInputs, PublicKernelPublicInputs, simulatePublicKernelCircuit } from '@aztec/circuits.js'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { elapsed } from '@aztec/foundation/timer'; import { PublicKernelCircuitSimulator } from './index.js'; @@ -6,14 +8,24 @@ import { PublicKernelCircuitSimulator } from './index.js'; * Implements the PublicKernelCircuitSimulator by calling the wasm implementations of the circuits. */ export class WasmPublicKernelCircuitSimulator implements PublicKernelCircuitSimulator { + private log = createDebugLogger('aztec:public-kernel-simulator'); + /** * Simulates the public kernel circuit (with a previous private kernel circuit run) from its inputs. * @param input - Inputs to the circuit. * @returns The public inputs as outputs of the simulation. */ - public publicKernelCircuitPrivateInput(input: PublicKernelInputs): Promise { + public async publicKernelCircuitPrivateInput(input: PublicKernelInputs): Promise { if (!input.previousKernel.publicInputs.isPrivate) throw new Error(`Expected private kernel previous inputs`); - return simulatePublicKernelCircuit(input); + const [time, result] = await elapsed(() => simulatePublicKernelCircuit(input)); + this.log(`Simulated public kernel circuit with private input`, { + eventName: 'circuit-simulation', + circuitName: 'public-kernel-private-input', + duration: time.ms(), + inputSize: input.toBuffer().length, + outputSize: result.toBuffer().length, + }); + return result; } /** @@ -21,8 +33,16 @@ export class WasmPublicKernelCircuitSimulator implements PublicKernelCircuitSimu * @param input - Inputs to the circuit. * @returns The public inputs as outputs of the simulation. */ - publicKernelCircuitNonFirstIteration(input: PublicKernelInputs): Promise { + public async publicKernelCircuitNonFirstIteration(input: PublicKernelInputs): Promise { if (input.previousKernel.publicInputs.isPrivate) throw new Error(`Expected public kernel previous inputs`); - return simulatePublicKernelCircuit(input); + const [time, result] = await elapsed(() => simulatePublicKernelCircuit(input)); + this.log(`Simulated public kernel circuit non-first iteration`, { + eventName: 'circuit-simulation', + circuitName: 'public-kernel-non-first-iteration', + duration: time.ms(), + inputSize: input.toBuffer().length, + outputSize: result.toBuffer().length, + }); + return result; } } diff --git a/yarn-project/sequencer-client/src/simulator/rollup.ts b/yarn-project/sequencer-client/src/simulator/rollup.ts index 99fe554bcc6d..d9a1f97442b3 100644 --- a/yarn-project/sequencer-client/src/simulator/rollup.ts +++ b/yarn-project/sequencer-client/src/simulator/rollup.ts @@ -10,6 +10,8 @@ import { mergeRollupSim, rootRollupSim, } from '@aztec/circuits.js'; +import { createDebugLogger } from '@aztec/foundation/log'; +import { elapsed } from '@aztec/foundation/timer'; import { RollupSimulator } from './index.js'; @@ -17,31 +19,28 @@ import { RollupSimulator } from './index.js'; * Implements the rollup circuit simulator using the wasm circuits implementation. */ export class WasmRollupCircuitSimulator implements RollupSimulator { - private wasm: CircuitsWasm; - - constructor(wasm: CircuitsWasm) { - this.wasm = wasm; - } - - /** - * Creates a new instance using the default CircuitsWasm module. - * @returns A new instance. - */ - public static async new() { - return new this(await CircuitsWasm.get()); - } + private log = createDebugLogger('aztec:rollup-simulator'); /** * Simulates the base rollup circuit from its inputs. * @param input - Inputs to the circuit. * @returns The public inputs as outputs of the simulation. */ - baseRollupCircuit(input: BaseRollupInputs): Promise { - const result = baseRollupSim(this.wasm, input); + public async baseRollupCircuit(input: BaseRollupInputs): Promise { + const wasm = await CircuitsWasm.get(); + const [time, result] = await elapsed(() => baseRollupSim(wasm, input)); if (result instanceof CircuitError) { throw new CircuitError(result.code, result.message); } + this.log(`Simulated base rollup circuit`, { + eventName: 'circuit-simulation', + circuitName: 'base-rollup', + duration: time.ms(), + inputSize: input.toBuffer().length, + outputSize: result.toBuffer().length, + }); + return Promise.resolve(result); } /** @@ -49,13 +48,22 @@ export class WasmRollupCircuitSimulator implements RollupSimulator { * @param input - Inputs to the circuit. * @returns The public inputs as outputs of the simulation. */ - mergeRollupCircuit(input: MergeRollupInputs): Promise { - const result = mergeRollupSim(this.wasm, input); + public async mergeRollupCircuit(input: MergeRollupInputs): Promise { + const wasm = await CircuitsWasm.get(); + const [time, result] = await elapsed(() => mergeRollupSim(wasm, input)); if (result instanceof CircuitError) { throw new CircuitError(result.code, result.message); } - return Promise.resolve(result); + this.log(`Simulated merge rollup circuit`, { + eventName: 'circuit-simulation', + circuitName: 'merge-rollup', + duration: time.ms(), + inputSize: input.toBuffer().length, + outputSize: result.toBuffer().length, + }); + + return result; } /** @@ -63,12 +71,21 @@ export class WasmRollupCircuitSimulator implements RollupSimulator { * @param input - Inputs to the circuit. * @returns The public inputs as outputs of the simulation. */ - rootRollupCircuit(input: RootRollupInputs): Promise { - const result = rootRollupSim(this.wasm, input); + public async rootRollupCircuit(input: RootRollupInputs): Promise { + const wasm = await CircuitsWasm.get(); + const [time, result] = await elapsed(() => rootRollupSim(wasm, input)); if (result instanceof CircuitError) { throw new CircuitError(result.code, result.message); } - return Promise.resolve(result); + this.log(`Simulated root rollup circuit`, { + eventName: 'circuit-simulation', + circuitName: 'root-rollup', + duration: time.ms(), + inputSize: input.toBuffer().length, + outputSize: result.toBuffer().length, + }); + + return result; } }