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 de6005c1942..e431959807e 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 @@ -153,10 +153,11 @@ class AcirHonkRecursionConstraint : public ::testing::Test { std::vector key_witnesses = verification_key->to_field_elements(); std::vector proof_witnesses = inner_proof; - const size_t num_public_inputs = inner_circuit.get_public_inputs().size(); + const size_t num_public_inputs_to_extract = + inner_circuit.get_public_inputs().size() - bb::PAIRING_POINT_ACCUMULATOR_SIZE; auto [key_indices, proof_indices, inner_public_inputs] = ProofSurgeon::populate_recursion_witness_data( - witness, proof_witnesses, key_witnesses, num_public_inputs); + witness, proof_witnesses, key_witnesses, num_public_inputs_to_extract); RecursionConstraint honk_recursion_constraint{ .key = key_indices, 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 4bd58eff722..f815610631e 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 @@ -13,91 +13,222 @@ namespace acir_format { using namespace bb; -using field_ct = stdlib::field_t; -ClientIVC create_mock_ivc_from_constraints(const std::vector& constraints) +/** + * @brief Create an IVC object with mocked state corresponding to a set of IVC recursion constraints + * @details Construction of a kernel circuit requires two inputs: kernel prgram acir constraints and an IVC instance + * containing state needed to complete the kernel logic, e.g. proofs for input to recursive verifiers. To construct + * verification keys for kernel circuits without running a full IVC, we mock the IVC state corresponding to a provided + * set of IVC recurson constraints. For example, if the constraints contain a single PG recursive verification, we + * initialize an IVC with mocked data for the verifier accumulator, the folding proof, the circuit verification key, + * and a merge proof. + * @note There are only three valid combinations of IVC recursion constraints for a kernel program. See below for + * details. + * + * @param constraints IVC recursion constraints from a kernel circuit + * @param trace_settings + * @return ClientIVC + */ +ClientIVC create_mock_ivc_from_constraints(const std::vector& constraints, + const TraceSettings& trace_settings) { - ClientIVC ivc{ { SMALL_TEST_STRUCTURE } }; + ClientIVC ivc{ trace_settings }; - for (const auto& constraint : constraints) { - if (static_cast(PROOF_TYPE::OINK) == constraint.proof_type) { - mock_ivc_oink_accumulation(ivc, constraint.public_inputs.size()); - } else if (static_cast(PROOF_TYPE::PG) == constraint.proof_type) { - // perform equivalent mocking for PG accumulation - } + uint32_t oink_type = static_cast(PROOF_TYPE::OINK); + uint32_t pg_type = static_cast(PROOF_TYPE::PG); + + // There are only three valid combinations of IVC recursion constraints for Aztec kernel circuits: + + // Case: INIT kernel; single Oink recursive verification of an app + if (constraints.size() == 1 && constraints[0].proof_type == oink_type) { + mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::OINK, /*is_kernel=*/false); + return ivc; + } + + // Case: RESET or TAIL kernel; single PG recursive verification of a kernel + if (constraints.size() == 1 && constraints[0].proof_type == pg_type) { + ivc.verifier_accumulator = create_mock_decider_vk(); + mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG, /*is_kernel=*/true); + return ivc; } - return ivc; + // Case: INNER kernel; two PG recursive verifications, kernel and app in that order + if (constraints.size() == 2) { + ASSERT(constraints[0].proof_type == pg_type && constraints[1].proof_type == pg_type); + ivc.verifier_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{}; } /** - * @brief Populate an IVC instance with data that mimics the state after accumulating the first app (which runs the oink - * prover) - *@details Mock state consists a mock verification queue entry of type OINK (proof, VK) and a mocked merge proof + * @brief Populate an IVC instance with data that mimics the state after a single IVC accumulation (Oink or PG) + * @details Mock state consists of a mock verification queue entry of type OINK (proof, VK) and a mocked merge proof * * @param ivc * @param num_public_inputs_app num pub inputs in accumulated app, excluding fixed components, e.g. pairing points */ -void mock_ivc_oink_accumulation(ClientIVC& ivc, size_t num_public_inputs_app) +void mock_ivc_accumulation(ClientIVC& ivc, ClientIVC::QUEUE_TYPE type, const bool is_kernel) { - ClientIVC::VerifierInputs oink_entry = - acir_format::create_dummy_vkey_and_proof_oink(ivc.trace_settings, num_public_inputs_app); - ivc.verification_queue.emplace_back(oink_entry); + 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; } /** - * @brief Create a mock oink proof and VK that have the correct structure but are not necessarily valid + * @brief Create a mock verification queue entry with proof and VK that have the correct structure but are not + * necessarily valid * */ -ClientIVC::VerifierInputs create_dummy_vkey_and_proof_oink(const TraceSettings& trace_settings, - const size_t num_public_inputs = 0) +ClientIVC::VerifierInputs create_mock_verification_queue_entry(const ClientIVC::QUEUE_TYPE verification_type, + const TraceSettings& trace_settings, + const bool is_kernel) { - using Flavor = MegaFlavor; - using FF = bb::fr; + using FF = ClientIVC::FF; + using MegaVerificationKey = ClientIVC::MegaVerificationKey; + // Use the trace settings to determine the correct dyadic size and the public inputs offset MegaExecutionTraceBlocks blocks; blocks.set_fixed_block_sizes(trace_settings); blocks.compute_offsets(/*is_structured=*/true); - size_t structured_dyadic_size = blocks.get_structured_dyadic_size(); + size_t dyadic_size = blocks.get_structured_dyadic_size(); size_t pub_inputs_offset = blocks.pub_inputs.trace_offset; + // All circuits have pairing point public inputs; kernels have additional public inputs for two databus commitments + size_t num_public_inputs = bb::PAIRING_POINT_ACCUMULATOR_SIZE; + if (is_kernel) { + num_public_inputs += bb::PROPAGATED_DATABUS_COMMITMENTS_SIZE; + } - ClientIVC::VerifierInputs verifier_inputs; - verifier_inputs.type = ClientIVC::QUEUE_TYPE::OINK; + // Construct a mock Oink or PG proof + std::vector proof; + if (verification_type == ClientIVC::QUEUE_TYPE::OINK) { + proof = create_mock_oink_proof(dyadic_size, num_public_inputs, pub_inputs_offset); + } else { // ClientIVC::QUEUE_TYPE::PG) + proof = create_mock_pg_proof(dyadic_size, num_public_inputs, pub_inputs_offset); + } - FF mock_val(5); + // Construct a mock MegaHonk verification key + std::shared_ptr verification_key = + create_mock_honk_vk(dyadic_size, num_public_inputs, pub_inputs_offset); - auto mock_commitment = curve::BN254::AffineElement::one() * mock_val; - std::vector mock_commitment_frs = field_conversion::convert_to_bn254_frs(mock_commitment); + // If the verification queue entry corresponds to a kernel circuit, set the databus data to indicate the presence of + // propagated return data commitments on the public inputs + if (is_kernel) { + verification_key->databus_propagation_data = bb::DatabusPropagationData::kernel_default(); + } + + return ClientIVC::VerifierInputs{ proof, verification_key, verification_type }; +} + +/** + * @brief Create a mock oink proof that has the correct structure but is not in general valid + * + */ +std::vector create_mock_oink_proof(const size_t dyadic_size, + const size_t num_public_inputs, + const size_t pub_inputs_offset) +{ + using Flavor = ClientIVC::Flavor; + using FF = ClientIVC::FF; - // Set proof preamble (metadata plus public inputs) - size_t total_num_public_inputs = num_public_inputs + bb::PAIRING_POINT_ACCUMULATOR_SIZE; - verifier_inputs.proof.emplace_back(structured_dyadic_size); - verifier_inputs.proof.emplace_back(total_num_public_inputs); - verifier_inputs.proof.emplace_back(pub_inputs_offset); - for (size_t i = 0; i < total_num_public_inputs; ++i) { - verifier_inputs.proof.emplace_back(0); + std::vector proof; + + // Populate proof metadata + proof.emplace_back(dyadic_size); + proof.emplace_back(num_public_inputs); + proof.emplace_back(pub_inputs_offset); + + // Populate mock public inputs + for (size_t i = 0; i < num_public_inputs; ++i) { + proof.emplace_back(0); } - // Witness polynomial commitments + // Populate mock witness polynomial commitments + auto mock_commitment = curve::BN254::AffineElement::one(); + std::vector mock_commitment_frs = field_conversion::convert_to_bn254_frs(mock_commitment); for (size_t i = 0; i < Flavor::NUM_WITNESS_ENTITIES; ++i) { for (const FF& val : mock_commitment_frs) { - verifier_inputs.proof.emplace_back(val); + proof.emplace_back(val); } } + return proof; +} + +/** + * @brief Create a mock PG proof that has the correct structure but is not in general valid + * + */ +std::vector create_mock_pg_proof(const size_t dyadic_size, + const size_t num_public_inputs, + const size_t pub_inputs_offset) +{ + using FF = ClientIVC::FF; + using DeciderProvingKeys = ClientIVC::DeciderProvingKeys; + + // The first part of a PG proof is an Oink proof + std::vector proof = create_mock_oink_proof(dyadic_size, num_public_inputs, pub_inputs_offset); + + // Populate mock perturbator coefficients + for (size_t idx = 1; idx <= CONST_PG_LOG_N; idx++) { + proof.emplace_back(0); + } + + // Populate mock combiner quotient coefficients + for (size_t idx = DeciderProvingKeys::NUM; idx < DeciderProvingKeys::BATCHED_EXTENDED_LENGTH; idx++) { + proof.emplace_back(0); + } + + return proof; +} + +/** + * @brief Create a mock MegaHonk VK that has the correct structure + * + */ +std::shared_ptr create_mock_honk_vk(const size_t dyadic_size, + const size_t num_public_inputs, + const size_t pub_inputs_offset) +{ + // Set relevant VK metadata and commitments + auto honk_verification_key = std::make_shared(); + honk_verification_key->circuit_size = dyadic_size; + honk_verification_key->num_public_inputs = num_public_inputs; + honk_verification_key->pub_inputs_offset = pub_inputs_offset; // must be set correctly + honk_verification_key->contains_pairing_point_accumulator = true; + + for (auto& commitment : honk_verification_key->get_all()) { + commitment = curve::BN254::AffineElement::one(); // arbitrary mock commitment + } + + return honk_verification_key; +} + +/** + * @brief Create a mock Decider verification key for initilization of a mock verifier accumulator + * + */ +std::shared_ptr create_mock_decider_vk() +{ + using FF = ClientIVC::FF; + // Set relevant VK metadata and commitments - verifier_inputs.honk_verification_key = std::make_shared(); - verifier_inputs.honk_verification_key->circuit_size = structured_dyadic_size; - verifier_inputs.honk_verification_key->num_public_inputs = total_num_public_inputs; - verifier_inputs.honk_verification_key->pub_inputs_offset = blocks.pub_inputs.trace_offset; // must be set correctly - verifier_inputs.honk_verification_key->contains_pairing_point_accumulator = true; - for (auto& commitment : verifier_inputs.honk_verification_key->get_all()) { - commitment = mock_commitment; + auto decider_verification_key = std::make_shared(); + decider_verification_key->verification_key = create_mock_honk_vk(0, 0, 0); // metadata does not need to be accurate + decider_verification_key->is_accumulator = true; + decider_verification_key->gate_challenges = std::vector(static_cast(CONST_PG_LOG_N), 0); + + for (auto& commitment : decider_verification_key->witness_commitments.get_all()) { + commitment = curve::BN254::AffineElement::one(); // arbitrary mock commitment } - return verifier_inputs; + return decider_verification_key; } /** @@ -107,12 +238,12 @@ ClientIVC::VerifierInputs create_dummy_vkey_and_proof_oink(const TraceSettings& */ ClientIVC::MergeProof create_dummy_merge_proof() { - using FF = bb::fr; + using FF = ClientIVC::FF; std::vector proof; FF mock_val(5); - auto mock_commitment = curve::BN254::AffineElement::one() * mock_val; + auto mock_commitment = curve::BN254::AffineElement::one(); std::vector mock_commitment_frs = field_conversion::convert_to_bn254_frs(mock_commitment); // There are 12 entities in the merge protocol (4 columns x 3 components; aggregate transcript, previous aggregate @@ -148,8 +279,7 @@ void populate_dummy_vk_in_constraint(MegaCircuitBuilder& builder, const std::shared_ptr& mock_verification_key, std::vector& key_witness_indices) { - using Flavor = MegaFlavor; - using FF = Flavor::FF; + using FF = ClientIVC::FF; // Convert the VerificationKey to fields std::vector mock_vk_fields = mock_verification_key->to_field_elements(); 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 7709bee73c0..8d89c6ecfc5 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,12 +11,28 @@ 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); +ClientIVC create_mock_ivc_from_constraints(const std::vector& constraints, + const TraceSettings& trace_settings); -void mock_ivc_oink_accumulation(ClientIVC& ivc, size_t num_public_inputs_app = 0); +void mock_ivc_accumulation(ClientIVC& ivc, ClientIVC::QUEUE_TYPE type, const bool is_kernel); -ClientIVC::VerifierInputs create_dummy_vkey_and_proof_oink(const TraceSettings& trace_settings, - const size_t num_public_inputs); +std::vector create_mock_oink_proof(const size_t dyadic_size, + const size_t num_public_inputs, + const size_t pub_inputs_offset); + +std::vector create_mock_pg_proof(const size_t dyadic_size, + const size_t num_public_inputs, + const size_t pub_inputs_offset); + +std::shared_ptr create_mock_honk_vk(const size_t dyadic_size, + const size_t num_public_inputs, + const size_t pub_inputs_offset); + +std::shared_ptr create_mock_decider_vk(); + +ClientIVC::VerifierInputs create_mock_verification_queue_entry(const ClientIVC::QUEUE_TYPE type, + const TraceSettings& trace_settings, + const bool is_kernel); ClientIVC::MergeProof create_dummy_merge_proof(); 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 4ea3df6a72e..1cd9d5b5595 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 @@ -28,17 +28,13 @@ class IvcRecursionConstraintTest : public ::testing::Test { /** * @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(ClientIVC& 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()); + GoblinMockCircuits::add_some_ecc_op_gates(circuit); + MockCircuits::add_arithmetic_gates(circuit); return circuit; } @@ -49,20 +45,17 @@ class IvcRecursionConstraintTest : public ::testing::Test { * * @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) + static RecursionConstraint create_recursion_constraint(const VerifierInputs& input, SlabVector& witness) { // 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; // proof contains the public inputs at this stage // 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); + auto [key_indices, proof_indices, public_inputs_indices] = ProofSurgeon::populate_recursion_witness_data( + witness, proof_witnesses, key_witnesses, /*num_public_inputs_to_extract=*/0); // The proof type can be either Oink or PG PROOF_TYPE proof_type = input.type == QUEUE_TYPE::OINK ? OINK : PG; @@ -88,19 +81,15 @@ class IvcRecursionConstraintTest : public ::testing::Test { * @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) + static AcirProgram construct_mock_kernel_program(const VerificationQueue& verification_queue) { - 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])); + for (const auto& queue_entry : verification_queue) { + ivc_recursion_constraints.push_back(create_recursion_constraint(queue_entry, program.witness)); } // Construct a constraint system containing the business logic and ivc recursion constraints @@ -113,6 +102,32 @@ class IvcRecursionConstraintTest : public ::testing::Test { return program; } + /** + * @brief Construct a kernel circuit VK from an acir program with IVC recursion constraints + * + * @param program Acir program representing a kernel circuit + * @param trace_settings needed for construction of the VK + * @return std::shared_ptr + */ + static std::shared_ptr construct_kernel_vk_from_acir_program( + 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); + + // 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=*/{}); + // Note: adding pairing point normally happens in accumulate() + kernel.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices(kernel)); + + // Manually construct the VK for the kernel circuit + auto proving_key = std::make_shared(kernel, trace_settings); + MegaProver prover(proving_key); + + return std::make_shared(prover.proving_key->proving_key); + } + protected: void SetUp() override { @@ -136,7 +151,7 @@ TEST_F(IvcRecursionConstraintTest, AccumulateTwo) 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() }); + 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); EXPECT_TRUE(CircuitChecker::check(kernel_0)); @@ -158,8 +173,7 @@ TEST_F(IvcRecursionConstraintTest, AccumulateFour) 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 }); + 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); @@ -168,10 +182,7 @@ TEST_F(IvcRecursionConstraintTest, AccumulateFour) 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 }); + 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); EXPECT_TRUE(CircuitChecker::check(kernel_1)); @@ -187,17 +198,15 @@ 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; - size_t num_app_public_inputs = 0; { ClientIVC ivc{ trace_settings }; // Construct and accumulate mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); ivc.accumulate(app_circuit); - num_app_public_inputs = app_circuit.public_inputs.size(); // Construct and accumulate kernel consisting only of the kernel completion logic - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue, { num_app_public_inputs }); + 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; @@ -208,10 +217,10 @@ TEST_F(IvcRecursionConstraintTest, GenerateVK) { ClientIVC ivc{ trace_settings }; - acir_format::mock_ivc_oink_accumulation(ivc, num_app_public_inputs - bb::PAIRING_POINT_ACCUMULATOR_SIZE); + 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, { num_app_public_inputs }); + AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc); // Note that this would normally happen in accumulate() kernel.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices(kernel)); @@ -229,23 +238,21 @@ TEST_F(IvcRecursionConstraintTest, GenerateVK) } // Test generation of "init" kernel VK via dummy IVC data -TEST_F(IvcRecursionConstraintTest, GenerateVKFromConstraints) +TEST_F(IvcRecursionConstraintTest, GenerateInitKernelVKFromConstraints) { const TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel) std::shared_ptr expected_kernel_vk; - size_t num_app_public_inputs = 0; { ClientIVC ivc{ trace_settings }; // Construct and accumulate mock app_circuit Builder app_circuit = construct_mock_app_circuit(ivc); ivc.accumulate(app_circuit); - num_app_public_inputs = app_circuit.public_inputs.size(); // Construct and accumulate kernel consisting only of the kernel completion logic - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue, { num_app_public_inputs }); + AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); Builder kernel = acir_format::create_kernel_circuit(program.constraints, ivc, program.witness); ivc.accumulate(kernel); @@ -258,22 +265,120 @@ TEST_F(IvcRecursionConstraintTest, GenerateVKFromConstraints) ClientIVC ivc{ trace_settings }; // Construct kernel consisting only of the kernel completion logic - acir_format::mock_ivc_oink_accumulation(ivc, num_app_public_inputs - bb::PAIRING_POINT_ACCUMULATOR_SIZE); - AcirProgram program = construct_mock_kernel_program(ivc.verification_queue, { num_app_public_inputs }); - program.witness = {}; // erase witness to mimic VK construction context + acir_format::mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::OINK, /*is_kernel=*/false); + AcirProgram program = construct_mock_kernel_program(ivc.verification_queue); - // 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); + kernel_vk = construct_kernel_vk_from_acir_program(program, trace_settings); + } - // Create a kernel circuit from the kernel program and the mocked IVC - Builder kernel = acir_format::create_kernel_circuit(program.constraints, mock_ivc); - // Note: adding pairing point normally happens in accumulate() - kernel.add_pairing_point_accumulator(stdlib::recursion::init_default_agg_obj_indices(kernel)); + // PCS verification keys will not match so set to null before comparing + kernel_vk->pcs_verification_key = nullptr; + expected_kernel_vk->pcs_verification_key = nullptr; - // Manually construct the VK for the kernel circuit - auto proving_key = std::make_shared>(kernel, ivc.trace_settings); - MegaProver prover(proving_key); - kernel_vk = std::make_shared(prover.proving_key->proving_key); + // Compare the VK constructed via running the IVc with the one constructed via mocking + EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get()); +} + +// Test generation of "reset" or "tail" kernel VK via dummy IVC data +TEST_F(IvcRecursionConstraintTest, GenerateResetKernelVKFromConstraints) +{ + const TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; + + // 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 }; + + // Construct and accumulate mock app_circuit + Builder app_circuit = construct_mock_app_circuit(ivc); + 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); + } + + { // 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); + } + + 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 }; + + // 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); + + kernel_vk = construct_kernel_vk_from_acir_program(program, trace_settings); + } + + // PCS verification keys will not match so set to null before comparing + kernel_vk->pcs_verification_key = nullptr; + expected_kernel_vk->pcs_verification_key = nullptr; + + // Compare the VK constructed via running the IVc with the one constructed via mocking + EXPECT_EQ(*kernel_vk.get(), *expected_kernel_vk.get()); +} + +// Test generation of "inner" kernel VK via dummy IVC data +TEST_F(IvcRecursionConstraintTest, GenerateInnerKernelVKFromConstraints) +{ + const TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; + + // 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 }; + + { // Construct and accumulate mock app_circuit + Builder app_circuit = construct_mock_app_circuit(ivc); + 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); + } + + { // Construct and accumulate a second mock app_circuit + Builder app_circuit = construct_mock_app_circuit(ivc); + 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); + } + + 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 }; + + // 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); + + kernel_vk = construct_kernel_vk_from_acir_program(program, trace_settings); } // PCS verification keys will not match so set to null before comparing diff --git a/barretenberg/cpp/src/barretenberg/dsl/acir_format/proof_surgeon.hpp b/barretenberg/cpp/src/barretenberg/dsl/acir_format/proof_surgeon.hpp index 0b2d1768bca..40bbedb02a3 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/proof_surgeon.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/proof_surgeon.hpp @@ -146,11 +146,9 @@ class ProofSurgeon { static RecursionWitnessData populate_recursion_witness_data(bb::SlabVector& witness, std::vector& proof_witnesses, const std::vector& key_witnesses, - const size_t num_public_inputs) + const size_t num_public_inputs_to_extract) { // Extract all public inputs except for those corresponding to the aggregation object - ASSERT(num_public_inputs >= bb::PAIRING_POINT_ACCUMULATOR_SIZE); - const size_t num_public_inputs_to_extract = num_public_inputs - bb::PAIRING_POINT_ACCUMULATOR_SIZE; std::vector public_input_witnesses = cut_public_inputs_from_proof(proof_witnesses, num_public_inputs_to_extract); diff --git a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp index 10739d2aa45..607f7b17f17 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/protogalaxy_verifier/protogalaxy_recursive_verifier.cpp @@ -167,6 +167,7 @@ std::shared_ptr ProtogalaxyRecursiv accumulator->is_accumulator = true; accumulator->target_sum = perturbator_evaluation * lagranges[0] + vanishing_polynomial_at_challenge * combiner_quotient_at_challenge; + accumulator->gate_challenges = update_gate_challenges(perturbator_challenge, accumulator->gate_challenges, deltas); // Set the accumulator circuit size data based on the max of the keys being accumulated @@ -178,6 +179,7 @@ std::shared_ptr ProtogalaxyRecursiv for (auto [combination, to_combine] : zip_view(accumulator->alphas, keys_to_fold.get_alphas())) { combination = linear_combination(to_combine, lagranges); } + for (auto [combination, to_combine] : zip_view(accumulator->relation_parameters.get_to_fold(), keys_to_fold.get_relation_parameters())) { combination = linear_combination(to_combine, lagranges); diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp index 92fda074a99..44e5797f51e 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/databus.hpp @@ -95,6 +95,16 @@ struct DatabusPropagationData { return os; }; + // Construct an instance of this class with the default settings for a kernel circuit + static DatabusPropagationData kernel_default() + { + DatabusPropagationData data; + data.kernel_return_data_public_input_idx = 0; // kernel return data commitment is first public input + data.app_return_data_public_input_idx = 8; // followed by app return data commitment + data.is_kernel = true; + return data; + } + MSGPACK_FIELDS(app_return_data_public_input_idx, kernel_return_data_public_input_idx, is_kernel); };