Skip to content

Commit

Permalink
feat: CIVC browser proveThenVerify (#10431)
Browse files Browse the repository at this point in the history
Add c_binds for ClientIVC `prove` and `verify`. Wrap these in
`proveThenVerify` and use replace use of `proveAndVerify` by this in the
ivc-integration suite.
  • Loading branch information
codygunton authored Dec 10, 2024
1 parent 0379718 commit 8c064d4
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 69 deletions.
82 changes: 81 additions & 1 deletion barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "barretenberg/plonk/proof_system/proving_key/serialize.hpp"
#include "barretenberg/plonk/proof_system/verification_key/verification_key.hpp"
#include "barretenberg/serialize/msgpack.hpp"
#include "barretenberg/srs/global_crs.hpp"
#include "honk_contract.hpp"
#include <cstdint>
#include <memory>
Expand Down Expand Up @@ -274,6 +273,87 @@ WASM_EXPORT void acir_prove_and_verify_aztec_client(uint8_t const* acir_stack,
*verified = result;
}

WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack,
uint8_t const* witness_stack,
uint8_t** out_proof,
uint8_t** out_vk)
{
using Program = acir_format::AcirProgram;

std::vector<std::vector<uint8_t>> witnesses = from_buffer<std::vector<std::vector<uint8_t>>>(witness_stack);
std::vector<std::vector<uint8_t>> acirs = from_buffer<std::vector<std::vector<uint8_t>>>(acir_stack);
std::vector<Program> folding_stack;

for (auto [bincode, wit] : zip_view(acirs, witnesses)) {
acir_format::WitnessVector witness = acir_format::witness_buf_to_witness_data(wit);
acir_format::AcirFormat constraints =
acir_format::circuit_buf_to_acir_format(bincode, /*honk_recursion=*/false);
folding_stack.push_back(Program{ constraints, witness });
}
// TODO(#7371) dedupe this with the rest of the similar code
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1101): remove use of auto_verify_mode
ClientIVC ivc{ { CLIENT_IVC_BENCH_STRUCTURE }, /*auto_verify_mode=*/true };

// Accumulate the entire program stack into the IVC
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel once databus
// has been integrated into noir kernel programs
bool is_kernel = false;
auto start = std::chrono::steady_clock::now();
for (Program& program : folding_stack) {
// Construct a bberg circuit from the acir representation then accumulate it into the IVC
vinfo("constructing circuit...");
auto circuit = acir_format::create_circuit<MegaCircuitBuilder>(
program.constraints, false, 0, program.witness, false, ivc.goblin.op_queue);

// Set the internal is_kernel flag based on the local mechanism only if it has not already been set to true
if (!circuit.databus_propagation_data.is_kernel) {
circuit.databus_propagation_data.is_kernel = is_kernel;
}
is_kernel = !is_kernel;

vinfo("done constructing circuit. calling ivc.accumulate...");
ivc.accumulate(circuit);
vinfo("done accumulating.");
}
auto end = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
vinfo("time to construct and accumulate all circuits: ", diff.count());

vinfo("calling ivc.prove ...");
ClientIVC::Proof proof = ivc.prove();
end = std::chrono::steady_clock::now();
diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
vinfo("time to construct, accumulate, prove all circuits: ", diff.count());

start = std::chrono::steady_clock::now();
*out_proof = to_heap_buffer(to_buffer(proof));
end = std::chrono::steady_clock::now();
diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
vinfo("time to serialize proof: ", diff.count());

start = std::chrono::steady_clock::now();
auto eccvm_vk = std::make_shared<ECCVMFlavor::VerificationKey>(ivc.goblin.get_eccvm_proving_key());
auto translator_vk = std::make_shared<TranslatorFlavor::VerificationKey>(ivc.goblin.get_translator_proving_key());
*out_vk = to_heap_buffer(to_buffer(ClientIVC::VerificationKey{ ivc.honk_vk, eccvm_vk, translator_vk }));
end = std::chrono::steady_clock::now();
diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
vinfo("time to serialize vk: ", diff.count());
}

