13#include <gtest/gtest.h>
22using Fr =
typename G1::Fr;
29void expand_shiftable_to_virtual_size(
Polynomial& polynomial)
43 &polys.msm_size_of_msm,
55 &polys.msm_collision_x1,
56 &polys.msm_collision_x2,
57 &polys.msm_collision_x3,
58 &polys.msm_collision_x4,
68 &polys.msm_transition,
72 &polys.msm_accumulator_x,
73 &polys.msm_accumulator_y,
78 &polys.msm_round_minus_31_inv,
87 auto generators = G1::derive_generators(
"test generators", 3);
88 auto a = generators[0];
89 auto b = generators[1];
94 op_queue->mul_accumulate(
a, x);
95 op_queue->mul_accumulate(
b, y);
96 op_queue->eq_and_reset();
113 auto generators = G1::derive_generators(
"test generators", num_points);
119 op_queue->eq_and_reset();
135 const FF beta_sqr = beta.
sqr();
136 const FF beta_cube = beta_sqr * beta;
137 auto eccvm_set_permutation_delta =
138 gamma * (gamma + beta_sqr) * (gamma + beta_sqr + beta_sqr) * (gamma + beta_sqr + beta_sqr + beta_sqr);
139 eccvm_set_permutation_delta = eccvm_set_permutation_delta.
invert();
145 .public_input_delta = 0,
146 .beta_sqr = beta_sqr,
147 .beta_cube = beta_cube,
148 .eccvm_set_permutation_delta = eccvm_set_permutation_delta,
151 compute_logderivative_inverse<FF, ECCVMLookupRelation<FF>>(polynomials, params,
Flavor::TRACE_OFFSET);
152 compute_grand_product<Flavor, ECCVMSetRelation<FF>>(polynomials, params);
153 polynomials.z_perm_shift =
Polynomial(polynomials.z_perm.shifted());
163 const size_t num_rows = polynomials.get_polynomial_size();
165 if (polynomials.transcript_add.get(i) ==
FF(0) && polynomials.transcript_mul.get(i) ==
FF(0) &&
166 polynomials.transcript_eq.get(i) ==
FF(0) && polynomials.transcript_reset_accumulator.get(i) ==
FF(0) &&
167 polynomials.lagrange_first.get(i) ==
FF(0) && polynomials.lagrange_last.get(i) ==
FF(0)) {
192 auto polynomials = build_valid_eccvm_msm_state();
197 EXPECT_TRUE(baseline.empty()) <<
"Baseline MSM relation should pass";
201 ASSERT_EQ(polynomials.msm_add.get(first_msm_row),
FF(1)) <<
"First MSM row should be an active MSM add row";
202 ASSERT_EQ(polynomials.msm_transition.get(first_msm_row),
FF(1)) <<
"First MSM row should have msm_transition=1";
207 polynomials.set_shifted();
211 EXPECT_TRUE(failures.empty()) <<
"MSM relation should STILL PASS — acc is unused when msm_transition=1";
230 auto polynomials = build_valid_eccvm_msm_state();
234 EXPECT_TRUE(baseline.empty()) <<
"Baseline MSM relation should pass";
237 const size_t num_rows = polynomials.get_polynomial_size();
238 size_t active_row = 0;
240 if (polynomials.msm_add.get(i) ==
FF(1) && polynomials.msm_transition.get(i) ==
FF(0)) {
245 ASSERT_NE(active_row, 0) <<
"Should find an interior active MSM add row";
249 polynomials.set_shifted();
253 EXPECT_FALSE(failures.empty()) <<
"MSM relation should fail after active-row accumulator corruption";
258 auto polynomials = build_valid_eccvm_msm_state();
261 const size_t num_rows = polynomials.get_polynomial_size();
262 size_t no_op_row = 0;
264 if (polynomials.msm_add.get(i) ==
FF(0) && polynomials.msm_double.get(i) ==
FF(0) &&
265 polynomials.msm_skew.get(i) ==
FF(0) && polynomials.msm_transition.get(i) ==
FF(0) &&
266 polynomials.lagrange_first.get(i) ==
FF(0)) {
271 ASSERT_NE(no_op_row, 0) <<
"Should find a no-op row in the MSM table";
273 expand_shiftable_to_virtual_size(polynomials.msm_accumulator_x);
274 expand_shiftable_to_virtual_size(polynomials.msm_accumulator_y);
277 polynomials.set_shifted();
281 EXPECT_FALSE(failures.empty()) <<
"MSM relation should fail after no-op accumulator corruption";
284 bool found_noop_subrelation_failure = failures.contains(45) || failures.contains(46);
285 EXPECT_TRUE(found_noop_subrelation_failure)
286 <<
"Failure should be detected by subrelations 45/46 (no-op accumulator preservation)";
307 auto polynomials = build_valid_eccvm_msm_state();
313 EXPECT_TRUE(baseline.empty()) <<
"Baseline MSM relation should pass";
315 auto msm_polys = get_msm_polynomials(polynomials);
319 for (
auto* poly : msm_polys) {
320 for (
size_t k = poly->end_index() - 1; k >= ofs + 2; k--) {
321 poly->at(k) = (*poly)[k - 1];
323 poly->at(ofs + 1) =
FF(0);
327 polynomials.msm_size_of_msm.at(ofs + 1) = polynomials.msm_pc.get(ofs + 1) - polynomials.msm_pc.get(ofs + 2);
330 polynomials.set_shifted();
334 EXPECT_FALSE(failures.empty()) <<
"MSM relation should fail after shifting MSM table by one row";
337 for (
const auto& [subrelation_idx, row_idx] : failures) {
338 info(
"Shifted MSM table: subrelation ", subrelation_idx,
" first failed at row ", row_idx);
341 EXPECT_TRUE(failures.contains(45)) <<
"Subrelation 45 (no-op acc_x preservation) should fail";
342 EXPECT_TRUE(failures.contains(46)) <<
"Subrelation 46 (no-op acc_y preservation) should fail";
347 auto full_params = compute_full_relation_params(polynomials);
352 EXPECT_TRUE(transcript_failures.empty()) <<
"ECCVMTranscriptRelation should still pass";
356 EXPECT_TRUE(point_table_failures.empty()) <<
"ECCVMPointTableRelation should still pass";
360 EXPECT_TRUE(wnaf_failures.empty()) <<
"ECCVMWnafRelation should still pass";
364 EXPECT_TRUE(bools_failures.empty()) <<
"ECCVMBoolsRelation should still pass";
372 EXPECT_FALSE(set_failures.empty()) <<
"ECCVMSetRelation should also fail (MSM output tuples are shifted)";
379 EXPECT_TRUE(lookup_failures.empty()) <<
"ECCVMLookupRelation should still pass (inverse computed post-shift)";
392 auto polynomials = build_valid_eccvm_msm_state();
397 EXPECT_TRUE(baseline.empty()) <<
"Baseline transcript relation should pass";
399 size_t noop_row = find_transcript_noop_row(polynomials);
400 ASSERT_NE(noop_row, 0) <<
"Should find a transcript no-op row";
404 expand_shiftable_to_virtual_size(polynomials.transcript_accumulator_not_empty);
405 polynomials.transcript_accumulator_not_empty.at(noop_row + 1) =
FF(1);
406 polynomials.set_shifted();
410 EXPECT_FALSE(failures.empty()) <<
"Transcript relation should fail after corrupting accumulator_not_empty on "
411 "the row following a no-op";
413 <<
"ACCUMULATOR_EMPTY_UPDATE subrelation should catch the corruption";
429 auto polynomials = build_valid_eccvm_msm_state();
430 auto params = compute_full_relation_params(polynomials);
435 EXPECT_TRUE(baseline.empty()) <<
"Baseline shiftable init relation should pass";
438 ASSERT_TRUE(polynomials.z_perm.is_shiftable());
442 const auto& lagrange_first = polynomials.lagrange_first;
443 size_t scanned_first_row = 0;
445 for (
size_t i = lagrange_first.start_index(); i < lagrange_first.end_index(); ++i) {
446 if (lagrange_first[i] !=
FF(0)) {
447 scanned_first_row = i;
452 ASSERT_TRUE(found) <<
"lagrange_first has no non-zero entry";
453 ASSERT_EQ(structural_first_row, scanned_first_row)
454 <<
"lagrange_first position doesn't match z_perm shiftable structure";
456 const size_t first_row = scanned_first_row;
459 polynomials.z_perm = polynomials.z_perm.full();
460 polynomials.z_perm_shift = polynomials.z_perm_shift.full();
462 ASSERT_EQ(polynomials.z_perm.get(first_row),
FF(0));
465 polynomials.z_perm.at(first_row) =
FF(1);
468 polynomials, params,
"ECCVMShiftableInitRelation - After z_perm != 0 at lagrange_first",
Flavor::TRACE_OFFSET);
469 EXPECT_FALSE(failures.empty()) <<
"Shiftable init relation should fail after z_perm corruption";
471 <<
"Sub-relation Z_PERM_INIT should catch the corruption";
473 <<
"Failure should be at lagrange_first row";
537 auto polynomials = build_valid_eccvm_msm_state();
542 ASSERT_TRUE(baseline.empty()) <<
"Baseline MSM relation should pass";
546 const size_t num_rows = polynomials.get_polynomial_size();
547 size_t target_row = 0;
549 if (polynomials.msm_double[i + 1] ==
FF(1)) {
554 ASSERT_NE(target_row, 0U) <<
"Should find a row preceding a doubling row";
555 ASSERT_NE(polynomials.msm_round[target_row],
FF(31)) <<
"Honest predecessors of double rows have round != 31";
556 ASSERT_NE(polynomials.msm_round_minus_31_inv[target_row],
FF(0))
557 <<
"Honest inverse witness should be non-zero where round != 31";
559 polynomials.msm_round_minus_31_inv.at(target_row) =
FF(0);
560 polynomials.set_shifted();
564 EXPECT_FALSE(failures.empty()) <<
"MSM relation should fail without the round != 31 witness";
566 <<
"DOUBLE_SHIFT_FORBIDS_ROUND_31 should be the failing subrelation";
610 auto polynomials = build_valid_eccvm_msm_state();
611 auto params = compute_full_relation_params(polynomials);
622 ASSERT_EQ(polynomials.msm_transition[first_msm_row],
FF(1));
623 ASSERT_EQ(polynomials.msm_add[first_msm_row],
FF(1));
624 polynomials.msm_transition.at(first_msm_row) =
FF(0);
625 polynomials.set_shifted();
628 auto params_after = compute_full_relation_params(polynomials);
632 EXPECT_FALSE(msm_failures.empty()) <<
"MSM-start anchor should reject msm_transition[first_msm_row] = 0";
634 <<
"The rejecting subrelation should be the MSM-start anchor";
658 auto polynomials = build_valid_eccvm_msm_state();
663 EXPECT_TRUE(baseline.empty()) <<
"Baseline MSM relation should pass";
668 const size_t num_rows = polynomials.get_polynomial_size();
669 size_t tamper_row = 0;
671 const bool curr_is_add = polynomials.msm_add[i] ==
FF(1);
672 const bool prev_is_active = polynomials.msm_add[i - 1] ==
FF(1) || polynomials.msm_double[i - 1] ==
FF(1);
673 const bool not_segment_boundary = polynomials.msm_transition[i] ==
FF(0);
674 if (curr_is_add && prev_is_active && not_segment_boundary) {
679 ASSERT_NE(tamper_row, 0) <<
"Should find an interior ADD row with an active predecessor";
681 polynomials.msm_pc.at(tamper_row) = polynomials.msm_pc[tamper_row] +
FF(0xdead);
682 polynomials.set_shifted();
686 EXPECT_FALSE(failures.empty()) <<
"MSM relation should reject msm_pc tamper on an interior row";
688 <<
"MSM_PC_CONTINUITY should be among the failing subrelations";
703 auto polynomials = build_valid_eccvm_large_msm_state(10);
708 EXPECT_TRUE(baseline.empty()) <<
"Baseline MSM relation should pass";
714 const size_t num_rows = polynomials.get_polynomial_size();
715 size_t tamper_row = 0;
717 if (polynomials.msm_skew[i - 1] ==
FF(1) && polynomials.msm_skew[i] ==
FF(1) &&
718 polynomials.msm_skew[i + 1] ==
FF(1)) {
723 ASSERT_NE(tamper_row, 0) <<
"Should find an interior skew row (msm_size >= 9 gives >= 3 skew rows)";
725 polynomials.msm_pc.at(tamper_row) = polynomials.msm_pc[tamper_row] +
FF(0xdead);
726 polynomials.set_shifted();
730 EXPECT_FALSE(failures.empty()) <<
"MSM relation should reject msm_pc tamper on an interior skew row";
732 <<
"MSM_PC_SKEW_CONTINUITY should be among the failing subrelations";
static void SetUpTestSuite()
A container for the prover polynomials.
typename Curve::ScalarField FF
bb::Polynomial< FF > Polynomial
static constexpr size_t TRACE_OFFSET
MSM relations that evaluate the Strauss multiscalar multiplication algorithm.
Pins every shiftable wire to 0 at the lagrange_first row (TRACE_OFFSET).
ECCVMTranscriptRelationImpl evaluates the correctness of the ECCVM transcript columns.
size_t start_index() const
std::size_t virtual_size() const
A debugging utility for checking whether a set of polynomials satisfies the relations for a given Fla...
A wrapper for Relations to expose methods used by the Sumcheck prover or verifier to add the contribu...
typename ECCVMFlavor::ProverPolynomials ProverPolynomials
void add_hiding_op_for_test(const std::shared_ptr< ECCOpQueue > &op_queue)
Set a hiding op on the op_queue for testing.
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
group< fq, fr, Bn254G1Params > g1
TEST_F(IPATest, ChallengesAreZero)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Container for parameters used by the grand product (permutation, lookup) Honk relations.
constexpr field invert() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
BB_INLINE constexpr field sqr() const noexcept