Refactor jubjub implementation to be abstract over field, parameters.
This commit is contained in:
+23
-214
@@ -1,5 +1,4 @@
|
||||
use pairing::{
|
||||
Engine,
|
||||
Field,
|
||||
SqrtField,
|
||||
PrimeField,
|
||||
@@ -8,11 +7,10 @@ use pairing::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
JubjubEngine,
|
||||
JubjubParams,
|
||||
Unknown,
|
||||
PrimeOrder,
|
||||
Fs,
|
||||
FsRepr,
|
||||
montgomery
|
||||
};
|
||||
|
||||
@@ -24,7 +22,7 @@ use std::marker::PhantomData;
|
||||
|
||||
// Represents the affine point (X/Z, Y/Z) via the extended
|
||||
// twisted Edwards coordinates.
|
||||
pub struct Point<E: Engine, Subgroup> {
|
||||
pub struct Point<E: JubjubEngine, Subgroup> {
|
||||
x: E::Fr,
|
||||
y: E::Fr,
|
||||
t: E::Fr,
|
||||
@@ -32,7 +30,7 @@ pub struct Point<E: Engine, Subgroup> {
|
||||
_marker: PhantomData<Subgroup>
|
||||
}
|
||||
|
||||
fn convert_subgroup<E: Engine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
|
||||
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
|
||||
{
|
||||
Point {
|
||||
x: from.x,
|
||||
@@ -43,7 +41,7 @@ fn convert_subgroup<E: Engine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
|
||||
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
|
||||
{
|
||||
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
|
||||
{
|
||||
@@ -51,14 +49,14 @@ impl<E: Engine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, Subgroup> Clone for Point<E, Subgroup>
|
||||
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
convert_subgroup(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, Subgroup> PartialEq for Point<E, Subgroup> {
|
||||
impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
|
||||
fn eq(&self, other: &Point<E, Subgroup>) -> bool {
|
||||
// p1 = (x1/z1, y1/z1)
|
||||
// p2 = (x2/z2, y2/z2)
|
||||
@@ -82,9 +80,9 @@ impl<E: Engine, Subgroup> PartialEq for Point<E, Subgroup> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Point<E, Unknown> {
|
||||
impl<E: JubjubEngine> Point<E, Unknown> {
|
||||
/// This guarantees the point is in the prime order subgroup
|
||||
pub fn mul_by_cofactor(&self, params: &JubjubParams<E>) -> Point<E, PrimeOrder>
|
||||
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder>
|
||||
{
|
||||
let tmp = self.double(params)
|
||||
.double(params)
|
||||
@@ -93,7 +91,7 @@ impl<E: Engine> Point<E, Unknown> {
|
||||
convert_subgroup(&tmp)
|
||||
}
|
||||
|
||||
pub fn rand<R: Rng>(rng: &mut R, params: &JubjubParams<E>) -> Self
|
||||
pub fn rand<R: Rng>(rng: &mut R, params: &E::Params) -> Self
|
||||
{
|
||||
loop {
|
||||
// given an x on the curve, y^2 = (1 + x^2) / (1 - dx^2)
|
||||
@@ -104,7 +102,7 @@ impl<E: Engine> Point<E, Unknown> {
|
||||
let mut num = E::Fr::one();
|
||||
num.add_assign(&x2);
|
||||
|
||||
x2.mul_assign(¶ms.edwards_d);
|
||||
x2.mul_assign(params.edwards_d());
|
||||
|
||||
let mut den = E::Fr::one();
|
||||
den.sub_assign(&x2);
|
||||
@@ -139,11 +137,11 @@ impl<E: Engine> Point<E, Unknown> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine, Subgroup> Point<E, Subgroup> {
|
||||
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
|
||||
/// Convert from a Montgomery point
|
||||
pub fn from_montgomery(
|
||||
m: &montgomery::Point<E, Subgroup>,
|
||||
params: &JubjubParams<E>
|
||||
params: &E::Params
|
||||
) -> Self
|
||||
{
|
||||
match m.into_xy() {
|
||||
@@ -214,7 +212,7 @@ impl<E: Engine, Subgroup> Point<E, Subgroup> {
|
||||
|
||||
// u = xs
|
||||
let mut u = x;
|
||||
u.mul_assign(¶ms.scale);
|
||||
u.mul_assign(params.scale());
|
||||
|
||||
// v = x - 1
|
||||
let mut v = x;
|
||||
@@ -251,8 +249,8 @@ impl<E: Engine, Subgroup> Point<E, Subgroup> {
|
||||
|
||||
/// Attempts to cast this as a prime order element, failing if it's
|
||||
/// not in the prime order subgroup.
|
||||
pub fn as_prime_order(&self, params: &JubjubParams<E>) -> Option<Point<E, PrimeOrder>> {
|
||||
if self.mul(Fs::char(), params) == Point::zero() {
|
||||
pub fn as_prime_order(&self, params: &E::Params) -> Option<Point<E, PrimeOrder>> {
|
||||
if self.mul(E::Fs::char(), params) == Point::zero() {
|
||||
Some(convert_subgroup(self))
|
||||
} else {
|
||||
None
|
||||
@@ -291,11 +289,11 @@ impl<E: Engine, Subgroup> Point<E, Subgroup> {
|
||||
p
|
||||
}
|
||||
|
||||
pub fn double(&self, params: &JubjubParams<E>) -> Self {
|
||||
pub fn double(&self, params: &E::Params) -> Self {
|
||||
self.add(self, params)
|
||||
}
|
||||
|
||||
pub fn add(&self, other: &Self, params: &JubjubParams<E>) -> Self
|
||||
pub fn add(&self, other: &Self, params: &E::Params) -> Self
|
||||
{
|
||||
// A = x1 * x2
|
||||
let mut a = self.x;
|
||||
@@ -306,7 +304,7 @@ impl<E: Engine, Subgroup> Point<E, Subgroup> {
|
||||
b.mul_assign(&other.y);
|
||||
|
||||
// C = d * t1 * t2
|
||||
let mut c = params.edwards_d;
|
||||
let mut c = params.edwards_d().clone();
|
||||
c.mul_assign(&self.t);
|
||||
c.mul_assign(&other.t);
|
||||
|
||||
@@ -363,7 +361,11 @@ impl<E: Engine, Subgroup> Point<E, Subgroup> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul<S: Into<FsRepr>>(&self, scalar: S, params: &JubjubParams<E>) -> Self
|
||||
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
|
||||
&self,
|
||||
scalar: S,
|
||||
params: &E::Params
|
||||
) -> Self
|
||||
{
|
||||
let mut res = Self::zero();
|
||||
|
||||
@@ -378,196 +380,3 @@ impl<E: Engine, Subgroup> Point<E, Subgroup> {
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rand::{XorShiftRng, SeedableRng, Rand};
|
||||
use super::{JubjubParams, Point, PrimeOrder, Fs};
|
||||
use pairing::bls12_381::{Bls12};
|
||||
use pairing::{Engine, Field};
|
||||
|
||||
fn is_on_curve<E: Engine>(
|
||||
x: E::Fr,
|
||||
y: E::Fr,
|
||||
params: &JubjubParams<E>
|
||||
) -> bool
|
||||
{
|
||||
let mut x2 = x;
|
||||
x2.square();
|
||||
|
||||
let mut y2 = y;
|
||||
y2.square();
|
||||
|
||||
// -x^2 + y^2
|
||||
let mut lhs = y2;
|
||||
lhs.sub_assign(&x2);
|
||||
|
||||
// 1 + d x^2 y^2
|
||||
let mut rhs = y2;
|
||||
rhs.mul_assign(&x2);
|
||||
rhs.mul_assign(¶ms.edwards_d);
|
||||
rhs.add_assign(&E::Fr::one());
|
||||
|
||||
lhs == rhs
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rand() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let params = JubjubParams::new();
|
||||
|
||||
for _ in 0..100 {
|
||||
let (x, y) = Point::rand(&mut rng, ¶ms).into_xy();
|
||||
|
||||
assert!(is_on_curve(x, y, ¶ms));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_identities() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let params = JubjubParams::new();
|
||||
|
||||
let z = Point::<Bls12, PrimeOrder>::zero();
|
||||
assert!(z.double(¶ms) == z);
|
||||
assert!(z.negate() == z);
|
||||
|
||||
for _ in 0..100 {
|
||||
let r = Point::rand(&mut rng, ¶ms);
|
||||
|
||||
assert!(r.add(&Point::zero(), ¶ms) == r);
|
||||
assert!(r.add(&r.negate(), ¶ms) == Point::zero());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_associativity() {
|
||||
let mut rng = XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let params = JubjubParams::new();
|
||||
|
||||
for _ in 0..1000 {
|
||||
let a = Point::rand(&mut rng, ¶ms);
|
||||
let b = Point::rand(&mut rng, ¶ms);
|
||||
let c = Point::rand(&mut rng, ¶ms);
|
||||
|
||||
assert!(a.add(&b, ¶ms).add(&c, ¶ms) == c.add(&a, ¶ms).add(&b, ¶ms));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_order() {
|
||||
let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let params = &JubjubParams::new();
|
||||
|
||||
// The neutral element is in the prime order subgroup.
|
||||
assert!(Point::<Bls12, PrimeOrder>::zero().as_prime_order(params).is_some());
|
||||
|
||||
for _ in 0..50 {
|
||||
// Pick a random point and multiply it by the cofactor
|
||||
let base = Point::rand(rng, params).mul_by_cofactor(params);
|
||||
|
||||
// Any point multiplied by the cofactor will be in the prime
|
||||
// order subgroup
|
||||
assert!(base.as_prime_order(params).is_some());
|
||||
}
|
||||
|
||||
// It's very likely that at least one out of 50 random points on the curve
|
||||
// is not in the prime order subgroup.
|
||||
let mut at_least_one_not_in_prime_order_subgroup = false;
|
||||
for _ in 0..50 {
|
||||
// Pick a random point.
|
||||
let base = Point::rand(rng, params);
|
||||
|
||||
at_least_one_not_in_prime_order_subgroup |= base.as_prime_order(params).is_none();
|
||||
}
|
||||
assert!(at_least_one_not_in_prime_order_subgroup);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_associativity() {
|
||||
let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let params = &JubjubParams::new();
|
||||
|
||||
for _ in 0..100 {
|
||||
// Pick a random point and multiply it by the cofactor
|
||||
let base = Point::rand(rng, params).mul_by_cofactor(params);
|
||||
|
||||
let mut a = Fs::rand(rng);
|
||||
let b = Fs::rand(rng);
|
||||
let c = Fs::rand(rng);
|
||||
|
||||
let res1 = base.mul(a, params).mul(b, params).mul(c, params);
|
||||
let res2 = base.mul(b, params).mul(c, params).mul(a, params);
|
||||
let res3 = base.mul(c, params).mul(a, params).mul(b, params);
|
||||
a.mul_assign(&b);
|
||||
a.mul_assign(&c);
|
||||
let res4 = base.mul(a, params);
|
||||
|
||||
assert!(res1 == res2);
|
||||
assert!(res2 == res3);
|
||||
assert!(res3 == res4);
|
||||
|
||||
let (x, y) = res1.into_xy();
|
||||
assert!(is_on_curve(x, y, params));
|
||||
|
||||
let (x, y) = res2.into_xy();
|
||||
assert!(is_on_curve(x, y, params));
|
||||
|
||||
let (x, y) = res3.into_xy();
|
||||
assert!(is_on_curve(x, y, params));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_montgomery_conversion() {
|
||||
use super::montgomery;
|
||||
|
||||
let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let params = &JubjubParams::new();
|
||||
|
||||
for _ in 0..200 {
|
||||
// compute base in montgomery
|
||||
let base = montgomery::Point::rand(rng, params);
|
||||
|
||||
// sample random exponent
|
||||
let exp = Fs::rand(rng);
|
||||
|
||||
// exponentiate in montgomery, convert to edwards
|
||||
let ed_expected = Point::from_montgomery(&base.mul(exp, params), params);
|
||||
|
||||
// convert to edwards and exponentiate
|
||||
let ed_exponentiated = Point::from_montgomery(&base, params).mul(exp, params);
|
||||
|
||||
let (x, y) = ed_expected.into_xy();
|
||||
assert!(is_on_curve(x, y, params));
|
||||
|
||||
assert!(ed_exponentiated == ed_expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_back_and_forth() {
|
||||
use super::montgomery;
|
||||
|
||||
let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||
let params = &JubjubParams::new();
|
||||
|
||||
for _ in 0..200 {
|
||||
// compute base in montgomery
|
||||
let base = montgomery::Point::rand(rng, params);
|
||||
|
||||
// convert to edwards
|
||||
let base_ed = Point::from_montgomery(&base, params);
|
||||
|
||||
{
|
||||
let (x, y) = base_ed.into_xy();
|
||||
assert!(is_on_curve(x, y, params));
|
||||
}
|
||||
|
||||
// convert back to montgomery
|
||||
let base_mont = montgomery::Point::from_edwards(&base_ed, params);
|
||||
|
||||
assert!(base == base_mont);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user