Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 36 additions & 17 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include "barretenberg/relations/ecc_vm/ecc_lookup_relation.hpp"
#include "barretenberg/relations/ecc_vm/ecc_msm_relation.hpp"
#include "barretenberg/relations/ecc_vm/ecc_point_table_relation.hpp"
#include "barretenberg/relations/ecc_vm/ecc_set_relation.hpp"
#include "barretenberg/relations/ecc_vm/ecc_set_relation.hpp" // ECCVMSetWnaf/Scalar/MsmRelation
#include "barretenberg/relations/ecc_vm/ecc_transcript_relation.hpp"
#include "barretenberg/relations/ecc_vm/ecc_wnaf_relation.hpp"
#include "barretenberg/relations/relation_parameters.hpp"
Expand Down Expand Up @@ -74,18 +74,16 @@ class ECCVMFlavor {
// The number of multivariate polynomials on which a sumcheck prover sumcheck operates (including shifts). We often
// need containers of this size to hold related data, so we choose a name more agnostic than `NUM_POLYNOMIALS`.
// Note: this number does not include the individual sorted list polynomials.
// Includes gemini_masking_poly for ZK (NUM_ALL_ENTITIES = 117 + NUM_MASKING_POLYNOMIALS)
static constexpr size_t NUM_ALL_ENTITIES = 118;
static constexpr size_t NUM_ALL_ENTITIES = 123;
// The number of polynomials precomputed to describe a circuit and to aid a prover in constructing a satisfying
// assignment of witnesses. We again choose a neutral name.
static constexpr size_t NUM_PRECOMPUTED_ENTITIES = 4;
// The total number of witness entities not including shifts.
// Includes gemini_masking_poly for ZK (NUM_WITNESS_ENTITIES = 86 + NUM_MASKING_POLYNOMIALS)
static constexpr size_t NUM_WITNESS_ENTITIES = 87;
static constexpr size_t NUM_WITNESS_ENTITIES = 90;
// The number of entities in ShiftedEntities.
static constexpr size_t NUM_SHIFTED_ENTITIES = 26;
static constexpr size_t NUM_SHIFTED_ENTITIES = 28;
// The number of entities in DerivedWitnessEntities that are not going to be shifted.
static constexpr size_t NUM_DERIVED_WITNESS_ENTITIES_NON_SHIFTED = 1;
static constexpr size_t NUM_DERIVED_WITNESS_ENTITIES_NON_SHIFTED = 2;
// Indices into the Shplemini commitments vector that identify which "to-be-shifted" witness commitments in the
// unshifted block are duplicated in the shifted block, so their scalar muls can be merged.
//
Expand All @@ -110,18 +108,21 @@ class ECCVMFlavor {
static_assert(NUM_MASKING_POLYNOMIALS == 1, "MaskingEntities size changed — review REPEATED_COMMITMENTS offset");
static_assert(REPEATED_COMMITMENTS.first.original_start == 64,
"REPEATED_COMMITMENTS original_start changed — verify Shplemini offset convention");
static_assert(REPEATED_COMMITMENTS.first.duplicate_start == 91,
static_assert(REPEATED_COMMITMENTS.first.duplicate_start == 94,
"REPEATED_COMMITMENTS duplicate_start changed — verify Shplemini offset convention");
static_assert(REPEATED_COMMITMENTS.first.count == 26, "REPEATED_COMMITMENTS count changed");
static_assert(REPEATED_COMMITMENTS.first.count == 28, "REPEATED_COMMITMENTS count changed");

using GrandProductRelations = std::tuple<ECCVMSetRelation<FF>>;
using GrandProductRelations =
std::tuple<ECCVMSetWnafRelation<FF>, ECCVMSetScalarRelation<FF>, ECCVMSetMsmRelation<FF>>;
// define the tuple of Relations that comprise the Sumcheck relation
template <typename FF>
using Relations_ = std::tuple<ECCVMTranscriptRelation<FF>,
ECCVMPointTableRelation<FF>,
ECCVMWnafRelation<FF>,
ECCVMMSMRelation<FF>,
ECCVMSetRelation<FF>,
ECCVMSetWnafRelation<FF>,
ECCVMSetScalarRelation<FF>,
ECCVMSetMsmRelation<FF>,
ECCVMLookupRelation<FF>,
ECCVMBoolsRelation<FF>>;
using Relations = Relations_<FF>;
Expand Down Expand Up @@ -207,8 +208,11 @@ class ECCVMFlavor {
*/
template <typename DataType> struct DerivedWitnessEntities {
DEFINE_FLAVOR_MEMBERS(DataType,
z_perm, // column 0
lookup_inverses); // column 1
z_perm, // column 0 (shifted)
z_perm_scalar, // column 1 (shifted)
z_perm_msm, // column 2 (shifted)
den_wnaf_partial, // column 3 (non-shifted)
lookup_inverses); // column 4 (non-shifted)
};
template <typename DataType> class WireNonShiftedEntities {
public:
Expand Down Expand Up @@ -392,7 +396,9 @@ class ECCVMFlavor {
transcript_accumulator_not_empty_shift, // column 22
transcript_accumulator_x_shift, // column 23
transcript_accumulator_y_shift, // column 24
z_perm_shift); // column 25
z_perm_shift, // column 25
z_perm_scalar_shift, // column 26
z_perm_msm_shift); // column 27
};

template <typename DataType, typename PrecomputedAndWitnessEntitiesSuperset>
Expand Down Expand Up @@ -424,7 +430,9 @@ class ECCVMFlavor {
entities.transcript_accumulator_not_empty, // column 22
entities.transcript_accumulator_x, // column 23
entities.transcript_accumulator_y, // column 24
entities.z_perm }; // column 25
entities.z_perm, // column 25
entities.z_perm_scalar, // column 26
entities.z_perm_msm }; // column 27
}

