1use crate::{ConfigureEvm, Database, OnStateHook, TxEnvFor};
4use alloc::{boxed::Box, sync::Arc, vec::Vec};
5use alloy_consensus::{BlockHeader, Header};
6use alloy_eip7928::{compute_block_access_list_hash, BlockAccessList};
7use alloy_eips::eip2718::WithEncoded;
8pub use alloy_evm::block::{BlockExecutor, BlockExecutorFactory, GasOutput};
9use alloy_evm::{
10 block::{CommitChanges, ExecutableTxParts},
11 Evm, EvmEnv, EvmFactory, RecoveredTx, ToTxEnv,
12};
13use alloy_primitives::{Address, B256};
14pub use reth_execution_errors::{
15 BlockExecutionError, BlockValidationError, InternalBlockExecutionError,
16};
17use reth_execution_types::BlockExecutionResult;
18pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
19use reth_primitives_traits::{
20 Block, HeaderTy, NodePrimitives, ReceiptTy, Recovered, RecoveredBlock, SealedHeader, TxTy,
21};
22use reth_storage_api::StateProvider;
23pub use reth_storage_errors::provider::ProviderError;
24use reth_trie_common::{updates::TrieUpdates, HashedPostState};
25use revm::{
26 database::{states::bundle_state::BundleRetention, BundleState, State},
27 state::bal::Bal,
28};
29
30pub trait Executor<DB: Database>: Sized {
33 type Primitives: NodePrimitives;
35 type Error;
37
38 fn execute_one(
40 &mut self,
41 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
42 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
43
44 fn execute_one_with_state_hook<F>(
47 &mut self,
48 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
49 state_hook: F,
50 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
51 where
52 F: OnStateHook + 'static;
53
54 fn execute(
62 mut self,
63 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
64 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
65 {
66 let result = self.execute_one(block)?;
67 let mut state = self.into_state();
68 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
69 }
70
71 fn execute_batch<'a, I>(
73 mut self,
74 blocks: I,
75 ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
76 where
77 I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
78 {
79 let blocks_iter = blocks.into_iter();
80 let capacity = blocks_iter.size_hint().0;
81 let mut results = Vec::with_capacity(capacity);
82 let mut first_block = None;
83 for block in blocks_iter {
84 if first_block.is_none() {
85 first_block = Some(block.header().number());
86 }
87 results.push(self.execute_one(block)?);
88 }
89
90 Ok(ExecutionOutcome::from_blocks(
91 first_block.unwrap_or_default(),
92 self.into_state().take_bundle(),
93 results,
94 ))
95 }
96
97 fn execute_with_state_closure<F>(
100 mut self,
101 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
102 mut f: F,
103 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
104 where
105 F: FnMut(&State<DB>),
106 {
107 let result = self.execute_one(block)?;
108 let mut state = self.into_state();
109 f(&state);
110 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
111 }
112
113 fn execute_with_state_closure_always<F>(
116 mut self,
117 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
118 mut f: F,
119 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
120 where
121 F: FnMut(&State<DB>),
122 {
123 let result = self.execute_one(block);
124 let mut state = self.into_state();
125 f(&state);
126
127 Ok(BlockExecutionOutput { state: state.take_bundle(), result: result? })
128 }
129
130 fn execute_with_state_hook<F>(
133 mut self,
134 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
135 state_hook: F,
136 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
137 where
138 F: OnStateHook + 'static,
139 {
140 let result = self.execute_one_with_state_hook(block, state_hook)?;
141 let mut state = self.into_state();
142 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
143 }
144
145 fn into_state(self) -> State<DB>;
147
148 fn size_hint(&self) -> usize;
152
153 fn take_bal(&mut self) -> Option<BlockAccessList>;
155}
156
157#[derive(derive_more::Debug)]
195#[non_exhaustive]
196pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
197 pub evm_env:
201 EvmEnv<<F::EvmFactory as EvmFactory>::Spec, <F::EvmFactory as EvmFactory>::BlockEnv>,
202 pub execution_ctx: F::ExecutionCtx<'a>,
204 pub parent: &'a SealedHeader<H>,
206 pub transactions: Vec<F::Transaction>,
208 pub output: &'b BlockExecutionResult<F::Receipt>,
210 pub bundle_state: &'a BundleState,
212 #[debug(skip)]
214 pub state_provider: &'b dyn StateProvider,
215 pub state_root: B256,
217 pub block_access_list_hash: Option<B256>,
219}
220
221impl<'a, 'b, F: BlockExecutorFactory, H> BlockAssemblerInput<'a, 'b, F, H> {
222 #[expect(clippy::too_many_arguments)]
224 pub fn new(
225 evm_env: EvmEnv<
226 <F::EvmFactory as EvmFactory>::Spec,
227 <F::EvmFactory as EvmFactory>::BlockEnv,
228 >,
229 execution_ctx: F::ExecutionCtx<'a>,
230 parent: &'a SealedHeader<H>,
231 transactions: Vec<F::Transaction>,
232 output: &'b BlockExecutionResult<F::Receipt>,
233 bundle_state: &'a BundleState,
234 state_provider: &'b dyn StateProvider,
235 state_root: B256,
236 block_access_list_hash: Option<B256>,
237 ) -> Self {
238 Self {
239 evm_env,
240 execution_ctx,
241 parent,
242 transactions,
243 output,
244 bundle_state,
245 state_provider,
246 state_root,
247 block_access_list_hash,
248 }
249 }
250}
251
252#[auto_impl::auto_impl(&, Arc)]
295pub trait BlockAssembler<F: BlockExecutorFactory> {
296 type Block: Block;
298
299 fn assemble_block(
301 &self,
302 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
303 ) -> Result<Self::Block, BlockExecutionError>;
304}
305
306#[derive(Debug, Clone)]
308pub struct BlockBuilderOutcome<N: NodePrimitives> {
309 pub execution_result: BlockExecutionResult<N::Receipt>,
311 pub hashed_state: HashedPostState,
313 pub trie_updates: TrieUpdates,
315 pub block: RecoveredBlock<N::Block>,
317 pub block_access_list: Option<BlockAccessList>,
319}
320
321pub trait BlockBuilder {
328 type Primitives: NodePrimitives;
330 type Executor: BlockExecutor<
332 Transaction = TxTy<Self::Primitives>,
333 Receipt = ReceiptTy<Self::Primitives>,
334 >;
335
336 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
338
339 fn execute_transaction_with_commit_condition(
342 &mut self,
343 tx: impl ExecutorTx<Self::Executor>,
344 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
345 ) -> Result<Option<GasOutput>, BlockExecutionError>;
346
347 fn execute_transaction_with_result_closure(
350 &mut self,
351 tx: impl ExecutorTx<Self::Executor>,
352 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result),
353 ) -> Result<GasOutput, BlockExecutionError> {
354 self.execute_transaction_with_commit_condition(tx, |res| {
355 f(res);
356 CommitChanges::Yes
357 })
358 .map(Option::unwrap_or_default)
359 }
360
361 fn execute_transaction(
364 &mut self,
365 tx: impl ExecutorTx<Self::Executor>,
366 ) -> Result<GasOutput, BlockExecutionError> {
367 self.execute_transaction_with_result_closure(tx, |_| ())
368 }
369
370 fn finish(
376 self,
377 state_provider: impl StateProvider,
378 state_root_precomputed: Option<(B256, TrieUpdates)>,
379 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
380
381 fn executor_mut(&mut self) -> &mut Self::Executor;
383
384 fn executor(&self) -> &Self::Executor;
386
387 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
389 self.executor_mut().evm_mut()
390 }
391
392 fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
394 self.executor().evm()
395 }
396
397 fn into_executor(self) -> Self::Executor;
399}
400
401#[derive(Debug)]
403pub struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
404where
405 F: BlockExecutorFactory,
406{
407 pub executor: Executor,
409 pub transactions: Vec<Recovered<TxTy<N>>>,
411 pub ctx: F::ExecutionCtx<'a>,
413 pub parent: &'a SealedHeader<HeaderTy<N>>,
415 pub assembler: Builder,
417}
418
419pub trait ExecutorTx<Executor: BlockExecutor> {
421 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>);
423}
424
425impl<Executor: BlockExecutor> ExecutorTx<Executor>
426 for WithEncoded<Recovered<Executor::Transaction>>
427{
428 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
429 (self.to_tx_env(), self.1)
430 }
431}
432
433impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
434 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Self) {
435 (self.to_tx_env(), self)
436 }
437}
438
439impl<Executor: BlockExecutor> ExecutorTx<Executor>
440 for (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>)
441{
442 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
443 self
444 }
445}
446
447impl<Executor> ExecutorTx<Executor>
448 for WithTxEnv<<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>>
449where
450 Executor: BlockExecutor<Transaction: Clone>,
451{
452 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
453 (self.tx_env, Arc::unwrap_or_clone(self.tx))
454 }
455}
456
457impl<'a, F, DB, Executor, Builder, N> BlockBuilder
458 for BasicBlockBuilder<'a, F, Executor, Builder, N>
459where
460 F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
461 Executor: BlockExecutor<
462 Evm: Evm<
463 Spec = <F::EvmFactory as EvmFactory>::Spec,
464 HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
465 BlockEnv = <F::EvmFactory as EvmFactory>::BlockEnv,
466 DB = &'a mut State<DB>,
467 >,
468 Transaction = N::SignedTx,
469 Receipt = N::Receipt,
470 >,
471 DB: Database + 'a,
472 Builder: BlockAssembler<F, Block = N::Block>,
473 N: NodePrimitives,
474{
475 type Primitives = N;
476 type Executor = Executor;
477
478 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
479 self.executor.apply_pre_execution_changes()?;
480 self.executor.evm_mut().db_mut().bump_bal_index();
481
482 Ok(())
483 }
484
485 fn execute_transaction_with_commit_condition(
486 &mut self,
487 tx: impl ExecutorTx<Self::Executor>,
488 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
489 ) -> Result<Option<GasOutput>, BlockExecutionError> {
490 let (tx_env, tx) = tx.into_parts();
491 if let Some(gas_used) =
492 self.executor.execute_transaction_with_commit_condition((tx_env, &tx), f)?
493 {
494 self.transactions.push(tx);
495 self.executor.evm_mut().db_mut().bump_bal_index();
496 Ok(Some(gas_used))
497 } else {
498 Ok(None)
499 }
500 }
501
502 fn finish(
503 self,
504 state: impl StateProvider,
505 state_root_precomputed: Option<(B256, TrieUpdates)>,
506 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
507 let (evm, result) = self.executor.finish()?;
508 let (db, evm_env) = evm.finish();
509
510 db.merge_transitions(BundleRetention::Reverts);
512
513 let block_access_list = db.take_built_alloy_bal();
514 let block_access_list_hash =
515 block_access_list.as_ref().map(|bal| compute_block_access_list_hash(bal.as_slice()));
516
517 let hashed_state =
518 state.hashed_post_state(&db.bundle_state).map_err(BlockExecutionError::other)?;
519 let (state_root, trie_updates) = match state_root_precomputed {
520 Some(precomputed) => precomputed,
521 None => state
522 .state_root_with_updates(hashed_state.clone())
523 .map_err(BlockExecutionError::other)?,
524 };
525
526 let (transactions, senders) =
527 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
528
529 let block = self.assembler.assemble_block(BlockAssemblerInput {
530 evm_env,
531 execution_ctx: self.ctx,
532 parent: self.parent,
533 transactions,
534 output: &result,
535 bundle_state: &db.bundle_state,
536 state_provider: &state,
537 state_root,
538 block_access_list_hash,
539 })?;
540
541 let block = RecoveredBlock::new_unhashed(block, senders);
542
543 Ok(BlockBuilderOutcome {
544 execution_result: result,
545 hashed_state,
546 trie_updates,
547 block,
548 block_access_list,
549 })
550 }
551
552 fn executor_mut(&mut self) -> &mut Self::Executor {
553 &mut self.executor
554 }
555
556 fn executor(&self) -> &Self::Executor {
557 &self.executor
558 }
559
560 fn into_executor(self) -> Self::Executor {
561 self.executor
562 }
563}
564
565#[expect(missing_debug_implementations)]
568pub struct BasicBlockExecutor<F, DB> {
569 pub(crate) strategy_factory: F,
571 pub(crate) db: State<DB>,
573}
574
575impl<F, DB: Database> BasicBlockExecutor<F, DB> {
576 pub fn new(strategy_factory: F, db: DB) -> Self {
578 let db = State::builder().with_database(db).with_bundle_update().build();
579 Self { strategy_factory, db }
580 }
581}
582
583impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
584where
585 F: ConfigureEvm,
586 DB: Database,
587{
588 type Primitives = F::Primitives;
589 type Error = BlockExecutionError;
590
591 fn execute_one(
592 &mut self,
593 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
594 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
595 {
596 let mut executor = self
597 .strategy_factory
598 .executor_for_block(&mut self.db, block)
599 .map_err(BlockExecutionError::other)?;
600
601 let has_bal = block.header().block_access_list_hash().is_some();
602
603 if has_bal {
604 executor.evm_mut().db_mut().bal_state.bal_builder = Some(Bal::new());
605 } else {
606 executor.evm_mut().db_mut().bal_state.bal_builder = None;
607 }
608
609 executor.apply_pre_execution_changes()?;
610
611 if has_bal {
612 executor.evm_mut().db_mut().bump_bal_index();
613 }
614
615 for tx in block.transactions_recovered() {
616 executor.execute_transaction(tx)?;
617 if has_bal {
618 executor.evm_mut().db_mut().bump_bal_index();
619 }
620 }
621
622 let result = executor.apply_post_execution_changes()?;
623
624 self.db.merge_transitions(BundleRetention::Reverts);
625
626 Ok(result)
627 }
628
629 fn execute_one_with_state_hook<H>(
630 &mut self,
631 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
632 state_hook: H,
633 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
634 where
635 H: OnStateHook + 'static,
636 {
637 let mut executor = self
638 .strategy_factory
639 .executor_for_block(&mut self.db, block)
640 .map_err(BlockExecutionError::other)?;
641
642 executor.evm_mut().db_mut().set_state_hook(Some(Box::new(state_hook)));
643
644 let result = executor.execute_block(block.transactions_recovered());
645
646 self.db.set_state_hook(None);
647 self.db.merge_transitions(BundleRetention::Reverts);
648
649 result
650 }
651
652 fn into_state(self) -> State<DB> {
653 self.db
654 }
655
656 fn size_hint(&self) -> usize {
657 self.db.bundle_state.size_hint()
658 }
659
660 fn take_bal(&mut self) -> Option<BlockAccessList> {
661 self.db.take_built_alloy_bal()
662 }
663}
664
665pub trait ExecutableTxFor<Evm: ConfigureEvm>:
668 ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
669{
670}
671
672impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
673 T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
674{
675}
676
677#[derive(Debug)]
682pub struct WithTxEnv<TxEnv, T> {
683 pub tx_env: TxEnv,
685 pub tx: Arc<T>,
687}
688
689impl<TxEnv, T> WithTxEnv<TxEnv, T> {
690 pub fn new<Tx, InnerTx>(tx: Tx) -> Self
693 where
694 Tx: ExecutableTxParts<TxEnv, InnerTx, Recovered = T>,
695 {
696 let (tx_env, tx) = tx.into_parts();
697 Self { tx_env, tx: Arc::new(tx) }
698 }
699}
700
701impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
702 fn clone(&self) -> Self {
703 Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
704 }
705}
706
707impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
708 fn tx(&self) -> &Tx {
709 self.tx.tx()
710 }
711
712 fn signer(&self) -> &Address {
713 self.tx.signer()
714 }
715}
716
717impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
718 type Recovered = Arc<T>;
719
720 fn into_parts(self) -> (TxEnv, Self::Recovered) {
721 (self.tx_env, self.tx)
722 }
723}
724
725#[cfg(test)]
726mod tests {
727 use super::*;
728 use core::marker::PhantomData;
729 use reth_ethereum_primitives::EthPrimitives;
730 use revm::database::{CacheDB, EmptyDB};
731
732 #[derive(Clone, Debug, Default)]
733 struct TestExecutorProvider;
734
735 impl TestExecutorProvider {
736 fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
737 where
738 DB: Database,
739 {
740 TestExecutor(PhantomData)
741 }
742 }
743
744 struct TestExecutor<DB>(PhantomData<DB>);
745
746 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
747 type Primitives = EthPrimitives;
748 type Error = BlockExecutionError;
749
750 fn execute_one(
751 &mut self,
752 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
753 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
754 {
755 Err(BlockExecutionError::msg("execution unavailable for tests"))
756 }
757
758 fn execute_one_with_state_hook<F>(
759 &mut self,
760 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
761 _state_hook: F,
762 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
763 where
764 F: OnStateHook + 'static,
765 {
766 Err(BlockExecutionError::msg("execution unavailable for tests"))
767 }
768
769 fn into_state(self) -> State<DB> {
770 unreachable!()
771 }
772
773 fn size_hint(&self) -> usize {
774 0
775 }
776
777 fn take_bal(&mut self) -> Option<BlockAccessList> {
778 None
779 }
780 }
781
782 #[test]
783 fn test_provider() {
784 let provider = TestExecutorProvider;
785 let db = CacheDB::<EmptyDB>::default();
786 let executor = provider.executor(db);
787 let _ = executor.execute(&Default::default());
788 }
789}