diff --git a/crates/engine/tree/benches/state_root_task.rs b/crates/engine/tree/benches/state_root_task.rs index 93f1ff4ec330..7a84a4c62d67 100644 --- a/crates/engine/tree/benches/state_root_task.rs +++ b/crates/engine/tree/benches/state_root_task.rs @@ -10,7 +10,7 @@ use reth_primitives::{Account as RethAccount, StorageEntry}; use reth_provider::{ providers::ConsistentDbView, test_utils::{create_test_provider_factory, MockNodeTypesWithDB}, - HashingWriter, ProviderFactory, + AccountReader, HashingWriter, ProviderFactory, }; use reth_testing_utils::generators::{self, Rng}; use reth_trie::{ @@ -28,38 +28,54 @@ struct BenchParams { num_accounts: usize, updates_per_account: usize, storage_slots_per_account: usize, + selfdestructs_per_update: usize, } +/// Generates a series of random state updates with configurable accounts, +/// storage, and self-destructs fn create_bench_state_updates(params: &BenchParams) -> Vec { let mut rng = generators::rng(); let all_addresses: Vec
= (0..params.num_accounts).map(|_| rng.gen()).collect(); let mut updates = Vec::new(); for _ in 0..params.updates_per_account { - let num_accounts_in_update = rng.gen_range(1..=params.num_accounts); let mut state_update = EvmState::default(); + let num_accounts_in_update = rng.gen_range(1..=params.num_accounts); - let selected_addresses = &all_addresses[0..num_accounts_in_update]; - - for &address in selected_addresses { - let mut storage = HashMap::default(); - for _ in 0..params.storage_slots_per_account { - let slot = U256::from(rng.gen::()); - storage.insert( - slot, - EvmStorageSlot::new_changed(U256::ZERO, U256::from(rng.gen::())), - ); - } + // regular updates for randomly selected accounts + for &address in &all_addresses[0..num_accounts_in_update] { + // randomly choose to self-destruct with probability + // (selfdestructs/accounts) + let is_selfdestruct = + rng.gen_bool(params.selfdestructs_per_update as f64 / params.num_accounts as f64); - let account = RevmAccount { - info: AccountInfo { - balance: U256::from(rng.gen::()), - nonce: rng.gen::(), - code_hash: KECCAK_EMPTY, - code: Some(Default::default()), - }, - storage, - status: AccountStatus::Touched, + let account = if is_selfdestruct { + RevmAccount { + info: AccountInfo::default(), + storage: HashMap::default(), + status: AccountStatus::SelfDestructed, + } + } else { + RevmAccount { + info: AccountInfo { + balance: U256::from(rng.gen::()), + nonce: rng.gen::(), + code_hash: KECCAK_EMPTY, + code: Some(Default::default()), + }, + storage: (0..rng.gen_range(0..=params.storage_slots_per_account)) + .map(|_| { + ( + U256::from(rng.gen::()), + EvmStorageSlot::new_changed( + U256::ZERO, + U256::from(rng.gen::()), + ), + ) + }) + .collect(), + status: AccountStatus::Touched, + } }; state_update.insert(address, account); @@ -71,50 +87,99 @@ fn create_bench_state_updates(params: &BenchParams) -> Vec { updates } -fn convert_revm_to_reth_account(revm_account: &RevmAccount) -> RethAccount { - RethAccount { - balance: revm_account.info.balance, - nonce: revm_account.info.nonce, - bytecode_hash: if revm_account.info.code_hash == KECCAK_EMPTY { - None - } else { - Some(revm_account.info.code_hash) - }, +fn convert_revm_to_reth_account(revm_account: &RevmAccount) -> Option { + match revm_account.status { + AccountStatus::SelfDestructed => None, + _ => Some(RethAccount { + balance: revm_account.info.balance, + nonce: revm_account.info.nonce, + bytecode_hash: if revm_account.info.code_hash == KECCAK_EMPTY { + None + } else { + Some(revm_account.info.code_hash) + }, + }), } } +/// Applies state updates to the provider, ensuring self-destructs only affect +/// existing accounts fn setup_provider( factory: &ProviderFactory, state_updates: &[EvmState], ) -> Result<(), Box> { - let provider_rw = factory.provider_rw()?; - for update in state_updates { - let account_updates = update - .iter() - .map(|(address, account)| (*address, Some(convert_revm_to_reth_account(account)))); - provider_rw.insert_account_for_hashing(account_updates)?; - - let storage_updates = update.iter().map(|(address, account)| { - let storage_entries = account.storage.iter().map(|(slot, value)| StorageEntry { - key: B256::from(*slot), - value: value.present_value, - }); - (*address, storage_entries) - }); - provider_rw.insert_storage_for_hashing(storage_updates)?; + let provider_rw = factory.provider_rw()?; + + let mut account_updates = Vec::new(); + + for (address, account) in update { + // only process self-destructs if account exists, always process + // other updates + let should_process = match account.status { + AccountStatus::SelfDestructed => { + provider_rw.basic_account(*address).ok().flatten().is_some() + } + _ => true, + }; + + if should_process { + account_updates.push(( + *address, + convert_revm_to_reth_account(account), + (account.status == AccountStatus::Touched).then(|| { + account + .storage + .iter() + .map(|(slot, value)| StorageEntry { + key: B256::from(*slot), + value: value.present_value, + }) + .collect::>() + }), + )); + } + } + + // update in the provider account and its storage (if available) + for (address, account, maybe_storage) in account_updates { + provider_rw.insert_account_for_hashing(std::iter::once((address, account)))?; + if let Some(storage) = maybe_storage { + provider_rw + .insert_storage_for_hashing(std::iter::once((address, storage.into_iter())))?; + } + } + + provider_rw.commit()?; } - provider_rw.commit()?; Ok(()) } fn bench_state_root(c: &mut Criterion) { + reth_tracing::init_test_tracing(); + let mut group = c.benchmark_group("state_root"); let scenarios = vec![ - BenchParams { num_accounts: 100, updates_per_account: 5, storage_slots_per_account: 10 }, - BenchParams { num_accounts: 1000, updates_per_account: 10, storage_slots_per_account: 20 }, + BenchParams { + num_accounts: 100, + updates_per_account: 5, + storage_slots_per_account: 10, + selfdestructs_per_update: 2, + }, + BenchParams { + num_accounts: 1000, + updates_per_account: 10, + storage_slots_per_account: 20, + selfdestructs_per_update: 5, + }, + BenchParams { + num_accounts: 500, + updates_per_account: 8, + storage_slots_per_account: 15, + selfdestructs_per_update: 20, + }, ]; for params in scenarios { @@ -122,10 +187,11 @@ fn bench_state_root(c: &mut Criterion) { BenchmarkId::new( "state_root_task", format!( - "accounts_{}_updates_{}_slots_{}", + "accounts_{}_updates_{}_slots_{}_selfdestructs_{}", params.num_accounts, params.updates_per_account, - params.storage_slots_per_account + params.storage_slots_per_account, + params.selfdestructs_per_update ), ), ¶ms,