Skip to content

Commit

Permalink
feat(blockifier): add secp-related logic
Browse files Browse the repository at this point in the history
  • Loading branch information
xrvdg authored and rodrigo-pino committed Nov 14, 2024
1 parent a07960d commit c737b8b
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 52 deletions.
2 changes: 2 additions & 0 deletions crates/blockifier/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
88 changes: 88 additions & 0 deletions crates/blockifier/src/execution/native/syscall_handler.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::collections::HashSet;
use std::convert::From;
use std::fmt;
use std::hash::RandomState;
use std::sync::Arc;

use ark_ec::short_weierstrass::{Affine, Projective, SWCurveConfig};
use ark_ff::PrimeField;
use cairo_native::starknet::{
BlockInfo,
ExecutionInfo,
Expand All @@ -14,6 +18,7 @@ use cairo_native::starknet::{
TxV2Info,
U256,
};
use cairo_native::starknet_stub::u256_to_biguint;
use cairo_vm::vm::runners::cairo_runner::ExecutionResources;
use starknet_api::contract_class::EntryPointType;
use starknet_api::core::{
Expand Down Expand Up @@ -45,6 +50,7 @@ use crate::execution::entry_point::{
use crate::execution::errors::EntryPointExecutionError;
use crate::execution::execution_utils::execute_deployment;
use crate::execution::native::utils::{calculate_resource_bounds, default_tx_v2_info};
use crate::execution::secp;
use crate::execution::syscalls::exceeds_event_size_limit;
use crate::execution::syscalls::hint_processor::{
SyscallExecutionError,
Expand Down Expand Up @@ -700,3 +706,85 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> {
Ok(())
}
}

/// A wrapper around an elliptic curve point in affine coordinates (x,y) on a
/// short Weierstrass curve, specifically for Secp256k1/r1 curves.
///
/// This type provides a unified interface for working with points on both
/// secp256k1 and secp256r1 curves through the generic `Curve` parameter.
#[derive(PartialEq, Clone, Copy)]
struct Secp256Point<Curve: SWCurveConfig>(Affine<Curve>);

// todo(xrvdg) remove dead_code annotation after adding syscalls
#[allow(dead_code)]
impl<Curve: SWCurveConfig> Secp256Point<Curve>
where
Curve::BaseField: PrimeField, // constraint for get_point_by_id
{
fn wrap_secp_result<T>(
result: Result<Option<T>, SyscallExecutionError>,
) -> Result<Option<Secp256Point<Curve>>, SyscallExecutionError>
where
T: Into<Affine<Curve>>,
{
match result {
Ok(None) => Ok(None),
Ok(Some(point)) => Ok(Some(Secp256Point(point.into()))),
Err(error) => Err(error),
}
}

/// Given an (x, y) pair, this function:
/// - Returns the point at infinity for (0, 0).
/// - Returns `Err` if either `x` or `y` is outside the modulus.
/// - Returns `Ok(None)` if (x, y) are within the modulus but not on the curve.
/// - Ok(Some(Point)) if (x,y) are on the curve.
fn new(x: U256, y: U256) -> Result<Option<Self>, SyscallExecutionError> {
let x = u256_to_biguint(x);
let y = u256_to_biguint(y);

Self::wrap_secp_result(secp::new_affine(x, y))
}

fn add(p0: Self, p1: Self) -> Self {
let result: Projective<Curve> = 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<Option<Self>, SyscallExecutionError> {
let x = u256_to_biguint(x);

Self::wrap_secp_result(secp::get_point_from_x(x, y_parity))
}
}

impl<Curve: SWCurveConfig> fmt::Debug for Secp256Point<Curve> {
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 crate::execution::native::syscall_handler::Secp256Point;

#[test]
fn infinity_test() {
let p1 =
Secp256Point::<ark_secp256k1::Config>::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));
}
}
80 changes: 80 additions & 0 deletions crates/blockifier/src/execution/secp.rs
Original file line number Diff line number Diff line change
@@ -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<Curve: SWCurveConfig>(
x: num_bigint::BigUint,
y_parity: bool,
) -> Result<Option<Affine<Curve>>, SyscallExecutionError>
where
Curve::BaseField: PrimeField, // constraint for get_point_by_id
{
modulus_bound_check::<Curve>(&[&x])?;

let x = x.into();
let maybe_ec_point = Affine::<Curve>::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::<Curve>::new_unchecked(x, y))
.filter(|p| p.is_in_correct_subgroup_assuming_on_curve());

Ok(maybe_ec_point)
}

pub fn new_affine<Curve: SWCurveConfig>(
x: num_bigint::BigUint,
y: num_bigint::BigUint,
) -> Result<Option<Affine<Curve>>, SyscallExecutionError>
where
Curve::BaseField: PrimeField, // constraint for get_point_by_id
{
modulus_bound_check::<Curve>(&[&x, &y])?;

Ok(maybe_affine(x.into(), y.into()))
}

fn modulus_bound_check<Curve: SWCurveConfig>(
bounds: &[&num_bigint::BigUint],
) -> Result<(), SyscallExecutionError>
where
Curve::BaseField: PrimeField, // constraint for get_point_by_id
{
let modulus = Curve::BaseField::MODULUS.into();

if bounds.iter().any(|p| **p >= modulus) {
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<Curve>::new`] that doesn't panic and maps (x,y) = (0,0) -> infinity
fn maybe_affine<Curve: SWCurveConfig>(
x: Curve::BaseField,
y: Curve::BaseField,
) -> Option<Affine<Curve>> {
let ec_point = if x.is_zero() && y.is_zero() {
Affine::<Curve>::identity()
} else {
Affine::<Curve>::new_unchecked(x, y)
};

if ec_point.is_on_curve() && ec_point.is_in_correct_subgroup_assuming_on_curve() {
Some(ec_point)
} else {
None
}
}
66 changes: 14 additions & 52 deletions crates/blockifier/src/execution/syscalls/secp.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -40,8 +36,8 @@ where
}

pub fn secp_mul(&mut self, request: SecpMulRequest) -> SyscallResult<SecpMulResponse> {
let ep_point = self.get_point_by_id(request.ec_point_id)?;
let result = *ep_point * Curve::ScalarField::from(request.multiplier);
let ec_point = self.get_point_by_id(request.ec_point_id)?;
let result = *ec_point * Curve::ScalarField::from(request.multiplier);
let ec_point_id = self.allocate_point(result.into());
Ok(SecpOpRespone { ec_point_id })
}
Expand All @@ -50,26 +46,9 @@ where
&mut self,
request: SecpGetPointFromXRequest,
) -> SyscallResult<SecpGetPointFromXResponse> {
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::<Curve>::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::<Curve>::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)),
})
}
Expand All @@ -81,27 +60,10 @@ where
}

pub fn secp_new(&mut self, request: SecpNewRequest) -> SyscallResult<SecpNewResponse> {
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::<Curve>::identity()
} else {
short_weierstrass::Affine::<Curve>::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::<Curve>(request.x, request.y)?;
Ok(SecpNewResponse {
optional_ec_point_id: affine.map(|ec_point| self.allocate_point(ec_point)),
})
}

fn allocate_point(&mut self, ec_point: short_weierstrass::Affine<Curve>) -> usize {
Expand Down

0 comments on commit c737b8b

Please sign in to comment.