WASM_EXPORT void acir_verify_aztec_client(uint8_t const* proof_buf, uint8_t const* vk_buf, bool* result)
{

const auto proof = from_buffer<ClientIVC::Proof>(from_buffer<std::vector<uint8_t>>(proof_buf));
const auto vk = from_buffer<ClientIVC::VerificationKey>(from_buffer<std::vector<uint8_t>>(vk_buf));

vk.mega->pcs_verification_key = std::make_shared<VerifierCommitmentKey<curve::BN254>>();
vk.eccvm->pcs_verification_key =
std::make_shared<VerifierCommitmentKey<curve::Grumpkin>>(vk.eccvm->circuit_size + 1);
vk.translator->pcs_verification_key = std::make_shared<VerifierCommitmentKey<curve::BN254>>();

*result = ClientIVC::verify(proof, vk);
}

WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec,
bool const* recursive,
uint8_t const* witness_vec,
Expand Down
11 changes: 9 additions & 2 deletions barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* constraint_syst
bool* result);

/**
* @brief Construct and verify a MegaHonk proof
*
* @brief Construct and verify a ClientIVC proof
* @deprecated
*/
WASM_EXPORT void acir_prove_and_verify_mega_honk(uint8_t const* constraint_system_buf,
bool const* recursive,
Expand All @@ -53,6 +53,13 @@ WASM_EXPORT void acir_prove_and_verify_aztec_client(uint8_t const* constraint_sy
uint8_t const* witness_buf,
bool* result);

WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack,
uint8_t const* witness_stack,
uint8_t** out_proof,
uint8_t** out_vk);

WASM_EXPORT void acir_verify_aztec_client(uint8_t const* proof_buf, uint8_t const* vk_buf, bool* result);

/**
* @brief Fold and verify a set of circuits using ClientIvc
*
Expand Down
10 changes: 10 additions & 0 deletions barretenberg/ts/src/barretenberg/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,16 @@ export class AztecClientBackend {
}
}

async prove(witnessMsgpack: Uint8Array[]): Promise<[Uint8Array, Uint8Array]> {
await this.instantiate();
return this.api.acirProveAztecClient(this.acirMsgpack, witnessMsgpack);
}

async verify(proof: Uint8Array, vk: Uint8Array): Promise<boolean> {
await this.instantiate();
return this.api.acirVerifyAztecClient(proof, vk);
}

async proveAndVerify(witnessMsgpack: Uint8Array[]): Promise<boolean> {
await this.instantiate();
return this.api.acirProveAndVerifyAztecClient(this.acirMsgpack, witnessMsgpack);
Expand Down
24 changes: 24 additions & 0 deletions barretenberg/ts/src/barretenberg_api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,30 @@ export class BarretenbergApi {
return out[0];
}

async acirProveAztecClient(acirVec: Uint8Array[], witnessVec: Uint8Array[]): Promise<[Uint8Array, Uint8Array]> {
const inArgs = [acirVec, witnessVec].map(serializeBufferable);
const outTypes: OutputType[] = [BufferDeserializer(), BufferDeserializer()];
const result = await this.wasm.callWasmExport(
'acir_prove_aztec_client',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
const out = result.map((r, i) => outTypes[i].fromBuffer(r));
return [out[0], out[1]];
}

async acirVerifyAztecClient(proofBuf: Uint8Array, vkBuf: Uint8Array): Promise<boolean> {
const inArgs = [proofBuf, vkBuf].map(serializeBufferable);
const outTypes: OutputType[] = [BoolDeserializer()];
const result = await this.wasm.callWasmExport(
'acir_verify_aztec_client',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
const out = result.map((r, i) => outTypes[i].fromBuffer(r));
return out[0];
}

async acirProveUltraHonk(acirVec: Uint8Array, recursive: boolean, witnessVec: Uint8Array): Promise<Uint8Array> {
const inArgs = [acirVec, recursive, witnessVec].map(serializeBufferable);
const outTypes: OutputType[] = [BufferDeserializer()];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import {
generate3FunctionTestingIVCStack,
generate6FunctionTestingIVCStack,
proveAndVerifyAztecClient,
proveThenVerifyAztecClient,
} from './index.js';

/* eslint-disable camelcase */
Expand Down Expand Up @@ -49,19 +49,37 @@ function formatAndPrintLog(message: string): void {
logger(formattedMessage);
}

