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 "../field/field_utils.hpp"
15#include "../memory/rom_table.hpp"
16#include "../memory/twin_rom_table.hpp"
21#include <cstddef>
22
24
25// ( ͡° ͜ʖ ͡°)
26template <class Builder_, class Fq, class Fr, class NativeGroup> class element {
27 public:
28 using Builder = Builder_;
32 using biggroup_tag = element; // Facilitates a constexpr check IsBigGroup
33 using BaseField = Fq;
34
35 // Number of bb::fr field elements used to represent a goblin element in the public inputs
36 static constexpr size_t PUBLIC_INPUTS_SIZE = BIGGROUP_PUBLIC_INPUTS_SIZE;
37
38 element();
39 element(const typename NativeGroup::affine_element& input);
40
41 // Construct a biggroup element from its coordinates
42 // By default, we validate that the point is on the curve
43 element(const Fq& x, const Fq& y, const bool assert_on_curve = true);
44 element(const Fq& x, const Fq& y, const bool_ct& is_infinity, bool assert_on_curve = true);
45
46 element(const element& other);
47 element(element&& other) noexcept;
48
49 ~element() = default;
50
56 uint32_t set_public() const
57 {
58 const uint32_t start_idx = _x.set_public();
59 _y.set_public();
60
61 return start_idx;
62 }
63
71 {
72 const size_t FRS_PER_FQ = Fq::PUBLIC_INPUTS_SIZE;
73 std::span<const Fr, FRS_PER_FQ> x_limbs{ limbs.data(), FRS_PER_FQ };
74 std::span<const Fr, FRS_PER_FQ> y_limbs{ limbs.data() + FRS_PER_FQ, FRS_PER_FQ };
75
76 const Fq x = Fq::reconstruct_from_public(x_limbs);
77 const Fq y = Fq::reconstruct_from_public(y_limbs);
78 return element(x, y);
79 }
80
89 static element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input)
90 {
91 element out;
92 if (input.is_point_at_infinity()) {
93 Fq x = Fq::from_witness(ctx, NativeGroup::affine_one.x);
94 Fq y = Fq::from_witness(ctx, NativeGroup::affine_one.y);
95 out._x = x;
96 out._y = y;
97 } else {
98 Fq x = Fq::from_witness(ctx, input.x);
99 Fq y = Fq::from_witness(ctx, input.y);
100 out._x = x;
101 out._y = y;
102 }
103 out.set_point_at_infinity(witness_ct(ctx, input.is_point_at_infinity()));
104
105 // Mark the element as coming out of nowhere
107 out.validate_on_curve();
108 return out;
109 }
110
114 void validate_on_curve(std::string const& msg = "biggroup::validate_on_curve") const
115 {
116 bool has_circuit_failed = get_context()->failed();
117
118 Fq b(get_context(), uint256_t(NativeGroup::curve_b));
119 Fq adjusted_b = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), b);
120 Fq adjusted_x = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), _x);
121 Fq adjusted_y = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), _y);
122 if constexpr (!NativeGroup::has_a) {
123 // we validate y^2 = x^3 + b by setting "fix_remainder_zero = true" when calling mult_madd
124 Fq::mult_madd({ adjusted_x.sqr(), adjusted_y }, { adjusted_x, -adjusted_y }, { adjusted_b }, true);
125 } else {
126 Fq a(get_context(), uint256_t(NativeGroup::curve_a));
127 Fq adjusted_a = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), a);
128 // we validate y^2 = x^3 + ax + b by setting "fix_remainder_zero = true" when calling mult_madd
129 Fq::mult_madd({ adjusted_x.sqr(), adjusted_x, adjusted_y },
130 { adjusted_x, adjusted_a, -adjusted_y },
131 { adjusted_b },
132 true);
133 }
134
135 if ((!has_circuit_failed) && (get_context()->failed())) {
136 vinfo("Original bigfield error generated by biggroup::validate_on_curve: ", get_context()->err());
137 get_context()->failure(msg);
138 }
139 }
140
141 [[nodiscard]] bool is_constant() const
142 {
143 const bool x_is_const = _x.is_constant();
144 const bool y_is_const = _y.is_constant();
145 BB_ASSERT_EQ(x_is_const, y_is_const, "biggroup: x and y coordinate constant status mismatch");
146 return x_is_const;
147 }
148
153 {
154 this->_x.convert_constant_to_fixed_witness(builder);
155 this->_y.convert_constant_to_fixed_witness(builder);
156 // Origin tags should be unset after fixing the witness
158 }
159
164 {
165 // Origin tags should be updated within
166 this->_x.fix_witness();
167 this->_y.fix_witness();
168
169 // This is now effectively a constant
171 }
172
176 static element one(Builder* ctx)
177 {
178 uint256_t x = uint256_t(NativeGroup::one.x);
179 uint256_t y = uint256_t(NativeGroup::one.y);
180 Fq x_fq(ctx, x);
181 Fq y_fq(ctx, y);
182 return element(x_fq, y_fq, /*assert_on_curve=*/false);
183 }
184
186 {
187 Fr zero = Fr::from_witness_index(ctx, ctx->zero_idx());
188 zero.unset_free_witness_tag();
189 Fq x_fq(zero, zero);
190 Fq y_fq(zero, zero);
191 element result(x_fq, y_fq, /*assert_on_curve=*/false);
192 result.set_point_at_infinity(true);
193 return result;
194 }
195
196 element& operator=(const element& other);
197 element& operator=(element&& other) noexcept;
198
199 element checked_unconditional_add(const element& other) const;
201
202 element operator+(const element& other) const;
203 element operator-(const element& other) const;
205 {
206 element result(*this);
207 result._y = -result._y;
208 return result;
209 }
211 {
212 *this = *this + other;
213 return *this;
214 }
216 {
217 *this = *this - other;
218 return *this;
219 }
220
221 element operator*(const Fr& scalar) const;
222
223 element conditional_negate(const bool_ct& predicate) const
224 {
225 element result(*this);
226 result._y = result._y.conditional_negate(predicate);
227 return result;
228 }
229
237 element conditional_select(const element& other, const bool_ct& predicate) const
238 {
239 // If predicate is constant, we can select out of circuit
240 if (predicate.is_constant()) {
241 auto result = predicate.get_value() ? other : *this;
242 result.set_origin_tag(
243 OriginTag(predicate.get_origin_tag(), other.get_origin_tag(), this->get_origin_tag()));
244 return result;
245 }
246
247 // Get the builder context
248 Builder* ctx = validate_context<Builder>(get_context(), other.get_context(), predicate.get_context());
249 BB_ASSERT_NEQ(ctx, nullptr, "biggroup::conditional_select must have a context");
250
251 element result(*this);
252 result._x = result._x.conditional_select(other._x, predicate);
253 result._y = result._y.conditional_select(other._y, predicate);
254 result._is_infinity =
256 return result;
257 }
258
270 const std::string msg = "biggroup::incomplete_assert_equal") const
271 {
272 is_point_at_infinity().assert_equal(other.is_point_at_infinity(), msg + " (infinity flag)");
273 _x.assert_equal(other._x, msg + " (x coordinate)");
274 _y.assert_equal(other._y, msg + " (y coordinate)");
275 }
276
278 {
279 element result(*this);
280 result._x.reduce_mod_target_modulus();
281 result._y.reduce_mod_target_modulus();
282 return result;
283 }
284 element scalar_mul(const Fr& scalar, const size_t max_num_bits = 0) const;
285
287 {
288 element result(*this);
289 result._x.self_reduce();
290 result._y.self_reduce();
291 return result;
292 }
293
294 void assert_coordinates_in_field(const std::string& msg = "biggroup::assert_coordinates_in_field") const
295 {
296 _x.assert_is_in_field(msg + " (x coordinate)");
297 _y.assert_is_in_field(msg + " (y coordinate)");
298 }
299
300 element dbl() const;
301
302 // we use this data structure to add together a sequence of points.
303 // By tracking the previous values of x_1, y_1, \lambda, we can avoid
304 // computing the output y-coordinate of intermediate additions
311 bool is_full_element = false;
312
314 explicit chain_add_accumulator(const element& input)
315 : x3_prev(input._x)
316 , y3_prev(input._y)
317 , is_full_element(true)
318 {}
320 chain_add_accumulator(chain_add_accumulator&& other) noexcept = default;
324 };
325
331 static chain_add_accumulator chain_add_start(const element& p1, const element& p2);
332 static chain_add_accumulator chain_add(const element& p1, const chain_add_accumulator& accumulator);
333 static element chain_add_end(const chain_add_accumulator& accumulator);
335
336 typename NativeGroup::affine_element get_value() const
337 {
338 uint512_t x_val = _x.get_value() % Fq::modulus_u512;
339 uint512_t y_val = _y.get_value() % Fq::modulus_u512;
340 auto result = typename NativeGroup::affine_element(x_val.lo, y_val.lo);
342 result.self_set_infinity();
343 }
344 return result;
345 }
346
347 static element batch_mul(const std::vector<element>& points,
348 const std::vector<Fr>& scalars,
349 const size_t max_num_bits = 0,
350 const bool with_edgecases = false,
351 const Fr& masking_scalar = Fr(1));
352
353 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256k1::g1>::value>>
354 static element secp256k1_ecdsa_mul(const element& pubkey, const Fr& u1, const Fr& u2);
355
360 static std::vector<bool_ct> compute_naf(const Fr& scalar, const size_t max_num_bits = 0);
361
362 // Internal struct to represent wNAF for a secp256k1 scalar
370
371 // Internal struct to represent a pair of secp256k1 wNAFs
376
381 template <size_t wnaf_size, size_t staggered_lo_offset = 0, size_t staggered_hi_offset = 0>
382 static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr& scalar, const bool range_constrain_wnaf = true);
383
384 Builder* get_context() const { return validate_context<Builder>(_x.get_context(), _y.get_context()); }
385
386 Builder* get_context(const element& other) const
387 {
388 return validate_context<Builder>(get_context(), other.get_context());
389 }
390
391 // Coordinate accessors (non-owning, const reference)
392 const Fq& x() const { return _x; }
393 const Fq& y() const { return _y; }
394
396 void set_point_at_infinity(const bool_ct& is_infinity, const bool& add_to_used_witnesses = false)
397 {
398 _is_infinity = is_infinity.normalize();
399 if (add_to_used_witnesses) {
401 };
402 }
404
406 {
407 _x.set_origin_tag(tag);
408 _y.set_origin_tag(tag);
410 }
411
413 {
414 _x.clear_round_provenance();
415 _y.clear_round_provenance();
417 }
418
420 {
421 return OriginTag(_x.get_origin_tag(), _y.get_origin_tag(), _is_infinity.get_origin_tag());
422 }
423
428 {
429 _x.unset_free_witness_tag();
430 _y.unset_free_witness_tag();
432 }
433
438 {
439 _x.set_free_witness_tag();
440 _y.set_free_witness_tag();
442 }
443
444 // For testing purposes only
446
447 private:
451
457
467 static std::pair<std::vector<element>, std::vector<Fr>> mask_points(const std::vector<element>& _points,
468 const std::vector<Fr>& _scalars,
469 const Fr& masking_scalar);
470
480 const std::vector<element>& _points, const std::vector<Fr>& _scalars);
481
496 template <size_t num_bits, size_t wnaf_size, size_t lo_stagger, size_t hi_stagger>
498 const secp256k1::fr& scalar,
499 size_t stagger,
500 bool is_negative,
501 const bool range_constrain_wnaf = true,
502 bool is_lo = false);
503
513 template <size_t wnaf_size>
514 static std::pair<uint64_t, bool> get_staggered_wnaf_fragment_value(const uint64_t fragment_u64,
515 const uint64_t stagger,
516 bool is_negative,
517 bool wnaf_skew);
518
532 template <size_t wnaf_size>
534 const uint64_t* wnaf_values,
535 bool is_negative,
536 size_t rounds,
537 const bool range_constrain_wnaf = true);
538
550 template <size_t wnaf_size>
552 const std::vector<field_ct>& wnaf,
553 const bool_ct& positive_skew,
554 const bool_ct& negative_skew,
555 const field_ct& stagger_fragment,
556 const size_t stagger,
557 const size_t rounds);
558
559 template <size_t num_elements>
562
563 template <size_t num_elements>
564 static element read_group_element_rom_tables(const std::array<twin_rom_table<Builder>, Fq::NUM_LIMBS + 1>& tables,
565 const field_ct& index,
567
568 static std::pair<element, element> compute_offset_generators(const size_t num_rounds);
569 static typename NativeGroup::affine_element compute_table_offset_generator();
570
578 four_bit_table_plookup(const element& input);
579
585
586 element operator[](const field_ct& index) const;
587 element operator[](const size_t idx) const { return element_table[idx]; }
589
590 // Each coordinate is an Fq element, which has 4 binary basis limbs and 1 prime basis limb
592 std::array<uint256_t, Fq::NUM_LIMBS * 2> limb_max; // tracks the maximum size of each binary basis limb
593 };
594
603 eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
604 : curve_type(input_curve_type)
605 , use_endomorphism(use_endo)
606 {}
607
613
614 element operator[](const field_ct& index) const;
615
616 element operator[](const size_t index) const;
617
620 };
621
623 const element& input);
624
629 template <size_t length> struct lookup_table_plookup {
630 static constexpr size_t table_size = (1ULL << (length));
635 lookup_table_plookup(lookup_table_plookup&& other) noexcept = default;
638
639 element get(const std::array<bool_ct, length>& bits) const;
640
641 element operator[](const size_t idx) const { return element_table[idx]; }
642
644
645 // Each coordinate is an Fq element, which has 4 binary basis limbs and 1 prime basis limb
646 // ROM tables: (idx, x0, x1), (idx, x2, x3), (idx, y0, y1), (idx, y2, y3), (idx, xp, yp)
649 };
650
652
654
656
663 {
664 quad_lookup_table base_table(inputs);
665 quad_lookup_table endo_table;
667 Fq beta(bb::fr(beta_val.slice(0, 136)), bb::fr(beta_val.slice(136, 256)), false);
668 for (size_t i = 0; i < 8; ++i) {
669 endo_table.element_table[i + 8].x = base_table[7 - i].x * beta;
670 endo_table.element_table[i + 8].y = base_table[7 - i].y;
671
672 endo_table.element_table[7 - i] = (-endo_table.element_table[i + 8]);
673 }
674
675 endo_table.coordinates = create_group_element_rom_tables<16>(endo_table.element_table, endo_table.limb_max);
676 return std::make_pair<quad_lookup_table, quad_lookup_table>(base_table, endo_table);
677 }
678
685 : num_points(points.size())
686 , num_fives(num_points / 5)
687 {
688 // size-6 table is expensive and only benefits us if creating them reduces the number of total tables
689 if (num_points == 1) {
690 num_fives = 0;
691 num_sixes = 0;
692 } else if (num_fives * 5 == (num_points - 1)) {
693 // last 6 points to be added as one 6-table
694 num_fives -= 1;
695 num_sixes = 1;
696 } else if (num_fives * 5 == (num_points - 2) && num_fives >= 2) {
697 // last 12 points to be added as two 6-tables
698 num_fives -= 2;
699 num_sixes = 2;
700 } else if (num_fives * 5 == (num_points - 3) && num_fives >= 3) {
701 // last 18 points to be added as three 6-tables
702 num_fives -= 3;
703 num_sixes = 3;
704 }
705
706 // Calculate remaining points after allocating fives and sixes tables
707 size_t remaining_points = num_points - (num_fives * 5 + num_sixes * 6);
708
709 // Allocate one quad table if required (and update remaining points)
710 has_quad = (remaining_points >= 4) && (num_points >= 4);
711 if (has_quad) {
712 remaining_points -= 4;
713 }
714
715 // Allocate one triple table if required (and update remaining points)
716 has_triple = (remaining_points >= 3) && (num_points >= 3);
717 if (has_triple) {
718 remaining_points -= 3;
719 }
720
721 // Allocate one twin table if required (and update remaining points)
722 has_twin = (remaining_points >= 2) && (num_points >= 2);
723 if (has_twin) {
724 remaining_points -= 2;
725 }
726
727 // If there is anything remaining, allocate a singleton
728 has_singleton = (remaining_points != 0) && (num_points >= 1);
729
730 // Sanity check
732 num_sixes * 6 + num_fives * 5 + static_cast<size_t>(has_quad) * 4 +
733 static_cast<size_t>(has_triple) * 3 + static_cast<size_t>(has_twin) * 2 +
734 static_cast<size_t>(has_singleton) * 1,
735 "point allocation mismatch");
736
737 size_t offset = 0;
738 for (size_t i = 0; i < num_sixes; ++i) {
740 points[offset + (6 * i)],
741 points[offset + (6 * i) + 1],
742 points[offset + (6 * i) + 2],
743 points[offset + (6 * i) + 3],
744 points[offset + (6 * i) + 4],
745 points[offset + (6 * i) + 5],
746 }));
747 }
748 offset += 6 * num_sixes;
749 for (size_t i = 0; i < num_fives; ++i) {
751 points[offset + (5 * i)],
752 points[offset + (5 * i) + 1],
753 points[offset + (5 * i) + 2],
754 points[offset + (5 * i) + 3],
755 points[offset + (5 * i) + 4],
756 }));
757 }
758 offset += 5 * num_fives;
759
760 if (has_quad) {
761 quad_tables.push_back(
762 quad_lookup_table({ points[offset], points[offset + 1], points[offset + 2], points[offset + 3] }));
763 }
764 if (has_triple) {
765 triple_tables.push_back(
766 triple_lookup_table({ points[offset], points[offset + 1], points[offset + 2] }));
767 }
768 if (has_twin) {
769 twin_tables.push_back(twin_lookup_table({ points[offset], points[offset + 1] }));
770 }
771 if (has_singleton) {
772 singletons.push_back(points[points.size() - 1]);
773 }
774 }
775
777 {
778 std::vector<element> add_accumulator;
779 for (size_t i = 0; i < num_sixes; ++i) {
780 add_accumulator.push_back(six_tables[i][0]);
781 }
782 for (size_t i = 0; i < num_fives; ++i) {
783 add_accumulator.push_back(five_tables[i][0]);
784 }
785 if (has_quad) {
786 add_accumulator.push_back(quad_tables[0][0]);
787 }
788 if (has_twin) {
789 add_accumulator.push_back(twin_tables[0][0]);
790 }
791 if (has_triple) {
792 add_accumulator.push_back(triple_tables[0][0]);
793 }
794 if (has_singleton) {
795 add_accumulator.push_back(singletons[0]);
796 }
797 if (add_accumulator.size() >= 2) {
798 chain_add_accumulator output = element::chain_add_start(add_accumulator[0], add_accumulator[1]);
799 for (size_t i = 2; i < add_accumulator.size(); ++i) {
800 output = element::chain_add(add_accumulator[i], output);
801 }
802 return output;
803 }
804 return chain_add_accumulator(add_accumulator[0]);
805 }
806
807 element::chain_add_accumulator get_chain_add_accumulator(std::vector<bool_ct>& naf_entries) const
808 {
809 std::vector<element> round_accumulator;
810 for (size_t j = 0; j < num_sixes; ++j) {
811 round_accumulator.push_back(six_tables[j].get({ naf_entries[6 * j],
812 naf_entries[(6 * j) + 1],
813 naf_entries[(6 * j) + 2],
814 naf_entries[(6 * j) + 3],
815 naf_entries[(6 * j) + 4],
816 naf_entries[(6 * j) + 5] }));
817 }
818 size_t offset = num_sixes * 6;
819 for (size_t j = 0; j < num_fives; ++j) {
820 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + (j * 5)],
821 naf_entries[offset + (j * 5) + 1],
822 naf_entries[offset + (j * 5) + 2],
823 naf_entries[offset + (j * 5) + 3],
824 naf_entries[offset + (j * 5) + 4] }));
825 }
826 offset += num_fives * 5;
827 if (has_quad) {
828 round_accumulator.push_back(quad_tables[0].get({ naf_entries[offset],
829 naf_entries[offset + 1],
830 naf_entries[offset + 2],
831 naf_entries[offset + 3] }));
832 }
833
834 if (has_triple) {
835 round_accumulator.push_back(
836 triple_tables[0].get({ naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2] }));
837 }
838 if (has_twin) {
839 round_accumulator.push_back(twin_tables[0].get({ naf_entries[offset], naf_entries[offset + 1] }));
840 }
841 if (has_singleton) {
842 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
843 }
844
845 if (round_accumulator.size() == 1) {
846 return element::chain_add_accumulator(round_accumulator[0]);
847 }
848
849 if (round_accumulator.size() == 2) {
850 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
851 }
852
853 // Use chain add for at least 3 elements
854 element::chain_add_accumulator accumulator =
855 element::chain_add_start(round_accumulator[0], round_accumulator[1]);
856 for (size_t j = 2; j < round_accumulator.size(); ++j) {
857 accumulator = element::chain_add(round_accumulator[j], accumulator);
858 }
859
860 return (accumulator);
861 }
862
863 element get(std::vector<bool_ct>& naf_entries) const
864 {
865 std::vector<element> round_accumulator;
866 for (size_t j = 0; j < num_sixes; ++j) {
867 round_accumulator.push_back(six_tables[j].get({ naf_entries[(6 * j)],
868 naf_entries[(6 * j) + 1],
869 naf_entries[(6 * j) + 2],
870 naf_entries[(6 * j) + 3],
871 naf_entries[(6 * j) + 4],
872 naf_entries[(6 * j) + 5] }));
873 }
874 size_t offset = num_sixes * 6;
875
876 for (size_t j = 0; j < num_fives; ++j) {
877 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + (5 * j)],
878 naf_entries[offset + (5 * j) + 1],
879 naf_entries[offset + (5 * j) + 2],
880 naf_entries[offset + (5 * j) + 3],
881 naf_entries[offset + (5 * j) + 4] }));
882 }
883
884 offset += num_fives * 5;
885
886 if (has_quad) {
887 round_accumulator.push_back(quad_tables[0].get(
888 naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2], naf_entries[offset + 3]));
889 }
890 if (has_triple) {
891 round_accumulator.push_back(
892 triple_tables[0].get(naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2]));
893 }
894 if (has_twin) {
895 round_accumulator.push_back(twin_tables[0].get(naf_entries[offset], naf_entries[offset + 1]));
896 }
897 if (has_singleton) {
898 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
899 }
900
901 element result = round_accumulator[0];
902 element::chain_add_accumulator accumulator;
903 if (round_accumulator.size() == 1) {
904 return result;
905 }
906
907 if (round_accumulator.size() == 2) {
908 return result + round_accumulator[1];
909 }
910
911 // For 3 or more elements, use chain addition
912 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
913 for (size_t j = 2; j < round_accumulator.size(); ++j) {
914 accumulator = element::chain_add(round_accumulator[j], accumulator);
915 }
916
917 return element::chain_add_end(accumulator);
918 }
919
927
928 size_t num_sixes = 0;
929 size_t num_fives;
934 };
935
937
939 const std::vector<Fr>& scalars,
940 const size_t max_num_bits);
941};
942
943// For testing purposes only
945 public:
946 template <typename C, typename Fq, typename Fr, typename G, size_t wnaf_size>
947 static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64,
948 uint64_t stagger,
949 bool is_negative,
950 bool wnaf_skew)
951 {
952 return element<C, Fq, Fr, G>::template get_staggered_wnaf_fragment_value<wnaf_size>(
953 fragment_u64, stagger, is_negative, wnaf_skew);
954 }
955
956 template <typename C, typename Fq, typename Fr, typename G>
958 {
959 return elem1.checked_unconditional_add_sub(elem2);
960 }
961
962 // Overload for goblin_element
963 template <typename C, typename Fq, typename Fr, typename G>
969};
970
971template <typename C, typename Fq, typename Fr, typename G>
972inline std::ostream& operator<<(std::ostream& os, element<C, Fq, Fr, G> const& v)
973{
974 return os << "{ " << v.x() << " , " << v.y() << " }";
975}
976} // namespace bb::stdlib::element_default
977
978namespace bb::stdlib {
979template <typename T>
981
982template <typename Builder, class Fq, class Fr, class NativeGroup>
986
992template <typename C, typename Fq, typename Fr, typename G>
996} // namespace bb::stdlib
998#include "biggroup_goblin.hpp"
999#include "biggroup_impl.hpp"
1000#include "biggroup_nafs.hpp"
1001#include "biggroup_secp256k1.hpp"
1002#include "biggroup_tables.hpp"
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:92
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:77
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Implements boolean logic in-circuit.
Definition bool.hpp:59
bool get_value() const
Definition bool.hpp:124
bool is_constant() const
Definition bool.hpp:126
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:153
bool_t normalize() const
A bool_t element is normalized if witness_inverted == false. For a given *this, output its normalized...
Definition bool.cpp:516
void set_free_witness_tag()
Definition bool.hpp:155
static bool_t conditional_assign(const bool_t< Builder > &predicate, const bool_t &lhs, const bool_t &rhs)
Conditionally assign lhs or rhs based on predicate, always returns normalized result.
Definition bool.cpp:465
void unset_free_witness_tag()
Definition bool.hpp:156
Builder * get_context() const
Definition bool.hpp:151
void clear_round_provenance() const
Definition bool.hpp:157
void assert_equal(const bool_t &rhs, std::string const &msg="bool_t::assert_equal") const
Implements copy constraint for bool_t elements.
Definition bool.cpp:421
OriginTag get_origin_tag() const
Definition bool.hpp:154
static auto checked_unconditional_add_sub(const element_goblin::goblin_element< C, Fq, Fr, G > &elem1, const element_goblin::goblin_element< C, Fq, Fr, G > &elem2)
Definition biggroup.hpp:964
static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64, uint64_t stagger, bool is_negative, bool wnaf_skew)
Definition biggroup.hpp:947
static auto checked_unconditional_add_sub(const element< C, Fq, Fr, G > &elem1, const element< C, Fq, Fr, G > &elem2)
Definition biggroup.hpp:957
element checked_unconditional_subtract(const element &other) const
element & operator=(const element &other)
void assert_coordinates_in_field(const std::string &msg="biggroup::assert_coordinates_in_field") const
Definition biggroup.hpp:294
element operator-=(const element &other)
Definition biggroup.hpp:215
NativeGroup::affine_element get_value() const
Definition biggroup.hpp:336
static std::pair< Fr, secp256k1_wnaf > compute_secp256k1_single_wnaf(Builder *builder, const secp256k1::fr &scalar, size_t stagger, bool is_negative, const bool range_constrain_wnaf=true, bool is_lo=false)
Compute the wNAF representation (in circuit) of a scalar for secp256k1.
Builder * get_context(const element &other) const
Definition biggroup.hpp:386
element multiple_montgomery_ladder(const std::vector< chain_add_accumulator > &to_add) const
Perform repeated iterations of the montgomery ladder algorithm.
void validate_on_curve(std::string const &msg="biggroup::validate_on_curve") const
Check that the point is on the curve.
Definition biggroup.hpp:114
static element one(Builder *ctx)
Creates a constant group generator.
Definition biggroup.hpp:176
static chain_add_accumulator chain_add_start(const element &p1, const element &p2)
Begin a chain of additions.
static element from_witness(Builder *ctx, const typename NativeGroup::affine_element &input)
Create a biggroup witness from a native group element, allocating new witnesses as necessary.
Definition biggroup.hpp:89
void fix_witness()
Fix a witness. The value of the witness is constrained with a selector.
Definition biggroup.hpp:163
static std::pair< four_bit_table_plookup, four_bit_table_plookup > create_endo_pair_four_bit_table_plookup(const element &input)
Create a endo pair four bit table for the given group element.
static element chain_add_end(const chain_add_accumulator &accumulator)
End an addition chain and compute the final y-coordinate.
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, const Fr &masking_scalar=Fr(1))
Generic batch multiplication that works for all elliptic curve types.
void unset_free_witness_tag()
Unset the free witness flag for the element's tags.
Definition biggroup.hpp:427
static element point_at_infinity(Builder *ctx)
Definition biggroup.hpp:185
static NativeGroup::affine_element compute_table_offset_generator()
Compute an offset generator for use in biggroup tables.
static std::vector< field_ct > convert_wnaf_values_to_witnesses(Builder *builder, const uint64_t *wnaf_values, bool is_negative, size_t rounds, const bool range_constrain_wnaf=true)
Convert wNAF values to witness values.
void set_origin_tag(OriginTag tag) const
Definition biggroup.hpp:405
void incomplete_assert_equal(const element &other, const std::string msg="biggroup::incomplete_assert_equal") const
Asserts that two group elements are equal (i.e., x, y coordinates and infinity flag are all equal).
Definition biggroup.hpp:269
void set_point_at_infinity(const bool_ct &is_infinity, const bool &add_to_used_witnesses=false)
Definition biggroup.hpp:396
stdlib::witness_t< Builder > witness_ct
Definition biggroup.hpp:31
static std::pair< quad_lookup_table, quad_lookup_table > create_endo_pair_quad_lookup_table(const std::array< element, 4 > &inputs)
Definition biggroup.hpp:661
element(const typename NativeGroup::affine_element &input)
static element process_strauss_msm_rounds(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits)
static Fr reconstruct_bigfield_from_wnaf(Builder *builder, const std::vector< field_ct > &wnaf, const bool_ct &positive_skew, const bool_ct &negative_skew, const field_ct &stagger_fragment, const size_t stagger, const size_t rounds)
Reconstruct a scalar from its wNAF representation in circuit.
static std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > create_group_element_rom_tables(const std::array< element, num_elements > &rom_data, std::array< uint256_t, Fq::NUM_LIMBS *2 > &limb_max)
void convert_constant_to_fixed_witness(Builder *builder)
Creates fixed witnesses from a constant element.
Definition biggroup.hpp:152
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:210
static element read_group_element_rom_tables(const std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > &tables, const field_ct &index, const std::array< uint256_t, Fq::NUM_LIMBS *2 > &limb_max)
static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr &scalar, const bool range_constrain_wnaf=true)
Compute endomorphism for a secp256k1 scalar, and then compute the wNAF representation of both halves.
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)
Compute Non-Adjacent Form (NAF) representation of a scalar.
static std::pair< uint64_t, bool > get_staggered_wnaf_fragment_value(const uint64_t fragment_u64, const uint64_t stagger, bool is_negative, bool wnaf_skew)
Compute the stagger-related part of wNAF and the final skew.
uint32_t set_public() const
Set the witness indices for the x and y coordinates to public.
Definition biggroup.hpp:56
static std::pair< std::vector< element >, std::vector< Fr > > mask_points(const std::vector< element > &_points, const std::vector< Fr > &_scalars, const Fr &masking_scalar)
Mask points for batch multiplication to handle edge cases.
element operator*(const Fr &scalar) const
element conditional_negate(const bool_ct &predicate) const
Definition biggroup.hpp:223
static std::pair< std::vector< element >, std::vector< Fr > > handle_points_at_infinity(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Handle points at infinity in batch operations, replaces (∞, scalar) pairs by (G, 0)
void set_free_witness_tag()
Set the free witness flag for the element's tags.
Definition biggroup.hpp:437
element conditional_select(const element &other, const bool_ct &predicate) const
Selects this if predicate is false, other if predicate is true.
Definition biggroup.hpp:237
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:70
std::array< element, 2 > checked_unconditional_add_sub(const element &other) const
Compute both add and subtract (a + b, a - b) results simultaneously.
static constexpr size_t PUBLIC_INPUTS_SIZE
Definition biggroup.hpp:36
element operator+(const element &other) const
static chain_add_accumulator chain_add(const element &p1, const chain_add_accumulator &accumulator)
Evaluate a chain addition using incomplete addition formulae.
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.
std::array< goblin_element, 2 > checked_unconditional_add_sub(const goblin_element &other) const
#define vinfo(...)
Definition log.hpp:80
AluTraceBuilder builder
Definition alu.test.cpp:124
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
AvmProvingInputs inputs
std::ostream & operator<<(std::ostream &os, element< C, Fq, Fr, G > const &v)
Definition biggroup.hpp:972
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...
Definition biggroup.hpp:995
void mark_witness_as_used(const field_t< Builder > &field)
Mark a field_t witness as used (for UltraBuilder only).
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()
element get(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:863
std::vector< lookup_table_plookup< 6 > > six_tables
Definition biggroup.hpp:920
std::vector< lookup_table_plookup< 5 > > five_tables
Definition biggroup.hpp:921
element::chain_add_accumulator get_chain_add_accumulator(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:807
batch_lookup_table_plookup(const std::vector< element > &points)
Definition biggroup.hpp:684
chain_add_accumulator(chain_add_accumulator &&other) noexcept=default
chain_add_accumulator(const chain_add_accumulator &other)=default
chain_add_accumulator & operator=(const chain_add_accumulator &other)=default
chain_add_accumulator & operator=(chain_add_accumulator &&other) noexcept=default
Eight-bit fixed base table for scalar multiplication.
Definition biggroup.hpp:601
eight_bit_fixed_base_table & operator=(eight_bit_fixed_base_table &&other) noexcept=default
eight_bit_fixed_base_table & operator=(const eight_bit_fixed_base_table &other)=default
eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
Definition biggroup.hpp:603
eight_bit_fixed_base_table(eight_bit_fixed_base_table &&other) noexcept=default
eight_bit_fixed_base_table(const eight_bit_fixed_base_table &other)=default
Four-bit variable-base table for scalar multiplication.
Definition biggroup.hpp:576
std::array< uint256_t, Fq::NUM_LIMBS *2 > limb_max
Definition biggroup.hpp:592
four_bit_table_plookup & operator=(four_bit_table_plookup &&other) noexcept=default
four_bit_table_plookup(const four_bit_table_plookup &other)=default
four_bit_table_plookup(four_bit_table_plookup &&other) noexcept=default
std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > coordinates
Definition biggroup.hpp:591
four_bit_table_plookup & operator=(const four_bit_table_plookup &other)=default
Generic lookup table that uses ROM tables internally to access group elements.
Definition biggroup.hpp:629
std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > coordinates
Definition biggroup.hpp:647
lookup_table_plookup(const lookup_table_plookup &other)=default
lookup_table_plookup(lookup_table_plookup &&other) noexcept=default
lookup_table_plookup & operator=(lookup_table_plookup &&other) noexcept=default
lookup_table_plookup & operator=(const lookup_table_plookup &other)=default
element get(const std::array< bool_ct, length > &bits) const
std::array< uint256_t, Fq::NUM_LIMBS *2 > limb_max
Definition biggroup.hpp:648
curve::BN254::BaseField Fq