From 24ff54609f795bc4c8b5e91e4be4cb76aa9e17ef Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 14 May 2024 14:38:43 +0000 Subject: [PATCH] feat: benchmark private proving --- .../src/prover/bb_native_proof_creator.ts | 37 ++++- .../bb-prover/src/prover/bb_prover.ts | 79 +++++----- yarn-project/bb-prover/src/stats.ts | 59 ++------ .../src/interfaces/proof_creator.ts | 7 +- .../circuit-types/src/stats/metrics.ts | 75 ++++++++-- yarn-project/circuit-types/src/stats/stats.ts | 18 ++- .../src/benchmarks/bench_proving.test.ts | 136 ++++++++++++++---- yarn-project/prover-client/src/stats.ts | 82 ----------- yarn-project/pxe/src/kernel_oracle/index.ts | 15 ++ .../pxe/src/kernel_prover/kernel_prover.ts | 6 + .../src/kernel_prover/proving_data_oracle.ts | 2 + .../kernel_prover/test/test_circuit_prover.ts | 2 +- .../scripts/src/benchmarks/aggregate.ts | 50 +++++-- .../scripts/src/benchmarks/markdown.ts | 10 +- 14 files changed, 346 insertions(+), 232 deletions(-) delete mode 100644 yarn-project/prover-client/src/stats.ts diff --git a/yarn-project/bb-prover/src/prover/bb_native_proof_creator.ts b/yarn-project/bb-prover/src/prover/bb_native_proof_creator.ts index 977200103a85..a63b03638940 100644 --- a/yarn-project/bb-prover/src/prover/bb_native_proof_creator.ts +++ b/yarn-project/bb-prover/src/prover/bb_native_proof_creator.ts @@ -1,4 +1,5 @@ import { type AppCircuitProofOutput, type KernelProofOutput, type ProofCreator } from '@aztec/circuit-types'; +import { type CircuitProvingStats, type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; import { Fr, NESTED_RECURSIVE_PROOF_LENGTH, @@ -18,6 +19,7 @@ import { siloNoteHash } from '@aztec/circuits.js/hash'; import { randomBytes } from '@aztec/foundation/crypto'; import { createDebugLogger } from '@aztec/foundation/log'; import { type Tuple } from '@aztec/foundation/serialize'; +import { Timer } from '@aztec/foundation/timer'; import { ClientCircuitArtifacts, type ClientProtocolArtifact, @@ -47,6 +49,7 @@ import { generateProof, verifyProof, } from '../bb/execute.js'; +import { mapProtocolArtifactNameToCircuitName } from '../stats.js'; import { AGGREGATION_OBJECT_SIZE, CIRCUIT_PUBLIC_INPUTS_INDEX, @@ -128,13 +131,14 @@ export class BBNativeProofCreator implements ProofCreator { public async createAppCircuitProof( partialWitness: Map, bytecode: Buffer, + appCircuitName?: string, ): Promise { const directory = `${this.bbWorkingDirectory}/${randomBytes(8).toString('hex')}`; await fs.mkdir(directory, { recursive: true }); this.log.debug(`Created directory: ${directory}`); try { this.log.debug(`Proving app circuit`); - const proofOutput = await this.createProof(directory, partialWitness, bytecode, 'App'); + const proofOutput = await this.createProof(directory, partialWitness, bytecode, 'App', appCircuitName); if (proofOutput.proof.proof.length != RECURSIVE_PROOF_LENGTH) { throw new Error(`Incorrect proof length`); } @@ -286,9 +290,16 @@ export class BBNativeProofCreator implements ProofCreator { this.log.debug(`Generating witness for ${circuitType}`); const compiledCircuit: NoirCompiledCircuit = ClientCircuitArtifacts[circuitType]; + const timer = new Timer(); const outputWitness = await this.simulator.simulateCircuit(inputs, compiledCircuit); - this.log.debug(`Generated witness for ${circuitType}`); + this.log.debug(`Generated witness for ${circuitType}`, { + eventName: 'circuit-witness-generation', + circuitName: mapProtocolArtifactNameToCircuitName(circuitType), + duration: timer.ms(), + inputSize: inputs.size * Fr.SIZE_IN_BYTES, + outputSize: outputWitness.size * Fr.SIZE_IN_BYTES, + } satisfies CircuitWitnessGenerationStats); const publicInputs = PrivateKernelArtifactMapping[circuitType].convertOutputs(outputWitness) as T; @@ -316,6 +327,7 @@ export class BBNativeProofCreator implements ProofCreator { partialWitness: WitnessMap, bytecode: Buffer, circuitType: ClientProtocolArtifact | 'App', + appCircuitName?: string, ): Promise<{ proof: RecursiveProof | RecursiveProof; verificationKey: VerificationKeyAsFields; @@ -345,11 +357,32 @@ export class BBNativeProofCreator implements ProofCreator { if (circuitType === 'App') { const vkData = await this.convertVk(directory); const proof = await this.readProofAsFields(directory, circuitType, vkData); + + this.log.debug(`Generated proof`, { + eventName: 'circuit-proving', + circuitName: 'private-app-circuit', + duration: provingResult.duration, + inputSize: partialWitness.size * Fr.SIZE_IN_BYTES, + proofSize: proof.binaryProof.buffer.byteLength, + appCircuitName, + circuitSize: vkData.circuitSize, + } as CircuitProvingStats); + return { proof, verificationKey: new VerificationKeyAsFields(vkData.keyAsFields, vkData.hash) }; } const vkData = await this.updateVerificationKeyAfterProof(directory, circuitType); const proof = await this.readProofAsFields(directory, circuitType, vkData); + + this.log.debug(`Generated proof`, { + circuitName: mapProtocolArtifactNameToCircuitName(circuitType), + duration: provingResult.duration, + eventName: 'circuit-proving', + inputSize: partialWitness.size * Fr.SIZE_IN_BYTES, + proofSize: proof.binaryProof.buffer.byteLength, + circuitSize: vkData.circuitSize, + } as CircuitProvingStats); + return { proof, verificationKey: new VerificationKeyAsFields(vkData.keyAsFields, vkData.hash) }; } diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index bca2596b015f..00b287fff5ba 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -7,6 +7,7 @@ import { type ServerCircuitProver, makePublicInputsAndProof, } from '@aztec/circuit-types'; +import { type CircuitProvingStats, type CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; import { type BaseOrMergeRollupPublicInputs, type BaseParityInputs, @@ -65,7 +66,7 @@ import { verifyProof, } from '../bb/execute.js'; import { PublicKernelArtifactMapping } from '../mappings/mappings.js'; -import { circuitTypeToCircuitName, emitCircuitProvingStats, emitCircuitWitnessGenerationStats } from '../stats.js'; +import { mapProtocolArtifactNameToCircuitName } from '../stats.js'; import { AGGREGATION_OBJECT_SIZE, CIRCUIT_PUBLIC_INPUTS_INDEX, @@ -267,13 +268,13 @@ export class BBNativeRollupProver implements ServerCircuitProver { const timer = new Timer(); const outputWitness = await simulator.simulateCircuit(witnessMap, artifact); - emitCircuitWitnessGenerationStats( - circuitTypeToCircuitName(circuitType), - timer.ms(), - witnessMap.size * Fr.SIZE_IN_BYTES, - outputWitness.size * Fr.SIZE_IN_BYTES, - logger, - ); + logger.debug(`Generated witness`, { + circuitName: mapProtocolArtifactNameToCircuitName(circuitType), + duration: timer.ms(), + inputSize: witnessMap.size * Fr.SIZE_IN_BYTES, + outputSize: outputWitness.size * Fr.SIZE_IN_BYTES, + eventName: 'circuit-witness-generation', + } satisfies CircuitWitnessGenerationStats); // Now prove the circuit from the generated witness logger.debug(`Proving ${circuitType}...`); @@ -293,24 +294,22 @@ export class BBNativeRollupProver implements ServerCircuitProver { } // Ensure our vk cache is up to date - await this.updateVerificationKeyAfterProof(provingResult.vkPath!, circuitType); + const vkData = await this.updateVerificationKeyAfterProof(provingResult.vkPath!, circuitType); // Read the proof and then cleanup up our temporary directory const proof = await fs.readFile(`${provingResult.proofPath!}/${PROOF_FILENAME}`); - // does not include reading the proof from disk above because duration comes from the bb wrapper - emitCircuitProvingStats( - circuitTypeToCircuitName(circuitType), - provingResult.duration, - witnessMap.size * Fr.SIZE_IN_BYTES, - outputWitness.size * Fr.SIZE_IN_BYTES, - proof.length, - logger, - ); - await fs.rm(bbWorkingDirectory, { recursive: true, force: true }); - logger.info(`Generated proof for ${circuitType} in ${provingResult.duration} ms, size: ${proof.length} fields`); + logger.info(`Generated proof for ${circuitType} in ${provingResult.duration} ms, size: ${proof.length} fields`, { + circuitName: mapProtocolArtifactNameToCircuitName(circuitType), + // does not include reading the proof from disk + duration: provingResult.duration, + proofSize: proof.length, + eventName: 'circuit-proving', + inputSize: witnessMap.size * Fr.SIZE_IN_BYTES, + circuitSize: vkData.circuitSize, + } satisfies CircuitProvingStats); return [outputWitness, new Proof(proof)]; } @@ -352,13 +351,13 @@ export class BBNativeRollupProver implements ServerCircuitProver { const timer = new Timer(); const outputWitness = await simulator.simulateCircuit(witnessMap, artifact); - emitCircuitWitnessGenerationStats( - circuitTypeToCircuitName(circuitType), - timer.ms(), - witnessMap.size * Fr.SIZE_IN_BYTES, - outputWitness.size * Fr.SIZE_IN_BYTES, - logger, - ); + logger.debug(`Generated witness`, { + circuitName: mapProtocolArtifactNameToCircuitName(circuitType), + duration: timer.ms(), + inputSize: witnessMap.size * Fr.SIZE_IN_BYTES, + outputSize: outputWitness.size * Fr.SIZE_IN_BYTES, + eventName: 'circuit-witness-generation', + } satisfies CircuitWitnessGenerationStats); const outputType = convertOutput(outputWitness); @@ -380,22 +379,21 @@ export class BBNativeRollupProver implements ServerCircuitProver { } // Ensure our vk cache is up to date - await this.updateVerificationKeyAfterProof(provingResult.vkPath!, circuitType); + const vkData = await this.updateVerificationKeyAfterProof(provingResult.vkPath!, circuitType); // Read the proof and then cleanup up our temporary directory const proof = await this.readProofAsFields(provingResult.proofPath!, circuitType); logger.info( `Generated proof for ${circuitType} in ${provingResult.duration} ms, size: ${proof.proof.length} fields`, - ); - - emitCircuitProvingStats( - circuitTypeToCircuitName(circuitType), - provingResult.duration, - witnessMap.size * Fr.SIZE_IN_BYTES, - outputWitness.size * Fr.SIZE_IN_BYTES, - proof.binaryProof.buffer.length, - logger, + { + circuitName: mapProtocolArtifactNameToCircuitName(circuitType), + circuitSize: vkData.circuitSize, + duration: provingResult.duration, + inputSize: witnessMap.size * Fr.SIZE_IN_BYTES, + proofSize: proof.binaryProof.buffer.length, + eventName: 'circuit-proving', + } satisfies CircuitProvingStats, ); return [outputType, proof]; @@ -515,13 +513,16 @@ export class BBNativeRollupProver implements ServerCircuitProver { * @param filePath - The directory containing the verification key data files * @param circuitType - The type of circuit to which the verification key corresponds */ - private async updateVerificationKeyAfterProof(filePath: string, circuitType: ServerProtocolArtifact) { + private async updateVerificationKeyAfterProof( + filePath: string, + circuitType: ServerProtocolArtifact, + ): Promise { let promise = this.verificationKeys.get(circuitType); if (!promise) { promise = this.convertVk(filePath); this.verificationKeys.set(circuitType, promise); } - await promise; + return promise; } /** diff --git a/yarn-project/bb-prover/src/stats.ts b/yarn-project/bb-prover/src/stats.ts index fbd68bde90be..04e690d01cba 100644 --- a/yarn-project/bb-prover/src/stats.ts +++ b/yarn-project/bb-prover/src/stats.ts @@ -1,45 +1,6 @@ import { type PublicKernelRequest, PublicKernelType } from '@aztec/circuit-types'; -import type { CircuitName, CircuitProvingStats, CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; -import { type Logger } from '@aztec/foundation/log'; -import { type ServerProtocolArtifact } from '@aztec/noir-protocol-circuits-types'; - -export function emitCircuitWitnessGenerationStats( - circuitName: CircuitName, - duration: number, - inputSize: number, - outputSize: number, - logger: Logger, -) { - const stats: CircuitWitnessGenerationStats = { - eventName: 'circuit-witness-generation', - circuitName, - inputSize, - outputSize, - duration, - }; - - logger.debug('Circuit witness generation stats', stats); -} - -export function emitCircuitProvingStats( - circuitName: CircuitName, - duration: number, - inputSize: number, - outputSize: number, - proofSize: number, - logger: Logger, -) { - const stats: CircuitProvingStats = { - eventName: 'circuit-proving', - circuitName, - duration, - inputSize, - outputSize, - proofSize, - }; - - logger.debug('Circuit proving stats', stats); -} +import type { CircuitName } from '@aztec/circuit-types/stats'; +import { type ClientProtocolArtifact, type ServerProtocolArtifact } from '@aztec/noir-protocol-circuits-types'; export function mapPublicKernelToCircuitName(kernelType: PublicKernelRequest['type']): CircuitName { switch (kernelType) { @@ -56,8 +17,10 @@ export function mapPublicKernelToCircuitName(kernelType: PublicKernelRequest['ty } } -export function circuitTypeToCircuitName(circuitType: ServerProtocolArtifact): CircuitName { - switch (circuitType) { +export function mapProtocolArtifactNameToCircuitName( + artifact: ServerProtocolArtifact | ClientProtocolArtifact, +): CircuitName { + switch (artifact) { case 'BaseParityArtifact': return 'base-parity'; case 'RootParityArtifact': @@ -76,7 +39,15 @@ export function circuitTypeToCircuitName(circuitType: ServerProtocolArtifact): C return 'public-kernel-teardown'; case 'PublicKernelTailArtifact': return 'public-kernel-tail'; + case 'PrivateKernelInitArtifact': + return 'private-kernel-init'; + case 'PrivateKernelInnerArtifact': + return 'private-kernel-inner'; + case 'PrivateKernelTailArtifact': + return 'private-kernel-tail'; + case 'PrivateKernelTailToPublicArtifact': + return 'private-kernel-tail-to-public'; default: - throw new Error(`Unknown circuit type: ${circuitType}`); + throw new Error(`Unknown circuit type: ${artifact}`); } } diff --git a/yarn-project/circuit-types/src/interfaces/proof_creator.ts b/yarn-project/circuit-types/src/interfaces/proof_creator.ts index 5181864cbb15..8cdadf6dd907 100644 --- a/yarn-project/circuit-types/src/interfaces/proof_creator.ts +++ b/yarn-project/circuit-types/src/interfaces/proof_creator.ts @@ -92,7 +92,12 @@ export interface ProofCreator { * * @param partialWitness - The witness produced via circuit simulation * @param bytecode - The circuit bytecode in gzipped bincode format + * @param appCircuitName - Optionally specify the name of the app circuit * @returns A Promise resolving to a Proof object */ - createAppCircuitProof(partialWitness: WitnessMap, bytecode: Buffer): Promise; + createAppCircuitProof( + partialWitness: WitnessMap, + bytecode: Buffer, + appCircuitName?: string, + ): Promise; } diff --git a/yarn-project/circuit-types/src/stats/metrics.ts b/yarn-project/circuit-types/src/stats/metrics.ts index 5b7217e42f57..8ac7a55d3bc2 100644 --- a/yarn-project/circuit-types/src/stats/metrics.ts +++ b/yarn-project/circuit-types/src/stats/metrics.ts @@ -4,7 +4,8 @@ import { type StatsEventName } from './stats.js'; export type MetricGroupBy = | 'block-size' | 'chain-length' - | 'circuit-name' + | 'kernel-circuit-name' + | 'app-circuit-name' | 'classes-registered' | 'leaf-count' | 'data-writes' @@ -111,41 +112,89 @@ export const Metrics = [ events: ['note-processor-caught-up'], }, { - name: 'circuit_simulation_time_in_ms', - groupBy: 'circuit-name', + name: 'kernel_circuit_simulation_time_in_ms', + groupBy: 'kernel-circuit-name', description: 'Time to run a circuit simulation.', events: ['circuit-simulation'], }, { - name: 'circuit_witness_generation_time_in_ms', - groupBy: 'circuit-name', + name: 'kernel_circuit_witness_generation_time_in_ms', + groupBy: 'kernel-circuit-name', description: 'Time to generate the partial witness for a circuit', events: ['circuit-simulation'], }, { - name: 'circuit_proving_time_in_ms', - groupBy: 'circuit-name', + name: 'kernel_circuit_proving_time_in_ms', + groupBy: 'kernel-circuit-name', description: 'Time to prove circuit execution.', events: ['circuit-proving'], }, { - name: 'circuit_input_size_in_bytes', - groupBy: 'circuit-name', + name: 'kernel_circuit_input_size_in_bytes', + groupBy: 'kernel-circuit-name', description: 'Size of the inputs to a circuit simulation.', events: ['circuit-simulation'], }, { - name: 'circuit_output_size_in_bytes', - groupBy: 'circuit-name', + name: 'kernel_circuit_output_size_in_bytes', + groupBy: 'kernel-circuit-name', description: 'Size of the outputs (ie public inputs) from a circuit simulation.', events: ['circuit-simulation'], }, { - name: 'circuit_proof_size_in_bytes', - groupBy: 'circuit-name', + name: 'kernel_circuit_proof_size_in_bytes', + groupBy: 'kernel-circuit-name', description: 'Size of the proof produced by a circuit.', events: ['circuit-proving'], }, + { + name: 'kernel_circuit_size_in_gates', + groupBy: 'kernel-circuit-name', + description: 'Size of the proof produced by a circuit.', + events: ['circuit-proving'], + }, + { + name: 'app_circuit_simulation_time_in_ms', + groupBy: 'app-circuit-name', + description: 'Time to run a circuit simulation.', + events: ['circuit-simulation'], + }, + { + name: 'app_circuit_input_size_in_bytes', + groupBy: 'app-circuit-name', + description: 'Size of the inputs to a circuit simulation.', + events: ['circuit-simulation'], + }, + { + name: 'app_circuit_output_size_in_bytes', + groupBy: 'app-circuit-name', + description: 'Size of the outputs (ie public inputs) from a circuit simulation.', + events: ['circuit-simulation'], + }, + { + name: 'app_circuit_proof_size_in_bytes', + groupBy: 'app-circuit-name', + description: 'Size of the proof produced by a circuit.', + events: ['circuit-proving'], + }, + { + name: 'app_circuit_witness_generation_time_in_ms', + groupBy: 'app-circuit-name', + description: 'Time to generate the partial witness for a circuit', + events: ['circuit-simulation'], + }, + { + name: 'app_circuit_proving_time_in_ms', + groupBy: 'app-circuit-name', + description: 'Duration of proving an app circuit.', + events: ['circuit-proving'], + }, + { + name: 'app_circuit_size_in_gates', + groupBy: 'app-circuit-name', + description: 'Size of an app circuit.', + events: ['circuit-proving'], + }, { name: 'tx_size_in_bytes', groupBy: 'classes-registered', diff --git a/yarn-project/circuit-types/src/stats/stats.ts b/yarn-project/circuit-types/src/stats/stats.ts index 6c28b6087eb3..d443123bd9bf 100644 --- a/yarn-project/circuit-types/src/stats/stats.ts +++ b/yarn-project/circuit-types/src/stats/stats.ts @@ -52,11 +52,13 @@ export type CircuitName = | 'base-parity' | 'root-parity' | 'base-rollup' - | 'private-kernel-init' - | 'private-kernel-ordering' - | 'root-rollup' | 'merge-rollup' + | 'root-rollup' + | 'private-kernel-init' | 'private-kernel-inner' + | 'private-kernel-tail' + | 'private-kernel-tail-to-public' + | 'private-app-circuit' | 'public-kernel-setup' | 'public-kernel-app-logic' | 'public-kernel-teardown' @@ -68,6 +70,8 @@ export type CircuitSimulationStats = { eventName: 'circuit-simulation'; /** Name of the circuit. */ circuitName: CircuitName; + /** Optional. The function name that's being simulated */ + appCircuitName?: string; /** Duration in ms. */ duration: number; /** Size in bytes of circuit inputs. */ @@ -82,6 +86,8 @@ export type CircuitWitnessGenerationStats = { eventName: 'circuit-witness-generation'; /** Name of the circuit. */ circuitName: CircuitName; + /** Optional. The function name that's being proven */ + appCircuitName?: string; /** Duration in ms. */ duration: number; /** Size in bytes of circuit inputs. */ @@ -96,12 +102,14 @@ export type CircuitProvingStats = { eventName: 'circuit-proving'; /** Name of the circuit. */ circuitName: CircuitName; + /** Optional. The function name that was proven */ + appCircuitName?: string; /** Duration in ms. */ duration: number; + /** The size of the circuit (in gates) */ + circuitSize: number; /** Size in bytes of circuit inputs. */ inputSize: number; - /** Size in bytes of circuit outputs (aka public inputs). */ - outputSize: number; /** Size in bytes of the proof. */ proofSize: number; }; diff --git a/yarn-project/end-to-end/src/benchmarks/bench_proving.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_proving.test.ts index 52f1d2f88269..31c199e9e51f 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_proving.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_proving.test.ts @@ -1,26 +1,38 @@ +import { getSchnorrAccount, getSchnorrWallet } from '@aztec/accounts/schnorr'; import { type AztecNodeService } from '@aztec/aztec-node'; -import { type AccountWallet, EthAddress, PublicFeePaymentMethod, TxStatus } from '@aztec/aztec.js'; -import { GasSettings } from '@aztec/circuits.js'; +import { EthAddress, PublicFeePaymentMethod, TxStatus } from '@aztec/aztec.js'; +import { type AccountWallet } from '@aztec/aztec.js/wallet'; +import { type CompleteAddress, Fq, Fr, GasSettings } from '@aztec/circuits.js'; import { FPCContract, GasTokenContract, TestContract, TokenContract } from '@aztec/noir-contracts.js'; import { getCanonicalGasTokenAddress } from '@aztec/protocol-contracts/gas-token'; import { ProverPool } from '@aztec/prover-client/prover-pool'; +import { type PXEService, createPXEService } from '@aztec/pxe'; import { jest } from '@jest/globals'; import { getACVMConfig } from '../fixtures/get_acvm_config.js'; import { getBBConfig } from '../fixtures/get_bb_config.js'; -import { type EndToEndContext, publicDeployAccounts, setup } from '../fixtures/utils.js'; +import { type EndToEndContext, setup } from '../fixtures/utils.js'; -jest.setTimeout(600_000); +jest.setTimeout(1_200_000); -const txTimeoutSec = 600; +const txTimeoutSec = 1200; describe('benchmarks/proving', () => { let ctx: EndToEndContext; - let wallet: AccountWallet; - let testContract: TestContract; - let tokenContract: TokenContract; - let fpContract: FPCContract; + + let schnorrWalletSalt: Fr; + let schnorrWalletEncKey: Fr; + let schnorrWalletSigningKey: Fq; + let schnorrWalletAddress: CompleteAddress; + + let initialGasContract: GasTokenContract; + let initialTestContract: TestContract; + let initialTokenContract: TokenContract; + let initialFpContract: FPCContract; + + let provingPxes: PXEService[]; + let acvmCleanup: () => Promise; let bbCleanup: () => Promise; let proverPool: ProverPool; @@ -40,21 +52,47 @@ describe('benchmarks/proving', () => { true, // enable gas ); - wallet = ctx.wallet; - - await publicDeployAccounts(wallet, ctx.wallets); - - testContract = await TestContract.deploy(wallet).send().deployed(); - tokenContract = await TokenContract.deploy(wallet, wallet.getAddress(), 'test', 't', 18).send().deployed(); - const gas = await GasTokenContract.at( + schnorrWalletSalt = Fr.random(); + schnorrWalletEncKey = Fr.random(); + schnorrWalletSigningKey = Fq.random(); + const initialSchnorrWallet = await getSchnorrAccount( + ctx.pxe, + schnorrWalletEncKey, + schnorrWalletSigningKey, + schnorrWalletSalt, + ) + .deploy({ + skipClassRegistration: false, + skipPublicDeployment: false, + }) + .getWallet(); + schnorrWalletAddress = initialSchnorrWallet.getCompleteAddress(); + + initialTestContract = await TestContract.deploy(initialSchnorrWallet).send().deployed(); + initialTokenContract = await TokenContract.deploy( + initialSchnorrWallet, + initialSchnorrWallet.getAddress(), + 'test', + 't', + 18, + ) + .send() + .deployed(); + initialGasContract = await GasTokenContract.at( getCanonicalGasTokenAddress(ctx.deployL1ContractsValues.l1ContractAddresses.gasPortalAddress), - wallet, + initialSchnorrWallet, ); - fpContract = await FPCContract.deploy(wallet, tokenContract.address, gas.address).send().deployed(); + initialFpContract = await FPCContract.deploy( + initialSchnorrWallet, + initialTokenContract.address, + initialGasContract.address, + ) + .send() + .deployed(); await Promise.all([ - gas.methods.mint_public(fpContract.address, 1e12).send().wait(), - tokenContract.methods.mint_public(wallet.getAddress(), 1e12).send().wait(), + initialGasContract.methods.mint_public(initialFpContract.address, 1e12).send().wait(), + initialTokenContract.methods.mint_public(initialSchnorrWallet.getAddress(), 1e12).send().wait(), ]); }); @@ -87,9 +125,36 @@ describe('benchmarks/proving', () => { ctx.logger.info('Starting real provers'); await proverPool.start((ctx.aztecNode as AztecNodeService).getProver().getProvingJobSource()); + + ctx.logger.info('Starting PXEs configured with real proofs'); + provingPxes = []; + for (let i = 0; i < 4; i++) { + const pxe = await createPXEService( + ctx.aztecNode, + { + proverEnabled: true, + bbBinaryPath: bbConfig.bbBinaryPath, + bbWorkingDirectory: bbConfig.bbWorkingDirectory, + l2BlockPollingIntervalMS: 1000, + l2StartingBlock: 1, + }, + `proving-pxe-${i}`, + ); + + await getSchnorrAccount(pxe, schnorrWalletEncKey, schnorrWalletSigningKey, schnorrWalletSalt).register(); + await pxe.registerContract(initialTokenContract); + await pxe.registerContract(initialTestContract); + await pxe.registerContract(initialFpContract); + await pxe.registerContract(initialGasContract); + + provingPxes.push(pxe); + } }); afterAll(async () => { + for (const pxe of provingPxes) { + await pxe.stop(); + } await proverPool.stop(); await ctx.teardown(); await acvmCleanup(); @@ -99,26 +164,43 @@ describe('benchmarks/proving', () => { it('builds a full block', async () => { const txs = [ // fully private tx - testContract.methods.emit_nullifier(42).send(), + (await getTestContractOnPXE(0)).methods.emit_nullifier(42).send(), // tx with setup, app, teardown - testContract.methods.emit_unencrypted(43).send({ + (await getTestContractOnPXE(1)).methods.emit_unencrypted(43).send({ fee: { gasSettings: GasSettings.default(), - paymentMethod: new PublicFeePaymentMethod(tokenContract.address, fpContract.address, wallet), + paymentMethod: new PublicFeePaymentMethod( + initialTokenContract.address, + initialFpContract.address, + await getWalletOnPxe(1), + ), }, }), // tx with messages - testContract.methods.create_l2_to_l1_message_public(45, 46, EthAddress.random()).send(), + (await getTestContractOnPXE(2)).methods.create_l2_to_l1_message_public(45, 46, EthAddress.random()).send(), // tx with private and public exec - testContract.methods.set_tx_max_block_number(100, true).send({ + (await getTestContractOnPXE(3)).methods.set_tx_max_block_number(100, true).send({ fee: { gasSettings: GasSettings.default(), - paymentMethod: new PublicFeePaymentMethod(tokenContract.address, fpContract.address, wallet), + paymentMethod: new PublicFeePaymentMethod( + initialTokenContract.address, + initialFpContract.address, + await getWalletOnPxe(3), + ), }, }), ]; const receipts = await Promise.all(txs.map(tx => tx.wait({ timeout: txTimeoutSec }))); expect(receipts.every(r => r.status === TxStatus.MINED)).toBe(true); - }, 1_200_000); + }); + + function getWalletOnPxe(idx: number): Promise { + return getSchnorrWallet(provingPxes[idx], schnorrWalletAddress.address, schnorrWalletSigningKey); + } + + async function getTestContractOnPXE(idx: number): Promise { + const wallet = await getWalletOnPxe(idx); + return TestContract.at(initialTestContract.address, wallet); + } }); diff --git a/yarn-project/prover-client/src/stats.ts b/yarn-project/prover-client/src/stats.ts deleted file mode 100644 index fbd68bde90be..000000000000 --- a/yarn-project/prover-client/src/stats.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { type PublicKernelRequest, PublicKernelType } from '@aztec/circuit-types'; -import type { CircuitName, CircuitProvingStats, CircuitWitnessGenerationStats } from '@aztec/circuit-types/stats'; -import { type Logger } from '@aztec/foundation/log'; -import { type ServerProtocolArtifact } from '@aztec/noir-protocol-circuits-types'; - -export function emitCircuitWitnessGenerationStats( - circuitName: CircuitName, - duration: number, - inputSize: number, - outputSize: number, - logger: Logger, -) { - const stats: CircuitWitnessGenerationStats = { - eventName: 'circuit-witness-generation', - circuitName, - inputSize, - outputSize, - duration, - }; - - logger.debug('Circuit witness generation stats', stats); -} - -export function emitCircuitProvingStats( - circuitName: CircuitName, - duration: number, - inputSize: number, - outputSize: number, - proofSize: number, - logger: Logger, -) { - const stats: CircuitProvingStats = { - eventName: 'circuit-proving', - circuitName, - duration, - inputSize, - outputSize, - proofSize, - }; - - logger.debug('Circuit proving stats', stats); -} - -export function mapPublicKernelToCircuitName(kernelType: PublicKernelRequest['type']): CircuitName { - switch (kernelType) { - case PublicKernelType.SETUP: - return 'public-kernel-setup'; - case PublicKernelType.APP_LOGIC: - return 'public-kernel-app-logic'; - case PublicKernelType.TEARDOWN: - return 'public-kernel-teardown'; - case PublicKernelType.TAIL: - return 'public-kernel-tail'; - default: - throw new Error(`Unknown kernel type: ${kernelType}`); - } -} - -export function circuitTypeToCircuitName(circuitType: ServerProtocolArtifact): CircuitName { - switch (circuitType) { - case 'BaseParityArtifact': - return 'base-parity'; - case 'RootParityArtifact': - return 'root-parity'; - case 'BaseRollupArtifact': - return 'base-rollup'; - case 'MergeRollupArtifact': - return 'merge-rollup'; - case 'RootRollupArtifact': - return 'root-rollup'; - case 'PublicKernelSetupArtifact': - return 'public-kernel-setup'; - case 'PublicKernelAppLogicArtifact': - return 'public-kernel-app-logic'; - case 'PublicKernelTeardownArtifact': - return 'public-kernel-teardown'; - case 'PublicKernelTailArtifact': - return 'public-kernel-tail'; - default: - throw new Error(`Unknown circuit type: ${circuitType}`); - } -} diff --git a/yarn-project/pxe/src/kernel_oracle/index.ts b/yarn-project/pxe/src/kernel_oracle/index.ts index 54b848c55315..6fd50855f5f4 100644 --- a/yarn-project/pxe/src/kernel_oracle/index.ts +++ b/yarn-project/pxe/src/kernel_oracle/index.ts @@ -64,4 +64,19 @@ export class KernelOracle implements ProvingDataOracle { public getMasterNullifierSecretKey(nullifierPublicKey: Point) { return this.keyStore.getMasterNullifierSecretKeyForPublicKey(nullifierPublicKey); } + + public async getFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise { + try { + const contractInstance = await this.contractDataOracle.getContractInstance(contractAddress); + + const [contractArtifact, functionArtifact] = await Promise.all([ + this.contractDataOracle.getContractArtifact(contractInstance.contractClassId), + this.contractDataOracle.getFunctionArtifact(contractAddress, selector), + ]); + + return `${contractArtifact.name}:${functionArtifact.name}`; + } catch (e) { + return undefined; + } + } } diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts index aa180e23b033..b14c48c74735 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.ts @@ -81,9 +81,15 @@ export class KernelProver { ? CallRequest.empty() : currentExecution.publicTeardownFunctionCall.toCallRequest(); + const functionName = await this.oracle.getFunctionName( + currentExecution.callStackItem.contractAddress, + currentExecution.callStackItem.functionData.selector, + ); + const proofOutput = await this.proofCreator.createAppCircuitProof( currentExecution.partialWitness, currentExecution.acir, + functionName, ); const privateCallData = await this.createPrivateCallData( diff --git a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts index 04af3cad3edc..f1dc0b39dda1 100644 --- a/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts +++ b/yarn-project/pxe/src/kernel_prover/proving_data_oracle.ts @@ -76,4 +76,6 @@ export interface ProvingDataOracle { * @returns the master nullifier secret key. */ getMasterNullifierSecretKey(nullifierPublicKey: Point): Promise; + + getFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise; } diff --git a/yarn-project/pxe/src/kernel_prover/test/test_circuit_prover.ts b/yarn-project/pxe/src/kernel_prover/test/test_circuit_prover.ts index ee18de6092dd..dcca119add28 100644 --- a/yarn-project/pxe/src/kernel_prover/test/test_circuit_prover.ts +++ b/yarn-project/pxe/src/kernel_prover/test/test_circuit_prover.ts @@ -68,7 +68,7 @@ export class TestProofCreator implements ProofCreator { ); this.log.debug(`Simulated private kernel ordering`, { eventName: 'circuit-simulation', - circuitName: 'private-kernel-ordering', + circuitName: 'private-kernel-tail', duration, inputSize: privateInputs.toBuffer().length, outputSize: result.toBuffer().length, diff --git a/yarn-project/scripts/src/benchmarks/aggregate.ts b/yarn-project/scripts/src/benchmarks/aggregate.ts index f0bf75811f93..078767af88c6 100644 --- a/yarn-project/scripts/src/benchmarks/aggregate.ts +++ b/yarn-project/scripts/src/benchmarks/aggregate.ts @@ -97,13 +97,20 @@ function processRollupBlockSynced(entry: L2BlockHandledStats, results: Benchmark * Buckets are circuit names */ function processCircuitSimulation(entry: CircuitSimulationStats, results: BenchmarkCollectedResults) { - const bucket = entry.circuitName; - if (!bucket) { - return; + if (entry.circuitName === 'private-app-circuit') { + const bucket = entry.appCircuitName; + if (!bucket) { + return; + } + append(results, 'app_circuit_simulation_time_in_ms', bucket, entry.duration); + append(results, 'app_circuit_input_size_in_bytes', bucket, entry.inputSize); + append(results, 'app_circuit_output_size_in_bytes', bucket, entry.outputSize); + } else { + const bucket = entry.circuitName; + append(results, 'kernel_circuit_simulation_time_in_ms', bucket, entry.duration); + append(results, 'kernel_circuit_input_size_in_bytes', bucket, entry.inputSize); + append(results, 'kernel_circuit_output_size_in_bytes', bucket, entry.outputSize); } - append(results, 'circuit_simulation_time_in_ms', bucket, entry.duration); - append(results, 'circuit_input_size_in_bytes', bucket, entry.inputSize); - append(results, 'circuit_output_size_in_bytes', bucket, entry.outputSize); } /** @@ -111,12 +118,20 @@ function processCircuitSimulation(entry: CircuitSimulationStats, results: Benchm * Buckets are circuit names */ function processCircuitProving(entry: CircuitProvingStats, results: BenchmarkCollectedResults) { - const bucket = entry.circuitName; - if (!bucket) { - return; + if (entry.circuitName === 'private-app-circuit') { + const bucket = entry.appCircuitName; + if (!bucket) { + return; + } + append(results, 'app_circuit_proving_time_in_ms', bucket, entry.duration); + append(results, 'app_circuit_proof_size_in_bytes', bucket, entry.proofSize); + append(results, 'app_circuit_size_in_gates', bucket, entry.circuitSize); + } else { + const bucket = entry.circuitName; + append(results, 'kernel_circuit_proving_time_in_ms', bucket, entry.duration); + append(results, 'kernel_circuit_proof_size_in_bytes', bucket, entry.proofSize); + append(results, 'kernel_circuit_size_in_gates', bucket, entry.circuitSize); } - append(results, 'circuit_proving_time_in_ms', bucket, entry.duration); - append(results, 'circuit_proof_size_in_bytes', bucket, entry.proofSize); } /** @@ -124,11 +139,16 @@ function processCircuitProving(entry: CircuitProvingStats, results: BenchmarkCol * Buckets are circuit names */ function processCircuitWitnessGeneration(entry: CircuitWitnessGenerationStats, results: BenchmarkCollectedResults) { - const bucket = entry.circuitName; - if (!bucket) { - return; + if (entry.circuitName === 'private-app-circuit') { + const bucket = entry.appCircuitName; + if (!bucket) { + return; + } + append(results, 'app_circuit_witness_generation_time_in_ms', bucket, entry.duration); + } else { + const bucket = entry.circuitName; + append(results, 'kernel_circuit_witness_generation_time_in_ms', bucket, entry.duration); } - append(results, 'circuit_witness_generation_time_in_ms', bucket, entry.duration); } /** * Processes an entry with event name 'note-processor-caught-up' and updates results diff --git a/yarn-project/scripts/src/benchmarks/markdown.ts b/yarn-project/scripts/src/benchmarks/markdown.ts index 772238679a94..2f9155099bf6 100644 --- a/yarn-project/scripts/src/benchmarks/markdown.ts +++ b/yarn-project/scripts/src/benchmarks/markdown.ts @@ -185,7 +185,8 @@ export function getMarkdown(prNumber: number) { const metricsByBlockSize = Metrics.filter(m => m.groupBy === 'block-size').map(m => m.name); const metricsByChainLength = Metrics.filter(m => m.groupBy === 'chain-length').map(m => m.name); - const metricsByCircuitName = Metrics.filter(m => m.groupBy === 'circuit-name').map(m => m.name); + const kernelCircuitMetrics = Metrics.filter(m => m.groupBy === 'kernel-circuit-name').map(m => m.name); + const appCircuitMetrics = Metrics.filter(m => m.groupBy === 'app-circuit-name').map(m => m.name); const metricsByClassesRegistered = Metrics.filter(m => m.groupBy === 'classes-registered').map(m => m.name); const metricsByFeePaymentMethod = Metrics.filter(m => m.groupBy === 'fee-payment-method').map(m => m.name); const metricsByLeafCount = Metrics.filter(m => m.groupBy === 'leaf-count').map(m => m.name); @@ -229,8 +230,11 @@ ${getTableContent(pick(benchmark, metricsByChainLength), baseBenchmark, 'blocks' ### Circuits stats -Stats on running time and I/O sizes collected for every circuit run across all benchmarks. -${getTableContent(transpose(pick(benchmark, metricsByCircuitName)), transpose(baseBenchmark), '', 'Circuit')} +Stats on running time and I/O sizes collected for every kernel circuit run across all benchmarks. +${getTableContent(transpose(pick(benchmark, kernelCircuitMetrics)), transpose(baseBenchmark), '', 'Circuit')} + +Stats on running time collected for app circuits +${getTableContent(transpose(pick(benchmark, appCircuitMetrics)), transpose(baseBenchmark), '', 'Function')} ### Tree insertion stats