From e0a957e722d9da654c56170e8ad59c0b4e79fc77 Mon Sep 17 00:00:00 2001 From: Gilad Chase Date: Mon, 22 Jan 2024 08:57:51 +0200 Subject: [PATCH] refactor: make `AccountTransactionContext` hold BlockContext - Rename `AccountTransactionContext` into `TransactionInfo`: i want to use `Context` for something else, and `Account` is a misnomer, since L1Transactions also generate this instance. - Create a new `AccountTransactionContext`, which holds `BlockContext` and `TransactionInfo`: This mirrors `BlockContext`, which holds `BlockInfo` as well as higher level contexts. - Remove all unnecessary `get_account_tx` calls. - Replace all functions that take both `block_context` and `tx_info` with a single `TransactionContext`. - Make `EntryPointExecutionContext` hold an `Arc` instead of both a block_context and `tx_info`: previously every entry point cloned the blockContext and generated a new tx_info, now they all share the same (identical) one. --- crates/blockifier/src/context.rs | 23 +- .../deprecated_syscalls_test.rs | 28 +- .../deprecated_syscalls/hint_processor.rs | 25 +- .../src/execution/deprecated_syscalls/mod.rs | 12 +- .../blockifier/src/execution/entry_point.rs | 75 +++-- .../src/execution/execution_utils.rs | 10 +- .../src/execution/syscalls/hint_processor.rs | 57 ++-- .../blockifier/src/execution/syscalls/mod.rs | 3 +- .../src/execution/syscalls/syscalls_test.rs | 47 ++- crates/blockifier/src/fee/actual_cost.rs | 49 ++-- crates/blockifier/src/fee/fee_checks.rs | 75 ++--- crates/blockifier/src/fee/fee_test.rs | 6 +- crates/blockifier/src/fee/fee_utils.rs | 46 ++- crates/blockifier/src/fee/gas_usage.rs | 29 +- crates/blockifier/src/test_utils/prices.rs | 5 +- .../blockifier/src/test_utils/struct_impls.rs | 38 ++- .../src/transaction/account_transaction.rs | 249 ++++++---------- .../transaction/account_transactions_test.rs | 34 +-- crates/blockifier/src/transaction/objects.rs | 36 +-- .../src/transaction/post_execution_test.rs | 13 +- .../src/transaction/transaction_execution.rs | 22 +- .../src/transaction/transactions.rs | 267 +++++++++--------- .../src/transaction/transactions_test.rs | 76 ++--- crates/native_blockifier/src/py_validator.rs | 31 +- .../src/transaction_executor.rs | 12 +- 25 files changed, 583 insertions(+), 685 deletions(-) diff --git a/crates/blockifier/src/context.rs b/crates/blockifier/src/context.rs index 9ee31b0ca1..f01e537494 100644 --- a/crates/blockifier/src/context.rs +++ b/crates/blockifier/src/context.rs @@ -1,10 +1,16 @@ use starknet_api::core::{ChainId, ContractAddress}; use crate::block::BlockInfo; -use crate::transaction::objects::FeeType; +use crate::transaction::objects::{FeeType, TransactionInfo, TransactionInfoCreator}; use crate::versioned_constants::VersionedConstants; /// Create via [`crate::block::pre_process_block`] to ensure correctness. +#[derive(Clone, Debug)] +pub struct TransactionContext { + pub block_context: BlockContext, + pub tx_info: TransactionInfo, +} + #[derive(Clone, Debug)] pub struct BlockContext { pub(crate) block_info: BlockInfo, @@ -37,6 +43,18 @@ impl BlockContext { } } +impl BlockContext { + pub fn to_tx_context( + &self, + tx_info_creator: &impl TransactionInfoCreator, + ) -> TransactionContext { + TransactionContext { + block_context: self.clone(), + tx_info: tx_info_creator.create_tx_info(), + } + } +} + #[derive(Clone, Debug)] pub struct ChainInfo { pub chain_id: ChainId, @@ -44,6 +62,9 @@ pub struct ChainInfo { } impl ChainInfo { + // TODO(Gilad): since fee_type comes from TransactionInfo, we can move this method into + // TransactionContext, which has both the chain_info (through BlockContext) and the tx_info. + // That is, add to BlockContext with the signature `pub fn fee_token_address(&self)`. pub fn fee_token_address(&self, fee_type: &FeeType) -> ContractAddress { self.fee_token_addresses.get_by_fee_type(fee_type) } diff --git a/crates/blockifier/src/execution/deprecated_syscalls/deprecated_syscalls_test.rs b/crates/blockifier/src/execution/deprecated_syscalls/deprecated_syscalls_test.rs index 07fdc98306..9f1cb2b37f 100644 --- a/crates/blockifier/src/execution/deprecated_syscalls/deprecated_syscalls_test.rs +++ b/crates/blockifier/src/execution/deprecated_syscalls/deprecated_syscalls_test.rs @@ -37,7 +37,7 @@ use crate::test_utils::{ }; use crate::transaction::constants::QUERY_VERSION_BASE_BIT; use crate::transaction::objects::{ - AccountTransactionContext, CommonAccountFields, DeprecatedAccountTransactionContext, + CommonAccountFields, DeprecatedTransactionInfo, TransactionInfo, }; use crate::{check_entry_point_execution_error_for_custom_hint, retdata}; @@ -463,20 +463,20 @@ fn test_tx_info(#[case] only_query: bool) { calldata: expected_tx_info, ..trivial_external_entry_point() }; - let account_tx_context = - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext { - common_fields: CommonAccountFields { - transaction_hash: tx_hash, - version: TransactionVersion::ONE, - nonce, - sender_address, - only_query, - ..Default::default() - }, - max_fee, - }); + let tx_info = TransactionInfo::Deprecated(DeprecatedTransactionInfo { + common_fields: CommonAccountFields { + transaction_hash: tx_hash, + version: TransactionVersion::ONE, + nonce, + sender_address, + only_query, + ..Default::default() + }, + max_fee, + }); + let limit_steps_by_resources = true; let result = entry_point_call - .execute_directly_given_account_context(&mut state, account_tx_context, true) + .execute_directly_given_tx_info(&mut state, tx_info, limit_steps_by_resources) .unwrap(); assert!(!result.execution.failed) diff --git a/crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs b/crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs index c38c843557..1b679fd107 100644 --- a/crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs +++ b/crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs @@ -25,6 +25,8 @@ use starknet_api::StarknetApiError; use thiserror::Error; use crate::abi::constants; +use crate::block::BlockInfo; +use crate::context::TransactionContext; use crate::execution::call_info::{CallInfo, OrderedEvent, OrderedL2ToL1Message}; use crate::execution::common_hints::{ extended_builtin_hint_processor, ExecutionMode, HintExecutionResult, @@ -295,7 +297,7 @@ impl<'a> DeprecatedSyscallHintProcessor<'a> { &mut self, vm: &mut VirtualMachine, ) -> DeprecatedSyscallResult { - let signature = &self.context.account_tx_context.signature().0; + let signature = &self.context.tx_context.tx_info.signature().0; let signature = signature.iter().map(|&x| MaybeRelocatable::from(stark_felt_to_felt(x))).collect(); let signature_segment_start_ptr = self.read_only_segments.allocate(vm, &signature)?; @@ -308,18 +310,17 @@ impl<'a> DeprecatedSyscallHintProcessor<'a> { vm: &mut VirtualMachine, ) -> DeprecatedSyscallResult { let tx_signature_start_ptr = self.get_or_allocate_tx_signature_segment(vm)?; - let account_tx_context = &self.context.account_tx_context; - let tx_signature_length = account_tx_context.signature().0.len(); + let TransactionContext { block_context, tx_info } = self.context.tx_context.as_ref(); + let tx_signature_length = tx_info.signature().0.len(); let tx_info: Vec = vec![ - stark_felt_to_felt(account_tx_context.signed_version().0).into(), - stark_felt_to_felt(*account_tx_context.sender_address().0.key()).into(), - max_fee_for_execution_info(account_tx_context).into(), + stark_felt_to_felt(tx_info.signed_version().0).into(), + stark_felt_to_felt(*tx_info.sender_address().0.key()).into(), + max_fee_for_execution_info(tx_info).into(), tx_signature_length.into(), tx_signature_start_ptr.into(), - stark_felt_to_felt(account_tx_context.transaction_hash().0).into(), - Felt252::from_bytes_be(self.context.block_context.chain_info.chain_id.0.as_bytes()) - .into(), - stark_felt_to_felt(account_tx_context.nonce().0).into(), + stark_felt_to_felt(tx_info.transaction_hash().0).into(), + Felt252::from_bytes_be(block_context.chain_info.chain_id.0.as_bytes()).into(), + stark_felt_to_felt(tx_info.nonce().0).into(), ]; let tx_info_start_ptr = self.read_only_segments.allocate(vm, &tx_info)?; @@ -347,6 +348,10 @@ impl<'a> DeprecatedSyscallHintProcessor<'a> { Ok(StorageWriteResponse {}) } + + pub fn get_block_info(&self) -> &BlockInfo { + &self.context.tx_context.block_context.block_info + } } impl ResourceTracker for DeprecatedSyscallHintProcessor<'_> { diff --git a/crates/blockifier/src/execution/deprecated_syscalls/mod.rs b/crates/blockifier/src/execution/deprecated_syscalls/mod.rs index 6e6bf6e252..cf166edf50 100644 --- a/crates/blockifier/src/execution/deprecated_syscalls/mod.rs +++ b/crates/blockifier/src/execution/deprecated_syscalls/mod.rs @@ -397,9 +397,7 @@ pub fn get_block_number( syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, ) -> DeprecatedSyscallResult { // TODO(Yoni, 1/5/2024): disable for validate. - Ok(GetBlockNumberResponse { - block_number: syscall_handler.context.block_context.block_info.block_number, - }) + Ok(GetBlockNumberResponse { block_number: syscall_handler.get_block_info().block_number }) } // GetBlockTimestamp syscall. @@ -425,7 +423,7 @@ pub fn get_block_timestamp( ) -> DeprecatedSyscallResult { // TODO(Yoni, 1/5/2024): disable for validate. Ok(GetBlockTimestampResponse { - block_timestamp: syscall_handler.context.block_context.block_info.block_timestamp, + block_timestamp: syscall_handler.get_block_info().block_timestamp, }) } @@ -477,9 +475,7 @@ pub fn get_sequencer_address( syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, ) -> DeprecatedSyscallResult { syscall_handler.verify_not_in_validate_mode("get_sequencer_address")?; - Ok(GetSequencerAddressResponse { - address: syscall_handler.context.block_context.block_info.sequencer_address, - }) + Ok(GetSequencerAddressResponse { address: syscall_handler.get_block_info().sequencer_address }) } // GetTxInfo syscall. @@ -518,7 +514,7 @@ pub fn get_tx_signature( syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, ) -> DeprecatedSyscallResult { let start_ptr = syscall_handler.get_or_allocate_tx_signature_segment(vm)?; - let length = syscall_handler.context.account_tx_context.signature().0.len(); + let length = syscall_handler.context.tx_context.tx_info.signature().0.len(); Ok(GetTxSignatureResponse { segment: ReadOnlySegment { start_ptr, length } }) } diff --git a/crates/blockifier/src/execution/entry_point.rs b/crates/blockifier/src/execution/entry_point.rs index c7723640dc..aea12f175d 100644 --- a/crates/blockifier/src/execution/entry_point.rs +++ b/crates/blockifier/src/execution/entry_point.rs @@ -13,7 +13,7 @@ use starknet_api::transaction::{Calldata, TransactionVersion}; use crate::abi::abi_utils::selector_from_name; use crate::abi::constants; -use crate::context::BlockContext; +use crate::context::{BlockContext, TransactionContext}; use crate::execution::call_info::CallInfo; use crate::execution::common_hints::ExecutionMode; use crate::execution::deprecated_syscalls::hint_processor::SyscallCounter; @@ -21,9 +21,7 @@ use crate::execution::errors::{EntryPointExecutionError, PreExecutionError}; use crate::execution::execution_utils::execute_entry_point_call; use crate::fee::os_resources::OS_RESOURCES; use crate::state::state_api::State; -use crate::transaction::objects::{ - AccountTransactionContext, HasRelatedFeeType, TransactionExecutionResult, -}; +use crate::transaction::objects::{HasRelatedFeeType, TransactionExecutionResult, TransactionInfo}; use crate::transaction::transaction_types::TransactionType; #[cfg(test)] @@ -68,9 +66,10 @@ impl CallEntryPoint { resources: &mut ExecutionResources, context: &mut EntryPointExecutionContext, ) -> EntryPointExecutionResult { + let tx_context = &context.tx_context; let mut decrement_when_dropped = RecursionDepthGuard::new( context.current_recursion_depth.clone(), - context.block_context.versioned_constants.max_recursion_depth, + tx_context.block_context.versioned_constants.max_recursion_depth, ); decrement_when_dropped.try_increment_and_check_depth()?; @@ -86,7 +85,7 @@ impl CallEntryPoint { None => storage_class_hash, // If not given, take the storage contract class hash. }; // Hack to prevent version 0 attack on argent accounts. - if context.account_tx_context.version() == TransactionVersion::ZERO + if tx_context.tx_info.version() == TransactionVersion::ZERO && class_hash == ClassHash( StarkFelt::try_from(FAULTY_CLASS_HASH).expect("A class hash must be a felt."), @@ -139,8 +138,9 @@ pub struct ExecutionResources { #[derive(Debug)] pub struct EntryPointExecutionContext { - pub block_context: BlockContext, - pub account_tx_context: AccountTransactionContext, + // We use `Arc` to avoid the clone of this potentially large object, as inner calls + // are created during execution. + pub tx_context: Arc, // VM execution limits. pub vm_run_resources: RunResources, /// Used for tracking events order during the current execution. @@ -159,61 +159,46 @@ pub struct EntryPointExecutionContext { impl EntryPointExecutionContext { pub fn new( - block_context: &BlockContext, - account_tx_context: &AccountTransactionContext, + tx_context: Arc, mode: ExecutionMode, limit_steps_by_resources: bool, ) -> TransactionExecutionResult { - let max_steps = - Self::max_steps(block_context, account_tx_context, &mode, limit_steps_by_resources)?; + let max_steps = Self::max_steps(&tx_context, &mode, limit_steps_by_resources)?; Ok(Self { vm_run_resources: RunResources::new(max_steps), n_emitted_events: 0, n_sent_messages_to_l1: 0, error_stack: vec![], - account_tx_context: account_tx_context.clone(), + tx_context: tx_context.clone(), current_recursion_depth: Default::default(), - block_context: block_context.clone(), execution_mode: mode, }) } pub fn new_validate( - block_context: &BlockContext, - account_tx_context: &AccountTransactionContext, + tx_context: Arc, limit_steps_by_resources: bool, ) -> TransactionExecutionResult { - Self::new( - block_context, - account_tx_context, - ExecutionMode::Validate, - limit_steps_by_resources, - ) + Self::new(tx_context, ExecutionMode::Validate, limit_steps_by_resources) } pub fn new_invoke( - block_context: &BlockContext, - account_tx_context: &AccountTransactionContext, + tx_context: Arc, limit_steps_by_resources: bool, ) -> TransactionExecutionResult { - Self::new( - block_context, - account_tx_context, - ExecutionMode::Execute, - limit_steps_by_resources, - ) + Self::new(tx_context, ExecutionMode::Execute, limit_steps_by_resources) } /// Returns the maximum number of cairo steps allowed, given the max fee, gas price and the /// execution mode. /// If fee is disabled, returns the global maximum. fn max_steps( - block_context: &BlockContext, - account_tx_context: &AccountTransactionContext, + tx_context: &TransactionContext, mode: &ExecutionMode, limit_steps_by_resources: bool, ) -> TransactionExecutionResult { - let versioned_constants = &block_context.versioned_constants; + let TransactionContext { block_context, tx_info } = tx_context; + let BlockContext { block_info, versioned_constants, .. } = block_context; let block_upper_bound = match mode { // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion // works. @@ -233,7 +218,7 @@ impl EntryPointExecutionContext { ), }; - if !limit_steps_by_resources || !account_tx_context.enforce_fee()? { + if !limit_steps_by_resources || !tx_info.enforce_fee()? { return Ok(block_upper_bound); } @@ -246,16 +231,20 @@ impl EntryPointExecutionContext { // New transactions derive the step limit by the L1 gas resource bounds; deprecated // transactions derive this value from the `max_fee`. - let tx_gas_upper_bound = match account_tx_context { - AccountTransactionContext::Deprecated(context) => { - (context.max_fee.0 - / block_context - .block_info - .gas_prices - .get_gas_price_by_fee_type(&account_tx_context.fee_type())) - as usize + let tx_gas_upper_bound = match tx_info { + TransactionInfo::Deprecated(context) => { + let max_cairo_steps = context.max_fee.0 + / block_info.gas_prices.get_gas_price_by_fee_type(&tx_info.fee_type()); + // FIXME: This is saturating in the python bootstrapping test. Fix the value so + // that it'll fit in a usize and remove the `as`. + usize::try_from(max_cairo_steps).unwrap_or_else(|_| { + log::error!( + "Performed a saturating cast from u128 to usize: {max_cairo_steps:?}" + ); + usize::MAX + }) } - AccountTransactionContext::Current(context) => { + TransactionInfo::Current(context) => { // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the // convertion works. context diff --git a/crates/blockifier/src/execution/execution_utils.rs b/crates/blockifier/src/execution/execution_utils.rs index 44858904ef..d5498f6a57 100644 --- a/crates/blockifier/src/execution/execution_utils.rs +++ b/crates/blockifier/src/execution/execution_utils.rs @@ -28,7 +28,7 @@ use crate::execution::errors::PostExecutionError; use crate::execution::{deprecated_entry_point_execution, entry_point_execution}; use crate::state::errors::StateError; use crate::state::state_api::State; -use crate::transaction::objects::AccountTransactionContext; +use crate::transaction::objects::TransactionInfo; pub type Args = Vec; @@ -269,10 +269,10 @@ pub fn write_maybe_relocatable>( Ok(()) } -pub fn max_fee_for_execution_info(account_tx_context: &AccountTransactionContext) -> Felt252 { - match account_tx_context { - AccountTransactionContext::Current(_) => 0, - AccountTransactionContext::Deprecated(context) => context.max_fee.0, +pub fn max_fee_for_execution_info(tx_info: &TransactionInfo) -> Felt252 { + match tx_info { + TransactionInfo::Current(_) => 0, + TransactionInfo::Deprecated(tx_info) => tx_info.max_fee.0, } .into() } diff --git a/crates/blockifier/src/execution/syscalls/hint_processor.rs b/crates/blockifier/src/execution/syscalls/hint_processor.rs index 2e9d5772c1..09b333244d 100644 --- a/crates/blockifier/src/execution/syscalls/hint_processor.rs +++ b/crates/blockifier/src/execution/syscalls/hint_processor.rs @@ -49,7 +49,7 @@ use crate::execution::syscalls::{ }; use crate::state::errors::StateError; use crate::state::state_api::State; -use crate::transaction::objects::{AccountTransactionContext, CurrentAccountTransactionContext}; +use crate::transaction::objects::{CurrentTransactionInfo, TransactionInfo}; use crate::transaction::transaction_utils::update_remaining_gas; pub type SyscallCounter = HashMap; @@ -319,11 +319,11 @@ impl<'a> SyscallHintProcessor<'a> { fn allocate_tx_resource_bounds_segment( &mut self, vm: &mut VirtualMachine, - context: &CurrentAccountTransactionContext, + tx_info: &CurrentTransactionInfo, ) -> SyscallResult<(Relocatable, Relocatable)> { let l1_gas = StarkFelt::try_from(L1_GAS).map_err(SyscallExecutionError::from)?; let l2_gas = StarkFelt::try_from(L2_GAS).map_err(SyscallExecutionError::from)?; - let flat_resource_bounds = context + let flat_resource_bounds: Vec = tx_info .resource_bounds .0 .iter() @@ -341,7 +341,7 @@ impl<'a> SyscallHintProcessor<'a> { }) .collect(); - self.allocate_data_segment(vm, flat_resource_bounds) + self.allocate_data_segment(vm, &flat_resource_bounds) } fn execute_syscall( @@ -434,7 +434,7 @@ impl<'a> SyscallHintProcessor<'a> { &mut self, vm: &mut VirtualMachine, ) -> SyscallResult { - let block_info = &self.context.block_context.block_info; + let block_info = &self.context.tx_context.block_context.block_info; let block_timestamp = StarkFelt::from(block_info.block_timestamp.0); let block_number = StarkFelt::from(block_info.block_number.0); let block_data: Vec = if self.is_validate_mode() { @@ -448,7 +448,7 @@ impl<'a> SyscallHintProcessor<'a> { } else { vec![block_number, block_timestamp, *block_info.sequencer_address.0.key()] }; - let (block_info_segment_start_ptr, _) = self.allocate_data_segment(vm, block_data)?; + let (block_info_segment_start_ptr, _) = self.allocate_data_segment(vm, &block_data)?; Ok(block_info_segment_start_ptr) } @@ -456,7 +456,7 @@ impl<'a> SyscallHintProcessor<'a> { fn allocate_data_segment( &mut self, vm: &mut VirtualMachine, - data: Vec, + data: &[StarkFelt], ) -> SyscallResult<(Relocatable, Relocatable)> { let data = data.iter().map(|&x| MaybeRelocatable::from(stark_felt_to_felt(x))).collect(); let data_segment_start_ptr = self.read_only_segments.allocate(vm, &data)?; @@ -465,34 +465,36 @@ impl<'a> SyscallHintProcessor<'a> { } fn allocate_tx_info_segment(&mut self, vm: &mut VirtualMachine) -> SyscallResult { + let tx_info = &self.context.tx_context.clone().tx_info; let (tx_signature_start_ptr, tx_signature_end_ptr) = - &self.allocate_data_segment(vm, self.context.account_tx_context.signature().0)?; - let account_tx_context = self.context.account_tx_context.clone(); + &self.allocate_data_segment(vm, &tx_info.signature().0)?; - let mut tx_info: Vec = vec![ - stark_felt_to_felt(self.context.account_tx_context.signed_version().0).into(), - stark_felt_to_felt(*self.context.account_tx_context.sender_address().0.key()).into(), - max_fee_for_execution_info(&account_tx_context).into(), + let mut tx_data: Vec = vec![ + stark_felt_to_felt(tx_info.signed_version().0).into(), + stark_felt_to_felt(*tx_info.sender_address().0.key()).into(), + max_fee_for_execution_info(tx_info).into(), tx_signature_start_ptr.into(), tx_signature_end_ptr.into(), - stark_felt_to_felt((self.context.account_tx_context).transaction_hash().0).into(), - Felt252::from_bytes_be(self.context.block_context.chain_info.chain_id.0.as_bytes()) - .into(), - stark_felt_to_felt((self.context.account_tx_context).nonce().0).into(), + stark_felt_to_felt((tx_info).transaction_hash().0).into(), + Felt252::from_bytes_be( + self.context.tx_context.block_context.chain_info.chain_id.0.as_bytes(), + ) + .into(), + stark_felt_to_felt((tx_info).nonce().0).into(), ]; - match account_tx_context { - AccountTransactionContext::Current(context) => { + match tx_info { + TransactionInfo::Current(context) => { let (tx_resource_bounds_start_ptr, tx_resource_bounds_end_ptr) = - &self.allocate_tx_resource_bounds_segment(vm, &context)?; + &self.allocate_tx_resource_bounds_segment(vm, context)?; let (tx_paymaster_data_start_ptr, tx_paymaster_data_end_ptr) = - &self.allocate_data_segment(vm, context.paymaster_data.0)?; + &self.allocate_data_segment(vm, &context.paymaster_data.0)?; let (tx_account_deployment_data_start_ptr, tx_account_deployment_data_end_ptr) = - &self.allocate_data_segment(vm, context.account_deployment_data.0)?; + &self.allocate_data_segment(vm, &context.account_deployment_data.0)?; - tx_info.extend_from_slice(&[ + tx_data.extend_from_slice(&[ tx_resource_bounds_start_ptr.into(), tx_resource_bounds_end_ptr.into(), Felt252::from(context.tip.0).into(), @@ -504,9 +506,9 @@ impl<'a> SyscallHintProcessor<'a> { tx_account_deployment_data_end_ptr.into(), ]); } - AccountTransactionContext::Deprecated(_) => { + TransactionInfo::Deprecated(_) => { let zero_felt: MaybeRelocatable = Felt252::zero().into(); - tx_info.extend_from_slice(&[ + tx_data.extend_from_slice(&[ zero_felt.clone(), // Empty segment of resource bounds (start ptr). zero_felt.clone(), // Empty segment of resource bounds (end ptr). zero_felt.clone(), // Tip. @@ -520,7 +522,7 @@ impl<'a> SyscallHintProcessor<'a> { } }; - let tx_info_start_ptr = self.read_only_segments.allocate(vm, &tx_info)?; + let tx_info_start_ptr = self.read_only_segments.allocate(vm, &tx_data)?; Ok(tx_info_start_ptr) } @@ -665,8 +667,7 @@ pub fn create_retdata_segment( syscall_handler: &mut SyscallHintProcessor<'_>, raw_retdata: &[StarkFelt], ) -> SyscallResult { - let (retdata_segment_start_ptr, _) = - syscall_handler.allocate_data_segment(vm, raw_retdata.to_vec())?; + let (retdata_segment_start_ptr, _) = syscall_handler.allocate_data_segment(vm, raw_retdata)?; Ok(ReadOnlySegment { start_ptr: retdata_segment_start_ptr, length: raw_retdata.len() }) } diff --git a/crates/blockifier/src/execution/syscalls/mod.rs b/crates/blockifier/src/execution/syscalls/mod.rs index 41817a0bc9..ff5a98546d 100644 --- a/crates/blockifier/src/execution/syscalls/mod.rs +++ b/crates/blockifier/src/execution/syscalls/mod.rs @@ -352,7 +352,8 @@ pub fn get_block_hash( } let requested_block_number = request.block_number.0; - let current_block_number = syscall_handler.context.block_context.block_info.block_number.0; + let current_block_number = + syscall_handler.context.tx_context.block_context.block_info.block_number.0; if current_block_number < constants::STORED_BLOCK_HASH_BUFFER || requested_block_number > current_block_number - constants::STORED_BLOCK_HASH_BUFFER diff --git a/crates/blockifier/src/execution/syscalls/syscalls_test.rs b/crates/blockifier/src/execution/syscalls/syscalls_test.rs index 4ebb052dde..a84f049cd7 100644 --- a/crates/blockifier/src/execution/syscalls/syscalls_test.rs +++ b/crates/blockifier/src/execution/syscalls/syscalls_test.rs @@ -47,8 +47,7 @@ use crate::test_utils::{ }; use crate::transaction::constants::QUERY_VERSION_BASE_BIT; use crate::transaction::objects::{ - AccountTransactionContext, CommonAccountFields, CurrentAccountTransactionContext, - DeprecatedAccountTransactionContext, + CommonAccountFields, CurrentTransactionInfo, DeprecatedTransactionInfo, TransactionInfo, }; use crate::{check_entry_point_execution_error_for_custom_hint, retdata}; @@ -320,7 +319,7 @@ fn test_get_execution_info( let expected_tx_info: Vec; let mut expected_resource_bounds: Vec = vec![]; - let account_tx_context: AccountTransactionContext; + let tx_info: TransactionInfo; if version == TransactionVersion::ONE { expected_tx_info = vec![ version.0, // Transaction version. @@ -336,18 +335,17 @@ fn test_get_execution_info( stark_felt!(0_u16), // Length of resource bounds array. ]; } - account_tx_context = - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext { - common_fields: CommonAccountFields { - transaction_hash: tx_hash, - version: TransactionVersion::ONE, - nonce, - sender_address, - only_query, - ..Default::default() - }, - max_fee, - }); + tx_info = TransactionInfo::Deprecated(DeprecatedTransactionInfo { + common_fields: CommonAccountFields { + transaction_hash: tx_hash, + version: TransactionVersion::ONE, + nonce, + sender_address, + only_query, + ..Default::default() + }, + max_fee, + }); } else { let max_amount = Fee(13); let max_price_per_unit = Fee(61); @@ -371,7 +369,7 @@ fn test_get_execution_info( StarkFelt::ZERO, // Max price per unit. ]; } - account_tx_context = AccountTransactionContext::Current(CurrentAccountTransactionContext { + tx_info = TransactionInfo::Current(CurrentTransactionInfo { common_fields: CommonAccountFields { transaction_hash: tx_hash, version: TransactionVersion::THREE, @@ -427,17 +425,12 @@ fn test_get_execution_info( }; let result = match execution_mode { - ExecutionMode::Validate => entry_point_call - .execute_directly_given_account_context_in_validate_mode( - state, - account_tx_context, - false, - ), - ExecutionMode::Execute => entry_point_call.execute_directly_given_account_context( - state, - account_tx_context, - false, - ), + ExecutionMode::Validate => { + entry_point_call.execute_directly_given_tx_info_in_validate_mode(state, tx_info, false) + } + ExecutionMode::Execute => { + entry_point_call.execute_directly_given_tx_info(state, tx_info, false) + } }; assert!(!result.unwrap().execution.failed); diff --git a/crates/blockifier/src/fee/actual_cost.rs b/crates/blockifier/src/fee/actual_cost.rs index ba5aa00f8e..eee4548c71 100644 --- a/crates/blockifier/src/fee/actual_cost.rs +++ b/crates/blockifier/src/fee/actual_cost.rs @@ -1,15 +1,17 @@ +use std::sync::Arc; + use starknet_api::core::ContractAddress; use starknet_api::transaction::Fee; use crate::abi::constants as abi_constants; -use crate::context::BlockContext; +use crate::context::TransactionContext; use crate::execution::call_info::CallInfo; use crate::execution::entry_point::ExecutionResources; use crate::fee::gas_usage::calculate_tx_gas_usage_vector; use crate::state::cached_state::{CachedState, StateChanges, StateChangesCount}; use crate::state::state_api::{StateReader, StateResult}; use crate::transaction::objects::{ - AccountTransactionContext, HasRelatedFeeType, ResourcesMapping, TransactionExecutionResult, + HasRelatedFeeType, ResourcesMapping, TransactionExecutionResult, }; use crate::transaction::transaction_types::TransactionType; use crate::transaction::transaction_utils::calculate_tx_resources; @@ -22,28 +24,21 @@ pub struct ActualCost { } impl ActualCost { - pub fn builder_for_l1_handler( - block_context: &BlockContext, - tx_context: AccountTransactionContext, + pub fn builder_for_l1_handler<'a>( + tx_context: Arc, l1_handler_payload_size: usize, - ) -> ActualCostBuilder<'_> { - ActualCostBuilder::new( - block_context, - tx_context, - TransactionType::L1Handler, - l1_handler_payload_size, - ) - .without_sender_address() - .with_l1_payload_size(l1_handler_payload_size) + ) -> ActualCostBuilder<'a> { + ActualCostBuilder::new(tx_context, TransactionType::L1Handler, l1_handler_payload_size) + .without_sender_address() + .with_l1_payload_size(l1_handler_payload_size) } } #[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_context: Arc, pub tx_type: TransactionType, - pub block_context: BlockContext, validate_call_info: Option<&'a CallInfo>, execute_call_info: Option<&'a CallInfo>, state_changes: StateChanges, @@ -56,15 +51,13 @@ pub struct ActualCostBuilder<'a> { impl<'a> ActualCostBuilder<'a> { // Recommendation: use constructor from account transaction, or from actual cost, to build this. pub fn new( - block_context: &BlockContext, - account_tx_context: AccountTransactionContext, + tx_context: Arc, tx_type: TransactionType, calldata_length: usize, ) -> Self { Self { - block_context: block_context.clone(), - sender_address: Some(account_tx_context.sender_address()), - account_tx_context, + sender_address: Some(tx_context.tx_info.sender_address()), + tx_context, tx_type, validate_call_info: None, execute_call_info: None, @@ -105,8 +98,11 @@ impl<'a> ActualCostBuilder<'a> { mut self, state: &mut CachedState, ) -> StateResult { - let fee_token_address = - self.block_context.chain_info.fee_token_address(&self.account_tx_context.fee_type()); + let fee_token_address = self + .tx_context + .block_context + .chain_info + .fee_token_address(&self.tx_context.tx_info.fee_type()); let new_state_changes = state .get_actual_state_changes_for_fee_charge(fee_token_address, self.sender_address)?; @@ -141,7 +137,7 @@ impl<'a> ActualCostBuilder<'a> { non_optional_call_infos, state_changes_count, self.l1_payload_size, - self.block_context.block_info.use_kzg_da, + self.tx_context.block_context.block_info.use_kzg_da, )?; let mut actual_resources = calculate_tx_resources( @@ -155,11 +151,12 @@ impl<'a> ActualCostBuilder<'a> { *actual_resources.0.get_mut(&abi_constants::N_STEPS_RESOURCE.to_string()).unwrap() += n_reverted_steps; - let actual_fee = if self.account_tx_context.enforce_fee()? + let tx_info = &self.tx_context.tx_info; + let actual_fee = if tx_info.enforce_fee()? // L1 handler transactions are not charged an L2 fee but it is compared to the L1 fee. || self.tx_type == TransactionType::L1Handler { - self.account_tx_context.calculate_tx_fee(&actual_resources, &self.block_context)? + tx_info.calculate_tx_fee(&actual_resources, &self.tx_context.block_context)? } else { Fee(0) }; diff --git a/crates/blockifier/src/fee/fee_checks.rs b/crates/blockifier/src/fee/fee_checks.rs index d3636f8120..6883cb60d1 100644 --- a/crates/blockifier/src/fee/fee_checks.rs +++ b/crates/blockifier/src/fee/fee_checks.rs @@ -2,8 +2,7 @@ use starknet_api::hash::StarkFelt; use starknet_api::transaction::Fee; use thiserror::Error; -use crate::block::BlockInfo; -use crate::context::{BlockContext, ChainInfo}; +use crate::context::TransactionContext; use crate::fee::actual_cost::ActualCost; use crate::fee::fee_utils::{ calculate_tx_gas_vector, get_balance_and_if_covers_fee, get_fee_by_gas_vector, @@ -12,7 +11,7 @@ use crate::fee::gas_usage::compute_discounted_gas_from_gas_vector; use crate::state::state_api::StateReader; use crate::transaction::errors::TransactionExecutionError; use crate::transaction::objects::{ - AccountTransactionContext, FeeType, GasVector, TransactionExecutionResult, + FeeType, GasVector, TransactionExecutionResult, TransactionInfo, }; #[derive(Clone, Copy, Debug, Error)] @@ -60,8 +59,7 @@ impl FeeCheckReport { pub fn from_fee_check_error( actual_fee: Fee, error: FeeCheckError, - block_info: &BlockInfo, - account_tx_context: &AccountTransactionContext, + tx_context: &TransactionContext, ) -> TransactionExecutionResult { let recommended_fee = match error { // If the error is insufficient balance, the recommended fee is the actual fee. @@ -73,16 +71,16 @@ impl FeeCheckReport { // If the transaction passed pre-validation checks (i.e. balance initially covered the // resource bounds), the sender should be able to pay this fee. FeeCheckError::MaxFeeExceeded { .. } | FeeCheckError::MaxL1GasAmountExceeded { .. } => { - match account_tx_context { - AccountTransactionContext::Current(context) => get_fee_by_gas_vector( - block_info, + match &tx_context.tx_info { + TransactionInfo::Current(info) => get_fee_by_gas_vector( + &tx_context.block_context.block_info, GasVector { - l1_gas: context.l1_resource_bounds()?.max_amount.into(), + l1_gas: info.l1_resource_bounds()?.max_amount.into(), blob_gas: 0, }, &FeeType::Strk, ), - AccountTransactionContext::Deprecated(context) => context.max_fee, + TransactionInfo::Deprecated(context) => context.max_fee, } } }; @@ -92,16 +90,16 @@ impl FeeCheckReport { /// If the actual cost exceeds the resource bounds on the transaction, returns a fee check /// error. fn check_actual_cost_within_bounds( - block_context: &BlockContext, - account_tx_context: &AccountTransactionContext, + tx_context: &TransactionContext, actual_cost: &ActualCost, ) -> TransactionExecutionResult<()> { let ActualCost { actual_fee, actual_resources } = actual_cost; + let TransactionContext { tx_info, block_context } = tx_context; // First, compare the actual resources used against the upper bound(s) defined by the // sender. - match account_tx_context { - AccountTransactionContext::Current(context) => { + match tx_info { + TransactionInfo::Current(context) => { // Check L1 gas limit. let max_l1_gas = context.l1_resource_bounds()?.max_amount.into(); @@ -110,8 +108,7 @@ impl FeeCheckReport { // limit exceeded). let total_discounted_gas_used = compute_discounted_gas_from_gas_vector( &calculate_tx_gas_vector(actual_resources, &block_context.versioned_constants)?, - account_tx_context, - &block_context.block_info, + tx_context, ); if total_discounted_gas_used > max_l1_gas { @@ -121,7 +118,7 @@ impl FeeCheckReport { })?; } } - AccountTransactionContext::Deprecated(context) => { + TransactionInfo::Deprecated(context) => { // Check max fee. let max_fee = context.max_fee; if actual_fee > &max_fee { @@ -139,18 +136,17 @@ impl FeeCheckReport { /// If the actual cost exceeds the sender's balance, returns a fee check error. fn check_can_pay_fee( state: &mut S, - chain_info: &ChainInfo, - account_tx_context: &AccountTransactionContext, + tx_context: &TransactionContext, actual_cost: &ActualCost, ) -> TransactionExecutionResult<()> { - let ActualCost { actual_fee, .. } = actual_cost; + let ActualCost { actual_fee, .. } = *actual_cost; let (balance_low, balance_high, can_pay) = - get_balance_and_if_covers_fee(state, account_tx_context, chain_info, *actual_fee)?; + get_balance_and_if_covers_fee(state, tx_context, actual_fee)?; if can_pay { return Ok(()); } Err(FeeCheckError::InsufficientFeeTokenBalance { - fee: *actual_fee, + fee: actual_fee, balance_low, balance_high, })? @@ -182,20 +178,15 @@ impl PostValidationReport { /// Note: the balance cannot be changed in `__validate__` (which cannot call other contracts), /// so there is no need to recheck that balance >= actual_cost. pub fn verify( - block_context: &BlockContext, - account_tx_context: &AccountTransactionContext, + tx_context: &TransactionContext, actual_cost: &ActualCost, ) -> TransactionExecutionResult<()> { // If fee is not enforced, no need to check post-execution. - if !account_tx_context.enforce_fee()? { + if !tx_context.tx_info.enforce_fee()? { return Ok(()); } - FeeCheckReport::check_actual_cost_within_bounds( - block_context, - account_tx_context, - actual_cost, - ) + FeeCheckReport::check_actual_cost_within_bounds(tx_context, actual_cost) } } @@ -204,35 +195,26 @@ impl PostExecutionReport { /// that should be charged in revert flow. pub fn new( state: &mut S, - block_context: &BlockContext, - account_tx_context: &AccountTransactionContext, + tx_context: &TransactionContext, actual_cost: &ActualCost, charge_fee: bool, ) -> TransactionExecutionResult { let ActualCost { actual_fee, .. } = actual_cost; // If fee is not enforced, no need to check post-execution. - if !charge_fee || !account_tx_context.enforce_fee()? { + if !charge_fee || !tx_context.tx_info.enforce_fee()? { return Ok(Self(FeeCheckReport::success_report(*actual_fee))); } // First, compare the actual resources used against the upper bound(s) defined by the // sender. - let cost_with_bounds_result = FeeCheckReport::check_actual_cost_within_bounds( - block_context, - account_tx_context, - actual_cost, - ); + let cost_with_bounds_result = + FeeCheckReport::check_actual_cost_within_bounds(tx_context, actual_cost); // Next, verify the actual cost is covered by the account balance, which may have changed // after execution. If the above check passes, the pre-execution balance covers the actual // cost for sure. - let can_pay_fee_result = FeeCheckReport::check_can_pay_fee( - state, - &block_context.chain_info, - account_tx_context, - actual_cost, - ); + let can_pay_fee_result = FeeCheckReport::check_can_pay_fee(state, tx_context, actual_cost); for fee_check_result in [cost_with_bounds_result, can_pay_fee_result] { match fee_check_result { @@ -243,14 +225,13 @@ impl PostExecutionReport { return Ok(Self(FeeCheckReport::from_fee_check_error( *actual_fee, fee_check_error, - &block_context.block_info, - account_tx_context, + tx_context, )?)); } Err(other_error) => return Err(other_error), } } - Ok(Self(FeeCheckReport::success_report(actual_cost.actual_fee))) + Ok(Self(FeeCheckReport::success_report(*actual_fee))) } } diff --git a/crates/blockifier/src/fee/fee_test.rs b/crates/blockifier/src/fee/fee_test.rs index f9dc7591b7..091896d6e4 100644 --- a/crates/blockifier/src/fee/fee_test.rs +++ b/crates/blockifier/src/fee/fee_test.rs @@ -90,12 +90,12 @@ fn test_discounted_gas_overdraft( (constants::BLOB_GAS_USAGE.to_string(), l1_data_gas_used), ])), }; + let charge_fee = true; let report = PostExecutionReport::new( &mut state, - &block_context, - &tx.get_account_tx_context(), + &block_context.to_tx_context(&tx), &actual_cost, - true, + charge_fee, ) .unwrap(); diff --git a/crates/blockifier/src/fee/fee_utils.rs b/crates/blockifier/src/fee/fee_utils.rs index 37b5048f92..427fa52bcf 100644 --- a/crates/blockifier/src/fee/fee_utils.rs +++ b/crates/blockifier/src/fee/fee_utils.rs @@ -5,12 +5,11 @@ use starknet_api::transaction::Fee; use crate::abi::constants; use crate::block::BlockInfo; -use crate::context::{BlockContext, ChainInfo}; +use crate::context::{BlockContext, TransactionContext}; use crate::state::state_api::StateReader; use crate::transaction::errors::TransactionFeeError; use crate::transaction::objects::{ - AccountTransactionContext, FeeType, GasVector, HasRelatedFeeType, ResourcesMapping, - TransactionFeeResult, + FeeType, GasVector, HasRelatedFeeType, ResourcesMapping, TransactionFeeResult, TransactionInfo, }; use crate::utils::u128_from_usize; use crate::versioned_constants::VersionedConstants; @@ -86,8 +85,9 @@ pub fn get_fee_by_gas_vector( gas_vector: GasVector, fee_type: &FeeType, ) -> Fee { - Fee(gas_vector.l1_gas * block_info.gas_prices.get_gas_price_by_fee_type(fee_type) - + gas_vector.blob_gas * block_info.gas_prices.get_data_gas_price_by_fee_type(fee_type)) + let gas_prices = &block_info.gas_prices; + Fee(gas_vector.l1_gas * gas_prices.get_gas_price_by_fee_type(fee_type) + + gas_vector.blob_gas * gas_prices.get_data_gas_price_by_fee_type(fee_type)) } /// Calculates the fee that should be charged, given execution resources. @@ -103,13 +103,13 @@ pub fn calculate_tx_fee( /// Returns the current fee balance and a boolean indicating whether the balance covers the fee. pub fn get_balance_and_if_covers_fee( state: &mut dyn StateReader, - account_tx_context: &AccountTransactionContext, - chain_info: &ChainInfo, + tx_context: &TransactionContext, fee: Fee, ) -> TransactionFeeResult<(StarkFelt, StarkFelt, bool)> { + let tx_info = &tx_context.tx_info; let (balance_low, balance_high) = state.get_fee_token_balance( - account_tx_context.sender_address(), - chain_info.fee_token_address(&account_tx_context.fee_type()), + tx_info.sender_address(), + tx_context.block_context.chain_info.fee_token_address(&tx_info.fee_type()), )?; Ok(( balance_low, @@ -124,26 +124,26 @@ pub fn get_balance_and_if_covers_fee( /// Error may indicate insufficient balance, or some other error. pub fn verify_can_pay_committed_bounds( state: &mut dyn StateReader, - account_tx_context: &AccountTransactionContext, - chain_info: &ChainInfo, + tx_context: &TransactionContext, ) -> TransactionFeeResult<()> { - let committed_fee = match account_tx_context { - AccountTransactionContext::Current(context) => { + let tx_info = &tx_context.tx_info; + let committed_fee = match tx_info { + TransactionInfo::Current(context) => { let l1_bounds = context.l1_resource_bounds()?; let max_amount: u128 = l1_bounds.max_amount.into(); // Sender will not be charged by `max_price_per_unit`, but this check should not depend // on the current gas price. Fee(max_amount * l1_bounds.max_price_per_unit) } - AccountTransactionContext::Deprecated(context) => context.max_fee, + TransactionInfo::Deprecated(context) => context.max_fee, }; let (balance_low, balance_high, can_pay) = - get_balance_and_if_covers_fee(state, account_tx_context, chain_info, committed_fee)?; + get_balance_and_if_covers_fee(state, tx_context, committed_fee)?; if can_pay { Ok(()) } else { - Err(match account_tx_context { - AccountTransactionContext::Current(context) => { + Err(match tx_info { + TransactionInfo::Current(context) => { let l1_bounds = context.l1_resource_bounds()?; TransactionFeeError::L1GasBoundsExceedBalance { max_amount: l1_bounds.max_amount, @@ -152,13 +152,11 @@ pub fn verify_can_pay_committed_bounds( balance_high, } } - AccountTransactionContext::Deprecated(context) => { - TransactionFeeError::MaxFeeExceedsBalance { - max_fee: context.max_fee, - balance_low, - balance_high, - } - } + TransactionInfo::Deprecated(context) => TransactionFeeError::MaxFeeExceedsBalance { + max_fee: context.max_fee, + balance_low, + balance_high, + }, }) } } diff --git a/crates/blockifier/src/fee/gas_usage.rs b/crates/blockifier/src/fee/gas_usage.rs index ad93bb48bb..57900c9a53 100644 --- a/crates/blockifier/src/fee/gas_usage.rs +++ b/crates/blockifier/src/fee/gas_usage.rs @@ -1,19 +1,16 @@ use std::collections::HashMap; -use starknet_api::transaction::Fee; - -use super::fee_utils::{calculate_tx_gas_vector, get_fee_by_gas_vector}; +use super::fee_utils::calculate_tx_gas_vector; use crate::abi::constants; -use crate::block::BlockInfo; -use crate::context::BlockContext; +use crate::context::{BlockContext, TransactionContext}; use crate::execution::call_info::{CallInfo, MessageL1CostInfo}; use crate::fee::eth_gas_constants; use crate::fee::os_resources::OS_RESOURCES; use crate::state::cached_state::StateChangesCount; use crate::transaction::account_transaction::AccountTransaction; use crate::transaction::objects::{ - AccountTransactionContext, GasVector, HasRelatedFeeType, ResourcesMapping, - TransactionExecutionResult, TransactionPreValidationResult, + GasVector, HasRelatedFeeType, ResourcesMapping, TransactionExecutionResult, + TransactionPreValidationResult, }; use crate::utils::{u128_from_usize, usize_from_u128}; @@ -257,14 +254,6 @@ pub fn estimate_minimal_gas_vector( Ok(calculate_tx_gas_vector(&resources, &block_context.versioned_constants)?) } -pub fn estimate_minimal_fee( - block_context: &BlockContext, - tx: &AccountTransaction, -) -> TransactionExecutionResult { - let estimated_minimal_l1_gas = estimate_minimal_gas_vector(block_context, tx)?; - Ok(get_fee_by_gas_vector(&block_context.block_info, estimated_minimal_l1_gas, &tx.fee_type())) -} - /// Compute l1_gas estimation from gas_vector using the following formula: /// One byte of data costs either 1 data gas (in blob mode) or 16 gas (in calldata /// mode). For gas price GP and data gas price DGP, the discount for using blobs @@ -274,12 +263,12 @@ pub fn estimate_minimal_fee( /// summand, we get total_gas = (X + Y * DGP / GP). pub fn compute_discounted_gas_from_gas_vector( gas_usage_vector: &GasVector, - account_tx_context: &AccountTransactionContext, - block_info: &BlockInfo, + tx_context: &TransactionContext, ) -> u128 { + let gas_prices = &tx_context.block_context.block_info.gas_prices; let GasVector { l1_gas: gas_usage, blob_gas: blob_gas_usage } = gas_usage_vector; - let fee_type = account_tx_context.fee_type(); - let gas_price = block_info.gas_prices.get_gas_price_by_fee_type(&fee_type); - let data_gas_price = block_info.gas_prices.get_data_gas_price_by_fee_type(&fee_type); + let fee_type = tx_context.tx_info.fee_type(); + let gas_price = gas_prices.get_gas_price_by_fee_type(&fee_type); + let data_gas_price = gas_prices.get_data_gas_price_by_fee_type(&fee_type); gas_usage + (blob_gas_usage * data_gas_price) / gas_price } diff --git a/crates/blockifier/src/test_utils/prices.rs b/crates/blockifier/src/test_utils/prices.rs index 1b8994ecfd..291a4a12ec 100644 --- a/crates/blockifier/src/test_utils/prices.rs +++ b/crates/blockifier/src/test_utils/prices.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use cached::proc_macro::cached; use cairo_vm::vm::runners::cairo_runner::ExecutionResources as VmExecutionResources; use starknet_api::core::ContractAddress; @@ -73,8 +75,7 @@ fn fee_transfer_resources( state, &mut ExecutionResources::default(), &mut EntryPointExecutionContext::new( - block_context, - &account_invoke_tx(InvokeTxArgs::default()).get_account_tx_context(), + Arc::new(block_context.to_tx_context(&account_invoke_tx(InvokeTxArgs::default()))), ExecutionMode::Execute, false, ) diff --git a/crates/blockifier/src/test_utils/struct_impls.rs b/crates/blockifier/src/test_utils/struct_impls.rs index 3b44ea1092..4793c543a5 100644 --- a/crates/blockifier/src/test_utils/struct_impls.rs +++ b/crates/blockifier/src/test_utils/struct_impls.rs @@ -17,7 +17,7 @@ use super::{ }; use crate::abi::constants; use crate::block::{BlockInfo, GasPrices}; -use crate::context::{BlockContext, ChainInfo, FeeTokenAddresses}; +use crate::context::{BlockContext, ChainInfo, FeeTokenAddresses, TransactionContext}; use crate::execution::call_info::{CallExecution, CallInfo, Retdata}; use crate::execution::contract_class::{ContractClassV0, ContractClassV1}; use crate::execution::entry_point::{ @@ -25,33 +25,31 @@ use crate::execution::entry_point::{ }; use crate::state::state_api::State; use crate::test_utils::get_raw_contract_class; -use crate::transaction::objects::{AccountTransactionContext, DeprecatedAccountTransactionContext}; +use crate::transaction::objects::{DeprecatedTransactionInfo, TransactionInfo}; use crate::versioned_constants::VersionedConstants; impl CallEntryPoint { /// Executes the call directly, without account context. Limits the number of steps by resource /// bounds. pub fn execute_directly(self, state: &mut dyn State) -> EntryPointExecutionResult { - self.execute_directly_given_account_context( + self.execute_directly_given_tx_info( state, - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext::default()), + TransactionInfo::Deprecated(DeprecatedTransactionInfo::default()), true, ) } - pub fn execute_directly_given_account_context( + pub fn execute_directly_given_tx_info( self, state: &mut dyn State, - account_tx_context: AccountTransactionContext, + tx_info: TransactionInfo, limit_steps_by_resources: bool, ) -> EntryPointExecutionResult { - let block_context = BlockContext::create_for_testing(); - let mut context = EntryPointExecutionContext::new_invoke( - &block_context, - &account_tx_context, - limit_steps_by_resources, - ) - .unwrap(); + let tx_context = + TransactionContext { block_context: BlockContext::create_for_testing(), tx_info }; + let mut context = + EntryPointExecutionContext::new_invoke(Arc::new(tx_context), limit_steps_by_resources) + .unwrap(); self.execute(state, &mut ExecutionResources::default(), &mut context) } @@ -61,23 +59,23 @@ impl CallEntryPoint { self, state: &mut dyn State, ) -> EntryPointExecutionResult { - self.execute_directly_given_account_context_in_validate_mode( + self.execute_directly_given_tx_info_in_validate_mode( state, - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext::default()), + TransactionInfo::Deprecated(DeprecatedTransactionInfo::default()), true, ) } - pub fn execute_directly_given_account_context_in_validate_mode( + pub fn execute_directly_given_tx_info_in_validate_mode( self, state: &mut dyn State, - account_tx_context: AccountTransactionContext, + tx_info: TransactionInfo, limit_steps_by_resources: bool, ) -> EntryPointExecutionResult { - let block_context = BlockContext::create_for_testing(); + let tx_context = + TransactionContext { block_context: BlockContext::create_for_testing(), tx_info }; let mut context = EntryPointExecutionContext::new_validate( - &block_context, - &account_tx_context, + Arc::new(tx_context), limit_steps_by_resources, ) .unwrap(); diff --git a/crates/blockifier/src/transaction/account_transaction.rs b/crates/blockifier/src/transaction/account_transaction.rs index f2332596ed..61dfd9977e 100644 --- a/crates/blockifier/src/transaction/account_transaction.rs +++ b/crates/blockifier/src/transaction/account_transaction.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use itertools::concat; use starknet_api::calldata; use starknet_api::core::{ContractAddress, EntryPointSelector}; @@ -7,7 +9,7 @@ use starknet_api::transaction::{Calldata, Fee, ResourceBounds, TransactionVersio use crate::abi::abi_utils::selector_from_name; use crate::abi::constants as abi_constants; -use crate::context::BlockContext; +use crate::context::{BlockContext, TransactionContext}; use crate::execution::call_info::{CallInfo, Retdata}; use crate::execution::contract_class::ContractClass; use crate::execution::entry_point::{ @@ -25,8 +27,8 @@ use crate::transaction::errors::{ TransactionExecutionError, TransactionFeeError, TransactionPreValidationError, }; use crate::transaction::objects::{ - AccountTransactionContext, HasRelatedFeeType, TransactionExecutionInfo, - TransactionExecutionResult, TransactionPreValidationResult, + HasRelatedFeeType, TransactionExecutionInfo, TransactionExecutionResult, TransactionInfo, + TransactionInfoCreator, TransactionPreValidationResult, }; use crate::transaction::transaction_execution::Transaction; use crate::transaction::transaction_types::TransactionType; @@ -94,7 +96,7 @@ impl AccountTransaction { } // Calldata for validation contains transaction fields that cannot be obtained by calling - // `get_tx_info()`. + // `et_tx_info()`. fn validate_entrypoint_calldata(&self) -> Calldata { match self { Self::Declare(tx) => calldata![tx.class_hash().0], @@ -118,14 +120,6 @@ impl AccountTransaction { } } - pub fn get_account_tx_context(&self) -> AccountTransactionContext { - match self { - Self::Declare(tx) => tx.get_account_tx_context(), - Self::DeployAccount(tx) => tx.get_account_tx_context(), - Self::Invoke(tx) => tx.get_account_tx_context(), - } - } - fn verify_tx_version(&self, version: TransactionVersion) -> TransactionExecutionResult<()> { let allowed_versions: Vec = match self { // Support `Declare` of version 0 in order to allow bootstrapping of a new system. @@ -156,17 +150,17 @@ impl AccountTransaction { pub fn perform_pre_validation_stage( &self, state: &mut S, - account_tx_context: &AccountTransactionContext, - block_context: &BlockContext, + tx_context: &TransactionContext, charge_fee: bool, strict_nonce_check: bool, ) -> TransactionPreValidationResult<()> { - Self::handle_nonce(state, account_tx_context, strict_nonce_check)?; + let tx_info = &tx_context.tx_info; + Self::handle_nonce(state, tx_info, strict_nonce_check)?; - if charge_fee && account_tx_context.enforce_fee()? { - self.check_fee_bounds(account_tx_context, block_context)?; + if charge_fee && tx_info.enforce_fee()? { + self.check_fee_bounds(tx_context)?; - verify_can_pay_committed_bounds(state, account_tx_context, &block_context.chain_info)?; + verify_can_pay_committed_bounds(state, tx_context)?; } Ok(()) @@ -174,21 +168,19 @@ impl AccountTransaction { fn check_fee_bounds( &self, - account_tx_context: &AccountTransactionContext, - block_context: &BlockContext, + tx_context: &TransactionContext, ) -> TransactionPreValidationResult<()> { - let minimal_l1_gas_amount_vector = estimate_minimal_gas_vector(block_context, self)?; + let minimal_l1_gas_amount_vector = + estimate_minimal_gas_vector(&tx_context.block_context, self)?; // TODO(Aner, 30/01/24): modify once data gas limit is enforced. - let minimal_l1_gas_amount = compute_discounted_gas_from_gas_vector( - &minimal_l1_gas_amount_vector, - account_tx_context, - &block_context.block_info, - ); + let minimal_l1_gas_amount = + compute_discounted_gas_from_gas_vector(&minimal_l1_gas_amount_vector, tx_context); + let TransactionContext { block_context, tx_info } = tx_context; let block_info = &block_context.block_info; - - match account_tx_context { - AccountTransactionContext::Current(context) => { + let fee_type = &tx_info.fee_type(); + match tx_info { + TransactionInfo::Current(context) => { let ResourceBounds { max_amount: max_l1_gas_amount, max_price_per_unit: max_l1_gas_price, @@ -206,8 +198,7 @@ impl AccountTransaction { })?; } - let actual_l1_gas_price = - block_info.gas_prices.get_gas_price_by_fee_type(&account_tx_context.fee_type()); + let actual_l1_gas_price = block_info.gas_prices.get_gas_price_by_fee_type(fee_type); if max_l1_gas_price < actual_l1_gas_price { return Err(TransactionFeeError::MaxL1GasPriceTooLow { max_l1_gas_price, @@ -215,13 +206,10 @@ impl AccountTransaction { })?; } } - AccountTransactionContext::Deprecated(context) => { + TransactionInfo::Deprecated(context) => { let max_fee = context.max_fee; - let min_fee = get_fee_by_gas_vector( - block_info, - minimal_l1_gas_amount_vector, - &account_tx_context.fee_type(), - ); + let min_fee = + get_fee_by_gas_vector(block_info, minimal_l1_gas_amount_vector, fee_type); if max_fee < min_fee { return Err(TransactionFeeError::MaxFeeTooLow { min_fee, max_fee })?; } @@ -232,16 +220,16 @@ impl AccountTransaction { fn handle_nonce( state: &mut dyn State, - account_tx_context: &AccountTransactionContext, + tx_info: &TransactionInfo, strict: bool, ) -> TransactionPreValidationResult<()> { - if account_tx_context.is_v0() { + if tx_info.is_v0() { return Ok(()); } - let address = account_tx_context.sender_address(); + let address = tx_info.sender_address(); let account_nonce = state.get_nonce_at(address)?; - let incoming_tx_nonce = account_tx_context.nonce(); + let incoming_tx_nonce = tx_info.nonce(); let valid_nonce = if strict { account_nonce == incoming_tx_nonce } else { @@ -257,26 +245,17 @@ impl AccountTransaction { }) } - #[allow(clippy::too_many_arguments)] fn handle_validate_tx( &self, state: &mut dyn State, resources: &mut ExecutionResources, - account_tx_context: &AccountTransactionContext, + tx_context: Arc, remaining_gas: &mut u64, - block_context: &BlockContext, validate: bool, limit_steps_by_resources: bool, ) -> TransactionExecutionResult> { if validate { - self.validate_tx( - state, - resources, - account_tx_context, - remaining_gas, - block_context, - limit_steps_by_resources, - ) + self.validate_tx(state, resources, tx_context, remaining_gas, limit_steps_by_resources) } else { Ok(None) } @@ -285,7 +264,7 @@ impl AccountTransaction { fn handle_fee( &self, state: &mut dyn State, - block_context: &BlockContext, + tx_context: Arc, actual_fee: Fee, charge_fee: bool, ) -> TransactionExecutionResult> { @@ -295,17 +274,14 @@ impl AccountTransaction { } // Charge fee. - let account_tx_context = self.get_account_tx_context(); - let fee_transfer_call_info = - Self::execute_fee_transfer(state, block_context, account_tx_context, actual_fee)?; + let fee_transfer_call_info = Self::execute_fee_transfer(state, tx_context, actual_fee)?; Ok(Some(fee_transfer_call_info)) } fn execute_fee_transfer( state: &mut dyn State, - block_context: &BlockContext, - account_tx_context: AccountTransactionContext, + tx_context: Arc, actual_fee: Fee, ) -> TransactionExecutionResult { // The least significant 128 bits of the amount transferred. @@ -313,9 +289,10 @@ impl AccountTransaction { // The most significant 128 bits of the amount transferred. let msb_amount = StarkFelt::from(0_u8); + let TransactionContext { block_context, tx_info } = tx_context.as_ref(); + // TODO(Gilad): add test that correct fee address is taken, once we add V3 test support. - let storage_address = - block_context.chain_info.fee_token_address(&account_tx_context.fee_type()); + let storage_address = block_context.chain_info.fee_token_address(&tx_info.fee_type()); let fee_transfer_call = CallEntryPoint { class_hash: None, code_address: None, @@ -327,14 +304,13 @@ impl AccountTransaction { msb_amount ], storage_address, - caller_address: account_tx_context.sender_address(), + caller_address: tx_info.sender_address(), call_type: CallType::Call, // The fee-token contract is a Cairo 0 contract, hence the initial gas is irrelevant. initial_gas: abi_constants::INITIAL_GAS_COST, }; - let mut context = - EntryPointExecutionContext::new_invoke(block_context, &account_tx_context, true)?; + let mut context = EntryPointExecutionContext::new_invoke(tx_context, true)?; Ok(fee_transfer_call .execute(state, &mut ExecutionResources::default(), &mut context) @@ -358,9 +334,8 @@ impl AccountTransaction { fn run_non_revertible( &self, state: &mut TransactionalState<'_, S>, - account_tx_context: &AccountTransactionContext, + tx_context: Arc, remaining_gas: &mut u64, - block_context: &BlockContext, validate: bool, charge_fee: bool, ) -> TransactionExecutionResult { @@ -371,34 +346,26 @@ impl AccountTransaction { // Handle `DeployAccount` transactions separately, due to different order of things. // Also, the execution context required form the `DeployAccount` execute phase is // validation context. - let mut execution_context = EntryPointExecutionContext::new_validate( - block_context, - account_tx_context, - charge_fee, - )?; + let mut execution_context = + EntryPointExecutionContext::new_validate(tx_context.clone(), charge_fee)?; execute_call_info = self.run_execute(state, &mut resources, &mut execution_context, remaining_gas)?; validate_call_info = self.handle_validate_tx( state, &mut resources, - account_tx_context, + tx_context.clone(), remaining_gas, - block_context, validate, charge_fee, )?; } else { - let mut execution_context = EntryPointExecutionContext::new_invoke( - block_context, - account_tx_context, - charge_fee, - )?; + let mut execution_context = + EntryPointExecutionContext::new_invoke(tx_context.clone(), charge_fee)?; validate_call_info = self.handle_validate_tx( state, &mut resources, - account_tx_context, + tx_context.clone(), remaining_gas, - block_context, validate, charge_fee, )?; @@ -407,19 +374,14 @@ impl AccountTransaction { } let actual_cost = self - .to_actual_cost_builder(block_context) + .to_actual_cost_builder(tx_context.clone()) .with_validate_call_info(&validate_call_info) .with_execute_call_info(&execute_call_info) .try_add_state_changes(state)? .build(&resources)?; - let post_execution_report = PostExecutionReport::new( - state, - block_context, - account_tx_context, - &actual_cost, - charge_fee, - )?; + let post_execution_report = + PostExecutionReport::new(state, &tx_context, &actual_cost, charge_fee)?; match post_execution_report.error() { Some(error) => Err(error.into()), None => Ok(ValidateExecuteCallInfo::new_accepted( @@ -430,27 +392,23 @@ impl AccountTransaction { } } - #[allow(clippy::too_many_arguments)] fn run_revertible( &self, state: &mut TransactionalState<'_, S>, - account_tx_context: &AccountTransactionContext, + tx_context: Arc, remaining_gas: &mut u64, - block_context: &BlockContext, validate: bool, charge_fee: bool, ) -> TransactionExecutionResult { let mut resources = ExecutionResources::default(); let mut execution_context = - EntryPointExecutionContext::new_invoke(block_context, account_tx_context, charge_fee)?; - let account_tx_context = self.get_account_tx_context(); + EntryPointExecutionContext::new_invoke(tx_context.clone(), charge_fee)?; // Run the validation, and if execution later fails, only keep the validation diff. let validate_call_info = self.handle_validate_tx( state, &mut resources, - &account_tx_context, + tx_context.clone(), remaining_gas, - block_context, validate, charge_fee, )?; @@ -464,7 +422,7 @@ impl AccountTransaction { // Save the state changes resulting from running `validate_tx`, to be used later for // resource and fee calculation. let actual_cost_builder_with_validation_changes = self - .to_actual_cost_builder(block_context) + .to_actual_cost_builder(tx_context.clone()) .with_validate_call_info(&validate_call_info) .try_add_state_changes(state)?; @@ -503,8 +461,7 @@ impl AccountTransaction { // Post-execution checks. let post_execution_report = PostExecutionReport::new( &mut execution_state, - block_context, - &account_tx_context, + &tx_context, &actual_cost, charge_fee, )?; @@ -538,13 +495,8 @@ impl AccountTransaction { Err(_) => { // Error during execution. Revert, even if the error is sequencer-related. execution_state.abort(); - let post_execution_report = PostExecutionReport::new( - state, - block_context, - &account_tx_context, - &revert_cost, - charge_fee, - )?; + let post_execution_report = + PostExecutionReport::new(state, &tx_context, &revert_cost, charge_fee)?; Ok(ValidateExecuteCallInfo::new_reverted( validate_call_info, execution_context.error_trace(), @@ -557,7 +509,7 @@ impl AccountTransaction { } } - fn is_non_revertible(&self) -> bool { + fn is_non_revertible(&self, tx_info: &TransactionInfo) -> bool { // Reverting a Declare or Deploy transaction is not currently supported in the OS. match self { Self::Declare(_) => true, @@ -565,7 +517,7 @@ impl AccountTransaction { Self::Invoke(_) => { // V0 transactions do not have validation; we cannot deduct fee for execution. Thus, // invoke transactions of are non-revertible iff they are of version 0. - self.get_account_tx_context().is_v0() + tx_info.is_v0() } } } @@ -575,40 +527,22 @@ impl AccountTransaction { &self, state: &mut TransactionalState<'_, S>, remaining_gas: &mut u64, - block_context: &BlockContext, + tx_context: Arc, validate: bool, charge_fee: bool, ) -> TransactionExecutionResult { - let account_tx_context = self.get_account_tx_context(); - - if self.is_non_revertible() { - return self.run_non_revertible( - state, - &account_tx_context, - remaining_gas, - block_context, - validate, - charge_fee, - ); + if self.is_non_revertible(&tx_context.tx_info) { + return self.run_non_revertible(state, tx_context, remaining_gas, validate, charge_fee); } - self.run_revertible( - state, - &account_tx_context, - remaining_gas, - block_context, - validate, - charge_fee, - ) + self.run_revertible(state, tx_context, remaining_gas, validate, charge_fee) } - pub fn to_actual_cost_builder(&self, block_context: &BlockContext) -> ActualCostBuilder<'_> { - ActualCostBuilder::new( - block_context, - self.get_account_tx_context(), - self.tx_type(), - self.calldata_length(), - ) + pub fn to_actual_cost_builder( + &self, + tx_context: Arc, + ) -> ActualCostBuilder<'_> { + ActualCostBuilder::new(tx_context, self.tx_type(), self.calldata_length()) } } @@ -620,19 +554,12 @@ impl ExecutableTransaction for AccountTransaction { charge_fee: bool, validate: bool, ) -> TransactionExecutionResult { - let account_tx_context = self.get_account_tx_context(); - - self.verify_tx_version(account_tx_context.version())?; + let tx_context = Arc::new(block_context.to_tx_context(&self)); + self.verify_tx_version(tx_context.tx_info.version())?; // Nonce and fee check should be done before running user code. let strict_nonce_check = true; - self.perform_pre_validation_stage( - state, - &account_tx_context, - block_context, - charge_fee, - strict_nonce_check, - )?; + self.perform_pre_validation_stage(state, &tx_context, charge_fee, strict_nonce_check)?; // Run validation and execution. let mut remaining_gas = Transaction::initial_gas(); @@ -641,10 +568,15 @@ impl ExecutableTransaction for AccountTransaction { execute_call_info, revert_error, final_cost: ActualCost { actual_fee: final_fee, actual_resources: final_resources }, - } = self.run_or_revert(state, &mut remaining_gas, block_context, validate, charge_fee)?; + } = self.run_or_revert( + state, + &mut remaining_gas, + tx_context.clone(), + validate, + charge_fee, + )?; - let fee_transfer_call_info = - self.handle_fee(state, block_context, final_fee, charge_fee)?; + let fee_transfer_call_info = self.handle_fee(state, tx_context, final_fee, charge_fee)?; let tx_execution_info = TransactionExecutionInfo { validate_call_info, @@ -658,6 +590,16 @@ impl ExecutableTransaction for AccountTransaction { } } +impl TransactionInfoCreator for AccountTransaction { + fn create_tx_info(&self) -> TransactionInfo { + match self { + Self::Declare(tx) => tx.create_tx_info(), + Self::DeployAccount(tx) => tx.create_tx_info(), + Self::Invoke(tx) => tx.create_tx_info(), + } + } +} + /// Represents a bundle of validate-execute stage execution effects. struct ValidateExecuteCallInfo { validate_call_info: Option, @@ -694,21 +636,18 @@ impl ValidatableTransaction for AccountTransaction { &self, state: &mut dyn State, resources: &mut ExecutionResources, - account_tx_context: &AccountTransactionContext, + tx_context: Arc, remaining_gas: &mut u64, - block_context: &BlockContext, limit_steps_by_resources: bool, ) -> TransactionExecutionResult> { - let mut context = EntryPointExecutionContext::new_validate( - block_context, - account_tx_context, - limit_steps_by_resources, - )?; - if context.account_tx_context.is_v0() { + let mut context = + EntryPointExecutionContext::new_validate(tx_context, limit_steps_by_resources)?; + let tx_info = &context.tx_context.tx_info; + if tx_info.is_v0() { return Ok(None); } - let storage_address = account_tx_context.sender_address(); + let storage_address = tx_info.sender_address(); let validate_call = CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector: self.validate_entry_point_selector(), diff --git a/crates/blockifier/src/transaction/account_transactions_test.rs b/crates/blockifier/src/transaction/account_transactions_test.rs index d7c34c59ed..57b7017285 100644 --- a/crates/blockifier/src/transaction/account_transactions_test.rs +++ b/crates/blockifier/src/transaction/account_transactions_test.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet}; +use std::sync::Arc; use assert_matches::assert_matches; use cairo_felt::Felt252; @@ -41,7 +42,7 @@ use crate::test_utils::{ use crate::transaction::account_transaction::AccountTransaction; use crate::transaction::constants::TRANSFER_ENTRY_POINT_NAME; use crate::transaction::errors::TransactionExecutionError; -use crate::transaction::objects::{FeeType, HasRelatedFeeType}; +use crate::transaction::objects::{FeeType, HasRelatedFeeType, TransactionInfoCreator}; use crate::transaction::test_utils::{ account_invoke_tx, block_context, create_account_tx_for_validate_test, create_test_init_data, deploy_and_fund_account, l1_resource_bounds, max_fee, max_resource_bounds, run_invoke_tx, @@ -73,7 +74,7 @@ fn test_fee_enforcement( ); let account_tx = AccountTransaction::DeployAccount(deploy_account_tx); - let enforce_fee = account_tx.get_account_tx_context().enforce_fee().unwrap(); + let enforce_fee = account_tx.create_tx_info().enforce_fee().unwrap(); let result = account_tx.execute(state, &block_context, true, true); assert_eq!(result.is_err(), enforce_fee); } @@ -107,7 +108,7 @@ fn test_enforce_fee_false_works(block_context: BlockContext, #[case] version: Tr } // TODO(Dori, 15/9/2023): Convert version variance to attribute macro. -// TODO(Dori, 10/10/2023): Add V3 case once `get_account_tx_context` is supported for V3. +// TODO(Dori, 10/10/2023): Add V3 case once `create_tx_info` is supported for V3. #[rstest] fn test_account_flow_test( block_context: BlockContext, @@ -141,7 +142,7 @@ fn test_account_flow_test( #[rstest] #[case(TransactionVersion::ZERO)] #[case(TransactionVersion::ONE)] -// TODO(Nimrod, 10/10/2023): Add V3 case once `get_account_tx_context` is supported for V3. +// TODO(Nimrod, 10/10/2023): Add V3 case once `get_tx_info` is supported for V3. fn test_invoke_tx_from_non_deployed_account( block_context: BlockContext, max_fee: Fee, @@ -572,12 +573,9 @@ fn test_fail_declare(block_context: BlockContext, max_fee: Fee) { ); // Fail execution, assert nonce and balance are unchanged. - let account_tx_context = declare_account_tx.get_account_tx_context(); + let tx_info = declare_account_tx.create_tx_info(); let initial_balance = state - .get_fee_token_balance( - account_address, - chain_info.fee_token_address(&account_tx_context.fee_type()), - ) + .get_fee_token_balance(account_address, chain_info.fee_token_address(&tx_info.fee_type())) .unwrap(); declare_account_tx.execute(&mut state, &block_context, true, true).unwrap_err(); @@ -586,7 +584,7 @@ fn test_fail_declare(block_context: BlockContext, max_fee: Fee) { state .get_fee_token_balance( account_address, - chain_info.fee_token_address(&account_tx_context.fee_type()) + chain_info.fee_token_address(&tx_info.fee_type()) ) .unwrap(), initial_balance @@ -851,12 +849,8 @@ fn test_max_fee_to_max_steps_conversion( resource_bounds: l1_resource_bounds(actual_gas_used, actual_strk_gas_price), nonce: nonce_manager.next(account_address), }); - let execution_context1 = EntryPointExecutionContext::new_invoke( - &block_context, - &account_tx1.get_account_tx_context(), - true, - ) - .unwrap(); + let tx_context1 = Arc::new(block_context.to_tx_context(&account_tx1)); + let execution_context1 = EntryPointExecutionContext::new_invoke(tx_context1, true).unwrap(); let max_steps_limit1 = execution_context1.vm_run_resources.get_n_steps(); let tx_execution_info1 = account_tx1.execute(&mut state, &block_context, true, true).unwrap(); let n_steps1 = tx_execution_info1.actual_resources.n_steps(); @@ -875,12 +869,8 @@ fn test_max_fee_to_max_steps_conversion( resource_bounds: l1_resource_bounds(2 * actual_gas_used, actual_strk_gas_price), nonce: nonce_manager.next(account_address), }); - let execution_context2 = EntryPointExecutionContext::new_invoke( - &block_context, - &account_tx2.get_account_tx_context(), - true, - ) - .unwrap(); + let tx_context2 = Arc::new(block_context.to_tx_context(&account_tx2)); + let execution_context2 = EntryPointExecutionContext::new_invoke(tx_context2, true).unwrap(); let max_steps_limit2 = execution_context2.vm_run_resources.get_n_steps(); let tx_execution_info2 = account_tx2.execute(&mut state, &block_context, true, true).unwrap(); let n_steps2 = tx_execution_info2.actual_resources.n_steps(); diff --git a/crates/blockifier/src/transaction/objects.rs b/crates/blockifier/src/transaction/objects.rs index 479bda79b4..1a30127922 100644 --- a/crates/blockifier/src/transaction/objects.rs +++ b/crates/blockifier/src/transaction/objects.rs @@ -37,20 +37,14 @@ macro_rules! implement_getters { }; } -#[derive(Clone, Copy, Hash, EnumIter, Eq, PartialEq)] -pub enum FeeType { - Strk, - Eth, -} - /// Contains the account information of the transaction (outermost call). #[derive(Clone, Debug, Eq, PartialEq)] -pub enum AccountTransactionContext { - Current(CurrentAccountTransactionContext), - Deprecated(DeprecatedAccountTransactionContext), +pub enum TransactionInfo { + Current(CurrentTransactionInfo), + Deprecated(DeprecatedTransactionInfo), } -impl AccountTransactionContext { +impl TransactionInfo { implement_getters!( (transaction_hash, TransactionHash), (version, TransactionVersion), @@ -83,17 +77,17 @@ impl AccountTransactionContext { pub fn enforce_fee(&self) -> TransactionFeeResult { match self { - AccountTransactionContext::Current(context) => { + TransactionInfo::Current(context) => { let l1_bounds = context.l1_resource_bounds()?; let max_amount: u128 = l1_bounds.max_amount.into(); Ok(max_amount * l1_bounds.max_price_per_unit > 0) } - AccountTransactionContext::Deprecated(context) => Ok(context.max_fee != Fee(0)), + TransactionInfo::Deprecated(context) => Ok(context.max_fee != Fee(0)), } } } -impl HasRelatedFeeType for AccountTransactionContext { +impl HasRelatedFeeType for TransactionInfo { fn version(&self) -> TransactionVersion { self.version() } @@ -104,7 +98,7 @@ impl HasRelatedFeeType for AccountTransactionContext { } #[derive(Clone, Debug, Eq, PartialEq)] -pub struct CurrentAccountTransactionContext { +pub struct CurrentTransactionInfo { pub common_fields: CommonAccountFields, pub resource_bounds: ResourceBoundsMapping, pub tip: Tip, @@ -114,7 +108,7 @@ pub struct CurrentAccountTransactionContext { pub account_deployment_data: AccountDeploymentData, } -impl CurrentAccountTransactionContext { +impl CurrentTransactionInfo { /// Fetch the L1 resource bounds, if they exist. pub fn l1_resource_bounds(&self) -> TransactionFeeResult { match self.resource_bounds.0.get(&Resource::L1Gas).copied() { @@ -125,7 +119,7 @@ impl CurrentAccountTransactionContext { } #[derive(Clone, Debug, Default, Eq, PartialEq)] -pub struct DeprecatedAccountTransactionContext { +pub struct DeprecatedTransactionInfo { pub common_fields: CommonAccountFields, pub max_fee: Fee, } @@ -236,3 +230,13 @@ pub trait HasRelatedFeeType { Ok(calculate_tx_fee(resources, block_context, &self.fee_type())?) } } + +#[derive(Clone, Copy, Hash, EnumIter, Eq, PartialEq)] +pub enum FeeType { + Strk, + Eth, +} + +pub trait TransactionInfoCreator { + fn create_tx_info(&self) -> TransactionInfo; +} diff --git a/crates/blockifier/src/transaction/post_execution_test.rs b/crates/blockifier/src/transaction/post_execution_test.rs index 211bbcb274..06fffe1479 100644 --- a/crates/blockifier/src/transaction/post_execution_test.rs +++ b/crates/blockifier/src/transaction/post_execution_test.rs @@ -17,7 +17,7 @@ use crate::test_utils::initial_test_state::test_state; use crate::test_utils::{create_calldata, CairoVersion, BALANCE, MAX_L1_GAS_PRICE}; use crate::transaction::account_transaction::AccountTransaction; use crate::transaction::errors::TransactionExecutionError; -use crate::transaction::objects::{FeeType, HasRelatedFeeType}; +use crate::transaction::objects::{FeeType, HasRelatedFeeType, TransactionInfoCreator}; use crate::transaction::test_utils::{ account_invoke_tx, block_context, l1_resource_bounds, max_fee, max_resource_bounds, run_invoke_tx, TestInitData, @@ -108,7 +108,7 @@ fn test_revert_on_overdraft( resource_bounds: max_resource_bounds.clone(), nonce: nonce_manager.next(account_address), }); - let account_tx_context = approve_tx.get_account_tx_context(); + let tx_info = approve_tx.create_tx_info(); let approval_execution_info = approve_tx.execute(&mut state, &block_context, true, true).unwrap(); assert!(!approval_execution_info.is_reverted()); @@ -141,10 +141,7 @@ fn test_revert_on_overdraft( // Check the current balance, before next transaction. let (balance, _) = state - .get_fee_token_balance( - account_address, - chain_info.fee_token_address(&account_tx_context.fee_type()), - ) + .get_fee_token_balance(account_address, chain_info.fee_token_address(&tx_info.fee_type())) .unwrap(); // Attempt to transfer the entire balance, such that no funds remain to pay transaction fee. @@ -187,7 +184,7 @@ fn test_revert_on_overdraft( state .get_fee_token_balance( account_address, - chain_info.fee_token_address(&account_tx_context.fee_type()), + chain_info.fee_token_address(&tx_info.fee_type()), ) .unwrap(), (expected_new_balance, stark_felt!(0_u8)) @@ -196,7 +193,7 @@ fn test_revert_on_overdraft( state .get_fee_token_balance( recipient_address, - chain_info.fee_token_address(&account_tx_context.fee_type()) + chain_info.fee_token_address(&tx_info.fee_type()) ) .unwrap(), (final_received_amount, stark_felt!(0_u8)) diff --git a/crates/blockifier/src/transaction/transaction_execution.rs b/crates/blockifier/src/transaction/transaction_execution.rs index b73da0ec93..6be04265f7 100644 --- a/crates/blockifier/src/transaction/transaction_execution.rs +++ b/crates/blockifier/src/transaction/transaction_execution.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use starknet_api::core::{calculate_contract_address, ContractAddress}; use starknet_api::transaction::{Fee, Transaction as StarknetApiTransaction, TransactionHash}; @@ -10,12 +12,15 @@ use crate::state::cached_state::TransactionalState; use crate::state::state_api::StateReader; use crate::transaction::account_transaction::AccountTransaction; use crate::transaction::errors::TransactionFeeError; -use crate::transaction::objects::{TransactionExecutionInfo, TransactionExecutionResult}; +use crate::transaction::objects::{ + TransactionExecutionInfo, TransactionExecutionResult, TransactionInfo, TransactionInfoCreator, +}; use crate::transaction::transactions::{ DeclareTransaction, DeployAccountTransaction, Executable, ExecutableTransaction, InvokeTransaction, L1HandlerTransaction, }; +// TODO: Move into transaction.rs, makes more sense to be defined there. #[derive(Debug, derive_more::From)] pub enum Transaction { AccountTransaction(AccountTransaction), @@ -88,6 +93,15 @@ impl Transaction { } } +impl TransactionInfoCreator for Transaction { + fn create_tx_info(&self) -> TransactionInfo { + match self { + Self::AccountTransaction(account_tx) => account_tx.create_tx_info(), + Self::L1HandlerTransaction(l1_handler_tx) => l1_handler_tx.create_tx_info(), + } + } +} + impl ExecutableTransaction for L1HandlerTransaction { fn execute_raw( self, @@ -96,17 +110,17 @@ impl ExecutableTransaction for L1HandlerTransaction { _charge_fee: bool, _validate: bool, ) -> TransactionExecutionResult { - let tx_context = self.get_account_tx_context(); + let tx_context = Arc::new(block_context.to_tx_context(&self)); let mut execution_resources = ExecutionResources::default(); - let mut context = EntryPointExecutionContext::new_invoke(block_context, &tx_context, true)?; + let mut context = EntryPointExecutionContext::new_invoke(tx_context.clone(), true)?; let mut remaining_gas = Transaction::initial_gas(); let execute_call_info = self.run_execute(state, &mut execution_resources, &mut context, &mut remaining_gas)?; let l1_handler_payload_size = self.payload_size(); let ActualCost { actual_fee, actual_resources } = - ActualCost::builder_for_l1_handler(block_context, tx_context, l1_handler_payload_size) + ActualCost::builder_for_l1_handler(tx_context, l1_handler_payload_size) .with_execute_call_info(&execute_call_info) .try_add_state_changes(state)? .build(&execution_resources)?; diff --git a/crates/blockifier/src/transaction/transactions.rs b/crates/blockifier/src/transaction/transactions.rs index a60a6fc8da..1988cbf020 100644 --- a/crates/blockifier/src/transaction/transactions.rs +++ b/crates/blockifier/src/transaction/transactions.rs @@ -8,7 +8,7 @@ use starknet_api::transaction::{ }; use crate::abi::abi_utils::selector_from_name; -use crate::context::BlockContext; +use crate::context::{BlockContext, TransactionContext}; use crate::execution::call_info::CallInfo; use crate::execution::contract_class::ContractClass; use crate::execution::entry_point::{ @@ -21,9 +21,8 @@ use crate::state::state_api::{State, StateReader}; use crate::transaction::constants; use crate::transaction::errors::TransactionExecutionError; use crate::transaction::objects::{ - AccountTransactionContext, CommonAccountFields, CurrentAccountTransactionContext, - DeprecatedAccountTransactionContext, HasRelatedFeeType, TransactionExecutionInfo, - TransactionExecutionResult, + CommonAccountFields, CurrentTransactionInfo, DeprecatedTransactionInfo, HasRelatedFeeType, + TransactionExecutionInfo, TransactionExecutionResult, TransactionInfo, TransactionInfoCreator, }; use crate::transaction::transaction_utils::{update_remaining_gas, verify_contract_class_version}; @@ -68,8 +67,10 @@ pub trait ExecutableTransaction: Sized { } } - /// Executes the transaction in a transactional manner - /// (if it fails, given state might become corrupted; i.e., changes until failure will appear). + /// Note: In case of execution failure, the state may become corrupted. This means that + /// any changes made up to the point of failure will persist in the state. To revert these + /// changes, you should call `state.abort()`. Alternatively, consider using `execute` + /// for automatic handling of such cases. fn execute_raw( self, state: &mut TransactionalState<'_, S>, @@ -95,9 +96,8 @@ pub trait ValidatableTransaction { &self, state: &mut dyn State, resources: &mut ExecutionResources, - account_tx_context: &AccountTransactionContext, + tx_context: Arc, remaining_gas: &mut u64, - block_context: &BlockContext, limit_steps_by_resources: bool, ) -> TransactionExecutionResult>; } @@ -153,45 +153,6 @@ impl DeclareTransaction { self.contract_class.clone() } - pub fn get_account_tx_context(&self) -> AccountTransactionContext { - // TODO(Nir, 01/11/2023): Consider to move this (from all get_account_tx_context methods). - let common_fields = CommonAccountFields { - transaction_hash: self.tx_hash(), - version: self.tx.version(), - signature: self.tx.signature(), - nonce: self.tx.nonce(), - sender_address: self.tx.sender_address(), - only_query: self.only_query, - }; - - match &self.tx { - starknet_api::transaction::DeclareTransaction::V0(tx) - | starknet_api::transaction::DeclareTransaction::V1(tx) => { - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::DeclareTransaction::V2(tx) => { - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::DeclareTransaction::V3(tx) => { - AccountTransactionContext::Current(CurrentAccountTransactionContext { - common_fields, - resource_bounds: tx.resource_bounds.clone(), - tip: tx.tip, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - fee_data_availability_mode: tx.fee_data_availability_mode, - paymaster_data: tx.paymaster_data.clone(), - account_deployment_data: tx.account_deployment_data.clone(), - }) - } - } - } - pub fn only_query(&self) -> bool { self.only_query } @@ -241,6 +202,46 @@ impl Executable for DeclareTransaction { } } +impl TransactionInfoCreator for DeclareTransaction { + fn create_tx_info(&self) -> TransactionInfo { + // TODO(Nir, 01/11/2023): Consider to move this (from all get_tx_info methods). + let common_fields = CommonAccountFields { + transaction_hash: self.tx_hash(), + version: self.tx.version(), + signature: self.tx.signature(), + nonce: self.tx.nonce(), + sender_address: self.tx.sender_address(), + only_query: self.only_query, + }; + + match &self.tx { + starknet_api::transaction::DeclareTransaction::V0(tx) + | starknet_api::transaction::DeclareTransaction::V1(tx) => { + TransactionInfo::Deprecated(DeprecatedTransactionInfo { + common_fields, + max_fee: tx.max_fee, + }) + } + starknet_api::transaction::DeclareTransaction::V2(tx) => { + TransactionInfo::Deprecated(DeprecatedTransactionInfo { + common_fields, + max_fee: tx.max_fee, + }) + } + starknet_api::transaction::DeclareTransaction::V3(tx) => { + TransactionInfo::Current(CurrentTransactionInfo { + common_fields, + resource_bounds: tx.resource_bounds.clone(), + tip: tx.tip, + nonce_data_availability_mode: tx.nonce_data_availability_mode, + fee_data_availability_mode: tx.fee_data_availability_mode, + paymaster_data: tx.paymaster_data.clone(), + account_deployment_data: tx.account_deployment_data.clone(), + }) + } + } + } +} #[derive(Debug, Clone)] pub struct DeployAccountTransaction { pub tx: starknet_api::transaction::DeployAccountTransaction, @@ -278,37 +279,6 @@ impl DeployAccountTransaction { pub fn tx(&self) -> &starknet_api::transaction::DeployAccountTransaction { &self.tx } - - pub fn get_account_tx_context(&self) -> AccountTransactionContext { - let common_fields = CommonAccountFields { - transaction_hash: self.tx_hash, - version: self.tx.version(), - signature: self.tx.signature(), - nonce: self.tx.nonce(), - sender_address: self.contract_address, - only_query: self.only_query, - }; - - match &self.tx { - starknet_api::transaction::DeployAccountTransaction::V1(tx) => { - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::DeployAccountTransaction::V3(tx) => { - AccountTransactionContext::Current(CurrentAccountTransactionContext { - common_fields, - resource_bounds: tx.resource_bounds.clone(), - tip: tx.tip, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - fee_data_availability_mode: tx.fee_data_availability_mode, - paymaster_data: tx.paymaster_data.clone(), - account_deployment_data: AccountDeploymentData::default(), - }) - } - } - } } impl Executable for DeployAccountTransaction { @@ -341,6 +311,39 @@ impl Executable for DeployAccountTransaction { } } +impl TransactionInfoCreator for DeployAccountTransaction { + fn create_tx_info(&self) -> TransactionInfo { + let common_fields = CommonAccountFields { + transaction_hash: self.tx_hash, + version: self.tx.version(), + signature: self.tx.signature(), + nonce: self.tx.nonce(), + sender_address: self.contract_address, + only_query: self.only_query, + }; + + match &self.tx { + starknet_api::transaction::DeployAccountTransaction::V1(tx) => { + TransactionInfo::Deprecated(DeprecatedTransactionInfo { + common_fields, + max_fee: tx.max_fee, + }) + } + starknet_api::transaction::DeployAccountTransaction::V3(tx) => { + TransactionInfo::Current(CurrentTransactionInfo { + common_fields, + resource_bounds: tx.resource_bounds.clone(), + tip: tx.tip, + nonce_data_availability_mode: tx.nonce_data_availability_mode, + fee_data_availability_mode: tx.fee_data_availability_mode, + paymaster_data: tx.paymaster_data.clone(), + account_deployment_data: AccountDeploymentData::default(), + }) + } + } + } +} + #[derive(Debug, Clone)] pub struct InvokeTransaction { pub tx: starknet_api::transaction::InvokeTransaction, @@ -369,43 +372,6 @@ impl InvokeTransaction { (signature, TransactionSignature), (sender_address, ContractAddress) ); - - pub fn get_account_tx_context(&self) -> AccountTransactionContext { - let common_fields = CommonAccountFields { - transaction_hash: self.tx_hash, - version: self.tx.version(), - signature: self.tx.signature(), - nonce: self.tx.nonce(), - sender_address: self.tx.sender_address(), - only_query: self.only_query, - }; - - match &self.tx { - starknet_api::transaction::InvokeTransaction::V0(tx) => { - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::InvokeTransaction::V1(tx) => { - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext { - common_fields, - max_fee: tx.max_fee, - }) - } - starknet_api::transaction::InvokeTransaction::V3(tx) => { - AccountTransactionContext::Current(CurrentAccountTransactionContext { - common_fields, - resource_bounds: tx.resource_bounds.clone(), - tip: tx.tip, - nonce_data_availability_mode: tx.nonce_data_availability_mode, - fee_data_availability_mode: tx.fee_data_availability_mode, - paymaster_data: tx.paymaster_data.clone(), - account_deployment_data: tx.account_deployment_data.clone(), - }) - } - } - } } impl Executable for InvokeTransaction { @@ -423,7 +389,7 @@ impl Executable for InvokeTransaction { selector_from_name(constants::EXECUTE_ENTRY_POINT_NAME) } }; - let storage_address = context.account_tx_context.sender_address(); + let storage_address = context.tx_context.tx_info.sender_address(); let execute_call = CallEntryPoint { entry_point_type: EntryPointType::External, entry_point_selector, @@ -445,6 +411,45 @@ impl Executable for InvokeTransaction { } } +impl TransactionInfoCreator for InvokeTransaction { + fn create_tx_info(&self) -> TransactionInfo { + let common_fields = CommonAccountFields { + transaction_hash: self.tx_hash, + version: self.tx.version(), + signature: self.tx.signature(), + nonce: self.tx.nonce(), + sender_address: self.tx.sender_address(), + only_query: self.only_query, + }; + + match &self.tx { + starknet_api::transaction::InvokeTransaction::V0(tx) => { + TransactionInfo::Deprecated(DeprecatedTransactionInfo { + common_fields, + max_fee: tx.max_fee, + }) + } + starknet_api::transaction::InvokeTransaction::V1(tx) => { + TransactionInfo::Deprecated(DeprecatedTransactionInfo { + common_fields, + max_fee: tx.max_fee, + }) + } + starknet_api::transaction::InvokeTransaction::V3(tx) => { + TransactionInfo::Current(CurrentTransactionInfo { + common_fields, + resource_bounds: tx.resource_bounds.clone(), + tip: tx.tip, + nonce_data_availability_mode: tx.nonce_data_availability_mode, + fee_data_availability_mode: tx.fee_data_availability_mode, + paymaster_data: tx.paymaster_data.clone(), + account_deployment_data: tx.account_deployment_data.clone(), + }) + } + } + } +} + #[derive(Debug)] pub struct L1HandlerTransaction { pub tx: starknet_api::transaction::L1HandlerTransaction, @@ -453,20 +458,6 @@ pub struct L1HandlerTransaction { } impl L1HandlerTransaction { - pub fn get_account_tx_context(&self) -> AccountTransactionContext { - AccountTransactionContext::Deprecated(DeprecatedAccountTransactionContext { - common_fields: CommonAccountFields { - transaction_hash: self.tx_hash, - version: self.tx.version, - signature: TransactionSignature::default(), - nonce: self.tx.nonce, - sender_address: self.tx.contract_address, - only_query: false, - }, - max_fee: Fee::default(), - }) - } - pub fn payload_size(&self) -> usize { // The calldata includes the "from" field, which is not a part of the payload. self.tx.calldata.0.len() - 1 @@ -511,3 +502,19 @@ impl Executable for L1HandlerTransaction { .map_err(TransactionExecutionError::ExecutionError) } } + +impl TransactionInfoCreator for L1HandlerTransaction { + fn create_tx_info(&self) -> TransactionInfo { + TransactionInfo::Deprecated(DeprecatedTransactionInfo { + common_fields: CommonAccountFields { + transaction_hash: self.tx_hash, + version: self.tx.version, + signature: TransactionSignature::default(), + nonce: self.tx.nonce, + sender_address: self.tx.contract_address, + only_query: false, + }, + max_fee: Fee::default(), + }) + } +} diff --git a/crates/blockifier/src/transaction/transactions_test.rs b/crates/blockifier/src/transaction/transactions_test.rs index 0544df6d48..414e6e88d4 100644 --- a/crates/blockifier/src/transaction/transactions_test.rs +++ b/crates/blockifier/src/transaction/transactions_test.rs @@ -26,7 +26,7 @@ use crate::abi::abi_utils::{ }; use crate::abi::constants as abi_constants; use crate::abi::sierra_types::next_storage_key; -use crate::context::{BlockContext, ChainInfo, FeeTokenAddresses}; +use crate::context::{BlockContext, ChainInfo, FeeTokenAddresses, TransactionContext}; use crate::execution::call_info::{ CallExecution, CallInfo, MessageToL1, OrderedEvent, OrderedL2ToL1Message, Retdata, }; @@ -60,8 +60,8 @@ use crate::transaction::errors::{ TransactionExecutionError, TransactionFeeError, TransactionPreValidationError, }; use crate::transaction::objects::{ - AccountTransactionContext, FeeType, GasVector, HasRelatedFeeType, ResourcesMapping, - TransactionExecutionInfo, + FeeType, GasVector, HasRelatedFeeType, ResourcesMapping, TransactionExecutionInfo, + TransactionInfo, }; use crate::transaction::test_utils::{ account_invoke_tx, create_account_tx_for_validate_test, l1_resource_bounds, @@ -154,12 +154,13 @@ fn expected_validate_call_info( } fn expected_fee_transfer_call_info( - block_context: &BlockContext, + tx_context: &TransactionContext, account_address: ContractAddress, actual_fee: Fee, - fee_type: &FeeType, expected_fee_token_class_hash: ClassHash, ) -> Option { + let block_context = &tx_context.block_context; + let fee_type = &tx_context.tx_info.fee_type(); let expected_sequencer_address = block_context.block_info.sequencer_address; let expected_sequencer_address_felt = *expected_sequencer_address.0.key(); // The least significant 128 bits of the expected amount transferred. @@ -344,7 +345,8 @@ fn test_invoke_tx( let sender_address = invoke_tx.sender_address(); let account_tx = AccountTransaction::Invoke(invoke_tx); - let fee_type = &account_tx.fee_type(); + let tx_context = block_context.to_tx_context(&account_tx); + let actual_execution_info = account_tx.execute(state, block_context, true, true).unwrap(); // Build expected validate call info. @@ -399,13 +401,13 @@ fn test_invoke_tx( }); // Build expected fee transfer call info. + let fee_type = &tx_context.tx_info.fee_type(); let expected_actual_fee = calculate_tx_fee(&actual_execution_info.actual_resources, block_context, fee_type).unwrap(); let expected_fee_transfer_call_info = expected_fee_transfer_call_info( - block_context, + &tx_context, sender_address, expected_actual_fee, - fee_type, FeatureContract::ERC20.get_class_hash(), ); @@ -709,8 +711,8 @@ fn assert_failure_if_resource_bounds_exceed_balance( block_context: &BlockContext, invalid_tx: AccountTransaction, ) { - match invalid_tx.get_account_tx_context() { - AccountTransactionContext::Deprecated(context) => { + match block_context.to_tx_context(&invalid_tx).tx_info { + TransactionInfo::Deprecated(context) => { assert_matches!( invalid_tx.execute(state, block_context, true, true).unwrap_err(), TransactionExecutionError::TransactionPreValidationError( @@ -719,7 +721,7 @@ fn assert_failure_if_resource_bounds_exceed_balance( if max_fee == context.max_fee ); } - AccountTransactionContext::Current(context) => { + TransactionInfo::Current(context) => { let l1_bounds = context.l1_resource_bounds().unwrap(); assert_matches!( invalid_tx.execute(state, block_context, true, true).unwrap_err(), @@ -810,12 +812,8 @@ fn test_insufficient_resource_bounds(account_cairo_version: CairoVersion) { ); // The minimal gas estimate does not depend on tx version. - let minimal_l1_gas = estimate_minimal_gas_vector( - block_context, - &account_invoke_tx(valid_invoke_tx_args.clone()), - ) - .unwrap() - .l1_gas; + let tx = &account_invoke_tx(valid_invoke_tx_args.clone()); + let minimal_l1_gas = estimate_minimal_gas_vector(block_context, tx).unwrap().l1_gas; // Test V1 transaction. @@ -901,10 +899,8 @@ fn test_actual_fee_gt_resource_bounds(account_cairo_version: CairoVersion) { test_contract.get_instance_address(0), ); - let minimal_l1_gas = - estimate_minimal_gas_vector(block_context, &account_invoke_tx(invoke_tx_args.clone())) - .unwrap() - .l1_gas; + let tx = &account_invoke_tx(invoke_tx_args.clone()); + let minimal_l1_gas = estimate_minimal_gas_vector(block_context, tx).unwrap().l1_gas; let minimal_fee = Fee(minimal_l1_gas * block_context.block_info.gas_prices.eth_l1_gas_price); // The estimated minimal fee is lower than the actual fee. let invalid_tx = account_invoke_tx(invoke_tx_args! { max_fee: minimal_fee, ..invoke_tx_args }); @@ -938,15 +934,9 @@ fn test_invalid_nonce(account_cairo_version: CairoVersion) { let invalid_nonce = Nonce(stark_felt!(1_u8)); let invalid_tx = account_invoke_tx(invoke_tx_args! { nonce: invalid_nonce, ..valid_invoke_tx_args.clone() }); - let invalid_tx_context = invalid_tx.get_account_tx_context(); + let invalid_tx_context = block_context.to_tx_context(&invalid_tx); let pre_validation_err = invalid_tx - .perform_pre_validation_stage( - &mut transactional_state, - &invalid_tx_context, - block_context, - false, - true, - ) + .perform_pre_validation_stage(&mut transactional_state, &invalid_tx_context, false, true) .unwrap_err(); // Test error. @@ -963,29 +953,19 @@ fn test_invalid_nonce(account_cairo_version: CairoVersion) { let valid_nonce = Nonce(stark_felt!(1_u8)); let valid_tx = account_invoke_tx(invoke_tx_args! { nonce: valid_nonce, ..valid_invoke_tx_args.clone() }); - let valid_tx_context = valid_tx.get_account_tx_context(); + + let valid_tx_context = block_context.to_tx_context(&valid_tx); valid_tx - .perform_pre_validation_stage( - &mut transactional_state, - &valid_tx_context, - block_context, - false, - false, - ) + .perform_pre_validation_stage(&mut transactional_state, &valid_tx_context, false, false) .unwrap(); // Negative flow: account nonce = 1, incoming tx nonce = 0. let invalid_nonce = Nonce(stark_felt!(0_u8)); let invalid_tx = account_invoke_tx(invoke_tx_args! { nonce: invalid_nonce, ..valid_invoke_tx_args.clone() }); + let invalid_tx_context = block_context.to_tx_context(&invalid_tx); let pre_validation_err = invalid_tx - .perform_pre_validation_stage( - &mut transactional_state, - &invalid_tx.get_account_tx_context(), - block_context, - false, - false, - ) + .perform_pre_validation_stage(&mut transactional_state, &invalid_tx_context, false, false) .unwrap_err(); // Test error. @@ -1111,6 +1091,7 @@ fn test_declare_tx( undeclared_class_hash == class_hash ); let fee_type = &account_tx.fee_type(); + let tx_context = &block_context.to_tx_context(&account_tx); let actual_execution_info = account_tx.execute(state, block_context, true, true).unwrap(); // Build expected validate call info. @@ -1126,10 +1107,9 @@ fn test_declare_tx( let expected_actual_fee = calculate_tx_fee(&actual_execution_info.actual_resources, block_context, fee_type).unwrap(); let expected_fee_transfer_call_info = expected_fee_transfer_call_info( - block_context, + tx_context, sender_address, expected_actual_fee, - fee_type, FeatureContract::ERC20.get_class_hash(), ); @@ -1227,6 +1207,7 @@ fn test_deploy_account_tx( let account_tx = AccountTransaction::DeployAccount(deploy_account); let fee_type = &account_tx.fee_type(); + let tx_context = &block_context.to_tx_context(&account_tx); let actual_execution_info = account_tx.execute(state, block_context, true, true).unwrap(); // Build expected validate call info. @@ -1260,10 +1241,9 @@ fn test_deploy_account_tx( let expected_actual_fee = calculate_tx_fee(&actual_execution_info.actual_resources, block_context, fee_type).unwrap(); let expected_fee_transfer_call_info = expected_fee_transfer_call_info( - block_context, + tx_context, deployed_account_address, expected_actual_fee, - fee_type, FeatureContract::ERC20.get_class_hash(), ); diff --git a/crates/native_blockifier/src/py_validator.rs b/crates/native_blockifier/src/py_validator.rs index 54ca0cc4d7..ce54072251 100644 --- a/crates/native_blockifier/src/py_validator.rs +++ b/crates/native_blockifier/src/py_validator.rs @@ -1,10 +1,11 @@ +use blockifier::context::TransactionContext; use blockifier::execution::call_info::CallInfo; use blockifier::fee::actual_cost::ActualCost; use blockifier::fee::fee_checks::PostValidationReport; use blockifier::state::cached_state::{GlobalContractCache, GLOBAL_CONTRACT_CACHE_SIZE_FOR_TEST}; use blockifier::state::state_api::StateReader; use blockifier::transaction::account_transaction::AccountTransaction; -use blockifier::transaction::objects::{AccountTransactionContext, TransactionExecutionResult}; +use blockifier::transaction::objects::{TransactionExecutionResult, TransactionInfo}; use blockifier::transaction::transaction_execution::Transaction; use blockifier::versioned_constants::VersionedConstants; use pyo3::prelude::*; @@ -67,7 +68,7 @@ impl PyValidator { deploy_account_tx_hash: Option, ) -> NativeBlockifierResult<()> { let account_tx = py_account_tx(tx, raw_contract_class)?; - let account_tx_context = account_tx.get_account_tx_context(); + let tx_context = self.tx_executor.block_context.to_tx_context(&account_tx); // Deploy account transactions should be fully executed, since the constructor must run // before `__validate_deploy__`. The execution already includes all necessary validations, // so they are skipped here. @@ -83,10 +84,10 @@ impl PyValidator { // processed. It is done before the pre-validations checks because, in these checks, we // change the state (more precisely, we increment the nonce). let skip_validate = self.skip_validate_due_to_unprocessed_deploy_account( - &account_tx_context, + &tx_context.tx_info, deploy_account_tx_hash, )?; - self.perform_pre_validation_stage(&account_tx)?; + self.perform_pre_validation_stage(&account_tx, &tx_context)?; if skip_validate { return Ok(()); @@ -98,7 +99,7 @@ impl PyValidator { // Post validations. // TODO(Ayelet, 09/11/2023): Check call succeeded. - self.perform_post_validation_stage(&account_tx_context, &actual_cost)?; + self.perform_post_validation_stage(&tx_context, &actual_cost)?; Ok(()) } @@ -137,16 +138,14 @@ impl PyValidator { fn perform_pre_validation_stage( &mut self, account_tx: &AccountTransaction, + tx_context: &TransactionContext, ) -> NativeBlockifierResult<()> { - let account_tx_context = account_tx.get_account_tx_context(); - let strict_nonce_check = false; // Run pre-validation in charge fee mode to perform fee and balance related checks. let charge_fee = true; account_tx.perform_pre_validation_stage( &mut self.tx_executor.state, - &account_tx_context, - &self.tx_executor.block_context, + tx_context, charge_fee, strict_nonce_check, )?; @@ -159,11 +158,11 @@ impl PyValidator { // (they will otherwise fail solely because the deploy account hasn't been processed yet). fn skip_validate_due_to_unprocessed_deploy_account( &mut self, - account_tx_context: &AccountTransactionContext, + tx_info: &TransactionInfo, deploy_account_tx_hash: Option, ) -> NativeBlockifierResult { - let nonce = self.tx_executor.state.get_nonce_at(account_tx_context.sender_address())?; - let tx_nonce = account_tx_context.nonce(); + let nonce = self.tx_executor.state.get_nonce_at(tx_info.sender_address())?; + let tx_nonce = tx_info.nonce(); let deploy_account_not_processed = deploy_account_tx_hash.is_some() && nonce == Nonce(StarkFelt::ZERO); @@ -191,13 +190,9 @@ impl PyValidator { fn perform_post_validation_stage( &mut self, - account_tx_context: &AccountTransactionContext, + tx_context: &TransactionContext, actual_cost: &ActualCost, ) -> TransactionExecutionResult<()> { - PostValidationReport::verify( - &self.tx_executor.block_context, - account_tx_context, - actual_cost, - ) + PostValidationReport::verify(tx_context, actual_cost) } } diff --git a/crates/native_blockifier/src/transaction_executor.rs b/crates/native_blockifier/src/transaction_executor.rs index 2bf8c29cc4..2b1a03a84c 100644 --- a/crates/native_blockifier/src/transaction_executor.rs +++ b/crates/native_blockifier/src/transaction_executor.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet}; +use std::sync::Arc; use std::vec::IntoIter; use blockifier::block::{pre_process_block, BlockNumberHashPair}; @@ -39,6 +40,7 @@ pub(crate) struct TypedTransactionExecutionInfo { tx_type: String, } +// TODO(Gilad): make this hold TransactionContext instead of BlockContext. pub struct TransactionExecutor { pub block_context: BlockContext, @@ -166,26 +168,26 @@ impl TransactionExecutor { mut remaining_gas: u64, ) -> NativeBlockifierResult<(Option, ActualCost)> { let mut execution_resources = ExecutionResources::default(); - let account_tx_context = account_tx.get_account_tx_context(); + let tx_context = Arc::new(self.block_context.to_tx_context(account_tx)); + let tx_info = &tx_context.tx_info; // TODO(Amos, 01/12/2023): Delete this once deprecated txs call // PyValidator.perform_validations(). // For fee charging purposes, the nonce-increment cost is taken into consideration when // calculating the fees for validation. // Note: This assumes that the state is reset between calls to validate. - self.state.increment_nonce(account_tx_context.sender_address())?; + self.state.increment_nonce(tx_info.sender_address())?; let validate_call_info = account_tx.validate_tx( &mut self.state, &mut execution_resources, - &account_tx_context, + tx_context.clone(), &mut remaining_gas, - &self.block_context, true, )?; let actual_cost = account_tx - .to_actual_cost_builder(&self.block_context) + .to_actual_cost_builder(tx_context) .with_validate_call_info(&validate_call_info) .try_add_state_changes(&mut self.state)? .build(&execution_resources)?;