Skip to content

Commit

Permalink
feat: simplified bb Honk interface (#5319)
Browse files Browse the repository at this point in the history
Purpose of this PR is to clarify and simplify the bb interface for
constructing and verifying Honk proofs (both UltraHonk and
GoblinUltraHonk).

A similar flow was previously achieved somewhat indirectly through the
`goblin` class via `goblin.accumulate`. This was simply done for
convenience a while back and is not the right thing long term. The new
Honk flows are simplified and do not make use of anything like the
`AcirComposer` used for Plonk.

I have only added flows of the prove-AND-verify variety, i.e. more logic
will be needed in order to separate out the proving and verifying (a la
the prove-THEN-verify flows for Plonk). This includes serialization of
proving and verification keys.
  • Loading branch information
ledwards2225 authored Mar 21, 2024
1 parent 9dc0d2a commit a2d138f
Show file tree
Hide file tree
Showing 15 changed files with 206 additions and 187 deletions.
7 changes: 4 additions & 3 deletions barretenberg/acir_tests/Dockerfile.bb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ COPY . .
# Run every acir test through native bb build prove_then_verify flow for UltraPlonk.
# This ensures we test independent pk construction through real/garbage witness data paths.
RUN FLOW=prove_then_verify ./run_acir_tests.sh
# This flow is essentially the GoblinUltraHonk equivalent to the UltraPlonk "prove and verify". (This functionality is
# accessed via the goblin "accumulate" mechanism).
RUN FLOW=accumulate_and_verify_goblin ./run_acir_tests.sh
# Construct and verify a UltraHonk proof for all acir programs
RUN FLOW=prove_and_verify_ultra_honk ./run_acir_tests.sh
# Construct and verify a Goblin UltraHonk (GUH) proof for a single arbitrary program
RUN FLOW=prove_and_verify_goblin_ultra_honk ./run_acir_tests.sh 6_array
# This is a "full" Goblin flow. It constructs and verifies four proofs: GoblinUltraHonk, ECCVM, Translator, and merge
RUN FLOW=prove_and_verify_goblin ./run_acir_tests.sh 6_array
# Run 1_mul through native bb build, all_cmds flow, to test all cli args.
Expand Down
4 changes: 3 additions & 1 deletion barretenberg/acir_tests/Dockerfile.bb.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ COPY . .
ENV VERBOSE=1
# Run double_verify_proof through bb.js on node to check 512k support.
RUN BIN=../ts/dest/node/main.js FLOW=prove_then_verify ./run_acir_tests.sh double_verify_proof
# Run a single arbitrary test not involving recursion through bb.js for UltraHonk
RUN BIN=../ts/dest/node/main.js FLOW=prove_and_verify_ultra_honk ./run_acir_tests.sh 6_array
# Run a single arbitrary test not involving recursion through bb.js for GoblinUltraHonk
RUN BIN=../ts/dest/node/main.js FLOW=accumulate_and_verify_goblin ./run_acir_tests.sh 6_array
RUN BIN=../ts/dest/node/main.js FLOW=prove_and_verify_goblin_ultra_honk ./run_acir_tests.sh 6_array
# Run a single arbitrary test not involving recursion through bb.js for full Goblin
RUN BIN=../ts/dest/node/main.js FLOW=prove_and_verify_goblin ./run_acir_tests.sh 6_array
# Run 1_mul through bb.js build, all_cmds flow, to test all cli args.
Expand Down
6 changes: 0 additions & 6 deletions barretenberg/acir_tests/flows/accumulate_and_verify_goblin.sh

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
set -eu

VFLAG=${VERBOSE:+-v}

$BIN prove_and_verify_goblin_ultra_honk $VFLAG -c $CRS_PATH -b ./target/acir.gz
6 changes: 6 additions & 0 deletions barretenberg/acir_tests/flows/prove_and_verify_ultra_honk.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
set -eu

VFLAG=${VERBOSE:+-v}

$BIN prove_and_verify_ultra_honk $VFLAG -c $CRS_PATH -b ./target/acir.gz
54 changes: 29 additions & 25 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,40 +128,41 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP
}

