1use crate::{ConfigureEvm, Database, OnStateHook, TxEnvFor};
4use alloc::{boxed::Box, sync::Arc, vec::Vec};
5use alloy_consensus::{BlockHeader, Header};
6use alloy_eip7928::{bal::DecodedBal, compute_block_access_list_hash_with_buf, 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<DecodedBal>,
320}
321
322pub trait BlockBuilder {
329 type Primitives: NodePrimitives;
331 type Executor: BlockExecutor<
333 Transaction = TxTy<Self::Primitives>,
334 Receipt = ReceiptTy<Self::Primitives>,
335 >;
336
337 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
339
340 fn execute_transaction_with_commit_condition(
343 &mut self,
344 tx: impl ExecutorTx<Self::Executor>,
345 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
346 ) -> Result<Option<GasOutput>, BlockExecutionError>;
347
348 fn execute_transaction_with_result_closure(
351 &mut self,
352 tx: impl ExecutorTx<Self::Executor>,
353 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result),
354 ) -> Result<GasOutput, BlockExecutionError> {
355 self.execute_transaction_with_commit_condition(tx, |res| {
356 f(res);
357 CommitChanges::Yes
358 })
359 .map(Option::unwrap_or_default)
360 }
361
362 fn execute_transaction(
365 &mut self,
366 tx: impl ExecutorTx<Self::Executor>,
367 ) -> Result<GasOutput, BlockExecutionError> {
368 self.execute_transaction_with_result_closure(tx, |_| ())
369 }
370
371 fn finish(
377 self,
378 state_provider: impl StateProvider,
379 state_root_precomputed: Option<(B256, TrieUpdates)>,
380 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
381
382 fn executor_mut(&mut self) -> &mut Self::Executor;
384
385 fn executor(&self) -> &Self::Executor;
387
388 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
390 self.executor_mut().evm_mut()
391 }
392
393 fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
395 self.executor().evm()
396 }
397
398 fn into_executor(self) -> Self::Executor;
400}
401
402#[derive(Debug)]
404pub struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
405where
406 F: BlockExecutorFactory,
407{
408 pub executor: Executor,
410 pub transactions: Vec<Recovered<TxTy<N>>>,
412 pub ctx: F::ExecutionCtx<'a>,
414 pub parent: &'a SealedHeader<HeaderTy<N>>,
416 pub assembler: Builder,
418}
419
420pub trait ExecutorTx<Executor: BlockExecutor> {
422 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>);
424}
425
426impl<Executor: BlockExecutor> ExecutorTx<Executor>
427 for WithEncoded<Recovered<Executor::Transaction>>
428{
429 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
430 (self.to_tx_env(), self.1)
431 }
432}
433
434impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
435 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Self) {
436 (self.to_tx_env(), self)
437 }
438}
439
440impl<Executor: BlockExecutor> ExecutorTx<Executor>
441 for (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>)
442{
443 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
444 self
445 }
446}
447
448impl<Executor> ExecutorTx<Executor>
449 for WithTxEnv<<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>>
450where
451 Executor: BlockExecutor<Transaction: Clone>,
452{
453 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
454 (self.tx_env, Arc::unwrap_or_clone(self.tx))
455 }
456}
457
458impl<'a, F, DB, Executor, Builder, N> BlockBuilder
459 for BasicBlockBuilder<'a, F, Executor, Builder, N>
460where
461 F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
462 Executor: BlockExecutor<
463 Evm: Evm<
464 Spec = <F::EvmFactory as EvmFactory>::Spec,
465 HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
466 BlockEnv = <F::EvmFactory as EvmFactory>::BlockEnv,
467 DB = &'a mut State<DB>,
468 >,
469 Transaction = N::SignedTx,
470 Receipt = N::Receipt,
471 >,
472 DB: Database + 'a,
473 Builder: BlockAssembler<F, Block = N::Block>,
474 N: NodePrimitives,
475{
476 type Primitives = N;
477 type Executor = Executor;
478
479 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
480 self.executor.apply_pre_execution_changes()?;
481 self.executor.evm_mut().db_mut().bump_bal_index();
482
483 Ok(())
484 }
485
486 fn execute_transaction_with_commit_condition(
487 &mut self,
488 tx: impl ExecutorTx<Self::Executor>,
489 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
490 ) -> Result<Option<GasOutput>, BlockExecutionError> {
491 let (tx_env, tx) = tx.into_parts();
492 if let Some(gas_used) =
493 self.executor.execute_transaction_with_commit_condition((tx_env, &tx), f)?
494 {
495 self.transactions.push(tx);
496 self.executor.evm_mut().db_mut().bump_bal_index();
497 Ok(Some(gas_used))
498 } else {
499 Ok(None)
500 }
501 }
502
503 fn finish(
504 self,
505 state: impl StateProvider,
506 state_root_precomputed: Option<(B256, TrieUpdates)>,
507 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
508 let (evm, result) = self.executor.finish()?;
509 let (db, evm_env) = evm.finish();
510
511 db.merge_transitions(BundleRetention::Reverts);
513
514 let block_access_list = db.take_built_alloy_bal().map(|bal| {
516 let mut raw = Vec::new();
517 let hash = compute_block_access_list_hash_with_buf(&bal, &mut raw);
518 DecodedBal::new_unchecked(bal.into(), raw.into(), hash)
519 });
520 let block_access_list_hash = block_access_list.as_ref().map(DecodedBal::hash);
521
522 let hashed_state =
523 state.hashed_post_state(&db.bundle_state).map_err(BlockExecutionError::other)?;
524 let (state_root, trie_updates) = match state_root_precomputed {
525 Some(precomputed) => precomputed,
526 None => state
527 .state_root_with_updates(hashed_state.clone())
528 .map_err(BlockExecutionError::other)?,
529 };
530
531 let (transactions, senders) =
532 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
533
534 let block = self.assembler.assemble_block(BlockAssemblerInput {
535 evm_env,
536 execution_ctx: self.ctx,
537 parent: self.parent,
538 transactions,
539 output: &result,
540 bundle_state: &db.bundle_state,
541 state_provider: &state,
542 state_root,
543 block_access_list_hash,
544 })?;
545
546 let block = RecoveredBlock::new_unhashed(block, senders);
547
548 Ok(BlockBuilderOutcome {
549 execution_result: result,
550 hashed_state,
551 trie_updates,
552 block,
553 block_access_list,
554 })
555 }
556
557 fn executor_mut(&mut self) -> &mut Self::Executor {
558 &mut self.executor
559 }
560
561 fn executor(&self) -> &Self::Executor {
562 &self.executor
563 }
564
565 fn into_executor(self) -> Self::Executor {
566 self.executor
567 }
568}
569
570#[expect(missing_debug_implementations)]
573pub struct BasicBlockExecutor<F, DB> {
574 pub(crate) strategy_factory: F,
576 pub(crate) db: State<DB>,
578}
579
580impl<F, DB: Database> BasicBlockExecutor<F, DB> {
581 pub fn new(strategy_factory: F, db: DB) -> Self {
583 let db = State::builder().with_database(db).with_bundle_update().build();
584 Self { strategy_factory, db }
585 }
586}
587
588impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
589where
590 F: ConfigureEvm,
591 DB: Database,
592{
593 type Primitives = F::Primitives;
594 type Error = BlockExecutionError;
595
596 fn execute_one(
597 &mut self,
598 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
599 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
600 {
601 let mut executor = self
602 .strategy_factory
603 .executor_for_block(&mut self.db, block)
604 .map_err(BlockExecutionError::other)?;
605
606 let has_bal = block.header().block_access_list_hash().is_some();
607
608 if has_bal {
609 executor.evm_mut().db_mut().bal_state.bal_builder = Some(Bal::new());
610 } else {
611 executor.evm_mut().db_mut().bal_state.bal_builder = None;
612 }
613
614 executor.apply_pre_execution_changes()?;
615
616 if has_bal {
617 executor.evm_mut().db_mut().bump_bal_index();
618 }
619
620 for tx in block.transactions_recovered() {
621 executor.execute_transaction(tx)?;
622 if has_bal {
623 executor.evm_mut().db_mut().bump_bal_index();
624 }
625 }
626
627 let result = executor.apply_post_execution_changes()?;
628
629 self.db.merge_transitions(BundleRetention::Reverts);
630
631 Ok(result)
632 }
633
634 fn execute_one_with_state_hook<H>(
635 &mut self,
636 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
637 state_hook: H,
638 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
639 where
640 H: OnStateHook + 'static,
641 {
642 let mut executor = self
643 .strategy_factory
644 .executor_for_block(&mut self.db, block)
645 .map_err(BlockExecutionError::other)?;
646
647 executor.evm_mut().db_mut().set_state_hook(Some(Box::new(state_hook)));
648
649 let result = executor.execute_block(block.transactions_recovered());
650
651 self.db.set_state_hook(None);
652 self.db.merge_transitions(BundleRetention::Reverts);
653
654 result
655 }
656
657 fn into_state(self) -> State<DB> {
658 self.db
659 }
660
661 fn size_hint(&self) -> usize {
662 self.db.bundle_size_hint()
663 }
664
665 fn take_bal(&mut self) -> Option<BlockAccessList> {
666 self.db.take_built_alloy_bal()
667 }
668}
669
670pub trait ExecutableTxFor<Evm: ConfigureEvm>:
673 ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
674{
675}
676
677impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
678 T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
679{
680}
681
682#[derive(Debug)]
687pub struct WithTxEnv<TxEnv, T> {
688 pub tx_env: TxEnv,
690 pub tx: Arc<T>,
692}
693
694impl<TxEnv, T> WithTxEnv<TxEnv, T> {
695 pub fn new<Tx, InnerTx>(tx: Tx) -> Self
698 where
699 Tx: ExecutableTxParts<TxEnv, InnerTx, Recovered = T>,
700 {
701 let (tx_env, tx) = tx.into_parts();
702 Self { tx_env, tx: Arc::new(tx) }
703 }
704}
705
706impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
707 fn clone(&self) -> Self {
708 Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
709 }
710}
711
712impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
713 fn tx(&self) -> &Tx {
714 self.tx.tx()
715 }
716
717 fn signer(&self) -> &Address {
718 self.tx.signer()
719 }
720}
721
722impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
723 type Recovered = Arc<T>;
724
725 fn into_parts(self) -> (TxEnv, Self::Recovered) {
726 (self.tx_env, self.tx)
727 }
728}
729
730#[cfg(test)]
731mod tests {
732
733 use super::*;
734 use core::marker::PhantomData;
735 use reth_ethereum_primitives::EthPrimitives;
736 use revm::database::InMemoryDB;
737
738 #[derive(Clone, Debug, Default)]
739 struct TestExecutorProvider;
740
741 impl TestExecutorProvider {
742 fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
743 where
744 DB: Database,
745 {
746 TestExecutor(PhantomData)
747 }
748 }
749
750 struct TestExecutor<DB>(PhantomData<DB>);
751
752 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
753 type Primitives = EthPrimitives;
754 type Error = BlockExecutionError;
755
756 fn execute_one(
757 &mut self,
758 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
759 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
760 {
761 Err(BlockExecutionError::msg("execution unavailable for tests"))
762 }
763
764 fn execute_one_with_state_hook<F>(
765 &mut self,
766 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
767 _state_hook: F,
768 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
769 where
770 F: OnStateHook + 'static,
771 {
772 Err(BlockExecutionError::msg("execution unavailable for tests"))
773 }
774
775 fn into_state(self) -> State<DB> {
776 unreachable!()
777 }
778
779 fn size_hint(&self) -> usize {
780 0
781 }
782
783 fn take_bal(&mut self) -> Option<BlockAccessList> {
784 None
785 }
786 }
787
788 #[test]
789 fn test_provider() {
790 let provider = TestExecutorProvider;
791 let db = InMemoryDB::default();
792 let executor = provider.executor(db);
793 let _ = executor.execute(&Default::default());
794 }
795}