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.
19
20#include <gtest/gtest.h>
21
22using namespace bb;
23
25
26template <typename Flavor> class UltraHonkTests : public ::testing::Test {
27 public:
32
33 std::vector<uint32_t> add_variables(auto& circuit_builder, std::vector<bb::fr> variables)
34 {
35 std::vector<uint32_t> res;
36 for (auto& variable : variables) {
37 res.emplace_back(circuit_builder.add_variable(variable));
38 }
39 return res;
40 }
41
43 {
45 if constexpr (HasIPAAccumulator<Flavor>) {
46 auto [stdlib_opening_claim, ipa_proof] =
47 IPA<stdlib::grumpkin<UltraCircuitBuilder>>::create_fake_ipa_claim_and_proof(builder);
48 stdlib_opening_claim.set_public();
49 builder.ipa_proof = ipa_proof;
50 }
51 }
52
53 void prove_and_verify(typename Flavor::CircuitBuilder& circuit_builder, bool expected_result)
54 {
55 auto proving_key = std::make_shared<DeciderProvingKey>(circuit_builder);
57 };
58
60 {
61 auto verification_key = std::make_shared<VerificationKey>(proving_key->get_precomputed());
62 Prover prover(proving_key, verification_key);
63 auto proof = prover.construct_proof();
64 if constexpr (HasIPAAccumulator<Flavor>) {
65 VerifierCommitmentKey<curve::Grumpkin> ipa_verification_key(1 << CONST_ECCVM_LOG_N);
66 Verifier verifier(verification_key, ipa_verification_key);
67 bool result = verifier.template verify_proof<RollupIO>(proof, proving_key->ipa_proof).result;
68 EXPECT_EQ(result, expected_result);
69 } else {
70 Verifier verifier(verification_key);
71 bool result = verifier.template verify_proof<DefaultIO>(proof).result;
72 EXPECT_EQ(result, expected_result);
73 }
74 };
75
76 protected:
78};
79
80#ifdef STARKNET_GARAGA_FLAVORS
81using FlavorTypes = testing::Types<UltraFlavor,
86 UltraStarknetFlavor,
87 UltraStarknetZKFlavor>;
88#else
90 testing::Types<UltraFlavor, UltraZKFlavor, UltraKeccakFlavor, UltraKeccakZKFlavor, UltraRollupFlavor>;
91#endif
92
94
104TYPED_TEST(UltraHonkTests, ProofLengthCheck)
105{
106 using Flavor = TypeParam;
111 using Proof = typename Flavor::Transcript::Proof;
112
113 auto builder = Builder{};
114 IO::add_default(builder);
115 // Construct a UH proof and ensure its size matches expectation; if not, the constant may need to be updated
117 auto verification_key = std::make_shared<typename Flavor::VerificationKey>(proving_key->get_precomputed());
118 UltraProver_<Flavor> prover(proving_key, verification_key);
119 Proof ultra_proof = prover.construct_proof();
120 const size_t virtual_log_n = Flavor::USE_PADDING ? CONST_PROOF_SIZE_LOG_N : proving_key->log_dyadic_size();
121 size_t expected_proof_length = Flavor::PROOF_LENGTH_WITHOUT_PUB_INPUTS(virtual_log_n) + IO::PUBLIC_INPUTS_SIZE;
122 EXPECT_EQ(ultra_proof.size(), expected_proof_length);
123}
124
132TYPED_TEST(UltraHonkTests, ANonZeroPolynomialIsAGoodPolynomial)
133{
134 auto circuit_builder = UltraCircuitBuilder();
135 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
136
137 auto proving_key = std::make_shared<typename TestFixture::DeciderProvingKey>(circuit_builder);
138 auto verification_key = std::make_shared<typename TypeParam::VerificationKey>(proving_key->get_precomputed());
139 typename TestFixture::Prover prover(proving_key, verification_key);
140 auto proof = prover.construct_proof();
141 auto& polynomials = proving_key->polynomials;
142
143 auto ensure_non_zero = [](auto& polynomial) {
144 bool has_non_zero_coefficient = false;
145 for (auto& coeff : polynomial.coeffs()) {
146 has_non_zero_coefficient |= !coeff.is_zero();
147 }
148 ASSERT_TRUE(has_non_zero_coefficient);
149 };
150
151 for (auto& poly : polynomials.get_selectors()) {
152 ensure_non_zero(poly);
153 }
154
155 for (auto& poly : polynomials.get_tables()) {
156 ensure_non_zero(poly);
157 }
158
159 for (auto& poly : polynomials.get_wires()) {
160 ensure_non_zero(poly);
161 }
162}
163
169{
171 size_t num_gates = 10;
172
173 // Add some arbitrary arithmetic gates that utilize public inputs
175 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
176
177 TestFixture::prove_and_verify(builder, /*expected_result=*/true);
178}
179
181{
182 auto circuit_builder = UltraCircuitBuilder();
183
184 uint32_t left_value = engine.get_random_uint32();
185 uint32_t right_value = engine.get_random_uint32();
186
187 fr left_witness_value = fr{ left_value, 0, 0, 0 }.to_montgomery_form();
188 fr right_witness_value = fr{ right_value, 0, 0, 0 }.to_montgomery_form();
189
190 uint32_t left_witness_index = circuit_builder.add_variable(left_witness_value);
191 uint32_t right_witness_index = circuit_builder.add_variable(right_witness_value);
192
193 uint32_t xor_result_expected = left_value ^ right_value;
194
195 const auto lookup_accumulators = plookup::get_lookup_accumulators(
196 plookup::MultiTableId::UINT32_XOR, left_witness_value, right_witness_value, true);
197 auto xor_result = lookup_accumulators[plookup::ColumnIdx::C3]
198 [0]; // The zeroth index in the 3rd column is the fully accumulated xor
199
200 EXPECT_EQ(xor_result, xor_result_expected);
201 circuit_builder.create_gates_from_plookup_accumulators(
202 plookup::MultiTableId::UINT32_XOR, lookup_accumulators, left_witness_index, right_witness_index);
203 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
204
205 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
206}
207
208TYPED_TEST(UltraHonkTests, CreateGatesFromPlookupAccumulators)
209{
210 auto circuit_builder = UltraCircuitBuilder();
211
212 fr input_value = fr::random_element();
213 const fr input_lo = static_cast<uint256_t>(input_value).slice(0, plookup::fixed_base::table::BITS_PER_LO_SCALAR);
214 const auto input_lo_index = circuit_builder.add_variable(input_lo);
215
216 const auto sequence_data_lo = plookup::get_lookup_accumulators(plookup::MultiTableId::FIXED_BASE_LEFT_LO, input_lo);
217
218 const auto lookup_witnesses = circuit_builder.create_gates_from_plookup_accumulators(
219 plookup::MultiTableId::FIXED_BASE_LEFT_LO, sequence_data_lo, input_lo_index);
220
221 const size_t num_lookups = plookup::fixed_base::table::NUM_TABLES_PER_LO_MULTITABLE;
222
223 EXPECT_EQ(num_lookups, lookup_witnesses[plookup::ColumnIdx::C1].size());
224
225 {
226 const auto mask = plookup::fixed_base::table::MAX_TABLE_SIZE - 1;
227
229 std::vector<uint8_t> input_buf;
230 write(input_buf, base_point);
231 const auto offset_generators =
232 grumpkin::g1::derive_generators(input_buf, plookup::fixed_base::table::NUM_TABLES_PER_LO_MULTITABLE);
233
234 grumpkin::g1::element accumulator = base_point;
235 uint256_t expected_scalar(input_lo);
236 const auto table_bits = plookup::fixed_base::table::BITS_PER_TABLE;
237 const auto num_tables = plookup::fixed_base::table::NUM_TABLES_PER_LO_MULTITABLE;
238 for (size_t i = 0; i < num_tables; ++i) {
239
240 auto round_scalar = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C1][i]);
241 auto round_x = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C2][i]);
242 auto round_y = circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C3][i]);
243
244 EXPECT_EQ(uint256_t(round_scalar), expected_scalar);
245
246 auto next_scalar = static_cast<uint256_t>(
247 (i == num_tables - 1) ? fr(0)
248 : circuit_builder.get_variable(lookup_witnesses[plookup::ColumnIdx::C1][i + 1]));
249
250 uint256_t slice = static_cast<uint256_t>(round_scalar) - (next_scalar << table_bits);
251 EXPECT_EQ(slice, (uint256_t(input_lo) >> (i * table_bits)) & mask);
252
253 grumpkin::g1::affine_element expected_point(accumulator * static_cast<uint256_t>(slice) +
254 offset_generators[i]);
255
256 EXPECT_EQ(round_x, expected_point.x);
257 EXPECT_EQ(round_y, expected_point.y);
258 for (size_t j = 0; j < table_bits; ++j) {
259 accumulator = accumulator.dbl();
260 }
261 expected_scalar >>= table_bits;
262 }
263 }
264 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
265
266 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
267}
268
274{
275 using DeciderProvingKey = typename TestFixture::DeciderProvingKey;
276 // Construct a circuit with lookup and arithmetic gates
277 auto construct_circuit_with_lookups = [this]() {
279
282 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(builder);
283
284 return builder;
285 };
286
287 // Ensure the unaltered test circuit is valid
288 {
289 auto builder = construct_circuit_with_lookups();
290
291 TestFixture::prove_and_verify(builder, true);
292 }
293
294 // Failure mode 1: bad read counts/tags
295 {
296 auto builder = construct_circuit_with_lookups();
297
299 auto& polynomials = proving_key->polynomials;
300
301 // Erroneously update the read counts/tags at an arbitrary index
302 // Note: updating only one or the other may not cause failure due to the design of the relation algebra. For
303 // example, the inverse is only computed if read tags is non-zero, otherwise the inverse at the row in
304 // question will be zero. So if read counts is incremented at some arbitrary index but read tags is not, the
305 // inverse will be 0 and the erroneous read_counts value will get multiplied by 0 in the relation. This is
306 // expected behavior.
307 polynomials.lookup_inverses = polynomials.lookup_inverses.full();
308 polynomials.lookup_read_counts = polynomials.lookup_read_counts.full();
309 polynomials.lookup_read_counts.at(25) = 1;
310 polynomials.lookup_read_tags = polynomials.lookup_read_tags.full();
311 polynomials.lookup_read_tags.at(25) = 1;
312
313 TestFixture::prove_and_verify(proving_key, false);
314 }
315
316 // Failure mode 2: bad lookup gate wire value
317 {
318 auto builder = construct_circuit_with_lookups();
319
321 auto& polynomials = proving_key->polynomials;
322
323 bool altered = false;
324 // Find a lookup gate and alter one of the wire values
325 for (auto [i, q_lookup] : polynomials.q_lookup.indexed_values()) {
326 if (!q_lookup.is_zero() && polynomials.q_lookup.is_valid_set_index(i)) {
327 polynomials.w_o.at(i) += 1;
328 altered = true;
329 break;
330 }
331 }
332 EXPECT_TRUE(altered);
333 TestFixture::prove_and_verify(proving_key, false);
334 }
335
336 // Failure mode 3: erroneous lookup gate
337 {
338 auto builder = construct_circuit_with_lookups();
339
341 auto& polynomials = proving_key->polynomials;
342
343 // Turn the lookup selector on for an arbitrary row where it is not already active
344 polynomials.lookup_inverses = polynomials.lookup_inverses.full();
345 polynomials.q_lookup = polynomials.q_lookup.full();
346 EXPECT_TRUE(polynomials.q_lookup[25] != 1);
347 polynomials.q_lookup.at(25) = 1;
348
349 TestFixture::prove_and_verify(proving_key, false);
350 }
351}
352
353TYPED_TEST(UltraHonkTests, TestNoLookupProof)
354{
355 auto circuit_builder = UltraCircuitBuilder();
356
357 for (size_t i = 0; i < 16; ++i) {
358 for (size_t j = 0; j < 16; ++j) {
359 uint64_t left = static_cast<uint64_t>(j);
360 uint64_t right = static_cast<uint64_t>(i);
361 uint32_t left_idx = circuit_builder.add_variable(fr(left));
362 uint32_t right_idx = circuit_builder.add_variable(fr(right));
363 uint32_t result_idx = circuit_builder.add_variable(fr(left ^ right));
364
365 uint32_t add_idx =
366 circuit_builder.add_variable(fr(left) + fr(right) + circuit_builder.get_variable(result_idx));
367 circuit_builder.create_big_add_gate(
368 { left_idx, right_idx, result_idx, add_idx, fr(1), fr(1), fr(1), fr(-1), fr(0) });
369 }
370 }
371 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
372
373 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
374}
375
376TYPED_TEST(UltraHonkTests, TestEllipticGate)
377{
379 typedef grumpkin::g1::element element;
380 auto circuit_builder = UltraCircuitBuilder();
381
384
385 affine_element p3(element(p1) + element(p2));
386
387 uint32_t x1 = circuit_builder.add_variable(p1.x);
388 uint32_t y1 = circuit_builder.add_variable(p1.y);
389 uint32_t x2 = circuit_builder.add_variable(p2.x);
390 uint32_t y2 = circuit_builder.add_variable(p2.y);
391 uint32_t x3 = circuit_builder.add_variable(p3.x);
392 uint32_t y3 = circuit_builder.add_variable(p3.y);
393
394 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
395
396 p3 = affine_element(element(p1) + element(p2));
397 x3 = circuit_builder.add_variable(p3.x);
398 y3 = circuit_builder.add_variable(p3.y);
399 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, 1 });
400
401 p3 = affine_element(element(p1) - element(p2));
402 x3 = circuit_builder.add_variable(p3.x);
403 y3 = circuit_builder.add_variable(p3.y);
404 circuit_builder.create_ecc_add_gate({ x1, y1, x2, y2, x3, y3, -1 });
405
406 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
407
408 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
409}
410
411TYPED_TEST(UltraHonkTests, NonTrivialTagPermutation)
412{
413 auto circuit_builder = UltraCircuitBuilder();
415 fr b = -a;
416
417 auto a_idx = circuit_builder.add_variable(a);
418 auto b_idx = circuit_builder.add_variable(b);
419 auto c_idx = circuit_builder.add_variable(b);
420 auto d_idx = circuit_builder.add_variable(a);
421
422 circuit_builder.create_add_gate(
423 { a_idx, b_idx, circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), fr::zero() });
424 circuit_builder.create_add_gate(
425 { c_idx, d_idx, circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), fr::zero() });
426
427 circuit_builder.create_tag(1, 2);
428 circuit_builder.create_tag(2, 1);
429
430 circuit_builder.assign_tag(a_idx, 1);
431 circuit_builder.assign_tag(b_idx, 1);
432 circuit_builder.assign_tag(c_idx, 2);
433 circuit_builder.assign_tag(d_idx, 2);
434 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
435
436 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
437}
438
439TYPED_TEST(UltraHonkTests, NonTrivialTagPermutationAndCycles)
440{
441 auto circuit_builder = UltraCircuitBuilder();
443 fr c = -a;
444
445 auto a_idx = circuit_builder.add_variable(a);
446 auto b_idx = circuit_builder.add_variable(a);
447 circuit_builder.assert_equal(a_idx, b_idx);
448 auto c_idx = circuit_builder.add_variable(c);
449 auto d_idx = circuit_builder.add_variable(c);
450 circuit_builder.assert_equal(c_idx, d_idx);
451 auto e_idx = circuit_builder.add_variable(a);
452 auto f_idx = circuit_builder.add_variable(a);
453 circuit_builder.assert_equal(e_idx, f_idx);
454 auto g_idx = circuit_builder.add_variable(c);
455 auto h_idx = circuit_builder.add_variable(c);
456 circuit_builder.assert_equal(g_idx, h_idx);
457
458 circuit_builder.create_tag(1, 2);
459 circuit_builder.create_tag(2, 1);
460
461 circuit_builder.assign_tag(a_idx, 1);
462 circuit_builder.assign_tag(c_idx, 1);
463 circuit_builder.assign_tag(e_idx, 2);
464 circuit_builder.assign_tag(g_idx, 2);
465
466 circuit_builder.create_add_gate(
467 { b_idx, a_idx, circuit_builder.zero_idx, fr::one(), fr::neg_one(), fr::zero(), fr::zero() });
468 circuit_builder.create_add_gate(
469 { c_idx, g_idx, circuit_builder.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() });
470 circuit_builder.create_add_gate(
471 { e_idx, f_idx, circuit_builder.zero_idx, fr::one(), -fr::one(), fr::zero(), fr::zero() });
472 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
473
474 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
475}
476
477TYPED_TEST(UltraHonkTests, BadTagPermutation)
478{
479 {
480 auto circuit_builder = UltraCircuitBuilder();
482 fr b = -a;
483
484 auto a_idx = circuit_builder.add_variable(a);
485 auto b_idx = circuit_builder.add_variable(b);
486 auto c_idx = circuit_builder.add_variable(b);
487 auto d_idx = circuit_builder.add_variable(a + 1);
488
489 circuit_builder.create_add_gate({ a_idx, b_idx, circuit_builder.zero_idx, 1, 1, 0, 0 });
490 circuit_builder.create_add_gate({ c_idx, d_idx, circuit_builder.zero_idx, 1, 1, 0, -1 });
491
492 circuit_builder.create_tag(1, 2);
493 circuit_builder.create_tag(2, 1);
494
495 circuit_builder.assign_tag(a_idx, 1);
496 circuit_builder.assign_tag(b_idx, 1);
497 circuit_builder.assign_tag(c_idx, 2);
498 circuit_builder.assign_tag(d_idx, 2);
499 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
500
501 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
502 }
503 // Same as above but without tag creation to check reason of failure is really tag mismatch
504 {
505 auto circuit_builder = UltraCircuitBuilder();
507 fr b = -a;
508
509 auto a_idx = circuit_builder.add_variable(a);
510 auto b_idx = circuit_builder.add_variable(b);
511 auto c_idx = circuit_builder.add_variable(b);
512 auto d_idx = circuit_builder.add_variable(a + 1);
513
514 circuit_builder.create_add_gate({ a_idx, b_idx, circuit_builder.zero_idx, 1, 1, 0, 0 });
515 circuit_builder.create_add_gate({ c_idx, d_idx, circuit_builder.zero_idx, 1, 1, 0, -1 });
516 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
517
518 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
519 }
520}
521
523{
524 auto circuit_builder = UltraCircuitBuilder();
525 fr a = fr::one();
526 fr b = fr(2);
527 fr c = fr(3);
528 fr d = fr(4);
529
530 auto a_idx = circuit_builder.add_variable(a);
531 auto b_idx = circuit_builder.add_variable(b);
532 auto c_idx = circuit_builder.add_variable(c);
533 auto d_idx = circuit_builder.add_variable(d);
534 circuit_builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx });
535
536 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
537
538 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
539}
540
541TYPED_TEST(UltraHonkTests, SortWithEdgesGate)
542{
543 fr a = fr::one();
544 fr b = fr(2);
545 fr c = fr(3);
546 fr d = fr(4);
547 fr e = fr(5);
548 fr f = fr(6);
549 fr g = fr(7);
550 fr h = fr(8);
551
552 {
553 auto circuit_builder = UltraCircuitBuilder();
554 auto a_idx = circuit_builder.add_variable(a);
555 auto b_idx = circuit_builder.add_variable(b);
556 auto c_idx = circuit_builder.add_variable(c);
557 auto d_idx = circuit_builder.add_variable(d);
558 auto e_idx = circuit_builder.add_variable(e);
559 auto f_idx = circuit_builder.add_variable(f);
560 auto g_idx = circuit_builder.add_variable(g);
561 auto h_idx = circuit_builder.add_variable(h);
562 circuit_builder.create_sort_constraint_with_edges(
563 { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, h);
564
565 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
566
567 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
568 }
569
570 {
571 auto circuit_builder = UltraCircuitBuilder();
572 auto a_idx = circuit_builder.add_variable(a);
573 auto b_idx = circuit_builder.add_variable(b);
574 auto c_idx = circuit_builder.add_variable(c);
575 auto d_idx = circuit_builder.add_variable(d);
576 auto e_idx = circuit_builder.add_variable(e);
577 auto f_idx = circuit_builder.add_variable(f);
578 auto g_idx = circuit_builder.add_variable(g);
579 auto h_idx = circuit_builder.add_variable(h);
580 circuit_builder.create_sort_constraint_with_edges(
581 { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, a, g);
582
583 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
584
585 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
586 }
587 {
588 auto circuit_builder = UltraCircuitBuilder();
589 auto a_idx = circuit_builder.add_variable(a);
590 auto b_idx = circuit_builder.add_variable(b);
591 auto c_idx = circuit_builder.add_variable(c);
592 auto d_idx = circuit_builder.add_variable(d);
593 auto e_idx = circuit_builder.add_variable(e);
594 auto f_idx = circuit_builder.add_variable(f);
595 auto g_idx = circuit_builder.add_variable(g);
596 auto h_idx = circuit_builder.add_variable(h);
597 circuit_builder.create_sort_constraint_with_edges(
598 { a_idx, b_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h);
599
600 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
601
602 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
603 }
604 {
605 auto circuit_builder = UltraCircuitBuilder();
606 auto a_idx = circuit_builder.add_variable(a);
607 auto c_idx = circuit_builder.add_variable(c);
608 auto d_idx = circuit_builder.add_variable(d);
609 auto e_idx = circuit_builder.add_variable(e);
610 auto f_idx = circuit_builder.add_variable(f);
611 auto g_idx = circuit_builder.add_variable(g);
612 auto h_idx = circuit_builder.add_variable(h);
613 auto b2_idx = circuit_builder.add_variable(fr(15));
614 circuit_builder.create_sort_constraint_with_edges(
615 { a_idx, b2_idx, c_idx, d_idx, e_idx, f_idx, g_idx, h_idx }, b, h);
616
617 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
618
619 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
620 }
621 {
622 auto circuit_builder = UltraCircuitBuilder();
623 auto idx =
624 TestFixture::add_variables(circuit_builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25,
625 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 });
626 circuit_builder.create_sort_constraint_with_edges(idx, 1, 45);
627
628 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
629
630 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
631 }
632 {
633 auto circuit_builder = UltraCircuitBuilder();
634 auto idx =
635 TestFixture::add_variables(circuit_builder, { 1, 2, 5, 6, 7, 10, 11, 13, 16, 17, 20, 22, 22, 25,
636 26, 29, 29, 32, 32, 33, 35, 38, 39, 39, 42, 42, 43, 45 });
637 circuit_builder.create_sort_constraint_with_edges(idx, 1, 29);
638
639 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
640
641 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
642 }
643}
644
645TYPED_TEST(UltraHonkTests, RangeConstraint)
646{
647 {
648 auto circuit_builder = UltraCircuitBuilder();
649 auto indices = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
650 for (size_t i = 0; i < indices.size(); i++) {
651 circuit_builder.create_new_range_constraint(indices[i], 8);
652 }
653 // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx};
654 circuit_builder.create_sort_constraint(indices);
655
656 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
657
658 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
659 }
660 {
661 auto circuit_builder = UltraCircuitBuilder();
662 auto indices = TestFixture::add_variables(circuit_builder, { 3 });
663 for (size_t i = 0; i < indices.size(); i++) {
664 circuit_builder.create_new_range_constraint(indices[i], 3);
665 }
666 // auto ind = {a_idx,b_idx,c_idx,d_idx,e_idx,f_idx,g_idx,h_idx};
667 circuit_builder.create_dummy_constraints(indices);
668
669 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
670
671 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
672 }
673 {
674 auto circuit_builder = UltraCircuitBuilder();
675 auto indices = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 8, 25 });
676 for (size_t i = 0; i < indices.size(); i++) {
677 circuit_builder.create_new_range_constraint(indices[i], 8);
678 }
679 circuit_builder.create_sort_constraint(indices);
680
681 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
682
683 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
684 }
685 {
686 auto circuit_builder = UltraCircuitBuilder();
687 auto indices = TestFixture::add_variables(
688 circuit_builder, { 1, 2, 3, 4, 5, 6, 10, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 19, 51 });
689 for (size_t i = 0; i < indices.size(); i++) {
690 circuit_builder.create_new_range_constraint(indices[i], 128);
691 }
692 circuit_builder.create_dummy_constraints(indices);
693
694 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
695
696 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
697 }
698 {
699 auto circuit_builder = UltraCircuitBuilder();
700 auto indices = TestFixture::add_variables(
701 circuit_builder, { 1, 2, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 });
702 for (size_t i = 0; i < indices.size(); i++) {
703 circuit_builder.create_new_range_constraint(indices[i], 79);
704 }
705 circuit_builder.create_dummy_constraints(indices);
706
707 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
708
709 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
710 }
711 {
712 auto circuit_builder = UltraCircuitBuilder();
713 auto indices = TestFixture::add_variables(
714 circuit_builder, { 1, 0, 3, 80, 5, 6, 29, 8, 15, 11, 32, 21, 42, 79, 16, 10, 3, 26, 13, 14 });
715 for (size_t i = 0; i < indices.size(); i++) {
716 circuit_builder.create_new_range_constraint(indices[i], 79);
717 }
718 circuit_builder.create_dummy_constraints(indices);
719
720 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
721
722 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
723 }
724}
725
727{
728 auto circuit_builder = UltraCircuitBuilder();
729 auto idx = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
730 for (size_t i = 0; i < idx.size(); i++) {
731 circuit_builder.create_new_range_constraint(idx[i], 8);
732 }
733
734 circuit_builder.create_add_gate({ idx[0], idx[1], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -3 });
735 circuit_builder.create_add_gate({ idx[2], idx[3], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -7 });
736 circuit_builder.create_add_gate(
737 { idx[4], idx[5], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -11 });
738 circuit_builder.create_add_gate(
739 { idx[6], idx[7], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -15 });
740
741 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
742
743 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
744}
745
746TYPED_TEST(UltraHonkTests, RangeWithGatesWhereRangeIsNotAPowerOfTwo)
747{
748 auto circuit_builder = UltraCircuitBuilder();
749 auto idx = TestFixture::add_variables(circuit_builder, { 1, 2, 3, 4, 5, 6, 7, 8 });
750 for (size_t i = 0; i < idx.size(); i++) {
751 circuit_builder.create_new_range_constraint(idx[i], 12);
752 }
753
754 circuit_builder.create_add_gate({ idx[0], idx[1], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -3 });
755 circuit_builder.create_add_gate({ idx[2], idx[3], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -7 });
756 circuit_builder.create_add_gate(
757 { idx[4], idx[5], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -11 });
758 circuit_builder.create_add_gate(
759 { idx[6], idx[7], circuit_builder.zero_idx, fr::one(), fr::one(), fr::zero(), -15 });
760
761 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
762
763 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
764}
765
766TYPED_TEST(UltraHonkTests, SortWidgetComplex)
767{
768 {
769
770 auto circuit_builder = UltraCircuitBuilder();
771 std::vector<fr> a = { 1, 3, 4, 7, 7, 8, 11, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 };
772 std::vector<uint32_t> ind;
773 for (const fr& val : a)
774 ind.emplace_back(circuit_builder.add_variable(val));
775 circuit_builder.create_sort_constraint(ind);
776
777 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
778
779 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
780 }
781 {
782
783 auto circuit_builder = UltraCircuitBuilder();
784 std::vector<fr> a = { 1, 3, 4, 7, 7, 8, 16, 14, 15, 15, 18, 19, 21, 21, 24, 25, 26, 27, 30, 32 };
785 std::vector<uint32_t> ind;
786 for (const fr& val : a)
787 ind.emplace_back(circuit_builder.add_variable(val));
788 circuit_builder.create_sort_constraint(ind);
789
790 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
791
792 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
793 }
794}
795
797{
798 auto circuit_builder = UltraCircuitBuilder();
799 fr a = fr::one();
800 fr b = fr(2);
801 fr c = fr(3);
802 fr d = fr(8);
803
804 auto a_idx = circuit_builder.add_variable(a);
805 auto b_idx = circuit_builder.add_variable(b);
806 auto c_idx = circuit_builder.add_variable(c);
807 auto d_idx = circuit_builder.add_variable(d);
808 circuit_builder.create_sort_constraint({ a_idx, b_idx, c_idx, d_idx });
809
810 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
811
812 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/false);
813}
814
815TYPED_TEST(UltraHonkTests, ComposedRangeConstraint)
816{
817 auto circuit_builder = UltraCircuitBuilder();
818 auto c = fr::random_element();
819 auto d = uint256_t(c).slice(0, 133);
820 auto e = fr(d);
821 auto a_idx = circuit_builder.add_variable(fr(e));
822 circuit_builder.create_add_gate({ a_idx, circuit_builder.zero_idx, circuit_builder.zero_idx, 1, 0, 0, -fr(e) });
823 circuit_builder.decompose_into_default_range(a_idx, 134);
824
825 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
826
827 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
828}
829
830TYPED_TEST(UltraHonkTests, NonNativeFieldMultiplication)
831{
832 using fq = fq;
833 auto circuit_builder = UltraCircuitBuilder();
834
837 uint256_t modulus = fq::modulus;
838
841 uint1024_t p_big = uint512_t(uint256_t(modulus));
842
843 uint1024_t q_big = (a_big * b_big) / p_big;
844 uint1024_t r_big = (a_big * b_big) % p_big;
845
846 uint256_t q(q_big.lo.lo);
847 uint256_t r(r_big.lo.lo);
848
849 const auto split_into_limbs = [&](const uint512_t& input) {
850 constexpr size_t NUM_BITS = 68;
851 std::array<fr, 4> limbs;
852 limbs[0] = input.slice(0, NUM_BITS).lo;
853 limbs[1] = input.slice(NUM_BITS * 1, NUM_BITS * 2).lo;
854 limbs[2] = input.slice(NUM_BITS * 2, NUM_BITS * 3).lo;
855 limbs[3] = input.slice(NUM_BITS * 3, NUM_BITS * 4).lo;
856 return limbs;
857 };
858
859 const auto get_limb_witness_indices = [&](const std::array<fr, 4>& limbs) {
860 std::array<uint32_t, 4> limb_indices;
861 limb_indices[0] = circuit_builder.add_variable(limbs[0]);
862 limb_indices[1] = circuit_builder.add_variable(limbs[1]);
863 limb_indices[2] = circuit_builder.add_variable(limbs[2]);
864 limb_indices[3] = circuit_builder.add_variable(limbs[3]);
865 return limb_indices;
866 };
867 const uint512_t BINARY_BASIS_MODULUS = uint512_t(1) << (68 * 4);
868 auto modulus_limbs = split_into_limbs(BINARY_BASIS_MODULUS - uint512_t(modulus));
869
870 const auto a_indices = get_limb_witness_indices(split_into_limbs(uint256_t(a)));
871 const auto b_indices = get_limb_witness_indices(split_into_limbs(uint256_t(b)));
872 const auto q_indices = get_limb_witness_indices(split_into_limbs(uint256_t(q)));
873 const auto r_indices = get_limb_witness_indices(split_into_limbs(uint256_t(r)));
874
876 a_indices, b_indices, q_indices, r_indices, modulus_limbs,
877 };
878 const auto [lo_1_idx, hi_1_idx] = circuit_builder.evaluate_non_native_field_multiplication(inputs);
879 circuit_builder.range_constrain_two_limbs(lo_1_idx, hi_1_idx, 70, 70);
880
881 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
882
883 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
884}
885
887{
888 auto circuit_builder = UltraCircuitBuilder();
889
890 uint32_t rom_values[8]{
891 circuit_builder.add_variable(fr::random_element()), circuit_builder.add_variable(fr::random_element()),
892 circuit_builder.add_variable(fr::random_element()), circuit_builder.add_variable(fr::random_element()),
893 circuit_builder.add_variable(fr::random_element()), circuit_builder.add_variable(fr::random_element()),
894 circuit_builder.add_variable(fr::random_element()), circuit_builder.add_variable(fr::random_element()),
895 };
896
897 size_t rom_id = circuit_builder.create_ROM_array(8);
898
899 for (size_t i = 0; i < 8; ++i) {
900 circuit_builder.set_ROM_element(rom_id, i, rom_values[i]);
901 }
902
903 uint32_t a_idx = circuit_builder.read_ROM_array(rom_id, circuit_builder.add_variable(5));
904 EXPECT_EQ(a_idx != rom_values[5], true);
905 uint32_t b_idx = circuit_builder.read_ROM_array(rom_id, circuit_builder.add_variable(4));
906 uint32_t c_idx = circuit_builder.read_ROM_array(rom_id, circuit_builder.add_variable(1));
907
908 const auto d_value =
909 circuit_builder.get_variable(a_idx) + circuit_builder.get_variable(b_idx) + circuit_builder.get_variable(c_idx);
910 uint32_t d_idx = circuit_builder.add_variable(d_value);
911
912 circuit_builder.create_big_add_gate({
913 a_idx,
914 b_idx,
915 c_idx,
916 d_idx,
917 1,
918 1,
919 1,
920 -1,
921 0,
922 });
923 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
924
925 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
926}
927
929{
930 auto circuit_builder = UltraCircuitBuilder();
931
932 uint32_t ram_values[8]{
933 circuit_builder.add_variable(fr::random_element()), circuit_builder.add_variable(fr::random_element()),
934 circuit_builder.add_variable(fr::random_element()), circuit_builder.add_variable(fr::random_element()),
935 circuit_builder.add_variable(fr::random_element()), circuit_builder.add_variable(fr::random_element()),
936 circuit_builder.add_variable(fr::random_element()), circuit_builder.add_variable(fr::random_element()),
937 };
938
939 size_t ram_id = circuit_builder.create_RAM_array(8);
940
941 for (size_t i = 0; i < 8; ++i) {
942 circuit_builder.init_RAM_element(ram_id, i, ram_values[i]);
943 }
944
945 uint32_t a_idx = circuit_builder.read_RAM_array(ram_id, circuit_builder.add_variable(5));
946 EXPECT_EQ(a_idx != ram_values[5], true);
947
948 uint32_t b_idx = circuit_builder.read_RAM_array(ram_id, circuit_builder.add_variable(4));
949 uint32_t c_idx = circuit_builder.read_RAM_array(ram_id, circuit_builder.add_variable(1));
950
951 circuit_builder.write_RAM_array(ram_id, circuit_builder.add_variable(4), circuit_builder.add_variable(500));
952 uint32_t d_idx = circuit_builder.read_RAM_array(ram_id, circuit_builder.add_variable(4));
953
954 EXPECT_EQ(circuit_builder.get_variable(d_idx), 500);
955
956 // ensure these vars get used in another arithmetic gate
957 const auto e_value = circuit_builder.get_variable(a_idx) + circuit_builder.get_variable(b_idx) +
958 circuit_builder.get_variable(c_idx) + circuit_builder.get_variable(d_idx);
959 uint32_t e_idx = circuit_builder.add_variable(e_value);
960
961 circuit_builder.create_big_add_gate(
962 {
963 a_idx,
964 b_idx,
965 c_idx,
966 d_idx,
967 -1,
968 -1,
969 -1,
970 -1,
971 0,
972 },
973 true);
974 circuit_builder.create_big_add_gate(
975 {
976 circuit_builder.zero_idx,
977 circuit_builder.zero_idx,
978 circuit_builder.zero_idx,
979 e_idx,
980 0,
981 0,
982 0,
983 0,
984 0,
985 },
986 false);
987
988 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
989
990 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
991}
992
993TYPED_TEST(UltraHonkTests, RangeChecksOnDuplicates)
994{
995 auto circuit_builder = UltraCircuitBuilder();
996
997 uint32_t a = circuit_builder.add_variable(100);
998 uint32_t b = circuit_builder.add_variable(100);
999 uint32_t c = circuit_builder.add_variable(100);
1000 uint32_t d = circuit_builder.add_variable(100);
1001
1002 circuit_builder.assert_equal(a, b);
1003 circuit_builder.assert_equal(a, c);
1004 circuit_builder.assert_equal(a, d);
1005
1006 circuit_builder.create_new_range_constraint(a, 1000);
1007 circuit_builder.create_new_range_constraint(b, 1001);
1008 circuit_builder.create_new_range_constraint(c, 999);
1009 circuit_builder.create_new_range_constraint(d, 1000);
1010
1011 circuit_builder.create_big_add_gate(
1012 {
1013 a,
1014 b,
1015 c,
1016 d,
1017 0,
1018 0,
1019 0,
1020 0,
1021 0,
1022 },
1023 false);
1024
1025 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
1026
1027 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
1028}
1029
1030// Ensure copy constraints added on variables smaller than 2^14, which have been previously
1031// range constrained, do not break the set equivalence checks because of indices mismatch.
1032// 2^14 is DEFAULT_PLOOKUP_RANGE_BITNUM i.e. the maximum size before a variable gets sliced
1033// before range constraints are applied to it.
1034TYPED_TEST(UltraHonkTests, RangeConstraintSmallVariable)
1035{
1036 auto circuit_builder = UltraCircuitBuilder();
1037
1038 uint16_t mask = (1 << 8) - 1;
1039 int a = engine.get_random_uint16() & mask;
1040 uint32_t a_idx = circuit_builder.add_variable(fr(a));
1041 uint32_t b_idx = circuit_builder.add_variable(fr(a));
1042 ASSERT_NE(a_idx, b_idx);
1043 uint32_t c_idx = circuit_builder.add_variable(fr(a));
1044 ASSERT_NE(c_idx, b_idx);
1045 circuit_builder.create_range_constraint(b_idx, 8, "bad range");
1046 circuit_builder.assert_equal(a_idx, b_idx);
1047 circuit_builder.create_range_constraint(c_idx, 8, "bad range");
1048 circuit_builder.assert_equal(a_idx, c_idx);
1049
1050 TestFixture::set_default_pairing_points_and_ipa_claim_and_proof(circuit_builder);
1051
1052 TestFixture::prove_and_verify(circuit_builder, /*expected_result=*/true);
1053}
static void SetUpTestSuite()
std::vector< uint32_t > add_variables(auto &circuit_builder, std::vector< bb::fr > variables)
typename Flavor::VerificationKey VerificationKey
void prove_and_verify(const std::shared_ptr< DeciderProvingKey > &proving_key, bool expected_result)
void set_default_pairing_points_and_ipa_claim_and_proof(UltraCircuitBuilder &builder)
void prove_and_verify(typename Flavor::CircuitBuilder &circuit_builder, bool expected_result)
A DeciderProvingKey is normally constructed from a finalized circuit and it contains all the informat...
static constexpr size_t PROOF_LENGTH_WITHOUT_PUB_INPUTS
IPA (inner product argument) commitment scheme class.
Definition ipa.hpp:95
The verification key is responsible for storing the commitments to the precomputed (non-witness) poly...
MegaCircuitBuilder CircuitBuilder
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.
Child class of UltraFlavor that runs with ZK Sumcheck.
Representation of the Grumpkin Verifier Commitment Key inside a bn254 circuit.
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
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:123
FF a
FF b
bool expected_result
grumpkin::g1::affine_element affine_element
Definition c_bind.hpp:15
numeric::RNG & engine
Base class templates for structures that contain data parameterized by the fundamental polynomials of...
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.
std::filesystem::path bb_crs_path()
void init_file_crs_factory(const std::filesystem::path &path)
Entry point for Barretenberg command-line interface.
field< Bn254FqParams > fq
Definition fq.hpp:169
TYPED_TEST_SUITE(ShpleminiTest, TestSettings)
field< Bn254FrParams > fr
Definition fr.hpp:174
void write(B &buf, field2< base_field, Params > const &value)
C slice(C const &container, size_t start)
Definition container.hpp:9
UltraCircuitBuilder_< UltraExecutionTraceBlocks > UltraCircuitBuilder
TYPED_TEST(ShpleminiTest, CorrectnessOfMultivariateClaimBatching)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
static constexpr field neg_one()
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.
static void add_default_to_public_inputs(Builder &builder)
Adds default public inputs to the builder.
void ensure_non_zero(auto &polynomial)