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