Skip to content

Commit

Permalink
feat: Encapsulated Goblin (#3524)
Browse files Browse the repository at this point in the history
Adds a class to encapsulate Goblin. Uses this to thread a single
transcript through the two VMs for proper challenge generation. The
Goblin class has an interface via function: `accumulate` (construct a
proof and a merge proof), `prove` (run the VM proofs) and `verify` (a
testing function to natively verify those proofs. Rewrites the Goblin
tests accordingly. Adds tests that use recursive verification of Honk
proofs. When we have recursive merge verification we will have something
worth measuring the assess the performance of Goblin proving.

Closes AztecProtocol/barretenberg#785 
Closes AztecProtocol/barretenberg#786

---------

Co-authored-by: codygunton <codygunton@gmail.com>
  • Loading branch information
ledwards2225 and codygunton authored Dec 6, 2023
1 parent 0e2db8b commit 2f08423
Show file tree
Hide file tree
Showing 24 changed files with 406 additions and 198 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ void construct_proof_with_specified_num_iterations(
Composer composer;

for (auto _ : state) {
// Constuct circuit and prover; don't include this part in measurement
// Construct circuit and prover; don't include this part in measurement
state.PauseTiming();
auto prover = get_prover(composer, test_circuit_function, num_iterations);
state.ResumeTiming();
Expand Down
11 changes: 7 additions & 4 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,31 +345,34 @@ template <ECCVMFlavor Flavor> void ECCVMProver_<Flavor>::execute_transcript_cons
transcript->send_to_verifier("Translation:hack_evaluation", hack.evaluate(evaluation_challenge_x));

// Get another challenge for batching the univariate claims
FF batching_challenge = transcript->get_challenge("Translation:batching_challenge");
FF ipa_batching_challenge = transcript->get_challenge("Translation:ipa_batching_challenge");

// Collect the polynomials and evaluations to be batched
RefArray univariate_polynomials{ key->transcript_op, key->transcript_Px, key->transcript_Py,
key->transcript_z1, key->transcript_z2, hack };
std::array<FF, univariate_polynomials.size()> univariate_evaluations;

// Constuct the batched polynomial and batched evaluation
// Construct the batched polynomial and batched evaluation
Polynomial batched_univariate{ key->circuit_size };
FF batched_evaluation{ 0 };
auto batching_scalar = FF(1);
for (auto [polynomial, eval] : zip_view(univariate_polynomials, univariate_evaluations)) {
batched_univariate.add_scaled(polynomial, batching_scalar);
batched_evaluation += eval * batching_scalar;
batching_scalar *= batching_challenge;
batching_scalar *= ipa_batching_challenge;
}

// Compute a proof for the batched univariate opening
PCS::compute_opening_proof(
commitment_key, { evaluation_challenge_x, batched_evaluation }, batched_univariate, transcript);

// Get another challenge for batching the univariate claims
translation_batching_challenge_v = transcript->get_challenge("Translation:batching_challenge");
}

template <ECCVMFlavor Flavor> plonk::proof& ECCVMProver_<Flavor>::export_proof()
{
proof.proof_data = transcript->proof_data;
proof.proof_data = transcript->export_proof();
return proof;
}

Expand Down
1 change: 1 addition & 0 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ template <ECCVMFlavor Flavor> class ECCVMProver_ {
Polynomial quotient_W;

FF evaluation_challenge_x;
FF translation_batching_challenge_v; // to be rederived by the translator verifier

sumcheck::SumcheckOutput<Flavor> sumcheck_output;
pcs::gemini::ProverOutput<Curve> gemini_output;
Expand Down
9 changes: 5 additions & 4 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,17 @@ template <typename Flavor> bool ECCVMVerifier_<Flavor>::verify_proof(const plonk
transcript->template receive_from_prover<FF>("Translation:hack_evaluation")
};

FF batching_challenge = transcript->get_challenge("Translation:batching_challenge");
// Get another challenge for batching the univariate claims
FF ipa_batching_challenge = transcript->get_challenge("Translation:ipa_batching_challenge");

// Constuct batched commitment and batched evaluation
// Construct batched commitment and batched evaluation
auto batched_commitment = transcript_commitments[0];
auto batched_transcript_eval = transcript_evaluations[0];
auto batching_scalar = batching_challenge;
auto batching_scalar = ipa_batching_challenge;
for (size_t idx = 1; idx < transcript_commitments.size(); ++idx) {
batched_commitment = batched_commitment + transcript_commitments[idx] * batching_scalar;
batched_transcript_eval += batching_scalar * transcript_evaluations[idx];
batching_scalar *= batching_challenge;
batching_scalar *= ipa_batching_challenge;
}

// Construct and verify batched opening claim
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ template <typename BuilderType> class GoblinUltraRecursive_ {
* @param builder
* @param native_key Native verification key from which to extract the precomputed commitments
*/
VerificationKey(CircuitBuilder* builder, std::shared_ptr<NativeVerificationKey> native_key)
VerificationKey(CircuitBuilder* builder, const std::shared_ptr<NativeVerificationKey>& native_key)
: VerificationKey_<GoblinUltra::PrecomputedEntities<Commitment>>(native_key->circuit_size,
native_key->num_public_inputs)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ template <typename BuilderType> class UltraRecursive_ {
* @param builder
* @param native_key Native verification key from which to extract the precomputed commitments
*/
VerificationKey(CircuitBuilder* builder, std::shared_ptr<NativeVerificationKey> native_key)
VerificationKey(CircuitBuilder* builder, const std::shared_ptr<NativeVerificationKey>& native_key)
: VerificationKey_<PrecomputedEntities<Commitment>>(native_key->circuit_size, native_key->num_public_inputs)
{
this->q_m = Commitment::from_witness(builder, native_key->q_m);
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)
barretenberg_module(goblin ultra_honk eccvm translator_vm transcript)
163 changes: 17 additions & 146 deletions barretenberg/cpp/src/barretenberg/goblin/full_goblin_composer.test.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#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"
Expand All @@ -8,14 +10,11 @@

#include <gtest/gtest.h>

using namespace barretenberg;
using namespace proof_system::honk;

namespace test_full_goblin_composer {

namespace {
auto& engine = numeric::random::get_debug_engine();
}

class FullGoblinComposerTests : public ::testing::Test {
protected:
static void SetUpTestSuite()
Expand All @@ -30,110 +29,11 @@ class FullGoblinComposerTests : public ::testing::Test {
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>;
using ECCVMComposer = ECCVMComposer_<ECCVMFlavor>;

static constexpr size_t NUM_OP_QUEUE_COLUMNS = flavor::GoblinUltra::NUM_WIRES;

/**
* @brief Generate a simple test circuit with some ECC op gates and conventional arithmetic gates
*
* @param builder
*/
static void generate_test_circuit(proof_system::GoblinUltraCircuitBuilder& builder)
{
// Add some arbitrary ecc op gates
for (size_t i = 0; i < 3; ++i) {
auto point = Point::random_element();
auto scalar = FF::random_element();
builder.queue_ecc_add_accum(point);
builder.queue_ecc_mul_accum(point, scalar);
}
// queues the result of the preceding ECC
builder.queue_ecc_eq(); // should be eq and reset

// Add some conventional gates that utilize public inputs
for (size_t i = 0; i < 10; ++i) {
FF a = FF::random_element();
FF b = FF::random_element();
FF c = FF::random_element();
FF d = a + b + c;
uint32_t a_idx = builder.add_public_variable(a);
uint32_t b_idx = builder.add_variable(b);
uint32_t c_idx = builder.add_variable(c);
uint32_t d_idx = builder.add_variable(d);

builder.create_big_add_gate({ a_idx, b_idx, c_idx, d_idx, FF(1), FF(1), FF(1), FF(-1), FF(0) });
}
}

/**
* @brief Mock the interactions of a simple curcuit with the op_queue
* @details The transcript aggregation protocol in the Goblin proof system can not yet support an empty "previous
* transcript" (see issue #723). This function mocks the interactions with the op queue of a fictional "first"
* circuit. This way, when we go to generate a proof over our first "real" circuit, the transcript aggregation
* protocol can proceed nominally. The mock data is valid in the sense that it can be processed by all stages of
* Goblin as if it came from a genuine circuit.
*
* @todo WOKTODO: this is a zero commitments issue
*
* @param op_queue
*/
static void perform_op_queue_interactions_for_mock_first_circuit(
std::shared_ptr<proof_system::ECCOpQueue>& op_queue)
{
proof_system::GoblinUltraCircuitBuilder builder{ op_queue };

// Add a mul accum op and an equality op
auto point = Point::one() * FF::random_element();
auto scalar = FF::random_element();
builder.queue_ecc_mul_accum(point, scalar);
builder.queue_ecc_eq();

op_queue->set_size_data();

// Manually compute the op queue transcript commitments (which would normally be done by the prover)
auto crs_factory_ = barretenberg::srs::get_crs_factory();
auto commitment_key = CommitmentKey(op_queue->get_current_size(), crs_factory_);
std::array<Point, NUM_OP_QUEUE_COLUMNS> op_queue_commitments;
size_t idx = 0;
for (auto& entry : op_queue->get_aggregate_transcript()) {
op_queue_commitments[idx++] = commitment_key.commit(entry);
}
// 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
*
*/
static bool construct_and_verify_honk_proof(GoblinUltraComposer& composer,
proof_system::GoblinUltraCircuitBuilder& 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
*
*/
static bool construct_and_verify_merge_proof(GoblinUltraComposer& composer, std::shared_ptr<OpQueue>& op_queue)
{
auto merge_prover = composer.create_merge_prover(op_queue);
auto merge_verifier = composer.create_merge_verifier(/*srs_size=*/10);
auto merge_proof = merge_prover.construct_proof();
bool verified = merge_verifier.verify_proof(merge_proof);

return verified;
}
using KernelInput = Goblin::AccumulationOutput;
};

/**
Expand All @@ -145,52 +45,23 @@ class FullGoblinComposerTests : public ::testing::Test {
*/
TEST_F(FullGoblinComposerTests, SimpleCircuit)
{
auto op_queue = std::make_shared<proof_system::ECCOpQueue>();

// Add mock data to op queue to simulate interaction with a "first" circuit
perform_op_queue_interactions_for_mock_first_circuit(op_queue);
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 = 3;
size_t NUM_CIRCUITS = 2;
for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) {
proof_system::GoblinUltraCircuitBuilder builder{ op_queue };

generate_test_circuit(builder);

// The same composer is used to manage Honk and Merge prover/verifier
proof_system::honk::GoblinUltraComposer composer;

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

// Construct and verify op queue merge proof
bool merge_verified = construct_and_verify_merge_proof(composer, op_queue);
EXPECT_TRUE(merge_verified);
GoblinUltraBuilder circuit_builder{ goblin.op_queue };
GoblinTestingUtils::construct_arithmetic_circuit(circuit_builder);
kernel_input = goblin.accumulate(circuit_builder);
}

// Execute the ECCVM
// TODO(https://github.com/AztecProtocol/barretenberg/issues/785) Properly initialize transcript
auto eccvm_builder = ECCVMBuilder(op_queue);
auto eccvm_composer = ECCVMComposer();
auto eccvm_prover = eccvm_composer.create_prover(eccvm_builder);
auto eccvm_verifier = eccvm_composer.create_verifier(eccvm_builder);
auto eccvm_proof = eccvm_prover.construct_proof();
bool eccvm_verified = eccvm_verifier.verify_proof(eccvm_proof);
EXPECT_TRUE(eccvm_verified);

// Execute the Translator
// TODO(https://github.com/AztecProtocol/barretenberg/issues/786) Properly derive batching_challenge
auto batching_challenge = Fbase::random_element();
auto evaluation_input = eccvm_prover.evaluation_challenge_x;
proof_system::GoblinTranslatorCircuitBuilder translator_builder{ batching_challenge, evaluation_input, op_queue };
GoblinTranslatorComposer translator_composer;
GoblinTranslatorProver translator_prover = translator_composer.create_prover(translator_builder);
GoblinTranslatorVerifier translator_verifier = translator_composer.create_verifier(translator_builder);
proof_system::plonk::proof translator_proof = translator_prover.construct_proof();
bool accumulator_construction_verified = translator_verifier.verify_proof(translator_proof);
bool translation_verified = translator_verifier.verify_translation(eccvm_prover.translation_evaluations);
EXPECT_TRUE(accumulator_construction_verified && translation_verified);
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
Loading

0 comments on commit 2f08423

Please sign in to comment.