1#![allow(non_snake_case)]
3use crate::{
4 constants::{BN_LIMB_WIDTH as LIMB_WIDTH, BN_N_LIMBS as N_LIMBS},
5 frontend::{
6 num::AllocatedNum, AllocatedBit, Assignment, Boolean, ConstraintSystem, SynthesisError,
7 },
8 gadgets::{
9 nonnative::{bignat::BigNat, util::f_to_nat},
10 utils::{
11 alloc_bignat_constant, alloc_constant, alloc_num_equals, alloc_one, alloc_zero,
12 conditionally_select, conditionally_select2, conditionally_select_bignat, select_num_or_one,
13 select_num_or_zero, select_num_or_zero2, select_one_or_diff2, select_one_or_num2,
14 select_zero_or_num2,
15 },
16 },
17 traits::{Engine, Group, ROCircuitTrait},
18};
19use ff::{Field, PrimeField};
20use num_bigint::BigInt;
21
22#[derive(Clone)]
24pub struct AllocatedPoint<E: Engine> {
25 pub x: AllocatedNum<E::Base>,
27 pub y: AllocatedNum<E::Base>,
29 pub is_infinity: AllocatedNum<E::Base>,
31}
32
33impl<E> AllocatedPoint<E>
34where
35 E: Engine,
36{
37 pub fn alloc<CS: ConstraintSystem<E::Base>>(
40 mut cs: CS,
41 coords: Option<(E::Base, E::Base, bool)>,
42 ) -> Result<Self, SynthesisError> {
43 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
44 Ok(coords.map_or(E::Base::ZERO, |c| c.0))
45 })?;
46 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
47 Ok(coords.map_or(E::Base::ZERO, |c| c.1))
48 })?;
49 let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || {
50 Ok(if coords.map_or(true, |c| c.2) {
51 E::Base::ONE
52 } else {
53 E::Base::ZERO
54 })
55 })?;
56 cs.enforce(
57 || "is_infinity is bit",
58 |lc| lc + is_infinity.get_variable(),
59 |lc| lc + CS::one() - is_infinity.get_variable(),
60 |lc| lc,
61 );
62
63 Ok(AllocatedPoint { x, y, is_infinity })
64 }
65
66 pub fn check_on_curve<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
68 where
69 CS: ConstraintSystem<E::Base>,
70 {
71 let y_square = self.y.square(cs.namespace(|| "y_square"))?;
75 let x_square = self.x.square(cs.namespace(|| "x_square"))?;
76 let x_cube = self.x.mul(cs.namespace(|| "x_cube"), &x_square)?;
77
78 let rhs = AllocatedNum::alloc(cs.namespace(|| "rhs"), || {
79 if *self.is_infinity.get_value().get()? == E::Base::ONE {
80 Ok(E::Base::ZERO)
81 } else {
82 Ok(
83 *x_cube.get_value().get()?
84 + *self.x.get_value().get()? * E::GE::group_params().0
85 + E::GE::group_params().1,
86 )
87 }
88 })?;
89
90 cs.enforce(
91 || "rhs = (1-is_infinity) * (x^3 + Ax + B)",
92 |lc| {
93 lc + x_cube.get_variable()
94 + (E::GE::group_params().0, self.x.get_variable())
95 + (E::GE::group_params().1, CS::one())
96 },
97 |lc| lc + CS::one() - self.is_infinity.get_variable(),
98 |lc| lc + rhs.get_variable(),
99 );
100
101 cs.enforce(
103 || "check that y_square * (1 - is_infinity) = rhs",
104 |lc| lc + y_square.get_variable(),
105 |lc| lc + CS::one() - self.is_infinity.get_variable(),
106 |lc| lc + rhs.get_variable(),
107 );
108
109 Ok(())
110 }
111
112 pub fn default<CS: ConstraintSystem<E::Base>>(mut cs: CS) -> Result<Self, SynthesisError> {
114 let zero = alloc_zero(cs.namespace(|| "zero"));
115 let one = alloc_one(cs.namespace(|| "one"));
116
117 Ok(AllocatedPoint {
118 x: zero.clone(),
119 y: zero,
120 is_infinity: one,
121 })
122 }
123
124 pub fn negate<CS: ConstraintSystem<E::Base>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
126 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(-*self.y.get_value().get()?))?;
127
128 cs.enforce(
129 || "check y = - self.y",
130 |lc| lc + self.y.get_variable(),
131 |lc| lc + CS::one(),
132 |lc| lc - y.get_variable(),
133 );
134
135 Ok(Self {
136 x: self.x.clone(),
137 y,
138 is_infinity: self.is_infinity.clone(),
139 })
140 }
141
142 pub fn add<CS: ConstraintSystem<E::Base>>(
144 &self,
145 mut cs: CS,
146 other: &AllocatedPoint<E>,
147 ) -> Result<Self, SynthesisError> {
148 let equal_x = alloc_num_equals(
151 cs.namespace(|| "check self.x == other.x"),
152 &self.x,
153 &other.x,
154 )?;
155
156 let equal_y = alloc_num_equals(
157 cs.namespace(|| "check self.y == other.y"),
158 &self.y,
159 &other.y,
160 )?;
161
162 let result_from_add = self.add_internal(cs.namespace(|| "add internal"), other, &equal_x)?;
164 let result_from_double = self.double(cs.namespace(|| "double"))?;
165
166 let result_for_equal_x = AllocatedPoint::select_point_or_infinity(
177 cs.namespace(|| "equal_y ? result_from_double : infinity"),
178 &result_from_double,
179 &Boolean::from(equal_y),
180 )?;
181
182 AllocatedPoint::conditionally_select(
183 cs.namespace(|| "equal ? result_from_double : result_from_add"),
184 &result_for_equal_x,
185 &result_from_add,
186 &Boolean::from(equal_x),
187 )
188 }
189
190 pub fn add_internal<CS: ConstraintSystem<E::Base>>(
193 &self,
194 mut cs: CS,
195 other: &AllocatedPoint<E>,
196 equal_x: &AllocatedBit,
197 ) -> Result<Self, SynthesisError> {
198 let at_least_one_inf = AllocatedNum::alloc(cs.namespace(|| "at least one inf"), || {
209 Ok(
210 E::Base::ONE
211 - (E::Base::ONE - *self.is_infinity.get_value().get()?)
212 * (E::Base::ONE - *other.is_infinity.get_value().get()?),
213 )
214 })?;
215 cs.enforce(
216 || "1 - at least one inf = (1-self.is_infinity) * (1-other.is_infinity)",
217 |lc| lc + CS::one() - self.is_infinity.get_variable(),
218 |lc| lc + CS::one() - other.is_infinity.get_variable(),
219 |lc| lc + CS::one() - at_least_one_inf.get_variable(),
220 );
221
222 let x_diff_is_actual =
224 AllocatedNum::alloc(cs.namespace(|| "allocate x_diff_is_actual"), || {
225 Ok(if *equal_x.get_value().get()? {
226 E::Base::ONE
227 } else {
228 *at_least_one_inf.get_value().get()?
229 })
230 })?;
231 cs.enforce(
232 || "1 - x_diff_is_actual = (1-equal_x) * (1-at_least_one_inf)",
233 |lc| lc + CS::one() - at_least_one_inf.get_variable(),
234 |lc| lc + CS::one() - equal_x.get_variable(),
235 |lc| lc + CS::one() - x_diff_is_actual.get_variable(),
236 );
237
238 let x_diff = select_one_or_diff2(
241 cs.namespace(|| "Compute x_diff"),
242 &other.x,
243 &self.x,
244 &x_diff_is_actual,
245 )?;
246
247 let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
248 let x_diff_inv = if *x_diff_is_actual.get_value().get()? == E::Base::ONE {
249 E::Base::ONE
251 } else {
252 (*other.x.get_value().get()? - *self.x.get_value().get()?)
254 .invert()
255 .unwrap()
256 };
257
258 Ok((*other.y.get_value().get()? - *self.y.get_value().get()?) * x_diff_inv)
259 })?;
260 cs.enforce(
261 || "Check that lambda is correct",
262 |lc| lc + lambda.get_variable(),
263 |lc| lc + x_diff.get_variable(),
264 |lc| lc + other.y.get_variable() - self.y.get_variable(),
265 );
266
267 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
271 Ok(
272 *lambda.get_value().get()? * lambda.get_value().get()?
273 - *self.x.get_value().get()?
274 - *other.x.get_value().get()?,
275 )
276 })?;
277 cs.enforce(
278 || "check that x is correct",
279 |lc| lc + lambda.get_variable(),
280 |lc| lc + lambda.get_variable(),
281 |lc| lc + x.get_variable() + self.x.get_variable() + other.x.get_variable(),
282 );
283
284 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
288 Ok(
289 *lambda.get_value().get()? * (*self.x.get_value().get()? - *x.get_value().get()?)
290 - *self.y.get_value().get()?,
291 )
292 })?;
293
294 cs.enforce(
295 || "Check that y is correct",
296 |lc| lc + lambda.get_variable(),
297 |lc| lc + self.x.get_variable() - x.get_variable(),
298 |lc| lc + y.get_variable() + self.y.get_variable(),
299 );
300
301 let x1 = conditionally_select2(
311 cs.namespace(|| "x1 = other.is_infinity ? self.x : x"),
312 &self.x,
313 &x,
314 &other.is_infinity,
315 )?;
316
317 let x = conditionally_select2(
318 cs.namespace(|| "x = self.is_infinity ? other.x : x1"),
319 &other.x,
320 &x1,
321 &self.is_infinity,
322 )?;
323
324 let y1 = conditionally_select2(
325 cs.namespace(|| "y1 = other.is_infinity ? self.y : y"),
326 &self.y,
327 &y,
328 &other.is_infinity,
329 )?;
330
331 let y = conditionally_select2(
332 cs.namespace(|| "y = self.is_infinity ? other.y : y1"),
333 &other.y,
334 &y1,
335 &self.is_infinity,
336 )?;
337
338 let is_infinity1 = select_num_or_zero2(
339 cs.namespace(|| "is_infinity1 = other.is_infinity ? self.is_infinity : 0"),
340 &self.is_infinity,
341 &other.is_infinity,
342 )?;
343
344 let is_infinity = conditionally_select2(
345 cs.namespace(|| "is_infinity = self.is_infinity ? other.is_infinity : is_infinity1"),
346 &other.is_infinity,
347 &is_infinity1,
348 &self.is_infinity,
349 )?;
350
351 Ok(Self { x, y, is_infinity })
352 }
353
354 pub fn double<CS: ConstraintSystem<E::Base>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
356 let tmp_actual = AllocatedNum::alloc(cs.namespace(|| "tmp_actual"), || {
363 Ok(*self.y.get_value().get()? + *self.y.get_value().get()?)
364 })?;
365 cs.enforce(
366 || "check tmp_actual",
367 |lc| lc + CS::one() + CS::one(),
368 |lc| lc + self.y.get_variable(),
369 |lc| lc + tmp_actual.get_variable(),
370 );
371
372 let tmp = select_one_or_num2(cs.namespace(|| "tmp"), &tmp_actual, &self.is_infinity)?;
373
374 let prod_1 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 1"), || {
377 Ok(E::Base::from(3) * self.x.get_value().get()? * self.x.get_value().get()?)
378 })?;
379 cs.enforce(
380 || "Check prod 1",
381 |lc| lc + (E::Base::from(3), self.x.get_variable()),
382 |lc| lc + self.x.get_variable(),
383 |lc| lc + prod_1.get_variable(),
384 );
385
386 let lambda = AllocatedNum::alloc(cs.namespace(|| "alloc lambda"), || {
387 let tmp_inv = if *self.is_infinity.get_value().get()? == E::Base::ONE {
388 E::Base::ONE
390 } else {
391 (*tmp.get_value().get()?).invert().unwrap()
393 };
394
395 Ok(tmp_inv * (*prod_1.get_value().get()? + E::GE::group_params().0))
396 })?;
397
398 cs.enforce(
399 || "Check lambda",
400 |lc| lc + tmp.get_variable(),
401 |lc| lc + lambda.get_variable(),
402 |lc| lc + prod_1.get_variable() + (E::GE::group_params().0, CS::one()),
403 );
404
405 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
410 Ok(
411 ((*lambda.get_value().get()?) * (*lambda.get_value().get()?))
412 - *self.x.get_value().get()?
413 - self.x.get_value().get()?,
414 )
415 })?;
416 cs.enforce(
417 || "Check x",
418 |lc| lc + lambda.get_variable(),
419 |lc| lc + lambda.get_variable(),
420 |lc| lc + x.get_variable() + self.x.get_variable() + self.x.get_variable(),
421 );
422
423 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
428 Ok(
429 (*lambda.get_value().get()?) * (*self.x.get_value().get()? - x.get_value().get()?)
430 - self.y.get_value().get()?,
431 )
432 })?;
433 cs.enforce(
434 || "Check y",
435 |lc| lc + lambda.get_variable(),
436 |lc| lc + self.x.get_variable() - x.get_variable(),
437 |lc| lc + y.get_variable() + self.y.get_variable(),
438 );
439
440 let x = select_zero_or_num2(cs.namespace(|| "final x"), &x, &self.is_infinity)?;
446
447 let y = select_zero_or_num2(cs.namespace(|| "final y"), &y, &self.is_infinity)?;
449
450 let is_infinity = self.is_infinity.clone();
452
453 Ok(Self { x, y, is_infinity })
454 }
455
456 pub fn scalar_mul<CS: ConstraintSystem<E::Base>>(
460 &self,
461 mut cs: CS,
462 scalar_bits: &[AllocatedBit],
463 ) -> Result<Self, SynthesisError> {
464 let split_len = core::cmp::min(scalar_bits.len(), (E::Base::NUM_BITS - 2) as usize);
465 let (incomplete_bits, complete_bits) = scalar_bits.split_at(split_len);
466
467 let mut p = AllocatedPointNonInfinity::from_allocated_point(self);
469
470 let mut acc = p;
473 p = acc.double_incomplete(cs.namespace(|| "double"))?;
474
475 for (i, bit) in incomplete_bits.iter().enumerate().skip(1) {
477 let temp = acc.add_incomplete(cs.namespace(|| format!("add {i}")), &p)?;
478 acc = AllocatedPointNonInfinity::conditionally_select(
479 cs.namespace(|| format!("acc_iteration_{i}")),
480 &temp,
481 &acc,
482 &Boolean::from(bit.clone()),
483 )?;
484
485 p = p.double_incomplete(cs.namespace(|| format!("double {i}")))?;
486 }
487
488 let res = {
490 let acc = acc.to_allocated_point(&self.is_infinity)?;
492
493 let acc_minus_initial = {
495 let neg = self.negate(cs.namespace(|| "negate"))?;
496 acc.add(cs.namespace(|| "res minus self"), &neg)
497 }?;
498
499 AllocatedPoint::conditionally_select(
500 cs.namespace(|| "remove slack if necessary"),
501 &acc,
502 &acc_minus_initial,
503 &Boolean::from(scalar_bits[0].clone()),
504 )?
505 };
506
507 let default = Self::default(cs.namespace(|| "default"))?;
510 let x = conditionally_select2(
511 cs.namespace(|| "check if self.is_infinity is zero (x)"),
512 &default.x,
513 &res.x,
514 &self.is_infinity,
515 )?;
516
517 let y = conditionally_select2(
518 cs.namespace(|| "check if self.is_infinity is zero (y)"),
519 &default.y,
520 &res.y,
521 &self.is_infinity,
522 )?;
523
524 let mut acc = AllocatedPoint {
526 x,
527 y,
528 is_infinity: res.is_infinity,
529 };
530 let mut p_complete = p.to_allocated_point(&self.is_infinity)?;
531
532 for (i, bit) in complete_bits.iter().enumerate() {
533 let temp = acc.add(cs.namespace(|| format!("add_complete {i}")), &p_complete)?;
534 acc = AllocatedPoint::conditionally_select(
535 cs.namespace(|| format!("acc_complete_iteration_{i}")),
536 &temp,
537 &acc,
538 &Boolean::from(bit.clone()),
539 )?;
540
541 p_complete = p_complete.double(cs.namespace(|| format!("double_complete {i}")))?;
542 }
543
544 Ok(acc)
545 }
546
547 pub fn conditionally_select<CS: ConstraintSystem<E::Base>>(
549 mut cs: CS,
550 a: &Self,
551 b: &Self,
552 condition: &Boolean,
553 ) -> Result<Self, SynthesisError> {
554 let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?;
555
556 let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?;
557
558 let is_infinity = conditionally_select(
559 cs.namespace(|| "select is_infinity"),
560 &a.is_infinity,
561 &b.is_infinity,
562 condition,
563 )?;
564
565 Ok(Self { x, y, is_infinity })
566 }
567
568 pub fn select_point_or_infinity<CS: ConstraintSystem<E::Base>>(
570 mut cs: CS,
571 a: &Self,
572 condition: &Boolean,
573 ) -> Result<Self, SynthesisError> {
574 let x = select_num_or_zero(cs.namespace(|| "select x"), &a.x, condition)?;
575
576 let y = select_num_or_zero(cs.namespace(|| "select y"), &a.y, condition)?;
577
578 let is_infinity = select_num_or_one(
579 cs.namespace(|| "select is_infinity"),
580 &a.is_infinity,
581 condition,
582 )?;
583
584 Ok(Self { x, y, is_infinity })
585 }
586
587 pub fn alloc_constant<CS: ConstraintSystem<E::Base>>(
589 mut cs: CS,
590 coords: (E::Base, E::Base),
591 is_infinity: AllocatedNum<E::Base>,
592 ) -> Result<AllocatedPoint<E>, SynthesisError> {
593 let x = alloc_constant(cs.namespace(|| "x"), &coords.0)?;
594 let y = alloc_constant(cs.namespace(|| "y"), &coords.1)?;
595
596 Ok(AllocatedPoint { x, y, is_infinity })
597 }
598
599 pub fn absorb_in_ro<CS: ConstraintSystem<E::Base>>(
605 &self,
606 mut cs: CS,
607 ro: &mut E::ROCircuit,
608 ) -> Result<(), SynthesisError> {
609 let (_, b, _, _) = E::GE::group_params();
610 if b != E::Base::ZERO {
611 let zero = alloc_zero(cs.namespace(|| "zero for absorb"));
612 let x = conditionally_select2(
613 cs.namespace(|| "select x"),
614 &zero,
615 &self.x,
616 &self.is_infinity,
617 )?;
618 let y = conditionally_select2(
619 cs.namespace(|| "select y"),
620 &zero,
621 &self.y,
622 &self.is_infinity,
623 )?;
624 ro.absorb(&x);
625 ro.absorb(&y);
626 } else {
627 ro.absorb(&self.x);
628 ro.absorb(&self.y);
629 ro.absorb(&self.is_infinity);
630 }
631
632 Ok(())
633 }
634
635 pub fn enforce_equal<CS: ConstraintSystem<E::Base>>(
637 &self,
638 mut cs: CS,
639 other: &Self,
640 ) -> Result<(), SynthesisError> {
641 cs.enforce(
642 || "check x equality",
643 |lc| lc + self.x.get_variable() - other.x.get_variable(),
644 |lc| lc + CS::one(),
645 |lc| lc,
646 );
647 cs.enforce(
648 || "check y equality",
649 |lc| lc + self.y.get_variable() - other.y.get_variable(),
650 |lc| lc + CS::one(),
651 |lc| lc,
652 );
653 cs.enforce(
654 || "check is_inf equality",
655 |lc| lc + self.is_infinity.get_variable() - other.is_infinity.get_variable(),
656 |lc| lc + CS::one(),
657 |lc| lc,
658 );
659
660 Ok(())
661 }
662}
663
664#[derive(Clone)]
665pub struct AllocatedPointNonInfinity<E: Engine> {
667 pub x: AllocatedNum<E::Base>,
669 pub y: AllocatedNum<E::Base>,
671}
672
673impl<E: Engine> AllocatedPointNonInfinity<E> {
674 pub fn from_allocated_point(p: &AllocatedPoint<E>) -> Self {
676 Self {
677 x: p.x.clone(),
678 y: p.y.clone(),
679 }
680 }
681
682 pub fn to_allocated_point(
684 &self,
685 is_infinity: &AllocatedNum<E::Base>,
686 ) -> Result<AllocatedPoint<E>, SynthesisError> {
687 Ok(AllocatedPoint {
688 x: self.x.clone(),
689 y: self.y.clone(),
690 is_infinity: is_infinity.clone(),
691 })
692 }
693
694 pub fn add_incomplete<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
696 where
697 CS: ConstraintSystem<E::Base>,
698 {
699 let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
701 if *other.x.get_value().get()? == *self.x.get_value().get()? {
702 Ok(E::Base::ONE)
703 } else {
704 Ok(
705 (*other.y.get_value().get()? - *self.y.get_value().get()?)
706 * (*other.x.get_value().get()? - *self.x.get_value().get()?)
707 .invert()
708 .unwrap(),
709 )
710 }
711 })?;
712 cs.enforce(
713 || "Check that lambda is computed correctly",
714 |lc| lc + lambda.get_variable(),
715 |lc| lc + other.x.get_variable() - self.x.get_variable(),
716 |lc| lc + other.y.get_variable() - self.y.get_variable(),
717 );
718
719 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
723 Ok(
724 *lambda.get_value().get()? * lambda.get_value().get()?
725 - *self.x.get_value().get()?
726 - *other.x.get_value().get()?,
727 )
728 })?;
729 cs.enforce(
730 || "check that x is correct",
731 |lc| lc + lambda.get_variable(),
732 |lc| lc + lambda.get_variable(),
733 |lc| lc + x.get_variable() + self.x.get_variable() + other.x.get_variable(),
734 );
735
736 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
740 Ok(
741 *lambda.get_value().get()? * (*self.x.get_value().get()? - *x.get_value().get()?)
742 - *self.y.get_value().get()?,
743 )
744 })?;
745
746 cs.enforce(
747 || "Check that y is correct",
748 |lc| lc + lambda.get_variable(),
749 |lc| lc + self.x.get_variable() - x.get_variable(),
750 |lc| lc + y.get_variable() + self.y.get_variable(),
751 );
752
753 Ok(Self { x, y })
754 }
755
756 pub fn double_incomplete<CS: ConstraintSystem<E::Base>>(
758 &self,
759 mut cs: CS,
760 ) -> Result<Self, SynthesisError> {
761 let x_sq = self.x.square(cs.namespace(|| "x_sq"))?;
764
765 let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
766 let n = E::Base::from(3) * x_sq.get_value().get()? + E::GE::group_params().0;
767 let d = E::Base::from(2) * *self.y.get_value().get()?;
768 if d == E::Base::ZERO {
769 Ok(E::Base::ONE)
770 } else {
771 Ok(n * d.invert().unwrap())
772 }
773 })?;
774 cs.enforce(
775 || "Check that lambda is computed correctly",
776 |lc| lc + lambda.get_variable(),
777 |lc| lc + (E::Base::from(2), self.y.get_variable()),
778 |lc| lc + (E::Base::from(3), x_sq.get_variable()) + (E::GE::group_params().0, CS::one()),
779 );
780
781 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
782 Ok(
783 *lambda.get_value().get()? * *lambda.get_value().get()?
784 - *self.x.get_value().get()?
785 - *self.x.get_value().get()?,
786 )
787 })?;
788
789 cs.enforce(
790 || "check that x is correct",
791 |lc| lc + lambda.get_variable(),
792 |lc| lc + lambda.get_variable(),
793 |lc| lc + x.get_variable() + (E::Base::from(2), self.x.get_variable()),
794 );
795
796 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
797 Ok(
798 *lambda.get_value().get()? * (*self.x.get_value().get()? - *x.get_value().get()?)
799 - *self.y.get_value().get()?,
800 )
801 })?;
802
803 cs.enforce(
804 || "Check that y is correct",
805 |lc| lc + lambda.get_variable(),
806 |lc| lc + self.x.get_variable() - x.get_variable(),
807 |lc| lc + y.get_variable() + self.y.get_variable(),
808 );
809
810 Ok(Self { x, y })
811 }
812
813 pub fn conditionally_select<CS: ConstraintSystem<E::Base>>(
815 mut cs: CS,
816 a: &Self,
817 b: &Self,
818 condition: &Boolean,
819 ) -> Result<Self, SynthesisError> {
820 let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?;
821 let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?;
822
823 Ok(Self { x, y })
824 }
825
826 pub fn alloc<CS: ConstraintSystem<E::Base>>(
828 mut cs: CS,
829 coords: Option<(E::Base, E::Base)>,
830 ) -> Result<AllocatedPointNonInfinity<E>, SynthesisError> {
831 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
832 coords.map_or(Err(SynthesisError::AssignmentMissing), |c| Ok(c.0))
833 })?;
834 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
835 coords.map_or(Err(SynthesisError::AssignmentMissing), |c| Ok(c.1))
836 })?;
837
838 Ok(AllocatedPointNonInfinity { x, y })
839 }
840
841 pub fn conditionally_select2<CS: ConstraintSystem<E::Base>>(
843 mut cs: CS,
844 a: &Self,
845 b: &Self,
846 condition: &AllocatedNum<E::Base>,
847 ) -> Result<AllocatedPointNonInfinity<E>, SynthesisError> {
848 let x = conditionally_select2(cs.namespace(|| "select x"), &a.x, &b.x, condition)?;
849 let y = conditionally_select2(cs.namespace(|| "select y"), &a.y, &b.y, condition)?;
850
851 Ok(AllocatedPointNonInfinity { x, y })
852 }
853
854 pub fn enforce_equal<CS: ConstraintSystem<E::Base>>(
856 &self,
857 mut cs: CS,
858 other: &Self,
859 ) -> Result<(), SynthesisError> {
860 cs.enforce(
861 || "check x equality",
862 |lc| lc + self.x.get_variable() - other.x.get_variable(),
863 |lc| lc + CS::one(),
864 |lc| lc,
865 );
866 cs.enforce(
867 || "check y equality",
868 |lc| lc + self.y.get_variable() - other.y.get_variable(),
869 |lc| lc + CS::one(),
870 |lc| lc,
871 );
872
873 Ok(())
874 }
875}
876
877#[derive(Clone, Debug)]
881pub struct AllocatedNonnativePoint<E: Engine> {
882 pub x: BigNat<E::Scalar>,
884 pub y: BigNat<E::Scalar>,
886 pub is_infinity: AllocatedNum<E::Scalar>,
888}
889
890impl<E: Engine> AllocatedNonnativePoint<E> {
891 pub fn alloc<CS: ConstraintSystem<E::Scalar>>(
893 mut cs: CS,
894 coords: Option<(E::Base, E::Base, bool)>,
895 ) -> Result<Self, SynthesisError> {
896 let x = BigNat::alloc_from_nat(
897 cs.namespace(|| "x as BigNat"),
898 || Ok(coords.map_or(f_to_nat(&E::Base::ZERO), |v| f_to_nat(&v.0))),
899 LIMB_WIDTH,
900 N_LIMBS,
901 )?;
902
903 let y = BigNat::alloc_from_nat(
904 cs.namespace(|| "y as BigNat"),
905 || Ok(coords.map_or(f_to_nat(&E::Base::ZERO), |v| f_to_nat(&v.1))),
906 LIMB_WIDTH,
907 N_LIMBS,
908 )?;
909
910 let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || {
911 Ok(if coords.map_or(true, |c| c.2) {
912 E::Scalar::ONE
913 } else {
914 E::Scalar::ZERO
915 })
916 })?;
917
918 cs.enforce(
919 || "is_infinity is bit",
920 |lc| lc + is_infinity.get_variable(),
921 |lc| lc + CS::one() - is_infinity.get_variable(),
922 |lc| lc,
923 );
924
925 Ok(Self { x, y, is_infinity })
926 }
927
928 pub fn default<CS>(mut cs: CS) -> Result<Self, SynthesisError>
930 where
931 CS: ConstraintSystem<E::Scalar>,
932 {
933 let one = alloc_one(cs.namespace(|| "one"));
934 let zero = alloc_bignat_constant(
935 cs.namespace(|| "zero"),
936 &BigInt::from(0),
937 LIMB_WIDTH,
938 N_LIMBS,
939 )?;
940
941 Ok(AllocatedNonnativePoint {
942 x: zero.clone(),
943 y: zero,
944 is_infinity: one,
945 })
946 }
947
948 pub fn absorb_in_ro<CS: ConstraintSystem<E::Scalar>>(
954 &self,
955 mut cs: CS,
956 ro: &mut E::RO2Circuit,
957 ) -> Result<(), SynthesisError> {
958 for (i, limb) in self.x.as_limbs().iter().enumerate() {
959 let limb_num =
960 limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of num x")))?;
961 ro.absorb(&limb_num);
962 }
963
964 for (i, limb) in self.y.as_limbs().iter().enumerate() {
965 let limb_num =
966 limb.as_allocated_num(cs.namespace(|| format!("convert limb {i} of num y")))?;
967 ro.absorb(&limb_num);
968 }
969
970 let (_, b, _, _) = E::GE::group_params();
972 if b == E::Base::ZERO {
973 ro.absorb(&self.is_infinity);
974 }
975
976 Ok(())
977 }
978
979 pub fn conditionally_select<CS: ConstraintSystem<E::Scalar>>(
981 mut cs: CS,
982 a: &Self,
983 b: &Self,
984 condition: &Boolean,
985 ) -> Result<Self, SynthesisError> {
986 let x = conditionally_select_bignat(cs.namespace(|| "select x"), &a.x, &b.x, condition)?;
987 let y = conditionally_select_bignat(cs.namespace(|| "select y"), &a.y, &b.y, condition)?;
988 let is_infinity = conditionally_select(
989 cs.namespace(|| "select is_infinity"),
990 &a.is_infinity,
991 &b.is_infinity,
992 condition,
993 )?;
994
995 Ok(Self { x, y, is_infinity })
996 }
997}
998
999#[cfg(test)]
1000mod tests {
1001 use super::*;
1002 use crate::{
1003 frontend::{
1004 r1cs::{NovaShape, NovaWitness},
1005 solver::SatisfyingAssignment,
1006 test_shape_cs::TestShapeCS,
1007 },
1008 provider::{
1009 bn256_grumpkin::{bn256, grumpkin},
1010 pasta::{pallas, vesta},
1011 secp_secq::{secp256k1, secq256k1},
1012 Bn256EngineKZG, GrumpkinEngine, PallasEngine, Secp256k1Engine, Secq256k1Engine, VestaEngine,
1013 },
1014 r1cs::R1CSShape,
1015 traits::snark::default_ck_hint,
1016 };
1017 use ff::{Field, PrimeFieldBits};
1018 use halo2curves::{group::Curve, CurveAffine};
1019 use rand::rngs::OsRng;
1020
1021 #[derive(Debug, Clone)]
1022 pub struct Point<E: Engine> {
1023 x: E::Base,
1024 y: E::Base,
1025 is_infinity: bool,
1026 }
1027
1028 impl<E: Engine> Point<E> {
1029 pub fn new(x: E::Base, y: E::Base, is_infinity: bool) -> Self {
1030 Self { x, y, is_infinity }
1031 }
1032
1033 pub fn random_vartime() -> Self {
1034 loop {
1035 let x = E::Base::random(&mut OsRng);
1036 let y = (x.square() * x + E::GE::group_params().1).sqrt();
1037 if y.is_some().unwrap_u8() == 1 {
1038 return Self {
1039 x,
1040 y: y.unwrap(),
1041 is_infinity: false,
1042 };
1043 }
1044 }
1045 }
1046
1047 pub fn add(&self, other: &Point<E>) -> Self {
1049 if self.x == other.x {
1050 if self.y == other.y {
1052 self.double()
1053 } else {
1054 Self {
1056 x: E::Base::ZERO,
1057 y: E::Base::ZERO,
1058 is_infinity: true,
1059 }
1060 }
1061 } else {
1062 self.add_internal(other)
1063 }
1064 }
1065
1066 pub fn add_internal(&self, other: &Point<E>) -> Self {
1068 if self.is_infinity {
1069 return other.clone();
1070 }
1071
1072 if other.is_infinity {
1073 return self.clone();
1074 }
1075
1076 let lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
1077 let x = lambda * lambda - self.x - other.x;
1078 let y = lambda * (self.x - x) - self.y;
1079 Self {
1080 x,
1081 y,
1082 is_infinity: false,
1083 }
1084 }
1085
1086 pub fn double(&self) -> Self {
1087 if self.is_infinity {
1088 return Self {
1089 x: E::Base::ZERO,
1090 y: E::Base::ZERO,
1091 is_infinity: true,
1092 };
1093 }
1094
1095 let lambda = E::Base::from(3)
1096 * self.x
1097 * self.x
1098 * ((E::Base::ONE + E::Base::ONE) * self.y).invert().unwrap();
1099 let x = lambda * lambda - self.x - self.x;
1100 let y = lambda * (self.x - x) - self.y;
1101 Self {
1102 x,
1103 y,
1104 is_infinity: false,
1105 }
1106 }
1107
1108 pub fn scalar_mul(&self, scalar: &E::Scalar) -> Self {
1109 let mut res = Self {
1110 x: E::Base::ZERO,
1111 y: E::Base::ZERO,
1112 is_infinity: true,
1113 };
1114
1115 let bits = scalar.to_le_bits();
1116 for i in (0..bits.len()).rev() {
1117 res = res.double();
1118 if bits[i] {
1119 res = self.add(&res);
1120 }
1121 }
1122 res
1123 }
1124 }
1125
1126 pub fn alloc_random_point<E: Engine, CS: ConstraintSystem<E::Base>>(
1128 mut cs: CS,
1129 ) -> Result<AllocatedPoint<E>, SynthesisError> {
1130 let p = Point::<E>::random_vartime();
1132 AllocatedPoint::alloc(cs.namespace(|| "alloc p"), Some((p.x, p.y, p.is_infinity)))
1133 }
1134
1135 pub fn inputize_allocated_point<E: Engine, CS: ConstraintSystem<E::Base>>(
1137 p: &AllocatedPoint<E>,
1138 mut cs: CS,
1139 ) {
1140 let _ = p.x.inputize(cs.namespace(|| "Input point.x"));
1141 let _ = p.y.inputize(cs.namespace(|| "Input point.y"));
1142 let _ = p
1143 .is_infinity
1144 .inputize(cs.namespace(|| "Input point.is_infinity"));
1145 }
1146
1147 #[test]
1148 fn test_ecc_ops() {
1149 test_ecc_ops_with::<pallas::Affine, PallasEngine>();
1150 test_ecc_ops_with::<vesta::Affine, VestaEngine>();
1151
1152 test_ecc_ops_with::<bn256::Affine, Bn256EngineKZG>();
1153 test_ecc_ops_with::<grumpkin::Affine, GrumpkinEngine>();
1154
1155 test_ecc_ops_with::<secp256k1::Affine, Secp256k1Engine>();
1156 test_ecc_ops_with::<secq256k1::Affine, Secq256k1Engine>();
1157 }
1158
1159 fn test_ecc_ops_with<C, E>()
1160 where
1161 E: Engine,
1162 C: CurveAffine<Base = E::Base, ScalarExt = E::Scalar>,
1163 {
1164 let a = Point::<E>::random_vartime();
1166 let b = Point::<E>::random_vartime();
1167 let c = a.add(&b);
1168 let d = a.double();
1169 let s = <E as Engine>::Scalar::random(&mut OsRng);
1170 let e = a.scalar_mul(&s);
1171
1172 let a_curve = C::from_xy(
1174 C::Base::from_repr(a.x.to_repr()).unwrap(),
1175 C::Base::from_repr(a.y.to_repr()).unwrap(),
1176 )
1177 .unwrap();
1178 let b_curve = C::from_xy(
1179 C::Base::from_repr(b.x.to_repr()).unwrap(),
1180 C::Base::from_repr(b.y.to_repr()).unwrap(),
1181 )
1182 .unwrap();
1183 let c_curve = (a_curve + b_curve).to_affine();
1184 let d_curve = (a_curve + a_curve).to_affine();
1185 let e_curve = a_curve
1186 .mul(C::Scalar::from_repr(s.to_repr()).unwrap())
1187 .to_affine();
1188
1189 let c_curve_2 = C::from_xy(
1191 C::Base::from_repr(c.x.to_repr()).unwrap(),
1192 C::Base::from_repr(c.y.to_repr()).unwrap(),
1193 )
1194 .unwrap();
1195 let d_curve_2 = C::from_xy(
1196 C::Base::from_repr(d.x.to_repr()).unwrap(),
1197 C::Base::from_repr(d.y.to_repr()).unwrap(),
1198 )
1199 .unwrap();
1200 let e_curve_2 = C::from_xy(
1201 C::Base::from_repr(e.x.to_repr()).unwrap(),
1202 C::Base::from_repr(e.y.to_repr()).unwrap(),
1203 )
1204 .unwrap();
1205
1206 assert_eq!(c_curve, c_curve_2);
1208 assert_eq!(d_curve, d_curve_2);
1209 assert_eq!(e_curve, e_curve_2);
1210 }
1211
1212 fn synthesize_smul<E, CS>(mut cs: CS) -> (AllocatedPoint<E>, AllocatedPoint<E>, E::Scalar)
1213 where
1214 E: Engine,
1215 CS: ConstraintSystem<E::Base>,
1216 {
1217 let a = alloc_random_point(cs.namespace(|| "a")).unwrap();
1218 inputize_allocated_point(&a, cs.namespace(|| "inputize a"));
1219
1220 let s = E::Scalar::random(&mut OsRng);
1221 let bits: Vec<AllocatedBit> = s
1223 .to_le_bits()
1224 .into_iter()
1225 .enumerate()
1226 .map(|(i, bit)| AllocatedBit::alloc(cs.namespace(|| format!("bit {i}")), Some(bit)))
1227 .collect::<Result<Vec<AllocatedBit>, SynthesisError>>()
1228 .unwrap();
1229 let e = a.scalar_mul(cs.namespace(|| "Scalar Mul"), &bits).unwrap();
1230 inputize_allocated_point(&e, cs.namespace(|| "inputize e"));
1231 (a, e, s)
1232 }
1233
1234 #[test]
1235 fn test_ecc_circuit_ops() {
1236 test_ecc_circuit_ops_with::<PallasEngine, VestaEngine>();
1237 test_ecc_circuit_ops_with::<VestaEngine, PallasEngine>();
1238
1239 test_ecc_circuit_ops_with::<Bn256EngineKZG, GrumpkinEngine>();
1240 test_ecc_circuit_ops_with::<GrumpkinEngine, Bn256EngineKZG>();
1241
1242 test_ecc_circuit_ops_with::<Secp256k1Engine, Secq256k1Engine>();
1243 test_ecc_circuit_ops_with::<Secq256k1Engine, Secp256k1Engine>();
1244 }
1245
1246 fn test_ecc_circuit_ops_with<E1, E2>()
1247 where
1248 E1: Engine<Base = <E2 as Engine>::Scalar>,
1249 E2: Engine<Base = <E1 as Engine>::Scalar>,
1250 {
1251 let mut cs: TestShapeCS<E2> = TestShapeCS::new();
1253 let _ = synthesize_smul::<E1, _>(cs.namespace(|| "synthesize"));
1254 println!("Number of constraints: {}", cs.num_constraints());
1255 let shape = cs.r1cs_shape().unwrap();
1256 let ck = R1CSShape::commitment_key(&[&shape], &[&*default_ck_hint()]).unwrap();
1257
1258 let mut cs = SatisfyingAssignment::<E2>::new();
1260 let (a, e, s) = synthesize_smul::<E1, _>(cs.namespace(|| "synthesize"));
1261 let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &ck).unwrap();
1262
1263 let a_p: Point<E1> = Point::new(
1264 a.x.get_value().unwrap(),
1265 a.y.get_value().unwrap(),
1266 a.is_infinity.get_value().unwrap() == <E1 as Engine>::Base::ONE,
1267 );
1268 let e_p: Point<E1> = Point::new(
1269 e.x.get_value().unwrap(),
1270 e.y.get_value().unwrap(),
1271 e.is_infinity.get_value().unwrap() == <E1 as Engine>::Base::ONE,
1272 );
1273 let e_new = a_p.scalar_mul(&s);
1274 assert!(e_p.x == e_new.x && e_p.y == e_new.y);
1275 assert!(shape.is_sat(&ck, &inst, &witness).is_ok());
1277 }
1278
1279 fn synthesize_add_equal<E, CS>(mut cs: CS) -> (AllocatedPoint<E>, AllocatedPoint<E>)
1280 where
1281 E: Engine,
1282 CS: ConstraintSystem<E::Base>,
1283 {
1284 let a = alloc_random_point(cs.namespace(|| "a")).unwrap();
1285 inputize_allocated_point(&a, cs.namespace(|| "inputize a"));
1286 let e = a.add(cs.namespace(|| "add a to a"), &a).unwrap();
1287 inputize_allocated_point(&e, cs.namespace(|| "inputize e"));
1288 (a, e)
1289 }
1290
1291 #[test]
1292 fn test_ecc_circuit_add_equal() {
1293 test_ecc_circuit_add_equal_with::<PallasEngine, VestaEngine>();
1294 test_ecc_circuit_add_equal_with::<VestaEngine, PallasEngine>();
1295
1296 test_ecc_circuit_add_equal_with::<Bn256EngineKZG, GrumpkinEngine>();
1297 test_ecc_circuit_add_equal_with::<GrumpkinEngine, Bn256EngineKZG>();
1298
1299 test_ecc_circuit_add_equal_with::<Secp256k1Engine, Secq256k1Engine>();
1300 test_ecc_circuit_add_equal_with::<Secq256k1Engine, Secp256k1Engine>();
1301 }
1302
1303 fn test_ecc_circuit_add_equal_with<E1, E2>()
1304 where
1305 E1: Engine<Base = <E2 as Engine>::Scalar>,
1306 E2: Engine<Base = <E1 as Engine>::Scalar>,
1307 {
1308 let mut cs: TestShapeCS<E2> = TestShapeCS::new();
1310 let _ = synthesize_add_equal::<E1, _>(cs.namespace(|| "synthesize add equal"));
1311 println!("Number of constraints: {}", cs.num_constraints());
1312 let shape = cs.r1cs_shape().unwrap();
1313 let ck = R1CSShape::commitment_key(&[&shape], &[&*default_ck_hint()]).unwrap();
1314
1315 let mut cs = SatisfyingAssignment::<E2>::new();
1317 let (a, e) = synthesize_add_equal::<E1, _>(cs.namespace(|| "synthesize add equal"));
1318 let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &ck).unwrap();
1319 let a_p: Point<E1> = Point::new(
1320 a.x.get_value().unwrap(),
1321 a.y.get_value().unwrap(),
1322 a.is_infinity.get_value().unwrap() == <E1 as Engine>::Base::ONE,
1323 );
1324 let e_p: Point<E1> = Point::new(
1325 e.x.get_value().unwrap(),
1326 e.y.get_value().unwrap(),
1327 e.is_infinity.get_value().unwrap() == <E1 as Engine>::Base::ONE,
1328 );
1329 let e_new = a_p.add(&a_p);
1330 assert!(e_p.x == e_new.x && e_p.y == e_new.y);
1331 assert!(shape.is_sat(&ck, &inst, &witness).is_ok());
1333 }
1334
1335 fn synthesize_add_negation<E, CS>(mut cs: CS) -> AllocatedPoint<E>
1336 where
1337 E: Engine,
1338 CS: ConstraintSystem<E::Base>,
1339 {
1340 let a = alloc_random_point(cs.namespace(|| "a")).unwrap();
1341 inputize_allocated_point(&a, cs.namespace(|| "inputize a"));
1342 let b = &mut a.clone();
1343 b.y = AllocatedNum::alloc(cs.namespace(|| "allocate negation of a"), || {
1344 Ok(E::Base::ZERO)
1345 })
1346 .unwrap();
1347 inputize_allocated_point(b, cs.namespace(|| "inputize b"));
1348 let e = a.add(cs.namespace(|| "add a to b"), b).unwrap();
1349 e
1350 }
1351
1352 #[test]
1353 fn test_ecc_circuit_add_negation() {
1354 test_ecc_circuit_add_negation_with::<PallasEngine, VestaEngine>();
1355 test_ecc_circuit_add_negation_with::<VestaEngine, PallasEngine>();
1356
1357 test_ecc_circuit_add_negation_with::<Bn256EngineKZG, GrumpkinEngine>();
1358 test_ecc_circuit_add_negation_with::<GrumpkinEngine, Bn256EngineKZG>();
1359
1360 test_ecc_circuit_add_negation_with::<Secp256k1Engine, Secq256k1Engine>();
1361 test_ecc_circuit_add_negation_with::<Secq256k1Engine, Secp256k1Engine>();
1362 }
1363
1364 fn test_ecc_circuit_add_negation_with<E1, E2>()
1365 where
1366 E1: Engine<Base = <E2 as Engine>::Scalar>,
1367 E2: Engine<Base = <E1 as Engine>::Scalar>,
1368 {
1369 let mut cs: TestShapeCS<E2> = TestShapeCS::new();
1371 let _ = synthesize_add_negation::<E1, _>(cs.namespace(|| "synthesize add equal"));
1372 println!("Number of constraints: {}", cs.num_constraints());
1373 let shape = cs.r1cs_shape().unwrap();
1374 let ck = R1CSShape::commitment_key(&[&shape], &[&*default_ck_hint()]).unwrap();
1375
1376 let mut cs = SatisfyingAssignment::<E2>::new();
1378 let e = synthesize_add_negation::<E1, _>(cs.namespace(|| "synthesize add negation"));
1379 let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &ck).unwrap();
1380 let e_p: Point<E1> = Point::new(
1381 e.x.get_value().unwrap(),
1382 e.y.get_value().unwrap(),
1383 e.is_infinity.get_value().unwrap() == <E1 as Engine>::Base::ONE,
1384 );
1385 assert!(e_p.is_infinity);
1386 assert!(shape.is_sat(&ck, &inst, &witness).is_ok());
1388 }
1389}