diff --git a/crates/blockifier/src/blockifier/stateful_validator.rs b/crates/blockifier/src/blockifier/stateful_validator.rs index 5dbba123a9..ffee732a25 100644 --- a/crates/blockifier/src/blockifier/stateful_validator.rs +++ b/crates/blockifier/src/blockifier/stateful_validator.rs @@ -136,6 +136,7 @@ impl StatefulValidator { &execution_resources, CallInfo::summarize_many(validate_call_info.iter()), 0, + 0, ); Ok((validate_call_info, tx_receipt)) diff --git a/crates/blockifier/src/execution.rs b/crates/blockifier/src/execution.rs index e3b5567d77..f2ddf27dce 100644 --- a/crates/blockifier/src/execution.rs +++ b/crates/blockifier/src/execution.rs @@ -1,3 +1,4 @@ +pub mod alias_keys; pub mod call_info; pub mod common_hints; pub mod contract_address; diff --git a/crates/blockifier/src/execution/alias_keys.rs b/crates/blockifier/src/execution/alias_keys.rs new file mode 100644 index 0000000000..ebf8ce2564 --- /dev/null +++ b/crates/blockifier/src/execution/alias_keys.rs @@ -0,0 +1,11 @@ +use crate::state::cached_state::{CachedState, StateChanges}; +use crate::state::state_api::{StateReader, StateResult}; + +/// Returns the number of aliases we charge the transaction for. +pub fn n_charged_invoke_aliases( + _state: &CachedState, + _state_changes: &StateChanges, +) -> StateResult { + // TODO: Implement this function + Ok(0) +} diff --git a/crates/blockifier/src/fee/gas_usage.rs b/crates/blockifier/src/fee/gas_usage.rs index 7e68193a20..37267b3fc3 100644 --- a/crates/blockifier/src/fee/gas_usage.rs +++ b/crates/blockifier/src/fee/gas_usage.rs @@ -31,6 +31,8 @@ pub fn get_onchain_data_segment_length(state_changes_count: &StateChangesCount) state_changes_count.n_class_hash_updates * constants::CLASS_UPDATE_SIZE; // For each modified storage cell: key, new value. onchain_data_segment_length += state_changes_count.n_storage_updates * 2; + // For each modified alias storage cell: key, new value. + onchain_data_segment_length += state_changes_count.n_allocated_leaves_for_fee * 2; // For each compiled class updated (through declare): class_hash, compiled_class_hash onchain_data_segment_length += state_changes_count.n_compiled_class_hash_updates * 2; @@ -170,12 +172,14 @@ pub fn estimate_minimal_gas_vector( n_class_hash_updates: 0, n_compiled_class_hash_updates: 0, n_modified_contracts: 1, + n_allocated_leaves_for_fee: 0, }, Transaction::Invoke(_) => StateChangesCount { n_storage_updates: 1, n_class_hash_updates: 0, n_compiled_class_hash_updates: 0, n_modified_contracts: 1, + n_allocated_leaves_for_fee: 0, }, // DeployAccount also updates the address -> class hash mapping. Transaction::DeployAccount(_) => StateChangesCount { @@ -183,6 +187,7 @@ pub fn estimate_minimal_gas_vector( n_class_hash_updates: 1, n_compiled_class_hash_updates: 0, n_modified_contracts: 1, + n_allocated_leaves_for_fee: 0, }, }; diff --git a/crates/blockifier/src/fee/gas_usage_test.rs b/crates/blockifier/src/fee/gas_usage_test.rs index 153062234f..56535d1a79 100644 --- a/crates/blockifier/src/fee/gas_usage_test.rs +++ b/crates/blockifier/src/fee/gas_usage_test.rs @@ -75,6 +75,7 @@ fn starknet_resources() -> StarknetResources { n_class_hash_updates: 11, n_compiled_class_hash_updates: 13, n_modified_contracts: 17, + n_allocated_leaves_for_fee: 0, }); StarknetResources::new(2_usize, 3_usize, 4_usize, state_resources, 6.into(), execution_summary) } @@ -166,6 +167,7 @@ fn test_get_event_gas_cost( n_class_hash_updates:0, n_compiled_class_hash_updates:0, n_modified_contracts:0, + n_allocated_leaves_for_fee: 0, }) ] #[case::deploy_account(StateChangesCount { @@ -173,6 +175,7 @@ fn test_get_event_gas_cost( n_class_hash_updates:1, n_compiled_class_hash_updates:0, n_modified_contracts:1, + n_allocated_leaves_for_fee: 1, }) ] #[case::declare(StateChangesCount { @@ -180,6 +183,7 @@ fn test_get_event_gas_cost( n_class_hash_updates:0, n_compiled_class_hash_updates:1, n_modified_contracts:0, + n_allocated_leaves_for_fee: 1, }) ] #[case::general_scenario(StateChangesCount { @@ -187,11 +191,13 @@ fn test_get_event_gas_cost( n_class_hash_updates:11, n_compiled_class_hash_updates:13, n_modified_contracts:17, + n_allocated_leaves_for_fee: 19, }) ] fn test_get_da_gas_cost_basic(#[case] state_changes_count: StateChangesCount) { // Manual calculation. let on_chain_data_segment_length = state_changes_count.n_storage_updates * 2 + + state_changes_count.n_allocated_leaves_for_fee * 2 + state_changes_count.n_class_hash_updates + state_changes_count.n_compiled_class_hash_updates * 2 + state_changes_count.n_modified_contracts * 2; diff --git a/crates/blockifier/src/fee/receipt.rs b/crates/blockifier/src/fee/receipt.rs index 05a403b080..a0525524ac 100644 --- a/crates/blockifier/src/fee/receipt.rs +++ b/crates/blockifier/src/fee/receipt.rs @@ -33,6 +33,7 @@ struct TransactionReceiptParameters<'a> { execution_resources: &'a ExecutionResources, tx_type: TransactionType, reverted_steps: usize, + n_allocated_leaves_for_fee: usize, } // TODO(Gilad): Use everywhere instead of passing the `actual_{fee,resources}` tuple, which often @@ -61,13 +62,19 @@ impl TransactionReceipt { execution_resources, tx_type, reverted_steps, + n_allocated_leaves_for_fee, } = tx_receipt_params; let starknet_resources = StarknetResources::new( calldata_length, signature_length, code_size, - StateResources::new(state_changes, sender_address, tx_context.fee_token_address()), + StateResources::new( + state_changes, + sender_address, + tx_context.fee_token_address(), + n_allocated_leaves_for_fee, + ), l1_handler_payload_size, execution_summary_without_fee_transfer, ); @@ -128,6 +135,7 @@ impl TransactionReceipt { execution_resources, tx_type: TransactionType::L1Handler, reverted_steps: 0, + n_allocated_leaves_for_fee: 0, }) } @@ -139,6 +147,7 @@ impl TransactionReceipt { execution_resources: &'a ExecutionResources, execution_summary_without_fee_transfer: ExecutionSummary, reverted_steps: usize, + n_allocated_leaves_for_fee: usize, ) -> Self { Self::from_params(TransactionReceiptParameters { tx_context, @@ -152,6 +161,7 @@ impl TransactionReceipt { execution_resources, tx_type: account_tx.tx_type(), reverted_steps, + n_allocated_leaves_for_fee, }) } } diff --git a/crates/blockifier/src/fee/receipt_test.rs b/crates/blockifier/src/fee/receipt_test.rs index 95c315918b..22b3867426 100644 --- a/crates/blockifier/src/fee/receipt_test.rs +++ b/crates/blockifier/src/fee/receipt_test.rs @@ -110,6 +110,7 @@ fn test_calculate_tx_gas_usage_basic<'a>( n_class_hash_updates: 1, n_compiled_class_hash_updates: 0, n_modified_contracts: 1, + n_allocated_leaves_for_fee: 0, }; // Manual calculation. @@ -224,6 +225,7 @@ fn test_calculate_tx_gas_usage_basic<'a>( n_class_hash_updates: 0, n_compiled_class_hash_updates: 0, n_modified_contracts: 1, + n_allocated_leaves_for_fee: 0, }; let l2_to_l1_starknet_resources = StarknetResources::new( 0, @@ -271,6 +273,7 @@ fn test_calculate_tx_gas_usage_basic<'a>( n_class_hash_updates: 0, n_compiled_class_hash_updates: 0, n_modified_contracts, + n_allocated_leaves_for_fee: 0, }; let storage_writes_starknet_resources = StarknetResources::new( 0, @@ -300,6 +303,7 @@ fn test_calculate_tx_gas_usage_basic<'a>( n_compiled_class_hash_updates: 0, n_modified_contracts: storage_writes_state_changes_count.n_modified_contracts + l2_to_l1_state_changes_count.n_modified_contracts, + n_allocated_leaves_for_fee: 0, }; let combined_cases_starknet_resources = StarknetResources::new( l1_handler_payload_size, @@ -375,6 +379,7 @@ fn test_calculate_tx_gas_usage( n_class_hash_updates: 0, n_modified_contracts, n_compiled_class_hash_updates: 0, + n_allocated_leaves_for_fee: 0, }; let starknet_resources = StarknetResources::new( calldata_length, @@ -429,6 +434,7 @@ fn test_calculate_tx_gas_usage( n_class_hash_updates: 0, n_modified_contracts, n_compiled_class_hash_updates: 0, + n_allocated_leaves_for_fee: 0, }; let execution_call_info = &tx_execution_info.execute_call_info.expect("Execution call info should exist."); diff --git a/crates/blockifier/src/fee/resources.rs b/crates/blockifier/src/fee/resources.rs index b92e50bf0e..23b0937b90 100644 --- a/crates/blockifier/src/fee/resources.rs +++ b/crates/blockifier/src/fee/resources.rs @@ -150,10 +150,14 @@ impl StateResources { state_changes: &StateChanges, sender_address: Option, fee_token_address: ContractAddress, + n_allocated_leaves_for_fee: usize, ) -> Self { Self { - state_changes_for_fee: state_changes - .count_for_fee_charge(sender_address, fee_token_address), + state_changes_for_fee: state_changes.count_for_fee_charge( + sender_address, + fee_token_address, + n_allocated_leaves_for_fee, + ), } } diff --git a/crates/blockifier/src/state/cached_state.rs b/crates/blockifier/src/state/cached_state.rs index f78f1b5307..ce6a842e87 100644 --- a/crates/blockifier/src/state/cached_state.rs +++ b/crates/blockifier/src/state/cached_state.rs @@ -645,6 +645,7 @@ impl StateChangesKeys { n_class_hash_updates: self.class_hash_keys.len(), n_compiled_class_hash_updates: self.compiled_class_hash_keys.len(), n_modified_contracts: self.modified_contracts.len(), + n_allocated_leaves_for_fee: 0, } } @@ -687,6 +688,7 @@ impl StateChanges { &self, sender_address: Option, fee_token_address: ContractAddress, + n_allocated_leaves_for_fee: usize, ) -> StateChangesCount { let mut modified_contracts = self.get_modified_contracts(); @@ -712,6 +714,7 @@ impl StateChanges { n_class_hash_updates: self.0.class_hashes.len(), n_compiled_class_hash_updates: self.0.compiled_class_hashes.len(), n_modified_contracts: modified_contracts.len(), + n_allocated_leaves_for_fee, } } @@ -740,4 +743,5 @@ pub struct StateChangesCount { pub n_class_hash_updates: usize, pub n_compiled_class_hash_updates: usize, pub n_modified_contracts: usize, + pub n_allocated_leaves_for_fee: usize, } diff --git a/crates/blockifier/src/state/cached_state_test.rs b/crates/blockifier/src/state/cached_state_test.rs index 21f856ec05..38289a0086 100644 --- a/crates/blockifier/src/state/cached_state_test.rs +++ b/crates/blockifier/src/state/cached_state_test.rs @@ -335,13 +335,19 @@ fn test_from_state_changes_for_fee_charge( let fee_token_address = contract_address!("0x17"); let state_changes = create_state_changes_for_test(&mut state, sender_address, fee_token_address); - let state_changes_count = state_changes.count_for_fee_charge(sender_address, fee_token_address); + let n_allocated_leaves_for_fee = 5; + let state_changes_count = state_changes.count_for_fee_charge( + sender_address, + fee_token_address, + n_allocated_leaves_for_fee, + ); let expected_state_changes_count = StateChangesCount { // 1 for storage update + 1 for sender balance update if sender is defined. n_storage_updates: 1 + usize::from(sender_address.is_some()), n_class_hash_updates: 1, n_compiled_class_hash_updates: 1, n_modified_contracts: 2, + n_allocated_leaves_for_fee, }; assert_eq!(state_changes_count, expected_state_changes_count); } @@ -516,7 +522,8 @@ fn test_state_changes_keys() { n_storage_updates: 2, n_class_hash_updates: 1, n_compiled_class_hash_updates: 2, - n_modified_contracts: 2 + n_modified_contracts: 2, + n_allocated_leaves_for_fee: 0, } ); diff --git a/crates/blockifier/src/transaction/account_transaction.rs b/crates/blockifier/src/transaction/account_transaction.rs index 18152788d2..547b5e63de 100644 --- a/crates/blockifier/src/transaction/account_transaction.rs +++ b/crates/blockifier/src/transaction/account_transaction.rs @@ -8,6 +8,7 @@ use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; use starknet_api::data_availability::DataAvailabilityMode; use starknet_api::executable_transaction::{ AccountTransaction as Transaction, + AccountTransaction as ExecutableAccountTransaction, DeclareTransaction, DeployAccountTransaction, InvokeTransaction, @@ -28,6 +29,7 @@ use starknet_types_core::felt::Felt; use crate::abi::abi_utils::selector_from_name; use crate::context::{BlockContext, TransactionContext}; +use crate::execution::alias_keys::n_charged_invoke_aliases; use crate::execution::call_info::CallInfo; use crate::execution::contract_class::RunnableContractClass; use crate::execution::entry_point::{CallEntryPoint, CallType, EntryPointExecutionContext}; @@ -41,8 +43,8 @@ use crate::fee::fee_utils::{ use crate::fee::gas_usage::estimate_minimal_gas_vector; use crate::fee::receipt::TransactionReceipt; use crate::retdata; -use crate::state::cached_state::{StateChanges, TransactionalState}; -use crate::state::state_api::{State, StateReader, UpdatableState}; +use crate::state::cached_state::{CachedState, StateChanges, TransactionalState}; +use crate::state::state_api::{State, StateReader, StateResult, UpdatableState}; use crate::transaction::constants; use crate::transaction::errors::{ TransactionExecutionError, @@ -595,13 +597,17 @@ impl AccountTransaction { self.run_execute(state, &mut resources, &mut execution_context, remaining_gas)?; } + let state_changes = state.get_actual_state_changes()?; + let n_allocated_leaves_for_fee = + self.n_charged_aliases(tx_context.clone(), state, &state_changes)?; let tx_receipt = TransactionReceipt::from_account_tx( self, &tx_context, - &state.get_actual_state_changes()?, + &state_changes, &resources, CallInfo::summarize_many(validate_call_info.iter().chain(execute_call_info.iter())), 0, + n_allocated_leaves_for_fee, ); let post_execution_report = @@ -669,24 +675,29 @@ impl AccountTransaction { &resources, CallInfo::summarize_many(validate_call_info.iter()), execution_steps_consumed, + 0, ); match execution_result { Ok(execute_call_info) => { + let execution_state_changes = execution_state.get_actual_state_changes()?; + let n_allocated_leaves_for_fee = self.n_charged_aliases( + tx_context.clone(), + &execution_state, + &execution_state_changes, + )?; // When execution succeeded, calculate the actual required fee before committing the // transactional state. If max_fee is insufficient, revert the `run_execute` part. let tx_receipt = TransactionReceipt::from_account_tx( self, &tx_context, - &StateChanges::merge(vec![ - validate_state_changes, - execution_state.get_actual_state_changes()?, - ]), + &StateChanges::merge(vec![validate_state_changes, execution_state_changes]), &execution_resources, CallInfo::summarize_many( validate_call_info.iter().chain(execute_call_info.iter()), ), 0, + n_allocated_leaves_for_fee, ); // Post-execution checks. let post_execution_report = PostExecutionReport::new( @@ -773,6 +784,27 @@ impl AccountTransaction { self.run_revertible(state, tx_context, remaining_gas, validate, charge_fee) } + + fn n_charged_aliases( + &self, + tx_context: Arc, + state: &CachedState, + state_changes: &StateChanges, + ) -> StateResult { + if tx_context.as_ref().block_context.versioned_constants.enable_stateful_compression { + Ok(match self.tx { + // Charge for introducing a new class hash. + ExecutableAccountTransaction::Declare(_) => 1, + // Charge for introducing a new contract address. + ExecutableAccountTransaction::DeployAccount(_) => 1, + ExecutableAccountTransaction::Invoke(_) => { + n_charged_invoke_aliases(state, state_changes)? + } + }) + } else { + Ok(0) + } + } } impl ExecutableTransaction for AccountTransaction { diff --git a/crates/blockifier/src/transaction/account_transactions_test.rs b/crates/blockifier/src/transaction/account_transactions_test.rs index 8548801fa8..91bd343c43 100644 --- a/crates/blockifier/src/transaction/account_transactions_test.rs +++ b/crates/blockifier/src/transaction/account_transactions_test.rs @@ -1375,8 +1375,12 @@ fn test_count_actual_storage_changes( expected_sequencer_fee_update, ]); - let state_changes_count_1 = - state_changes_1.clone().count_for_fee_charge(Some(account_address), fee_token_address); + let n_allocated_leaves_for_fee = 0; + let state_changes_count_1 = state_changes_1.clone().count_for_fee_charge( + Some(account_address), + fee_token_address, + n_allocated_leaves_for_fee, + ); let expected_state_changes_count_1 = StateChangesCount { // See expected storage updates. n_storage_updates: 3, @@ -1412,8 +1416,11 @@ fn test_count_actual_storage_changes( let expected_storage_updates_2 = HashMap::from([account_balance_storage_change, expected_sequencer_fee_update]); - let state_changes_count_2 = - state_changes_2.clone().count_for_fee_charge(Some(account_address), fee_token_address); + let state_changes_count_2 = state_changes_2.clone().count_for_fee_charge( + Some(account_address), + fee_token_address, + n_allocated_leaves_for_fee, + ); let expected_state_changes_count_2 = StateChangesCount { // See expected storage updates. n_storage_updates: 2, @@ -1457,9 +1464,11 @@ fn test_count_actual_storage_changes( expected_sequencer_fee_update, ]); - let state_changes_count_3 = state_changes_transfer - .clone() - .count_for_fee_charge(Some(account_address), fee_token_address); + let state_changes_count_3 = state_changes_transfer.clone().count_for_fee_charge( + Some(account_address), + fee_token_address, + n_allocated_leaves_for_fee, + ); let expected_state_changes_count_3 = StateChangesCount { // See expected storage updates. n_storage_updates: 3, diff --git a/crates/blockifier/src/transaction/transactions_test.rs b/crates/blockifier/src/transaction/transactions_test.rs index 3044e969ee..cc8c64118b 100644 --- a/crates/blockifier/src/transaction/transactions_test.rs +++ b/crates/blockifier/src/transaction/transactions_test.rs @@ -1451,17 +1451,23 @@ fn declare_validate_callinfo( /// Returns the expected used L1 gas and blob gas (according to use_kzg_da flag) due to execution of /// a declare transaction. -fn declare_expected_state_changes_count(version: TransactionVersion) -> StateChangesCount { +fn declare_expected_state_changes_count( + version: TransactionVersion, + enable_stateful_compression: bool, +) -> StateChangesCount { // TODO: Make TransactionVersion an enum and use match here. + let n_allocated_leaves_for_fee = if enable_stateful_compression { 1 } else { 0 }; if version == TransactionVersion::ZERO { StateChangesCount { n_storage_updates: 1, // Sender balance. + n_allocated_leaves_for_fee, ..StateChangesCount::default() } } else if version == TransactionVersion::ONE { StateChangesCount { n_storage_updates: 1, // Sender balance. n_modified_contracts: 1, // Nonce. + n_allocated_leaves_for_fee, ..StateChangesCount::default() } } else if version == TransactionVersion::TWO || version == TransactionVersion::THREE { @@ -1469,6 +1475,7 @@ fn declare_expected_state_changes_count(version: TransactionVersion) -> StateCha n_storage_updates: 1, // Sender balance. n_modified_contracts: 1, // Nonce. n_compiled_class_hash_updates: 1, // Also set compiled class hash. + n_allocated_leaves_for_fee, ..StateChangesCount::default() } } else { @@ -1487,8 +1494,10 @@ fn test_declare_tx( #[case] tx_version: TransactionVersion, #[case] empty_contract_version: CairoVersion, #[values(false, true)] use_kzg_da: bool, + #[values(false, true)] enable_stateful_compression: bool, ) { - let block_context = &BlockContext::create_for_account_testing_with_kzg(use_kzg_da); + let block_context = &mut BlockContext::create_for_account_testing_with_kzg(use_kzg_da); + block_context.versioned_constants.enable_stateful_compression = enable_stateful_compression; let versioned_constants = &block_context.versioned_constants; let empty_contract = FeatureContract::Empty(empty_contract_version); let account = FeatureContract::AccountWithoutValidations(account_cairo_version); @@ -1499,7 +1508,8 @@ fn test_declare_tx( let class_info = calculate_class_info_for_testing(empty_contract.get_class()); let sender_address = account.get_instance_address(0); let mut nonce_manager = NonceManager::default(); - let state_changes_for_fee = declare_expected_state_changes_count(tx_version); + let state_changes_for_fee = + declare_expected_state_changes_count(tx_version, enable_stateful_compression); let starknet_resources = StarknetResources::new( 0, 0, @@ -1686,9 +1696,11 @@ fn test_declare_tx_v0(default_l1_resource_bounds: ValidResourceBounds) { fn test_deploy_account_tx( #[values(CairoVersion::Cairo0, CairoVersion::Cairo1)] cairo_version: CairoVersion, #[values(false, true)] use_kzg_da: bool, + #[values(false, true)] enable_stateful_compression: bool, default_all_resource_bounds: ValidResourceBounds, ) { - let block_context = &BlockContext::create_for_account_testing_with_kzg(use_kzg_da); + let block_context = &mut BlockContext::create_for_account_testing_with_kzg(use_kzg_da); + block_context.versioned_constants.enable_stateful_compression = enable_stateful_compression; let versioned_constants = &block_context.versioned_constants; let chain_info = &block_context.chain_info; let mut nonce_manager = NonceManager::default(); @@ -1782,7 +1794,7 @@ fn test_deploy_account_tx( let starknet_resources = actual_execution_info.receipt.resources.starknet_resources.clone(); let state_changes_count = StateChangesCount { - n_storage_updates: 1, + n_storage_updates: if enable_stateful_compression { 2 } else { 1 }, n_modified_contracts: 1, n_class_hash_updates: 1, ..StateChangesCount::default()