diff --git a/crates/blockifier/Cargo.toml b/crates/blockifier/Cargo.toml index e63f33baef..bd5cefe3bb 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -10,7 +10,7 @@ description = "The transaction-executing component in the Starknet sequencer." workspace = true [features] -testing = ["rstest"] +testing = ["rand", "rstest"] concurrency = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -39,6 +39,7 @@ num-traits.workspace = true num-rational.workspace = true once_cell.workspace = true phf.workspace = true +rand = { workspace = true, optional = true } rstest = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["arbitrary_precision"] } diff --git a/crates/blockifier/bench/blockifier_bench.rs b/crates/blockifier/bench/blockifier_bench.rs index eea3030073..c2b35316d1 100644 --- a/crates/blockifier/bench/blockifier_bench.rs +++ b/crates/blockifier/bench/blockifier_bench.rs @@ -7,139 +7,19 @@ //! //! Run the benchmarks using `cargo bench --bench blockifier_bench`. -use blockifier::abi::abi_utils::selector_from_name; -use blockifier::blockifier::config::TransactionExecutorConfig; -use blockifier::blockifier::transaction_executor::TransactionExecutor; -use blockifier::context::{BlockContext, ChainInfo}; -use blockifier::invoke_tx_args; -use blockifier::test_utils::contracts::FeatureContract; -use blockifier::test_utils::dict_state_reader::DictStateReader; -use blockifier::test_utils::initial_test_state::test_state; -use blockifier::test_utils::invoke::invoke_tx; -use blockifier::test_utils::{CairoVersion, NonceManager, BALANCE, MAX_FEE}; -use blockifier::transaction::account_transaction::AccountTransaction; -use blockifier::transaction::constants::TRANSFER_ENTRY_POINT_NAME; -use blockifier::transaction::transaction_execution::Transaction; +use blockifier::test_utils::transfers_generator::TransfersGenerator; use criterion::{criterion_group, criterion_main, Criterion}; -use rand::{Rng, SeedableRng}; -use starknet_api::core::ContractAddress; -use starknet_api::hash::StarkFelt; -use starknet_api::transaction::{Calldata, Fee, TransactionVersion}; -use starknet_api::{calldata, stark_felt}; - -const N_ACCOUNTS: u16 = 10000; -const CHUNK_SIZE: usize = 10; -const RANDOMIZATION_SEED: u64 = 0; -const CHARGE_FEE: bool = false; -const TRANSACTION_VERSION: TransactionVersion = TransactionVersion(StarkFelt::ONE); pub fn transfers_benchmark(c: &mut Criterion) { - let mut transfers_simulator = TransfersGenerator::new(); + let mut transfers_generator = TransfersGenerator::new(); // Create a benchmark group called "transfers", which iterates over the accounts round-robin // and performs transfers. c.bench_function("transfers", |benchmark| { benchmark.iter(|| { - transfers_simulator.execute_chunk_of_transfers(); + transfers_generator.execute_chunk_of_transfers(); }) }); } -pub struct TransfersGenerator { - account_addresses: Vec, - chain_info: ChainInfo, - executor: TransactionExecutor, - nonce_manager: NonceManager, - recipient_generator: rand::rngs::StdRng, - sender_index: usize, -} - -impl TransfersGenerator { - pub fn new() -> Self { - let account_contract = FeatureContract::AccountWithoutValidations(CairoVersion::Cairo0); - let block_context = BlockContext::create_for_account_testing(); - let chain_info = block_context.chain_info().clone(); - let state = test_state(&chain_info, BALANCE * 1000, &[(account_contract, N_ACCOUNTS)]); - // TODO(Avi, 20/05/2024): Enable concurrency. - let executor_config = TransactionExecutorConfig::default(); - let executor = TransactionExecutor::new(state, block_context, executor_config); - let account_addresses = (0..N_ACCOUNTS) - .map(|instance_id| account_contract.get_instance_address(instance_id)) - .collect::>(); - let nonce_manager = NonceManager::default(); - let random_generator = rand::rngs::StdRng::seed_from_u64(RANDOMIZATION_SEED); - Self { - account_addresses, - nonce_manager, - chain_info, - executor, - sender_index: 0, - recipient_generator: random_generator, - } - } - - pub fn execute_chunk_of_transfers(&mut self) { - let mut chunk: Vec = Vec::with_capacity(CHUNK_SIZE); - for _ in 0..CHUNK_SIZE { - let sender_address = self.account_addresses[self.sender_index]; - self.sender_index = (self.sender_index + 1) % self.account_addresses.len(); - let recipient_index = - self.recipient_generator.gen::() % self.account_addresses.len(); - let recipient_address = self.account_addresses[recipient_index]; - - let account_tx = self.generate_transfer(sender_address, recipient_address); - chunk.push(Transaction::AccountTransaction(account_tx)); - } - let results = self.executor.execute_txs(&chunk, CHARGE_FEE); - assert_eq!(results.len(), CHUNK_SIZE); - for result in results { - assert!(!result.unwrap().is_reverted()); - } - // TODO(Avi, 01/06/2024): Run the same transactions concurrently on a new state and compare - // the state diffs. - } - - pub fn generate_transfer( - &mut self, - sender_address: ContractAddress, - recipient_address: ContractAddress, - ) -> AccountTransaction { - let nonce = self.nonce_manager.next(sender_address); - - let entry_point_selector = selector_from_name(TRANSFER_ENTRY_POINT_NAME); - let contract_address = match TRANSACTION_VERSION { - TransactionVersion::ONE => { - *self.chain_info.fee_token_addresses.eth_fee_token_address.0.key() - } - TransactionVersion::THREE => { - *self.chain_info.fee_token_addresses.strk_fee_token_address.0.key() - } - _ => panic!("Unsupported transaction version: {TRANSACTION_VERSION:?}"), - }; - - let execute_calldata = calldata![ - contract_address, // Contract address. - entry_point_selector.0, // EP selector. - stark_felt!(3_u8), // Calldata length. - *recipient_address.0.key(), // Calldata: recipient. - stark_felt!(1_u8), // Calldata: lsb amount. - stark_felt!(0_u8) // Calldata: msb amount. - ]; - - let tx = invoke_tx(invoke_tx_args! { - max_fee: Fee(MAX_FEE), - sender_address, - calldata: execute_calldata, - version: TRANSACTION_VERSION, - nonce, - }); - AccountTransaction::Invoke(tx) - } -} - -impl Default for TransfersGenerator { - fn default() -> Self { - Self::new() - } -} criterion_group!(benches, transfers_benchmark); criterion_main!(benches); diff --git a/crates/blockifier/src/blockifier.rs b/crates/blockifier/src/blockifier.rs index 6bd74a63ec..629c66ff78 100644 --- a/crates/blockifier/src/blockifier.rs +++ b/crates/blockifier/src/blockifier.rs @@ -2,3 +2,5 @@ pub mod block; pub mod config; pub mod stateful_validator; pub mod transaction_executor; +#[cfg(test)] +pub mod transfers_flow_test; diff --git a/crates/blockifier/src/blockifier/transfers_flow_test.rs b/crates/blockifier/src/blockifier/transfers_flow_test.rs new file mode 100644 index 0000000000..894ec3e465 --- /dev/null +++ b/crates/blockifier/src/blockifier/transfers_flow_test.rs @@ -0,0 +1,7 @@ +use crate::test_utils::transfers_generator::TransfersGenerator; + +#[test] +pub fn transfers_flow_test() { + let mut transfers_generator = TransfersGenerator::new(); + transfers_generator.execute_chunk_of_transfers(); +} diff --git a/crates/blockifier/src/test_utils.rs b/crates/blockifier/src/test_utils.rs index 58be205ff0..f515e67531 100644 --- a/crates/blockifier/src/test_utils.rs +++ b/crates/blockifier/src/test_utils.rs @@ -6,6 +6,7 @@ pub mod initial_test_state; pub mod invoke; pub mod prices; pub mod struct_impls; +pub mod transfers_generator; use std::collections::HashMap; use std::fs; use std::path::PathBuf; diff --git a/crates/blockifier/src/test_utils/transfers_generator.rs b/crates/blockifier/src/test_utils/transfers_generator.rs new file mode 100644 index 0000000000..b367190731 --- /dev/null +++ b/crates/blockifier/src/test_utils/transfers_generator.rs @@ -0,0 +1,123 @@ +use rand::{Rng, SeedableRng}; +use starknet_api::core::ContractAddress; +use starknet_api::hash::StarkFelt; +use starknet_api::transaction::{Calldata, Fee, TransactionVersion}; +use starknet_api::{calldata, stark_felt}; + +use crate::abi::abi_utils::selector_from_name; +use crate::blockifier::config::TransactionExecutorConfig; +use crate::blockifier::transaction_executor::TransactionExecutor; +use crate::context::{BlockContext, ChainInfo}; +use crate::invoke_tx_args; +use crate::test_utils::contracts::FeatureContract; +use crate::test_utils::dict_state_reader::DictStateReader; +use crate::test_utils::initial_test_state::test_state; +use crate::test_utils::invoke::invoke_tx; +use crate::test_utils::{CairoVersion, NonceManager, BALANCE, MAX_FEE}; +use crate::transaction::account_transaction::AccountTransaction; +use crate::transaction::constants::TRANSFER_ENTRY_POINT_NAME; +use crate::transaction::transaction_execution::Transaction; + +const N_ACCOUNTS: u16 = 10000; +const CHUNK_SIZE: usize = 10; +const RANDOMIZATION_SEED: u64 = 0; +const CHARGE_FEE: bool = false; +const TRANSACTION_VERSION: TransactionVersion = TransactionVersion(StarkFelt::ONE); + +pub struct TransfersGenerator { + account_addresses: Vec, + chain_info: ChainInfo, + executor: TransactionExecutor, + nonce_manager: NonceManager, + recipient_generator: rand::rngs::StdRng, + sender_index: usize, +} + +impl TransfersGenerator { + pub fn new() -> Self { + let account_contract = FeatureContract::AccountWithoutValidations(CairoVersion::Cairo0); + let block_context = BlockContext::create_for_account_testing(); + let chain_info = block_context.chain_info().clone(); + let state = test_state(&chain_info, BALANCE * 1000, &[(account_contract, N_ACCOUNTS)]); + // TODO(Avi, 20/05/2024): Enable concurrency. + let executor_config = TransactionExecutorConfig::default(); + let executor = TransactionExecutor::new(state, block_context, executor_config); + let account_addresses = (0..N_ACCOUNTS) + .map(|instance_id| account_contract.get_instance_address(instance_id)) + .collect::>(); + let nonce_manager = NonceManager::default(); + let random_generator = rand::rngs::StdRng::seed_from_u64(RANDOMIZATION_SEED); + Self { + account_addresses, + nonce_manager, + chain_info, + executor, + sender_index: 0, + recipient_generator: random_generator, + } + } + + pub fn execute_chunk_of_transfers(&mut self) { + let mut chunk: Vec = Vec::with_capacity(CHUNK_SIZE); + for _ in 0..CHUNK_SIZE { + let sender_address = self.account_addresses[self.sender_index]; + self.sender_index = (self.sender_index + 1) % self.account_addresses.len(); + let recipient_index = + self.recipient_generator.gen::() % self.account_addresses.len(); + let recipient_address = self.account_addresses[recipient_index]; + + let account_tx = self.generate_transfer(sender_address, recipient_address); + chunk.push(Transaction::AccountTransaction(account_tx)); + } + let results = self.executor.execute_txs(&chunk, CHARGE_FEE); + assert_eq!(results.len(), CHUNK_SIZE); + for result in results { + assert!(!result.unwrap().is_reverted()); + } + // TODO(Avi, 01/06/2024): Run the same transactions concurrently on a new state and compare + // the state diffs. + } + + pub fn generate_transfer( + &mut self, + sender_address: ContractAddress, + recipient_address: ContractAddress, + ) -> AccountTransaction { + let nonce = self.nonce_manager.next(sender_address); + + let entry_point_selector = selector_from_name(TRANSFER_ENTRY_POINT_NAME); + let contract_address = match TRANSACTION_VERSION { + TransactionVersion::ONE => { + *self.chain_info.fee_token_addresses.eth_fee_token_address.0.key() + } + TransactionVersion::THREE => { + *self.chain_info.fee_token_addresses.strk_fee_token_address.0.key() + } + _ => panic!("Unsupported transaction version: {TRANSACTION_VERSION:?}"), + }; + + let execute_calldata = calldata![ + contract_address, // Contract address. + entry_point_selector.0, // EP selector. + stark_felt!(3_u8), // Calldata length. + *recipient_address.0.key(), // Calldata: recipient. + stark_felt!(1_u8), // Calldata: lsb amount. + stark_felt!(0_u8) // Calldata: msb amount. + ]; + + let tx = invoke_tx(invoke_tx_args! { + max_fee: Fee(MAX_FEE), + sender_address, + calldata: execute_calldata, + version: TRANSACTION_VERSION, + nonce, + }); + AccountTransaction::Invoke(tx) + } +} + +impl Default for TransfersGenerator { + fn default() -> Self { + Self::new() + } +}