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: Complete, auditors: [Suyash], commit: 553c5eb82901955c638b943065acd3e47fc918c0}
3// external_1: { status: not started, auditors: [], commit: }
4// external_2: { status: not started, auditors: [], commit: }
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
40 // Construct a biggroup element from its coordinates
41 // The infinity flag is automatically set based on whether both coordinates are zero.
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
45 element(const element& other);
46 element(element&& other) noexcept;
47
48 ~element() = default;
49
57 uint32_t set_public() const
58 {
59 auto standard = get_standard_form(); // if point is at infinity, ensure coordinates are (0,0).
60 const uint32_t start_idx = standard._x.set_public();
61 standard._y.set_public();
62
63 return start_idx;
64 }
65
74 static element from_witness(Builder* ctx, const typename NativeGroup::affine_element& input)
75 {
76 // By convention we set the coordinates of the point at infinity to (0,0).
77 Fq x;
78 Fq y;
79 if (input.is_point_at_infinity()) {
80 x = Fq::from_witness(ctx, typename NativeGroup::Fq(0));
81 y = Fq::from_witness(ctx, typename NativeGroup::Fq(0));
82 } else {
83 x = Fq::from_witness(ctx, input.x);
84 y = Fq::from_witness(ctx, input.y);
85 }
86
87 // Create _is_infinity as a witness with the actual infinity status.
88 // Security: Since assert_on_curve is true, validate_on_curve() enforces that if infinity=true, then x = y = 0.
89 bool_ct is_infinity = bool_ct(witness_ct(ctx, input.is_point_at_infinity()));
90
91 // Construct the biggroup element with private constructor to avoid redundant on-curve check
92 element out = element(x, y, is_infinity, /*assert_on_curve=*/true);
93
94 // Mark the element as coming out of nowhere
96 return out;
97 }
98
102 Fq validate_on_curve(std::string const& msg = "biggroup::validate_on_curve", bool assert_is_on_curve = true) const
103 {
104 // Return early for constant inputs
105 if (this->is_constant()) {
106 BB_ASSERT(this->get_value().on_curve(), "biggroup::validate_on_curve: constant point not on curve");
107 return typename Fq::native(this->get_value().on_curve() ? 0 : 1);
108 }
109
110 // If this is a point at infinity, it must have (x, y) = (0, 0)
111 _x.assert_zero_if(is_point_at_infinity(), "biggroup::validate_on_curve: infinity point must have x = 0");
112 _y.assert_zero_if(is_point_at_infinity(), "biggroup::validate_on_curve: infinity point must have y = 0");
113
114 bool has_circuit_failed = get_context()->failed();
115 Fq result;
116
117 Fq b(get_context(), uint256_t(NativeGroup::curve_b));
118 Fq adjusted_b = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), b);
119 // If `assert_is_on_curve` is true, we validate y^2 = x^3 + b by setting "fix_remainder_zero = true" when
120 // calling mult_madd. Otherwise, we return the difference x^3 + ax + b - y^2
121 if constexpr (!NativeGroup::has_a) {
122 result = Fq::mult_madd({ _x.sqr(), _y }, { _x, -_y }, { adjusted_b }, assert_is_on_curve);
123 ;
124 } else {
125 Fq a(get_context(), uint256_t(NativeGroup::curve_a));
126 Fq adjusted_a = Fq::conditional_assign(is_point_at_infinity(), Fq::zero(), a);
127 result = Fq::mult_madd({ _x.sqr(), _x, _y }, { _x, adjusted_a, -_y }, { adjusted_b }, assert_is_on_curve);
128 }
129
130 if ((!has_circuit_failed) && (get_context()->failed())) {
131 vinfo("Original bigfield error generated by biggroup::validate_on_curve: ", get_context()->err());
132 get_context()->failure(msg);
133 }
134
135 return result;
136 }
137
138 [[nodiscard]] bool is_constant() const
139 {
140 const bool x_is_const = _x.is_constant();
141 const bool y_is_const = _y.is_constant();
142 BB_ASSERT_EQ(x_is_const, y_is_const, "biggroup: x and y coordinate constant status mismatch");
143 return x_is_const;
144 }
145
150 {
151 this->_x.convert_constant_to_fixed_witness(builder);
152 this->_y.convert_constant_to_fixed_witness(builder);
153 // Origin tags should be unset after fixing the witness
155 }
156
161 {
162 // Origin tags should be updated within
163 this->_x.fix_witness();
164 this->_y.fix_witness();
166
167 // This is now effectively a constant
169 }
170
174 static element one(Builder* ctx)
175 {
176 uint256_t x = uint256_t(NativeGroup::one.x);
177 uint256_t y = uint256_t(NativeGroup::one.y);
178 Fq x_fq(ctx, x);
179 Fq y_fq(ctx, y);
180 // Use private 4-arg constructor with explicit is_infinity=false (the generator is never infinity).
181 // This avoids the expensive bigfield equality checks in the 2-arg constructor's infinity auto-detection.
182 return element(x_fq, y_fq, bool_ct(ctx, false), /*assert_on_curve=*/false);
183 }
184
190 {
191 Fq x_fq(ctx, uint256_t(0));
192 Fq y_fq(ctx, uint256_t(0));
193 return element(x_fq, y_fq, /*is_infinity=*/bool_ct(ctx, true), /*assert_on_curve=*/false);
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
259 static element conditional_assign(const bool_t<Builder>& predicate, const element& lhs, const element& rhs)
260 {
261 return rhs.conditional_select(lhs, predicate);
262 }
263
275 const std::string msg = "biggroup::incomplete_assert_equal") const
276 {
277 is_point_at_infinity().assert_equal(other.is_point_at_infinity(), msg + " (infinity flag)");
278 _x.assert_equal(other._x, msg + " (x coordinate)");
279 _y.assert_equal(other._y, msg + " (y coordinate)");
280 }
281
283 {
284 element result(*this);
285 result._x.reduce_mod_target_modulus();
286 result._y.reduce_mod_target_modulus();
287 return result;
288 }
289 element scalar_mul(const Fr& scalar, const size_t max_num_bits = 0) const;
290
292 {
293 element result(*this);
294 result._x.self_reduce();
295 result._y.self_reduce();
296 return result;
297 }
298
299 void assert_coordinates_in_field(const std::string& msg = "biggroup::assert_coordinates_in_field") const
300 {
301 _x.assert_is_in_field(msg + " (x coordinate)");
302 _y.assert_is_in_field(msg + " (y coordinate)");
303 }
304
305 element dbl() const;
306
307 // we use this data structure to add together a sequence of points.
308 // By tracking the previous values of x_1, y_1, \lambda, we can avoid
309 // computing the output y-coordinate of intermediate additions
316 bool is_full_element = false;
317
319 explicit chain_add_accumulator(const element& input)
320 : x3_prev(input._x)
321 , y3_prev(input._y)
322 , is_full_element(true)
323 {}
325 chain_add_accumulator(chain_add_accumulator&& other) noexcept = default;
329 };
330
342 static chain_add_accumulator chain_add_start(const element& p1, const element& p2);
343 static chain_add_accumulator chain_add(const element& p1, const chain_add_accumulator& accumulator);
344 static element chain_add_end(const chain_add_accumulator& accumulator);
346
347 typename NativeGroup::affine_element get_value() const
348 {
349 uint512_t x_val = _x.get_value() % Fq::modulus_u512;
350 uint512_t y_val = _y.get_value() % Fq::modulus_u512;
351 auto result = typename NativeGroup::affine_element(x_val.lo, y_val.lo);
353 result.self_set_infinity();
354 }
355 return result;
356 }
357
358 static element batch_mul(const std::vector<element>& points,
359 const std::vector<Fr>& scalars,
360 const size_t max_num_bits = 0,
361 const bool with_edgecases = false);
362
363 template <typename X = NativeGroup, typename = typename std::enable_if_t<std::is_same<X, secp256k1::g1>::value>>
364 static element secp256k1_ecdsa_mul(const element& pubkey, const Fr& u1, const Fr& u2);
365
370 static std::vector<bool_ct> compute_naf(const Fr& scalar, const size_t max_num_bits = 0);
371
372 // Internal struct to represent wNAF for a secp256k1 scalar
380
381 // Internal struct to represent a pair of secp256k1 wNAFs
386
391 template <size_t wnaf_size, size_t staggered_lo_offset = 0, size_t staggered_hi_offset = 0>
392 static secp256k1_wnaf_pair compute_secp256k1_endo_wnaf(const Fr& scalar, const bool range_constrain_wnaf = true);
393
394 Builder* get_context() const { return validate_context<Builder>(_x.get_context(), _y.get_context()); }
395
396 Builder* get_context(const element& other) const
397 {
398 return validate_context<Builder>(get_context(), other.get_context());
399 }
400
401 // Coordinate accessors (non-owning, const reference)
402 const Fq& x() const { return _x; }
403 const Fq& y() const { return _y; }
404
407
409 {
410 _x.set_origin_tag(tag);
411 _y.set_origin_tag(tag);
413 }
414
416 {
417 _x.clear_round_provenance();
418 _y.clear_round_provenance();
420 }
421
423 {
424 return OriginTag(_x.get_origin_tag(), _y.get_origin_tag(), _is_infinity.get_origin_tag());
425 }
426
431 {
432 _x.unset_free_witness_tag();
433 _y.unset_free_witness_tag();
435 }
436
441 {
442 _x.set_free_witness_tag();
443 _y.set_free_witness_tag();
445 }
446
447 // For testing purposes only
449
450 private:
451 // Private constructor with explicit infinity flag control.
452 // Use public constructors or factory methods instead - they auto-detect infinity from coordinates.
453 element(const Fq& x, const Fq& y, const bool_ct& is_infinity, bool assert_on_curve);
454
458
459 // Internal implementations - may produce non-canonical infinity representation (efficient for chaining)
460 element add_internal(const element& other) const;
461 element subtract_internal(const element& other) const;
462 element dbl_internal() const;
464 const std::vector<Fr>& scalars,
465 const size_t max_num_bits = 0,
466 const bool with_edgecases = false);
467
473
482 static std::pair<std::vector<element>, std::vector<Fr>> mask_points(const std::vector<element>& _points,
483 const std::vector<Fr>& _scalars);
484
494 const std::vector<element>& _points, const std::vector<Fr>& _scalars);
495
510 template <size_t num_bits, size_t wnaf_size, size_t lo_stagger, size_t hi_stagger>
512 const secp256k1::fr& scalar,
513 size_t stagger,
514 bool is_negative,
515 const bool range_constrain_wnaf = true,
516 bool is_lo = false);
517
527 template <size_t wnaf_size>
528 static std::pair<uint64_t, bool> get_staggered_wnaf_fragment_value(const uint64_t fragment_u64,
529 const uint64_t stagger,
530 bool is_negative,
531 bool wnaf_skew);
532
546 template <size_t wnaf_size>
548 const uint64_t* wnaf_values,
549 bool is_negative,
550 size_t rounds,
551 const bool range_constrain_wnaf = true);
552
564 template <size_t wnaf_size>
566 const std::vector<field_ct>& wnaf,
567 const bool_ct& positive_skew,
568 const bool_ct& negative_skew,
569 const field_ct& stagger_fragment,
570 const size_t stagger,
571 const size_t rounds);
572
573 template <size_t num_elements>
576
577 template <size_t num_elements>
578 static element read_group_element_rom_tables(const std::array<twin_rom_table<Builder>, Fq::NUM_LIMBS + 1>& tables,
579 const field_ct& index,
581
582 static std::pair<element, element> compute_offset_generators(const size_t num_rounds);
583 static typename NativeGroup::affine_element compute_table_offset_generator();
584
592 four_bit_table_plookup(const element& input);
593
599
600 element operator[](const field_ct& index) const;
601 element operator[](const size_t idx) const { return element_table[idx]; }
603
604 // Each coordinate is an Fq element, which has 4 binary basis limbs and 1 prime basis limb
606 std::array<uint256_t, Fq::NUM_LIMBS * 2> limb_max; // tracks the maximum size of each binary basis limb
607 };
608
617 eight_bit_fixed_base_table(const CurveType input_curve_type, bool use_endo)
618 : curve_type(input_curve_type)
619 , use_endomorphism(use_endo)
620 {}
621
627
628 element operator[](const field_ct& index) const;
629
630 element operator[](const size_t index) const;
631
634 };
635
637 const element& input);
638
643 template <size_t length> struct lookup_table_plookup {
644 static constexpr size_t table_size = (1ULL << (length));
649 lookup_table_plookup(lookup_table_plookup&& other) noexcept = default;
652
653 element get(const std::array<bool_ct, length>& bits) const;
654
655 element operator[](const size_t idx) const { return element_table[idx]; }
656
658
659 // Each coordinate is an Fq element, which has 4 binary basis limbs and 1 prime basis limb
660 // ROM tables: (idx, x0, x1), (idx, x2, x3), (idx, y0, y1), (idx, y2, y3), (idx, xp, yp)
663 };
664
666
668
670
677 {
678 quad_lookup_table base_table(inputs);
679 quad_lookup_table endo_table;
681 Fq beta(bb::fr(beta_val.slice(0, 136)), bb::fr(beta_val.slice(136, 256)), false);
682 for (size_t i = 0; i < 8; ++i) {
683 endo_table.element_table[i + 8].x = base_table[7 - i].x * beta;
684 endo_table.element_table[i + 8].y = base_table[7 - i].y;
685
686 endo_table.element_table[7 - i] = (-endo_table.element_table[i + 8]);
687 }
688
689 endo_table.coordinates = create_group_element_rom_tables<16>(endo_table.element_table, endo_table.limb_max);
690 return std::make_pair<quad_lookup_table, quad_lookup_table>(base_table, endo_table);
691 }
692
699 : num_points(points.size())
700 , num_fives(num_points / 5)
701 {
702 // size-6 table is expensive and only benefits us if creating them reduces the number of total tables
703 if (num_points == 1) {
704 num_fives = 0;
705 num_sixes = 0;
706 } else if (num_fives * 5 == (num_points - 1)) {
707 // last 6 points to be added as one 6-table
708 num_fives -= 1;
709 num_sixes = 1;
710 } else if (num_fives * 5 == (num_points - 2) && num_fives >= 2) {
711 // last 12 points to be added as two 6-tables
712 num_fives -= 2;
713 num_sixes = 2;
714 } else if (num_fives * 5 == (num_points - 3) && num_fives >= 3) {
715 // last 18 points to be added as three 6-tables
716 num_fives -= 3;
717 num_sixes = 3;
718 }
719
720 // Calculate remaining points after allocating fives and sixes tables
721 size_t remaining_points = num_points - (num_fives * 5 + num_sixes * 6);
722
723 // Allocate one quad table if required (and update remaining points)
724 has_quad = (remaining_points >= 4) && (num_points >= 4);
725 if (has_quad) {
726 remaining_points -= 4;
727 }
728
729 // Allocate one triple table if required (and update remaining points)
730 has_triple = (remaining_points >= 3) && (num_points >= 3);
731 if (has_triple) {
732 remaining_points -= 3;
733 }
734
735 // Allocate one twin table if required (and update remaining points)
736 has_twin = (remaining_points >= 2) && (num_points >= 2);
737 if (has_twin) {
738 remaining_points -= 2;
739 }
740
741 // If there is anything remaining, allocate a singleton
742 has_singleton = (remaining_points != 0) && (num_points >= 1);
743
744 // Sanity check
746 num_sixes * 6 + num_fives * 5 + static_cast<size_t>(has_quad) * 4 +
747 static_cast<size_t>(has_triple) * 3 + static_cast<size_t>(has_twin) * 2 +
748 static_cast<size_t>(has_singleton) * 1,
749 "point allocation mismatch");
750
751 size_t offset = 0;
752 for (size_t i = 0; i < num_sixes; ++i) {
754 points[offset + (6 * i)],
755 points[offset + (6 * i) + 1],
756 points[offset + (6 * i) + 2],
757 points[offset + (6 * i) + 3],
758 points[offset + (6 * i) + 4],
759 points[offset + (6 * i) + 5],
760 }));
761 }
762 offset += 6 * num_sixes;
763 for (size_t i = 0; i < num_fives; ++i) {
765 points[offset + (5 * i)],
766 points[offset + (5 * i) + 1],
767 points[offset + (5 * i) + 2],
768 points[offset + (5 * i) + 3],
769 points[offset + (5 * i) + 4],
770 }));
771 }
772 offset += 5 * num_fives;
773
774 if (has_quad) {
775 quad_tables.push_back(
776 quad_lookup_table({ points[offset], points[offset + 1], points[offset + 2], points[offset + 3] }));
777 }
778 if (has_triple) {
779 triple_tables.push_back(
780 triple_lookup_table({ points[offset], points[offset + 1], points[offset + 2] }));
781 }
782 if (has_twin) {
783 twin_tables.push_back(twin_lookup_table({ points[offset], points[offset + 1] }));
784 }
785 if (has_singleton) {
786 singletons.push_back(points[points.size() - 1]);
787 }
788 }
789
791 {
792 std::vector<element> add_accumulator;
793 for (size_t i = 0; i < num_sixes; ++i) {
794 add_accumulator.push_back(six_tables[i][0]);
795 }
796 for (size_t i = 0; i < num_fives; ++i) {
797 add_accumulator.push_back(five_tables[i][0]);
798 }
799 if (has_quad) {
800 add_accumulator.push_back(quad_tables[0][0]);
801 }
802 if (has_twin) {
803 add_accumulator.push_back(twin_tables[0][0]);
804 }
805 if (has_triple) {
806 add_accumulator.push_back(triple_tables[0][0]);
807 }
808 if (has_singleton) {
809 add_accumulator.push_back(singletons[0]);
810 }
811 if (add_accumulator.size() >= 2) {
812 chain_add_accumulator output = element::chain_add_start(add_accumulator[0], add_accumulator[1]);
813 for (size_t i = 2; i < add_accumulator.size(); ++i) {
814 output = element::chain_add(add_accumulator[i], output);
815 }
816 return output;
817 }
818 return chain_add_accumulator(add_accumulator[0]);
819 }
820
821 element::chain_add_accumulator get_chain_add_accumulator(std::vector<bool_ct>& naf_entries) const
822 {
823 std::vector<element> round_accumulator;
824 for (size_t j = 0; j < num_sixes; ++j) {
825 round_accumulator.push_back(six_tables[j].get({ naf_entries[6 * j],
826 naf_entries[(6 * j) + 1],
827 naf_entries[(6 * j) + 2],
828 naf_entries[(6 * j) + 3],
829 naf_entries[(6 * j) + 4],
830 naf_entries[(6 * j) + 5] }));
831 }
832 size_t offset = num_sixes * 6;
833 for (size_t j = 0; j < num_fives; ++j) {
834 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + (j * 5)],
835 naf_entries[offset + (j * 5) + 1],
836 naf_entries[offset + (j * 5) + 2],
837 naf_entries[offset + (j * 5) + 3],
838 naf_entries[offset + (j * 5) + 4] }));
839 }
840 offset += num_fives * 5;
841 if (has_quad) {
842 round_accumulator.push_back(quad_tables[0].get({ naf_entries[offset],
843 naf_entries[offset + 1],
844 naf_entries[offset + 2],
845 naf_entries[offset + 3] }));
846 }
847
848 if (has_triple) {
849 round_accumulator.push_back(
850 triple_tables[0].get({ naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2] }));
851 }
852 if (has_twin) {
853 round_accumulator.push_back(twin_tables[0].get({ naf_entries[offset], naf_entries[offset + 1] }));
854 }
855 if (has_singleton) {
856 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
857 }
858
859 if (round_accumulator.size() == 1) {
860 return element::chain_add_accumulator(round_accumulator[0]);
861 }
862
863 if (round_accumulator.size() == 2) {
864 return element::chain_add_start(round_accumulator[0], round_accumulator[1]);
865 }
866
867 // Use chain add for at least 3 elements
868 element::chain_add_accumulator accumulator =
869 element::chain_add_start(round_accumulator[0], round_accumulator[1]);
870 for (size_t j = 2; j < round_accumulator.size(); ++j) {
871 accumulator = element::chain_add(round_accumulator[j], accumulator);
872 }
873
874 return (accumulator);
875 }
876
877 element get(std::vector<bool_ct>& naf_entries) const
878 {
879 std::vector<element> round_accumulator;
880 for (size_t j = 0; j < num_sixes; ++j) {
881 round_accumulator.push_back(six_tables[j].get({ naf_entries[(6 * j)],
882 naf_entries[(6 * j) + 1],
883 naf_entries[(6 * j) + 2],
884 naf_entries[(6 * j) + 3],
885 naf_entries[(6 * j) + 4],
886 naf_entries[(6 * j) + 5] }));
887 }
888 size_t offset = num_sixes * 6;
889
890 for (size_t j = 0; j < num_fives; ++j) {
891 round_accumulator.push_back(five_tables[j].get({ naf_entries[offset + (5 * j)],
892 naf_entries[offset + (5 * j) + 1],
893 naf_entries[offset + (5 * j) + 2],
894 naf_entries[offset + (5 * j) + 3],
895 naf_entries[offset + (5 * j) + 4] }));
896 }
897
898 offset += num_fives * 5;
899
900 if (has_quad) {
901 round_accumulator.push_back(quad_tables[0].get(
902 naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2], naf_entries[offset + 3]));
903 }
904 if (has_triple) {
905 round_accumulator.push_back(
906 triple_tables[0].get(naf_entries[offset], naf_entries[offset + 1], naf_entries[offset + 2]));
907 }
908 if (has_twin) {
909 round_accumulator.push_back(twin_tables[0].get(naf_entries[offset], naf_entries[offset + 1]));
910 }
911 if (has_singleton) {
912 round_accumulator.push_back(singletons[0].conditional_negate(naf_entries[num_points - 1]));
913 }
914
915 element result = round_accumulator[0];
916 element::chain_add_accumulator accumulator;
917 if (round_accumulator.size() == 1) {
918 return result;
919 }
920
921 if (round_accumulator.size() == 2) {
922 return result + round_accumulator[1];
923 }
924
925 // For 3 or more elements, use chain addition
926 accumulator = element::chain_add_start(round_accumulator[0], round_accumulator[1]);
927 for (size_t j = 2; j < round_accumulator.size(); ++j) {
928 accumulator = element::chain_add(round_accumulator[j], accumulator);
929 }
930
931 return element::chain_add_end(accumulator);
932 }
933
941
942 size_t num_sixes = 0;
943 size_t num_fives;
948 };
949
951
953 const std::vector<Fr>& scalars,
954 const size_t max_num_bits);
955};
956
957// For testing purposes only
959 public:
960 template <typename C, typename Fq, typename Fr, typename G, size_t wnaf_size>
961 static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64,
962 uint64_t stagger,
963 bool is_negative,
964 bool wnaf_skew)
965 {
966 return element<C, Fq, Fr, G>::template get_staggered_wnaf_fragment_value<wnaf_size>(
967 fragment_u64, stagger, is_negative, wnaf_skew);
968 }
969
970 template <typename C, typename Fq, typename Fr, typename G>
972 {
973 return elem1.checked_unconditional_add_sub(elem2);
974 }
975
976 // Overload for goblin_element
977 template <typename C, typename Fq, typename Fr, typename G>
983
989 template <typename C, typename Fq, typename Fr, typename G>
991 const Fq& y,
992 const stdlib::bool_t<C>& is_infinity,
993 bool assert_on_curve = false)
994 {
995 return element<C, Fq, Fr, G>(x, y, is_infinity, assert_on_curve);
996 }
997};
998
999template <typename C, typename Fq, typename Fr, typename G>
1000inline std::ostream& operator<<(std::ostream& os, element<C, Fq, Fr, G> const& v)
1001{
1002 return os << "{ " << v.x() << " , " << v.y() << " }";
1003}
1004} // namespace bb::stdlib::element_default
1005
1006namespace bb::stdlib {
1007template <typename T>
1009
1010template <typename Builder, class Fq, class Fr, class NativeGroup>
1014
1020template <typename C, typename Fq, typename Fr, typename G>
1024} // namespace bb::stdlib
1026#include "biggroup_goblin.hpp"
1027#include "biggroup_impl.hpp"
1028#include "biggroup_nafs.hpp"
1029#include "biggroup_secp256k1.hpp"
1030#include "biggroup_tables.hpp"
#define BB_ASSERT(expression,...)
Definition assert.hpp:70
#define BB_ASSERT_NEQ(actual, expected,...)
Definition assert.hpp:98
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:83
constexpr uint256_t slice(uint64_t start, uint64_t end) const
Implements boolean logic in-circuit.
Definition bool.hpp:60
bool get_value() const
Definition bool.hpp:125
void fix_witness()
Definition bool.hpp:159
bool is_constant() const
Definition bool.hpp:127
void set_origin_tag(const OriginTag &new_tag) const
Definition bool.hpp:154
void set_free_witness_tag()
Definition bool.hpp:156
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:478
void unset_free_witness_tag()
Definition bool.hpp:157
Builder * get_context() const
Definition bool.hpp:152
void clear_round_provenance() const
Definition bool.hpp:158
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:433
OriginTag get_origin_tag() const
Definition bool.hpp:155
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:978
static auto get_staggered_wnaf_fragment_value(uint64_t fragment_u64, uint64_t stagger, bool is_negative, bool wnaf_skew)
Definition biggroup.hpp:961
static element< C, Fq, Fr, G > create_element_with_explicit_infinity(const Fq &x, const Fq &y, const stdlib::bool_t< C > &is_infinity, bool assert_on_curve=false)
Create an element with explicit infinity flag (for testing only).
Definition biggroup.hpp:990
static auto checked_unconditional_add_sub(const element< C, Fq, Fr, G > &elem1, const element< C, Fq, Fr, G > &elem2)
Definition biggroup.hpp:971
element checked_unconditional_subtract(const element &other) const
element & operator=(const element &other)
element add_internal(const element &other) const
Internal implementation of ECC point addition.
void assert_coordinates_in_field(const std::string &msg="biggroup::assert_coordinates_in_field") const
Definition biggroup.hpp:299
element operator-=(const element &other)
Definition biggroup.hpp:215
stdlib::bool_t< Builder > bool_ct
Definition biggroup.hpp:29
NativeGroup::affine_element get_value() const
Definition biggroup.hpp:347
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.
element(const Fq &x, const Fq &y, const bool_ct &is_infinity, bool assert_on_curve)
Builder * get_context(const element &other) const
Definition biggroup.hpp:396
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)
Creates a constant group generator.
Definition biggroup.hpp:174
static chain_add_accumulator chain_add_start(const element &p1, const element &p2)
Optimized chained addition for non-infinity points.
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:74
void fix_witness()
Fix a witness. The value of the witness is constrained with a selector.
Definition biggroup.hpp:160
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.
Fq validate_on_curve(std::string const &msg="biggroup::validate_on_curve", bool assert_is_on_curve=true) const
Check that the point is on the curve.
Definition biggroup.hpp:102
void unset_free_witness_tag()
Unset the free witness flag for the element's tags.
Definition biggroup.hpp:430
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:408
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:274
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:675
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:149
element dbl_internal() const
Internal implementation of point doubling.
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:57
static element conditional_assign(const bool_t< Builder > &predicate, const element &lhs, const element &rhs)
Definition biggroup.hpp:259
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)
static element constant_infinity(Builder *ctx)
Creates a constant point at infinity with canonical (0, 0) coordinates.
Definition biggroup.hpp:189
void set_free_witness_tag()
Set the free witness flag for the element's tags.
Definition biggroup.hpp:440
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 subtract_internal(const element &other) const
Internal implementation of ECC point subtraction.
element get_standard_form() const
Enforce x and y coordinates of a point to be (0, 0) in the case of point at infinity.
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
Evaluate ECC point addition over *this and other.
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< std::vector< element >, std::vector< Fr > > mask_points(const std::vector< element > &_points, const std::vector< Fr > &_scalars)
Mask points for batch multiplication to handle edge cases.
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 batch_mul_internal(const std::vector< element > &points, const std::vector< Fr > &scalars, const size_t max_num_bits=0, const bool with_edgecases=false)
Internal implementation of generic batch multiplication that works for all elliptic curve types.
static element secp256k1_ecdsa_mul(const element &pubkey, const Fr &u1, const Fr &u2)
element dbl() const
Evaluates a point doubling.
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:94
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:52
AvmProvingInputs inputs
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()
BB_INLINE constexpr field sqr() const noexcept
static constexpr field zero()
element get(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:877
std::vector< lookup_table_plookup< 6 > > six_tables
Definition biggroup.hpp:934
std::vector< lookup_table_plookup< 5 > > five_tables
Definition biggroup.hpp:935
element::chain_add_accumulator get_chain_add_accumulator(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:821
batch_lookup_table_plookup(const std::vector< element > &points)
Definition biggroup.hpp:698
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:615
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:617
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:590
std::array< uint256_t, Fq::NUM_LIMBS *2 > limb_max
Definition biggroup.hpp:606
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:605
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:643
std::array< twin_rom_table< Builder >, Fq::NUM_LIMBS+1 > coordinates
Definition biggroup.hpp:661
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:662
curve::BN254::BaseField Fq