From 53d0d55594cc4a71894394455308472f90b434be Mon Sep 17 00:00:00 2001 From: maramihali Date: Fri, 31 May 2024 21:17:21 +0100 Subject: [PATCH] feat: Goblin Recursive Verifier (#6778) Implements a Goblin Recursive Verifier without the Merge. Removes hack in Translator Recursive Verifier and ensures we are able to pass the Transcript from ECCVM to Translator Recursive Verifier. --------- Co-authored-by: ledwards2225 --- .../commitment_schemes/ipa/ipa.hpp | 2 +- .../cpp/src/barretenberg/goblin/goblin.hpp | 10 ++ .../stdlib/honk_recursion/CMakeLists.txt | 3 + .../verifier/goblin_recursive_verifier.cpp | 36 ++++++ .../verifier/goblin_recursive_verifier.hpp | 45 ++++++++ .../goblin_recursive_verifier.test.cpp | 106 ++++++++++++++++++ .../verifier/merge_verifier.test.cpp | 29 +++-- .../stdlib/primitives/field/field.cpp | 1 - .../translator_recursive_verifier.cpp | 15 +-- .../translator_recursive_verifier.hpp | 6 +- .../translator_recursive_verifier.test.cpp | 14 +-- 11 files changed, 232 insertions(+), 35 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.cpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.hpp create mode 100644 barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.test.cpp diff --git a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp index 1274b696a68..288ad34b3ab 100644 --- a/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp +++ b/barretenberg/cpp/src/barretenberg/commitment_schemes/ipa/ipa.hpp @@ -491,7 +491,7 @@ template class IPA { // to a bb::fr, not a grumpkin::fr, which is a BaseField element for // Grumpkin - // Ensure polynomial length cannot be changed from it's genuine value + // Ensure polynomial length cannot be changed from its default specified valued poly_length_var.fix_witness(); const uint32_t poly_length = static_cast(poly_length_var.get_value()); diff --git a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp index f0d3462b6ad..1fa923c620f 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp @@ -186,6 +186,11 @@ class GoblinVerifier { using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey; using MergeVerifier = bb::MergeVerifier_; + struct VerifierInput { + std::shared_ptr eccvm_verification_key; + std::shared_ptr translator_verification_key; + }; + private: std::shared_ptr eccvm_verification_key; std::shared_ptr translator_verification_key; @@ -197,6 +202,11 @@ class GoblinVerifier { , translator_verification_key(translator_verification_key) {} + GoblinVerifier(VerifierInput input) + : eccvm_verification_key(input.eccvm_verification_key) + , translator_verification_key(input.translator_verification_key) + {} + /** * @brief Verify a full Goblin proof (ECCVM, Translator, merge) * diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/CMakeLists.txt index 53416eef593..947af5ea8f9 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/CMakeLists.txt @@ -6,5 +6,8 @@ barretenberg_module( ultra_honk stdlib_poseidon2 protogalaxy + goblin + translator_vm_recursion + eccvm_recursion client_ivc ) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.cpp new file mode 100644 index 00000000000..62000cbeda9 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.cpp @@ -0,0 +1,36 @@ +#include "barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.hpp" + +namespace bb::stdlib::recursion::honk { + +void GoblinRecursiveVerifier::verify(GoblinProof& proof) +{ + // Run the ECCVM recursive verifier + ECCVMVerifier eccvm_verifier{ builder, verification_keys.eccvm_verification_key }; + eccvm_verifier.verify_proof(proof.eccvm_proof); + + // Run the Translator recursive verifier + TranslatorVerifier translator_verifier{ builder, + verification_keys.translator_verification_key, + eccvm_verifier.transcript }; + translator_verifier.verify_proof(proof.translator_proof); + + // Verify the consistency between the ECCVM and Translator transcript polynomial evaluations + // In reality the Goblin Proof is going to already be a stdlib proof and this conversion is not going to happen here + // (see https://github.com/AztecProtocol/barretenberg/issues/991) + auto native_translation_evaluations = proof.translation_evaluations; + auto translation_evaluations = + TranslationEvaluations{ TranslatorBF::from_witness(builder, native_translation_evaluations.op), + TranslatorBF::from_witness(builder, native_translation_evaluations.Px), + TranslatorBF::from_witness(builder, native_translation_evaluations.Py), + TranslatorBF::from_witness(builder, native_translation_evaluations.z1), + TranslatorBF::from_witness(builder, native_translation_evaluations.z2) + + }; + translator_verifier.verify_translation(translation_evaluations); + + // TODO(https://github.com/AztecProtocol/barretenberg/issues/1024): Perform recursive merge verification once it + // works with Ultra arithmetization + // MergeVerifier merge_verified{ builder }; + // [[maybe_unused]] auto merge_pairing_points = merge_verifier.verify_proof(proof.merge_proof); +} +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.hpp new file mode 100644 index 00000000000..584e566a194 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.hpp @@ -0,0 +1,45 @@ +#pragma once +#include "barretenberg/eccvm_recursion/eccvm_recursive_verifier.hpp" +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/stdlib/honk_recursion/verifier/merge_recursive_verifier.hpp" +#include "barretenberg/translator_vm_recursion/translator_recursive_verifier.hpp" + +namespace bb::stdlib::recursion::honk { +class GoblinRecursiveVerifier { + public: + // Goblin Recursive Verifier circuit is using Ultra arithmetisation + using Builder = UltraCircuitBuilder; + using MergeVerifier = goblin::MergeRecursiveVerifier_; + + using TranslatorFlavor = TranslatorRecursiveFlavor_; + using TranslatorVerifier = TranslatorRecursiveVerifier_; + using TranslationEvaluations = TranslatorVerifier::TranslationEvaluations; + using TranslatorBF = TranslatorFlavor::BF; + + using ECCVMFlavor = ECCVMRecursiveFlavor_; + using ECCVMVerifier = ECCVMRecursiveVerifier_; + + // ECCVM and Translator verification keys + using VerifierInput = GoblinVerifier::VerifierInput; + + GoblinRecursiveVerifier(Builder* builder, VerifierInput& verification_keys) + : builder(builder) + , verification_keys(verification_keys){}; + + /** + * @brief Construct a Goblin recursive verifier circuit + * @details Contains three recursive verifiers: Merge, ECCVM, and Translator + * @todo(https://github.com/AztecProtocol/barretenberg/issues/1021): The values returned by the recursive verifiers + * are not aggregated here. We need to aggregate and return the pairing points from Merge/Translator plus deal with + * the IPA accumulator from ECCVM. + * + * @todo(https://github.com/AztecProtocol/barretenberg/issues/991): The GoblinProof should aleady be a stdlib proof + */ + void verify(GoblinProof&); + + private: + Builder* builder; + VerifierInput verification_keys; // ECCVM and Translator verification keys +}; + +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.test.cpp new file mode 100644 index 00000000000..bd9e9533a94 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.test.cpp @@ -0,0 +1,106 @@ +#include "barretenberg/stdlib/honk_recursion/verifier/goblin_recursive_verifier.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" +#include "barretenberg/common/test.hpp" +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/ultra_honk/ultra_prover.hpp" +#include "barretenberg/ultra_honk/ultra_verifier.hpp" + +namespace bb::stdlib::recursion::honk { +class GoblinRecursiveVerifierTests : public testing::Test { + public: + using Builder = GoblinRecursiveVerifier::Builder; + using ECCVMVK = GoblinVerifier::ECCVMVerificationKey; + using TranslatorVK = GoblinVerifier::TranslatorVerificationKey; + + using OuterFlavor = UltraFlavor; + using OuterProver = UltraProver_; + using OuterVerifier = UltraVerifier_; + using OuterProverInstance = ProverInstance_; + + static void SetUpTestSuite() + { + bb::srs::init_crs_factory("../srs_db/ignition"); + bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } + + static MegaCircuitBuilder construct_mock_circuit(std::shared_ptr op_queue) + { + MegaCircuitBuilder circuit{ op_queue }; + MockCircuits::construct_arithmetic_circuit(circuit, /*target_log2_dyadic_size=*/8); + MockCircuits::construct_goblin_ecc_op_circuit(circuit); + return circuit; + } + + struct ProverOutput { + GoblinProof proof; + GoblinVerifier::VerifierInput verfier_input; + }; + + /** + * @brief Create a goblin proof and the VM verification keys needed by the goblin recursive verifier + * + * @return ProverOutput + */ + ProverOutput create_goblin_prover_output() + { + GoblinProver goblin; + + // Construct and accumulate multiple circuits + size_t NUM_CIRCUITS = 3; + for (size_t idx = 0; idx < NUM_CIRCUITS; ++idx) { + auto circuit = construct_mock_circuit(goblin.op_queue); + goblin.merge(circuit); // appends a recurisve merge verifier if a merge proof exists + } + + // Output is a goblin proof plus ECCVM/Translator verification keys + return { goblin.prove(), + { std::make_shared(goblin.get_eccvm_proving_key()), + std::make_shared(goblin.get_translator_proving_key()) } }; + } +}; + +/** + * @brief Ensure the Goblin proof produced by the test method can be natively verified + * + */ +TEST_F(GoblinRecursiveVerifierTests, NativeVerification) +{ + auto [proof, verifier_input] = create_goblin_prover_output(); + + GoblinVerifier verifier{ verifier_input }; + + EXPECT_TRUE(verifier.verify(proof)); +} + +/** + * @brief Construct and check a goblin recursive verification circuit + * + */ +TEST_F(GoblinRecursiveVerifierTests, Basic) +{ + auto [proof, verifier_input] = create_goblin_prover_output(); + + Builder builder; + GoblinRecursiveVerifier verifier{ &builder, verifier_input }; + verifier.verify(proof); + + info("Recursive Verifier: num gates = ", builder.num_gates); + + EXPECT_EQ(builder.failed(), false) << builder.err(); + + EXPECT_TRUE(CircuitChecker::check(builder)); + + // Construct and verify a proof for the Goblin Recursive Verifier circuit + { + auto instance = std::make_shared(builder); + OuterProver prover(instance); + auto verification_key = std::make_shared(instance->proving_key); + OuterVerifier verifier(verification_key); + auto proof = prover.construct_proof(); + bool verified = verifier.verify_proof(proof); + + ASSERT(verified); + } +} + +} // namespace bb::stdlib::recursion::honk \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/merge_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/merge_verifier.test.cpp index 45134d2098c..b4490805360 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/merge_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/honk_recursion/verifier/merge_verifier.test.cpp @@ -1,4 +1,5 @@ #include "barretenberg/ultra_honk/merge_verifier.hpp" +#include "barretenberg/circuit_checker/circuit_checker.hpp" #include "barretenberg/common/test.hpp" #include "barretenberg/goblin/mock_circuits.hpp" #include "barretenberg/stdlib/honk_recursion/verifier/merge_recursive_verifier.hpp" @@ -16,10 +17,9 @@ namespace bb::stdlib::recursion::goblin { * * @tparam Builder */ -class RecursiveMergeVerifierTest : public testing::Test { +template class RecursiveMergeVerifierTest : public testing::Test { // Types for recursive verifier circuit - using RecursiveBuilder = MegaCircuitBuilder; using RecursiveMergeVerifier = MergeRecursiveVerifier_; // Define types relevant for inner circuit @@ -82,23 +82,22 @@ class RecursiveMergeVerifierTest : public testing::Test { EXPECT_EQ(recursive_manifest[i], native_manifest[i]); } - // Check 3: Construct and verify a (goblin) ultra honk proof of the Merge recursive verifier circuit - { - auto instance = std::make_shared(outer_circuit); - MegaProver prover(instance); - auto verification_key = std::make_shared(instance->proving_key); - MegaVerifier verifier(verification_key); - auto proof = prover.construct_proof(); - bool verified = verifier.verify_proof(proof); - - EXPECT_TRUE(verified); - } + // Check the recursive merge verifier circuit + CircuitChecker::check(outer_circuit); } }; -TEST_F(RecursiveMergeVerifierTest, SingleRecursiveVerification) +// Run the recursive verifier tests with Ultra and Mega builders +// TODO(https://github.com/AztecProtocol/barretenberg/issues/1024): Ultra fails, possibly due to repeated points in +// batch mul? +// using Builders = testing::Types; +using Builders = testing::Types; + +TYPED_TEST_SUITE(RecursiveMergeVerifierTest, Builders); + +TYPED_TEST(RecursiveMergeVerifierTest, SingleRecursiveVerification) { - RecursiveMergeVerifierTest::test_recursive_merge_verification(); + TestFixture::test_recursive_merge_verification(); }; } // namespace bb::stdlib::recursion::goblin \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.cpp b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.cpp index 30ef8cdd818..44da799f729 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.cpp +++ b/barretenberg/cpp/src/barretenberg/stdlib/primitives/field/field.cpp @@ -672,7 +672,6 @@ template bb::fr field_t::get_value() const template bool_t field_t::operator==(const field_t& other) const { Builder* ctx = (context == nullptr) ? other.context : context; - if (is_constant() && other.is_constant()) { return (get_value() == other.get_value()); } diff --git a/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.cpp b/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.cpp index 45414aa2c17..e22a831aa26 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.cpp @@ -11,8 +11,11 @@ namespace bb { template TranslatorRecursiveVerifier_::TranslatorRecursiveVerifier_( - Builder* builder, const std::shared_ptr& native_verifier_key) + Builder* builder, + const std::shared_ptr& native_verifier_key, + const std::shared_ptr& transcript) : key(std::make_shared(builder, native_verifier_key)) + , transcript(transcript) , builder(builder) {} @@ -60,15 +63,10 @@ std::array TranslatorRecursiveVerifier_; using VerifierCommitments = typename Flavor::VerifierCommitments; using CommitmentLabels = typename Flavor::CommitmentLabels; - using Transcript = typename Flavor::Transcript; StdlibProof stdlib_proof = bb::convert_proof_to_witness(builder, proof); - transcript = std::make_shared(stdlib_proof); + transcript->load_proof(stdlib_proof); - // TODO(github.com/AztecProtocol/barretenberg/issues/985): Normally, the ECCVM verifier would have run - // before the translator and there will already by data in the transcript that can be hash to get the batching - // challenge. Once this is implemented the hack can be removed. - transcript->template receive_from_prover("init"); batching_challenge_v = transcript->template get_challenge("Translation:batching_challenge"); VerifierCommitments commitments{ key }; @@ -125,7 +123,6 @@ std::array TranslatorRecursiveVerifier_ bool TranslatorRecursiveVerifier_::verify_translation( const TranslationEvaluations_& translation_evaluations) @@ -152,7 +149,7 @@ bool TranslatorRecursiveVerifier_::verify_translation( const BF eccvm_opening = (op + (v1 * Px) + (v2 * Py) + (v3 * z1) + (v4 * z2)); // multiply by x here to deal with shift eccvm_opening.assert_equal(x * accumulated_result); - return true; + return (eccvm_opening.get_value() == (x * accumulated_result).get_value()); }; bool is_value_reconstructed = diff --git a/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.hpp b/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.hpp index 09b2c3bb25e..94a4e143536 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.hpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.hpp @@ -21,7 +21,7 @@ template class TranslatorRecursiveVerifier_ { using RelationSeparator = typename Flavor::RelationSeparator; using PairingPoints = std::array; using TranslationEvaluations = TranslationEvaluations_; - using Transcript = bb::BaseTranscript>; + using Transcript = typename Flavor::Transcript; using RelationParams = ::bb::RelationParameters; BF evaluation_input_x = 0; @@ -35,7 +35,9 @@ template class TranslatorRecursiveVerifier_ { RelationParams relation_parameters; - TranslatorRecursiveVerifier_(Builder* builder, const std::shared_ptr& native_verifier_key); + TranslatorRecursiveVerifier_(Builder* builder, + const std::shared_ptr& native_verifier_key, + const std::shared_ptr& transcript); void put_translation_data_in_relation_parameters(const BF& evaluation_input_x, const BF& batching_challenge_v, diff --git a/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.test.cpp b/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.test.cpp index 483188fa47f..12aebb7c93d 100644 --- a/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.test.cpp +++ b/barretenberg/cpp/src/barretenberg/translator_vm_recursion/translator_recursive_verifier.test.cpp @@ -72,15 +72,15 @@ template class TranslatorRecursiveTests : public ::te InnerProver prover{ circuit_builder, prover_transcript }; auto proof = prover.construct_proof(); - // TODO(https://github.com/AztecProtocol/barretenberg/issues/985): Insert the proof that serves as the content - // neeeded by the Translator verifier in the ECCVM proof. Unlike the native verifier where we can directly pass - // a transcript initialised with the correct information, in the recursive scenario the transcript is a circuit - // primitives that is initialised from the proof so we need the batching challenge contained in the proof. - proof.insert(proof.begin(), fake_inital_proof.begin(), fake_inital_proof.end()); + OuterBuilder outer_circuit; + + // Mock a previous verifier that would in reality be the ECCVM recursive verifier + StdlibProof stdlib_proof = bb::convert_proof_to_witness(&outer_circuit, fake_inital_proof); + auto transcript = std::make_shared(stdlib_proof); + transcript->template receive_from_prover("init"); auto verification_key = std::make_shared(prover.key); - OuterBuilder outer_circuit; - RecursiveVerifier verifier{ &outer_circuit, verification_key }; + RecursiveVerifier verifier{ &outer_circuit, verification_key, transcript }; auto pairing_points = verifier.verify_proof(proof); info("Recursive Verifier: num gates = ", outer_circuit.num_gates);