diff --git a/barretenberg/cpp/src/barretenberg/bb/api_client_ivc.hpp b/barretenberg/cpp/src/barretenberg/bb/api_client_ivc.hpp index 8576f918d40..8d0705b91c0 100644 --- a/barretenberg/cpp/src/barretenberg/bb/api_client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/bb/api_client_ivc.hpp @@ -125,79 +125,38 @@ class ClientIVCAPI : public API { return folding_stack; }; - static ClientIVC _accumulate(std::vector& folding_stack) + static std::shared_ptr _accumulate(std::vector& folding_stack, + bool auto_verify = false) { using Builder = MegaCircuitBuilder; using Program = acir_format::AcirProgram; - using namespace acir_format; - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically - init_bn254_crs(1 << 20); - init_grumpkin_crs(1 << 15); + vinfo("performing accumulation with auto-verify = ", auto_verify); + + TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE }; + auto ivc = std::make_shared(trace_settings, auto_verify); - // 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{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; + const ProgramMetadata metadata{ ivc }; // Accumulate the entire program stack into the IVC // TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel bool is_kernel = false; for (Program& program : folding_stack) { // Construct a bberg circuit from the acir representation then accumulate it into the IVC - Builder circuit = acir_format::create_circuit( - program.constraints, true, 0, program.witness, false, ivc.goblin.op_queue); + Builder circuit = acir_format::create_circuit(program, metadata); // 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; + if (ivc->auto_verify_mode) { + if (!circuit.databus_propagation_data.is_kernel) { + circuit.databus_propagation_data.is_kernel = is_kernel; + } + is_kernel = !is_kernel; } - is_kernel = !is_kernel; // Do one step of ivc accumulator or, if there is only one circuit in the stack, prove that circuit. In this // case, no work is added to the Goblin opqueue, but VM proofs for trivials inputs are produced. - ivc.accumulate(circuit, /*one_circuit=*/folding_stack.size() == 1); - } - - return ivc; - }; - - static ClientIVC _accumulate_without_auto_verify(std::vector& folding_stack) - { - using Builder = MegaCircuitBuilder; - using Program = acir_format::AcirProgram; - - using namespace acir_format; - - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically - init_bn254_crs(1 << 20); - init_grumpkin_crs(1 << 15); - - // 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{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/false }; - - // 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) { - - Builder circuit; - - is_kernel = !program.constraints.ivc_recursion_constraints.empty(); - if (is_kernel) { - vinfo("Accumulating KERNEL."); - circuit = create_kernel_circuit(program.constraints, ivc, program.witness); - } else { - vinfo("Accumulating APP."); - circuit = create_circuit( - program.constraints, /*recursive=*/false, 0, program.witness, false, ivc.goblin.op_queue); - } - - // Do one step of ivc accumulator or, if there is only one circuit in the stack, prove that circuit. In this - // case, no work is added to the Goblin opqueue, but VM proofs for trivial inputs are produced. - ivc.accumulate(circuit, /*one_circuit=*/folding_stack.size() == 1); + ivc->accumulate(circuit, /*one_circuit=*/folding_stack.size() == 1); } return ivc; @@ -217,33 +176,27 @@ class ClientIVCAPI : public API { throw_or_abort("No input_type or input_type not supported"); } - std::vector folding_stack = - _build_folding_stack(*flags.input_type, bytecode_path, witness_path); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically init_bn254_crs(1 << 20); init_grumpkin_crs(1 << 15); - ClientIVC ivc; - if (flags.no_auto_verify) { - vinfo("performing accumulation WITHOUT auto-verify"); - ivc = _accumulate_without_auto_verify(folding_stack); - } else { - vinfo("performing accumulation with auto-verify"); - ivc = _accumulate(folding_stack); - } - ClientIVC::Proof proof = ivc.prove(); + std::vector folding_stack = + _build_folding_stack(*flags.input_type, bytecode_path, witness_path); + + bool auto_verify = !flags.no_auto_verify; + std::shared_ptr ivc = _accumulate(folding_stack, auto_verify); + ClientIVC::Proof proof = ivc->prove(); // Write the proof and verification keys into the working directory in 'binary' format (in practice it seems // this directory is passed by bb.js) vinfo("writing ClientIVC proof and vk..."); write_file(output_dir / "client_ivc_proof", to_buffer(proof)); - auto eccvm_vk = std::make_shared(ivc.goblin.get_eccvm_proving_key()); + auto eccvm_vk = std::make_shared(ivc->goblin.get_eccvm_proving_key()); auto translator_vk = - std::make_shared(ivc.goblin.get_translator_proving_key()); + std::make_shared(ivc->goblin.get_translator_proving_key()); write_file(output_dir / "client_ivc_vk", - to_buffer(ClientIVC::VerificationKey{ ivc.honk_vk, eccvm_vk, translator_vk })); + to_buffer(ClientIVC::VerificationKey{ ivc->honk_vk, eccvm_vk, translator_vk })); }; /** @@ -286,10 +239,15 @@ class ClientIVCAPI : public API { if (!flags.input_type || !(*flags.input_type == "compiletime_stack" || *flags.input_type == "runtime_stack")) { throw_or_abort("No input_type or input_type not supported"); } + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically + init_bn254_crs(1 << 20); + init_grumpkin_crs(1 << 15); + std::vector folding_stack = _build_folding_stack(*flags.input_type, bytecode_path, witness_path); - ClientIVC ivc = _accumulate(folding_stack); - const bool verified = ivc.prove_and_verify(); + std::shared_ptr ivc = _accumulate(folding_stack, /*auto_verify=*/true); + const bool verified = ivc->prove_and_verify(); return verified; }; diff --git a/barretenberg/cpp/src/barretenberg/bb/main.cpp b/barretenberg/cpp/src/barretenberg/bb/main.cpp index 79d3e5511f9..c3850f41ae0 100644 --- a/barretenberg/cpp/src/barretenberg/bb/main.cpp +++ b/barretenberg/cpp/src/barretenberg/bb/main.cpp @@ -110,21 +110,15 @@ bool proveAndVerify(const std::string& bytecodePath, const bool recursive, const } template -bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, - const bool recursive, - acir_format::WitnessVector witness) +bool proveAndVerifyHonkAcirFormat(acir_format::AcirProgram program, acir_format::ProgramMetadata metadata) { using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; 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, recursive, 0, witness, honk_recursion); + auto builder = acir_format::create_circuit(program, metadata); // Construct Honk proof Prover prover{ builder }; @@ -149,15 +143,15 @@ bool proveAndVerifyHonkAcirFormat(acir_format::AcirFormat constraint_system, template bool proveAndVerifyHonk(const std::string& bytecodePath, const bool recursive, const std::string& witnessPath) { - bool honk_recursion = false; - if constexpr (IsAnyOf) { - honk_recursion = true; - } + constexpr bool honk_recursion = IsAnyOf; + const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = honk_recursion }; + // Populate the acir constraint system and witness from gzipped data - auto constraint_system = get_constraint_system(bytecodePath, honk_recursion); - auto witness = get_witness(witnessPath); + acir_format::AcirProgram program; + program.constraints = get_constraint_system(bytecodePath, metadata.honk_recursion); + program.witness = get_witness(witnessPath); - return proveAndVerifyHonkAcirFormat(constraint_system, recursive, witness); + return proveAndVerifyHonkAcirFormat(program, metadata); } /** @@ -171,14 +165,14 @@ bool proveAndVerifyHonk(const std::string& bytecodePath, const bool recursive, c template bool proveAndVerifyHonkProgram(const std::string& bytecodePath, const bool recursive, const std::string& witnessPath) { - bool honk_recursion = false; - if constexpr (IsAnyOf) { - honk_recursion = true; - } - auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath, honk_recursion); + constexpr bool honk_recursion = IsAnyOf; + const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = honk_recursion }; + + auto program_stack = acir_format::get_acir_program_stack(bytecodePath, witnessPath, metadata.honk_recursion); + while (!program_stack.empty()) { - auto stack_item = program_stack.back(); - if (!proveAndVerifyHonkAcirFormat(stack_item.constraints, recursive, stack_item.witness)) { + auto program = program_stack.back(); + if (!proveAndVerifyHonkAcirFormat(program, metadata)) { return false; } program_stack.pop_back(); @@ -329,25 +323,29 @@ void gateCount(const std::string& bytecodePath, bool recursive, bool honk_recurs // All circuit reports will be built into the string below std::string functions_string = "{\"functions\": [\n "; auto constraint_systems = get_constraint_systems(bytecodePath, honk_recursion); + + const acir_format::ProgramMetadata metadata{ .recursive = recursive, + .honk_recursion = honk_recursion, + .collect_gates_per_opcode = true }; size_t i = 0; - for (auto constraint_system : constraint_systems) { - auto builder = acir_format::create_circuit( - constraint_system, recursive, 0, {}, honk_recursion, std::make_shared(), true); + for (const auto& constraint_system : constraint_systems) { + acir_format::AcirProgram program{ constraint_system }; + auto builder = acir_format::create_circuit(program, metadata); builder.finalize_circuit(/*ensure_nonzero=*/true); size_t circuit_size = builder.num_gates; vinfo("Calculated circuit size in gateCount: ", circuit_size); // Build individual circuit report std::string gates_per_opcode_str; - for (size_t j = 0; j < constraint_system.gates_per_opcode.size(); j++) { - gates_per_opcode_str += std::to_string(constraint_system.gates_per_opcode[j]); - if (j != constraint_system.gates_per_opcode.size() - 1) { + for (size_t j = 0; j < program.constraints.gates_per_opcode.size(); j++) { + gates_per_opcode_str += std::to_string(program.constraints.gates_per_opcode[j]); + if (j != program.constraints.gates_per_opcode.size() - 1) { gates_per_opcode_str += ","; } } auto result_string = format("{\n \"acir_opcodes\": ", - constraint_system.num_acir_opcodes, + program.constraints.num_acir_opcodes, ",\n \"circuit_size\": ", circuit_size, ",\n \"gates_per_opcode\": [", @@ -714,17 +712,15 @@ UltraProver_ compute_valid_prover(const std::string& bytecodePath, using Builder = Flavor::CircuitBuilder; using Prover = UltraProver_; - bool honk_recursion = false; - if constexpr (IsAnyOf) { - honk_recursion = true; - } - auto constraint_system = get_constraint_system(bytecodePath, honk_recursion); - acir_format::WitnessVector witness = {}; + constexpr bool honk_recursion = IsAnyOf; + const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = honk_recursion }; + + acir_format::AcirProgram program{ get_constraint_system(bytecodePath, metadata.honk_recursion) }; if (!witnessPath.empty()) { - witness = get_witness(witnessPath); + program.witness = get_witness(witnessPath); } - auto builder = acir_format::create_circuit(constraint_system, recursive, 0, witness, honk_recursion); + auto builder = acir_format::create_circuit(program, metadata); auto prover = Prover{ builder }; init_bn254_crs(prover.proving_key->proving_key.circuit_size); return std::move(prover); @@ -847,28 +843,23 @@ void write_vk_for_ivc(const std::string& bytecodePath, const std::string& output using Prover = ClientIVC::MegaProver; using DeciderProvingKey = ClientIVC::DeciderProvingKey; using VerificationKey = ClientIVC::MegaVerificationKey; + using Program = acir_format::AcirProgram; + using ProgramMetadata = acir_format::ProgramMetadata; // TODO(https://github.com/AztecProtocol/barretenberg/issues/1163) set these dynamically init_bn254_crs(1 << 20); init_grumpkin_crs(1 << 15); - auto constraints = get_constraint_system(bytecodePath, /*honk_recursion=*/false); - acir_format::WitnessVector witness = {}; + Program program{ get_constraint_system(bytecodePath, /*honk_recursion=*/false), /*witness=*/{} }; + auto& ivc_constraints = program.constraints.ivc_recursion_constraints; TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE }; - // The presence of ivc recursion constraints determines whether or not the program is a kernel - bool is_kernel = !constraints.ivc_recursion_constraints.empty(); + const ProgramMetadata metadata{ .ivc = ivc_constraints.empty() + ? nullptr + : create_mock_ivc_from_constraints(ivc_constraints, trace_settings) }; + Builder builder = acir_format::create_circuit(program, metadata); - Builder builder; - if (is_kernel) { - // Create a mock IVC instance based on the IVC recursion constraints in the kernel program - ClientIVC mock_ivc = create_mock_ivc_from_constraints(constraints.ivc_recursion_constraints, trace_settings); - builder = acir_format::create_kernel_circuit(constraints, mock_ivc, witness); - } else { - builder = acir_format::create_circuit( - constraints, /*recursive=*/false, 0, witness, /*honk_recursion=*/false); - } // Add public inputs corresponding to pairing point accumulator builder.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices(builder)); @@ -908,10 +899,12 @@ void write_recursion_inputs_honk(const std::string& bytecodePath, using VerificationKey = Flavor::VerificationKey; using FF = Flavor::FF; - bool honk_recursion = true; - auto constraints = get_constraint_system(bytecodePath, honk_recursion); - auto witness = get_witness(witnessPath); - auto builder = acir_format::create_circuit(constraints, recursive, 0, witness, honk_recursion); + const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = true }; + + acir_format::AcirProgram program; + program.constraints = get_constraint_system(bytecodePath, metadata.honk_recursion); + program.witness = get_witness(witnessPath); + auto builder = acir_format::create_circuit(program, metadata); // Construct Honk proof and verification key Prover prover{ builder }; @@ -1059,15 +1052,13 @@ void prove_honk_output_all(const std::string& bytecodePath, using Prover = UltraProver_; using VerificationKey = Flavor::VerificationKey; - bool honk_recursion = false; - if constexpr (IsAnyOf) { - honk_recursion = true; - } + constexpr bool honk_recursion = IsAnyOf; + const acir_format::ProgramMetadata metadata{ .recursive = recursive, .honk_recursion = honk_recursion }; - auto constraint_system = get_constraint_system(bytecodePath, honk_recursion); - auto witness = get_witness(witnessPath); + acir_format::AcirProgram program{ get_constraint_system(bytecodePath, metadata.honk_recursion), + get_witness(witnessPath) }; - auto builder = acir_format::create_circuit(constraint_system, recursive, 0, witness, honk_recursion); + auto builder = acir_format::create_circuit(program, metadata); // Construct Honk proof Prover prover{ builder }; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp index a4a864625ca..1a8f5d7f43f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -19,12 +19,12 @@ template class DSLBigInts; template class DSLBigInts; template -void build_constraints(Builder& builder, - AcirFormat& constraint_system, - bool has_valid_witness_assignments, - bool honk_recursion, - bool collect_gates_per_opcode) +void build_constraints(Builder& builder, AcirProgram& program, const ProgramMetadata& metadata) { + bool has_valid_witness_assignments = !program.witness.empty(); + bool collect_gates_per_opcode = metadata.collect_gates_per_opcode; + AcirFormat& constraint_system = program.constraints; + if (collect_gates_per_opcode) { constraint_system.gates_per_opcode.resize(constraint_system.num_acir_opcodes, 0); } @@ -231,6 +231,9 @@ void build_constraints(Builder& builder, if (!constraint_system.avm_recursion_constraints.empty()) { info("WARNING: this circuit contains unhandled avm_recursion_constraints!"); } + if (!constraint_system.ivc_recursion_constraints.empty()) { + process_ivc_recursion_constraints(builder, constraint_system, metadata.ivc, has_valid_witness_assignments); + } } else { process_plonk_recursion_constraints(builder, constraint_system, has_valid_witness_assignments, gate_counter); PairingPointAccumulatorIndices current_aggregation_object = @@ -246,9 +249,9 @@ void build_constraints(Builder& builder, // default one if the circuit is recursive and honk_recursion is true. if (!constraint_system.honk_recursion_constraints.empty() || !constraint_system.avm_recursion_constraints.empty()) { - ASSERT(honk_recursion); + ASSERT(metadata.honk_recursion); builder.add_pairing_point_accumulator(current_aggregation_object); - } else if (honk_recursion && builder.is_recursive_circuit) { + } else if (metadata.honk_recursion && builder.is_recursive_circuit) { // Make sure the verification key records the public input indices of the // final recursion output. builder.add_pairing_point_accumulator(current_aggregation_object); @@ -392,6 +395,59 @@ PairingPointAccumulatorIndices process_honk_recursion_constraints( return current_aggregation_object; } +void process_ivc_recursion_constraints(MegaCircuitBuilder& builder, + AcirFormat& constraints, + const std::shared_ptr& ivc, + bool has_valid_witness_assignments) +{ + using StdlibVerificationKey = ClientIVC::RecursiveVerificationKey; + + // We expect the length of the internal verification queue to match the number of ivc recursion constraints + if (constraints.ivc_recursion_constraints.size() != ivc->verification_queue.size()) { + info("WARNING: Mismatch in number of recursive verifications during kernel creation!"); + ASSERT(false); + } + + // If no witness is provided, populate the VK and public inputs in the recursion constraint with dummy values so + // that the present kernel circuit is constructed correctly. (Used for constructing VKs without witnesses). + if (!has_valid_witness_assignments) { + // Create stdlib representations of each {proof, vkey} pair to be recursively verified + for (auto [constraint, queue_entry] : + zip_view(constraints.ivc_recursion_constraints, ivc->verification_queue)) { + populate_dummy_vk_in_constraint(builder, queue_entry.honk_verification_key, constraint.key); + } + } + + // Construct a stdlib verification key for each constraint based on the verification key witness indices therein + std::vector> stdlib_verification_keys; + stdlib_verification_keys.reserve(constraints.ivc_recursion_constraints.size()); + for (const auto& constraint : constraints.ivc_recursion_constraints) { + stdlib_verification_keys.push_back(std::make_shared( + StdlibVerificationKey::from_witness_indices(builder, constraint.key))); + } + // Create stdlib representations of each {proof, vkey} pair to be recursively verified + ivc->instantiate_stdlib_verification_queue(builder, stdlib_verification_keys); + + // Connect the public_input witnesses in each constraint to the corresponding public input witnesses in the internal + // verification queue. This ensures that the witnesses utlized in constraints generated based on acir are properly + // connected to the constraints generated herein via the ivc scheme (e.g. recursive verifications). + for (auto [constraint, queue_entry] : + zip_view(constraints.ivc_recursion_constraints, ivc->stdlib_verification_queue)) { + + // Get the witness indices for the public inputs contained within the proof in the verification queue + std::vector public_input_indices = ProofSurgeon::get_public_inputs_witness_indices_from_proof( + queue_entry.proof, constraint.public_inputs.size()); + + // Assert equality between the internal public input witness indices and those in the acir constraint + for (auto [witness_idx, constraint_witness_idx] : zip_view(public_input_indices, constraint.public_inputs)) { + builder.assert_equal(witness_idx, constraint_witness_idx); + } + } + + // Complete the kernel circuit with all required recursive verifications, databus consistency checks etc. + ivc->complete_kernel_circuit_logic(builder); +} + #ifndef DISABLE_AZTEC_VM PairingPointAccumulatorIndices process_avm_recursion_constraints( Builder& builder, @@ -414,28 +470,19 @@ PairingPointAccumulatorIndices process_avm_recursion_constraints( #endif // DISABLE_AZTEC_VM /** - * @brief Specialization for creating Ultra circuit from acir constraints and optionally a witness + * @brief Specialization for creating an Ultra circuit from an acir program * - * @tparam Builder - * @param constraint_system - * @param size_hint - * @param witness - * @return Builder + * @param program constraints and optionally a witness + * @param metadata additional data needed to construct the circuit */ -template <> -UltraCircuitBuilder create_circuit(AcirFormat& constraint_system, - bool recursive, - const size_t size_hint, - const WitnessVector& witness, - bool honk_recursion, - [[maybe_unused]] std::shared_ptr, - bool collect_gates_per_opcode) +template <> UltraCircuitBuilder create_circuit(AcirProgram& program, const ProgramMetadata& metadata) { - Builder builder{ size_hint, witness, constraint_system.public_inputs, constraint_system.varnum, recursive }; + AcirFormat& constraints = program.constraints; + WitnessVector& witness = program.witness; + + Builder builder{ metadata.size_hint, witness, constraints.public_inputs, constraints.varnum, metadata.recursive }; - bool has_valid_witness_assignments = !witness.empty(); - build_constraints( - builder, constraint_system, has_valid_witness_assignments, honk_recursion, collect_gates_per_opcode); + build_constraints(builder, program, metadata); vinfo("created circuit"); @@ -443,114 +490,59 @@ UltraCircuitBuilder create_circuit(AcirFormat& constraint_system, }; /** - * @brief Specialization for creating Mega circuit from acir constraints and optionally a witness + * @brief Specialization for creating a Mega circuit from an acir program * - * @tparam Builder - * @param constraint_system - * @param size_hint - * @param witness - * @return Builder + * @param program constraints and optionally a witness + * @param metadata additional data needed to construct the circuit */ -template <> -MegaCircuitBuilder create_circuit(AcirFormat& constraint_system, - [[maybe_unused]] bool recursive, - [[maybe_unused]] const size_t size_hint, - const WitnessVector& witness, - bool honk_recursion, - std::shared_ptr op_queue, - bool collect_gates_per_opcode) +template <> MegaCircuitBuilder create_circuit(AcirProgram& program, const ProgramMetadata& metadata) { + AcirFormat& constraints = program.constraints; + WitnessVector& witness = program.witness; + + auto op_queue = (metadata.ivc == nullptr) ? std::make_shared() : metadata.ivc->goblin.op_queue; + // Construct a builder using the witness and public input data from acir and with the goblin-owned op_queue - auto builder = MegaCircuitBuilder{ op_queue, witness, constraint_system.public_inputs, constraint_system.varnum }; + auto builder = MegaCircuitBuilder{ op_queue, witness, constraints.public_inputs, constraints.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, honk_recursion, collect_gates_per_opcode); + build_constraints(builder, program, metadata); return builder; }; /** - * @brief Create a kernel circuit from a constraint system and an IVC instance - * @details This method processes ivc_recursion_constraints using the kernel completion logic contained in ClientIVC. - * Since verification keys are known at the time of acir generation, the verification key witnesses contained in the - * constraints are used directly to instantiate the recursive verifiers. On the other hand, the proof witnesses - * contained in the constraints are generally 'dummy' values since proofs are not known during acir generation (with the - * exception of public inputs). This is remedied by connecting the dummy proof witnesses to the genuine proof witnesses, - * known internally to the IVC class, via copy constraints. + * @brief Specialization for creating Ultra circuit from acir constraints and optionally a witness * - * @param constraint_system AcirFormat constraint system possibly containing IVC recursion constraints - * @param ivc An IVC instance containing internal data about proofs to be verified + * @tparam Builder + * @param constraint_system * @param size_hint * @param witness - * @return MegaCircuitBuilder + * @return Builder */ -MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system, - ClientIVC& ivc, - const WitnessVector& witness, - const size_t size_hint) +template <> +UltraCircuitBuilder create_circuit(AcirFormat& constraint_system, + bool recursive, + const size_t size_hint, + const WitnessVector& witness, + bool honk_recursion, + [[maybe_unused]] std::shared_ptr, + bool collect_gates_per_opcode) { - using StdlibVerificationKey = ClientIVC::RecursiveVerificationKey; - - // Construct the main kernel circuit logic excluding recursive verifiers - auto circuit = create_circuit(constraint_system, - /*recursive=*/false, - size_hint, - witness, - /*honk_recursion=*/false, - ivc.goblin.op_queue, - /*collect_gates_per_opcode=*/false); - - // We expect the length of the internal verification queue to match the number of ivc recursion constraints - if (constraint_system.ivc_recursion_constraints.size() != ivc.verification_queue.size()) { - info("WARNING: Mismatch in number of recursive verifications during kernel creation!"); - ASSERT(false); - } - - // If no witness is provided, populate the VK and public inputs in the recursion constraint with dummy values so - // that the present kernel circuit is constructed correctly. (Used for constructing VKs without witnesses). - if (witness.empty()) { - // Create stdlib representations of each {proof, vkey} pair to be recursively verified - for (auto [constraint, queue_entry] : - zip_view(constraint_system.ivc_recursion_constraints, ivc.verification_queue)) { - - populate_dummy_vk_in_constraint(circuit, queue_entry.honk_verification_key, constraint.key); - } - } - - // Construct a stdlib verification key for each constraint based on the verification key witness indices therein - std::vector> stdlib_verification_keys; - stdlib_verification_keys.reserve(constraint_system.ivc_recursion_constraints.size()); - for (const auto& constraint : constraint_system.ivc_recursion_constraints) { - stdlib_verification_keys.push_back(std::make_shared( - StdlibVerificationKey::from_witness_indices(circuit, constraint.key))); - } - // Create stdlib representations of each {proof, vkey} pair to be recursively verified - ivc.instantiate_stdlib_verification_queue(circuit, stdlib_verification_keys); - - // Connect the public_input witnesses in each constraint to the corresponding public input witnesses in the internal - // verification queue. This ensures that the witnesses utlized in constraints generated based on acir are properly - // connected to the constraints generated herein via the ivc scheme (e.g. recursive verifications). - for (auto [constraint, queue_entry] : - zip_view(constraint_system.ivc_recursion_constraints, ivc.stdlib_verification_queue)) { - - // Get the witness indices for the public inputs contained within the proof in the verification queue - std::vector public_input_indices = ProofSurgeon::get_public_inputs_witness_indices_from_proof( - queue_entry.proof, constraint.public_inputs.size()); + Builder builder{ size_hint, witness, constraint_system.public_inputs, constraint_system.varnum, recursive }; - // Assert equality between the internal public input witness indices and those in the acir constraint - for (auto [witness_idx, constraint_witness_idx] : zip_view(public_input_indices, constraint.public_inputs)) { - circuit.assert_equal(witness_idx, constraint_witness_idx); - } - } + AcirProgram program{ constraint_system, witness }; + const ProgramMetadata metadata{ .recursive = recursive, + .honk_recursion = honk_recursion, + .collect_gates_per_opcode = collect_gates_per_opcode, + .size_hint = size_hint }; + build_constraints(builder, program, metadata); - // Complete the kernel circuit with all required recursive verifications, databus consistency checks etc. - ivc.complete_kernel_circuit_logic(circuit); + vinfo("created circuit"); - return circuit; + return builder; }; -template void build_constraints(MegaCircuitBuilder&, AcirFormat&, bool, bool, bool); +template void build_constraints(MegaCircuitBuilder&, AcirProgram&, const ProgramMetadata&); } // namespace acir_format diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp index aaa7d40ac0b..6a6c6efd7da 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -160,7 +160,7 @@ using WitnessVectorStack = std::vector>; struct AcirProgram { AcirFormat constraints; - WitnessVector witness; + WitnessVector witness = {}; }; /** @@ -193,6 +193,27 @@ struct AcirProgramStack { void pop_back() { witness_stack.pop_back(); } }; +struct ProgramMetadata { + + // An IVC instance; needed to construct a circuit from IVC recursion constraints + std::shared_ptr ivc = nullptr; + + bool recursive = false; // Specifies whether a prover that produces SNARK recursion friendly proofs should be used. + // The proof produced when this flag is true should be friendly for recursive verification + // inside of another SNARK. For example, a recursive friendly proof may use Blake3Pedersen + // for hashing in its transcript, while we still want a prove that uses Keccak for its + // transcript in order to be able to verify SNARKs on Ethereum. + bool honk_recursion = false; // honk_recursion means we will honk to recursively verify this + // circuit. This distinction is needed to not add the default + // aggregation object when we're not using the honk RV. + bool collect_gates_per_opcode = false; + size_t size_hint = 0; +}; + +// TODO(https://github.com/AztecProtocol/barretenberg/issues/1161) Refactor this function +template +Builder create_circuit(AcirProgram& program, const ProgramMetadata& metadata = ProgramMetadata{}); + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1161) Refactor this function template Builder create_circuit(AcirFormat& constraint_system, @@ -208,20 +229,8 @@ Builder create_circuit(AcirFormat& constraint_system, std::shared_ptr op_queue = std::make_shared(), bool collect_gates_per_opcode = false); -MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system, - ClientIVC& ivc, - const WitnessVector& witness = {}, - const size_t size_hint = 0); - template -void build_constraints( - Builder& builder, - AcirFormat& constraint_system, - bool has_valid_witness_assignments, - bool honk_recursion = false, - bool collect_gates_per_opcode = false); // honk_recursion means we will honk to recursively verify this - // circuit. This distinction is needed to not add the default - // aggregation object when we're not using the honk RV. +void build_constraints(Builder& builder, AcirProgram& program, const ProgramMetadata& metadata); /** * @brief Utility class for tracking the gate count of acir constraints @@ -267,6 +276,11 @@ void process_honk_recursion_constraints(Builder& builder, bool has_valid_witness_assignments, GateCounter& gate_counter); +void process_ivc_recursion_constraints(MegaCircuitBuilder& builder, + AcirFormat& constraints, + ClientIVC* ivc, + bool has_valid_witness_assignments); + #ifndef DISABLE_AZTEC_VM void process_avm_recursion_constraints(Builder& builder, AcirFormat& constraint_system, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp index 87e95cbcd8a..c2f9d7d997e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.test.cpp @@ -341,15 +341,11 @@ TEST_F(AcirFormatTests, TestCollectsGateCounts) }; mock_opcode_indices(constraint_system); WitnessVector witness{ 5, 27, 32 }; - auto builder = create_circuit(constraint_system, - /*recursive*/ false, - /*size_hint*/ 0, - witness, - false, - std::make_shared(), - true); - - EXPECT_EQ(constraint_system.gates_per_opcode, std::vector({ 2, 1 })); + AcirProgram program{ constraint_system, witness }; + const ProgramMetadata metadata{ .collect_gates_per_opcode = true }; + auto builder = create_circuit(program, metadata); + + EXPECT_EQ(program.constraints.gates_per_opcode, std::vector({ 2, 1 })); } TEST_F(AcirFormatTests, TestBigAdd) 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 45bf0703edb..3ed1f1df1c8 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 @@ -154,8 +154,7 @@ TEST_P(AcirIntegrationSingleTest, DISABLED_ProveAndVerifyProgram) 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, /*recursive*/ false, 0, acir_program.witness); + Builder builder = acir_format::create_circuit(acir_program); // Construct and verify Honk proof if constexpr (IsPlonkFlavor) { @@ -380,8 +379,7 @@ TEST_P(AcirIntegrationFoldingTest, DISABLED_ProveAndVerifyProgramStack) auto program = program_stack.back(); // Construct a bberg circuit from the acir representation - auto builder = - acir_format::create_circuit(program.constraints, /*recursive*/ false, 0, program.witness); + auto builder = acir_format::create_circuit(program); // Construct and verify Honk proof for the individidual circuit EXPECT_TRUE(prove_and_verify_honk(builder)); @@ -400,16 +398,18 @@ TEST_P(AcirIntegrationFoldingTest, DISABLED_FoldAndVerifyProgramStack) test_name, /*honk_recursion=*/false); // TODO(https://github.com/AztecProtocol/barretenberg/issues/1013): // Assumes Flavor is not UltraHonk - ClientIVC ivc{ { SMALL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; + TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; + auto ivc = std::make_shared(trace_settings, /*auto_verify_mode=*/true); + + const acir_format::ProgramMetadata metadata{ ivc }; while (!program_stack.empty()) { auto program = program_stack.back(); // Construct a bberg circuit from the acir representation - auto circuit = acir_format::create_circuit( - program.constraints, /*recursive*/ false, 0, program.witness, false, ivc.goblin.op_queue); + auto circuit = acir_format::create_circuit(program, metadata); - ivc.accumulate(circuit); + ivc->accumulate(circuit); CircuitChecker::check(circuit); // EXPECT_TRUE(prove_and_verify_honk(circuit)); @@ -417,7 +417,7 @@ TEST_P(AcirIntegrationFoldingTest, DISABLED_FoldAndVerifyProgramStack) program_stack.pop_back(); } - EXPECT_TRUE(ivc.prove_and_verify()); + EXPECT_TRUE(ivc->prove_and_verify()); } INSTANTIATE_TEST_SUITE_P(AcirTests, @@ -438,8 +438,7 @@ TEST_F(AcirIntegrationTest, DISABLED_Databus) acir_format::AcirProgram acir_program = get_program_data_from_test_file(test_name); // Construct a bberg circuit from the acir representation - Builder builder = - acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); + Builder builder = acir_format::create_circuit(acir_program); // This prints a summary of the types of gates in the circuit builder.blocks.summarize(); @@ -463,8 +462,7 @@ TEST_F(AcirIntegrationTest, DISABLED_DatabusTwoCalldata) acir_format::AcirProgram acir_program = get_program_data_from_test_file(test_name); // Construct a bberg circuit from the acir representation - Builder builder = - acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); + Builder builder = acir_format::create_circuit(acir_program); // Check that the databus columns in the builder have been populated as expected const auto& calldata = builder.get_calldata(); @@ -518,8 +516,7 @@ TEST_F(AcirIntegrationTest, DISABLED_UpdateAcirCircuit) // Assumes Flavor is not UltraHonk // Construct a bberg circuit from the acir representation - auto circuit = - acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); + Builder circuit = acir_format::create_circuit(acir_program); EXPECT_TRUE(CircuitChecker::check(circuit)); @@ -558,8 +555,7 @@ TEST_F(AcirIntegrationTest, DISABLED_HonkRecursion) /*honk_recursion=*/false); // Construct a bberg circuit from the acir representation - auto circuit = - acir_format::create_circuit(acir_program.constraints, /*recursive*/ false, 0, acir_program.witness); + Builder circuit = acir_format::create_circuit(acir_program); EXPECT_TRUE(CircuitChecker::check(circuit)); EXPECT_TRUE(prove_and_verify_honk(circuit)); diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.cpp index d6bf0d93323..b39d9e33882 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.cpp @@ -87,7 +87,6 @@ void create_block_constraints(MegaCircuitBuilder& builder, // The presence of calldata is used to indicate that the present circuit is a kernel. This is needed in the // databus consistency checks to indicate that the corresponding return data belongs to a kernel (else an app). // TODO(https://github.com/AztecProtocol/barretenberg/issues/1165): is_kernel must be known prior to this stage - // since we must determine whether to use create_circuit or create_kernel_circuit. Resolve. builder.databus_propagation_data.is_kernel = true; } break; case BlockType::ReturnData: { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp index 6eed50bf027..53ae075c146 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/block_constraint.test.cpp @@ -136,9 +136,9 @@ size_t generate_block_constraint(BlockConstraint& constraint, WitnessVector& wit TEST_F(UltraPlonkRAM, TestBlockConstraint) { BlockConstraint block; - WitnessVector witness_values; - size_t num_variables = generate_block_constraint(block, witness_values); - AcirFormat constraint_system{ + AcirProgram program; + size_t num_variables = generate_block_constraint(block, program.witness); + program.constraints = { .varnum = static_cast(num_variables), .num_acir_opcodes = 7, .public_inputs = {}, @@ -169,9 +169,9 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .block_constraints = { block }, .original_opcode_indices = create_empty_original_opcode_indices(), }; - mock_opcode_indices(constraint_system); + mock_opcode_indices(program.constraints); - auto builder = create_circuit(constraint_system, /*recursive*/ false, /*size_hint*/ 0, witness_values); + auto builder = create_circuit(program); auto composer = Composer(); auto prover = composer.create_prover(builder); @@ -184,11 +184,11 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) TEST_F(MegaHonk, Databus) { BlockConstraint block; - WitnessVector witness_values; - size_t num_variables = generate_block_constraint(block, witness_values); + AcirProgram program; + size_t num_variables = generate_block_constraint(block, program.witness); block.type = BlockType::CallData; - AcirFormat constraint_system{ + program.constraints = { .varnum = static_cast(num_variables), .num_acir_opcodes = 1, .public_inputs = {}, @@ -219,10 +219,10 @@ TEST_F(MegaHonk, Databus) .block_constraints = { block }, .original_opcode_indices = create_empty_original_opcode_indices(), }; - mock_opcode_indices(constraint_system); + mock_opcode_indices(program.constraints); // Construct a bberg circuit from the acir representation - auto circuit = acir_format::create_circuit(constraint_system, /*recursive*/ false, 0, witness_values); + auto circuit = create_circuit(program); EXPECT_TRUE(prove_and_verify(circuit)); } @@ -230,8 +230,8 @@ TEST_F(MegaHonk, Databus) TEST_F(MegaHonk, DatabusReturn) { BlockConstraint block; - WitnessVector witness_values; - size_t num_variables = generate_block_constraint(block, witness_values); + AcirProgram program; + size_t num_variables = generate_block_constraint(block, program.witness); block.type = BlockType::CallData; poly_triple rd_index{ @@ -244,7 +244,7 @@ TEST_F(MegaHonk, DatabusReturn) .q_o = 0, .q_c = 0, }; - witness_values.emplace_back(0); + program.witness.emplace_back(0); ++num_variables; auto fr_five = fr(5); poly_triple rd_read{ @@ -257,7 +257,7 @@ TEST_F(MegaHonk, DatabusReturn) .q_o = 0, .q_c = 0, }; - witness_values.emplace_back(fr_five); + program.witness.emplace_back(fr_five); poly_triple five{ .a = 0, .b = 0, @@ -293,7 +293,7 @@ TEST_F(MegaHonk, DatabusReturn) .q_c = 0, }; - AcirFormat constraint_system{ + program.constraints = { .varnum = static_cast(num_variables), .num_acir_opcodes = 2, .public_inputs = {}, @@ -324,10 +324,10 @@ TEST_F(MegaHonk, DatabusReturn) .block_constraints = { block }, .original_opcode_indices = create_empty_original_opcode_indices(), }; - mock_opcode_indices(constraint_system); + mock_opcode_indices(program.constraints); // Construct a bberg circuit from the acir representation - auto circuit = acir_format::create_circuit(constraint_system, /*recursive*/ false, 0, witness_values); + auto circuit = create_circuit(program); EXPECT_TRUE(prove_and_verify(circuit)); } diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.cpp index f815610631e..7a6bc5e8864 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.cpp @@ -29,10 +29,10 @@ using namespace bb; * @param trace_settings * @return ClientIVC */ -ClientIVC create_mock_ivc_from_constraints(const std::vector& constraints, - const TraceSettings& trace_settings) +std::shared_ptr create_mock_ivc_from_constraints(const std::vector& constraints, + const TraceSettings& trace_settings) { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); uint32_t oink_type = static_cast(PROOF_TYPE::OINK); uint32_t pg_type = static_cast(PROOF_TYPE::PG); @@ -47,7 +47,7 @@ ClientIVC create_mock_ivc_from_constraints(const std::vectorverifier_accumulator = create_mock_decider_vk(); mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG, /*is_kernel=*/true); return ivc; } @@ -55,14 +55,14 @@ ClientIVC create_mock_ivc_from_constraints(const std::vectorverifier_accumulator = create_mock_decider_vk(); mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG, /*is_kernel=*/true); mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG, /*is_kernel=*/false); return ivc; } ASSERT(false && "WARNING: Invalid set of IVC recursion constraints!"); - return ClientIVC{}; + return ivc; } /** @@ -72,13 +72,13 @@ ClientIVC create_mock_ivc_from_constraints(const std::vector& ivc, ClientIVC::QUEUE_TYPE type, const bool is_kernel) { ClientIVC::VerifierInputs entry = - acir_format::create_mock_verification_queue_entry(type, ivc.trace_settings, is_kernel); - ivc.verification_queue.emplace_back(entry); - ivc.merge_verification_queue.emplace_back(acir_format::create_dummy_merge_proof()); - ivc.initialized = true; + acir_format::create_mock_verification_queue_entry(type, ivc->trace_settings, is_kernel); + ivc->verification_queue.emplace_back(entry); + ivc->merge_verification_queue.emplace_back(acir_format::create_dummy_merge_proof()); + ivc->initialized = true; } /** diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp index 8d89c6ecfc5..ec36ed6f90f 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.hpp @@ -11,10 +11,10 @@ using namespace bb; // TODO(https://github.com/AztecProtocol/barretenberg/issues/1148): logic in this file is incomplete. See issue for // details. -ClientIVC create_mock_ivc_from_constraints(const std::vector& constraints, - const TraceSettings& trace_settings); +std::shared_ptr create_mock_ivc_from_constraints(const std::vector& constraints, + const TraceSettings& trace_settings); -void mock_ivc_accumulation(ClientIVC& ivc, ClientIVC::QUEUE_TYPE type, const bool is_kernel); +void mock_ivc_accumulation(const std::shared_ptr& ivc, ClientIVC::QUEUE_TYPE type, const bool is_kernel); std::vector create_mock_oink_proof(const size_t dyadic_size, const size_t num_public_inputs, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp index 1cd9d5b5595..283f675e59a 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp @@ -30,9 +30,9 @@ class IvcRecursionConstraintTest : public ::testing::Test { * @brief Constuct a simple arbitrary circuit to represent a mock app circuit * */ - static Builder construct_mock_app_circuit(ClientIVC& ivc) + static Builder construct_mock_app_circuit(const std::shared_ptr& ivc) { - Builder circuit{ ivc.goblin.op_queue }; + Builder circuit{ ivc->goblin.op_queue }; GoblinMockCircuits::add_some_ecc_op_gates(circuit); MockCircuits::add_arithmetic_gates(circuit); @@ -113,11 +113,11 @@ class IvcRecursionConstraintTest : public ::testing::Test { AcirProgram& program, const TraceSettings& trace_settings) { // Create a mock IVC instance from the IVC recursion constraints in the kernel program - ClientIVC mock_ivc = - create_mock_ivc_from_constraints(program.constraints.ivc_recursion_constraints, trace_settings); + auto mock_ivc = create_mock_ivc_from_constraints(program.constraints.ivc_recursion_constraints, trace_settings); // Create kernel circuit from kernel program and the mocked IVC (empty witness mimics VK construction context) - Builder kernel = acir_format::create_kernel_circuit(program.constraints, mock_ivc, /*witness=*/{}); + const ProgramMetadata metadata{ mock_ivc }; + Builder kernel = acir_format::create_circuit(program, metadata); // Note: adding pairing point normally happens in accumulate() kernel.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices(kernel)); @@ -142,22 +142,25 @@ class IvcRecursionConstraintTest : public ::testing::Test { */ TEST_F(IvcRecursionConstraintTest, AccumulateTwo) { - ClientIVC ivc{ { SMALL_TEST_STRUCTURE } }; + TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; + auto ivc = std::make_shared(trace_settings); // construct a mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); // Complete instance and generate an oink proof - ivc.accumulate(app_circuit); + ivc->accumulate(app_circuit); - // Construct kernel_0 consisting only of the kernel completion logic - AcirProgram program_0 = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel_0 = acir_format::create_kernel_circuit(program_0.constraints, ivc, program_0.witness); + // Construct kernel consisting only of the kernel completion logic + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); - EXPECT_TRUE(CircuitChecker::check(kernel_0)); - ivc.accumulate(kernel_0); + const ProgramMetadata metadata{ ivc }; + Builder kernel = acir_format::create_circuit(program, metadata); - EXPECT_TRUE(ivc.prove_and_verify()); + EXPECT_TRUE(CircuitChecker::check(kernel)); + ivc->accumulate(kernel); + + EXPECT_TRUE(ivc->prove_and_verify()); } /** @@ -166,29 +169,32 @@ TEST_F(IvcRecursionConstraintTest, AccumulateTwo) */ TEST_F(IvcRecursionConstraintTest, AccumulateFour) { - ClientIVC ivc{ { SMALL_TEST_STRUCTURE } }; + TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; + auto ivc = std::make_shared(trace_settings); // construct a mock app_circuit Builder app_circuit_0 = construct_mock_app_circuit(ivc); - ivc.accumulate(app_circuit_0); + ivc->accumulate(app_circuit_0); + + const ProgramMetadata metadata{ ivc }; // Construct kernel_0; consists of a single oink recursive verification for app (plus databus/merge logic) - AcirProgram program_0 = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel_0 = acir_format::create_kernel_circuit(program_0.constraints, ivc, program_0.witness); - ivc.accumulate(kernel_0); + AcirProgram program_0 = construct_mock_kernel_program(ivc->verification_queue); + Builder kernel_0 = acir_format::create_circuit(program_0, metadata); + ivc->accumulate(kernel_0); // construct a mock app_circuit Builder app_circuit_1 = construct_mock_app_circuit(ivc); - ivc.accumulate(app_circuit_1); + ivc->accumulate(app_circuit_1); // Construct kernel_1; consists of two PG recursive verifications for kernel_0 and app_1 (plus databus/merge logic) - AcirProgram program_1 = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel_1 = acir_format::create_kernel_circuit(program_1.constraints, ivc, program_1.witness); + AcirProgram program_1 = construct_mock_kernel_program(ivc->verification_queue); + Builder kernel_1 = acir_format::create_circuit(program_1, metadata); EXPECT_TRUE(CircuitChecker::check(kernel_1)); - ivc.accumulate(kernel_1); + ivc->accumulate(kernel_1); - EXPECT_TRUE(ivc.prove_and_verify()); + EXPECT_TRUE(ivc->prove_and_verify()); } // Test generation of "init" kernel VK via dummy IVC data @@ -199,29 +205,31 @@ TEST_F(IvcRecursionConstraintTest, GenerateVK) // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel) std::shared_ptr expected_kernel_vk; { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); // Construct and accumulate mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); - ivc.accumulate(app_circuit); + ivc->accumulate(app_circuit); // Construct and accumulate kernel consisting only of the kernel completion logic - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); - ivc.accumulate(kernel); - expected_kernel_vk = ivc.verification_queue.back().honk_verification_key; + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); + const ProgramMetadata metadata{ ivc }; + Builder kernel = acir_format::create_circuit(program, metadata); + ivc->accumulate(kernel); + expected_kernel_vk = ivc->verification_queue.back().honk_verification_key; } // Now, construct the kernel VK by mocking the post app accumulation state of the IVC std::shared_ptr kernel_vk; { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); acir_format::mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::OINK, /*is_kernel=*/false); // Construct kernel consisting only of the kernel completion logic - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); + const ProgramMetadata metadata{ ivc }; + Builder kernel = acir_format::create_circuit(program, metadata); // Note that this would normally happen in accumulate() kernel.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices(kernel)); @@ -245,28 +253,29 @@ TEST_F(IvcRecursionConstraintTest, GenerateInitKernelVKFromConstraints) // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel) std::shared_ptr expected_kernel_vk; { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); // Construct and accumulate mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); - ivc.accumulate(app_circuit); + ivc->accumulate(app_circuit); // Construct and accumulate kernel consisting only of the kernel completion logic - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); + const ProgramMetadata metadata{ ivc }; + Builder kernel = acir_format::create_circuit(program, metadata); - ivc.accumulate(kernel); - expected_kernel_vk = ivc.verification_queue.back().honk_verification_key; + ivc->accumulate(kernel); + expected_kernel_vk = ivc->verification_queue.back().honk_verification_key; } // Now, construct the kernel VK by mocking the post app accumulation state of the IVC std::shared_ptr kernel_vk; { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); // Construct kernel consisting only of the kernel completion logic acir_format::mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::OINK, /*is_kernel=*/false); - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); kernel_vk = construct_kernel_vk_from_acir_program(program, trace_settings); } @@ -287,37 +296,39 @@ TEST_F(IvcRecursionConstraintTest, GenerateResetKernelVKFromConstraints) // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel) std::shared_ptr expected_kernel_vk; { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); + + const ProgramMetadata metadata{ ivc }; // Construct and accumulate mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); - ivc.accumulate(app_circuit); + ivc->accumulate(app_circuit); { // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation) - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); - ivc.accumulate(kernel); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); + Builder kernel = acir_format::create_circuit(program, metadata); + ivc->accumulate(kernel); } { // Construct and accumulate a mock RESET kernel (PG recursion for kernel accumulation) - EXPECT_TRUE(ivc.verification_queue.size() == 1); - EXPECT_TRUE(ivc.verification_queue[0].type == bb::ClientIVC::QUEUE_TYPE::PG); - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); - ivc.accumulate(kernel); + EXPECT_TRUE(ivc->verification_queue.size() == 1); + EXPECT_TRUE(ivc->verification_queue[0].type == bb::ClientIVC::QUEUE_TYPE::PG); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); + Builder kernel = acir_format::create_circuit(program, metadata); + ivc->accumulate(kernel); } - expected_kernel_vk = ivc.verification_queue.back().honk_verification_key; + expected_kernel_vk = ivc->verification_queue.back().honk_verification_key; } // Now, construct the kernel VK by mocking the IVC state prior to kernel construction std::shared_ptr kernel_vk; { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); // Construct kernel consisting only of the kernel completion logic acir_format::mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG, /*is_kernel=*/true); - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); kernel_vk = construct_kernel_vk_from_acir_program(program, trace_settings); } @@ -338,45 +349,47 @@ TEST_F(IvcRecursionConstraintTest, GenerateInnerKernelVKFromConstraints) // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel) std::shared_ptr expected_kernel_vk; { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); + + const ProgramMetadata metadata{ ivc }; { // Construct and accumulate mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); - ivc.accumulate(app_circuit); + ivc->accumulate(app_circuit); } { // Construct and accumulate a mock INIT kernel (oink recursion for app accumulation) - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); - ivc.accumulate(kernel); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); + Builder kernel = acir_format::create_circuit(program, metadata); + ivc->accumulate(kernel); } { // Construct and accumulate a second mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); - ivc.accumulate(app_circuit); + ivc->accumulate(app_circuit); } { // Construct and accumulate a mock RESET kernel (PG recursion for kernel accumulation) - EXPECT_TRUE(ivc.verification_queue.size() == 2); - EXPECT_TRUE(ivc.verification_queue[0].type == bb::ClientIVC::QUEUE_TYPE::PG); - EXPECT_TRUE(ivc.verification_queue[1].type == bb::ClientIVC::QUEUE_TYPE::PG); - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); - Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); - ivc.accumulate(kernel); + EXPECT_TRUE(ivc->verification_queue.size() == 2); + EXPECT_TRUE(ivc->verification_queue[0].type == bb::ClientIVC::QUEUE_TYPE::PG); + EXPECT_TRUE(ivc->verification_queue[1].type == bb::ClientIVC::QUEUE_TYPE::PG); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); + Builder kernel = acir_format::create_circuit(program, metadata); + ivc->accumulate(kernel); } - expected_kernel_vk = ivc.verification_queue.back().honk_verification_key; + expected_kernel_vk = ivc->verification_queue.back().honk_verification_key; } // Now, construct the kernel VK by mocking the IVC state prior to kernel construction std::shared_ptr kernel_vk; { - ClientIVC ivc{ trace_settings }; + auto ivc = std::make_shared(trace_settings); // Construct kernel consisting only of the kernel completion logic acir_format::mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG, /*is_kernel=*/true); acir_format::mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG, /*is_kernel=*/false); - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); + AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); kernel_vk = construct_kernel_vk_from_acir_program(program, trace_settings); } 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 4ae4f09c296..817e772ade1 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_proofs/c_bind.cpp @@ -17,9 +17,12 @@ WASM_EXPORT void acir_get_circuit_sizes( uint8_t const* acir_vec, bool const* recursive, bool const* honk_recursion, uint32_t* total, uint32_t* subgroup) { - auto constraint_system = - acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), *honk_recursion); - auto builder = acir_format::create_circuit(constraint_system, recursive, 1 << 19, {}, *honk_recursion); + const acir_format::ProgramMetadata metadata{ .recursive = *recursive, + .honk_recursion = *honk_recursion, + .size_hint = 1 << 19 }; + acir_format::AcirProgram program{ acir_format::circuit_buf_to_acir_format( + from_buffer>(acir_vec), *honk_recursion) }; + auto builder = acir_format::create_circuit(program, metadata); builder.finalize_circuit(/*ensure_nonzero=*/true); *total = htonl((uint32_t)builder.get_finalized_total_circuit_size()); *subgroup = htonl((uint32_t)builder.get_circuit_subgroup_size(builder.get_finalized_total_circuit_size())); @@ -65,12 +68,13 @@ 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), /*honk_recursion=*/true); - auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); + const acir_format::ProgramMetadata metadata{ .recursive = *recursive, .honk_recursion = true }; + acir_format::AcirProgram program{ + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), metadata.honk_recursion), + acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)) + }; - auto builder = acir_format::create_circuit( - constraint_system, *recursive, 0, witness, /*honk_recursion=*/true); + auto builder = acir_format::create_circuit(program, metadata); UltraProver prover{ builder }; auto proof = prover.construct_proof(); @@ -96,28 +100,26 @@ WASM_EXPORT void acir_fold_and_verify_program_stack(uint8_t const* acir_vec, ProgramStack program_stack{ constraint_systems, witness_stack }; - ClientIVC ivc{ { SMALL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; + TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; + auto ivc = std::make_shared(trace_settings, /*auto_verify_mode=*/true); + + const acir_format::ProgramMetadata metadata{ ivc, *recursive }; bool is_kernel = false; while (!program_stack.empty()) { - auto stack_item = program_stack.back(); + auto program = program_stack.back(); // Construct a bberg circuit from the acir representation - auto builder = acir_format::create_circuit(stack_item.constraints, - *recursive, - 0, - stack_item.witness, - /*honk_recursion=*/false, - ivc.goblin.op_queue); + auto builder = acir_format::create_circuit(program, metadata); builder.databus_propagation_data.is_kernel = is_kernel; is_kernel = !is_kernel; // toggle on/off so every second circuit is intepreted as a kernel - ivc.accumulate(builder); + ivc->accumulate(builder); program_stack.pop_back(); } - *result = ivc.prove_and_verify(); + *result = ivc->prove_and_verify(); info("acir_fold_and_verify_program_stack result: ", *result); } @@ -126,12 +128,14 @@ 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), /*honk_recursion=*/false); - auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); + const acir_format::ProgramMetadata metadata{ .recursive = *recursive, .honk_recursion = false }; - auto builder = acir_format::create_circuit( - constraint_system, *recursive, 0, witness, /*honk_recursion=*/false); + acir_format::AcirProgram program{ + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), metadata.honk_recursion), + acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)) + }; + + auto builder = acir_format::create_circuit(program, metadata); MegaProver prover{ builder }; auto proof = prover.construct_proof(); @@ -235,7 +239,10 @@ WASM_EXPORT void acir_prove_and_verify_aztec_client(uint8_t const* acir_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{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; + TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE }; + auto ivc = std::make_shared(trace_settings, /*auto_verify_mode=*/true); + + const acir_format::ProgramMetadata metadata{ ivc }; // Accumulate the entire program stack into the IVC // TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel once databus @@ -245,8 +252,7 @@ WASM_EXPORT void acir_prove_and_verify_aztec_client(uint8_t const* acir_stack, for (Program& program : folding_stack) { // Construct a bberg circuit from the acir representation then accumulate it into the IVC vinfo("constructing circuit..."); - auto circuit = acir_format::create_circuit( - program.constraints, false, 0, program.witness, false, ivc.goblin.op_queue); + auto circuit = acir_format::create_circuit(program, metadata); // 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) { @@ -255,7 +261,7 @@ WASM_EXPORT void acir_prove_and_verify_aztec_client(uint8_t const* acir_stack, is_kernel = !is_kernel; vinfo("done constructing circuit. calling ivc.accumulate..."); - ivc.accumulate(circuit); + ivc->accumulate(circuit); vinfo("done accumulating."); } auto end = std::chrono::steady_clock::now(); @@ -263,7 +269,7 @@ WASM_EXPORT void acir_prove_and_verify_aztec_client(uint8_t const* acir_stack, vinfo("time to construct and accumulate all circuits: ", diff.count()); vinfo("calling ivc.prove_and_verify..."); - bool result = ivc.prove_and_verify(); + bool result = ivc->prove_and_verify(); info("verified?: ", result); end = std::chrono::steady_clock::now(); @@ -290,9 +296,11 @@ WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack, acir_format::circuit_buf_to_acir_format(bincode, /*honk_recursion=*/false); folding_stack.push_back(Program{ constraints, witness }); } - // 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{ { E2E_FULL_TEST_STRUCTURE }, /*auto_verify_mode=*/true }; + TraceSettings trace_settings{ E2E_FULL_TEST_STRUCTURE }; + auto ivc = std::make_shared(trace_settings, /*auto_verify_mode=*/true); + + const acir_format::ProgramMetadata metadata{ ivc }; // Accumulate the entire program stack into the IVC // TODO(https://github.com/AztecProtocol/barretenberg/issues/1116): remove manual setting of is_kernel once databus @@ -302,8 +310,7 @@ WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack, for (Program& program : folding_stack) { // Construct a bberg circuit from the acir representation then accumulate it into the IVC vinfo("constructing circuit..."); - auto circuit = acir_format::create_circuit( - program.constraints, false, 0, program.witness, false, ivc.goblin.op_queue); + auto circuit = acir_format::create_circuit(program, metadata); // 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) { @@ -312,7 +319,7 @@ WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack, is_kernel = !is_kernel; vinfo("done constructing circuit. calling ivc.accumulate..."); - ivc.accumulate(circuit); + ivc->accumulate(circuit); vinfo("done accumulating."); } auto end = std::chrono::steady_clock::now(); @@ -320,7 +327,7 @@ WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack, vinfo("time to construct and accumulate all circuits: ", diff.count()); vinfo("calling ivc.prove ..."); - ClientIVC::Proof proof = ivc.prove(); + ClientIVC::Proof proof = ivc->prove(); end = std::chrono::steady_clock::now(); diff = std::chrono::duration_cast(end - start); vinfo("time to construct, accumulate, prove all circuits: ", diff.count()); @@ -332,9 +339,9 @@ WASM_EXPORT void acir_prove_aztec_client(uint8_t const* acir_stack, vinfo("time to serialize proof: ", diff.count()); start = std::chrono::steady_clock::now(); - auto eccvm_vk = std::make_shared(ivc.goblin.get_eccvm_proving_key()); - auto translator_vk = std::make_shared(ivc.goblin.get_translator_proving_key()); - *out_vk = to_heap_buffer(to_buffer(ClientIVC::VerificationKey{ ivc.honk_vk, eccvm_vk, translator_vk })); + auto eccvm_vk = std::make_shared(ivc->goblin.get_eccvm_proving_key()); + auto translator_vk = std::make_shared(ivc->goblin.get_translator_proving_key()); + *out_vk = to_heap_buffer(to_buffer(ClientIVC::VerificationKey{ ivc->honk_vk, eccvm_vk, translator_vk })); end = std::chrono::steady_clock::now(); diff = std::chrono::duration_cast(end - start); vinfo("time to serialize vk: ", diff.count()); @@ -359,12 +366,14 @@ 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), /*honk_recursion=*/true); - auto witness = acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)); + const acir_format::ProgramMetadata metadata{ .recursive = *recursive, .honk_recursion = true }; - auto builder = acir_format::create_circuit( - constraint_system, *recursive, 0, witness, /*honk_recursion=*/true); + acir_format::AcirProgram program{ + acir_format::circuit_buf_to_acir_format(from_buffer>(acir_vec), metadata.honk_recursion), + acir_format::witness_buf_to_witness_data(from_buffer>(witness_vec)) + }; + + auto builder = acir_format::create_circuit(program, metadata); UltraProver prover{ builder }; auto proof = prover.construct_proof(); @@ -423,10 +432,11 @@ WASM_EXPORT void acir_write_vk_ultra_honk(uint8_t const* acir_vec, bool const* r using DeciderProvingKey = DeciderProvingKey_; using VerificationKey = UltraFlavor::VerificationKey; - 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, *recursive, 0, {}, /*honk_recursion=*/true); + const acir_format::ProgramMetadata metadata{ .recursive = *recursive, .honk_recursion = true }; + + acir_format::AcirProgram program{ acir_format::circuit_buf_to_acir_format( + from_buffer>(acir_vec), metadata.honk_recursion) }; + auto builder = acir_format::create_circuit(program, metadata); DeciderProvingKey proving_key(builder); VerificationKey vk(proving_key.proving_key); @@ -438,10 +448,11 @@ WASM_EXPORT void acir_write_vk_ultra_keccak_honk(uint8_t const* acir_vec, bool c using DeciderProvingKey = DeciderProvingKey_; using VerificationKey = UltraKeccakFlavor::VerificationKey; - 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, *recursive, 0, {}, /*honk_recursion=*/true); + const acir_format::ProgramMetadata metadata{ .recursive = *recursive, .honk_recursion = true }; + + acir_format::AcirProgram program{ acir_format::circuit_buf_to_acir_format( + from_buffer>(acir_vec), metadata.honk_recursion) }; + auto builder = acir_format::create_circuit(program, metadata); DeciderProvingKey proving_key(builder); VerificationKey vk(proving_key.proving_key);