use ff::Field;
use midnight_proofs::{circuit::Layouter, plonk::Error};
use super::{
mtc_cpu::MapToCurveCPU,
mtc_params::{MapToEdwardsParams, MapToWeierstrassParams},
};
use crate::{
ecc::{
curves::{CircuitCurve, EdwardsCurve},
native::{AssignedNativePoint, EccChip},
},
instructions::{
BinaryInstructions, DecompositionInstructions, EccInstructions, EqualityInstructions,
FieldInstructions,
},
types::{AssignedBit, InnerConstants, InnerValue, Instantiable},
CircuitField,
};
pub trait MapToCurveInstructions<F, C>: EccInstructions<F, C>
where
F: CircuitField,
C: CircuitCurve + MapToCurveCPU<C>,
{
fn map_to_curve(
&self,
layouter: &mut impl Layouter<F>,
u: &Self::Coordinate,
) -> Result<Self::Point, Error>;
}
impl<C> MapToCurveInstructions<C::Base, C> for EccChip<C>
where
C: EdwardsCurve + MapToCurveCPU<C> + MapToEdwardsParams<C::Base>,
{
fn map_to_curve(
&self,
layouter: &mut impl Layouter<C::Base>,
u: &Self::Coordinate,
) -> Result<AssignedNativePoint<C>, Error> {
let (weierstrass_x, weierstrass_y) = svdw_map_to_weierstrass::<C::Base, C, Self::Coordinate>(
layouter,
self.base_field(),
self.native_gadget(),
u,
)?;
let (montgomery_x, montgomery_y) = weierstrass_to_montgomery::<C::Base, C, Self::Coordinate>(
layouter,
self.base_field(),
&weierstrass_x,
&weierstrass_y,
)?;
let (edwards_x, edwards_y) = montgomery_to_edwards::<C::Base, C, Self::Coordinate>(
layouter,
self.base_field(),
&montgomery_x,
&montgomery_y,
)?;
let point = self.point_from_coordinates_unsafe(layouter, &edwards_x, &edwards_y)?;
self.clear_cofactor(layouter, &point)
}
}
fn svdw_map_to_weierstrass<F, C, T>(
layouter: &mut impl Layouter<F>,
base_field: &impl DecompositionInstructions<F, T>,
bool_chip: &(impl BinaryInstructions<F> + EqualityInstructions<F, AssignedBit<F>>),
u: &T,
) -> Result<(T, T), Error>
where
F: CircuitField,
C: CircuitCurve + MapToWeierstrassParams<C::Base>,
T: InnerValue<Element = C::Base> + Instantiable<F> + InnerConstants + Clone,
{
let tv1 = base_field.mul(layouter, u, u, None)?;
let tv1 = base_field.mul_by_constant(layouter, &tv1, C::c1())?;
let tv2 = base_field.add_constant(layouter, &tv1, C::Base::ONE)?;
let tv1 = base_field.linear_combination(layouter, &[(-C::Base::ONE, tv1)], C::Base::ONE)?;
let tv3 = base_field.mul(layouter, &tv1, &tv2, None)?;
let tv3 = base_field.inv0(layouter, &tv3)?;
let tv4 = base_field.mul(layouter, u, &tv1, None)?;
let tv4 = base_field.mul(layouter, &tv4, &tv3, None)?;
let tv4 = base_field.mul_by_constant(layouter, &tv4, C::c3())?;
let x1 = base_field.linear_combination(layouter, &[(-C::Base::ONE, tv4.clone())], C::c2())?;
let gx1 = base_field.mul(layouter, &x1, &x1, None)?;
let gx1 = base_field.add_constant(layouter, &gx1, C::A)?;
let gx1 = base_field.mul(layouter, &gx1, &x1, None)?;
let gx1 = base_field.add_constant(layouter, &gx1, C::B)?;
let e1 = base_field.is_square(layouter, &gx1)?;
let x2 = base_field.add_constant(layouter, &tv4, C::c2())?;
let gx2 = base_field.mul(layouter, &x2, &x2, None)?;
let gx2 = base_field.add_constant(layouter, &gx2, C::A)?;
let gx2 = base_field.mul(layouter, &gx2, &x2, None)?;
let gx2 = base_field.add_constant(layouter, &gx2, C::B)?;
let e2 = {
let e2 = base_field.is_square(layouter, &gx2)?;
let not_e1 = bool_chip.not(layouter, &e1)?;
bool_chip.and(layouter, &[e2, not_e1])?
};
let x3 = base_field.mul(layouter, &tv2, &tv2, None)?;
let x3 = base_field.mul(layouter, &x3, &tv3, None)?;
let x3 = base_field.mul(layouter, &x3, &x3, None)?;
let x3 = base_field.mul_by_constant(layouter, &x3, C::c4())?;
let x3 = base_field.add_constant(layouter, &x3, C::SVDW_Z)?;
let x = base_field.select(layouter, &e1, &x1, &x3)?;
let x = base_field.select(layouter, &e2, &x2, &x)?;
let gx = base_field.mul(layouter, &x, &x, None)?;
let gx = base_field.add_constant(layouter, &gx, C::A)?;
let gx = base_field.mul(layouter, &gx, &x, None)?;
let gx = base_field.add_constant(layouter, &gx, C::B)?;
let y = {
let y_val = gx
.value()
.map(|gx| gx.sqrt().expect("gx should be a quadratic residue but is not."));
let y = base_field.assign(layouter, y_val)?;
let y_square = base_field.mul(layouter, &y, &y, None)?;
base_field.assert_equal(layouter, &y_square, &gx)?;
y
};
let e3 = {
let sgn0_u = base_field.sgn0(layouter, u)?;
let sgn0_y = base_field.sgn0(layouter, &y)?;
bool_chip.is_equal(layouter, &sgn0_u, &sgn0_y)?
};
let y = {
let minus_y = base_field.neg(layouter, &y)?;
base_field.select(layouter, &e3, &y, &minus_y)?
};
Ok((x, y))
}
fn weierstrass_to_montgomery<F, C, T>(
layouter: &mut impl Layouter<F>,
base_field: &impl FieldInstructions<F, T>,
x: &T,
y: &T,
) -> Result<(T, T), Error>
where
F: CircuitField,
C: CircuitCurve + MapToEdwardsParams<C::Base>,
T: InnerValue<Element = C::Base> + Instantiable<F> + InnerConstants + Clone,
{
let tv1 = base_field.mul_by_constant(layouter, x, C::MONT_K)?;
let x_prime = base_field.add_constant(
layouter,
&tv1,
-C::MONT_J * C::Base::from(3).invert().unwrap(),
)?;
let y_prime = base_field.mul_by_constant(layouter, y, C::MONT_K)?;
Ok((x_prime, y_prime))
}
fn montgomery_to_edwards<F, C, T>(
layouter: &mut impl Layouter<F>,
base_field: &impl FieldInstructions<F, T>,
x: &T,
y: &T,
) -> Result<(T, T), Error>
where
F: CircuitField,
C: CircuitCurve + MapToEdwardsParams<C::Base>,
T: InnerValue<Element = C::Base> + Instantiable<F> + InnerConstants + Clone,
{
let mut tv1 = base_field.add_constant(layouter, x, C::Base::ONE)?;
let mut tv2 = base_field.mul(layouter, &tv1, y, None)?;
tv2 = base_field.inv0(layouter, &tv2)?;
let mut x_prime = base_field.mul(layouter, &tv2, &tv1, None)?;
x_prime = base_field.mul(layouter, &x_prime, x, None)?;
let mut y_prime = base_field.mul(layouter, &tv2, y, None)?;
tv1 = base_field.add_constant(layouter, x, -C::Base::ONE)?;
y_prime = base_field.mul(layouter, &y_prime, &tv1, None)?;
let e = base_field.is_zero(layouter, &tv2)?;
let assigned_one = base_field.assign_fixed(layouter, C::Base::ONE)?;
y_prime = base_field.select(layouter, &e, &assigned_one, &y_prime)?;
Ok((x_prime, y_prime))
}