diff --git a/crates/blockifier/src/blockifier/block.rs b/crates/blockifier/src/blockifier/block.rs index 2d91d4f858..d3bd40932d 100644 --- a/crates/blockifier/src/blockifier/block.rs +++ b/crates/blockifier/src/blockifier/block.rs @@ -6,12 +6,9 @@ use starknet_api::hash::StarkFelt; use starknet_api::state::StorageKey; use crate::abi::constants; -use crate::bouncer::BouncerConfig; -use crate::context::{BlockContext, ChainInfo}; use crate::state::errors::StateError; use crate::state::state_api::{State, StateResult}; use crate::transaction::objects::FeeType; -use crate::versioned_constants::VersionedConstants; #[cfg(test)] #[path = "block_test.rs"] @@ -60,14 +57,10 @@ impl GasPrices { pub fn pre_process_block( state: &mut dyn State, old_block_number_and_hash: Option, - block_info: BlockInfo, - chain_info: ChainInfo, - versioned_constants: VersionedConstants, - bouncer_config: BouncerConfig, - concurrency_mode: bool, -) -> StateResult { + next_block_number: BlockNumber, +) -> StateResult<()> { let should_block_hash_be_provided = - block_info.block_number >= BlockNumber(constants::STORED_BLOCK_HASH_BUFFER); + next_block_number >= BlockNumber(constants::STORED_BLOCK_HASH_BUFFER); if let Some(BlockNumberHashPair { number: block_number, hash: block_hash }) = old_block_number_and_hash { @@ -83,13 +76,7 @@ pub fn pre_process_block( return Err(StateError::OldBlockHashNotProvided); } - Ok(BlockContext { - block_info, - chain_info, - versioned_constants, - bouncer_config, - concurrency_mode, - }) + Ok(()) } pub struct BlockNumberHashPair { diff --git a/crates/blockifier/src/blockifier/block_test.rs b/crates/blockifier/src/blockifier/block_test.rs index 9dc41dd531..4b36ac9b00 100644 --- a/crates/blockifier/src/blockifier/block_test.rs +++ b/crates/blockifier/src/blockifier/block_test.rs @@ -4,71 +4,41 @@ use starknet_api::hash::StarkFelt; use starknet_api::state::StorageKey; use crate::abi::constants; -use crate::blockifier::block::{pre_process_block, BlockInfo, BlockNumberHashPair}; -use crate::bouncer::BouncerConfig; +use crate::blockifier::block::{pre_process_block, BlockNumberHashPair}; use crate::context::ChainInfo; use crate::state::state_api::StateReader; use crate::test_utils::contracts::FeatureContract; use crate::test_utils::initial_test_state::test_state; use crate::test_utils::{CairoVersion, BALANCE}; -use crate::versioned_constants::VersionedConstants; #[test] fn test_pre_process_block() { let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1); - let chain_info = &ChainInfo::create_for_testing(); - let mut state = test_state(chain_info, BALANCE, &[(test_contract, 1)]); + let mut state = test_state(&ChainInfo::create_for_testing(), BALANCE, &[(test_contract, 1)]); // Test the positive flow of pre_process_block inside the allowed block number interval - let block_number = constants::STORED_BLOCK_HASH_BUFFER; + let block_number = BlockNumber(constants::STORED_BLOCK_HASH_BUFFER); let block_hash = StarkFelt::from(20_u8); - let mut block_info = BlockInfo::create_for_testing(); - block_info.block_number = BlockNumber(block_number); pre_process_block( &mut state, - Some(BlockNumberHashPair::new(block_number, block_hash)), - block_info, - ChainInfo::default(), - VersionedConstants::default(), - BouncerConfig::max(), - false, + Some(BlockNumberHashPair::new(block_number.0, block_hash)), + block_number, ) .unwrap(); let written_hash = state.get_storage_at( ContractAddress::from(constants::BLOCK_HASH_CONTRACT_ADDRESS), - StorageKey::from(block_number), + StorageKey::from(block_number.0), ); assert_eq!(written_hash.unwrap(), block_hash); // Test that block pre-process with block hash None is successful only within the allowed // block number interval. - let mut block_info = BlockInfo::create_for_testing(); - block_info.block_number = BlockNumber(constants::STORED_BLOCK_HASH_BUFFER - 1); - assert!( - pre_process_block( - &mut state, - None, - block_info, - ChainInfo::default(), - VersionedConstants::default(), - BouncerConfig::max(), - false, - ) - .is_ok() - ); + let block_number = BlockNumber(constants::STORED_BLOCK_HASH_BUFFER - 1); + assert!(pre_process_block(&mut state, None, block_number).is_ok()); - let mut block_info = BlockInfo::create_for_testing(); - block_info.block_number = BlockNumber(constants::STORED_BLOCK_HASH_BUFFER); - let error = pre_process_block( - &mut state, - None, - block_info, - ChainInfo::default(), - VersionedConstants::default(), - BouncerConfig::max(), - false, - ); + let block_number = BlockNumber(constants::STORED_BLOCK_HASH_BUFFER); + let error = pre_process_block(&mut state, None, block_number); assert_eq!( format!( "A block hash must be provided for block number > {}.", diff --git a/crates/blockifier/src/context.rs b/crates/blockifier/src/context.rs index 994ab2120c..4c387f80e8 100644 --- a/crates/blockifier/src/context.rs +++ b/crates/blockifier/src/context.rs @@ -22,6 +22,7 @@ impl TransactionContext { #[derive(Clone, Debug)] pub struct BlockContext { + // TODO(Yoni, 1/10/2024): consider making these fields public. pub(crate) block_info: BlockInfo, pub(crate) chain_info: ChainInfo, pub(crate) versioned_constants: VersionedConstants, @@ -30,20 +31,19 @@ pub struct BlockContext { } impl BlockContext { - /// Note: Prefer using the recommended constructor methods as detailed in the struct - /// documentation. This method is intended for internal use and will be deprecated in future - /// versions. - pub fn new_unchecked( - block_info: &BlockInfo, - chain_info: &ChainInfo, - versioned_constants: &VersionedConstants, + pub fn new( + block_info: BlockInfo, + chain_info: ChainInfo, + versioned_constants: VersionedConstants, + bouncer_config: BouncerConfig, + concurrency_mode: bool, ) -> Self { BlockContext { - block_info: block_info.clone(), - chain_info: chain_info.clone(), - versioned_constants: versioned_constants.clone(), - bouncer_config: BouncerConfig::max(), - concurrency_mode: false, + block_info, + chain_info, + versioned_constants, + bouncer_config, + concurrency_mode, } } @@ -59,12 +59,6 @@ impl BlockContext { &self.versioned_constants } - pub fn concurrency_mode(&self) -> bool { - self.concurrency_mode - } -} - -impl BlockContext { pub fn to_tx_context( &self, tx_info_creator: &impl TransactionInfoCreator, diff --git a/crates/native_blockifier/src/py_block_executor.rs b/crates/native_blockifier/src/py_block_executor.rs index 45aae333e8..afba923d79 100644 --- a/crates/native_blockifier/src/py_block_executor.rs +++ b/crates/native_blockifier/src/py_block_executor.rs @@ -1,8 +1,6 @@ use std::collections::HashMap; -use blockifier::blockifier::block::{ - pre_process_block as pre_process_block_blockifier, BlockInfo, BlockNumberHashPair, GasPrices, -}; +use blockifier::blockifier::block::pre_process_block; use blockifier::blockifier::config::TransactionExecutorConfig; use blockifier::blockifier::transaction_executor::{TransactionExecutor, TransactionExecutorError}; use blockifier::bouncer::BouncerConfig; @@ -10,7 +8,6 @@ use blockifier::context::{BlockContext, ChainInfo, FeeTokenAddresses}; use blockifier::execution::call_info::CallInfo; use blockifier::state::cached_state::CachedState; use blockifier::state::global_cache::GlobalContractCache; -use blockifier::state::state_api::State; use blockifier::transaction::objects::{GasVector, ResourcesMapping, TransactionExecutionInfo}; use blockifier::transaction::transaction_execution::Transaction; use blockifier::versioned_constants::VersionedConstants; @@ -18,19 +15,16 @@ use pyo3::prelude::*; use pyo3::types::{PyBytes, PyList}; use pyo3::{FromPyObject, PyAny, Python}; use serde::Serialize; -use starknet_api::block::{BlockNumber, BlockTimestamp}; +use starknet_api::block::BlockNumber; use starknet_api::core::{ChainId, ContractAddress}; use starknet_api::hash::StarkFelt; use starknet_api::transaction::Fee; -use crate::errors::{ - InvalidNativeBlockifierInputError, NativeBlockifierError, NativeBlockifierInputError, - NativeBlockifierResult, -}; +use crate::errors::{NativeBlockifierError, NativeBlockifierResult}; use crate::py_objects::{PyBouncerConfig, PyConcurrencyConfig}; use crate::py_state_diff::{PyBlockInfo, PyStateDiff}; use crate::py_transaction::{get_py_tx_type, py_tx, PyClassInfo, PY_TX_PARSING_ERR}; -use crate::py_utils::{int_to_chain_id, PyFelt}; +use crate::py_utils::{int_to_chain_id, into_block_number_hash_pair, PyFelt}; use crate::state_readers::papyrus_state::PapyrusReader; use crate::storage::{PapyrusStorage, Storage, StorageConfig}; @@ -111,7 +105,7 @@ impl TypedTransactionExecutionInfo { pub struct PyBlockExecutor { pub bouncer_config: BouncerConfig, pub tx_executor_config: TransactionExecutorConfig, - pub general_config: PyGeneralConfig, + pub chain_info: ChainInfo, pub versioned_constants: VersionedConstants, pub tx_executor: Option>, /// `Send` trait is required for `pyclass` compatibility as Python objects must be threadsafe. @@ -134,7 +128,7 @@ impl PyBlockExecutor { ) -> Self { log::debug!("Initializing Block Executor..."); let storage = - PapyrusStorage::new(target_storage_config).expect("Failed to initialize storage"); + PapyrusStorage::new(target_storage_config).expect("Failed to initialize storage."); let versioned_constants = VersionedConstants::latest_constants_with_overrides( validate_max_n_steps, max_recursion_depth, @@ -146,7 +140,7 @@ impl PyBlockExecutor { tx_executor_config: TransactionExecutorConfig { concurrency_config: concurrency_config.into(), }, - general_config, + chain_info: general_config.starknet_os_config.into_chain_info(), versioned_constants, tx_executor: None, storage: Box::new(storage), @@ -163,16 +157,24 @@ impl PyBlockExecutor { next_block_info: PyBlockInfo, old_block_number_and_hash: Option<(u64, PyFelt)>, ) -> NativeBlockifierResult<()> { - let papyrus_reader = self.get_aligned_reader(next_block_info.block_number); - let mut state = CachedState::new(papyrus_reader); - let block_context = pre_process_block( - &mut state, - old_block_number_and_hash, - &self.general_config, - &next_block_info, - &self.versioned_constants, + // Create block context. + let block_context = BlockContext::new( + next_block_info.try_into()?, + self.chain_info.clone(), + self.versioned_constants.clone(), self.bouncer_config.clone(), self.tx_executor_config.concurrency_config.enabled, + ); + let next_block_number = block_context.block_info().block_number; + + // Create state reader. + let papyrus_reader = self.get_aligned_reader(next_block_number); + let mut state = CachedState::new(papyrus_reader); + + pre_process_block( + &mut state, + into_block_number_hash_pair(old_block_number_and_hash), + next_block_number, )?; let tx_executor = @@ -397,7 +399,7 @@ impl PyBlockExecutor { path, &general_config.starknet_os_config.chain_id, )), - general_config, + chain_info: general_config.starknet_os_config.into_chain_info(), versioned_constants: VersionedConstants::latest_constants().clone(), tx_executor: None, global_contract_cache: GlobalContractCache::new(GLOBAL_CONTRACT_CACHE_SIZE_FOR_TEST), @@ -410,12 +412,12 @@ impl PyBlockExecutor { self.tx_executor.as_mut().expect("Transaction executor should be initialized") } - fn get_aligned_reader(&self, next_block_number: u64) -> PapyrusReader { + fn get_aligned_reader(&self, next_block_number: BlockNumber) -> PapyrusReader { // Full-node storage must be aligned to the Python storage before initializing a reader. - self.storage.validate_aligned(next_block_number); + self.storage.validate_aligned(next_block_number.0); PapyrusReader::new( self.storage.reader().clone(), - BlockNumber(next_block_number), + next_block_number, self.global_contract_cache.clone(), ) } @@ -427,7 +429,7 @@ impl PyBlockExecutor { bouncer_config: BouncerConfig::max(), tx_executor_config: TransactionExecutorConfig::default(), storage: Box::new(storage), - general_config: PyGeneralConfig::default(), + chain_info: ChainInfo::default(), versioned_constants: VersionedConstants::latest_constants().clone(), tx_executor: None, global_contract_cache: GlobalContractCache::new(GLOBAL_CONTRACT_CACHE_SIZE_FOR_TEST), @@ -450,6 +452,12 @@ pub struct PyOsConfig { pub fee_token_address: PyFelt, } +impl PyOsConfig { + pub fn into_chain_info(self) -> ChainInfo { + ChainInfo::try_from(self).expect("Failed to convert chain info.") + } +} + impl TryFrom for ChainInfo { type Error = NativeBlockifierError; @@ -478,83 +486,6 @@ impl Default for PyOsConfig { } } -pub fn into_block_context_args( - general_config: &PyGeneralConfig, - block_info: &PyBlockInfo, -) -> NativeBlockifierResult<(BlockInfo, ChainInfo)> { - let chain_info: ChainInfo = general_config.starknet_os_config.clone().try_into()?; - let block_info = BlockInfo { - block_number: BlockNumber(block_info.block_number), - block_timestamp: BlockTimestamp(block_info.block_timestamp), - sequencer_address: ContractAddress::try_from(block_info.sequencer_address.0)?, - gas_prices: GasPrices { - eth_l1_gas_price: block_info.l1_gas_price.price_in_wei.try_into().map_err(|_| { - NativeBlockifierInputError::InvalidNativeBlockifierInputError( - InvalidNativeBlockifierInputError::InvalidGasPriceWei( - block_info.l1_gas_price.price_in_wei, - ), - ) - })?, - strk_l1_gas_price: block_info.l1_gas_price.price_in_fri.try_into().map_err(|_| { - NativeBlockifierInputError::InvalidNativeBlockifierInputError( - InvalidNativeBlockifierInputError::InvalidGasPriceFri( - block_info.l1_gas_price.price_in_fri, - ), - ) - })?, - eth_l1_data_gas_price: block_info.l1_data_gas_price.price_in_wei.try_into().map_err( - |_| { - NativeBlockifierInputError::InvalidNativeBlockifierInputError( - InvalidNativeBlockifierInputError::InvalidDataGasPriceWei( - block_info.l1_data_gas_price.price_in_wei, - ), - ) - }, - )?, - strk_l1_data_gas_price: block_info.l1_data_gas_price.price_in_fri.try_into().map_err( - |_| { - NativeBlockifierInputError::InvalidNativeBlockifierInputError( - InvalidNativeBlockifierInputError::InvalidDataGasPriceFri( - block_info.l1_data_gas_price.price_in_fri, - ), - ) - }, - )?, - }, - use_kzg_da: block_info.use_kzg_da, - }; - - Ok((block_info, chain_info)) -} - -// Executes block pre-processing; see `blockifier::blockifier::block::pre_process_block` -// documentation. -fn pre_process_block( - state: &mut dyn State, - old_block_number_and_hash: Option<(u64, PyFelt)>, - general_config: &PyGeneralConfig, - block_info: &PyBlockInfo, - versioned_constants: &VersionedConstants, - bouncer_config: BouncerConfig, - concurrency_mode: bool, -) -> NativeBlockifierResult { - let old_block_number_and_hash = old_block_number_and_hash - .map(|(block_number, block_hash)| BlockNumberHashPair::new(block_number, block_hash.0)); - - let (block_info, chain_info) = into_block_context_args(general_config, block_info)?; - let block_context = pre_process_block_blockifier( - state, - old_block_number_and_hash, - block_info, - chain_info, - versioned_constants.clone(), - bouncer_config, - concurrency_mode, - )?; - - Ok(block_context) -} - fn serialize_failure_reason(error: TransactionExecutorError) -> RawTransactionExecutionResult { // TODO(Yoni, 1/7/2024): re-consider this serialization. serde_json::to_vec(&format!("{}", error)).expect(RESULT_SERIALIZE_ERR) diff --git a/crates/native_blockifier/src/py_state_diff.rs b/crates/native_blockifier/src/py_state_diff.rs index e220c59892..5fc23409e8 100644 --- a/crates/native_blockifier/src/py_state_diff.rs +++ b/crates/native_blockifier/src/py_state_diff.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::convert::TryFrom; +use blockifier::blockifier::block::{BlockInfo, GasPrices}; use blockifier::state::cached_state::CommitmentStateDiff; use blockifier::test_utils::{ DEFAULT_ETH_L1_DATA_GAS_PRICE, DEFAULT_ETH_L1_GAS_PRICE, DEFAULT_STRK_L1_DATA_GAS_PRICE, @@ -8,10 +9,15 @@ use blockifier::test_utils::{ }; use indexmap::IndexMap; use pyo3::prelude::*; +use pyo3::FromPyObject; +use starknet_api::block::{BlockNumber, BlockTimestamp}; use starknet_api::core::{ClassHash, ContractAddress, Nonce}; use starknet_api::state::{StateDiff, StorageKey}; -use crate::errors::{NativeBlockifierError, NativeBlockifierResult}; +use crate::errors::{ + InvalidNativeBlockifierInputError, NativeBlockifierError, NativeBlockifierInputError, + NativeBlockifierResult, +}; use crate::py_utils::PyFelt; #[pyclass] @@ -153,3 +159,58 @@ impl Default for PyBlockInfo { } } } + +impl TryFrom for BlockInfo { + type Error = NativeBlockifierError; + + fn try_from(block_info: PyBlockInfo) -> Result { + Ok(Self { + block_number: BlockNumber(block_info.block_number), + block_timestamp: BlockTimestamp(block_info.block_timestamp), + sequencer_address: ContractAddress::try_from(block_info.sequencer_address.0)?, + gas_prices: GasPrices { + eth_l1_gas_price: block_info.l1_gas_price.price_in_wei.try_into().map_err( + |_| { + NativeBlockifierInputError::InvalidNativeBlockifierInputError( + InvalidNativeBlockifierInputError::InvalidGasPriceWei( + block_info.l1_gas_price.price_in_wei, + ), + ) + }, + )?, + strk_l1_gas_price: block_info.l1_gas_price.price_in_fri.try_into().map_err( + |_| { + NativeBlockifierInputError::InvalidNativeBlockifierInputError( + InvalidNativeBlockifierInputError::InvalidGasPriceFri( + block_info.l1_gas_price.price_in_fri, + ), + ) + }, + )?, + eth_l1_data_gas_price: block_info + .l1_data_gas_price + .price_in_wei + .try_into() + .map_err(|_| { + NativeBlockifierInputError::InvalidNativeBlockifierInputError( + InvalidNativeBlockifierInputError::InvalidDataGasPriceWei( + block_info.l1_data_gas_price.price_in_wei, + ), + ) + })?, + strk_l1_data_gas_price: block_info + .l1_data_gas_price + .price_in_fri + .try_into() + .map_err(|_| { + NativeBlockifierInputError::InvalidNativeBlockifierInputError( + InvalidNativeBlockifierInputError::InvalidDataGasPriceFri( + block_info.l1_data_gas_price.price_in_fri, + ), + ) + })?, + }, + use_kzg_da: block_info.use_kzg_da, + }) + } +} diff --git a/crates/native_blockifier/src/py_utils.rs b/crates/native_blockifier/src/py_utils.rs index e9ef4facae..1e258c71b7 100644 --- a/crates/native_blockifier/src/py_utils.rs +++ b/crates/native_blockifier/src/py_utils.rs @@ -1,5 +1,6 @@ use std::convert::TryFrom; +use blockifier::blockifier::block::BlockNumberHashPair; use num_bigint::BigUint; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; @@ -98,3 +99,10 @@ where { Ok(obj.getattr(attr)?.extract()?) } + +pub fn into_block_number_hash_pair( + old_block_number_and_hash: Option<(u64, PyFelt)>, +) -> Option { + old_block_number_and_hash + .map(|(block_number, block_hash)| BlockNumberHashPair::new(block_number, block_hash.0)) +} diff --git a/crates/native_blockifier/src/py_validator.rs b/crates/native_blockifier/src/py_validator.rs index e602532ee1..5e986d1280 100644 --- a/crates/native_blockifier/src/py_validator.rs +++ b/crates/native_blockifier/src/py_validator.rs @@ -1,4 +1,5 @@ use blockifier::blockifier::stateful_validator::StatefulValidator; +use blockifier::bouncer::BouncerConfig; use blockifier::context::BlockContext; use blockifier::state::cached_state::CachedState; use blockifier::versioned_constants::VersionedConstants; @@ -7,7 +8,7 @@ use starknet_api::core::Nonce; use starknet_api::transaction::TransactionHash; use crate::errors::NativeBlockifierResult; -use crate::py_block_executor::{into_block_context_args, PyGeneralConfig}; +use crate::py_block_executor::PyGeneralConfig; use crate::py_state_diff::PyBlockInfo; use crate::py_transaction::{py_account_tx, PyClassInfo, PY_TX_PARSING_ERR}; use crate::py_utils::PyFelt; @@ -35,13 +36,17 @@ impl PyValidator { let state = CachedState::new(state_reader); // Create the block context. - let (block_info, chain_info) = into_block_context_args(&general_config, &next_block_info)?; let versioned_constants = VersionedConstants::latest_constants_with_overrides( validate_max_n_steps, max_recursion_depth, ); - let block_context = - BlockContext::new_unchecked(&block_info, &chain_info, &versioned_constants); + let block_context = BlockContext::new( + next_block_info.try_into().expect("Failed to convert block info."), + general_config.starknet_os_config.into_chain_info(), + versioned_constants, + BouncerConfig::max(), + false, + ); // Create the stateful validator. let max_nonce_for_validation_skip = Nonce(max_nonce_for_validation_skip.0);