diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index e8b45b3515b0..27f3abd99ee0 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -109,6 +109,7 @@ std::pair ClientIVC:: merge_commitments.T_prev_commitments = T_prev_commitments; switch (verifier_inputs.type) { + case QUEUE_TYPE::PG_TAIL: 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); @@ -127,6 +128,7 @@ std::pair ClientIVC:: break; } + case QUEUE_TYPE::OINK: { // Construct an incomplete stdlib verifier accumulator from the corresponding stdlib verification key auto verifier_accum = @@ -338,12 +340,14 @@ void ClientIVC::accumulate(ClientCircuit& circuit, fold_output = folding_prover.prove(); vinfo("constructed folding proof"); - // If this is the final circuit to be folded, set queue entry type to PG_FINAL and run the decider prover - bool is_final_fold = (num_circuits_accumulated == num_circuits - 1); - if (is_final_fold) { + if (num_circuits_accumulated == num_circuits - 1) { + // we are folding in the "Tail" kernel, so the verification_queue entry should have type PG_FINAL queue_entry.type = QUEUE_TYPE::PG_FINAL; decider_proof = decider_prove(); vinfo("constructed decider proof"); + } else if (num_circuits_accumulated == num_circuits - 2) { + // we are folding in the "Inner/Reset" kernel, so the verification_queue entry should have type PG_TAIL + queue_entry.type = QUEUE_TYPE::PG_TAIL; } else { queue_entry.type = QUEUE_TYPE::PG; } diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index bb3d445b70f3..5f34d9308c9c 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -136,7 +136,7 @@ class ClientIVC { MSGPACK_FIELDS(mega, eccvm, translator); }; - enum class QUEUE_TYPE { OINK, PG, PG_FINAL }; // for specifying type of proof in the verification queue + enum class QUEUE_TYPE { OINK, PG, PG_FINAL, PG_TAIL }; // for specifying type of proof in the verification queue // An entry in the native verification queue struct VerifierInputs { 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 11a2f61ad3b8..c34abea2c94a 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 @@ -749,6 +749,7 @@ void handle_blackbox_func_call(Acir::Opcode::BlackBoxFuncCall const& arg, AcirFo break; case OINK: case PG: + case PG_TAIL: case PG_FINAL: af.ivc_recursion_constraints.push_back(c); af.original_opcode_indices.ivc_recursion_constraints.push_back(opcode_index); 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 fdd63d2e5bbc..5043f0322eec 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 @@ -46,6 +46,7 @@ std::shared_ptr create_mock_ivc_from_constraints(const std::vector(PROOF_TYPE::OINK); uint32_t pg_type = static_cast(PROOF_TYPE::PG); uint32_t pg_final_type = static_cast(PROOF_TYPE::PG_FINAL); + uint32_t pg_tail_type = static_cast(PROOF_TYPE::PG_TAIL); // There is a fixed set of valid combinations of IVC recursion constraints for Aztec kernel circuits: @@ -55,13 +56,20 @@ std::shared_ptr create_mock_ivc_from_constraints(const std::vectorverifier_accumulator = create_mock_decider_vk(); mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG, /*is_kernel=*/true); return ivc; } + // Case: TAIL kernel; single PG recursive verification of a kernel + if (constraints.size() == 1 && constraints[0].proof_type == pg_tail_type) { + ivc->verifier_accumulator = create_mock_decider_vk(); + mock_ivc_accumulation(ivc, ClientIVC::QUEUE_TYPE::PG_TAIL, /*is_kernel=*/true); + return ivc; + } + // Case: INNER kernel; two PG recursive verifications, kernel and app in that order if (constraints.size() == 2) { BB_ASSERT_EQ(constraints[0].proof_type, pg_type); @@ -120,6 +128,7 @@ ClientIVC::VerifierInputs create_mock_verification_queue_entry(const ClientIVC:: break; case ClientIVC::QUEUE_TYPE::PG: case ClientIVC::QUEUE_TYPE::PG_FINAL: + case ClientIVC::QUEUE_TYPE::PG_TAIL: proof = create_mock_pg_proof(); break; default: 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 b4fe5f15f649..f48afc9ce372 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 @@ -141,6 +141,9 @@ class IvcRecursionConstraintTest : public ::testing::Test { case QUEUE_TYPE::PG_FINAL: proof_type = PG_FINAL; break; + case QUEUE_TYPE::PG_TAIL: + proof_type = PG_TAIL; + break; default: throw std::runtime_error("Invalid proof type"); } @@ -254,7 +257,8 @@ TEST_F(IvcRecursionConstraintTest, AccumulateTwo) TEST_F(IvcRecursionConstraintTest, AccumulateFour) { TraceSettings trace_settings{ SMALL_TEST_STRUCTURE }; - auto ivc = std::make_shared(/*num_circuits=*/4, trace_settings); + // 4 ciruits and the tail kernel + auto ivc = std::make_shared(/*num_circuits=*/5, trace_settings); // construct a mock app_circuit Builder app_circuit_0 = construct_mock_app_circuit(ivc); @@ -278,6 +282,12 @@ TEST_F(IvcRecursionConstraintTest, AccumulateFour) EXPECT_TRUE(CircuitChecker::check(kernel_1)); ivc->accumulate(kernel_1); + // Now we add the tail kernel + EXPECT_EQ(ivc->verification_queue.size(), 1); + EXPECT_EQ(ivc->verification_queue[0].type, QUEUE_TYPE::PG_TAIL); + AcirProgram tail_program = construct_mock_kernel_program(ivc->verification_queue); + Builder tail_kernel = acir_format::create_circuit(tail_program, metadata); + ivc->accumulate(tail_kernel, construct_kernel_vk_from_acir_program(tail_program, trace_settings)); EXPECT_TRUE(ivc->prove_and_verify()); } @@ -345,7 +355,7 @@ TEST_F(IvcRecursionConstraintTest, GenerateResetKernelVKFromConstraints) { // Construct and accumulate a mock RESET/TAIL 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); + EXPECT_TRUE(ivc->verification_queue[0].type == bb::ClientIVC::QUEUE_TYPE::PG_TAIL); AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); Builder kernel = acir_format::create_circuit(program, metadata); ivc->accumulate(kernel); @@ -379,7 +389,9 @@ TEST_F(IvcRecursionConstraintTest, GenerateInnerKernelVKFromConstraints) // First, construct the kernel VK by running the full IVC (accumulate one app and one kernel) std::shared_ptr expected_kernel_vk; { - auto ivc = std::make_shared(/*num_circuits=*/4, trace_settings); + // we have to set the number of circuits one more than the number of circuits we're accumulating as otherwise + // the last circuit will be seen as a tail + auto ivc = std::make_shared(/*num_circuits=*/5, trace_settings); const ProgramMetadata metadata{ ivc }; @@ -401,7 +413,6 @@ TEST_F(IvcRecursionConstraintTest, GenerateInnerKernelVKFromConstraints) { // Construct and accumulate a mock INNER 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_circuit(program, metadata); @@ -455,7 +466,7 @@ TEST_F(IvcRecursionConstraintTest, GenerateHidingKernelVKFromConstraints) { // Construct and accumulate a mock TAIL 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); + EXPECT_TRUE(ivc->verification_queue[0].type == bb::ClientIVC::QUEUE_TYPE::PG_TAIL); AcirProgram program = construct_mock_kernel_program(ivc->verification_queue); Builder kernel = acir_format::create_circuit(program, metadata); ivc->accumulate(kernel); 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 93c7c80577fd..9933241e3326 100644 --- a/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp +++ b/barretenberg/cpp/src/barretenberg/dsl/acir_format/recursion_constraint.hpp @@ -15,7 +15,7 @@ namespace acir_format { // ACIR // Keep this enum values in sync with their noir counterpart constants defined in // noir-protocol-circuits/crates/types/src/constants.nr -enum PROOF_TYPE { PLONK, HONK, OINK, PG, AVM, ROLLUP_HONK, ROOT_ROLLUP_HONK, HONK_ZK, PG_FINAL }; +enum PROOF_TYPE { PLONK, HONK, OINK, PG, AVM, ROLLUP_HONK, ROOT_ROLLUP_HONK, HONK_ZK, PG_FINAL, PG_TAIL }; /** * @brief RecursionConstraint struct contains information required to recursively verify a proof! diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr index 445fe2cfdb25..34e7d00414e1 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/previous_kernel_validator.nr @@ -19,9 +19,13 @@ impl PreviousKernelValidator { PreviousKernelValidator { previous_kernel } } - pub fn verify_proof(self: Self, allowed_indices: [u32; N]) { + pub fn verify_proof( + self: Self, + allowed_indices: [u32; N], + is_last_inner_kernel: bool, + ) { if !dep::std::runtime::is_unconstrained() { - self.previous_kernel.verify(); + self.previous_kernel.verify(is_last_inner_kernel); self.previous_kernel.validate_vk_in_vk_tree(allowed_indices); } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr index 69e67f91afdd..f1792876c43e 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -49,7 +49,7 @@ impl PrivateKernelInnerCircuitPrivateInputs { let previous_kernel_validator = PreviousKernelValidator::new(self.previous_kernel); - previous_kernel_validator.verify_proof(ALLOWED_PREVIOUS_CIRCUITS); + previous_kernel_validator.verify_proof(ALLOWED_PREVIOUS_CIRCUITS, false); let private_call_data_validator = PrivateCallDataValidator::new(self.private_call); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr index eb9cce258411..e853e7062d65 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_reset.nr @@ -114,7 +114,7 @@ impl(self, allowed_vk_indices: [u32; N]) { let previous_kernel = self.previous_kernel.to_private_kernel_data(); - PreviousKernelValidator::new(previous_kernel).verify_proof(allowed_vk_indices); + PreviousKernelValidator::new(previous_kernel).verify_proof(allowed_vk_indices, false); } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel_data.nr index ccb62a76f477..a302b14dcbe0 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_kernel_data.nr @@ -1,7 +1,9 @@ use crate::{ abis::kernel_circuit_public_inputs::PrivateKernelCircuitPublicInputs, - constants::{MEGA_VK_LENGTH_IN_FIELDS, PRIVATE_KERNEL_RESET_INDEX, PROOF_TYPE_PG}, - proof::{traits::Verifiable, vk_data::VkData}, + constants::{ + MEGA_VK_LENGTH_IN_FIELDS, PRIVATE_KERNEL_RESET_INDEX, PROOF_TYPE_PG, PROOF_TYPE_PG_TAIL, + }, + proof::vk_data::VkData, utils::arrays::find_index_hint, }; @@ -38,19 +40,18 @@ impl PrivateKernelData { // Note: The hash of the verification key is checked in `verify_proof_with_type` against the given vk hash. self.vk_data.validate_in_vk_tree(self.public_inputs.constants.vk_tree_root); } -} - -impl Verifiable for PrivateKernelData { - /// Verifies the proof against the verification key and public inputs. - /// The vk hash is also checked in the backend to match the key. - fn verify(self) { + pub fn verify(self, is_last_inner_kernel: bool) { + let mut proof_type = PROOF_TYPE_PG; // Client IVC public inputs are linked in the backend via the databus + if is_last_inner_kernel { + proof_type = PROOF_TYPE_PG_TAIL; + } std::verify_proof_with_type( self.vk_data.vk.key, [], [], self.vk_data.vk.hash, - PROOF_TYPE_PG, + proof_type, ); } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index c288a1f7eb8f..0b3b2430753d 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -1078,7 +1078,9 @@ pub global PROOF_TYPE_PG: u32 = 3; pub global PROOF_TYPE_AVM: u32 = 4; pub global PROOF_TYPE_ROLLUP_HONK: u32 = 5; pub global PROOF_TYPE_ROOT_ROLLUP_HONK: u32 = 6; -pub global PROOF_TYPE_PG_FINAL: u32 = 7; +// worktodo comment here where 7 is +pub global PROOF_TYPE_PG_FINAL: u32 = 8; +pub global PROOF_TYPE_PG_TAIL: u32 = 9; pub global TWO_POW_64: Field = 18446744073709551616; // AVM_WRITTEN_PUBLIC_DATA_SLOTS_TREE_HEIGHT = LOG2(MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX + 1)