From b7312bf4dd46f53c042442021a95e3be4ffdce84 Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Tue, 6 Feb 2024 20:23:24 +0200 Subject: [PATCH] feat(fee): add gas cost for code bytes --- crates/blockifier/src/fee/actual_cost.rs | 1 + crates/blockifier/src/fee/gas_usage.rs | 28 +++++++++- crates/blockifier/src/fee/gas_usage_test.rs | 54 +++++++++++++++---- .../src/transaction/transactions.rs | 2 +- .../src/transaction/transactions_test.rs | 11 ++-- 5 files changed, 82 insertions(+), 14 deletions(-) diff --git a/crates/blockifier/src/fee/actual_cost.rs b/crates/blockifier/src/fee/actual_cost.rs index ddf64d6f01..d98576e368 100644 --- a/crates/blockifier/src/fee/actual_cost.rs +++ b/crates/blockifier/src/fee/actual_cost.rs @@ -163,6 +163,7 @@ impl<'a> ActualCostBuilder<'a> { self.calldata_length, self.signature_length, self.l1_payload_size, + self.class_info, use_kzg_da, )?; diff --git a/crates/blockifier/src/fee/gas_usage.rs b/crates/blockifier/src/fee/gas_usage.rs index 8f8a4e0fac..6037f1aae2 100644 --- a/crates/blockifier/src/fee/gas_usage.rs +++ b/crates/blockifier/src/fee/gas_usage.rs @@ -11,6 +11,7 @@ use crate::transaction::objects::{ GasVector, HasRelatedFeeType, ResourcesMapping, TransactionExecutionResult, TransactionPreValidationResult, }; +use crate::transaction::transactions::ClassInfo; use crate::utils::{u128_from_usize, usize_from_u128}; use crate::versioned_constants::VersionedConstants; @@ -22,6 +23,7 @@ pub mod test; /// 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. +#[allow(clippy::too_many_arguments)] pub fn calculate_tx_gas_usage_vector<'a>( versioned_constants: &VersionedConstants, call_infos: impl Iterator, @@ -29,6 +31,7 @@ pub fn calculate_tx_gas_usage_vector<'a>( calldata_length: usize, signature_length: usize, l1_handler_payload_size: Option, + class_info: Option, use_kzg_da: bool, ) -> TransactionExecutionResult { Ok(calculate_messages_gas_vector(call_infos, l1_handler_payload_size)? @@ -37,7 +40,8 @@ pub fn calculate_tx_gas_usage_vector<'a>( calldata_length, signature_length, versioned_constants, - )) + ) + + get_code_gas_cost(class_info, versioned_constants)) } /// Returns an estimation of the gas usage for processing L1<>L2 messages on L1. Accounts for both @@ -98,6 +102,28 @@ pub fn get_calldata_and_signature_gas_cost( GasVector { l1_gas: l1_milligas / 1000, l1_data_gas: 0 } } +// Returns the gas cost of class information added to L2 via a Declare transaction. Each code felt +// costs a fixed and configurable amount of gas. The cost is 0 for non-Declare transactions. +pub fn get_code_gas_cost( + class_info: Option, + versioned_constants: &VersionedConstants, +) -> GasVector { + if let Some(class_info) = class_info { + let total_code_size = u128_from_usize( + (class_info.bytecode_length() + class_info.sierra_program_length) + // We assume each felt is a word. + * eth_gas_constants::WORD_WIDTH + + class_info.abi_length, + ) + .expect("Failed to convert total code size from usize to u128."); + let l1_milligas = + total_code_size * versioned_constants.l2_resource_gas_costs.milligas_per_code_byte; + GasVector { l1_gas: l1_milligas / 1000, l1_data_gas: 0 } + } else { + GasVector { l1_gas: 0, l1_data_gas: 0 } + } +} + /// Returns the number of felts added to the output data availability segment as a result of adding /// a transaction to a batch. Note that constant cells - such as the one that holds the number of /// modified contracts - are not counted. diff --git a/crates/blockifier/src/fee/gas_usage_test.rs b/crates/blockifier/src/fee/gas_usage_test.rs index f81d6657cc..03b6756cc6 100644 --- a/crates/blockifier/src/fee/gas_usage_test.rs +++ b/crates/blockifier/src/fee/gas_usage_test.rs @@ -11,7 +11,10 @@ use crate::fee::gas_usage::{ get_log_message_to_l1_emissions_cost, get_message_segment_length, }; use crate::state::cached_state::StateChangesCount; +use crate::test_utils::contracts::FeatureContract; +use crate::test_utils::CairoVersion; use crate::transaction::objects::GasVector; +use crate::transaction::test_utils::calculate_class_info_for_testing; use crate::utils::{u128_from_usize, usize_from_u128}; use crate::versioned_constants::VersionedConstants; @@ -62,13 +65,14 @@ fn test_get_da_gas_cost_basic(#[case] state_changes_count: StateChangesCount) { /// This test goes over six cases. In each case, we calculate the gas usage given the parameters. /// We then perform the same calculation manually, each time using only the relevant parameters. -/// The six cases are: +/// The seven cases are: /// 1. An empty transaction. -/// 2. A DeployAccount transaction. -/// 3. An L1 handler. -/// 4. A transaction with L2-to-L1 messages. -/// 5. A transaction that modifies the storage. -/// 6. A combination of cases 3. 4. and 5. +/// 2. A Declare transaction. +/// 3. A DeployAccount transaction. +/// 4. An L1 handler. +/// 5. A transaction with L2-to-L1 messages. +/// 6. A transaction that modifies the storage. +/// 7. A combination of cases 4. 5. and 6. // TODO(Aner, 29/01/24) Refactor with assert on GasVector objects. // TODO(Aner, 29/01/24) Refactor to replace match with if when formatting is nicer #[rstest] @@ -82,11 +86,39 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { 0, 0, None, + None, use_kzg_da, ) .unwrap(); assert_eq!(empty_tx_gas_usage_vector, GasVector { l1_gas: 0, l1_data_gas: 0 }); + // Declare. + for cairo_version in [CairoVersion::Cairo0, CairoVersion::Cairo1] { + let empty_contract = FeatureContract::Empty(cairo_version).get_class(); + let class_info = calculate_class_info_for_testing(cairo_version, empty_contract); + let code_milligas_cost = u128_from_usize( + (class_info.bytecode_length() + class_info.sierra_program_length) + * eth_gas_constants::WORD_WIDTH + + class_info.abi_length, + ) + .unwrap() + * versioned_constants.l2_resource_gas_costs.milligas_per_code_byte; + let manual_gas_vector = + GasVector { l1_gas: code_milligas_cost / 1000, ..Default::default() }; + let declare_gas_usage_vector = calculate_tx_gas_usage_vector( + &versioned_constants, + std::iter::empty(), + StateChangesCount::default(), + 0, + 0, + None, + Some(class_info), + use_kzg_da, + ) + .unwrap(); + assert_eq!(manual_gas_vector, declare_gas_usage_vector); + } + // DeployAccount. let deploy_account_state_changes_count = StateChangesCount { @@ -97,15 +129,14 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { }; // Manual calculation. - let manual_starknet_gas_usage = 0; let calldata_length = 0; let signature_length = 2; let calldata_and_signature_milligas_cost = u128_from_usize(calldata_length + signature_length) .unwrap() * versioned_constants.l2_resource_gas_costs.milligas_per_data_felt; + let manual_starknet_gas_usage = calldata_and_signature_milligas_cost / 1000; let manual_gas_vector = GasVector { l1_gas: manual_starknet_gas_usage, ..Default::default() } - + get_da_gas_cost(deploy_account_state_changes_count, use_kzg_da) - + GasVector { l1_gas: calldata_and_signature_milligas_cost / 1000, l1_data_gas: 0 }; + + get_da_gas_cost(deploy_account_state_changes_count, use_kzg_da); let deploy_account_gas_usage_vector = calculate_tx_gas_usage_vector( &versioned_constants, @@ -114,6 +145,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { calldata_length, signature_length, None, + None, use_kzg_da, ) .unwrap(); @@ -129,6 +161,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { l1_handler_payload_size, signature_length, Some(l1_handler_payload_size), + None, use_kzg_da, ) .unwrap(); @@ -198,6 +231,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { 0, 0, None, + None, use_kzg_da, ) .unwrap(); @@ -239,6 +273,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { 0, 0, None, + None, use_kzg_da, ) .unwrap(); @@ -263,6 +298,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { l1_handler_payload_size, signature_length, Some(l1_handler_payload_size), + None, use_kzg_da, ) .unwrap(); diff --git a/crates/blockifier/src/transaction/transactions.rs b/crates/blockifier/src/transaction/transactions.rs index 788ea2fd0b..c8403119b9 100644 --- a/crates/blockifier/src/transaction/transactions.rs +++ b/crates/blockifier/src/transaction/transactions.rs @@ -112,7 +112,7 @@ pub struct ClassInfo { } impl ClassInfo { - fn _bytecode_length(&self) -> usize { + pub fn bytecode_length(&self) -> usize { self.contract_class.bytecode_length() } } diff --git a/crates/blockifier/src/transaction/transactions_test.rs b/crates/blockifier/src/transaction/transactions_test.rs index 297e0237aa..3a45b47cf4 100644 --- a/crates/blockifier/src/transaction/transactions_test.rs +++ b/crates/blockifier/src/transaction/transactions_test.rs @@ -36,7 +36,7 @@ use crate::execution::execution_utils::{felt_to_stark_felt, stark_felt_to_felt}; use crate::fee::fee_utils::calculate_tx_fee; use crate::fee::gas_usage::{ calculate_tx_gas_usage_vector, estimate_minimal_gas_vector, - get_calldata_and_signature_gas_cost, get_da_gas_cost, + get_calldata_and_signature_gas_cost, get_code_gas_cost, get_da_gas_cost, }; use crate::state::cached_state::{CachedState, StateChangesCount}; use crate::state::errors::StateError; @@ -1083,6 +1083,7 @@ fn test_declare_tx( #[values(false, true)] use_kzg_da: bool, ) { let block_context = &BlockContext::create_for_account_testing_with_kzg(use_kzg_da); + let versioned_constants = &block_context.versioned_constants; let empty_contract = FeatureContract::Empty(empty_contract_version); let account = FeatureContract::AccountWithoutValidations(account_cairo_version); let chain_info = &block_context.chain_info; @@ -1133,6 +1134,8 @@ 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 expected_execution_info = TransactionExecutionInfo { validate_call_info: expected_validate_call_info, @@ -1142,8 +1145,8 @@ fn test_declare_tx( da_gas: da_gas.clone(), revert_error: None, actual_resources: ResourcesMapping(HashMap::from([ - (abi_constants::L1_GAS_USAGE.to_string(), da_gas.l1_gas.try_into().unwrap()), - (abi_constants::BLOB_GAS_USAGE.to_string(), da_gas.l1_data_gas.try_into().unwrap()), + (abi_constants::L1_GAS_USAGE.to_string(), gas_usage.l1_gas.try_into().unwrap()), + (abi_constants::BLOB_GAS_USAGE.to_string(), gas_usage.l1_data_gas.try_into().unwrap()), (HASH_BUILTIN_NAME.to_string(), 16), ( RANGE_CHECK_BUILTIN_NAME.to_string(), @@ -1510,6 +1513,7 @@ fn test_calculate_tx_gas_usage(#[values(false, true)] use_kzg_da: bool) { calldata_length, signature_length, None, + None, use_kzg_da, ) .unwrap(); @@ -1564,6 +1568,7 @@ fn test_calculate_tx_gas_usage(#[values(false, true)] use_kzg_da: bool) { calldata_length, signature_length, None, + None, use_kzg_da, ) .unwrap();