diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index a2c8ba9eb15..1a7a853b55b 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -103,7 +103,7 @@ include(GNUInstallDirs) message(STATUS "Compiling all-in-one barretenberg archive") set(BARRETENBERG_TARGET_OBJECTS - $ + $ $ $ $ diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp index 58df09e229e..6a66dfaebcb 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.cpp @@ -4,67 +4,90 @@ namespace bb { /** - * @brief Append logic to complete a kernel circuit - * @details A kernel circuit may contain some combination of PG recursive verification, merge recursive verification, - * and databus commitment consistency checks. This method appends this logic to a provided kernel circuit. + * @brief Instantiate a stdlib verification queue corresponding to the native counterpart * * @param circuit */ -void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) +void AztecIVC::instantiate_stdlib_verification_queue(ClientCircuit& circuit) { - circuit.databus_propagation_data.is_kernel = true; - - // Perform recursive verification and databus consistency checks for each entry in the verification queue for (auto& [proof, vkey, type] : verification_queue) { // Construct stdlib verification key and proof auto stdlib_proof = bb::convert_proof_to_witness(&circuit, proof); auto stdlib_vkey = std::make_shared(&circuit, vkey); - switch (type) { - case QUEUE_TYPE::PG: { - // Construct stdlib verifier accumulator from the native counterpart computed on a previous round - auto stdlib_verifier_accum = - std::make_shared(&circuit, verifier_accumulator); - - // Perform folding recursive verification to update the verifier accumulator - FoldingRecursiveVerifier verifier{ &circuit, stdlib_verifier_accum, { stdlib_vkey } }; - auto verifier_accum = verifier.verify_folding_proof(stdlib_proof); - - // Extract native verifier accumulator from the stdlib accum for use on the next round - verifier_accumulator = std::make_shared(verifier_accum->get_value()); - - // Perform databus commitment consistency checks and propagate return data commitments via public inputs - bus_depot.execute(verifier.keys_to_fold[1]->witness_commitments, - verifier.keys_to_fold[1]->public_inputs, - verifier.keys_to_fold[1]->verification_key->databus_propagation_data); - break; - } - case QUEUE_TYPE::OINK: { - // Construct an incomplete stdlib verifier accumulator from the corresponding stdlib verification key - auto verifier_accum = std::make_shared(&circuit, stdlib_vkey); - - // Perform oink recursive verification to complete the initial verifier accumulator - OinkRecursiveVerifier oink{ &circuit, verifier_accum }; - oink.verify_proof(stdlib_proof); - verifier_accum->is_accumulator = true; // indicate to PG that it should not run oink on this key - - // Extract native verifier accumulator from the stdlib accum for use on the next round - verifier_accumulator = std::make_shared(verifier_accum->get_value()); - // Initialize the gate challenges to zero for use in first round of folding - verifier_accumulator->gate_challenges = - std::vector(verifier_accum->verification_key->log_circuit_size, 0); - - // Perform databus commitment consistency checks and propagate return data commitments via public inputs - bus_depot.execute(verifier_accum->witness_commitments, - verifier_accum->public_inputs, - verifier_accum->verification_key->databus_propagation_data); - - break; - } - } + stdlib_verification_queue.emplace_back(stdlib_proof, stdlib_vkey, type); + } + verification_queue.clear(); // the native data is not needed beyond this point +} + +/** + * @brief Populate the provided circuit with constraints for (1) recursive verification of the provided accumulation + * proof and (2) the associated databus commitment consistency checks. + * @details The recursive verifier will be either Oink or Protogalaxy depending on the specified proof type. In either + * case, the verifier accumulator is updated in place via the verification algorithm. Databus commitment consistency + * checks are performed on the witness commitments and public inputs extracted from the proof by the verifier. + * + * @param circuit The circuit to which the constraints are appended + * @param proof A stdlib proof to be recursively verified (either oink or PG) + * @param vkey The stdlib verfication key associated with the proof + * @param type The type of the proof (equivalently, the type of the verifier) + */ +void AztecIVC::perform_recursive_verification_and_databus_consistency_checks( + ClientCircuit& circuit, + const StdlibProof& proof, + const std::shared_ptr& vkey, + const QUEUE_TYPE type) +{ + switch (type) { + case QUEUE_TYPE::PG: { + // Construct stdlib verifier accumulator from the native counterpart computed on a previous round + auto stdlib_verifier_accum = std::make_shared(&circuit, verifier_accumulator); + + // Perform folding recursive verification to update the verifier accumulator + FoldingRecursiveVerifier verifier{ &circuit, stdlib_verifier_accum, { vkey } }; + auto verifier_accum = verifier.verify_folding_proof(proof); + + // Extract native verifier accumulator from the stdlib accum for use on the next round + verifier_accumulator = std::make_shared(verifier_accum->get_value()); + + // Perform databus commitment consistency checks and propagate return data commitments via public inputs + bus_depot.execute(verifier.keys_to_fold[1]->witness_commitments, + verifier.keys_to_fold[1]->public_inputs, + verifier.keys_to_fold[1]->verification_key->databus_propagation_data); + break; } - verification_queue.clear(); + case QUEUE_TYPE::OINK: { + // Construct an incomplete stdlib verifier accumulator from the corresponding stdlib verification key + auto verifier_accum = std::make_shared(&circuit, vkey); + + // Perform oink recursive verification to complete the initial verifier accumulator + OinkRecursiveVerifier oink{ &circuit, verifier_accum }; + oink.verify_proof(proof); + verifier_accum->is_accumulator = true; // indicate to PG that it should not run oink + + // Extract native verifier accumulator from the stdlib accum for use on the next round + verifier_accumulator = std::make_shared(verifier_accum->get_value()); + // Initialize the gate challenges to zero for use in first round of folding + auto log_circuit_size = static_cast(verifier_accum->verification_key->log_circuit_size); + verifier_accumulator->gate_challenges = std::vector(log_circuit_size, 0); + + // Perform databus commitment consistency checks and propagate return data commitments via public inputs + bus_depot.execute(verifier_accum->witness_commitments, + verifier_accum->public_inputs, + verifier_accum->verification_key->databus_propagation_data); + + break; + } + } +} +/** + * @brief Perform recursive merge verification for each merge proof in the queue + * + * @param circuit + */ +void AztecIVC::process_recursive_merge_verification_queue(ClientCircuit& circuit) +{ // Recusively verify all merge proofs in queue for (auto& proof : merge_verification_queue) { goblin.verify_merge(circuit, proof); @@ -72,6 +95,33 @@ void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) merge_verification_queue.clear(); } +/** + * @brief Append logic to complete a kernel circuit + * @details A kernel circuit may contain some combination of PG recursive verification, merge recursive + * verification, and databus commitment consistency checks. This method appends this logic to a provided kernel + * circuit. + * + * @param circuit + */ +void AztecIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) +{ + circuit.databus_propagation_data.is_kernel = true; + + // Instantiate stdlib verifier inputs from their native counterparts + if (stdlib_verification_queue.empty()) { + instantiate_stdlib_verification_queue(circuit); + } + + // Peform recursive verification and databus consistency checks for each entry in the verification queue + for (auto& [proof, vkey, type] : stdlib_verification_queue) { + perform_recursive_verification_and_databus_consistency_checks(circuit, proof, vkey, type); + } + stdlib_verification_queue.clear(); + + // Perform recursive merge verification for every merge proof in the queue + process_recursive_merge_verification_queue(circuit); +} + /** * @brief Execute prover work for accumulation * @details Construct an proving key for the provided circuit. If this is the first step in the IVC, simply initialize @@ -115,7 +165,7 @@ void AztecIVC::accumulate(ClientCircuit& circuit, const std::shared_ptrproof_data, honk_vk, QUEUE_TYPE::OINK }); + bb::AztecIVC::VerifierInputs{ oink_prover.transcript->proof_data, honk_vk, QUEUE_TYPE::OINK }); initialized = true; } else { // Otherwise, fold the new key into the accumulator @@ -123,8 +173,7 @@ void AztecIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr>& vk_stack) +bool AztecIVC::verify(const Proof& proof, const std::vector>& vk_stack) { auto eccvm_vk = std::make_shared(goblin.get_eccvm_proving_key()); auto translator_vk = std::make_shared(goblin.get_translator_proving_key()); diff --git a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp index 2b07425f27e..1d2b60bcded 100644 --- a/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/aztec_ivc/aztec_ivc.hpp @@ -64,12 +64,23 @@ class AztecIVC { MSGPACK_FIELDS(folding_proof, decider_proof, goblin_proof); }; - enum class QUEUE_TYPE { OINK, PG }; - struct RecursiveVerifierInputs { + enum class QUEUE_TYPE { OINK, PG }; // for specifying type of proof in the verification queue + + // An entry in the native verification queue + struct VerifierInputs { std::vector proof; // oink or PG std::shared_ptr honk_verification_key; QUEUE_TYPE type; }; + using VerificationQueue = std::vector; + + // An entry in the stdlib verification queue + struct StdlibVerifierInputs { + StdlibProof proof; // oink or PG + std::shared_ptr honk_verification_key; + QUEUE_TYPE type; + }; + using StdlibVerificationQueue = std::vector; // Utility for tracking the max size of each block across the full IVC MaxBlockSizeTracker max_block_size_tracker; @@ -85,8 +96,10 @@ class AztecIVC { std::shared_ptr verifier_accumulator; // verifier accumulator std::shared_ptr honk_vk; // honk vk to be completed and folded into the accumulator - // Set of pairs of {fold_proof, verification_key} to be recursively verified - std::vector verification_queue; + // Set of tuples {proof, verification_key, type} to be recursively verified + VerificationQueue verification_queue; + // Set of tuples {stdlib_proof, stdlib_verification_key, type} corresponding to the native verification queue + StdlibVerificationQueue stdlib_verification_queue; // Set of merge proofs to be recursively verified std::vector merge_verification_queue; @@ -98,6 +111,16 @@ class AztecIVC { bool initialized = false; // Is the IVC accumulator initialized + void instantiate_stdlib_verification_queue(ClientCircuit& circuit); + + void perform_recursive_verification_and_databus_consistency_checks( + ClientCircuit& circuit, + const StdlibProof& proof, + const std::shared_ptr& vkey, + const QUEUE_TYPE type); + + void process_recursive_merge_verification_queue(ClientCircuit& circuit); + // Complete the logic of a kernel circuit (e.g. PG/merge recursive verification, databus consistency checks) void complete_kernel_circuit_logic(ClientCircuit& circuit); @@ -112,7 +135,7 @@ class AztecIVC { const std::shared_ptr& eccvm_vk, const std::shared_ptr& translator_vk); - bool verify(Proof& proof, const std::vector>& vk_stack); + bool verify(const Proof& proof, const std::vector>& vk_stack); bool prove_and_verify(); diff --git a/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt index 1cd7f441909..52ea5f9db30 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/dsl/CMakeLists.txt @@ -2,6 +2,7 @@ # We do not need to bloat barretenberg.wasm with gzip functionality in a browser context as the browser can do this set(DSL_DEPENDENCIES + aztec_ivc plonk ultra_honk client_ivc 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 62e23117c59..5455857dfc7 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.cpp @@ -383,8 +383,8 @@ void process_honk_recursion_constraints(Builder& builder, */ template <> UltraCircuitBuilder create_circuit(AcirFormat& constraint_system, - size_t size_hint, - WitnessVector const& witness, + const size_t size_hint, + const WitnessVector& witness, bool honk_recursion, [[maybe_unused]] std::shared_ptr, bool collect_gates_per_opcode) @@ -411,8 +411,8 @@ UltraCircuitBuilder create_circuit(AcirFormat& constraint_system, */ template <> MegaCircuitBuilder create_circuit(AcirFormat& constraint_system, - [[maybe_unused]] size_t size_hint, - WitnessVector const& witness, + [[maybe_unused]] const size_t size_hint, + const WitnessVector& witness, bool honk_recursion, std::shared_ptr op_queue, bool collect_gates_per_opcode) @@ -428,6 +428,62 @@ MegaCircuitBuilder create_circuit(AcirFormat& constraint_system, return builder; }; +/** + * @brief Create a kernel circuit from a constraint system and an IVC instance + * + * @param constraint_system AcirFormat constraint system possibly containing IVC recursion constraints + * @param ivc An IVC instance containing internal data about proofs to be verified + * @param size_hint + * @param witness + * @return MegaCircuitBuilder + */ +MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system, + AztecIVC& ivc, + const WitnessVector& witness, + const size_t size_hint) +{ + // Construct the main kernel circuit logic excluding recursive verifiers + auto circuit = create_circuit(constraint_system, + 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 matche 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); + } + + // Create stdlib representations of each {proof, vkey} pair in the queue based on their native counterparts + ivc.instantiate_stdlib_verification_queue(circuit); + + // Connect each {proof, vkey} pair from the constraint to the corresponding entry 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)) { + + // Reconstruct complete proof indices from acir constraint data (in which proof is stripped of public inputs) + std::vector complete_proof_indices = + ProofSurgeon::create_indices_for_reconstructed_proof(constraint.proof, constraint.public_inputs); + ASSERT(complete_proof_indices.size() == queue_entry.proof.size()); + + // Assert equality between the proof indices from the constraint data and those of the internal proof + for (auto [proof_idx, proof_value] : zip_view(complete_proof_indices, queue_entry.proof)) { + circuit.assert_equal(proof_value.get_witness_index(), proof_idx); + } + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1090): assert equality between the internal vkey + // and the constaint vkey, or simply use the constraint vkey directly to construct the stdlib vkey used in IVC. + } + + // Complete the kernel circuit with all required recursive verifications, databus consistency checks etc. + ivc.complete_kernel_circuit_logic(circuit); + + return circuit; +}; + template void build_constraints(MegaCircuitBuilder&, AcirFormat&, bool, bool, bool); } // 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 a43c4fa2648..e5b637ab3b8 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format.hpp @@ -1,5 +1,6 @@ #pragma once #include "aes128_constraint.hpp" +#include "barretenberg/aztec_ivc/aztec_ivc.hpp" #include "barretenberg/common/slab_allocator.hpp" #include "barretenberg/serialize/msgpack.hpp" #include "bigint_constraint.hpp" @@ -50,6 +51,7 @@ struct AcirFormatOriginalOpcodeIndices { std::vector ec_add_constraints; std::vector recursion_constraints; std::vector honk_recursion_constraints; + std::vector ivc_recursion_constraints; std::vector bigint_from_le_bytes_constraints; std::vector bigint_to_le_bytes_constraints; std::vector bigint_operations; @@ -75,6 +77,7 @@ struct AcirFormat { uint32_t num_acir_opcodes; + using PolyTripleConstraint = bb::poly_triple_; std::vector public_inputs; std::vector logic_constraints; @@ -96,6 +99,7 @@ struct AcirFormat { std::vector ec_add_constraints; std::vector recursion_constraints; std::vector honk_recursion_constraints; + std::vector ivc_recursion_constraints; std::vector bigint_from_le_bytes_constraints; std::vector bigint_to_le_bytes_constraints; std::vector bigint_operations; @@ -104,7 +108,7 @@ struct AcirFormat { // A standard plonk arithmetic constraint, as defined in the poly_triple struct, consists of selector values // for q_M,q_L,q_R,q_O,q_C and indices of three variables taking the role of left, right and output wire // This could be a large vector so use slab allocator, we don't expect the blackbox implementations to be so large. - bb::SlabVector> poly_triple_constraints; + bb::SlabVector poly_triple_constraints; bb::SlabVector> quad_constraints; std::vector block_constraints; @@ -140,6 +144,7 @@ struct AcirFormat { ec_add_constraints, recursion_constraints, honk_recursion_constraints, + ivc_recursion_constraints, poly_triple_constraints, block_constraints, bigint_from_le_bytes_constraints, @@ -190,12 +195,17 @@ struct AcirProgramStack { template Builder create_circuit(AcirFormat& constraint_system, - size_t size_hint = 0, - WitnessVector const& witness = {}, + const size_t size_hint = 0, + const WitnessVector& witness = {}, bool honk_recursion = false, std::shared_ptr op_queue = std::make_shared(), bool collect_gates_per_opcode = false); +MegaCircuitBuilder create_kernel_circuit(AcirFormat& constraint_system, + AztecIVC& ivc, + const WitnessVector& witness = {}, + const size_t size_hint = 0); + template void build_constraints( Builder& builder, 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 5b98fa0e249..79814a519b1 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 @@ -61,6 +61,7 @@ TEST_F(AcirFormatTests, TestASingleConstraintNoPubInputs) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -183,6 +184,7 @@ TEST_F(AcirFormatTests, TestLogicGateFromNoirCircuit) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -263,6 +265,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifyPass) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -370,6 +373,7 @@ TEST_F(AcirFormatTests, TestSchnorrVerifySmallRange) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -490,6 +494,7 @@ TEST_F(AcirFormatTests, TestVarKeccak) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -570,6 +575,7 @@ TEST_F(AcirFormatTests, TestKeccakPermutation) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -647,6 +653,7 @@ TEST_F(AcirFormatTests, TestCollectsGateCounts) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format_mocks.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format_mocks.cpp index 8055948ff7b..1b58cef549e 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format_mocks.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/acir_format_mocks.cpp @@ -22,6 +22,7 @@ acir_format::AcirFormatOriginalOpcodeIndices create_empty_original_opcode_indice .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -92,6 +93,9 @@ void mock_opcode_indices(acir_format::AcirFormat& constraint_system) for (size_t i = 0; i < constraint_system.honk_recursion_constraints.size(); i++) { constraint_system.original_opcode_indices.honk_recursion_constraints.push_back(current_opcode++); } + for (size_t i = 0; i < constraint_system.ivc_recursion_constraints.size(); i++) { + constraint_system.original_opcode_indices.ivc_recursion_constraints.push_back(current_opcode++); + } for (size_t i = 0; i < constraint_system.bigint_from_le_bytes_constraints.size(); i++) { constraint_system.original_opcode_indices.bigint_from_le_bytes_constraints.push_back(current_opcode++); } 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 0fb92a275d6..f73bdf7a1f1 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 @@ -466,8 +466,8 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg, // TODO(https://github.com/AztecProtocol/barretenberg/issues/1074): Eventually arg.proof_type will be // the only means for setting the proof type. use of honk_recursion flag in this context can go away // once all noir programs (e.g. protocol circuits) are updated to use the new pattern. - if (honk_recursion && proof_type_in != HONK_RECURSION) { - proof_type_in = HONK_RECURSION; + if (honk_recursion && proof_type_in != HONK) { + proof_type_in = HONK; } auto c = RecursionConstraint{ @@ -478,10 +478,10 @@ void handle_blackbox_func_call(Program::Opcode::BlackBoxFuncCall const& arg, .proof_type = proof_type_in, }; // Add the recursion constraint to the appropriate container based on proof type - if (c.proof_type == PLONK_RECURSION) { + if (c.proof_type == PLONK) { af.recursion_constraints.push_back(c); af.original_opcode_indices.recursion_constraints.push_back(opcode_index); - } else if (c.proof_type == HONK_RECURSION) { + } else if (c.proof_type == HONK) { af.honk_recursion_constraints.push_back(c); af.original_opcode_indices.honk_recursion_constraints.push_back(opcode_index); } else { diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp index 2ecd7e37f61..9c3d842fbfc 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/bigint_constraint.test.cpp @@ -194,6 +194,7 @@ TEST_F(BigIntTests, TestBigIntConstraintMultiple) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -268,6 +269,7 @@ TEST_F(BigIntTests, TestBigIntConstraintSimple) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = { from_le_bytes_constraint_bigint1 }, .bigint_to_le_bytes_constraints = { result2_to_le_bytes }, .bigint_operations = { add_constraint }, @@ -326,6 +328,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -389,6 +392,7 @@ TEST_F(BigIntTests, TestBigIntConstraintReuse2) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -473,6 +477,7 @@ TEST_F(BigIntTests, TestBigIntDIV) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = { from_le_bytes_constraint_bigint1, from_le_bytes_constraint_bigint2 }, .bigint_to_le_bytes_constraints = { result3_to_le_bytes }, .bigint_operations = { div_constraint }, 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 aa2958d4d56..4ea78bae375 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 @@ -162,6 +162,7 @@ TEST_F(UltraPlonkRAM, TestBlockConstraint) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -214,6 +215,7 @@ TEST_F(MegaHonk, Databus) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -321,6 +323,7 @@ TEST_F(MegaHonk, DatabusReturn) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp index 6f56c69851a..075e11ce596 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ec_operations.test.cpp @@ -84,6 +84,7 @@ TEST_F(EcOperations, TestECOperations) .ec_add_constraints = { ec_add_constraint }, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -221,6 +222,7 @@ TEST_F(EcOperations, TestECMultiScalarMul) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp index 459aa7ccde5..b72acd21892 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256k1.test.cpp @@ -116,6 +116,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintSucceed) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -171,6 +172,7 @@ TEST_F(ECDSASecp256k1, TestECDSACompilesForVerifier) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -221,6 +223,7 @@ TEST_F(ECDSASecp256k1, TestECDSAConstraintFail) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp index ad09ed2cd8f..e4d8c5696f7 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ecdsa_secp256r1.test.cpp @@ -150,6 +150,7 @@ TEST(ECDSASecp256r1, test_hardcoded) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -207,6 +208,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintSucceed) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -262,6 +264,7 @@ TEST(ECDSASecp256r1, TestECDSACompilesForVerifier) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -313,6 +316,7 @@ TEST(ECDSASecp256r1, TestECDSAConstraintFail) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp index 8813b9c7614..d83fa5fcc5a 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.cpp @@ -161,7 +161,7 @@ AggregationObjectIndices create_honk_recursion_constraints(Builder& builder, using RecursiveVerificationKey = Flavor::VerificationKey; using RecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_; - ASSERT(input.proof_type == HONK_RECURSION); + ASSERT(input.proof_type == HONK); // Construct an in-circuit representation of the verification key. // For now, the v-key is a circuit constant and is fixed for the circuit. diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp index a7209c74759..b101c814140 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/honk_recursion_constraint.test.cpp @@ -112,6 +112,7 @@ class AcirHonkRecursionConstraint : public ::testing::Test { .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -164,47 +165,18 @@ class AcirHonkRecursionConstraint : public ::testing::Test { .proof = proof_indices, .public_inputs = inner_public_inputs, .key_hash = 0, // not used - .proof_type = HONK_RECURSION, + .proof_type = HONK, }; honk_recursion_constraints.push_back(honk_recursion_constraint); } - std::vector honk_recursion_opcode_indices(honk_recursion_constraints.size()); - std::iota(honk_recursion_opcode_indices.begin(), honk_recursion_opcode_indices.end(), 0); + AcirFormat constraint_system{}; + constraint_system.varnum = static_cast(witness.size()); + constraint_system.recursive = false; + constraint_system.num_acir_opcodes = static_cast(honk_recursion_constraints.size()); + constraint_system.honk_recursion_constraints = honk_recursion_constraints; + constraint_system.original_opcode_indices = create_empty_original_opcode_indices(); - AcirFormat constraint_system{ - .varnum = static_cast(witness.size()), - .recursive = false, - .num_acir_opcodes = static_cast(honk_recursion_constraints.size()), - .public_inputs = {}, - .logic_constraints = {}, - .range_constraints = {}, - .aes128_constraints = {}, - .sha256_constraints = {}, - .sha256_compression = {}, - .schnorr_constraints = {}, - .ecdsa_k1_constraints = {}, - .ecdsa_r1_constraints = {}, - .blake2s_constraints = {}, - .blake3_constraints = {}, - .keccak_constraints = {}, - .keccak_permutations = {}, - .pedersen_constraints = {}, - .pedersen_hash_constraints = {}, - .poseidon2_constraints = {}, - .multi_scalar_mul_constraints = {}, - .ec_add_constraints = {}, - .recursion_constraints = {}, - .honk_recursion_constraints = honk_recursion_constraints, - .bigint_from_le_bytes_constraints = {}, - .bigint_to_le_bytes_constraints = {}, - .bigint_operations = {}, - .assert_equalities = {}, - .poly_triple_constraints = {}, - .quad_constraints = {}, - .block_constraints = {}, - .original_opcode_indices = create_empty_original_opcode_indices(), - }; mock_opcode_indices(constraint_system); auto outer_circuit = create_circuit(constraint_system, /*size_hint*/ 0, witness, /*honk recursion*/ true); 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 new file mode 100644 index 00000000000..981992accc1 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/ivc_recursion_constraint.test.cpp @@ -0,0 +1,278 @@ +#include "acir_format.hpp" +#include "acir_format_mocks.hpp" +#include "barretenberg/aztec_ivc/aztec_ivc.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/sumcheck/instance/prover_instance.hpp" +#include "barretenberg/ultra_honk/ultra_prover.hpp" +#include "barretenberg/ultra_honk/ultra_verifier.hpp" +#include "honk_recursion_constraint.hpp" +#include "proof_surgeon.hpp" + +#include +#include + +using namespace acir_format; +using namespace bb; + +class IvcRecursionConstraintTest : public ::testing::Test { + + public: + using Builder = MegaCircuitBuilder; + using Flavor = MegaFlavor; + using FF = Flavor::FF; + using VerifierInputs = AztecIVC::VerifierInputs; + using QUEUE_TYPE = AztecIVC::QUEUE_TYPE; + using VerificationQueue = AztecIVC::VerificationQueue; + using ArithmeticConstraint = AcirFormat::PolyTripleConstraint; + + /** + * @brief Constuct a simple arbitrary circuit to represent a mock app circuit + * @details Includes a single unique public input for robustness and to distinguish the public inputs of one "app" + * from another in testing. + * + */ + static Builder construct_mock_app_circuit(AztecIVC& ivc) + { + Builder circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_simple_circuit(circuit); + + // add a random (unique) public input + circuit.add_public_variable(FF::random_element()); + + return circuit; + } + + /** + * @brief Create an ACIR RecursionConstraint given the corresponding verifier inputs + * @brief In practice such constraints are created via a call to verify_proof(...) in noir + * + * @param input bberg style proof and verification key + * @param witness Array of witnesses into which the above data is placed + * @param num_public_inputs Number of public inputs to be extracted from the proof + * @return RecursionConstraint + */ + static RecursionConstraint create_recursion_constraint(const VerifierInputs& input, + SlabVector& witness, + const size_t num_public_inputs) + { + // Assemble simple vectors of witnesses for vkey and proof + std::vector key_witnesses = input.honk_verification_key->to_field_elements(); + std::vector proof_witnesses = input.proof; + + // Construct witness indices for each component in the constraint; populate the witness array + auto [key_indices, proof_indices, public_inputs_indices] = + ProofSurgeon::populate_recursion_witness_data(witness, proof_witnesses, key_witnesses, num_public_inputs); + + // The proof type can be either Oink or PG + PROOF_TYPE proof_type = input.type == QUEUE_TYPE::OINK ? OINK : PG; + + return RecursionConstraint{ + .key = key_indices, + .proof = proof_indices, + .public_inputs = public_inputs_indices, + .key_hash = 0, // not used + .proof_type = proof_type, + }; + } + + /** + * @brief Create an arithmetic constraint fixing the first public input witness to it's present value + * @details Meant to mimic the "business logic" of the aztec kernel. Used to facilitate failure testing since this + * will lead to failure of the kernel circuit to verify if a different proof witness is used in the business logic + * VS the recursive verification logic. + * + * @param public_inputs Witness indices of public inputs of some proof to be constrained + * @param witness + * @return ArithmeticConstraint + */ + static ArithmeticConstraint create_public_input_value_constraint(const std::vector& public_inputs, + const SlabVector& witness) + { + const uint32_t pub_input_idx = public_inputs[0]; + const FF pub_input_val = witness[pub_input_idx]; + return { + .a = pub_input_idx, + .b = 0, + .c = 0, + .q_m = 0, + .q_l = -1, + .q_r = 0, + .q_o = 0, + .q_c = pub_input_val, + }; + } + + /** + * @brief Generate an acir program {constraints, witness} for a mock kernel + * @details The IVC contains and internal verification queue that contains proofs to be recursively verified. + * Construct an AcirProgram with a RecursionConstraint for each entry in the ivc verification queue. (In practice + * these constraints would come directly from calls to verify_proof in noir). Also add mock "business logic" which + * simply enforces some constraint on the public inputs of the proof. + * @note This method needs the number of public inputs in each proof-to-be-verified so they can be extracted and + * provided separately as is required in the acir constraint system. + * + * @param ivc + * @param inner_circuit_num_pub_inputs Num pub inputs for each circuit whose accumulation is recursively verified + * @return Builder + */ + static AcirProgram construct_mock_kernel_program(const VerificationQueue& verification_queue, + const std::vector& inner_circuit_num_pub_inputs) + { + ASSERT(verification_queue.size() == inner_circuit_num_pub_inputs.size()); + + AcirProgram program; + + // Construct recursion constraints based on the ivc verification queue; populate the witness along the way + std::vector ivc_recursion_constraints; + ivc_recursion_constraints.reserve(verification_queue.size()); + for (size_t idx = 0; idx < verification_queue.size(); ++idx) { + ivc_recursion_constraints.push_back(create_recursion_constraint( + verification_queue[idx], program.witness, inner_circuit_num_pub_inputs[idx])); + } + + // Add some mock kernel "business logic" which simply fixes one of the public inputs to a particular value + ArithmeticConstraint pub_input_constraint = + create_public_input_value_constraint(ivc_recursion_constraints[0].public_inputs, program.witness); + + // Construct a constraint system containing the business logic and ivc recursion constraints + program.constraints.varnum = static_cast(program.witness.size()); + program.constraints.recursive = false; + program.constraints.num_acir_opcodes = static_cast(ivc_recursion_constraints.size()); + program.constraints.poly_triple_constraints = { pub_input_constraint }; + program.constraints.ivc_recursion_constraints = ivc_recursion_constraints; + program.constraints.original_opcode_indices = create_empty_original_opcode_indices(); + mock_opcode_indices(program.constraints); + + return program; + } + + protected: + void SetUp() override + { + bb::srs::init_crs_factory("../srs_db/ignition"); + srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } +}; + +/** + * @brief Test IVC accumulation of a one app and one kernel; The kernel includes a recursive oink verification for the + * app, specified via an ACIR RecursionConstraint. + */ +TEST_F(IvcRecursionConstraintTest, AccumulateTwo) +{ + AztecIVC ivc; + ivc.trace_structure = TraceStructure::SMALL_TEST; + + // construct a mock app_circuit + Builder app_circuit = construct_mock_app_circuit(ivc); + + // Complete instance and generate an oink proof + ivc.accumulate(app_circuit); + + // Construct kernel_0 consisting only of the kernel completion logic + AcirProgram program_0 = construct_mock_kernel_program(ivc.verification_queue, { app_circuit.public_inputs.size() }); + Builder kernel_0 = acir_format::create_kernel_circuit(program_0.constraints, ivc, program_0.witness); + + EXPECT_TRUE(CircuitChecker::check(kernel_0)); + ivc.accumulate(kernel_0); + + EXPECT_TRUE(ivc.prove_and_verify()); +} + +/** + * @brief Test IVC accumulation of two apps and two kernels; The first kernel contains a recursive oink verification and + * the second contains two recursive PG verifications, all specified via ACIR RecursionConstraints. + */ +TEST_F(IvcRecursionConstraintTest, AccumulateFour) +{ + AztecIVC ivc; + ivc.trace_structure = TraceStructure::SMALL_TEST; + + // construct a mock app_circuit + Builder app_circuit_0 = construct_mock_app_circuit(ivc); + ivc.accumulate(app_circuit_0); + + // Construct kernel_0; consists of a single oink recursive verification for app (plus databus/merge logic) + size_t num_pub_inputs_app_0 = app_circuit_0.public_inputs.size(); + AcirProgram program_0 = construct_mock_kernel_program(ivc.verification_queue, { num_pub_inputs_app_0 }); + Builder kernel_0 = acir_format::create_kernel_circuit(program_0.constraints, ivc, program_0.witness); + ivc.accumulate(kernel_0); + + // construct a mock app_circuit + Builder app_circuit_1 = construct_mock_app_circuit(ivc); + ivc.accumulate(app_circuit_1); + + // Construct kernel_1; consists of two PG recursive verifications for kernel_0 and app_1 (plus databus/merge logic) + size_t num_pub_inputs_kernel_0 = kernel_0.public_inputs.size(); + size_t num_pub_inputs_app_1 = app_circuit_0.public_inputs.size(); + AcirProgram program_1 = + construct_mock_kernel_program(ivc.verification_queue, { num_pub_inputs_kernel_0, num_pub_inputs_app_1 }); + Builder kernel_1 = acir_format::create_kernel_circuit(program_1.constraints, ivc, program_1.witness); + + EXPECT_TRUE(CircuitChecker::check(kernel_1)); + ivc.accumulate(kernel_1); + + EXPECT_TRUE(ivc.prove_and_verify()); +} + +/** + * @brief Demonstrate failure of the IVC if the proof witness differs between those encoded in the constraint system + * (i.e. those used in the noir program) and those used in constructing the recursive verifiers internally + * @brief The idea is to construct two valid but unique verification queue entries of the form {proof, vkey}. One is + * used to construct the acir constraint system and the other is used to construct the recursive verification logic + * internally in the IVC. Since the proof/vkey witnesses in the constraint system are asserted equal to those used to + * construct the recursive verifiers, the use of different verification queue witnesses should result in failure as long + * as they were used in some nontrivial way in the main logic of the kernel. (Specifically, failure results from a + * failure of the "business logic" of the kernel which constrains one of the public inputs to a particular value). + */ +TEST_F(IvcRecursionConstraintTest, AccumulateTwoFailure) +{ + // Accumulate a single app in order to construct a valid verification queue entry {proof, vkey} to be used later on. + // Demonstrate that it is indeed valid by completing the IVC with a kernel (which recursively verifies the entry) + // then proving and verifying the full IVC. + VerifierInputs alternative_verification_queue_entry; + { + AztecIVC ivc; + ivc.trace_structure = TraceStructure::SMALL_TEST; + + // construct and accumulate a mock app circuit with a single unique public input + Builder app_circuit = construct_mock_app_circuit(ivc); + ivc.accumulate(app_circuit); + + // Save the single entry in the verification queue at this point + alternative_verification_queue_entry = ivc.verification_queue[0]; + + // Construct and accumulate kernel_0 + size_t num_pub_inputs_app = app_circuit.public_inputs.size(); + AcirProgram program_0 = construct_mock_kernel_program(ivc.verification_queue, { num_pub_inputs_app }); + Builder kernel_0 = acir_format::create_kernel_circuit(program_0.constraints, ivc, program_0.witness); + ivc.accumulate(kernel_0); + + EXPECT_TRUE(ivc.prove_and_verify()); + } + + // Repeat a similar IVC but use the alternative queue entry just created to provide different (but independently + // valid) witnesses during constraint system construction VS recursive verifier construction. + + AztecIVC ivc; + ivc.trace_structure = TraceStructure::SMALL_TEST; + + // construct and accumulate a mock app circuit with a single unique public input + Builder app_circuit = construct_mock_app_circuit(ivc); + ivc.accumulate(app_circuit); + + // Construct kernel_0 + AcirProgram program_0 = construct_mock_kernel_program(ivc.verification_queue, { app_circuit.public_inputs.size() }); + + // Replace the existing verification queue entry that was used to construct the acir constraint system for the + // kernel with a different (but valid, as shown above) set of inputs + ivc.verification_queue[0] = alternative_verification_queue_entry; + Builder kernel_0 = acir_format::create_kernel_circuit(program_0.constraints, ivc, program_0.witness); + + // The witness should fail to check due to the business logic of the kernel failing + EXPECT_FALSE(CircuitChecker::check(kernel_0)); + ivc.accumulate(kernel_0); + + // The full IVC should of course also fail to verify since we've accumulated an invalid witness for the kernel + EXPECT_FALSE(ivc.prove_and_verify()); +} diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp index 57db2db8c71..97546e7789d 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/multi_scalar_mul.test.cpp @@ -84,6 +84,7 @@ TEST_F(MSMTests, TestMSM) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp index 150acbc0561..4484009f707 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/poseidon2_constraint.test.cpp @@ -64,6 +64,7 @@ TEST_F(Poseidon2Tests, TestPoseidon2Permutation) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp index 1a98fd15ff6..f73454c5479 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -8,7 +8,7 @@ namespace acir_format { // Used to specify the type of recursive verifier via the proof_type specified by the RecursiveAggregation opcode from // ACIR -enum PROOF_TYPE { PLONK_RECURSION, HONK_RECURSION }; +enum PROOF_TYPE { PLONK, HONK, OINK, PG }; using namespace bb::plonk; using Builder = bb::UltraCircuitBuilder; diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp index c01d6edfc23..b0ed01dd270 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.test.cpp @@ -109,6 +109,7 @@ Builder create_inner_circuit() .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, @@ -210,7 +211,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) .proof = proof_indices, .public_inputs = inner_public_inputs, .key_hash = key_hash_start_idx, - .proof_type = PLONK_RECURSION, + .proof_type = PLONK, }; recursion_constraints.push_back(recursion_constraint); @@ -271,6 +272,7 @@ Builder create_outer_circuit(std::vector& inner_circuits) .ec_add_constraints = {}, .recursion_constraints = recursion_constraints, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp index 0f87cb34f94..ab6319b1d14 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/sha256_constraint.test.cpp @@ -59,6 +59,7 @@ TEST_F(Sha256Tests, TestSha256Compression) .ec_add_constraints = {}, .recursion_constraints = {}, .honk_recursion_constraints = {}, + .ivc_recursion_constraints = {}, .bigint_from_le_bytes_constraints = {}, .bigint_to_le_bytes_constraints = {}, .bigint_operations = {}, diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/max_block_size_tracker.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/max_block_size_tracker.hpp index b9d1f64f721..864d3f25944 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/max_block_size_tracker.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/max_block_size_tracker.hpp @@ -1,3 +1,5 @@ +#pragma once + #include "barretenberg/plonk_honk_shared/arithmetization/mega_arithmetization.hpp" #include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.cpp index c05e1499fc5..b38dbf5cb65 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.cpp @@ -27,7 +27,7 @@ OinkRecursiveVerifier_::OinkRecursiveVerifier_(Builder* builder, , domain_separator(std::move(domain_separator)) {} -template void OinkRecursiveVerifier_::verify_proof(OinkProof& proof) +template void OinkRecursiveVerifier_::verify_proof(const OinkProof& proof) { transcript = std::make_shared(proof); verify(); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.hpp index b8a96fb9e81..a2e4bb0b6a3 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_verifier/oink_recursive_verifier.hpp @@ -55,7 +55,7 @@ template class OinkRecursiveVerifier_ { * @brief Constructs an oink recursive verifier circuit for a provided oink proof. * */ - void verify_proof(OinkProof& proof); + void verify_proof(const OinkProof& proof); std::shared_ptr verification_key; Builder* builder;