From 753a4cbfb954e60552976372179e6aec9c32807e Mon Sep 17 00:00:00 2001 From: Barak Date: Wed, 7 Feb 2024 22:15:48 +0200 Subject: [PATCH] feat(fee): add events gas cost --- crates/blockifier/src/fee/gas_usage.rs | 52 +++++++++--- crates/blockifier/src/fee/gas_usage_test.rs | 81 ++++++++++++++++++- crates/blockifier/src/transaction/objects.rs | 4 +- .../src/transaction/transactions_test.rs | 9 ++- 4 files changed, 128 insertions(+), 18 deletions(-) diff --git a/crates/blockifier/src/fee/gas_usage.rs b/crates/blockifier/src/fee/gas_usage.rs index c8c16a03db..7bebb03775 100644 --- a/crates/blockifier/src/fee/gas_usage.rs +++ b/crates/blockifier/src/fee/gas_usage.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::abi::constants; use crate::context::{BlockContext, TransactionContext}; -use crate::execution::call_info::{CallInfo, MessageL1CostInfo}; +use crate::execution::call_info::{CallInfo, MessageL1CostInfo, OrderedEvent}; use crate::fee::eth_gas_constants; use crate::fee::fee_utils::calculate_tx_gas_vector; use crate::state::cached_state::StateChangesCount; @@ -19,14 +19,16 @@ use crate::versioned_constants::VersionedConstants; #[path = "gas_usage_test.rs"] pub mod test; -/// Returns an estimation of the L1 gas amount that will be used (by Starknet's state update and -/// the Verifier) following the addition of a transaction with the given parameters to a batch; -/// e.g., a message from L2 to L1 is followed by a storage write operation in Starknet L1 contract -/// which requires gas. +/// Calculates an estimated gas usage for a transaction on L1, based on the provided parameters. +/// This function estimates the amount of L1 gas required for processing state updates and +/// verification after adding a transaction to a batch. This includes scenarios such as L2 to L1 +/// messages, which result in storage write operations on the StarkNet L1 contract, consuming +/// gas. +// TODO(barak, 18/03/2024): Move to ActualCostBuilder impl block. #[allow(clippy::too_many_arguments)] pub fn calculate_tx_gas_usage_vector<'a>( versioned_constants: &VersionedConstants, - call_infos: impl Iterator, + call_infos: impl Iterator + Clone, state_changes_count: StateChangesCount, calldata_length: usize, signature_length: usize, @@ -34,19 +36,51 @@ pub fn calculate_tx_gas_usage_vector<'a>( class_info: Option, use_kzg_da: bool, ) -> TransactionExecutionResult { - Ok(calculate_messages_gas_vector(call_infos, l1_handler_payload_size)? + Ok(get_messages_gas_cost(call_infos.clone(), l1_handler_payload_size)? + get_da_gas_cost(state_changes_count, use_kzg_da) + get_calldata_and_signature_gas_cost( calldata_length, signature_length, versioned_constants, ) - + get_code_gas_cost(class_info, versioned_constants)) + + get_code_gas_cost(class_info, versioned_constants) + + get_tx_events_gas_cost(call_infos, versioned_constants)) +} + +pub fn get_tx_events_gas_cost<'a>( + call_infos: impl Iterator, + versioned_constants: &VersionedConstants, +) -> GasVector { + let l1_milligas: u128 = call_infos + .map(|call_info| get_events_milligas_cost(&call_info.execution.events, versioned_constants)) + .sum(); + GasVector { l1_gas: l1_milligas / 1000_u128, l1_data_gas: 0_u128 } +} + +pub fn get_events_milligas_cost( + events: &[OrderedEvent], + versioned_constants: &VersionedConstants, +) -> u128 { + let l2_resource_gas_costs = &versioned_constants.l2_resource_gas_costs; + let (event_key_factor, data_word_cost) = + (l2_resource_gas_costs.event_key_factor, l2_resource_gas_costs.milligas_per_data_felt); + let safe_u128_from_usize = + |x| u128_from_usize(x).expect("Could not convert starknet gas usage from usize to u128."); + events + .iter() + .map(|OrderedEvent { event, .. }| { + // TODO(barak: 18/03/2024): Once we start charging per byte change to num_bytes_keys and + // num_bytes_data. + let keys_size = safe_u128_from_usize(event.keys.len()); + let data_size = safe_u128_from_usize(event.data.0.len()); + event_key_factor * data_word_cost * keys_size + data_word_cost * data_size + }) + .sum() } /// Returns an estimation of the gas usage for processing L1<>L2 messages on L1. Accounts for both /// Starknet and SHARP contracts. -pub fn calculate_messages_gas_vector<'a>( +pub fn get_messages_gas_cost<'a>( call_infos: impl Iterator, l1_handler_payload_size: Option, ) -> TransactionExecutionResult { diff --git a/crates/blockifier/src/fee/gas_usage_test.rs b/crates/blockifier/src/fee/gas_usage_test.rs index 64efd1bfd0..6b8dff0975 100644 --- a/crates/blockifier/src/fee/gas_usage_test.rs +++ b/crates/blockifier/src/fee/gas_usage_test.rs @@ -1,20 +1,93 @@ use pretty_assertions::assert_eq; -use rstest::rstest; +use rstest::{fixture, rstest}; use starknet_api::hash::StarkFelt; use starknet_api::stark_felt; -use starknet_api::transaction::L2ToL1Payload; +use starknet_api::transaction::{EventContent, EventData, EventKey, L2ToL1Payload}; -use crate::execution::call_info::{CallExecution, CallInfo, MessageToL1, OrderedL2ToL1Message}; +use crate::execution::call_info::{ + CallExecution, CallInfo, MessageToL1, OrderedEvent, OrderedL2ToL1Message, +}; use crate::fee::eth_gas_constants; use crate::fee::gas_usage::{ calculate_tx_gas_usage_vector, get_consumed_message_to_l2_emissions_cost, get_da_gas_cost, - get_log_message_to_l1_emissions_cost, get_message_segment_length, + get_log_message_to_l1_emissions_cost, get_message_segment_length, get_tx_events_gas_cost, }; use crate::state::cached_state::StateChangesCount; use crate::transaction::objects::GasVector; use crate::utils::{u128_from_usize, usize_from_u128}; use crate::versioned_constants::VersionedConstants; +#[fixture] +fn versioned_constants() -> &'static VersionedConstants { + VersionedConstants::latest_constants() +} + +#[rstest] +fn test_get_event_gas_cost(versioned_constants: &VersionedConstants) { + let l2_resource_gas_costs = &versioned_constants.l2_resource_gas_costs; + let (event_key_factor, data_word_cost) = + (l2_resource_gas_costs.event_key_factor, l2_resource_gas_costs.milligas_per_data_felt); + + let call_infos = vec![CallInfo::default(), CallInfo::default(), CallInfo::default()]; + assert_eq!( + GasVector::default(), + get_tx_events_gas_cost(call_infos.iter(), versioned_constants) + ); + + let create_trivial_ordered_event_from_keys_and_data_sizes = + |keys_size: usize, data_size: usize| OrderedEvent { + order: 0, + event: EventContent { + keys: vec![EventKey(StarkFelt::ZERO); keys_size], + data: EventData(vec![StarkFelt::ZERO; data_size]), + }, + }; + let call_infos = vec![ + CallInfo { + execution: CallExecution { + events: vec![ + create_trivial_ordered_event_from_keys_and_data_sizes(1, 2), + create_trivial_ordered_event_from_keys_and_data_sizes(1, 2), + ], + ..Default::default() + }, + ..Default::default() + }, + CallInfo { + execution: CallExecution { + events: vec![ + create_trivial_ordered_event_from_keys_and_data_sizes(1, 0), + create_trivial_ordered_event_from_keys_and_data_sizes(0, 1), + ], + ..Default::default() + }, + ..Default::default() + }, + CallInfo { + execution: CallExecution { + events: vec![create_trivial_ordered_event_from_keys_and_data_sizes(0, 1)], + ..Default::default() + }, + inner_calls: vec![CallInfo { + execution: CallExecution { + events: vec![create_trivial_ordered_event_from_keys_and_data_sizes(1, 0)], + ..Default::default() + }, + ..Default::default() + }], + ..Default::default() + }, + ]; + let expected = GasVector { + // 4 keys and 6 data words overall. + l1_gas: (event_key_factor * data_word_cost * 4_u128 + data_word_cost * 6_u128) / 1000, + l1_data_gas: 0_u128, + }; + let gas_vector = get_tx_events_gas_cost(call_infos.iter(), versioned_constants); + assert_eq!(expected, gas_vector); + assert_ne!(GasVector::default(), gas_vector) +} + #[rstest] #[case::storage_write(StateChangesCount { n_storage_updates: 1, diff --git a/crates/blockifier/src/transaction/objects.rs b/crates/blockifier/src/transaction/objects.rs index 12f119a76d..6320b54b23 100644 --- a/crates/blockifier/src/transaction/objects.rs +++ b/crates/blockifier/src/transaction/objects.rs @@ -124,7 +124,9 @@ pub struct DeprecatedTransactionInfo { pub max_fee: Fee, } -#[derive(derive_more::Add, derive_more::Sum, Clone, Debug, Default, Eq, PartialEq, Serialize)] +#[derive( + derive_more::Add, derive_more::Sum, Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, +)] pub struct GasVector { pub l1_gas: u128, pub l1_data_gas: u128, diff --git a/crates/blockifier/src/transaction/transactions_test.rs b/crates/blockifier/src/transaction/transactions_test.rs index 3a45b47cf4..6fcf01acb0 100644 --- a/crates/blockifier/src/transaction/transactions_test.rs +++ b/crates/blockifier/src/transaction/transactions_test.rs @@ -285,6 +285,7 @@ fn validate_final_balances( } } +// TODO(Gilad, 30/03/2024): Make this an associated function of InvokeTxArgs. fn default_invoke_tx_args( account_contract_address: ContractAddress, test_contract_address: ContractAddress, @@ -434,7 +435,7 @@ fn test_invoke_tx( execute_call_info: expected_execute_call_info, fee_transfer_call_info: expected_fee_transfer_call_info, actual_fee: expected_actual_fee, - da_gas: da_gas.clone(), + da_gas, actual_resources: ResourcesMapping(HashMap::from([ ( abi_constants::BLOB_GAS_USAGE.to_string(), @@ -1135,14 +1136,14 @@ fn test_declare_tx( let da_gas = declare_expected_gas_vector(tx_version, use_kzg_da); let code_gas: GasVector = get_code_gas_cost(Some(class_info.clone()), versioned_constants); - let gas_usage = code_gas + da_gas.clone(); + let gas_usage = code_gas + da_gas; let expected_execution_info = TransactionExecutionInfo { validate_call_info: expected_validate_call_info, execute_call_info: None, fee_transfer_call_info: expected_fee_transfer_call_info, actual_fee: expected_actual_fee, - da_gas: da_gas.clone(), + da_gas, revert_error: None, actual_resources: ResourcesMapping(HashMap::from([ (abi_constants::L1_GAS_USAGE.to_string(), gas_usage.l1_gas.try_into().unwrap()), @@ -1279,7 +1280,7 @@ fn test_deploy_account_tx( execute_call_info: expected_execute_call_info, fee_transfer_call_info: expected_fee_transfer_call_info, actual_fee: expected_actual_fee, - da_gas: da_gas.clone(), + da_gas, revert_error: None, actual_resources: ResourcesMapping(HashMap::from([ (abi_constants::L1_GAS_USAGE.to_string(), usize_from_u128(da_gas.l1_gas).unwrap()),