Skip to content

Commit

Permalink
chore: fixing the sizes of VMs in CIVC (#11793)
Browse files Browse the repository at this point in the history
The sizes of Translator and ECCVM circuits leak the amount of circuits
that have been folded, so we need to fix them.

`benchmark_client_ivc` after fixing the sizes (note that regression is
expected, as the IPA prover is opening a dense polynomial of size 2^16
instead of 2^15 + there is not much skipping in Sumcheck except for
`SetRelation`)


ECCVMProver(CircuitBuilder&)(t)          190     0.87%
ECCVMProver::construct_proof(t)         2405    10.95%
TranslatorProver::construct_proof(t)    1316     5.99%
Goblin::merge(t)                         132     0.60%

Total time accounted for: 21963ms/23040ms = 95.32%

Benchmark on master: 

ECCVMProver(CircuitBuilder&)(t)          182     0.85%
ECCVMProver::construct_proof(t)         1612     7.51%
TranslatorProver::construct_proof(t)    1650     7.69%
Goblin::merge(t)                         132     0.62%

Total time accounted for: 21468ms/22538ms = 95.25%
  • Loading branch information
iakovenkos authored Feb 11, 2025
1 parent 05afb5b commit 1afddbd
Show file tree
Hide file tree
Showing 14 changed files with 273 additions and 109 deletions.
42 changes: 42 additions & 0 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,48 @@ TEST_F(ClientIVCTests, StructuredPrecomputedVKs)
EXPECT_TRUE(ivc.prove_and_verify());
};

/**
* @brief Perform accumulation with a structured trace and precomputed verification keys
*
*/
TEST_F(ClientIVCTests, VKIndependenceTest)
{
const size_t MIN_NUM_CIRCUITS = 2;
// Folding more than 20 circuits requires to double the number of gates in Translator.
const size_t MAX_NUM_CIRCUITS = 20;
const size_t log2_num_gates = 5; // number of gates in baseline mocked circuit

auto generate_vk = [&](size_t num_circuits) {
ClientIVC ivc{ { SMALL_TEST_STRUCTURE } };
MockCircuitProducer circuit_producer;
for (size_t j = 0; j < num_circuits; ++j) {
auto circuit = circuit_producer.create_next_circuit(ivc, log2_num_gates);
ivc.accumulate(circuit);
}
ivc.prove();
auto ivc_vk = ivc.get_vk();

// PCS verification keys will not match so set to null before comparing
ivc_vk.mega->pcs_verification_key = nullptr;
ivc_vk.eccvm->pcs_verification_key = nullptr;
ivc_vk.translator->pcs_verification_key = nullptr;

return ivc_vk;
};

auto civc_vk_2 = generate_vk(MIN_NUM_CIRCUITS);
auto civc_vk_20 = generate_vk(MAX_NUM_CIRCUITS);

// Check the equality of the Mega components of the ClientIVC VKeys.
EXPECT_EQ(*civc_vk_2.mega.get(), *civc_vk_20.mega.get());

// Check the equality of the ECCVM components of the ClientIVC VKeys.
EXPECT_EQ(*civc_vk_2.eccvm.get(), *civc_vk_20.eccvm.get());

// Check the equality of the Translator components of the ClientIVC VKeys.
EXPECT_EQ(*civc_vk_2.translator.get(), *civc_vk_20.translator.get());
};

/**
* @brief Run a test using functions shared with the ClientIVC benchmark.
* @details We do have this in addition to the above tests anyway so we can believe that the benchmark is running on
Expand Down
25 changes: 25 additions & 0 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_composer.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ TEST_F(ECCVMTests, BaseCase)

ASSERT_TRUE(verified);
}
TEST_F(ECCVMTests, BaseCaseFixedSize)
{
ECCVMCircuitBuilder builder = generate_circuit(&engine);
ECCVMProver prover(builder, /*fixed_size = */ true);
ECCVMProof proof = prover.construct_proof();
ECCVMVerifier verifier(prover.key);
bool verified = verifier.verify_proof(proof);

ASSERT_TRUE(verified);
}

TEST_F(ECCVMTests, EqFails)
{
Expand All @@ -114,6 +124,21 @@ TEST_F(ECCVMTests, EqFails)
ASSERT_FALSE(verified);
}

