arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/gadgets/ecc.rs
src/gadgets/ecc.rsRaw
  1#![allow(non_snake_case)]
  2use crate::gadgets::utils::{
  3  alloc_one, alloc_zero, conditionally_select, conditionally_select2, select_one_or, select_zero_or,
  4};
  5use bellperson::{
  6  gadgets::{
  7    boolean::{AllocatedBit, Boolean},
  8    num::AllocatedNum,
  9    Assignment,
 10  },
 11  ConstraintSystem, SynthesisError,
 12};
 13use ff::PrimeField;
 14
 15#[derive(Clone)]
 16pub struct AllocatedPoint<Fp>
 17where
 18  Fp: PrimeField,
 19{
 20  pub(crate) x: AllocatedNum<Fp>,
 21  pub(crate) y: AllocatedNum<Fp>,
 22  pub(crate) is_infinity: AllocatedNum<Fp>,
 23}
 24
 25impl<Fp> AllocatedPoint<Fp>
 26where
 27  Fp: PrimeField,
 28{
 29  pub fn alloc<CS>(mut cs: CS, coords: Option<(Fp, Fp, bool)>) -> Result<Self, SynthesisError>
 30  where
 31    CS: ConstraintSystem<Fp>,
 32  {
 33    let x = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(coords.unwrap().0))?;
 34    let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(coords.unwrap().1))?;
 35    let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || {
 36      Ok(if coords.unwrap().2 {
 37        Fp::one()
 38      } else {
 39        Fp::zero()
 40      })
 41    })?;
 42    cs.enforce(
 43      || "is_infinity is bit",
 44      |lc| lc + is_infinity.get_variable(),
 45      |lc| lc + CS::one() - is_infinity.get_variable(),
 46      |lc| lc,
 47    );
 48
 49    Ok(AllocatedPoint { x, y, is_infinity })
 50  }
 51
 52  // Creates a new allocated point from allocated nums.
 53  pub fn new(x: AllocatedNum<Fp>, y: AllocatedNum<Fp>, is_infinity: AllocatedNum<Fp>) -> Self {
 54    Self { x, y, is_infinity }
 55  }
 56
 57  // Allocate a random point. Only used for testing
 58  #[cfg(test)]
 59  pub fn random_vartime<CS: ConstraintSystem<Fp>>(mut cs: CS) -> Result<Self, SynthesisError> {
 60    loop {
 61      let x = Fp::random(&mut OsRng);
 62      let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt();
 63      if y.is_some().unwrap_u8() == 1 {
 64        let x_alloc = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x))?;
 65        let y_alloc = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y.unwrap()))?;
 66        let is_infinity = alloc_zero(cs.namespace(|| "Is Infinity"))?;
 67        return Ok(Self::new(x_alloc, y_alloc, is_infinity));
 68      }
 69    }
 70  }
 71
 72  // Make the point io
 73  #[cfg(test)]
 74  pub fn inputize<CS: ConstraintSystem<Fp>>(&self, mut cs: CS) -> Result<(), SynthesisError> {
 75    let _ = self.x.inputize(cs.namespace(|| "Input point.x"));
 76    let _ = self.y.inputize(cs.namespace(|| "Input point.y"));
 77    let _ = self
 78      .is_infinity
 79      .inputize(cs.namespace(|| "Input point.is_infinity"));
 80    Ok(())
 81  }
 82
 83  // Adds other point to this point and returns the result
 84  // Assumes that both other.is_infinity and this.is_infinty are bits
 85  pub fn add<CS: ConstraintSystem<Fp>>(
 86    &self,
 87    mut cs: CS,
 88    other: &AllocatedPoint<Fp>,
 89  ) -> Result<Self, SynthesisError> {
 90    // Allocate the boolean variables that check if either of the points is infinity
 91
 92    //************************************************************************/
 93    // lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
 94    //************************************************************************/
 95    // First compute (other.x - self.x).inverse()
 96    // If either self or other are 1 then compute bogus values
 97
 98    // x_diff = other != inf && self != inf ? (other.x - self.x) : 1
 99    let x_diff_actual = AllocatedNum::alloc(cs.namespace(|| "actual x diff"), || {
100      Ok(*other.x.get_value().get()? - *self.x.get_value().get()?)
101    })?;
102    cs.enforce(
103      || "actual x_diff is correct",
104      |lc| lc + other.x.get_variable() - self.x.get_variable(),
105      |lc| lc + CS::one(),
106      |lc| lc + x_diff_actual.get_variable(),
107    );
108
109    // Compute self.is_infinity OR other.is_infinity
110    let at_least_one_inf = AllocatedNum::alloc(cs.namespace(|| "at least one inf"), || {
111      Ok(*self.is_infinity.get_value().get()? * *other.is_infinity.get_value().get()?)
112    })?;
113    cs.enforce(
114      || "at least one inf = self.is_infinity * other.is_infinity",
115      |lc| lc + self.is_infinity.get_variable(),
116      |lc| lc + other.is_infinity.get_variable(),
117      |lc| lc + at_least_one_inf.get_variable(),
118    );
119
120    // x_diff = 1 if either self.is_infinity or other.is_infinity else x_diff_actual
121    let x_diff = select_one_or(
122      cs.namespace(|| "Compute x_diff"),
123      &x_diff_actual,
124      &at_least_one_inf,
125    )?;
126
127    let x_diff_inv = AllocatedNum::alloc(cs.namespace(|| "x diff inverse"), || {
128      if *at_least_one_inf.get_value().get()? == Fp::one() {
129        // Set to default
130        Ok(Fp::one())
131      } else {
132        // Set to the actual inverse
133        let inv = (*other.x.get_value().get()? - *self.x.get_value().get()?).invert();
134        if inv.is_some().unwrap_u8() == 1 {
135          Ok(inv.unwrap())
136        } else {
137          Err(SynthesisError::DivisionByZero)
138        }
139      }
140    })?;
141
142    cs.enforce(
143      || "Check inverse",
144      |lc| lc + x_diff.get_variable(),
145      |lc| lc + x_diff_inv.get_variable(),
146      |lc| lc + CS::one(),
147    );
148
149    let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
150      Ok(
151        (*other.y.get_value().get()? - *self.y.get_value().get()?)
152          * x_diff_inv.get_value().get()?,
153      )
154    })?;
155    cs.enforce(
156      || "Check that lambda is correct",
157      |lc| lc + other.y.get_variable() - self.y.get_variable(),
158      |lc| lc + x_diff_inv.get_variable(),
159      |lc| lc + lambda.get_variable(),
160    );
161
162    //************************************************************************/
163    // x = lambda * lambda - self.x - other.x;
164    //************************************************************************/
165    let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
166      Ok(
167        *lambda.get_value().get()? * lambda.get_value().get()?
168          - *self.x.get_value().get()?
169          - *other.x.get_value().get()?,
170      )
171    })?;
172    cs.enforce(
173      || "check that x is correct",
174      |lc| lc + lambda.get_variable(),
175      |lc| lc + lambda.get_variable(),
176      |lc| lc + x.get_variable() + self.x.get_variable() + other.x.get_variable(),
177    );
178
179    //************************************************************************/
180    // y = lambda * (self.x - x) - self.y;
181    //************************************************************************/
182    let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
183      Ok(
184        *lambda.get_value().get()? * (*self.x.get_value().get()? - *x.get_value().get()?)
185          - *self.y.get_value().get()?,
186      )
187    })?;
188
189    cs.enforce(
190      || "Check that y is correct",
191      |lc| lc + lambda.get_variable(),
192      |lc| lc + self.x.get_variable() - x.get_variable(),
193      |lc| lc + y.get_variable() + self.y.get_variable(),
194    );
195
196    let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is infinity"), || Ok(Fp::zero()))?;
197
198    //************************************************************************/
199    // We only return the computed x, y if neither of the points is infinity.
200    // if self.is_infinity return other.clone()
201    // elif other.is_infinity return self.clone()
202    // Otherwise return the computed points.
203    //************************************************************************/
204    // Now compute the output x
205    let inner_x = conditionally_select2(
206      cs.namespace(|| "final x: inner if"),
207      &self.x,
208      &x,
209      &other.is_infinity,
210    )?;
211    let final_x = conditionally_select2(
212      cs.namespace(|| "final x: outer if"),
213      &other.x,
214      &inner_x,
215      &self.is_infinity,
216    )?;
217
218    // The output y
219    let inner_y = conditionally_select2(
220      cs.namespace(|| "final y: inner if"),
221      &self.y,
222      &y,
223      &other.is_infinity,
224    )?;
225    let final_y = conditionally_select2(
226      cs.namespace(|| "final y: outer if"),
227      &other.y,
228      &inner_y,
229      &self.is_infinity,
230    )?;
231
232    // The output is_infinity
233    let inner_is_infinity = conditionally_select2(
234      cs.namespace(|| "final is infinity: inner if"),
235      &self.is_infinity,
236      &is_infinity,
237      &other.is_infinity,
238    )?;
239    let final_is_infinity = conditionally_select2(
240      cs.namespace(|| "final is infinity: outer if"),
241      &other.is_infinity,
242      &inner_is_infinity,
243      &self.is_infinity,
244    )?;
245    Ok(Self::new(final_x, final_y, final_is_infinity))
246  }
247
248  pub fn double<CS: ConstraintSystem<Fp>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
249    //*************************************************************/
250    // lambda = (Fp::one() + Fp::one() + Fp::one())
251    //  * self.x
252    //  * self.x
253    //  * ((Fp::one() + Fp::one()) * self.y).invert().unwrap();
254    /*************************************************************/
255
256    // Compute tmp = (Fp::one() + Fp::one())* self.y ? self != inf : 1
257    let tmp_actual = AllocatedNum::alloc(cs.namespace(|| "tmp_actual"), || {
258      Ok(*self.y.get_value().get()? + *self.y.get_value().get()?)
259    })?;
260    cs.enforce(
261      || "check tmp_actual",
262      |lc| lc + CS::one() + CS::one(),
263      |lc| lc + self.y.get_variable(),
264      |lc| lc + tmp_actual.get_variable(),
265    );
266
267    let tmp = select_one_or(cs.namespace(|| "tmp"), &tmp_actual, &self.is_infinity)?;
268
269    // Compute inv = tmp.invert
270    let tmp_inv = AllocatedNum::alloc(cs.namespace(|| "tmp inverse"), || {
271      if *self.is_infinity.get_value().get()? == Fp::one() {
272        // Return default value 1
273        Ok(Fp::one())
274      } else {
275        // Return the actual inverse
276        let inv = (*tmp.get_value().get()?).invert();
277        if inv.is_some().unwrap_u8() == 1 {
278          Ok(inv.unwrap())
279        } else {
280          Err(SynthesisError::DivisionByZero)
281        }
282      }
283    })?;
284    cs.enforce(
285      || "Check inverse",
286      |lc| lc + tmp.get_variable(),
287      |lc| lc + tmp_inv.get_variable(),
288      |lc| lc + CS::one(),
289    );
290
291    // Now compute lambda as (Fp::one() + Fp::one + Fp::one()) * self.x * self.x * tmp_inv
292    let prod_1 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 1"), || {
293      Ok(*tmp_inv.get_value().get()? * self.x.get_value().get()?)
294    })?;
295    cs.enforce(
296      || "Check prod 1",
297      |lc| lc + self.x.get_variable(),
298      |lc| lc + tmp_inv.get_variable(),
299      |lc| lc + prod_1.get_variable(),
300    );
301
302    let prod_2 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 2"), || {
303      Ok(*prod_1.get_value().get()? * self.x.get_value().get()?)
304    })?;
305    cs.enforce(
306      || "Check prod 2",
307      |lc| lc + self.x.get_variable(),
308      |lc| lc + prod_1.get_variable(),
309      |lc| lc + prod_2.get_variable(),
310    );
311
312    let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
313      Ok(*prod_2.get_value().get()? * (Fp::one() + Fp::one() + Fp::one()))
314    })?;
315    cs.enforce(
316      || "Check lambda",
317      |lc| lc + CS::one() + CS::one() + CS::one(),
318      |lc| lc + prod_2.get_variable(),
319      |lc| lc + lambda.get_variable(),
320    );
321
322    /*************************************************************/
323    //          x = lambda * lambda - self.x - self.x;
324    /*************************************************************/
325
326    let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
327      Ok(
328        ((*lambda.get_value().get()?) * (*lambda.get_value().get()?))
329          - *self.x.get_value().get()?
330          - self.x.get_value().get()?,
331      )
332    })?;
333    cs.enforce(
334      || "Check x",
335      |lc| lc + lambda.get_variable(),
336      |lc| lc + lambda.get_variable(),
337      |lc| lc + x.get_variable() + self.x.get_variable() + self.x.get_variable(),
338    );
339
340    /*************************************************************/
341    //        y = lambda * (self.x - x) - self.y;
342    /*************************************************************/
343
344    let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
345      Ok(
346        (*lambda.get_value().get()?) * (*self.x.get_value().get()? - x.get_value().get()?)
347          - self.y.get_value().get()?,
348      )
349    })?;
350    cs.enforce(
351      || "Check y",
352      |lc| lc + lambda.get_variable(),
353      |lc| lc + self.x.get_variable() - x.get_variable(),
354      |lc| lc + y.get_variable() + self.y.get_variable(),
355    );
356
357    /*************************************************************/
358    // Only return the computed x and y if the point is not infinity
359    /*************************************************************/
360
361    // x
362    let final_x = select_zero_or(cs.namespace(|| "final x"), &x, &self.is_infinity)?;
363
364    // y
365    let final_y = select_zero_or(cs.namespace(|| "final y"), &y, &self.is_infinity)?;
366
367    // is_infinity
368    let final_is_infinity = self.is_infinity.clone();
369
370    Ok(Self::new(final_x, final_y, final_is_infinity))
371  }
372
373  pub fn scalar_mul<CS: ConstraintSystem<Fp>>(
374    &self,
375    mut cs: CS,
376    scalar: Vec<AllocatedBit>,
377  ) -> Result<Self, SynthesisError> {
378    /*************************************************************/
379    // Initialize res = Self {
380    //  x: Fp::zero(),
381    //  y: Fp::zero(),
382    //  is_infinity: true,
383    //  _p: Default::default(),
384    //};
385    /*************************************************************/
386
387    let zero = alloc_zero(cs.namespace(|| "Allocate zero"))?;
388    let one = alloc_one(cs.namespace(|| "Allocate one"))?;
389    let mut res = Self::new(zero.clone(), zero, one);
390
391    for i in (0..scalar.len()).rev() {
392      /*************************************************************/
393      //  res = res.double();
394      /*************************************************************/
395
396      res = res.double(cs.namespace(|| format!("{}: double", i)))?;
397
398      /*************************************************************/
399      //  if scalar[i] {
400      //    res = self.add(&res);
401      //  }
402      /*************************************************************/
403
404      let self_and_res = self.add(cs.namespace(|| format!("{}: add", i)), &res)?;
405      res = Self::conditionally_select(
406        cs.namespace(|| format!("{}: Update res", i)),
407        &self_and_res,
408        &res,
409        &Boolean::from(scalar[i].clone()),
410      )?;
411    }
412    Ok(res)
413  }
414
415  /// If condition outputs a otherwise outputs b
416  pub fn conditionally_select<CS: ConstraintSystem<Fp>>(
417    mut cs: CS,
418    a: &Self,
419    b: &Self,
420    condition: &Boolean,
421  ) -> Result<Self, SynthesisError> {
422    let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?;
423
424    let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?;
425
426    let is_infinity = conditionally_select(
427      cs.namespace(|| "select is_infinity"),
428      &a.is_infinity,
429      &b.is_infinity,
430      condition,
431    )?;
432
433    Ok(Self::new(x, y, is_infinity))
434  }
435}
436
437#[cfg(test)]
438use ff::PrimeFieldBits;
439#[cfg(test)]
440use rand::rngs::OsRng;
441#[cfg(test)]
442use std::marker::PhantomData;
443
444#[cfg(test)]
445#[derive(Debug, Clone)]
446pub struct Point<Fp, Fq>
447where
448  Fp: PrimeField,
449  Fq: PrimeField + PrimeFieldBits,
450{
451  x: Fp,
452  y: Fp,
453  is_infinity: bool,
454  _p: PhantomData<Fq>,
455}
456
457#[cfg(test)]
458impl<Fp, Fq> Point<Fp, Fq>
459where
460  Fp: PrimeField,
461  Fq: PrimeField + PrimeFieldBits,
462{
463  pub fn new(x: Fp, y: Fp, is_infinity: bool) -> Self {
464    Self {
465      x,
466      y,
467      is_infinity,
468      _p: Default::default(),
469    }
470  }
471
472  pub fn random_vartime() -> Self {
473    loop {
474      let x = Fp::random(&mut OsRng);
475      let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt();
476      if y.is_some().unwrap_u8() == 1 {
477        return Self {
478          x,
479          y: y.unwrap(),
480          is_infinity: false,
481          _p: Default::default(),
482        };
483      }
484    }
485  }
486
487  pub fn add(&self, other: &Point<Fp, Fq>) -> Self {
488    if self.is_infinity {
489      return other.clone();
490    }
491
492    if other.is_infinity {
493      return self.clone();
494    }
495
496    let lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
497    let x = lambda * lambda - self.x - other.x;
498    let y = lambda * (self.x - x) - self.y;
499    Self {
500      x,
501      y,
502      is_infinity: false,
503      _p: Default::default(),
504    }
505  }
506
507  pub fn double(&self) -> Self {
508    if self.is_infinity {
509      return Self {
510        x: Fp::zero(),
511        y: Fp::zero(),
512        is_infinity: true,
513        _p: Default::default(),
514      };
515    }
516
517    let lambda = (Fp::one() + Fp::one() + Fp::one())
518      * self.x
519      * self.x
520      * ((Fp::one() + Fp::one()) * self.y).invert().unwrap();
521    let x = lambda * lambda - self.x - self.x;
522    let y = lambda * (self.x - x) - self.y;
523    Self {
524      x,
525      y,
526      is_infinity: false,
527      _p: Default::default(),
528    }
529  }
530
531  pub fn scalar_mul(&self, scalar: &Fq) -> Self {
532    let mut res = Self {
533      x: Fp::zero(),
534      y: Fp::zero(),
535      is_infinity: true,
536      _p: Default::default(),
537    };
538
539    let bits = scalar.to_le_bits();
540    for i in (0..bits.len()).rev() {
541      res = res.double();
542      if bits[i] {
543        res = self.add(&res);
544      }
545    }
546    res
547  }
548}
549
550#[cfg(test)]
551#[allow(clippy::too_many_arguments)]
552mod fp {
553  use ff::PrimeField;
554
555  #[derive(PrimeField)]
556  #[PrimeFieldModulus = "28948022309329048855892746252171976963363056481941560715954676764349967630337"]
557  #[PrimeFieldGenerator = "5"]
558  #[PrimeFieldReprEndianness = "little"]
559  pub struct Fp([u64; 4]);
560}
561
562#[cfg(test)]
563#[allow(clippy::too_many_arguments)]
564mod fq {
565  use ff::PrimeField;
566
567  #[derive(PrimeField)]
568  #[PrimeFieldModulus = "28948022309329048855892746252171976963363056481941647379679742748393362948097"]
569  #[PrimeFieldGenerator = "5"]
570  #[PrimeFieldReprEndianness = "little"]
571  pub struct Fq([u64; 4]);
572}
573
574#[cfg(test)]
575mod tests {
576  use super::*;
577
578  #[test]
579  fn test_ecc_ops() {
580    use super::{fp::Fp, fq::Fq};
581
582    // perform some curve arithmetic
583    let a = Point::<Fp, Fq>::random_vartime();
584    let b = Point::<Fp, Fq>::random_vartime();
585    let c = a.add(&b);
586    let d = a.double();
587    let s = Fq::random(&mut OsRng);
588    let e = a.scalar_mul(&s);
589
590    // perform the same computation by translating to pasta_curve types
591    let a_pasta = EpAffine::from_xy(
592      pasta_curves::Fp::from_repr(a.x.to_repr().0).unwrap(),
593      pasta_curves::Fp::from_repr(a.y.to_repr().0).unwrap(),
594    )
595    .unwrap();
596    let b_pasta = EpAffine::from_xy(
597      pasta_curves::Fp::from_repr(b.x.to_repr().0).unwrap(),
598      pasta_curves::Fp::from_repr(b.y.to_repr().0).unwrap(),
599    )
600    .unwrap();
601    let c_pasta = (a_pasta + b_pasta).to_affine();
602    let d_pasta = (a_pasta + a_pasta).to_affine();
603    let e_pasta = a_pasta
604      .mul(pasta_curves::Fq::from_repr(s.to_repr().0).unwrap())
605      .to_affine();
606
607    // transform c, d, and e into pasta_curve types
608    let c_pasta_2 = EpAffine::from_xy(
609      pasta_curves::Fp::from_repr(c.x.to_repr().0).unwrap(),
610      pasta_curves::Fp::from_repr(c.y.to_repr().0).unwrap(),
611    )
612    .unwrap();
613    let d_pasta_2 = EpAffine::from_xy(
614      pasta_curves::Fp::from_repr(d.x.to_repr().0).unwrap(),
615      pasta_curves::Fp::from_repr(d.y.to_repr().0).unwrap(),
616    )
617    .unwrap();
618    let e_pasta_2 = EpAffine::from_xy(
619      pasta_curves::Fp::from_repr(e.x.to_repr().0).unwrap(),
620      pasta_curves::Fp::from_repr(e.y.to_repr().0).unwrap(),
621    )
622    .unwrap();
623
624    // check that we have the same outputs
625    assert_eq!(c_pasta, c_pasta_2);
626    assert_eq!(d_pasta, d_pasta_2);
627    assert_eq!(e_pasta, e_pasta_2);
628  }
629
630  use crate::bellperson::{shape_cs::ShapeCS, solver::SatisfyingAssignment};
631  use ff::{Field, PrimeFieldBits};
632  use pasta_curves::{arithmetic::CurveAffine, group::Curve, EpAffine};
633  use std::ops::Mul;
634  type G = pasta_curves::pallas::Point;
635  type Fp = pasta_curves::pallas::Scalar;
636  type Fq = pasta_curves::vesta::Scalar;
637  use crate::bellperson::r1cs::{NovaShape, NovaWitness};
638
639  fn synthesize_smul<Fp, Fq, CS>(mut cs: CS) -> (AllocatedPoint<Fp>, AllocatedPoint<Fp>, Fq)
640  where
641    Fp: PrimeField,
642    Fq: PrimeField + PrimeFieldBits,
643    CS: ConstraintSystem<Fp>,
644  {
645    let a = AllocatedPoint::<Fp>::random_vartime(cs.namespace(|| "a")).unwrap();
646    let _ = a.inputize(cs.namespace(|| "inputize a")).unwrap();
647    let s = Fq::random(&mut OsRng);
648    // Allocate random bits and only keep 128 bits
649    let bits: Vec<AllocatedBit> = s
650      .to_le_bits()
651      .into_iter()
652      .enumerate()
653      .map(|(i, bit)| AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(bit)))
654      .collect::<Result<Vec<AllocatedBit>, SynthesisError>>()
655      .unwrap();
656    let e = a.scalar_mul(cs.namespace(|| "Scalar Mul"), bits).unwrap();
657    let _ = e.inputize(cs.namespace(|| "inputize e")).unwrap();
658    (a, e, s)
659  }
660
661  #[test]
662  fn test_ecc_circuit_ops() {
663    // First create the shape
664    let mut cs: ShapeCS<G> = ShapeCS::new();
665    let _ = synthesize_smul::<Fp, Fq, _>(cs.namespace(|| "synthesize"));
666    println!("Number of constraints: {}", cs.num_constraints());
667    let shape = cs.r1cs_shape();
668    let gens = cs.r1cs_gens();
669
670    // Then the satisfying assignment
671    let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
672    let (a, e, s) = synthesize_smul::<Fp, Fq, _>(cs.namespace(|| "synthesize"));
673    let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
674
675    let a_p: Point<Fp, Fq> = Point::new(
676      a.x.get_value().unwrap(),
677      a.y.get_value().unwrap(),
678      a.is_infinity.get_value().unwrap() == Fp::one(),
679    );
680    let e_p: Point<Fp, Fq> = Point::new(
681      e.x.get_value().unwrap(),
682      e.y.get_value().unwrap(),
683      e.is_infinity.get_value().unwrap() == Fp::one(),
684    );
685    let e_new = a_p.scalar_mul(&s);
686    assert!(e_p.x == e_new.x && e_p.y == e_new.y);
687    // Make sure that this is satisfiable
688    assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
689  }
690}