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

Commit

Permalink
Add ActualCost and builder for it + one usage (#1001)
Browse files Browse the repository at this point in the history
New module is a wrapper around `calculate_actual_fee_and_resources`,
which was copied as is from `AccountTransaction`, without any changes.
This module takes care of all the various optional customizations for the calc,
which helps separate the complex setup from the actual calculation.

Added one usage for the new module, 3 more usages coming up in
subsequent commits.
After the last usage is added `calculate_actual_fee_and_resources` will
be removed from `AccountTransaction` in favor of this module.

Also bundled `actual_{fee,resources}` into dedicated struct, as they are
often passed around together.
Added TODO to actually pass the struct instead of unpacking it, but out
of scope for this commit.

Motivation: external validation of transaction fee/resources will soon
be added and we don't want to expose `AccountTransaction` just for that sake.
Also removing boilerplate from `run_[non_]_revertible`.
  • Loading branch information
giladchase authored Oct 18, 2023
1 parent e7523c6 commit 71b0e7b
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 15 deletions.
1 change: 1 addition & 0 deletions crates/blockifier/src/fee.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod actual_cost;
pub mod eth_gas_constants;
pub mod fee_utils;
pub mod gas_usage;
Expand Down
132 changes: 132 additions & 0 deletions crates/blockifier/src/fee/actual_cost.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use std::cmp::min;

use starknet_api::transaction::Fee;

use crate::abi::constants as abi_constants;
use crate::block_context::BlockContext;
use crate::execution::call_info::CallInfo;
use crate::execution::entry_point::ExecutionResources;
use crate::state::cached_state::{CachedState, StateChanges, StateChangesCount};
use crate::state::state_api::{StateReader, StateResult};
use crate::transaction::objects::{
AccountTransactionContext, HasRelatedFeeType, ResourcesMapping, TransactionExecutionResult,
};
use crate::transaction::transaction_types::TransactionType;
use crate::transaction::transaction_utils::{calculate_l1_gas_usage, calculate_tx_resources};

// TODO(Gilad): Use everywhere instead of passing the `actual_{fee,resources}` tuple, which often
// get passed around together.
pub struct ActualCost {
pub actual_fee: Fee,
pub actual_resources: ResourcesMapping,
}

#[derive(Debug, Clone)]
// Invariant: private fields initialized after `new` is called via dedicated methods.
pub struct ActualCostBuilder<'a> {
pub account_tx_context: AccountTransactionContext,
pub tx_type: TransactionType,
pub block_context: BlockContext,
validate_call_info: Option<&'a CallInfo>,
execute_call_info: Option<&'a CallInfo>,
state_changes: StateChanges,
}

