diff --git a/examples/erc20.rs b/examples/erc20.rs index 257f2d7db..f96329998 100644 --- a/examples/erc20.rs +++ b/examples/erc20.rs @@ -4,7 +4,10 @@ use cairo_native::{ context::NativeContext, executor::JitNativeExecutor, metadata::syscall_handler::SyscallHandlerMeta, - starknet::{BlockInfo, ExecutionInfo, StarkNetSyscallHandler, SyscallResult, TxInfo, U256}, + starknet::{ + BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, StarkNetSyscallHandler, + SyscallResult, TxInfo, TxV2Info, U256, + }, utils::find_entry_point_by_idx, }; use starknet_types_core::felt::Felt; @@ -46,6 +49,42 @@ impl StarkNetSyscallHandler for SyscallHandler { }) } + fn get_execution_info_v2( + &mut self, + _remaining_gas: &mut u128, + ) -> SyscallResult { + println!("Called `get_execution_info_v2()` from MLIR."); + Ok(ExecutionInfoV2 { + block_info: BlockInfo { + block_number: 1234, + block_timestamp: 2345, + sequencer_address: 3456.into(), + }, + tx_info: TxV2Info { + version: 1.into(), + account_contract_address: 1.into(), + max_fee: 0, + signature: vec![1.into()], + transaction_hash: 1.into(), + chain_id: 1.into(), + nonce: 1.into(), + tip: 1, + paymaster_data: vec![1.into()], + nonce_data_availability_mode: 0, + fee_data_availability_mode: 0, + account_deployment_data: vec![1.into()], + resource_bounds: vec![ResourceBounds { + resource: 2.into(), + max_amount: 10, + max_price_per_unit: 20, + }], + }, + caller_address: 6543.into(), + contract_address: 5432.into(), + entry_point_selector: 4321.into(), + }) + } + fn deploy( &mut self, class_hash: Felt, diff --git a/examples/starknet.rs b/examples/starknet.rs index 2779731cb..6a90237ba 100644 --- a/examples/starknet.rs +++ b/examples/starknet.rs @@ -4,7 +4,10 @@ use cairo_native::{ context::NativeContext, executor::JitNativeExecutor, metadata::syscall_handler::SyscallHandlerMeta, - starknet::{BlockInfo, ExecutionInfo, StarkNetSyscallHandler, SyscallResult, TxInfo, U256}, + starknet::{ + BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, StarkNetSyscallHandler, + SyscallResult, TxInfo, TxV2Info, U256, + }, utils::find_entry_point_by_idx, }; use starknet_types_core::felt::Felt; @@ -46,6 +49,42 @@ impl StarkNetSyscallHandler for SyscallHandler { }) } + fn get_execution_info_v2( + &mut self, + _remaining_gas: &mut u128, + ) -> SyscallResult { + println!("Called `get_execution_info_v2()` from MLIR."); + Ok(ExecutionInfoV2 { + block_info: BlockInfo { + block_number: 1234, + block_timestamp: 2345, + sequencer_address: 3456.into(), + }, + tx_info: TxV2Info { + version: 1.into(), + account_contract_address: 1.into(), + max_fee: 0, + signature: vec![1.into()], + transaction_hash: 1.into(), + chain_id: 1.into(), + nonce: 1.into(), + tip: 1, + paymaster_data: vec![1.into()], + nonce_data_availability_mode: 0, + fee_data_availability_mode: 0, + account_deployment_data: vec![1.into()], + resource_bounds: vec![ResourceBounds { + resource: 2.into(), + max_amount: 10, + max_price_per_unit: 20, + }], + }, + caller_address: 6543.into(), + contract_address: 5432.into(), + entry_point_selector: 4321.into(), + }) + } + fn deploy( &mut self, class_hash: Felt, diff --git a/src/libfuncs/stark_net.rs b/src/libfuncs/stark_net.rs index aab4a19d4..8c5040c7b 100644 --- a/src/libfuncs/stark_net.rs +++ b/src/libfuncs/stark_net.rs @@ -137,8 +137,8 @@ where } StarkNetConcreteLibfunc::Testing(_) => todo!("implement starknet testing libfunc"), StarkNetConcreteLibfunc::Secp256(_) => todo!("implement starknet Secp256 libfunc"), - StarkNetConcreteLibfunc::GetExecutionInfoV2(_) => { - todo!("implement starknet GetExecutionInfoV2 libfunc") + StarkNetConcreteLibfunc::GetExecutionInfoV2(info) => { + build_get_execution_info_v2(context, registry, entry, location, helper, metadata, info) } } } @@ -2569,6 +2569,288 @@ where Ok(()) } +pub fn build_get_execution_info_v2<'ctx, 'this, TType, TLibfunc>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> +where + TType: GenericType, + TLibfunc: GenericLibfunc, + ::Concrete: TypeBuilder, + ::Concrete: LibfuncBuilder, +{ + // Extract self pointer. + let ptr = entry + .append_operation(llvm::load( + context, + entry.argument(1)?.into(), + llvm::r#type::opaque_pointer(context), + location, + LoadStoreOptions::default(), + )) + .result(0)? + .into(); + + // Allocate space for the return value. + let (result_layout, (result_tag_ty, result_tag_layout), variant_tys) = + crate::types::r#enum::get_type_for_variants( + context, + helper, + registry, + metadata, + &[ + info.branch_signatures()[0].vars[2].ty.clone(), + info.branch_signatures()[1].vars[2].ty.clone(), + ], + )?; + + let k1 = helper + .init_block() + .append_operation(arith::constant( + context, + IntegerAttribute::new(1, IntegerType::new(context, 64).into()).into(), + location, + )) + .result(0)? + .into(); + let result_ptr = helper + .init_block() + .append_operation( + OperationBuilder::new("llvm.alloca", location) + .add_attributes(&[ + ( + Identifier::new(context, "alignment"), + IntegerAttribute::new( + result_layout.align().try_into()?, + IntegerType::new(context, 64).into(), + ) + .into(), + ), + ( + Identifier::new(context, "elem_type"), + TypeAttribute::new(llvm::r#type::r#struct( + context, + &[ + result_tag_ty, + llvm::r#type::array( + IntegerType::new(context, 8).into(), + (result_layout.size() - 1).try_into()?, + ), + ], + false, + )) + .into(), + ), + ]) + .add_operands(&[k1]) + .add_results(&[llvm::r#type::opaque_pointer(context)]) + .build()?, + ) + .result(0)? + .into(); + + // Allocate space and write the current gas. + let gas_builtin_ptr = helper + .init_block() + .append_operation( + OperationBuilder::new("llvm.alloca", location) + .add_attributes(&[( + Identifier::new(context, "alignment"), + IntegerAttribute::new( + result_layout.align().try_into()?, + IntegerType::new(context, 64).into(), + ) + .into(), + )]) + .add_operands(&[k1]) + .add_results(&[llvm::r#type::pointer( + IntegerType::new(context, 128).into(), + 0, + )]) + .build()?, + ) + .result(0)? + .into(); + entry.append_operation(llvm::store( + context, + entry.argument(0)?.into(), + gas_builtin_ptr, + location, + LoadStoreOptions::default(), + )); + + // Extract function pointer. + let fn_ptr_ty = llvm::r#type::function( + llvm::r#type::void(context), + &[ + llvm::r#type::opaque_pointer(context), + llvm::r#type::opaque_pointer(context), + llvm::r#type::pointer(IntegerType::new(context, 128).into(), 0), + ], + false, + ); + let fn_ptr = entry + .append_operation(llvm::get_element_ptr( + context, + entry.argument(1)?.into(), + DenseI32ArrayAttribute::new( + context, + &[StarkNetSyscallHandlerCallbacks::<()>::GET_EXECUTION_INFOV2.try_into()?], + ), + llvm::r#type::opaque_pointer(context), + llvm::r#type::opaque_pointer(context), + location, + )) + .result(0)? + .into(); + let fn_ptr = entry + .append_operation(llvm::load( + context, + fn_ptr, + llvm::r#type::pointer(fn_ptr_ty, 0), + location, + LoadStoreOptions::default(), + )) + .result(0)? + .into(); + + entry.append_operation( + OperationBuilder::new("llvm.call", location) + .add_operands(&[fn_ptr, result_ptr, ptr, gas_builtin_ptr]) + .build()?, + ); + + let result = entry + .append_operation(llvm::load( + context, + result_ptr, + llvm::r#type::r#struct( + context, + &[ + result_tag_ty, + llvm::r#type::array( + IntegerType::new(context, 8).into(), + (result_layout.size() - 1).try_into()?, + ), + ], + false, + ), + location, + LoadStoreOptions::default(), + )) + .result(0)? + .into(); + let result_tag = entry + .append_operation(llvm::extract_value( + context, + result, + DenseI64ArrayAttribute::new(context, &[0]), + IntegerType::new(context, 1).into(), + location, + )) + .result(0)? + .into(); + + let payload_ok = { + let ptr = entry + .append_operation( + OperationBuilder::new("llvm.getelementptr", location) + .add_attributes(&[ + ( + Identifier::new(context, "rawConstantIndices"), + DenseI32ArrayAttribute::new( + context, + &[result_tag_layout.extend(variant_tys[0].1)?.1.try_into()?], + ) + .into(), + ), + ( + Identifier::new(context, "elem_type"), + TypeAttribute::new(IntegerType::new(context, 8).into()).into(), + ), + ]) + .add_operands(&[result_ptr]) + .add_results(&[llvm::r#type::opaque_pointer(context)]) + .build()?, + ) + .result(0)? + .into(); + entry + .append_operation(llvm::load( + context, + ptr, + variant_tys[0].0, + location, + LoadStoreOptions::default(), + )) + .result(0)? + .into() + }; + let payload_err = { + let ptr = entry + .append_operation( + OperationBuilder::new("llvm.getelementptr", location) + .add_attributes(&[ + ( + Identifier::new(context, "rawConstantIndices"), + DenseI32ArrayAttribute::new( + context, + &[result_tag_layout.extend(variant_tys[1].1)?.1.try_into()?], + ) + .into(), + ), + ( + Identifier::new(context, "elem_type"), + TypeAttribute::new(IntegerType::new(context, 8).into()).into(), + ), + ]) + .add_operands(&[result_ptr]) + .add_results(&[llvm::r#type::opaque_pointer(context)]) + .build()?, + ) + .result(0)? + .into(); + entry + .append_operation(llvm::load( + context, + ptr, + variant_tys[1].0, + location, + LoadStoreOptions::default(), + )) + .result(0)? + .into() + }; + + let remaining_gas = entry + .append_operation(llvm::load( + context, + gas_builtin_ptr, + IntegerType::new(context, 128).into(), + location, + LoadStoreOptions::default(), + )) + .result(0)? + .into(); + + entry.append_operation(helper.cond_br( + context, + result_tag, + [1, 0], + [ + &[remaining_gas, entry.argument(1)?.into(), payload_err], + &[remaining_gas, entry.argument(1)?.into(), payload_ok], + ], + location, + )); + Ok(()) +} + pub fn build_deploy<'ctx, 'this, TType, TLibfunc>( context: &'ctx Context, registry: &ProgramRegistry, diff --git a/src/starknet.rs b/src/starknet.rs index 05f122d1e..4a4903ff0 100644 --- a/src/starknet.rs +++ b/src/starknet.rs @@ -19,6 +19,7 @@ pub struct Felt252Abi(pub [u8; 32]); #[cfg_attr(not(target_arch = "x86_64"), repr(C, align(16)))] pub struct U256(pub [u8; 32]); +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ExecutionInfo { pub block_info: BlockInfo, pub tx_info: TxInfo, @@ -27,12 +28,47 @@ pub struct ExecutionInfo { pub entry_point_selector: Felt, } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ExecutionInfoV2 { + pub block_info: BlockInfo, + pub tx_info: TxV2Info, + pub caller_address: Felt, + pub contract_address: Felt, + pub entry_point_selector: Felt, +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TxV2Info { + pub version: Felt, + pub account_contract_address: Felt, + pub max_fee: u128, + pub signature: Vec, + pub transaction_hash: Felt, + pub chain_id: Felt, + pub nonce: Felt, + pub resource_bounds: Vec, + pub tip: u128, + pub paymaster_data: Vec, + pub nonce_data_availability_mode: u32, + pub fee_data_availability_mode: u32, + pub account_deployment_data: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ResourceBounds { + pub resource: Felt, + pub max_amount: u64, + pub max_price_per_unit: u128, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BlockInfo { pub block_number: u64, pub block_timestamp: u64, pub sequencer_address: Felt, } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TxInfo { pub version: Felt, pub account_contract_address: Felt, @@ -57,8 +93,12 @@ pub trait StarkNetSyscallHandler { block_number: u64, remaining_gas: &mut u128, ) -> SyscallResult; + fn get_execution_info(&mut self, remaining_gas: &mut u128) -> SyscallResult; + fn get_execution_info_v2(&mut self, remaining_gas: &mut u128) + -> SyscallResult; + fn deploy( &mut self, class_hash: Felt, @@ -287,6 +327,40 @@ pub(crate) mod handler { entry_point_selector: Felt252Abi, } + #[repr(C)] + struct ExecutionInfoV2Abi { + block_info: NonNull, + tx_info: NonNull, + caller_address: Felt252Abi, + contract_address: Felt252Abi, + entry_point_selector: Felt252Abi, + } + + #[repr(C)] + struct TxInfoV2Abi { + version: Felt252Abi, + account_contract_address: Felt252Abi, + max_fee: u128, + signature: (NonNull, u32, u32), + transaction_hash: Felt252Abi, + chain_id: Felt252Abi, + nonce: Felt252Abi, + resource_bounds: (NonNull, u32, u32), + tip: u128, + paymaster_data: (NonNull, u32, u32), + nonce_data_availability_mode: u32, + fee_data_availability_mode: u32, + account_deployment_data: (NonNull, u32, u32), + } + + #[repr(C)] + #[derive(Debug, Clone)] + struct ResourceBoundsAbi { + resource: Felt252Abi, + max_amount: u64, + max_price_per_unit: u128, + } + #[repr(C)] struct BlockInfoAbi { block_number: u64, @@ -321,6 +395,11 @@ pub(crate) mod handler { ptr: &mut T, gas: &mut u128, ), + get_execution_info_v2: extern "C" fn( + result_ptr: &mut SyscallResultAbi>, + ptr: &mut T, + gas: &mut u128, + ), deploy: extern "C" fn( result_ptr: &mut SyscallResultAbi<(Felt252Abi, (NonNull, u32, u32))>, ptr: &mut T, @@ -400,6 +479,7 @@ pub(crate) mod handler { pub const EMIT_EVENT: usize = field_offset!(Self, emit_event) >> 3; pub const GET_BLOCK_HASH: usize = field_offset!(Self, get_block_hash) >> 3; pub const GET_EXECUTION_INFO: usize = field_offset!(Self, get_execution_info) >> 3; + pub const GET_EXECUTION_INFOV2: usize = field_offset!(Self, get_execution_info_v2) >> 3; pub const KECCAK: usize = field_offset!(Self, keccak) >> 3; pub const LIBRARY_CALL: usize = field_offset!(Self, library_call) >> 3; pub const REPLACE_CLASS: usize = field_offset!(Self, replace_class) >> 3; @@ -417,6 +497,7 @@ pub(crate) mod handler { self_ptr: handler, get_block_hash: Self::wrap_get_block_hash, get_execution_info: Self::wrap_get_execution_info, + get_execution_info_v2: Self::wrap_get_execution_info_v2, deploy: Self::wrap_deploy, replace_class: Self::wrap_replace_class, library_call: Self::wrap_library_call, @@ -536,6 +617,100 @@ pub(crate) mod handler { }; } + extern "C" fn wrap_get_execution_info_v2( + result_ptr: &mut SyscallResultAbi>, + ptr: &mut T, + gas: &mut u128, + ) { + let result = ptr.get_execution_info_v2(gas); + + *result_ptr = match result { + Ok(x) => SyscallResultAbi { + ok: ManuallyDrop::new(SyscallResultAbiOk { + tag: 0u8, + payload: unsafe { + let mut execution_info_ptr = + NonNull::new(libc::malloc(size_of::()) + as *mut ExecutionInfoV2Abi) + .unwrap(); + + let mut block_info_ptr = + NonNull::new( + libc::malloc(size_of::()) as *mut BlockInfoAbi + ) + .unwrap(); + block_info_ptr.as_mut().block_number = x.block_info.block_number; + block_info_ptr.as_mut().block_timestamp = x.block_info.block_timestamp; + block_info_ptr.as_mut().sequencer_address = + Felt252Abi(x.block_info.sequencer_address.to_bytes_le()); + + let mut tx_info_ptr = NonNull::new( + libc::malloc(size_of::()) as *mut TxInfoV2Abi, + ) + .unwrap(); + tx_info_ptr.as_mut().version = + Felt252Abi(x.tx_info.version.to_bytes_le()); + tx_info_ptr.as_mut().signature = Self::alloc_mlir_array( + &x.tx_info + .signature + .into_iter() + .map(|x| Felt252Abi(x.to_bytes_le())) + .collect::>(), + ); + tx_info_ptr.as_mut().max_fee = x.tx_info.max_fee; + tx_info_ptr.as_mut().transaction_hash = + Felt252Abi(x.tx_info.transaction_hash.to_bytes_le()); + tx_info_ptr.as_mut().chain_id = + Felt252Abi(x.tx_info.chain_id.to_bytes_le()); + tx_info_ptr.as_mut().nonce = Felt252Abi(x.tx_info.nonce.to_bytes_le()); + tx_info_ptr.as_mut().resource_bounds = Self::alloc_mlir_array( + &x.tx_info + .resource_bounds + .into_iter() + .map(|x| ResourceBoundsAbi { + resource: Felt252Abi(x.resource.to_bytes_le()), + max_amount: x.max_amount, + max_price_per_unit: x.max_price_per_unit, + }) + .collect::>(), + ); + tx_info_ptr.as_mut().tip = x.tx_info.tip; + tx_info_ptr.as_mut().paymaster_data = Self::alloc_mlir_array( + &x.tx_info + .paymaster_data + .into_iter() + .map(|x| Felt252Abi(x.to_bytes_le())) + .collect::>(), + ); + tx_info_ptr.as_mut().nonce_data_availability_mode = + x.tx_info.nonce_data_availability_mode; + tx_info_ptr.as_mut().fee_data_availability_mode = + x.tx_info.fee_data_availability_mode; + tx_info_ptr.as_mut().account_deployment_data = Self::alloc_mlir_array( + &x.tx_info + .account_deployment_data + .into_iter() + .map(|x| Felt252Abi(x.to_bytes_le())) + .collect::>(), + ); + + execution_info_ptr.as_mut().block_info = block_info_ptr; + execution_info_ptr.as_mut().tx_info = tx_info_ptr; + execution_info_ptr.as_mut().caller_address = + Felt252Abi(x.caller_address.to_bytes_le()); + execution_info_ptr.as_mut().contract_address = + Felt252Abi(x.contract_address.to_bytes_le()); + execution_info_ptr.as_mut().entry_point_selector = + Felt252Abi(x.entry_point_selector.to_bytes_le()); + + ManuallyDrop::new(execution_info_ptr) + }, + }), + }, + Err(e) => Self::wrap_error(&e), + }; + } + // TODO: change all from_bytes_be to from_bytes_ne when added and undo byte swapping. extern "C" fn wrap_deploy( diff --git a/src/utils.rs b/src/utils.rs index 478ec32eb..19d538f8b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -646,7 +646,10 @@ pub mod test { MetadataStorage, }, module::NativeModule, - starknet::{BlockInfo, ExecutionInfo, StarkNetSyscallHandler, SyscallResult, TxInfo, U256}, + starknet::{ + BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, StarkNetSyscallHandler, + SyscallResult, TxInfo, TxV2Info, U256, + }, utils::*, values::JitValue, }; @@ -927,6 +930,41 @@ pub mod test { }) } + fn get_execution_info_v2( + &mut self, + _remaining_gas: &mut u128, + ) -> SyscallResult { + Ok(ExecutionInfoV2 { + block_info: BlockInfo { + block_number: 1234, + block_timestamp: 2345, + sequencer_address: 3456.into(), + }, + tx_info: TxV2Info { + version: 1.into(), + account_contract_address: 1.into(), + max_fee: 0, + signature: vec![1.into()], + transaction_hash: 1.into(), + chain_id: 1.into(), + nonce: 1.into(), + tip: 1, + paymaster_data: vec![1.into()], + nonce_data_availability_mode: 0, + fee_data_availability_mode: 0, + account_deployment_data: vec![1.into()], + resource_bounds: vec![ResourceBounds { + resource: 2.into(), + max_amount: 10, + max_price_per_unit: 20, + }], + }, + caller_address: 6543.into(), + contract_address: 5432.into(), + entry_point_selector: 4321.into(), + }) + } + fn deploy( &mut self, class_hash: Felt, diff --git a/tests/starknet/keccak.rs b/tests/starknet/keccak.rs index a4acdebc0..a262a24dc 100644 --- a/tests/starknet/keccak.rs +++ b/tests/starknet/keccak.rs @@ -2,7 +2,8 @@ use crate::common::run_native_starknet_contract; use cairo_lang_compiler::CompilerConfig; use cairo_lang_starknet::contract_class::{compile_path, ContractClass}; use cairo_native::starknet::{ - BlockInfo, ExecutionInfo, StarkNetSyscallHandler, SyscallResult, TxInfo, U256, + BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, StarkNetSyscallHandler, + SyscallResult, TxInfo, TxV2Info, U256, }; use lazy_static::lazy_static; use starknet_types_core::felt::Felt; @@ -43,6 +44,42 @@ impl StarkNetSyscallHandler for SyscallHandler { }) } + fn get_execution_info_v2( + &mut self, + _remaining_gas: &mut u128, + ) -> SyscallResult { + println!("Called `get_execution_info_v2()` from MLIR."); + Ok(ExecutionInfoV2 { + block_info: BlockInfo { + block_number: 1234, + block_timestamp: 2345, + sequencer_address: 3456.into(), + }, + tx_info: TxV2Info { + version: 1.into(), + account_contract_address: 1.into(), + max_fee: 0, + signature: vec![1.into()], + transaction_hash: 1.into(), + chain_id: 1.into(), + nonce: 1.into(), + tip: 1, + paymaster_data: vec![1.into()], + nonce_data_availability_mode: 0, + fee_data_availability_mode: 0, + account_deployment_data: vec![1.into()], + resource_bounds: vec![ResourceBounds { + resource: 2.into(), + max_amount: 10, + max_price_per_unit: 20, + }], + }, + caller_address: 6543.into(), + contract_address: 5432.into(), + entry_point_selector: 4321.into(), + }) + } + fn deploy( &mut self, class_hash: Felt,