Skip to content

Commit

Permalink
feat: keccak honk proving in bb.js (#10489)
Browse files Browse the repository at this point in the history
  • Loading branch information
Maddiaa0 authored Dec 10, 2024
1 parent adadfa5 commit e0d7431
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 21 deletions.
4 changes: 4 additions & 0 deletions barretenberg/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ barretenberg-acir-tests-sol-honk:
FROM ../build-images/+from-registry

COPY ./cpp/+preset-sol/ /usr/src/barretenberg/cpp/build
COPY ./ts/+build/build/ /usr/src/barretenberg/ts
COPY ./cpp/+preset-clang-assert/bin/bb /usr/src/barretenberg/cpp/build/bin/bb
COPY ./+acir-tests/ /usr/src/barretenberg/acir_tests
COPY ./+sol/ /usr/src/barretenberg/sol
Expand All @@ -131,6 +132,9 @@ barretenberg-acir-tests-sol-honk:
RUN (cd sol-test && yarn)
RUN PARALLEL=1 FLOW=honk_sol ./run_acir_tests.sh assert_statement 1_mul slices verify_honk_proof

# Include bbjs tests for UltraHonk with keccak hash function
RUN BIN=../ts/dest/node/main.js FLOW=honk_sol ./run_acir_tests.sh assert_statement 1_mul slices verify_honk_proof

barretenberg-acir-tests-bb.js:
# Playwright not supported on base image ubuntu:noble, results in unmet dependencies
FROM ../build-images/+base-slim-node
Expand Down
4 changes: 2 additions & 2 deletions barretenberg/acir_tests/flows/honk_sol.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export PROOF_AS_FIELDS="$(pwd)/proof_fields.json"
# Create a proof, write the solidity contract, write the proof as fields in order to extract the public inputs
$BIN prove_ultra_keccak_honk -o proof $FLAGS $BFLAG
$BIN write_vk_ultra_keccak_honk -o vk $FLAGS $BFLAG
$BIN verify_ultra_keccak_honk -k vk -p proof $FLAGS $BFLAG
$BIN proof_as_fields_honk -k vk $FLAGS -p $PROOF
$BIN verify_ultra_keccak_honk -k vk -p proof $FLAGS
$BIN proof_as_fields_honk $FLAGS -p $PROOF -o proof_fields.json
$BIN contract_ultra_honk -k vk $FLAGS -o Verifier.sol

# Export the paths to the environment variables for the js test runner
Expand Down
51 changes: 47 additions & 4 deletions barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,23 @@ WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec,
*out = to_heap_buffer(to_buffer</*include_size=*/true>(proof));
}

WASM_EXPORT void acir_prove_ultra_keccak_honk(uint8_t const* acir_vec,
bool const* recursive,
uint8_t const* witness_vec,
uint8_t** out)
{
auto constraint_system =
acir_format::circuit_buf_to_acir_format(from_buffer<std::vector<uint8_t>>(acir_vec), /*honk_recursion=*/true);
auto witness = acir_format::witness_buf_to_witness_data(from_buffer<std::vector<uint8_t>>(witness_vec));

auto builder = acir_format::create_circuit<UltraCircuitBuilder>(
constraint_system, *recursive, 0, witness, /*honk_recursion=*/true);

UltraKeccakProver prover{ builder };
auto proof = prover.construct_proof();
*out = to_heap_buffer(to_buffer</*include_size=*/true>(proof));
}

WASM_EXPORT void acir_verify_ultra_honk(uint8_t const* proof_buf, uint8_t const* vk_buf, bool* result)
{
using VerificationKey = UltraFlavor::VerificationKey;
Expand All @@ -306,6 +323,21 @@ WASM_EXPORT void acir_verify_ultra_honk(uint8_t const* proof_buf, uint8_t const*
*result = verifier.verify_proof(proof);
}

WASM_EXPORT void acir_verify_ultra_keccak_honk(uint8_t const* proof_buf, uint8_t const* vk_buf, bool* result)
{
using VerificationKey = UltraKeccakFlavor::VerificationKey;
using VerifierCommitmentKey = bb::VerifierCommitmentKey<curve::BN254>;
using Verifier = UltraVerifier_<UltraKeccakFlavor>;

auto proof = from_buffer<std::vector<bb::fr>>(from_buffer<std::vector<uint8_t>>(proof_buf));
auto verification_key = std::make_shared<VerificationKey>(from_buffer<VerificationKey>(vk_buf));
verification_key->pcs_verification_key = std::make_shared<VerifierCommitmentKey>();

Verifier verifier{ verification_key };

*result = verifier.verify_proof(proof);
}

WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, bool const* recursive, uint8_t** out)
{
using DeciderProvingKey = DeciderProvingKey_<UltraFlavor>;
Expand All @@ -321,10 +353,10 @@ WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, bool const* r
*out = to_heap_buffer(to_buffer(vk));
}

