use crate::bellman::plonk::better_better_cs::cs::ConstraintSystem;
use crate::bellman::{Engine, Field, PrimeField, SqrtField, SynthesisError};
use crate::generic_twisted_edwards::edwards::*;
use crate::plonk::circuit::allocated_num::{AllocatedNum, Num};
use crate::plonk::circuit::simple_term::Term;
use crate::plonk::circuit::Assignment;
use crate::plonk::circuit::{boolean::Boolean, linear_combination::LinearCombination};
pub struct CircuitTwistedEdwardsCurveImplementor<E: Engine, C: TwistedEdwardsCurveParams<E>> {
pub implementor: TwistedEdwardsCurveImplementor<E, C>,
}
impl<E: Engine, C: TwistedEdwardsCurveParams<E>> CircuitTwistedEdwardsCurveImplementor<E, C> {
pub fn new_from_params(params: C) -> Self {
let out_of_circuit_implementor = TwistedEdwardsCurveImplementor::new_from_params(params);
Self {
implementor: out_of_circuit_implementor,
}
}
pub fn add<CS: ConstraintSystem<E>>(&self, cs: &mut CS, p: &CircuitTwistedEdwardsPoint<E>, q: &CircuitTwistedEdwardsPoint<E>) -> Result<CircuitTwistedEdwardsPoint<E>, SynthesisError> {
if !self.implementor.curve_params.is_param_a_equals_minus_one() {
unimplemented!("not yet implemented for a != -1");
}
let t0 = p.x.add(cs, &p.y)?;
let t1 = q.x.add(cs, &q.y)?;
let u = t0.mul(cs, &t1)?;
let a = q.y.mul(cs, &p.x)?;
let b = q.x.mul(cs, &p.y)?;
let mut c = Term::from_num(a).mul(cs, &Term::from_num(b))?;
c.scale(&self.implementor.curve_params.param_d());
let t3 = a.add(cs, &b)?;
let mut c_plus_one = c.clone();
c_plus_one.add_constant(&E::Fr::one());
let t3 = Term::from_num(t3);
let x3 = t3.div(cs, &c_plus_one)?.into_num();
let u = Term::from_num(u);
let mut t5 = t3;
t5.negate();
let t6 = u.add(cs, &t5)?;
let mut t7 = c.clone();
t7.negate();
t7.add_constant(&E::Fr::one());
let y3 = t6.div(cs, &t7)?;
let y3 = y3.into_num();
Ok(CircuitTwistedEdwardsPoint { x: x3, y: y3 })
}
pub fn double<CS: ConstraintSystem<E>>(&self, cs: &mut CS, p: &CircuitTwistedEdwardsPoint<E>) -> Result<CircuitTwistedEdwardsPoint<E>, SynthesisError> {
if !self.implementor.curve_params.is_param_a_equals_minus_one() {
unimplemented!("not yet implemented for a != -1");
}
let t0 = p.x.add(cs, &p.y)?;
let t = t0.mul(cs, &t0)?;
let a = p.x.mul(cs, &p.y)?;
let mut two = E::Fr::one();
two.double();
let mut c = Term::from_num(a).mul(cs, &Term::from_num(a))?;
c.scale(&self.implementor.curve_params.param_d());
let mut t3 = Term::from_num(a);
t3.scale(&two);
let mut c_plus_one = c.clone();
c_plus_one.add_constant(&E::Fr::one());
let x3 = t3.div(cs, &c_plus_one)?.into_num();
let t5 = Term::from_num(t).sub(cs, &t3)?;
let mut t6 = c.clone();
t6.negate();
t6.add_constant(&E::Fr::one());
let y3 = t5.div(cs, &t6)?;
let y3 = y3.into_num();
Ok(CircuitTwistedEdwardsPoint { x: x3, y: y3 })
}
pub fn mul<CS: ConstraintSystem<E>>(&self, cs: &mut CS, p: &CircuitTwistedEdwardsPoint<E>, s: &[Boolean]) -> Result<CircuitTwistedEdwardsPoint<E>, SynthesisError> {
if !self.implementor.curve_params.is_param_a_equals_minus_one() {
unimplemented!("not yet implemented for a != -1");
}
let mut curbase = None;
let mut result = None;
let inf = CircuitTwistedEdwardsPoint::zero();
for (_i, bit) in s.iter().enumerate() {
if curbase.is_none() {
curbase = Some(p.clone())
} else {
let tmp = curbase.unwrap();
curbase = Some(self.double(cs, &tmp.clone())?);
}
let tmp = curbase.as_ref().unwrap().clone();
let thisbase = CircuitTwistedEdwardsPoint::conditionally_select(cs, bit, &tmp, &inf)?;
if result.is_none() {
result = Some(thisbase);
} else {
result = Some(self.add(cs, &result.unwrap(), &thisbase)?);
}
}
Ok(result.unwrap())
}
pub fn is_in_main_subgroup<CS: ConstraintSystem<E>>(&self, cs: &mut CS, p: &CircuitTwistedEdwardsPoint<E>) -> Result<Boolean, SynthesisError> {
if !self.implementor.curve_params.is_param_a_equals_minus_one() {
unimplemented!("not yet implemented for a != -1");
}
use crate::plonk::circuit::utils::words_to_msb_first_bits;
let mut tmp = p.clone();
let msb_bits = words_to_msb_first_bits(C::Fs::char().as_ref());
for b in msb_bits.into_iter().skip(1) {
tmp = self.double(cs, &tmp)?;
if b {
tmp = self.add(cs, &tmp, p)?;
}
}
CircuitTwistedEdwardsPoint::equals(cs, &CircuitTwistedEdwardsPoint::zero(), &tmp)
}
pub fn mul_by_generator<CS: ConstraintSystem<E>>(&self, cs: &mut CS, s: &[Boolean]) -> Result<CircuitTwistedEdwardsPoint<E>, SynthesisError> {
if !self.implementor.curve_params.is_param_a_equals_minus_one() {
unimplemented!("not yet implemented for a != -1");
}
let generator = self.generator();
self.mul(cs, &generator, s)
}
pub fn alloc_point_enforce_on_curve<CS: ConstraintSystem<E>>(&self, cs: &mut CS, p: Option<TwistedEdwardsPoint<E>>) -> Result<CircuitTwistedEdwardsPoint<E>, SynthesisError> {
let p = p.map(|p| p.into_xy());
let x_witness = p.map(|p| p.0);
let y_witness = p.map(|p| p.1);
let x = Num::Variable(AllocatedNum::alloc(cs, || Ok(*x_witness.get()?))?);
let y = Num::Variable(AllocatedNum::alloc(cs, || Ok(*y_witness.get()?))?);
self.from_xy_assert_on_curve(cs, &x, &y)?;
Ok(CircuitTwistedEdwardsPoint { x, y })
}
pub fn alloc_point_unchecked<CS: ConstraintSystem<E>>(&self, cs: &mut CS, p: Option<TwistedEdwardsPoint<E>>) -> Result<CircuitTwistedEdwardsPoint<E>, SynthesisError> {
let p = p.map(|p| p.into_xy());
let x_witness = p.map(|p| p.0);
let y_witness = p.map(|p| p.1);
let x = Num::Variable(AllocatedNum::alloc(cs, || Ok(*x_witness.get()?))?);
let y = Num::Variable(AllocatedNum::alloc(cs, || Ok(*y_witness.get()?))?);
Ok(CircuitTwistedEdwardsPoint { x, y })
}
pub fn check_is_on_curve<CS: ConstraintSystem<E>>(&self, cs: &mut CS, p: &CircuitTwistedEdwardsPoint<E>) -> Result<Boolean, SynthesisError> {
if !self.implementor.curve_params.is_param_a_equals_minus_one() {
unimplemented!("not yet implemented for a != -1");
}
let x = p.x;
let y = p.y;
let x2 = x.mul(cs, &x)?;
let y2 = y.mul(cs, &y)?;
let x2y2 = x2.mul(cs, &y2)?;
let lhs = y2.sub(cs, &x2)?;
let param_d = Num::Constant(self.implementor.curve_params.param_d());
let tmp = param_d.mul(cs, &x2y2)?;
let rhs = Num::Constant(E::Fr::one()).add(cs, &tmp)?;
Num::equals(cs, &lhs, &rhs)
}
pub fn generator(&self) -> CircuitTwistedEdwardsPoint<E> {
let gen = self.implementor.curve_params.generator();
let (x, y) = gen.into_xy();
let x = Num::Constant(x);
let y = Num::Constant(y);
CircuitTwistedEdwardsPoint { x, y }
}
pub fn from_xy_assert_on_curve<CS>(&self, cs: &mut CS, x: &Num<E>, y: &Num<E>) -> Result<CircuitTwistedEdwardsPoint<E>, SynthesisError>
where
CS: ConstraintSystem<E>,
{
if !self.implementor.curve_params.is_param_a_equals_minus_one() {
unimplemented!("not yet implemented for a != -1");
}
let x2 = x.mul(cs, &x)?;
let y2 = y.mul(cs, &y)?;
let x2y2 = x2.mul(cs, &y2)?;
let lhs = y2.sub(cs, &x2)?;
let param_d = Num::Constant(self.implementor.curve_params.param_d());
let tmp = param_d.mul(cs, &x2y2)?;
let rhs = Num::Constant(E::Fr::one()).add(cs, &tmp)?;
lhs.enforce_equal(cs, &rhs)?;
Ok(CircuitTwistedEdwardsPoint { x: x.clone(), y: y.clone() })
}
}
#[derive(Clone, Debug)]
pub struct CircuitTwistedEdwardsPoint<E: Engine> {
pub x: Num<E>,
pub y: Num<E>,
}
impl<E: Engine> Copy for CircuitTwistedEdwardsPoint<E> {}
impl<E: Engine> CircuitTwistedEdwardsPoint<E> {
pub fn zero() -> Self {
Self { x: Num::zero(), y: Num::one() }
}
pub fn conditionally_select<CS: ConstraintSystem<E>>(cs: &mut CS, flag: &Boolean, first: &Self, second: &Self) -> Result<Self, SynthesisError> {
let x = Num::conditionally_select(cs, flag, &first.x, &second.x)?;
let y = Num::conditionally_select(cs, flag, &first.y, &second.y)?;
Ok(Self { x, y })
}
pub fn equals<CS: ConstraintSystem<E>>(cs: &mut CS, first: &Self, second: &Self) -> Result<Boolean, SynthesisError> {
let a = Num::equals(cs, &first.x, &second.x)?;
let b = Num::equals(cs, &first.y, &second.y)?;
Boolean::and(cs, &a, &b)
}
}