/**
Expand Down Expand Up @@ -664,8 +672,10 @@ class ECCVMFlavor {
poly = Polynomial(num_rows - 1, dyadic_num_rows, 1);
}

// 3. z_perm: must stay full-size (grand product computed over unmasked_witness_size)
// 3. Grand product polynomials: must stay full-size (computed over unmasked_witness_size), shiftable
z_perm = Polynomial(dyadic_num_rows - 1, dyadic_num_rows, 1);
z_perm_scalar = Polynomial(dyadic_num_rows - 1, dyadic_num_rows, 1);
z_perm_msm = Polynomial(dyadic_num_rows - 1, dyadic_num_rows, 1);

// 4. Catch-all: precomputed, lookup_inverses, gemini_masking_poly → full size
for (auto& poly : get_all()) {
Expand Down Expand Up @@ -940,8 +950,13 @@ class ECCVMFlavor {
Base::transcript_msm_count_zero_at_transition = "TRANSCRIPT_MSM_COUNT_ZERO_AT_TRANSITION";
Base::transcript_msm_count_at_transition_inverse = "TRANSCRIPT_MSM_COUNT_AT_TRANSITION_INVERSE";
Base::z_perm = "Z_PERM";
Base::z_perm_shift = "Z_PERM_SHIFT";
Base::z_perm_scalar = "Z_PERM_SCALAR";
Base::z_perm_msm = "Z_PERM_MSM";
Base::den_wnaf_partial = "DEN_WNAF_PARTIAL";
Base::lookup_inverses = "LOOKUP_INVERSES";
Base::z_perm_shift = "Z_PERM_SHIFT";
Base::z_perm_scalar_shift = "Z_PERM_SCALAR_SHIFT";
Base::z_perm_msm_shift = "Z_PERM_MSM_SHIFT";
// The ones beginning with "__" are only used for debugging
Base::lagrange_first = "__LAGRANGE_FIRST";
Base::lagrange_second = "__LAGRANGE_SECOND";
Expand Down Expand Up @@ -1049,6 +1064,10 @@ class ECCVMFlavor {
// 4: We also force that `transcript_op==0`.
return (polynomials.z_perm[edge_idx] == polynomials.z_perm_shift[edge_idx]) &&
(polynomials.z_perm[edge_idx + 1] == polynomials.z_perm_shift[edge_idx + 1]) &&
(polynomials.z_perm_scalar[edge_idx] == polynomials.z_perm_scalar_shift[edge_idx]) &&
(polynomials.z_perm_scalar[edge_idx + 1] == polynomials.z_perm_scalar_shift[edge_idx + 1]) &&
(polynomials.z_perm_msm[edge_idx] == polynomials.z_perm_msm_shift[edge_idx]) &&
(polynomials.z_perm_msm[edge_idx + 1] == polynomials.z_perm_msm_shift[edge_idx + 1]) &&
(polynomials.lagrange_last[edge_idx] == 0 && polynomials.lagrange_last[edge_idx + 1] == 0) &&
(polynomials.msm_transition[edge_idx] == 0 && polynomials.msm_transition[edge_idx + 1] == 0) &&
(polynomials.transcript_mul[edge_idx] == 0 && polynomials.transcript_mul[edge_idx + 1] == 0) &&
Expand Down
48 changes: 48 additions & 0 deletions barretenberg/cpp/src/barretenberg/eccvm/eccvm_prover.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,43 @@ void ECCVMProver::execute_log_derivative_commitments_round()
transcript->send_to_verifier(commitment_labels.lookup_inverses,
key->commitment_key.commit(li) +
key->commitment_key.commit(key->masking_tail_data.tails.lookup_inverses));

// Compute the intermediate committed polynomial den_wnaf_partial (product of first two wnaf denominator factors).
// This depends on beta/gamma, so it must be computed here (after challenges are received).
// den_wnaf_partial[i] = wnaf_out1 * wnaf_out2 where each wnaf_outK is:
// addK * (sliceK + gamma + (pc - count - (K-1)) * beta + round * beta_sqr + tag) + (1 - addK)
{
auto& polys = key->polynomials;
const auto& gamma = relation_parameters.gamma;
const auto& beta_val = relation_parameters.beta;
const auto& beta_sqr_val = relation_parameters.beta_sqr;
const auto& beta_quartic_val = relation_parameters.beta_quartic;
const auto first_term_tag = beta_quartic_val * ECCVMSetRelationConstants::FIRST_TERM_TAG;

for (size_t i = 0; i < unmasked_witness_size; ++i) {
const auto msm_pc_val = polys.msm_pc[i];
const auto msm_count_val = polys.msm_count[i];
const auto msm_round_val = polys.msm_round[i];

const auto add1_val = polys.msm_add1[i];
const auto slice1_val = polys.msm_slice1[i];
auto wnaf_out1 = add1_val * (slice1_val + gamma + (msm_pc_val - msm_count_val) * beta_val +
msm_round_val * beta_sqr_val + first_term_tag) +
(-add1_val + 1);

const auto add2_val = polys.msm_add2[i];
const auto slice2_val = polys.msm_slice2[i];
auto wnaf_out2 = add2_val * (slice2_val + gamma + (msm_pc_val - msm_count_val - 1) * beta_val +
msm_round_val * beta_sqr_val + first_term_tag) +
(-add2_val + 1);

polys.den_wnaf_partial.at(i) = wnaf_out1 * wnaf_out2;
}
}
auto& dwp = key->polynomials.den_wnaf_partial;
transcript->send_to_verifier(commitment_labels.den_wnaf_partial,
key->commitment_key.commit(dwp) +
key->commitment_key.commit(key->masking_tail_data.tails.den_wnaf_partial));
}

/**
Expand All @@ -125,10 +162,21 @@ void ECCVMProver::execute_grand_product_computation_round()
BB_BENCH_NAME("ECCVMProver::execute_grand_product_computation_round");
// Compute permutation grand product and their commitments
compute_grand_products<Flavor>(key->polynomials, relation_parameters, unmasked_witness_size);

auto& zp = key->polynomials.z_perm;
transcript->send_to_verifier(commitment_labels.z_perm,
key->commitment_key.commit(zp) +
key->commitment_key.commit(key->masking_tail_data.tails.z_perm));

auto& zps = key->polynomials.z_perm_scalar;
transcript->send_to_verifier(commitment_labels.z_perm_scalar,
key->commitment_key.commit(zps) +
key->commitment_key.commit(key->masking_tail_data.tails.z_perm_scalar));

auto& zpm = key->polynomials.z_perm_msm;
transcript->send_to_verifier(commitment_labels.z_perm_msm,
key->commitment_key.commit(zpm) +
key->commitment_key.commit(key->masking_tail_data.tails.z_perm_msm));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,19 @@ ProverPolynomials build_valid_eccvm_msm_state()

/**
* @brief Compute random Fiat-Shamir challenges and derived polynomials (logderivative inverse, grand product)
* needed to check ECCVMSetRelation and ECCVMLookupRelation.
* needed to check ECCVMSetWnaf/Scalar/MsmRelation and ECCVMLookupRelation.
*/
RelationParameters<FF> compute_full_relation_params(ProverPolynomials& polynomials)
{
const FF beta = FF::random_element(&engine);
const FF gamma = FF::random_element(&engine);
const FF beta_sqr = beta.sqr();
const FF beta_cube = beta_sqr * beta;
auto eccvm_set_permutation_delta =
gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr);
const FF beta_quartic = beta_sqr * beta_sqr;
auto first_term_tag = beta_quartic; // FIRST_TERM_TAG (= 1) * beta_quartic
auto eccvm_set_permutation_delta = (gamma + first_term_tag) * (gamma + beta_sqr + first_term_tag) *
(gamma + beta_sqr + beta_sqr + first_term_tag) *
(gamma + beta_sqr + beta_sqr + beta_sqr + first_term_tag);
eccvm_set_permutation_delta = eccvm_set_permutation_delta.invert();

