Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
biggroup_impl.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 "../circuit_builders/circuit_builders.hpp"
10#include "../plookup/plookup.hpp"
15
17
18template <typename C, class Fq, class Fr, class G>
20 : x()
21 , y()
22 , _is_infinity()
23{}
24
25template <typename C, class Fq, class Fr, class G>
26element<C, Fq, Fr, G>::element(const typename G::affine_element& input)
27 : x(nullptr, input.x)
28 , y(nullptr, input.y)
29 , _is_infinity(nullptr, input.is_point_at_infinity())
30{}
31
32template <typename C, class Fq, class Fr, class G>
33element<C, Fq, Fr, G>::element(const Fq& x_in, const Fq& y_in)
34 : x(x_in)
35 , y(y_in)
36 , _is_infinity(x.get_context() ? x.get_context() : y.get_context(), false)
37{}
38
39template <typename C, class Fq, class Fr, class G>
40element<C, Fq, Fr, G>::element(const Fq& x_in, const Fq& y_in, const bool_ct& is_infinity)
41 : x(x_in)
42 , y(y_in)
43 , _is_infinity(is_infinity)
44{}
45
46template <typename C, class Fq, class Fr, class G>
48 : x(other.x)
49 , y(other.y)
50 , _is_infinity(other.is_point_at_infinity())
51{}
52
53template <typename C, class Fq, class Fr, class G>
55 : x(other.x)
56 , y(other.y)
57 , _is_infinity(other.is_point_at_infinity())
58{}
59
60template <typename C, class Fq, class Fr, class G>
62{
63 if (&other == this) {
64 return *this;
65 }
66 x = other.x;
67 y = other.y;
68 _is_infinity = other.is_point_at_infinity();
69 return *this;
70}
71
72template <typename C, class Fq, class Fr, class G>
74{
75 if (&other == this) {
76 return *this;
77 }
78 x = other.x;
79 y = other.y;
80 _is_infinity = other.is_point_at_infinity();
81 return *this;
82}
83
84template <typename C, class Fq, class Fr, class G>
86{
87
88 // Adding in `x_coordinates_match` ensures that lambda will always be well-formed
89 // Our curve has the form y^2 = x^3 + b.
90 // If (x_1, y_1), (x_2, y_2) have x_1 == x_2, and the generic formula for lambda has a division by 0.
91 // Then y_1 == y_2 (i.e. we are doubling) or y_2 == y_1 (the sum is infinity).
92 // The cases have a special addition formula. The following booleans allow us to handle these cases uniformly.
93 const bool_ct x_coordinates_match = other.x == x;
94 const bool_ct y_coordinates_match = (y == other.y);
95 const bool_ct infinity_predicate = (x_coordinates_match && !y_coordinates_match);
96 const bool_ct double_predicate = (x_coordinates_match && y_coordinates_match);
97 const bool_ct lhs_infinity = is_point_at_infinity();
98 const bool_ct rhs_infinity = other.is_point_at_infinity();
99 const bool_ct has_infinity_input = lhs_infinity || rhs_infinity;
100
101 // Compute the gradient `lambda`. If we add, `lambda = (y2 - y1)/(x2 - x1)`, else `lambda = 3x1*x1/2y1
102 const Fq add_lambda_numerator = other.y - y;
103 const Fq xx = x * x;
104 const Fq dbl_lambda_numerator = xx + xx + xx;
105 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
106
107 const Fq add_lambda_denominator = other.x - x;
108 const Fq dbl_lambda_denominator = y + y;
109 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
110 // If either inputs are points at infinity, we set lambda_denominator to be 1. This ensures we never trigger a
111 // divide by zero error.
112 // Note: if either inputs are points at infinity we will not use the result of this computation.
113 Fq safe_edgecase_denominator = Fq(1);
114 lambda_denominator =
115 Fq::conditional_assign(has_infinity_input || infinity_predicate, safe_edgecase_denominator, lambda_denominator);
116 const Fq lambda = Fq::div_without_denominator_check({ lambda_numerator }, lambda_denominator);
117
118 const Fq x3 = lambda.sqradd({ -other.x, -x });
119 const Fq y3 = lambda.madd(x - x3, { -y });
120
121 element result(x3, y3);
122 // if lhs infinity, return rhs
123 result.x = Fq::conditional_assign(lhs_infinity, other.x, result.x);
124 result.y = Fq::conditional_assign(lhs_infinity, other.y, result.y);
125 // if rhs infinity, return lhs
126 result.x = Fq::conditional_assign(rhs_infinity, x, result.x);
127 result.y = Fq::conditional_assign(rhs_infinity, y, result.y);
128
129 // is result point at infinity?
130 // yes = infinity_predicate && !lhs_infinity && !rhs_infinity
131 // yes = lhs_infinity && rhs_infinity
132 // n.b. can likely optimize this
133 bool_ct result_is_infinity = (infinity_predicate && !has_infinity_input) || (lhs_infinity && rhs_infinity);
134 result.set_point_at_infinity(result_is_infinity, /* add_to_used_witnesses */ true);
135
136 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
137 return result;
138}
139
147template <typename C, class Fq, class Fr, class G>
149{
150
151 const bool_ct is_infinity = is_point_at_infinity();
152 element result(*this);
153 const Fq zero = Fq::zero();
154 result.x = Fq::conditional_assign(is_infinity, zero, this->x);
155 result.y = Fq::conditional_assign(is_infinity, zero, this->y);
156 return result;
157}
158
159template <typename C, class Fq, class Fr, class G>
161{
162
163 // if x_coordinates match, lambda triggers a divide by zero error.
164 // Adding in `x_coordinates_match` ensures that lambda will always be well-formed
165 const bool_ct x_coordinates_match = other.x == x;
166 const bool_ct y_coordinates_match = (y == other.y);
167 const bool_ct infinity_predicate = (x_coordinates_match && y_coordinates_match);
168 const bool_ct double_predicate = (x_coordinates_match && !y_coordinates_match);
169 const bool_ct lhs_infinity = is_point_at_infinity();
170 const bool_ct rhs_infinity = other.is_point_at_infinity();
171 const bool_ct has_infinity_input = lhs_infinity || rhs_infinity;
172
173 // Compute the gradient `lambda`. If we add, `lambda = (y2 - y1)/(x2 - x1)`, else `lambda = 3x1*x1/2y1
174 const Fq add_lambda_numerator = -other.y - y;
175 const Fq xx = x * x;
176 const Fq dbl_lambda_numerator = xx + xx + xx;
177 const Fq lambda_numerator = Fq::conditional_assign(double_predicate, dbl_lambda_numerator, add_lambda_numerator);
178
179 const Fq add_lambda_denominator = other.x - x;
180 const Fq dbl_lambda_denominator = y + y;
181 Fq lambda_denominator = Fq::conditional_assign(double_predicate, dbl_lambda_denominator, add_lambda_denominator);
182 // If either inputs are points at infinity, we set lambda_denominator to be 1. This ensures we never trigger
183 // a divide by zero error. (if either inputs are points at infinity we will not use the result of this
184 // computation)
185 Fq safe_edgecase_denominator = Fq(1);
186 lambda_denominator =
187 Fq::conditional_assign(has_infinity_input || infinity_predicate, safe_edgecase_denominator, lambda_denominator);
188 const Fq lambda = Fq::div_without_denominator_check({ lambda_numerator }, lambda_denominator);
189
190 const Fq x3 = lambda.sqradd({ -other.x, -x });
191 const Fq y3 = lambda.madd(x - x3, { -y });
192
193 element result(x3, y3);
194 // if lhs infinity, return rhs
195 result.x = Fq::conditional_assign(lhs_infinity, other.x, result.x);
196 result.y = Fq::conditional_assign(lhs_infinity, -other.y, result.y);
197 // if rhs infinity, return lhs
198 result.x = Fq::conditional_assign(rhs_infinity, x, result.x);
199 result.y = Fq::conditional_assign(rhs_infinity, y, result.y);
200
201 // is result point at infinity?
202 // yes = infinity_predicate && !lhs_infinity && !rhs_infinity
203 // yes = lhs_infinity && rhs_infinity
204 // n.b. can likely optimize this
205 bool_ct result_is_infinity = (infinity_predicate && !has_infinity_input) || (lhs_infinity && rhs_infinity);
206
207 result.set_point_at_infinity(result_is_infinity, /* add_to_used_witnesses */ true);
208 result.set_origin_tag(OriginTag(get_origin_tag(), other.get_origin_tag()));
209 return result;
210}
211
212template <typename C, class Fq, class Fr, class G>
214{
215 other.x.assert_is_not_equal(x);
216 const Fq lambda = Fq::div_without_denominator_check({ other.y, -y }, (other.x - x));
217 const Fq x3 = lambda.sqradd({ -other.x, -x });
218 const Fq y3 = lambda.madd(x - x3, { -y });
219 return element(x3, y3);
220}
221
222template <typename C, class Fq, class Fr, class G>
224{
225
226 other.x.assert_is_not_equal(x);
227 const Fq lambda = Fq::div_without_denominator_check({ other.y, y }, (other.x - x));
228 const Fq x_3 = lambda.sqradd({ -other.x, -x });
229 const Fq y_3 = lambda.madd(x_3 - x, { -y });
230
231 return element(x_3, y_3);
232}
233
248// TODO(https://github.com/AztecProtocol/barretenberg/issues/657): This function is untested
249template <typename C, class Fq, class Fr, class G>
251{
252
253 // TODO(https://github.com/AztecProtocol/barretenberg/issues/971): This will fail when the two elements are
254 // the same even in the case of a valid circuit
255 other.x.assert_is_not_equal(x);
256
257 const Fq denominator = other.x - x;
258 const Fq x2x1 = -(other.x + x);
259
260 const Fq lambda1 = Fq::div_without_denominator_check({ other.y, -y }, denominator);
261 const Fq x_3 = lambda1.sqradd({ x2x1 });
262 const Fq y_3 = lambda1.madd(x - x_3, { -y });
263 const Fq lambda2 = Fq::div_without_denominator_check({ -other.y, -y }, denominator);
264 const Fq x_4 = lambda2.sqradd({ x2x1 });
265 const Fq y_4 = lambda2.madd(x - x_4, { -y });
266
267 return { element(x_3, y_3), element(x_4, y_4) };
268}
269
270template <typename C, class Fq, class Fr, class G> element<C, Fq, Fr, G> element<C, Fq, Fr, G>::dbl() const
271{
272
273 Fq two_x = x + x;
274 if constexpr (G::has_a) {
275 Fq a(get_context(), uint256_t(G::curve_a));
276 Fq neg_lambda = Fq::msub_div({ x }, { (two_x + x) }, (y + y), { a }, /*enable_divisor_nz_check*/ false);
277 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
278 Fq y_3 = neg_lambda.madd(x_3 - x, { -y });
279 // TODO(suyash): do we handle the point at infinity case here?
280 return element(x_3, y_3);
281 }
282 // TODO(): handle y = 0 case.
283 Fq neg_lambda = Fq::msub_div({ x }, { (two_x + x) }, (y + y), {}, /*enable_divisor_nz_check*/ false);
284 Fq x_3 = neg_lambda.sqradd({ -(two_x) });
285 Fq y_3 = neg_lambda.madd(x_3 - x, { -y });
286 element result = element(x_3, y_3);
287 result.set_point_at_infinity(is_point_at_infinity());
288 return result;
289}
290
309template <typename C, class Fq, class Fr, class G>
311 const element& p2)
312{
314 output.x1_prev = p1.x;
315 output.y1_prev = p1.y;
316
317 p1.x.assert_is_not_equal(p2.x);
318 const Fq lambda = Fq::div_without_denominator_check({ p2.y, -p1.y }, (p2.x - p1.x));
319
320 const Fq x3 = lambda.sqradd({ -p2.x, -p1.x });
321 output.x3_prev = x3;
322 output.lambda_prev = lambda;
323 return output;
324}
325
326template <typename C, class Fq, class Fr, class G>
328 const chain_add_accumulator& acc)
329{
330 // use `chain_add_start` to start an addition chain (i.e. if acc has a y-coordinate)
331 if (acc.is_element) {
332 return chain_add_start(p1, element(acc.x3_prev, acc.y3_prev));
333 }
334 // validate we can use incomplete addition formulae
335 p1.x.assert_is_not_equal(acc.x3_prev);
336
337 // lambda = (y2 - y1) / (x2 - x1)
338 // but we don't have y2!
339 // however, we do know that y2 = lambda_prev * (x1_prev - x2) - y1_prev
340 // => lambda * (x2 - x1) = lambda_prev * (x1_prev - x2) - y1_prev - y1
341 // => lambda * (x2 - x1) + lambda_prev * (x2 - x1_prev) + y1 + y1_pev = 0
342 // => lambda = lambda_prev * (x1_prev - x2) - y1_prev - y1 / (x2 - x1)
343 // => lambda = - (lambda_prev * (x2 - x1_prev) + y1_prev + y1) / (x2 - x1)
344
354 auto& x2 = acc.x3_prev;
355 const auto lambda =
356 Fq::msub_div({ acc.lambda_prev },
357 { (x2 - acc.x1_prev) },
358 (x2 - p1.x),
359 { acc.y1_prev, p1.y },
360 /*enable_divisor_nz_check*/ false); // divisor is non-zero as x2 != p1.x is enforced
361 const auto x3 = lambda.sqradd({ -x2, -p1.x });
362
364 output.x3_prev = x3;
365 output.x1_prev = p1.x;
366 output.y1_prev = p1.y;
367 output.lambda_prev = lambda;
368
369 return output;
370}
371
375template <typename C, class Fq, class Fr, class G>
377{
378 if (acc.is_element) {
379 return element(acc.x3_prev, acc.y3_prev);
380 }
381 auto& x3 = acc.x3_prev;
382 auto& lambda = acc.lambda_prev;
383
384 Fq y3 = lambda.madd((acc.x1_prev - x3), { -acc.y1_prev });
385 return element(x3, y3);
386}
408// #################################
409// ### SCALAR MULTIPLICATION METHODS
410// #################################
428template <typename C, class Fq, class Fr, class G>
430{
431 other.x.assert_is_not_equal(x);
432 const Fq lambda_1 = Fq::div_without_denominator_check({ other.y - y }, (other.x - x));
433
434 const Fq x_3 = lambda_1.sqradd({ -other.x, -x });
435
436 const Fq minus_lambda_2 = lambda_1 + Fq::div_without_denominator_check({ y + y }, (x_3 - x));
437
438 const Fq x_4 = minus_lambda_2.sqradd({ -x, -x_3 });
439
440 const Fq y_4 = minus_lambda_2.madd(x_4 - x, { -y });
441 return element(x_4, y_4);
442}
443
464template <typename C, class Fq, class Fr, class G>
466{
467 if (to_add.is_element) {
468 throw_or_abort("An accumulator expected");
469 }
470 x.assert_is_not_equal(to_add.x3_prev);
471
472 // lambda = (y2 - y1) / (x2 - x1)
473 // but we don't have y2!
474 // however, we do know that y2 = lambda_prev * (x1_prev - x2) - y1_prev
475 // => lambda * (x2 - x1) = lambda_prev * (x1_prev - x2) - y1_prev - y1
476 // => lambda * (x2 - x1) + lambda_prev * (x2 - x1_prev) + y1 + y1_pev = 0
477 // => lambda = lambda_prev * (x1_prev - x2) - y1_prev - y1 / (x2 - x1)
478 // => lambda = - (lambda_prev * (x2 - x1_prev) + y1_prev + y1) / (x2 - x1)
479
480 auto& x2 = to_add.x3_prev;
481 const auto lambda = Fq::msub_div({ to_add.lambda_prev },
482 { (x2 - to_add.x1_prev) },
483 (x2 - x),
484 { to_add.y1_prev, y },
485 /*enable_divisor_nz_check*/ false); // divisor is non-zero as x2 != x is enforced
486 const auto x3 = lambda.sqradd({ -x2, -x });
487
488 const Fq minus_lambda_2 = lambda + Fq::div_without_denominator_check({ y + y }, (x3 - x));
489
490 const Fq x4 = minus_lambda_2.sqradd({ -x, -x3 });
491
492 const Fq y4 = minus_lambda_2.madd(x4 - x, { -y });
493 return element(x4, y4);
494}
495
510template <typename C, class Fq, class Fr, class G>
512{
513 const Fq two_x = x + x;
514 Fq x_1;
515 Fq minus_lambda_dbl;
516 if constexpr (G::has_a) {
517 Fq a(get_context(), uint256_t(G::curve_a));
518 minus_lambda_dbl = Fq::msub_div({ x }, { (two_x + x) }, (y + y), { a }, /*enable_divisor_nz_check*/ false);
519 x_1 = minus_lambda_dbl.sqradd({ -(two_x) });
520 } else {
521 // TODO(): handle y = 0 case.
522 minus_lambda_dbl = Fq::msub_div({ x }, { (two_x + x) }, (y + y), {}, /*enable_divisor_nz_check*/ false);
523 x_1 = minus_lambda_dbl.sqradd({ -(two_x) });
524 }
525
526 BB_ASSERT_GT(to_add.size(), 0);
527 to_add[0].x.assert_is_not_equal(x_1);
528
529 const Fq x_minus_x_1 = x - x_1;
530
531 const Fq lambda_1 =
532 Fq::msub_div({ minus_lambda_dbl },
533 { x_minus_x_1 },
534 (x_1 - to_add[0].x),
535 { to_add[0].y, y },
536 /*enable_divisor_nz_check*/ false); // divisor is non-zero as x1 != to_add[0].x is enforced
537
538 const Fq x_3 = lambda_1.sqradd({ -to_add[0].x, -x_1 });
539
540 // TODO(): analyse if (x3 - x1) can be zero.
541 const Fq half_minus_lambda_2_minus_lambda_1 =
542 Fq::msub_div({ minus_lambda_dbl },
543 { x_minus_x_1 },
544 (x_3 - x_1),
545 { y },
546 /*enable_divisor_nz_check*/ false); // divisor is non-zero as x3 != x1 is enforced
547
548 const Fq minus_lambda_2_minus_lambda_1 = half_minus_lambda_2_minus_lambda_1 + half_minus_lambda_2_minus_lambda_1;
549 const Fq minus_lambda_2 = minus_lambda_2_minus_lambda_1 + lambda_1;
550
551 const Fq x_4 = minus_lambda_2.sqradd({ -x_1, -x_3 });
552
553 const Fq x_4_sub_x_1 = x_4 - x_1;
554
555 if (to_add.size() == 1) {
556 const Fq y_4 = Fq::dual_madd(minus_lambda_2, x_4_sub_x_1, minus_lambda_dbl, x_minus_x_1, { y });
557 return element(x_4, y_4);
558 }
559 to_add[1].x.assert_is_not_equal(to_add[0].x);
560
561 // TODO(): analyse if (x4 - x1) can be zero.
562 Fq minus_lambda_3 = Fq::msub_div({ minus_lambda_dbl, minus_lambda_2 },
563 { x_minus_x_1, x_4_sub_x_1 },
564 (x_4 - to_add[1].x),
565 { y, -(to_add[1].y) },
566 /*enable_divisor_nz_check*/ false);
567
568 // X5 = L3.L3 - X4 - XB
569 const Fq x_5 = minus_lambda_3.sqradd({ -x_4, -to_add[1].x });
570
571 if (to_add.size() == 2) {
572 // Y5 = L3.(XB - X5) - YB
573 const Fq y_5 = minus_lambda_3.madd(x_5 - to_add[1].x, { -to_add[1].y });
574 return element(x_5, y_5);
575 }
576
577 Fq x_prev = x_5;
578 Fq minus_lambda_prev = minus_lambda_3;
579
580 for (size_t i = 2; i < to_add.size(); ++i) {
581
582 to_add[i].x.assert_is_not_equal(to_add[i - 1].x);
583 // Lambda = Yprev - Yadd[i] / Xprev - Xadd[i]
584 // = -Lprev.(Xprev - Xadd[i-1]) - Yadd[i - 1] - Yadd[i] / Xprev - Xadd[i]
585 const Fq minus_lambda =
586 Fq::msub_div({ minus_lambda_prev },
587 { to_add[i - 1].x - x_prev },
588 (to_add[i].x - x_prev),
589 { to_add[i - 1].y, to_add[i].y },
590 /*enable_divisor_nz_check*/ false); // divisor is non-zero as x_prev != to_add[i].x is enforced
591
592 // X = Lambda * Lambda - Xprev - Xadd[i]
593 const Fq x_out = minus_lambda.sqradd({ -x_prev, -to_add[i].x });
594
595 x_prev = x_out;
596 minus_lambda_prev = minus_lambda;
597 }
598 const Fq y_out = minus_lambda_prev.madd(x_prev - to_add[to_add.size() - 1].x, { -to_add[to_add.size() - 1].y });
599
600 return element(x_prev, y_out);
601}
602
622template <typename C, class Fq, class Fr, class G>
624 const std::vector<chain_add_accumulator>& add) const
625{
626 struct composite_y {
627 std::vector<Fq> mul_left;
628 std::vector<Fq> mul_right;
629 std::vector<Fq> add;
630 bool is_negative = false;
631 };
632
633 Fq previous_x = x;
634 composite_y previous_y{ std::vector<Fq>(), std::vector<Fq>(), std::vector<Fq>(), false };
635 for (size_t i = 0; i < add.size(); ++i) {
636 previous_x.assert_is_not_equal(add[i].x3_prev);
637
638 // composite_y add_y;
639 bool negate_add_y = (i > 0) && !previous_y.is_negative;
640 std::vector<Fq> lambda1_left;
641 std::vector<Fq> lambda1_right;
642 std::vector<Fq> lambda1_add;
643
644 if (i == 0) {
645 lambda1_add.emplace_back(-y);
646 } else {
647 lambda1_left = previous_y.mul_left;
648 lambda1_right = previous_y.mul_right;
649 lambda1_add = previous_y.add;
650 }
651
652 if (!add[i].is_element) {
653 lambda1_left.emplace_back(add[i].lambda_prev);
654 lambda1_right.emplace_back(negate_add_y ? add[i].x3_prev - add[i].x1_prev
655 : add[i].x1_prev - add[i].x3_prev);
656 lambda1_add.emplace_back(negate_add_y ? add[i].y1_prev : -add[i].y1_prev);
657 } else if (i > 0) {
658 lambda1_add.emplace_back(negate_add_y ? -add[i].y3_prev : add[i].y3_prev);
659 }
660 // if previous_y is negated then add stays positive
661 // if previous_y is positive then add stays negated
662 // | add.y is negated | previous_y is negated | output of msub_div is -lambda |
663 // | --- | --- | --- |
664 // | no | yes | yes |
665 // | yes | no | no |
666
667 Fq lambda1;
668 if (!add[i].is_element || i > 0) {
669 bool flip_lambda1_denominator = !negate_add_y;
670 Fq denominator = flip_lambda1_denominator ? previous_x - add[i].x3_prev : add[i].x3_prev - previous_x;
671 lambda1 = Fq::msub_div(
672 lambda1_left,
673 lambda1_right,
674 denominator,
675 lambda1_add,
676 /*enable_divisor_nz_check*/ false); // divisor is non-zero as previous_x != add[i].x3_prev is enforced
677 } else {
678 lambda1 = Fq::div_without_denominator_check({ add[i].y3_prev - y }, (add[i].x3_prev - x));
679 }
680
681 Fq x_3 = lambda1.madd(lambda1, { -add[i].x3_prev, -previous_x });
682
683 // We can avoid computing y_4, instead substituting the expression `minus_lambda_2 * (x_4 - x) - y`
684 // where needed. This is cheaper, because we can evaluate two field multiplications (or a field
685 // multiplication + a field division) with only one non-native field reduction. E.g. evaluating (a * b)
686 // + (c * d) = e mod p only requires 1 quotient and remainder, which is the major cost of a non-native
687 // field multiplication
688 Fq lambda2;
689 if (i == 0) {
690 lambda2 = Fq::div_without_denominator_check({ y + y }, (previous_x - x_3)) - lambda1;
691 } else {
692 Fq l2_denominator = previous_y.is_negative ? previous_x - x_3 : x_3 - previous_x;
693 // TODO(): analyse if l2_denominator can be zero.
694 Fq partial_lambda2 = Fq::msub_div(previous_y.mul_left,
695 previous_y.mul_right,
696 l2_denominator,
697 previous_y.add,
698 /*enable_divisor_nz_check*/ false);
699 partial_lambda2 = partial_lambda2 + partial_lambda2;
700 lambda2 = partial_lambda2 - lambda1;
701 }
702
703 Fq x_4 = lambda2.sqradd({ -x_3, -previous_x });
704 composite_y y_4;
705 if (i == 0) {
706 // We want to make sure that at the final iteration, `y_previous.is_negative = false`
707 // Each iteration flips the sign of y_previous.is_negative.
708 // i.e. whether we store y_4 or -y_4 depends on the number of points we have
709 bool num_points_even = ((add.size() & 0x01UL) == 0);
710 y_4.add.emplace_back(num_points_even ? y : -y);
711 y_4.mul_left.emplace_back(lambda2);
712 y_4.mul_right.emplace_back(num_points_even ? x_4 - previous_x : previous_x - x_4);
713 y_4.is_negative = num_points_even;
714 } else {
715 y_4.is_negative = !previous_y.is_negative;
716 y_4.mul_left.emplace_back(lambda2);
717 y_4.mul_right.emplace_back(previous_y.is_negative ? previous_x - x_4 : x_4 - previous_x);
718 // append terms in previous_y to y_4. We want to make sure the terms above are added into the start
719 // of y_4. This is to ensure they are cached correctly when
720 // `builder::evaluate_partial_non_native_field_multiplication` is called.
721 // (the 1st mul_left, mul_right elements will trigger
722 // builder::evaluate_non_native_field_multiplication
723 // when Fq::mult_madd is called - this term cannot be cached so we want to make sure it is unique)
724 std::copy(previous_y.mul_left.begin(), previous_y.mul_left.end(), std::back_inserter(y_4.mul_left));
725 std::copy(previous_y.mul_right.begin(), previous_y.mul_right.end(), std::back_inserter(y_4.mul_right));
726 std::copy(previous_y.add.begin(), previous_y.add.end(), std::back_inserter(y_4.add));
727 }
728 previous_x = x_4;
729 previous_y = y_4;
730 }
731 Fq x_out = previous_x;
732
733 ASSERT(!previous_y.is_negative);
734
735 Fq y_out = Fq::mult_madd(previous_y.mul_left, previous_y.mul_right, previous_y.add);
736 return element(x_out, y_out);
737}
738
776template <typename C, class Fq, class Fr, class G>
778 const size_t num_rounds)
779{
780 constexpr typename G::affine_element offset_generator =
781 get_precomputed_generators<G, "biggroup offset generator", 1>()[0];
782
783 const uint256_t offset_multiplier = uint256_t(1) << uint256_t(num_rounds - 1);
784
785 const typename G::affine_element offset_generator_end = typename G::element(offset_generator) * offset_multiplier;
786 return std::make_pair<element, element>(offset_generator, offset_generator_end);
787}
788
805template <typename C, class Fq, class Fr, class G>
807 const std::vector<Fr>& _scalars,
808 const size_t max_num_bits,
809 const bool with_edgecases)
810{
811 auto [points, scalars] = handle_points_at_infinity(_points, _scalars);
812 OriginTag tag{};
813 const auto empty_tag = OriginTag();
814
815 for (size_t i = 0; i < _points.size(); i++) {
816 tag = OriginTag(tag, OriginTag(_points[i].get_origin_tag(), _scalars[i].get_origin_tag()));
817 }
818 for (size_t i = 0; i < scalars.size(); i++) {
819 // If batch_mul actually performs batch multiplication on the points and scalars, subprocedures can do
820 // operations like addition or subtraction of points, which can trigger OriginTag security mechanisms
821 // even though the final result satisfies the security logic For example result = submitted_in_round_0
822 // *challenge_from_round_0 +submitted_in_round_1 * challenge_in_round_1 will trigger it, because the
823 // addition of submitted_in_round_0 to submitted_in_round_1 is dangerous by itself. To avoid this, we
824 // remove the tags, merge them separately and set the result appropriately
825 points[i].set_origin_tag(empty_tag);
826 scalars[i].set_origin_tag(empty_tag);
827 }
828
829 // Perform goblinized batched mul if available; supported only for BN254
830 if (with_edgecases) {
831 std::tie(points, scalars) = mask_points(points, scalars);
832 }
833 const size_t num_points = points.size();
834 BB_ASSERT_EQ(scalars.size(), num_points);
835
836 batch_lookup_table point_table(points);
837 const size_t num_rounds = (max_num_bits == 0) ? Fr::modulus.get_msb() + 1 : max_num_bits;
838
840 for (size_t i = 0; i < num_points; ++i) {
841 naf_entries.emplace_back(compute_naf(scalars[i], max_num_bits));
842 }
843 const auto offset_generators = compute_offset_generators(num_rounds);
844 element accumulator =
845 element::chain_add_end(element::chain_add(offset_generators.first, point_table.get_chain_initial_entry()));
846
847 constexpr size_t num_rounds_per_iteration = 4;
848 size_t num_iterations = num_rounds / num_rounds_per_iteration;
849 num_iterations += ((num_iterations * num_rounds_per_iteration) == num_rounds) ? 0 : 1;
850 const size_t num_rounds_per_final_iteration = (num_rounds - 1) - ((num_iterations - 1) * num_rounds_per_iteration);
851 for (size_t i = 0; i < num_iterations; ++i) {
852
853 std::vector<bool_ct> nafs(num_points);
855 const size_t inner_num_rounds =
856 (i != num_iterations - 1) ? num_rounds_per_iteration : num_rounds_per_final_iteration;
857 for (size_t j = 0; j < inner_num_rounds; ++j) {
858 for (size_t k = 0; k < num_points; ++k) {
859 nafs[k] = (naf_entries[k][i * num_rounds_per_iteration + j + 1]);
860 }
861 to_add.emplace_back(point_table.get_chain_add_accumulator(nafs));
862 }
863 accumulator = accumulator.multiple_montgomery_ladder(to_add);
864 }
865 for (size_t i = 0; i < num_points; ++i) {
866 element skew = accumulator - points[i];
867 Fq out_x = accumulator.x.conditional_select(skew.x, naf_entries[i][num_rounds]);
868 Fq out_y = accumulator.y.conditional_select(skew.y, naf_entries[i][num_rounds]);
869 accumulator = element(out_x, out_y);
870 }
871 accumulator = accumulator - offset_generators.second;
872
873 accumulator.set_origin_tag(tag);
874 return accumulator;
875}
879template <typename C, class Fq, class Fr, class G>
881{
882 // Use `scalar_mul` method without specifying the length of `scalar`.
883 return scalar_mul(scalar);
884}
885
886template <typename C, class Fq, class Fr, class G>
895element<C, Fq, Fr, G> element<C, Fq, Fr, G>::scalar_mul(const Fr& scalar, const size_t max_num_bits) const
896{
897 BB_ASSERT_EQ(max_num_bits % 2, 0U);
921 OriginTag tag{};
922 tag = OriginTag(tag, OriginTag(this->get_origin_tag(), scalar.get_origin_tag()));
923
924 bool_ct is_point_at_infinity = this->is_point_at_infinity();
925
926 const size_t num_rounds = (max_num_bits == 0) ? Fr::modulus.get_msb() + 1 : max_num_bits;
927
928 element result;
929 if (max_num_bits != 0) {
930 // The case of short scalars
931 result = element::bn254_endo_batch_mul({}, {}, { *this }, { scalar }, num_rounds);
932 } else {
933 // The case of arbitrary length scalars
934 result = element::bn254_endo_batch_mul({ *this }, { scalar }, {}, {}, num_rounds);
935 };
936
937 // Handle point at infinity
938 result.x = Fq::conditional_assign(is_point_at_infinity, x, result.x);
939 result.y = Fq::conditional_assign(is_point_at_infinity, y, result.y);
940
941 result.set_point_at_infinity(is_point_at_infinity);
942
943 // Propagate the origin tag
944 result.set_origin_tag(tag);
945
946 return result;
947}
948} // namespace bb::stdlib::element_default
#define BB_ASSERT_GT(left, right,...)
Definition assert.hpp:118
#define BB_ASSERT_EQ(actual, expected,...)
Definition assert.hpp:88
#define ASSERT(expression,...)
Definition assert.hpp:77
Implements boolean logic in-circuit.
Definition bool.hpp:59
element multiple_montgomery_ladder(const std::vector< chain_add_accumulator > &to_add) const
Perform repeated iterations of the montgomery ladder algorithm.
void set_origin_tag(OriginTag tag) const
Definition biggroup.hpp:465
void set_point_at_infinity(const bool_ct &is_infinity, const bool &add_to_used_witnesses=false)
Definition biggroup.hpp:456
FF a
#define G(r, i, a, b, c, d)
Definition blake2s.cpp:116
constexpr T get_msb(const T in)
Definition get_msb.hpp:47
constexpr std::span< const typename Group::affine_element > get_precomputed_generators()
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
This file contains part of the logic for the Origin Tag mechanism that tracks the use of in-circuit p...
static constexpr uint256_t modulus
BB_INLINE constexpr field add(const field &other) const noexcept
static constexpr field zero()
element::chain_add_accumulator get_chain_add_accumulator(std::vector< bool_ct > &naf_entries) const
Definition biggroup.hpp:860
void throw_or_abort(std::string const &err)
bb::fq Fq