diff --git a/barretenberg/cpp/src/barretenberg/vm/avm/tests/verification_key.test.cpp b/barretenberg/cpp/src/barretenberg/vm/avm/tests/verification_key.test.cpp new file mode 100644 index 000000000000..9d143b336bd8 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/vm/avm/tests/verification_key.test.cpp @@ -0,0 +1,25 @@ +#include "barretenberg/vm/avm/generated/flavor.hpp" +#include "barretenberg/vm/avm/tests/helpers.test.hpp" +#include "barretenberg/vm/avm/trace/helper.hpp" +#include "barretenberg/vm/aztec_constants.hpp" +#include "barretenberg/vm/constants.hpp" +#include "common.test.hpp" + +namespace tests_avm { + +using namespace bb; + +TEST(AvmTests, VerificationKeySizeMatches) +{ + srs::init_crs_factory("../srs_db/ignition"); + + auto circuit_builder = AvmCircuitBuilder(); + circuit_builder.set_trace({}); + + AvmComposer composer = AvmComposer(); + AvmVerifier verifier = composer.create_verifier(circuit_builder); + + EXPECT_EQ(verifier.key->size(), AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS); +} + +} // namespace tests_avm \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp b/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp index 09119cec6991..bf18a50bcd3c 100644 --- a/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp +++ b/barretenberg/cpp/src/barretenberg/vm/aztec_constants.hpp @@ -33,6 +33,7 @@ #define HEADER_LENGTH 24 #define PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH 691 #define PUBLIC_CONTEXT_INPUTS_LENGTH 42 +#define AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS 16 #define SENDER_SELECTOR 0 #define ADDRESS_SELECTOR 1 #define STORAGE_ADDRESS_SELECTOR 1 diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index ebb231a48959..6122fa3b0113 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -290,6 +290,7 @@ global NESTED_RECURSIVE_PROOF_LENGTH = 439; global TUBE_PROOF_LENGTH = RECURSIVE_PROOF_LENGTH; // in the future these can differ global VERIFICATION_KEY_LENGTH_IN_FIELDS = 128; +global AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS = 16; /** * Enumerate the hash_indices which are used for pedersen hashing. diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index dcd09631a13d..2c38db4869eb 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -1,6 +1,6 @@ /* eslint-disable require-await */ import { - type ProofAndVerificationKey, + type AvmProofAndVerificationKey, type PublicInputsAndRecursiveProof, type PublicKernelNonTailRequest, type PublicKernelTailRequest, @@ -11,6 +11,7 @@ import { type CircuitProvingStats, type CircuitWitnessGenerationStats } from '@a import { AGGREGATION_OBJECT_LENGTH, type AvmCircuitInputs, + type AvmVerificationKeyData, type BaseOrMergeRollupPublicInputs, type BaseParityInputs, type BaseRollupInputs, @@ -94,7 +95,7 @@ import type { ACVMConfig, BBConfig } from '../config.js'; import { ProverInstrumentation } from '../instrumentation.js'; import { PublicKernelArtifactMapping } from '../mappings/mappings.js'; import { mapProtocolArtifactNameToCircuitName } from '../stats.js'; -import { extractVkData } from '../verification_key/verification_key_data.js'; +import { extractAvmVkData, extractVkData } from '../verification_key/verification_key_data.js'; const logger = createDebugLogger('aztec:bb-prover'); @@ -202,7 +203,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { @trackSpan('BBNativeRollupProver.getAvmProof', inputs => ({ [Attributes.APP_CIRCUIT_NAME]: inputs.functionName, })) - public async getAvmProof(inputs: AvmCircuitInputs): Promise { + public async getAvmProof(inputs: AvmCircuitInputs): Promise { const proofAndVk = await this.createAvmProof(inputs); await this.verifyAvmProof(proofAndVk.proof, proofAndVk.verificationKey); return proofAndVk; @@ -626,14 +627,14 @@ export class BBNativeRollupProver implements ServerCircuitProver { return provingResult; } - private async createAvmProof(input: AvmCircuitInputs): Promise { - const operation = async (bbWorkingDirectory: string): Promise => { + private async createAvmProof(input: AvmCircuitInputs): Promise { + const operation = async (bbWorkingDirectory: string): Promise => { const provingResult = await this.generateAvmProofWithBB(input, bbWorkingDirectory); const rawProof = await fs.readFile(provingResult.proofPath!); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/6773): this VK data format is wrong. // In particular, the number of public inputs, etc will be wrong. - const verificationKey = await extractVkData(provingResult.vkPath!); + const verificationKey = await extractAvmVkData(provingResult.vkPath!); const proof = new Proof(rawProof, verificationKey.numPublicInputs); const circuitType = 'avm-circuit' as const; @@ -765,7 +766,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { return await this.verifyWithKey(verificationKey, proof); } - public async verifyAvmProof(proof: Proof, verificationKey: VerificationKeyData) { + public async verifyAvmProof(proof: Proof, verificationKey: AvmVerificationKeyData) { return await this.verifyWithKeyInternal(proof, verificationKey, verifyAvmProof); } @@ -775,7 +776,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { private async verifyWithKeyInternal( proof: Proof, - verificationKey: VerificationKeyData, + verificationKey: { keyAsBytes: Buffer }, verificationFunction: VerificationFunction, ) { const operation = async (bbWorkingDirectory: string) => { diff --git a/yarn-project/bb-prover/src/test/test_circuit_prover.ts b/yarn-project/bb-prover/src/test/test_circuit_prover.ts index ec8ed4ea3312..63a5d7cf1baa 100644 --- a/yarn-project/bb-prover/src/test/test_circuit_prover.ts +++ b/yarn-project/bb-prover/src/test/test_circuit_prover.ts @@ -1,5 +1,5 @@ import { - type ProofAndVerificationKey, + type AvmProofAndVerificationKey, type PublicInputsAndRecursiveProof, type PublicKernelNonTailRequest, type PublicKernelTailRequest, @@ -8,6 +8,7 @@ import { } from '@aztec/circuit-types'; import { type AvmCircuitInputs, + AvmVerificationKeyData, type BaseOrMergeRollupPublicInputs, type BaseParityInputs, type BaseRollupInputs, @@ -475,12 +476,12 @@ export class TestCircuitProver implements ServerCircuitProver { ); } - public async getAvmProof(_inputs: AvmCircuitInputs): Promise { + public async getAvmProof(_inputs: AvmCircuitInputs): Promise { // We can't simulate the AVM because we don't have enough context to do so (e.g., DBs). // We just return an empty proof and VK data. this.logger.debug('Skipping AVM simulation in TestCircuitProver.'); await this.delay(); - return { proof: makeEmptyProof(), verificationKey: VerificationKeyData.makeFake() }; + return { proof: makeEmptyProof(), verificationKey: AvmVerificationKeyData.makeFake() }; } private async delay(): Promise { diff --git a/yarn-project/bb-prover/src/verification_key/verification_key_data.ts b/yarn-project/bb-prover/src/verification_key/verification_key_data.ts index b2fe18cc5e18..41dc68c5c14f 100644 --- a/yarn-project/bb-prover/src/verification_key/verification_key_data.ts +++ b/yarn-project/bb-prover/src/verification_key/verification_key_data.ts @@ -1,4 +1,7 @@ import { + type AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, + AvmVerificationKeyAsFields, + AvmVerificationKeyData, Fr, type VERIFICATION_KEY_LENGTH_IN_FIELDS, VerificationKeyAsFields, @@ -29,3 +32,20 @@ export async function extractVkData(vkDirectoryPath: string): Promise { + const [rawFields, rawBinary] = await Promise.all([ + fs.readFile(path.join(vkDirectoryPath, VK_FIELDS_FILENAME), { encoding: 'utf-8' }), + fs.readFile(path.join(vkDirectoryPath, VK_FILENAME)), + ]); + const fieldsJson = JSON.parse(rawFields); + const fields = fieldsJson.map(Fr.fromString); + // The first item is the hash, this is not part of the actual VK + const vkHash = fields[0]; + const vkAsFields = new AvmVerificationKeyAsFields( + fields as Tuple, + vkHash, + ); + const vk = new AvmVerificationKeyData(vkAsFields, rawBinary); + return vk; +} diff --git a/yarn-project/circuit-types/src/interfaces/proving-job.ts b/yarn-project/circuit-types/src/interfaces/proving-job.ts index e496875bcf57..33127e4c5351 100644 --- a/yarn-project/circuit-types/src/interfaces/proving-job.ts +++ b/yarn-project/circuit-types/src/interfaces/proving-job.ts @@ -1,5 +1,6 @@ import { type AvmCircuitInputs, + type AvmVerificationKeyData, type BaseOrMergeRollupPublicInputs, type BaseParityInputs, type BaseRollupInputs, @@ -25,9 +26,9 @@ import { import type { PublicKernelNonTailRequest, PublicKernelTailRequest } from '../tx/processed_tx.js'; -export type ProofAndVerificationKey = { +export type AvmProofAndVerificationKey = { proof: Proof; - verificationKey: VerificationKeyData; + verificationKey: AvmVerificationKeyData; }; export type PublicInputsAndRecursiveProof = { @@ -133,7 +134,7 @@ export type ProvingRequest = export type ProvingRequestPublicInputs = { [ProvingRequestType.PRIVATE_KERNEL_EMPTY]: PublicInputsAndRecursiveProof; - [ProvingRequestType.PUBLIC_VM]: ProofAndVerificationKey; + [ProvingRequestType.PUBLIC_VM]: AvmProofAndVerificationKey; [ProvingRequestType.PUBLIC_KERNEL_NON_TAIL]: PublicInputsAndRecursiveProof; [ProvingRequestType.PUBLIC_KERNEL_TAIL]: PublicInputsAndRecursiveProof; diff --git a/yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts b/yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts index 60fc09fb9219..802cc5ea983d 100644 --- a/yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts +++ b/yarn-project/circuit-types/src/interfaces/server_circuit_prover.ts @@ -1,5 +1,5 @@ import { - type ProofAndVerificationKey, + type AvmProofAndVerificationKey, type PublicInputsAndRecursiveProof, type PublicInputsAndTubeProof, type PublicKernelNonTailRequest, @@ -149,7 +149,11 @@ export interface ServerCircuitProver { * Create a proof for the AVM circuit. * @param inputs - Inputs to the AVM circuit. */ - getAvmProof(inputs: AvmCircuitInputs, signal?: AbortSignal, epochNumber?: number): Promise; + getAvmProof( + inputs: AvmCircuitInputs, + signal?: AbortSignal, + epochNumber?: number, + ): Promise; } /** diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 306aca914065..494cf38c6954 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -202,6 +202,7 @@ export const RECURSIVE_PROOF_LENGTH = 439; export const NESTED_RECURSIVE_PROOF_LENGTH = 439; export const TUBE_PROOF_LENGTH = 439; export const VERIFICATION_KEY_LENGTH_IN_FIELDS = 128; +export const AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS = 16; export const SENDER_SELECTOR = 0; export const ADDRESS_SELECTOR = 1; export const STORAGE_ADDRESS_SELECTOR = 1; diff --git a/yarn-project/circuits.js/src/structs/verification_key.ts b/yarn-project/circuits.js/src/structs/verification_key.ts index 2baffbf8039d..12ac5f2e2dc9 100644 --- a/yarn-project/circuits.js/src/structs/verification_key.ts +++ b/yarn-project/circuits.js/src/structs/verification_key.ts @@ -3,7 +3,7 @@ import { times } from '@aztec/foundation/collection'; import { Fq, Fr } from '@aztec/foundation/fields'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; -import { VERIFICATION_KEY_LENGTH_IN_FIELDS } from '../constants.gen.js'; +import { AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, VERIFICATION_KEY_LENGTH_IN_FIELDS } from '../constants.gen.js'; import { CircuitType } from './shared.js'; /** @@ -135,6 +135,68 @@ export class VerificationKeyAsFields { } } +/** + * Provides a 'fields' representation of the AVM's verification key + */ +export class AvmVerificationKeyAsFields { + constructor(public key: Tuple, public hash: Fr) {} + + public get numPublicInputs() { + return Number(this.key[CIRCUIT_PUBLIC_INPUTS_INDEX]); + } + + public get circuitSize() { + return Number(this.key[CIRCUIT_SIZE_INDEX]); + } + + public get isRecursive() { + return this.key[CIRCUIT_RECURSIVE_INDEX] == Fr.ONE; + } + + /** + * Serialize as a buffer. + * @returns The buffer. + */ + toBuffer() { + return serializeToBuffer(this.key, this.hash); + } + toFields() { + return [...this.key, this.hash]; + } + + /** + * Deserializes from a buffer or reader, corresponding to a write in cpp. + * @param buffer - Buffer to read from. + * @returns The AvmVerificationKeyAsFields. + */ + static fromBuffer(buffer: Buffer | BufferReader): AvmVerificationKeyAsFields { + const reader = BufferReader.asReader(buffer); + return new AvmVerificationKeyAsFields( + reader.readArray(AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, Fr), + reader.readObject(Fr), + ); + } + + /** + * Builds a fake verification key that should be accepted by circuits. + * @returns A fake verification key. + */ + static makeFake(seed = 1): AvmVerificationKeyAsFields { + return new AvmVerificationKeyAsFields( + makeTuple(AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, Fr.random, seed), + Fr.random(), + ); + } + + /** + * Builds an 'empty' verification key + * @returns An 'empty' verification key + */ + static makeEmpty(): AvmVerificationKeyAsFields { + return new AvmVerificationKeyAsFields(makeTuple(AVM_VERIFICATION_KEY_LENGTH_IN_FIELDS, Fr.zero), Fr.zero()); + } +} + export class VerificationKey { constructor( /** @@ -257,3 +319,51 @@ export class VerificationKeyData { return VerificationKeyData.fromBuffer(this.toBuffer()); } } + +export class AvmVerificationKeyData { + constructor(public readonly keyAsFields: AvmVerificationKeyAsFields, public readonly keyAsBytes: Buffer) {} + + public get numPublicInputs() { + return this.keyAsFields.numPublicInputs; + } + + public get circuitSize() { + return this.keyAsFields.circuitSize; + } + + public get isRecursive() { + return this.keyAsFields.isRecursive; + } + + static makeFake(): AvmVerificationKeyData { + return new AvmVerificationKeyData(AvmVerificationKeyAsFields.makeFake(), VerificationKey.makeFake().toBuffer()); + } + + /** + * Serialize as a buffer. + * @returns The buffer. + */ + toBuffer() { + return serializeToBuffer(this.keyAsFields, this.keyAsBytes.length, this.keyAsBytes); + } + + toString() { + return this.toBuffer().toString('hex'); + } + + static fromBuffer(buffer: Buffer | BufferReader): AvmVerificationKeyData { + const reader = BufferReader.asReader(buffer); + const verificationKeyAsFields = reader.readObject(AvmVerificationKeyAsFields); + const length = reader.readNumber(); + const bytes = reader.readBytes(length); + return new AvmVerificationKeyData(verificationKeyAsFields, bytes); + } + + static fromString(str: string): AvmVerificationKeyData { + return AvmVerificationKeyData.fromBuffer(Buffer.from(str, 'hex')); + } + + public clone() { + return AvmVerificationKeyData.fromBuffer(this.toBuffer()); + } +} diff --git a/yarn-project/prover-client/src/prover-agent/memory-proving-queue.ts b/yarn-project/prover-client/src/prover-agent/memory-proving-queue.ts index be11c990a14e..7dc8e10012ad 100644 --- a/yarn-project/prover-client/src/prover-agent/memory-proving-queue.ts +++ b/yarn-project/prover-client/src/prover-agent/memory-proving-queue.ts @@ -1,5 +1,5 @@ import { - type ProofAndVerificationKey, + type AvmProofAndVerificationKey, type ProvingJob, type ProvingJobSource, type ProvingRequest, @@ -408,7 +408,11 @@ export class MemoryProvingQueue implements ServerCircuitProver, ProvingJobSource /** * Creates an AVM proof. */ - getAvmProof(inputs: AvmCircuitInputs, signal?: AbortSignal, epochNumber?: number): Promise { + getAvmProof( + inputs: AvmCircuitInputs, + signal?: AbortSignal, + epochNumber?: number, + ): Promise { return this.enqueue({ type: ProvingRequestType.PUBLIC_VM, inputs }, signal, epochNumber); }