From c739383b4164b22ce5968ce5576588e405a45a91 Mon Sep 17 00:00:00 2001 From: Ayelet Zilber Date: Wed, 24 Jan 2024 17:00:52 +0200 Subject: [PATCH] feat(fee): calculates messages size field --- crates/blockifier/src/execution/call_info.rs | 32 ++++++++++++-- crates/blockifier/src/fee/gas_usage.rs | 42 +++++-------------- crates/blockifier/src/fee/gas_usage_test.rs | 12 +++--- .../src/transaction/transaction_execution.rs | 3 +- .../src/transaction/transactions.rs | 5 +++ .../src/transaction_executor.rs | 21 ++++++++-- 6 files changed, 69 insertions(+), 46 deletions(-) diff --git a/crates/blockifier/src/execution/call_info.rs b/crates/blockifier/src/execution/call_info.rs index 104b6bc4d6..0697017684 100644 --- a/crates/blockifier/src/execution/call_info.rs +++ b/crates/blockifier/src/execution/call_info.rs @@ -7,6 +7,7 @@ use starknet_api::state::StorageKey; use starknet_api::transaction::{EventContent, L2ToL1Payload}; use crate::execution::entry_point::CallEntryPoint; +use crate::fee::gas_usage::get_message_segment_length; use crate::state::cached_state::StorageEntry; use crate::transaction::errors::TransactionExecutionError; use crate::transaction::objects::TransactionExecutionResult; @@ -28,6 +29,29 @@ pub struct OrderedEvent { pub event: EventContent, } +#[derive(Debug, Default, Eq, PartialEq)] +pub struct MessageL1CostInfo { + pub l2_to_l1_payload_lengths: Vec, + pub message_segment_length: usize, +} + +impl MessageL1CostInfo { + pub fn calculate<'a>( + call_infos: impl Iterator, + l1_handler_payload_size: Option, + ) -> TransactionExecutionResult { + let mut l2_to_l1_payload_lengths = Vec::new(); + for call_info in call_infos { + l2_to_l1_payload_lengths.extend(call_info.l2_to_l1_payload_lengths()?); + } + + let message_segment_length = + get_message_segment_length(&l2_to_l1_payload_lengths, l1_handler_payload_size); + + Ok(Self { l2_to_l1_payload_lengths, message_segment_length }) + } +} + #[cfg_attr(test, derive(Clone))] #[derive(Debug, Default, Eq, PartialEq)] pub struct MessageToL1 { @@ -99,9 +123,9 @@ impl CallInfo { /// Returns a list of Starknet L2ToL1Payload length collected during the execution, sorted /// by the order in which they were sent. - pub fn get_sorted_l2_to_l1_payloads_length(&self) -> TransactionExecutionResult> { + pub fn l2_to_l1_payload_lengths(&self) -> TransactionExecutionResult> { let n_messages = self.into_iter().map(|call| call.execution.l2_to_l1_messages.len()).sum(); - let mut starknet_l2_to_l1_payloads_length: Vec> = vec![None; n_messages]; + let mut starknet_l2_to_l1_payload_lengths: Vec> = vec![None; n_messages]; for call_info in self.into_iter() { for ordered_message_content in &call_info.execution.l2_to_l1_messages { @@ -113,12 +137,12 @@ impl CallInfo { max_order: n_messages, }); } - starknet_l2_to_l1_payloads_length[message_order] = + starknet_l2_to_l1_payload_lengths[message_order] = Some(ordered_message_content.message.payload.0.len()); } } - starknet_l2_to_l1_payloads_length.into_iter().enumerate().try_fold( + starknet_l2_to_l1_payload_lengths.into_iter().enumerate().try_fold( Vec::new(), |mut acc, (i, option)| match option { Some(value) => { diff --git a/crates/blockifier/src/fee/gas_usage.rs b/crates/blockifier/src/fee/gas_usage.rs index a250137566..22bc7de50c 100644 --- a/crates/blockifier/src/fee/gas_usage.rs +++ b/crates/blockifier/src/fee/gas_usage.rs @@ -5,7 +5,7 @@ use starknet_api::transaction::Fee; use super::fee_utils::{calculate_tx_l1_gas_usage, get_fee_by_l1_gas_usage}; use crate::abi::constants; use crate::block_context::BlockContext; -use crate::execution::call_info::CallInfo; +use crate::execution::call_info::{CallInfo, MessageL1CostInfo}; use crate::fee::eth_gas_constants; use crate::fee::os_resources::OS_RESOURCES; use crate::state::cached_state::StateChangesCount; @@ -18,23 +18,6 @@ use crate::transaction::objects::{ #[path = "gas_usage_test.rs"] pub mod test; -// TODO(Ayelet, 10/1/2024): Use to calculate message segment length in transaction_executer's -// execute -fn calculate_l2_to_l1_payloads_length_and_message_segment_length<'a>( - call_infos: impl Iterator, - l1_handler_payload_size: Option, -) -> TransactionExecutionResult<(Vec, usize)> { - let mut l2_to_l1_payloads_length = Vec::new(); - for call_info in call_infos { - l2_to_l1_payloads_length.extend(call_info.get_sorted_l2_to_l1_payloads_length()?); - } - - let message_segment_length = - get_message_segment_length(&l2_to_l1_payloads_length, l1_handler_payload_size); - - Ok((l2_to_l1_payloads_length, message_segment_length)) -} - /// Returns the blob-gas (data-gas) needed to publish the transaction's state diff in a blob. pub fn calculate_tx_blob_gas_usage(state_changes_count: StateChangesCount) -> usize { let onchain_data_segment_length = get_onchain_data_segment_length(state_changes_count); @@ -62,18 +45,15 @@ pub fn calculate_tx_gas_usage_messages<'a>( call_infos: impl Iterator, l1_handler_payload_size: Option, ) -> TransactionExecutionResult { - let (l2_to_l1_payloads_length, residual_message_segment_length) = - calculate_l2_to_l1_payloads_length_and_message_segment_length( - call_infos, - l1_handler_payload_size, - )?; + let MessageL1CostInfo { l2_to_l1_payload_lengths, message_segment_length } = + MessageL1CostInfo::calculate(call_infos, l1_handler_payload_size)?; - let n_l2_to_l1_messages = l2_to_l1_payloads_length.len(); + let n_l2_to_l1_messages = l2_to_l1_payload_lengths.len(); let n_l1_to_l2_messages = usize::from(l1_handler_payload_size.is_some()); let starknet_gas_usage = // Starknet's updateState gets the message segment as an argument. - residual_message_segment_length * eth_gas_constants::GAS_PER_MEMORY_WORD + message_segment_length * eth_gas_constants::GAS_PER_MEMORY_WORD // Starknet's updateState increases a (storage) counter for each L2-to-L1 message. + n_l2_to_l1_messages * eth_gas_constants::GAS_PER_ZERO_TO_NONZERO_STORAGE_SET // Starknet's updateState decreases a (storage) counter for each L1-to-L2 consumed message. @@ -81,10 +61,10 @@ pub fn calculate_tx_gas_usage_messages<'a>( // ignore it since refunded gas cannot be used for the current transaction execution). + n_l1_to_l2_messages * eth_gas_constants::GAS_PER_COUNTER_DECREASE + get_consumed_message_to_l2_emissions_cost(l1_handler_payload_size) - + get_log_message_to_l1_emissions_cost(&l2_to_l1_payloads_length); + + get_log_message_to_l1_emissions_cost(&l2_to_l1_payload_lengths); let sharp_gas_usage_without_data = - residual_message_segment_length * eth_gas_constants::SHARP_GAS_PER_MEMORY_WORD; + message_segment_length * eth_gas_constants::SHARP_GAS_PER_MEMORY_WORD; Ok(starknet_gas_usage + sharp_gas_usage_without_data) } @@ -139,12 +119,12 @@ pub fn get_onchain_data_cost(state_changes_count: StateChangesCount) -> usize { /// a transaction with the given parameters to a batch. Note that constant cells - such as the one /// that holds the segment size - are not counted. pub fn get_message_segment_length( - l2_to_l1_payloads_length: &[usize], + l2_to_l1_payload_lengths: &[usize], l1_handler_payload_size: Option, ) -> usize { // Add L2-to-L1 message segment length; for each message, the OS outputs the following: // to_address, from_address, payload_size, payload. - let mut message_segment_length = l2_to_l1_payloads_length + let mut message_segment_length = l2_to_l1_payload_lengths .iter() .map(|payload_length| constants::L2_TO_L1_MSG_HEADER_SIZE + payload_length) .sum(); @@ -176,8 +156,8 @@ pub fn get_consumed_message_to_l2_emissions_cost(l1_handler_payload_size: Option } /// Returns the cost of LogMessageToL1 event emissions caused by the given messages payload length. -pub fn get_log_message_to_l1_emissions_cost(l2_to_l1_payloads_length: &[usize]) -> usize { - l2_to_l1_payloads_length +pub fn get_log_message_to_l1_emissions_cost(l2_to_l1_payload_lengths: &[usize]) -> usize { + l2_to_l1_payload_lengths .iter() .map(|length| { get_event_emission_cost( diff --git a/crates/blockifier/src/fee/gas_usage_test.rs b/crates/blockifier/src/fee/gas_usage_test.rs index 0c5f74fcaa..a01cb245fc 100644 --- a/crates/blockifier/src/fee/gas_usage_test.rs +++ b/crates/blockifier/src/fee/gas_usage_test.rs @@ -131,11 +131,11 @@ fn test_calculate_tx_gas_usage_basic() { call_infos.push(call_info); } - // l2_to_l1_payloads_length is [0, 1, 2, 3] + // l2_to_l1_payload_lengths is [0, 1, 2, 3] let call_infos_iter = call_infos.iter(); - let l2_to_l1_payloads_length: Vec = call_infos_iter + let l2_to_l1_payload_lengths: Vec = call_infos_iter .clone() - .flat_map(|call_info| call_info.get_sorted_l2_to_l1_payloads_length().unwrap()) + .flat_map(|call_info| call_info.l2_to_l1_payload_lengths().unwrap()) .collect(); let l2_to_l1_state_changes_count = StateChangesCount { @@ -149,11 +149,11 @@ fn test_calculate_tx_gas_usage_basic() { .unwrap(); // Manual calculation. - let message_segment_length = get_message_segment_length(&l2_to_l1_payloads_length, None); - let n_l2_to_l1_messages = l2_to_l1_payloads_length.len(); + let message_segment_length = get_message_segment_length(&l2_to_l1_payload_lengths, None); + let n_l2_to_l1_messages = l2_to_l1_payload_lengths.len(); let manual_starknet_gas_usage = message_segment_length * eth_gas_constants::GAS_PER_MEMORY_WORD + n_l2_to_l1_messages * eth_gas_constants::GAS_PER_ZERO_TO_NONZERO_STORAGE_SET - + get_log_message_to_l1_emissions_cost(&l2_to_l1_payloads_length); + + get_log_message_to_l1_emissions_cost(&l2_to_l1_payload_lengths); let manual_sharp_gas_usage = message_segment_length * eth_gas_constants::SHARP_GAS_PER_MEMORY_WORD + get_onchain_data_cost(l2_to_l1_state_changes_count); diff --git a/crates/blockifier/src/transaction/transaction_execution.rs b/crates/blockifier/src/transaction/transaction_execution.rs index 28e8caf23c..5f87725803 100644 --- a/crates/blockifier/src/transaction/transaction_execution.rs +++ b/crates/blockifier/src/transaction/transaction_execution.rs @@ -103,8 +103,7 @@ impl ExecutableTransaction for L1HandlerTransaction { let mut remaining_gas = Transaction::initial_gas(); let execute_call_info = self.run_execute(state, &mut execution_resources, &mut context, &mut remaining_gas)?; - // The calldata includes the "from" field, which is not a part of the payload. - let l1_handler_payload_size = self.tx.calldata.0.len() - 1; + let l1_handler_payload_size = self.payload_size(); let ActualCost { actual_fee, actual_resources } = ActualCost::builder_for_l1_handler(block_context, tx_context, l1_handler_payload_size) diff --git a/crates/blockifier/src/transaction/transactions.rs b/crates/blockifier/src/transaction/transactions.rs index 485cbf48b2..0bd266b7a8 100644 --- a/crates/blockifier/src/transaction/transactions.rs +++ b/crates/blockifier/src/transaction/transactions.rs @@ -466,6 +466,11 @@ impl L1HandlerTransaction { max_fee: Fee::default(), }) } + + pub fn payload_size(&self) -> usize { + // The calldata includes the "from" field, which is not a part of the payload. + self.tx.calldata.0.len() - 1 + } } impl HasRelatedFeeType for L1HandlerTransaction { diff --git a/crates/native_blockifier/src/transaction_executor.rs b/crates/native_blockifier/src/transaction_executor.rs index 83b59b1049..c9e626b9c0 100644 --- a/crates/native_blockifier/src/transaction_executor.rs +++ b/crates/native_blockifier/src/transaction_executor.rs @@ -1,8 +1,9 @@ use std::collections::{HashMap, HashSet}; +use std::vec::IntoIter; use blockifier::block_context::BlockContext; use blockifier::block_execution::pre_process_block; -use blockifier::execution::call_info::CallInfo; +use blockifier::execution::call_info::{CallInfo, MessageL1CostInfo}; use blockifier::execution::entry_point::ExecutionResources; use blockifier::fee::actual_cost::ActualCost; use blockifier::state::cached_state::{ @@ -74,7 +75,12 @@ impl TransactionExecutor { charge_fee: bool, ) -> NativeBlockifierResult<(PyTransactionExecutionInfo, PyBouncerInfo)> { let tx: Transaction = py_tx(tx, raw_contract_class)?; - + let l1_handler_payload_size: usize = + if let Transaction::L1HandlerTransaction(l1_handler_tx) = &tx { + l1_handler_tx.payload_size() + } else { + 0 + }; let mut tx_executed_class_hashes = HashSet::::new(); let mut tx_visited_storage_entries = HashSet::::new(); let mut transactional_state = CachedState::create_transactional(&mut self.state); @@ -88,9 +94,18 @@ impl TransactionExecutor { // TODO(Elin, 01/06/2024): consider traversing the calls to collect data once. tx_executed_class_hashes.extend(tx_execution_info.get_executed_class_hashes()); tx_visited_storage_entries.extend(tx_execution_info.get_visited_storage_entries()); + let call_infos: IntoIter<&CallInfo> = + [&tx_execution_info.validate_call_info, &tx_execution_info.execute_call_info] + .iter() + .filter_map(|&call_info| call_info.as_ref()) + .collect::>() + .into_iter(); + let MessageL1CostInfo { l2_to_l1_payload_lengths: _, message_segment_length } = + MessageL1CostInfo::calculate(call_infos, Some(l1_handler_payload_size))?; // TODO(Elin, 01/06/2024): consider moving Bouncer logic to a function. let py_tx_execution_info = PyTransactionExecutionInfo::from(tx_execution_info); + let mut additional_os_resources = get_casm_hash_calculation_resources( &mut transactional_state, &self.executed_class_hashes, @@ -101,7 +116,7 @@ impl TransactionExecutor { &tx_visited_storage_entries, )?; let py_bouncer_info = PyBouncerInfo { - message_segment_length: 0, + message_segment_length, state_diff_size: 0, additional_os_resources: PyVmExecutionResources::from(additional_os_resources), };