diff --git a/Cargo.lock b/Cargo.lock index 3ba1d2f3a6c..e7c58917737 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -250,14 +250,15 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +checksum = "407510740da514b694fecb44d8b3cebdc60d448f70cc5d24743e8ba273448a6e" dependencies = [ "alloy-primitives", "alloy-rlp", "arbitrary", "borsh", + "once_cell", "serde", ] @@ -2949,7 +2950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fa6adedc626..a8fef07e0ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -449,7 +449,7 @@ alloy-sol-types = { version = "1.5.6", default-features = false } alloy-chains = { version = "0.2.33", default-features = false } alloy-eip2124 = { version = "0.2.0", default-features = false } -alloy-eip7928 = { version = "0.3.0", default-features = false } +alloy-eip7928 = { version = "0.3.4", default-features = false } alloy-evm = { version = "0.33.0", default-features = false } alloy-rlp = { version = "0.3.13", default-features = false, features = ["core-net"] } alloy-trie = { version = "0.9.4", default-features = false } diff --git a/crates/engine/tree/src/tree/payload_processor/mod.rs b/crates/engine/tree/src/tree/payload_processor/mod.rs index cfe929d1283..cfb774af102 100644 --- a/crates/engine/tree/src/tree/payload_processor/mod.rs +++ b/crates/engine/tree/src/tree/payload_processor/mod.rs @@ -7,7 +7,7 @@ use crate::tree::{ CacheWaitDurations, CachedStateMetrics, CachedStateMetricsSource, ExecutionCache, PayloadExecutionCache, SavedCache, StateProviderBuilder, TreeConfig, WaitForCaches, }; -use alloy_eip7928::BlockAccessList; +use alloy_eip7928::bal::DecodedBal; use alloy_eips::{eip1898::BlockWithParent, eip4895::Withdrawal}; use alloy_primitives::B256; use crossbeam_channel::{Receiver as CrossbeamReceiver, Sender as CrossbeamSender}; @@ -250,7 +250,6 @@ where provider_builder: StateProviderBuilder, multiproof_provider_factory: F, config: &TreeConfig, - bal: Option>, ) -> IteratorPayloadHandle where P: BlockReader + StateProviderFactory + StateReader + Clone + 'static, @@ -273,13 +272,12 @@ where halve_workers, config, ); - let install_state_hook = bal.is_none(); + let install_state_hook = env.decoded_bal.is_none(); let prewarm_handle = self.spawn_caching_with( env, prewarm_rx, provider_builder, Some(state_root_handle.updates_tx().clone()), - bal, ); PayloadHandle { @@ -300,14 +298,13 @@ where env: ExecutionEnv, transactions: I, provider_builder: StateProviderBuilder, - bal: Option>, ) -> IteratorPayloadHandle where P: BlockReader + StateProviderFactory + StateReader + Clone + 'static, { let (prewarm_rx, execution_rx) = self.spawn_tx_iterator(transactions, env.transaction_count); - let prewarm_handle = self.spawn_caching_with(env, prewarm_rx, provider_builder, None, bal); + let prewarm_handle = self.spawn_caching_with(env, prewarm_rx, provider_builder, None); PayloadHandle { state_root_handle: None, install_state_hook: false, @@ -465,7 +462,7 @@ where level = "debug", target = "engine::tree::payload_processor", skip_all, - fields(bal=%bal.is_some()) + fields(bal=%env.decoded_bal.is_some()) )] fn spawn_caching_with

