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 memory = SharedMemory::new_with_buffer(ctx.local().shared_memory_buffer().clone());
302
303 let (tx, journal) = ctx.tx_journal_mut();
304 let bytecode = if let Some(&to) = tx.kind().to() {
305 let account = &journal.load_account_code(to)?.info;
306
307 if let Some(Bytecode::Eip7702(eip7702_bytecode)) = &account.code {
308 let delegated_address = eip7702_bytecode.delegated_address;
309 let account = &journal.load_account_code(delegated_address)?.info;
310 Some((
311 account.code.clone().unwrap_or_default(),
312 account.code_hash(),
313 ))
314 } else {
315 Some((
316 account.code.clone().unwrap_or_default(),
317 account.code_hash(),
318 ))
319 }
320 } else {
321 None
322 };
323
324 Ok(FrameInit {
325 depth: 0,
326 memory,
327 frame_input: execution::create_init_frame(tx, bytecode, gas_limit),
328 })
329 }
330
331 /// Processes the result of the initial call and handles returned gas.
332 #[inline]
333 fn last_frame_result(
334 &mut self,
335 evm: &mut Self::Evm,
336 frame_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
337 ) -> Result<(), Self::Error> {
338 let instruction_result = frame_result.interpreter_result().result;
339 let gas = frame_result.gas_mut();
340 let remaining = gas.remaining();
341 let refunded = gas.refunded();
342
343 // Spend the gas limit. Gas is reimbursed when the tx returns successfully.
344 *gas = Gas::new_spent(evm.ctx().tx().gas_limit());
345
346 if instruction_result.is_ok_or_revert() {
347 gas.erase_cost(remaining);
348 }
349
350 if instruction_result.is_ok() {
351 gas.record_refund(refunded);
352 }
353 Ok(())
354 }
355
356 /* FRAMES */
357
358 /// Executes the main frame processing loop.
359 ///
360 /// This loop manages the frame stack, processing each frame until execution completes.
361 /// For each iteration:
362 /// 1. Calls the current frame
363 /// 2. Handles the returned frame input or result
364 /// 3. Creates new frames or propagates results as needed
365 #[inline]
366 fn run_exec_loop(
367 &mut self,
368 evm: &mut Self::Evm,
369 first_frame_input: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameInit,
370 ) -> Result<FrameResult, Self::Error> {
371 let res = evm.frame_init(first_frame_input)?;
372
373 if let ItemOrResult::Result(frame_result) = res {
374 return Ok(frame_result);
375 }
376
377 loop {
378 let call_or_result = evm.frame_run()?;
379
380 let result = match call_or_result {
381 ItemOrResult::Item(init) => {
382 match evm.frame_init(init)? {
383 ItemOrResult::Item(_) => {
384 continue;
385 }
386 // Do not pop the frame since no new frame was created
387 ItemOrResult::Result(result) => result,
388 }
389 }
390 ItemOrResult::Result(result) => result,
391 };
392
393 if let Some(result) = evm.frame_return_result(result)? {
394 return Ok(result);
395 }
396 }
397 }
398
399 /* POST EXECUTION */
400
401 /// Validates that the minimum gas floor requirements are satisfied.
402 ///
403 /// Ensures that at least the floor gas amount has been consumed during execution.
404 #[inline]
405 fn eip7623_check_gas_floor(
406 &self,
407 _evm: &mut Self::Evm,
408 exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
409 init_and_floor_gas: InitialAndFloorGas,
410 ) {
411 post_execution::eip7623_check_gas_floor(exec_result.gas_mut(), init_and_floor_gas)
412 }
413
414 /// Calculates the final gas refund amount, including any EIP-7702 refunds.
415 #[inline]
416 fn refund(
417 &self,
418 evm: &mut Self::Evm,
419 exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
420 eip7702_refund: i64,
421 ) {
422 let spec = evm.ctx().cfg().spec().into();
423 post_execution::refund(spec, exec_result.gas_mut(), eip7702_refund)
424 }
425
426 /// Returns unused gas costs to the transaction sender's account.
427 #[inline]
428 fn reimburse_caller(
429 &self,
430 evm: &mut Self::Evm,
431 exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
432 ) -> Result<(), Self::Error> {
433 post_execution::reimburse_caller(evm.ctx(), exec_result.gas(), U256::ZERO)
434 .map_err(From::from)
435 }
436
437 /// Transfers transaction fees to the block beneficiary's account.
438 #[inline]
439 fn reward_beneficiary(
440 &self,
441 evm: &mut Self::Evm,
442 exec_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
443 ) -> Result<(), Self::Error> {
444 post_execution::reward_beneficiary(evm.ctx(), exec_result.gas()).map_err(From::from)
445 }
446
447 /// Processes the final execution output.
448 ///
449 /// This method, retrieves the final state from the journal, converts internal results to the external output format.
450 /// Internal state is cleared and EVM is prepared for the next transaction.
451 #[inline]
452 fn execution_result(
453 &mut self,
454 evm: &mut Self::Evm,
455 result: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
456 ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
457 match core::mem::replace(evm.ctx().error(), Ok(())) {
458 Err(ContextError::Db(e)) => return Err(e.into()),
459 Err(ContextError::Custom(e)) => return Err(Self::Error::from_string(e)),
460 Ok(()) => (),
461 }
462
463 let exec_result = post_execution::output(evm.ctx(), result);
464
465 // commit transaction
466 evm.ctx().journal_mut().commit_tx();
467 evm.ctx().local_mut().clear();
468 evm.frame_stack().clear();
469
470 Ok(exec_result)
471 }
472
473 /// Handles cleanup when an error occurs during execution.
474 ///
475 /// Ensures the journal state is properly cleared before propagating the error.
476 /// On happy path journal is cleared in [`Handler::execution_result`] method.
477 #[inline]
478 fn catch_error(
479 &self,
480 evm: &mut Self::Evm,
481 error: Self::Error,
482 ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
483 // clean up local context. Initcode cache needs to be discarded.
484 evm.ctx().local_mut().clear();
485 evm.ctx().journal_mut().discard_tx();
486 evm.frame_stack().clear();
487 Err(error)
488 }
489}