/**
* @brief Constructs and verifies a Honk proof for an ACIR circuit via the Goblin accumulate mechanism
* @brief Constructs and verifies a Honk proof for an acir-generated circuit
*
* Communication:
* - proc_exit: A boolean value is returned indicating whether the proof is valid.
* an exit code of 0 will be returned for success and 1 for failure.
*
* @param bytecodePath Path to the file containing the serialized acir constraint system
* @param witnessPath Path to the file containing the serialized witness
* @return verified
* @tparam Flavor
* @param bytecodePath Path to serialized acir circuit data
* @param witnessPath Path to serialized acir witness data
*/
bool accumulateAndVerifyGoblin(const std::string& bytecodePath, const std::string& witnessPath)
template <IsUltraFlavor Flavor> bool proveAndVerifyHonk(const std::string& bytecodePath, const std::string& witnessPath)
{
// TODO(https://github.com/AztecProtocol/barretenberg/issues/811): Don't hardcode dyadic circuit size. Currently set
// to max circuit size present in acir tests suite.
size_t hardcoded_bn254_dyadic_size_hack = 1 << 19;
init_bn254_crs(hardcoded_bn254_dyadic_size_hack);
size_t hardcoded_grumpkin_dyadic_size_hack = 1 << 10; // For eccvm only
init_grumpkin_crs(hardcoded_grumpkin_dyadic_size_hack);
using Builder = Flavor::CircuitBuilder;
using Prover = UltraProver_<Flavor>;
using Verifier = UltraVerifier_<Flavor>;
using VerificationKey = Flavor::VerificationKey;

// Populate the acir constraint system and witness from gzipped data
auto constraint_system = get_constraint_system(bytecodePath);
auto witness = get_witness(witnessPath);

// Instantiate a Goblin acir composer and construct a bberg circuit from the acir representation
acir_proofs::GoblinAcirComposer acir_composer;
acir_composer.create_circuit(constraint_system, witness);
// Construct a bberg circuit from the acir representation
auto builder = acir_format::create_circuit<Builder>(constraint_system, 0, witness);

// Call accumulate to generate a GoblinUltraHonk proof
auto proof = acir_composer.accumulate();
// TODO(https://github.com/AztecProtocol/barretenberg/issues/811): Add a buffer to the expected circuit size to
// account for the addition of "gates to ensure nonzero polynomials" (in Honk only).
const size_t additional_gates_buffer = 15; // conservatively large to be safe
size_t srs_size = builder.get_circuit_subgroup_size(builder.get_total_circuit_size() + additional_gates_buffer);
init_bn254_crs(srs_size);

// Verify the GoblinUltraHonk proof
auto verified = acir_composer.verify_accumulator(proof);
// Construct Honk proof
Prover prover{ builder };
auto proof = prover.construct_proof();

return verified;
// Verify Honk proof
auto verification_key = std::make_shared<VerificationKey>(prover.instance->proving_key);
Verifier verifier{ verification_key };

return verifier.verify_proof(proof);
}

