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