Skip to content

Commit

Permalink
refactor: interaction for a mock first circuit handled inside the `Ec…
Browse files Browse the repository at this point in the history
…cOpQueue` (#4854)

Closes AztecProtocol/barretenberg#723.

Because of issues with handling zero-commitments, we need to populate
the `EccOpQueue` with some mock ops from a first circuit in order for
the first merge prover to work without problems. This was done
client-side which required manually calling either
`GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit`
or `op_queue->populate_with_mock_initital_data` (two functions for the
same purpose) everywhere goblin and the merge protocol is used/tested.
This PR ensures only
`GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit`
is used across the codebase since it's the one updating both `ultra_ops`
and `raw_ops`, removing the other function, and isolates mocking inside
the Goblin class so it doesn't have to be done in benchmarks.

Note: tried to isolate it inside EccOpQueue, which is possible but then
requires us to have an op_offset (from which non-mocked operations
start) which complicates some code and tests unnecessarily.
  • Loading branch information
maramihali committed Mar 12, 2024
1 parent e68b56a commit d9cbdc8
Show file tree
Hide file tree
Showing 13 changed files with 68 additions and 122 deletions.
28 changes: 14 additions & 14 deletions barretenberg/cpp/src/barretenberg/bb/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ bool proveAndVerify(const std::string& bytecodePath, const std::string& witnessP
*/
bool accumulateAndVerifyGoblin(const std::string& bytecodePath, const std::string& witnessPath)
{
// TODO(https://github.com/AztecProtocol/barretenberg/issues/811): Don't hardcode dyadic circuit size. Currently set
// to max circuit size present in acir tests suite.
size_t hardcoded_bn254_dyadic_size_hack = 1 << 19;
init_bn254_crs(hardcoded_bn254_dyadic_size_hack);
size_t hardcoded_grumpkin_dyadic_size_hack = 1 << 10; // For eccvm only
init_grumpkin_crs(hardcoded_grumpkin_dyadic_size_hack);

// Populate the acir constraint system and witness from gzipped data
auto constraint_system = get_constraint_system(bytecodePath);
auto witness = get_witness(witnessPath);
Expand All @@ -148,13 +155,6 @@ bool accumulateAndVerifyGoblin(const std::string& bytecodePath, const std::strin
acir_proofs::GoblinAcirComposer acir_composer;
acir_composer.create_circuit(constraint_system, witness);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/811): Don't hardcode dyadic circuit size. Currently set
// to max circuit size present in acir tests suite.
size_t hardcoded_bn254_dyadic_size_hack = 1 << 19;
init_bn254_crs(hardcoded_bn254_dyadic_size_hack);
size_t hardcoded_grumpkin_dyadic_size_hack = 1 << 10; // For eccvm only
init_grumpkin_crs(hardcoded_grumpkin_dyadic_size_hack);

// Call accumulate to generate a GoblinUltraHonk proof
auto proof = acir_composer.accumulate();

Expand All @@ -179,6 +179,13 @@ bool accumulateAndVerifyGoblin(const std::string& bytecodePath, const std::strin
*/
bool proveAndVerifyGoblin(const std::string& bytecodePath, const std::string& witnessPath)
{
// TODO(https://github.com/AztecProtocol/barretenberg/issues/811): Don't hardcode dyadic circuit size. Currently set
// to max circuit size present in acir tests suite.
size_t hardcoded_bn254_dyadic_size_hack = 1 << 19;
init_bn254_crs(hardcoded_bn254_dyadic_size_hack);
size_t hardcoded_grumpkin_dyadic_size_hack = 1 << 10; // For eccvm only
init_grumpkin_crs(hardcoded_grumpkin_dyadic_size_hack);

// Populate the acir constraint system and witness from gzipped data
auto constraint_system = get_constraint_system(bytecodePath);
auto witness = get_witness(witnessPath);
Expand All @@ -187,13 +194,6 @@ bool proveAndVerifyGoblin(const std::string& bytecodePath, const std::string& wi
acir_proofs::GoblinAcirComposer acir_composer;
acir_composer.create_circuit(constraint_system, witness);

// TODO(https://github.com/AztecProtocol/barretenberg/issues/811): Don't hardcode dyadic circuit size. Currently set
// to max circuit size present in acir tests suite.
size_t hardcoded_bn254_dyadic_size_hack = 1 << 19;
init_bn254_crs(hardcoded_bn254_dyadic_size_hack);
size_t hardcoded_grumpkin_dyadic_size_hack = 1 << 10; // For eccvm only
init_grumpkin_crs(hardcoded_grumpkin_dyadic_size_hack);

// Generate a GoblinUltraHonk proof and a full Goblin proof
auto proof = acir_composer.accumulate_and_prove();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ class GoblinBench : public benchmark::Fixture {
// Construct and accumulate the mock kernel circuit
// Note: in first round, kernel_accum is empty since there is no previous kernel to recursively verify
GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue };
GoblinMockCircuits::construct_mock_recursion_kernel_circuit(circuit_builder, function_accum, kernel_accum);
GoblinMockCircuits::construct_mock_recursion_kernel_circuit(
circuit_builder,
{ function_accum.proof, function_accum.verification_key },
{ kernel_accum.proof, kernel_accum.verification_key });
kernel_accum = goblin.accumulate(circuit_builder);
}
}
Expand All @@ -62,10 +65,6 @@ BENCHMARK_DEFINE_F(GoblinBench, GoblinFull)(benchmark::State& state)
{
Goblin goblin;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/723): Simply populate the OpQueue with some data
// and corresponding commitments so the merge protocol has "prev" data into which it can accumulate
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue);

for (auto _ : state) {
BB_REPORT_OP_COUNT_IN_BENCH(state);
// Perform a specified number of iterations of function/kernel accumulation
Expand All @@ -84,9 +83,6 @@ BENCHMARK_DEFINE_F(GoblinBench, GoblinAccumulate)(benchmark::State& state)
{
Goblin goblin;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/723)
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue);

// Perform a specified number of iterations of function/kernel accumulation
for (auto _ : state) {
perform_goblin_accumulation_rounds(state, goblin);
Expand All @@ -101,9 +97,6 @@ BENCHMARK_DEFINE_F(GoblinBench, GoblinECCVMProve)(benchmark::State& state)
{
Goblin goblin;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/723)
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue);

// Perform a specified number of iterations of function/kernel accumulation
perform_goblin_accumulation_rounds(state, goblin);

Expand All @@ -121,9 +114,6 @@ BENCHMARK_DEFINE_F(GoblinBench, GoblinTranslatorProve)(benchmark::State& state)
{
Goblin goblin;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/723)
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue);

// Perform a specified number of iterations of function/kernel accumulation
perform_goblin_accumulation_rounds(state, goblin);

Expand Down
15 changes: 5 additions & 10 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@

namespace bb {

ClientIVC::ClientIVC()
{
// TODO(https://github.com/AztecProtocol/barretenberg/issues/723):
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue);
}

/**
* @brief Initialize the IVC with a first circuit
* @details Initializes the accumulator and performs the initial goblin merge
Expand Down Expand Up @@ -83,6 +77,9 @@ HonkProof ClientIVC::decider_prove() const
* recursive merge verifier), initial kernel verification key (with recursive merge verifier appended, no previous
* kernel to fold), "full" kernel verification key( two recursive folding verifiers and merge verifier).
*
* TODO(https://github.com/AztecProtocol/barretenberg/issues/904): This function should ultimately be moved outside of
* this class since it's used only for testing and benchmarking purposes and it requires us to clear state afterwards.
* (e.g. in the Goblin object)
*/
void ClientIVC::precompute_folding_verification_keys()
{
Expand Down Expand Up @@ -125,10 +122,8 @@ void ClientIVC::precompute_folding_verification_keys()

vks.kernel_vk = std::make_shared<VerificationKey>(prover_instance->proving_key);

// Clean the ivc state
goblin.op_queue = std::make_shared<Goblin::OpQueue>();
goblin.merge_proof_exists = false;
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue);
// Clean the Goblin state (reinitialise op_queue with mocking and clear merge proofs)
goblin = Goblin();
}

} // namespace bb
2 changes: 0 additions & 2 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ class ClientIVC {
// be needed in the real IVC as they are provided as inputs
std::shared_ptr<ProverInstance> prover_instance;

ClientIVC();

void initialize(ClientCircuit& circuit);

FoldProof accumulate(ClientCircuit& circuit);
Expand Down
7 changes: 7 additions & 0 deletions barretenberg/cpp/src/barretenberg/goblin/goblin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "barretenberg/eccvm/eccvm_composer.hpp"
#include "barretenberg/flavor/goblin_ultra.hpp"
#include "barretenberg/goblin/mock_circuits.hpp"
#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"
Expand Down Expand Up @@ -88,6 +89,12 @@ class Goblin {
AccumulationOutput accumulator; // Used only for ACIR methods for now

public:
Goblin()
{ // Mocks the interaction of a first circuit with the op queue due to the inability to currently handle zero
// commitments (https://github.com/AztecProtocol/barretenberg/issues/871) which would otherwise appear in the
// first round of the merge protocol. To be removed once the issue has been resolved.
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(op_queue);
}
/**
* @brief Construct a GUH proof and a merge proof for the present circuit.
* @details If there is a previous merge proof, recursively verify it.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ TEST_F(GoblinRecursionTests, Vanilla)

Goblin::AccumulationOutput kernel_accum;

// TODO(https://github.com/AztecProtocol/barretenberg/issues/723):
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue);

size_t NUM_CIRCUITS = 2;
for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) {

Expand All @@ -58,7 +55,9 @@ TEST_F(GoblinRecursionTests, Vanilla)

// Construct and accumulate the mock kernel circuit (no kernel accum in first round)
GoblinUltraCircuitBuilder kernel_circuit{ goblin.op_queue };
GoblinMockCircuits::construct_mock_kernel_small(kernel_circuit, function_accum, kernel_accum);
GoblinMockCircuits::construct_mock_kernel_small(kernel_circuit,
{ function_accum.proof, function_accum.verification_key },
{ kernel_accum.proof, kernel_accum.verification_key });
info("kernel accum");
goblin.merge(kernel_circuit);
kernel_accum = construct_accumulator(kernel_circuit);
Expand Down
13 changes: 7 additions & 6 deletions barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "barretenberg/crypto/merkle_tree/memory_store.hpp"
#include "barretenberg/crypto/merkle_tree/merkle_tree.hpp"
#include "barretenberg/flavor/goblin_ultra.hpp"
#include "barretenberg/goblin/goblin.hpp"
#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp"
#include "barretenberg/srs/global_crs.hpp"
#include "barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp"
Expand All @@ -30,13 +29,17 @@ class GoblinMockCircuits {
using Flavor = bb::GoblinUltraFlavor;
using RecursiveFlavor = bb::GoblinUltraRecursiveFlavor_<GoblinUltraBuilder>;
using RecursiveVerifier = bb::stdlib::recursion::honk::UltraRecursiveVerifier_<RecursiveFlavor>;
using KernelInput = Goblin::AccumulationOutput;
using VerifierInstance = bb::VerifierInstance_<Flavor>;
using RecursiveVerifierInstance = ::bb::stdlib::recursion::honk::RecursiveVerifierInstance_<RecursiveFlavor>;
using RecursiveVerifierAccumulator = std::shared_ptr<RecursiveVerifierInstance>;
using VerificationKey = Flavor::VerificationKey;
static constexpr size_t NUM_OP_QUEUE_COLUMNS = Flavor::NUM_WIRES;

struct KernelInput {
HonkProof proof;
std::shared_ptr<Flavor::VerificationKey> verification_key;
};

/**
* @brief Information required by the verifier to verify a folding round besides the previous accumulator.
*/
Expand Down Expand Up @@ -147,6 +150,7 @@ class GoblinMockCircuits {
op_queue->set_size_data();

// Manually compute the op queue transcript commitments (which would normally be done by the merge prover)
bb::srs::init_crs_factory("../srs_db/ignition");
auto commitment_key = CommitmentKey(op_queue->get_current_size());
std::array<Point, Flavor::NUM_WIRES> op_queue_commitments;
size_t idx = 0;
Expand All @@ -162,11 +166,8 @@ class GoblinMockCircuits {
*
* @param builder
*/
static void construct_simple_initial_circuit(GoblinUltraBuilder& builder)
static void construct_simple_circuit(GoblinUltraBuilder& builder)
{
// TODO(https://github.com/AztecProtocol/barretenberg/issues/800) Testing cleanup
perform_op_queue_interactions_for_mock_first_circuit(builder.op_queue);

// Add some arbitrary ecc op gates
for (size_t i = 0; i < 3; ++i) {
auto point = Point::random_element();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ TEST_F(MockCircuits, PinRecursionKernelSizes)
const auto run_test = [](bool large) {
{
Goblin goblin;
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(goblin.op_queue);
Goblin::AccumulationOutput kernel_accum;
GoblinUltraCircuitBuilder app_circuit{ goblin.op_queue };
GoblinMockCircuits::construct_mock_function_circuit(app_circuit, large);
auto function_accum = goblin.accumulate(app_circuit);
GoblinUltraCircuitBuilder kernel_circuit{ goblin.op_queue };
GoblinMockCircuits::construct_mock_recursion_kernel_circuit(kernel_circuit, function_accum, kernel_accum);
GoblinMockCircuits::construct_mock_recursion_kernel_circuit(
kernel_circuit,
{ function_accum.proof, function_accum.verification_key },
{ kernel_accum.proof, kernel_accum.verification_key });

auto instance = std::make_shared<ProverInstance>(kernel_circuit);
if (large) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,30 +166,6 @@ class ECCOpQueue {
return result;
}

/**
* @brief TESTING PURPOSES ONLY: Populate ECC op queue with mock data as stand in for "previous circuit" in tests
* @details TODO(#723): We currently cannot support Goblin proofs (specifically, transcript aggregation) if there
* is not existing data in the ECC op queue (since this leads to zero-commitment issues). This method populates the
* op queue with mock data so that the prover of an arbitrary 'first' circuit can behave as if it were not the
* prover over the first circuit in the stack. This method should be removed entirely once this is resolved.
*
* @param op_queue
*/
void populate_with_mock_initital_data()
{
// Add a single row of data to the op queue and commit to each column as [1] * FF(data)
std::array<Point, 4> mock_op_queue_commitments;
for (size_t idx = 0; idx < 4; idx++) {
auto mock_data = Fr::random_element();
this->ultra_ops[idx].emplace_back(mock_data);
mock_op_queue_commitments[idx] = Point::one() * mock_data;
}
// Set some internal data based on the size of the op queue data
this->set_size_data();
// Add the commitments to the op queue data for use by the next circuit
this->set_commitment_data(mock_op_queue_commitments);
}

/**
* @brief Write point addition op to queue and natively perform addition
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,13 @@ template <typename OuterFlavor> class GoblinRecursiveVerifierTest : public testi

// Instantiate ECC op queue and add mock data to simulate interaction with a previous circuit
auto op_queue = std::make_shared<ECCOpQueue>();
op_queue->populate_with_mock_initital_data();

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

// Create 2^log_n many add gates based on input log num gates
const size_t num_gates = 1 << log_num_gates;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#include "barretenberg/ultra_honk/merge_verifier.hpp"
#include "barretenberg/common/test.hpp"
#include "barretenberg/goblin/mock_circuits.hpp"
#include "barretenberg/stdlib/primitives/curves/bn254.hpp"
#include "barretenberg/stdlib/recursion/honk/verifier/merge_recursive_verifier.hpp"
#include "barretenberg/ultra_honk/merge_prover.hpp"
#include "barretenberg/ultra_honk/ultra_prover.hpp"
#include "barretenberg/ultra_honk/ultra_verifier.hpp"

namespace bb::stdlib::recursion::goblin {

Expand Down Expand Up @@ -42,9 +46,11 @@ class RecursiveMergeVerifierTest : public testing::Test {
static void test_recursive_merge_verification()
{
auto op_queue = std::make_shared<ECCOpQueue>();
// TODO(https://github.com/AztecProtocol/barretenberg/issues/800) Testing cleanup
GoblinMockCircuits::perform_op_queue_interactions_for_mock_first_circuit(op_queue);

InnerBuilder sample_circuit{ op_queue };
GoblinMockCircuits::construct_simple_initial_circuit(sample_circuit);
GoblinMockCircuits::construct_simple_circuit(sample_circuit);

// Generate a proof over the inner circuit
MergeProver merge_prover{ op_queue };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "barretenberg/proof_system/instance_inspector.hpp"

#include "barretenberg/ultra_honk/ultra_prover.hpp"
#include "barretenberg/ultra_honk/ultra_verifier.hpp"

using namespace bb;

namespace {
Expand Down Expand Up @@ -49,7 +51,7 @@ TEST_F(DataBusComposerTests, CallDataRead)
auto op_queue = std::make_shared<bb::ECCOpQueue>();

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

auto builder = GoblinUltraCircuitBuilder{ op_queue };

Expand Down

0 comments on commit d9cbdc8

Please sign in to comment.