Skip to content

Commit

Permalink
feat: enable honk_recursion through acir (#6719)
Browse files Browse the repository at this point in the history
We want to be able to verify honk proofs without adding a new opcode.
The workaround that this PR introduces is adding a flag that determines
whether to create a plonk vs a honk recursion constraint based on which
proof system we're using to prove. If we are using ultra honk, we will
generate a honk recursion constraint and the same for plonk.

This will need to be reverted after the offsite:
AztecProtocol/barretenberg#1013.

Please read [contributing guidelines](CONTRIBUTING.md) and remove this
line.
  • Loading branch information
lucasxia01 authored May 30, 2024
1 parent 7b3a72c commit 7ce4cbe
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 87 deletions.
10 changes: 6 additions & 4 deletions barretenberg/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ barretenberg-acir-tests-bb:
# This ensures we test independent pk construction through real/garbage witness data paths.
RUN FLOW=prove_then_verify ./run_acir_tests.sh
# Construct and separately verify a UltraHonk proof for a single program
RUN FLOW=prove_then_verify_ultra_honk ./run_acir_tests.sh double_verify_nested_proof
RUN FLOW=prove_then_verify_ultra_honk ./run_acir_tests.sh sha256
# Construct and separately verify a MegaHonk proof for all acir programs
RUN FLOW=prove_then_verify_mega_honk ./run_acir_tests.sh
# Construct and verify a UltraHonk proof for a single program
RUN FLOW=prove_and_verify_ultra_honk ./run_acir_tests.sh double_verify_nested_proof
RUN FLOW=prove_and_verify_ultra_honk ./run_acir_tests.sh pedersen_hash
# Construct and verify a MegaHonk proof for a single arbitrary program
RUN FLOW=prove_and_verify_mega_honk ./run_acir_tests.sh 6_array
# Construct and verify a UltraHonk proof for all ACIR programs using the new witness stack workflow
RUN FLOW=prove_and_verify_ultra_honk_program ./run_acir_tests.sh
# Construct and verify a MegaHonk proof on one non-recursive program using the new witness stack workflow
RUN FLOW=prove_and_verify_ultra_honk_program ./run_acir_tests.sh merkle_insert
# Construct and verify a MegaHonk proof for all ACIR programs using the new witness stack workflow
RUN FLOW=prove_and_verify_mega_honk_program ./run_acir_tests.sh
# Fold and verify an ACIR program stack using ClientIvc
RUN FLOW=fold_and_verify_program ./run_acir_tests.sh fold_basic
# Run 1_mul through native bb build, all_cmds flow, to test all cli args.
Expand Down
63 changes: 45 additions & 18 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ acir_format::WitnessVector get_witness(std::string const& witness_path)
return acir_format::witness_buf_to_witness_data(witness_data);
}

acir_format::AcirFormat get_constraint_system(std::string const& bytecode_path)
acir_format::AcirFormat get_constraint_system(std::string const& bytecode_path, bool honk_recursion)
{
auto bytecode = get_bytecode(bytecode_path);
return acir_format::circuit_buf_to_acir_format(bytecode);
return acir_format::circuit_buf_to_acir_format(bytecode, honk_recursion);
}

acir_format::WitnessVectorStack get_witness_stack(std::string const& witness_path)
Expand All @@ -90,10 +90,10 @@ acir_format::WitnessVectorStack get_witness_stack(std::string const& witness_pat
return acir_format::witness_buf_to_witness_stack(witness_data);
}

std::vector<acir_format::AcirFormat> get_constraint_systems(std::string const& bytecode_path)
std::vector<acir_format::AcirFormat> get_constraint_systems(std::string const& bytecode_path, bool honk_recursion)
{
auto bytecode = get_bytecode(bytecode_path);
return acir_format::program_buf_to_acir_format(bytecode);
return acir_format::program_buf_to_acir_format(bytecode, honk_recursion);
}

std::string proof_to_json(std::vector<bb::fr>& proof)
Expand Down Expand Up @@ -124,7 +124,7 @@ std::string vk_to_json(std::vector<bb::fr>& data)
*/
bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessPath)
{
auto constraint_system = get_constraint_system(bytecodePath);
auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false);
auto witness = get_witness(witnessPath);

acir_proofs::AcirComposer acir_composer{ 0, verbose };
Expand Down Expand Up @@ -161,8 +161,12 @@ bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, aci
using Verifier = UltraVerifier_<Flavor>;
using VerificationKey = Flavor::VerificationKey;

bool honk_recursion = false;
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
honk_recursion = true;
}
// Construct a bberg circuit from the acir representation
auto builder = acir_format::create_circuit<Builder>(constraint_system, 0, witness);
auto builder = acir_format::create_circuit<Builder>(constraint_system, 0, witness, honk_recursion);

