From 7d6a57661be9e23ead48b2dfcb58baefb70ed7e3 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Sun, 17 Dec 2017 10:07:00 -0700 Subject: [PATCH] Add Montgomery point interpretation. --- src/circuit/mont.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++ src/circuit/num.rs | 56 +++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) diff --git a/src/circuit/mont.rs b/src/circuit/mont.rs index e5837a9..1c06aef 100644 --- a/src/circuit/mont.rs +++ b/src/circuit/mont.rs @@ -28,6 +28,34 @@ pub struct MontgomeryPoint { } impl MontgomeryPoint { + pub fn interpret( + mut cs: CS, + x: &AllocatedNum, + y: &AllocatedNum, + params: &E::Params + ) -> Result + where CS: ConstraintSystem + { + // y^2 = x^3 + A.x^2 + x + + let x2 = x.square(cs.namespace(|| "x^2"))?; + let x3 = x2.mul(cs.namespace(|| "x^3"), x)?; + + cs.enforce( + || "on curve check", + LinearCombination::zero() + y.get_variable(), + LinearCombination::zero() + y.get_variable(), + LinearCombination::zero() + x3.get_variable() + + (*params.montgomery_a(), x2.get_variable()) + + x.get_variable() + ); + + Ok(MontgomeryPoint { + x: x.clone(), + y: y.clone() + }) + } + /// Performs an affine point doubling, not defined for /// the point of order two (0, 0). pub fn double( @@ -146,6 +174,54 @@ mod test { }; use super::{MontgomeryPoint, AllocatedNum}; + #[test] + fn test_interpret() { + let params = &JubjubBls12::new(); + let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + + for _ in 0..100 { + let p = montgomery::Point::::rand(rng, ¶ms); + let (mut x, mut y) = p.into_xy().unwrap(); + + { + let mut cs = TestConstraintSystem::::new(); + let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || { + Ok(x) + }).unwrap(); + let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || { + Ok(y) + }).unwrap(); + + let p = MontgomeryPoint::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + + assert!(cs.is_satisfied()); + assert_eq!(p.x.get_value().unwrap(), x); + assert_eq!(p.y.get_value().unwrap(), y); + + y.negate(); + cs.set("y/num", y); + assert!(cs.is_satisfied()); + x.negate(); + cs.set("x/num", x); + assert!(!cs.is_satisfied()); + } + + { + let mut cs = TestConstraintSystem::::new(); + let numx = AllocatedNum::alloc(cs.namespace(|| "x"), || { + Ok(x) + }).unwrap(); + let numy = AllocatedNum::alloc(cs.namespace(|| "y"), || { + Ok(y) + }).unwrap(); + + MontgomeryPoint::interpret(&mut cs, &numx, &numy, ¶ms).unwrap(); + + assert_eq!(cs.which_is_unsatisfied().unwrap(), "on curve check"); + } + } + } + #[test] fn test_doubling_order_2() { let params = &JubjubBls12::new(); diff --git a/src/circuit/num.rs b/src/circuit/num.rs index 73eca1a..bcdb31b 100644 --- a/src/circuit/num.rs +++ b/src/circuit/num.rs @@ -25,6 +25,15 @@ pub struct AllocatedNum { variable: Var } +impl Clone for AllocatedNum { + fn clone(&self) -> Self { + AllocatedNum { + value: self.value, + variable: self.variable + } + } +} + impl AllocatedNum { pub fn alloc( mut cs: CS, @@ -190,6 +199,38 @@ impl AllocatedNum { Ok(num) } + pub fn mul( + &self, + mut cs: CS, + other: &Self + ) -> Result + where CS: ConstraintSystem + { + let mut value = None; + + let var = cs.alloc(|| "product num", || { + let mut tmp = *self.value.get()?; + tmp.mul_assign(other.value.get()?); + + value = Some(tmp); + + Ok(tmp) + })?; + + // Constrain: a * b = ab + cs.enforce( + || "multiplication constraint", + LinearCombination::zero() + self.variable, + LinearCombination::zero() + other.variable, + LinearCombination::zero() + var + ); + + Ok(AllocatedNum { + value: value, + variable: var + }) + } + pub fn square( &self, mut cs: CS @@ -294,6 +335,21 @@ mod test { assert!(!cs.is_satisfied()); } + #[test] + fn test_num_multiplication() { + let mut cs = TestConstraintSystem::::new(); + + let n = AllocatedNum::alloc(cs.namespace(|| "a"), || Ok(Fr::from_str("12").unwrap())).unwrap(); + let n2 = AllocatedNum::alloc(cs.namespace(|| "b"), || Ok(Fr::from_str("10").unwrap())).unwrap(); + let n3 = n.mul(&mut cs, &n2).unwrap(); + + assert!(cs.is_satisfied()); + assert!(cs.get("product num") == Fr::from_str("120").unwrap()); + assert!(n3.value.unwrap() == Fr::from_str("120").unwrap()); + cs.set("product num", Fr::from_str("121").unwrap()); + assert!(!cs.is_satisfied()); + } + #[test] fn test_num_nonzero() { {