export async function proveThenVerifyAztecClientBrowser(
page: Page,
bytecodes: string[],
witnessStack: Uint8Array[],
): Promise<boolean> {
const threads = 16;

const result: boolean = await page.evaluate(
([acir, witness, numThreads]) => {
(window as any).proveThenVerifyAztecClient = proveThenVerifyAztecClient;
return (window as any).proveThenVerifyAztecClient(acir, witness, numThreads);
},
[bytecodes, witnessStack, threads],
);

return result;
}

describe('Client IVC Integration', () => {
let page: Page;
let browser: Browser;

beforeAll(async () => {
beforeEach(async () => {
browser = await chromium.launch({ headless: true });
const context = await browser.newContext();
page = await context.newPage();
page.on('console', msg => formatAndPrintLog(msg.text()));
await page.goto('http://localhost:8080');
});

afterAll(async () => {
afterEach(async () => {
await browser.close();
});

Expand All @@ -72,9 +90,9 @@ describe('Client IVC Integration', () => {
it('Should generate a verifiable client IVC proof from a simple mock tx via bb.js', async () => {
const [bytecodes, witnessStack] = await generate3FunctionTestingIVCStack();

logger(`calling prove and verify...`);
const verifyResult = await proveAndVerifyAztecClient(page, bytecodes, witnessStack);
logger(`generated and verified proof. result: ${verifyResult}`);
logger(`calling prove then verify...`);
const verifyResult = await proveThenVerifyAztecClientBrowser(page, bytecodes, witnessStack);
logger(`generated then verified proof. result: ${verifyResult}`);

expect(verifyResult).toEqual(true);
});
Expand All @@ -89,9 +107,9 @@ describe('Client IVC Integration', () => {
it('Should generate a verifiable client IVC proof from a simple mock tx via bb.js', async () => {
const [bytecodes, witnessStack] = await generate6FunctionTestingIVCStack();

logger(`calling prove and verify...`);
const verifyResult = await proveAndVerifyAztecClient(page, bytecodes, witnessStack);
logger(`generated and verified proof. result: ${verifyResult}`);
logger(`calling prove then verify...`);
const verifyResult = await proveThenVerifyAztecClientBrowser(page, bytecodes, witnessStack);
logger(`generated then verified proof. result: ${verifyResult}`);

expect(verifyResult).toEqual(true);
});
Expand Down
32 changes: 10 additions & 22 deletions yarn-project/ivc-integration/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { type CLIENT_IVC_VERIFICATION_KEY_LENGTH_IN_FIELDS } from '@aztec/circui
import { type ForeignCallOutput, Noir } from '@noir-lang/noir_js';
import createDebug from 'debug';
import { ungzip } from 'pako';
import { type Page } from 'playwright';

import MockAppCreatorCircuit from '../artifacts/app_creator.json' assert { type: 'json' };
import MockAppReaderCircuit from '../artifacts/app_reader.json' assert { type: 'json' };
Expand Down Expand Up @@ -242,30 +241,19 @@ function base64ToUint8Array(base64: string): Uint8Array {
return Uint8Array.from(atob(base64), c => c.charCodeAt(0));
}

export async function proveAndVerifyBrowser(bytecodes: string[], witnessStack: Uint8Array[], threads?: number) {
const { AztecClientBackend } = await import('@aztec/bb.js');
const preparedBytecodes = bytecodes.map(base64ToUint8Array).map((arr: Uint8Array) => ungzip(arr));
const backend = new AztecClientBackend(preparedBytecodes, { threads });
const verified = await backend.proveAndVerify(witnessStack.map((arr: Uint8Array) => ungzip(arr)));

await backend.destroy();
return verified;
}

export async function proveAndVerifyAztecClient(
page: Page,
export async function proveThenVerifyAztecClient(
bytecodes: string[],
witnessStack: Uint8Array[],
threads?: number,
): Promise<boolean> {
const threads = 16;

const result: boolean = await page.evaluate(
([acir, witness, numThreads]) => {
(window as any).proveAndVerifyBrowser = proveAndVerifyBrowser;
return (window as any).proveAndVerifyBrowser(acir, witness, numThreads);
},
[bytecodes, witnessStack, threads],
const { AztecClientBackend } = await import('@aztec/bb.js');
const backend = new AztecClientBackend(
bytecodes.map(base64ToUint8Array).map((arr: Uint8Array) => ungzip(arr)),
{ threads },
);

return result;
const [proof, vk] = await backend.prove(witnessStack.map((arr: Uint8Array) => ungzip(arr)));
const verified = await backend.verify(proof, vk);
await backend.destroy();
return verified;
}
6 changes: 3 additions & 3 deletions yarn-project/ivc-integration/src/serve.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import createDebug from 'debug';

import { generate3FunctionTestingIVCStack, proveAndVerifyBrowser } from './index.js';
import { generate3FunctionTestingIVCStack, proveThenVerifyAztecClient } from './index.js';

createDebug.enable('*');
const logger = createDebug('aztec:ivc-test');
Expand Down Expand Up @@ -78,7 +78,7 @@ function setupConsoleOutput() {
};
}

(window as any).proveAndVerifyBrowser = proveAndVerifyBrowser;
(window as any).proveThenVerifyAztecClient = proveThenVerifyAztecClient;

document.addEventListener('DOMContentLoaded', function () {
setupConsoleOutput(); // Initialize console output capture
Expand All @@ -89,7 +89,7 @@ document.addEventListener('DOMContentLoaded', function () {
logger(`generating circuit and witness...`);
const [bytecodes, witnessStack] = await generate3FunctionTestingIVCStack();
logger(`done. proving and verifying...`);
const verified = await proveAndVerifyBrowser(bytecodes, witnessStack);
const verified = await proveThenVerifyAztecClient(bytecodes, witnessStack);
logger(`verified? ${verified}`);
});
document.body.appendChild(button);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { createLogger } from '@aztec/foundation/log';

import { jest } from '@jest/globals';
import { ungzip } from 'pako';

import {
MOCK_MAX_COMMITMENTS_PER_TX,
Expand All @@ -17,6 +16,7 @@ import {
MockPrivateKernelResetVk,
MockPrivateKernelTailCircuit,
getVkAsFields,
proveThenVerifyAztecClient,
witnessGenCreatorAppMockCircuit,
witnessGenMockPrivateKernelInitCircuit,
witnessGenMockPrivateKernelInnerCircuit,
Expand All @@ -34,33 +34,6 @@ jest.setTimeout(120_000);
describe('Client IVC Integration', () => {
beforeEach(async () => {});

function base64ToUint8Array(base64: string) {
const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
}

async function proveAndVerifyAztecClient(
witnessStack: Uint8Array[],
bytecodes: string[],
threads?: number,
): Promise<boolean> {
const { AztecClientBackend } = await import('@aztec/bb.js');
const backend = new AztecClientBackend(
bytecodes.map(base64ToUint8Array).map((arr: Uint8Array) => ungzip(arr)),
{ threads },
);

const verified = await backend.proveAndVerify(witnessStack.map((arr: Uint8Array) => ungzip(arr)));
logger.debug(`finished running proveAndVerify ${verified}`);
await backend.destroy();
return verified;
}

// This test will verify a client IVC proof of a simple tx:
// 1. Run a mock app that creates two commitments
// 2. Run the init kernel to process the app run
Expand Down Expand Up @@ -96,8 +69,8 @@ describe('Client IVC Integration', () => {
const witnessStack = [appWitnessGenResult.witness, initWitnessGenResult.witness, tailWitnessGenResult.witness];
logger.debug('built witness stack');

const verifyResult = await proveAndVerifyAztecClient(witnessStack, bytecodes);
logger.debug(`generated and verified proof. result: ${verifyResult}`);
const verifyResult = await proveThenVerifyAztecClient(bytecodes, witnessStack);
logger.debug(`generated then verified proof. result: ${verifyResult}`);

expect(verifyResult).toEqual(true);
});
Expand Down Expand Up @@ -163,8 +136,8 @@ describe('Client IVC Integration', () => {
tailWitnessGenResult.witness,
];

const verifyResult = await proveAndVerifyAztecClient(witnessStack, bytecodes);
logger.debug(`generated and verified proof. result: ${verifyResult}`);
const verifyResult = await proveThenVerifyAztecClient(bytecodes, witnessStack);
logger.debug(`generated then verified proof. result: ${verifyResult}`);

expect(verifyResult).toEqual(true);
});
Expand Down

0 comments on commit 8c064d4

Please sign in to comment.