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 11, 2024
1 parent 1224df1 commit c2d4d1e
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 50 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
80 changes: 80 additions & 0 deletions crates/blockifier/src/execution/native/syscall_handler.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -10,13 +14,15 @@ 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;

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;
Expand Down Expand Up @@ -319,3 +325,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<Curve: SWCurveConfig> Secp256Point<Curve>
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<Option<Self>, Vec<Felt>> {
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<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>, Vec<Felt>> {
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<Curve>`
#[derive(PartialEq, Clone, Copy)]
struct Secp256Point<Curve: SWCurveConfig>(Affine<Curve>);

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 super::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 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<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
}
}
62 changes: 12 additions & 50 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 Down Expand Up @@ -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);
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<Curve>) -> usize {
Expand Down

0 comments on commit c2d4d1e

Please sign in to comment.