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}