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: CIVC browser proveThenVerify #10431

Merged
merged 13 commits into from
Dec 10, 2024
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you not deleting this because its being used somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I didn't want to get into changing the CI and broader conversations to make sure this is ok to go now.

*/
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 @@ -292,6 +292,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
Loading