RelationParameters<FF> params{
Expand All @@ -116,14 +119,44 @@ RelationParameters<FF> compute_full_relation_params(ProverPolynomials& polynomia
.public_input_delta = 0,
.beta_sqr = beta_sqr,
.beta_cube = beta_cube,
.beta_quartic = beta_quartic,
.eccvm_set_permutation_delta = eccvm_set_permutation_delta,
};

const size_t num_rows = polynomials.get_polynomial_size();
const size_t unmasked_witness_size = num_rows - NUM_DISABLED_ROWS_IN_SUMCHECK;
compute_logderivative_inverse<FF, ECCVMLookupRelation<FF>>(polynomials, params, unmasked_witness_size);
compute_grand_product<Flavor, ECCVMSetRelation<FF>>(polynomials, params, unmasked_witness_size);

// Compute den_wnaf_partial before wnaf grand product
{
const auto first_term_tag_val = beta_quartic * ECCVMSetRelationConstants::FIRST_TERM_TAG;
for (size_t i = 0; i < unmasked_witness_size; ++i) {
const auto msm_pc_val = polynomials.msm_pc[i];
const auto msm_count_val = polynomials.msm_count[i];
const auto msm_round_val = polynomials.msm_round[i];

const auto add1_val = polynomials.msm_add1[i];
const auto slice1_val = polynomials.msm_slice1[i];
auto wnaf_out1 = add1_val * (slice1_val + gamma + (msm_pc_val - msm_count_val) * beta +
msm_round_val * beta_sqr + first_term_tag_val) +
(-add1_val + 1);

const auto add2_val = polynomials.msm_add2[i];
const auto slice2_val = polynomials.msm_slice2[i];
auto wnaf_out2 = add2_val * (slice2_val + gamma + (msm_pc_val - msm_count_val - 1) * beta +
msm_round_val * beta_sqr + first_term_tag_val) +
(-add2_val + 1);

polynomials.den_wnaf_partial.at(i) = wnaf_out1 * wnaf_out2;
}
}

compute_grand_product<Flavor, ECCVMSetWnafRelation<FF>>(polynomials, params, unmasked_witness_size);
compute_grand_product<Flavor, ECCVMSetScalarRelation<FF>>(polynomials, params, unmasked_witness_size);
compute_grand_product<Flavor, ECCVMSetMsmRelation<FF>>(polynomials, params, unmasked_witness_size);
polynomials.z_perm_shift = Polynomial(polynomials.z_perm.shifted());
polynomials.z_perm_scalar_shift = Polynomial(polynomials.z_perm_scalar.shifted());
polynomials.z_perm_msm_shift = Polynomial(polynomials.z_perm_msm.shifted());

return params;
}
Expand Down Expand Up @@ -310,7 +343,7 @@ TEST_F(ECCVMRelationCorruptionTests, MSMRelationFailsOnShiftedMSMTable)

