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