use fff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
use rand::Rng;
use rand_core::RngCore;
use std::marker::PhantomData;
use std::io::{self, Read, Write};
pub struct Point<E: JubjubEngine, Subgroup> {
x: E::Fr,
y: E::Fr,
t: E::Fr,
z: E::Fr,
_marker: PhantomData<Subgroup>,
}
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2> {
Point {
x: from.x,
y: from.y,
t: from.t,
z: from.z,
_marker: PhantomData,
}
}
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown> {
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown> {
convert_subgroup(&p)
}
}
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup> {
fn clone(&self) -> Self {
convert_subgroup(self)
}
}
impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
fn eq(&self, other: &Point<E, Subgroup>) -> bool {
let mut x1 = self.x;
x1.mul_assign(&other.z);
let mut y1 = self.y;
y1.mul_assign(&other.z);
let mut x2 = other.x;
x2.mul_assign(&self.z);
let mut y2 = other.y;
y2.mul_assign(&self.z);
x1 == x2 && y1 == y2
}
}
impl<E: JubjubEngine> Point<E, Unknown> {
pub fn read<R: Read>(reader: R, params: &E::Params) -> io::Result<Self> {
let mut y_repr = <E::Fr as PrimeField>::Repr::default();
y_repr.read_le(reader)?;
let x_sign = (y_repr.as_ref()[3] >> 63) == 1;
y_repr.as_mut()[3] &= 0x7fffffffffffffff;
match E::Fr::from_repr(y_repr) {
Ok(y) => match Self::get_for_y(y, x_sign, params) {
Some(p) => Ok(p),
None => Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")),
},
Err(_) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"y is not in field",
)),
}
}
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self> {
let mut tmp1 = y;
tmp1.square();
let mut tmp2 = tmp1;
tmp2.mul_assign(params.edwards_d());
tmp2.add_assign(&E::Fr::one());
tmp1.sub_assign(&E::Fr::one());
match tmp2.inverse() {
Some(tmp2) => {
tmp1.mul_assign(&tmp2);
match tmp1.sqrt() {
Some(mut x) => {
if x.into_repr().is_odd() != sign {
x.negate();
}
let mut t = x;
t.mul_assign(&y);
Some(Point {
x: x,
y: y,
t: t,
z: E::Fr::one(),
_marker: PhantomData,
})
}
None => None,
}
}
None => None,
}
}
#[must_use]
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder> {
let tmp = self.double(params).double(params).double(params);
convert_subgroup(&tmp)
}
pub fn random<R: RngCore>(rng: &mut R, params: &E::Params) -> Self {
loop {
let y = E::Fr::random(rng);
if let Some(p) = Self::get_for_y(y, rng.gen(), params) {
return p;
}
}
}
}
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
let (x, y) = self.into_xy();
assert_eq!(E::Fr::NUM_BITS, 255);
let x_repr = x.into_repr();
let mut y_repr = y.into_repr();
if x_repr.is_odd() {
y_repr.as_mut()[3] |= 0x8000000000000000u64;
}
y_repr.write_le(writer)
}
pub fn from_montgomery(m: &montgomery::Point<E, Subgroup>, params: &E::Params) -> Self {
match m.into_xy() {
None => {
Point::zero()
}
Some((x, y)) => {
if y.is_zero() {
let mut neg1 = E::Fr::one();
neg1.negate();
Point {
x: E::Fr::zero(),
y: neg1,
t: E::Fr::zero(),
z: E::Fr::one(),
_marker: PhantomData,
}
} else {
let mut u = x;
u.mul_assign(params.scale());
let mut v = x;
v.sub_assign(&E::Fr::one());
let mut t = u;
t.mul_assign(&v);
let mut z = x;
z.add_assign(&E::Fr::one());
u.mul_assign(&z);
z.mul_assign(&y);
v.mul_assign(&y);
Point {
x: u,
y: v,
t: t,
z: z,
_marker: PhantomData,
}
}
}
}
}
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
}
}
pub fn zero() -> Self {
Point {
x: E::Fr::zero(),
y: E::Fr::one(),
t: E::Fr::zero(),
z: E::Fr::one(),
_marker: PhantomData,
}
}
pub fn into_xy(&self) -> (E::Fr, E::Fr) {
let zinv = self.z.inverse().unwrap();
let mut x = self.x;
x.mul_assign(&zinv);
let mut y = self.y;
y.mul_assign(&zinv);
(x, y)
}
#[must_use]
pub fn negate(&self) -> Self {
let mut p = self.clone();
p.x.negate();
p.t.negate();
p
}
#[must_use]
pub fn double(&self, _: &E::Params) -> Self {
let mut a = self.x;
a.square();
let mut b = self.y;
b.square();
let mut c = self.z;
c.square();
c.double();
let mut d = a;
d.negate();
let mut e = self.x;
e.add_assign(&self.y);
e.square();
e.add_assign(&d); e.sub_assign(&b);
let mut g = d;
g.add_assign(&b);
let mut f = g;
f.sub_assign(&c);
let mut h = d;
h.sub_assign(&b);
let mut x3 = e;
x3.mul_assign(&f);
let mut y3 = g;
y3.mul_assign(&h);
let mut t3 = e;
t3.mul_assign(&h);
let mut z3 = f;
z3.mul_assign(&g);
Point {
x: x3,
y: y3,
t: t3,
z: z3,
_marker: PhantomData,
}
}
#[must_use]
pub fn add(&self, other: &Self, params: &E::Params) -> Self {
let mut a = self.x;
a.mul_assign(&other.x);
let mut b = self.y;
b.mul_assign(&other.y);
let mut c = params.edwards_d().clone();
c.mul_assign(&self.t);
c.mul_assign(&other.t);
let mut d = self.z;
d.mul_assign(&other.z);
let mut h = b;
h.add_assign(&a);
let mut e = self.x;
e.add_assign(&self.y);
{
let mut tmp = other.x;
tmp.add_assign(&other.y);
e.mul_assign(&tmp);
}
e.sub_assign(&h);
let mut f = d;
f.sub_assign(&c);
let mut g = d;
g.add_assign(&c);
let mut x3 = e;
x3.mul_assign(&f);
let mut y3 = g;
y3.mul_assign(&h);
let mut t3 = e;
t3.mul_assign(&h);
let mut z3 = f;
z3.mul_assign(&g);
Point {
x: x3,
y: y3,
t: t3,
z: z3,
_marker: PhantomData,
}
}
#[must_use]
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(&self, scalar: S, params: &E::Params) -> Self {
let mut res = Self::zero();
for b in BitIterator::new(scalar.into()) {
res = res.double(params);
if b {
res = res.add(self, params);
}
}
res
}
}