Skip to content

Commit

Permalink
feat: Goblin Recursive Verifier (#6778)
Browse files Browse the repository at this point in the history
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 <l.edwards.d@gmail.com>
  • Loading branch information
maramihali and ledwards2225 authored May 31, 2024
1 parent a98d30b commit 53d0d55
Show file tree
Hide file tree
Showing 11 changed files with 232 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ template <typename Curve_> 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<uint32_t>(poly_length_var.get_value());
Expand Down
10 changes: 10 additions & 0 deletions barretenberg/cpp/src/barretenberg/goblin/goblin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ class GoblinVerifier {
using TranslatorVerificationKey = bb::TranslatorFlavor::VerificationKey;
using MergeVerifier = bb::MergeVerifier_<MegaFlavor>;

struct VerifierInput {
std::shared_ptr<ECCVMVerificationKey> eccvm_verification_key;
std::shared_ptr<TranslatorVerificationKey> translator_verification_key;
};

private:
std::shared_ptr<ECCVMVerificationKey> eccvm_verification_key;
std::shared_ptr<TranslatorVerificationKey> translator_verification_key;
Expand All @@ -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)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ barretenberg_module(
ultra_honk
stdlib_poseidon2
protogalaxy
goblin
translator_vm_recursion
eccvm_recursion
client_ivc
)
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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_<Builder>;

using TranslatorFlavor = TranslatorRecursiveFlavor_<Builder>;
using TranslatorVerifier = TranslatorRecursiveVerifier_<TranslatorFlavor>;
using TranslationEvaluations = TranslatorVerifier::TranslationEvaluations;
using TranslatorBF = TranslatorFlavor::BF;

using ECCVMFlavor = ECCVMRecursiveFlavor_<Builder>;
using ECCVMVerifier = ECCVMRecursiveVerifier_<ECCVMFlavor>;

// 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
Original file line number Diff line number Diff line change
@@ -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_<OuterFlavor>;
using OuterVerifier = UltraVerifier_<OuterFlavor>;
using OuterProverInstance = ProverInstance_<OuterFlavor>;

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<ECCOpQueue> 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<ECCVMVK>(goblin.get_eccvm_proving_key()),
std::make_shared<TranslatorVK>(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<OuterProverInstance>(builder);
OuterProver prover(instance);
auto verification_key = std::make_shared<typename OuterFlavor::VerificationKey>(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
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -16,10 +17,9 @@ namespace bb::stdlib::recursion::goblin {
*
* @tparam Builder
*/
class RecursiveMergeVerifierTest : public testing::Test {
template <class RecursiveBuilder> class RecursiveMergeVerifierTest : public testing::Test {

// Types for recursive verifier circuit
using RecursiveBuilder = MegaCircuitBuilder;
using RecursiveMergeVerifier = MergeRecursiveVerifier_<RecursiveBuilder>;

// Define types relevant for inner circuit
Expand Down Expand Up @@ -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<InnerProverInstance>(outer_circuit);
MegaProver prover(instance);
auto verification_key = std::make_shared<MegaFlavor::VerificationKey>(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<MegaCircuitBuilder, UltraCircuitBuilder>;
using Builders = testing::Types<MegaCircuitBuilder>;

TYPED_TEST_SUITE(RecursiveMergeVerifierTest, Builders);

TYPED_TEST(RecursiveMergeVerifierTest, SingleRecursiveVerification)
{
RecursiveMergeVerifierTest::test_recursive_merge_verification();
TestFixture::test_recursive_merge_verification();
};

} // namespace bb::stdlib::recursion::goblin
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,6 @@ template <typename Builder> bb::fr field_t<Builder>::get_value() const
template <typename Builder> bool_t<Builder> field_t<Builder>::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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ namespace bb {

template <typename Flavor>
TranslatorRecursiveVerifier_<Flavor>::TranslatorRecursiveVerifier_(
Builder* builder, const std::shared_ptr<NativeVerificationKey>& native_verifier_key)
Builder* builder,
const std::shared_ptr<NativeVerificationKey>& native_verifier_key,
const std::shared_ptr<Transcript>& transcript)
: key(std::make_shared<VerificationKey>(builder, native_verifier_key))
, transcript(transcript)
, builder(builder)
{}

Expand Down Expand Up @@ -60,15 +63,10 @@ std::array<typename Flavor::GroupElement, 2> TranslatorRecursiveVerifier_<Flavor
using ZeroMorph = ::bb::ZeroMorphVerifier_<PCS>;
using VerifierCommitments = typename Flavor::VerifierCommitments;
using CommitmentLabels = typename Flavor::CommitmentLabels;
using Transcript = typename Flavor::Transcript;

StdlibProof<Builder> stdlib_proof = bb::convert_proof_to_witness(builder, proof);
transcript = std::make_shared<Transcript>(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<BF>("init");
batching_challenge_v = transcript->template get_challenge<BF>("Translation:batching_challenge");

VerifierCommitments commitments{ key };
Expand Down Expand Up @@ -125,7 +123,6 @@ std::array<typename Flavor::GroupElement, 2> TranslatorRecursiveVerifier_<Flavor
return pairing_points;
}

// this we verify outside translator
template <typename Flavor>
bool TranslatorRecursiveVerifier_<Flavor>::verify_translation(
const TranslationEvaluations_<typename Flavor::BF, typename Flavor::FF>& translation_evaluations)
Expand All @@ -152,7 +149,7 @@ bool TranslatorRecursiveVerifier_<Flavor>::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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ template <typename Flavor> class TranslatorRecursiveVerifier_ {
using RelationSeparator = typename Flavor::RelationSeparator;
using PairingPoints = std::array<GroupElement, 2>;
using TranslationEvaluations = TranslationEvaluations_<BF, FF>;
using Transcript = bb::BaseTranscript<bb::stdlib::recursion::honk::StdlibTranscriptParams<Builder>>;
using Transcript = typename Flavor::Transcript;
using RelationParams = ::bb::RelationParameters<FF>;

BF evaluation_input_x = 0;
Expand All @@ -35,7 +35,9 @@ template <typename Flavor> class TranslatorRecursiveVerifier_ {

RelationParams relation_parameters;

TranslatorRecursiveVerifier_(Builder* builder, const std::shared_ptr<NativeVerificationKey>& native_verifier_key);
TranslatorRecursiveVerifier_(Builder* builder,
const std::shared_ptr<NativeVerificationKey>& native_verifier_key,
const std::shared_ptr<Transcript>& transcript);

void put_translation_data_in_relation_parameters(const BF& evaluation_input_x,
const BF& batching_challenge_v,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ template <typename RecursiveFlavor> 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<OuterBuilder> stdlib_proof = bb::convert_proof_to_witness(&outer_circuit, fake_inital_proof);
auto transcript = std::make_shared<typename RecursiveFlavor::Transcript>(stdlib_proof);
transcript->template receive_from_prover<typename RecursiveFlavor::BF>("init");

auto verification_key = std::make_shared<typename InnerFlavor::VerificationKey>(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);

Expand Down

0 comments on commit 53d0d55

Please sign in to comment.