Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
ultra_honk.test.cpp
Go to the documentation of this file.
1#include "ultra_honk.test.hpp"
3
4#include <gtest/gtest.h>
5
6using namespace bb;
7
9
10#ifdef STARKNET_GARAGA_FLAVORS
11using FlavorTypes = testing::Types<UltraFlavor,
16 UltraStarknetFlavor,
17 UltraStarknetZKFlavor>;
18#else
20 testing::Types<UltraFlavor, UltraZKFlavor, UltraKeccakFlavor, UltraKeccakZKFlavor, UltraRollupFlavor>;
21#endif
32TYPED_TEST(UltraHonkTests, ProofLengthCheck)
33{
34 using Flavor = TypeParam;
39 using Proof = typename Flavor::Transcript::Proof;
40
41 auto builder = Builder{};
42 IO::add_default(builder);
43 // Construct a UH proof and ensure its size matches expectation; if not, the constant may need to be updated
45 auto verification_key = std::make_shared<typename Flavor::VerificationKey>(prover_instance->get_precomputed());
46 UltraProver_<Flavor> prover(prover_instance, verification_key);
47 Proof ultra_proof = prover.construct_proof();
48 const size_t virtual_log_n = Flavor::USE_PADDING ? CONST_PROOF_SIZE_LOG_N : prover_instance->log_dyadic_size();
49 size_t expected_proof_length = Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(virtual_log_n) + IO::PUBLIC_INPUTS_SIZE;
50 EXPECT_EQ(ultra_proof.size(), expected_proof_length);
51}
52
60TYPED_TEST(UltraHonkTests, ANonZeroPolynomialIsAGoodPolynomial)
61{
62 auto circuit_builder = UltraCircuitBuilder();
63 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
64
65 auto prover_instance = std::make_shared<typename TestFixture::ProverInstance>(circuit_builder);
66 auto verification_key = std::make_shared<typename TypeParam::VerificationKey>(prover_instance->get_precomputed());
67 typename TestFixture::Prover prover(prover_instance, verification_key);
68 auto proof = prover.construct_proof();
69 auto& polynomials = prover_instance->polynomials;
70
71 auto ensure_non_zero = [](auto& polynomial) {
72 bool has_non_zero_coefficient = false;
73 for (auto& coeff : polynomial.coeffs()) {
74 has_non_zero_coefficient |= !coeff.is_zero();
75 }
76 ASSERT_TRUE(has_non_zero_coefficient);
77 };
78
79 for (auto& poly : polynomials.get_selectors()) {
80 ensure_non_zero(poly);
81 }
82
83 for (auto& poly : polynomials.get_tables()) {
84 ensure_non_zero(poly);
85 }
86
87 for (auto& poly : polynomials.get_wires()) {
88 ensure_non_zero(poly);
89 }
90}
91
97{
99 size_t num_gates = 10;
100
101 // Add some arbitrary arithmetic gates that utilize public inputs
103 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
104
105 TestFixture::prove_and_verify(builder, /*expected_result=*/true);
106}
107
109{
110 auto circuit_builder = UltraCircuitBuilder();
111
112 uint32_t left_value = engine.get_random_uint32();
113 uint32_t right_value = engine.get_random_uint32();
114
115 fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form();
116 fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form();
117
118 uint32_t left_witness_index = circuit_builder.add_variable(left_witness_value);
119 uint32_t right_witness_index = circuit_builder.add_variable(right_witness_value);
120
121 uint32_t xor_result_expected = left_value ^ right_value;
122
123 const auto lookup_accumulators = plookup::get_lookup_accumulators(
124 plookup::MultiTableId::UINT32_XOR, left_witness_value, right_witness_value, true);
125 auto xor_result = lookup_accumulators[plookup::ColumnIdx::C3]
126 [0]; // The zeroth index in the 3rd column is the fully accumulated xor
127
128 EXPECT_EQ(xor_result, xor_result_expected);
129 circuit_builder.create_gates_from_plookup_accumulators(
130 plookup::MultiTableId::UINT32_XOR, lookup_accumulators, left_witness_index, right_witness_index);
131 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
132
133 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
134}
135
136TYPED_TEST(UltraHonkTests, CreateGatesFromPlookupAccumulators)
137{
138 auto circuit_builder = UltraCircuitBuilder();
139
140 fr input_value = fr::random_element();
141 const fr input_lo = static_cast<uint256_t>(input_value).slice(0, plookup::fixed_base::table::BITS_PER_LO_SCALAR);
142 const auto input_lo_index = circuit_builder.add_variable(input_lo);
143
144 const auto sequence_data_lo = plookup::get_lookup_accumulators(plookup::MultiTableId::FIXED_BASE_LEFT_LO, input_lo);
145
146 const auto lookup_witnesses = circuit_builder.create_gates_from_plookup_accumulators(
147 plookup::MultiTableId::FIXED_BASE_LEFT_LO, sequence_data_lo, input_lo_index);
148
149 const size_t num_lookups = plookup::fixed_base::table::NUM_TABLES_PER_LO_MULTITABLE;
150
151 EXPECT_EQ(num_lookups, lookup_witnesses[plookup::ColumnIdx::C1].size());
152
153 {
154 const auto mask = plookup::fixed_base::table::MAX_TABLE_SIZE - 1;
155
157 std::vector<uint8_t> input_buf;
158 write(input_buf, base_point);
159 const auto offset_generators =
160 grumpkin::g1::derive_generators(input_buf, plookup::fixed_base::table::NUM_TABLES_PER_LO_MULTITABLE);
161
162 grumpkin::g1::element accumulator = base_point;
163 uint256_t expected_scalar(input_lo);
164 const auto table_bits = plookup::fixed_base::table::BITS_PER_TABLE;
165 const auto num_tables = plookup::fixed_base::table::NUM_TABLES_PER_LO_MULTITABLE;
166 for (size_t i = 0; i < num_tables; ++i) {
167
168 auto round_scalar = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C1][i]);
169 auto round_x = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C2][i]);
170 auto round_y = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C3][i]);
171
172 EXPECT_EQ(uint256_t(round_scalar), expected_scalar);
173
174 auto next_scalar = static_cast<uint256_t>(
175 (i == num_tables - 1) ? fr(0)
176 : circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C1][i + 1]));
177
178 uint256_t slice = static_cast<uint256_t>(round_scalar) - (next_scalar << table_bits);
179 EXPECT_EQ(slice, (uint256_t(input_lo) >> (i * table_bits)) & mask);
180
181 grumpkin::g1::affine_element expected_point(accumulator * static_cast<uint256_t>(slice) +
182 offset_generators[i]);
183
184 EXPECT_EQ(round_x, expected_point.x);
185 EXPECT_EQ(round_y, expected_point.y);
186 for (size_t j = 0; j < table_bits; ++j) {
187 accumulator = accumulator.dbl();
188 }
189 expected_scalar >>= table_bits;
190 }
191 }
192 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
193
194 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
195}
196
202{
203 using ProverInstance = typename TestFixture::ProverInstance;
204 // Construct a circuit with lookup and arithmetic gates
205 auto construct_circuit_with_lookups = [this]() {
207
210 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
211
212 return builder;
213 };
214
215 // Ensure the unaltered test circuit is valid
216 {
217 auto builder = construct_circuit_with_lookups();
218
219 TestFixture::prove_and_verify(builder, true);
220 }
221
222 // Failure mode 1: bad read counts/tags
223 {
224 auto builder = construct_circuit_with_lookups();
225
226 auto prover_instance = std::make_shared<ProverInstance>(builder);
227 auto& polynomials = prover_instance->polynomials;
228
229 // Erroneously update the read counts/tags at an arbitrary index
230 // Note: updating only one or the other may not cause failure due to the design of the relation algebra. For
231 // example, the inverse is only computed if read tags is non-zero, otherwise the inverse at the row in
232 // question will be zero. So if read counts is incremented at some arbitrary index but read tags is not, the
233 // inverse will be 0 and the erroneous read_counts value will get multiplied by 0 in the relation. This is
234 // expected behavior.
235 polynomials.lookup_inverses = polynomials.lookup_inverses.full();
236 polynomials.lookup_read_counts = polynomials.lookup_read_counts.full();
237 polynomials.lookup_read_counts.at(25) = 1;
238 polynomials.lookup_read_tags = polynomials.lookup_read_tags.full();
239 polynomials.lookup_read_tags.at(25) = 1;
240
241 TestFixture::prove_and_verify(prover_instance, false);
242 }
243
244 // Failure mode 2: bad lookup gate wire value
245 {
246 auto builder = construct_circuit_with_lookups();
247
248 auto prover_instance = std::make_shared<ProverInstance>(builder);
249 auto& polynomials = prover_instance->polynomials;
250
251 bool altered = false;
252 // Find a lookup gate and alter one of the wire values
253 for (auto [i, q_lookup] : polynomials.q_lookup.indexed_values()) {
254 if (!q_lookup.is_zero() && polynomials.q_lookup.is_valid_set_index(i)) {
255 polynomials.w_o.at(i) += 1;
256 altered = true;
257 break;
258 }
259 }
260 EXPECT_TRUE(altered);
261 TestFixture::prove_and_verify(prover_instance, false);
262 }
263
264 // Failure mode 3: erroneous lookup gate
265 {
266 auto builder = construct_circuit_with_lookups();
267
268 auto prover_instance = std::make_shared<ProverInstance>(builder);
269 auto& polynomials = prover_instance->polynomials;
270
271 // Turn the lookup selector on for an arbitrary row where it is not already active
272 polynomials.lookup_inverses = polynomials.lookup_inverses.full();
273 polynomials.q_lookup = polynomials.q_lookup.full();
274 EXPECT_TRUE(polynomials.q_lookup[25] != 1);
275 polynomials.q_lookup.at(25) = 1;
276
277 TestFixture::prove_and_verify(prover_instance, false);
278 }
279}
280
281TYPED_TEST(UltraHonkTests, TestNoLookupProof)
282{
283 auto circuit_builder = UltraCircuitBuilder();
284
285 for (size_t i = 0; i < 16; ++i) {
286 for (size_t j = 0; j < 16; ++j) {
287 uint64_t left = static_cast<uint64_t>(j);
288 uint64_t right = static_cast<uint64_t>(i);
289 uint32_t left_idx = circuit_builder.add_variable(fr(left));
290 uint32_t right_idx = circuit_builder.add_variable(fr(right));
291 uint32_t result_idx = circuit_builder.add_variable(fr(left ^ right));
292
293 uint32_t add_idx =
294 circuit_builder.add_variable(fr(left) + fr(right) + circuit_builder.get_variable(result_idx));
295 circuit_builder.create_big_add_gate(
296 { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
297 }
298 }
299 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
300
301 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
302}
303
304TYPED_TEST(UltraHonkTests, TestEllipticGate)
305{
307 typedef grumpkin::g1::element element;
308 auto circuit_builder = UltraCircuitBuilder();
309
312
313 affine_element p3(element(p1) + element(p2));
314
315 uint32_t x1 = circuit_builder.add_variable(p1.x);
316 uint32_t y1 = circuit_builder.add_variable(p1.y);
317 uint32_t x2 = circuit_builder.add_variable(p2.x);
318 uint32_t y2 = circuit_builder.add_variable(p2.y);
319 uint32_t x3 = circuit_builder.add_variable(p3.x);
320 uint32_t y3 = circuit_builder.add_variable(p3.y);
321
322 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
323
324 p3 = affine_element(element(p1) + element(p2));
325 x3 = circuit_builder.add_variable(p3.x);
326 y3 = circuit_builder.add_variable(p3.y);
327 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
328
329 p3 = affine_element(element(p1) - element(p2));
330 x3 = circuit_builder.add_variable(p3.x);
331 y3 = circuit_builder.add_variable(p3.y);
332 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, -1 });
333
334 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
335
336 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
337}
338
340{
341 auto circuit_builder = UltraCircuitBuilder();
342 fr a = fr::one();
343 fr b = fr(2);
344 fr c = fr(3);
345 fr d = fr(4);
346
347 auto a_idx = circuit_builder.add_variable(a);
348 auto b_idx = circuit_builder.add_variable(b);
349 auto c_idx = circuit_builder.add_variable(c);
350 auto d_idx = circuit_builder.add_variable(d);
351 circuit_builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx });
352
353 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
354
355 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
356}
357
358TYPED_TEST(UltraHonkTests, SortWithEdgesGate)
359{
360 fr a = fr::one();
361 fr b = fr(2);
362 fr c = fr(3);
363 fr d = fr(4);
364 fr e = fr(5);
365 fr f = fr(6);
366 fr g = fr(7);
367 fr h = fr(8);
368
369 {
370 auto circuit_builder = UltraCircuitBuilder();
371 auto a_idx = circuit_builder.add_variable(a);
372 auto b_idx = circuit_builder.add_variable(b);
373 auto c_idx = circuit_builder.add_variable(c);
374 auto d_idx = circuit_builder.add_variable(d);
375 auto e_idx = circuit_builder.add_variable(e);
376 auto f_idx = circuit_builder.add_variable(f);
377 auto g_idx = circuit_builder.add_variable(g);
378 auto h_idx = circuit_builder.add_variable(h);
379 circuit_builder.create_sort_constraint_with_edges(
380 { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h);
381
382 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
383
384 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
385 }
386
387 {
388 auto circuit_builder = UltraCircuitBuilder();
389 auto a_idx = circuit_builder.add_variable(a);
390 auto b_idx = circuit_builder.add_variable(b);
391 auto c_idx = circuit_builder.add_variable(c);
392 auto d_idx = circuit_builder.add_variable(d);
393 auto e_idx = circuit_builder.add_variable(e);
394 auto f_idx = circuit_builder.add_variable(f);
395 auto g_idx = circuit_builder.add_variable(g);
396 auto h_idx = circuit_builder.add_variable(h);
397 circuit_builder.create_sort_constraint_with_edges(
398 { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, g);
399
400 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
401
402 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
403 }
404 {
405 auto circuit_builder = UltraCircuitBuilder();
406 auto a_idx = circuit_builder.add_variable(a);
407 auto b_idx = circuit_builder.add_variable(b);
408 auto c_idx = circuit_builder.add_variable(c);
409 auto d_idx = circuit_builder.add_variable(d);
410 auto e_idx = circuit_builder.add_variable(e);
411 auto f_idx = circuit_builder.add_variable(f);
412 auto g_idx = circuit_builder.add_variable(g);
413 auto h_idx = circuit_builder.add_variable(h);
414 circuit_builder.create_sort_constraint_with_edges(
415 { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h);
416
417 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
418
419 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
420 }
421 {
422 auto circuit_builder = UltraCircuitBuilder();
423 auto a_idx = circuit_builder.add_variable(a);
424 auto c_idx = circuit_builder.add_variable(c);
425 auto d_idx = circuit_builder.add_variable(d);
426 auto e_idx = circuit_builder.add_variable(e);
427 auto f_idx = circuit_builder.add_variable(f);
428 auto g_idx = circuit_builder.add_variable(g);
429 auto h_idx = circuit_builder.add_variable(h);
430 auto b2_idx = circuit_builder.add_variable(fr(15));
431 circuit_builder.create_sort_constraint_with_edges(
432 { a_idx, b2_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h);
433
434 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
435
436 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
437 }
438 {
439 auto circuit_builder = UltraCircuitBuilder();
440 auto idx =
441 TestFixture::add_variables(circuit_builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25,
442 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 });
443 circuit_builder.create_sort_constraint_with_edges(idx, 1, 45);
444
445 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
446
447 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
448 }
449 {
450 auto circuit_builder = UltraCircuitBuilder();
451 auto idx =
452 TestFixture::add_variables(circuit_builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25,
453 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 });
454 circuit_builder.create_sort_constraint_with_edges(idx, 1, 29);
455
456 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
457
458 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
459 }
460}
461
462TYPED_TEST(UltraHonkTests, RangeConstraint)
463{
464 {
465 auto circuit_builder = UltraCircuitBuilder();
466 auto indices = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
467 for (size_t i = 0; i < indices.size(); i++) {
468 circuit_builder.create_new_range_constraint(indices[i], 8);
469 }
470 // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx};
471 circuit_builder.create_sort_constraint(indices);
472
473 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
474
475 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
476 }
477 {
478 auto circuit_builder = UltraCircuitBuilder();
479 auto indices = TestFixture::add_variables(circuit_builder, { 3 });
480 for (size_t i = 0; i < indices.size(); i++) {
481 circuit_builder.create_new_range_constraint(indices[i], 3);
482 }
483 // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx};
484 circuit_builder.create_unconstrained_gates(indices);
485
486 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
487
488 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
489 }
490 {
491 auto circuit_builder = UltraCircuitBuilder();
492 auto indices = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 8, 25 });
493 for (size_t i = 0; i < indices.size(); i++) {
494 circuit_builder.create_new_range_constraint(indices[i], 8);
495 }
496 circuit_builder.create_sort_constraint(indices);
497
498 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
499
500 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
501 }
502 {
503 auto circuit_builder = UltraCircuitBuilder();
504 auto indices = TestFixture::add_variables(
505 circuit_builder, { 1, 2, 3, 4, 5, 6, 10, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 19, 51 });
506 for (size_t i = 0; i < indices.size(); i++) {
507 circuit_builder.create_new_range_constraint(indices[i], 128);
508 }
509 circuit_builder.create_unconstrained_gates(indices);
510
511 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
512
513 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
514 }
515 {
516 auto circuit_builder = UltraCircuitBuilder();
517 auto indices = TestFixture::add_variables(
518 circuit_builder, { 1, 2, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 });
519 for (size_t i = 0; i < indices.size(); i++) {
520 circuit_builder.create_new_range_constraint(indices[i], 79);
521 }
522 circuit_builder.create_unconstrained_gates(indices);
523
524 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
525
526 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
527 }
528 {
529 auto circuit_builder = UltraCircuitBuilder();
530 auto indices = TestFixture::add_variables(
531 circuit_builder, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 });
532 for (size_t i = 0; i < indices.size(); i++) {
533 circuit_builder.create_new_range_constraint(indices[i], 79);
534 }
535 circuit_builder.create_unconstrained_gates(indices);
536
537 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
538
539 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
540 }
541}
542
544{
545 auto circuit_builder = UltraCircuitBuilder();
546 auto idx = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
547 for (size_t i = 0; i < idx.size(); i++) {
548 circuit_builder.create_new_range_constraint(idx[i], 8);
549 }
550
551 circuit_builder.create_add_gate(
552 { idx[0], idx[1], circuit_builder.zero_idx(), fr::one(), fr::one(), fr::zero(), -3 });
553 circuit_builder.create_add_gate(
554 { idx[2], idx[3], circuit_builder.zero_idx(), fr::one(), fr::one(), fr::zero(), -7 });
555 circuit_builder.create_add_gate(
556 { idx[4], idx[5], circuit_builder.zero_idx(), fr::one(), fr::one(), fr::zero(), -11 });
557 circuit_builder.create_add_gate(
558 { idx[6], idx[7], circuit_builder.zero_idx(), fr::one(), fr::one(), fr::zero(), -15 });
559
560 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
561
562 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
563}
564
565TYPED_TEST(UltraHonkTests, RangeWithGatesWhereRangeIsNotAPowerOfTwo)
566{
567 auto circuit_builder = UltraCircuitBuilder();
568 auto idx = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
569 for (size_t i = 0; i < idx.size(); i++) {
570 circuit_builder.create_new_range_constraint(idx[i], 12);
571 }
572
573 circuit_builder.create_add_gate(
574 { idx[0], idx[1], circuit_builder.zero_idx(), fr::one(), fr::one(), fr::zero(), -3 });
575 circuit_builder.create_add_gate(
576 { idx[2], idx[3], circuit_builder.zero_idx(), fr::one(), fr::one(), fr::zero(), -7 });
577 circuit_builder.create_add_gate(
578 { idx[4], idx[5], circuit_builder.zero_idx(), fr::one(), fr::one(), fr::zero(), -11 });
579 circuit_builder.create_add_gate(
580 { idx[6], idx[7], circuit_builder.zero_idx(), fr::one(), fr::one(), fr::zero(), -15 });
581
582 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
583
584 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
585}
586
587TYPED_TEST(UltraHonkTests, SortWidgetComplex)
588{
589 {
590
591 auto circuit_builder = UltraCircuitBuilder();
592 std::vector<fr> a = { 1, 3, 4, 7, 7, 8, 11, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 };
593 std::vector<uint32_t> ind;
594 for (const fr& val : a)
595 ind.emplace_back(circuit_builder.add_variable(val));
596 circuit_builder.create_sort_constraint(ind);
597
598 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
599
600 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
601 }
602 {
603
604 auto circuit_builder = UltraCircuitBuilder();
605 std::vector<fr> a = { 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 };
606 std::vector<uint32_t> ind;
607 for (const fr& val : a)
608 ind.emplace_back(circuit_builder.add_variable(val));
609 circuit_builder.create_sort_constraint(ind);
610
611 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
612
613 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
614 }
615}
616
618{
619 auto circuit_builder = UltraCircuitBuilder();
620 fr a = fr::one();
621 fr b = fr(2);
622 fr c = fr(3);
623 fr d = fr(8);
624
625 auto a_idx = circuit_builder.add_variable(a);
626 auto b_idx = circuit_builder.add_variable(b);
627 auto c_idx = circuit_builder.add_variable(c);
628 auto d_idx = circuit_builder.add_variable(d);
629 circuit_builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx });
630
631 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
632
633 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
634}
635
636TYPED_TEST(UltraHonkTests, ComposedRangeConstraint)
637{
638 auto circuit_builder = UltraCircuitBuilder();
639 auto c = fr::random_element();
640 auto d = uint256_t(c).slice(0, 133);
641 auto e = fr(d);
642 auto a_idx = circuit_builder.add_variable(fr(e));
643 circuit_builder.create_add_gate({ a_idx, circuit_builder.zero_idx(), circuit_builder.zero_idx(), 1, 0, 0, -fr(e) });
644 circuit_builder.decompose_into_default_range(a_idx, 134);
645
646 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
647
648 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
649}
650
651TYPED_TEST(UltraHonkTests, NonNativeFieldMultiplication)
652{
653 using fq = fq;
654 auto circuit_builder = UltraCircuitBuilder();
655
658 uint256_t modulus = fq::modulus;
659
662 uint1024_t p_big = uint512_t(uint256_t(modulus));
663
664 uint1024_t q_big = (a_big * b_big) / p_big;
665 uint1024_t r_big = (a_big * b_big) % p_big;
666
667 uint256_t q(q_big.lo.lo);
668 uint256_t r(r_big.lo.lo);
669
670 const auto split_into_limbs = [&](const uint512_t& input) {
671 constexpr size_t NUM_BITS = 68;
672 std::array<fr, 4> limbs;
673 limbs[0] = input.slice(0, NUM_BITS).lo;
674 limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo;
675 limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo;
676 limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo;
677 return limbs;
678 };
679
680 const auto get_limb_witness_indices = [&](const std::array<fr, 4>& limbs) {
681 std::array<uint32_t, 4> limb_indices;
682 limb_indices[0] = circuit_builder.add_variable(limbs[0]);
683 limb_indices[1] = circuit_builder.add_variable(limbs[1]);
684 limb_indices[2] = circuit_builder.add_variable(limbs[2]);
685 limb_indices[3] = circuit_builder.add_variable(limbs[3]);
686 return limb_indices;
687 };
688 const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4);
689 auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus));
690
691 const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a)));
692 const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b)));
693 const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q)));
694 const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r)));
695
697 a_indices, b_indices, q_indices, r_indices, modulus_limbs,
698 };
699 const auto [lo_1_idx, hi_1_idx] = circuit_builder.evaluate_non_native_field_multiplication(inputs);
700
701 // Range constrain the lo and hi carry outputs
702 const bool is_low_70_bits = uint256_t(circuit_builder.get_variable(lo_1_idx)).get_msb() < 70;
703 const bool is_high_70_bits = uint256_t(circuit_builder.get_variable(hi_1_idx)).get_msb() < 70;
704 if (is_low_70_bits && is_high_70_bits) {
705 // Uses more efficient NNF range check if both limbs are < 2^70
706 circuit_builder.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70);
707 } else {
708 // Fallback to default range checks
709 circuit_builder.decompose_into_default_range(lo_1_idx, 72);
710 circuit_builder.decompose_into_default_range(hi_1_idx, 72);
711 }
712
713 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
714
715 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
716}
717
718TYPED_TEST(UltraHonkTests, RangeChecksOnDuplicates)
719{
720 auto circuit_builder = UltraCircuitBuilder();
721
722 uint32_t a = circuit_builder.add_variable(fr(100));
723 uint32_t b = circuit_builder.add_variable(fr(100));
724 uint32_t c = circuit_builder.add_variable(fr(100));
725 uint32_t d = circuit_builder.add_variable(fr(100));
726
727 circuit_builder.assert_equal(a, b);
728 circuit_builder.assert_equal(a, c);
729 circuit_builder.assert_equal(a, d);
730
731 circuit_builder.create_new_range_constraint(a, 1000);
732 circuit_builder.create_new_range_constraint(b, 1001);
733 circuit_builder.create_new_range_constraint(c, 999);
734 circuit_builder.create_new_range_constraint(d, 1000);
735
736 circuit_builder.create_big_add_gate(
737 {
738 a,
739 b,
740 c,
741 d,
742 0,
743 0,
744 0,
745 0,
746 0,
747 },
748 false);
749
750 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
751
752 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
753}
754
755// Ensure copy constraints added on variables smaller than 2^14, which have been previously
756// range constrained, do not break the set equivalence checks because of indices mismatch.
757// 2^14 is DEFAULT_PLOOKUP_RANGE_BITNUM i.e. the maximum size before a variable gets sliced
758// before range constraints are applied to it.
759TYPED_TEST(UltraHonkTests, RangeConstraintSmallVariable)
760{
761 auto circuit_builder = UltraCircuitBuilder();
762
763 uint16_t mask = (1 << 8) - 1;
764 int a = engine.get_random_uint16() & mask;
765 uint32_t a_idx = circuit_builder.add_variable(fr(a));
766 uint32_t b_idx = circuit_builder.add_variable(fr(a));
767 ASSERT_NE(a_idx, b_idx);
768 uint32_t c_idx = circuit_builder.add_variable(fr(a));
769 ASSERT_NE(c_idx, b_idx);
770 circuit_builder.create_range_constraint(b_idx, 8, "bad range");
771 circuit_builder.assert_equal(a_idx, b_idx);
772 circuit_builder.create_range_constraint(c_idx, 8, "bad range");
773 circuit_builder.assert_equal(a_idx, c_idx);
774
775 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
776
777 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
778}
ECCVMCircuitBuilder CircuitBuilder
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS
static constexpr bool USE_PADDING
static void add_arithmetic_gates_with_public_inputs(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates (with public inputs) to the provided circuit.
static void add_lookup_gates(Builder &builder, size_t num_iterations=1)
Add lookup gates using the uint32 XOR lookup table (table size 4096)
static void add_arithmetic_gates(Builder &builder, const size_t num_gates=4)
Add a specified number of arithmetic gates to the provided circuit.
A ProverInstance is normally constructed from a finalized circuit and it contains all the information...
Child class of UltraFlavor that runs with ZK Sumcheck.
static affine_element random_element(numeric::RNG *engine=nullptr) noexcept
Samples a random point on the curve.
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition element.hpp:33
constexpr element dbl() const noexcept
static std::vector< affine_element > derive_generators(const std::vector< uint8_t > &domain_separator_bytes, const size_t num_generators, const size_t starting_index=0)
Derives generator points via hash-to-curve.
Definition group.hpp:87
virtual uint16_t get_random_uint16()=0
virtual uint32_t get_random_uint32()=0
constexpr uint256_t slice(uint64_t start, uint64_t end) const
constexpr uint64_t get_msb() const
static constexpr affine_element lhs_generator_point()
Manages the data that is propagated on the public inputs of an application/function circuit.
The data that is propagated on the public inputs of a rollup circuit.
AluTraceBuilder builder
Definition alu.test.cpp:124
FF a
FF b
grumpkin::g1::affine_element affine_element
Definition c_bind.hpp:15
numeric::RNG & engine
AvmProvingInputs inputs
testing::Types< MegaFlavor, UltraFlavor, UltraZKFlavor, UltraRollupFlavor > FlavorTypes
uintx< uint256_t > uint512_t
Definition uintx.hpp:307
ReadData< bb::fr > get_lookup_accumulators(const MultiTableId id, const fr &key_a, const fr &key_b, const bool is_2_to_1_lookup)
Given a table ID and the key(s) for a key-value lookup, return the lookup accumulators.
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
field< Bn254FqParams > fq
Definition fq.hpp:169
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
field< Bn254FrParams > fr
Definition fr.hpp:174
C slice(C const &container, size_t start)
Definition container.hpp:9
void write(std::vector< uint8_t > &buf, Chonk::VerificationKey const &vk)
Definition chonk.hpp:366
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field one()
static constexpr uint256_t modulus
BB_INLINE constexpr field to_montgomery_form() const noexcept
static field random_element(numeric::RNG *engine=nullptr) noexcept
static constexpr field zero()
An object storing two EC points that represent the inputs to a pairing check.
void ensure_non_zero(auto &polynomial)