Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
eccvm_relation_corruption.test.cpp
Go to the documentation of this file.
1
13#include <gtest/gtest.h>
14
15using namespace bb;
16
17namespace {
18
19using Flavor = ECCVMFlavor;
20using FF = typename Flavor::FF;
21using G1 = bb::g1;
22using Fr = typename G1::Fr;
23using Polynomial = typename Flavor::Polynomial;
26
28
29void expand_shiftable_to_virtual_size(Polynomial& polynomial)
30{
31 polynomial = Polynomial(polynomial, polynomial.virtual_size() - polynomial.start_index());
32}
33
39std::vector<Polynomial*> get_msm_polynomials(ProverPolynomials& polys)
40{
41 return {
42 // From WireNonShiftedEntities (columns 21-44)
43 &polys.msm_size_of_msm,
44 &polys.msm_add2,
45 &polys.msm_add3,
46 &polys.msm_add4,
47 &polys.msm_x1,
48 &polys.msm_y1,
49 &polys.msm_x2,
50 &polys.msm_y2,
51 &polys.msm_x3,
52 &polys.msm_y3,
53 &polys.msm_x4,
54 &polys.msm_y4,
55 &polys.msm_collision_x1,
56 &polys.msm_collision_x2,
57 &polys.msm_collision_x3,
58 &polys.msm_collision_x4,
59 &polys.msm_lambda1,
60 &polys.msm_lambda2,
61 &polys.msm_lambda3,
62 &polys.msm_lambda4,
63 &polys.msm_slice1,
64 &polys.msm_slice2,
65 &polys.msm_slice3,
66 &polys.msm_slice4,
67 // From WireToBeShiftedWithoutAccumulatorsEntities (columns 68-77)
68 &polys.msm_transition,
69 &polys.msm_add,
70 &polys.msm_double,
71 &polys.msm_skew,
72 &polys.msm_accumulator_x,
73 &polys.msm_accumulator_y,
74 &polys.msm_count,
75 &polys.msm_round,
76 &polys.msm_add1,
77 &polys.msm_pc,
78 &polys.msm_round_minus_31_inv,
79 };
80}
81
85ProverPolynomials build_valid_eccvm_msm_state()
86{
87 auto generators = G1::derive_generators("test generators", 3);
88 auto a = generators[0];
89 auto b = generators[1];
92
93 auto op_queue = std::make_shared<ECCOpQueue>();
94 op_queue->mul_accumulate(a, x);
95 op_queue->mul_accumulate(b, y);
96 op_queue->eq_and_reset();
97 op_queue->merge();
98 add_hiding_op_for_test(op_queue);
99
100 ECCVMCircuitBuilder builder{ op_queue };
102}
103
111ProverPolynomials build_valid_eccvm_large_msm_state(size_t num_points)
112{
113 auto generators = G1::derive_generators("test generators", num_points);
114
115 auto op_queue = std::make_shared<ECCOpQueue>();
116 for (size_t i = 0; i < num_points; i++) {
117 op_queue->mul_accumulate(generators[i], Fr::random_element(&engine));
118 }
119 op_queue->eq_and_reset();
120 op_queue->merge();
121 add_hiding_op_for_test(op_queue);
122
123 ECCVMCircuitBuilder builder{ op_queue };
125}
126
131RelationParameters<FF> compute_full_relation_params(ProverPolynomials& polynomials)
132{
133 const FF beta = FF::random_element(&engine);
134 const FF gamma = FF::random_element(&engine);
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();
140
142 .eta = 0,
143 .beta = beta,
144 .gamma = gamma,
145 .public_input_delta = 0,
146 .beta_sqr = beta_sqr,
147 .beta_cube = beta_cube,
148 .eccvm_set_permutation_delta = eccvm_set_permutation_delta,
149 };
150
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());
154
155 return params;
156}
157
161size_t find_transcript_noop_row(const ProverPolynomials& polynomials)
162{
163 const size_t num_rows = polynomials.get_polynomial_size();
164 for (size_t i = Flavor::TRACE_OFFSET; i < num_rows - 1; i++) {
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)) {
168 return i;
169 }
170 }
171 return 0;
172}
173
174} // anonymous namespace
175
176class ECCVMRelationCorruptionTests : public ::testing::Test {
177 protected:
179};
180
190TEST_F(ECCVMRelationCorruptionTests, MSMAccumulatorCorruptionAtTransitionRowIsHarmless)
191{
192 auto polynomials = build_valid_eccvm_msm_state();
193 RelationParameters<FF> params{};
194
196 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
197 EXPECT_TRUE(baseline.empty()) << "Baseline MSM relation should pass";
198
199 // Confirm the first active MSM row is the transition row (offset by disabled head region)
200 constexpr size_t first_msm_row = Flavor::TRACE_OFFSET + 1;
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";
203
204 // Corrupt the accumulator at the transition row
205 polynomials.msm_accumulator_x.at(first_msm_row) = FF::random_element(&engine);
206 polynomials.msm_accumulator_y.at(first_msm_row) = FF::random_element(&engine);
207 polynomials.set_shifted();
208
210 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
211 EXPECT_TRUE(failures.empty()) << "MSM relation should STILL PASS — acc is unused when msm_transition=1";
212}
213
224TEST_F(ECCVMRelationCorruptionTests, MSMAccumulatorCorruptionAtInteriorAndNoOpRows)
225{
226 RelationParameters<FF> params{};
227
228 // --- Part 1: corrupt the accumulator at an interior active MSM row (q_add=1, msm_transition=0) ---
229 {
230 auto polynomials = build_valid_eccvm_msm_state();
231
233 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
234 EXPECT_TRUE(baseline.empty()) << "Baseline MSM relation should pass";
235
236 // Find an interior addition row: q_add=1, msm_transition=0
237 const size_t num_rows = polynomials.get_polynomial_size();
238 size_t active_row = 0;
239 for (size_t i = Flavor::TRACE_OFFSET; i < num_rows - 1; i++) {
240 if (polynomials.msm_add.get(i) == FF(1) && polynomials.msm_transition.get(i) == FF(0)) {
241 active_row = i;
242 break;
243 }
244 }
245 ASSERT_NE(active_row, 0) << "Should find an interior active MSM add row";
246
247 polynomials.msm_accumulator_x.at(active_row) = FF::random_element(&engine);
248 polynomials.msm_accumulator_y.at(active_row) = FF::random_element(&engine);
249 polynomials.set_shifted();
250
252 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
253 EXPECT_FALSE(failures.empty()) << "MSM relation should fail after active-row accumulator corruption";
254 }
255
256 // --- Part 2: corrupt the accumulator at a trailing no-op row ---
257 {
258 auto polynomials = build_valid_eccvm_msm_state();
259
260 // Find the first no-op row (all MSM selectors zero, not lagrange_first)
261 const size_t num_rows = polynomials.get_polynomial_size();
262 size_t no_op_row = 0;
263 for (size_t i = Flavor::TRACE_OFFSET; i < num_rows - 1; i++) {
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)) {
267 no_op_row = i;
268 break;
269 }
270 }
271 ASSERT_NE(no_op_row, 0) << "Should find a no-op row in the MSM table";
272
273 expand_shiftable_to_virtual_size(polynomials.msm_accumulator_x);
274 expand_shiftable_to_virtual_size(polynomials.msm_accumulator_y);
275 polynomials.msm_accumulator_x.at(no_op_row) = FF::random_element(&engine);
276 polynomials.msm_accumulator_y.at(no_op_row) = FF::random_element(&engine);
277 polynomials.set_shifted();
278
280 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
281 EXPECT_FALSE(failures.empty()) << "MSM relation should fail after no-op accumulator corruption";
282
283 // The failure should be in subrelations 45 or 46 (the no-op accumulator preservation constraints)
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)";
287 }
288}
289
305TEST_F(ECCVMRelationCorruptionTests, MSMRelationFailsOnShiftedMSMTable)
306{
307 auto polynomials = build_valid_eccvm_msm_state();
308 RelationParameters<FF> params{};
309
310 // Baseline: MSM relation passes on clean data
312 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
313 EXPECT_TRUE(baseline.empty()) << "Baseline MSM relation should pass";
314
315 auto msm_polys = get_msm_polynomials(polynomials);
316
317 // Shift every MSM column down by 1 within the active region
318 constexpr size_t ofs = Flavor::TRACE_OFFSET;
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];
322 }
323 poly->at(ofs + 1) = FF(0);
324 }
325
326 // Patch msm_size_of_msm at the injected row so the pc-continuity constraint is satisfied
327 polynomials.msm_size_of_msm.at(ofs + 1) = polynomials.msm_pc.get(ofs + 1) - polynomials.msm_pc.get(ofs + 2);
328
329 // Refresh shifted views
330 polynomials.set_shifted();
331
333 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
334 EXPECT_FALSE(failures.empty()) << "MSM relation should fail after shifting MSM table by one row";
335
336 // Log all failing subrelations for visibility
337 for (const auto& [subrelation_idx, row_idx] : failures) {
338 info("Shifted MSM table: subrelation ", subrelation_idx, " first failed at row ", row_idx);
339 }
340
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";
343
344 // Verify that all other ECCVM relations still pass after the shift.
345 // We compute random Fiat-Shamir challenges and derived polynomials (logderivative inverse, grand product)
346 // so we can also check ECCVMSetRelation and ECCVMLookupRelation.
347 auto full_params = compute_full_relation_params(polynomials);
348
349 // Relations that don't touch MSM columns should be completely unaffected.
351 polynomials, full_params, "ECCVMTranscriptRelation", Flavor::TRACE_OFFSET);
352 EXPECT_TRUE(transcript_failures.empty()) << "ECCVMTranscriptRelation should still pass";
353
355 polynomials, full_params, "ECCVMPointTableRelation", Flavor::TRACE_OFFSET);
356 EXPECT_TRUE(point_table_failures.empty()) << "ECCVMPointTableRelation should still pass";
357
359 polynomials, full_params, "ECCVMWnafRelation", Flavor::TRACE_OFFSET);
360 EXPECT_TRUE(wnaf_failures.empty()) << "ECCVMWnafRelation should still pass";
361
363 polynomials, full_params, "ECCVMBoolsRelation", Flavor::TRACE_OFFSET);
364 EXPECT_TRUE(bools_failures.empty()) << "ECCVMBoolsRelation should still pass";
365
366 // The Set relation enforces a multiset equality between MSM output tuples (pc, acc_x, acc_y, msm_size)
367 // and the transcript. Shifting the MSM columns corrupts these tuples, so the grand product (computed
368 // post-shift) reflects mismatched reads/writes and the relation correctly fails. It is possible that with more
369 // care, we could make this also pass.
371 polynomials, full_params, "ECCVMSetRelation", Flavor::TRACE_OFFSET);
372 EXPECT_FALSE(set_failures.empty()) << "ECCVMSetRelation should also fail (MSM output tuples are shifted)";
373
374 // The Lookup relation's logderivative inverse is computed post-shift, so it adapts to the
375 // shifted column values. The per-row subrelation passes, and the sum-over-trace (linearly
376 // dependent) subrelation also vanishes since the inverse was derived from the current data.
377 auto lookup_failures = RelationChecker<void>::check<ECCVMLookupRelation<FF>, /*has_linearly_dependent=*/true>(
378 polynomials, full_params, "ECCVMLookupRelation", Flavor::TRACE_OFFSET);
379 EXPECT_TRUE(lookup_failures.empty()) << "ECCVMLookupRelation should still pass (inverse computed post-shift)";
380}
381
390TEST_F(ECCVMRelationCorruptionTests, TranscriptNoOpRowRejectsAccumulatorNotEmpty)
391{
392 auto polynomials = build_valid_eccvm_msm_state();
393 RelationParameters<FF> params{};
394
396 polynomials, params, "ECCVMTranscriptRelation", Flavor::TRACE_OFFSET);
397 EXPECT_TRUE(baseline.empty()) << "Baseline transcript relation should pass";
398
399 size_t noop_row = find_transcript_noop_row(polynomials);
400 ASSERT_NE(noop_row, 0) << "Should find a transcript no-op row";
401
402 // The no-op constraint at row `noop_row` constrains is_accumulator_empty_shift,
403 // which reads from accumulator_not_empty at row `noop_row + 1`.
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();
407
409 polynomials, params, "ECCVMTranscriptRelation", Flavor::TRACE_OFFSET);
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";
414}
415
427TEST_F(ECCVMRelationCorruptionTests, ShiftableInitFailsOnZPermNonZeroAtFirstRow)
428{
429 auto polynomials = build_valid_eccvm_msm_state();
430 auto params = compute_full_relation_params(polynomials);
431
432 // Baseline: the shiftable init relation passes
434 polynomials, params, "ECCVMShiftableInitRelation", Flavor::TRACE_OFFSET);
435 EXPECT_TRUE(baseline.empty()) << "Baseline shiftable init relation should pass";
436
437 // Derive expected lagrange_first position from z_perm shiftable structure
438 ASSERT_TRUE(polynomials.z_perm.is_shiftable());
439 size_t structural_first_row = Flavor::TRACE_OFFSET;
440
441 // Independently scan lagrange_first for its non-zero entry
442 const auto& lagrange_first = polynomials.lagrange_first;
443 size_t scanned_first_row = 0;
444 bool found = false;
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;
448 found = true;
449 break;
450 }
451 }
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";
455
456 const size_t first_row = scanned_first_row;
457
458 // Expand to full polynomials so we can write at index 0
459 polynomials.z_perm = polynomials.z_perm.full();
460 polynomials.z_perm_shift = polynomials.z_perm_shift.full();
461
462 ASSERT_EQ(polynomials.z_perm.get(first_row), FF(0));
463
464 // Tamper: set z_perm to non-zero where lagrange_first is active
465 polynomials.z_perm.at(first_row) = FF(1);
466
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";
470 EXPECT_TRUE(failures.contains(ECCVMShiftableInitRelationImpl<FF>::Z_PERM_INIT))
471 << "Sub-relation Z_PERM_INIT should catch the corruption";
472 EXPECT_EQ(failures.at(ECCVMShiftableInitRelationImpl<FF>::Z_PERM_INIT), first_row)
473 << "Failure should be at lagrange_first row";
474}
475
535TEST_F(ECCVMRelationCorruptionTests, MSMRelationRejectsMissingRoundMinus31Inverse)
536{
537 auto polynomials = build_valid_eccvm_msm_state();
538 RelationParameters<FF> params{};
539
541 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
542 ASSERT_TRUE(baseline.empty()) << "Baseline MSM relation should pass";
543
544 // Find a row whose successor is an MSM double. The new gate constrains row k (carrying
545 // round[k] and round_minus_31_inv[k]) whenever q_double[k+1] = 1.
546 const size_t num_rows = polynomials.get_polynomial_size();
547 size_t target_row = 0;
548 for (size_t i = Flavor::TRACE_OFFSET; i + 1 < num_rows; ++i) {
549 if (polynomials.msm_double[i + 1] == FF(1)) {
550 target_row = i;
551 break;
552 }
553 }
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";
558
559 polynomials.msm_round_minus_31_inv.at(target_row) = FF(0);
560 polynomials.set_shifted();
561
563 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
564 EXPECT_FALSE(failures.empty()) << "MSM relation should fail without the round != 31 witness";
565 EXPECT_TRUE(failures.contains(ECCVMMSMRelationImpl<FF>::DOUBLE_SHIFT_FORBIDS_ROUND_31))
566 << "DOUBLE_SHIFT_FORBIDS_ROUND_31 should be the failing subrelation";
567}
568
608TEST_F(ECCVMRelationCorruptionTests, MSMRelationRejectsTransitionZeroOnFirstRow)
609{
610 auto polynomials = build_valid_eccvm_msm_state();
611 auto params = compute_full_relation_params(polynomials);
612
613 EXPECT_TRUE(
614 RelationChecker<void>::check<ECCVMMSMRelation<FF>>(polynomials, params, "MSM", Flavor::TRACE_OFFSET).empty());
615 EXPECT_TRUE(
616 RelationChecker<void>::check<ECCVMSetRelation<FF>>(polynomials, params, "Set", Flavor::TRACE_OFFSET).empty());
617 EXPECT_TRUE((RelationChecker<void>::check<ECCVMLookupRelation<FF>, /*has_linearly_dependent=*/true>(
618 polynomials, params, "Lookup", Flavor::TRACE_OFFSET)
619 .empty()));
620
621 constexpr size_t first_msm_row = Flavor::TRACE_OFFSET + 1;
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();
626
627 // Recompute logderivative inverse / grand product since msm_transition feeds into them.
628 auto params_after = compute_full_relation_params(polynomials);
629
630 auto msm_failures =
632 EXPECT_FALSE(msm_failures.empty()) << "MSM-start anchor should reject msm_transition[first_msm_row] = 0";
633 EXPECT_TRUE(msm_failures.contains(ECCVMMSMRelationImpl<FF>::MSM_TRANSITION_AT_ACTIVE_START))
634 << "The rejecting subrelation should be the MSM-start anchor";
635
636 // The rejection is exclusively in the MSM relation: the other relations have no role anchoring
637 // msm_transition at the first MSM row.
638 EXPECT_TRUE(
640 .empty());
641 EXPECT_TRUE((RelationChecker<void>::check<ECCVMLookupRelation<FF>, /*has_linearly_dependent=*/true>(
642 polynomials, params_after, "Lookup", Flavor::TRACE_OFFSET)
643 .empty()));
644}
645
656TEST_F(ECCVMRelationCorruptionTests, MSMRelationRejectsInteriorMsmPcTamper)
657{
658 auto polynomials = build_valid_eccvm_msm_state();
659 RelationParameters<FF> params{};
660
662 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
663 EXPECT_TRUE(baseline.empty()) << "Baseline MSM relation should pass";
664
665 // Find an interior ADD row such that the previous row is also active and not a segment
666 // boundary (msm_transition_shift on the previous row is 0). MSM_PC_CONTINUITY will fire at
667 // that previous row when we tamper msm_pc on the chosen row.
668 const size_t num_rows = polynomials.get_polynomial_size();
669 size_t tamper_row = 0;
670 for (size_t i = Flavor::TRACE_OFFSET + 2; i < num_rows - 1; i++) {
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) {
675 tamper_row = i;
676 break;
677 }
678 }
679 ASSERT_NE(tamper_row, 0) << "Should find an interior ADD row with an active predecessor";
680
681 polynomials.msm_pc.at(tamper_row) = polynomials.msm_pc[tamper_row] + FF(0xdead);
682 polynomials.set_shifted();
683
685 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
686 EXPECT_FALSE(failures.empty()) << "MSM relation should reject msm_pc tamper on an interior row";
687 EXPECT_TRUE(failures.contains(ECCVMMSMRelationImpl<FF>::MSM_PC_CONTINUITY))
688 << "MSM_PC_CONTINUITY should be among the failing subrelations";
689}
690
701TEST_F(ECCVMRelationCorruptionTests, MSMRelationRejectsInteriorSkewMsmPcTamper)
702{
703 auto polynomials = build_valid_eccvm_large_msm_state(/*num_points=*/10);
704 RelationParameters<FF> params{};
705
707 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
708 EXPECT_TRUE(baseline.empty()) << "Baseline MSM relation should pass";
709
710 // Find an interior skew row: q_skew = 1 on the previous, current and next rows. Tampering
711 // msm_pc here is caught only by MSM_PC_SKEW_CONTINUITY (firing at the previous skew row), since
712 // neither MSM_PC_CONTINUITY (q_skew excluded) nor MSM_TRANSITION_PC (msm_transition_shift = 0)
713 // constrains it.
714 const size_t num_rows = polynomials.get_polynomial_size();
715 size_t tamper_row = 0;
716 for (size_t i = Flavor::TRACE_OFFSET + 1; i < num_rows - 1; i++) {
717 if (polynomials.msm_skew[i - 1] == FF(1) && polynomials.msm_skew[i] == FF(1) &&
718 polynomials.msm_skew[i + 1] == FF(1)) {
719 tamper_row = i;
720 break;
721 }
722 }
723 ASSERT_NE(tamper_row, 0) << "Should find an interior skew row (msm_size >= 9 gives >= 3 skew rows)";
724
725 polynomials.msm_pc.at(tamper_row) = polynomials.msm_pc[tamper_row] + FF(0xdead);
726 polynomials.set_shifted();
727
729 polynomials, params, "ECCVMMSMRelation", Flavor::TRACE_OFFSET);
730 EXPECT_FALSE(failures.empty()) << "MSM relation should reject msm_pc tamper on an interior skew row";
731 EXPECT_TRUE(failures.contains(ECCVMMSMRelationImpl<FF>::MSM_PC_SKEW_CONTINUITY))
732 << "MSM_PC_SKEW_CONTINUITY should be among the failing subrelations";
733}
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...
#define info(...)
Definition log.hpp:93
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
typename ECCVMFlavor::ProverPolynomials ProverPolynomials
numeric::RNG & engine
void add_hiding_op_for_test(const std::shared_ptr< ECCOpQueue > &op_queue)
Set a hiding op on the op_queue for testing.
const size_t num_points
RNG & get_debug_randomness(bool reset, std::uint_fast64_t seed)
Definition engine.cpp:245
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
group< fq, fr, Bn254G1Params > g1
Definition g1.hpp:34
TEST_F(IPATest, ChallengesAreZero)
Definition ipa.test.cpp:160
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::AffineElement G1
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