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}