From 6558eea3a1b28dd6ec053978d8291971f0f3a8b6 Mon Sep 17 00:00:00 2001 From: Pearson White Date: Tue, 29 Oct 2024 13:32:32 -0400 Subject: [PATCH] feat(blockifier): add Secp256r1 cairo native syscalls --- crates/blockifier/cairo_native | 2 +- .../src/execution/native/syscall_handler.rs | 236 ++++++++++++++++-- .../execution/syscalls/syscall_tests/secp.rs | 1 + 3 files changed, 214 insertions(+), 25 deletions(-) diff --git a/crates/blockifier/cairo_native b/crates/blockifier/cairo_native index b5769e4f6ba..e9f52b5cd26 160000 --- a/crates/blockifier/cairo_native +++ b/crates/blockifier/cairo_native @@ -1 +1 @@ -Subproject commit b5769e4f6ba914b36eef68e0b1f71c791d7d075c +Subproject commit e9f52b5cd26d5d3610879a5661ee8625739175a8 diff --git a/crates/blockifier/src/execution/native/syscall_handler.rs b/crates/blockifier/src/execution/native/syscall_handler.rs index 665a7c46a4b..2e7ebaa6eb4 100644 --- a/crates/blockifier/src/execution/native/syscall_handler.rs +++ b/crates/blockifier/src/execution/native/syscall_handler.rs @@ -14,8 +14,10 @@ use cairo_native::starknet::{ SyscallResult, U256, }; -use cairo_native::starknet_stub::u256_to_biguint; +use cairo_native::starknet_stub::{big4int_to_u256, encode_str_as_felts, u256_to_biguint}; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; +use cairo_vm::with_std::fmt; +use num_traits::Zero; use starknet_api::state::StorageKey; use starknet_types_core::felt::Felt; @@ -275,55 +277,241 @@ impl<'state> StarknetSyscallHandler for &mut NativeSyscallHandler<'state> { fn secp256r1_new( &mut self, - _x: U256, - _y: U256, - _remaining_gas: &mut u128, + x: U256, + y: U256, + remaining_gas: &mut u128, ) -> SyscallResult> { - todo!("Implement secp256r1_new syscall."); + self.substract_syscall_gas_cost( + remaining_gas, + SyscallSelector::Secp256r1New, + self.context.gas_costs().secp256r1_new_gas_cost, + )?; + + Secp256Point::new(x, y).map(|op| op.map(|p| p.into())) } fn secp256r1_add( &mut self, - _p0: Secp256r1Point, - _p1: Secp256r1Point, - _remaining_gas: &mut u128, + p0: Secp256r1Point, + p1: Secp256r1Point, + remaining_gas: &mut u128, ) -> SyscallResult { - todo!("Implement secp256r1_add syscall."); + self.substract_syscall_gas_cost( + remaining_gas, + SyscallSelector::Secp256r1Add, + self.context.gas_costs().secp256r1_new_gas_cost, + )?; + Ok(Secp256Point::add(p0.into(), p1.into()).into()) } fn secp256r1_mul( &mut self, - _p: Secp256r1Point, - _m: U256, - _remaining_gas: &mut u128, + p: Secp256r1Point, + m: U256, + remaining_gas: &mut u128, ) -> SyscallResult { - todo!("Implement secp256r1_mul syscall."); + self.substract_syscall_gas_cost( + remaining_gas, + SyscallSelector::Secp256r1Mul, + self.context.gas_costs().secp256r1_new_gas_cost, + )?; + + Ok(Secp256Point::mul(p.into(), m).into()) } fn secp256r1_get_point_from_x( &mut self, - _x: U256, - _y_parity: bool, - _remaining_gas: &mut u128, + x: U256, + y_parity: bool, + remaining_gas: &mut u128, ) -> SyscallResult> { - todo!("Implement secp256r1_get_point_from_x syscall."); + self.substract_syscall_gas_cost( + remaining_gas, + SyscallSelector::Secp256r1GetPointFromX, + self.context.gas_costs().secp256r1_new_gas_cost, + )?; + + Secp256Point::get_point_from_x(x, y_parity).map(|op| op.map(|p| p.into())) } fn secp256r1_get_xy( &mut self, - _p: Secp256r1Point, - _remaining_gas: &mut u128, + p: Secp256r1Point, + remaining_gas: &mut u128, ) -> SyscallResult<(U256, U256)> { - todo!("Implement secp256r1_get_xy syscall."); + self.substract_syscall_gas_cost( + remaining_gas, + SyscallSelector::Secp256r1GetXy, + self.context.gas_costs().secp256r1_get_xy_gas_cost, + )?; + + Ok((p.x, p.y)) } fn sha256_process_block( &mut self, - _prev_state: &mut [u32; 8], - _current_block: &[u32; 16], - _remaining_gas: &mut u128, + prev_state: &mut [u32; 8], + current_block: &[u32; 16], + remaining_gas: &mut u128, ) -> SyscallResult<()> { - todo!("Implement sha256_process_block syscall."); + const SHA256_STATE_SIZE: usize = 8; + self.substract_syscall_gas_cost( + remaining_gas, + SyscallSelector::Sha256ProcessBlock, + self.context.gas_costs().sha256_process_block_gas_cost, + )?; + + let data_as_bytes = sha2::digest::generic_array::GenericArray::from_exact_iter( + current_block.iter().flat_map(|x| x.to_be_bytes()), + ) + .expect( + "u32.to_be_bytes() returns 4 bytes, and data.len() == 16. So data contains 64 bytes.", + ); + let mut state: [u32; SHA256_STATE_SIZE] = *prev_state; + sha2::compress256(&mut state, &[data_as_bytes]); + + prev_state.copy_from_slice(&state); + + Ok(()) + } +} + +// From cairo_native/src/starknet_stub.rs + +#[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() + } +} + +impl From> for Secp256k1Point { + fn from(Secp256Point(Affine { x, y, infinity }): Secp256Point) -> Self { + Secp256k1Point { + x: big4int_to_u256(x.into()), + y: big4int_to_u256(y.into()), + is_infinity: infinity, + } + } +} + +impl From> for Secp256r1Point { + fn from(Secp256Point(Affine { x, y, infinity }): Secp256Point) -> Self { + Secp256r1Point { + x: big4int_to_u256(x.into()), + y: big4int_to_u256(y.into()), + is_infinity: infinity, + } + } +} + +impl From for Secp256Point { + fn from(p: Secp256k1Point) -> Self { + Secp256Point(Affine { + x: u256_to_biguint(p.x).into(), + y: u256_to_biguint(p.y).into(), + infinity: p.is_infinity, + }) + } +} + +impl From for Secp256Point { + fn from(p: Secp256r1Point) -> Self { + Secp256Point(Affine { + x: u256_to_biguint(p.x).into(), + y: u256_to_biguint(p.y).into(), + infinity: p.is_infinity, + }) + } +} + +// Implementation from: cairo_native/src/starknet_stub.rs + +use ark_ff::PrimeField; + +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); + let modulos = Curve::BaseField::MODULUS.into(); + + if x >= modulos || y >= modulos { + let error = Felt::from_hex( + "0x00000000000000000000000000000000496e76616c696420617267756d656e74", + ) // INVALID_ARGUMENT + .map_err(|err| encode_str_as_felts(&err.to_string()))?; + + return Err(vec![error]); + } + + Ok(maybe_affine(x.into(), y.into())) + } + + 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 modulos = Curve::BaseField::MODULUS.into(); + let x = u256_to_biguint(x); + + if x >= modulos { + let error = Felt::from_hex( + "0x00000000000000000000000000000000496e76616c696420617267756d656e74", + ) // INVALID_ARGUMENT + .map_err(|err| encode_str_as_felts(&err.to_string()))?; + + return Err(vec![error]); + } + + 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.map(Secp256Point)) + } +} + +/// 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(Secp256Point(ec_point)) + } else { + None } } diff --git a/crates/blockifier/src/execution/syscalls/syscall_tests/secp.rs b/crates/blockifier/src/execution/syscalls/syscall_tests/secp.rs index a8a363c66b4..bdefe687446 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_tests/secp.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_tests/secp.rs @@ -27,6 +27,7 @@ fn test_secp256k1(test_contract: FeatureContract, expected_gas: u64) { ); } +#[test_case(FeatureContract::TestContract(CairoVersion::Native), 674500; "Native")] #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), 27565680; "VM")] fn test_secp256r1(test_contract: FeatureContract, expected_gas: u64) { let chain_info = &ChainInfo::create_for_testing();