WASM_EXPORT void get_honk_solidity_verifier_vk(uint8_t const* acir_vec, bool const* recursive, uint8_t** out)
WASM_EXPORT void acir_write_vk_ultra_keccak_honk(uint8_t const* acir_vec, bool const* recursive, uint8_t** out)
{
using DeciderProvingKey = DeciderProvingKey_<UltraFlavor>;
using VerificationKey = UltraFlavor::VerificationKey;
using DeciderProvingKey = DeciderProvingKey_<UltraKeccakFlavor>;
using VerificationKey = UltraKeccakFlavor::VerificationKey;

auto constraint_system =
acir_format::circuit_buf_to_acir_format(from_buffer<std::vector<uint8_t>>(acir_vec), /*honk_recursion=*/true);
Expand All @@ -333,8 +365,19 @@ WASM_EXPORT void get_honk_solidity_verifier_vk(uint8_t const* acir_vec, bool con

DeciderProvingKey proving_key(builder);
VerificationKey vk(proving_key.proving_key);
*out = to_heap_buffer(to_buffer(vk));
}

WASM_EXPORT void acir_honk_solidity_verifier(uint8_t const* proof_buf, uint8_t const* vk_buf, uint8_t** out)
{
using VerificationKey = UltraKeccakFlavor::VerificationKey;
using VerifierCommitmentKey = bb::VerifierCommitmentKey<curve::BN254>;

auto proof = from_buffer<std::vector<bb::fr>>(from_buffer<std::vector<uint8_t>>(proof_buf));
auto verification_key = from_buffer<VerificationKey>(vk_buf);
verification_key.pcs_verification_key = std::make_shared<VerifierCommitmentKey>();

auto str = get_honk_solidity_verifier(&vk);
auto str = get_honk_solidity_verifier(&verification_key);
*out = to_heap_buffer(str);
}

Expand Down
7 changes: 7 additions & 0 deletions barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr,
WASM_EXPORT void acir_verify_proof(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result);

WASM_EXPORT void acir_get_solidity_verifier(in_ptr acir_composer_ptr, out_str_buf out);
WASM_EXPORT void acir_honk_solidity_verifier(uint8_t const* proof_buf, uint8_t const* vk_buf, uint8_t** out);

WASM_EXPORT void acir_serialize_proof_into_fields(in_ptr acir_composer_ptr,
uint8_t const* proof_buf,
Expand All @@ -90,10 +91,16 @@ WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec,
bool const* recursive,
uint8_t const* witness_vec,
uint8_t** out);
WASM_EXPORT void acir_prove_ultra_keccak_honk(uint8_t const* acir_vec,
bool const* recursive,
uint8_t const* witness_vec,
uint8_t** out);

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

WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, bool const* recursive, uint8_t** out);
WASM_EXPORT void acir_write_vk_ultra_keccak_honk(uint8_t const* acir_vec, bool const* recursive, uint8_t** out);

WASM_EXPORT void acir_proof_as_fields_ultra_honk(uint8_t const* proof_buf, fr::vec_out_buf out);

Expand Down
41 changes: 33 additions & 8 deletions barretenberg/ts/src/barretenberg/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ const fieldByteSize = 32;
const publicInputOffset = 3;
const publicInputsOffsetBytes = publicInputOffset * fieldByteSize;

/**
* Options for the UltraHonkBackend.
*/
export type UltraHonkBackendOptions = {
/**Selecting this option will use the keccak hash function instead of poseidon
* when generating challenges in the proof.
* Use this when you want to verify the created proof on an EVM chain.
*/
keccak: boolean;
};

export class UltraHonkBackend {
// These type assertions are used so that we don't
// have to initialize `api` in the constructor.
Expand Down Expand Up @@ -182,9 +193,14 @@ export class UltraHonkBackend {
}
}

