diff --git a/crates/blockifier/src/execution.rs b/crates/blockifier/src/execution.rs index e3b5567d77..cc4a0b67a5 100644 --- a/crates/blockifier/src/execution.rs +++ b/crates/blockifier/src/execution.rs @@ -9,6 +9,8 @@ pub mod entry_point_execution; pub mod errors; pub mod execution_utils; pub mod hint_code; +pub mod secp; + #[cfg(feature = "cairo_native")] pub mod native; pub mod stack_trace; diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index 8dde577c95..665a7c46a4 100644 --- a/crates/blockifier/src/execution/native/syscall_handler.rs +++ b/crates/blockifier/src/execution/native/syscall_handler.rs @@ -1,6 +1,10 @@ use std::collections::HashSet; +use std::convert::From; +use std::fmt; use std::hash::RandomState; +use ark_ec::short_weierstrass::{Affine, Projective, SWCurveConfig}; +use ark_ff::PrimeField; use cairo_native::starknet::{ ExecutionInfo, ExecutionInfoV2, @@ -10,6 +14,7 @@ use cairo_native::starknet::{ SyscallResult, U256, }; +use cairo_native::starknet_stub::u256_to_biguint; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use starknet_api::state::StorageKey; use starknet_types_core::felt::Felt; @@ -17,6 +22,7 @@ use starknet_types_core::felt::Felt; use crate::execution::call_info::{CallInfo, OrderedEvent, OrderedL2ToL1Message, Retdata}; use crate::execution::entry_point::{CallEntryPoint, EntryPointExecutionContext}; use crate::execution::native::utils::encode_str_as_felts; +use crate::execution::secp; use crate::execution::syscalls::hint_processor::{SyscallCounter, OUT_OF_GAS_ERROR}; use crate::execution::syscalls::SyscallSelector; use crate::state::state_api::State; @@ -320,3 +326,77 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> { todo!("Implement sha256_process_block syscall."); } } + +// todo(xrvdg) remove dead_code annotation after adding syscalls +#[allow(dead_code)] +impl Secp256Point +where + Curve::BaseField: PrimeField, // constraint for get_point_by_id +{ + /// Given a (x,y) pair it will + /// - return the point at infinity for (0,0) + /// - Err if either x or y is outside of the modulus + /// - Ok(None) if (x,y) are within the modules but not on the curve + /// - Ok(Some(Point)) if (x,y) are on the curve + fn new(x: U256, y: U256) -> Result, Vec> { + let x = u256_to_biguint(x); + let y = u256_to_biguint(y); + + match secp::new_affine(x, y) { + Ok(None) => Ok(None), + Ok(Some(affine)) => Ok(Some(Secp256Point(affine))), + Err(error) => Err(encode_str_as_felts(&error.to_string())), + } + } + + fn add(p0: Self, p1: Self) -> Self { + let result: Projective = p0.0 + p1.0; + Secp256Point(result.into()) + } + + fn mul(p: Self, m: U256) -> Self { + let result = p.0 * Curve::ScalarField::from(u256_to_biguint(m)); + Secp256Point(result.into()) + } + + fn get_point_from_x(x: U256, y_parity: bool) -> Result, Vec> { + let x = u256_to_biguint(x); + + match secp::get_point_from_x(x, y_parity) { + Ok(None) => Ok(None), + Ok(Some(point)) => Ok(Some(Secp256Point(point))), + Err(error) => Err(encode_str_as_felts(&error.to_string())), + } + } +} + +/// Data structure to tie together k1 and r1 points to it's corresponding +/// `Affine` +#[derive(PartialEq, Clone, Copy)] +struct Secp256Point(Affine); + +impl fmt::Debug for Secp256Point { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Secp256Point").field(&self.0).finish() + } +} + +#[cfg(test)] +mod test { + use cairo_native::starknet::U256; + + use super::Secp256Point; + + #[test] + fn infinity_test() { + let p1 = + Secp256Point::::get_point_from_x(U256 { lo: 1, hi: 0 }, false) + .unwrap() + .unwrap(); + + let p2 = Secp256Point::mul(p1, U256 { lo: 0, hi: 0 }); + assert!(p2.0.infinity); + + assert_eq!(p1, Secp256Point::add(p1, p2)); + } +} diff --git a/crates/blockifier/src/execution/secp.rs b/crates/blockifier/src/execution/secp.rs new file mode 100644 index 0000000000..3d630dd051 --- /dev/null +++ b/crates/blockifier/src/execution/secp.rs @@ -0,0 +1,80 @@ +use ark_ec::short_weierstrass::{Affine, SWCurveConfig}; +use ark_ff::{PrimeField, Zero}; +use starknet_types_core::felt::Felt; + +use super::syscalls::hint_processor::{SyscallExecutionError, INVALID_ARGUMENT}; + +pub fn get_point_from_x( + x: num_bigint::BigUint, + y_parity: bool, +) -> Result>, SyscallExecutionError> +where + Curve::BaseField: PrimeField, // constraint for get_point_by_id +{ + modulus_bound_check::(&[&x])?; + + let x = x.into(); + let maybe_ec_point = Affine::::get_ys_from_x_unchecked(x) + .map(|(smaller, greater)| { + // Return the correct y coordinate based on the parity. + if ark_ff::BigInteger::is_odd(&smaller.into_bigint()) == y_parity { + smaller + } else { + greater + } + }) + .map(|y| Affine::::new_unchecked(x, y)) + .filter(|p| p.is_in_correct_subgroup_assuming_on_curve()); + + Ok(maybe_ec_point) +} + +pub fn new_affine( + x: num_bigint::BigUint, + y: num_bigint::BigUint, +) -> Result>, SyscallExecutionError> +where + Curve::BaseField: PrimeField, // constraint for get_point_by_id +{ + modulus_bound_check::(&[&x, &y])?; + + Ok(maybe_affine(x.into(), y.into())) +} + +fn modulus_bound_check( + bounds: &[&num_bigint::BigUint], +) -> Result<(), SyscallExecutionError> +where + Curve::BaseField: PrimeField, // constraint for get_point_by_id +{ + let modulos = Curve::BaseField::MODULUS.into(); + + if bounds.iter().any(|p| **p >= modulos) { + let error = match Felt::from_hex(INVALID_ARGUMENT) { + Ok(err) => SyscallExecutionError::SyscallError { error_data: vec![err] }, + Err(err) => SyscallExecutionError::from(err), + }; + + return Err(error); + } + + Ok(()) +} + +/// Variation on [`Affine::new`] that doesn't panic and maps (x,y) = (0,0) -> infinity +fn maybe_affine( + x: Curve::BaseField, + y: Curve::BaseField, +) -> Option> { + let ec_point = if x.is_zero() && y.is_zero() { + Affine::::identity() + } else { + Affine::::new_unchecked(x, y) + }; + + if ec_point.is_on_curve() && ec_point.is_in_correct_subgroup_assuming_on_curve() { + Some(ec_point) + } else { + None + } +} diff --git a/crates/blockifier/src/execution/syscalls/secp.rs b/crates/blockifier/src/execution/syscalls/secp.rs index d23bbd15dc..5e0fe46a70 100644 --- a/crates/blockifier/src/execution/syscalls/secp.rs +++ b/crates/blockifier/src/execution/syscalls/secp.rs @@ -1,19 +1,15 @@ -use ark_ec::short_weierstrass; -use ark_ec::short_weierstrass::SWCurveConfig; -use ark_ff::{BigInteger, PrimeField}; +use ark_ec::short_weierstrass::{self, SWCurveConfig}; +use ark_ff::PrimeField; use cairo_vm::types::relocatable::Relocatable; use cairo_vm::vm::vm_core::VirtualMachine; use num_bigint::BigUint; -use num_traits::{ToPrimitive, Zero}; +use num_traits::ToPrimitive; use starknet_types_core::felt::Felt; use crate::abi::sierra_types::{SierraType, SierraU256}; use crate::execution::execution_utils::{felt_from_ptr, write_maybe_relocatable, write_u256}; -use crate::execution::syscalls::hint_processor::{ - felt_to_bool, - SyscallHintProcessor, - INVALID_ARGUMENT, -}; +use crate::execution::secp::new_affine; +use crate::execution::syscalls::hint_processor::{felt_to_bool, SyscallHintProcessor}; use crate::execution::syscalls::{ SyscallExecutionError, SyscallRequest, @@ -50,26 +46,9 @@ where &mut self, request: SecpGetPointFromXRequest, ) -> SyscallResult { - let modulos = Curve::BaseField::MODULUS.into(); - - if request.x >= modulos { - return Err(SyscallExecutionError::SyscallError { - error_data: vec![ - Felt::from_hex(INVALID_ARGUMENT).map_err(SyscallExecutionError::from)?, - ], - }); - } - - let x = request.x.into(); - let maybe_ec_point = short_weierstrass::Affine::::get_ys_from_x_unchecked(x) - .map(|(smaller, greater)| { - // Return the correct y coordinate based on the parity. - if smaller.into_bigint().is_odd() == request.y_parity { smaller } else { greater } - }) - .map(|y| short_weierstrass::Affine::::new_unchecked(x, y)) - .filter(|p| p.is_in_correct_subgroup_assuming_on_curve()); - - Ok(SecpGetPointFromXResponse { + let affine = crate::execution::secp::get_point_from_x(request.x, request.y_parity); + + affine.map(|maybe_ec_point| SecpGetPointFromXResponse { optional_ec_point_id: maybe_ec_point.map(|ec_point| self.allocate_point(ec_point)), }) } @@ -81,27 +60,10 @@ where } pub fn secp_new(&mut self, request: SecpNewRequest) -> SyscallResult { - let modulos = Curve::BaseField::MODULUS.into(); - let (x, y) = (request.x, request.y); - if x >= modulos || y >= modulos { - return Err(SyscallExecutionError::SyscallError { - error_data: vec![ - Felt::from_hex(INVALID_ARGUMENT).map_err(SyscallExecutionError::from)?, - ], - }); - } - let ec_point = if x.is_zero() && y.is_zero() { - short_weierstrass::Affine::::identity() - } else { - short_weierstrass::Affine::::new_unchecked(x.into(), y.into()) - }; - let optional_ec_point_id = - if ec_point.is_on_curve() && ec_point.is_in_correct_subgroup_assuming_on_curve() { - Some(self.allocate_point(ec_point)) - } else { - None - }; - Ok(SecpNewResponse { optional_ec_point_id }) + let affine = new_affine::(request.x, request.y); + affine.map(|maybe_ec_point| SecpNewResponse { + optional_ec_point_id: maybe_ec_point.map(|point| self.allocate_point(point)), + }) } fn allocate_point(&mut self, ec_point: short_weierstrass::Affine) -> usize {