TEST_F(ECCVMTests, EqFailsFixedSize)
{
auto builder = generate_circuit(&engine);
// Tamper with the eq op such that the expected value is incorect
builder.op_queue->add_erroneous_equality_op_for_testing();

builder.op_queue->num_transcript_rows++;
ECCVMProver prover(builder, /*fixed_size = */ true);

ECCVMProof proof = prover.construct_proof();
ECCVMVerifier verifier(prover.key);
bool verified = verifier.verify_proof(proof);
ASSERT_FALSE(verified);
}

TEST_F(ECCVMTests, CommittedSumcheck)
{
using Flavor = ECCVMFlavor;
Expand Down
226 changes: 138 additions & 88 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp

Large diffs are not rendered by default.

25 changes: 18 additions & 7 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@
namespace bb {

ECCVMProver::ECCVMProver(CircuitBuilder& builder,
const bool fixed_size,
const std::shared_ptr<Transcript>& transcript,
const std::shared_ptr<Transcript>& ipa_transcript)
: transcript(transcript)
, ipa_transcript(ipa_transcript)
, fixed_size(fixed_size)
{
PROFILE_THIS_NAME("ECCVMProver(CircuitBuilder&)");

// TODO(https://github.com/AztecProtocol/barretenberg/issues/939): Remove redundancy between
// ProvingKey/ProverPolynomials and update the model to reflect what's done in all other proving systems.

// Construct the proving key; populates all polynomials except for witness polys
key = std::make_shared<ProvingKey>(builder);
key = fixed_size ? std::make_shared<ProvingKey>(builder, fixed_size) : std::make_shared<ProvingKey>(builder);

key->commitment_key = std::make_shared<CommitmentKey>(key->circuit_size);
}
Expand All @@ -45,10 +47,19 @@ void ECCVMProver::execute_preamble_round()
*/
void ECCVMProver::execute_wire_commitments_round()
{
auto wire_polys = key->polynomials.get_wires();
auto labels = commitment_labels.get_wires();
for (size_t idx = 0; idx < wire_polys.size(); ++idx) {
transcript->send_to_verifier(labels[idx], key->commitment_key->commit(wire_polys[idx]));
// Commit to wires whose length is bounded by the real size of the ECCVM
for (const auto& [wire, label] : zip_view(key->polynomials.get_wires_without_accumulators(),
commitment_labels.get_wires_without_accumulators())) {
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1240) Structured Polynomials in
// ECCVM/Translator/MegaZK
PolynomialSpan<FF> wire_span = wire;
transcript->send_to_verifier(label, key->commitment_key->commit(wire_span.subspan(0, key->real_size)));
}

// The accumulators are populated until the 2^{CONST_ECCVM_LOG_N}, therefore we commit to a full-sized polynomial
for (const auto& [wire, label] :
zip_view(key->polynomials.get_accumulators(), commitment_labels.get_accumulators())) {
transcript->send_to_verifier(label, key->commitment_key->commit(wire));
}
}

Expand All @@ -58,6 +69,7 @@ void ECCVMProver::execute_wire_commitments_round()
*/
void ECCVMProver::execute_log_derivative_commitments_round()
{

// Compute and add beta to relation parameters
auto [beta, gamma] = transcript->template get_challenges<FF>("beta", "gamma");

Expand Down Expand Up @@ -95,6 +107,7 @@ void ECCVMProver::execute_grand_product_computation_round()
*/
void ECCVMProver::execute_relation_check_rounds()
{

using Sumcheck = SumcheckProver<Flavor>;

auto sumcheck = Sumcheck(key->circuit_size, transcript);
Expand Down Expand Up @@ -199,8 +212,6 @@ void ECCVMProver::execute_pcs_rounds()

// Produce another challenge passed as input to the translator verifier
translation_batching_challenge_v = transcript->template get_challenge<FF>("Translation:batching_challenge");

vinfo("computed opening proof");
}

ECCVMProof ECCVMProver::export_proof()
Expand Down
3 changes: 3 additions & 0 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ECCVMProver {
using SmallSubgroupIPA = SmallSubgroupIPAProver<Flavor>;

explicit ECCVMProver(CircuitBuilder& builder,
const bool fixed_size = false,
const std::shared_ptr<Transcript>& transcript = std::make_shared<Transcript>(),
const std::shared_ptr<Transcript>& ipa_transcript = std::make_shared<Transcript>());

Expand All @@ -47,6 +48,8 @@ class ECCVMProver {
std::shared_ptr<Transcript> transcript;
std::shared_ptr<Transcript> ipa_transcript;

bool fixed_size;

TranslationEvaluations translation_evaluations;

std::vector<FF> public_inputs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ class ECCVMTranscriptTests : public ::testing::Test {
manifest_expected.add_entry(round, "TRANSCRIPT_MSM_COUNT_AT_TRANSITION_INVERSE", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_MUL", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_MSM_COUNT", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_X", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_Y", frs_per_G);
manifest_expected.add_entry(round, "PRECOMPUTE_SCALAR_SUM", frs_per_G);
manifest_expected.add_entry(round, "PRECOMPUTE_S1HI", frs_per_G);
manifest_expected.add_entry(round, "PRECOMPUTE_DX", frs_per_G);
Expand All @@ -120,8 +118,10 @@ class ECCVMTranscriptTests : public ::testing::Test {
manifest_expected.add_entry(round, "PRECOMPUTE_PC", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_PC", frs_per_G);
manifest_expected.add_entry(round, "PRECOMPUTE_ROUND", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_EMPTY", frs_per_G);
manifest_expected.add_entry(round, "PRECOMPUTE_SELECT", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_EMPTY", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_X", frs_per_G);
manifest_expected.add_entry(round, "TRANSCRIPT_ACCUMULATOR_Y", frs_per_G);
manifest_expected.add_challenge(round, "beta", "gamma");

round++;
Expand Down
3 changes: 2 additions & 1 deletion barretenberg/cpp/src/barretenberg/goblin/goblin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ class GoblinProver {
PROFILE_THIS_NAME("Create ECCVMBuilder and ECCVMProver");

auto eccvm_builder = std::make_unique<ECCVMBuilder>(op_queue);
eccvm_prover = std::make_unique<ECCVMProver>(*eccvm_builder);
// As is it used in ClientIVC, we make it fixed size = 2^{CONST_ECCVM_LOG_N}
eccvm_prover = std::make_unique<ECCVMProver>(*eccvm_builder, /*fixed_size =*/true);
}
{

Expand Down
6 changes: 4 additions & 2 deletions barretenberg/cpp/src/barretenberg/polynomials/polynomial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ template <typename Fr> struct PolynomialSpan {
ASSERT(index >= start_index && index < end_index());
return span[index - start_index];
}
PolynomialSpan subspan(size_t offset)
PolynomialSpan subspan(size_t offset, size_t length)
{
if (offset > span.size()) { // Return a null span
return { 0, span.subspan(span.size()) };
}
return { start_index + offset, span.subspan(offset) };
size_t new_length = std::min(length, span.size() - offset);
return { start_index + offset, span.subspan(offset, new_length) };
}
operator PolynomialSpan<const Fr>() const { return PolynomialSpan<const Fr>(start_index, span); }
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ template <typename FF_> class ECCVMSetRelationImpl {
3 // left-shiftable polynomial sub-relation
};

template <typename AllEntities> inline static bool skip(const AllEntities& in)
{
// If z_perm == z_perm_shift, this implies that none of the wire values for the present input are involved in
// non-trivial copy constraints.
return (in.z_perm - in.z_perm_shift).is_zero();
}

template <typename Accumulator> static Accumulator convert_to_wnaf(const auto& s0, const auto& s1)
{
auto t = s0 + s0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace bb {
class TranslatorFlavor {

public:
static constexpr size_t mini_circuit_size = 2048;
static constexpr size_t mini_circuit_size = 8192;
using CircuitBuilder = TranslatorCircuitBuilder;
using Curve = curve::BN254;
using PCS = KZG<Curve>;
Expand All @@ -40,7 +40,8 @@ class TranslatorFlavor {

// Indicates that this flavor runs with ZK Sumcheck.
static constexpr bool HasZK = true;
static constexpr size_t MINIMUM_MINI_CIRCUIT_SIZE = 2048;
// A minicircuit of such size allows for 10 rounds of folding (i.e. 20 circuits).
static constexpr size_t MINIMUM_MINI_CIRCUIT_SIZE = 8192;

// The size of the circuit which is filled with non-zero values for most polynomials. Most relations (everything
// except for Permutation and DeltaRangeConstraint) can be evaluated just on the first chunk
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,21 @@ void TranslatorProver::execute_preamble_round()
*/
void TranslatorProver::execute_wire_and_sorted_constraints_commitments_round()
{
// Commit to all wire polynomials and ordered range constraint polynomials
auto wire_polys = key->proving_key->polynomials.get_wires_and_ordered_range_constraints();
auto labels = commitment_labels.get_wires_and_ordered_range_constraints();
for (size_t idx = 0; idx < wire_polys.size(); ++idx) {
transcript->send_to_verifier(labels[idx], key->proving_key->commitment_key->commit(wire_polys[idx]));
// Commit to all wire polynomials, note that the wire polynomials have at most `mini_circuit_dyadic_size` non-zero
// values. Therefore we could commit to a subspan of that size.
for (const auto& [wire, label] :
zip_view(key->proving_key->polynomials.get_wires(), commitment_labels.get_wires())) {
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1240) Structured Polynomials in
// ECCVM/Translator/MegaZK
PolynomialSpan<FF> wire_span = wire;
transcript->send_to_verifier(
label, key->proving_key->commitment_key->commit(wire_span.subspan(0, key->mini_circuit_dyadic_size)));
}

// The ordered range constraints are of full circuit size.
for (const auto& [ordered_range_constraint, label] : zip_view(
key->proving_key->polynomials.get_ordered_constraints(), commitment_labels.get_ordered_constraints())) {
transcript->send_to_verifier(label, key->proving_key->commitment_key->commit(ordered_range_constraint));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class TranslatorProver {
using PCS = typename Flavor::PCS;
using Transcript = typename Flavor::Transcript;
using ZKData = ZKSumcheckData<Flavor>;
static constexpr size_t MINIMUM_MINI_CIRCUIT_SIZE = 2048;
static constexpr size_t MINIMUM_MINI_CIRCUIT_SIZE = Flavor::MINIMUM_MINI_CIRCUIT_SIZE;
size_t total_num_gates = 0; // num_gates (already include zero row offset) (used to compute dyadic size)
size_t dyadic_circuit_size = 0; // final power-of-2 circuit size
size_t mini_circuit_dyadic_size = 0; // The size of the small circuit that contains non-range constraint relations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ class TranslatorProvingKey {

inline void compute_mini_circuit_dyadic_size(const Circuit& circuit)
{
// Check that the Translator Circuit does not exceed the fixed upper bound, the current value 8192 corresponds
// to 10 rounds of folding (i.e. 20 circuits)
if (circuit.num_gates > Flavor::MINIMUM_MINI_CIRCUIT_SIZE) {
info("The Translator circuit size has exceeded the fixed upper bound");
ASSERT(false);
}
const size_t total_num_gates = std::max(circuit.num_gates, Flavor::MINIMUM_MINI_CIRCUIT_SIZE);
// Next power of 2
mini_circuit_dyadic_size = circuit.get_circuit_subgroup_size(total_num_gates);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ TYPED_TEST(MegaHonkTests, BasicStructured)

// In MegaZKFlavor, we mask witness polynomials by placing random values at the indices `dyadic_circuit_size`-i for
// i=1,2,3. This mechanism does not work with structured polynomials yet.
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1240) Structured Polynomials in
// ECCVM/Translator/MegaZK
if constexpr (std::is_same_v<Flavor, MegaZKFlavor>) {
GTEST_SKIP() << "Skipping 'BasicStructured' test for MegaZKFlavor.";
}
Expand Down Expand Up @@ -152,6 +154,8 @@ TYPED_TEST(MegaHonkTests, DynamicVirtualSizeIncrease)

// In MegaZKFlavor, we mask witness polynomials by placing random values at the indices `dyadic_circuit_size`-i for
// i=1,2,3. This mechanism does not work with structured polynomials yet.
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1240) Structured Polynomials in
// ECCVM/Translator/MegaZK
if constexpr (std::is_same_v<Flavor, MegaZKFlavor>) {
GTEST_SKIP() << "Skipping 'DynamicVirtualSizeIncrease' test for MegaZKFlavor.";
}
Expand Down Expand Up @@ -396,6 +400,8 @@ TYPED_TEST(MegaHonkTests, PolySwap)
using Flavor = TypeParam;
// In MegaZKFlavor, we mask witness polynomials by placing random values at the indices `dyadic_circuit_size`-i, for
// i=1,2,3. This mechanism does not work with structured polynomials yet.
// TODO(https://github.com/AztecProtocol/barretenberg/issues/1240) Structured Polynomials in
// ECCVM/Translator/MegaZK
if constexpr (std::is_same_v<Flavor, MegaZKFlavor>) {
GTEST_SKIP() << "Skipping 'PolySwap' test for MegaZKFlavor.";
}
Expand Down

0 comments on commit 1afddbd

Please sign in to comment.