impl<'a> ActualCostBuilder<'a> {
// Recommendation: use constructor from account transaction to build this.
pub fn new(
block_context: &BlockContext,
account_tx_context: AccountTransactionContext,
tx_type: TransactionType,
) -> Self {
Self {
block_context: block_context.clone(),
account_tx_context,
tx_type,
validate_call_info: None,
execute_call_info: None,
state_changes: StateChanges::default(),
}
}

// Call the `build_*` methods to construct the actual cost object, after feeding the builder
// using the setters below.
pub fn build_for_non_reverted_tx(
self,
execution_resources: &ExecutionResources,
) -> TransactionExecutionResult<ActualCost> {
let is_reverted = false;
let n_reverted_steps = 0;
self.calculate_actual_fee_and_resources(execution_resources, is_reverted, n_reverted_steps)
}

pub fn build_for_reverted_tx(
self,
execution_resources: &ExecutionResources,
n_reverted_steps: usize,
) -> TransactionExecutionResult<ActualCost> {
let is_reverted = true;
self.calculate_actual_fee_and_resources(execution_resources, is_reverted, n_reverted_steps)
}

// Setters.

pub fn with_validate_call_info(mut self, validate_call_info: &'a Option<CallInfo>) -> Self {
self.validate_call_info = validate_call_info.as_ref();
self
}

pub fn with_execute_call_info(mut self, execute_call_info: &'a Option<CallInfo>) -> Self {
self.execute_call_info = execute_call_info.as_ref();
self
}

pub fn try_add_state_changes(
mut self,
state: &mut CachedState<impl StateReader>,
) -> StateResult<Self> {
let fee_token_address =
self.block_context.fee_token_address(&self.account_tx_context.fee_type());

let new_state_changes = state.get_actual_state_changes_for_fee_charge(
fee_token_address,
Some(self.account_tx_context.sender_address()),
)?;

self.state_changes = StateChanges::merge(vec![self.state_changes, new_state_changes]);
Ok(self)
}

// Private methods.

// Construct the actual cost object using all fields that were set in the builder.
fn calculate_actual_fee_and_resources(
&self,
execution_resources: &ExecutionResources,
is_reverted: bool,
n_reverted_steps: usize,
) -> TransactionExecutionResult<ActualCost> {
let state_changes_count = StateChangesCount::from(&self.state_changes);
let non_optional_call_infos = vec![self.validate_call_info, self.execute_call_info]
.into_iter()
.flatten()
.collect::<Vec<&CallInfo>>();
let l1_gas_usage =
calculate_l1_gas_usage(&non_optional_call_infos, state_changes_count, None)?;
let mut actual_resources =
calculate_tx_resources(execution_resources, l1_gas_usage, self.tx_type)?;

// Add reverted steps to actual_resources' n_steps for correct fee charge.
*actual_resources.0.get_mut(&abi_constants::N_STEPS_RESOURCE.to_string()).unwrap() +=
n_reverted_steps;

let mut actual_fee =
self.account_tx_context.calculate_tx_fee(&actual_resources, &self.block_context)?;
if is_reverted || !self.account_tx_context.enforce_fee() {
// We cannot charge more than max_fee for reverted txs.
actual_fee = min(actual_fee, self.account_tx_context.max_fee());
}

Ok(ActualCost { actual_fee, actual_resources })
}
}
28 changes: 13 additions & 15 deletions crates/blockifier/src/transaction/account_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::execution::contract_class::ContractClass;
use crate::execution::entry_point::{
CallEntryPoint, CallType, EntryPointExecutionContext, ExecutionResources,
};
use crate::fee::actual_cost::{ActualCost, ActualCostBuilder};
use crate::fee::gas_usage::estimate_minimal_fee;
use crate::fee::os_resources::OS_RESOURCES;
use crate::retdata;
Expand Down Expand Up @@ -315,8 +316,6 @@ impl AccountTransaction {
let mut resources = ExecutionResources::default();
let validate_call_info: Option<CallInfo>;
let execute_call_info: Option<CallInfo>;
let account_tx_context = self.get_account_tx_context();
let fee_token_address = block_context.fee_token_address(&account_tx_context.fee_type());
if matches!(self, Self::DeployAccount(_)) {
// Handle `DeployAccount` transactions separately, due to different order of things.
execute_call_info =
Expand All @@ -339,19 +338,14 @@ impl AccountTransaction {
execute_call_info =
self.run_execute(state, &mut resources, &mut execution_context, remaining_gas)?;
}
let state_changes = state.get_actual_state_changes_for_fee_charge(
fee_token_address,
Some(account_tx_context.sender_address()),
)?;
let (actual_fee, actual_resources) = self.calculate_actual_fee_and_resources(
StateChangesCount::from(&state_changes),
&execute_call_info,
&validate_call_info,
&resources,
block_context,
false,
0,
)?;

let ActualCost { actual_fee, actual_resources } = self
.into_actual_cost_builder(block_context)
.with_validate_call_info(&validate_call_info)
.with_execute_call_info(&execute_call_info)
.try_add_state_changes(state)?
.build_for_non_reverted_tx(&resources)?;

Ok(ValidateExecuteCallInfo::new_accepted(
validate_call_info,
execute_call_info,
Expand Down Expand Up @@ -614,6 +608,10 @@ impl AccountTransaction {

Ok((actual_fee, actual_resources))
}

pub fn into_actual_cost_builder(&self, block_context: &BlockContext) -> ActualCostBuilder<'_> {
ActualCostBuilder::new(block_context, self.get_account_tx_context(), self.tx_type())
}
}

impl<S: StateReader> ExecutableTransaction<S> for AccountTransaction {
Expand Down

0 comments on commit 71b0e7b

Please sign in to comment.