diff --git a/barretenberg/Earthfile b/barretenberg/Earthfile index 9eff98f64c52..5eed3ec9446a 100644 --- a/barretenberg/Earthfile +++ b/barretenberg/Earthfile @@ -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. diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 2ee1a5e62114..2978f3401d28 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -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) @@ -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 get_constraint_systems(std::string const& bytecode_path) +std::vector 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& proof) @@ -124,7 +124,7 @@ std::string vk_to_json(std::vector& 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 }; @@ -161,8 +161,12 @@ bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, aci using Verifier = UltraVerifier_; using VerificationKey = Flavor::VerificationKey; + bool honk_recursion = false; + if constexpr (IsAnyOf) { + honk_recursion = true; + } // Construct a bberg circuit from the acir representation - auto builder = acir_format::create_circuit(constraint_system, 0, witness); + auto builder = acir_format::create_circuit(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); @@ -188,8 +192,12 @@ bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, aci */ template bool proveAndVerifyHonk(const std::string& bytecodePath, const std::string& witnessPath) { + bool honk_recursion = false; + if constexpr (IsAnyOf) { + 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(constraint_system, witness); @@ -206,7 +214,11 @@ template bool proveAndVerifyHonk(const std::string& bytec template 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) { + 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(); @@ -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()) { @@ -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 }; @@ -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); @@ -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()); @@ -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()); @@ -572,7 +586,11 @@ void prove_honk(const std::string& bytecodePath, const std::string& witnessPath, using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; - auto constraint_system = get_constraint_system(bytecodePath); + bool honk_recursion = false; + if constexpr (IsAnyOf) { + honk_recursion = true; + } + auto constraint_system = get_constraint_system(bytecodePath, honk_recursion); auto witness = get_witness(witnessPath); auto builder = acir_format::create_circuit(constraint_system, 0, witness); @@ -645,7 +663,11 @@ template void write_vk_honk(const std::string& bytecodePa using ProverInstance = ProverInstance_; using VerificationKey = Flavor::VerificationKey; - auto constraint_system = get_constraint_system(bytecodePath); + bool honk_recursion = false; + if constexpr (IsAnyOf) { + honk_recursion = true; + } + auto constraint_system = get_constraint_system(bytecodePath, honk_recursion); auto builder = acir_format::create_circuit(constraint_system, 0, {}); auto num_extra_gates = builder.get_num_gates_added_to_ensure_nonzero_polynomials(); @@ -733,7 +755,7 @@ template 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 }; @@ -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. @@ -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") { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp index 33045c675cd7..919c973b027c 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_integration.test.cpp @@ -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(); @@ -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(acir_program.constraints, 0, acir_program.witness); @@ -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(); @@ -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; @@ -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(acir_program.constraints, 0, acir_program.witness); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp index 6756d17f1323..9d081b85bb03 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.cpp @@ -177,7 +177,7 @@ void handle_arithmetic(Program::Opcode::AssertZero const& arg, AcirFormat& af) } } -void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg, AcirFormat& af) +void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg, AcirFormat& af, bool honk_recursion) { std::visit( [&](auto&& arg) { @@ -354,13 +354,22 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg, Aci .result = map(arg.outputs, [](auto& e) { return e.value; }), }); } else if constexpr (std::is_same_v) { - auto c = RecursionConstraint{ - .key = map(arg.verification_key, [](auto& e) { return e.witness.value; }), - .proof = map(arg.proof, [](auto& e) { return e.witness.value; }), - .public_inputs = map(arg.public_inputs, [](auto& e) { return e.witness.value; }), - .key_hash = arg.key_hash.witness.value, - }; - af.recursion_constraints.push_back(c); + if (honk_recursion) { // if we're using the honk recursive verifier + auto c = HonkRecursionConstraint{ + .key = map(arg.verification_key, [](auto& e) { return e.witness.value; }), + .proof = map(arg.proof, [](auto& e) { return e.witness.value; }), + .public_inputs = map(arg.public_inputs, [](auto& e) { return e.witness.value; }), + }; + af.honk_recursion_constraints.push_back(c); + } else { + auto c = RecursionConstraint{ + .key = map(arg.verification_key, [](auto& e) { return e.witness.value; }), + .proof = map(arg.proof, [](auto& e) { return e.witness.value; }), + .public_inputs = map(arg.public_inputs, [](auto& e) { return e.witness.value; }), + .key_hash = arg.key_hash.witness.value, + }; + af.recursion_constraints.push_back(c); + } } else if constexpr (std::is_same_v) { af.bigint_from_le_bytes_constraints.push_back(BigIntFromLeBytes{ .inputs = map(arg.inputs, [](auto& e) { return e.witness.value; }), @@ -467,7 +476,7 @@ void handle_memory_op(Program::Opcode::MemoryOp const& mem_op, BlockConstraint& block.trace.push_back(acir_mem_op); } -AcirFormat circuit_serde_to_acir_format(Program::Circuit const& circuit) +AcirFormat circuit_serde_to_acir_format(Program::Circuit const& circuit, bool honk_recursion) { AcirFormat af; // `varnum` is the true number of variables, thus we add one to the index which starts at zero @@ -484,7 +493,7 @@ AcirFormat circuit_serde_to_acir_format(Program::Circuit const& circuit) if constexpr (std::is_same_v) { handle_arithmetic(arg, af); } else if constexpr (std::is_same_v) { - handle_blackbox_func_call(arg, af); + handle_blackbox_func_call(arg, af, honk_recursion); } else if constexpr (std::is_same_v) { auto block = handle_memory_init(arg); uint32_t block_id = arg.block_id.value; @@ -507,14 +516,14 @@ AcirFormat circuit_serde_to_acir_format(Program::Circuit const& circuit) return af; } -AcirFormat circuit_buf_to_acir_format(std::vector const& buf) +AcirFormat circuit_buf_to_acir_format(std::vector const& buf, bool honk_recursion) { // TODO(https://github.com/AztecProtocol/barretenberg/issues/927): Move to using just `program_buf_to_acir_format` // once Honk fully supports all ACIR test flows // For now the backend still expects to work with a single ACIR function auto circuit = Program::Program::bincodeDeserialize(buf).functions[0]; - return circuit_serde_to_acir_format(circuit); + return circuit_serde_to_acir_format(circuit, honk_recursion); } /** @@ -562,14 +571,14 @@ WitnessVector witness_buf_to_witness_data(std::vector const& buf) return witness_map_to_witness_vector(w); } -std::vector program_buf_to_acir_format(std::vector const& buf) +std::vector program_buf_to_acir_format(std::vector const& buf, bool honk_recursion) { auto program = Program::Program::bincodeDeserialize(buf); std::vector constraint_systems; constraint_systems.reserve(program.functions.size()); for (auto const& function : program.functions) { - constraint_systems.emplace_back(circuit_serde_to_acir_format(function)); + constraint_systems.emplace_back(circuit_serde_to_acir_format(function, honk_recursion)); } return constraint_systems; @@ -588,10 +597,15 @@ WitnessVectorStack witness_buf_to_witness_stack(std::vector const& buf) } #ifndef __wasm__ -AcirProgramStack get_acir_program_stack(std::string const& bytecode_path, std::string const& witness_path) +AcirProgramStack get_acir_program_stack(std::string const& bytecode_path, + std::string const& witness_path, + bool honk_recursion) { auto bytecode = get_bytecode(bytecode_path); - auto constraint_systems = program_buf_to_acir_format(bytecode); + auto constraint_systems = + program_buf_to_acir_format(bytecode, + honk_recursion); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): + // Remove honk recursion flag auto witness_data = get_bytecode(witness_path); auto witness_stack = witness_buf_to_witness_stack(witness_data); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp index eebb97dfc208..adfd8a75806d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_to_constraint_buf.hpp @@ -4,7 +4,7 @@ namespace acir_format { -AcirFormat circuit_buf_to_acir_format(std::vector const& buf); +AcirFormat circuit_buf_to_acir_format(std::vector const& buf, bool honk_recursion); /** * @brief Converts from the ACIR-native `WitnessMap` format to Barretenberg's internal `WitnessVector` format. @@ -16,11 +16,13 @@ AcirFormat circuit_buf_to_acir_format(std::vector const& buf); */ WitnessVector witness_buf_to_witness_data(std::vector const& buf); -std::vector program_buf_to_acir_format(std::vector const& buf); +std::vector program_buf_to_acir_format(std::vector const& buf, bool honk_recursion); WitnessVectorStack witness_buf_to_witness_stack(std::vector const& buf); #ifndef __wasm__ -AcirProgramStack get_acir_program_stack(std::string const& bytecode_path, std::string const& witness_path); +AcirProgramStack get_acir_program_stack(std::string const& bytecode_path, + std::string const& witness_path, + bool honk_recursion); #endif } // namespace acir_format \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp index d7ba6ba9b864..3db10487480e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -13,10 +13,12 @@ #include #include -WASM_EXPORT void acir_get_circuit_sizes(uint8_t const* acir_vec, uint32_t* exact, uint32_t* total, uint32_t* subgroup) +WASM_EXPORT void acir_get_circuit_sizes( + uint8_t const* acir_vec, bool const* honk_recursion, uint32_t* exact, uint32_t* total, uint32_t* subgroup) { - auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); - auto builder = acir_format::create_circuit(constraint_system, 1 << 19); + auto constraint_system = + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), *honk_recursion); + auto builder = acir_format::create_circuit(constraint_system, 1 << 19, {}, *honk_recursion); *exact = htonl((uint32_t)builder.get_num_gates()); *total = htonl((uint32_t)builder.get_total_circuit_size()); *subgroup = htonl((uint32_t)builder.get_circuit_subgroup_size(builder.get_total_circuit_size())); @@ -35,7 +37,8 @@ WASM_EXPORT void acir_delete_acir_composer(in_ptr acir_composer_ptr) WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); - auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); + auto constraint_system = + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); acir_composer->create_circuit(constraint_system); acir_composer->init_proving_key(); @@ -47,7 +50,8 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, uint8_t** out) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); - auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); + auto constraint_system = + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); acir_composer->create_circuit(constraint_system, witness); @@ -59,10 +63,12 @@ WASM_EXPORT void acir_create_proof(in_ptr acir_composer_ptr, WASM_EXPORT void acir_prove_and_verify_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>(acir_vec)); + auto constraint_system = + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/true); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); - auto builder = acir_format::create_circuit(constraint_system, 0, witness); + auto builder = + acir_format::create_circuit(constraint_system, 0, witness, /*honk_recursion=*/true); UltraProver prover{ builder }; auto proof = prover.construct_proof(); @@ -78,7 +84,8 @@ WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, uin using ProgramStack = acir_format::AcirProgramStack; using Builder = MegaCircuitBuilder; - auto constraint_systems = acir_format::program_buf_to_acir_format(from_buffer>(acir_vec)); + auto constraint_systems = + acir_format::program_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); auto witness_stack = acir_format::witness_buf_to_witness_stack(from_buffer>(witness_vec)); ProgramStack program_stack{ constraint_systems, witness_stack }; @@ -91,7 +98,7 @@ WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, uin // Construct a bberg circuit from the acir representation auto builder = acir_format::create_circuit( - stack_item.constraints, 0, stack_item.witness, false, ivc.goblin.op_queue); + stack_item.constraints, 0, stack_item.witness, /*honk_recursion=*/false, ivc.goblin.op_queue); ivc.accumulate(builder); @@ -102,10 +109,12 @@ WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, uin WASM_EXPORT void acir_prove_and_verify_mega_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>(acir_vec)); + auto constraint_system = + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); - auto builder = acir_format::create_circuit(constraint_system, 0, witness); + auto builder = + acir_format::create_circuit(constraint_system, 0, witness, /*honk_recursion=*/false); MegaProver prover{ builder }; auto proof = prover.construct_proof(); @@ -140,7 +149,8 @@ WASM_EXPORT void acir_get_verification_key(in_ptr acir_composer_ptr, uint8_t** o WASM_EXPORT void acir_get_proving_key(in_ptr acir_composer_ptr, uint8_t const* acir_vec, uint8_t** out) { auto acir_composer = reinterpret_cast(*acir_composer_ptr); - auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); + auto constraint_system = + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/false); acir_composer->create_circuit(constraint_system); auto pk = acir_composer->init_proving_key(); // We flatten to a vector first, as that's how we treat it on the calling side. @@ -189,10 +199,12 @@ WASM_EXPORT void acir_serialize_verification_key_into_fields(in_ptr acir_compose WASM_EXPORT void acir_prove_ultra_honk(uint8_t const* acir_vec, uint8_t const* witness_vec, uint8_t** out) { - auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); + auto constraint_system = + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/true); auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); - auto builder = acir_format::create_circuit(constraint_system, 0, witness); + auto builder = + acir_format::create_circuit(constraint_system, 0, witness, /*honk_recursion=*/true); UltraProver prover{ builder }; auto proof = prover.construct_proof(); @@ -219,8 +231,9 @@ WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, uint8_t** out using ProverInstance = ProverInstance_; using VerificationKey = UltraFlavor::VerificationKey; - auto constraint_system = acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec)); - auto builder = acir_format::create_circuit(constraint_system, 0, {}); + auto constraint_system = + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), /*honk_recursion=*/true); + auto builder = acir_format::create_circuit(constraint_system, 0, {}, /*honk_recursion=*/true); ProverInstance prover_inst(builder); VerificationKey vk(prover_inst.proving_key); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp index 72d856cc94d5..2ccaab46f423 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.hpp @@ -7,6 +7,7 @@ using namespace bb; WASM_EXPORT void acir_get_circuit_sizes(uint8_t const* constraint_system_buf, + bool const* honk_recursion, uint32_t* exact, uint32_t* total, uint32_t* subgroup); @@ -15,10 +16,6 @@ WASM_EXPORT void acir_new_acir_composer(uint32_t const* size_hint, out_ptr out); WASM_EXPORT void acir_delete_acir_composer(in_ptr acir_composer_ptr); -WASM_EXPORT void acir_create_circuit(in_ptr acir_composer_ptr, - uint8_t const* constraint_system_buf, - uint32_t const* size_hint); - WASM_EXPORT void acir_init_proving_key(in_ptr acir_composer_ptr, uint8_t const* constraint_system_buf); /** diff --git a/barretenberg/exports.json b/barretenberg/exports.json index 59d5091bd2f0..b1ecdee8d83a 100644 --- a/barretenberg/exports.json +++ b/barretenberg/exports.json @@ -500,6 +500,10 @@ { "name": "constraint_system_buf", "type": "const uint8_t *" + }, + { + "name": "honk_recursion", + "type": "const bool *" } ], "outArgs": [ diff --git a/barretenberg/ts/src/barretenberg_api/index.ts b/barretenberg/ts/src/barretenberg_api/index.ts index a5caf50e16c7..f08e4f32a527 100644 --- a/barretenberg/ts/src/barretenberg_api/index.ts +++ b/barretenberg/ts/src/barretenberg_api/index.ts @@ -328,8 +328,11 @@ export class BarretenbergApi { return; } - async acirGetCircuitSizes(constraintSystemBuf: Uint8Array): Promise<[number, number, number]> { - const inArgs = [constraintSystemBuf].map(serializeBufferable); + async acirGetCircuitSizes( + constraintSystemBuf: Uint8Array, + honkRecursion: boolean, + ): Promise<[number, number, number]> { + const inArgs = [constraintSystemBuf, honkRecursion].map(serializeBufferable); const outTypes: OutputType[] = [NumberDeserializer(), NumberDeserializer(), NumberDeserializer()]; const result = await this.wasm.callWasmExport( 'acir_get_circuit_sizes', @@ -916,8 +919,8 @@ export class BarretenbergApiSync { return; } - acirGetCircuitSizes(constraintSystemBuf: Uint8Array): [number, number, number] { - const inArgs = [constraintSystemBuf].map(serializeBufferable); + acirGetCircuitSizes(constraintSystemBuf: Uint8Array, honkRecursion: boolean): [number, number, number] { + const inArgs = [constraintSystemBuf, honkRecursion].map(serializeBufferable); const outTypes: OutputType[] = [NumberDeserializer(), NumberDeserializer(), NumberDeserializer()]; const result = this.wasm.callWasmExport( 'acir_get_circuit_sizes', diff --git a/barretenberg/ts/src/main.ts b/barretenberg/ts/src/main.ts index 448360b96d7c..a3e17540fc63 100755 --- a/barretenberg/ts/src/main.ts +++ b/barretenberg/ts/src/main.ts @@ -32,8 +32,8 @@ function getBytecode(bytecodePath: string) { return decompressed; } -async function getGates(bytecodePath: string, api: Barretenberg) { - const { total } = await computeCircuitSize(bytecodePath, api); +async function getGates(bytecodePath: string, honkRecursion: boolean, api: Barretenberg) { + const { total } = await computeCircuitSize(bytecodePath, honkRecursion, api); return total; } @@ -43,17 +43,17 @@ function getWitness(witnessPath: string) { return decompressed; } -async function computeCircuitSize(bytecodePath: string, api: Barretenberg) { +async function computeCircuitSize(bytecodePath: string, honkRecursion: boolean, api: Barretenberg) { debug(`computing circuit size...`); const bytecode = getBytecode(bytecodePath); - const [exact, total, subgroup] = await api.acirGetCircuitSizes(bytecode); + const [exact, total, subgroup] = await api.acirGetCircuitSizes(bytecode, honkRecursion); return { exact, total, subgroup }; } -async function init(bytecodePath: string, crsPath: string, subgroupSizeOverride = -1) { +async function init(bytecodePath: string, crsPath: string, subgroupSizeOverride = -1, honkRecursion = false) { const api = await Barretenberg.new({ threads }); - const circuitSize = await getGates(bytecodePath, api); + const circuitSize = await getGates(bytecodePath, honkRecursion, api); // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): remove subgroupSizeOverride hack for goblin const subgroupSize = Math.max(subgroupSizeOverride, Math.pow(2, Math.ceil(Math.log2(circuitSize)))); if (subgroupSize > MAX_CIRCUIT_SIZE) { @@ -122,7 +122,7 @@ export async function proveAndVerify(bytecodePath: string, witnessPath: string, export async function proveAndVerifyUltraHonk(bytecodePath: string, witnessPath: string, crsPath: string) { /* eslint-disable camelcase */ - const { api } = await init(bytecodePath, crsPath); + const { api } = await init(bytecodePath, crsPath, -1, true); try { const bytecode = getBytecode(bytecodePath); const witness = getWitness(witnessPath); @@ -186,10 +186,10 @@ export async function prove(bytecodePath: string, witnessPath: string, crsPath: } } -export async function gateCount(bytecodePath: string) { +export async function gateCount(bytecodePath: string, honkRecursion: boolean) { const api = await Barretenberg.new({ threads: 1 }); try { - const numberOfGates = await getGates(bytecodePath, api); + const numberOfGates = await getGates(bytecodePath, honkRecursion, api); // Create an 8-byte buffer and write the number into it. // Writing number directly to stdout will result in a variable sized @@ -326,7 +326,7 @@ export async function vkAsFields(vkPath: string, vkeyOutputPath: string) { } export async function proveUltraHonk(bytecodePath: string, witnessPath: string, crsPath: string, outputPath: string) { - const { api } = await init(bytecodePath, crsPath); + const { api } = await init(bytecodePath, crsPath, -1, true); try { debug(`creating proof...`); const bytecode = getBytecode(bytecodePath); @@ -347,7 +347,7 @@ export async function proveUltraHonk(bytecodePath: string, witnessPath: string, } export async function writeVkUltraHonk(bytecodePath: string, crsPath: string, outputPath: string) { - const { api } = await init(bytecodePath, crsPath); + const { api } = await init(bytecodePath, crsPath, -1, true); try { const bytecode = getBytecode(bytecodePath); debug('initing verification key...'); @@ -489,9 +489,10 @@ program .command('gates') .description('Print gate count to standard output.') .option('-b, --bytecode-path ', 'Specify the bytecode path', './target/program.json') - .action(async ({ bytecodePath: bytecodePath }) => { + .option('-hr, --honk-recursion ', 'Specify whether to use UltraHonk recursion', 'false') + .action(async ({ bytecodePath: bytecodePath, honkRecursion: honkRecursion }) => { handleGlobalOptions(); - await gateCount(bytecodePath); + await gateCount(bytecodePath, honkRecursion); }); program diff --git a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts index d07681dd8c11..ce2c27124912 100644 --- a/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts +++ b/noir/noir-repo/tooling/noir_js_backend_barretenberg/src/backend.ts @@ -45,7 +45,10 @@ export class BarretenbergVerifierBackend implements VerifierBackend { const { Barretenberg, RawBuffer, Crs } = await import('@aztec/bb.js'); const api = await Barretenberg.new(this.options); - const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes(this.acirUncompressedBytecode); + const [_exact, _total, subgroupSize] = await api.acirGetCircuitSizes( + this.acirUncompressedBytecode, + /*honkRecursion=*/ false, // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): Remove this flag + ); const crs = await Crs.new(subgroupSize + 1); await api.commonInitSlabAllocator(subgroupSize); await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data()));