1use crate::{ConfigureEvm, Database, DynDatabase, EvmEnv, TxEnvFor};
4use alloc::{boxed::Box, sync::Arc, vec::Vec};
5use alloy_consensus::{
6 transaction::{Either, Recovered, TransactionEnvelope},
7 BlockHeader as _, Header, Transaction, TxEip2930, TxEip4844, TxLegacy,
8};
9use alloy_eip7928::{
10 bal::DecodedBal, compute_block_access_list_hash_with_buf, BlockAccessIndex, BlockAccessList,
11};
12use alloy_eips::{
13 eip2718::{Typed2718, WithEncoded},
14 eip2930::AccessList,
15};
16use alloy_primitives::{Address, B256};
17use core::fmt::Debug;
18use evm2::interpreter::{Interpreter, Message, MessageResult};
19use reth_execution_types::{BlockExecutionResult, StateUpdate};
20use reth_primitives_traits::{
21 Block, HeaderTy, NodePrimitives, ReceiptTy, RecoveredBlock, SealedHeader, SignedTransaction,
22 TxTy,
23};
24use reth_storage_api::StateProvider;
25use reth_trie_common::{bundle_state::BundleState, updates::TrieUpdates, HashedPostState};
26
27#[cfg(feature = "std")]
28use evm2::evm::{CacheDB, Db};
29#[cfg(feature = "std")]
30use reth_execution_types::BundleSource;
31#[cfg(feature = "std")]
32use reth_primitives_traits::BlockTy;
33
34pub use evm2::block::{
35 map_database_error, map_handler_error, BlockTransactionResult, CommitChanges, GasOutput,
36 ReceiptBuilderCtx,
37};
38pub use reth_execution_errors::{
39 BlockExecutionError, BlockValidationError, InternalBlockExecutionError, InvalidTxError,
40};
41pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
42
43#[auto_impl::auto_impl(&, Arc)]
45pub trait ReceiptBuilder {
46 type Transaction: SignedTransaction + TransactionEnvelope<TxType: Send + 'static>;
48 type Receipt: reth_primitives_traits::Receipt;
50
51 fn build_receipt<T: evm2::EvmTypes>(
53 &self,
54 ctx: ReceiptBuilderCtx<
55 <Self::Transaction as TransactionEnvelope>::TxType,
56 evm2::TxResult<T>,
57 >,
58 ) -> Self::Receipt;
59}
60
61pub trait Evm {
63 type EvmTypes: evm2::EvmTypes<Tx = Self::Transaction>;
65 type Transaction;
67
68 fn transact(
70 &mut self,
71 transaction: &Recovered<Self::Transaction>,
72 ) -> Result<evm2::TxResultWithState<Self::EvmTypes>, BlockExecutionError>;
73
74 fn transact_commit(
76 &mut self,
77 transaction: &Recovered<Self::Transaction>,
78 ) -> Result<evm2::TxResult<Self::EvmTypes>, BlockExecutionError>;
79
80 fn transact_result(
82 &mut self,
83 transaction: &Recovered<Self::Transaction>,
84 ) -> Result<evm2::TxResult<Self::EvmTypes>, BlockExecutionError>;
85
86 fn transact_with_inspector<I>(
88 &mut self,
89 transaction: &Recovered<Self::Transaction>,
90 inspector: I,
91 ) -> Result<(I, evm2::TxResultWithState<Self::EvmTypes>), BlockExecutionError>
92 where
93 I: evm2::Inspector<Self::EvmTypes> + 'static;
94
95 fn set_inspector<I>(&mut self, inspector: I)
97 where
98 I: evm2::Inspector<Self::EvmTypes> + 'static;
99
100 unsafe fn with_inspector<I, R>(&mut self, f: impl FnOnce(&mut I) -> R) -> Option<R>
108 where
109 I: evm2::Inspector<Self::EvmTypes> + 'static;
110
111 fn take_inspector(&mut self) -> Option<Box<dyn evm2::Inspector<Self::EvmTypes> + '_>>;
113
114 fn state_db_mut(&mut self) -> &mut dyn DynDatabase;
116
117 fn commit_state(&mut self, state: &evm2::evm::PendingState);
119
120 fn take_state_cache(&mut self) -> evm2::evm::Cache;
122
123 fn precompile_ids(&self) -> Vec<(Address, evm2::precompiles::PrecompileId)>;
125
126 fn has_precompile(&self, address: &Address) -> bool;
128
129 fn account_info(
131 &mut self,
132 address: &Address,
133 ) -> Result<Option<evm2::evm::AccountInfo>, BlockExecutionError>;
134
135 fn move_precompiles(
137 &mut self,
138 moves: impl IntoIterator<Item = (Address, Address)>,
139 ) -> Result<(), evm2::precompiles::MovePrecompileError>;
140
141 fn transact_and_discard<R>(
144 &mut self,
145 transaction: &Recovered<Self::Transaction>,
146 observe: impl FnOnce(&evm2::ExecutedTx<'_, '_, Self::EvmTypes>) -> R,
147 ) -> Result<R, BlockExecutionError>;
148}
149
150impl<'a, T: evm2::EvmTypes<Tx: Typed2718>> Evm for evm2::Evm<'a, T> {
151 type EvmTypes = T;
152 type Transaction = T::Tx;
153
154 fn transact(
155 &mut self,
156 transaction: &Recovered<Self::Transaction>,
157 ) -> Result<evm2::TxResultWithState<T>, BlockExecutionError> {
158 evm2::Evm::transact(self, transaction)
159 .map(|executed| executed.detach())
160 .map_err(map_handler_error)
161 }
162
163 fn transact_commit(
164 &mut self,
165 transaction: &Recovered<Self::Transaction>,
166 ) -> Result<evm2::TxResult<T>, BlockExecutionError> {
167 execute_result(self, transaction, true)
168 }
169
170 fn transact_result(
171 &mut self,
172 transaction: &Recovered<Self::Transaction>,
173 ) -> Result<evm2::TxResult<T>, BlockExecutionError> {
174 execute_result(self, transaction, false)
175 }
176
177 fn transact_with_inspector<I: evm2::Inspector<T> + 'a>(
178 &mut self,
179 transaction: &Recovered<Self::Transaction>,
180 inspector: I,
181 ) -> Result<(I, evm2::TxResultWithState<T>), BlockExecutionError> {
182 evm2::Evm::set_inspector(self, inspector);
184 let result = evm2::Evm::transact(self, transaction)
185 .map(|executed| executed.detach())
186 .map_err(map_handler_error);
187 let inspector = self.clear_inspector().expect("inspector was set before execution");
188 let inspector = unsafe { Box::from_raw(Box::into_raw(inspector).cast::<I>()) };
190 result.map(|result| (*inspector, result))
191 }
192
193 fn set_inspector<I: evm2::Inspector<T> + 'a>(&mut self, inspector: I) {
194 evm2::Evm::set_inspector(self, inspector);
195 }
196
197 unsafe fn with_inspector<I, R>(&mut self, f: impl FnOnce(&mut I) -> R) -> Option<R>
198 where
199 I: evm2::Inspector<T> + 'static,
200 {
201 let inspector: &mut (dyn evm2::evm::NonStaticAny + 'a) = &mut **self.inspector_mut()?;
202 if evm2::evm::NonStaticAny::type_id(inspector) != core::any::TypeId::of::<I>() {
203 return None
204 }
205 Some(f(unsafe { inspector.downcast_mut_unchecked::<I>() }))
208 }
209
210 fn take_inspector(&mut self) -> Option<Box<dyn evm2::Inspector<T> + '_>> {
211 self.clear_inspector()
212 }
213
214 fn state_db_mut(&mut self) -> &mut dyn DynDatabase {
215 self.overlay_db_mut()
216 }
217
218 fn commit_state(&mut self, state: &evm2::evm::PendingState) {
219 self.overlay_db_mut().commit_pending(state);
220 }
221
222 fn take_state_cache(&mut self) -> evm2::evm::Cache {
223 core::mem::take(&mut self.overlay_db_mut().cache)
224 }
225
226 fn precompile_ids(&self) -> Vec<(Address, evm2::precompiles::PrecompileId)> {
227 self.precompiles().precompile_ids()
228 }
229
230 fn has_precompile(&self, address: &Address) -> bool {
231 self.precompiles().contains(address)
232 }
233
234 fn account_info(
235 &mut self,
236 address: &Address,
237 ) -> Result<Option<evm2::evm::AccountInfo>, BlockExecutionError> {
238 self.state_mut().account_info_untracked(address).map_err(map_database_error)
239 }
240
241 fn move_precompiles(
242 &mut self,
243 moves: impl IntoIterator<Item = (Address, Address)>,
244 ) -> Result<(), evm2::precompiles::MovePrecompileError> {
245 self.precompiles_mut().move_precompiles(&moves.into_iter().collect::<Vec<_>>())
246 }
247
248 fn transact_and_discard<R>(
249 &mut self,
250 transaction: &Recovered<Self::Transaction>,
251 observe: impl FnOnce(&evm2::ExecutedTx<'_, '_, T>) -> R,
252 ) -> Result<R, BlockExecutionError> {
253 let executed = self.transact(transaction).map_err(map_handler_error)?;
254 let observed = observe(&executed);
255 drop(executed);
256 Ok(observed)
257 }
258}
259
260fn execute_result<T: evm2::EvmTypes<Tx: Typed2718>>(
261 evm: &mut evm2::Evm<'_, T>,
262 transaction: &Recovered<T::Tx>,
263 commit: bool,
264) -> Result<evm2::TxResult<T>, BlockExecutionError> {
265 let executed = evm2::Evm::transact(evm, transaction).map_err(map_handler_error)?;
266 Ok(if commit { executed.commit() } else { executed.discard() })
267}
268
269#[derive(Debug)]
275struct TracedInspector<I>(I);
276
277impl<I: 'static> TracedInspector<I> {
278 fn downcast<T: evm2::EvmTypesHost>(inspector: Box<dyn evm2::Inspector<T> + '_>) -> Option<I> {
280 let erased: &dyn evm2::evm::NonStaticAny = &*inspector;
281 if erased.type_id() != core::any::TypeId::of::<Self>() {
282 return None
283 }
284 let traced = unsafe { Box::from_raw(Box::into_raw(inspector).cast::<Self>()) };
288 Some(traced.0)
289 }
290}
291
292impl<T: evm2::EvmTypesHost, I: evm2::Inspector<T>> evm2::Inspector<T> for TracedInspector<I> {
293 fn initialize_interp(&mut self, interp: &mut Interpreter<'_, '_, T>) {
294 self.0.initialize_interp(interp);
295 }
296
297 fn inspector_config(&self) -> evm2::InspectorConfig {
298 self.0.inspector_config()
299 }
300
301 fn step(&mut self, interp: &mut Interpreter<'_, '_, T>) {
302 self.0.step(interp);
303 }
304
305 fn step_end(&mut self, interp: &mut Interpreter<'_, '_, T>) {
306 self.0.step_end(interp);
307 }
308
309 fn log(&mut self, log: &alloy_primitives::Log, host: &mut T::Host<'_>) {
310 self.0.log(log, host);
311 }
312
313 fn call(
314 &mut self,
315 interp: &mut Interpreter<'_, '_, T>,
316 message: &mut Message<T>,
317 ) -> Option<MessageResult<T>> {
318 self.0.call(interp, message)
319 }
320
321 fn call_end(
322 &mut self,
323 interp: &mut Interpreter<'_, '_, T>,
324 message: &Message<T>,
325 result: &mut MessageResult<T>,
326 ) {
327 self.0.call_end(interp, message, result);
328 }
329
330 fn create(
331 &mut self,
332 interp: &mut Interpreter<'_, '_, T>,
333 message: &mut Message<T>,
334 ) -> Option<MessageResult<T>> {
335 self.0.create(interp, message)
336 }
337
338 fn create_end(
339 &mut self,
340 interp: &mut Interpreter<'_, '_, T>,
341 message: &Message<T>,
342 result: &mut MessageResult<T>,
343 ) {
344 self.0.create_end(interp, message, result);
345 }
346
347 fn selfdestruct(
348 &mut self,
349 contract: &Address,
350 target: &Address,
351 value: &alloy_primitives::U256,
352 host: &mut T::Host<'_>,
353 ) {
354 self.0.selfdestruct(contract, target, value, host);
355 }
356}
357
358pub trait BlockExecutor: Sized {
360 type Transaction;
362 type Receipt;
364 type Evm: Evm;
366 type TransactionResultWithState: BlockTransactionResult<<Self::Evm as Evm>::EvmTypes> + Send;
368 type BlockAccessList: Send + Sync;
370
371 fn evm(&self) -> &Self::Evm;
373
374 fn evm_mut(&mut self) -> &mut Self::Evm;
376
377 fn set_state_hook(&mut self, _hook: impl FnMut(StateUpdate) + Send + 'static) -> bool {
382 false
383 }
384
385 fn convert_block_access_list(
387 block_access_list: &BlockAccessList,
388 ) -> Result<Self::BlockAccessList, BlockExecutionError>;
389
390 fn set_block_access_list(&mut self, block_access_list: Arc<Self::BlockAccessList>);
392
393 fn set_block_access_index(&mut self, index: BlockAccessIndex);
395
396 fn enable_block_access_list_builder(&mut self);
398
399 fn take_block_access_list(&mut self) -> Option<BlockAccessList>;
401
402 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
404
405 fn into_state(self) -> BundleState;
408
409 fn execute_transaction_with_commit_condition(
415 &mut self,
416 transaction: impl ExecutorTx<Self>,
417 f: impl FnOnce(
418 &evm2::TxResult<<Self::Evm as Evm>::EvmTypes>,
419 evm2::TxStateChanges<'_>,
420 ) -> CommitChanges,
421 ) -> Result<Option<GasOutput>, BlockExecutionError> {
422 let output = self.execute_transaction_without_commit(transaction)?;
423 let result = output.result();
424 if f(&result.result, result.pending_state.changes()).should_commit() {
425 self.commit_transaction(output).map(Some)
426 } else {
427 Ok(None)
428 }
429 }
430
431 fn validate_transaction_gas_limit(&mut self, gas_limit: u64)
435 -> Result<(), BlockExecutionError>;
436
437 fn execute_transaction_without_commit(
439 &mut self,
440 transaction: impl ExecutorTx<Self>,
441 ) -> Result<Self::TransactionResultWithState, BlockExecutionError>;
442
443 fn inspect_transaction_without_commit<I>(
451 &mut self,
452 transaction: impl ExecutorTx<Self>,
453 inspector: I,
454 ) -> Result<(I, Self::TransactionResultWithState), BlockExecutionError>
455 where
456 I: evm2::Inspector<<Self::Evm as Evm>::EvmTypes> + 'static,
457 {
458 self.evm_mut().set_inspector(TracedInspector(inspector));
459 let output = self.execute_transaction_without_commit(transaction);
460 let inspector =
461 self.evm_mut().take_inspector().and_then(TracedInspector::downcast).ok_or_else(|| {
462 BlockExecutionError::msg("block executor replaced the tracing inspector")
463 });
464 let (inspector, output) = (inspector?, output?);
465 Ok((inspector, output))
466 }
467
468 fn commit_transaction(
470 &mut self,
471 output: Self::TransactionResultWithState,
472 ) -> Result<GasOutput, BlockExecutionError>;
473
474 fn execute_transaction_with_result_closure(
477 &mut self,
478 transaction: impl ExecutorTx<Self>,
479 f: impl FnOnce(&evm2::TxResult<<Self::Evm as Evm>::EvmTypes>),
480 ) -> Result<GasOutput, BlockExecutionError> {
481 self.execute_transaction_with_commit_condition(transaction, |result, _| {
482 f(result);
483 CommitChanges::Yes
484 })
485 .map(Option::unwrap_or_default)
486 }
487
488 fn execute_transaction(
490 &mut self,
491 transaction: impl ExecutorTx<Self>,
492 ) -> Result<GasOutput, BlockExecutionError> {
493 self.execute_transaction_with_result_closure(transaction, |_| {})
494 }
495
496 fn receipts(&self) -> &[Self::Receipt];
498
499 fn finish_with_block_access_list(
501 self,
502 ) -> Result<(BlockExecutionOutput<Self::Receipt>, Option<BlockAccessList>), BlockExecutionError>;
503
504 fn finish_with_prepared_block_access_list(
509 self,
510 ) -> Result<(BlockExecutionOutput<Self::Receipt>, Option<evm2::evm::Bal>), BlockExecutionError>
511 {
512 let (output, bal) = self.finish_with_block_access_list()?;
513 let bal = bal
514 .map(|bal| evm2::evm::Bal::try_from(bal.as_slice()))
515 .transpose()
516 .map_err(BlockExecutionError::other)?;
517 Ok((output, bal))
518 }
519
520 fn finish(self) -> Result<BlockExecutionOutput<Self::Receipt>, BlockExecutionError> {
522 self.finish_with_block_access_list().map(|(output, _)| output)
523 }
524}
525
526pub trait BlockExecutorFactory {
528 type EvmFactory;
530 type EvmTypes: evm2::EvmTypes<
532 TxResultExt: Send + Sync,
533 SpecId: Eq + core::hash::Hash + Debug + Send + Sync + 'static,
534 >;
535 type Transaction: Debug + Clone + Send + Sync + 'static;
537 type Receipt;
539 type Evm<'a>: Evm<
541 EvmTypes = Self::EvmTypes,
542 Transaction = <Self::EvmTypes as evm2::EvmTypesHost>::Tx,
543 >;
544 type EvmEnv: EvmEnv<EvmTypes = Self::EvmTypes>;
546 type ExecutionCtx<'a>: Debug + Clone + Send
548 where
549 Self: 'a;
550 type Executor<'a>: BlockExecutor<
552 Transaction = Self::Transaction,
553 Receipt = Self::Receipt,
554 Evm = Self::Evm<'a>,
555 BlockAccessList = evm2::evm::Bal,
556 >
557 where
558 Self: 'a;
559
560 fn create_executor<'a>(
562 &'a self,
563 evm: Self::Evm<'a>,
564 ctx: Self::ExecutionCtx<'a>,
565 ) -> Self::Executor<'a>
566 where
567 Self: 'a;
568
569 fn evm_factory(&self) -> &Self::EvmFactory;
571
572 fn evm_with_env<'a, DB>(&self, db: DB, evm_env: Self::EvmEnv) -> Self::Evm<'a>
574 where
575 DB: DynDatabase + 'a;
576
577 #[cfg(feature = "std")]
579 fn evm_with_database<'a, DB>(&self, db: DB, evm_env: Self::EvmEnv) -> Self::Evm<'a>
580 where
581 DB: Database + 'a,
582 {
583 self.evm_with_env(Db::new(db), evm_env)
584 }
585}
586
587#[expect(missing_debug_implementations)]
589#[non_exhaustive]
590pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory + 'a, H = Header> {
591 pub evm_env: F::EvmEnv,
593 pub execution_ctx: F::ExecutionCtx<'a>,
595 pub parent: &'a SealedHeader<H>,
597 pub transactions: Vec<F::Transaction>,
599 pub output: &'b BlockExecutionResult<F::Receipt>,
601 pub execution_state: &'b BundleState,
603 pub state_provider: &'b dyn StateProvider,
605 pub state_root: B256,
607 pub block_access_list_hash: Option<B256>,
609}
610
611impl<'a, 'b, F: BlockExecutorFactory + 'a, H> BlockAssemblerInput<'a, 'b, F, H> {
612 #[expect(clippy::too_many_arguments)]
614 pub const fn new(
615 evm_env: F::EvmEnv,
616 execution_ctx: F::ExecutionCtx<'a>,
617 parent: &'a SealedHeader<H>,
618 transactions: Vec<F::Transaction>,
619 output: &'b BlockExecutionResult<F::Receipt>,
620 execution_state: &'b BundleState,
621 state_provider: &'b dyn StateProvider,
622 state_root: B256,
623 block_access_list_hash: Option<B256>,
624 ) -> Self {
625 Self {
626 evm_env,
627 execution_ctx,
628 parent,
629 transactions,
630 output,
631 execution_state,
632 state_provider,
633 state_root,
634 block_access_list_hash,
635 }
636 }
637}
638
639#[auto_impl::auto_impl(&, Arc)]
641pub trait BlockAssembler<F: BlockExecutorFactory> {
642 type Block: Block;
644
645 fn assemble_block(
647 &self,
648 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
649 ) -> Result<Self::Block, BlockExecutionError>;
650}
651
652#[derive(Debug, Clone)]
654pub struct BlockBuilderOutcome<N: NodePrimitives> {
655 pub execution_result: BlockExecutionResult<N::Receipt>,
657 pub execution_state: BundleState,
659 pub hashed_state: HashedPostState,
661 pub trie_updates: TrieUpdates,
663 pub block: RecoveredBlock<N::Block>,
665 pub block_access_list: Option<DecodedBal>,
667}
668
669pub trait BlockBuilder: Sized {
671 type Primitives: NodePrimitives;
673 type Executor: BlockExecutor<
675 Transaction = TxTy<Self::Primitives>,
676 Receipt = ReceiptTy<Self::Primitives>,
677 Evm: Evm<Transaction: FromTxWithEncoded<TxTy<Self::Primitives>>>,
678 >;
679 type EvmEnv: EvmEnv;
681
682 fn evm_env(&self) -> &Self::EvmEnv;
684
685 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
687
688 fn execute_transaction_with_commit_condition(
691 &mut self,
692 tx: impl ExecutorTx<Self::Executor>,
693 f: impl FnOnce(
694 &evm2::TxResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::EvmTypes>,
695 evm2::TxStateChanges<'_>,
696 ) -> CommitChanges,
697 ) -> Result<Option<GasOutput>, BlockExecutionError>;
698
699 fn execute_transaction_with_result_closure(
702 &mut self,
703 tx: impl ExecutorTx<Self::Executor>,
704 f: impl FnOnce(&<Self::Executor as BlockExecutor>::TransactionResultWithState),
705 ) -> Result<GasOutput, BlockExecutionError>;
706
707 fn execute_transaction(
709 &mut self,
710 tx: impl ExecutorTx<Self::Executor>,
711 ) -> Result<GasOutput, BlockExecutionError> {
712 self.execute_transaction_with_commit_condition(tx, |_, _| CommitChanges::Yes)
713 .map(Option::unwrap_or_default)
714 }
715
716 fn finish(
718 self,
719 state_provider: impl StateProvider,
720 state_root_precomputed: Option<(B256, TrieUpdates)>,
721 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError> {
722 self.finish_with_state_root(state_provider, move |_| Ok(state_root_precomputed))
723 }
724
725 fn finish_with_state_root(
727 self,
728 state_provider: impl StateProvider,
729 state_root: impl FnOnce(
730 &BlockExecutionOutput<ReceiptTy<Self::Primitives>>,
731 ) -> Result<Option<(B256, TrieUpdates)>, BlockExecutionError>,
732 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
733
734 fn set_state_hook(&mut self, _hook: impl FnMut(StateUpdate) + Send + 'static) -> bool {
738 false
739 }
740
741 fn executor_mut(&mut self) -> &mut Self::Executor;
743
744 fn executor(&self) -> &Self::Executor;
746
747 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
749 self.executor_mut().evm_mut()
750 }
751
752 fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
754 self.executor().evm()
755 }
756
757 fn into_executor(self) -> Self::Executor;
759}
760
761#[derive(Debug)]
763pub struct BasicBlockBuilder<'a, F, Executor, Assembler, N>
764where
765 F: BlockExecutorFactory + 'a,
766 N: NodePrimitives,
767{
768 pub executor: Executor,
770 pub evm_env: F::EvmEnv,
772 pub transactions: Vec<Recovered<TxTy<N>>>,
774 pub ctx: F::ExecutionCtx<'a>,
776 pub parent: &'a SealedHeader<HeaderTy<N>>,
778 pub assembler: &'a Assembler,
780}
781
782impl<'a, F, Assembler, N> BasicBlockBuilder<'a, F, F::Executor<'a>, Assembler, N>
783where
784 F: BlockExecutorFactory<Transaction = TxTy<N>, Receipt = ReceiptTy<N>> + 'a,
785 Assembler: BlockAssembler<F, Block = N::Block> + 'a,
786 N: NodePrimitives,
787{
788 pub fn new(
790 executor_factory: &'a F,
791 assembler: &'a Assembler,
792 evm: F::Evm<'a>,
793 evm_env: F::EvmEnv,
794 parent: &'a SealedHeader<HeaderTy<N>>,
795 ctx: F::ExecutionCtx<'a>,
796 ) -> Self {
797 Self {
798 executor: executor_factory.create_executor(evm, ctx.clone()),
799 evm_env,
800 transactions: Vec::new(),
801 ctx,
802 parent,
803 assembler,
804 }
805 }
806}
807
808pub trait ExecutorTx<Executor: BlockExecutor>:
810 ExecutableTxParts<Recovered<<Executor::Evm as Evm>::Transaction>, Executor::Transaction>
811{
812}
813
814impl<T, Executor> ExecutorTx<Executor> for T
815where
816 Executor: BlockExecutor,
817 T: ExecutableTxParts<Recovered<<Executor::Evm as Evm>::Transaction>, Executor::Transaction>,
818{
819}
820
821impl<'a, F, Executor, Assembler, N> BlockBuilder
822 for BasicBlockBuilder<'a, F, Executor, Assembler, N>
823where
824 F: BlockExecutorFactory<Transaction = TxTy<N>, Receipt = ReceiptTy<N>>,
825 Executor: BlockExecutor<
826 Transaction = TxTy<N>,
827 Receipt = ReceiptTy<N>,
828 Evm: Evm<EvmTypes = F::EvmTypes>,
829 >,
830 Assembler: BlockAssembler<F, Block = N::Block>,
831 N: NodePrimitives,
832 TxTy<N>: Clone,
833 <<Executor as BlockExecutor>::Evm as Evm>::Transaction: FromTxWithEncoded<TxTy<N>>,
834{
835 type Primitives = N;
836 type Executor = Executor;
837 type EvmEnv = F::EvmEnv;
838
839 fn evm_env(&self) -> &Self::EvmEnv {
840 &self.evm_env
841 }
842
843 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
844 self.executor.apply_pre_execution_changes()
845 }
846
847 fn execute_transaction_with_commit_condition(
848 &mut self,
849 tx: impl ExecutorTx<Self::Executor>,
850 f: impl FnOnce(
851 &evm2::TxResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::EvmTypes>,
852 evm2::TxStateChanges<'_>,
853 ) -> CommitChanges,
854 ) -> Result<Option<GasOutput>, BlockExecutionError> {
855 let (tx_env, tx) = tx.into_parts();
856 if let Some(output) =
857 self.executor.execute_transaction_with_commit_condition((tx_env, &tx), f)?
858 {
859 self.transactions.push(Recovered::new_unchecked(tx.tx().clone(), *tx.signer()));
860 Ok(Some(output))
861 } else {
862 Ok(None)
863 }
864 }
865
866 fn execute_transaction_with_result_closure(
867 &mut self,
868 tx: impl ExecutorTx<Self::Executor>,
869 f: impl FnOnce(&<Self::Executor as BlockExecutor>::TransactionResultWithState),
870 ) -> Result<GasOutput, BlockExecutionError> {
871 let (tx_env, tx) = tx.into_parts();
872 let output = self.executor.execute_transaction_without_commit((tx_env, &tx))?;
873 f(&output);
874 let gas_output = self.executor.commit_transaction(output)?;
875 self.transactions.push(Recovered::new_unchecked(tx.tx().clone(), *tx.signer()));
876 Ok(gas_output)
877 }
878
879 fn finish_with_state_root(
880 self,
881 state_provider: impl StateProvider,
882 state_root: impl FnOnce(
883 &BlockExecutionOutput<ReceiptTy<Self::Primitives>>,
884 ) -> Result<Option<(B256, TrieUpdates)>, BlockExecutionError>,
885 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
886 let Self { executor, evm_env, transactions, ctx, parent, assembler } = self;
887
888 let (output, block_access_list) = executor.finish_with_block_access_list()?;
889 let block_access_list = block_access_list.map(|bal| {
890 let mut raw = Vec::new();
891 let hash = compute_block_access_list_hash_with_buf(&bal, &mut raw);
892 DecodedBal::new_unchecked(bal.into(), raw.into(), hash)
893 });
894 let block_access_list_hash = block_access_list.as_ref().map(DecodedBal::hash);
895 let hashed_state =
896 state_provider.hashed_post_state(&output.state).map_err(BlockExecutionError::other)?;
897 let (state_root, trie_updates) = match state_root(&output)? {
898 Some(precomputed) => precomputed,
899 None => state_provider
900 .state_root_with_updates(hashed_state.clone())
901 .map_err(BlockExecutionError::other)?,
902 };
903 let (transactions, senders) = transactions.into_iter().map(|tx| tx.into_parts()).unzip();
904
905 let block = assembler.assemble_block(BlockAssemblerInput {
906 evm_env,
907 execution_ctx: ctx,
908 parent,
909 transactions,
910 output: &output.result,
911 execution_state: &output.state,
912 state_provider: &state_provider,
913 state_root,
914 block_access_list_hash,
915 })?;
916 let block = RecoveredBlock::new_unhashed(block, senders);
917
918 Ok(BlockBuilderOutcome {
919 execution_result: output.result,
920 execution_state: output.state,
921 hashed_state,
922 trie_updates,
923 block,
924 block_access_list,
925 })
926 }
927
928 fn set_state_hook(&mut self, hook: impl FnMut(StateUpdate) + Send + 'static) -> bool {
929 self.executor.set_state_hook(hook)
930 }
931
932 fn executor_mut(&mut self) -> &mut Self::Executor {
933 &mut self.executor
934 }
935
936 fn executor(&self) -> &Self::Executor {
937 &self.executor
938 }
939
940 fn into_executor(self) -> Self::Executor {
941 self.executor
942 }
943}
944
945pub trait Executor<DB: Database>: Sized {
947 type Primitives: NodePrimitives;
949 type Error;
951
952 fn execute_one(
954 &mut self,
955 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
956 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
957
958 fn execute_one_with_state_hook<F>(
960 &mut self,
961 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
962 state_hook: F,
963 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
964 where
965 F: FnMut(StateUpdate) + Send + 'static;
966
967 fn execute(
969 mut self,
970 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
971 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
972 {
973 let result = self.execute_one(block)?;
974 let state = self.into_state();
975 Ok(BlockExecutionOutput::new(result, state))
976 }
977
978 fn execute_with_state_hook<F>(
981 mut self,
982 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
983 state_hook: F,
984 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
985 where
986 F: FnMut(StateUpdate) + Send + 'static,
987 {
988 let result = self.execute_one_with_state_hook(block, state_hook)?;
989 let state = self.into_state();
990 Ok(BlockExecutionOutput::new(result, state))
991 }
992
993 fn execute_with_state_closure<F>(
995 mut self,
996 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
997 mut f: F,
998 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
999 where
1000 F: FnMut(&BundleState),
1001 {
1002 let result = self.execute_one(block)?;
1003 let state = self.into_state();
1004 f(&state);
1005
1006 Ok(BlockExecutionOutput::new(result, state))
1007 }
1008
1009 fn execute_with_state_closure_always<F>(
1012 mut self,
1013 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1014 mut f: F,
1015 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1016 where
1017 F: FnMut(&BundleState),
1018 {
1019 let result = self.execute_one(block);
1020 let state = self.into_state();
1021 f(&state);
1022
1023 Ok(BlockExecutionOutput::new(result?, state))
1024 }
1025
1026 fn execute_batch<'a, I>(
1028 mut self,
1029 blocks: I,
1030 ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1031 where
1032 I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
1033 {
1034 let blocks_iter = blocks.into_iter();
1035 let capacity = blocks_iter.size_hint().0;
1036 let mut results = Vec::with_capacity(capacity);
1037 let mut first_block = None;
1038 for block in blocks_iter {
1039 if first_block.is_none() {
1040 first_block = Some(block.header().number());
1041 }
1042 results.push(self.execute_one(block)?);
1043 }
1044
1045 Ok(ExecutionOutcome::from_blocks(
1046 first_block.unwrap_or_default(),
1047 self.into_state(),
1048 results,
1049 ))
1050 }
1051
1052 fn size_hint(&self) -> usize;
1054
1055 fn into_state(self) -> BundleState;
1057
1058 fn take_bal(&mut self) -> Option<BlockAccessList>;
1060}
1061
1062#[cfg(feature = "std")]
1064#[expect(missing_debug_implementations)]
1065pub struct BasicBlockExecutor<Evm, DB: Database> {
1066 evm_config: Evm,
1067 batch_database: CacheDB<Db<DB>>,
1068 batch_state: BundleState,
1069 block_access_list: Option<BlockAccessList>,
1070}
1071
1072#[cfg(feature = "std")]
1073impl<Evm, DB: Database> BasicBlockExecutor<Evm, DB> {
1074 pub fn new(evm_config: Evm, database: DB) -> Self {
1076 Self {
1077 evm_config,
1078 batch_database: CacheDB::new(Db::new(database)),
1079 batch_state: BundleState::default(),
1080 block_access_list: None,
1081 }
1082 }
1083}
1084
1085#[cfg(feature = "std")]
1086impl<Evm, DB> BasicBlockExecutor<Evm, DB>
1087where
1088 Evm: ConfigureEvm,
1089 DB: Database,
1090{
1091 fn merge_batch_output(
1092 mut batch_state: BundleState,
1093 output: BlockExecutionOutput<ReceiptTy<Evm::Primitives>>,
1094 ) -> BlockExecutionOutput<ReceiptTy<Evm::Primitives>> {
1095 if batch_state.reverts.is_empty() {
1096 return output
1097 }
1098
1099 let result = output.result;
1100 let state = output.state;
1101 batch_state.extend(state);
1102 BlockExecutionOutput::new(result, batch_state)
1103 }
1104
1105 #[expect(clippy::type_complexity)]
1106 fn execute_block_with_database(
1107 evm_config: &Evm,
1108 block: &RecoveredBlock<BlockTy<Evm::Primitives>>,
1109 database: impl DynDatabase,
1110 ) -> Result<
1111 (BlockExecutionOutput<ReceiptTy<Evm::Primitives>>, Option<BlockAccessList>),
1112 BlockExecutionError,
1113 > {
1114 Self::execute_block_with_database_and_state_hook(evm_config, block, database, None)
1115 }
1116
1117 #[expect(clippy::type_complexity)]
1118 fn execute_block_with_database_and_state_hook(
1119 evm_config: &Evm,
1120 block: &RecoveredBlock<BlockTy<Evm::Primitives>>,
1121 database: impl DynDatabase,
1122 state_hook: Option<Box<dyn FnMut(StateUpdate) + Send>>,
1123 ) -> Result<
1124 (BlockExecutionOutput<ReceiptTy<Evm::Primitives>>, Option<BlockAccessList>),
1125 BlockExecutionError,
1126 > {
1127 let evm_env = evm_config.evm_env(block.header()).map_err(BlockExecutionError::other)?;
1128 let evm = evm_config.block_executor_factory().evm_with_env(database, evm_env);
1129 let ctx = evm_config
1130 .context_for_block(block.sealed_block())
1131 .map_err(BlockExecutionError::other)?;
1132 let mut executor = evm_config.block_executor_factory().create_executor(evm, ctx);
1133 if block.header().block_access_list_hash().is_some() {
1134 executor.enable_block_access_list_builder();
1135 }
1136 if let Some(hook) = state_hook &&
1137 !executor.set_state_hook(hook)
1138 {
1139 return Err(BlockExecutionError::msg("block executor does not support state hooks"))
1140 }
1141
1142 executor.apply_pre_execution_changes()?;
1143 for transaction in block.transactions_recovered() {
1144 executor.execute_transaction(transaction)?;
1145 }
1146 let (output, block_access_list) = executor.finish_with_block_access_list()?;
1147 Ok((output, block_access_list))
1148 }
1149}
1150
1151#[cfg(feature = "std")]
1152impl<Evm, DB> Executor<DB> for BasicBlockExecutor<Evm, DB>
1153where
1154 Evm: ConfigureEvm,
1155 DB: Database,
1156{
1157 type Primitives = Evm::Primitives;
1158 type Error = BlockExecutionError;
1159
1160 fn execute_one(
1161 &mut self,
1162 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1163 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1164 {
1165 let (output, block_access_list) =
1166 Self::execute_block_with_database(&self.evm_config, block, &mut self.batch_database)?;
1167 self.block_access_list = block_access_list;
1168 self.batch_database.commit_source(&BundleSource(&output.state));
1169
1170 let block_state = output.state;
1171 self.batch_state.extend(block_state);
1172
1173 Ok(output.result)
1174 }
1175
1176 fn execute_one_with_state_hook<F>(
1177 &mut self,
1178 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1179 state_hook: F,
1180 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1181 where
1182 F: FnMut(StateUpdate) + Send + 'static,
1183 {
1184 let (output, block_access_list) = Self::execute_block_with_database_and_state_hook(
1185 &self.evm_config,
1186 block,
1187 &mut self.batch_database,
1188 Some(Box::new(state_hook)),
1189 )?;
1190 self.block_access_list = block_access_list;
1191 self.batch_database.commit_source(&BundleSource(&output.state));
1192
1193 let block_state = output.state;
1194 self.batch_state.extend(block_state);
1195
1196 Ok(output.result)
1197 }
1198
1199 fn execute(
1200 self,
1201 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1202 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1203 {
1204 let Self { evm_config, batch_database, batch_state, .. } = self;
1205 let (output, _) = Self::execute_block_with_database(&evm_config, block, batch_database)?;
1206 Ok(Self::merge_batch_output(batch_state, output))
1207 }
1208
1209 fn execute_with_state_hook<F>(
1210 self,
1211 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1212 state_hook: F,
1213 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1214 where
1215 F: FnMut(StateUpdate) + Send + 'static,
1216 {
1217 let Self { evm_config, batch_database, batch_state, .. } = self;
1218 let (output, _) = Self::execute_block_with_database_and_state_hook(
1219 &evm_config,
1220 block,
1221 batch_database,
1222 Some(Box::new(state_hook)),
1223 )?;
1224 Ok(Self::merge_batch_output(batch_state, output))
1225 }
1226
1227 fn size_hint(&self) -> usize {
1228 self.batch_state.size_hint()
1229 }
1230
1231 fn into_state(self) -> BundleState {
1232 self.batch_state
1233 }
1234
1235 fn take_bal(&mut self) -> Option<BlockAccessList> {
1236 self.block_access_list.take()
1237 }
1238}
1239
1240#[cfg(not(feature = "std"))]
1242pub(crate) struct UnsupportedExecutor<N> {
1243 _marker: core::marker::PhantomData<N>,
1244}
1245
1246#[cfg(not(feature = "std"))]
1247impl<N> Default for UnsupportedExecutor<N> {
1248 fn default() -> Self {
1249 Self { _marker: core::marker::PhantomData }
1250 }
1251}
1252
1253#[cfg(not(feature = "std"))]
1254impl<N, DB> Executor<DB> for UnsupportedExecutor<N>
1255where
1256 N: NodePrimitives,
1257 DB: Database,
1258{
1259 type Primitives = N;
1260 type Error = BlockExecutionError;
1261
1262 fn execute_one(
1263 &mut self,
1264 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1265 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1266 {
1267 Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1268 }
1269
1270 fn execute(
1271 self,
1272 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1273 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1274 {
1275 Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1276 }
1277
1278 fn execute_one_with_state_hook<F>(
1279 &mut self,
1280 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1281 _state_hook: F,
1282 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1283 where
1284 F: FnMut(StateUpdate) + Send + 'static,
1285 {
1286 Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1287 }
1288
1289 fn execute_with_state_hook<F>(
1290 self,
1291 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
1292 _state_hook: F,
1293 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1294 where
1295 F: FnMut(StateUpdate) + Send + 'static,
1296 {
1297 Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1298 }
1299
1300 fn execute_batch<'a, I>(
1301 self,
1302 _blocks: I,
1303 ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
1304 where
1305 I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
1306 {
1307 Err(BlockExecutionError::msg("block execution is unsupported by this EVM configuration"))
1308 }
1309
1310 fn size_hint(&self) -> usize {
1311 0
1312 }
1313
1314 fn into_state(self) -> BundleState {
1315 BundleState::default()
1316 }
1317
1318 fn take_bal(&mut self) -> Option<BlockAccessList> {
1319 None
1320 }
1321}
1322
1323#[auto_impl::auto_impl(&)]
1327pub trait RecoveredTx<T> {
1328 fn tx(&self) -> &T;
1330
1331 fn signer(&self) -> &Address;
1333}
1334
1335impl<T> RecoveredTx<T> for Recovered<&T> {
1336 fn tx(&self) -> &T {
1337 self.inner()
1338 }
1339
1340 fn signer(&self) -> &Address {
1341 self.signer_ref()
1342 }
1343}
1344
1345impl<T> RecoveredTx<T> for Recovered<Arc<T>> {
1346 fn tx(&self) -> &T {
1347 self.inner().as_ref()
1348 }
1349
1350 fn signer(&self) -> &Address {
1351 self.signer_ref()
1352 }
1353}
1354
1355impl<T> RecoveredTx<T> for Recovered<T> {
1356 fn tx(&self) -> &T {
1357 self.inner()
1358 }
1359
1360 fn signer(&self) -> &Address {
1361 self.signer_ref()
1362 }
1363}
1364
1365impl<Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithEncoded<T> {
1366 fn tx(&self) -> &Tx {
1367 self.1.tx()
1368 }
1369
1370 fn signer(&self) -> &Address {
1371 self.1.signer()
1372 }
1373}
1374
1375impl<L, R, Tx> RecoveredTx<Tx> for Either<L, R>
1376where
1377 L: RecoveredTx<Tx>,
1378 R: RecoveredTx<Tx>,
1379{
1380 fn tx(&self) -> &Tx {
1381 match self {
1382 Self::Left(l) => l.tx(),
1383 Self::Right(r) => r.tx(),
1384 }
1385 }
1386
1387 fn signer(&self) -> &Address {
1388 match self {
1389 Self::Left(l) => l.signer(),
1390 Self::Right(r) => r.signer(),
1391 }
1392 }
1393}
1394
1395impl<Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for Arc<T> {
1396 fn tx(&self) -> &Tx {
1397 (**self).tx()
1398 }
1399
1400 fn signer(&self) -> &Address {
1401 (**self).signer()
1402 }
1403}
1404
1405pub trait ExecutableTxParts<TxEnv, T> {
1408 type Recovered: RecoveredTx<T>;
1410
1411 fn into_parts(self) -> (TxEnv, Self::Recovered);
1414}
1415
1416pub trait FromRecoveredTx<T> {
1418 fn from_recovered_tx(tx: Recovered<T>) -> Self;
1420}
1421
1422impl<T> FromRecoveredTx<T> for evm2::ethereum::TxEnvelope
1423where
1424 Self: From<T>,
1425{
1426 fn from_recovered_tx(tx: Recovered<T>) -> Self {
1427 tx.into_inner().into()
1428 }
1429}
1430
1431impl<T, TxEnv> FromRecoveredTx<T> for Recovered<TxEnv>
1432where
1433 TxEnv: FromRecoveredTx<T>,
1434{
1435 fn from_recovered_tx(tx: Recovered<T>) -> Self {
1436 let signer = tx.signer();
1437 Self::new_unchecked(TxEnv::from_recovered_tx(tx), signer)
1438 }
1439}
1440
1441pub trait FromTxWithEncoded<T>: FromRecoveredTx<T> {
1444 fn from_tx_with_encoded(tx: WithEncoded<Recovered<T>>) -> Self
1447 where
1448 Self: Sized,
1449 {
1450 Self::from_recovered_tx(tx.1)
1451 }
1452}
1453
1454impl<T> FromTxWithEncoded<T> for evm2::ethereum::TxEnvelope where Self: From<T> {}
1455
1456impl<T, TxEnv> FromTxWithEncoded<T> for Recovered<TxEnv>
1457where
1458 TxEnv: FromTxWithEncoded<T>,
1459{
1460 fn from_tx_with_encoded(tx: WithEncoded<Recovered<T>>) -> Self {
1461 let signer = tx.1.signer();
1462 Self::new_unchecked(TxEnv::from_tx_with_encoded(tx), signer)
1463 }
1464}
1465
1466pub trait IntoTxEnv<TxEnv> {
1468 fn into_tx_env(self) -> TxEnv;
1470}
1471
1472impl<T, TxEnv> IntoTxEnv<TxEnv> for Recovered<T>
1473where
1474 TxEnv: FromRecoveredTx<T>,
1475{
1476 fn into_tx_env(self) -> TxEnv {
1477 TxEnv::from_recovered_tx(self)
1478 }
1479}
1480
1481impl<T, TxEnv> IntoTxEnv<TxEnv> for WithEncoded<Recovered<T>>
1482where
1483 TxEnv: FromTxWithEncoded<T>,
1484{
1485 fn into_tx_env(self) -> TxEnv {
1486 TxEnv::from_tx_with_encoded(self)
1487 }
1488}
1489
1490pub trait TransactionEnvMut: Transaction + Clone {
1496 fn set_gas_limit(&mut self, gas_limit: u64);
1498
1499 fn set_access_list(&mut self, access_list: AccessList);
1501}
1502
1503impl TransactionEnvMut for evm2::ethereum::TxEnvelope {
1504 fn set_gas_limit(&mut self, gas_limit: u64) {
1505 match self {
1506 Self::Legacy(tx) => tx.gas_limit = gas_limit,
1507 Self::Eip2930(tx) => tx.gas_limit = gas_limit,
1508 Self::Eip1559(tx) => tx.gas_limit = gas_limit,
1509 Self::Eip4844(tx) => AsMut::<TxEip4844>::as_mut(tx).gas_limit = gas_limit,
1510 Self::Eip7702(tx) => tx.gas_limit = gas_limit,
1511 }
1512 }
1513
1514 fn set_access_list(&mut self, access_list: AccessList) {
1515 match self {
1516 Self::Legacy(tx) => {
1519 let TxLegacy { chain_id, nonce, gas_price, gas_limit, to, value, input } =
1520 core::mem::take(tx);
1521 *self = Self::Eip2930(TxEip2930 {
1522 chain_id: chain_id.unwrap_or_default(),
1523 nonce,
1524 gas_price,
1525 gas_limit,
1526 to,
1527 value,
1528 access_list,
1529 input,
1530 });
1531 }
1532 Self::Eip2930(tx) => tx.access_list = access_list,
1533 Self::Eip1559(tx) => tx.access_list = access_list,
1534 Self::Eip4844(tx) => AsMut::<TxEip4844>::as_mut(tx).access_list = access_list,
1535 Self::Eip7702(tx) => tx.access_list = access_list,
1536 }
1537 }
1538}
1539
1540impl<T: Clone, TxEnv> ExecutableTxParts<TxEnv, T> for Recovered<T>
1541where
1542 TxEnv: FromRecoveredTx<T>,
1543{
1544 type Recovered = Self;
1545
1546 fn into_parts(self) -> (TxEnv, Self) {
1547 (TxEnv::from_recovered_tx(self.clone()), self)
1548 }
1549}
1550
1551impl<T: Clone, TxEnv> ExecutableTxParts<TxEnv, T> for Recovered<&T>
1552where
1553 TxEnv: FromRecoveredTx<T>,
1554{
1555 type Recovered = Self;
1556
1557 fn into_parts(self) -> (TxEnv, Self) {
1558 (
1559 TxEnv::from_recovered_tx(Recovered::new_unchecked(
1560 (*self.inner()).clone(),
1561 *self.signer_ref(),
1562 )),
1563 self,
1564 )
1565 }
1566}
1567
1568impl<T: Clone, TxEnv> ExecutableTxParts<TxEnv, T> for WithEncoded<Recovered<T>>
1569where
1570 TxEnv: FromTxWithEncoded<T>,
1571{
1572 type Recovered = Self;
1573
1574 fn into_parts(self) -> (TxEnv, Self) {
1575 (TxEnv::from_tx_with_encoded(self.clone()), self)
1576 }
1577}
1578
1579impl<TxEnv, Tx, T> ExecutableTxParts<TxEnv, Tx> for (TxEnv, T)
1580where
1581 T: RecoveredTx<Tx>,
1582{
1583 type Recovered = T;
1584
1585 fn into_parts(self) -> (TxEnv, Self::Recovered) {
1586 self
1587 }
1588}
1589
1590impl<L, R, TxEnv, T> ExecutableTxParts<TxEnv, T> for Either<L, R>
1591where
1592 L: ExecutableTxParts<TxEnv, T>,
1593 R: ExecutableTxParts<TxEnv, T>,
1594{
1595 type Recovered = Either<L::Recovered, R::Recovered>;
1596
1597 fn into_parts(self) -> (TxEnv, Self::Recovered) {
1598 match self {
1599 Self::Left(l) => {
1600 let (tx_env, recovered) = l.into_parts();
1601 (tx_env, Either::Left(recovered))
1602 }
1603 Self::Right(r) => {
1604 let (tx_env, recovered) = r.into_parts();
1605 (tx_env, Either::Right(recovered))
1606 }
1607 }
1608 }
1609}
1610
1611pub trait ExecutableTxFor<Evm: ConfigureEvm>:
1614 ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
1615{
1616}
1617
1618impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
1619 T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
1620{
1621}
1622
1623#[derive(Debug)]
1628pub struct WithTxEnv<TxEnv, T> {
1629 pub tx_env: TxEnv,
1631 pub tx: Arc<T>,
1633}
1634
1635impl<TxEnv, T> WithTxEnv<TxEnv, T> {
1636 pub fn new<Tx, InnerTx>(tx: Tx) -> Self
1639 where
1640 Tx: ExecutableTxParts<TxEnv, InnerTx, Recovered = T>,
1641 {
1642 let (tx_env, tx) = tx.into_parts();
1643 Self { tx_env, tx: Arc::new(tx) }
1644 }
1645}
1646
1647impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
1648 fn clone(&self) -> Self {
1649 Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
1650 }
1651}
1652
1653impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
1654 fn tx(&self) -> &Tx {
1655 self.tx.tx()
1656 }
1657
1658 fn signer(&self) -> &Address {
1659 self.tx.signer()
1660 }
1661}
1662
1663impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
1664 type Recovered = Arc<T>;
1665
1666 fn into_parts(self) -> (TxEnv, Self::Recovered) {
1667 (self.tx_env, self.tx)
1668 }
1669}
1670
1671#[cfg(test)]
1672mod tests {
1673 use super::*;
1674 use alloy_eips::eip2930::AccessListItem;
1675 use alloy_primitives::{Bytes, TxKind, U256};
1676
1677 #[test]
1678 fn traced_inspector_forwards_inspector_config() {
1679 struct NoSteps;
1680 impl<T: evm2::EvmTypesHost> evm2::Inspector<T> for NoSteps {
1681 fn inspector_config(&self) -> evm2::InspectorConfig {
1682 evm2::InspectorConfig::without_steps()
1683 }
1684 }
1685
1686 assert_eq!(
1688 evm2::Inspector::<evm2::BaseEvmTypes>::inspector_config(&TracedInspector(NoSteps)),
1689 evm2::InspectorConfig::without_steps(),
1690 );
1691 }
1692
1693 #[test]
1694 fn transaction_env_mut_upgrades_legacy_with_access_list() {
1695 let mut tx = evm2::ethereum::TxEnvelope::Legacy(TxLegacy {
1696 chain_id: Some(1),
1697 nonce: 2,
1698 gas_price: 3,
1699 gas_limit: 4,
1700 to: TxKind::Call(Address::with_last_byte(5)),
1701 value: U256::from(6),
1702 input: Bytes::from_static(&[7]),
1703 });
1704 let access_list = AccessList(vec![AccessListItem {
1705 address: Address::with_last_byte(8),
1706 storage_keys: vec![B256::with_last_byte(9)],
1707 }]);
1708
1709 tx.set_gas_limit(10);
1710 tx.set_access_list(access_list.clone());
1711
1712 assert_eq!(
1713 tx,
1714 evm2::ethereum::TxEnvelope::Eip2930(TxEip2930 {
1715 chain_id: 1,
1716 nonce: 2,
1717 gas_price: 3,
1718 gas_limit: 10,
1719 to: TxKind::Call(Address::with_last_byte(5)),
1720 value: U256::from(6),
1721 access_list,
1722 input: Bytes::from_static(&[7]),
1723 })
1724 );
1725 }
1726
1727 #[test]
1728 fn recovered_tx_wrappers_preserve_transaction_and_signer() {
1729 let transaction = TxLegacy { nonce: 7, ..Default::default() };
1730 let signer = Address::with_last_byte(42);
1731 let recovered = Recovered::new_unchecked(transaction.clone(), signer);
1732
1733 let assert_parts = |tx: &dyn RecoveredTx<TxLegacy>| {
1734 assert_eq!(tx.tx(), &transaction);
1735 assert_eq!(tx.signer(), &signer);
1736 };
1737
1738 assert_parts(&recovered);
1739 assert_parts(&&recovered);
1740 assert_parts(&Recovered::new_unchecked(&transaction, signer));
1741 assert_parts(&Recovered::new_unchecked(Arc::new(transaction.clone()), signer));
1742 assert_parts(&Arc::new(recovered.clone()));
1743 assert_parts(&WithEncoded::new(Bytes::new(), recovered.clone()));
1744 assert_parts(&Either::<_, Recovered<TxLegacy>>::Left(recovered.clone()));
1745 assert_parts(&Either::<Recovered<TxLegacy>, _>::Right(recovered.clone()));
1746 assert_parts(&WithTxEnv { tx_env: (), tx: Arc::new(recovered) });
1747 }
1748}