Skip to content

Commit

Permalink
feat: Separate aggregation protocol (#2736)
Browse files Browse the repository at this point in the history
This PR moves the Goblin ECC op queue transcript aggregation protocol
from the main Honk protocol to its own separate mini-protocol, referred
to as "Merge" (based on Zac's original Goblin doc). Zac pointed out that
this was likely the right approach once we go to incorporate folding.

This is also a necessary step for completing integration of ZeroMorph
(and deprecation of Gemini/Shplonk) because the univariate evaluation
claims related to this merge protocol would have otherwise needed to be
incorporated via Shplonk.

This work automatically resolves one of the issues previously described
in bberg [723](AztecProtocol/barretenberg#723)
related to the size of the transcript polynomials being tied to the size
of the present circuit.
  • Loading branch information
ledwards2225 authored Oct 10, 2023
1 parent 9c28008 commit ad16937
Show file tree
Hide file tree
Showing 14 changed files with 502 additions and 254 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,56 @@ class FullGoblinComposerTests : public ::testing::Test {
// Store the commitment data for use by the prover of the next circuit
op_queue->set_commitment_data(op_queue_commitments);
}

/**
* @brief Construct and a verify a Honk proof
*
*/
bool construct_and_verify_honk_proof(auto& composer, auto& builder)
{
auto instance = composer.create_instance(builder);
auto prover = composer.create_prover(instance);
auto verifier = composer.create_verifier(instance);
auto proof = prover.construct_proof();
bool verified = verifier.verify_proof(proof);

return verified;
}

/**
* @brief Construct and verify a Goblin ECC op queue merge proof
*
*/
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_proof = merge_prover.construct_proof();
bool verified = merge_verifier.verify_proof(merge_proof);

return verified;
}

/**
* @brief Construct and verify a Goblin ECC op queue merge proof
*
*/
bool construct_and_verify_eccvm_proof(auto& composer, auto& builder)
{
auto prover = composer.create_prover(builder);
auto proof = prover.construct_proof();
auto verifier = composer.create_verifier(builder);
bool verified = verifier.verify_proof(proof);

return verified;
}
};

/**
* @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 avoid complications with zero-commitments etc.
* with non-empty 'previous' data. This avoids complications with zero-commitments etc.
*
*/
TEST_F(FullGoblinComposerTests, SimpleCircuit)
Expand All @@ -124,29 +167,27 @@ TEST_F(FullGoblinComposerTests, SimpleCircuit)

generate_test_circuit(builder);

// The same composer is used to manage Honk and Merge prover/verifier
auto composer = GoblinUltraComposer();
auto instance = composer.create_instance(builder);
auto prover = composer.create_prover(instance);
auto verifier = composer.create_verifier(instance);
auto proof = prover.construct_proof();
bool verified = verifier.verify_proof(proof);
EXPECT_EQ(verified, true);

// Construct and verify Ultra Goblin Honk proof
auto honk_verified = construct_and_verify_honk_proof(composer, builder);
EXPECT_TRUE(honk_verified);

// Construct and verify op queue merge proof
auto merge_verified = construct_and_verify_merge_proof(composer, op_queue);
EXPECT_TRUE(merge_verified);
}

// Construct an ECCVM circuit then generate and verify its proof
{
// Instantiate an ECCVM builder with the vm ops stored in the op queue
auto builder = ECCVMBuilder(op_queue->raw_ops);

// // Can fiddle with one of the operands to trigger a failure
// builder.vm_operations[0].z1 *= 2;

// Construct and verify ECCVM proof
auto composer = ECCVMComposer();
auto prover = composer.create_prover(builder);
auto proof = prover.construct_proof();
auto verifier = composer.create_verifier(builder);
bool verified = verifier.verify_proof(proof);
ASSERT_TRUE(verified);
auto eccvm_verified = construct_and_verify_eccvm_proof(composer, builder);
EXPECT_TRUE(eccvm_verified);
}
}

Expand All @@ -168,13 +209,16 @@ TEST_F(FullGoblinComposerTests, SimpleCircuitFailureCase)

generate_test_circuit(builder);

// The same composer is used to manage Honk and Merge prover/verifier
auto composer = GoblinUltraComposer();
auto instance = composer.create_instance(builder);
auto prover = composer.create_prover(instance);
auto verifier = composer.create_verifier(instance);
auto proof = prover.construct_proof();
bool verified = verifier.verify_proof(proof);
EXPECT_EQ(verified, true);

// Construct and verify Ultra Goblin Honk proof
auto honk_verified = construct_and_verify_honk_proof(composer, builder);
EXPECT_TRUE(honk_verified);

// Construct and verify op queue merge proof
auto merge_verified = construct_and_verify_merge_proof(composer, op_queue);
EXPECT_TRUE(merge_verified);
}

