Sean Bowe
7 years ago
3 changed files with 391 additions and 0 deletions
@ -0,0 +1,225 @@
|
||||
use pairing::{ |
||||
Engine, |
||||
Field, |
||||
// TODO
|
||||
// PrimeField
|
||||
}; |
||||
|
||||
use bellman::{ |
||||
SynthesisError, |
||||
ConstraintSystem, |
||||
LinearCombination |
||||
}; |
||||
|
||||
use super::{ |
||||
Assignment |
||||
}; |
||||
|
||||
use super::num::AllocatedNum; |
||||
|
||||
use ::jubjub::{ |
||||
JubjubEngine, |
||||
JubjubParams |
||||
}; |
||||
|
||||
pub struct MontgomeryPoint<E: Engine, Var> { |
||||
x: AllocatedNum<E, Var>, |
||||
y: AllocatedNum<E, Var> |
||||
} |
||||
|
||||
impl<E: JubjubEngine, Var: Copy> MontgomeryPoint<E, Var> { |
||||
/// Performs an affine point doubling, not defined for
|
||||
/// the point of order two (0, 0).
|
||||
pub fn double<CS>( |
||||
&self, |
||||
mut cs: CS, |
||||
params: &E::Params |
||||
) -> Result<Self, SynthesisError> |
||||
where CS: ConstraintSystem<E, Variable=Var> |
||||
{ |
||||
// Square x
|
||||
let xx = self.x.square(&mut cs)?; |
||||
|
||||
// Compute lambda = (3.xx + 2.A.x + 1) / 2.y
|
||||
let lambda = AllocatedNum::alloc(cs.namespace(|| "lambda"), || { |
||||
let mut t0 = *xx.get_value().get()?; |
||||
let mut t1 = t0; |
||||
t0.double(); // t0 = 2.xx
|
||||
t0.add_assign(&t1); // t0 = 3.xx
|
||||
t1 = *self.x.get_value().get()?; // t1 = x
|
||||
t1.mul_assign(params.montgomery_2a()); // t1 = 2.A.x
|
||||
t0.add_assign(&t1); |
||||
t0.add_assign(&E::Fr::one()); |
||||
t1 = *self.y.get_value().get()?; // t1 = y
|
||||
t1.double(); // t1 = 2.y
|
||||
match t1.inverse() { |
||||
Some(t1) => { |
||||
t0.mul_assign(&t1); |
||||
|
||||
Ok(t0) |
||||
}, |
||||
None => { |
||||
// TODO: add a more descriptive error to bellman
|
||||
Err(SynthesisError::AssignmentMissing) |
||||
} |
||||
} |
||||
})?; |
||||
|
||||
// (2.y) * (lambda) = (3.xx + 2.A.x + 1)
|
||||
let one = cs.one(); |
||||
cs.enforce( |
||||
|| "evaluate lambda", |
||||
LinearCombination::<Var, E>::zero() + self.y.get_variable() |
||||
+ self.y.get_variable(), |
||||
|
||||
LinearCombination::zero() + lambda.get_variable(), |
||||
|
||||
LinearCombination::<Var, E>::zero() + xx.get_variable() |
||||
+ xx.get_variable() |
||||
+ xx.get_variable() |
||||
+ (*params.montgomery_2a(), self.x.get_variable()) |
||||
+ one |
||||
); |
||||
|
||||
// Compute x' = (lambda^2) - A - 2.x
|
||||
let xprime = AllocatedNum::alloc(cs.namespace(|| "xprime"), || { |
||||
let mut t0 = *lambda.get_value().get()?; |
||||
t0.square(); |
||||
t0.sub_assign(params.montgomery_a()); |
||||
t0.sub_assign(self.x.get_value().get()?); |
||||
t0.sub_assign(self.x.get_value().get()?); |
||||
|
||||
Ok(t0) |
||||
})?; |
||||
|
||||
// (lambda) * (lambda) = (A + 2.x + x')
|
||||
cs.enforce( |
||||
|| "evaluate xprime", |
||||
LinearCombination::zero() + lambda.get_variable(), |
||||
LinearCombination::zero() + lambda.get_variable(), |
||||
LinearCombination::<Var, E>::zero() + (*params.montgomery_a(), one) |
||||
+ self.x.get_variable() |
||||
+ self.x.get_variable() |
||||
+ xprime.get_variable() |
||||
); |
||||
|
||||
// Compute y' = -(y + lambda(x' - x))
|
||||
let yprime = AllocatedNum::alloc(cs.namespace(|| "yprime"), || { |
||||
let mut t0 = *xprime.get_value().get()?; |
||||
t0.sub_assign(self.x.get_value().get()?); |
||||
t0.mul_assign(lambda.get_value().get()?); |
||||
t0.add_assign(self.y.get_value().get()?); |
||||
t0.negate(); |
||||
|
||||
Ok(t0) |
||||
})?; |
||||
|
||||
// y' + y = lambda(x - x')
|
||||
cs.enforce( |
||||
|| "evaluate yprime", |
||||
LinearCombination::zero() + self.x.get_variable() |
||||
- xprime.get_variable(), |
||||
|
||||
LinearCombination::zero() + lambda.get_variable(), |
||||
|
||||
LinearCombination::<Var, E>::zero() + yprime.get_variable() |
||||
+ self.y.get_variable() |
||||
); |
||||
|
||||
Ok(MontgomeryPoint { |
||||
x: xprime, |
||||
y: yprime |
||||
}) |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod test { |
||||
use bellman::{ConstraintSystem}; |
||||
use rand::{XorShiftRng, SeedableRng, Rng}; |
||||
use pairing::bls12_381::{Bls12, Fr}; |
||||
use pairing::{Field}; |
||||
use ::circuit::test::*; |
||||
use ::jubjub::{ |
||||
montgomery, |
||||
JubjubBls12 |
||||
}; |
||||
use super::{MontgomeryPoint, AllocatedNum}; |
||||
|
||||
#[test] |
||||
fn test_doubling_order_2() { |
||||
let params = &JubjubBls12::new(); |
||||
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new(); |
||||
|
||||
let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { |
||||
Ok(Fr::zero()) |
||||
}).unwrap(); |
||||
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { |
||||
Ok(Fr::zero()) |
||||
}).unwrap(); |
||||
|
||||
let p = MontgomeryPoint { |
||||
x: x, |
||||
y: y |
||||
}; |
||||
|
||||
assert!(p.double(&mut cs, params).is_err()); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_doubling() { |
||||
let params = &JubjubBls12::new(); |
||||
let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); |
||||
|
||||
for _ in 0..100 { |
||||
let p = loop { |
||||
let x: Fr = rng.gen(); |
||||
let s: bool = rng.gen(); |
||||
|
||||
if let Some(p) = montgomery::Point::<Bls12, _>::get_for_x(x, s, params) { |
||||
break p; |
||||
} |
||||
}; |
||||
|
||||
let p2 = p.double(params); |
||||
|
||||
let (x0, y0) = p.into_xy().unwrap(); |
||||
let (x1, y1) = p2.into_xy().unwrap(); |
||||
|
||||
let mut cs = TestConstraintSystem::<Bls12>::new(); |
||||
|
||||
let x = AllocatedNum::alloc(cs.namespace(|| "x"), || { |
||||
Ok(x0) |
||||
}).unwrap(); |
||||
let y = AllocatedNum::alloc(cs.namespace(|| "y"), || { |
||||
Ok(y0) |
||||
}).unwrap(); |
||||
|
||||
let p = MontgomeryPoint { |
||||
x: x, |
||||
y: y |
||||
}; |
||||
|
||||
let p2 = p.double(cs.namespace(|| "doubling"), params).unwrap(); |
||||
|
||||
assert!(cs.is_satisfied()); |
||||
|
||||
assert!(p2.x.get_value().unwrap() == x1); |
||||
assert!(p2.y.get_value().unwrap() == y1); |
||||
|
||||
cs.set("doubling/yprime/num", rng.gen()); |
||||
assert_eq!(cs.which_is_unsatisfied(), Some("doubling/evaluate yprime")); |
||||
cs.set("doubling/yprime/num", y1); |
||||
assert!(cs.is_satisfied()); |
||||
|
||||
cs.set("doubling/xprime/num", rng.gen()); |
||||
assert_eq!(cs.which_is_unsatisfied(), Some("doubling/evaluate xprime")); |
||||
cs.set("doubling/xprime/num", x1); |
||||
assert!(cs.is_satisfied()); |
||||
|
||||
cs.set("doubling/lambda/num", rng.gen()); |
||||
assert_eq!(cs.which_is_unsatisfied(), Some("doubling/evaluate lambda")); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,164 @@
|
||||
use pairing::{ |
||||
Engine, |
||||
Field |
||||
}; |
||||
|
||||
use bellman::{ |
||||
SynthesisError, |
||||
ConstraintSystem, |
||||
LinearCombination |
||||
}; |
||||
|
||||
use super::{ |
||||
Assignment |
||||
}; |
||||
|
||||
pub struct AllocatedNum<E: Engine, Var> { |
||||
value: Option<E::Fr>, |
||||
variable: Var |
||||
} |
||||
|
||||
impl<E: Engine, Var: Copy> AllocatedNum<E, Var> { |
||||
pub fn alloc<CS, F>( |
||||
mut cs: CS, |
||||
value: F, |
||||
) -> Result<Self, SynthesisError> |
||||
where CS: ConstraintSystem<E, Variable=Var>, |
||||
F: FnOnce() -> Result<E::Fr, SynthesisError> |
||||
{ |
||||
let mut new_value = None; |
||||
let var = cs.alloc(|| "num", || { |
||||
let tmp = value()?; |
||||
|
||||
new_value = Some(tmp); |
||||
|
||||
Ok(tmp) |
||||
})?; |
||||
|
||||
Ok(AllocatedNum { |
||||
value: new_value, |
||||
variable: var |
||||
}) |
||||
} |
||||
|
||||
pub fn square<CS>( |
||||
&self, |
||||
mut cs: CS |
||||
) -> Result<Self, SynthesisError> |
||||
where CS: ConstraintSystem<E, Variable=Var> |
||||
{ |
||||
let mut value = None; |
||||
|
||||
let var = cs.alloc(|| "squared num", || { |
||||
let mut tmp = *self.value.get()?; |
||||
tmp.square(); |
||||
|
||||
value = Some(tmp); |
||||
|
||||
Ok(tmp) |
||||
})?; |
||||
|
||||
// Constrain: a * a = aa
|
||||
cs.enforce( |
||||
|| "squaring constraint", |
||||
LinearCombination::zero() + self.variable, |
||||
LinearCombination::zero() + self.variable, |
||||
LinearCombination::zero() + var |
||||
); |
||||
|
||||
Ok(AllocatedNum { |
||||
value: value, |
||||
variable: var |
||||
}) |
||||
} |
||||
|
||||
pub fn assert_nonzero<CS>( |
||||
&self, |
||||
mut cs: CS |
||||
) -> Result<(), SynthesisError> |
||||
where CS: ConstraintSystem<E, Variable=Var> |
||||
{ |
||||
let inv = cs.alloc(|| "ephemeral inverse", || { |
||||
let tmp = *self.value.get()?; |
||||
|
||||
if tmp.is_zero() { |
||||
// TODO: add a more descriptive error to bellman
|
||||
Err(SynthesisError::AssignmentMissing) |
||||
} else { |
||||
Ok(tmp.inverse().unwrap()) |
||||
} |
||||
})?; |
||||
|
||||
// Constrain a * inv = 1, which is only valid
|
||||
// iff a has a multiplicative inverse, untrue
|
||||
// for zero.
|
||||
let one = cs.one(); |
||||
cs.enforce( |
||||
|| "nonzero assertion constraint", |
||||
LinearCombination::zero() + self.variable, |
||||
LinearCombination::zero() + inv, |
||||
LinearCombination::zero() + one |
||||
); |
||||
|
||||
Ok(()) |
||||
} |
||||
|
||||
pub fn get_value(&self) -> Option<E::Fr> { |
||||
self.value |
||||
} |
||||
|
||||
pub fn get_variable(&self) -> Var { |
||||
self.variable |
||||
} |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod test { |
||||
use pairing::bls12_381::{Bls12, Fr}; |
||||
use pairing::{Field, PrimeField}; |
||||
use ::circuit::test::*; |
||||
use super::{AllocatedNum}; |
||||
|
||||
#[test] |
||||
fn test_allocated_num() { |
||||
let mut cs = TestConstraintSystem::<Bls12>::new(); |
||||
|
||||
AllocatedNum::alloc(&mut cs, || Ok(Fr::one())).unwrap(); |
||||
|
||||
assert!(cs.get("num") == Fr::one()); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_num_squaring() { |
||||
let mut cs = TestConstraintSystem::<Bls12>::new(); |
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap(); |
||||
let n2 = n.square(&mut cs).unwrap(); |
||||
|
||||
assert!(cs.is_satisfied()); |
||||
assert!(cs.get("squared num") == Fr::from_str("9").unwrap()); |
||||
assert!(n2.value.unwrap() == Fr::from_str("9").unwrap()); |
||||
cs.set("squared num", Fr::from_str("10").unwrap()); |
||||
assert!(!cs.is_satisfied()); |
||||
} |
||||
|
||||
#[test] |
||||
fn test_num_nonzero() { |
||||
{ |
||||
let mut cs = TestConstraintSystem::<Bls12>::new(); |
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::from_str("3").unwrap())).unwrap(); |
||||
n.assert_nonzero(&mut cs).unwrap(); |
||||
|
||||
assert!(cs.is_satisfied()); |
||||
cs.set("ephemeral inverse", Fr::from_str("3").unwrap()); |
||||
assert!(cs.which_is_unsatisfied() == Some("nonzero assertion constraint")); |
||||
} |
||||
{ |
||||
let mut cs = TestConstraintSystem::<Bls12>::new(); |
||||
|
||||
let n = AllocatedNum::alloc(&mut cs, || Ok(Fr::zero())).unwrap(); |
||||
assert!(n.assert_nonzero(&mut cs).is_err()); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue