diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index b33da49057c0..236595b152eb 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -82,7 +82,7 @@ std::string honk_vk_to_json(std::vector& data) * * @param bytecodePath Path to the file containing the serialized circuit * @param witnessPath Path to the file containing the serialized witness - * @param recursive Whether to use recursive proof generation of non-recursive + * @param recursive Whether to use recursive proof generation or non-recursive * @return true if the proof is valid * @return false if the proof is invalid */ @@ -215,7 +215,7 @@ void prove_tube(const std::string& output_path) std::string vkPath = output_path + "/client_ivc_vk"; std::string proofPath = output_path + "/client_ivc_proof"; - // Note: this could be decreased once we optimise the size of the ClientIVC recursiveve rifier + // Note: this could be decreased once we optimise the size of the ClientIVC recursive verifier init_bn254_crs(1 << 25); init_grumpkin_crs(1 << 18); @@ -224,7 +224,7 @@ void prove_tube(const std::string& output_path) auto vk = from_buffer(read_file(vkPath)); // We don't serialise and deserialise the Grumkin SRS so initialise with circuit_size + 1 to be able to recursively - // IPA. The + 1 is to satisfy IPA verification key requirements. + // verify IPA. The + 1 is to satisfy IPA verification key requirements. // TODO(https://github.com/AztecProtocol/barretenberg/issues/1025) vk.eccvm->pcs_verification_key = std::make_shared(vk.eccvm->circuit_size + 1); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index 1789562498d8..a7a63bc2a853 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -487,7 +487,7 @@ void process_ivc_recursion_constraints(MegaCircuitBuilder& builder, ivc->instantiate_stdlib_verification_queue(builder, stdlib_verification_keys); // Connect the public_input witnesses in each constraint to the corresponding public input witnesses in the internal - // verification queue. This ensures that the witnesses utlized in constraints generated based on acir are properly + // verification queue. This ensures that the witnesses utilized in constraints generated based on acir are properly // connected to the constraints generated herein via the ivc scheme (e.g. recursive verifications). for (auto [constraint, queue_entry] : zip_view(constraints.ivc_recursion_constraints, ivc->stdlib_verification_queue)) { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index 980c57eec921..9b8c3358774a 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -453,3 +453,23 @@ WASM_EXPORT void acir_vk_as_fields_mega_honk(uint8_t const* vk_buf, fr::vec_out_ std::vector vkey_as_fields = verification_key->to_field_elements(); *out_vkey = to_heap_buffer(vkey_as_fields); } + +WASM_EXPORT void acir_gates_aztec_client(uint8_t const* acir_stack, uint8_t** out) +{ + + std::vector> acirs = from_buffer>>(acir_stack); + std::vector totals; + + TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE }; + auto ivc = std::make_shared(trace_settings); + const acir_format::ProgramMetadata metadata{ ivc }; + for (auto& bincode : acirs) { + acir_format::AcirProgram program{ acir_format::circuit_buf_to_acir_format(bincode, /*honk_recursion=*/0) }; + auto builder = acir_format::create_circuit(program, metadata); + builder.finalize_circuit(/*ensure_nonzero=*/true); + totals.push_back(static_cast(builder.get_finalized_total_circuit_size())); + } + auto totalsBytes = to_buffer(totals); + + *out = to_heap_buffer(totals); +} diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 64debff164ef..033579e4515e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -113,4 +113,6 @@ WASM_EXPORT void acir_proof_as_fields_ultra_honk(uint8_t const* proof_buf, fr::v WASM_EXPORT void acir_vk_as_fields_ultra_honk(uint8_t const* vk_buf, fr::vec_out_buf out_vkey); -WASM_EXPORT void acir_vk_as_fields_mega_honk(uint8_t const* vk_buf, fr::vec_out_buf out_vkey); \ No newline at end of file +WASM_EXPORT void acir_vk_as_fields_mega_honk(uint8_t const* vk_buf, fr::vec_out_buf out_vkey); + +WASM_EXPORT void acir_gates_aztec_client(uint8_t const* acir_stack, uint8_t** out); \ No newline at end of file diff --git a/barretenberg/exports.json b/barretenberg/exports.json index 83b2a64ec71e..ec246bb837b2 100644 --- a/barretenberg/exports.json +++ b/barretenberg/exports.json @@ -530,6 +530,20 @@ ], "isAsync": false }, + { + "functionName": "acir_gates_client_ivc", + "inArgs": [ + { + "name": "acir_stack", + "type": "const uint8_t *" + }, + { + "name": "totals", + "type": "uint8_t **" + } + ], + "isAsync": false + }, { "functionName": "acir_new_acir_composer", "inArgs": [ diff --git a/barretenberg/ts/src/barretenberg/backend.ts b/barretenberg/ts/src/barretenberg/backend.ts index ef001ed362b9..4b0cbf1c23f3 100644 --- a/barretenberg/ts/src/barretenberg/backend.ts +++ b/barretenberg/ts/src/barretenberg/backend.ts @@ -391,6 +391,12 @@ export class AztecClientBackend { return this.api.acirProveAndVerifyAztecClient(this.acirMsgpack, witnessMsgpack); } + async gates(): Promise { + // call function on API + await this.instantiate(); + return this.api.acirGatesAztecClient(this.acirMsgpack); + } + async destroy(): Promise { if (!this.api) { return; diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index 2b08aaa90df4..523d1899a152 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -12,6 +12,28 @@ import { OutputType, } from '../serialize/index.js'; import { Fr, Fq, Point, Buffer32, Buffer128, Ptr } from '../types/index.js'; +function parseBigEndianU32Array(buffer: Uint8Array, hasSizePrefix = false): number[] { + const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); + + let offset = 0; + let count = buffer.byteLength >>> 2; // default is entire buffer length / 4 + + if (hasSizePrefix) { + // Read the first 4 bytes as the size (big-endian). + count = dv.getUint32(0, /* littleEndian= */ false); + offset = 4; + } + + const out: number[] = new Array(count); + for (let i = 0; i < count; i++) { + out[i] = dv.getUint32(offset, false); + offset += 4; + } + + return out; +} +import createDebug from 'debug'; +const log = createDebug('index-ts'); export class BarretenbergApi { constructor(protected wasm: BarretenbergWasmWorker | BarretenbergWasmMain) {} @@ -357,6 +379,21 @@ export class BarretenbergApi { return out as [number, number]; } + async acirGatesAztecClient( + // cf acirProveAztecClient + acirVec: Uint8Array[], + ): Promise { + const inArgs = [acirVec].map(serializeBufferable); + const outTypes: OutputType[] = [BufferDeserializer()]; + const resultBuffer = await this.wasm.callWasmExport( + 'acir_gates_aztec_client', + inArgs, + outTypes.map(t => t.SIZE_IN_BYTES), + ); + + return parseBigEndianU32Array(resultBuffer[0], /*hasSizePrefix=*/ true); + } + async acirNewAcirComposer(sizeHint: number): Promise { const inArgs = [sizeHint].map(serializeBufferable); const outTypes: OutputType[] = [Ptr]; diff --git a/yarn-project/ivc-integration/src/wasm_client_ivc_integration.test.ts b/yarn-project/ivc-integration/src/wasm_client_ivc_integration.test.ts index ce175e8d1e24..c2537a0b48fa 100644 --- a/yarn-project/ivc-integration/src/wasm_client_ivc_integration.test.ts +++ b/yarn-project/ivc-integration/src/wasm_client_ivc_integration.test.ts @@ -1,7 +1,10 @@ +import { AztecClientBackend } from '@aztec/bb.js'; + import { jest } from '@jest/globals'; /* eslint-disable camelcase */ import createDebug from 'debug'; +import { ungzip } from 'pako'; import { MOCK_MAX_COMMITMENTS_PER_TX, @@ -65,6 +68,7 @@ describe('Client IVC Integration', () => { MockPrivateKernelInitCircuit.bytecode, MockPrivateKernelTailCircuit.bytecode, ]; + logger('built bytecode array'); const witnessStack = [appWitnessGenResult.witness, initWitnessGenResult.witness, tailWitnessGenResult.witness]; logger('built witness stack'); @@ -75,6 +79,25 @@ describe('Client IVC Integration', () => { expect(verifyResult).toEqual(true); }); + it('Should generate an array of gate numbers for the stack of programs being proved by ClientIVC', async () => { + // Create ACIR bytecodes + const bytecodes = [ + MockAppCreatorCircuit.bytecode, + MockPrivateKernelInitCircuit.bytecode, + MockPrivateKernelTailCircuit.bytecode, + ]; + + // Initialize AztecClientBackend with the given bytecodes + const backend = new AztecClientBackend(bytecodes.map(base64ToUint8Array).map((arr: Uint8Array) => ungzip(arr))); + + // Compute the numbers of gates in each circuit + const gateNumbers = await backend.gates(); + await backend.destroy(); + logger('Gate numbers for each circuit:', gateNumbers); + // STARTER: add a test here instantiate an AztecClientBackend with the above bytecodes, call gates, and check they're correct (maybe just + // eyeball against logs to start... better is to make another test that actually pins the sizes since the mock protocol circuits are + // intended not to change, though for sure there will be some friction, and such test should actually just be located in barretenberg/ts) + }); // This test will verify a client IVC proof of a more complex tx: // 1. Run a mock app that creates two commitments // 2. Run the init kernel to process the app run @@ -142,3 +165,6 @@ describe('Client IVC Integration', () => { expect(verifyResult).toEqual(true); }); }); +function base64ToUint8Array(base64: string): Uint8Array { + return Uint8Array.from(atob(base64), c => c.charCodeAt(0)); +}