revm_handler/
handler.rs

1use crate::{
2    evm::FrameTr, execution, post_execution, pre_execution, validation, EvmTr, FrameResult,
3    ItemOrResult,
4};
5use context::result::{ExecutionResult, FromStringError};
6use context::LocalContextTr;
7use context_interface::context::ContextError;
8use context_interface::ContextTr;
9use context_interface::{
10    result::{HaltReasonTr, InvalidHeader, InvalidTransaction},
11    Cfg, Database, JournalTr, Transaction,
12};
13use interpreter::interpreter_action::FrameInit;
14use interpreter::{Gas, InitialAndFloorGas, SharedMemory};
15use primitives::U256;
16use state::Bytecode;
17
18/// Trait for errors that can occur during EVM execution.
19///
20/// This trait represents the minimal error requirements for EVM execution,
21/// ensuring that all necessary error types can be converted into the handler's error type.
22pub trait EvmTrError<EVM: EvmTr>:
23    From<InvalidTransaction>
24    + From<InvalidHeader>
25    + From<<<EVM::Context as ContextTr>::Db as Database>::Error>
26    + From<ContextError<<<EVM::Context as ContextTr>::Db as Database>::Error>>
27    + FromStringError
28{
29}
30
31impl<
32        EVM: EvmTr,
33        T: From<InvalidTransaction>
34            + From<InvalidHeader>
35            + From<<<EVM::Context as ContextTr>::Db as Database>::Error>
36            + From<ContextError<<<EVM::Context as ContextTr>::Db as Database>::Error>>
37            + FromStringError,
38    > EvmTrError<EVM> for T
39{
40}
41
42/// The main implementation of Ethereum Mainnet transaction execution.
43///
44/// The [`Handler::run`] method serves as the entry point for execution and provides
45/// out-of-the-box support for executing Ethereum mainnet transactions.
46///
47/// This trait allows EVM variants to customize execution logic by implementing
48/// their own method implementations.
49///
50/// The handler logic consists of four phases:
51///   * Validation - Validates tx/block/config fields and loads caller account and validates initial gas requirements and
52///     balance checks.
53///   * Pre-execution - Loads and warms accounts, deducts initial gas
54///   * Execution - Executes the main frame loop, delegating to [`EvmTr`] for creating and running call frames.
55///   * Post-execution - Calculates final refunds, validates gas floor, reimburses caller,
56///     and rewards beneficiary
57///
58///
59/// The [`Handler::catch_error`] method handles cleanup of intermediate state if an error
60/// occurs during execution.
61///
62/// # Returns
63///
64/// Returns execution status, error, gas spend and logs. State change is not returned and it is
65/// contained inside Context Journal. This setup allows multiple transactions to be chain executed.
66///
67/// To finalize the execution and obtain changed state, call [`JournalTr::finalize`] function.
68pub trait Handler {
69    /// The EVM type containing Context, Instruction, and Precompiles implementations.
70    type Evm: EvmTr<
71        Context: ContextTr<Journal: JournalTr, Local: LocalContextTr>,
72        Frame: FrameTr<FrameInit = FrameInit, FrameResult = FrameResult>,
73    >;
74    /// The error type returned by this handler.
75    type Error: EvmTrError<Self::Evm>;
76    /// The halt reason type included in the output
77    type HaltReason: HaltReasonTr;
78
79    /// The main entry point for transaction execution.
80    ///
81    /// This method calls [`Handler::run_without_catch_error`] and if it returns an error,
82    /// calls [`Handler::catch_error`] to handle the error and cleanup.
83    ///
84    /// The [`Handler::catch_error`] method ensures intermediate state is properly cleared.
85    ///
86    /// # Error handling
87    ///
88    /// In case of error, the journal can be in an inconsistent state and should be cleared by calling
89    /// [`JournalTr::discard_tx`] method or dropped.
90    ///
91    /// # Returns
92    ///
93    /// Returns execution result, error, gas spend and logs.
94    #[inline]
95    fn run(
96        &mut self,
97        evm: &mut Self::Evm,
98    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
99        // Run inner handler and catch all errors to handle cleanup.
100        match self.run_without_catch_error(evm) {
101            Ok(output) => Ok(output),
102            Err(e) => self.catch_error(evm, e),
103        }
104    }
105
106    /// Runs the system call.
107    ///
108    /// System call is a special transaction where caller is a [`crate::SYSTEM_ADDRESS`]
109    ///
110    /// It is used to call a system contracts and it skips all the `validation` and `pre-execution` and most of `post-execution` phases.
111    /// For example it will not deduct the caller or reward the beneficiary.
112    ///
113    /// State changs can be obtained by calling [`JournalTr::finalize`] method from the [`EvmTr::Context`].
114    ///
115    /// # Error handling
116    ///
117    /// By design system call should not fail and should always succeed.
118    /// In case of an error (If fetching account/storage on rpc fails), the journal can be in an inconsistent
119    /// state and should be cleared by calling [`JournalTr::discard_tx`] method or dropped.
120    #[inline]
121    fn run_system_call(
122        &mut self,
123        evm: &mut Self::Evm,
124    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
125        // dummy values that are not used.
126        let init_and_floor_gas = InitialAndFloorGas::new(0, 0);
127        // call execution and than output.
128        match self
129            .execution(evm, &init_and_floor_gas)
130            .and_then(|exec_result| self.execution_result(evm, exec_result))
131        {
132            out @ Ok(_) => out,
133            Err(e) => self.catch_error(evm, e),
134        }
135    }
136
137    /// Called by [`Handler::run`] to execute the core handler logic.
138    ///
139    /// Executes the four phases in sequence: [Handler::validate],
140    /// [Handler::pre_execution], [Handler::execution], [Handler::post_execution].
141    ///
142    /// Returns any errors without catching them or calling [`Handler::catch_error`].
143    #[inline]
144    fn run_without_catch_error(
145        &mut self,
146        evm: &mut Self::Evm,
147    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
148        let init_and_floor_gas = self.validate(evm)?;
149        let eip7702_refund = self.pre_execution(evm)? as i64;
150        let mut exec_result = self.execution(evm, &init_and_floor_gas)?;
151        self.post_execution(evm, &mut exec_result, init_and_floor_gas, eip7702_refund)?;
152
153        // Prepare the output
154        self.execution_result(evm, exec_result)
155    }
156
157    /// Validates the execution environment and transaction parameters.
158    ///
159    /// Calculates initial and floor gas requirements and verifies they are covered by the gas limit.
160    ///
161    /// Validation against state is done later in pre-execution phase in deduct_caller function.
162    #[inline]
163    fn validate(&self, evm: &mut Self::Evm) -> Result<InitialAndFloorGas, Self::Error> {
164        self.validate_env(evm)?;
165        self.validate_initial_tx_gas(evm)
166    }
167
168    /// Prepares the EVM state for execution.
169    ///
170    /// Loads the beneficiary account (EIP-3651: Warm COINBASE) and all accounts/storage from the access list (EIP-2929).
171    ///
172    /// Deducts the maximum possible fee from the caller's balance.
173    ///
174    /// For EIP-7702 transactions, applies the authorization list and delegates successful authorizations.
175    /// Returns the gas refund amount from EIP-7702. Authorizations are applied before execution begins.
176    #[inline]
177    fn pre_execution(&self, evm: &mut Self::Evm) -> Result<u64, Self::Error> {
178        self.validate_against_state_and_deduct_caller(evm)?;
179        self.load_accounts(evm)?;
180
181        let gas = self.apply_eip7702_auth_list(evm)?;
182        Ok(gas)
183    }
184
185    /// Creates and executes the initial frame, then processes the execution loop.
186    ///
187    /// Always calls [Handler::last_frame_result] to handle returned gas from the call.
188    #[inline]
189    fn execution(
190        &mut self,
191        evm: &mut Self::Evm,
192        init_and_floor_gas: &InitialAndFloorGas,
193    ) -> Result<FrameResult, Self::Error> {
194        let gas_limit = evm.ctx().tx().gas_limit() - init_and_floor_gas.initial_gas;
195        // Create first frame action
196        let first_frame_input = self.first_frame_input(evm, gas_limit)?;
197
198        // Run execution loop
199        let mut frame_result = self.run_exec_loop(evm, first_frame_input)?;
200
201        // Handle last frame result
202        self.last_frame_result(evm, &mut frame_result)?;
203        Ok(frame_result)
204    }
205
206    /// Handles the final steps of transaction execution.
207    ///
208    /// Calculates final refunds and validates the gas floor (EIP-7623) to ensure minimum gas is spent.
209    /// After EIP-7623, at least floor gas must be consumed.
210    ///
211    /// Reimburses unused gas to the caller and rewards the beneficiary with transaction fees.
212    /// The effective gas price determines rewards, with the base fee being burned.
213    ///
214    /// Finally, finalizes output by returning the journal state and clearing internal state
215    /// for the next execution.
216    #[inline]
217    fn post_execution(
218        &self,
219        evm: &mut Self::Evm,
220        exec_result: &mut FrameResult,
221        init_and_floor_gas: InitialAndFloorGas,
222        eip7702_gas_refund: i64,
223    ) -> Result<(), Self::Error> {
224        // Calculate final refund and add EIP-7702 refund to gas.
225        self.refund(evm, exec_result, eip7702_gas_refund);
226        // Ensure gas floor is met and minimum floor gas is spent.
227        // if `cfg.is_eip7623_disabled` is true, floor gas will be set to zero
228        self.eip7623_check_gas_floor(evm, exec_result, init_and_floor_gas);
229        // Return unused gas to caller
230        self.reimburse_caller(evm, exec_result)?;
231        // Pay transaction fees to beneficiary
232        self.reward_beneficiary(evm, exec_result)?;
233        Ok(())
234    }
235
236    /* VALIDATION */
237
238    /// Validates block, transaction and configuration fields.
239    ///
240    /// Performs all validation checks that can be done without loading state.
241    /// For example, verifies transaction gas limit is below block gas limit.
242    #[inline]
243    fn validate_env(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
244        validation::validate_env(evm.ctx())
245    }
246
247    /// Calculates initial gas costs based on transaction type and input data.
248    ///
249    /// Includes additional costs for access list and authorization list.
250    ///
251    /// Verifies the initial cost does not exceed the transaction gas limit.
252    #[inline]
253    fn validate_initial_tx_gas(&self, evm: &Self::Evm) -> Result<InitialAndFloorGas, Self::Error> {
254        let ctx = evm.ctx_ref();
255        validation::validate_initial_tx_gas(
256            ctx.tx(),
257            ctx.cfg().spec().into(),
258            ctx.cfg().is_eip7623_disabled(),
259        )
260        .map_err(From::from)
261    }
262
263    /* PRE EXECUTION */
264
265    /// Loads access list and beneficiary account, marking them as warm in the [`context::Journal`].
266    #[inline]
267    fn load_accounts(&self, evm: &mut Self::Evm) -> Result<(), Self::Error> {
268        pre_execution::load_accounts(evm)
269    }
270
271    /// Processes the authorization list, validating authority signatures, nonces and chain IDs.
272    /// Applies valid authorizations to accounts.
273    ///
274    /// Returns the gas refund amount specified by EIP-7702.
275    #[inline]
276    fn apply_eip7702_auth_list(&self, evm: &mut Self::Evm) -> Result<u64, Self::Error> {
277        pre_execution::apply_eip7702_auth_list(evm.ctx())
278    }
279
280    /// Deducts maximum possible fee and transfer value from caller's balance.
281    ///
282    /// Unused fees are returned to caller after execution completes.
283    #[inline]
284    fn validate_against_state_and_deduct_caller(
285        &self,
286        evm: &mut Self::Evm,
287    ) -> Result<(), Self::Error> {
288        pre_execution::validate_against_state_and_deduct_caller(evm.ctx())
289    }
290
291    /* EXECUTION */
292
293    /// Creates initial frame input using transaction parameters, gas limit and configuration.
294    #[inline]
295    fn first_frame_input(
296        &mut self,
297        evm: &mut Self::Evm,
298        gas_limit: u64,
299    ) -> Result<FrameInit, Self::Error> {
300        let ctx = evm.ctx_mut();
301        let mut memory = SharedMemory::new_with_buffer(ctx.local().shared_memory_buffer().clone());
302        memory.set_memory_limit(ctx.cfg().memory_limit());
303
304        let (tx, journal) = ctx.tx_journal_mut();
305        let bytecode = if let Some(&to) = tx.kind().to() {
306            let account = &journal.load_account_with_code(to)?.info;
307
308            if let Some(Bytecode::Eip7702(eip7702_bytecode)) = &account.code {
309                let delegated_address = eip7702_bytecode.delegated_address;
310                let account = &journal.load_account_with_code(delegated_address)?.info;
311                Some((
312                    account.code.clone().unwrap_or_default(),
313                    account.code_hash(),
314                ))
315            } else {
316                Some((
317                    account.code.clone().unwrap_or_default(),
318                    account.code_hash(),
319                ))
320            }
321        } else {
322            None
323        };
324
325        Ok(FrameInit {
326            depth: 0,
327            memory,
328            frame_input: execution::create_init_frame(tx, bytecode, gas_limit),
329        })
330    }
331
332    /// Processes the result of the initial call and handles returned gas.
333    #[inline]
334    fn last_frame_result(
335        &mut self,
336        evm: &mut Self::Evm,
337        frame_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
338    ) -> Result<(), Self::Error> {
339        let instruction_result = frame_result.interpreter_result().result;
340        let gas = frame_result.gas_mut();
341        let remaining = gas.remaining();
342        let refunded = gas.refunded();
343
344        // Spend the gas limit. Gas is reimbursed when the tx returns successfully.
345        *gas = Gas::new_spent(evm.ctx().tx().gas_limit());
346
347        if instruction_result.is_ok_or_revert() {
348            gas.erase_cost(remaining);
349        }
350
351        if instruction_result.is_ok() {
352            gas.record_refund(refunded);
353        }
354        Ok(())
355    }
356
357    /* FRAMES */
358
359    /// Executes the main frame processing loop.
360    ///
361    /// This loop manages the frame stack, processing each frame until execution completes.
362    /// For each iteration:
363    /// 1. Calls the current frame
364    /// 2. Handles the returned frame input or result
365    /// 3. Creates new frames or propagates results as needed
366    #[inline]
367    fn run_exec_loop(
368        &mut self,
369        evm: &mut Self::Evm,
370        first_frame_input: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameInit,
371    ) -> Result<FrameResult, Self::Error> {
372        let res = evm.frame_init(first_frame_input)?;
373
374        if let ItemOrResult::Result(frame_result) = res {
375            return Ok(frame_result);
376        }
377
378        loop {
379            let call_or_result = evm.frame_run()?;
380
381            let result = match call_or_result {
382                ItemOrResult::Item(init) => {
383                    match evm.frame_init(init)? {
384                        ItemOrResult::Item(_) => {
385                            continue;
386                        }
387                        // Do not pop the frame since no new frame was created
388                        ItemOrResult::Result(result) => result,
389                    }
390                }
391                ItemOrResult::Result(result) => result,
392            };
393
394            if let Some(result) = evm.frame_return_result(result)? {
395                return Ok(result);
396            }
397        }
398    }
399
400    /* POST EXECUTION */
401
402    /// Validates that the minimum gas floor requirements are satisfied.
403    ///
404    /// Ensures that at least the floor gas amount has been consumed during execution.
405    #[inline]
406    fn eip7623_check_gas_floor(
407        &self,
408        _evm: &mut Self::Evm,
409        exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
410        init_and_floor_gas: InitialAndFloorGas,
411    ) {
412        post_execution::eip7623_check_gas_floor(exec_result.gas_mut(), init_and_floor_gas)
413    }
414
415    /// Calculates the final gas refund amount, including any EIP-7702 refunds.
416    #[inline]
417    fn refund(
418        &self,
419        evm: &mut Self::Evm,
420        exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
421        eip7702_refund: i64,
422    ) {
423        let spec = evm.ctx().cfg().spec().into();
424        post_execution::refund(spec, exec_result.gas_mut(), eip7702_refund)
425    }
426
427    /// Returns unused gas costs to the transaction sender's account.
428    #[inline]
429    fn reimburse_caller(
430        &self,
431        evm: &mut Self::Evm,
432        exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
433    ) -> Result<(), Self::Error> {
434        post_execution::reimburse_caller(evm.ctx(), exec_result.gas(), U256::ZERO)
435            .map_err(From::from)
436    }
437
438    /// Transfers transaction fees to the block beneficiary's account.
439    #[inline]
440    fn reward_beneficiary(
441        &self,
442        evm: &mut Self::Evm,
443        exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
444    ) -> Result<(), Self::Error> {
445        post_execution::reward_beneficiary(evm.ctx(), exec_result.gas()).map_err(From::from)
446    }
447
448    /// Processes the final execution output.
449    ///
450    /// This method, retrieves the final state from the journal, converts internal results to the external output format.
451    /// Internal state is cleared and EVM is prepared for the next transaction.
452    #[inline]
453    fn execution_result(
454        &mut self,
455        evm: &mut Self::Evm,
456        result: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
457    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
458        match core::mem::replace(evm.ctx().error(), Ok(())) {
459            Err(ContextError::Db(e)) => return Err(e.into()),
460            Err(ContextError::Custom(e)) => return Err(Self::Error::from_string(e)),
461            Ok(()) => (),
462        }
463
464        let exec_result = post_execution::output(evm.ctx(), result);
465
466        // commit transaction
467        evm.ctx().journal_mut().commit_tx();
468        evm.ctx().local_mut().clear();
469        evm.frame_stack().clear();
470
471        Ok(exec_result)
472    }
473
474    /// Handles cleanup when an error occurs during execution.
475    ///
476    /// Ensures the journal state is properly cleared before propagating the error.
477    /// On happy path journal is cleared in [`Handler::execution_result`] method.
478    #[inline]
479    fn catch_error(
480        &self,
481        evm: &mut Self::Evm,
482        error: Self::Error,
483    ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
484        // clean up local context. Initcode cache needs to be discarded.
485        evm.ctx().local_mut().clear();
486        evm.ctx().journal_mut().discard_tx();
487        evm.frame_stack().clear();
488        Err(error)
489    }
490}