diff --git a/rpc_state_reader/src/rpc_state.rs b/rpc_state_reader/src/rpc_state.rs index 8401a897b..76bdd4922 100644 --- a/rpc_state_reader/src/rpc_state.rs +++ b/rpc_state_reader/src/rpc_state.rs @@ -418,7 +418,7 @@ impl RpcState { contract_address.0.key().clone().to_string() ]), ) - .unwrap(); + .unwrap_or_default(); ClassHash(hash) } @@ -431,7 +431,8 @@ impl RpcState { contract_address.0.key().clone().to_string() ]), ) - .unwrap() + // When running deploy_account transactions, the nonce doesn't exist on the previous block so we return 0 + .unwrap_or_default() } pub fn get_storage_at( @@ -450,7 +451,7 @@ impl RpcState { self.block.to_value().unwrap() ]), ) - .unwrap() + .unwrap_or_default() } /// Requests the given transaction to the Feeder Gateway API. diff --git a/rpc_state_reader/src/utils.rs b/rpc_state_reader/src/utils.rs index a56d9f3b7..59c1d9121 100644 --- a/rpc_state_reader/src/utils.rs +++ b/rpc_state_reader/src/utils.rs @@ -82,6 +82,9 @@ pub fn deserialize_transaction_json( "unimplemented invoke version: {x}" ))), }, + "DEPLOY_ACCOUNT" => Ok(Transaction::DeployAccount(serde_json::from_value( + transaction, + )?)), x => Err(serde::de::Error::custom(format!( "unimplemented transaction type deserialization: {x}" ))), diff --git a/rpc_state_reader/tests/blockifier_tests.rs b/rpc_state_reader/tests/blockifier_tests.rs index a32139934..0bcb17f0e 100644 --- a/rpc_state_reader/tests/blockifier_tests.rs +++ b/rpc_state_reader/tests/blockifier_tests.rs @@ -6,8 +6,9 @@ use blockifier::{ state_api::{StateReader, StateResult}, }, transaction::{ - account_transaction::AccountTransaction, objects::TransactionExecutionInfo, - transactions::ExecutableTransaction, + account_transaction::AccountTransaction, + objects::TransactionExecutionInfo, + transactions::{DeployAccountTransaction, ExecutableTransaction}, }, }; use blockifier::{ @@ -27,7 +28,10 @@ use starknet::core::types::ContractClass as SNContractClass; use starknet_api::{ block::BlockNumber, contract_address, - core::{ClassHash, CompiledClassHash, ContractAddress, Nonce, PatriciaKey}, + core::{ + calculate_contract_address, ClassHash, CompiledClassHash, ContractAddress, Nonce, + PatriciaKey, + }, hash::{StarkFelt, StarkHash}, patricia_key, stark_felt, state::StorageKey, @@ -176,6 +180,20 @@ pub fn execute_tx( let invoke = InvokeTransaction { tx, tx_hash }; AccountTransaction::Invoke(invoke) } + SNTransaction::DeployAccount(tx) => { + let contract_address = calculate_contract_address( + tx.contract_address_salt, + tx.class_hash, + &tx.constructor_calldata, + ContractAddress::default(), + ) + .unwrap(); + AccountTransaction::DeployAccount(DeployAccountTransaction { + tx, + tx_hash, + contract_address, + }) + } _ => unimplemented!(), }; @@ -286,6 +304,16 @@ fn blockifier_test_recent_tx() { 186551, // real block 186552 RpcChain::MainNet )] +#[test_case( + "0x1cbc74e101a1533082a021ce53235cfd744899b0ff948d1949a64646e0f15c2", + 885298, // real block 885299 + RpcChain::TestNet +)] +#[test_case( + "0x5a5de1f42f6005f3511ea6099daed9bcbcf9de334ee714e8563977e25f71601", + 281513, // real block 281514 + RpcChain::MainNet +)] fn blockifier_test_case_tx(hash: &str, block_number: u64, chain: RpcChain) { let (tx_info, trace, receipt) = execute_tx(hash, chain, BlockNumber(block_number)); diff --git a/rpc_state_reader/tests/sir_tests.rs b/rpc_state_reader/tests/sir_tests.rs index 9cb71fe22..4fdbcbe12 100644 --- a/rpc_state_reader/tests/sir_tests.rs +++ b/rpc_state_reader/tests/sir_tests.rs @@ -28,7 +28,7 @@ use starknet_in_rust::{ state_cache::StorageEntry, BlockInfo, }, - transaction::InvokeFunction, + transaction::{DeployAccount, InvokeFunction}, utils::{Address, ClassHash}, }; @@ -143,6 +143,11 @@ pub fn execute_tx_configurable( SNTransaction::Invoke(tx) => InvokeFunction::from_invoke_transaction(tx, chain_id) .unwrap() .create_for_simulation(skip_validate, false, false, false, skip_nonce_check), + SNTransaction::DeployAccount(tx) => { + DeployAccount::from_sn_api_transaction(tx, chain_id.to_felt()) + .unwrap() + .create_for_simulation(skip_validate, false, false, false) + } _ => unimplemented!(), }; @@ -277,6 +282,16 @@ fn test_get_gas_price() { 317092, // real block 317093 RpcChain::MainNet )] +#[test_case( + "0x1cbc74e101a1533082a021ce53235cfd744899b0ff948d1949a64646e0f15c2", + 885298, // real block 885299 + RpcChain::TestNet +)] +#[test_case( + "0x5a5de1f42f6005f3511ea6099daed9bcbcf9de334ee714e8563977e25f71601", + 281513, // real block 281514 + RpcChain::MainNet +)] fn starknet_in_rust_test_case_tx(hash: &str, block_number: u64, chain: RpcChain) { let (tx_info, trace, receipt) = execute_tx(hash, chain, BlockNumber(block_number)); diff --git a/src/definitions/constants.rs b/src/definitions/constants.rs index 22be16e6c..4087af325 100644 --- a/src/definitions/constants.rs +++ b/src/definitions/constants.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; pub(crate) const L2_TO_L1_MSG_HEADER_SIZE: usize = 3; pub(crate) const L1_TO_L2_MSG_HEADER_SIZE: usize = 5; -pub(crate) const DEPLOYMENT_INFO_SIZE: usize = 1; +pub(crate) const CLASS_UPDATE_SIZE: usize = 1; pub(crate) const CONSUMED_MSG_TO_L2_N_TOPICS: usize = 3; pub(crate) const LOG_MSG_TO_L1_N_TOPICS: usize = 2; pub(crate) const N_DEFAULT_TOPICS: usize = 1; // Events have one default topic. diff --git a/src/execution/gas_usage.rs b/src/execution/gas_usage.rs index 683273177..85e26a90a 100644 --- a/src/execution/gas_usage.rs +++ b/src/execution/gas_usage.rs @@ -1,6 +1,7 @@ use crate::definitions::constants::*; use crate::execution::L2toL1MessageInfo; use crate::services::eth_definitions::eth_gas_constants::*; +use crate::state::state_api::StateChangesCount; /// Estimates L1 gas usage by Starknet's update state and the verifier /// @@ -19,16 +20,13 @@ use crate::services::eth_definitions::eth_gas_constants::*; /// The estimation of L1 gas usage as a `usize` value. pub fn calculate_tx_gas_usage( l2_to_l1_messages: Vec, - n_modified_contracts: usize, - n_storage_changes: usize, + state_changes: &StateChangesCount, l1_handler_payload_size: Option, - n_deployments: usize, ) -> usize { let residual_message_segment_length = get_message_segment_lenght(&l2_to_l1_messages, l1_handler_payload_size); - let residual_onchain_data_segment_length = - get_onchain_data_segment_length(n_modified_contracts, n_storage_changes, n_deployments); + let residual_onchain_data_segment_length = get_onchain_data_segment_length(state_changes); let n_l2_to_l1_messages = l2_to_l1_messages.len(); let n_l1_to_l2_messages = match l1_handler_payload_size { @@ -95,22 +93,18 @@ pub fn get_message_segment_lenght( } /// Calculates the amount of `felt252` added to the output message's segment by the given operations. -/// -/// # Parameters: -/// -/// - `n_modified_contracts`: The number of contracts modified by the transaction. -/// - `n_storage_changes`: The number of storage changes made by the transaction. -/// - `n_deployments`: The number of contracts deployed by the transaction. -/// -/// # Returns: -/// -/// The on-chain data segment length -pub const fn get_onchain_data_segment_length( - n_modified_contracts: usize, - n_storage_changes: usize, - n_deployments: usize, -) -> usize { - n_modified_contracts * 2 + n_deployments * DEPLOYMENT_INFO_SIZE + n_storage_changes * 2 +pub const fn get_onchain_data_segment_length(state_changes: &StateChangesCount) -> usize { + // For each newly modified contract: + // contract address (1 word). + // + 1 word with the following info: A flag indicating whether the class hash was updated, the + // number of entry updates, and the new nonce. + state_changes.n_modified_contracts * 2 + // For each class updated (through a deploy or a class replacement). + + state_changes.n_class_hash_updates * CLASS_UPDATE_SIZE + // For each modified storage cell: key, new value. + + state_changes.n_storage_updates * 2 + // For each compiled class updated (through declare): class_hash, compiled_class_hash + + state_changes.n_compiled_class_hash_updates * 2 } /// Calculates the cost of ConsumedMessageToL2 event emissions caused by an L1 handler with the given @@ -261,7 +255,16 @@ mod test { let message2 = L2toL1MessageInfo::new(ord_ev2, Address(1235.into())); assert_eq!( - calculate_tx_gas_usage(vec![message1, message2], 2, 2, Some(2), 1), + calculate_tx_gas_usage( + vec![message1, message2], + &StateChangesCount { + n_storage_updates: 2, + n_class_hash_updates: 1, + n_compiled_class_hash_updates: 0, + n_modified_contracts: 2 + }, + Some(2) + ), 76439 ) } diff --git a/src/services/api/contract_classes/compiled_class.rs b/src/services/api/contract_classes/compiled_class.rs index 7306d5756..26b56281f 100644 --- a/src/services/api/contract_classes/compiled_class.rs +++ b/src/services/api/contract_classes/compiled_class.rs @@ -130,7 +130,7 @@ impl From for CompiledClass { ) }) .collect::>(); - entry_points_by_type.insert(EntryPointType::Constructor, l1_handler_entries); + entry_points_by_type.insert(EntryPointType::L1Handler, l1_handler_entries); let v = serde_json::to_value(&_deprecated_contract_class.abi).unwrap(); let abi: Option = serde_json::from_value(v).unwrap(); diff --git a/src/state/cached_state.rs b/src/state/cached_state.rs index 4d9820c30..f47d5a60e 100644 --- a/src/state/cached_state.rs +++ b/src/state/cached_state.rs @@ -1,5 +1,5 @@ use super::{ - state_api::{State, StateReader}, + state_api::{State, StateChangesCount, StateReader}, state_cache::{StateCache, StorageEntry}, }; use crate::{ @@ -283,10 +283,10 @@ impl State for CachedState { Ok(()) } - fn count_actual_storage_changes( + fn count_actual_state_changes( &mut self, fee_token_and_sender_address: Option<(&Address, &Address)>, - ) -> Result<(usize, usize), StateError> { + ) -> Result { self.update_initial_values_of_write_only_accesses()?; let mut storage_updates = subtract_mappings( @@ -296,9 +296,16 @@ impl State for CachedState { let storage_unique_updates = storage_updates.keys().map(|k| k.0.clone()); - let class_hash_updates = subtract_mappings_keys( + let class_hash_updates: Vec<&Address> = subtract_mappings_keys( &self.cache.class_hash_writes, &self.cache.class_hash_initial_values, + ) + .collect(); + let n_class_hash_updates = class_hash_updates.len(); + + let compiled_class_hash_updates = subtract_mappings_keys( + &self.cache.compiled_class_hash_writes, + &self.cache.compiled_class_hash_initial_values, ); let nonce_updates = @@ -306,7 +313,7 @@ impl State for CachedState { let mut modified_contracts: HashSet
= HashSet::new(); modified_contracts.extend(storage_unique_updates); - modified_contracts.extend(class_hash_updates.cloned()); + modified_contracts.extend(class_hash_updates.into_iter().cloned()); modified_contracts.extend(nonce_updates.cloned()); // Add fee transfer storage update before actually charging it, as it needs to be included in the @@ -320,7 +327,12 @@ impl State for CachedState { modified_contracts.remove(fee_token_address); } - Ok((modified_contracts.len(), storage_updates.len())) + Ok(StateChangesCount { + n_storage_updates: storage_updates.len(), + n_class_hash_updates, + n_compiled_class_hash_updates: compiled_class_hash_updates.count(), + n_modified_contracts: modified_contracts.len(), + }) } /// Returns the class hash for a given contract address. @@ -800,7 +812,7 @@ mod tests { /// This test calculate the number of actual storage changes. #[test] - fn count_actual_storage_changes_test() { + fn count_actual_state_changes_test() { let state_reader = InMemoryStateReader::default(); let mut cached_state = CachedState::new(Arc::new(state_reader), HashMap::new()); @@ -822,14 +834,15 @@ mod tests { let fee_token_address = Address(123.into()); let sender_address = Address(321.into()); - let expected_changes = { - let n_storage_updates = 3 + 1; // + 1 fee transfer balance update - let n_modified_contracts = 2; - - (n_modified_contracts, n_storage_updates) + let expected_changes = StateChangesCount { + n_storage_updates: 3 + 1, // + 1 fee transfer balance update, + n_class_hash_updates: 0, + n_compiled_class_hash_updates: 0, + n_modified_contracts: 2, }; + let changes = cached_state - .count_actual_storage_changes(Some((&fee_token_address, &sender_address))) + .count_actual_state_changes(Some((&fee_token_address, &sender_address))) .unwrap(); assert_eq!(changes, expected_changes); diff --git a/src/state/state_api.rs b/src/state/state_api.rs index 818a063d6..4abe12354 100644 --- a/src/state/state_api.rs +++ b/src/state/state_api.rs @@ -26,6 +26,14 @@ pub trait StateReader { ) -> Result; } +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct StateChangesCount { + pub n_storage_updates: usize, + pub n_class_hash_updates: usize, + pub n_compiled_class_hash_updates: usize, + pub n_modified_contracts: usize, +} + pub trait State { fn set_contract_class( &mut self, @@ -63,11 +71,11 @@ pub trait State { fn apply_state_update(&mut self, sate_updates: &StateDiff) -> Result<(), StateError>; - /// Counts the amount of modified contracts and the updates to the storage - fn count_actual_storage_changes( + /// Counts the amount of state changes + fn count_actual_state_changes( &mut self, fee_token_and_sender_address: Option<(&Address, &Address)>, - ) -> Result<(usize, usize), StateError>; + ) -> Result; /// Returns the class hash of the contract class at the given address. /// Returns zero by default if the value is not present diff --git a/src/transaction/declare.rs b/src/transaction/declare.rs index 51e42fed1..762dfa5c9 100644 --- a/src/transaction/declare.rs +++ b/src/transaction/declare.rs @@ -166,7 +166,7 @@ impl Declare { } else { self.run_validate_entrypoint(state, &mut resources_manager, block_context)? }; - let changes = state.count_actual_storage_changes(Some(( + let changes = state.count_actual_state_changes(Some(( &block_context.starknet_os_config.fee_token_address, &self.sender_address, )))?; diff --git a/src/transaction/declare_v2.rs b/src/transaction/declare_v2.rs index 854e94468..879f4efa5 100644 --- a/src/transaction/declare_v2.rs +++ b/src/transaction/declare_v2.rs @@ -328,7 +328,7 @@ impl DeclareV2 { (info, gas) }; - let storage_changes = state.count_actual_storage_changes(Some(( + let storage_changes = state.count_actual_state_changes(Some(( &block_context.starknet_os_config.fee_token_address, &self.sender_address, )))?; diff --git a/src/transaction/deploy.rs b/src/transaction/deploy.rs index 9632d8786..69b5b4267 100644 --- a/src/transaction/deploy.rs +++ b/src/transaction/deploy.rs @@ -187,7 +187,7 @@ impl Deploy { let resources_manager = ExecutionResourcesManager::default(); - let changes = state.count_actual_storage_changes(None)?; + let changes = state.count_actual_state_changes(None)?; let actual_resources = calculate_tx_resources( resources_manager, &[Some(call_info.clone())], @@ -250,7 +250,7 @@ impl Deploy { block_context.validate_max_n_steps, )?; - let changes = state.count_actual_storage_changes(None)?; + let changes = state.count_actual_state_changes(None)?; let actual_resources = calculate_tx_resources( resources_manager, &[call_info.clone()], diff --git a/src/transaction/deploy_account.rs b/src/transaction/deploy_account.rs index 17bf5445b..94fcae14f 100644 --- a/src/transaction/deploy_account.rs +++ b/src/transaction/deploy_account.rs @@ -250,7 +250,7 @@ impl DeployAccount { resources_manager, &[Some(constructor_call_info.clone()), validate_info.clone()], TransactionType::DeployAccount, - state.count_actual_storage_changes(Some(( + state.count_actual_state_changes(Some(( &block_context.starknet_os_config.fee_token_address, &self.contract_address, )))?, @@ -400,7 +400,7 @@ impl DeployAccount { Ok(call_info) } - pub(crate) fn create_for_simulation( + pub fn create_for_simulation( &self, skip_validate: bool, skip_execute: bool, @@ -421,6 +421,42 @@ impl DeployAccount { Transaction::DeployAccount(tx) } + + pub fn from_sn_api_transaction( + value: starknet_api::transaction::DeployAccountTransaction, + chain_id: Felt252, + ) -> Result { + let max_fee = value.max_fee.0; + let version = Felt252::from_bytes_be(value.version.0.bytes()); + let nonce = Felt252::from_bytes_be(value.nonce.0.bytes()); + let class_hash: [u8; 32] = value.class_hash.0.bytes().try_into().unwrap(); + let contract_address_salt = Felt252::from_bytes_be(value.contract_address_salt.0.bytes()); + + let signature = value + .signature + .0 + .iter() + .map(|f| Felt252::from_bytes_be(f.bytes())) + .collect(); + let constructor_calldata = value + .constructor_calldata + .0 + .as_ref() + .iter() + .map(|f| Felt252::from_bytes_be(f.bytes())) + .collect(); + + DeployAccount::new( + class_hash, + max_fee, + version, + nonce, + constructor_calldata, + signature, + contract_address_salt, + chain_id, + ) + } } // ---------------------------------- diff --git a/src/transaction/invoke_function.rs b/src/transaction/invoke_function.rs index 22e89c406..f8777ec2c 100644 --- a/src/transaction/invoke_function.rs +++ b/src/transaction/invoke_function.rs @@ -265,7 +265,7 @@ impl InvokeFunction { remaining_gas, )? }; - let changes = state.count_actual_storage_changes(Some(( + let changes = state.count_actual_state_changes(Some(( &block_context.starknet_os_config.fee_token_address, &self.contract_address, )))?; diff --git a/src/transaction/l1_handler.rs b/src/transaction/l1_handler.rs index 39c3ab9ea..451f95af5 100644 --- a/src/transaction/l1_handler.rs +++ b/src/transaction/l1_handler.rs @@ -137,7 +137,7 @@ impl L1Handler { )? }; - let changes = state.count_actual_storage_changes(None)?; + let changes = state.count_actual_state_changes(None)?; let actual_resources = calculate_tx_resources( resources_manager, &[call_info.clone()], diff --git a/src/utils.rs b/src/utils.rs index 066fbd643..3d87e3a8f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,6 @@ use crate::core::errors::hash_errors::HashError; use crate::services::api::contract_classes::deprecated_contract_class::EntryPointType; -use crate::state::state_api::State; +use crate::state::state_api::{State, StateChangesCount}; use crate::{ definitions::transaction_type::TransactionType, execution::{ @@ -182,14 +182,11 @@ pub fn calculate_tx_resources( resources_manager: ExecutionResourcesManager, call_info: &[Option], tx_type: TransactionType, - storage_changes: (usize, usize), + state_changes: StateChangesCount, l1_handler_payload_size: Option, n_reverted_steps: usize, ) -> Result, TransactionError> { - let (n_modified_contracts, n_storage_changes) = storage_changes; - let non_optional_calls: Vec = call_info.iter().flatten().cloned().collect(); - let n_deployments = non_optional_calls.iter().map(get_call_n_deployments).sum(); let mut l2_to_l1_messages = Vec::new(); @@ -197,13 +194,8 @@ pub fn calculate_tx_resources( l2_to_l1_messages.extend(call_info.get_sorted_l2_to_l1_messages()?) } - let l1_gas_usage = calculate_tx_gas_usage( - l2_to_l1_messages, - n_modified_contracts, - n_storage_changes, - l1_handler_payload_size, - n_deployments, - ); + let l1_gas_usage = + calculate_tx_gas_usage(l2_to_l1_messages, &state_changes, l1_handler_payload_size); let cairo_usage = resources_manager.cairo_usage.clone(); let tx_syscall_counter = resources_manager.syscall_counter; diff --git a/tests/internals.rs b/tests/internals.rs index 9ca873c44..9f0ff4a81 100644 --- a/tests/internals.rs +++ b/tests/internals.rs @@ -1260,7 +1260,7 @@ fn expected_fib_transaction_execution_info( } let resources = HashMap::from([ ("n_steps".to_string(), n_steps), - ("l1_gas_usage".to_string(), 4896), + ("l1_gas_usage".to_string(), 5508), ("pedersen_builtin".to_string(), 16), ("range_check_builtin".to_string(), 104), ]); @@ -1477,7 +1477,7 @@ fn test_invoke_with_declarev2_tx() { ]; let invoke_tx = invoke_tx(calldata, u128::MAX); - let expected_gas_consumed = 4908; + let expected_gas_consumed = 5551; let result = invoke_tx .execute(state, block_context, expected_gas_consumed) .unwrap();