Skip to main content

nova_snark/gadgets/
ecc.rs

1//! This module implements various elliptic curve gadgets
2#![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/// `AllocatedPoint` provides an elliptic curve abstraction inside a circuit.
23#[derive(Clone)]
24pub struct AllocatedPoint<E: Engine> {
25  /// The x-coordinate of the point.
26  pub x: AllocatedNum<E::Base>,
27  /// The y-coordinate of the point.
28  pub y: AllocatedNum<E::Base>,
29  /// Flag indicating if this is the point at infinity (1 = infinity, 0 = not infinity).
30  pub is_infinity: AllocatedNum<E::Base>,
31}
32
33impl<E> AllocatedPoint<E>
34where
35  E: Engine,
36{
37  /// Allocates a new point on the curve using coordinates provided by `coords`.
38  /// If coords = None, it allocates the default infinity point
39  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  /// checks if `self` is on the curve or if it is infinity
67  pub fn check_on_curve<CS>(&self, mut cs: CS) -> Result<(), SynthesisError>
68  where
69    CS: ConstraintSystem<E::Base>,
70  {
71    // check that (x,y) is on the curve if it is not infinity
72    // we will check that (1- is_infinity) * y^2 = (1-is_infinity) * (x^3 + Ax + B)
73    // note that is_infinity is already restricted to be in the set {0, 1}
74    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    // check that (1-infinity) * y_square = rhs
102    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  /// Allocates a default point on the curve, set to the identity point.
113  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  /// Negates the provided point
125  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  /// Add two points (may be equal)
143  pub fn add<CS: ConstraintSystem<E::Base>>(
144    &self,
145    mut cs: CS,
146    other: &AllocatedPoint<E>,
147  ) -> Result<Self, SynthesisError> {
148    // Compute boolean equal indicating if self = other
149
150    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    // Compute the result of the addition and the result of double self
163    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    // Output:
167    // If (self == other) {
168    //  return double(self)
169    // }else {
170    //  if (self.x == other.x){
171    //      return infinity [negation]
172    //  } else {
173    //      return add(self, other)
174    //  }
175    // }
176    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  /// Adds other point to this point and returns the result. Assumes that the two points are
191  /// different and that both `other.is_infinity` and `this.is_infinity` are bits
192  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    //************************************************************************/
199    // lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
200    //************************************************************************/
201    // First compute (other.x - self.x).inverse()
202    // If either self or other are the infinity point or self.x = other.x  then compute bogus values
203    // Specifically,
204    // x_diff = self != inf && other != inf && self.x == other.x ? (other.x - self.x) : 1
205
206    // Compute self.is_infinity OR other.is_infinity =
207    // NOT(NOT(self.is_ifninity) AND NOT(other.is_infinity))
208    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    // Now compute x_diff_is_actual = at_least_one_inf OR equal_x
223    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    // x_diff = 1 if either self.is_infinity or other.is_infinity or self.x = other.x else self.x -
239    // other.x
240    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        // Set to default
250        E::Base::ONE
251      } else {
252        // Set to the actual inverse
253        (*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    //************************************************************************/
268    // x = lambda * lambda - self.x - other.x;
269    //************************************************************************/
270    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    //************************************************************************/
285    // y = lambda * (self.x - x) - self.y;
286    //************************************************************************/
287    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    //************************************************************************/
302    // We only return the computed x, y if neither of the points is infinity and self.x != other.y
303    // if self.is_infinity return other.clone()
304    // elif other.is_infinity return self.clone()
305    // elif self.x == other.x return infinity
306    // Otherwise return the computed points.
307    //************************************************************************/
308    // Now compute the output x
309
310    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  /// Doubles the supplied point.
355  pub fn double<CS: ConstraintSystem<E::Base>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
356    //*************************************************************/
357    // lambda = (E::Base::from(3) * self.x * self.x + E::GE::A())
358    //  * (E::Base::from(2)) * self.y).invert().unwrap();
359    /*************************************************************/
360
361    // Compute tmp = (E::Base::ONE + E::Base::ONE)* self.y ? self != inf : 1
362    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    // Now compute lambda as (E::Base::from(3) * self.x * self.x + E::GE::A()) * tmp_inv
375
376    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        // Return default value 1
389        E::Base::ONE
390      } else {
391        // Return the actual inverse
392        (*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    /*************************************************************/
406    //          x = lambda * lambda - self.x - self.x;
407    /*************************************************************/
408
409    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    /*************************************************************/
424    //        y = lambda * (self.x - x) - self.y;
425    /*************************************************************/
426
427    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    /*************************************************************/
441    // Only return the computed x and y if the point is not infinity
442    /*************************************************************/
443
444    // x
445    let x = select_zero_or_num2(cs.namespace(|| "final x"), &x, &self.is_infinity)?;
446
447    // y
448    let y = select_zero_or_num2(cs.namespace(|| "final y"), &y, &self.is_infinity)?;
449
450    // is_infinity
451    let is_infinity = self.is_infinity.clone();
452
453    Ok(Self { x, y, is_infinity })
454  }
455
456  /// A gadget for scalar multiplication, optimized to use incomplete addition law.
457  /// The optimization here is analogous to <https://github.com/arkworks-rs/r1cs-std/blob/6d64f379a27011b3629cf4c9cb38b7b7b695d5a0/src/groups/curves/short_weierstrass/mod.rs#L295>,
458  /// except we use complete addition law over affine coordinates instead of projective coordinates for the tail bits
459  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    // we convert AllocatedPoint into AllocatedPointNonInfinity; we deal with the case where self.is_infinity = 1 below
468    let mut p = AllocatedPointNonInfinity::from_allocated_point(self);
469
470    // we assume the first bit to be 1, so we must initialize acc to self and double it
471    // we remove this assumption below
472    let mut acc = p;
473    p = acc.double_incomplete(cs.namespace(|| "double"))?;
474
475    // perform the double-and-add loop to compute the scalar mul using incomplete addition law
476    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    // convert back to AllocatedPoint
489    let res = {
490      // we set acc.is_infinity = self.is_infinity
491      let acc = acc.to_allocated_point(&self.is_infinity)?;
492
493      // we remove the initial slack if bits[0] is as not as assumed (i.e., it is not 1)
494      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    // when self.is_infinity = 1, return the default point, else return res
508    // we already set res.is_infinity to be self.is_infinity, so we do not need to set it here
509    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    // we now perform the remaining scalar mul using complete addition law
525    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  /// If condition outputs a otherwise outputs b
548  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  /// If condition outputs a otherwise infinity
569  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  /// Allocate a point with constant x and y coordinates and a provided `is_infinity` flag.
588  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  /// Absorb the point into a random oracle circuit.
600  ///
601  /// When B != 0 (true for BN254, Grumpkin, etc.), (0,0) is not on the curve
602  /// so we can use it as a canonical representation for infinity.
603  /// This matches Nova's native Commitment absorb behavior.
604  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  /// Enforce that self equals other.
636  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)]
665/// `AllocatedPoint` but one that is guaranteed to be not infinity
666pub struct AllocatedPointNonInfinity<E: Engine> {
667  /// The x-coordinate of the point.
668  pub x: AllocatedNum<E::Base>,
669  /// The y-coordinate of the point.
670  pub y: AllocatedNum<E::Base>,
671}
672
673impl<E: Engine> AllocatedPointNonInfinity<E> {
674  /// Turns an `AllocatedPoint` into an `AllocatedPointNonInfinity` (assumes it is not infinity)
675  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  /// Returns an `AllocatedPoint` from an `AllocatedPointNonInfinity`
683  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  /// Add two points assuming self != +/- other
695  pub fn add_incomplete<CS>(&self, mut cs: CS, other: &Self) -> Result<Self, SynthesisError>
696  where
697    CS: ConstraintSystem<E::Base>,
698  {
699    // allocate a free variable that an honest prover sets to lambda = (y2-y1)/(x2-x1)
700    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    //************************************************************************/
720    // x = lambda * lambda - self.x - other.x;
721    //************************************************************************/
722    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    //************************************************************************/
737    // y = lambda * (self.x - x) - self.y;
738    //************************************************************************/
739    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  /// doubles the point; since this is called with a point not at infinity, it is guaranteed to be not infinity
757  pub fn double_incomplete<CS: ConstraintSystem<E::Base>>(
758    &self,
759    mut cs: CS,
760  ) -> Result<Self, SynthesisError> {
761    // lambda = (3 x^2 + a) / 2 * y
762
763    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  /// If condition outputs a otherwise outputs b
814  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  /// Allocate a new point from coordinates.
827  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  /// Conditional select using an AllocatedNum instead of Boolean.
842  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  /// Enforce that self equals other.
855  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/// `AllocatedNonnativePoint`s are points on an elliptic curve E'. We use the scalar field
878/// of another curve E (specified as the group G) to prove things about points on E'.
879/// `AllocatedNonnativePoint`s are always represented as affine coordinates.
880#[derive(Clone, Debug)]
881pub struct AllocatedNonnativePoint<E: Engine> {
882  /// The x-coordinate as a BigNat.
883  pub x: BigNat<E::Scalar>,
884  /// The y-coordinate as a BigNat.
885  pub y: BigNat<E::Scalar>,
886  /// Flag indicating if this is the point at infinity.
887  pub is_infinity: AllocatedNum<E::Scalar>,
888}
889
890impl<E: Engine> AllocatedNonnativePoint<E> {
891  /// Allocates a new nonnative point from coordinates.
892  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  /// Allocates a default point on the curve, set to the point at infinity.
929  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  /// Absorb the provided instance in the RO
949  ///
950  /// Note: For curves with B != 0 (e.g., Pallas, Vesta), we only absorb x and y coordinates.
951  /// For curves with B == 0 (e.g., secp256k1), we also absorb is_infinity.
952  /// This matches the native `AbsorbInRO2Trait` implementation in Pedersen.
953  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    // Only absorb is_infinity when B == 0 (matching native AbsorbInRO2Trait for Commitment)
971    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  /// If condition outputs a otherwise outputs b
980  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    /// Add any two points
1048    pub fn add(&self, other: &Point<E>) -> Self {
1049      if self.x == other.x {
1050        // If self == other then call double
1051        if self.y == other.y {
1052          self.double()
1053        } else {
1054          // if self.x == other.x and self.y != other.y then return infinity
1055          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    /// Add two different points
1067    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  // Allocate a random point. Only used for testing
1127  pub fn alloc_random_point<E: Engine, CS: ConstraintSystem<E::Base>>(
1128    mut cs: CS,
1129  ) -> Result<AllocatedPoint<E>, SynthesisError> {
1130    // get a random point
1131    let p = Point::<E>::random_vartime();
1132    AllocatedPoint::alloc(cs.namespace(|| "alloc p"), Some((p.x, p.y, p.is_infinity)))
1133  }
1134
1135  /// Make the point io
1136  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    // perform some curve arithmetic
1165    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    // perform the same computation by translating to curve types
1173    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    // transform c, d, and e into curve types
1190    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    // check that we have the same outputs
1207    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    // Allocate bits for s
1222    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    // First create the shape
1252    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    // Then the satisfying assignment
1259    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    // Make sure that this is satisfiable
1276    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    // First create the shape
1309    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    // Then the satisfying assignment
1316    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    // Make sure that it is satisfiable
1332    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    // First create the shape
1370    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    // Then the satisfying assignment
1377    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    // Make sure that it is satisfiable
1387    assert!(shape.is_sat(&ck, &inst, &witness).is_ok());
1388  }
1389}