// Construct an ECCVM circuit then generate and verify its proof
Expand All @@ -185,12 +229,10 @@ TEST_F(FullGoblinComposerTests, SimpleCircuitFailureCase)
// Fiddle with one of the operands to trigger a failure
builder.vm_operations[0].z1 += 1;

// Construct and verify ECCVM proof
auto composer = ECCVMComposer();
auto prover = composer.create_prover(builder);
auto proof = prover.construct_proof();
auto verifier = composer.create_verifier(builder);
bool verified = verifier.verify_proof(proof);
EXPECT_EQ(verified, false);
auto eccvm_verified = construct_and_verify_eccvm_proof(composer, builder);
EXPECT_FALSE(eccvm_verified);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,11 @@ class GoblinUltraHonkComposerTests : public ::testing::Test {
}

/**
* @brief Construct a goblin ultra circuit then generate a verify its proof
* @brief Construct and a verify a Honk proof
*
* @param op_queue
* @return auto
*/
bool construct_test_circuit_then_generate_and_verify_proof(auto& op_queue)
bool construct_and_verify_honk_proof(auto& composer, auto& builder)
{
auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue);

generate_test_circuit(builder);

auto composer = GoblinUltraComposer();
auto instance = composer.create_instance(builder);
auto prover = composer.create_prover(instance);
auto verifier = composer.create_verifier(instance);
Expand All @@ -76,6 +69,20 @@ class GoblinUltraHonkComposerTests : public ::testing::Test {

return verified;
}

/**
* @brief Construct and verify a Goblin ECC op queue merge proof
*
*/
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_proof = merge_prover.construct_proof();
bool verified = merge_verifier.verify_proof(merge_proof);

return verified;
}
};

/**
Expand All @@ -92,18 +99,27 @@ TEST_F(GoblinUltraHonkComposerTests, SingleCircuit)
// Add mock data to op queue to simulate interaction with a previous circuit
op_queue->populate_with_mock_initital_data();

// Construct a test circuit then generate and verify its proof
auto verified = construct_test_circuit_then_generate_and_verify_proof(op_queue);
auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue);

generate_test_circuit(builder);

EXPECT_EQ(verified, true);
auto composer = GoblinUltraComposer();

// Construct and verify Honk proof
auto honk_verified = construct_and_verify_honk_proof(composer, builder);
EXPECT_TRUE(honk_verified);

// Construct and verify Goblin ECC op queue Merge proof
auto merge_verified = construct_and_verify_merge_proof(composer, op_queue);
EXPECT_TRUE(merge_verified);
}

/**
* @brief Test proof construction/verification for a circuit with ECC op gates, public inputs, and basic arithmetic
* gates
* @brief Test Merge proof construction/verification for multiple circuits with ECC op gates, public inputs, and
* basic arithmetic gates
*
*/
TEST_F(GoblinUltraHonkComposerTests, MultipleCircuits)
TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsMergeOnly)
{
// Instantiate EccOpQueue. This will be shared across all circuits in the series
auto op_queue = std::make_shared<proof_system::ECCOpQueue>();
Expand All @@ -114,7 +130,75 @@ TEST_F(GoblinUltraHonkComposerTests, MultipleCircuits)
// Construct multiple test circuits that share an ECC op queue. Generate and verify a proof for each.
size_t NUM_CIRCUITS = 3;
for (size_t i = 0; i < NUM_CIRCUITS; ++i) {
construct_test_circuit_then_generate_and_verify_proof(op_queue);
auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue);

generate_test_circuit(builder);

auto composer = GoblinUltraComposer();

// Construct and verify Goblin ECC op queue Merge its proof
auto merge_verified = construct_and_verify_merge_proof(composer, op_queue);
EXPECT_TRUE(merge_verified);
}
}

/**
* @brief Test Honk proof construction/verification for multiple circuits with ECC op gates, public inputs, and
* basic arithmetic gates
*
*/
TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsHonkOnly)
{
// Instantiate EccOpQueue. This will be shared across all circuits in the series
auto op_queue = std::make_shared<proof_system::ECCOpQueue>();

// Add mock data to op queue to simulate interaction with a previous circuit
op_queue->populate_with_mock_initital_data();

// Construct multiple test circuits that share an ECC op queue. Generate and verify a proof for each.
size_t NUM_CIRCUITS = 3;
for (size_t i = 0; i < NUM_CIRCUITS; ++i) {
auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue);

generate_test_circuit(builder);

auto composer = GoblinUltraComposer();

// Construct and verify Honk proof
auto honk_verified = construct_and_verify_honk_proof(composer, builder);
EXPECT_TRUE(honk_verified);
}
}

