|
|
|
@ -26,12 +26,216 @@ use ::jubjub::{
|
|
|
|
|
montgomery |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
pub struct EdwardsPoint<E: Engine, Var> { |
|
|
|
|
x: AllocatedNum<E, Var>, |
|
|
|
|
y: AllocatedNum<E, Var> |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<E: JubjubEngine, Var: Copy> EdwardsPoint<E, Var> { |
|
|
|
|
/// This extracts the x-coordinate, which is an injective
|
|
|
|
|
/// encoding for elements of the prime order subgroup.
|
|
|
|
|
pub fn into_num(&self) -> AllocatedNum<E, Var> { |
|
|
|
|
self.x.clone() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Perform addition between any two points
|
|
|
|
|
pub fn add<CS>( |
|
|
|
|
&self, |
|
|
|
|
mut cs: CS, |
|
|
|
|
other: &Self, |
|
|
|
|
params: &E::Params |
|
|
|
|
) -> Result<Self, SynthesisError> |
|
|
|
|
where CS: ConstraintSystem<E, Variable=Var> |
|
|
|
|
{ |
|
|
|
|
// Compute U = (x1 + y1) * (x2 + y2)
|
|
|
|
|
let u = AllocatedNum::alloc(cs.namespace(|| "U"), || { |
|
|
|
|
let mut t0 = *self.x.get_value().get()?; |
|
|
|
|
t0.add_assign(self.y.get_value().get()?); |
|
|
|
|
|
|
|
|
|
let mut t1 = *other.x.get_value().get()?; |
|
|
|
|
t1.add_assign(other.y.get_value().get()?); |
|
|
|
|
|
|
|
|
|
t0.mul_assign(&t1); |
|
|
|
|
|
|
|
|
|
Ok(t0) |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
cs.enforce( |
|
|
|
|
|| "U computation", |
|
|
|
|
LinearCombination::<Var, E>::zero() + self.x.get_variable() |
|
|
|
|
+ self.y.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + other.x.get_variable() |
|
|
|
|
+ other.y.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + u.get_variable() |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// Compute A = y2 * x1
|
|
|
|
|
let a = other.y.mul(cs.namespace(|| "A computation"), &self.x)?; |
|
|
|
|
|
|
|
|
|
// Compute B = x2 * y1
|
|
|
|
|
let b = other.x.mul(cs.namespace(|| "B computation"), &self.y)?; |
|
|
|
|
|
|
|
|
|
// Compute C = d*A*B
|
|
|
|
|
let c = AllocatedNum::alloc(cs.namespace(|| "C"), || { |
|
|
|
|
let mut t0 = *a.get_value().get()?; |
|
|
|
|
t0.mul_assign(b.get_value().get()?); |
|
|
|
|
t0.mul_assign(params.edwards_d()); |
|
|
|
|
|
|
|
|
|
Ok(t0) |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
cs.enforce( |
|
|
|
|
|| "C computation", |
|
|
|
|
LinearCombination::<Var, E>::zero() + (*params.edwards_d(), a.get_variable()), |
|
|
|
|
LinearCombination::<Var, E>::zero() + b.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + c.get_variable() |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// Compute x3 = (A + B) / (1 + C)
|
|
|
|
|
let x3 = AllocatedNum::alloc(cs.namespace(|| "x3"), || { |
|
|
|
|
let mut t0 = *a.get_value().get()?; |
|
|
|
|
t0.add_assign(b.get_value().get()?); |
|
|
|
|
|
|
|
|
|
let mut t1 = E::Fr::one(); |
|
|
|
|
t1.add_assign(c.get_value().get()?); |
|
|
|
|
|
|
|
|
|
match t1.inverse() { |
|
|
|
|
Some(t1) => { |
|
|
|
|
t0.mul_assign(&t1); |
|
|
|
|
|
|
|
|
|
Ok(t0) |
|
|
|
|
}, |
|
|
|
|
None => { |
|
|
|
|
// TODO: add more descriptive error
|
|
|
|
|
Err(SynthesisError::AssignmentMissing) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
let one = cs.one(); |
|
|
|
|
cs.enforce( |
|
|
|
|
|| "x3 computation", |
|
|
|
|
LinearCombination::<Var, E>::zero() + one + c.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + x3.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + a.get_variable() |
|
|
|
|
+ b.get_variable() |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// Compute y3 = (U - A - B) / (1 - C)
|
|
|
|
|
let y3 = AllocatedNum::alloc(cs.namespace(|| "y3"), || { |
|
|
|
|
let mut t0 = *u.get_value().get()?; |
|
|
|
|
t0.sub_assign(a.get_value().get()?); |
|
|
|
|
t0.sub_assign(b.get_value().get()?); |
|
|
|
|
|
|
|
|
|
let mut t1 = E::Fr::one(); |
|
|
|
|
t1.sub_assign(c.get_value().get()?); |
|
|
|
|
|
|
|
|
|
match t1.inverse() { |
|
|
|
|
Some(t1) => { |
|
|
|
|
t0.mul_assign(&t1); |
|
|
|
|
|
|
|
|
|
Ok(t0) |
|
|
|
|
}, |
|
|
|
|
None => { |
|
|
|
|
// TODO: add more descriptive error
|
|
|
|
|
Err(SynthesisError::AssignmentMissing) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
cs.enforce( |
|
|
|
|
|| "y3 computation", |
|
|
|
|
LinearCombination::<Var, E>::zero() + one - c.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + y3.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + u.get_variable() |
|
|
|
|
- a.get_variable() |
|
|
|
|
- b.get_variable() |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
Ok(EdwardsPoint { |
|
|
|
|
x: x3, |
|
|
|
|
y: y3 |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub struct MontgomeryPoint<E: Engine, Var> { |
|
|
|
|
x: AllocatedNum<E, Var>, |
|
|
|
|
y: AllocatedNum<E, Var> |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<E: JubjubEngine, Var: Copy> MontgomeryPoint<E, Var> { |
|
|
|
|
/// Converts an element in the prime order subgroup into
|
|
|
|
|
/// a point in the birationally equivalent twisted
|
|
|
|
|
/// Edwards curve.
|
|
|
|
|
pub fn into_edwards<CS>( |
|
|
|
|
&self, |
|
|
|
|
mut cs: CS, |
|
|
|
|
params: &E::Params |
|
|
|
|
) -> Result<EdwardsPoint<E, Var>, SynthesisError> |
|
|
|
|
where CS: ConstraintSystem<E, Variable=Var> |
|
|
|
|
{ |
|
|
|
|
// Compute u = (scale*x) / y
|
|
|
|
|
let u = AllocatedNum::alloc(cs.namespace(|| "u"), || { |
|
|
|
|
let mut t0 = *self.x.get_value().get()?; |
|
|
|
|
t0.mul_assign(params.scale()); |
|
|
|
|
|
|
|
|
|
match self.y.get_value().get()?.inverse() { |
|
|
|
|
Some(invy) => { |
|
|
|
|
t0.mul_assign(&invy); |
|
|
|
|
|
|
|
|
|
Ok(t0) |
|
|
|
|
}, |
|
|
|
|
None => { |
|
|
|
|
// TODO: add more descriptive error
|
|
|
|
|
Err(SynthesisError::AssignmentMissing) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
cs.enforce( |
|
|
|
|
|| "u computation", |
|
|
|
|
LinearCombination::<Var, E>::zero() + self.y.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + u.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + (*params.scale(), self.x.get_variable()) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// Compute v = (x - 1) / (x + 1)
|
|
|
|
|
let v = AllocatedNum::alloc(cs.namespace(|| "v"), || { |
|
|
|
|
let mut t0 = *self.x.get_value().get()?; |
|
|
|
|
let mut t1 = t0; |
|
|
|
|
t0.sub_assign(&E::Fr::one()); |
|
|
|
|
t1.add_assign(&E::Fr::one()); |
|
|
|
|
|
|
|
|
|
match t1.inverse() { |
|
|
|
|
Some(t1) => { |
|
|
|
|
t0.mul_assign(&t1); |
|
|
|
|
|
|
|
|
|
Ok(t0) |
|
|
|
|
}, |
|
|
|
|
None => { |
|
|
|
|
// TODO: add more descriptive error
|
|
|
|
|
Err(SynthesisError::AssignmentMissing) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
let one = cs.one(); |
|
|
|
|
cs.enforce( |
|
|
|
|
|| "v computation", |
|
|
|
|
LinearCombination::<Var, E>::zero() + self.x.get_variable() |
|
|
|
|
+ one, |
|
|
|
|
LinearCombination::<Var, E>::zero() + v.get_variable(), |
|
|
|
|
LinearCombination::<Var, E>::zero() + self.x.get_variable() |
|
|
|
|
- one, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
Ok(EdwardsPoint { |
|
|
|
|
x: u, |
|
|
|
|
y: v |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn group_hash<CS>( |
|
|
|
|
mut cs: CS, |
|
|
|
|
tag: &[Boolean<Var>], |
|
|
|
@ -352,12 +556,57 @@ mod test {
|
|
|
|
|
use ::circuit::test::*; |
|
|
|
|
use ::jubjub::{ |
|
|
|
|
montgomery, |
|
|
|
|
edwards, |
|
|
|
|
JubjubBls12 |
|
|
|
|
}; |
|
|
|
|
use super::{MontgomeryPoint, AllocatedNum, Boolean}; |
|
|
|
|
use super::{ |
|
|
|
|
MontgomeryPoint, |
|
|
|
|
EdwardsPoint, |
|
|
|
|
AllocatedNum,
|
|
|
|
|
Boolean |
|
|
|
|
}; |
|
|
|
|
use super::super::boolean::AllocatedBit; |
|
|
|
|
use ::group_hash::group_hash; |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_into_edwards() { |
|
|
|
|
let params = &JubjubBls12::new(); |
|
|
|
|
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); |
|
|
|
|
|
|
|
|
|
for _ in 0..100 { |
|
|
|
|
let mut cs = TestConstraintSystem::<Bls12>::new(); |
|
|
|
|
|
|
|
|
|
let p = montgomery::Point::<Bls12, _>::rand(rng, params); |
|
|
|
|
let (u, v) = edwards::Point::from_montgomery(&p, params).into_xy(); |
|
|
|
|
let (x, y) = p.into_xy().unwrap(); |
|
|
|
|
|
|
|
|
|
let numx = AllocatedNum::alloc(cs.namespace(|| "mont x"), || { |
|
|
|
|
Ok(x) |
|
|
|
|
}).unwrap(); |
|
|
|
|
let numy = AllocatedNum::alloc(cs.namespace(|| "mont y"), || { |
|
|
|
|
Ok(y) |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
let p = MontgomeryPoint::interpret_unchecked(numx, numy); |
|
|
|
|
|
|
|
|
|
let q = p.into_edwards(&mut cs, params).unwrap(); |
|
|
|
|
|
|
|
|
|
assert!(cs.is_satisfied()); |
|
|
|
|
assert!(q.x.get_value().unwrap() == u); |
|
|
|
|
assert!(q.y.get_value().unwrap() == v); |
|
|
|
|
|
|
|
|
|
cs.set("u/num", rng.gen()); |
|
|
|
|
assert_eq!(cs.which_is_unsatisfied().unwrap(), "u computation"); |
|
|
|
|
cs.set("u/num", u); |
|
|
|
|
assert!(cs.is_satisfied()); |
|
|
|
|
|
|
|
|
|
cs.set("v/num", rng.gen()); |
|
|
|
|
assert_eq!(cs.which_is_unsatisfied().unwrap(), "v computation"); |
|
|
|
|
cs.set("v/num", v); |
|
|
|
|
assert!(cs.is_satisfied()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_group_hash() { |
|
|
|
|
let params = &JubjubBls12::new(); |
|
|
|
@ -493,7 +742,75 @@ mod test {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_addition() { |
|
|
|
|
fn test_edwards_addition() { |
|
|
|
|
let params = &JubjubBls12::new(); |
|
|
|
|
let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); |
|
|
|
|
|
|
|
|
|
for _ in 0..100 { |
|
|
|
|
let p1 = edwards::Point::<Bls12, _>::rand(rng, params); |
|
|
|
|
let p2 = edwards::Point::<Bls12, _>::rand(rng, params); |
|
|
|
|
|
|
|
|
|
let p3 = p1.add(&p2, params); |
|
|
|
|
|
|
|
|
|
let (x0, y0) = p1.into_xy(); |
|
|
|
|
let (x1, y1) = p2.into_xy(); |
|
|
|
|
let (x2, y2) = p3.into_xy(); |
|
|
|
|
|
|
|
|
|
let mut cs = TestConstraintSystem::<Bls12>::new(); |
|
|
|
|
|
|
|
|
|
let num_x0 = AllocatedNum::alloc(cs.namespace(|| "x0"), || { |
|
|
|
|
Ok(x0) |
|
|
|
|
}).unwrap(); |
|
|
|
|
let num_y0 = AllocatedNum::alloc(cs.namespace(|| "y0"), || { |
|
|
|
|
Ok(y0) |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
let num_x1 = AllocatedNum::alloc(cs.namespace(|| "x1"), || { |
|
|
|
|
Ok(x1) |
|
|
|
|
}).unwrap(); |
|
|
|
|
let num_y1 = AllocatedNum::alloc(cs.namespace(|| "y1"), || { |
|
|
|
|
Ok(y1) |
|
|
|
|
}).unwrap(); |
|
|
|
|
|
|
|
|
|
let p1 = EdwardsPoint { |
|
|
|
|
x: num_x0, |
|
|
|
|
y: num_y0 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let p2 = EdwardsPoint { |
|
|
|
|
x: num_x1, |
|
|
|
|
y: num_y1 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let p3 = p1.add(cs.namespace(|| "addition"), &p2, params).unwrap(); |
|
|
|
|
|
|
|
|
|
assert!(cs.is_satisfied()); |
|
|
|
|
|
|
|
|
|
assert!(p3.x.get_value().unwrap() == x2); |
|
|
|
|
assert!(p3.y.get_value().unwrap() == y2); |
|
|
|
|
|
|
|
|
|
let u = cs.get("addition/U/num"); |
|
|
|
|
cs.set("addition/U/num", rng.gen()); |
|
|
|
|
assert_eq!(cs.which_is_unsatisfied(), Some("addition/U computation")); |
|
|
|
|
cs.set("addition/U/num", u); |
|
|
|
|
assert!(cs.is_satisfied()); |
|
|
|
|
|
|
|
|
|
let x3 = cs.get("addition/x3/num"); |
|
|
|
|
cs.set("addition/x3/num", rng.gen()); |
|
|
|
|
assert_eq!(cs.which_is_unsatisfied(), Some("addition/x3 computation")); |
|
|
|
|
cs.set("addition/x3/num", x3); |
|
|
|
|
assert!(cs.is_satisfied()); |
|
|
|
|
|
|
|
|
|
let y3 = cs.get("addition/y3/num"); |
|
|
|
|
cs.set("addition/y3/num", rng.gen()); |
|
|
|
|
assert_eq!(cs.which_is_unsatisfied(), Some("addition/y3 computation")); |
|
|
|
|
cs.set("addition/y3/num", y3); |
|
|
|
|
assert!(cs.is_satisfied()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_montgomery_addition() { |
|
|
|
|
let params = &JubjubBls12::new(); |
|
|
|
|
let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); |
|
|
|
|
|
|
|
|
|