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