Skip to content

Commit

Permalink
feat: merge recursive verifier (AztecProtocol#3588)
Browse files Browse the repository at this point in the history
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 AztecProtocol/barretenberg#797
  • Loading branch information
ledwards2225 authored Dec 7, 2023
1 parent fe849e3 commit cdd9259
Show file tree
Hide file tree
Showing 16 changed files with 296 additions and 110 deletions.
2 changes: 1 addition & 1 deletion barretenberg/cpp/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 5 additions & 6 deletions barretenberg/cpp/src/barretenberg/commitment_schemes/kzg/kzg.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,15 @@ template <typename Curve> class KZG {
auto quotient_commitment = verifier_transcript->template receive_from_prover<Commitment>("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<GroupElement> commitments = { claim.commitment, quotient_commitment };
std::vector<Fr> scalars = { one, claim.opening_pair.challenge };
std::vector<GroupElement> commitments = { claim.commitment,
quotient_commitment,
GroupElement::one(builder) };
std::vector<Fr> 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;
Expand Down
2 changes: 1 addition & 1 deletion barretenberg/cpp/src/barretenberg/goblin/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
barretenberg_module(goblin ultra_honk eccvm translator_vm transcript)
barretenberg_module(goblin stdlib_recursion ultra_honk eccvm translator_vm)

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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<Curve>;
using OpQueue = proof_system::ECCOpQueue;
using GoblinUltraBuilder = proof_system::GoblinUltraCircuitBuilder;
using ECCVMFlavor = flavor::ECCVM;
using ECCVMBuilder = proof_system::ECCVMCircuitBuilder<ECCVMFlavor>;
Expand All @@ -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_<GoblinUltraBuilder>;
using RecursiveVerifier = proof_system::plonk::stdlib::recursion::honk::UltraRecursiveVerifier_<RecursiveFlavor>;
using Goblin = barretenberg::Goblin;
using KernelInput = Goblin::AccumulationOutput;
using UltraVerifier = UltraVerifier_<flavor::GoblinUltra>;

/**
* @brief Construct a mock kernel circuit
Expand All @@ -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);
}
};

Expand All @@ -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
Expand All @@ -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.
Expand Down
35 changes: 31 additions & 4 deletions barretenberg/cpp/src/barretenberg/goblin/goblin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -24,6 +25,7 @@ class Goblin {
};

struct Proof {
HonkProof merge_proof;
HonkProof eccvm_proof;
HonkProof translator_proof;
TranslationEvaluations translation_evaluations;
Expand All @@ -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_<GoblinUltraCircuitBuilder>;
using MergeVerifier = proof_system::honk::MergeVerifier_<proof_system::honk::flavor::GoblinUltra>;

std::shared_ptr<OpQueue> op_queue = std::make_shared<OpQueue>();

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<ECCVMBuilder> eccvm_builder;
Expand All @@ -59,23 +69,35 @@ 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 };
};

Proof prove()
{
Proof proof;

proof.merge_proof = std::move(merge_proof);

eccvm_builder = std::make_unique<ECCVMBuilder>(op_queue);
eccvm_composer = std::make_unique<ECCVMComposer>();
auto eccvm_prover = eccvm_composer->create_prover(*eccvm_builder);
Expand All @@ -87,11 +109,15 @@ class Goblin {
translator_composer = std::make_unique<TranslatorComposer>();
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);

Expand All @@ -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
9 changes: 2 additions & 7 deletions barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include "barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp"

namespace proof_system::plonk::stdlib::recursion::goblin {

template <typename CircuitBuilder>
MergeRecursiveVerifier_<CircuitBuilder>::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<typename Flavor::GroupElement, 2> Inputs to final pairing
*/
template <typename CircuitBuilder>
std::array<typename bn254<CircuitBuilder>::Element, 2> MergeRecursiveVerifier_<CircuitBuilder>::verify_proof(
const plonk::proof& proof)
{
transcript = std::make_shared<Transcript>(builder, proof.proof_data);

// Receive commitments [t_i^{shift}], [T_{i-1}], and [T_i]
std::array<Commitment, NUM_WIRES> C_T_prev;
std::array<Commitment, NUM_WIRES> C_t_shift;
std::array<Commitment, NUM_WIRES> C_T_current;
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
C_T_prev[idx] = transcript->template receive_from_prover<Commitment>("T_PREV_" + std::to_string(idx + 1));
C_t_shift[idx] = transcript->template receive_from_prover<Commitment>("t_SHIFT_" + std::to_string(idx + 1));
C_T_current[idx] = transcript->template receive_from_prover<Commitment>("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<FF, NUM_WIRES> T_prev_evals;
std::array<FF, NUM_WIRES> t_shift_evals;
std::array<FF, NUM_WIRES> T_current_evals;
std::vector<OpeningClaim> opening_claims;
for (size_t idx = 0; idx < NUM_WIRES; ++idx) {
T_prev_evals[idx] = transcript->template receive_from_prover<FF>("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<FF>("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<FF>("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<FF> scalars;
std::vector<Commitment> 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_<GoblinUltraCircuitBuilder>;

} // namespace proof_system::plonk::stdlib::recursion::goblin
Loading

0 comments on commit cdd9259

Please sign in to comment.