( &self, @@ -473,7 +470,6 @@ where transactions: mpsc::Receiver<(usize, impl ExecutableTxFor + Clone + Send + 'static)>, provider_builder: StateProviderBuilder, to_sparse_trie_task: Option>, - bal: Option>, ) -> CacheTaskHandle where P: BlockReader + StateProviderFactory + StateReader + Clone + 'static, @@ -484,7 +480,7 @@ where let saved_cache = self.disable_state_cache.not().then(|| self.cache_for(env.parent_hash)); let executed_tx_index = Arc::new(AtomicUsize::new(0)); - + let maybe_decoded_bal = env.decoded_bal.clone(); // configure prewarming let prewarm_ctx = PrewarmContext { env, @@ -507,15 +503,16 @@ where prewarm_ctx, to_sparse_trie_task, ); - { let to_prewarm_task = to_prewarm_task.clone(); let disable_bal_parallel_execution = self.disable_bal_parallel_execution; self.executor.spawn_blocking_named("prewarm", move || { let mode = if skip_prewarm { PrewarmMode::Skipped - } else if let Some(bal) = bal.filter(|_| !disable_bal_parallel_execution) { - PrewarmMode::BlockAccessList(bal) + } else if let Some(decoded_bal) = + maybe_decoded_bal.filter(|_| !disable_bal_parallel_execution) + { + PrewarmMode::BlockAccessList(decoded_bal) } else { PrewarmMode::Transactions(transactions) }; @@ -936,6 +933,9 @@ pub struct ExecutionEnv { /// Withdrawals included in the block. /// Used to generate prefetch targets for withdrawal addresses. pub withdrawals: Option>, + /// Optional decoded BAL for the block. + /// Used to validate and optimize execution. + pub decoded_bal: Option>, } impl ExecutionEnv @@ -953,6 +953,7 @@ where transaction_count: 0, gas_used: 0, withdrawals: None, + decoded_bal: None, } } } @@ -1251,7 +1252,6 @@ mod tests { StateProviderBuilder::new(provider_factory.clone(), genesis_hash, None), OverlayStateProviderFactory::new(provider_factory, ChangesetCache::new()), &TreeConfig::default(), - None, // No BAL for test ); let mut state_hook = handle.state_hook().expect("state hook is None"); diff --git a/crates/engine/tree/src/tree/payload_processor/prewarm.rs b/crates/engine/tree/src/tree/payload_processor/prewarm.rs index 274a0697226..0f34f655da1 100644 --- a/crates/engine/tree/src/tree/payload_processor/prewarm.rs +++ b/crates/engine/tree/src/tree/payload_processor/prewarm.rs @@ -18,7 +18,7 @@ use crate::tree::{ StateProviderBuilder, }; use alloy_consensus::transaction::TxHashRef; -use alloy_eip7928::BlockAccessList; +use alloy_eip7928::bal::DecodedBal; use alloy_eips::eip4895::Withdrawal; use alloy_primitives::{keccak256, StorageKey, B256}; use crossbeam_channel::Sender as CrossbeamSender; @@ -48,7 +48,7 @@ pub enum PrewarmMode { /// Prewarm by executing transactions from a stream, each paired with its block index. Transactions(Receiver<(usize, Tx)>), /// Prewarm by prefetching slots from a Block Access List. - BlockAccessList(Arc), + BlockAccessList(Arc), /// Transaction prewarming is skipped (e.g. small blocks where the overhead exceeds the /// benefit). No workers are spawned. Skipped, @@ -331,9 +331,10 @@ where #[instrument(level = "debug", target = "engine::tree::payload_processor::prewarm", skip_all)] fn run_bal_prewarm( &self, - bal: Arc, + decoded_bal: Arc, actions_tx: Sender>, ) { + let bal = decoded_bal.as_bal(); if bal.is_empty() { if let Some(to_sparse_trie_task) = self.to_sparse_trie_task.as_ref() { let _ = to_sparse_trie_task.send(StateRootMessage::FinishedStateUpdates); @@ -355,8 +356,8 @@ where let parent_span = Span::current(); let prefetch_parent_span = parent_span.clone(); let stream_parent_span = parent_span; - let prefetch_bal = Arc::clone(&bal); - let stream_bal = Arc::clone(&bal); + let prefetch_bal = Arc::clone(&decoded_bal); + let stream_bal = Arc::clone(&decoded_bal); let (prefetch_tx, prefetch_rx) = oneshot::channel(); let (stream_tx, stream_rx) = oneshot::channel(); @@ -367,12 +368,12 @@ where target: "engine::tree::payload_processor::prewarm", parent: &prefetch_parent_span, "bal_prefetch_storage", - bal_accounts = prefetch_bal.len(), + bal_accounts = prefetch_bal.as_bal().len(), ); let provider_parent_span = branch_span.clone(); let _span = branch_span.entered(); - prefetch_bal.par_iter().for_each_init( + prefetch_bal.as_bal().par_iter().for_each_init( || { ( prefetch_ctx.clone(), @@ -400,12 +401,12 @@ where target: "engine::tree::payload_processor::prewarm", parent: &stream_parent_span, "bal_hashed_state_stream", - bal_accounts = stream_bal.len(), + bal_accounts = stream_bal.as_bal().len(), ); let provider_parent_span = branch_span.clone(); let _span = branch_span.entered(); - stream_bal.par_iter().for_each_init( + stream_bal.as_bal().par_iter().for_each_init( || (ctx.clone(), None::>, provider_parent_span.clone()), |(ctx, provider, parent_span), account_changes| { ctx.send_bal_hashed_state( diff --git a/crates/engine/tree/src/tree/payload_validator.rs b/crates/engine/tree/src/tree/payload_validator.rs index 9ba884c8be7..0de28226b42 100644 --- a/crates/engine/tree/src/tree/payload_validator.rs +++ b/crates/engine/tree/src/tree/payload_validator.rs @@ -48,7 +48,10 @@ use crate::tree::{ PayloadHandle, StateProviderBuilder, StateProviderDatabase, TreeConfig, WaitForCaches, }; use alloy_consensus::transaction::{Either, TxHashRef}; -use alloy_eip7928::{bal::Bal, BlockAccessList}; +use alloy_eip7928::{ + bal::{Bal, DecodedBal}, + BlockAccessList, +}; use alloy_eips::{eip1898::BlockWithParent, eip4895::Withdrawal, NumHash}; use alloy_evm::Evm; use alloy_primitives::{map::B256Set, B256}; @@ -487,6 +490,12 @@ where .in_scope(|| self.evm_env_for(&input)) .map_err(NewPayloadError::other)?; + // Extract the decoded BAL, if valid and available. + let decoded_bal = ensure_ok!(input + .try_decoded_access_list() + .map_err(|err| { Box::::from(err) })) + .map(Arc::new); + let env = ExecutionEnv { evm_env, hash: input.hash(), @@ -495,6 +504,7 @@ where transaction_count: input.transaction_count(), gas_used: input.gas_used(), withdrawals: input.withdrawals().map(|w| w.to_vec()), + decoded_bal, }; // Plan the strategy used for state root computation. @@ -509,14 +519,6 @@ where // Get an iterator over the transactions in the payload let txs = self.tx_iterator_for(&input)?; - // Extract the BAL, if valid and available - let block_access_list = ensure_ok!(input - .block_access_list() - .transpose() - // Eventually gets converted to a `InsertBlockErrorKind::Other` - .map_err(Box::::from)) - .map(Arc::new); - // Create lazy overlay from ancestors - this doesn't block, allowing execution to start // before the trie data is ready. The overlay will be computed on first access. let (lazy_overlay, anchor_hash) = Self::get_parent_lazy_overlay(parent_hash, ctx.state()); @@ -535,7 +537,6 @@ where provider_builder, overlay_factory.clone(), strategy, - block_access_list, )); // Create optional cache stats for detailed block logging @@ -1439,7 +1440,6 @@ where provider_builder: StateProviderBuilder, overlay_factory: OverlayStateProviderFactory

, strategy: StateRootStrategy, - block_access_list: Option>, ) -> Result< PayloadHandle< impl ExecutableTxFor + use, @@ -1459,7 +1459,6 @@ where provider_builder, overlay_factory, &self.config, - block_access_list, ); // record prewarming initialization duration @@ -1472,12 +1471,8 @@ where } StateRootStrategy::Parallel | StateRootStrategy::Synchronous => { let start = Instant::now(); - let handle = self.payload_processor.spawn_cache_exclusive( - env, - txs, - provider_builder, - block_access_list, - ); + let handle = + self.payload_processor.spawn_cache_exclusive(env, txs, provider_builder); // Record prewarming initialization duration self.metrics @@ -2110,6 +2105,17 @@ impl BlockOrPayload { } } + /// Returns the decoded block access list, if present and successfully decoded. + pub fn try_decoded_access_list(&self) -> Result, alloy_rlp::Error> { + match self { + Self::Payload(payload) => payload + .block_access_list() + .map(|block_access_list| DecodedBal::from_rlp_bytes(block_access_list.clone())) + .transpose(), + Self::Block(_) => Ok(None), + } + } + /// Returns the number of transactions in the payload or block. pub fn transaction_count(&self) -> usize where