// Verify that all other ECCVM relations still pass after the shift.
// We compute random Fiat-Shamir challenges and derived polynomials (logderivative inverse, grand product)
// so we can also check ECCVMSetRelation and ECCVMLookupRelation.
// so we can also check ECCVMSetWnaf/Scalar/MsmRelation and ECCVMLookupRelation.
auto full_params = compute_full_relation_params(polynomials);

// Relations that don't touch MSM columns should be completely unaffected.
Expand All @@ -330,13 +363,12 @@ TEST_F(ECCVMRelationCorruptionTests, MSMRelationFailsOnShiftedMSMTable)
RelationChecker<void>::check<ECCVMBoolsRelation<FF>>(polynomials, full_params, "ECCVMBoolsRelation");
EXPECT_TRUE(bools_failures.empty()) << "ECCVMBoolsRelation should still pass";

// The Set relation enforces a multiset equality between MSM output tuples (pc, acc_x, acc_y, msm_size)
// The MSM set relation enforces a multiset equality between MSM output tuples (pc, acc_x, acc_y, msm_size)
// and the transcript. Shifting the MSM columns corrupts these tuples, so the grand product (computed
// post-shift) reflects mismatched reads/writes and the relation correctly fails. It is possible that with more
// care, we could make this also pass.
auto set_failures =
RelationChecker<void>::check<ECCVMSetRelation<FF>>(polynomials, full_params, "ECCVMSetRelation");
EXPECT_FALSE(set_failures.empty()) << "ECCVMSetRelation should also fail (MSM output tuples are shifted)";
// post-shift) reflects mismatched reads/writes and the relation correctly fails.
auto set_msm_failures =
RelationChecker<void>::check<ECCVMSetMsmRelation<FF>>(polynomials, full_params, "ECCVMSetMsmRelation");
EXPECT_FALSE(set_msm_failures.empty()) << "ECCVMSetMsmRelation should also fail (MSM output tuples are shifted)";

