Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

feat(fee): add events gas cost #1458

Merged
merged 1 commit into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 43 additions & 9 deletions crates/blockifier/src/fee/gas_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,34 +19,68 @@ 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.
/// Returns the gas usage of a transaction, specifically:
/// * L1 gas, used by Starknet's state update and the Verifier, e.g., a message from L2 to L1 is
/// followed by a storage write operation on L1.
/// * L1 data gas, for publishing data availability.
/// * L2 resources cost, e.g., for storing transaction calldata.
// 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<Item = &'a CallInfo>,
call_infos: impl Iterator<Item = &'a CallInfo> + Clone,
state_changes_count: StateChangesCount,
calldata_length: usize,
signature_length: usize,
l1_handler_payload_size: Option<usize>,
class_info: Option<ClassInfo>,
use_kzg_da: bool,
) -> TransactionExecutionResult<GasVector> {
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<Item = &'a CallInfo>,
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<Item = &'a CallInfo>,
l1_handler_payload_size: Option<usize>,
) -> TransactionExecutionResult<GasVector> {
Expand Down
67 changes: 63 additions & 4 deletions crates/blockifier/src/fee/gas_usage_test.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
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::test_utils::contracts::FeatureContract;
Expand All @@ -18,6 +20,63 @@ use crate::transaction::test_utils::calculate_class_info_for_testing;
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_info_1 = &CallInfo::default();
let call_info_2 = &CallInfo::default();
let call_info_3 = &CallInfo::default();
let call_infos = call_info_1.into_iter().chain(call_info_2).chain(call_info_3);
assert_eq!(GasVector::default(), get_tx_events_gas_cost(call_infos, versioned_constants));

let create_event = |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_info_1 = &CallInfo {
execution: CallExecution {
events: vec![create_event(1, 2), create_event(1, 2)],
..Default::default()
},
..Default::default()
};
let call_info_2 = &CallInfo {
execution: CallExecution {
events: vec![create_event(1, 0), create_event(0, 1)],
..Default::default()
},
..Default::default()
};
let call_info_3 = &CallInfo {
execution: CallExecution { events: vec![create_event(0, 1)], ..Default::default() },
inner_calls: vec![CallInfo {
execution: CallExecution { events: vec![create_event(1, 0)], ..Default::default() },
..Default::default()
}],
..Default::default()
};
let call_infos = call_info_1.into_iter().chain(call_info_2).chain(call_info_3);
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, versioned_constants);
assert_eq!(expected, gas_vector);
assert_ne!(GasVector::default(), gas_vector)
}

#[rstest]
#[case::storage_write(StateChangesCount {
n_storage_updates: 1,
Expand Down
4 changes: 3 additions & 1 deletion crates/blockifier/src/transaction/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 5 additions & 4 deletions crates/blockifier/src/transaction/transactions_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,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,
Expand Down Expand Up @@ -442,7 +443,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(),
Expand Down Expand Up @@ -1143,14 +1144,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()),
Expand Down Expand Up @@ -1287,7 +1288,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()),
Expand Down
Loading