1use crate::{ConfigureEvm, Database, OnStateHook, TxEnvFor};
4use alloc::{boxed::Box, vec::Vec};
5use alloy_consensus::{BlockHeader, Header};
6use alloy_eips::eip2718::WithEncoded;
7pub use alloy_evm::block::{BlockExecutor, BlockExecutorFactory};
8use alloy_evm::{
9 block::{CommitChanges, ExecutableTx},
10 Evm, EvmEnv, EvmFactory, RecoveredTx, ToTxEnv,
11};
12use alloy_primitives::{Address, B256};
13pub use reth_execution_errors::{
14 BlockExecutionError, BlockValidationError, InternalBlockExecutionError,
15};
16use reth_execution_types::BlockExecutionResult;
17pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
18use reth_primitives_traits::{
19 Block, HeaderTy, NodePrimitives, ReceiptTy, Recovered, RecoveredBlock, SealedHeader, TxTy,
20};
21use reth_storage_api::StateProvider;
22pub use reth_storage_errors::provider::ProviderError;
23use reth_trie_common::{updates::TrieUpdates, HashedPostState};
24use revm::{
25 context::result::ExecutionResult,
26 database::{states::bundle_state::BundleRetention, BundleState, State},
27};
28
29pub trait Executor<DB: Database>: Sized {
32 type Primitives: NodePrimitives;
34 type Error;
36
37 fn execute_one(
39 &mut self,
40 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
41 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
42
43 fn execute_one_with_state_hook<F>(
46 &mut self,
47 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
48 state_hook: F,
49 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
50 where
51 F: OnStateHook + 'static;
52
53 fn execute(
61 mut self,
62 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
63 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
64 {
65 let result = self.execute_one(block)?;
66 let mut state = self.into_state();
67 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
68 }
69
70 fn execute_batch<'a, I>(
72 mut self,
73 blocks: I,
74 ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
75 where
76 I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
77 {
78 let mut results = Vec::new();
79 let mut first_block = None;
80 for block in blocks {
81 if first_block.is_none() {
82 first_block = Some(block.header().number());
83 }
84 results.push(self.execute_one(block)?);
85 }
86
87 Ok(ExecutionOutcome::from_blocks(
88 first_block.unwrap_or_default(),
89 self.into_state().take_bundle(),
90 results,
91 ))
92 }
93
94 fn execute_with_state_closure<F>(
97 mut self,
98 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
99 mut f: F,
100 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
101 where
102 F: FnMut(&State<DB>),
103 {
104 let result = self.execute_one(block)?;
105 let mut state = self.into_state();
106 f(&state);
107 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
108 }
109
110 fn execute_with_state_closure_always<F>(
113 mut self,
114 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
115 mut f: F,
116 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
117 where
118 F: FnMut(&State<DB>),
119 {
120 let result = self.execute_one(block);
121 let mut state = self.into_state();
122 f(&state);
123
124 Ok(BlockExecutionOutput { state: state.take_bundle(), result: result? })
125 }
126
127 fn execute_with_state_hook<F>(
130 mut self,
131 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
132 state_hook: F,
133 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
134 where
135 F: OnStateHook + 'static,
136 {
137 let result = self.execute_one_with_state_hook(block, state_hook)?;
138 let mut state = self.into_state();
139 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
140 }
141
142 fn into_state(self) -> State<DB>;
144
145 fn size_hint(&self) -> usize;
149}
150
151#[derive(Debug, Clone)]
153pub struct ExecuteOutput<R> {
154 pub receipts: Vec<R>,
156 pub gas_used: u64,
158}
159
160#[derive(derive_more::Debug)]
196#[non_exhaustive]
197pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
198 pub evm_env: EvmEnv<<F::EvmFactory as EvmFactory>::Spec>,
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}
218
219impl<'a, 'b, F: BlockExecutorFactory, H> BlockAssemblerInput<'a, 'b, F, H> {
220 #[expect(clippy::too_many_arguments)]
222 pub fn new(
223 evm_env: EvmEnv<<F::EvmFactory as EvmFactory>::Spec>,
224 execution_ctx: F::ExecutionCtx<'a>,
225 parent: &'a SealedHeader<H>,
226 transactions: Vec<F::Transaction>,
227 output: &'b BlockExecutionResult<F::Receipt>,
228 bundle_state: &'a BundleState,
229 state_provider: &'b dyn StateProvider,
230 state_root: B256,
231 ) -> Self {
232 Self {
233 evm_env,
234 execution_ctx,
235 parent,
236 transactions,
237 output,
238 bundle_state,
239 state_provider,
240 state_root,
241 }
242 }
243}
244
245#[auto_impl::auto_impl(&, Arc)]
288pub trait BlockAssembler<F: BlockExecutorFactory> {
289 type Block: Block;
291
292 fn assemble_block(
294 &self,
295 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
296 ) -> Result<Self::Block, BlockExecutionError>;
297}
298
299#[derive(Debug, Clone)]
301pub struct BlockBuilderOutcome<N: NodePrimitives> {
302 pub execution_result: BlockExecutionResult<N::Receipt>,
304 pub hashed_state: HashedPostState,
306 pub trie_updates: TrieUpdates,
308 pub block: RecoveredBlock<N::Block>,
310}
311
312pub trait BlockBuilder {
319 type Primitives: NodePrimitives;
321 type Executor: BlockExecutor<
323 Transaction = TxTy<Self::Primitives>,
324 Receipt = ReceiptTy<Self::Primitives>,
325 >;
326
327 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
329
330 fn execute_transaction_with_commit_condition(
333 &mut self,
334 tx: impl ExecutorTx<Self::Executor>,
335 f: impl FnOnce(
336 &ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>,
337 ) -> CommitChanges,
338 ) -> Result<Option<u64>, BlockExecutionError>;
339
340 fn execute_transaction_with_result_closure(
343 &mut self,
344 tx: impl ExecutorTx<Self::Executor>,
345 f: impl FnOnce(&ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>),
346 ) -> Result<u64, BlockExecutionError> {
347 self.execute_transaction_with_commit_condition(tx, |res| {
348 f(res);
349 CommitChanges::Yes
350 })
351 .map(Option::unwrap_or_default)
352 }
353
354 fn execute_transaction(
357 &mut self,
358 tx: impl ExecutorTx<Self::Executor>,
359 ) -> Result<u64, BlockExecutionError> {
360 self.execute_transaction_with_result_closure(tx, |_| ())
361 }
362
363 fn finish(
365 self,
366 state_provider: impl StateProvider,
367 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
368
369 fn executor_mut(&mut self) -> &mut Self::Executor;
371
372 fn executor(&self) -> &Self::Executor;
374
375 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
377 self.executor_mut().evm_mut()
378 }
379
380 fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
382 self.executor().evm()
383 }
384
385 fn into_executor(self) -> Self::Executor;
387}
388
389#[derive(Debug)]
391pub struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
392where
393 F: BlockExecutorFactory,
394{
395 pub executor: Executor,
397 pub transactions: Vec<Recovered<TxTy<N>>>,
399 pub ctx: F::ExecutionCtx<'a>,
401 pub parent: &'a SealedHeader<HeaderTy<N>>,
403 pub assembler: Builder,
405}
406
407pub trait ExecutorTx<Executor: BlockExecutor> {
409 fn as_executable(&self) -> impl ExecutableTx<Executor>;
411
412 fn into_recovered(self) -> Recovered<Executor::Transaction>;
414}
415
416impl<Executor: BlockExecutor> ExecutorTx<Executor>
417 for WithEncoded<Recovered<Executor::Transaction>>
418{
419 fn as_executable(&self) -> impl ExecutableTx<Executor> {
420 self
421 }
422
423 fn into_recovered(self) -> Recovered<Executor::Transaction> {
424 self.1
425 }
426}
427
428impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
429 fn as_executable(&self) -> impl ExecutableTx<Executor> {
430 self
431 }
432
433 fn into_recovered(self) -> Self {
434 self
435 }
436}
437
438impl<T, Executor> ExecutorTx<Executor>
439 for WithTxEnv<<<Executor as BlockExecutor>::Evm as Evm>::Tx, T>
440where
441 T: ExecutorTx<Executor>,
442 Executor: BlockExecutor,
443 <<Executor as BlockExecutor>::Evm as Evm>::Tx: Clone,
444 Self: RecoveredTx<Executor::Transaction>,
445{
446 fn as_executable(&self) -> impl ExecutableTx<Executor> {
447 self
448 }
449
450 fn into_recovered(self) -> Recovered<Executor::Transaction> {
451 self.tx.into_recovered()
452 }
453}
454
455impl<'a, F, DB, Executor, Builder, N> BlockBuilder
456 for BasicBlockBuilder<'a, F, Executor, Builder, N>
457where
458 F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
459 Executor: BlockExecutor<
460 Evm: Evm<
461 Spec = <F::EvmFactory as EvmFactory>::Spec,
462 HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
463 DB = &'a mut State<DB>,
464 >,
465 Transaction = N::SignedTx,
466 Receipt = N::Receipt,
467 >,
468 DB: Database + 'a,
469 Builder: BlockAssembler<F, Block = N::Block>,
470 N: NodePrimitives,
471{
472 type Primitives = N;
473 type Executor = Executor;
474
475 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
476 self.executor.apply_pre_execution_changes()
477 }
478
479 fn execute_transaction_with_commit_condition(
480 &mut self,
481 tx: impl ExecutorTx<Self::Executor>,
482 f: impl FnOnce(
483 &ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>,
484 ) -> CommitChanges,
485 ) -> Result<Option<u64>, BlockExecutionError> {
486 if let Some(gas_used) =
487 self.executor.execute_transaction_with_commit_condition(tx.as_executable(), f)?
488 {
489 self.transactions.push(tx.into_recovered());
490 Ok(Some(gas_used))
491 } else {
492 Ok(None)
493 }
494 }
495
496 fn finish(
497 self,
498 state: impl StateProvider,
499 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
500 let (evm, result) = self.executor.finish()?;
501 let (db, evm_env) = evm.finish();
502
503 db.merge_transitions(BundleRetention::Reverts);
505
506 let hashed_state = state.hashed_post_state(&db.bundle_state);
508 let (state_root, trie_updates) = state
509 .state_root_with_updates(hashed_state.clone())
510 .map_err(BlockExecutionError::other)?;
511
512 let (transactions, senders) =
513 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
514
515 let block = self.assembler.assemble_block(BlockAssemblerInput {
516 evm_env,
517 execution_ctx: self.ctx,
518 parent: self.parent,
519 transactions,
520 output: &result,
521 bundle_state: &db.bundle_state,
522 state_provider: &state,
523 state_root,
524 })?;
525
526 let block = RecoveredBlock::new_unhashed(block, senders);
527
528 Ok(BlockBuilderOutcome { execution_result: result, hashed_state, trie_updates, block })
529 }
530
531 fn executor_mut(&mut self) -> &mut Self::Executor {
532 &mut self.executor
533 }
534
535 fn executor(&self) -> &Self::Executor {
536 &self.executor
537 }
538
539 fn into_executor(self) -> Self::Executor {
540 self.executor
541 }
542}
543
544#[expect(missing_debug_implementations)]
547pub struct BasicBlockExecutor<F, DB> {
548 pub(crate) strategy_factory: F,
550 pub(crate) db: State<DB>,
552}
553
554impl<F, DB: Database> BasicBlockExecutor<F, DB> {
555 pub fn new(strategy_factory: F, db: DB) -> Self {
557 let db =
558 State::builder().with_database(db).with_bundle_update().without_state_clear().build();
559 Self { strategy_factory, db }
560 }
561}
562
563impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
564where
565 F: ConfigureEvm,
566 DB: Database,
567{
568 type Primitives = F::Primitives;
569 type Error = BlockExecutionError;
570
571 fn execute_one(
572 &mut self,
573 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
574 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
575 {
576 let result = self
577 .strategy_factory
578 .executor_for_block(&mut self.db, block)
579 .map_err(BlockExecutionError::other)?
580 .execute_block(block.transactions_recovered())?;
581
582 self.db.merge_transitions(BundleRetention::Reverts);
583
584 Ok(result)
585 }
586
587 fn execute_one_with_state_hook<H>(
588 &mut self,
589 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
590 state_hook: H,
591 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
592 where
593 H: OnStateHook + 'static,
594 {
595 let result = self
596 .strategy_factory
597 .executor_for_block(&mut self.db, block)
598 .map_err(BlockExecutionError::other)?
599 .with_state_hook(Some(Box::new(state_hook)))
600 .execute_block(block.transactions_recovered())?;
601
602 self.db.merge_transitions(BundleRetention::Reverts);
603
604 Ok(result)
605 }
606
607 fn into_state(self) -> State<DB> {
608 self.db
609 }
610
611 fn size_hint(&self) -> usize {
612 self.db.bundle_state.size_hint()
613 }
614}
615
616pub trait ExecutableTxFor<Evm: ConfigureEvm>:
619 ToTxEnv<TxEnvFor<Evm>> + RecoveredTx<TxTy<Evm::Primitives>>
620{
621}
622
623impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
624 T: ToTxEnv<TxEnvFor<Evm>> + RecoveredTx<TxTy<Evm::Primitives>>
625{
626}
627
628#[derive(Debug, Clone)]
630pub struct WithTxEnv<TxEnv, T> {
631 pub tx_env: TxEnv,
633 pub tx: T,
635}
636
637impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
638 fn tx(&self) -> &Tx {
639 self.tx.tx()
640 }
641
642 fn signer(&self) -> &Address {
643 self.tx.signer()
644 }
645}
646
647impl<TxEnv: Clone, T> ToTxEnv<TxEnv> for WithTxEnv<TxEnv, T> {
648 fn to_tx_env(&self) -> TxEnv {
649 self.tx_env.clone()
650 }
651}
652
653#[cfg(test)]
654mod tests {
655 use super::*;
656 use crate::Address;
657 use alloy_consensus::constants::KECCAK_EMPTY;
658 use alloy_evm::block::state_changes::balance_increment_state;
659 use alloy_primitives::{address, map::HashMap, U256};
660 use core::marker::PhantomData;
661 use reth_ethereum_primitives::EthPrimitives;
662 use revm::{
663 database::{CacheDB, EmptyDB},
664 state::AccountInfo,
665 };
666
667 #[derive(Clone, Debug, Default)]
668 struct TestExecutorProvider;
669
670 impl TestExecutorProvider {
671 fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
672 where
673 DB: Database,
674 {
675 TestExecutor(PhantomData)
676 }
677 }
678
679 struct TestExecutor<DB>(PhantomData<DB>);
680
681 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
682 type Primitives = EthPrimitives;
683 type Error = BlockExecutionError;
684
685 fn execute_one(
686 &mut self,
687 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
688 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
689 {
690 Err(BlockExecutionError::msg("execution unavailable for tests"))
691 }
692
693 fn execute_one_with_state_hook<F>(
694 &mut self,
695 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
696 _state_hook: F,
697 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
698 where
699 F: OnStateHook + 'static,
700 {
701 Err(BlockExecutionError::msg("execution unavailable for tests"))
702 }
703
704 fn into_state(self) -> State<DB> {
705 unreachable!()
706 }
707
708 fn size_hint(&self) -> usize {
709 0
710 }
711 }
712
713 #[test]
714 fn test_provider() {
715 let provider = TestExecutorProvider;
716 let db = CacheDB::<EmptyDB>::default();
717 let executor = provider.executor(db);
718 let _ = executor.execute(&Default::default());
719 }
720
721 fn setup_state_with_account(
722 addr: Address,
723 balance: u128,
724 nonce: u64,
725 ) -> State<CacheDB<EmptyDB>> {
726 let db = CacheDB::<EmptyDB>::default();
727 let mut state = State::builder().with_database(db).with_bundle_update().build();
728
729 let account_info = AccountInfo {
730 balance: U256::from(balance),
731 nonce,
732 code_hash: KECCAK_EMPTY,
733 code: None,
734 };
735 state.insert_account(addr, account_info);
736 state
737 }
738
739 #[test]
740 fn test_balance_increment_state_zero() {
741 let addr = address!("0x1000000000000000000000000000000000000000");
742 let mut state = setup_state_with_account(addr, 100, 1);
743
744 let mut increments = HashMap::default();
745 increments.insert(addr, 0);
746
747 let result = balance_increment_state(&increments, &mut state).unwrap();
748 assert!(result.is_empty(), "Zero increments should be ignored");
749 }
750
751 #[test]
752 fn test_balance_increment_state_empty_increments_map() {
753 let mut state = State::builder()
754 .with_database(CacheDB::<EmptyDB>::default())
755 .with_bundle_update()
756 .build();
757
758 let increments = HashMap::default();
759 let result = balance_increment_state(&increments, &mut state).unwrap();
760 assert!(result.is_empty(), "Empty increments map should return empty state");
761 }
762
763 #[test]
764 fn test_balance_increment_state_multiple_valid_increments() {
765 let addr1 = address!("0x1000000000000000000000000000000000000000");
766 let addr2 = address!("0x2000000000000000000000000000000000000000");
767
768 let mut state = setup_state_with_account(addr1, 100, 1);
769
770 let account2 =
771 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
772 state.insert_account(addr2, account2);
773
774 let mut increments = HashMap::default();
775 increments.insert(addr1, 50);
776 increments.insert(addr2, 100);
777
778 let result = balance_increment_state(&increments, &mut state).unwrap();
779
780 assert_eq!(result.len(), 2);
781 assert_eq!(result.get(&addr1).unwrap().info.balance, U256::from(100));
782 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
783 }
784
785 #[test]
786 fn test_balance_increment_state_mixed_zero_and_nonzero_increments() {
787 let addr1 = address!("0x1000000000000000000000000000000000000000");
788 let addr2 = address!("0x2000000000000000000000000000000000000000");
789
790 let mut state = setup_state_with_account(addr1, 100, 1);
791
792 let account2 =
793 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
794 state.insert_account(addr2, account2);
795
796 let mut increments = HashMap::default();
797 increments.insert(addr1, 0);
798 increments.insert(addr2, 100);
799
800 let result = balance_increment_state(&increments, &mut state).unwrap();
801
802 assert_eq!(result.len(), 1, "Only non-zero increments should be included");
803 assert!(!result.contains_key(&addr1), "Zero increment account should not be included");
804 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
805 }
806}