async generateProof(compressedWitness: Uint8Array): Promise<ProofData> {
async generateProof(compressedWitness: Uint8Array, options?: UltraHonkBackendOptions): Promise<ProofData> {
await this.instantiate();
const proofWithPublicInputs = await this.api.acirProveUltraHonk(

const proveUltraHonk = options?.keccak
? this.api.acirProveUltraKeccakHonk.bind(this.api)
: this.api.acirProveUltraHonk.bind(this.api);

const proofWithPublicInputs = await proveUltraHonk(
this.acirUncompressedBytecode,
this.circuitOptions.recursive,
gunzip(compressedWitness),
Expand Down Expand Up @@ -213,12 +229,20 @@ export class UltraHonkBackend {
return { proof, publicInputs };
}

async verifyProof(proofData: ProofData): Promise<boolean> {
async verifyProof(proofData: ProofData, options?: UltraHonkBackendOptions): Promise<boolean> {
await this.instantiate();

const proof = reconstructHonkProof(flattenFieldsAsArray(proofData.publicInputs), proofData.proof);
const vkBuf = await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode, this.circuitOptions.recursive);

return await this.api.acirVerifyUltraHonk(proof, new RawBuffer(vkBuf));
const writeVkUltraHonk = options?.keccak
? this.api.acirWriteVkUltraKeccakHonk.bind(this.api)
: this.api.acirWriteVkUltraHonk.bind(this.api);
const verifyUltraHonk = options?.keccak
? this.api.acirVerifyUltraKeccakHonk.bind(this.api)
: this.api.acirVerifyUltraHonk.bind(this.api);

const vkBuf = await writeVkUltraHonk(this.acirUncompressedBytecode, this.circuitOptions.recursive);
return await verifyUltraHonk(proof, new RawBuffer(vkBuf));
}

async getVerificationKey(): Promise<Uint8Array> {
Expand All @@ -227,10 +251,11 @@ export class UltraHonkBackend {
}

/** @description Returns a solidity verifier */
async getSolidityVerifier(): Promise<string> {
async getSolidityVerifier(vk?: Uint8Array): Promise<string> {
await this.instantiate();
await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode, this.circuitOptions.recursive);
return await this.api.getHonkSolidityVerifier(this.acirUncompressedBytecode, this.circuitOptions.recursive);
const vkBuf =
vk ?? (await this.api.acirWriteVkUltraHonk(this.acirUncompressedBytecode, this.circuitOptions.recursive));
return await this.api.acirHonkSolidityVerifier(this.acirUncompressedBytecode, vkBuf);
}

// TODO(https://github.com/noir-lang/noir/issues/5661): Update this to handle Honk recursive aggregation in the browser once it is ready in the backend itself
Expand Down
41 changes: 39 additions & 2 deletions barretenberg/ts/src/barretenberg_api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,19 @@ export class BarretenbergApi {
return out[0];
}

async acirProveUltraKeccakHonk(acirVec: Uint8Array, recursive: boolean, witnessVec: Uint8Array): Promise<Uint8Array> {
console.log('acirProveUltraKeccakHonk in');

This comment has been minimized.

Copy link
@signorecello

signorecello Dec 17, 2024

Contributor

I have no idea how this came up here @Maddiaa0 ... Weird stuff, do you time travel or something?

const inArgs = [acirVec, recursive, witnessVec].map(serializeBufferable);
const outTypes: OutputType[] = [BufferDeserializer()];
const result = await this.wasm.callWasmExport(
'acir_prove_ultra_keccak_honk',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
const out = result.map((r, i) => outTypes[i].fromBuffer(r));
return out[0];
}

async acirVerifyUltraHonk(proofBuf: Uint8Array, vkBuf: Uint8Array): Promise<boolean> {
const inArgs = [proofBuf, vkBuf].map(serializeBufferable);
const outTypes: OutputType[] = [BoolDeserializer()];
Expand All @@ -581,6 +594,18 @@ export class BarretenbergApi {
return out[0];
}

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

async acirWriteVkUltraHonk(acirVec: Uint8Array, recursive: boolean): Promise<Uint8Array> {
const inArgs = [acirVec, recursive].map(serializeBufferable);
const outTypes: OutputType[] = [BufferDeserializer()];
Expand All @@ -593,11 +618,23 @@ export class BarretenbergApi {
return out[0];
}

async getHonkSolidityVerifier(acirVec: Uint8Array, recursive: boolean): Promise<string> {
async acirWriteVkUltraKeccakHonk(acirVec: Uint8Array, recursive: boolean): Promise<Uint8Array> {
const inArgs = [acirVec, recursive].map(serializeBufferable);
const outTypes: OutputType[] = [BufferDeserializer()];
const result = await this.wasm.callWasmExport(
'get_honk_solidity_verifier_vk',
'acir_write_vk_ultra_keccak_honk',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
const out = result.map((r, i) => outTypes[i].fromBuffer(r));
return out[0];
}

async acirHonkSolidityVerifier(acirVec: Uint8Array, vkBuf: Uint8Array): Promise<string> {
const inArgs = [acirVec, vkBuf].map(serializeBufferable);
const outTypes: OutputType[] = [BufferDeserializer()];
const result = await this.wasm.callWasmExport(
'acir_honk_solidity_verifier',
inArgs,
outTypes.map(t => t.SIZE_IN_BYTES),
);
Expand Down
Loading

1 comment on commit e0d7431

@AztecBot
Copy link
Collaborator

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: e0d7431 Previous: fbc8c0e Ratio
wasmClientIVCBench/Full/6 90260.531113 ms/iter 84269.571195 ms/iter 1.07
wasmconstruct_proof_ultrahonk_power_of_2/20 16539.915219 ms/iter 15143.453146 ms/iter 1.09

This comment was automatically generated by workflow using github-action-benchmark.

CC: @ludamad @codygunton

Please sign in to comment.