Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Client ivc proof verification in sequencer #7553

Merged
merged 2 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions yarn-project/bb-prover/src/bb/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<BBFailure | BBSuccess> {
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
Expand Down
56 changes: 44 additions & 12 deletions yarn-project/bb-prover/src/verifier/bb_verifier.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
}
Expand All @@ -128,19 +136,43 @@ export class BBCircuitVerifier implements ClientProtocolCircuitVerifier {
return fs.readFile(result.contractPath!, 'utf-8');
}

verifyProof(tx: Tx): Promise<boolean> {
const expectedCircuit: ClientProtocolArtifact = tx.data.forPublic
? 'PrivateKernelTailToPublicArtifact'
: 'PrivateKernelTailArtifact';

public async verifyProof(tx: Tx): Promise<boolean> {
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;
}
}
}
12 changes: 12 additions & 0 deletions yarn-project/circuit-types/src/stats/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down
24 changes: 16 additions & 8 deletions yarn-project/end-to-end/src/benchmarks/bench_prover.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';

Expand Down Expand Up @@ -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(),
Expand Down
Loading