Skip to content

Commit

Permalink
Integration test via bb.js
Browse files Browse the repository at this point in the history
  • Loading branch information
codygunton committed Oct 24, 2024
1 parent a80717e commit 3723307
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 22 deletions.
53 changes: 51 additions & 2 deletions barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ WASM_EXPORT void acir_serialize_verification_key_into_fields(in_ptr acir_compose
write(out_key_hash, vk_hash);
}

WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack, uint8_t const* witness_stack, bool* result)
WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack, uint8_t const* witness_stack, uint8_t** out)
{
using Program = acir_format::AcirProgram;
std::vector<std::vector<uint8_t>> witnesses = from_buffer<std::vector<std::vector<uint8_t>>>(witness_stack);
Expand Down Expand Up @@ -250,7 +250,56 @@ WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack, uint8_t cons

info("calling ivc.prove");
auto proof = ivc.prove();
*result = false;
*out = to_heap_buffer(to_buffer(proof));
}

WASM_EXPORT void acir_prove_and_verify_aztec_client(uint8_t const* acir_stack,
uint8_t const* witness_stack,
bool* verified)
{
using Program = acir_format::AcirProgram;
std::vector<std::vector<uint8_t>> witnesses = from_buffer<std::vector<std::vector<uint8_t>>>(witness_stack);
info("read witnesses from buffer");
std::vector<std::vector<uint8_t>> acirs = from_buffer<std::vector<std::vector<uint8_t>>>(acir_stack);
info("read acirs from buffer");
std::vector<Program> folding_stack;

for (auto [bincode, wit] : zip_view(acirs, witnesses)) {
acir_format::AcirFormat constraints =
acir_format::circuit_buf_to_acir_format(bincode, /*honk_recursion=*/false);
acir_format::WitnessVector witness = acir_format::witness_buf_to_witness_data(wit);

folding_stack.push_back(Program{ constraints, witness });
}
info("created folding stack");
// 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;
ivc.auto_verify_mode = true;
ivc.trace_structure = TraceStructure::E2E_FULL_TEST;

// 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;
for (Program& program : folding_stack) {
// Construct a bberg circuit from the acir representation then accumulate it into the IVC
auto circuit =
create_circuit<MegaCircuitBuilder>(program.constraints, 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;
info("calling ivc.accumulate:");
ivc.accumulate(circuit);
}

info("calling ivc.prove");
bool result = ivc.prove_and_verify();

*verified = result;
}

WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec, uint8_t const* witness_vec, uint8_t** out)
Expand Down
6 changes: 5 additions & 1 deletion barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ WASM_EXPORT void acir_prove_and_verify_mega_honk(uint8_t const* constraint_syste

WASM_EXPORT void acir_prove_aztec_client(uint8_t const* constraint_system_buf,
uint8_t const* witness_buf,
bool* result);
uint8_t** out);

WASM_EXPORT void acir_prove_and_verify_aztec_client(uint8_t const* constraint_system_buf,
uint8_t const* witness_buf,
bool* result);

WASM_EXPORT void acir_verify_aztec_client_proof(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result);

Expand Down
24 changes: 11 additions & 13 deletions barretenberg/ts/src/barretenberg_api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,19 +432,6 @@ export class BarretenbergApi {
return out[0];
}

async acirProveAndVerifyClientIvc(constraintSystemBuf: Uint8Array[], witnessBuf: Uint8Array[]): Promise<boolean> {
const inArgs = [constraintSystemBuf, witnessBuf].map(serializeBufferable);
console.log("serialized the unpacked unpacked and unzipped circuit and witness data");
const outTypes: OutputType[] = [BoolDeserializer()];
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];
}

