use miden_core::deferred::{DeferredError, PrecompileError};
use super::{CurvePoint, ShortWeierstrassSpec};
use crate::math::uint::{Limbs, UintSpec, ZERO_LIMBS};
const ONE_LIMBS: Limbs = [1, 0, 0, 0, 0, 0, 0, 0];
pub(super) fn point_from_affine<C: ShortWeierstrassSpec>(
x: Limbs,
y: Limbs,
) -> Result<CurvePoint, PrecompileError> {
if !affine_coordinates_on_curve::<C>(x, y) {
return Err(DeferredError::InvalidPayload.into());
}
Ok(CurvePoint::Affine { x, y })
}
pub(super) fn neg<C: ShortWeierstrassSpec>(point: CurvePoint) -> CurvePoint {
debug_assert!(C::is_on_curve(&point));
match point {
CurvePoint::Identity => CurvePoint::Identity,
CurvePoint::Affine { x, y } => {
CurvePoint::Affine { x, y: C::BaseField::sub(ZERO_LIMBS, y) }
},
}
}
pub(super) fn add<C: ShortWeierstrassSpec>(
lhs: CurvePoint,
rhs: CurvePoint,
) -> Result<CurvePoint, PrecompileError> {
debug_assert!(C::is_on_curve(&lhs));
debug_assert!(C::is_on_curve(&rhs));
match (lhs, rhs) {
(CurvePoint::Identity, point) | (point, CurvePoint::Identity) => Ok(point),
(CurvePoint::Affine { x: x1, y: y1 }, CurvePoint::Affine { x: x2, y: y2 }) => {
if x1 == x2 && (y1 != y2 || y1 == ZERO_LIMBS) {
return Ok(CurvePoint::Identity);
}
let numerator = if x1 == x2 {
let x1_squared = C::BaseField::mul(x1, x1);
let two_x1_squared = C::BaseField::add(x1_squared, x1_squared);
C::BaseField::add(C::BaseField::add(two_x1_squared, x1_squared), C::A)
} else {
C::BaseField::sub(y2, y1)
};
let denominator = if x1 == x2 {
C::BaseField::add(y1, y1)
} else {
C::BaseField::sub(x2, x1)
};
let denominator_inv =
C::BaseField::inv(denominator).ok_or(DeferredError::InvalidPayload)?;
let lambda = C::BaseField::mul(numerator, denominator_inv);
let lambda_squared = C::BaseField::mul(lambda, lambda);
let x3 = C::BaseField::sub(C::BaseField::sub(lambda_squared, x1), x2);
let x1_minus_x3 = C::BaseField::sub(x1, x3);
let y3 = C::BaseField::sub(C::BaseField::mul(lambda, x1_minus_x3), y1);
let point = CurvePoint::Affine { x: x3, y: y3 };
debug_assert!(C::is_on_curve(&point));
Ok(point)
},
}
}
pub(super) fn mul_scalar<C: ShortWeierstrassSpec>(
point: CurvePoint,
scalar: Limbs,
) -> Result<CurvePoint, PrecompileError> {
debug_assert!(C::is_on_curve(&point));
debug_assert!(C::ScalarField::is_canonical(&scalar));
let Some(highest_limb) = scalar.iter().rposition(|&limb| limb != 0) else {
return Ok(CurvePoint::Identity);
};
let highest_bit =
highest_limb * 32 + (u32::BITS - 1 - scalar[highest_limb].leading_zeros()) as usize;
let (x, y) = match point {
CurvePoint::Identity => return Ok(CurvePoint::Identity),
CurvePoint::Affine { x, y } => (x, y),
};
let mut acc = JACOBIAN_IDENTITY;
for bit_index in (0..=highest_bit).rev() {
acc = jacobian_double::<C>(acc);
if ((scalar[bit_index / 32] >> (bit_index % 32)) & 1) == 1 {
acc = jacobian_add_affine::<C>(acc, x, y);
}
}
let result = jacobian_to_affine::<C>(acc)?;
debug_assert!(C::is_on_curve(&result));
Ok(result)
}
type JacobianPoint = (Limbs, Limbs, Limbs);
const JACOBIAN_IDENTITY: JacobianPoint = (ONE_LIMBS, ONE_LIMBS, ZERO_LIMBS);
fn jacobian_double<C: ShortWeierstrassSpec>(point: JacobianPoint) -> JacobianPoint {
let (x1, y1, z1) = point;
if z1 == ZERO_LIMBS || y1 == ZERO_LIMBS {
return JACOBIAN_IDENTITY;
}
let xx = C::BaseField::mul(x1, x1);
let yy = C::BaseField::mul(y1, y1);
let yyyy = C::BaseField::mul(yy, yy);
let zz = C::BaseField::mul(z1, z1);
let x1_plus_yy = C::BaseField::add(x1, yy);
let s_inner =
C::BaseField::sub(C::BaseField::sub(C::BaseField::mul(x1_plus_yy, x1_plus_yy), xx), yyyy);
let s = C::BaseField::add(s_inner, s_inner);
let zzzz = C::BaseField::mul(zz, zz);
let a_zzzz = C::BaseField::mul(C::A, zzzz);
let three_xx = C::BaseField::add(C::BaseField::add(xx, xx), xx);
let m = C::BaseField::add(three_xx, a_zzzz);
let x3 = C::BaseField::sub(C::BaseField::mul(m, m), C::BaseField::add(s, s));
let two_yyyy = C::BaseField::add(yyyy, yyyy);
let four_yyyy = C::BaseField::add(two_yyyy, two_yyyy);
let eight_yyyy = C::BaseField::add(four_yyyy, four_yyyy);
let y3 = C::BaseField::sub(C::BaseField::mul(m, C::BaseField::sub(s, x3)), eight_yyyy);
let y1z1 = C::BaseField::mul(y1, z1);
let z3 = C::BaseField::add(y1z1, y1z1);
(x3, y3, z3)
}
fn jacobian_add_affine<C: ShortWeierstrassSpec>(
point: JacobianPoint,
x2: Limbs,
y2: Limbs,
) -> JacobianPoint {
let (x1, y1, z1) = point;
if z1 == ZERO_LIMBS {
return (x2, y2, ONE_LIMBS);
}
let zz = C::BaseField::mul(z1, z1);
let u2 = C::BaseField::mul(x2, zz);
let s2 = C::BaseField::mul(C::BaseField::mul(y2, z1), zz);
if u2 == x1 {
return if s2 == y1 {
jacobian_double::<C>(point)
} else {
JACOBIAN_IDENTITY
};
}
let h = C::BaseField::sub(u2, x1);
let hh = C::BaseField::mul(h, h);
let two_hh = C::BaseField::add(hh, hh);
let i = C::BaseField::add(two_hh, two_hh);
let j = C::BaseField::mul(h, i);
let s2_minus_y1 = C::BaseField::sub(s2, y1);
let r = C::BaseField::add(s2_minus_y1, s2_minus_y1);
let v = C::BaseField::mul(x1, i);
let x3 =
C::BaseField::sub(C::BaseField::sub(C::BaseField::mul(r, r), j), C::BaseField::add(v, v));
let y1j = C::BaseField::mul(y1, j);
let y3 = C::BaseField::sub(
C::BaseField::mul(r, C::BaseField::sub(v, x3)),
C::BaseField::add(y1j, y1j),
);
let z1_plus_h = C::BaseField::add(z1, h);
let z3 = C::BaseField::sub(C::BaseField::sub(C::BaseField::mul(z1_plus_h, z1_plus_h), zz), hh);
(x3, y3, z3)
}
fn jacobian_to_affine<C: ShortWeierstrassSpec>(
point: JacobianPoint,
) -> Result<CurvePoint, PrecompileError> {
let (x, y, z) = point;
if z == ZERO_LIMBS {
return Ok(CurvePoint::Identity);
}
let z_inv = C::BaseField::inv(z).ok_or(DeferredError::InvalidPayload)?;
let z_inv2 = C::BaseField::mul(z_inv, z_inv);
let z_inv3 = C::BaseField::mul(z_inv2, z_inv);
Ok(CurvePoint::Affine {
x: C::BaseField::mul(x, z_inv2),
y: C::BaseField::mul(y, z_inv3),
})
}
fn affine_coordinates_on_curve<C: ShortWeierstrassSpec>(x: Limbs, y: Limbs) -> bool {
if !C::BaseField::is_canonical(&x) || !C::BaseField::is_canonical(&y) {
return false;
}
let y2 = C::BaseField::mul(y, y);
let x2 = C::BaseField::mul(x, x);
let x3 = C::BaseField::mul(x2, x);
let ax = C::BaseField::mul(C::A, x);
let rhs = C::BaseField::add(C::BaseField::add(x3, ax), C::B);
y2 == rhs
}