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_num_equals, alloc_one, alloc_zero, conditionally_select, conditionally_select2,
5 select_num_or_one, select_num_or_zero, select_num_or_zero2, select_one_or_diff2,
6 select_one_or_num2, select_zero_or_num2,
7};
8use bellperson::{
9 gadgets::{
10 boolean::{AllocatedBit, Boolean},
11 num::AllocatedNum,
12 Assignment,
13 },
14 ConstraintSystem, SynthesisError,
15};
16use ff::PrimeField;
17
18/// AllocatedPoint provides an elliptic curve abstraction inside a circuit.
19#[derive(Clone)]
20pub struct AllocatedPoint<Fp>
21where
22 Fp: PrimeField,
23{
24 pub(crate) x: AllocatedNum<Fp>,
25 pub(crate) y: AllocatedNum<Fp>,
26 pub(crate) is_infinity: AllocatedNum<Fp>,
27}
28
29impl<Fp> AllocatedPoint<Fp>
30where
31 Fp: PrimeField,
32{
33 /// Allocates a new point on the curve using coordinates provided by `coords`.
34 /// If coords = None, it allocates the default infinity point
35 pub fn alloc<CS>(mut cs: CS, coords: Option<(Fp, Fp, bool)>) -> Result<Self, SynthesisError>
36 where
37 CS: ConstraintSystem<Fp>,
38 {
39 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
40 Ok(coords.map_or(Fp::zero(), |c| c.0))
41 })?;
42 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
43 Ok(coords.map_or(Fp::zero(), |c| c.1))
44 })?;
45 let is_infinity = AllocatedNum::alloc(cs.namespace(|| "is_infinity"), || {
46 Ok(if coords.map_or(true, |c| c.2) {
47 Fp::one()
48 } else {
49 Fp::zero()
50 })
51 })?;
52 cs.enforce(
53 || "is_infinity is bit",
54 |lc| lc + is_infinity.get_variable(),
55 |lc| lc + CS::one() - is_infinity.get_variable(),
56 |lc| lc,
57 );
58
59 Ok(AllocatedPoint { x, y, is_infinity })
60 }
61
62 /// Allocates a default point on the curve.
63 pub fn default<CS>(mut cs: CS) -> Result<Self, SynthesisError>
64 where
65 CS: ConstraintSystem<Fp>,
66 {
67 let zero = alloc_zero(cs.namespace(|| "zero"))?;
68 let one = alloc_one(cs.namespace(|| "one"))?;
69
70 Ok(AllocatedPoint {
71 x: zero.clone(),
72 y: zero,
73 is_infinity: one,
74 })
75 }
76
77 /// Returns coordinates associated with the point.
78 pub fn get_coordinates(&self) -> (&AllocatedNum<Fp>, &AllocatedNum<Fp>, &AllocatedNum<Fp>) {
79 (&self.x, &self.y, &self.is_infinity)
80 }
81
82 // Allocate a random point. Only used for testing
83 #[cfg(test)]
84 pub fn random_vartime<CS: ConstraintSystem<Fp>>(mut cs: CS) -> Result<Self, SynthesisError> {
85 loop {
86 let x = Fp::random(&mut OsRng);
87 let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt();
88 if y.is_some().unwrap_u8() == 1 {
89 let x_alloc = AllocatedNum::alloc(cs.namespace(|| "x"), || Ok(x))?;
90 let y_alloc = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(y.unwrap()))?;
91 let is_infinity = alloc_zero(cs.namespace(|| "Is Infinity"))?;
92 return Ok(Self {
93 x: x_alloc,
94 y: y_alloc,
95 is_infinity,
96 });
97 }
98 }
99 }
100
101 /// Make the point io
102 #[cfg(test)]
103 pub fn inputize<CS: ConstraintSystem<Fp>>(&self, mut cs: CS) -> Result<(), SynthesisError> {
104 let _ = self.x.inputize(cs.namespace(|| "Input point.x"));
105 let _ = self.y.inputize(cs.namespace(|| "Input point.y"));
106 let _ = self
107 .is_infinity
108 .inputize(cs.namespace(|| "Input point.is_infinity"));
109 Ok(())
110 }
111
112 /// Add two points (may be equal)
113 pub fn add<CS: ConstraintSystem<Fp>>(
114 &self,
115 mut cs: CS,
116 other: &AllocatedPoint<Fp>,
117 ) -> Result<Self, SynthesisError> {
118 // Compute boolean equal indicating if self = other
119
120 let equal_x = alloc_num_equals(
121 cs.namespace(|| "check self.x == other.x"),
122 &self.x,
123 &other.x,
124 )?;
125
126 let equal_y = alloc_num_equals(
127 cs.namespace(|| "check self.y == other.y"),
128 &self.y,
129 &other.y,
130 )?;
131
132 // Compute the result of the addition and the result of double self
133 let result_from_add = self.add_internal(cs.namespace(|| "add internal"), other, &equal_x)?;
134 let result_from_double = self.double(cs.namespace(|| "double"))?;
135
136 // Output:
137 // If (self == other) {
138 // return double(self)
139 // }else {
140 // if (self.x == other.x){
141 // return infinity [negation]
142 // } else {
143 // return add(self, other)
144 // }
145 // }
146 let result_for_equal_x = AllocatedPoint::select_point_or_infinity(
147 cs.namespace(|| "equal_y ? result_from_double : infinity"),
148 &result_from_double,
149 &Boolean::from(equal_y),
150 )?;
151
152 AllocatedPoint::conditionally_select(
153 cs.namespace(|| "equal ? result_from_double : result_from_add"),
154 &result_for_equal_x,
155 &result_from_add,
156 &Boolean::from(equal_x),
157 )
158 }
159
160 /// Adds other point to this point and returns the result. Assumes that the two points are
161 /// different and that both other.is_infinity and this.is_infinty are bits
162 pub fn add_internal<CS: ConstraintSystem<Fp>>(
163 &self,
164 mut cs: CS,
165 other: &AllocatedPoint<Fp>,
166 equal_x: &AllocatedBit,
167 ) -> Result<Self, SynthesisError> {
168 //************************************************************************/
169 // lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
170 //************************************************************************/
171 // First compute (other.x - self.x).inverse()
172 // If either self or other are the infinity point or self.x = other.x then compute bogus values
173 // Specifically,
174 // x_diff = self != inf && other != inf && self.x == other.x ? (other.x - self.x) : 1
175
176 // Compute self.is_infinity OR other.is_infinity =
177 // NOT(NOT(self.is_ifninity) AND NOT(other.is_infinity))
178 let at_least_one_inf = AllocatedNum::alloc(cs.namespace(|| "at least one inf"), || {
179 Ok(
180 Fp::one()
181 - (Fp::one() - *self.is_infinity.get_value().get()?)
182 * (Fp::one() - *other.is_infinity.get_value().get()?),
183 )
184 })?;
185 cs.enforce(
186 || "1 - at least one inf = (1-self.is_infinity) * (1-other.is_infinity)",
187 |lc| lc + CS::one() - self.is_infinity.get_variable(),
188 |lc| lc + CS::one() - other.is_infinity.get_variable(),
189 |lc| lc + CS::one() - at_least_one_inf.get_variable(),
190 );
191
192 // Now compute x_diff_is_actual = at_least_one_inf OR equal_x
193 let x_diff_is_actual =
194 AllocatedNum::alloc(cs.namespace(|| "allocate x_diff_is_actual"), || {
195 Ok(if *equal_x.get_value().get()? {
196 Fp::one()
197 } else {
198 *at_least_one_inf.get_value().get()?
199 })
200 })?;
201 cs.enforce(
202 || "1 - x_diff_is_actual = (1-equal_x) * (1-at_least_one_inf)",
203 |lc| lc + CS::one() - at_least_one_inf.get_variable(),
204 |lc| lc + CS::one() - equal_x.get_variable(),
205 |lc| lc + CS::one() - x_diff_is_actual.get_variable(),
206 );
207
208 // x_diff = 1 if either self.is_infinity or other.is_infinity or self.x = other.x else self.x -
209 // other.x
210 let x_diff = select_one_or_diff2(
211 cs.namespace(|| "Compute x_diff"),
212 &other.x,
213 &self.x,
214 &x_diff_is_actual,
215 )?;
216
217 let x_diff_inv = AllocatedNum::alloc(cs.namespace(|| "x diff inverse"), || {
218 if *x_diff_is_actual.get_value().get()? == Fp::one() {
219 // Set to default
220 Ok(Fp::one())
221 } else {
222 // Set to the actual inverse
223 let inv = (*other.x.get_value().get()? - *self.x.get_value().get()?).invert();
224 if inv.is_some().unwrap_u8() == 1 {
225 Ok(inv.unwrap())
226 } else {
227 Err(SynthesisError::DivisionByZero)
228 }
229 }
230 })?;
231
232 cs.enforce(
233 || "Check inverse",
234 |lc| lc + x_diff.get_variable(),
235 |lc| lc + x_diff_inv.get_variable(),
236 |lc| lc + CS::one(),
237 );
238
239 let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
240 Ok(
241 (*other.y.get_value().get()? - *self.y.get_value().get()?)
242 * x_diff_inv.get_value().get()?,
243 )
244 })?;
245 cs.enforce(
246 || "Check that lambda is correct",
247 |lc| lc + other.y.get_variable() - self.y.get_variable(),
248 |lc| lc + x_diff_inv.get_variable(),
249 |lc| lc + lambda.get_variable(),
250 );
251
252 //************************************************************************/
253 // x = lambda * lambda - self.x - other.x;
254 //************************************************************************/
255 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
256 Ok(
257 *lambda.get_value().get()? * lambda.get_value().get()?
258 - *self.x.get_value().get()?
259 - *other.x.get_value().get()?,
260 )
261 })?;
262 cs.enforce(
263 || "check that x is correct",
264 |lc| lc + lambda.get_variable(),
265 |lc| lc + lambda.get_variable(),
266 |lc| lc + x.get_variable() + self.x.get_variable() + other.x.get_variable(),
267 );
268
269 //************************************************************************/
270 // y = lambda * (self.x - x) - self.y;
271 //************************************************************************/
272 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
273 Ok(
274 *lambda.get_value().get()? * (*self.x.get_value().get()? - *x.get_value().get()?)
275 - *self.y.get_value().get()?,
276 )
277 })?;
278
279 cs.enforce(
280 || "Check that y is correct",
281 |lc| lc + lambda.get_variable(),
282 |lc| lc + self.x.get_variable() - x.get_variable(),
283 |lc| lc + y.get_variable() + self.y.get_variable(),
284 );
285
286 //************************************************************************/
287 // We only return the computed x, y if neither of the points is infinity and self.x != other.y
288 // if self.is_infinity return other.clone()
289 // elif other.is_infinity return self.clone()
290 // elif self.x == other.x return infinity
291 // Otherwise return the computed points.
292 //************************************************************************/
293 // Now compute the output x
294
295 let x1 = conditionally_select2(
296 cs.namespace(|| "x1 = other.is_infinity ? self.x : x"),
297 &self.x,
298 &x,
299 &other.is_infinity,
300 )?;
301
302 let x = conditionally_select2(
303 cs.namespace(|| "x = self.is_infinity ? other.x : x1"),
304 &other.x,
305 &x1,
306 &self.is_infinity,
307 )?;
308
309 let y1 = conditionally_select2(
310 cs.namespace(|| "y1 = other.is_infinity ? self.y : y"),
311 &self.y,
312 &y,
313 &other.is_infinity,
314 )?;
315
316 let y = conditionally_select2(
317 cs.namespace(|| "y = self.is_infinity ? other.y : y1"),
318 &other.y,
319 &y1,
320 &self.is_infinity,
321 )?;
322
323 let is_infinity1 = select_num_or_zero2(
324 cs.namespace(|| "is_infinity1 = other.is_infinity ? self.is_infinity : 0"),
325 &self.is_infinity,
326 &other.is_infinity,
327 )?;
328
329 let is_infinity = conditionally_select2(
330 cs.namespace(|| "is_infinity = self.is_infinity ? other.is_infinity : is_infinity1"),
331 &other.is_infinity,
332 &is_infinity1,
333 &self.is_infinity,
334 )?;
335
336 Ok(Self { x, y, is_infinity })
337 }
338
339 /// Doubles the supplied point.
340 pub fn double<CS: ConstraintSystem<Fp>>(&self, mut cs: CS) -> Result<Self, SynthesisError> {
341 //*************************************************************/
342 // lambda = (Fp::one() + Fp::one() + Fp::one())
343 // * self.x
344 // * self.x
345 // * ((Fp::one() + Fp::one()) * self.y).invert().unwrap();
346 /*************************************************************/
347
348 // Compute tmp = (Fp::one() + Fp::one())* self.y ? self != inf : 1
349 let tmp_actual = AllocatedNum::alloc(cs.namespace(|| "tmp_actual"), || {
350 Ok(*self.y.get_value().get()? + *self.y.get_value().get()?)
351 })?;
352 cs.enforce(
353 || "check tmp_actual",
354 |lc| lc + CS::one() + CS::one(),
355 |lc| lc + self.y.get_variable(),
356 |lc| lc + tmp_actual.get_variable(),
357 );
358
359 let tmp = select_one_or_num2(cs.namespace(|| "tmp"), &tmp_actual, &self.is_infinity)?;
360
361 // Compute inv = tmp.invert
362 let tmp_inv = AllocatedNum::alloc(cs.namespace(|| "tmp inverse"), || {
363 if *self.is_infinity.get_value().get()? == Fp::one() {
364 // Return default value 1
365 Ok(Fp::one())
366 } else {
367 // Return the actual inverse
368 let inv = (*tmp.get_value().get()?).invert();
369 if inv.is_some().unwrap_u8() == 1 {
370 Ok(inv.unwrap())
371 } else {
372 Err(SynthesisError::DivisionByZero)
373 }
374 }
375 })?;
376 cs.enforce(
377 || "Check inverse",
378 |lc| lc + tmp.get_variable(),
379 |lc| lc + tmp_inv.get_variable(),
380 |lc| lc + CS::one(),
381 );
382
383 // Now compute lambda as (Fp::one() + Fp::one + Fp::one()) * self.x * self.x * tmp_inv
384 let prod_1 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 1"), || {
385 Ok(*tmp_inv.get_value().get()? * self.x.get_value().get()?)
386 })?;
387 cs.enforce(
388 || "Check prod 1",
389 |lc| lc + self.x.get_variable(),
390 |lc| lc + tmp_inv.get_variable(),
391 |lc| lc + prod_1.get_variable(),
392 );
393
394 let prod_2 = AllocatedNum::alloc(cs.namespace(|| "alloc prod 2"), || {
395 Ok(*prod_1.get_value().get()? * self.x.get_value().get()?)
396 })?;
397 cs.enforce(
398 || "Check prod 2",
399 |lc| lc + self.x.get_variable(),
400 |lc| lc + prod_1.get_variable(),
401 |lc| lc + prod_2.get_variable(),
402 );
403
404 let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || {
405 Ok(*prod_2.get_value().get()? * (Fp::one() + Fp::one() + Fp::one()))
406 })?;
407 cs.enforce(
408 || "Check lambda",
409 |lc| lc + CS::one() + CS::one() + CS::one(),
410 |lc| lc + prod_2.get_variable(),
411 |lc| lc + lambda.get_variable(),
412 );
413
414 /*************************************************************/
415 // x = lambda * lambda - self.x - self.x;
416 /*************************************************************/
417
418 let x = AllocatedNum::alloc(cs.namespace(|| "x"), || {
419 Ok(
420 ((*lambda.get_value().get()?) * (*lambda.get_value().get()?))
421 - *self.x.get_value().get()?
422 - self.x.get_value().get()?,
423 )
424 })?;
425 cs.enforce(
426 || "Check x",
427 |lc| lc + lambda.get_variable(),
428 |lc| lc + lambda.get_variable(),
429 |lc| lc + x.get_variable() + self.x.get_variable() + self.x.get_variable(),
430 );
431
432 /*************************************************************/
433 // y = lambda * (self.x - x) - self.y;
434 /*************************************************************/
435
436 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
437 Ok(
438 (*lambda.get_value().get()?) * (*self.x.get_value().get()? - x.get_value().get()?)
439 - self.y.get_value().get()?,
440 )
441 })?;
442 cs.enforce(
443 || "Check y",
444 |lc| lc + lambda.get_variable(),
445 |lc| lc + self.x.get_variable() - x.get_variable(),
446 |lc| lc + y.get_variable() + self.y.get_variable(),
447 );
448
449 /*************************************************************/
450 // Only return the computed x and y if the point is not infinity
451 /*************************************************************/
452
453 // x
454 let x = select_zero_or_num2(cs.namespace(|| "final x"), &x, &self.is_infinity)?;
455
456 // y
457 let y = select_zero_or_num2(cs.namespace(|| "final y"), &y, &self.is_infinity)?;
458
459 // is_infinity
460 let is_infinity = self.is_infinity.clone();
461
462 Ok(Self { x, y, is_infinity })
463 }
464
465 /// A gadget for scalar multiplication.
466 pub fn scalar_mul<CS: ConstraintSystem<Fp>>(
467 &self,
468 mut cs: CS,
469 scalar: Vec<AllocatedBit>,
470 ) -> Result<Self, SynthesisError> {
471 let mut res = Self::default(cs.namespace(|| "res"))?;
472 for i in (0..scalar.len()).rev() {
473 /*************************************************************/
474 // res = res.double();
475 /*************************************************************/
476
477 res = res.double(cs.namespace(|| format!("{}: double", i)))?;
478
479 /*************************************************************/
480 // if scalar[i] {
481 // res = self.add(&res);
482 // }
483 /*************************************************************/
484 let self_and_res = self.add(cs.namespace(|| format!("{}: add", i)), &res)?;
485 res = Self::conditionally_select(
486 cs.namespace(|| format!("{}: Update res", i)),
487 &self_and_res,
488 &res,
489 &Boolean::from(scalar[i].clone()),
490 )?;
491 }
492 Ok(res)
493 }
494
495 /// If condition outputs a otherwise outputs b
496 pub fn conditionally_select<CS: ConstraintSystem<Fp>>(
497 mut cs: CS,
498 a: &Self,
499 b: &Self,
500 condition: &Boolean,
501 ) -> Result<Self, SynthesisError> {
502 let x = conditionally_select(cs.namespace(|| "select x"), &a.x, &b.x, condition)?;
503
504 let y = conditionally_select(cs.namespace(|| "select y"), &a.y, &b.y, condition)?;
505
506 let is_infinity = conditionally_select(
507 cs.namespace(|| "select is_infinity"),
508 &a.is_infinity,
509 &b.is_infinity,
510 condition,
511 )?;
512
513 Ok(Self { x, y, is_infinity })
514 }
515
516 /// If condition outputs a otherwise infinity
517 pub fn select_point_or_infinity<CS: ConstraintSystem<Fp>>(
518 mut cs: CS,
519 a: &Self,
520 condition: &Boolean,
521 ) -> Result<Self, SynthesisError> {
522 let x = select_num_or_zero(cs.namespace(|| "select x"), &a.x, condition)?;
523
524 let y = select_num_or_zero(cs.namespace(|| "select y"), &a.y, condition)?;
525
526 let is_infinity = select_num_or_one(
527 cs.namespace(|| "select is_infinity"),
528 &a.is_infinity,
529 condition,
530 )?;
531
532 Ok(Self { x, y, is_infinity })
533 }
534}
535
536#[cfg(test)]
537use ff::PrimeFieldBits;
538#[cfg(test)]
539use rand::rngs::OsRng;
540#[cfg(test)]
541use std::marker::PhantomData;
542
543#[cfg(test)]
544#[derive(Debug, Clone)]
545pub struct Point<Fp, Fq>
546where
547 Fp: PrimeField,
548 Fq: PrimeField + PrimeFieldBits,
549{
550 x: Fp,
551 y: Fp,
552 is_infinity: bool,
553 _p: PhantomData<Fq>,
554}
555
556#[cfg(test)]
557impl<Fp, Fq> Point<Fp, Fq>
558where
559 Fp: PrimeField,
560 Fq: PrimeField + PrimeFieldBits,
561{
562 pub fn new(x: Fp, y: Fp, is_infinity: bool) -> Self {
563 Self {
564 x,
565 y,
566 is_infinity,
567 _p: Default::default(),
568 }
569 }
570
571 pub fn random_vartime() -> Self {
572 loop {
573 let x = Fp::random(&mut OsRng);
574 let y = (x * x * x + Fp::one() + Fp::one() + Fp::one() + Fp::one() + Fp::one()).sqrt();
575 if y.is_some().unwrap_u8() == 1 {
576 return Self {
577 x,
578 y: y.unwrap(),
579 is_infinity: false,
580 _p: Default::default(),
581 };
582 }
583 }
584 }
585
586 /// Add any two points
587 pub fn add(&self, other: &Point<Fp, Fq>) -> Self {
588 if self.x == other.x {
589 // If self == other then call double
590 if self.y == other.y {
591 self.double()
592 } else {
593 // if self.x == other.x and self.y != other.y then return infinity
594 Self {
595 x: Fp::zero(),
596 y: Fp::zero(),
597 is_infinity: true,
598 _p: Default::default(),
599 }
600 }
601 } else {
602 self.add_internal(other)
603 }
604 }
605
606 /// Add two different points
607 pub fn add_internal(&self, other: &Point<Fp, Fq>) -> Self {
608 if self.is_infinity {
609 return other.clone();
610 }
611
612 if other.is_infinity {
613 return self.clone();
614 }
615
616 let lambda = (other.y - self.y) * (other.x - self.x).invert().unwrap();
617 let x = lambda * lambda - self.x - other.x;
618 let y = lambda * (self.x - x) - self.y;
619 Self {
620 x,
621 y,
622 is_infinity: false,
623 _p: Default::default(),
624 }
625 }
626
627 pub fn double(&self) -> Self {
628 if self.is_infinity {
629 return Self {
630 x: Fp::zero(),
631 y: Fp::zero(),
632 is_infinity: true,
633 _p: Default::default(),
634 };
635 }
636
637 let lambda = (Fp::one() + Fp::one() + Fp::one())
638 * self.x
639 * self.x
640 * ((Fp::one() + Fp::one()) * self.y).invert().unwrap();
641 let x = lambda * lambda - self.x - self.x;
642 let y = lambda * (self.x - x) - self.y;
643 Self {
644 x,
645 y,
646 is_infinity: false,
647 _p: Default::default(),
648 }
649 }
650
651 pub fn scalar_mul(&self, scalar: &Fq) -> Self {
652 let mut res = Self {
653 x: Fp::zero(),
654 y: Fp::zero(),
655 is_infinity: true,
656 _p: Default::default(),
657 };
658
659 let bits = scalar.to_le_bits();
660 for i in (0..bits.len()).rev() {
661 res = res.double();
662 if bits[i] {
663 res = self.add(&res);
664 }
665 }
666 res
667 }
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673
674 #[test]
675 fn test_ecc_ops() {
676 type Fp = pasta_curves::pallas::Base;
677 type Fq = pasta_curves::pallas::Scalar;
678
679 // perform some curve arithmetic
680 let a = Point::<Fp, Fq>::random_vartime();
681 let b = Point::<Fp, Fq>::random_vartime();
682 let c = a.add(&b);
683 let d = a.double();
684 let s = Fq::random(&mut OsRng);
685 let e = a.scalar_mul(&s);
686
687 // perform the same computation by translating to pasta_curve types
688 let a_pasta = EpAffine::from_xy(
689 pasta_curves::Fp::from_repr(a.x.to_repr()).unwrap(),
690 pasta_curves::Fp::from_repr(a.y.to_repr()).unwrap(),
691 )
692 .unwrap();
693 let b_pasta = EpAffine::from_xy(
694 pasta_curves::Fp::from_repr(b.x.to_repr()).unwrap(),
695 pasta_curves::Fp::from_repr(b.y.to_repr()).unwrap(),
696 )
697 .unwrap();
698 let c_pasta = (a_pasta + b_pasta).to_affine();
699 let d_pasta = (a_pasta + a_pasta).to_affine();
700 let e_pasta = a_pasta
701 .mul(pasta_curves::Fq::from_repr(s.to_repr()).unwrap())
702 .to_affine();
703
704 // transform c, d, and e into pasta_curve types
705 let c_pasta_2 = EpAffine::from_xy(
706 pasta_curves::Fp::from_repr(c.x.to_repr()).unwrap(),
707 pasta_curves::Fp::from_repr(c.y.to_repr()).unwrap(),
708 )
709 .unwrap();
710 let d_pasta_2 = EpAffine::from_xy(
711 pasta_curves::Fp::from_repr(d.x.to_repr()).unwrap(),
712 pasta_curves::Fp::from_repr(d.y.to_repr()).unwrap(),
713 )
714 .unwrap();
715 let e_pasta_2 = EpAffine::from_xy(
716 pasta_curves::Fp::from_repr(e.x.to_repr()).unwrap(),
717 pasta_curves::Fp::from_repr(e.y.to_repr()).unwrap(),
718 )
719 .unwrap();
720
721 // check that we have the same outputs
722 assert_eq!(c_pasta, c_pasta_2);
723 assert_eq!(d_pasta, d_pasta_2);
724 assert_eq!(e_pasta, e_pasta_2);
725 }
726
727 use crate::bellperson::{shape_cs::ShapeCS, solver::SatisfyingAssignment};
728 use ff::{Field, PrimeFieldBits};
729 use pasta_curves::{arithmetic::CurveAffine, group::Curve, EpAffine};
730 use std::ops::Mul;
731 type G = pasta_curves::pallas::Point;
732 type Fp = pasta_curves::pallas::Scalar;
733 type Fq = pasta_curves::vesta::Scalar;
734 use crate::bellperson::r1cs::{NovaShape, NovaWitness};
735
736 fn synthesize_smul<Fp, Fq, CS>(mut cs: CS) -> (AllocatedPoint<Fp>, AllocatedPoint<Fp>, Fq)
737 where
738 Fp: PrimeField,
739 Fq: PrimeField + PrimeFieldBits,
740 CS: ConstraintSystem<Fp>,
741 {
742 let a = AllocatedPoint::<Fp>::random_vartime(cs.namespace(|| "a")).unwrap();
743 a.inputize(cs.namespace(|| "inputize a")).unwrap();
744 let s = Fq::random(&mut OsRng);
745 // Allocate random bits and only keep 128 bits
746 let bits: Vec<AllocatedBit> = s
747 .to_le_bits()
748 .into_iter()
749 .enumerate()
750 .map(|(i, bit)| AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), Some(bit)))
751 .collect::<Result<Vec<AllocatedBit>, SynthesisError>>()
752 .unwrap();
753 let e = a.scalar_mul(cs.namespace(|| "Scalar Mul"), bits).unwrap();
754 e.inputize(cs.namespace(|| "inputize e")).unwrap();
755 (a, e, s)
756 }
757
758 #[test]
759 fn test_ecc_circuit_ops() {
760 // First create the shape
761 let mut cs: ShapeCS<G> = ShapeCS::new();
762 let _ = synthesize_smul::<Fp, Fq, _>(cs.namespace(|| "synthesize"));
763 println!("Number of constraints: {}", cs.num_constraints());
764 let shape = cs.r1cs_shape();
765 let gens = cs.r1cs_gens();
766
767 // Then the satisfying assignment
768 let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
769 let (a, e, s) = synthesize_smul::<Fp, Fq, _>(cs.namespace(|| "synthesize"));
770 let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
771
772 let a_p: Point<Fp, Fq> = Point::new(
773 a.x.get_value().unwrap(),
774 a.y.get_value().unwrap(),
775 a.is_infinity.get_value().unwrap() == Fp::one(),
776 );
777 let e_p: Point<Fp, Fq> = Point::new(
778 e.x.get_value().unwrap(),
779 e.y.get_value().unwrap(),
780 e.is_infinity.get_value().unwrap() == Fp::one(),
781 );
782 let e_new = a_p.scalar_mul(&s);
783 assert!(e_p.x == e_new.x && e_p.y == e_new.y);
784 // Make sure that this is satisfiable
785 assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
786 }
787
788 fn synthesize_add_equal<Fp, Fq, CS>(mut cs: CS) -> (AllocatedPoint<Fp>, AllocatedPoint<Fp>)
789 where
790 Fp: PrimeField,
791 Fq: PrimeField + PrimeFieldBits,
792 CS: ConstraintSystem<Fp>,
793 {
794 let a = AllocatedPoint::<Fp>::random_vartime(cs.namespace(|| "a")).unwrap();
795 a.inputize(cs.namespace(|| "inputize a")).unwrap();
796 let e = a.add(cs.namespace(|| "add a to a"), &a).unwrap();
797 e.inputize(cs.namespace(|| "inputize e")).unwrap();
798 (a, e)
799 }
800
801 #[test]
802 fn test_ecc_circuit_add_equal() {
803 // First create the shape
804 let mut cs: ShapeCS<G> = ShapeCS::new();
805 let _ = synthesize_add_equal::<Fp, Fq, _>(cs.namespace(|| "synthesize add equal"));
806 println!("Number of constraints: {}", cs.num_constraints());
807 let shape = cs.r1cs_shape();
808 let gens = cs.r1cs_gens();
809
810 // Then the satisfying assignment
811 let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
812 let (a, e) = synthesize_add_equal::<Fp, Fq, _>(cs.namespace(|| "synthesize add equal"));
813 let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
814 let a_p: Point<Fp, Fq> = Point::new(
815 a.x.get_value().unwrap(),
816 a.y.get_value().unwrap(),
817 a.is_infinity.get_value().unwrap() == Fp::one(),
818 );
819 let e_p: Point<Fp, Fq> = Point::new(
820 e.x.get_value().unwrap(),
821 e.y.get_value().unwrap(),
822 e.is_infinity.get_value().unwrap() == Fp::one(),
823 );
824 let e_new = a_p.add(&a_p);
825 assert!(e_p.x == e_new.x && e_p.y == e_new.y);
826 // Make sure that it is satisfiable
827 assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
828 }
829
830 fn synthesize_add_negation<Fp, Fq, CS>(mut cs: CS) -> AllocatedPoint<Fp>
831 where
832 Fp: PrimeField,
833 Fq: PrimeField + PrimeFieldBits,
834 CS: ConstraintSystem<Fp>,
835 {
836 let a = AllocatedPoint::<Fp>::random_vartime(cs.namespace(|| "a")).unwrap();
837 a.inputize(cs.namespace(|| "inputize a")).unwrap();
838 let mut b = a.clone();
839 b.y =
840 AllocatedNum::alloc(cs.namespace(|| "allocate negation of a"), || Ok(Fp::zero())).unwrap();
841 b.inputize(cs.namespace(|| "inputize b")).unwrap();
842 let e = a.add(cs.namespace(|| "add a to b"), &b).unwrap();
843 e
844 }
845
846 #[test]
847 fn test_ecc_circuit_add_negation() {
848 // First create the shape
849 let mut cs: ShapeCS<G> = ShapeCS::new();
850 let _ = synthesize_add_negation::<Fp, Fq, _>(cs.namespace(|| "synthesize add equal"));
851 println!("Number of constraints: {}", cs.num_constraints());
852 let shape = cs.r1cs_shape();
853 let gens = cs.r1cs_gens();
854
855 // Then the satisfying assignment
856 let mut cs: SatisfyingAssignment<G> = SatisfyingAssignment::new();
857 let e = synthesize_add_negation::<Fp, Fq, _>(cs.namespace(|| "synthesize add negation"));
858 let (inst, witness) = cs.r1cs_instance_and_witness(&shape, &gens).unwrap();
859 let e_p: Point<Fp, Fq> = Point::new(
860 e.x.get_value().unwrap(),
861 e.y.get_value().unwrap(),
862 e.is_infinity.get_value().unwrap() == Fp::one(),
863 );
864 assert!(e_p.is_infinity);
865 // Make sure that it is satisfiable
866 assert!(shape.is_sat(&gens, &inst, &witness).is_ok());
867 }
868}