revm_handler/
handler.rs

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