From ce104c517e4d8903f7b98e4c4687d720cc07cd65 Mon Sep 17 00:00:00 2001 From: fannyguthmann Date: Fri, 4 Aug 2023 11:12:59 +0300 Subject: [PATCH 1/7] added comments to origin/Document-transactionl1_handler-module --- src/transaction/l1_handler.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/transaction/l1_handler.rs b/src/transaction/l1_handler.rs index 9b479a340..de529a48b 100644 --- a/src/transaction/l1_handler.rs +++ b/src/transaction/l1_handler.rs @@ -29,6 +29,7 @@ use super::Transaction; #[allow(dead_code)] #[derive(Debug, Getters, Clone)] +/// L1Handler represents a handler for L1 transactions within the StarkNet protocol. pub struct L1Handler { #[getset(get = "pub")] hash_value: Felt252, @@ -42,6 +43,7 @@ pub struct L1Handler { skip_execute: bool, } +/// Constructor creates a new L1Handler instance. impl L1Handler { pub fn new( contract_address: Address, @@ -71,7 +73,7 @@ impl L1Handler { hash_value, ) } - + /// Creates a new L1Handler instance with a specified transaction hash. pub fn new_with_tx_hash( contract_address: Address, entry_point_selector: Felt252, @@ -189,6 +191,8 @@ impl L1Handler { L1_HANDLER_VERSION.into(), )) } + + /// Creates a L1Handler for simulation purposes. pub(crate) fn create_for_simulation( &self, skip_validate: bool, @@ -230,6 +234,7 @@ mod test { utils::Address, }; + /// Test the correct execution of the L1Handler. #[test] fn test_execute_l1_handler() { let l1_handler = L1Handler::new( @@ -286,6 +291,8 @@ mod test { assert_eq!(tx_exec, expected_tx_exec) } + /// Helper function to construct the expected transaction execution info. + /// Expected output of the L1Handler's execution. fn expected_tx_exec_info() -> TransactionExecutionInfo { TransactionExecutionInfo { validate_info: None, From ff5c6afa860129026022979a57093fd3b63343e9 Mon Sep 17 00:00:00 2001 From: fannyguthmann Date: Thu, 10 Aug 2023 12:31:41 +0300 Subject: [PATCH 2/7] modified comments --- src/transaction/l1_handler.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/transaction/l1_handler.rs b/src/transaction/l1_handler.rs index de529a48b..b915a5cf5 100644 --- a/src/transaction/l1_handler.rs +++ b/src/transaction/l1_handler.rs @@ -29,7 +29,7 @@ use super::Transaction; #[allow(dead_code)] #[derive(Debug, Getters, Clone)] -/// L1Handler represents a handler for L1 transactions within the StarkNet protocol. +/// Represents an L1Handler transaction in the StarkNet network. pub struct L1Handler { #[getset(get = "pub")] hash_value: Felt252, @@ -43,8 +43,8 @@ pub struct L1Handler { skip_execute: bool, } -/// Constructor creates a new L1Handler instance. impl L1Handler { + /// Constructor creates a new [L1Handler] instance. pub fn new( contract_address: Address, entry_point_selector: Felt252, @@ -73,7 +73,7 @@ impl L1Handler { hash_value, ) } - /// Creates a new L1Handler instance with a specified transaction hash. + /// Creates a new [L1Handler] instance with a specified transaction hash. pub fn new_with_tx_hash( contract_address: Address, entry_point_selector: Felt252, From b9cfdc91f62e0285f13eefc490563e312d191a5e Mon Sep 17 00:00:00 2001 From: Fanny Guthmann <57538139+fguthmann@users.noreply.github.com> Date: Fri, 11 Aug 2023 20:51:43 +0300 Subject: [PATCH 3/7] Test multi syscall (#687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * create multy syscall * remove the replace syscall, it failed because the contract adress didn't match * added library call_syscall * wip * wip * wip * wip * work in progress * remove .sjon files from starknet_programs * finished implemented all the syscalls * reorder code, create one call to syscall * fix pull bug * Update tests/multi_syscall_test.rs Co-authored-by: Matías Ignacio González * Update starknet_programs/cairo1/multi_syscall_test.cairo Co-authored-by: Matías Ignacio González * Update starknet_programs/cairo1/contract_a.cairo Co-authored-by: Matías Ignacio González * Update tests/multi_syscall_test.rs Co-authored-by: Matías Ignacio González * added test syscall for deploy * make format changes * corrected make clippy error * get_caller_address and get_contract_address return a adress * failed of get_contract_address * failed of get_contract_address * wip * modify the selector entrypoint_selector to be function specific * wip * wip * wip * add input to cairo functions * coorect format problem * wip * wip * wip * remove format problem * Fix sierra class hash calculation (#886) * reproduce bug * use pythonic formatter * rename test * fix test * cargo fmt * Fail with an Err transactions whose calculated fee exceed `max_fee` (#892) * Make tx fail when actual_fee exceeds max_fee * Changed test * Formatting * Fix logic * Leave fail only without charging * Change test * Fix test broken by better fee calc * Fixed test fee * Update fee on test_deploy_account * Remove comment --------- Co-authored-by: Juan Bono * Fix test_get_nonce_at (#910) * Fix test_get_nonce_at * Rely on another contract * fix get_sorted_events bug (#912) * fix get_sorted_events bug * fmt * fix clippy --------- Co-authored-by: Estéfano Bargas * Added documentations to syscalls/deprecated_syscall_handler module (#883) * added comments to file syscalls/deprecated_syscall_handler-module' * Update src/syscalls/deprecated_syscall_handler.rs Co-authored-by: Matías Ignacio González * Update src/syscalls/deprecated_syscall_handler.rs Co-authored-by: Matías Ignacio González --------- Co-authored-by: fannyguthmann Co-authored-by: Juan Bono Co-authored-by: Matías Ignacio González * wip * Modify the tests * fixed clippy errors --------- Co-authored-by: fannyguthmann Co-authored-by: Matías Ignacio González Co-authored-by: SantiagoPittella Co-authored-by: Juan Bono Co-authored-by: Estéfano Bargas --- starknet_programs/cairo1/contract_a.cairo | 1 - .../cairo1/multi_syscall_test.cairo | 97 +++++++ tests/multi_syscall_test.rs | 262 ++++++++++++++++++ 3 files changed, 359 insertions(+), 1 deletion(-) create mode 100644 starknet_programs/cairo1/multi_syscall_test.cairo create mode 100644 tests/multi_syscall_test.rs diff --git a/starknet_programs/cairo1/contract_a.cairo b/starknet_programs/cairo1/contract_a.cairo index 615d9ee59..040dda106 100644 --- a/starknet_programs/cairo1/contract_a.cairo +++ b/starknet_programs/cairo1/contract_a.cairo @@ -1,7 +1,6 @@ #[contract] mod ContractA { use traits::Into; - use starknet::info::get_contract_address; struct Storage { value: u128, } diff --git a/starknet_programs/cairo1/multi_syscall_test.cairo b/starknet_programs/cairo1/multi_syscall_test.cairo new file mode 100644 index 000000000..04d205dc5 --- /dev/null +++ b/starknet_programs/cairo1/multi_syscall_test.cairo @@ -0,0 +1,97 @@ +#[contract] +mod multy_syscall { + use starknet::get_caller_address; + use starknet::get_contract_address; + use starknet::get_execution_info_syscall; + use starknet::replace_class_syscall; + use starknet::library_call_syscall; + use starknet::call_contract_syscall; + use starknet::send_message_to_l1_syscall; + use starknet::storage_read_syscall; + use starknet::storage_write_syscall; + use starknet::deploy_syscall; + + use starknet::storage_access::{StorageAddress, storage_address_try_from_felt252}; + use starknet::contract_address::ContractAddress; + use array::{Array, ArrayTrait, Span, SpanTrait}; + use result::ResultTrait; + use starknet::info::ExecutionInfo; + use option::OptionTrait; + use starknet::class_hash::ClassHash; + use traits::TryInto; + use starknet::class_hash::Felt252TryIntoClassHash; + use box::Box; + use traits::Into; + use box::BoxTrait; + + #[external] + fn caller_address() -> ContractAddress { + get_caller_address() + } + + #[external] + fn contract_address() -> ContractAddress { + get_contract_address() + } + + #[external] + fn execution_info_syscall() -> (ContractAddress, ContractAddress) { + let return_data = get_execution_info_syscall().unwrap().unbox(); + (return_data.caller_address, return_data.contract_address) + } + + + #[external] + fn test_library_call_syscall(class_hash: ClassHash, function_selector: felt252, number: felt252) -> felt252 { + let mut calldata = ArrayTrait::new(); + calldata.append(number); + let return_data = library_call_syscall(class_hash, function_selector, calldata.span()).unwrap(); + *return_data.get(0_usize).unwrap().unbox() + } + + #[external] + fn test_call_contract_syscall(function_selector: felt252, number: felt252) -> felt252 { + let mut calldata = ArrayTrait::new(); + calldata.append(number); + let return_data = call_contract_syscall(get_contract_address(), function_selector, calldata.span()).unwrap(); + *return_data.get(0_usize).unwrap().unbox() + + } + + #[external] + fn test_send_message_to_l1(to_address: felt252, payload_0: felt252, payload_1: felt252) -> () { + let mut calldata = ArrayTrait::new(); + calldata.append(payload_0); + calldata.append(payload_1); + let return_data = send_message_to_l1_syscall(to_address, calldata.span()).unwrap(); + return_data + } + + #[external] + fn read()-> felt252{ + //write to storage + let address = storage_address_try_from_felt252(3534535754756246375475423547453).unwrap(); + storage_write_syscall(0, address, 'Hello'); + + //read from storage + match storage_read_syscall(0, address) { + Result::Ok(value) => value, + Result::Err(revert_reason) => *revert_reason.span().at(0), + } + } + + #[event] + fn EmitEvent(n: felt252){} + + #[external] + fn trigger_events() { + EmitEvent(1); + EmitEvent(2); + EmitEvent(3); + } + + #[external] + fn get_number(number: felt252) -> felt252 { + number + } +} diff --git a/tests/multi_syscall_test.rs b/tests/multi_syscall_test.rs new file mode 100644 index 000000000..7aa7890d7 --- /dev/null +++ b/tests/multi_syscall_test.rs @@ -0,0 +1,262 @@ +use cairo_lang_starknet::casm_contract_class::CasmContractClass; +use cairo_vm::felt::Felt252; +use num_traits::{Num, Zero}; +use starknet_in_rust::utils::calculate_sn_keccak; +use starknet_in_rust::EntryPointType; +use starknet_in_rust::{ + definitions::{block_context::BlockContext, constants::TRANSACTION_VERSION}, + execution::{ + execution_entry_point::ExecutionEntryPoint, CallInfo, CallType, OrderedEvent, + OrderedL2ToL1Message, TransactionExecutionContext, + }, + state::cached_state::CachedState, + state::{in_memory_state_reader::InMemoryStateReader, ExecutionResourcesManager}, + utils::{Address, ClassHash}, +}; +use std::{collections::HashMap, sync::Arc, vec}; + +#[test] +fn test_multiple_syscall() { + // Create program and entry point types for contract class + let program_data = include_bytes!("../starknet_programs/cairo1/multi_syscall_test.casm"); + let contract_class: CasmContractClass = serde_json::from_slice(program_data).unwrap(); + + // Create state reader with class hash data + let mut contract_class_cache: HashMap<[u8; 32], _> = HashMap::new(); + + let address = Address(1111.into()); + let class_hash: ClassHash = [1; 32]; + let nonce = Felt252::zero(); + + contract_class_cache.insert(class_hash, contract_class); + let mut state_reader = InMemoryStateReader::default(); + state_reader + .address_to_class_hash_mut() + .insert(address.clone(), class_hash); + state_reader + .address_to_nonce_mut() + .insert(address.clone(), nonce); + + // Create state from the state_reader and contract cache. + let mut state = CachedState::new( + Arc::new(state_reader), + None, + Some(contract_class_cache.clone()), + ); + + // Create an execution entry point + let calldata = [].to_vec(); + let caller_address = Address(0000.into()); + let entry_point_type = EntryPointType::External; + + // Block for get_caller_address. + { + let call_info = test_syscall( + "caller_address", + address.clone(), + vec![], + caller_address.clone(), + entry_point_type, + class_hash, + &mut state, + ); + assert_eq!(call_info.retdata, vec![caller_address.clone().0]) + } + + // Block for get_contact_address. + { + let call_info = test_syscall( + "contract_address", + address.clone(), + vec![], + caller_address.clone(), + entry_point_type, + class_hash, + &mut state, + ); + assert_eq!(call_info.retdata, vec![address.clone().0]) + } + // Block for get_execution_info_syscall. + { + let call_info = test_syscall( + "execution_info_syscall", + address.clone(), + calldata.clone(), + caller_address.clone(), + entry_point_type, + class_hash, + &mut state, + ); + assert_eq!(call_info.retdata, vec![0.into(), 1111.into()]); + } + + // Block for library_call_syscall + { + let entrypoint_selector = + Felt252::from_bytes_be(&calculate_sn_keccak("get_number".as_bytes())); + let new_call_data = vec![ + Felt252::from_bytes_be(&class_hash), + entrypoint_selector, + Felt252::from(25), + ]; + let call_info = test_syscall( + "test_library_call_syscall", + address.clone(), + new_call_data, + caller_address.clone(), + entry_point_type, + class_hash, + &mut state, + ); + assert_eq!(call_info.retdata, vec![25.into()]) + } + + // Block for call_contract_syscall + { + let entrypoint_selector = + Felt252::from_bytes_be(&calculate_sn_keccak("get_number".as_bytes())); + let new_call_data = vec![entrypoint_selector, Felt252::from(25)]; + let call_info = test_syscall( + "test_call_contract_syscall", + address.clone(), + new_call_data, + caller_address.clone(), + entry_point_type, + class_hash, + &mut state, + ); + assert_eq!(call_info.retdata, vec![25.into()]) + } + + // Block for send_message_to_l1_syscall + { + let new_call_data = vec![2222.into(), Felt252::from(25), Felt252::from(30)]; + let call_info = test_syscall( + "test_send_message_to_l1", + address.clone(), + new_call_data, + caller_address.clone(), + entry_point_type, + class_hash, + &mut state, + ); + assert_eq!( + call_info.l2_to_l1_messages, + vec![OrderedL2ToL1Message { + order: 0, + to_address: Address(2222.into()), + payload: vec![Felt252::from(25), Felt252::from(30)], + },] + ) + } + + // Block for read write + { + let call_info = test_syscall( + "read", + address.clone(), + calldata.clone(), + caller_address.clone(), + entry_point_type, + class_hash, + &mut state, + ); + assert_eq!( + call_info.retdata, + vec![Felt252::from_str_radix("310939249775", 10).unwrap()] + ) + } + + // Block for emit + { + let call_info = test_syscall( + "trigger_events", + address, + calldata, + caller_address, + entry_point_type, + class_hash, + &mut state, + ); + assert_eq!( + call_info.events, + vec![ + OrderedEvent { + order: 0, + keys: vec![Felt252::from_str_radix( + "1533133552972353850845856330693290141476612241335297758062928121906575244541", + 10 + ) + .unwrap()], + data: vec![1.into()] + }, + OrderedEvent { + order: 1, + keys: vec![Felt252::from_str_radix( + "1533133552972353850845856330693290141476612241335297758062928121906575244541", + 10 + ) + .unwrap()], + data: vec![2.into()] + }, + OrderedEvent { + order: 2, + keys: vec![Felt252::from_str_radix( + "1533133552972353850845856330693290141476612241335297758062928121906575244541", + 10 + ) + .unwrap()], + data: vec![3.into()] + } + ] + ) + } +} + +fn test_syscall( + entrypoint_selector: &str, + address: Address, + calldata: Vec, + caller_address: Address, + entry_point_type: EntryPointType, + class_hash: [u8; 32], + state: &mut CachedState, +) -> CallInfo { + let entrypoint_selector = + Felt252::from_bytes_be(&calculate_sn_keccak(entrypoint_selector.as_bytes())); + let exec_entry_point = ExecutionEntryPoint::new( + address, + calldata, + Felt252::new(entrypoint_selector), + caller_address, + entry_point_type, + Some(CallType::Delegate), + Some(class_hash), + 100000, + ); + + // Execute the entrypoint + let block_context = BlockContext::default(); + let mut tx_execution_context = TransactionExecutionContext::new( + Address(0.into()), + Felt252::zero(), + Vec::new(), + 0, + 10.into(), + block_context.invoke_tx_max_n_steps(), + TRANSACTION_VERSION.clone(), + ); + let mut resources_manager = ExecutionResourcesManager::default(); + exec_entry_point + .execute( + state, + &block_context, + &mut resources_manager, + &mut tx_execution_context, + false, + block_context.invoke_tx_max_n_steps(), + ) + .unwrap() + .call_info + .unwrap() +} From b6d398e64a44b34dec4ee04953ca4deb335a0e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Ignacio=20Gonz=C3=A1lez?= Date: Fri, 11 Aug 2023 15:36:09 -0300 Subject: [PATCH 4/7] Parse internal calls (#915) --- rpc_state_reader/src/lib.rs | 15 +++++++++++++++ src/execution/mod.rs | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/rpc_state_reader/src/lib.rs b/rpc_state_reader/src/lib.rs index cd7ae3734..e3c69565a 100644 --- a/rpc_state_reader/src/lib.rs +++ b/rpc_state_reader/src/lib.rs @@ -926,6 +926,7 @@ mod tests { ]), } ); + assert_eq!(tx_trace.validate_invocation.internal_calls.len(), 1); assert_eq!( tx_trace.function_invocation.calldata, @@ -965,6 +966,19 @@ mod tests { ]), } ); + assert_eq!(tx_trace.function_invocation.internal_calls.len(), 1); + assert_eq!( + tx_trace.function_invocation.internal_calls[0] + .internal_calls + .len(), + 1 + ); + assert_eq!( + tx_trace.function_invocation.internal_calls[0].internal_calls[0] + .internal_calls + .len(), + 7 + ); assert_eq!( tx_trace.fee_transfer_invocation.calldata, @@ -989,5 +1003,6 @@ mod tests { ]), } ); + assert_eq!(tx_trace.fee_transfer_invocation.internal_calls.len(), 1); } } diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 2f69860e5..8a6fd6837 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -265,10 +265,20 @@ impl<'de> Deserialize<'de> for CallInfo { let calldata_value = value["calldata"].clone(); let calldata = parse_felt_array(calldata_value.as_array().unwrap()); + // Parse internal calls + let internal_calls_value = value["internal_calls"].clone(); + let mut internal_calls = vec![]; + + for call in internal_calls_value.as_array().unwrap() { + internal_calls + .push(serde_json::from_value(call.clone()).map_err(serde::de::Error::custom)?); + } + Ok(CallInfo { execution_resources, retdata, calldata, + internal_calls, ..Default::default() }) } From 3833b738d127c0ede5f73de799857a2bd5be87b1 Mon Sep 17 00:00:00 2001 From: Fanny Guthmann <57538139+fguthmann@users.noreply.github.com> Date: Fri, 11 Aug 2023 21:38:36 +0300 Subject: [PATCH 5/7] Added comments to core/contract_address module (#900) Co-authored-by: fannyguthmann --- .../contract_address/casm_contract_address.rs | 3 +++ .../deprecated_contract_address.rs | 16 ++++++++++++++++ .../contract_address/sierra_contract_address.rs | 4 ++++ 3 files changed, 23 insertions(+) diff --git a/src/core/contract_address/casm_contract_address.rs b/src/core/contract_address/casm_contract_address.rs index 43b599725..537146e23 100644 --- a/src/core/contract_address/casm_contract_address.rs +++ b/src/core/contract_address/casm_contract_address.rs @@ -6,6 +6,7 @@ use starknet_crypto::{poseidon_hash_many, FieldElement}; const CONTRACT_CLASS_VERSION: &[u8] = b"COMPILED_CLASS_V1"; +/// Return hashed entry points for a given contract class and entry point type. fn get_contract_entry_points_hashed( contract_class: &CasmContractClass, entry_point_type: &EntryPointType, @@ -38,6 +39,7 @@ fn get_contract_entry_points_hashed( Ok(poseidon_hash_many(&entry_points_flatted)) } +/// Compute hash for the entire CASM contract class. pub fn compute_casm_class_hash( contract_class: &CasmContractClass, ) -> Result { @@ -80,6 +82,7 @@ pub fn compute_casm_class_hash( )) } +/// Helper function to fetch entry points based on their type. fn get_contract_entry_points( contract_class: &CasmContractClass, entry_point_type: &EntryPointType, diff --git a/src/core/contract_address/deprecated_contract_address.rs b/src/core/contract_address/deprecated_contract_address.rs index 3324fef81..9b5b2ccfb 100644 --- a/src/core/contract_address/deprecated_contract_address.rs +++ b/src/core/contract_address/deprecated_contract_address.rs @@ -21,6 +21,7 @@ use std::{borrow::Cow, collections::BTreeMap, io}; /// Instead of doing a Mask with 250 bits, we are only masking the most significant byte. pub const MASK_3: u8 = 0x03; +/// Returns the contract entry points. fn get_contract_entry_points( contract_class: &ContractClass, entry_point_type: &EntryPointType, @@ -40,6 +41,7 @@ fn get_contract_entry_points( Ok(entry_points.to_owned()) } +/// Recursively add extra spaces to Cairo named tuple representations in a JSON structure. fn add_extra_space_to_cairo_named_tuples(value: &mut serde_json::Value) { match value { serde_json::Value::Array(v) => walk_array(v), @@ -48,12 +50,14 @@ fn add_extra_space_to_cairo_named_tuples(value: &mut serde_json::Value) { } } +/// Helper function to walk through a JSON array and apply extra space to cairo named tuples. fn walk_array(array: &mut [serde_json::Value]) { for v in array.iter_mut() { add_extra_space_to_cairo_named_tuples(v); } } +/// Helper function to walk through a JSON map and apply extra space to cairo named tuples. fn walk_map(object: &mut serde_json::Map) { for (k, v) in object.iter_mut() { match v { @@ -68,6 +72,7 @@ fn walk_map(object: &mut serde_json::Map) { } } +/// Add extra space to named tuple type definition. fn add_extra_space_to_named_tuple_type_definition<'a>( key: &str, value: &'a str, @@ -79,6 +84,7 @@ fn add_extra_space_to_named_tuple_type_definition<'a>( } } +/// Replaces ": " with " : " and " :" with " :" for Cairo-specific formatting. fn add_extra_space_before_colon(v: &str) -> String { // This is required because if we receive an already correct ` : `, we will still // "repair" it to ` : ` which we then fix at the end. @@ -88,11 +94,13 @@ fn add_extra_space_before_colon(v: &str) -> String { struct KeccakWriter(sha3::Keccak256); impl std::io::Write for KeccakWriter { + /// Write data into the Keccak256 hasher. fn write(&mut self, buf: &[u8]) -> std::io::Result { self.0.update(buf); Ok(buf.len()) } + /// No operation is required for flushing, as we finalize after writing. fn flush(&mut self) -> std::io::Result<()> { // noop is fine, we'll finalize after the write phase Ok(()) @@ -104,6 +112,7 @@ impl std::io::Write for KeccakWriter { struct PythonDefaultFormatter; impl serde_json::ser::Formatter for PythonDefaultFormatter { + /// Handles formatting for array values. fn begin_array_value(&mut self, writer: &mut W, first: bool) -> std::io::Result<()> where W: ?Sized + std::io::Write, @@ -115,6 +124,7 @@ impl serde_json::ser::Formatter for PythonDefaultFormatter { } } + /// Handles formatting for object keys. fn begin_object_key(&mut self, writer: &mut W, first: bool) -> std::io::Result<()> where W: ?Sized + std::io::Write, @@ -126,6 +136,7 @@ impl serde_json::ser::Formatter for PythonDefaultFormatter { } } + /// Handles formatting for object values. fn begin_object_value(&mut self, writer: &mut W) -> std::io::Result<()> where W: ?Sized + std::io::Write, @@ -133,6 +144,7 @@ impl serde_json::ser::Formatter for PythonDefaultFormatter { writer.write_all(b": ") } + /// Custom logic for writing string fragments, handling non-ASCII characters. #[inline] fn write_string_fragment(&mut self, writer: &mut W, fragment: &str) -> io::Result<()> where @@ -157,6 +169,7 @@ impl serde_json::ser::Formatter for PythonDefaultFormatter { #[derive(serde::Deserialize, serde::Serialize)] #[serde(deny_unknown_fields)] + pub struct CairoContractDefinition<'a> { /// Contract ABI, which has no schema definition. pub abi: serde_json::Value, @@ -268,6 +281,7 @@ pub(crate) fn compute_hinted_class_hash( Ok(truncated_keccak(<[u8; 32]>::from(hash.finalize()))) } +/// Truncate the given Keccak hash to fit within Felt252's constraints. pub(crate) fn truncated_keccak(mut plain: [u8; 32]) -> Felt252 { // python code masks with (2**250 - 1) which starts 0x03 and is followed by 31 0xff in be // truncation is needed not to overflow the field element. @@ -275,6 +289,7 @@ pub(crate) fn truncated_keccak(mut plain: [u8; 32]) -> Felt252 { Felt252::from_bytes_be(&plain) } +/// Returns the hashed entry points of a contract class. fn get_contract_entry_points_hashed( contract_class: &ContractClass, entry_point_type: &EntryPointType, @@ -292,6 +307,7 @@ fn get_contract_entry_points_hashed( )?) } +/// Compute the hash for a deprecated contract class. pub fn compute_deprecated_class_hash( contract_class: &ContractClass, ) -> Result { diff --git a/src/core/contract_address/sierra_contract_address.rs b/src/core/contract_address/sierra_contract_address.rs index f85b8f66f..1cefac37d 100644 --- a/src/core/contract_address/sierra_contract_address.rs +++ b/src/core/contract_address/sierra_contract_address.rs @@ -12,6 +12,7 @@ const CONTRACT_CLASS_VERSION: &[u8] = b"CONTRACT_CLASS_V0.1.0"; // Version 2 functions and structs // --------------------------------- +/// Computes the hash of contract entry points. fn get_contract_entry_points_hashed( contract_class: &SierraContractClass, entry_point_type: &EntryPointType, @@ -33,6 +34,7 @@ fn get_contract_entry_points_hashed( Ok(hasher.finalize()) } +/// Computes the hash of a given Sierra contract class. pub fn compute_sierra_class_hash( contract_class: &SierraContractClass, ) -> Result { @@ -90,6 +92,7 @@ pub fn compute_sierra_class_hash( Ok(Felt252::from_bytes_be(&hash.to_bytes_be())) } +/// Returns the contract entry points. fn get_contract_entry_points( contract_class: &SierraContractClass, entry_point_type: &EntryPointType, @@ -120,6 +123,7 @@ mod tests { use cairo_vm::felt::felt_str; use std::{fs::File, io::BufReader}; + /// Test the correctness of the compute_sierra_class_hash function for a specific testnet contract. #[test] fn test_declare_tx_from_testnet() { let file = File::open("starknet_programs/cairo2/events.sierra").unwrap(); From 72b675a692de89fbf314ecdba78c602d89f33526 Mon Sep 17 00:00:00 2001 From: Juan Bono Date: Fri, 11 Aug 2023 18:05:35 -0300 Subject: [PATCH 6/7] Add more transaction tests and fee investigation (#914) * add function for getting tx and refactor tests * improve imports * separe tests into 2 groups * fix test * add comments * format * cargo clippy * add details to every test * add fee discrepancy to test doc * cargo fmt * improve imports --- rpc_state_reader/src/lib.rs | 893 +++++++++++++++++------------------- 1 file changed, 418 insertions(+), 475 deletions(-) diff --git a/rpc_state_reader/src/lib.rs b/rpc_state_reader/src/lib.rs index e3c69565a..ce8017042 100644 --- a/rpc_state_reader/src/lib.rs +++ b/rpc_state_reader/src/lib.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Deserializer}; use serde_json::json; use serde_with::{serde_as, DeserializeAs}; use starknet::core::types::ContractClass; +use starknet_in_rust::definitions::block_context::StarknetChainId; use starknet_in_rust::{ core::errors::state_errors::StateError, execution::CallInfo, @@ -15,6 +16,15 @@ use starknet_in_rust::{ use std::env; use thiserror::Error; +#[cfg(test)] +use ::{ + cairo_vm::felt::felt_str, + starknet_in_rust::{ + definitions::constants::EXECUTE_ENTRY_POINT_SELECTOR, + transaction::{InvokeFunction, Transaction}, + }, +}; + /// Starknet chains supported in Infura. #[derive(Debug, Clone, Copy)] pub enum RpcChain { @@ -23,6 +33,16 @@ pub enum RpcChain { TestNet2, } +impl From for StarknetChainId { + fn from(network: RpcChain) -> Self { + match network { + RpcChain::MainNet => StarknetChainId::MainNet, + RpcChain::TestNet => StarknetChainId::TestNet, + RpcChain::TestNet2 => StarknetChainId::TestNet2, + } + } +} + impl fmt::Display for RpcChain { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -174,6 +194,12 @@ impl<'de> Deserialize<'de> for TransactionTrace { #[cfg(test)] impl RpcState { + /// Requests the transaction trace to the Feeder Gateway API. + /// It's useful for testing the transaction outputs like: + /// - execution resources + /// - actual fee + /// - events + /// - return data pub fn get_transaction_trace(&self, hash: Felt252) -> TransactionTrace { let chain_name = self.get_chain_name(); let response = ureq::get(&format!( @@ -187,6 +213,96 @@ impl RpcState { serde_json::from_str(&response.into_string().unwrap()).unwrap() } + /// Requests the given transaction to the Feeder Gateway API. + pub fn get_transaction(&self, hash: &str) -> Transaction { + let params = ureq::json!({ + "jsonrpc": "2.0", + "method": "starknet_getTransactionByHash", + "params": [format!("0x{}", hash)], + "id": 1 + }); + let response: serde_json::Value = self.rpc_call(¶ms).unwrap(); + + match response["result"]["type"].as_str().unwrap() { + "INVOKE" => { + let sender_address = Address(felt_str!( + response["result"]["sender_address"] + .as_str() + .unwrap() + .strip_prefix("0x") + .unwrap(), + 16 + )); + + let entry_point_selector = EXECUTE_ENTRY_POINT_SELECTOR.clone(); + let max_fee = u128::from_str_radix( + response["result"]["max_fee"] + .as_str() + .unwrap() + .strip_prefix("0x") + .unwrap(), + 16, + ) + .unwrap(); + let version = felt_str!( + response["result"]["version"] + .as_str() + .unwrap() + .strip_prefix("0x") + .unwrap(), + 16 + ); + let calldata = response["result"]["calldata"] + .as_array() + .unwrap() + .iter() + .map(|felt_as_value| { + felt_str!( + felt_as_value.as_str().unwrap().strip_prefix("0x").unwrap(), + 16 + ) + }) + .collect::>(); + let signature = response["result"]["signature"] + .as_array() + .unwrap() + .iter() + .map(|felt_as_value| { + felt_str!( + felt_as_value.as_str().unwrap().strip_prefix("0x").unwrap(), + 16 + ) + }) + .collect::>(); + let nonce = Some(felt_str!( + response["result"]["nonce"] + .as_str() + .unwrap() + .strip_prefix("0x") + .unwrap(), + 16 + )); + + let hash_felt = felt_str!(format!("{}", hash), 16); + let tx = InvokeFunction::new_with_tx_hash( + sender_address, + entry_point_selector, + max_fee, + version, + calldata, + signature, + nonce, + hash_felt, + ) + .unwrap(); + + Transaction::InvokeFunction(tx) + } + + _ => unimplemented!(), + } + } + fn get_chain_name(&self) -> String { match self.chain { RpcChain::MainNet => "alpha-mainnet".to_string(), @@ -270,24 +386,11 @@ impl StateReader for RpcState { #[cfg(test)] mod tests { - use cairo_vm::vm::runners::cairo_runner::ExecutionResources; - use std::{collections::HashMap, sync::Arc}; + use std::collections::HashMap; use super::*; - use starknet_in_rust::{ - definitions::{ - block_context::{BlockContext, StarknetChainId, StarknetOsConfig}, - constants::{ - DEFAULT_CAIRO_RESOURCE_FEE_WEIGHTS, - DEFAULT_CONTRACT_STORAGE_COMMITMENT_TREE_HEIGHT, - DEFAULT_GLOBAL_STATE_COMMITMENT_TREE_HEIGHT, DEFAULT_INVOKE_TX_MAX_N_STEPS, - DEFAULT_VALIDATE_MAX_N_STEPS, EXECUTE_ENTRY_POINT_SELECTOR, - }, - }, - felt::felt_str, - state::{cached_state::CachedState, BlockInfo}, - transaction::InvokeFunction, - }; + use cairo_vm::vm::runners::cairo_runner::ExecutionResources; + use starknet_in_rust::felt::felt_str; #[test] fn test_get_contract_class_cairo1() { @@ -382,95 +485,201 @@ mod tests { } #[test] - fn test_invoke_execute() { - let contract_address = Address(felt_str!( - "06fcccb8c9c5bc490600d0d3a95134d3b2aacec7461fc1930178215803fa8d0c", - 16 - )); - let entry_point_selector = EXECUTE_ENTRY_POINT_SELECTOR.clone(); + fn test_get_transaction() { + let rpc_state = RpcState::new( + RpcChain::MainNet, + BlockValue::Tag(serde_json::to_value("latest").unwrap()), + ); + let tx_hash = "06da92cfbdceac5e5e94a1f40772d6c79d34f011815606742658559ec77b6955"; - let max_fee = 103000000000000; - let version = felt_str!("100000000000000000000000000000001", 16); + rpc_state.get_transaction(tx_hash); + } - let calldata = vec![ - felt_str!("1", 16), - felt_str!( - "49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", - 16 - ), - felt_str!( - "83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e", - 16 - ), - felt_str!("0", 16), - felt_str!("3", 16), - felt_str!("3", 16), - felt_str!( - "54ae4dbc24999badc9a161f6f4b72156ca7da92da93046fcbb48680a324ac7c", - 16 - ), - felt_str!("26f00fdabd0000", 16), - felt_str!("0", 16), - ]; + /// Tested with the following query to the Feeder Gateway API: + /// https://alpha4-2.starknet.io/feeder_gateway/get_transaction_trace?transactionHash=0x019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc + #[test] + fn test_get_transaction_trace() { + let state_reader = RpcState::new( + RpcChain::TestNet2, + BlockValue::Number(serde_json::to_value(838683).unwrap()), + ); - let signature = vec![ - felt_str!( - "63cdad41f8f99362b181296492597edef76083a23a71e890c150eda0a848ce2", - 16 - ), - felt_str!( - "3064c7d20438426f1384ddb09cc2bdc1304cfc5dd9d7f7c46de8173f2bc71cf", - 16 - ), - ]; + let tx_hash_str = "19feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc"; + let tx_hash = felt_str!(format!("{}", tx_hash_str), 16); - let nonce = Some(felt_str!("8")); + let tx_trace = state_reader.get_transaction_trace(tx_hash); - let internal_invoke_function = InvokeFunction::new_with_tx_hash( - contract_address, - entry_point_selector, - max_fee, - version, - calldata, - signature, - nonce, - felt_str!( - "014640564509873cf9d24a311e1207040c8b60efd38d96caef79855f0b0075d5", - 16 - ), - ) - .unwrap() - .create_for_simulation(false, false, true, false); // we could include the fee transfer by setting up correctly the BlockContext + assert_eq!( + tx_trace.signature, + vec![ + felt_str!( + "ffab1c47d8d5e5b76bdcc4af79e98205716c36b440f20244c69599a91ace58", + 16 + ), + felt_str!( + "6aa48a0906c9c1f7381c1a040c043b649eeac1eea08f24a9d07813f6b1d05fe", + 16 + ), + ] + ); - // Instantiate CachedState - let state_reader = RpcState::new( - RpcChain::MainNet, - BlockValue::Number(serde_json::to_value(90_006).unwrap()), + assert_eq!( + tx_trace.validate_invocation.calldata, + vec![ + felt_str!("1", 16), + felt_str!( + "690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232", + 16 + ), + felt_str!( + "1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573", + 16 + ), + felt_str!("0", 16), + felt_str!("9", 16), + felt_str!("9", 16), + felt_str!("4", 16), + felt_str!("4254432d55534443", 16), + felt_str!("f02e7324ecbd65ce267", 16), + felt_str!("5754492d55534443", 16), + felt_str!("8e13050d06d8f514c", 16), + felt_str!("4554482d55534443", 16), + felt_str!("f0e4a142c3551c149d", 16), + felt_str!("4a50592d55534443", 16), + felt_str!("38bd34c31a0a5c", 16), + ] + ); + assert_eq!(tx_trace.validate_invocation.retdata, vec![]); + assert_eq!( + tx_trace.validate_invocation.execution_resources, + ExecutionResources { + n_steps: 790, + n_memory_holes: 51, + builtin_instance_counter: HashMap::from([ + ("range_check_builtin".to_string(), 20), + ("ecdsa_builtin".to_string(), 1), + ("pedersen_builtin".to_string(), 2), + ]), + } ); + assert_eq!(tx_trace.validate_invocation.internal_calls.len(), 1); - let mut state = CachedState::new(Arc::new(state_reader), None, None); + assert_eq!( + tx_trace.function_invocation.calldata, + vec![ + felt_str!("1", 16), + felt_str!( + "690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232", + 16 + ), + felt_str!( + "1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573", + 16 + ), + felt_str!("0", 16), + felt_str!("9", 16), + felt_str!("9", 16), + felt_str!("4", 16), + felt_str!("4254432d55534443", 16), + felt_str!("f02e7324ecbd65ce267", 16), + felt_str!("5754492d55534443", 16), + felt_str!("8e13050d06d8f514c", 16), + felt_str!("4554482d55534443", 16), + felt_str!("f0e4a142c3551c149d", 16), + felt_str!("4a50592d55534443", 16), + felt_str!("38bd34c31a0a5c", 16), + ] + ); + assert_eq!(tx_trace.function_invocation.retdata, vec![0.into()]); + assert_eq!( + tx_trace.function_invocation.execution_resources, + ExecutionResources { + n_steps: 2808, + n_memory_holes: 136, + builtin_instance_counter: HashMap::from([ + ("range_check_builtin".to_string(), 49), + ("pedersen_builtin".to_string(), 14), + ]), + } + ); + assert_eq!(tx_trace.function_invocation.internal_calls.len(), 1); + assert_eq!( + tx_trace.function_invocation.internal_calls[0] + .internal_calls + .len(), + 1 + ); + assert_eq!( + tx_trace.function_invocation.internal_calls[0].internal_calls[0] + .internal_calls + .len(), + 7 + ); - let _result = internal_invoke_function - .execute(&mut state, &BlockContext::default(), 0) - .unwrap(); + assert_eq!( + tx_trace.fee_transfer_invocation.calldata, + vec![ + felt_str!( + "1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8", + 16 + ), + felt_str!("2b0322a23ba4", 16), + felt_str!("0", 16), + ] + ); + assert_eq!(tx_trace.fee_transfer_invocation.retdata, vec![1.into()]); + assert_eq!( + tx_trace.fee_transfer_invocation.execution_resources, + ExecutionResources { + n_steps: 586, + n_memory_holes: 42, + builtin_instance_counter: HashMap::from([ + ("range_check_builtin".to_string(), 21), + ("pedersen_builtin".to_string(), 4), + ]), + } + ); + assert_eq!(tx_trace.fee_transfer_invocation.internal_calls.len(), 1); } +} - /// Invoke transaction test using the transaction: - /// https://starkscan.co/tx/0x06da92cfbdceac5e5e94a1f40772d6c79d34f011815606742658559ec77b6955 +#[cfg(test)] +mod transaction_tests { + use super::*; + use starknet_in_rust::{ + definitions::{ + block_context::{BlockContext, StarknetChainId, StarknetOsConfig}, + constants::{ + DEFAULT_CAIRO_RESOURCE_FEE_WEIGHTS, + DEFAULT_CONTRACT_STORAGE_COMMITMENT_TREE_HEIGHT, + DEFAULT_GLOBAL_STATE_COMMITMENT_TREE_HEIGHT, DEFAULT_INVOKE_TX_MAX_N_STEPS, + DEFAULT_VALIDATE_MAX_N_STEPS, + }, + }, + felt::felt_str, + state::{cached_state::CachedState, BlockInfo}, + }; + use std::sync::Arc; + + /// - Transaction Hash: `0x014640564509873cf9d24a311e1207040c8b60efd38d96caef79855f0b0075d5` + /// - Network: `mainnet` + /// - Type: `Invoke` + /// - Contract: StarkGate `0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7` + /// - Entrypoint: `transfer(recipient, amount)` + /// - Fee discrepancy: test=83714806176032, explorer=67749104314311, diff=15965701861721 (23%) + /// - Link to Explorer: https://starkscan.co/tx/0x014640564509873cf9d24a311e1207040c8b60efd38d96caef79855f0b0075d5 #[test] - fn test_invoke_mainnet_0x06da92cfbdceac5e5e94a1f40772d6c79d34f011815606742658559ec77b6955() { - // Tx Hash without the "0x" prefix. - let tx_hash_str = "06da92cfbdceac5e5e94a1f40772d6c79d34f011815606742658559ec77b6955"; + fn test_invoke_0x014640564509873cf9d24a311e1207040c8b60efd38d96caef79855f0b0075d5() { + let tx_hash = "014640564509873cf9d24a311e1207040c8b60efd38d96caef79855f0b0075d5"; - // Retrieve the transaction information from the RPC endpoint. - let rpc_state = RpcState::new(RpcChain::MainNet, BlockValue::Number(90003.into())); - let get_tx_params = ureq::json!({ - "jsonrpc": "2.0", - "method": "starknet_getTransactionByHash", - "params": [format!("0x{}", tx_hash_str)], - "id": 1 - }); - let tx_mainnet: serde_json::Value = rpc_state.rpc_call(&get_tx_params).unwrap(); + // Instantiate the RPC StateReader and the CachedState + let rpc_state = Arc::new(RpcState::new( + RpcChain::MainNet, + BlockValue::Number(serde_json::to_value(90_006).unwrap()), + )); + let mut state = CachedState::new(rpc_state.clone(), None, None); + + // Retrieve the block context let get_block_info_params = ureq::json!({ "jsonrpc": "2.0", "method": "starknet_getBlockWithTxHashes", @@ -479,90 +688,84 @@ mod tests { }); let block_info: serde_json::Value = rpc_state.rpc_call(&get_block_info_params).unwrap(); - // Convert returned data from the tx to our types. - let tx_hash = felt_str!(format!("{}", tx_hash_str), 16); - let contract_address = Address(felt_str!( - tx_mainnet["result"]["sender_address"] - .as_str() - .unwrap() - .strip_prefix("0x") - .unwrap(), + // BlockContext with mainnet data. + // TODO look how to get this value from RPC call. + let gas_price_str = "13563643256"; + let gas_price_u128 = gas_price_str.parse::().unwrap(); + let gas_price_u64 = gas_price_str.parse::().unwrap(); + + let fee_token_address = Address(felt_str!( + "049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", 16 )); - let entry_point_selector = EXECUTE_ENTRY_POINT_SELECTOR.clone(); - let max_fee = u128::from_str_radix( - tx_mainnet["result"]["max_fee"] - .as_str() - .unwrap() - .strip_prefix("0x") + let network: StarknetChainId = rpc_state.chain.into(); + let starknet_os_config = + StarknetOsConfig::new(network.to_felt(), fee_token_address.clone(), gas_price_u128); + + let block_info = BlockInfo { + block_number: block_info["result"]["block_number"] + .to_string() + .parse::() .unwrap(), - 16, - ) - .unwrap(); - let version = felt_str!( - tx_mainnet["result"]["version"] - .as_str() - .unwrap() - .strip_prefix("0x") + block_timestamp: block_info["result"]["timestamp"] + .to_string() + .parse::() .unwrap(), - 16 + gas_price: gas_price_u64, + sequencer_address: fee_token_address, + }; + + let block_context = BlockContext::new( + starknet_os_config, + DEFAULT_CONTRACT_STORAGE_COMMITMENT_TREE_HEIGHT, + DEFAULT_GLOBAL_STATE_COMMITMENT_TREE_HEIGHT, + DEFAULT_CAIRO_RESOURCE_FEE_WEIGHTS.clone(), + DEFAULT_INVOKE_TX_MAX_N_STEPS, + DEFAULT_VALIDATE_MAX_N_STEPS, + block_info, + Default::default(), + true, ); - let calldata = tx_mainnet["result"]["calldata"] - .as_array() - .unwrap() - .iter() - .map(|felt_as_value| { - felt_str!( - felt_as_value.as_str().unwrap().strip_prefix("0x").unwrap(), - 16 - ) - }) - .collect::>(); - let signature = tx_mainnet["result"]["signature"] - .as_array() - .unwrap() - .iter() - .map(|felt_as_value| { - felt_str!( - felt_as_value.as_str().unwrap().strip_prefix("0x").unwrap(), - 16 - ) - }) - .collect::>(); - let nonce = Some(felt_str!( - tx_mainnet["result"]["nonce"] - .as_str() - .unwrap() - .strip_prefix("0x") - .unwrap(), - 16 - )); - // Create InvokeFunction with the converted data. - let internal_invoke_function = InvokeFunction::new_with_tx_hash( - contract_address, - entry_point_selector, - max_fee, - version, - calldata, - signature, - nonce, - tx_hash, - ) - .unwrap() - .create_for_simulation(false, false, false, false); + let tx = rpc_state.get_transaction(tx_hash); + let result = tx.execute(&mut state, &block_context, 0).unwrap(); + dbg!(&result.actual_resources); + dbg!(&result.actual_fee); // test=83714806176032, explorer=67749104314311, diff=15965701861721 (23%) + dbg!(&result.call_info.clone().unwrap().execution_resources); // Ok with explorer + dbg!(&result.call_info.unwrap().internal_calls.len()); // Ok with explorer + } - // Instantiate CachedState - let state_reader = RpcState::new( + /// - Transaction Hash: `0x06da92cfbdceac5e5e94a1f40772d6c79d34f011815606742658559ec77b6955` + /// - Network: `mainnet` + /// - Type: `Invoke` + /// - Contract: mySwap: `0x022b05f9396d2c48183f6deaf138a57522bcc8b35b67dee919f76403d1783136` and `0x010884171baf1914edc28d7afb619b40a4051cfae78a094a55d230f19e944a28` + /// - Entrypoint: 1 call to `approve(spender, amount)` and 1 call to `withdraw_liquidity(pool_id, shares_amount, amount_min_a, amount_min_b)` + /// - Fee discrepancy: test=267319013054160, explorer=219298652474858, diff=48020360579302 (22%) + /// - Link to Explorer: https://starkscan.co/tx/0x06da92cfbdceac5e5e94a1f40772d6c79d34f011815606742658559ec77b6955 + #[test] + fn test_invoke_mainnet_0x06da92cfbdceac5e5e94a1f40772d6c79d34f011815606742658559ec77b6955() { + // Tx Hash without the "0x" prefix. + let tx_hash = "06da92cfbdceac5e5e94a1f40772d6c79d34f011815606742658559ec77b6955"; + + // Create RPC StateReader and CachedState + let rpc_state = Arc::new(RpcState::new( RpcChain::MainNet, BlockValue::Number(serde_json::to_value(90_002).unwrap()), - ); + )); + let mut state = CachedState::new(rpc_state.clone(), None, None); - let mut state = CachedState::new(Arc::new(state_reader), None, None); + // Retrieve the block context + let get_block_info_params = ureq::json!({ + "jsonrpc": "2.0", + "method": "starknet_getBlockWithTxHashes", + "params": [rpc_state.block.to_value()], + "id": 1 + }); + let block_info: serde_json::Value = rpc_state.rpc_call(&get_block_info_params).unwrap(); // BlockContext with mainnet data. // TODO look how to get this value from RPC call. - let gas_price_str = "13575501577"; + let gas_price_str = "13572248835"; // from block 90_002 let gas_price_u128 = gas_price_str.parse::().unwrap(); let gas_price_u64 = gas_price_str.parse::().unwrap(); @@ -570,11 +773,9 @@ mod tests { "049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", 16 )); - let starknet_os_config = StarknetOsConfig::new( - StarknetChainId::MainNet.to_felt(), - fee_token_address.clone(), - gas_price_u128, - ); + let network: StarknetChainId = rpc_state.chain.into(); + let starknet_os_config = + StarknetOsConfig::new(network.to_felt(), fee_token_address.clone(), gas_price_u128); let block_info = BlockInfo { block_number: block_info["result"]["block_number"] @@ -601,83 +802,37 @@ mod tests { true, ); - let _result = internal_invoke_function - .execute(&mut state, &block_context, 0) - .unwrap(); + let tx = rpc_state.get_transaction(tx_hash); + let result = tx.execute(&mut state, &block_context, 0).unwrap(); + dbg!(&result.actual_resources); + dbg!(&result.actual_fee); // test=267319013054160, explorer=219298652474858, diff=48020360579302 (22%) + dbg!(&result.call_info.clone().unwrap().execution_resources); // Ok with explorer + dbg!(&result.call_info.unwrap().internal_calls.len()); // distinct, explorer=7, test=1 } - /// Invoke transaction test using the transaction: - /// https://testnet.starkscan.co/tx/0x074dab0828ec1b6cfde5188c41d41af1c198192a7d118217f95a802aa923dacf + /// - Transaction Hash: `0x074dab0828ec1b6cfde5188c41d41af1c198192a7d118217f95a802aa923dacf` + /// - Network: `testnet` + /// - Type: `Invoke` + /// - Contract: Fibonacci `0x012d37c39a385cf56801b57626e039147abce1183ce55e419e4296398b81d9e2` + /// - Entrypoint: `fib(first_element, second_element, n)` + /// - Fee discrepancy: test=7252831227950, explorer=7207614784695, diff=45216443255 (0.06%) + /// - Link to Explorer: https://testnet.starkscan.co/tx/0x074dab0828ec1b6cfde5188c41d41af1c198192a7d118217f95a802aa923dacf #[test] fn test_invoke_mainnet_0x074dab0828ec1b6cfde5188c41d41af1c198192a7d118217f95a802aa923dacf() { // Tx Hash without the "0x" prefix. let tx_hash_str = "074dab0828ec1b6cfde5188c41d41af1c198192a7d118217f95a802aa923dacf"; - let tx_hash = felt_str!(format!("{}", tx_hash_str), 16); - let contract_address = Address(felt_str!( - "02dc97a4cc28fa95be6a6ae92cc1a2e3fb07eb6866c65e039daa75391806c254", - 16 - )); - let entry_point_selector = EXECUTE_ENTRY_POINT_SELECTOR.clone(); - let max_fee = 10811422177042; - let version = felt_str!("1", 16); - let calldata = [ - felt_str!("1", 16), - felt_str!( - "12d37c39a385cf56801b57626e039147abce1183ce55e419e4296398b81d9e2", - 16 - ), - felt_str!( - "112e35f48499939272000bd72eb840e502ca4c3aefa8800992e8defb746e0c9", - 16 - ), - felt_str!("0", 16), - felt_str!("3", 16), - felt_str!("3", 16), - felt_str!("1", 16), - felt_str!("1", 16), - felt_str!("10", 16), - ] - .to_vec(); - - let signature = [ - felt_str!( - "3043488d10251917860d388304d993d259c750f28f147bf986e2f6e6af28df2", - 16 - ), - felt_str!( - "24bfcbb6be97350eaeb42f2d81dd66efa92ef725c2ad3127750b67ffd50508d", - 16 - ), - ] - .to_vec(); - let nonce = Some(felt_str!("4", 16)); - - // Create InvokeFunction with the converted data. - let internal_invoke_function = InvokeFunction::new_with_tx_hash( - contract_address, - entry_point_selector, - max_fee, - version, - calldata, - signature, - nonce, - tx_hash, - ) - .unwrap() - .create_for_simulation(false, false, false, false); - // Instantiate CachedState - let state_reader = RpcState::new( + let rpc_state = Arc::new(RpcState::new( RpcChain::TestNet, BlockValue::Number(serde_json::to_value(838683).unwrap()), - ); + )); - let mut state = CachedState::new(Arc::new(state_reader), None, None); + let mut state = CachedState::new(rpc_state.clone(), None, None); // BlockContext with mainnet data. // TODO look how to get this value from RPC call. - let gas_price_str = "2888823561"; + let gas_price_str = "2917470325"; // from block 838683 let gas_price_u128 = gas_price_str.parse::().unwrap(); let gas_price_u64 = gas_price_str.parse::().unwrap(); @@ -685,11 +840,9 @@ mod tests { "049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", 16 )); - let starknet_os_config = StarknetOsConfig::new( - StarknetChainId::TestNet.to_felt(), - fee_token_address, - gas_price_u128, - ); + let network: StarknetChainId = rpc_state.chain.into(); + let starknet_os_config = + StarknetOsConfig::new(network.to_felt(), fee_token_address, gas_price_u128); let block_info = BlockInfo { block_number: 838684, @@ -712,109 +865,44 @@ mod tests { Default::default(), true, ); + let tx = rpc_state.get_transaction(tx_hash_str); - let _result = internal_invoke_function - .execute(&mut state, &block_context, 0) - .unwrap(); + let result = tx.execute(&mut state, &block_context, 0).unwrap(); + dbg!(&result.actual_resources); + dbg!(&result.actual_fee); // test=7252831227950, explorer=7207614784695, diff=45216443255 (0.06%) + dbg!(&result.call_info.clone().unwrap().execution_resources); // Ok with explorer + dbg!(&result.call_info.unwrap().internal_calls.len()); // Ok with explorer } - /// Invoke transaction test using the transaction: - /// https://testnet-2.starkscan.co/tx/0x019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc + /// - Transaction Hash: 0x019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc + /// - Network: testnet-2 + /// - Type: Invoke + /// - Contract: 0x0690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232 + /// - Entrypoint: update_multiple_market_prices(market_prices_list_len, market_prices_list) + /// - Fee discrepancy: test=6361070805216, explorer=47292465953700, diff=5888146145679 (0.13%) + /// - Link to Explorer: https://testnet-2.starkscan.co/tx/0x019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc #[test] fn test_invoke_testnet2_0x019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc() { // Tx Hash without the "0x" prefix. let tx_hash_str = "019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc"; - let tx_hash = felt_str!(format!("{}", tx_hash_str), 16); - let entry_point_selector = EXECUTE_ENTRY_POINT_SELECTOR.clone(); - let max_fee = 10811422177042; - let version = felt_str!("1", 16); - - let calldata = [ - felt_str!("1", 16), - felt_str!( - "690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232", - 16 - ), - felt_str!( - "1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573", - 16 - ), - felt_str!("0", 16), - felt_str!("9", 16), - felt_str!("9", 16), - felt_str!("4", 16), - felt_str!("4254432d55534443", 16), - felt_str!("f02e7324ecbd65ce267", 16), - felt_str!("5754492d55534443", 16), - felt_str!("8e13050d06d8f514c", 16), - felt_str!("4554482d55534443", 16), - felt_str!("f0e4a142c3551c149d", 16), - felt_str!("4a50592d55534443", 16), - felt_str!("38bd34c31a0a5c", 16), - ] - .to_vec(); - - let signature = [ - felt_str!( - "ffab1c47d8d5e5b76bdcc4af79e98205716c36b440f20244c69599a91ace58", - 16 - ), - felt_str!( - "6aa48a0906c9c1f7381c1a040c043b649eeac1eea08f24a9d07813f6b1d05fe", - 16 - ), - ] - .to_vec(); - let nonce = Some(16930.into()); - - // Instantiate CachedState - let state_reader = RpcState::new( + // Instantiate the RPC StateReader and the CachedState + let rpc_state = Arc::new(RpcState::new( RpcChain::TestNet2, BlockValue::Number(serde_json::to_value(123001).unwrap()), - ); + )); - let get_tx_params = ureq::json!({ - "jsonrpc": "2.0", - "method": "starknet_getTransactionByHash", - "params": [format!("0x{}", tx_hash_str)], - "id": 1 - }); - let tx_testnet2: serde_json::Value = state_reader.rpc_call(&get_tx_params).unwrap(); let get_block_info_params = ureq::json!({ "jsonrpc": "2.0", "method": "starknet_getBlockWithTxHashes", - "params": [state_reader.block.to_value()], + "params": [rpc_state.block.to_value()], "id": 1 }); - let block_info: serde_json::Value = state_reader.rpc_call(&get_block_info_params).unwrap(); - - let contract_address = Address(felt_str!( - tx_testnet2["result"]["sender_address"] - .as_str() - .unwrap() - .strip_prefix("0x") - .unwrap(), - 16 - )); - - // Create InvokeFunction with the converted data. - let internal_invoke_function = InvokeFunction::new_with_tx_hash( - contract_address, - entry_point_selector, - max_fee, - version, - calldata, - signature, - nonce, - tx_hash, - ) - .unwrap() - .create_for_simulation(true, false, true, true); + let block_info: serde_json::Value = rpc_state.rpc_call(&get_block_info_params).unwrap(); // BlockContext with mainnet data. // TODO look how to get this value from RPC call. - let gas_price_str = "2888823561"; + let gas_price_str = "272679647"; // from block 123001 let gas_price_u128 = gas_price_str.parse::().unwrap(); let gas_price_u64 = gas_price_str.parse::().unwrap(); @@ -836,13 +924,11 @@ mod tests { sequencer_address: fee_token_address.clone(), }; - let mut state = CachedState::new(Arc::new(state_reader), None, None); + let mut state = CachedState::new(rpc_state.clone(), None, None); - let starknet_os_config = StarknetOsConfig::new( - StarknetChainId::TestNet2.to_felt(), - fee_token_address, - gas_price_u128, - ); + let network: StarknetChainId = rpc_state.chain.into(); + let starknet_os_config = + StarknetOsConfig::new(network.to_felt(), fee_token_address, gas_price_u128); let block_context = BlockContext::new( starknet_os_config, @@ -855,154 +941,11 @@ mod tests { Default::default(), true, ); - - let _result = internal_invoke_function - .execute(&mut state, &block_context, 0) - .unwrap(); - } - // https://alpha4-2.starknet.io/feeder_gateway/get_transaction_trace?transactionHash=0x019feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc - #[test] - fn test_get_transaction_trace() { - let state_reader = RpcState::new( - RpcChain::TestNet2, - BlockValue::Number(serde_json::to_value(838683).unwrap()), - ); - - let tx_hash_str = "19feb888a2d53ffddb7a1750264640afab8e9c23119e648b5259f1b5e7d51bc"; - let tx_hash = felt_str!(format!("{}", tx_hash_str), 16); - - let tx_trace = state_reader.get_transaction_trace(tx_hash); - - assert_eq!( - tx_trace.signature, - vec![ - felt_str!( - "ffab1c47d8d5e5b76bdcc4af79e98205716c36b440f20244c69599a91ace58", - 16 - ), - felt_str!( - "6aa48a0906c9c1f7381c1a040c043b649eeac1eea08f24a9d07813f6b1d05fe", - 16 - ), - ] - ); - - assert_eq!( - tx_trace.validate_invocation.calldata, - vec![ - felt_str!("1", 16), - felt_str!( - "690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232", - 16 - ), - felt_str!( - "1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573", - 16 - ), - felt_str!("0", 16), - felt_str!("9", 16), - felt_str!("9", 16), - felt_str!("4", 16), - felt_str!("4254432d55534443", 16), - felt_str!("f02e7324ecbd65ce267", 16), - felt_str!("5754492d55534443", 16), - felt_str!("8e13050d06d8f514c", 16), - felt_str!("4554482d55534443", 16), - felt_str!("f0e4a142c3551c149d", 16), - felt_str!("4a50592d55534443", 16), - felt_str!("38bd34c31a0a5c", 16), - ] - ); - assert_eq!(tx_trace.validate_invocation.retdata, vec![]); - assert_eq!( - tx_trace.validate_invocation.execution_resources, - ExecutionResources { - n_steps: 790, - n_memory_holes: 51, - builtin_instance_counter: HashMap::from([ - ("range_check_builtin".to_string(), 20), - ("ecdsa_builtin".to_string(), 1), - ("pedersen_builtin".to_string(), 2), - ]), - } - ); - assert_eq!(tx_trace.validate_invocation.internal_calls.len(), 1); - - assert_eq!( - tx_trace.function_invocation.calldata, - vec![ - felt_str!("1", 16), - felt_str!( - "690c876e61beda61e994543af68038edac4e1cb1990ab06e52a2d27e56a1232", - 16 - ), - felt_str!( - "1f24f689ced5802b706d7a2e28743fe45c7bfa37431c97b1c766e9622b65573", - 16 - ), - felt_str!("0", 16), - felt_str!("9", 16), - felt_str!("9", 16), - felt_str!("4", 16), - felt_str!("4254432d55534443", 16), - felt_str!("f02e7324ecbd65ce267", 16), - felt_str!("5754492d55534443", 16), - felt_str!("8e13050d06d8f514c", 16), - felt_str!("4554482d55534443", 16), - felt_str!("f0e4a142c3551c149d", 16), - felt_str!("4a50592d55534443", 16), - felt_str!("38bd34c31a0a5c", 16), - ] - ); - assert_eq!(tx_trace.function_invocation.retdata, vec![0.into()]); - assert_eq!( - tx_trace.function_invocation.execution_resources, - ExecutionResources { - n_steps: 2808, - n_memory_holes: 136, - builtin_instance_counter: HashMap::from([ - ("range_check_builtin".to_string(), 49), - ("pedersen_builtin".to_string(), 14), - ]), - } - ); - assert_eq!(tx_trace.function_invocation.internal_calls.len(), 1); - assert_eq!( - tx_trace.function_invocation.internal_calls[0] - .internal_calls - .len(), - 1 - ); - assert_eq!( - tx_trace.function_invocation.internal_calls[0].internal_calls[0] - .internal_calls - .len(), - 7 - ); - - assert_eq!( - tx_trace.fee_transfer_invocation.calldata, - vec![ - felt_str!( - "1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8", - 16 - ), - felt_str!("2b0322a23ba4", 16), - felt_str!("0", 16), - ] - ); - assert_eq!(tx_trace.fee_transfer_invocation.retdata, vec![1.into()]); - assert_eq!( - tx_trace.fee_transfer_invocation.execution_resources, - ExecutionResources { - n_steps: 586, - n_memory_holes: 42, - builtin_instance_counter: HashMap::from([ - ("range_check_builtin".to_string(), 21), - ("pedersen_builtin".to_string(), 4), - ]), - } - ); - assert_eq!(tx_trace.fee_transfer_invocation.internal_calls.len(), 1); + let tx = rpc_state.get_transaction(tx_hash_str); + let result = tx.execute(&mut state, &block_context, 0).unwrap(); + dbg!(&result.actual_resources); + dbg!(&result.actual_fee); // test=6361070805216, explorer=47292465953700, diff=5888146145679 (0.13%) + dbg!(&result.call_info.clone().unwrap().execution_resources); // Ok with explorer + dbg!(&result.call_info.unwrap().internal_calls.len()); // Ok with explorer } } From cf1d139bd76583a5209d251aedd52672b48f9cfe Mon Sep 17 00:00:00 2001 From: fannyguthmann Date: Mon, 14 Aug 2023 17:07:57 +0300 Subject: [PATCH 7/7] added safety element --- src/transaction/l1_handler.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/transaction/l1_handler.rs b/src/transaction/l1_handler.rs index b915a5cf5..964fc180d 100644 --- a/src/transaction/l1_handler.rs +++ b/src/transaction/l1_handler.rs @@ -74,6 +74,11 @@ impl L1Handler { ) } /// Creates a new [L1Handler] instance with a specified transaction hash. + /// + /// # Safety + /// + /// `tx_hash` will be assumed to be the same as would result from calling + /// `calculate_transaction_hash_common`. Non-compliance will result in silent misbehavior. pub fn new_with_tx_hash( contract_address: Address, entry_point_selector: Felt252,