diff --git a/crates/starknet-types-core/src/curve/affine_point.rs b/crates/starknet-types-core/src/curve/affine_point.rs index 5ee09db2..6ed616a9 100644 --- a/crates/starknet-types-core/src/curve/affine_point.rs +++ b/crates/starknet-types-core/src/curve/affine_point.rs @@ -4,6 +4,7 @@ use lambdaworks_math::{ elliptic_curve::{ short_weierstrass::{ curves::stark_curve::StarkCurve, point::ShortWeierstrassProjectivePoint, + traits::IsShortWeierstrass, }, traits::{FromAffine, IsEllipticCurve}, }, @@ -33,6 +34,28 @@ impl AffinePoint { ])) } + /// Construct new affine point from the `x` coordinate and the parity bit `y_parity`. + /// If `y_parity` is false, choose the y-coordinate with even parity. + /// If `y_parity` is true, choose the y-coordinate with odd parity. + pub fn new_from_x(x: &Felt, y_parity: bool) -> Option { + // right hand side of the stark curve equation `y^2 = x^3 + α*x + β (mod p)`. + let rhs = x * x * x + Felt(StarkCurve::a()) * x + Felt(StarkCurve::b()); + + let (root_1, root_2) = rhs.0.sqrt()?; + + let root_1_le_bits = root_1.to_bits_le(); + let first_bit = root_1_le_bits.first()?; + + let y = if *first_bit == y_parity { + root_1 + } else { + root_2 + }; + + // the curve equation is already satisfied above and is safe to create a new unchecked point + Some(Self::new_unchecked(*x, Felt(y))) + } + /// The point at infinity. pub fn identity() -> AffinePoint { Self(ShortWeierstrassProjectivePoint::neutral_element()) @@ -86,6 +109,7 @@ impl core::ops::Mul for &AffinePoint { #[cfg(test)] mod test { use super::*; + use std::ops::Neg; #[test] fn affine_point_new_unchecked() { @@ -191,4 +215,30 @@ mod test { .unwrap() ); } + + #[test] + fn affine_new_from_x_odd() { + let p = AffinePoint::new( + Felt::from_hex_unchecked("0x2d39148a92f479fb077389d"), + Felt::from_hex_unchecked( + "0x6e5d97edf7283fe7a7fe9deef2619224f42cb1bd531dd23380ad066c61ee20b", + ), + ) + .unwrap(); + + assert_eq!(p, AffinePoint::new_from_x(&p.x(), true).unwrap()); + } + + #[test] + fn affine_new_from_x_even() { + let p = AffinePoint::new( + Felt::from_hex_unchecked("0x2d39148a92f479fb077389d"), + Felt::from_hex_unchecked( + "0x6e5d97edf7283fe7a7fe9deef2619224f42cb1bd531dd23380ad066c61ee20b", + ), + ) + .unwrap(); + + assert_eq!(p.neg(), AffinePoint::new_from_x(&p.x(), false).unwrap()); + } }