barretenberg
Loading...
Searching...
No Matches
element_impl.hpp
1#pragma once
2#include "barretenberg/ecc/groups/element.hpp"
3#include "element.hpp"
4
5// NOLINTBEGIN(readability-implicit-bool-conversion, cppcoreguidelines-avoid-c-arrays)
6namespace barretenberg::group_elements {
7template <class Fq, class Fr, class T>
8constexpr element<Fq, Fr, T>::element(const Fq& a, const Fq& b, const Fq& c) noexcept
9 : x(a)
10 , y(b)
11 , z(c)
12{}
13
14template <class Fq, class Fr, class T>
15constexpr element<Fq, Fr, T>::element(const element& other) noexcept
16 : x(other.x)
17 , y(other.y)
18 , z(other.z)
19{}
20
21template <class Fq, class Fr, class T>
22constexpr element<Fq, Fr, T>::element(element&& other) noexcept
23 : x(other.x)
24 , y(other.y)
25 , z(other.z)
26{}
27
28template <class Fq, class Fr, class T>
29constexpr element<Fq, Fr, T>::element(const affine_element<Fq, Fr, T>& other) noexcept
30 : x(other.x)
31 , y(other.y)
32 , z(Fq::one())
33{}
34
35template <class Fq, class Fr, class T>
36constexpr element<Fq, Fr, T>& element<Fq, Fr, T>::operator=(const element& other) noexcept
37{
38 if (this == &other) {
39 return *this;
40 }
41 x = other.x;
42 y = other.y;
43 z = other.z;
44 return *this;
45}
46
47template <class Fq, class Fr, class T>
48constexpr element<Fq, Fr, T>& element<Fq, Fr, T>::operator=(element&& other) noexcept
49{
50 x = other.x;
51 y = other.y;
52 z = other.z;
53 return *this;
54}
55
56template <class Fq, class Fr, class T> constexpr element<Fq, Fr, T>::operator affine_element<Fq, Fr, T>() const noexcept
57{
58 if (is_point_at_infinity()) {
60 result.x = Fq(0);
61 result.y = Fq(0);
62 result.self_set_infinity();
63 return result;
64 }
65 Fq z_inv = z.invert();
66 Fq zz_inv = z_inv.sqr();
67 Fq zzz_inv = zz_inv * z_inv;
68 affine_element<Fq, Fr, T> result(x * zz_inv, y * zzz_inv);
69 if (is_point_at_infinity()) {
70 result.self_set_infinity();
71 }
72 return result;
73}
74
75template <class Fq, class Fr, class T> constexpr void element<Fq, Fr, T>::self_dbl() noexcept
76{
77 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
78 if (is_point_at_infinity()) {
79 return;
80 }
81 } else {
82 if (x.is_msb_set_word()) {
83 return;
84 }
85 }
86
87 // T0 = x*x
88 Fq T0 = x.sqr();
89
90 // T1 = y*y
91 Fq T1 = y.sqr();
92
93 // T2 = T2*T1 = y*y*y*y
94 Fq T2 = T1.sqr();
95
96 // T1 = T1 + x = x + y*y
97 T1 += x;
98
99 // T1 = T1 * T1
100 T1.self_sqr();
101
102 // T3 = T0 + T2 = xx + y*y*y*y
103 Fq T3 = T0 + T2;
104
105 // T1 = T1 - T3 = x*x + y*y*y*y + 2*x*x*y*y*y*y - x*x - y*y*y*y = 2*x*x*y*y*y*y = 2*S
106 T1 -= T3;
107
108 // T1 = 2T1 = 4*S
109 T1 += T1;
110
111 // T3 = 3T0
112 T3 = T0 + T0;
113 T3 += T0;
114 if constexpr (T::has_a) {
115 T3 += (T::a * z.sqr().sqr());
116 }
117
118 // z2 = 2*y*z
119 z += z;
120 z *= y;
121
122 // T0 = 2T1
123 T0 = T1 + T1;
124
125 // x2 = T3*T3
126 x = T3.sqr();
127
128 // x2 = x2 - 2T1
129 x -= T0;
130
131 // T2 = 8T2
132 T2 += T2;
133 T2 += T2;
134 T2 += T2;
135
136 // y2 = T1 - x2
137 y = T1 - x;
138
139 // y2 = y2 * T3 - T2
140 y *= T3;
141 y -= T2;
142}
143
144template <class Fq, class Fr, class T> constexpr element<Fq, Fr, T> element<Fq, Fr, T>::dbl() const noexcept
145{
146 element result(*this);
147 result.self_dbl();
148 return result;
149}
150
151template <class Fq, class Fr, class T>
152constexpr void element<Fq, Fr, T>::self_mixed_add_or_sub(const affine_element<Fq, Fr, T>& other,
153 const uint64_t predicate) noexcept
154{
155 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
156 if (is_point_at_infinity()) {
157 conditional_negate_affine(other, *(affine_element<Fq, Fr, T>*)this, predicate); // NOLINT
158 z = Fq::one();
159 return;
160 }
161 } else {
162 const bool edge_case_trigger = x.is_msb_set() || other.x.is_msb_set();
163 if (edge_case_trigger) {
164 if (x.is_msb_set()) {
165 conditional_negate_affine(other, *(affine_element<Fq, Fr, T>*)this, predicate); // NOLINT
166 z = Fq::one();
167 }
168 return;
169 }
170 }
171
172 // T0 = z1.z1
173 Fq T0 = z.sqr();
174
175 // T1 = x2.t0 - x1 = x2.z1.z1 - x1
176 Fq T1 = other.x * T0;
177 T1 -= x;
178
179 // T2 = T0.z1 = z1.z1.z1
180 // T2 = T2.y2 - y1 = y2.z1.z1.z1 - y1
181 Fq T2 = z * T0;
182 T2 *= other.y;
183 T2.self_conditional_negate(predicate);
184 T2 -= y;
185
186 if (__builtin_expect(T1.is_zero(), 0)) {
187 if (T2.is_zero()) {
188 // y2 equals y1, x2 equals x1, double x1
189 self_dbl();
190 return;
191 }
192 self_set_infinity();
193 return;
194 }
195
196 // T2 = 2T2 = 2(y2.z1.z1.z1 - y1) = R
197 // z3 = z1 + H
198 T2 += T2;
199 z += T1;
200
201 // T3 = T1*T1 = HH
202 Fq T3 = T1.sqr();
203
204 // z3 = z3 - z1z1 - HH
205 T0 += T3;
206
207 // z3 = (z1 + H)*(z1 + H)
208 z.self_sqr();
209 z -= T0;
210
211 // T3 = 4HH
212 T3 += T3;
213 T3 += T3;
214
215 // T1 = T1*T3 = 4HHH
216 T1 *= T3;
217
218 // T3 = T3 * x1 = 4HH*x1
219 T3 *= x;
220
221 // T0 = 2T3
222 T0 = T3 + T3;
223
224 // T0 = T0 + T1 = 2(4HH*x1) + 4HHH
225 T0 += T1;
226 x = T2.sqr();
227
228 // x3 = x3 - T0 = R*R - 8HH*x1 -4HHH
229 x -= T0;
230
231 // T3 = T3 - x3 = 4HH*x1 - x3
232 T3 -= x;
233
234 T1 *= y;
235 T1 += T1;
236
237 // T3 = T2 * T3 = R*(4HH*x1 - x3)
238 T3 *= T2;
239
240 // y3 = T3 - T1
241 y = T3 - T1;
242}
243
244template <class Fq, class Fr, class T>
245constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator+=(const affine_element<Fq, Fr, T>& other) noexcept
246{
247 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
248 if (is_point_at_infinity()) {
249 *this = { other.x, other.y, Fq::one() };
250 return *this;
251 }
252 } else {
253 const bool edge_case_trigger = x.is_msb_set() || other.x.is_msb_set();
254 if (edge_case_trigger) {
255 if (x.is_msb_set()) {
256 *this = { other.x, other.y, Fq::one() };
257 }
258 return *this;
259 }
260 }
261
262 // T0 = z1.z1
263 Fq T0 = z.sqr();
264
265 // T1 = x2.t0 - x1 = x2.z1.z1 - x1
266 Fq T1 = other.x * T0;
267 T1 -= x;
268
269 // T2 = T0.z1 = z1.z1.z1
270 // T2 = T2.y2 - y1 = y2.z1.z1.z1 - y1
271 Fq T2 = z * T0;
272 T2 *= other.y;
273 T2 -= y;
274
275 if (__builtin_expect(T1.is_zero(), 0)) {
276 if (T2.is_zero()) {
277 self_dbl();
278 return *this;
279 }
280 self_set_infinity();
281 return *this;
282 }
283
284 // T2 = 2T2 = 2(y2.z1.z1.z1 - y1) = R
285 // z3 = z1 + H
286 T2 += T2;
287 z += T1;
288
289 // T3 = T1*T1 = HH
290 Fq T3 = T1.sqr();
291
292 // z3 = z3 - z1z1 - HH
293 T0 += T3;
294
295 // z3 = (z1 + H)*(z1 + H)
296 z.self_sqr();
297 z -= T0;
298
299 // T3 = 4HH
300 T3 += T3;
301 T3 += T3;
302
303 // T1 = T1*T3 = 4HHH
304 T1 *= T3;
305
306 // T3 = T3 * x1 = 4HH*x1
307 T3 *= x;
308
309 // T0 = 2T3
310 T0 = T3 + T3;
311
312 // T0 = T0 + T1 = 2(4HH*x1) + 4HHH
313 T0 += T1;
314 x = T2.sqr();
315
316 // x3 = x3 - T0 = R*R - 8HH*x1 -4HHH
317 x -= T0;
318
319 // T3 = T3 - x3 = 4HH*x1 - x3
320 T3 -= x;
321
322 T1 *= y;
323 T1 += T1;
324
325 // T3 = T2 * T3 = R*(4HH*x1 - x3)
326 T3 *= T2;
327
328 // y3 = T3 - T1
329 y = T3 - T1;
330 return *this;
331}
332
333template <class Fq, class Fr, class T>
334constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator+(const affine_element<Fq, Fr, T>& other) const noexcept
335{
336 element result(*this);
337 return (result += other);
338}
339
340template <class Fq, class Fr, class T>
341constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator-=(const affine_element<Fq, Fr, T>& other) noexcept
342{
343 const affine_element<Fq, Fr, T> to_add{ other.x, -other.y };
344 return operator+=(to_add);
345}
346
347template <class Fq, class Fr, class T>
348constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator-(const affine_element<Fq, Fr, T>& other) const noexcept
349{
350 element result(*this);
351 return (result -= other);
352}
353
354template <class Fq, class Fr, class T>
355constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator+=(const element& other) noexcept
356{
357 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
358 bool p1_zero = is_point_at_infinity();
359 bool p2_zero = other.is_point_at_infinity();
360 if (__builtin_expect((p1_zero || p2_zero), 0)) {
361 if (p1_zero && !p2_zero) {
362 *this = other;
363 return *this;
364 }
365 if (p2_zero && !p1_zero) {
366 return *this;
367 }
368 self_set_infinity();
369 return *this;
370 }
371 } else {
372 bool p1_zero = x.is_msb_set();
373 bool p2_zero = other.x.is_msb_set();
374 if (__builtin_expect((p1_zero || p2_zero), 0)) {
375 if (p1_zero && !p2_zero) {
376 *this = other;
377 return *this;
378 }
379 if (p2_zero && !p1_zero) {
380 return *this;
381 }
382 self_set_infinity();
383 return *this;
384 }
385 }
386 Fq Z1Z1(z.sqr());
387 Fq Z2Z2(other.z.sqr());
388 Fq S2(Z1Z1 * z);
389 Fq U2(Z1Z1 * other.x);
390 S2 *= other.y;
391 Fq U1(Z2Z2 * x);
392 Fq S1(Z2Z2 * other.z);
393 S1 *= y;
394
395 Fq F(S2 - S1);
396
397 Fq H(U2 - U1);
398
399 if (__builtin_expect(H.is_zero(), 0)) {
400 if (F.is_zero()) {
401 self_dbl();
402 return *this;
403 }
404 self_set_infinity();
405 return *this;
406 }
407
408 F += F;
409
410 Fq I(H + H);
411 I.self_sqr();
412
413 Fq J(H * I);
414
415 U1 *= I;
416
417 U2 = U1 + U1;
418 U2 += J;
419
420 x = F.sqr();
421
422 x -= U2;
423
424 J *= S1;
425 J += J;
426
427 y = U1 - x;
428
429 y *= F;
430
431 y -= J;
432
433 z += other.z;
434
435 Z1Z1 += Z2Z2;
436
437 z.self_sqr();
438 z -= Z1Z1;
439 z *= H;
440 return *this;
441}
442
443template <class Fq, class Fr, class T>
444constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator+(const element& other) const noexcept
445{
446 element result(*this);
447 return (result += other);
448}
449
450template <class Fq, class Fr, class T>
451constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator-=(const element& other) noexcept
452{
453 const element to_add{ other.x, -other.y, other.z };
454 return operator+=(to_add);
455}
456
457template <class Fq, class Fr, class T>
458constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator-(const element& other) const noexcept
459{
460 element result(*this);
461 return (result -= other);
462}
463
464template <class Fq, class Fr, class T> constexpr element<Fq, Fr, T> element<Fq, Fr, T>::operator-() const noexcept
465{
466 return { x, -y, z };
467}
468
469template <class Fq, class Fr, class T>
470element<Fq, Fr, T> element<Fq, Fr, T>::operator*(const Fr& exponent) const noexcept
471{
472 if constexpr (T::USE_ENDOMORPHISM) {
473 return mul_with_endomorphism(exponent);
474 }
475 return mul_without_endomorphism(exponent);
476}
477
478template <class Fq, class Fr, class T> element<Fq, Fr, T> element<Fq, Fr, T>::operator*=(const Fr& exponent) noexcept
479{
480 *this = operator*(exponent);
481 return *this;
482}
483
484template <class Fq, class Fr, class T> constexpr element<Fq, Fr, T> element<Fq, Fr, T>::normalize() const noexcept
485{
486 const affine_element<Fq, Fr, T> converted = *this;
487 return element(converted);
488}
489
490template <class Fq, class Fr, class T> element<Fq, Fr, T> element<Fq, Fr, T>::infinity()
491{
492 element<Fq, Fr, T> e;
493 e.self_set_infinity();
494 return e;
495}
496
497template <class Fq, class Fr, class T> constexpr element<Fq, Fr, T> element<Fq, Fr, T>::set_infinity() const noexcept
498{
499 element result(*this);
500 result.self_set_infinity();
501 return result;
502}
503
504template <class Fq, class Fr, class T> constexpr void element<Fq, Fr, T>::self_set_infinity() noexcept
505{
506 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
507 // We set the value of x equal to modulus to represent inifinty
508 x.data[0] = Fq::modulus.data[0];
509 x.data[1] = Fq::modulus.data[1];
510 x.data[2] = Fq::modulus.data[2];
511 x.data[3] = Fq::modulus.data[3];
512 } else {
513 x.self_set_msb();
514 }
515}
516
517template <class Fq, class Fr, class T> constexpr bool element<Fq, Fr, T>::is_point_at_infinity() const noexcept
518{
519 if constexpr (Fq::modulus.data[3] >= 0x4000000000000000ULL) {
520 // We check if the value of x is equal to modulus to represent inifinty
521 return ((x.data[0] ^ Fq::modulus.data[0]) | (x.data[1] ^ Fq::modulus.data[1]) |
522 (x.data[2] ^ Fq::modulus.data[2]) | (x.data[3] ^ Fq::modulus.data[3])) == 0;
523 } else {
524 return (x.is_msb_set());
525 }
526}
527
528template <class Fq, class Fr, class T> constexpr bool element<Fq, Fr, T>::on_curve() const noexcept
529{
530 if (is_point_at_infinity()) {
531 return true;
532 }
533 // We specify the point at inifinity not by (0 \lambda 0), so z should not be 0
534 if (z.is_zero()) {
535 return false;
536 }
537 Fq zz = z.sqr();
538 Fq zzzz = zz.sqr();
539 Fq bz_6 = zzzz * zz * T::b;
540 if constexpr (T::has_a) {
541 bz_6 += (x * T::a) * zzzz;
542 }
543 Fq xxx = x.sqr() * x + bz_6;
544 Fq yy = y.sqr();
545 return (xxx == yy);
546}
547
548template <class Fq, class Fr, class T>
549constexpr bool element<Fq, Fr, T>::operator==(const element& other) const noexcept
550{
551 // If one of points is not on curve, we have no business comparing them.
552 if ((!on_curve()) || (!other.on_curve())) {
553 return false;
554 }
555 bool am_infinity = is_point_at_infinity();
556 bool is_infinity = other.is_point_at_infinity();
557 bool both_infinity = am_infinity && is_infinity;
558 // If just one is infinity, then they are obviously not equal.
559 if ((!both_infinity) && (am_infinity || is_infinity)) {
560 return false;
561 }
562 const Fq lhs_zz = z.sqr();
563 const Fq lhs_zzz = lhs_zz * z;
564 const Fq rhs_zz = other.z.sqr();
565 const Fq rhs_zzz = rhs_zz * other.z;
566
567 const Fq lhs_x = x * rhs_zz;
568 const Fq lhs_y = y * rhs_zzz;
569
570 const Fq rhs_x = other.x * lhs_zz;
571 const Fq rhs_y = other.y * lhs_zzz;
572 return both_infinity || ((lhs_x == rhs_x) && (lhs_y == rhs_y));
573}
574
575template <class Fq, class Fr, class T>
576element<Fq, Fr, T> element<Fq, Fr, T>::random_element(numeric::random::Engine* engine) noexcept
577{
578 if constexpr (T::can_hash_to_curve) {
579 element result = random_coordinates_on_curve(engine);
580 result.z = Fq::random_element(engine);
581 Fq zz = result.z.sqr();
582 Fq zzz = zz * result.z;
583 result.x *= zz;
584 result.y *= zzz;
585 return result;
586 } else {
587 Fr scalar = Fr::random_element(engine);
588 return (element{ T::one_x, T::one_y, Fq::one() } * scalar);
589 }
590}
591
592template <class Fq, class Fr, class T>
593element<Fq, Fr, T> element<Fq, Fr, T>::mul_without_endomorphism(const Fr& exponent) const noexcept
594{
595 const uint256_t converted_scalar(exponent);
596
597 if (converted_scalar == 0) {
598 element result{ Fq::zero(), Fq::zero(), Fq::zero() };
599 result.self_set_infinity();
600 return result;
601 }
602
603 element work_element(*this);
604 const uint64_t maximum_set_bit = converted_scalar.get_msb();
605 // This is simpler and doublings of infinity should be fast. We should think if we want to defend against the
606 // timing leak here (if used with ECDSA it can sometimes lead to private key compromise)
607 for (uint64_t i = maximum_set_bit - 1; i < maximum_set_bit; --i) {
608 work_element.self_dbl();
609 if (converted_scalar.get_bit(i)) {
610 work_element += *this;
611 }
612 }
613 return work_element;
614}
615
616template <class Fq, class Fr, class T>
617element<Fq, Fr, T> element<Fq, Fr, T>::mul_with_endomorphism(const Fr& exponent) const noexcept
618{
619 const Fr converted_scalar = exponent.from_montgomery_form();
620
621 if (converted_scalar.is_zero()) {
622 element result{ Fq::zero(), Fq::zero(), Fq::zero() };
623 result.self_set_infinity();
624 return result;
625 }
626
627 constexpr size_t lookup_size = 8;
628 constexpr size_t num_rounds = 32;
629 constexpr size_t num_wnaf_bits = 4;
630 std::array<element, lookup_size> lookup_table;
631
632 element d2 = element(*this);
633 d2.self_dbl();
634 lookup_table[0] = element(*this);
635 for (size_t i = 1; i < lookup_size; ++i) {
636 lookup_table[i] = lookup_table[i - 1] + d2;
637 }
638
639 uint64_t wnaf_table[num_rounds * 2];
640 Fr endo_scalar;
641 Fr::split_into_endomorphism_scalars(converted_scalar, endo_scalar, *(Fr*)&endo_scalar.data[2]); // NOLINT
642
643 bool skew = false;
644 bool endo_skew = false;
645
646 wnaf::fixed_wnaf(&endo_scalar.data[0], &wnaf_table[0], skew, 0, 2, num_wnaf_bits);
647 wnaf::fixed_wnaf(&endo_scalar.data[2], &wnaf_table[1], endo_skew, 0, 2, num_wnaf_bits);
648
649 element work_element{ T::one_x, T::one_y, Fq::one() };
650 work_element.self_set_infinity();
651
652 uint64_t wnaf_entry = 0;
653 uint64_t index = 0;
654 bool sign = false;
655 Fq beta = Fq::cube_root_of_unity();
656
657 for (size_t i = 0; i < num_rounds * 2; ++i) {
658 wnaf_entry = wnaf_table[i];
659 index = wnaf_entry & 0x0fffffffU;
660 sign = static_cast<bool>((wnaf_entry >> 31) & 1);
661 const bool is_odd = ((i & 1) == 1);
662 auto to_add = lookup_table[static_cast<size_t>(index)];
663 to_add.y.self_conditional_negate(sign ^ is_odd);
664 if (is_odd) {
665 to_add.x *= beta;
666 }
667 work_element += to_add;
668
669 if (i != ((2 * num_rounds) - 1) && is_odd) {
670 for (size_t j = 0; j < 4; ++j) {
671 work_element.self_dbl();
672 }
673 }
674 }
675
676 auto temporary = -lookup_table[0];
677 if (skew) {
678 work_element += temporary;
679 }
680
681 temporary = { lookup_table[0].x * beta, lookup_table[0].y, lookup_table[0].z };
682
683 if (endo_skew) {
684 work_element += temporary;
685 }
686
687 return work_element;
688}
689
690template <class Fq, class Fr, class T>
691std::vector<affine_element<Fq, Fr, T>> element<Fq, Fr, T>::batch_mul_with_endomorphism(
692 const std::vector<affine_element<Fq, Fr, T>>& points, const Fr& exponent) noexcept
693{
695 const size_t num_points = points.size();
696 std::vector<Fq> scratch_space(num_points);
697
698 // we can mutate rhs but NOT lhs!
699 // output is stored in rhs
700 const auto batch_affine_add = [num_points, &scratch_space](const affine_element* lhs, affine_element* rhs) {
701 Fq batch_inversion_accumulator = Fq::one();
702
703 for (size_t i = 0; i < num_points; i += 1) {
704 scratch_space[i] = lhs[i].x + rhs[i].x; // x2 + x1
705 rhs[i].x -= lhs[i].x; // x2 - x1
706 rhs[i].y -= lhs[i].y; // y2 - y1
707 rhs[i].y *= batch_inversion_accumulator; // (y2 - y1)*accumulator_old
708 batch_inversion_accumulator *= (rhs[i].x);
709 }
710 batch_inversion_accumulator = batch_inversion_accumulator.invert();
711
712 for (size_t i = (num_points)-1; i < num_points; i -= 1) {
713 rhs[i].y *= batch_inversion_accumulator; // update accumulator
714 batch_inversion_accumulator *= rhs[i].x;
715 rhs[i].x = rhs[i].y.sqr();
716 rhs[i].x = rhs[i].x - (scratch_space[i]); // x3 = lambda_squared - x2
717 // - x1
718 scratch_space[i] = lhs[i].x - rhs[i].x;
719 scratch_space[i] *= rhs[i].y;
720 rhs[i].y = scratch_space[i] - lhs[i].y;
721 }
722 };
723
724 // double the elements in lhs
725 const auto batch_affine_double = [num_points, &scratch_space](affine_element* lhs) {
726 Fq batch_inversion_accumulator = Fq::one();
727
728 for (size_t i = 0; i < num_points; i += 1) {
729
730 scratch_space[i] = lhs[i].x.sqr();
731 scratch_space[i] = scratch_space[i] + scratch_space[i] + scratch_space[i];
732
733 scratch_space[i] *= batch_inversion_accumulator;
734
735 batch_inversion_accumulator *= (lhs[i].y + lhs[i].y);
736 }
737 batch_inversion_accumulator = batch_inversion_accumulator.invert();
738
739 Fq temp;
740 for (size_t i = (num_points)-1; i < num_points; i -= 1) {
741
742 scratch_space[i] *= batch_inversion_accumulator;
743 batch_inversion_accumulator *= (lhs[i].y + lhs[i].y);
744
745 temp = lhs[i].x;
746 lhs[i].x = scratch_space[i].sqr() - (lhs[i].x + lhs[i].x);
747 lhs[i].y = scratch_space[i] * (temp - lhs[i].x) - lhs[i].y;
748 }
749 };
750
751 // Compute wnaf for scalar
752 const Fr converted_scalar = exponent.from_montgomery_form();
753
754 if (converted_scalar.is_zero()) {
755 affine_element result{ Fq::zero(), Fq::zero() };
756 result.self_set_infinity();
757 std::vector<affine_element> results;
758 for (size_t i = 0; i < num_points; ++i) {
759 results.emplace_back(result);
760 }
761 return results;
762 }
763
764 constexpr size_t lookup_size = 8;
765 constexpr size_t num_rounds = 32;
766 constexpr size_t num_wnaf_bits = 4;
767 std::array<std::vector<affine_element>, lookup_size> lookup_table;
768 for (auto& table : lookup_table) {
769 table.resize(num_points);
770 }
771 std::vector<affine_element> temp_point_vector(num_points);
772 for (size_t i = 0; i < num_points; ++i) {
773 temp_point_vector[i] = points[i];
774 lookup_table[0][i] = points[i];
775 }
776 batch_affine_double(&temp_point_vector[0]);
777 for (size_t j = 1; j < lookup_size; ++j) {
778
779 for (size_t i = 0; i < num_points; ++i) {
780 lookup_table[j][i] = lookup_table[j - 1][i];
781 }
782 batch_affine_add(&temp_point_vector[0], &lookup_table[j][0]);
783 }
784
785 uint64_t wnaf_table[num_rounds * 2];
786 Fr endo_scalar;
787 Fr::split_into_endomorphism_scalars(converted_scalar, endo_scalar, *(Fr*)&endo_scalar.data[2]); // NOLINT
788
789 bool skew = false;
790 bool endo_skew = false;
791
792 wnaf::fixed_wnaf(&endo_scalar.data[0], &wnaf_table[0], skew, 0, 2, num_wnaf_bits);
793 wnaf::fixed_wnaf(&endo_scalar.data[2], &wnaf_table[1], endo_skew, 0, 2, num_wnaf_bits);
794
795 std::vector<affine_element> work_elements(num_points);
796
797 uint64_t wnaf_entry = 0;
798 uint64_t index = 0;
799 bool sign = 0;
800 Fq beta = Fq::cube_root_of_unity();
801
802 for (size_t i = 0; i < 2; ++i) {
803 for (size_t j = 0; j < num_points; ++j) {
804 wnaf_entry = wnaf_table[i];
805 index = wnaf_entry & 0x0fffffffU;
806 sign = static_cast<bool>((wnaf_entry >> 31) & 1);
807 const bool is_odd = ((i & 1) == 1);
808 auto to_add = lookup_table[static_cast<size_t>(index)][j];
809 to_add.y.self_conditional_negate(sign ^ is_odd);
810 if (is_odd) {
811 to_add.x *= beta;
812 }
813 if (i == 0) {
814 work_elements[j] = to_add;
815 } else {
816 temp_point_vector[j] = to_add;
817 }
818 }
819 }
820 batch_affine_add(&temp_point_vector[0], &work_elements[0]);
821
822 for (size_t i = 2; i < num_rounds * 2; ++i) {
823 wnaf_entry = wnaf_table[i];
824 index = wnaf_entry & 0x0fffffffU;
825 sign = static_cast<bool>((wnaf_entry >> 31) & 1);
826 const bool is_odd = ((i & 1) == 1);
827 if (!is_odd) {
828 for (size_t k = 0; k < 4; ++k) {
829 batch_affine_double(&work_elements[0]);
830 }
831 }
832 for (size_t j = 0; j < num_points; ++j) {
833 auto to_add = lookup_table[static_cast<size_t>(index)][j];
834 to_add.y.self_conditional_negate(sign ^ is_odd);
835 if (is_odd) {
836 to_add.x *= beta;
837 }
838 temp_point_vector[j] = to_add;
839 }
840 batch_affine_add(&temp_point_vector[0], &work_elements[0]);
841 }
842
843 if (skew) {
844 for (size_t j = 0; j < num_points; ++j) {
845 temp_point_vector[j] = -lookup_table[0][j];
846 }
847 batch_affine_add(&temp_point_vector[0], &work_elements[0]);
848 }
849
850 if (endo_skew) {
851 for (size_t j = 0; j < num_points; ++j) {
852 temp_point_vector[j] = lookup_table[0][j];
853 temp_point_vector[j].x *= beta;
854 }
855 batch_affine_add(&temp_point_vector[0], &work_elements[0]);
856 }
857
858 return work_elements;
859}
860
861template <typename Fq, typename Fr, typename T>
862void element<Fq, Fr, T>::conditional_negate_affine(const affine_element<Fq, Fr, T>& in,
864 const uint64_t predicate) noexcept
865{
866 out = { in.x, predicate ? -in.y : in.y };
867}
868
869template <typename Fq, typename Fr, typename T>
870void element<Fq, Fr, T>::batch_normalize(element* elements, const size_t num_elements) noexcept
871{
872 std::vector<Fq> temporaries;
873 temporaries.reserve(num_elements * 2);
874 Fq accumulator = Fq::one();
875
876 // Iterate over the points, computing the product of their z-coordinates.
877 // At each iteration, store the currently-accumulated z-coordinate in `temporaries`
878 for (size_t i = 0; i < num_elements; ++i) {
879 temporaries.emplace_back(accumulator);
880 if (!elements[i].is_point_at_infinity()) {
881 accumulator *= elements[i].z;
882 }
883 }
884 // For the rest of this method we refer to the product of all z-coordinates as the 'global' z-coordinate
885 // Invert the global z-coordinate and store in `accumulator`
886 accumulator = accumulator.invert();
887
910 for (size_t i = num_elements - 1; i < num_elements; --i) {
911 if (!elements[i].is_point_at_infinity()) {
912 Fq z_inv = accumulator * temporaries[i];
913 Fq zz_inv = z_inv.sqr();
914 elements[i].x *= zz_inv;
915 elements[i].y *= (zz_inv * z_inv);
916 accumulator *= elements[i].z;
917 }
918 elements[i].z = Fq::one();
919 }
920}
921
922template <typename Fq, typename Fr, typename T>
923template <typename>
925{
926 bool found_one = false;
927 Fq yy;
928 Fq x;
929 Fq y;
930 while (!found_one) {
931 x = Fq::random_element(engine);
932 yy = x.sqr() * x + T::b;
933 if constexpr (T::has_a) {
934 yy += (x * T::a);
935 }
936 auto [found_root, y1] = yy.sqrt();
937 y = y1;
938 found_one = found_root;
939 }
940 return { x, y, Fq::one() };
941}
942
943} // namespace barretenberg::group_elements
944// NOLINTEND(readability-implicit-bool-conversion, cppcoreguidelines-avoid-c-arrays)
Definition: affine_element.hpp:11
element class. Implements ecc group arithmetic using Jacobian coordinates See https://hyperelliptic....
Definition: element.hpp:27
static void batch_normalize(element *elements, size_t num_elements) noexcept
Definition: element_impl.hpp:870
Definition: engine.hpp:10
bool_t< Builder > is_zero() const
Definition: field.cpp:588
BBERG_INLINE constexpr field sqr() const noexcept
Definition: field_impl.hpp:61
constexpr std::pair< bool, field > sqrt() const noexcept
Compute square root of the field element.
Definition: field_impl.hpp:507
static void split_into_endomorphism_scalars(const field &k, field &k1, field &k2)
Definition: field_declarations.hpp:320