Implementation of Montgomery point doubling in the circuit.
This commit is contained in:
parent
8c4433ee82
commit
46cbfb4831
|
@ -4,6 +4,8 @@ pub mod test;
|
||||||
pub mod boolean;
|
pub mod boolean;
|
||||||
pub mod uint32;
|
pub mod uint32;
|
||||||
pub mod blake2s;
|
pub mod blake2s;
|
||||||
|
pub mod num;
|
||||||
|
pub mod mont;
|
||||||
|
|
||||||
use bellman::SynthesisError;
|
use bellman::SynthesisError;
|
||||||
|
|
||||||
|
|
|
@ -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