revm_handler/
handler.rs

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