/**
Expand Down Expand Up @@ -569,8 +570,11 @@ int main(int argc, char* argv[])
if (command == "prove_and_verify") {
return proveAndVerify(bytecode_path, witness_path) ? 0 : 1;
}
if (command == "accumulate_and_verify_goblin") {
return accumulateAndVerifyGoblin(bytecode_path, witness_path) ? 0 : 1;
if (command == "prove_and_verify_ultra_honk") {
return proveAndVerifyHonk<UltraFlavor>(bytecode_path, witness_path) ? 0 : 1;
}
if (command == "prove_and_verify_goblin_ultra_honk") {
return proveAndVerifyHonk<GoblinUltraFlavor>(bytecode_path, witness_path) ? 0 : 1;
}
if (command == "prove_and_verify_goblin") {
return proveAndVerifyGoblin(bytecode_path, witness_path) ? 0 : 1;
Expand Down
42 changes: 35 additions & 7 deletions barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "acir_format.hpp"
#include "barretenberg/common/log.hpp"
#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp"
#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp"
#include <cstddef>

Expand Down Expand Up @@ -202,16 +203,16 @@ void build_constraints(Builder& builder, AcirFormat const& constraint_system, bo
}

/**
* @brief Create a circuit from acir constraints and optionally a witness
* @brief Specialization for creating Ultra circuit from acir constraints and optionally a witness
*
* @tparam Builder
* @param constraint_system
* @param size_hint
* @param witness
* @return Builder
*/
template <typename Builder>
Builder create_circuit(const AcirFormat& constraint_system, size_t size_hint, WitnessVector const& witness)
template <>
UltraCircuitBuilder create_circuit(const AcirFormat& constraint_system, size_t size_hint, WitnessVector const& witness)
{
Builder builder{
size_hint, witness, constraint_system.public_inputs, constraint_system.varnum, constraint_system.recursive
Expand All @@ -221,11 +222,38 @@ Builder create_circuit(const AcirFormat& constraint_system, size_t size_hint, Wi
build_constraints(builder, constraint_system, has_valid_witness_assignments);

return builder;
}
};

/**
* @brief Specialization for creating GoblinUltra circuit from acir constraints and optionally a witness
*
* @tparam Builder
* @param constraint_system
* @param size_hint
* @param witness
* @return Builder
*/
template <>
GoblinUltraCircuitBuilder create_circuit(const AcirFormat& constraint_system,
[[maybe_unused]] size_t size_hint,
WitnessVector const& witness)
{
// Construct a builder using the witness and public input data from acir and with the goblin-owned op_queue
auto op_queue = std::make_shared<ECCOpQueue>(); // instantiate empty op_queue
auto builder =
GoblinUltraCircuitBuilder{ op_queue, witness, constraint_system.public_inputs, constraint_system.varnum };

// Populate constraints in the builder via the data in constraint_system
bool has_valid_witness_assignments = !witness.empty();
acir_format::build_constraints(builder, constraint_system, has_valid_witness_assignments);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/817): Add some arbitrary op gates to ensure the
// associated polynomials are non-zero and to give ECCVM and Translator some ECC ops to process.
MockCircuits::construct_goblin_ecc_op_circuit(builder);

return builder;
};

template UltraCircuitBuilder create_circuit<UltraCircuitBuilder>(const AcirFormat& constraint_system,
size_t size_hint,
WitnessVector const& witness);
template void build_constraints<GoblinUltraCircuitBuilder>(GoblinUltraCircuitBuilder&, AcirFormat const&, bool);

} // namespace acir_format
47 changes: 28 additions & 19 deletions barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,38 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr,
*out = to_heap_buffer(proof_data);
}

WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr,
uint8_t const* acir_vec,
uint8_t const* witness_vec,
uint8_t** out)
WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* acir_vec, uint8_t const* witness_vec, bool* result)
{
auto acir_composer = reinterpret_cast<acir_proofs::GoblinAcirComposer*>(*acir_composer_ptr);
auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer<std::vector<uint8_t>>(acir_vec));
auto witness = acir_format::witness_buf_to_witness_data(from_buffer<std::vector<uint8_t>>(witness_vec));

acir_composer->create_circuit(constraint_system, witness);
auto proof = acir_composer->accumulate();
auto proof_data_buf = to_buffer</*include_size=*/true>(
proof); // template parameter needs to be set so that vector deserialization from
// buffer, which reads the size at the beginning can be done properly
*out = to_heap_buffer(proof_data_buf);
auto builder = acir_format::create_circuit<UltraCircuitBuilder>(constraint_system, 0, witness);

UltraProver prover{ builder };
auto proof = prover.construct_proof();

auto verification_key = std::make_shared<UltraFlavor::VerificationKey>(prover.instance->proving_key);
UltraVerifier verifier{ verification_key };

*result = verifier.verify_proof(proof);
}

WASM_EXPORT void acir_prove_and_verify_goblin_ultra_honk(uint8_t const* acir_vec,
uint8_t const* witness_vec,
bool* result)
{
auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer<std::vector<uint8_t>>(acir_vec));
auto witness = acir_format::witness_buf_to_witness_data(from_buffer<std::vector<uint8_t>>(witness_vec));

auto builder = acir_format::create_circuit<GoblinUltraCircuitBuilder>(constraint_system, 0, witness);

GoblinUltraProver prover{ builder };
auto proof = prover.construct_proof();

auto verification_key = std::make_shared<GoblinUltraFlavor::VerificationKey>(prover.instance->proving_key);
GoblinUltraVerifier verifier{ verification_key };

*result = verifier.verify_proof(proof);
}

WASM_EXPORT void acir_goblin_prove(in_ptr acir_composer_ptr,
Expand Down Expand Up @@ -127,14 +144,6 @@ WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* a
*out = to_heap_buffer(to_buffer(*pk));
}

