diff --git a/yarn-project/bb-prover/src/bb/execute.ts b/yarn-project/bb-prover/src/bb/execute.ts index d682e563fc5..4021001da96 100644 --- a/yarn-project/bb-prover/src/bb/execute.ts +++ b/yarn-project/bb-prover/src/bb/execute.ts @@ -621,6 +621,46 @@ export async function verifyAvmProof( return await verifyProofInternal(pathToBB, proofFullPath, verificationKeyPath, 'avm_verify', log); } +/** + * Verifies a ClientIvcProof + * TODO(#7370) The verification keys should be supplied separately + * @param pathToBB - The full path to the bb binary + * @param targetPath - The path to the folder with the proof, accumulator, and verification keys + * @param log - A logging function + * @returns An object containing a result indication and duration taken + */ +export async function verifyClientIvcProof( + pathToBB: string, + targetPath: string, + log: LogFn, +): Promise { + const binaryPresent = await fs + .access(pathToBB, fs.constants.R_OK) + .then(_ => true) + .catch(_ => false); + if (!binaryPresent) { + return { status: BB_RESULT.FAILURE, reason: `Failed to find bb binary at ${pathToBB}` }; + } + + try { + const args = ['-o', targetPath]; + const timer = new Timer(); + const command = 'verify_client_ivc'; + const result = await executeBB(pathToBB, command, args, log); + const duration = timer.ms(); + if (result.status == BB_RESULT.SUCCESS) { + return { status: BB_RESULT.SUCCESS, durationMs: duration }; + } + // Not a great error message here but it is difficult to decipher what comes from bb + return { + status: BB_RESULT.FAILURE, + reason: `Failed to verify proof. Exit code ${result.exitCode}. Signal ${result.signal}.`, + }; + } catch (error) { + return { status: BB_RESULT.FAILURE, reason: `${error}` }; + } +} + /** * Used for verifying proofs with BB * @param pathToBB - The full path to the bb binary diff --git a/yarn-project/bb-prover/src/verifier/bb_verifier.ts b/yarn-project/bb-prover/src/verifier/bb_verifier.ts index 3c13a699948..b09c2d981c5 100644 --- a/yarn-project/bb-prover/src/verifier/bb_verifier.ts +++ b/yarn-project/bb-prover/src/verifier/bb_verifier.ts @@ -1,4 +1,5 @@ import { type ClientProtocolCircuitVerifier, Tx } from '@aztec/circuit-types'; +import { type CircuitVerificationStats } from '@aztec/circuit-types/stats'; import { type Proof, type VerificationKeyData } from '@aztec/circuits.js'; import { runInDirectory } from '@aztec/foundation/fs'; import { type DebugLogger, type LogFn, createDebugLogger } from '@aztec/foundation/log'; @@ -17,9 +18,11 @@ import { VK_FILENAME, generateContractForCircuit, generateKeyForNoirCircuit, + verifyClientIvcProof, verifyProof, } from '../bb/execute.js'; import { type BBConfig } from '../config.js'; +import { mapProtocolArtifactNameToCircuitName } from '../stats.js'; import { extractVkData } from '../verification_key/verification_key_data.js'; export class BBCircuitVerifier implements ClientProtocolCircuitVerifier { @@ -106,7 +109,12 @@ export class BBCircuitVerifier implements ClientProtocolCircuitVerifier { throw new Error(errorMessage); } - this.logger.debug(`${circuit} verification successful`); + this.logger.debug(`${circuit} verification successful`, { + circuitName: mapProtocolArtifactNameToCircuitName(circuit), + duration: result.durationMs, + eventName: 'circuit-verification', + proofType: 'ultra-honk', + } satisfies CircuitVerificationStats); }; await runInDirectory(this.config.bbWorkingDirectory, operation); } @@ -128,19 +136,43 @@ export class BBCircuitVerifier implements ClientProtocolCircuitVerifier { return fs.readFile(result.contractPath!, 'utf-8'); } - verifyProof(tx: Tx): Promise { - const expectedCircuit: ClientProtocolArtifact = tx.data.forPublic - ? 'PrivateKernelTailToPublicArtifact' - : 'PrivateKernelTailArtifact'; - + public async verifyProof(tx: Tx): Promise { try { - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1050) we need a proper verify flow for clientIvcProof - // For now we handle only the trivial blank data case - // await this.verifyProofForCircuit(expectedCircuit, proof); - return Promise.resolve(!tx.clientIvcProof.isEmpty()); + // TODO(#7370) The verification keys should be supplied separately and based on the expectedCircuit + // rather than read from the tx object itself. We also need the vks for the translator and ecc, which + // are not being saved along the other vks yet. Reuse the 'verifyProofForCircuit' method above once + // we have all the verification keys available. + const expectedCircuit: ClientProtocolArtifact = tx.data.forPublic + ? 'PrivateKernelTailToPublicArtifact' + : 'PrivateKernelTailArtifact'; + const circuit = 'ClientIVC'; + + // Block below is almost copy-pasted from verifyProofForCircuit + const operation = async (bbWorkingDirectory: string) => { + const logFunction = (message: string) => { + this.logger.debug(`${circuit} BB out - ${message}`); + }; + + await tx.clientIvcProof.writeToOutputDirectory(bbWorkingDirectory); + const result = await verifyClientIvcProof(this.config.bbBinaryPath, bbWorkingDirectory, logFunction); + + if (result.status === BB_RESULT.FAILURE) { + const errorMessage = `Failed to verify ${circuit} proof!`; + throw new Error(errorMessage); + } + + this.logger.debug(`${circuit} verification successful`, { + circuitName: mapProtocolArtifactNameToCircuitName(expectedCircuit), + duration: result.durationMs, + eventName: 'circuit-verification', + proofType: 'client-ivc', + } satisfies CircuitVerificationStats); + }; + await runInDirectory(this.config.bbWorkingDirectory, operation); + return true; } catch (err) { - this.logger.warn(`Failed to verify ${expectedCircuit} proof for tx ${Tx.getHash(tx)}: ${String(err)}`); - return Promise.resolve(false); + this.logger.warn(`Failed to verify ClientIVC proof for tx ${Tx.getHash(tx)}: ${String(err)}`); + return false; } } } diff --git a/yarn-project/circuit-types/src/stats/stats.ts b/yarn-project/circuit-types/src/stats/stats.ts index ac87473565e..b94647e0948 100644 --- a/yarn-project/circuit-types/src/stats/stats.ts +++ b/yarn-project/circuit-types/src/stats/stats.ts @@ -164,6 +164,18 @@ export type CircuitProvingStats = { numPublicInputs: number; }; +/** Stats for verifying a circuit */ +export type CircuitVerificationStats = { + /** Name of the event. */ + eventName: 'circuit-verification'; + /** Name of the circuit. */ + circuitName: CircuitName; + /** Type of proof (client-ivc, honk, etc) */ + proofType: 'client-ivc' | 'ultra-honk'; + /** Duration in ms. */ + duration: number; +}; + /** Stats for an L2 block built by a sequencer. */ export type L2BlockBuiltStats = { /** Name of the event. */ diff --git a/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts index 7a714d1a343..c3b04643c88 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts @@ -1,6 +1,7 @@ import { getSchnorrAccount, getSchnorrWallet } from '@aztec/accounts/schnorr'; import { PublicFeePaymentMethod, TxStatus, sleep } from '@aztec/aztec.js'; import { type AccountWallet } from '@aztec/aztec.js/wallet'; +import { BBCircuitVerifier } from '@aztec/bb-prover'; import { CompleteAddress, Fq, Fr, GasSettings } from '@aztec/circuits.js'; import { FPCContract, GasTokenContract, TestContract, TokenContract } from '@aztec/noir-contracts.js'; import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token'; @@ -18,6 +19,11 @@ jest.setTimeout(1_800_000); const txTimeoutSec = 3600; +// How many times we'll run bb verify on each tx for benchmarking purposes +const txVerifyIterations = process.env.BENCH_TX_VERIFY_ITERATIONS + ? parseInt(process.env.BENCH_TX_VERIFY_ITERATIONS) + : 10; + // This makes AVM proving throw if there's a failure. process.env.AVM_PROVING_STRICT = '1'; @@ -191,22 +197,24 @@ describe('benchmarks/proving', () => { // }; ctx.logger.info('Proving transactions'); - await Promise.all([ - fnCalls[0].prove({ - fee: feeFnCall0, - }), + const provenTxs = await Promise.all([ + fnCalls[0].prove({ fee: feeFnCall0 }), fnCalls[1].prove(), // fnCalls[2].prove(), // fnCalls[3].prove(), ]); - ctx.logger.info('Finished proving'); + ctx.logger.info('Verifying transactions client proofs'); + const verifier = await BBCircuitVerifier.new((await getBBConfig(ctx.logger))!); + for (let i = 0; i < txVerifyIterations; i++) { + for (const tx of provenTxs) { + expect(await verifier.verifyProof(tx)).toBe(true); + } + } ctx.logger.info('Sending transactions'); const txs = [ - fnCalls[0].send({ - fee: feeFnCall0, - }), + fnCalls[0].send({ fee: feeFnCall0 }), fnCalls[1].send(), // fnCalls[2].send(), // fnCalls[3].send(),