async acirLoadVerificationKey(acirComposerPtr: Ptr, vkBuf: Uint8Array): Promise<void> {
const inArgs = [acirComposerPtr, vkBuf].map(serializeBufferable);
const outTypes: OutputType[] = [];
Expand Down Expand Up @@ -545,6 +532,17 @@ export class BarretenbergApi {
return out as any;
}

async acirProveAndVerifyAztecClient(acirVec: Uint8Array[], witnessVec: Uint8Array[]): Promise<Uint8Array> {
const inArgs = [acirVec, witnessVec].map(serializeBufferable);
const outTypes: OutputType[] = [BufferDeserializer()];
const result = await this.wasm.callWasmExport(
'acir_prove_and_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 acirProveAztecClient(acirVec: Uint8Array[], witnessVec: Uint8Array[]): Promise<Uint8Array> {
const inArgs = [acirVec, witnessVec].map(serializeBufferable);
Expand Down
59 changes: 53 additions & 6 deletions barretenberg/ts/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,39 @@ export async function proveAndVerifyMegaHonk(bytecodePath: string, witnessPath:
/* eslint-enable camelcase */
}

export async function proveAndVerifyClientIvc(bytecodePath: string, witnessPath: string, crsPath: string) {
export async function proveAndVerifyAztecClient(bytecodePath: string, witnessPath: string, crsPath: string) {
/* eslint-disable camelcase */
const { api } = await initClientIVC(crsPath);
try {
const bytecode = readStack(bytecodePath, 1);
const bytecode = readStack(bytecodePath, 0);
const witness = readStack(witnessPath, 0);

const result = await api.acirProveAndVerifyClientIvc(bytecode, witness);
return false;
const verified = await api.acirProveAndVerifyAztecClient(bytecode, witness);
return verified;
} finally {
await api.destroy();
}
/* eslint-enable camelcase */
}

export async function proveAztecClient(bytecodePath: string, witnessPath: string, crsPath: string, outputPath: string) {
/* eslint-disable camelcase */
const { api } = await initClientIVC(crsPath);
try {
debug(`creating proof...`);
const bytecode = readStack(bytecodePath, 0);
const witness = readStack(witnessPath, 0);
const proof = await api.acirProveAztecClient(bytecode, witness);
debug(`finished creating proof.`);

if (outputPath === '-') {
process.stdout.write(proof);
debug(`proof written to stdout`);
} else {
writeFileSync(outputPath, proof);
debug(`proof written to: ${outputPath}`);
}

} finally {
await api.destroy();
}
Expand Down Expand Up @@ -531,13 +555,13 @@ program
});

program
.command('client_ivc_prove_output_all')
.command('client_ivc_prove_and_verify')
.description('Generate a ClientIVC proof.')
.option('-b, --bytecode-path <path>', 'Specify the bytecode path', './target/acirs.msgpack')
.option('-w, --witness-path <path>', 'Specify the witness path', './target/witnesses.msgpack')
.action(async ({ bytecodePath, witnessPath, crsPath }) => {
handleGlobalOptions();
const result = await proveAndVerifyClientIvc(bytecodePath, witnessPath, crsPath);
const result = await proveAndVerifyAztecClient(bytecodePath, witnessPath, crsPath);
process.exit(result ? 0 : 1);
});

Expand All @@ -563,6 +587,29 @@ program
await prove(bytecodePath, witnessPath, crsPath, outputPath);
});

program
.command('client_ivc_prove')
.description('Generate a ClientIVC proof.')
.option('-b, --bytecode-path <path>', 'Specify the bytecode path', './target/acirs.msgpack')
.option('-w, --witness-path <path>', 'Specify the witness path', './target/witnesses.msgpack')
.option('-o, --output-path <path>', 'Specify the proof output path', './proofs/proof')
.action(async ({ bytecodePath, witnessPath, outputPath, crsPath }) => {
handleGlobalOptions();
await proveAztecClient(bytecodePath, witnessPath, outputPath, crsPath);
});

program
.command('client_ivc_prove_and_verify')
.description('Generate a ClientIVC proof.')
.option('-b, --bytecode-path <path>', 'Specify the bytecode path', './target/acirs.msgpack')
.option('-w, --witness-path <path>', 'Specify the witness path', './target/witnesses.msgpack')
.option('-o, --output-path <path>', 'Specify the proof output path', './proofs/proof')
.action(async ({ bytecodePath, witnessPath, crsPath }) => {
handleGlobalOptions();
const result = await proveAndVerifyAztecClient(bytecodePath, witnessPath, crsPath);
process.exit(result ? 0 : 1);
});

program
.command('gates')
.description('Print Ultra Builder gate count to standard output.')
Expand Down
48 changes: 48 additions & 0 deletions yarn-project/bb-prover/src/bb/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,54 @@ export async function executeBbClientIvcProof(
}
}

export async function executeBbClientIvcProveAndVerify(
pathToBB: string,
workingDirectory: string,
bytecodeStackPath: string,
witnessStackPath: string,
log: LogFn,
): Promise<BBFailure | BBSuccess> {
// Check that the working directory exists
try {
await fs.access(workingDirectory);
} catch (error) {
return { status: BB_RESULT.FAILURE, reason: `Working directory ${workingDirectory} does not exist` };
}

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 {
// Write the bytecode to the working directory
log(`bytecodePath ${bytecodeStackPath}`);
const args = ['-b', bytecodeStackPath, '-w', witnessStackPath, '-v'];
const timer = new Timer();
const logFunction = (message: string) => {
log(`client ivc proof BB out - ${message}`);
};

const result = await executeBB(pathToBB, 'client_ivc_prove_and_verify', args, logFunction);
const durationMs = timer.ms();

if (result.status == BB_RESULT.SUCCESS) {
return { status: BB_RESULT.SUCCESS, durationMs: durationMs };
}
// Not a great error message here but it is difficult to decipher what comes from bb
return {
status: BB_RESULT.FAILURE,
reason: `Failed to prove and verify. Exit code ${result.exitCode}. Signal ${result.signal}.`,
};
} catch (error) {
return { status: BB_RESULT.FAILURE, reason: `${error}` };
}
}


