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