/**
* @brief Test Honk and Merge proof construction/verification for multiple circuits with ECC op gates, public inputs,
* and basic arithmetic gates
*
*/
TEST_F(GoblinUltraHonkComposerTests, MultipleCircuitsHonkAndMerge)
{
// Instantiate EccOpQueue. This will be shared across all circuits in the series
auto op_queue = std::make_shared<proof_system::ECCOpQueue>();

// Add mock data to op queue to simulate interaction with a previous circuit
op_queue->populate_with_mock_initital_data();

// Construct multiple test circuits that share an ECC op queue. Generate and verify a proof for each.
size_t NUM_CIRCUITS = 3;
for (size_t i = 0; i < NUM_CIRCUITS; ++i) {
auto builder = proof_system::GoblinUltraCircuitBuilder(op_queue);

generate_test_circuit(builder);

auto composer = GoblinUltraComposer();

// Construct and verify Honk proof
auto honk_verified = construct_and_verify_honk_proof(composer, builder);
EXPECT_TRUE(honk_verified);

// Construct and verify Goblin ECC op queue Merge its proof
auto merge_verified = construct_and_verify_merge_proof(composer, op_queue);
EXPECT_TRUE(merge_verified);
}

// Compute the commitments to the aggregate op queue directly and check that they match those that were computed
Expand Down
30 changes: 30 additions & 0 deletions barretenberg/cpp/src/barretenberg/honk/composer/ultra_composer.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once
#include "barretenberg/honk/instance/prover_instance.hpp"
#include "barretenberg/honk/proof_system/goblin_merge/merge_prover.hpp"
#include "barretenberg/honk/proof_system/goblin_merge/merge_verifier.hpp"
#include "barretenberg/honk/proof_system/protogalaxy_prover.hpp"
#include "barretenberg/honk/proof_system/protogalaxy_verifier.hpp"
#include "barretenberg/honk/proof_system/ultra_prover.hpp"
Expand Down Expand Up @@ -72,6 +74,34 @@ template <UltraFlavor Flavor> class UltraComposer_ {
UltraProver_<Flavor> create_prover(std::shared_ptr<Instance>);
UltraVerifier_<Flavor> create_verifier(std::shared_ptr<Instance>);

/**
* @brief Create Prover for Goblin ECC op queue merge protocol
*
* @param op_queue
* @return MergeProver_<Flavor>
*/
MergeProver_<Flavor> create_merge_prover(std::shared_ptr<ECCOpQueue> op_queue)
{
// Store the previous aggregate op queue size and update the current one
op_queue->set_size_data();
// Merge requires a commitment key with size equal to that of the current op queue transcript T_i since the
// shift of the current contribution t_i will be of degree equal to deg(T_i)
auto commitment_key = compute_commitment_key(op_queue->get_current_size());
return MergeProver_<Flavor>(commitment_key, op_queue);
}

/**
* @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_<Flavor>
*/
MergeVerifier_<Flavor> create_merge_verifier(size_t size)
{
auto pcs_verification_key = std::make_unique<VerifierCommitmentKey>(size, crs_factory_);
return MergeVerifier_<Flavor>(std::move(pcs_verification_key));
}

ProtoGalaxyProver_<ProverInstances> create_folding_prover(std::vector<std::shared_ptr<Instance>> instances)
{
ProverInstances insts(instances);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,6 @@ class GoblinUltra {

size_t num_ecc_op_gates; // needed to determine public input offset

std::shared_ptr<ECCOpQueue> op_queue;

// The plookup wires that store plookup read data.
std::array<PolynomialHandle, 3> get_table_column_wires() { return { w_l, w_r, w_o }; };
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ std::shared_ptr<typename Flavor::ProvingKey> ProverInstance_<Flavor>::compute_pr

if constexpr (IsGoblinFlavor<Flavor>) {
proving_key->num_ecc_op_gates = num_ecc_op_gates;
proving_key->op_queue = circuit.op_queue;
}

return proving_key;
Expand Down
14 changes: 14 additions & 0 deletions barretenberg/cpp/src/barretenberg/honk/pcs/claim.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ template <typename Curve> class OpeningPair {
bool operator==(const OpeningPair& other) const = default;
};

/**
* @brief Polynomial p and an opening pair (r,v) such that p(r) = v
*
* @tparam Params for the given commitment scheme
*/
template <typename Curve> class ProverOpeningClaim {
using Fr = typename Curve::ScalarField;
using Polynomial = barretenberg::Polynomial<Fr>;

public:
Polynomial polynomial; // p
OpeningPair<Curve> opening_pair; // (challenge r, evaluation v = p(r))
};

/**
* @brief Unverified claim (C,r,v) for some witness polynomial p(X) such that
* - C = Commit(p(X))
Expand Down
Loading

0 comments on commit ad16937

Please sign in to comment.