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.1))
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}