Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
permutation.test.cpp
Go to the documentation of this file.
4#include "ultra_honk.test.hpp"
5
6using namespace bb;
7
8#ifdef STARKNET_GARAGA_FLAVORS
9using FlavorTypes = testing::Types<UltraFlavor,
14 UltraStarknetFlavor,
15 UltraStarknetZKFlavor>;
16#else
18 testing::Types<UltraFlavor, UltraZKFlavor, UltraKeccakFlavor, UltraKeccakZKFlavor, UltraRollupFlavor>;
19#endif
20template <typename T> using PermutationTests = UltraHonkTests<T>;
22using NonZKFlavorTypes = testing::Types<UltraFlavor, UltraKeccakFlavor, UltraRollupFlavor>;
23template <typename T> using PermutationNonZKTests = UltraHonkTests<T>;
25
39TYPED_TEST(PermutationTests, NonTrivialTagPermutation)
40{
42
43 // Create two distinct values
45 fr y = -x;
46
47 // first multiset {x, y}
48 auto x1_idx = builder.add_variable(x);
49 auto y1_idx = builder.add_variable(y);
50
51 // second multiset {y, x}
52 auto y2_idx = builder.add_variable(y);
53 auto x2_idx = builder.add_variable(x);
54
55 // Dummy gates to include variables in the trace
56 builder.create_add_gate({ x1_idx, y1_idx, builder.zero_idx(), 1, 1, 0, 0 });
57 builder.create_add_gate({ y2_idx, x2_idx, builder.zero_idx(), 1, 1, 0, 0 });
58
59 // Set up tag transposition: first_tag <-> second_tag
60 auto first_tag = builder.get_new_tag();
61 auto second_tag = builder.get_new_tag();
62 builder.set_tau_transposition(first_tag, second_tag);
63
64 // Assign tags: first_tag -> {x, y}, second_tag -> {y, x}
65 builder.assign_tag(x1_idx, first_tag);
66 builder.assign_tag(y1_idx, first_tag);
67 builder.assign_tag(y2_idx, second_tag);
68 builder.assign_tag(x2_idx, second_tag);
69
70 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
71 TestFixture::prove_and_verify(builder, /*expected_result=*/true);
72}
73
85TYPED_TEST(PermutationTests, NonTrivialGeneralizedPerm)
86{
88
90 fr y = -x;
91
92 // Helper to create a pair of equal variables (linked by copy constraint)
93 auto add_equal_pair = [&](fr value) {
94 auto idx1 = builder.add_variable(value);
95 auto idx2 = builder.add_variable(value);
96 builder.assert_equal(idx1, idx2);
97 return std::make_pair(idx1, idx2);
98 };
99
100 // Create pairs of equal variables
101 auto [x1_idx, x1_copy_idx] = add_equal_pair(x);
102 auto [y1_idx, y1_copy_idx] = add_equal_pair(y);
103 auto [x2_idx, x2_copy_idx] = add_equal_pair(x);
104 auto [y2_idx, y2_copy_idx] = add_equal_pair(y);
105
106 // Set up tag transposition for multiset-equality check
107 auto first_tag = builder.get_new_tag();
108 auto second_tag = builder.get_new_tag();
109 builder.set_tau_transposition(first_tag, second_tag);
110
111 // first_tag -> {x, y}, second_tag -> {x, y} (same multisets)
112 builder.assign_tag(x1_idx, first_tag);
113 builder.assign_tag(y1_idx, first_tag);
114 builder.assign_tag(x2_idx, second_tag);
115 builder.assign_tag(y2_idx, second_tag);
116
117 // Dummy gates using copy variables (z1 - z2 = 0)
118 builder.create_add_gate({ x1_copy_idx, x1_idx, builder.zero_idx(), 1, -1, 0, 0 });
119 builder.create_add_gate({ y1_idx, y2_idx, builder.zero_idx(), 1, -1, 0, 0 });
120 builder.create_add_gate({ x2_idx, x2_copy_idx, builder.zero_idx(), 1, -1, 0, 0 });
121
122 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
123 TestFixture::prove_and_verify(builder, /*expected_result=*/true);
124}
125
137TYPED_TEST(PermutationTests, BadTagPermutation)
138{
139 // With tags: multisets {x, y} vs {y, x+1} are NOT equal, so verification should FAIL
140 {
142
144 fr y = -x;
145
146 // multiset: {x, y}
147 auto x1_idx = builder.add_variable(x);
148 auto y1_idx = builder.add_variable(y);
149
150 // multiset: {y, x+1}
151 auto y2_idx = builder.add_variable(y);
152 auto x_plus_1_idx = builder.add_variable(x + 1);
153
154 // Dummy gates: x + y = 0, y + (x+1) = 1
155 builder.create_add_gate({ x1_idx, y1_idx, builder.zero_idx(), 1, 1, 0, 0 });
156 builder.create_add_gate({ y2_idx, x_plus_1_idx, builder.zero_idx(), 1, 1, 0, -1 });
157
158 auto first_tag = builder.get_new_tag();
159 auto second_tag = builder.get_new_tag();
160 builder.set_tau_transposition(first_tag, second_tag);
161
162 builder.assign_tag(x1_idx, first_tag);
163 builder.assign_tag(y1_idx, first_tag);
164 builder.assign_tag(y2_idx, second_tag);
165 builder.assign_tag(x_plus_1_idx, second_tag);
166
167 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
168 TestFixture::prove_and_verify(builder, /*expected_result=*/false);
169 }
170 // Without tags: same circuit passes, confirming failure above is due to tag mismatch
171 {
173
175 fr y = -x;
176
177 auto x1_idx = builder.add_variable(x);
178 auto y1_idx = builder.add_variable(y);
179 auto y2_idx = builder.add_variable(y);
180 auto x_plus_1_idx = builder.add_variable(x + 1);
181
182 builder.create_add_gate({ x1_idx, y1_idx, builder.zero_idx(), 1, 1, 0, 0 });
183 builder.create_add_gate({ y2_idx, x_plus_1_idx, builder.zero_idx(), 1, 1, 0, -1 });
184
185 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
186 TestFixture::prove_and_verify(builder, /*expected_result=*/true);
187 }
188}
189
207TYPED_TEST(PermutationNonZKTests, ZPermZeroedOutFailure)
208{
209 using Flavor = TypeParam;
210 using Builder = typename Flavor::CircuitBuilder;
211
214
215 using Prover = TestFixture::Prover;
216
218
219 auto a = fr::random_element();
220 auto b = fr::random_element();
221 auto c = a + b;
222
223 uint32_t a_idx = builder.add_variable(a);
224 uint32_t a_copy_idx = builder.add_variable(a);
225 uint32_t b_idx = builder.add_variable(b);
226 uint32_t c_idx = builder.add_variable(c);
227
228 builder.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 });
229 builder.create_add_gate({ a_copy_idx, b_idx, c_idx, 1, 1, -1, 0 });
230 builder.assert_equal(a_copy_idx, a_idx);
231
232 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
233
234 auto prover_instance = std::make_shared<ProverInstance>(builder);
235 auto verification_key = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
236
237 Prover prover(prover_instance, verification_key);
238 auto proof = prover.construct_proof();
239 auto& z_perm = prover_instance->polynomials.z_perm;
240
241 // First verify that the Permutation relation holds.
242 auto permutation_relation_failures = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
243 prover_instance->polynomials, prover_instance->relation_parameters, "UltraPermutation - Before Tampering");
244 EXPECT_TRUE(permutation_relation_failures.empty());
245
246 // Tamper: zero-out z_perm
247 for (size_t i = z_perm.start_index(); i < z_perm.end_index(); ++i) {
248 z_perm.at(i) = fr(0);
249 }
250 prover_instance->polynomials.set_shifted();
251 auto tampered_permutation_relation_failures = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
252 prover_instance->polynomials,
253 prover_instance->relation_parameters,
254 "UltraPermutation - After zeroing out z_perm");
255 // Verify that the Permutation relation now fails
256 EXPECT_FALSE(tampered_permutation_relation_failures.empty());
257}
258
273TYPED_TEST(PermutationNonZKTests, ZPermShiftNotZeroAtLagrangeLastFailure)
274{
275 using Flavor = TypeParam;
276 using Builder = typename Flavor::CircuitBuilder;
277
280
281 using Prover = TestFixture::Prover;
282
284
285 auto a = fr::random_element();
286 auto b = fr::random_element();
287 auto c = a + b;
288
289 uint32_t a_idx = builder.add_variable(a);
290 uint32_t a_copy_idx = builder.add_variable(a);
291 uint32_t b_idx = builder.add_variable(b);
292 uint32_t c_idx = builder.add_variable(c);
293
294 builder.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 });
295 builder.create_add_gate({ a_copy_idx, b_idx, c_idx, 1, 1, -1, 0 });
296 builder.assert_equal(a_copy_idx, a_idx);
297
298 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
299
300 auto prover_instance = std::make_shared<ProverInstance>(builder);
301 auto verification_key = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
302
303 Prover prover(prover_instance, verification_key);
304 auto proof = prover.construct_proof();
305
306 // first verify that the Permutation relation holds.
307 auto permutation_relation_failures = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
308 prover_instance->polynomials, prover_instance->relation_parameters, "UltraPermutation - Before Tampering");
309 EXPECT_TRUE(permutation_relation_failures.empty());
310 // we make z_perm and z_perm_shift full polynomials to tamper with values that are outside the usual allocated
311 // range. This allows us to failure test for the subrelation `z_perm_shift * lagrange_last == 0`.
312 auto& z_perm = prover_instance->polynomials.z_perm;
313 auto last_valid_index = z_perm.end_index();
314 auto& z_perm_shift = prover_instance->polynomials.z_perm_shift;
315 // make the polynomial full to tamper with a last value.
316 prover_instance->polynomials.z_perm = z_perm.full();
317 prover_instance->polynomials.z_perm_shift = z_perm_shift.full();
318
319 ASSERT_EQ(prover_instance->polynomials.lagrange_last.at(last_valid_index - 1), fr(1));
320 ASSERT_EQ(prover_instance->polynomials.z_perm.at(last_valid_index), fr(0));
321 ASSERT_EQ(prover_instance->polynomials.z_perm_shift.at(last_valid_index - 1), fr(0));
322 // Tamper: change `z_perm_shift` to something non-zero when `lagrange_last == 1`.
323 prover_instance->polynomials.z_perm_shift.at(last_valid_index - 1) += fr(1);
324 // Note that `z_perm_shift` and `z_perm` are no longer inextricably linked because we have replaced them by their
325 // full incarnations. Therefore, we still `z_perm.at(last_valid_index) == 0`. This does not effect the test we
326 // wish to check.
327
328 // Verify that the Permutation relation now fails.
329 auto tampered_permutation_relation_failures = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
330 prover_instance->polynomials,
331 prover_instance->relation_parameters,
332 "UltraPermutation - After incrementing z_perm_shift where lagrange_last is 1");
333 EXPECT_FALSE(tampered_permutation_relation_failures.empty());
334 // the first subrelation first fails at `row_idx == last_valid_index - 1`.
335 ASSERT_EQ(tampered_permutation_relation_failures[1], last_valid_index - 1);
336}
337
349TYPED_TEST(PermutationNonZKTests, SigmaCorruptionFailure)
350{
351 using Flavor = TypeParam;
354 using Prover = typename TestFixture::Prover;
355
357
358 // Create variables with a copy constraint
359 auto a = fr::random_element();
360 auto b = fr::random_element();
361 auto c = a + b;
362
363 uint32_t a_idx = builder.add_variable(a);
364 uint32_t a_copy_idx = builder.add_variable(a);
365 uint32_t b_idx = builder.add_variable(b);
366 uint32_t c_idx = builder.add_variable(c);
367
368 // Gates using a_idx and a_copy_idx (which should be equal)
369 builder.create_add_gate({ a_idx, b_idx, c_idx, 1, 1, -1, 0 });
370 builder.create_add_gate({ a_copy_idx, b_idx, c_idx, 1, 1, -1, 0 });
371 // copy cycle we'll break
372 builder.assert_equal(a_copy_idx, a_idx);
373
374 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
375
376 auto prover_instance = std::make_shared<ProverInstance>(builder);
377 auto verification_key = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
378
379 // Construct proof to compute z_perm
380 Prover prover(prover_instance, verification_key);
381 auto proof = prover.construct_proof();
382
383 // The Permutation relation holds before tampering
384 auto permutation_relation_failures = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
385 prover_instance->polynomials, prover_instance->relation_parameters, "Permutation Relation - Before Tampering");
386 ASSERT_TRUE(permutation_relation_failures.empty());
387
388 // TAMPER
389
390 // Corrupt sigma_1 at a row that's part of the copy cycle.
391 auto& sigma_1 = prover_instance->polynomials.sigma_1;
392 auto& id_1 = prover_instance->polynomials.id_1;
393
394 // Find the first row that's part of a non-trivial cycle (sigma != id)
395 size_t row_to_corrupt = 0;
396 for (size_t row = 1; row < sigma_1.size(); ++row) {
397 if (sigma_1.at(row) != id_1.at(row)) {
398 row_to_corrupt = row;
399 vinfo("Found copy cycle at row ", row, "; will corrupt this one");
400 break;
401 }
402 }
403 ASSERT_NE(row_to_corrupt, 0) << "No copy cycle found in sigma_1!";
404
405 fr original_value = sigma_1.at(row_to_corrupt);
406 sigma_1.at(row_to_corrupt) = original_value + fr(1); // Break the cycle by pointing elsewhere
407 // We perform two distinct tests.
408 //
409 // Failure test 1: make sure that the subrelation fails when when we change sigma_1 without updating z_perm.
410 {
411 auto failures_of_tampered_instance = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
412 prover_instance->polynomials,
413 prover_instance->relation_parameters,
414 "Permutation Relation - After corrupting sigma_1");
415
416 ASSERT_TRUE(failures_of_tampered_instance.at(0));
417 }
418 // Failure test 2: make sure the subrelation fails when we DO update z_perm
419 {
420 // Sanity check: if we are recompute z_perm, the first different value will be at `row_to_corrupt + 1`. Store
421 // this to ensure that this value indeed changes.
422 auto& z_perm = prover_instance->polynomials.z_perm;
423 auto z_perm_before = z_perm.at(row_to_corrupt + 1);
424
425 // Recompute z_perm with the corrupted sigma.
426 size_t real_circuit_size = prover_instance->get_final_active_wire_idx() + 1;
427 compute_grand_product<Flavor, UltraPermutationRelation<fr>>(
428 prover_instance->polynomials, prover_instance->relation_parameters, real_circuit_size);
429 prover_instance->polynomials.set_shifted(); // Refresh z_perm_shift
430
431 // Verify z_perm actually changed after recomputation with corrupted sigma
432 auto z_perm_after = z_perm.at(row_to_corrupt + 1);
433 ASSERT_NE(z_perm_before, z_perm_after) << "z_perm should change after recomputing with corrupted sigma";
434
435 // After recomputing z_perm, we expect a failure at the very last active wire.
436 auto failures_of_tampered_instance = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
437 prover_instance->polynomials,
438 prover_instance->relation_parameters,
439 "Permutation Relation - After corrupting sigma_1 and recomputing z_perm");
440
441 ASSERT_EQ(failures_of_tampered_instance.at(0), real_circuit_size - 1)
442 << "Expected failure at row " << (real_circuit_size - 1) << " (the recomputation boundary)";
443 }
444}
445
456TYPED_TEST(PermutationNonZKTests, PublicInputDeltaMismatch)
457{
458 using Flavor = TypeParam;
461 using Prover = typename TestFixture::Prover;
462
464
465 // Add a public input
466 fr public_value = fr(314159);
467 auto pub_var = builder.add_public_variable(public_value);
468
469 // Use the public input in a simple constraint so it appears in the trace
470 auto private_val = fr::random_element();
471 auto private_var = builder.add_variable(private_val);
472 auto result_var = builder.add_variable(public_value + private_val);
473 builder.create_add_gate({ pub_var, private_var, result_var, 1, 1, -1, 0 });
474
475 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
476
477 auto prover_instance = std::make_shared<ProverInstance>(builder);
478 auto verification_key = std::make_shared<VerificationKey>(prover_instance->get_precomputed());
479
480 // Construct proof to compute z_perm (with correct public_input_delta)
481 Prover prover(prover_instance, verification_key);
482 auto proof = prover.construct_proof();
483
484 // Verify the permutation relation holds before tampering
485 auto permutation_relation_failures = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
486 prover_instance->polynomials, prover_instance->relation_parameters, "Permutation Relation - Before Tampering");
487 ASSERT_TRUE(permutation_relation_failures.empty());
488
489 // Store the original public_input_delta
490 fr original_delta = prover_instance->relation_parameters.public_input_delta;
491
492 // TAMPER
493
494 // Recompute public_input_delta with a different public input value
495 // The wire polynomials still contain the original value (314159), but we compute delta as if it were 99999
496 fr tampered_public_val = fr(99999);
497 std::vector<fr> tampered_public_inputs = { tampered_public_val };
498 fr tampered_delta = compute_public_input_delta<Flavor>(tampered_public_inputs,
499 prover_instance->relation_parameters.beta,
500 prover_instance->relation_parameters.gamma,
501 prover_instance->pub_inputs_offset());
502
503 // Sanity check: the tampered delta should differ from the original
504 ASSERT_NE(original_delta, tampered_delta) << "Tampered delta should differ from original";
505
506 // Apply the tampered delta
507 prover_instance->relation_parameters.public_input_delta = tampered_delta;
508
509 // Verify the permutation relation now fails
510 auto failures_of_tampered_instance = RelationChecker<Flavor>::template check<UltraPermutationRelation<fr>>(
511 prover_instance->polynomials,
512 prover_instance->relation_parameters,
513 "Permutation Relation - After tampering with public_input_delta");
514 // The failure should be in subrelation 0 (the recurrence relation) since z_perm was computed with
515 // a different public_input_delta than what we're now using in the relation check
516 ASSERT_TRUE(failures_of_tampered_instance.contains(0)) << "Expected subrelation 0 to fail";
517 size_t final_active_wire_idx = prover_instance->get_final_active_wire_idx();
518 ASSERT_TRUE(failures_of_tampered_instance.at(0) == final_active_wire_idx);
519}
The verification key is responsible for storing the commitments to the precomputed (non-witnessk) pol...
ECCVMCircuitBuilder CircuitBuilder
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
A debugging utility for checking whether a set of polynomials satisfies the relations for a given Fla...
Child class of UltraFlavor that runs with ZK Sumcheck.
#define vinfo(...)
Definition log.hpp:80
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
UltraKeccakFlavor::VerificationKey VerificationKey
testing::Types< MegaFlavor, UltraFlavor, UltraZKFlavor, UltraRollupFlavor > FlavorTypes
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
field< Bn254FrParams > fr
Definition fr.hpp:174
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
testing::Types< UltraFlavor, UltraKeccakFlavor, UltraRollupFlavor > NonZKFlavorTypes
static field random_element(numeric::RNG *engine=nullptr) noexcept