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