From 41cf70e819048e7b5d8fd0215509fa7a3d564811 Mon Sep 17 00:00:00 2001 From: 0xsensei Date: Sun, 21 Dec 2025 17:58:34 +0530 Subject: [PATCH] Pre-calculate keccak hashes --- .../engine/tree/src/tree/payload_validator.rs | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/crates/engine/tree/src/tree/payload_validator.rs b/crates/engine/tree/src/tree/payload_validator.rs index b64611fb5e..0263f36baa 100644 --- a/crates/engine/tree/src/tree/payload_validator.rs +++ b/crates/engine/tree/src/tree/payload_validator.rs @@ -43,7 +43,7 @@ use reth_revm::db::State; use reth_storage_errors::db::DatabaseError; use reth_trie::{updates::TrieUpdates, HashedPostState, StateRoot, TrieInputSorted}; use reth_trie_parallel::root::{ParallelStateRoot, ParallelStateRootError}; -use revm_primitives::Address; +use revm_primitives::{keccak256, Address}; use std::{ collections::HashMap, panic::{self, AssertUnwindSafe}, @@ -206,6 +206,63 @@ where } } + /// Warms the keccak cache by pre-computing hashes for transaction addresses. + #[instrument(level = "trace", target = "engine::tree::payload_validator", skip_all)] + fn warm_keccak_cache(&self, input: &BlockOrPayload) + where + T: PayloadTypes>, + V: PayloadValidator, + Evm: ConfigureEngineEvm, + { + use alloy_consensus::Transaction as _; + use alloy_evm::RecoveredTx; + use alloy_primitives::TxKind; + use rayon::iter::ParallelIterator; + use reth_primitives_traits::SignerRecoverable; + + match input { + BlockOrPayload::Payload(payload) => { + // Get transaction bytes iterator and conversion function + if let Ok((txs, convert)) = + self.evm_config.tx_iterator_for_payload(payload).map(|iter| iter.into()) + { + // Use parallel iterator to warm cache for all transactions + use rayon::iter::IntoParallelIterator; + txs.into_par_iter().for_each(|tx_bytes| { + if let Ok(recovered_tx) = convert(tx_bytes) { + // Warm cache for sender address + let sender = recovered_tx.signer(); + let _ = keccak256(sender); + + // Unwrap to get the inner transaction for recipient + let tx = recovered_tx.tx(); + + // Warm cache for recipient address (skip contract creations) + if let TxKind::Call(to) = tx.kind() { + let _ = keccak256(to); + } + } + }); + } + } + + BlockOrPayload::Block(block) => { + // For blocks, transactions are already decoded + for tx in block.body().transactions() { + // Warm cache for sender address (skip if recovery fails) + if let Ok(sender) = tx.recover_signer() { + let _ = keccak256(sender); + } + + // Warm cache for recipient address + if let TxKind::Call(to) = tx.kind() { + let _ = keccak256(to); + } + } + } + } + } + /// Returns [`ExecutableTxIterator`] for the given payload or block. pub fn tx_iterator_for<'a, T: PayloadTypes>>( &'a self, @@ -401,6 +458,9 @@ where "Decided which state root algorithm to run" ); + // Warm keccak cache before spawning parallel tasks + self.warm_keccak_cache(&input); + // Get an iterator over the transactions in the payload let txs = self.tx_iterator_for(&input)?;