From ac7c0da38ff05d6f11c4d6a6244c4526ac00232e Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:50:53 -0700 Subject: [PATCH] feat: mock IVC state from arbitrary acir IVC recursion constraints (#10314) Generating a bberg kernel circuit from a noir kernel program represented as acir requires an IVC instance containing certain state including a verifier accumulator and verification queue containing proofs/VKs for input to recursive verifiers. In the context of a write_vk flow, this data is not known and must be mocked so that the recursive verifiers in the kernel can be constructed properly. (Similar to how we construct a dummy proof to generate a Honk recursive verifier). The main method in this PR is `create_mock_ivc_from_constraints()` which constructs an IVC instance with mocked state based on the IVC recursion constraints present in the acir data. For example, if there are two PG recursive verifications in the constraint system, we must generate two mocked PG proofs plus some other auxiliary data. So no actual write_vk flow exists but the logic is tested though the `IvcRecursionConstraintTest` suite which constructs VKs from programs containing each of the 3 different possible combinations of IVC recursion constraints that appear in Aztec kernel circuits. (These are: (a) 1 Oink recursive verification (init kernel), (b) 1 PG recursive verification (reset or tail kernel), and (c) 2 PG recursive verifications (inner kernel)). --- .../honk_recursion_constraint.test.cpp | 5 +- .../acir_format/ivc_recursion_constraint.cpp | 228 ++++++++++++++---- .../acir_format/ivc_recursion_constraint.hpp | 24 +- .../ivc_recursion_constraint.test.cpp | 201 +++++++++++---- .../dsl/acir_format/proof_surgeon.hpp | 4 +- .../protogalaxy_recursive_verifier.cpp | 2 + .../stdlib_circuit_builders/databus.hpp | 10 + 7 files changed, 368 insertions(+), 106 deletions(-) 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); };