Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
biggroup.hpp
Go to the documentation of this file.
1// === AUDIT STATUS ===
2// internal: { status: not started, auditors: [], date: YYYY-MM-DD }
3// external_1: { status: not started, auditors: [], date: YYYY-MM-DD }
4// external_2: { status: not started, auditors: [], date: YYYY-MM-DD }
5// =====================
6
7#pragma once
8
9#include "../bigfield/bigfield.hpp"
10#include "../bigfield/goblin_field.hpp"
11#include "../byte_array/byte_array.hpp"
12#include "../circuit_builders/circuit_builders_fwd.hpp"
13#include "../field/field.hpp"
14#include "../memory/rom_table.hpp"
15#include "../memory/twin_rom_table.hpp"
20
22
23// ( ͡° ͜ʖ ͡°)
24template <class Builder_, class Fq, class Fr, class NativeGroup> class element {
25 public:
26 using Builder = Builder_;
28 using biggroup_tag = element; // Facilitates a constexpr check IsBigGroup
29 using BaseField = Fq;
30
31 // Number of bb::fr field elements used to represent a goblin element in the public inputs
32 static constexpr size_t PUBLIC_INPUTS_SIZE = BIGGROUP_PUBLIC_INPUTS_SIZE;
44
45 element();
46 element(const typename NativeGroup::affine_element& input);
47 element(const Fq& x, const Fq& y);
48
49 element(const element& other);
50 element(element&& other) noexcept;
51
53 {
54 const typename NativeGroup::affine_element& native_val = NativeGroup::affine_element::one();
55 element val(native_val);
56 size_t idx = 0;
58 for (auto& limb : val.x.binary_basis_limbs) {
59 limb_vals[idx++] = limb.element.get_value();
60 }
61 for (auto& limb : val.y.binary_basis_limbs) {
62 limb_vals[idx++] = limb.element.get_value();
63 }
65 return limb_vals;
66 }
72 uint32_t set_public() const
73 {
74 const uint32_t start_idx = x.set_public();
75 y.set_public();
76
77 return start_idx;
78 }
79
87 {
88 const size_t FRS_PER_FQ = Fq::PUBLIC_INPUTS_SIZE;
89 std::span<const Fr, FRS_PER_FQ> x_limbs{ limbs.data(), FRS_PER_FQ };
90 std::span<const Fr, FRS_PER_FQ> y_limbs{ limbs.data() + FRS_PER_FQ, FRS_PER_FQ };
91
92 return { Fq::reconstruct_from_public(x_limbs), Fq::reconstruct_from_public(y_limbs) };
93 }
94
95 static element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input)
96 {
97 element out;
98 if (input.is_point_at_infinity()) {
99 Fq x = Fq::from_witness(ctx, NativeGroup::affine_one.x);
100 Fq y = Fq::from_witness(ctx, NativeGroup::affine_one.y);
101 out.x = x;
102 out.y = y;
103 } else {
104 Fq x = Fq::from_witness(ctx, input.x);
105 Fq y = Fq::from_witness(ctx, input.y);
106 out.x = x;
107 out.y = y;
108 }
109 out.set_point_at_infinity(witness_t<Builder>(ctx, input.is_point_at_infinity()));
110
111 // Mark the element as coming out of nowhere
113 out.validate_on_curve();
114 return out;
115 }
116
117 void validate_on_curve() const
118 {
119 Fq b(get_context(), uint256_t(NativeGroup::curve_b));
120 Fq _b = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), b);
121 Fq _x = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), x);
122 Fq _y = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), y);
123 if constexpr (!NativeGroup::has_a) {
124 // we validate y^2 = x^3 + b by setting "fix_remainder_zero = true" when calling mult_madd
125 Fq::mult_madd({ _x.sqr(), _y }, { _x, -_y }, { _b }, true);
126 } else {
127 Fq a(get_context(), uint256_t(NativeGroup::curve_a));
128 Fq _a = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), a);
129 // we validate y^2 = x^3 + ax + b by setting "fix_remainder_zero = true" when calling mult_madd
130 Fq::mult_madd({ _x.sqr(), _x, _y }, { _x, _a, -_y }, { _b }, true);
131 }
132 }
133
138 {
139 this->x.convert_constant_to_fixed_witness(builder);
140 this->y.convert_constant_to_fixed_witness(builder);
141 // Origin tags should be unset after fixing the witness
143 }
144
149 {
150 // Origin tags should be updated within
151 this->x.fix_witness();
152 this->y.fix_witness();
153
154 // This is now effectively a constant
156 }
157
158 static element one(Builder* ctx)
159 {
160 uint256_t x = uint256_t(NativeGroup::one.x);
161 uint256_t y = uint256_t(NativeGroup::one.y);
162 Fq x_fq(ctx, x);
163 Fq y_fq(ctx, y);
164 return element(x_fq, y_fq);
165 }
166
168 {
169 Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx);
170 zero.unset_free_witness_tag();
171 Fq x_fq(zero, zero);
172 Fq y_fq(zero, zero);
173 element result(x_fq, y_fq);
174 result.set_point_at_infinity(true);
175 return result;
176 }
177
178 element& operator=(const element& other);
179 element& operator=(element&& other) noexcept;
180
182 {
184 result.write(y.to_byte_array());
185 result.write(x.to_byte_array());
186 return result;
187 }
188
189 element checked_unconditional_add(const element& other) const;
191
192 element operator+(const element& other) const;
193 element operator-(const element& other) const;
195 {
196 element result(*this);
197 result.y = -result.y;
198 return result;
199 }
201 {
202 *this = *this + other;
203 return *this;
204 }
206 {
207 *this = *this - other;
208 return *this;
209 }
211
212 element operator*(const Fr& other) const;
213
214 element conditional_negate(const bool_ct& predicate) const
215 {
216 element result(*this);
217 result.y = result.y.conditional_negate(predicate);
218 return result;
219 }
220
222 {
223 element result(*this);
224 result.x.assert_is_in_field();
225 result.y.assert_is_in_field();
226 return result;
227 }
228 element scalar_mul(const Fr& scalar, const size_t max_num_bits = 0) const;
229
231 {
232 element result(*this);
233 result.x.self_reduce();
234 result.y.self_reduce();
235 return result;
236 }
237
238 element dbl() const;
239
240 // we use this data structure to add together a sequence of points.
241 // By tracking the previous values of x_1, y_1, \lambda, we can avoid
242 // computing the output y-coordinate of intermediate additions
249 bool is_element = false;
250
252 explicit chain_add_accumulator(const element& input)
253 {
254 x3_prev = input.x;
255 y3_prev = input.y;
256 is_element = true;
257 }
262 };
263
268 static chain_add_accumulator chain_add_start(const element& p1, const element& p2);
269 static chain_add_accumulator chain_add(const element& p1, const chain_add_accumulator& accumulator);
270 static element chain_add_end(const chain_add_accumulator& accumulator);
271 element montgomery_ladder(const element& other) const;
275
276 typename NativeGroup::affine_element get_value() const
277 {
278 uint512_t x_val = x.get_value() % Fq::modulus_u512;
279 uint512_t y_val = y.get_value() % Fq::modulus_u512;
280 auto result = typename NativeGroup::affine_element(x_val.lo, y_val.lo);
282 result.self_set_infinity();
283 }
284 return result;
285 }
286
287 static std::pair<std::vector<element>, std::vector<Fr>> mask_points(const std::vector<element>& _points,
288 const std::vector<Fr>& _scalars);
289
291 const std::vector<element>& _points, const std::vector<Fr>& _scalars);
292
293 // compute a multi-scalar-multiplication by creating a precomputed lookup table for each point,
294 // splitting each scalar multiplier up into a 4-bit sliding window wNAF.
295 // more efficient than batch_mul if num_points < 4
296 // only works with Plookup!
297 template <size_t max_num_bits = 0>
298 static element wnaf_batch_mul(const std::vector<element>& points, const std::vector<Fr>& scalars);
299 static element batch_mul(const std::vector<element>& points,
300 const std::vector<Fr>& scalars,
301 const size_t max_num_bits = 0,
302 const bool with_edgecases = false);
303
304 // we want to conditionally compile this method iff our curve params are the BN254 curve.
305 // This is a bit tricky to do with `std::enable_if`, because `bn254_endo_batch_mul` is a member function of a class
306 // template
307 // && the compiler can't perform partial template specialization on member functions of class templates
308 // => our template parameter cannot be a value but must instead by a type
309 // Our input to `std::enable_if` is a comparison between two types (NativeGroup and bb::g1), which
310 // resolves to either `true/false`.
311 // If `std::enable_if` resolves to `true`, it resolves to a `typedef` that equals `void`
312 // If `std::enable_if` resolves to `false`, there is no member typedef
313 // We want to take the *type* of the output typedef of `std::enable_if`
314 // i.e. for the bn254 curve, the template param is `typename = void`
315 // for any other curve, there is no template param
316 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, bb::g1>::value>>
319 const std::vector<Fr>& big_scalars,
320 const std::vector<element>& small_points,
321 const std::vector<Fr>& small_scalars,
322 const size_t max_num_small_bits);
323
324 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, bb::g1>::value>>
327 const std::vector<Fr>& big_scalars,
328 const std::vector<element>& small_points,
329 const std::vector<Fr>& small_scalars,
330 const Fr& generator_scalar,
331 const size_t max_num_small_bits);
332
333 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256k1::g1>::value>>
334 static element secp256k1_ecdsa_mul(const element& pubkey, const Fr& u1, const Fr& u2);
335
336 static std::vector<bool_ct> compute_naf(const Fr& scalar, const size_t max_num_bits = 0);
337
338 template <size_t max_num_bits = 0, size_t WNAF_SIZE = 4>
340
341 template <size_t wnaf_size, size_t staggered_lo_offset = 0, size_t staggered_hi_offset = 0>
343
345 {
346 if (x.context != nullptr) {
347 return x.context;
348 }
349 if (y.context != nullptr) {
350 return y.context;
351 }
352 return nullptr;
353 }
354
355 Builder* get_context(const element& other) const
356 {
357 if (x.context != nullptr) {
358 return x.context;
359 }
360 if (y.context != nullptr) {
361 return y.context;
362 }
363 if (other.x.context != nullptr) {
364 return other.x.context;
365 }
366 if (other.y.context != nullptr) {
367 return other.y.context;
368 }
369 return nullptr;
370 }
371
373 void set_point_at_infinity(const bool_ct& is_infinity) { _is_infinity = is_infinity; }
375
376 void set_origin_tag(OriginTag tag) const
377 {
378 x.set_origin_tag(tag);
379 y.set_origin_tag(tag);
381 }
382
384 {
385 return OriginTag(x.get_origin_tag(), y.get_origin_tag(), _is_infinity.get_origin_tag());
386 }
387
392 {
393 x.unset_free_witness_tag();
394 y.unset_free_witness_tag();
396 }
397
402 {
403 x.set_free_witness_tag();
404 y.set_free_witness_tag();
406 }
407
410
411 private:
413
414 template <size_t num_elements>
417
418 template <size_t num_elements>
420 const field_t<Builder>& index,
421 const std::array<uint256_t, 8>& limb_max);
422
423 static std::pair<element, element> compute_offset_generators(const size_t num_rounds);
424 static typename NativeGroup::affine_element compute_table_offset_generator();
425
428 four_bit_table_plookup(const element& input);
429
432
434 element operator[](const size_t idx) const { return element_table[idx]; }
437 std::array<uint256_t, 8> limb_max; // tracks the maximum limb size represented in each element_table entry
438 };
439
442 eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
443 : curve_type(input_curve_type)
444 , use_endomorphism(use_endo) {};
445
448
450
451 element operator[](const size_t idx) const;
452
455 };
456
458 const element& input)
459 {
462 element d2 = input.dbl();
463
464 P1.element_table[8] = input;
465 for (size_t i = 9; i < 16; ++i) {
466 P1.element_table[i] = P1.element_table[i - 1] + d2;
467 }
468 for (size_t i = 0; i < 8; ++i) {
469 P1.element_table[i] = (-P1.element_table[15 - i]);
470 }
471 for (size_t i = 0; i < 16; ++i) {
472 endoP1.element_table[i].y = P1.element_table[15 - i].y;
473 }
475 Fq beta(bb::fr(beta_val.slice(0, 136)), bb::fr(beta_val.slice(136, 256)), false);
476 for (size_t i = 0; i < 8; ++i) {
477 endoP1.element_table[i].x = P1.element_table[i].x * beta;
478 endoP1.element_table[15 - i].x = endoP1.element_table[i].x;
479 }
480 P1.coordinates = create_group_element_rom_tables<16>(P1.element_table, P1.limb_max);
481 endoP1.coordinates = create_group_element_rom_tables<16>(endoP1.element_table, endoP1.limb_max);
483 return result;
484 }
485
528
549
551
553
555
561 const std::array<element, 4>& inputs)
562 {
563 quad_lookup_table base_table(inputs);
564 quad_lookup_table endo_table;
566 Fq beta(bb::fr(beta_val.slice(0, 136)), bb::fr(beta_val.slice(136, 256)), false);
567 for (size_t i = 0; i < 8; ++i) {
568 endo_table.element_table[i + 8].x = base_table[7 - i].x * beta;
569 endo_table.element_table[i + 8].y = base_table[7 - i].y;
570
571 endo_table.element_table[7 - i] = (-endo_table.element_table[i + 8]);
572 }
573
574 endo_table.coordinates = create_group_element_rom_tables<16>(endo_table.element_table, endo_table.limb_max);
576 (quad_lookup_table)endo_table);
577 }
578
584 const std::array<element, 5>& inputs)
585 {
586 lookup_table_plookup<5> base_table(inputs);
587 lookup_table_plookup<5> endo_table;
589 Fq beta(bb::fr(beta_val.slice(0, 136)), bb::fr(beta_val.slice(136, 256)), false);
590 for (size_t i = 0; i < 16; ++i) {
591 endo_table.element_table[i + 16].x = base_table[15 - i].x * beta;
592 endo_table.element_table[i + 16].y = base_table[15 - i].y;
593
594 endo_table.element_table[15 - i] = (-endo_table.element_table[i + 16]);
595 }
596
597 endo_table.coordinates = create_group_element_rom_tables<32>(endo_table.element_table, endo_table.limb_max);
598
600 (lookup_table_plookup<5>)endo_table);
601 }
602
610 {
611 num_points = points.size();
612 num_fives = num_points / 5;
613 num_sixes = 0;
614 // size-6 table is expensive and only benefits us if creating them reduces the number of total tables
615 if (num_points == 1) {
616 num_fives = 0;
617 num_sixes = 0;
618 } else if (num_fives * 5 == (num_points - 1)) {
619 num_fives -= 1;
620 num_sixes = 1;
621 } else if (num_fives * 5 == (num_points - 2) && num_fives >= 2) {
622 num_fives -= 2;
623 num_sixes = 2;
624 } else if (num_fives * 5 == (num_points - 3) && num_fives >= 3) {
625 num_fives -= 3;
626 num_sixes = 3;
627 }
628
629 has_quad = ((num_fives * 5 + num_sixes * 6) < num_points - 3) && (num_points >= 4);
630
631 has_triple = ((num_fives * 5 + num_sixes * 6 + (size_t)has_quad * 4) < num_points - 2) && (num_points >= 3);
632
633 has_twin =
634 ((num_fives * 5 + num_sixes * 6 + (size_t)has_quad * 4 + (size_t)has_triple * 3) < num_points - 1) &&
635 (num_points >= 2);
636
637 has_singleton = num_points != ((num_fives * 5 + num_sixes * 6) + ((size_t)has_quad * 4) +
638 ((size_t)has_triple * 3) + ((size_t)has_twin * 2));
639
640 size_t offset = 0;
641 for (size_t i = 0; i < num_sixes; ++i) {
643 points[offset + 6 * i],
644 points[offset + 6 * i + 1],
645 points[offset + 6 * i + 2],
646 points[offset + 6 * i + 3],
647 points[offset + 6 * i + 4],
648 points[offset + 6 * i + 5],
649 }));
650 }
651 offset += 6 * num_sixes;
652 for (size_t i = 0; i < num_fives; ++i) {
654 points[offset + 5 * i],
655 points[offset + 5 * i + 1],
656 points[offset + 5 * i + 2],
657 points[offset + 5 * i + 3],
658 points[offset + 5 * i + 4],
659 }));
660 }
661 offset += 5 * num_fives;
662
663 if (has_quad) {
664 quad_tables.push_back(
665 quad_lookup_table({ points[offset], points[offset + 1], points[offset + 2], points[offset + 3] }));
666 }
667
668 if (has_triple) {
669 triple_tables.push_back(
670 triple_lookup_table({ points[offset], points[offset + 1], points[offset + 2] }));
671 }
672 if (has_twin) {
673 twin_tables.push_back(twin_lookup_table({ points[offset], points[offset + 1] }));
674 }
675
676 if (has_singleton) {
677 singletons.push_back(points[points.size() - 1]);
678 }
679 }
680
682 {
683 std::vector<element> add_accumulator;
684 for (size_t i = 0; i < num_sixes; ++i) {
685 add_accumulator.push_back(six_tables[i][0]);
686 }
687 for (size_t i = 0; i < num_fives; ++i) {
688 add_accumulator.push_back(five_tables[i][0]);
689 }
690 if (has_quad) {
691 add_accumulator.push_back(quad_tables[0][0]);
692 }
693 if (has_twin) {
694 add_accumulator.push_back(twin_tables[0][0]);
695 }
696 if (has_triple) {
697 add_accumulator.push_back(triple_tables[0][0]);
698 }
699 if (has_singleton) {
700 add_accumulator.push_back(singletons[0]);
701 }
702
703 element accumulator = add_accumulator[0];
704 for (size_t i = 1; i < add_accumulator.size(); ++i) {
705 accumulator = accumulator + add_accumulator[i];
706 }
707 return accumulator;
708 }
709
711 {
712 std::vector<element> add_accumulator;
713 for (size_t i = 0; i < num_sixes; ++i) {
714 add_accumulator.push_back(six_tables[i][0]);
715 }
716 for (size_t i = 0; i < num_fives; ++i) {
717 add_accumulator.push_back(five_tables[i][0]);
718 }
719 if (has_quad) {
720 add_accumulator.push_back(quad_tables[0][0]);
721 }
722 if (has_twin) {
723 add_accumulator.push_back(twin_tables[0][0]);
724 }
725 if (has_triple) {
726 add_accumulator.push_back(triple_tables[0][0]);
727 }
728 if (has_singleton) {
729 add_accumulator.push_back(singletons[0]);
730 }
731 if (add_accumulator.size() >= 2) {
732 chain_add_accumulator output = element::chain_add_start(add_accumulator[0], add_accumulator[1]);
733 for (size_t i = 2; i < add_accumulator.size(); ++i) {
734 output = element::chain_add(add_accumulator[i], output);
735 }
736 return output;
737 }
738 return chain_add_accumulator(add_accumulator[0]);
739 }
740
741 element::chain_add_accumulator get_chain_add_accumulator(std::vector<bool_ct>& naf_entries) const
742 {
743 std::vector<element> round_accumulator;
744 for (size_t j = 0; j < num_sixes; ++j) {
745 round_accumulator.push_back(six_tables[j].get({ naf_entries[6 * j],
746 naf_entries[6 * j + 1],
747 naf_entries[6 * j + 2],
748 naf_entries[6 * j + 3],
749 naf_entries[6 * j + 4],
750 naf_entries[6 * j + 5] }));
751 }
752 size_t offset = num_sixes * 6;
753 for (size_t j = 0; j < num_fives; ++j) {
754 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + j * 5],
755 naf_entries[offset + j * 5 + 1],
756 naf_entries[offset + j * 5 + 2],
757 naf_entries[offset + j * 5 + 3],
758 naf_entries[offset + j * 5 + 4] }));
759 }
760 offset += num_fives * 5;
761 if (has_quad) {
762 round_accumulator.push_back(quad_tables[0].get({ naf_entries[offset],
763 naf_entries[offset + 1],
764 naf_entries[offset + 2],
765 naf_entries[offset + 3] }));
766 }
767
768 if (has_triple) {
769 round_accumulator.push_back(
770 triple_tables[0].get({ naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2] }));
771 }
772 if (has_twin) {
773 round_accumulator.push_back(twin_tables[0].get({ naf_entries[offset], naf_entries[offset + 1] }));
774 }
775 if (has_singleton) {
776 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
777 }
778
779 element::chain_add_accumulator accumulator;
780 if (round_accumulator.size() == 1) {
781 return element::chain_add_accumulator(round_accumulator[0]);
782 } else if (round_accumulator.size() == 2) {
783 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
784 } else {
785 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
786 for (size_t j = 2; j < round_accumulator.size(); ++j) {
787 accumulator = element::chain_add(round_accumulator[j], accumulator);
788 }
789 }
790 return (accumulator);
791 }
792
793 element get(std::vector<bool_ct>& naf_entries) const
794 {
795 std::vector<element> round_accumulator;
796 for (size_t j = 0; j < num_sixes; ++j) {
797 round_accumulator.push_back(six_tables[j].get({ naf_entries[6 * j],
798 naf_entries[6 * j + 1],
799 naf_entries[6 * j + 2],
800 naf_entries[6 * j + 3],
801 naf_entries[6 * j + 4],
802 naf_entries[6 * j + 5] }));
803 }
804 size_t offset = num_sixes * 6;
805
806 for (size_t j = 0; j < num_fives; ++j) {
807 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + 5 * j],
808 naf_entries[offset + 5 * j + 1],
809 naf_entries[offset + 5 * j + 2],
810 naf_entries[offset + 5 * j + 3],
811 naf_entries[offset + 5 * j + 4] }));
812 }
813
814 offset += num_fives * 5;
815
816 if (has_quad) {
817 round_accumulator.push_back(quad_tables[0].get(
818 naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2], naf_entries[offset + 3]));
819 }
820
821 if (has_triple) {
822 round_accumulator.push_back(
823 triple_tables[0].get(naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2]));
824 }
825 if (has_twin) {
826 round_accumulator.push_back(twin_tables[0].get(naf_entries[offset], naf_entries[offset + 1]));
827 }
828 if (has_singleton) {
829 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
830 }
831
832 element result = round_accumulator[0];
833 element::chain_add_accumulator accumulator;
834 if (round_accumulator.size() == 1) {
835 return result;
836 } else if (round_accumulator.size() == 2) {
837 return result + round_accumulator[1];
838 } else {
839 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
840 for (size_t j = 2; j < round_accumulator.size(); ++j) {
841 accumulator = element::chain_add(round_accumulator[j], accumulator);
842 }
843 }
844 return element::chain_add_end(accumulator);
845 }
846
854
855 size_t num_sixes;
856 size_t num_fives;
861 };
862
869 {
870 num_points = points.size();
871 num_quads = num_points / 4;
872
873 has_triple = ((num_quads * 4) < num_points - 2) && (num_points >= 3);
874
875 has_twin = ((num_quads * 4 + (size_t)has_triple * 3) < num_points - 1) && (num_points >= 2);
876
877 has_singleton = num_points != (num_quads * 4 + ((size_t)has_triple * 3) + ((size_t)has_twin * 2));
878
879 for (size_t i = 0; i < num_quads; ++i) {
880 quad_tables.push_back(
881 quad_lookup_table({ points[4 * i], points[4 * i + 1], points[4 * i + 2], points[4 * i + 3] }));
882 }
883
884 if (has_triple) {
886 { points[4 * num_quads], points[4 * num_quads + 1], points[4 * num_quads + 2] }));
887 }
888 if (has_twin) {
889 twin_tables.push_back(twin_lookup_table({ points[4 * num_quads], points[4 * num_quads + 1] }));
890 }
891
892 if (has_singleton) {
893 singletons.push_back(points[points.size() - 1]);
894 }
895 }
896
898 {
899 std::vector<element> add_accumulator;
900 for (size_t i = 0; i < num_quads; ++i) {
901 add_accumulator.push_back(quad_tables[i][0]);
902 }
903 if (has_twin) {
904 add_accumulator.push_back(twin_tables[0][0]);
905 }
906 if (has_triple) {
907 add_accumulator.push_back(triple_tables[0][0]);
908 }
909 if (has_singleton) {
910 add_accumulator.push_back(singletons[0]);
911 }
912
913 element accumulator = add_accumulator[0];
914 for (size_t i = 1; i < add_accumulator.size(); ++i) {
915 accumulator = accumulator + add_accumulator[i];
916 }
917 return accumulator;
918 }
919
921 {
922 std::vector<element> add_accumulator;
923 for (size_t i = 0; i < num_quads; ++i) {
924 add_accumulator.push_back(quad_tables[i][0]);
925 }
926 if (has_twin) {
927 add_accumulator.push_back(twin_tables[0][0]);
928 }
929 if (has_triple) {
930 add_accumulator.push_back(triple_tables[0][0]);
931 }
932 if (has_singleton) {
933 add_accumulator.push_back(singletons[0]);
934 }
935 if (add_accumulator.size() >= 2) {
936 chain_add_accumulator output = element::chain_add_start(add_accumulator[0], add_accumulator[1]);
937 for (size_t i = 2; i < add_accumulator.size(); ++i) {
938 output = element::chain_add(add_accumulator[i], output);
939 }
940 return output;
941 }
942 return chain_add_accumulator(add_accumulator[0]);
943 }
944
945 element::chain_add_accumulator get_chain_add_accumulator(std::vector<bool_ct>& naf_entries) const
946 {
947 std::vector<element> round_accumulator;
948 for (size_t j = 0; j < num_quads; ++j) {
949 round_accumulator.push_back(quad_tables[j].get(std::array<bool_ct, 4>{
950 naf_entries[4 * j], naf_entries[4 * j + 1], naf_entries[4 * j + 2], naf_entries[4 * j + 3] }));
951 }
952
953 if (has_triple) {
954 round_accumulator.push_back(triple_tables[0].get(std::array<bool_ct, 3>{
955 naf_entries[num_quads * 4], naf_entries[num_quads * 4 + 1], naf_entries[num_quads * 4 + 2] }));
956 }
957 if (has_twin) {
958 round_accumulator.push_back(twin_tables[0].get(
959 std::array<bool_ct, 2>{ naf_entries[num_quads * 4], naf_entries[num_quads * 4 + 1] }));
960 }
961 if (has_singleton) {
962 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
963 }
964
965 element::chain_add_accumulator accumulator;
966 if (round_accumulator.size() == 1) {
967 accumulator.x3_prev = round_accumulator[0].x;
968 accumulator.y3_prev = round_accumulator[0].y;
969 accumulator.is_element = true;
970 return accumulator;
971 } else if (round_accumulator.size() == 2) {
972 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
973 } else {
974 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
975 for (size_t j = 2; j < round_accumulator.size(); ++j) {
976 accumulator = element::chain_add(round_accumulator[j], accumulator);
977 }
978 }
979 return (accumulator);
980 }
981
982 element get(std::vector<bool_ct>& naf_entries) const
983 {
984 std::vector<element> round_accumulator;
985 for (size_t j = 0; j < num_quads; ++j) {
986 round_accumulator.push_back(quad_tables[j].get(
987 { naf_entries[4 * j], naf_entries[4 * j + 1], naf_entries[4 * j + 2], naf_entries[4 * j + 3] }));
988 }
989
990 if (has_triple) {
991 round_accumulator.push_back(triple_tables[0].get(std::array<bool_ct, 3>{
992 naf_entries[num_quads * 4], naf_entries[num_quads * 4 + 1], naf_entries[num_quads * 4 + 2] }));
993 }
994 if (has_twin) {
995 round_accumulator.push_back(
996 twin_tables[0].get({ naf_entries[num_quads * 4], naf_entries[num_quads * 4 + 1] }));
997 }
998 if (has_singleton) {
999 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
1000 }
1001
1002 element result = round_accumulator[0];
1003 element::chain_add_accumulator accumulator;
1004 if (round_accumulator.size() == 1) {
1005 return result;
1006 } else if (round_accumulator.size() == 2) {
1007 return result + round_accumulator[1];
1008 } else {
1009 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
1010 for (size_t j = 2; j < round_accumulator.size(); ++j) {
1011 accumulator = element::chain_add(round_accumulator[j], accumulator);
1012 }
1013 }
1014 return element::chain_add_end(accumulator);
1015 }
1016
1022
1027 };
1028
1030};
1031
1032template <typename C, typename Fq, typename Fr, typename G>
1033inline std::ostream& operator<<(std::ostream& os, element<C, Fq, Fr, G> const& v)
1034{
1035 return os << "{ " << v.x << " , " << v.y << " }";
1036}
1037} // namespace bb::stdlib::element_default
1038
1039namespace bb::stdlib {
1040template <typename T>
1042
1043template <typename Builder, class Fq, class Fr, class NativeGroup>
1047
1052template <typename C, typename Fq, typename Fr, typename G>
1056} // namespace bb::stdlib
1057#include "biggroup_batch_mul.hpp"
1058#include "biggroup_bn254.hpp"
1059#include "biggroup_goblin.hpp"
1060#include "biggroup_impl.hpp"
1061#include "biggroup_nafs.hpp"
1062#include "biggroup_secp256k1.hpp"
1063#include "biggroup_tables.hpp"
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:59
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Implements boolean logic in-circuit.
Definition bool.hpp:59
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:119
void set_free_witness_tag()
Definition bool.hpp:121
void unset_free_witness_tag()
Definition bool.hpp:122
OriginTag get_origin_tag() const
Definition bool.hpp:120
Represents a dynamic array of bytes in-circuit.
byte_array & write(byte_array const &other)
Appends the contents of another byte_array (other) to the end of this one.
element checked_unconditional_subtract(const element &other) const
element & operator=(const element &other)
byte_array< Builder > to_byte_array() const
Definition biggroup.hpp:181
std::array< element, 2 > checked_unconditional_add_sub(const element &) const
Compute (*this) + other AND (*this) - other as a size-2 array.
element operator*(const Fr &other) const
element operator-=(const element &other)
Definition biggroup.hpp:205
NativeGroup::affine_element get_value() const
Definition biggroup.hpp:276
static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr &scalar)
Builder * get_context(const element &other) const
Definition biggroup.hpp:355
element multiple_montgomery_ladder(const std::vector< chain_add_accumulator > &to_add) const
Perform repeated iterations of the montgomery ladder algorithm.
static element one(Builder *ctx)
Definition biggroup.hpp:158
static chain_add_accumulator chain_add_start(const element &p1, const element &p2)
static element from_witness(Builder *ctx, const typename NativeGroup::affine_element &input)
Definition biggroup.hpp:95
element montgomery_ladder(const element &other) const
static element chain_add_end(const chain_add_accumulator &accumulator)
void unset_free_witness_tag()
Unset the free witness flag for the element's tags.
Definition biggroup.hpp:391
static element point_at_infinity(Builder *ctx)
Definition biggroup.hpp:167
static std::pair< four_bit_table_plookup, four_bit_table_plookup > create_endo_pair_four_bit_table_plookup(const element &input)
Definition biggroup.hpp:457
static std::vector< field_t< Builder > > compute_wnaf(const Fr &scalar)
static NativeGroup::affine_element compute_table_offset_generator()
Compute an offset generator for use in biggroup tables.
void set_origin_tag(OriginTag tag) const
Definition biggroup.hpp:376
void set_point_at_infinity(const bool_ct &is_infinity)
Definition biggroup.hpp:373
static element bn254_endo_batch_mul_with_generator(const std::vector< element > &big_points, const std::vector< Fr > &big_scalars, const std::vector< element > &small_points, const std::vector< Fr > &small_scalars, const Fr &generator_scalar, const size_t max_num_small_bits)
static std::pair< quad_lookup_table, quad_lookup_table > create_endo_pair_quad_lookup_table(const std::array< element, 4 > &inputs)
Definition biggroup.hpp:560
element(const typename NativeGroup::affine_element &input)
element quadruple_and_add(const std::vector< element > &to_add) const
Compute 4.P + to_add[0] + ... + to_add[to_add.size() - 1].
static element bn254_endo_batch_mul(const std::vector< element > &big_points, const std::vector< Fr > &big_scalars, const std::vector< element > &small_points, const std::vector< Fr > &small_scalars, const size_t max_num_small_bits)
void convert_constant_to_fixed_witness(Builder *builder)
Creates fixed witnesses from a constant element.
Definition biggroup.hpp:137
static std::array< fr, PUBLIC_INPUTS_SIZE > construct_dummy()
Definition biggroup.hpp:52
element scalar_mul(const Fr &scalar, const size_t max_num_bits=0) const
Implements scalar multiplication that supports short scalars. For multiple scalar multiplication use ...
element operator+=(const element &other)
Definition biggroup.hpp:200
static element wnaf_batch_mul(const std::vector< element > &points, const std::vector< Fr > &scalars)
element checked_unconditional_add(const element &other) const
static std::vector< bool_ct > compute_naf(const Fr &scalar, const size_t max_num_bits=0)
uint32_t set_public() const
Set the witness indices for the x and y coordinates to public.
Definition biggroup.hpp:72
static element read_group_element_rom_tables(const std::array< twin_rom_table< Builder >, 5 > &tables, const field_t< Builder > &index, const std::array< uint256_t, 8 > &limb_max)
static std::pair< lookup_table_plookup< 5 >, lookup_table_plookup< 5 > > create_endo_pair_five_lookup_table(const std::array< element, 5 > &inputs)
Definition biggroup.hpp:583
element conditional_negate(const bool_ct &predicate) const
Definition biggroup.hpp:214
static std::pair< std::vector< element >, std::vector< Fr > > handle_points_at_infinity(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Replace all pairs (∞, scalar) by the pair (one, 0) where one is a fixed generator of the curve.
void set_free_witness_tag()
Set the free witness flag for the element's tags.
Definition biggroup.hpp:401
element get_standard_form() const
Enforce x and y coordinates of a point to be (0,0) in the case of point at infinity.
static element reconstruct_from_public(const std::span< const Fr, PUBLIC_INPUTS_SIZE > &limbs)
Reconstruct a biggroup element from limbs of its coordinates (generally stored in the public inputs)
Definition biggroup.hpp:86
static constexpr size_t PUBLIC_INPUTS_SIZE
Definition biggroup.hpp:32
element operator+(const element &other) const
static chain_add_accumulator chain_add(const element &p1, const chain_add_accumulator &accumulator)
static std::array< twin_rom_table< Builder >, 5 > create_group_element_rom_tables(const std::array< element, num_elements > &elements, std::array< uint256_t, 8 > &limb_max)
static std::pair< std::vector< element >, std::vector< Fr > > mask_points(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Given two lists of points that need to be multiplied by scalars, create a new list of length +1 with ...
static element batch_mul(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits=0, const bool with_edgecases=false)
Generic batch multiplication that works for all elliptic curve types.
static std::pair< element, element > compute_offset_generators(const size_t num_rounds)
static element secp256k1_ecdsa_mul(const element &pubkey, const Fr &u1, const Fr &u2)
Custom element class for when using goblin.
AluTraceBuilder builder
Definition alu.test.cpp:123
FF a
FF b
#define G(r, i, a, b, c, d)
Definition blake2s.cpp:116
uint8_t const size_t length
Definition data_store.hpp:9
ssize_t offset
Definition engine.cpp:36
FF const & operator[](size_t idx) const
Retruns the element in beta_products at place #idx.
std::ostream & operator<<(std::ostream &os, element< C, Fq, Fr, G > const &v)
std::conditional_t< IsGoblinBigGroup< C, Fq, Fr, G >, element_goblin::goblin_element< C, goblin_field< C >, Fr, G >, element_default::element< C, Fq, Fr, G > > element
element wraps either element_default::element or element_goblin::goblin_element depending on parametr...
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
Curve::ScalarField Fr
static constexpr field cube_root_of_unity()
static constexpr size_t PUBLIC_INPUTS_SIZE
BB_INLINE constexpr field sqr() const noexcept
static field reconstruct_from_public(const std::span< const field< V >, PUBLIC_INPUTS_SIZE > &limbs)
static constexpr field zero()
batch_lookup_table_base(const std::vector< element > &points)
Definition biggroup.hpp:868
element get(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:982
element::chain_add_accumulator get_chain_add_accumulator(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:945
element get(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:793
std::vector< lookup_table_plookup< 6 > > six_tables
Definition biggroup.hpp:847
std::vector< lookup_table_plookup< 5 > > five_tables
Definition biggroup.hpp:848
element::chain_add_accumulator get_chain_add_accumulator(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:741
batch_lookup_table_plookup(const std::vector< element > &points)
Definition biggroup.hpp:609
chain_add_accumulator(chain_add_accumulator &&other)=default
chain_add_accumulator(const chain_add_accumulator &other)=default
chain_add_accumulator & operator=(chain_add_accumulator &&other)=default
chain_add_accumulator & operator=(const chain_add_accumulator &other)=default
eight_bit_fixed_base_table & operator=(const eight_bit_fixed_base_table &other)=default
element operator[](const field_t< Builder > &index) const
eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
Definition biggroup.hpp:442
eight_bit_fixed_base_table(const eight_bit_fixed_base_table &other)=default
std::array< twin_rom_table< Builder >, 5 > coordinates
Definition biggroup.hpp:436
element operator[](const field_t< Builder > &index) const
four_bit_table_plookup(const four_bit_table_plookup &other)=default
four_bit_table_plookup & operator=(const four_bit_table_plookup &other)=default
std::array< field_t< Builder >, table_size > x_b0_table
Definition biggroup.hpp:515
std::array< field_t< Builder >, table_size > y_b0_table
Definition biggroup.hpp:520
std::array< field_t< Builder >, table_size > y_b2_table
Definition biggroup.hpp:522
std::array< field_t< Builder >, table_size > y_b3_table
Definition biggroup.hpp:523
lookup_table_base & operator=(const lookup_table_base &other)=default
std::array< field_t< Builder >, table_size > x_b1_table
Definition biggroup.hpp:516
std::array< field_t< Builder >, table_size > x_b3_table
Definition biggroup.hpp:518
std::array< field_t< Builder >, table_size > y_b1_table
Definition biggroup.hpp:521
lookup_table_base(const lookup_table_base &other)=default
element get(const std::array< bool_ct, length > &bits) const
std::array< element, table_size > element_table
Definition biggroup.hpp:526
std::array< field_t< Builder >, table_size > x_b2_table
Definition biggroup.hpp:517
lookup_table_plookup(const lookup_table_plookup &other)=default
lookup_table_plookup & operator=(const lookup_table_plookup &other)=default
std::array< twin_rom_table< Builder >, 5 > coordinates
Definition biggroup.hpp:546
element get(const std::array< bool_ct, length > &bits) const
std::vector< field_t< Builder > > wnaf
Definition biggroup.hpp:34
bb::fq Fq