From 003aafd7a6392fafbce718ca01bc3efa161c6ab5 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:46:03 -0400 Subject: [PATCH] feat(evm-optimism): add OpBlockAccessListExecutor --- crates/optimism/evm/src/execute.rs | 117 +++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 15 deletions(-) diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index 2cea28bf3ab0..d921b817659e 100644 --- a/crates/optimism/evm/src/execute.rs +++ b/crates/optimism/evm/src/execute.rs @@ -18,17 +18,22 @@ use reth_optimism_consensus::validate_block_post_execution; use reth_primitives::{BlockWithSenders, Header, Receipt, Receipts, TxType}; use reth_prune_types::PruneModes; use reth_revm::{ - batch::BlockBatchRecord, db::states::bundle_state::BundleRetention, - state_change::post_block_balance_increments, Evm, State, + batch::BlockBatchRecord, + db::{ + states::{bundle_state::BundleRetention, StorageSlot}, + BundleAccount, + }, + state_change::post_block_balance_increments, + Evm, State, }; use revm_primitives::{ db::{Database, DatabaseCommit}, BlockEnv, CfgEnvWithHandlerCfg, EVMError, EnvWithHandlerCfg, ResultAndState, }; -use std::sync::Arc; +use std::{collections::hash_map::Entry, fmt::Display, sync::Arc}; use tracing::trace; -/// Provides executors to execute regular ethereum blocks +/// Provides executors to execute regular optimism blocks #[derive(Debug, Clone)] pub struct OpExecutorProvider { chain_spec: Arc, @@ -58,7 +63,7 @@ where { fn op_executor(&self, db: DB) -> OpBlockExecutor where - DB: Database + std::fmt::Display>, + DB: Database + Display>, { OpBlockExecutor::new( self.chain_spec.clone(), @@ -72,21 +77,21 @@ impl BlockExecutorProvider for OpExecutorProvider where EvmConfig: ConfigureEvm, { - type Executor + std::fmt::Display>> = + type Executor + Display>> = OpBlockExecutor; - type BatchExecutor + std::fmt::Display>> = + type BatchExecutor + Display>> = OpBatchExecutor; fn executor(&self, db: DB) -> Self::Executor where - DB: Database + std::fmt::Display>, + DB: Database + Display>, { self.op_executor(db) } fn batch_executor(&self, db: DB) -> Self::BatchExecutor where - DB: Database + std::fmt::Display>, + DB: Database + Display>, { let executor = self.op_executor(db); OpBatchExecutor { executor, batch_record: BlockBatchRecord::default() } @@ -119,7 +124,7 @@ where mut evm: Evm<'_, Ext, &mut State>, ) -> Result<(Vec, u64), BlockExecutionError> where - DB: Database + std::fmt::Display>, + DB: Database + Display>, { // apply pre execution changes apply_beacon_root_contract_call( @@ -230,7 +235,7 @@ where } } -/// A basic Ethereum block executor. +/// A basic Optimism block executor. /// /// Expected usage: /// - Create a new instance of the executor. @@ -244,7 +249,7 @@ pub struct OpBlockExecutor { } impl OpBlockExecutor { - /// Creates a new Ethereum block executor. + /// Creates a new Optimism block executor. pub const fn new(chain_spec: Arc, evm_config: EvmConfig, state: State) -> Self { Self { executor: OpEvmExecutor { chain_spec, evm_config }, state } } @@ -264,7 +269,7 @@ impl OpBlockExecutor { impl OpBlockExecutor where EvmConfig: ConfigureEvm, - DB: Database + std::fmt::Display>, + DB: Database + Display>, { /// Configures a new evm configuration and block environment for the given block. /// @@ -337,7 +342,7 @@ where impl Executor for OpBlockExecutor where EvmConfig: ConfigureEvm, - DB: Database + std::fmt::Display>, + DB: Database + Display>, { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = BlockExecutionOutput; @@ -366,6 +371,88 @@ where } } +/// An executor that retains all cache state from execution in its bundle state. +#[derive(Debug)] +pub struct OpBlockAccessListExecutor { + /// The executor used to execute single blocks + /// + /// All state changes are committed to the [State]. + executor: OpBlockExecutor, +} + +impl Executor for OpBlockAccessListExecutor +where + EvmConfig: ConfigureEvm, + DB: Database + Display>, +{ + type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; + type Output = BlockExecutionOutput; + type Error = BlockExecutionError; + + /// Executes the block and commits the changes to the internal state. + /// + /// Returns the receipts of the transactions in the block. + /// + /// This also returns the accounts from the internal state cache in the bundle state, allowing + /// access to not only the state that changed during execution, but also the state accessed + /// during execution. + /// + /// Returns an error if the block could not be executed or failed verification. + fn execute(mut self, input: Self::Input<'_>) -> Result { + let BlockExecutionInput { block, total_difficulty } = input; + let (receipts, gas_used) = + self.executor.execute_without_verification(block, total_difficulty)?; + + // NOTE: we need to merge keep the reverts for the bundle retention + self.executor.state.merge_transitions(BundleRetention::Reverts); + + // now, ensure each account from the state is included in the bundle state + let mut bundle_state = self.executor.state.take_bundle(); + for (address, account) in self.executor.state.cache.accounts { + // convert all slots, insert all slots + let account_info = account.account_info(); + let account_storage = account.account.map(|a| a.storage).unwrap_or_default(); + + match bundle_state.state.entry(address) { + Entry::Vacant(entry) => { + // we have to add the entire account here + let extracted_storage = account_storage + .into_iter() + .map(|(k, v)| { + (k, StorageSlot { previous_or_original_value: v, present_value: v }) + }) + .collect(); + + let bundle_account = BundleAccount { + info: account_info.clone(), + original_info: account_info, + storage: extracted_storage, + status: account.status, + }; + entry.insert(bundle_account); + } + Entry::Occupied(mut entry) => { + // only add slots that are unchanged + let current_account = entry.get_mut(); + + // iterate over all storage slots, checking keys that are not in the bundle + // state + for (k, v) in account_storage { + if let Entry::Vacant(storage_entry) = current_account.storage.entry(k) { + storage_entry.insert(StorageSlot { + previous_or_original_value: v, + present_value: v, + }); + } + } + } + } + } + + Ok(BlockExecutionOutput { state: bundle_state, receipts, requests: vec![], gas_used }) + } +} + /// An executor for a batch of blocks. /// /// State changes are tracked until the executor is finalized. @@ -392,7 +479,7 @@ impl OpBatchExecutor { impl BatchExecutor for OpBatchExecutor where EvmConfig: ConfigureEvm, - DB: Database + std::fmt::Display>, + DB: Database + Display>, { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = ExecutionOutcome;