auto num_extra_gates = builder.get_num_gates_added_to_ensure_nonzero_polynomials();
size_t srs_size = builder.get_circuit_subgroup_size(builder.get_total_circuit_size() + num_extra_gates);
Expand All @@ -188,8 +192,12 @@ bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, aci
*/
template <IsUltraFlavor Flavor> bool proveAndVerifyHonk(const std::string& bytecodePath, const std::string& witnessPath)
{
bool honk_recursion = false;
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
honk_recursion = true;
}
// Populate the acir constraint system and witness from gzipped data
auto constraint_system = get_constraint_system(bytecodePath);
auto constraint_system = get_constraint_system(bytecodePath, honk_recursion);
auto witness = get_witness(witnessPath);

return proveAndVerifyHonkAcirFormat<Flavor>(constraint_system, witness);
Expand All @@ -206,7 +214,11 @@ template <IsUltraFlavor Flavor> bool proveAndVerifyHonk(const std::string& bytec
template <IsUltraFlavor Flavor>
bool proveAndVerifyHonkProgram(const std::string& bytecodePath, const std::string& witnessPath)
{
auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath);
bool honk_recursion = false;
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
honk_recursion = true;
}
auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath, honk_recursion);

while (!program_stack.empty()) {
auto stack_item = program_stack.back();
Expand All @@ -230,7 +242,9 @@ bool foldAndVerifyProgram(const std::string& bytecodePath, const std::string& wi
ClientIVC ivc;
ivc.structured_flag = true;

auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath);
auto program_stack = acir_format::get_acir_program_stack(
bytecodePath, witnessPath, false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): this
// assumes that folding is never done with ultrahonk.

// Accumulate the entire program stack into the IVC
while (!program_stack.empty()) {
Expand Down Expand Up @@ -261,7 +275,7 @@ bool foldAndVerifyProgram(const std::string& bytecodePath, const std::string& wi
*/
void prove(const std::string& bytecodePath, const std::string& witnessPath, const std::string& outputPath)
{
auto constraint_system = get_constraint_system(bytecodePath);
auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false);
auto witness = get_witness(witnessPath);

acir_proofs::AcirComposer acir_composer{ 0, verbose };
Expand All @@ -287,11 +301,11 @@ void prove(const std::string& bytecodePath, const std::string& witnessPath, cons
*
* @param bytecodePath Path to the file containing the serialized circuit
*/
void gateCount(const std::string& bytecodePath)
void gateCount(const std::string& bytecodePath, bool honk_recursion)
{
// All circuit reports will be built into the string below
std::string functions_string = "{\"functions\": [\n ";
auto constraint_systems = get_constraint_systems(bytecodePath);
auto constraint_systems = get_constraint_systems(bytecodePath, honk_recursion);
size_t i = 0;
for (auto constraint_system : constraint_systems) {
acir_proofs::AcirComposer acir_composer(0, verbose);
Expand Down Expand Up @@ -360,7 +374,7 @@ bool verify(const std::string& proof_path, const std::string& vk_path)
*/
void write_vk(const std::string& bytecodePath, const std::string& outputPath)
{
auto constraint_system = get_constraint_system(bytecodePath);
auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false);
acir_proofs::AcirComposer acir_composer{ 0, verbose };
acir_composer.create_circuit(constraint_system);
init_bn254_crs(acir_composer.get_dyadic_circuit_size());
Expand All @@ -378,7 +392,7 @@ void write_vk(const std::string& bytecodePath, const std::string& outputPath)

void write_pk(const std::string& bytecodePath, const std::string& outputPath)
{
auto constraint_system = get_constraint_system(bytecodePath);
auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false);
acir_proofs::AcirComposer acir_composer{ 0, verbose };
acir_composer.create_circuit(constraint_system);
init_bn254_crs(acir_composer.get_dyadic_circuit_size());
Expand Down Expand Up @@ -572,7 +586,11 @@ void prove_honk(const std::string& bytecodePath, const std::string& witnessPath,
using Builder = Flavor::CircuitBuilder;
using Prover = UltraProver_<Flavor>;

auto constraint_system = get_constraint_system(bytecodePath);
bool honk_recursion = false;
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
honk_recursion = true;
}
auto constraint_system = get_constraint_system(bytecodePath, honk_recursion);
auto witness = get_witness(witnessPath);

auto builder = acir_format::create_circuit<Builder>(constraint_system, 0, witness);
Expand Down Expand Up @@ -645,7 +663,11 @@ template <IsUltraFlavor Flavor> void write_vk_honk(const std::string& bytecodePa
using ProverInstance = ProverInstance_<Flavor>;
using VerificationKey = Flavor::VerificationKey;

auto constraint_system = get_constraint_system(bytecodePath);
bool honk_recursion = false;
if constexpr (IsAnyOf<Flavor, UltraFlavor>) {
honk_recursion = true;
}
auto constraint_system = get_constraint_system(bytecodePath, honk_recursion);
auto builder = acir_format::create_circuit<Builder>(constraint_system, 0, {});

auto num_extra_gates = builder.get_num_gates_added_to_ensure_nonzero_polynomials();
Expand Down Expand Up @@ -733,7 +755,7 @@ template <IsUltraFlavor Flavor> void vk_as_fields_honk(const std::string& vk_pat
*/
void prove_output_all(const std::string& bytecodePath, const std::string& witnessPath, const std::string& outputPath)
{
auto constraint_system = get_constraint_system(bytecodePath);
auto constraint_system = get_constraint_system(bytecodePath, /*honk_recursion=*/false);
auto witness = get_witness(witnessPath);

acir_proofs::AcirComposer acir_composer{ 0, verbose };
Expand Down Expand Up @@ -802,6 +824,11 @@ int main(int argc, char* argv[])
std::string proof_path = get_option(args, "-p", "./proofs/proof");
std::string vk_path = get_option(args, "-k", "./target/vk");
std::string pk_path = get_option(args, "-r", "./target/pk");
std::string honk_recursion_str = get_option(args, "-h", "false");
bool honk_recursion = false;
if (honk_recursion_str == "true") {
honk_recursion = true;
}
CRS_PATH = get_option(args, "-c", CRS_PATH);

// Skip CRS initialization for any command which doesn't require the CRS.
Expand Down Expand Up @@ -835,7 +862,7 @@ int main(int argc, char* argv[])
std::string output_path = get_option(args, "-o", "./proofs");
prove_output_all(bytecode_path, witness_path, output_path);
} else if (command == "gates") {
gateCount(bytecode_path);
gateCount(bytecode_path, honk_recursion);
} else if (command == "verify") {
return verify(proof_path, vk_path) ? 0 : 1;
} else if (command == "contract") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,19 @@ class AcirIntegrationTest : public ::testing::Test {
return file.good();
}

acir_format::AcirProgramStack get_program_stack_data_from_test_file(const std::string& test_program_name)
acir_format::AcirProgramStack get_program_stack_data_from_test_file(const std::string& test_program_name,
bool honk_recursion)
{
std::string base_path = "../../acir_tests/acir_tests/" + test_program_name + "/target";
std::string bytecode_path = base_path + "/program.json";
std::string witness_path = base_path + "/witness.gz";

return acir_format::get_acir_program_stack(bytecode_path, witness_path);
return acir_format::get_acir_program_stack(bytecode_path, witness_path, honk_recursion);
}

acir_format::AcirProgram get_program_data_from_test_file(const std::string& test_program_name)
acir_format::AcirProgram get_program_data_from_test_file(const std::string& test_program_name, bool honk_recursion)
{
auto program_stack = get_program_stack_data_from_test_file(test_program_name);
auto program_stack = get_program_stack_data_from_test_file(test_program_name, honk_recursion);
ASSERT(program_stack.size() == 1); // Otherwise this method will not return full stack data

return program_stack.back();
Expand Down Expand Up @@ -142,7 +143,10 @@ TEST_P(AcirIntegrationSingleTest, DISABLED_ProveAndVerifyProgram)

std::string test_name = GetParam();
info("Test: ", test_name);
acir_format::AcirProgram acir_program = get_program_data_from_test_file(test_name);
acir_format::AcirProgram acir_program = get_program_data_from_test_file(
test_name,
/*honk_recursion=*/
false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): Assumes Flavor is not UltraHonk

// Construct a bberg circuit from the acir representation
Builder builder = acir_format::create_circuit<Builder>(acir_program.constraints, 0, acir_program.witness);
Expand Down Expand Up @@ -369,7 +373,9 @@ TEST_P(AcirIntegrationFoldingTest, DISABLED_ProveAndVerifyProgramStack)
std::string test_name = GetParam();
info("Test: ", test_name);

auto program_stack = get_program_stack_data_from_test_file(test_name);
auto program_stack = get_program_stack_data_from_test_file(
test_name, /*honk_recursion=*/false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013):
// Assumes Flavor is not UltraHonk

while (!program_stack.empty()) {
auto program = program_stack.back();
Expand All @@ -390,7 +396,9 @@ TEST_P(AcirIntegrationFoldingTest, DISABLED_FoldAndVerifyProgramStack)
using Builder = Flavor::CircuitBuilder;

std::string test_name = GetParam();
auto program_stack = get_program_stack_data_from_test_file(test_name);
auto program_stack = get_program_stack_data_from_test_file(
test_name, /*honk_recursion=*/false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013):
// Assumes Flavor is not UltraHonk

ClientIVC ivc;
ivc.structured_flag = true;
Expand Down Expand Up @@ -428,7 +436,9 @@ TEST_F(AcirIntegrationTest, DISABLED_UpdateAcirCircuit)
using Builder = Flavor::CircuitBuilder;

std::string test_name = "6_array"; // arbitrary program with RAM gates
auto acir_program = get_program_data_from_test_file(test_name);
auto acir_program = get_program_data_from_test_file(
test_name, /*honk_recursion=*/false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013):
// Assumes Flavor is not UltraHonk

// Construct a bberg circuit from the acir representation
auto circuit = acir_format::create_circuit<Builder>(acir_program.constraints, 0, acir_program.witness);
Expand Down
Loading

0 comments on commit 7ce4cbe

Please sign in to comment.