// The Lookup relation's logderivative inverse is computed post-shift, so it adapts to the
// shifted column values. The per-row subrelation passes, and the sum-over-trace (linearly
Expand Down Expand Up @@ -392,9 +424,9 @@ TEST_F(ECCVMRelationCorruptionTests, SetRelationFailsOnZPermNonZeroAtFirstRow)
auto polynomials = build_valid_eccvm_msm_state();
auto params = compute_full_relation_params(polynomials);

// Baseline: set relation passes
auto baseline = RelationChecker<void>::check<ECCVMSetRelation<FF>>(polynomials, params, "ECCVMSetRelation");
EXPECT_TRUE(baseline.empty()) << "Baseline set relation should pass";
// Baseline: wnaf set relation passes
auto baseline = RelationChecker<void>::check<ECCVMSetWnafRelation<FF>>(polynomials, params, "ECCVMSetWnafRelation");
EXPECT_TRUE(baseline.empty()) << "Baseline wnaf set relation should pass";

// Derive expected lagrange_first position from z_perm shiftable structure
ASSERT_TRUE(polynomials.z_perm.is_shiftable());
Expand Down Expand Up @@ -426,11 +458,11 @@ TEST_F(ECCVMRelationCorruptionTests, SetRelationFailsOnZPermNonZeroAtFirstRow)
// Tamper: set z_perm to non-zero where lagrange_first is active
polynomials.z_perm.at(first_row) = FF(1);

auto failures = RelationChecker<void>::check<ECCVMSetRelation<FF>>(
polynomials, params, "ECCVMSetRelation - After setting z_perm != 0 at lagrange_first");
auto failures = RelationChecker<void>::check<ECCVMSetWnafRelation<FF>>(
polynomials, params, "ECCVMSetWnafRelation - After setting z_perm != 0 at lagrange_first");
EXPECT_FALSE(failures.empty()) << "Set relation should fail after z_perm init corruption";
EXPECT_TRUE(failures.contains(ECCVMSetRelationImpl<FF>::Z_PERM_INIT))
EXPECT_TRUE(failures.contains(ECCVMSetWnafRelationImpl<FF>::Z_PERM_INIT))
<< "Sub-relation Z_PERM_INIT should catch the corruption";
EXPECT_EQ(failures.at(ECCVMSetRelationImpl<FF>::Z_PERM_INIT), first_row)
EXPECT_EQ(failures.at(ECCVMSetWnafRelationImpl<FF>::Z_PERM_INIT), first_row)
<< "Failure should be at lagrange_first row";
}
Loading
Loading