From 3ebe6ecd721132c4c636a16fda1a418fd0ea8873 Mon Sep 17 00:00:00 2001 From: Avi Cohen Date: Tue, 6 Feb 2024 18:38:32 +0200 Subject: [PATCH] feat(fee): charge for signatures per byte --- .../resources/versioned_constants.json | 6 +++- crates/blockifier/src/fee/actual_cost.rs | 3 +- crates/blockifier/src/fee/gas_usage.rs | 29 +++++++++++-------- crates/blockifier/src/fee/gas_usage_test.rs | 22 ++++++++++++-- .../src/transaction/execution_flavors_test.rs | 2 +- .../src/transaction/transactions_test.rs | 18 ++++++++---- crates/blockifier/src/versioned_constants.rs | 12 +++++++- 7 files changed, 66 insertions(+), 26 deletions(-) diff --git a/crates/blockifier/resources/versioned_constants.json b/crates/blockifier/resources/versioned_constants.json index f4cf158570..4907bcca5b 100644 --- a/crates/blockifier/resources/versioned_constants.json +++ b/crates/blockifier/resources/versioned_constants.json @@ -3,7 +3,11 @@ "max_calldata_length": 4000, "max_contract_bytecode_size": 61440 }, - "milli_gas_per_calldata_byte": 40, + "l2_resource_gas_costs": { + "milligas_per_data_felt": 1280, + "event_key_factor": 2, + "milligas_per_code_byte": 875 + }, "invoke_tx_max_n_steps": 3000000, "max_recursion_depth": 50, "os_constants": { diff --git a/crates/blockifier/src/fee/actual_cost.rs b/crates/blockifier/src/fee/actual_cost.rs index 50aad1d5c1..493deb2319 100644 --- a/crates/blockifier/src/fee/actual_cost.rs +++ b/crates/blockifier/src/fee/actual_cost.rs @@ -55,8 +55,6 @@ pub struct ActualCostBuilder<'a> { l1_payload_size: Option, calldata_length: usize, n_reverted_steps: usize, - // TODO(Avi,10/02/2024): use this field and remove the clippy tag. - #[allow(dead_code)] signature_length: usize, class_info: Option, } @@ -163,6 +161,7 @@ impl<'a> ActualCostBuilder<'a> { non_optional_call_infos, state_changes_count, self.calldata_length, + self.signature_length, self.l1_payload_size, use_kzg_da, )?; diff --git a/crates/blockifier/src/fee/gas_usage.rs b/crates/blockifier/src/fee/gas_usage.rs index dec21a2309..8f8a4e0fac 100644 --- a/crates/blockifier/src/fee/gas_usage.rs +++ b/crates/blockifier/src/fee/gas_usage.rs @@ -27,12 +27,17 @@ pub fn calculate_tx_gas_usage_vector<'a>( call_infos: impl Iterator, state_changes_count: StateChangesCount, calldata_length: usize, + signature_length: usize, l1_handler_payload_size: Option, use_kzg_da: bool, ) -> TransactionExecutionResult { Ok(calculate_messages_gas_vector(call_infos, l1_handler_payload_size)? + get_da_gas_cost(state_changes_count, use_kzg_da) - + get_calldata_gas_cost(calldata_length, versioned_constants)) + + get_calldata_and_signature_gas_cost( + calldata_length, + signature_length, + versioned_constants, + )) } /// Returns an estimation of the gas usage for processing L1<>L2 messages on L1. Accounts for both @@ -75,22 +80,22 @@ pub fn calculate_messages_gas_vector<'a>( Ok(starknet_gas_usage + sharp_gas_usage) } -// Return the gas cost for transaction calldata. Each calldata felt costs a fixed and configurable -// amount of gas. This cost represents the cost of storing the calldata on L2. -pub fn get_calldata_gas_cost( +// Return the gas cost for transaction calldata and transaction signature. Each felt costs a fixed +// and configurable amount of gas. This cost represents the cost of storing the calldata and the +// signature on L2. +pub fn get_calldata_and_signature_gas_cost( calldata_length: usize, + signature_length: usize, versioned_constants: &VersionedConstants, ) -> GasVector { // TODO(Avi, 28/2/2024): Use rational numbers to calculate the gas cost once implemented. // TODO(Avi, 20/2/2024): Calculate the number of bytes instead of the number of felts. - let milli_gas_per_calldata_word = - versioned_constants.milli_gas_per_calldata_byte * eth_gas_constants::WORD_WIDTH; - let calldata_gas_cost = calldata_length * milli_gas_per_calldata_word / 1000; - GasVector { - l1_gas: u128_from_usize(calldata_gas_cost) - .expect("Could not convert calldata gas cost from usize to u128."), - l1_data_gas: 0, - } + let total_data_size = u128_from_usize(calldata_length + signature_length) + .expect("Could not convert total data size from usize to u128"); + let l1_milligas = + total_data_size * versioned_constants.l2_resource_gas_costs.milligas_per_data_felt; + + GasVector { l1_gas: l1_milligas / 1000, l1_data_gas: 0 } } /// Returns the number of felts added to the output data availability segment as a result of adding diff --git a/crates/blockifier/src/fee/gas_usage_test.rs b/crates/blockifier/src/fee/gas_usage_test.rs index 41df143201..26a71e7aa4 100644 --- a/crates/blockifier/src/fee/gas_usage_test.rs +++ b/crates/blockifier/src/fee/gas_usage_test.rs @@ -7,7 +7,7 @@ use starknet_api::transaction::L2ToL1Payload; use crate::execution::call_info::{CallExecution, CallInfo, MessageToL1, OrderedL2ToL1Message}; use crate::fee::eth_gas_constants; use crate::fee::gas_usage::{ - calculate_tx_gas_usage_vector, get_calldata_gas_cost, + calculate_tx_gas_usage_vector, get_calldata_and_signature_gas_cost, get_consumed_message_to_l2_emissions_cost, get_da_gas_cost, get_log_message_to_l1_emissions_cost, get_message_segment_length, }; @@ -81,6 +81,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { std::iter::empty(), StateChangesCount::default(), 0, + 0, None, use_kzg_da, ) @@ -99,15 +100,21 @@ 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 manual_gas_vector = GasVector { l1_gas: manual_starknet_gas_usage, ..Default::default() } + get_da_gas_cost(deploy_account_state_changes_count, use_kzg_da) - + get_calldata_gas_cost(calldata_length, &versioned_constants); + + get_calldata_and_signature_gas_cost( + calldata_length, + signature_length, + &versioned_constants, + ); let deploy_account_gas_usage_vector = calculate_tx_gas_usage_vector( &versioned_constants, std::iter::empty(), deploy_account_state_changes_count, calldata_length, + signature_length, None, use_kzg_da, ) @@ -122,6 +129,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { std::iter::empty(), StateChangesCount::default(), l1_handler_payload_size, + signature_length, Some(l1_handler_payload_size), use_kzg_da, ) @@ -136,7 +144,12 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { ) .unwrap() + usize_from_u128( - get_calldata_gas_cost(l1_handler_payload_size, &versioned_constants).l1_gas, + get_calldata_and_signature_gas_cost( + l1_handler_payload_size, + signature_length, + &versioned_constants, + ) + .l1_gas, ) .unwrap(); let manual_sharp_gas_usage = @@ -190,6 +203,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { call_infos_iter.clone(), l2_to_l1_state_changes_count, 0, + 0, None, use_kzg_da, ) @@ -230,6 +244,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { std::iter::empty(), storage_writes_state_changes_count, 0, + 0, None, use_kzg_da, ) @@ -253,6 +268,7 @@ fn test_calculate_tx_gas_usage_basic(#[values(false, true)] use_kzg_da: bool) { call_infos_iter, combined_state_changes_count, l1_handler_payload_size, + signature_length, Some(l1_handler_payload_size), use_kzg_da, ) diff --git a/crates/blockifier/src/transaction/execution_flavors_test.rs b/crates/blockifier/src/transaction/execution_flavors_test.rs index d1ce63594d..b37ae1e91b 100644 --- a/crates/blockifier/src/transaction/execution_flavors_test.rs +++ b/crates/blockifier/src/transaction/execution_flavors_test.rs @@ -329,7 +329,7 @@ fn test_simulate_validate_charge_fee_fail_validate( } = create_flavors_test_state(&block_context.chain_info, cairo_version); // Validation scenario: fallible validation. - let (actual_gas_used, actual_fee) = gas_and_fee(30786, validate, &fee_type); + let (actual_gas_used, actual_fee) = gas_and_fee(30789, validate, &fee_type); let result = account_invoke_tx(invoke_tx_args! { max_fee, resource_bounds: l1_resource_bounds(MAX_L1_GAS_AMOUNT, MAX_L1_GAS_PRICE), diff --git a/crates/blockifier/src/transaction/transactions_test.rs b/crates/blockifier/src/transaction/transactions_test.rs index 91929ea581..5b6ea0436a 100644 --- a/crates/blockifier/src/transaction/transactions_test.rs +++ b/crates/blockifier/src/transaction/transactions_test.rs @@ -35,8 +35,8 @@ use crate::execution::errors::EntryPointExecutionError; 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_gas_cost, - get_da_gas_cost, + calculate_tx_gas_usage_vector, estimate_minimal_gas_vector, + get_calldata_and_signature_gas_cost, get_da_gas_cost, }; use crate::state::cached_state::{CachedState, StateChangesCount}; use crate::state::errors::StateError; @@ -353,7 +353,8 @@ fn test_invoke_tx( // Extract invoke transaction fields for testing, as it is consumed when creating an account // transaction. let calldata = Calldata(Arc::clone(&invoke_tx.calldata().0)); - let calldata_length = &invoke_tx.calldata().0.len(); + let calldata_length = invoke_tx.calldata().0.len(); + let signature_length = invoke_tx.signature().0.len(); let sender_address = invoke_tx.sender_address(); let account_tx = AccountTransaction::Invoke(invoke_tx); @@ -430,7 +431,8 @@ fn test_invoke_tx( }; let da_gas = get_da_gas_cost(state_changes_count, use_kzg_da); - let calldata_gas = get_calldata_gas_cost(*calldata_length, versioned_constants); + let calldata_and_signature_gas = + get_calldata_and_signature_gas_cost(calldata_length, signature_length, versioned_constants); let expected_execution_info = TransactionExecutionInfo { validate_call_info: expected_validate_call_info, execute_call_info: expected_execute_call_info, @@ -444,9 +446,9 @@ fn test_invoke_tx( ), ( abi_constants::L1_GAS_USAGE.to_string(), - usize_from_u128(da_gas.l1_gas + calldata_gas.l1_gas).unwrap(), + usize_from_u128(da_gas.l1_gas + calldata_and_signature_gas.l1_gas).unwrap(), ), - (HASH_BUILTIN_NAME.to_string(), 14 + calldata_length), + (HASH_BUILTIN_NAME.to_string(), 14 + &calldata_length), (RANGE_CHECK_BUILTIN_NAME.to_string(), expected_arguments.range_check), (abi_constants::N_STEPS_RESOURCE.to_string(), expected_arguments.n_steps), ])), @@ -1492,6 +1494,7 @@ fn test_calculate_tx_gas_usage(#[values(false, true)] use_kzg_da: bool) { test_contract.get_instance_address(0), )); let calldata_length = account_tx.calldata_length(); + let signature_length = account_tx.signature_length(); let fee_token_address = chain_info.fee_token_address(&account_tx.fee_type()); let tx_execution_info = account_tx.execute(state, block_context, true, true).unwrap(); @@ -1509,6 +1512,7 @@ fn test_calculate_tx_gas_usage(#[values(false, true)] use_kzg_da: bool) { std::iter::empty(), state_changes_count, calldata_length, + signature_length, None, use_kzg_da, ) @@ -1544,6 +1548,7 @@ fn test_calculate_tx_gas_usage(#[values(false, true)] use_kzg_da: bool) { }); let calldata_length = account_tx.calldata_length(); + let signature_length = account_tx.signature_length(); let tx_execution_info = account_tx.execute(state, block_context, true, true).unwrap(); // For the balance update of the sender and the recipient. let n_storage_updates = 2; @@ -1561,6 +1566,7 @@ fn test_calculate_tx_gas_usage(#[values(false, true)] use_kzg_da: bool) { std::iter::empty(), state_changes_count, calldata_length, + signature_length, None, use_kzg_da, ) diff --git a/crates/blockifier/src/versioned_constants.rs b/crates/blockifier/src/versioned_constants.rs index 26996e173e..735d378d28 100644 --- a/crates/blockifier/src/versioned_constants.rs +++ b/crates/blockifier/src/versioned_constants.rs @@ -46,12 +46,13 @@ pub struct VersionedConstants { // TODO: Consider making this a struct, this will require change the way we access these // values. vm_resource_fee_cost: Arc>, - pub milli_gas_per_calldata_byte: usize, // Cairo OS constants. // Note: if loaded from a json file, there are some assumptions made on its structure. // See the struct's docstring for more details. os_constants: Arc, + + pub l2_resource_gas_costs: L2ResourceGasCosts, } impl VersionedConstants { @@ -139,6 +140,15 @@ impl TryFrom<&Path> for VersionedConstants { } } +#[derive(Clone, Debug, Default, Deserialize)] +pub struct L2ResourceGasCosts { + // TODO(barak, 18/03/2024): Once we start charging per byte change to milligas_per_data_byte, + // divide the value by 32 in the JSON file. + pub milligas_per_data_felt: u128, + pub event_key_factor: u128, + pub milligas_per_code_byte: u128, +} + #[derive(Clone, Debug, Default, Deserialize)] // Serde trick for adding validations via a customr deserializer, without forgoing the derive. // See: https://github.com/serde-rs/serde/issues/1220.