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