WASM_EXPORT void acir_goblin_verify_accumulator(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result)
{
auto acir_composer = reinterpret_cast<acir_proofs::GoblinAcirComposer*>(*acir_composer_ptr);
auto proof_data_buf = from_buffer<std::vector<uint8_t>>(proof_buf);
auto proof = from_buffer<std::vector<bb::fr>>(proof_data_buf);
*result = acir_composer->verify_accumulator(proof);
}

WASM_EXPORT void acir_goblin_verify(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result)
{
auto acir_composer = reinterpret_cast<acir_proofs::GoblinAcirComposer*>(*acir_composer_ptr);
Expand Down
24 changes: 12 additions & 12 deletions barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr,
uint8_t** out);

/**
* @brief Perform the goblin accumulate operation
* @details Constructs a GUH proof and possibly handles transcript merge logic
* @brief Construct and verify an UltraHonk proof
*
*/
WASM_EXPORT void acir_goblin_accumulate(in_ptr acir_composer_ptr,
uint8_t const* constraint_system_buf,
uint8_t const* witness_buf,
uint8_t** out);
WASM_EXPORT void acir_prove_and_verify_ultra_honk(uint8_t const* constraint_system_buf,
uint8_t const* witness_buf,
bool* result);

/**
* @brief Construct and verify a GoblinUltraHonk proof
*
*/
WASM_EXPORT void acir_prove_and_verify_goblin_ultra_honk(uint8_t const* constraint_system_buf,
uint8_t const* witness_buf,
bool* result);

/**
* @brief Construct a full goblin proof
Expand All @@ -63,12 +69,6 @@ WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* a

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

/**
* @brief Verifies a GUH proof produced during goblin accumulation
*
*/
WASM_EXPORT void acir_goblin_verify_accumulator(in_ptr acir_composer_ptr, uint8_t const* proof_buf, bool* result);

/**
* @brief Verifies a full goblin proof (and the GUH proof produced by accumulation)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,6 @@ void GoblinAcirComposer::create_circuit(acir_format::AcirFormat& constraint_syst
MockCircuits::construct_goblin_ecc_op_circuit(builder_);
}

std::vector<bb::fr> GoblinAcirComposer::accumulate()
{
// // Construct a GUH proof for the circuit via the accumulate mechanism
// return goblin.accumulate_for_acir(builder_);

// Construct one final GUH proof via the accumulate mechanism
std::vector<bb::fr> ultra_proof = goblin.accumulate_for_acir(builder_);

// Construct a Goblin proof (ECCVM, Translator, Merge); result stored internally
goblin.prove_for_acir();

return ultra_proof;
}

bool GoblinAcirComposer::verify_accumulator(std::vector<bb::fr> const& proof)
{
return goblin.verify_accumulator_for_acir(proof);
}

std::vector<bb::fr> GoblinAcirComposer::accumulate_and_prove()
{
// Construct one final GUH proof via the accumulate mechanism
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,6 @@ class GoblinAcirComposer {
*/
void create_circuit(acir_format::AcirFormat& constraint_system, acir_format::WitnessVector& witness);

/**
* @brief Accumulate a circuit via Goblin
* @details For the present circuit, construct a GUH proof and the vkey needed to verify it
*
* @return std::vector<bb::fr> The GUH proof bytes
*/
std::vector<bb::fr> accumulate();

/**
* @brief Verify the Goblin accumulator (the GUH proof) using the vkey internal to Goblin
*
* @param proof
* @return bool Whether or not the proof was verified
*/
bool verify_accumulator(std::vector<bb::fr> const& proof);

/**
* @brief Accumulate a final circuit and construct a full Goblin proof
* @details Accumulation means constructing a GUH proof of a single (final) circuit. A full Goblin proof consists of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ template <typename FF> class GoblinUltraCircuitBuilder_ : public UltraCircuitBui
*/
GoblinUltraCircuitBuilder_(std::shared_ptr<ECCOpQueue> op_queue_in,
auto& witness_values,
std::vector<uint32_t>& public_inputs,
const std::vector<uint32_t>& public_inputs,
size_t varnum)
: UltraCircuitBuilder_<UltraHonkArith<FF>>(/*size_hint=*/0, witness_values, public_inputs, varnum)
, op_queue(op_queue_in)
Expand Down
Loading

0 comments on commit a2d138f

Please sign in to comment.