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