/**
* Used for generating verification keys of noir circuits.
* It is assumed that the working directory is a temporary and/or random directory used solely for generating this VK.
Expand Down
112 changes: 112 additions & 0 deletions yarn-project/ivc-integration/src/wasm_client_ivc_integration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { BB_RESULT, executeBbClientIvcProveAndVerify } from '@aztec/bb-prover';
import { ClientIvcProof } from '@aztec/circuits.js';
import { createDebugLogger } from '@aztec/foundation/log';

import { jest } from '@jest/globals';
import { encode } from '@msgpack/msgpack';
import fs from 'fs/promises';
import os from 'os';
import path from 'path';
import { fileURLToPath } from 'url';

import {
MOCK_MAX_COMMITMENTS_PER_TX,
MockAppCreatorCircuit,
MockAppReaderCircuit,
MockPrivateKernelInitCircuit,
MockPrivateKernelInnerCircuit,
MockPrivateKernelResetCircuit,
MockPrivateKernelTailCircuit,
witnessGenCreatorAppMockCircuit,
witnessGenMockPrivateKernelInitCircuit,
witnessGenMockPrivateKernelInnerCircuit,
witnessGenMockPrivateKernelResetCircuit,
witnessGenMockPrivateKernelTailCircuit,
witnessGenReaderAppMockCircuit,
} from './index.js';

/* eslint-disable camelcase */

const logger = createDebugLogger('aztec:clientivc-integration');

jest.setTimeout(120_000);

describe('Client IVC Integration', () => {
let bbWorkingDirectory: string;
let bbBinaryPath: string;

beforeEach(async () => {
// Create a temp working dir
bbWorkingDirectory = await fs.mkdtemp(path.join('/mnt/user-data/cody/bb-tmp/', 'bb-client-ivc-integration-'));
bbBinaryPath = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'../../../barretenberg/ts/dest/node',
'main.js',
);
logger.debug(`bbBinaryPath is ${bbBinaryPath}`);
});

async function proveAndVerifyAztecClient(witnessStack: Uint8Array[], bytecodes: string[]): Promise<boolean> {
await fs.writeFile(
path.join(bbWorkingDirectory, 'acir.msgpack'),
encode(bytecodes.map(bytecode => Buffer.from(bytecode, 'base64'))),
);
logger.debug('wrote acir.msgpack');

await fs.writeFile(path.join(bbWorkingDirectory, 'witnesses.msgpack'), encode(witnessStack));
logger.debug('wrote witnesses.msgpack');

const provingResult = await executeBbClientIvcProveAndVerify(
bbBinaryPath,
bbWorkingDirectory,
path.join(bbWorkingDirectory, 'acir.msgpack'),
path.join(bbWorkingDirectory, 'witnesses.msgpack'),
logger.info,
);

if (provingResult.status === BB_RESULT.FAILURE) {
throw new Error(provingResult.reason);
} else {
return true
}
}

// 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
// 3. Run the tail kernel to finish the client IVC chain.
it('Should generate a verifiable client IVC proof from a simple mock tx via bb.js', async () => {
const tx = {
number_of_calls: '0x1',
};
// Witness gen app and kernels
const appWitnessGenResult = await witnessGenCreatorAppMockCircuit({ commitments_to_create: ['0x1', '0x2'] });
logger.debug('generated app mock circuit witness');

const initWitnessGenResult = await witnessGenMockPrivateKernelInitCircuit({
app_inputs: appWitnessGenResult.publicInputs,
tx,
});
logger.debug('generated mock private kernel init witness');

const tailWitnessGenResult = await witnessGenMockPrivateKernelTailCircuit({
prev_kernel_public_inputs: initWitnessGenResult.publicInputs,
});
logger.debug('generated mock private kernel tail witness');

// Create client IVC proof
const bytecodes = [
MockAppCreatorCircuit.bytecode,
MockPrivateKernelInitCircuit.bytecode,
MockPrivateKernelTailCircuit.bytecode,
];
logger.debug('built bytecode array');
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');

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

0 comments on commit 3723307

Please sign in to comment.