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 = state.hashed_post_state(&db.bundle_state);
518 let (state_root, trie_updates) = match state_root_precomputed {
519 Some(precomputed) => precomputed,
520 None => state
521 .state_root_with_updates(hashed_state.clone())
522 .map_err(BlockExecutionError::other)?,
523 };
524
525 let (transactions, senders) =
526 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
527
528 let block = self.assembler.assemble_block(BlockAssemblerInput {
529 evm_env,
530 execution_ctx: self.ctx,
531 parent: self.parent,
532 transactions,
533 output: &result,
534 bundle_state: &db.bundle_state,
535 state_provider: &state,
536 state_root,
537 block_access_list_hash,
538 })?;
539
540 let block = RecoveredBlock::new_unhashed(block, senders);
541
542 Ok(BlockBuilderOutcome {
543 execution_result: result,
544 hashed_state,
545 trie_updates,
546 block,
547 block_access_list,
548 })
549 }
550
551 fn executor_mut(&mut self) -> &mut Self::Executor {
552 &mut self.executor
553 }
554
555 fn executor(&self) -> &Self::Executor {
556 &self.executor
557 }
558
559 fn into_executor(self) -> Self::Executor {
560 self.executor
561 }
562}
563
564#[expect(missing_debug_implementations)]
567pub struct BasicBlockExecutor<F, DB> {
568 pub(crate) strategy_factory: F,
570 pub(crate) db: State<DB>,
572}
573
574impl<F, DB: Database> BasicBlockExecutor<F, DB> {
575 pub fn new(strategy_factory: F, db: DB) -> Self {
577 let db = State::builder().with_database(db).with_bundle_update().build();
578 Self { strategy_factory, db }
579 }
580}
581
582impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
583where
584 F: ConfigureEvm,
585 DB: Database,
586{
587 type Primitives = F::Primitives;
588 type Error = BlockExecutionError;
589
590 fn execute_one(
591 &mut self,
592 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
593 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
594 {
595 let mut executor = self
596 .strategy_factory
597 .executor_for_block(&mut self.db, block)
598 .map_err(BlockExecutionError::other)?;
599
600 let has_bal = block.header().block_access_list_hash().is_some();
601
602 if has_bal {
603 executor.evm_mut().db_mut().bal_state.bal_builder = Some(Bal::new());
604 } else {
605 executor.evm_mut().db_mut().bal_state.bal_builder = None;
606 }
607
608 executor.apply_pre_execution_changes()?;
609
610 if has_bal {
611 executor.evm_mut().db_mut().bump_bal_index();
612 }
613
614 for tx in block.transactions_recovered() {
615 executor.execute_transaction(tx)?;
616 if has_bal {
617 executor.evm_mut().db_mut().bump_bal_index();
618 }
619 }
620
621 let result = executor.apply_post_execution_changes()?;
622
623 self.db.merge_transitions(BundleRetention::Reverts);
624
625 Ok(result)
626 }
627
628 fn execute_one_with_state_hook<H>(
629 &mut self,
630 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
631 state_hook: H,
632 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
633 where
634 H: OnStateHook + 'static,
635 {
636 let mut executor = self
637 .strategy_factory
638 .executor_for_block(&mut self.db, block)
639 .map_err(BlockExecutionError::other)?;
640
641 executor.evm_mut().db_mut().set_state_hook(Some(Box::new(state_hook)));
642
643 let result = executor.execute_block(block.transactions_recovered());
644
645 self.db.set_state_hook(None);
646 self.db.merge_transitions(BundleRetention::Reverts);
647
648 result
649 }
650
651 fn into_state(self) -> State<DB> {
652 self.db
653 }
654
655 fn size_hint(&self) -> usize {
656 self.db.bundle_state.size_hint()
657 }
658
659 fn take_bal(&mut self) -> Option<BlockAccessList> {
660 self.db.take_built_alloy_bal()
661 }
662}
663
664pub trait ExecutableTxFor<Evm: ConfigureEvm>:
667 ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
668{
669}
670
671impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
672 T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
673{
674}
675
676#[derive(Debug)]
681pub struct WithTxEnv<TxEnv, T> {
682 pub tx_env: TxEnv,
684 pub tx: Arc<T>,
686}
687
688impl<TxEnv, T> WithTxEnv<TxEnv, T> {
689 pub fn new<Tx, InnerTx>(tx: Tx) -> Self
692 where
693 Tx: ExecutableTxParts<TxEnv, InnerTx, Recovered = T>,
694 {
695 let (tx_env, tx) = tx.into_parts();
696 Self { tx_env, tx: Arc::new(tx) }
697 }
698}
699
700impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
701 fn clone(&self) -> Self {
702 Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
703 }
704}
705
706impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
707 fn tx(&self) -> &Tx {
708 self.tx.tx()
709 }
710
711 fn signer(&self) -> &Address {
712 self.tx.signer()
713 }
714}
715
716impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
717 type Recovered = Arc<T>;
718
719 fn into_parts(self) -> (TxEnv, Self::Recovered) {
720 (self.tx_env, self.tx)
721 }
722}
723
724#[cfg(test)]
725mod tests {
726 use super::*;
727 use core::marker::PhantomData;
728 use reth_ethereum_primitives::EthPrimitives;
729 use revm::database::{CacheDB, EmptyDB};
730
731 #[derive(Clone, Debug, Default)]
732 struct TestExecutorProvider;
733
734 impl TestExecutorProvider {
735 fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
736 where
737 DB: Database,
738 {
739 TestExecutor(PhantomData)
740 }
741 }
742
743 struct TestExecutor<DB>(PhantomData<DB>);
744
745 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
746 type Primitives = EthPrimitives;
747 type Error = BlockExecutionError;
748
749 fn execute_one(
750 &mut self,
751 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
752 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
753 {
754 Err(BlockExecutionError::msg("execution unavailable for tests"))
755 }
756
757 fn execute_one_with_state_hook<F>(
758 &mut self,
759 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
760 _state_hook: F,
761 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
762 where
763 F: OnStateHook + 'static,
764 {
765 Err(BlockExecutionError::msg("execution unavailable for tests"))
766 }
767
768 fn into_state(self) -> State<DB> {
769 unreachable!()
770 }
771
772 fn size_hint(&self) -> usize {
773 0
774 }
775
776 fn take_bal(&mut self) -> Option<BlockAccessList> {
777 None
778 }
779 }
780
781 #[test]
782 fn test_provider() {
783 let provider = TestExecutorProvider;
784 let db = CacheDB::<EmptyDB>::default();
785 let executor = provider.executor(db);
786 let _ = executor.execute(&Default::default());
787 }
788}