From 67fb1599686ca145cfa792e08df11a3ff2e814c3 Mon Sep 17 00:00:00 2001 From: Yael Doweck Date: Thu, 7 Mar 2024 11:18:50 +0200 Subject: [PATCH] refactor(execution): bouncer update function --- crates/blockifier/src/bouncer.rs | 114 +++++++++++++++++--- crates/blockifier/src/bouncer_test.rs | 8 +- crates/blockifier/src/transaction/errors.rs | 4 + 3 files changed, 110 insertions(+), 16 deletions(-) diff --git a/crates/blockifier/src/bouncer.rs b/crates/blockifier/src/bouncer.rs index deae773b73..3f51b6c50a 100644 --- a/crates/blockifier/src/bouncer.rs +++ b/crates/blockifier/src/bouncer.rs @@ -15,7 +15,8 @@ use crate::fee::gas_usage::{ }; use crate::state::cached_state::{StateChangesKeys, StorageEntry, TransactionalState}; use crate::state::state_api::StateReader; -use crate::transaction::objects::ResourcesMapping; +use crate::transaction::errors::TransactionExecutionError; +use crate::transaction::objects::{ResourcesMapping, TransactionExecutionInfo}; #[cfg(test)] #[path = "bouncer_test.rs"] @@ -37,6 +38,11 @@ macro_rules! impl_checked_sub { pub type HashMapWrapper = HashMap; +pub struct BouncerConfig { + pub block_max_capacity: BouncerWeights, + pub block_max_capacity_with_keccak: BouncerWeights, +} + #[derive( Clone, Copy, Debug, Default, derive_more::Add, derive_more::Sub, Deserialize, PartialEq, )] @@ -115,17 +121,19 @@ pub struct Bouncer { pub executed_class_hashes: HashSet, pub visited_storage_entries: HashSet, pub state_changes_keys: StateChangesKeys, + pub block_contains_keccak: bool, // The capacity is calculated based of the values of the other Bouncer fields. - capacity: BouncerWeights, + available_capacity: BouncerWeights, } impl Bouncer { - pub fn new(capacity: BouncerWeights) -> Self { + pub fn new(capacity: BouncerWeights, block_contains_keccak: bool) -> Self { Bouncer { executed_class_hashes: HashSet::new(), state_changes_keys: StateChangesKeys::default(), visited_storage_entries: HashSet::new(), - capacity, + available_capacity: capacity, + block_contains_keccak, } } @@ -137,25 +145,107 @@ impl Bouncer { self.executed_class_hashes.extend(other.executed_class_hashes); self.state_changes_keys.extend(&other.state_changes_keys); self.visited_storage_entries.extend(other.visited_storage_entries); - self.capacity = other.capacity; + self.block_contains_keccak = other.block_contains_keccak; + self.available_capacity = other.available_capacity; } } #[derive(Clone)] pub struct TransactionalBouncer { - // The bouncer can be modified only through the merge method. + // The block bouncer can be modified only through the merge method. bouncer: Bouncer, - // The transactional bouncer can be modified only through the update method. + // The transaction bouncer can be modified only through the update method. transactional: Bouncer, } impl TransactionalBouncer { - pub fn new(parent: Bouncer) -> TransactionalBouncer { - let capacity = parent.capacity; - TransactionalBouncer { bouncer: parent, transactional: Bouncer::new(capacity) } + pub fn new(block_bouncer: Bouncer) -> TransactionalBouncer { + let transactional = + Bouncer::new(block_bouncer.available_capacity, block_bouncer.block_contains_keccak); + TransactionalBouncer { bouncer: block_bouncer, transactional } + } + + pub fn update( + &mut self, + bouncer_config: &BouncerConfig, + state: &mut TransactionalState<'_, S>, + tx_execution_info: &TransactionExecutionInfo, + l1_handler_payload_size: Option, + ) -> TransactionExecutorResult<()> { + let tx_execution_summary = tx_execution_info.summarize(); + self.update_auxiliary_info(state, &tx_execution_summary)?; + self.update_capacity( + bouncer_config, + state, + &tx_execution_summary, + &tx_execution_info.bouncer_resources, + l1_handler_payload_size, + )?; + + Ok(()) + } + + fn update_capacity( + &mut self, + bouncer_config: &BouncerConfig, + state: &mut TransactionalState<'_, S>, + tx_execution_summary: &ExecutionSummary, + bouncer_resources: &ResourcesMapping, + l1_handler_payload_size: Option, + ) -> TransactionExecutorResult<()> { + let tx_weights = self.get_tx_weights( + state, + tx_execution_summary, + bouncer_resources, + l1_handler_payload_size, + )?; + + if !self.transactional.block_contains_keccak && tx_weights.builtin_count.keccak > 0 { + self.update_available_capacity_with_keccak(bouncer_config)?; + } + + // Check if the transaction is too large to fit any block. + let mut max_capacity = bouncer_config.block_max_capacity; + if self.transactional.block_contains_keccak { + max_capacity = bouncer_config.block_max_capacity_with_keccak; + } + max_capacity.checked_sub(tx_weights).ok_or(TransactionExecutionError::TxTooLarge)?; + + // Check if the transaction can fit the current block available capacity. + self.transactional.available_capacity = self + .transactional + .available_capacity + .checked_sub(tx_weights) + .ok_or(TransactionExecutionError::BlockFull)?; + Ok(()) } - // TODO update function (in the next PRs) + fn update_available_capacity_with_keccak( + &mut self, + bouncer_config: &BouncerConfig, + ) -> TransactionExecutorResult<()> { + // First zero the keccak capacity to be able to subtract the max_capacity_with_keccak from + // max_capacity (that is without keccak). + let mut max_capacity_with_keccak_tmp = bouncer_config.block_max_capacity_with_keccak; + max_capacity_with_keccak_tmp.builtin_count.keccak = 0; + // compute the diff between the max_capacity and the max_capacity_with_keccak. + let max_capacity_with_keccak_diff = bouncer_config + .block_max_capacity + .checked_sub(max_capacity_with_keccak_tmp) + .expect("max_capacity_with_keccak should be smaller than max_capacity"); + // Subtract the diff from the available capacity. + self.transactional.available_capacity = self + .transactional + .available_capacity + .checked_sub(max_capacity_with_keccak_diff) + .ok_or(TransactionExecutionError::BlockFull)?; + // Add back the keccack capacity that was reset at the beggining. + self.transactional.available_capacity.builtin_count.keccak = + bouncer_config.block_max_capacity_with_keccak.builtin_count.keccak; + // Mark this block as contains keccak. + self.transactional.block_contains_keccak = true; + Ok(()) + } pub fn get_tx_weights( &mut self, @@ -225,8 +315,8 @@ impl TransactionalBouncer { pub fn update_auxiliary_info( &mut self, - tx_execution_summary: &ExecutionSummary, state: &mut TransactionalState<'_, S>, + tx_execution_summary: &ExecutionSummary, ) -> TransactionExecutorResult<()> { self.transactional .executed_class_hashes diff --git a/crates/blockifier/src/bouncer_test.rs b/crates/blockifier/src/bouncer_test.rs index 6cfa40e626..52f7995a5d 100644 --- a/crates/blockifier/src/bouncer_test.rs +++ b/crates/blockifier/src/bouncer_test.rs @@ -99,15 +99,15 @@ fn test_transactional_bouncer() { state_diff_size: 2, }; - let bouncer = Bouncer::new(initial_bouncer_weights); + let bouncer = Bouncer::new(initial_bouncer_weights, true); let mut transactional_bouncer = bouncer.create_transactional(); - transactional_bouncer.transactional.capacity = weights_to_commit; + transactional_bouncer.transactional.available_capacity = weights_to_commit; // Test transactional bouncer abort. let final_weights = transactional_bouncer.clone().abort(); - assert!(final_weights.capacity == initial_bouncer_weights); + assert!(final_weights.available_capacity == initial_bouncer_weights); // Test transactional bouncer commit. let final_weights = transactional_bouncer.commit(); - assert!(final_weights.capacity == weights_to_commit); + assert!(final_weights.available_capacity == weights_to_commit); } diff --git a/crates/blockifier/src/transaction/errors.rs b/crates/blockifier/src/transaction/errors.rs index 83ee24aa13..c84005f579 100644 --- a/crates/blockifier/src/transaction/errors.rs +++ b/crates/blockifier/src/transaction/errors.rs @@ -51,6 +51,8 @@ pub enum TransactionFeeError { #[derive(Debug, Error)] pub enum TransactionExecutionError { + #[error("Transaction cannot be added to the current block, block capacity reached.")] + BlockFull, #[error( "Declare transaction version {declare_version:?} must have a contract class of Cairo \ version {cairo_version:?}." @@ -85,6 +87,8 @@ pub enum TransactionExecutionError { TransactionPreValidationError(#[from] TransactionPreValidationError), #[error(transparent)] TryFromIntError(#[from] std::num::TryFromIntError), + #[error("Transaction size exceeds the maximum block capacity.")] + TxTooLarge, // TODO(Zuphit): add `gen_transaction_execution_error_trace` if needed. #[error("Transaction validation has failed: {error}")] ValidateTransactionError { error: EntryPointExecutionError, storage_address: ContractAddress },