From cdd92595c313617189a530e0bfda987db211ae6b Mon Sep 17 00:00:00 2001 From: ledwards2225 <98505400+ledwards2225@users.noreply.github.com> Date: Thu, 7 Dec 2023 11:18:33 -0700 Subject: [PATCH] feat: merge recursive verifier (#3588) This PR includes - Recursive merge verifier implementation and independent test - Incorporation of recursive merge verification into `Goblin::accumulate` (and therefore into the full goblin recursion tests). - Verification of the final ultra and merge proofs in goblin recursion tests - Addition of direct constructors of Ultra and Merge verifiers from minimal inputs - no composer required Closes https://github.com/AztecProtocol/barretenberg/issues/797 --- barretenberg/cpp/src/CMakeLists.txt | 2 +- .../commitment_schemes/kzg/kzg.hpp | 11 +- .../src/barretenberg/goblin/CMakeLists.txt | 2 +- .../goblin/full_goblin_composer.test.cpp | 67 ------------ .../goblin/full_goblin_recursion.test.cpp | 24 +++-- .../cpp/src/barretenberg/goblin/goblin.hpp | 35 +++++- .../src/barretenberg/goblin/mock_circuits.hpp | 9 +- .../verifier/merge_recursive_verifier.cpp | 87 +++++++++++++++ .../verifier/merge_recursive_verifier.hpp | 31 ++++++ .../honk/verifier/merge_verifier.test.cpp | 100 ++++++++++++++++++ .../ultra_honk/goblin_ultra_composer.test.cpp | 2 +- .../ultra_honk/merge_verifier.cpp | 7 +- .../ultra_honk/merge_verifier.hpp | 4 +- .../ultra_honk/ultra_composer.hpp | 10 +- .../ultra_honk/ultra_verifier.cpp | 13 +++ .../ultra_honk/ultra_verifier.hpp | 2 + 16 files changed, 296 insertions(+), 110 deletions(-) delete mode 100644 barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp rename barretenberg/cpp/src/barretenberg/{stdlib/recursion => }/goblin/full_goblin_recursion.test.cpp (84%) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.cpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_verifier.test.cpp diff --git a/barretenberg/cpp/src/CMakeLists.txt b/barretenberg/cpp/src/CMakeLists.txt index b09a2181ebc..8f3ee4289c5 100644 --- a/barretenberg/cpp/src/CMakeLists.txt +++ b/barretenberg/cpp/src/CMakeLists.txt @@ -58,8 +58,8 @@ add_subdirectory(barretenberg/eccvm) add_subdirectory(barretenberg/env) add_subdirectory(barretenberg/examples) add_subdirectory(barretenberg/flavor) -add_subdirectory(barretenberg/grumpkin_srs_gen) add_subdirectory(barretenberg/goblin) +add_subdirectory(barretenberg/grumpkin_srs_gen) add_subdirectory(barretenberg/honk) add_subdirectory(barretenberg/join_split_example) add_subdirectory(barretenberg/numeric) diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp index e0cfc6e6477..926923d767e 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp @@ -81,16 +81,15 @@ template class KZG { auto quotient_commitment = verifier_transcript->template receive_from_prover("KZG:W"); GroupElement P_0; - // Note: In the recursive setting, we only add the contribution if it is not the point at infinity (i.e. if the - // evaluation is not equal to zero). if constexpr (Curve::is_stdlib_type) { auto builder = verifier_transcript->builder; auto one = Fr(builder, 1); - std::vector commitments = { claim.commitment, quotient_commitment }; - std::vector scalars = { one, claim.opening_pair.challenge }; + std::vector commitments = { claim.commitment, + quotient_commitment, + GroupElement::one(builder) }; + std::vector scalars = { one, claim.opening_pair.challenge, -claim.opening_pair.evaluation }; P_0 = GroupElement::batch_mul(commitments, scalars); - // Note: This implementation assumes the evaluation is zero (as is the case for shplonk). - ASSERT(claim.opening_pair.evaluation.get_value() == 0); + } else { P_0 = claim.commitment; P_0 += quotient_commitment * claim.opening_pair.challenge; diff --git a/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt index a6c3c61383a..adaa9814aed 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt @@ -1 +1 @@ -barretenberg_module(goblin ultra_honk eccvm translator_vm transcript) \ No newline at end of file +barretenberg_module(goblin stdlib_recursion ultra_honk eccvm translator_vm) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp deleted file mode 100644 index 7503307881a..00000000000 --- a/barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "barretenberg/eccvm/eccvm_composer.hpp" -#include "barretenberg/goblin/goblin.hpp" -#include "barretenberg/goblin/mock_circuits.hpp" -#include "barretenberg/goblin/translation_evaluations.hpp" -#include "barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp" -#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" -#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" -#include "barretenberg/translator_vm/goblin_translator_composer.hpp" -#include "barretenberg/ultra_honk/ultra_composer.hpp" - -#include - -using namespace barretenberg; -using namespace proof_system::honk; - -namespace test_full_goblin_composer { - -class FullGoblinComposerTests : public ::testing::Test { - protected: - static void SetUpTestSuite() - { - barretenberg::srs::init_crs_factory("../srs_db/ignition"); - barretenberg::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); - } - - using Curve = curve::BN254; - using FF = Curve::ScalarField; - using Fbase = Curve::BaseField; - using Point = Curve::AffineElement; - using CommitmentKey = pcs::CommitmentKey; - using OpQueue = proof_system::ECCOpQueue; - using GoblinUltraBuilder = proof_system::GoblinUltraCircuitBuilder; - using ECCVMFlavor = flavor::ECCVM; - using ECCVMBuilder = proof_system::ECCVMCircuitBuilder; - using ECCVMComposer = ECCVMComposer_; - using KernelInput = Goblin::AccumulationOutput; -}; - -/** - * @brief Test proof construction/verification for a circuit with ECC op gates, public inputs, and basic arithmetic - * gates - * @note We simulate op queue interactions with a previous circuit so the actual circuit under test utilizes an op queue - * with non-empty 'previous' data. This avoids complications with zero-commitments etc. - * - */ -TEST_F(FullGoblinComposerTests, SimpleCircuit) -{ - barretenberg::Goblin goblin; - GoblinUltraBuilder initial_circuit{ goblin.op_queue }; - GoblinTestingUtils::construct_simple_initial_circuit(initial_circuit); - KernelInput kernel_input = goblin.accumulate(initial_circuit); - - // Construct a series of simple Goblin circuits; generate and verify their proofs - size_t NUM_CIRCUITS = 2; - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - GoblinUltraBuilder circuit_builder{ goblin.op_queue }; - GoblinTestingUtils::construct_arithmetic_circuit(circuit_builder); - kernel_input = goblin.accumulate(circuit_builder); - } - - Goblin::Proof proof = goblin.prove(); - bool verified = goblin.verify(proof); - EXPECT_TRUE(verified); -} - -// TODO(https://github.com/AztecProtocol/barretenberg/issues/787) Expand these tests. -} // namespace test_full_goblin_composer diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/goblin/full_goblin_recursion.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp similarity index 84% rename from barretenberg/cpp/src/barretenberg/stdlib/recursion/goblin/full_goblin_recursion.test.cpp rename to barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp index 51d9f1943cb..2adc5049600 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/recursion/goblin/full_goblin_recursion.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/full_goblin_recursion.test.cpp @@ -24,10 +24,6 @@ class GoblinRecursionTests : public ::testing::Test { using Curve = curve::BN254; using FF = Curve::ScalarField; - using Fbase = Curve::BaseField; - using Point = Curve::AffineElement; - using CommitmentKey = pcs::CommitmentKey; - using OpQueue = proof_system::ECCOpQueue; using GoblinUltraBuilder = proof_system::GoblinUltraCircuitBuilder; using ECCVMFlavor = flavor::ECCVM; using ECCVMBuilder = proof_system::ECCVMCircuitBuilder; @@ -36,11 +32,11 @@ class GoblinRecursionTests : public ::testing::Test { using TranslatorBuilder = proof_system::GoblinTranslatorCircuitBuilder; using TranslatorComposer = GoblinTranslatorComposer; using TranslatorConsistencyData = barretenberg::TranslationEvaluations; - using Proof = proof_system::plonk::proof; - using NativeVerificationKey = flavor::GoblinUltra::VerificationKey; using RecursiveFlavor = flavor::GoblinUltraRecursive_; using RecursiveVerifier = proof_system::plonk::stdlib::recursion::honk::UltraRecursiveVerifier_; + using Goblin = barretenberg::Goblin; using KernelInput = Goblin::AccumulationOutput; + using UltraVerifier = UltraVerifier_; /** * @brief Construct a mock kernel circuit @@ -58,8 +54,11 @@ class GoblinRecursionTests : public ::testing::Test { // Execute recursive aggregation of previous kernel proof RecursiveVerifier verifier{ &builder, kernel_input.verification_key }; // TODO(https://github.com/AztecProtocol/barretenberg/issues/801): Aggregation - auto pairing_points = verifier.verify_proof(kernel_input.proof); // app function proof - pairing_points = verifier.verify_proof(kernel_input.proof); // previous kernel proof + auto pairing_points = verifier.verify_proof(kernel_input.proof); // previous kernel proof + // TODO(https://github.com/AztecProtocol/barretenberg/issues/803): Mock app circuit. In the absence of a mocked + // app circuit proof, we simply perform another recursive verification for the previous kernel proof to + // approximate the work done for the app proof. + pairing_points = verifier.verify_proof(kernel_input.proof); } }; @@ -69,11 +68,12 @@ class GoblinRecursionTests : public ::testing::Test { */ TEST_F(GoblinRecursionTests, Pseudo) { - barretenberg::Goblin goblin; + Goblin goblin; // Construct an initial circuit; its proof will be recursively verified by the first kernel GoblinUltraBuilder initial_circuit{ goblin.op_queue }; GoblinTestingUtils::construct_simple_initial_circuit(initial_circuit); + KernelInput kernel_input = goblin.accumulate(initial_circuit); // Construct a series of simple Goblin circuits; generate and verify their proofs @@ -88,8 +88,12 @@ TEST_F(GoblinRecursionTests, Pseudo) } Goblin::Proof proof = goblin.prove(); + // Verify the final ultra proof + UltraVerifier ultra_verifier{ kernel_input.verification_key }; + bool ultra_verified = ultra_verifier.verify_proof(kernel_input.proof); + // Verify the goblin proof (eccvm, translator, merge) bool verified = goblin.verify(proof); - EXPECT_TRUE(verified); + EXPECT_TRUE(ultra_verified && verified); } // TODO(https://github.com/AztecProtocol/barretenberg/issues/787) Expand these tests. diff --git a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp index 315b66657f3..3c2588288df 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp @@ -4,6 +4,7 @@ #include "barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_translator_circuit_builder.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp" #include "barretenberg/translator_vm/goblin_translator_composer.hpp" #include "barretenberg/ultra_honk/ultra_composer.hpp" @@ -24,6 +25,7 @@ class Goblin { }; struct Proof { + HonkProof merge_proof; HonkProof eccvm_proof; HonkProof translator_proof; TranslationEvaluations translation_evaluations; @@ -41,9 +43,17 @@ class Goblin { using ECCVMComposer = proof_system::honk::ECCVMComposer; using TranslatorBuilder = proof_system::GoblinTranslatorCircuitBuilder; using TranslatorComposer = proof_system::honk::GoblinTranslatorComposer; + using RecursiveMergeVerifier = + proof_system::plonk::stdlib::recursion::goblin::MergeRecursiveVerifier_; + using MergeVerifier = proof_system::honk::MergeVerifier_; std::shared_ptr op_queue = std::make_shared(); + HonkProof merge_proof; + + // on the first call to accumulate there is no merge proof to verify + bool merge_proof_exists{ false }; + private: // TODO(https://github.com/AztecProtocol/barretenberg/issues/798) unique_ptr use is a hack std::unique_ptr eccvm_builder; @@ -59,16 +69,25 @@ class Goblin { */ AccumulationOutput accumulate(GoblinUltraCircuitBuilder& circuit_builder) { - // TODO(https://github.com/AztecProtocol/barretenberg/issues/797) Complete the "kernel" logic by recursively - // verifying previous merge proof + // Complete the circuit logic by recursively verifying previous merge proof if it exists + if (merge_proof_exists) { + RecursiveMergeVerifier merge_verifier{ &circuit_builder }; + [[maybe_unused]] auto pairing_points = merge_verifier.verify_proof(merge_proof); + } + // Construct a Honk proof for the main circuit GoblinUltraComposer composer; auto instance = composer.create_instance(circuit_builder); auto prover = composer.create_prover(instance); auto ultra_proof = prover.construct_proof(); + // Construct and store the merge proof to be recursively verified on the next call to accumulate auto merge_prover = composer.create_merge_prover(op_queue); - [[maybe_unused]] auto merge_proof = merge_prover.construct_proof(); + merge_proof = merge_prover.construct_proof(); + + if (!merge_proof_exists) { + merge_proof_exists = true; + } return { ultra_proof, instance->verification_key }; }; @@ -76,6 +95,9 @@ class Goblin { Proof prove() { Proof proof; + + proof.merge_proof = std::move(merge_proof); + eccvm_builder = std::make_unique(op_queue); eccvm_composer = std::make_unique(); auto eccvm_prover = eccvm_composer->create_prover(*eccvm_builder); @@ -87,11 +109,15 @@ class Goblin { translator_composer = std::make_unique(); auto translator_prover = translator_composer->create_prover(*translator_builder, eccvm_prover.transcript); proof.translator_proof = translator_prover.construct_proof(); + return proof; }; bool verify(const Proof& proof) { + MergeVerifier merge_verifier; + bool merge_verified = merge_verifier.verify_proof(proof.merge_proof); + auto eccvm_verifier = eccvm_composer->create_verifier(*eccvm_builder); bool eccvm_verified = eccvm_verifier.verify_proof(proof.eccvm_proof); @@ -100,7 +126,8 @@ class Goblin { // TODO(https://github.com/AztecProtocol/barretenberg/issues/799): // Ensure translation_evaluations are passed correctly bool translation_verified = translator_verifier.verify_translation(proof.translation_evaluations); - return eccvm_verified && accumulator_construction_verified && translation_verified; + + return merge_verified && eccvm_verified && accumulator_construction_verified && translation_verified; }; }; } // namespace barretenberg \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index cb12bb08c37..5199a65ab63 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -1,12 +1,7 @@ #include "barretenberg/commitment_schemes/commitment_key.hpp" -#include "barretenberg/eccvm/eccvm_composer.hpp" -#include "barretenberg/goblin/goblin.hpp" -#include "barretenberg/goblin/translation_evaluations.hpp" -#include "barretenberg/proof_system/circuit_builder/eccvm/eccvm_circuit_builder.hpp" +#include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" -#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" -#include "barretenberg/translator_vm/goblin_translator_composer.hpp" -#include "barretenberg/ultra_honk/ultra_composer.hpp" +#include "barretenberg/srs/global_crs.hpp" namespace barretenberg { class GoblinTestingUtils { diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.cpp new file mode 100644 index 00000000000..f04c32c9583 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.cpp @@ -0,0 +1,87 @@ +#include "barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp" + +namespace proof_system::plonk::stdlib::recursion::goblin { + +template +MergeRecursiveVerifier_::MergeRecursiveVerifier_(CircuitBuilder* builder) + : builder(builder) +{} + +/** + * @brief Construct recursive verifier for Goblin Merge protocol, up to but not including the pairing + * + * @tparam Flavor + * @param proof + * @return std::array Inputs to final pairing + */ +template +std::array::Element, 2> MergeRecursiveVerifier_::verify_proof( + const plonk::proof& proof) +{ + transcript = std::make_shared(builder, proof.proof_data); + + // Receive commitments [t_i^{shift}], [T_{i-1}], and [T_i] + std::array C_T_prev; + std::array C_t_shift; + std::array C_T_current; + for (size_t idx = 0; idx < NUM_WIRES; ++idx) { + C_T_prev[idx] = transcript->template receive_from_prover("T_PREV_" + std::to_string(idx + 1)); + C_t_shift[idx] = transcript->template receive_from_prover("t_SHIFT_" + std::to_string(idx + 1)); + C_T_current[idx] = transcript->template receive_from_prover("T_CURRENT_" + std::to_string(idx + 1)); + } + + FF kappa = transcript->get_challenge("kappa"); + + // Receive transcript poly evaluations and add corresponding univariate opening claims {(\kappa, p(\kappa), [p(X)]} + std::array T_prev_evals; + std::array t_shift_evals; + std::array T_current_evals; + std::vector opening_claims; + for (size_t idx = 0; idx < NUM_WIRES; ++idx) { + T_prev_evals[idx] = transcript->template receive_from_prover("T_prev_eval_" + std::to_string(idx + 1)); + opening_claims.emplace_back(OpeningClaim{ { kappa, T_prev_evals[idx] }, C_T_prev[idx] }); + } + for (size_t idx = 0; idx < NUM_WIRES; ++idx) { + t_shift_evals[idx] = transcript->template receive_from_prover("t_shift_eval_" + std::to_string(idx + 1)); + opening_claims.emplace_back(OpeningClaim{ { kappa, t_shift_evals[idx] }, C_t_shift[idx] }); + } + for (size_t idx = 0; idx < NUM_WIRES; ++idx) { + T_current_evals[idx] = + transcript->template receive_from_prover("T_current_eval_" + std::to_string(idx + 1)); + opening_claims.emplace_back(OpeningClaim{ { kappa, T_current_evals[idx] }, C_T_current[idx] }); + } + + // Check the identity T_i(\kappa) = T_{i-1}(\kappa) + t_i^{shift}(\kappa) + for (size_t idx = 0; idx < NUM_WIRES; ++idx) { + T_current_evals[idx].assert_equal(T_prev_evals[idx] + t_shift_evals[idx]); + } + + FF alpha = transcript->get_challenge("alpha"); + + // Constuct batched commitment and batched evaluation from constituents using batching challenge \alpha + std::vector scalars; + std::vector commitments; + scalars.emplace_back(FF(builder, 1)); + commitments.emplace_back(opening_claims[0].commitment); + auto batched_eval = opening_claims[0].opening_pair.evaluation; + auto alpha_pow = alpha; + for (size_t idx = 1; idx < opening_claims.size(); ++idx) { + auto& claim = opening_claims[idx]; + scalars.emplace_back(alpha_pow); + commitments.emplace_back(claim.commitment); + batched_eval += alpha_pow * claim.opening_pair.evaluation; + alpha_pow *= alpha; + } + + auto batched_commitment = Commitment::batch_mul(commitments, scalars); + + OpeningClaim batched_claim = { { kappa, batched_eval }, batched_commitment }; + + auto pairing_points = KZG::compute_pairing_points(batched_claim, transcript); + + return pairing_points; +} + +template class MergeRecursiveVerifier_; + +} // namespace proof_system::plonk::stdlib::recursion::goblin diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp new file mode 100644 index 00000000000..341d91a1bd1 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "barretenberg/commitment_schemes/kzg/kzg.hpp" +#include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/recursion/honk/transcript/transcript.hpp" + +namespace proof_system::plonk::stdlib::recursion::goblin { +template class MergeRecursiveVerifier_ { + public: + using Curve = bn254; + using FF = typename Curve::ScalarField; + using Commitment = typename Curve::Element; + using GroupElement = typename Curve::Element; + using KZG = ::proof_system::honk::pcs::kzg::KZG; + using OpeningClaim = ::proof_system::honk::pcs::OpeningClaim; + using PairingPoints = std::array; + using Transcript = honk::Transcript; + + CircuitBuilder* builder; + std::shared_ptr transcript; + + static constexpr size_t NUM_WIRES = arithmetization::UltraHonk::NUM_WIRES; + + explicit MergeRecursiveVerifier_(CircuitBuilder* builder); + + PairingPoints verify_proof(const plonk::proof& proof); +}; + +extern template class MergeRecursiveVerifier_; + +} // namespace proof_system::plonk::stdlib::recursion::goblin diff --git a/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_verifier.test.cpp new file mode 100644 index 00000000000..39b981c4b6a --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/recursion/honk/verifier/merge_verifier.test.cpp @@ -0,0 +1,100 @@ +#include "barretenberg/common/test.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/stdlib/primitives/curves/bn254.hpp" +#include "barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" + +namespace proof_system::plonk::stdlib::recursion::goblin { + +/** + * @brief Test suite for recursive verification of Goblin Merge proofs + * @details The recursive verification circuit is arithmetized using Goblin-style Ultra arithmetization + * (GoblinUltraCircuitBuilder). + * + * @tparam Builder + */ +class RecursiveMergeVerifierTest : public testing::Test { + + // Types for recursive verifier circuit + using RecursiveBuilder = GoblinUltraCircuitBuilder; + using RecursiveMergeVerifier = MergeRecursiveVerifier_; + + // Define types relevant for inner circuit + using GoblinUltraFlavor = ::proof_system::honk::flavor::GoblinUltra; + using GoblinUltraComposer = ::proof_system::honk::UltraComposer_; + using InnerFlavor = GoblinUltraFlavor; + using InnerComposer = GoblinUltraComposer; + using InnerBuilder = typename InnerComposer::CircuitBuilder; + + // Define additional types for testing purposes + using Commitment = InnerFlavor::Commitment; + using FF = InnerFlavor::FF; + using VerifierCommitmentKey = ::proof_system::honk::pcs::VerifierCommitmentKey; + + public: + static void SetUpTestSuite() { barretenberg::srs::init_crs_factory("../srs_db/ignition"); } + + /** + * @brief Test recursive merge verification for the ops generated by a sample circuit + * @details We construct and verify an Ultra Honk proof of the recursive merge verifier circuit to check its + * correctness rather than calling check_circuit since this functionality is incomplete for the Goblin + * arithmetization + */ + static void test_recursive_merge_verification() + { + auto op_queue = std::make_shared(); + + InnerBuilder sample_circuit{ op_queue }; + GoblinTestingUtils::construct_simple_initial_circuit(sample_circuit); + + // Generate a proof over the inner circuit + InnerComposer inner_composer; + auto merge_prover = inner_composer.create_merge_prover(op_queue); + auto merge_proof = merge_prover.construct_proof(); + + // Create a recursive merge verification circuit for the merge proof + RecursiveBuilder outer_circuit; + RecursiveMergeVerifier verifier{ &outer_circuit }; + auto pairing_points = verifier.verify_proof(merge_proof); + + // Check for a failure flag in the recursive verifier circuit + EXPECT_EQ(outer_circuit.failed(), false) << outer_circuit.err(); + + // Check 1: Perform native merge verification then perform the pairing on the outputs of the recursive merge + // verifier and check that the result agrees. + auto native_verifier = inner_composer.create_merge_verifier(); + bool verified_native = native_verifier.verify_proof(merge_proof); + VerifierCommitmentKey pcs_verification_key(0, srs::get_crs_factory()); + auto verified_recursive = + pcs_verification_key.pairing_check(pairing_points[0].get_value(), pairing_points[1].get_value()); + EXPECT_EQ(verified_native, verified_recursive); + EXPECT_TRUE(verified_recursive); + + // Check 2: Ensure that the underlying native and recursive merge verification algorithms agree by ensuring + // the manifests produced by each agree. + auto recursive_manifest = verifier.transcript->get_manifest(); + auto native_manifest = native_verifier.transcript->get_manifest(); + for (size_t i = 0; i < recursive_manifest.size(); ++i) { + EXPECT_EQ(recursive_manifest[i], native_manifest[i]); + } + + // Check 3: Construct and verify a (goblin) ultra honk proof of the Merge recursive verifier circuit + { + GoblinUltraComposer composer; + auto instance = composer.create_instance(outer_circuit); + auto prover = composer.create_prover(instance); + auto verifier = composer.create_verifier(instance); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + + EXPECT_TRUE(verified); + } + } +}; + +TEST_F(RecursiveMergeVerifierTest, SingleRecursiveVerification) +{ + RecursiveMergeVerifierTest::test_recursive_merge_verification(); +}; + +} // namespace proof_system::plonk::stdlib::recursion::goblin \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp index a09a822085b..52bc8aa9a03 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp @@ -77,7 +77,7 @@ class GoblinUltraHonkComposerTests : public ::testing::Test { bool construct_and_verify_merge_proof(auto& composer, auto& op_queue) { auto merge_prover = composer.create_merge_prover(op_queue); - auto merge_verifier = composer.create_merge_verifier(10); + auto merge_verifier = composer.create_merge_verifier(); auto merge_proof = merge_prover.construct_proof(); bool verified = merge_verifier.verify_proof(merge_proof); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp index c93257af7de..ebf18518afa 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.cpp @@ -3,10 +3,9 @@ namespace proof_system::honk { template -MergeVerifier_::MergeVerifier_(std::unique_ptr verification_key, - const std::shared_ptr& transcript) - : transcript(transcript) - , pcs_verification_key(std::move(verification_key)){}; +MergeVerifier_::MergeVerifier_() + : transcript(std::make_shared()) + , pcs_verification_key(std::make_unique(0, barretenberg::srs::get_crs_factory())){}; /** * @brief Verify proper construction of the aggregate Goblin ECC op queue polynomials T_i^(j), j = 1,2,3,4. diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp index d6880476b79..da094df6b7e 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/merge_verifier.hpp @@ -5,6 +5,7 @@ #include "barretenberg/flavor/ultra.hpp" #include "barretenberg/plonk/proof_system/types/proof.hpp" #include "barretenberg/proof_system/op_queue/ecc_op_queue.hpp" +#include "barretenberg/srs/global_crs.hpp" #include "barretenberg/transcript/transcript.hpp" namespace proof_system::honk { @@ -31,8 +32,7 @@ template class MergeVerifier_ { std::shared_ptr op_queue; std::shared_ptr pcs_verification_key; - explicit MergeVerifier_(std::unique_ptr verification_key, - const std::shared_ptr& transcript); + explicit MergeVerifier_(); bool verify_proof(const plonk::proof& proof); }; diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp index cdfb202db39..0d4dd752d08 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_composer.hpp @@ -68,6 +68,8 @@ template class UltraComposer_ { * * @param op_queue * @return MergeProver_ + * TODO(https://github.com/AztecProtocol/barretenberg/issues/804): Goblin should be responsible for constructing + * merge prover/verifier. */ MergeProver_ create_merge_prover( const std::shared_ptr& op_queue, @@ -84,15 +86,9 @@ template class UltraComposer_ { /** * @brief Create Verifier for Goblin ECC op queue merge protocol * - * @param size Size of commitment key required to commit to shifted op queue contribution t_i * @return MergeVerifier_ */ - MergeVerifier_ create_merge_verifier( - size_t srs_size, const std::shared_ptr& transcript = std::make_shared()) - { - auto pcs_verification_key = std::make_unique(srs_size, crs_factory_); - return MergeVerifier_(std::move(pcs_verification_key), transcript); - } + MergeVerifier_ create_merge_verifier() { return MergeVerifier_(); } ProtoGalaxyProver_ create_folding_prover(const std::vector>& instances) { diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp index df70e5ac12c..2775b586c7e 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.cpp @@ -15,6 +15,19 @@ UltraVerifier_::UltraVerifier_(const std::shared_ptr& transc , transcript(transcript) {} +/** + * @brief Construct an UltraVerifier directly from a verification key + * + * @tparam Flavor + * @param verifier_key + */ +template +UltraVerifier_::UltraVerifier_(const std::shared_ptr& verifier_key) + : key(verifier_key) + , pcs_verification_key(std::make_unique(0, barretenberg::srs::get_crs_factory())) + , transcript(std::make_shared()) +{} + template UltraVerifier_::UltraVerifier_(UltraVerifier_&& other) : key(std::move(other.key)) diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp index 6a023071f02..8197e46a941 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/ultra_verifier.hpp @@ -2,6 +2,7 @@ #include "barretenberg/flavor/goblin_ultra.hpp" #include "barretenberg/flavor/ultra.hpp" #include "barretenberg/plonk/proof_system/types/proof.hpp" +#include "barretenberg/srs/global_crs.hpp" #include "barretenberg/sumcheck/sumcheck.hpp" namespace proof_system::honk { @@ -15,6 +16,7 @@ template class UltraVerifier_ { public: explicit UltraVerifier_(const std::shared_ptr& transcript, const std::shared_ptr& verifier_key = nullptr); + explicit UltraVerifier_(const std::shared_ptr& verifier_key); UltraVerifier_(UltraVerifier_&& other); UltraVerifier_& operator=(const UltraVerifier_& other) = delete;