revm_handler/
handler.rs

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