1use crate::{ConfigureEvm, Database, OnStateHook, TxEnvFor};
4use alloc::{boxed::Box, sync::Arc, 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#[deprecated(note = "Use reth_execution_types::BlockExecutionResult or BlockExecutionOutput")]
157#[derive(Debug, Clone)]
158pub struct ExecuteOutput<R> {
159 pub receipts: Vec<R>,
161 pub gas_used: u64,
163}
164
165#[derive(derive_more::Debug)]
201#[non_exhaustive]
202pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
203 pub evm_env:
207 EvmEnv<<F::EvmFactory as EvmFactory>::Spec, <F::EvmFactory as EvmFactory>::BlockEnv>,
208 pub execution_ctx: F::ExecutionCtx<'a>,
210 pub parent: &'a SealedHeader<H>,
212 pub transactions: Vec<F::Transaction>,
214 pub output: &'b BlockExecutionResult<F::Receipt>,
216 pub bundle_state: &'a BundleState,
218 #[debug(skip)]
220 pub state_provider: &'b dyn StateProvider,
221 pub state_root: B256,
223}
224
225impl<'a, 'b, F: BlockExecutorFactory, H> BlockAssemblerInput<'a, 'b, F, H> {
226 #[expect(clippy::too_many_arguments)]
228 pub fn new(
229 evm_env: EvmEnv<
230 <F::EvmFactory as EvmFactory>::Spec,
231 <F::EvmFactory as EvmFactory>::BlockEnv,
232 >,
233 execution_ctx: F::ExecutionCtx<'a>,
234 parent: &'a SealedHeader<H>,
235 transactions: Vec<F::Transaction>,
236 output: &'b BlockExecutionResult<F::Receipt>,
237 bundle_state: &'a BundleState,
238 state_provider: &'b dyn StateProvider,
239 state_root: B256,
240 ) -> Self {
241 Self {
242 evm_env,
243 execution_ctx,
244 parent,
245 transactions,
246 output,
247 bundle_state,
248 state_provider,
249 state_root,
250 }
251 }
252}
253
254#[auto_impl::auto_impl(&, Arc)]
297pub trait BlockAssembler<F: BlockExecutorFactory> {
298 type Block: Block;
300
301 fn assemble_block(
303 &self,
304 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
305 ) -> Result<Self::Block, BlockExecutionError>;
306}
307
308#[derive(Debug, Clone)]
310pub struct BlockBuilderOutcome<N: NodePrimitives> {
311 pub execution_result: BlockExecutionResult<N::Receipt>,
313 pub hashed_state: HashedPostState,
315 pub trie_updates: TrieUpdates,
317 pub block: RecoveredBlock<N::Block>,
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(
345 &ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>,
346 ) -> CommitChanges,
347 ) -> Result<Option<u64>, BlockExecutionError>;
348
349 fn execute_transaction_with_result_closure(
352 &mut self,
353 tx: impl ExecutorTx<Self::Executor>,
354 f: impl FnOnce(&ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>),
355 ) -> Result<u64, BlockExecutionError> {
356 self.execute_transaction_with_commit_condition(tx, |res| {
357 f(res);
358 CommitChanges::Yes
359 })
360 .map(Option::unwrap_or_default)
361 }
362
363 fn execute_transaction(
366 &mut self,
367 tx: impl ExecutorTx<Self::Executor>,
368 ) -> Result<u64, BlockExecutionError> {
369 self.execute_transaction_with_result_closure(tx, |_| ())
370 }
371
372 fn finish(
374 self,
375 state_provider: impl StateProvider,
376 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
377
378 fn executor_mut(&mut self) -> &mut Self::Executor;
380
381 fn executor(&self) -> &Self::Executor;
383
384 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
386 self.executor_mut().evm_mut()
387 }
388
389 fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
391 self.executor().evm()
392 }
393
394 fn into_executor(self) -> Self::Executor;
396}
397
398#[derive(Debug)]
400pub struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
401where
402 F: BlockExecutorFactory,
403{
404 pub executor: Executor,
406 pub transactions: Vec<Recovered<TxTy<N>>>,
408 pub ctx: F::ExecutionCtx<'a>,
410 pub parent: &'a SealedHeader<HeaderTy<N>>,
412 pub assembler: Builder,
414}
415
416pub trait ExecutorTx<Executor: BlockExecutor> {
418 fn as_executable(&self) -> impl ExecutableTx<Executor>;
420
421 fn into_recovered(self) -> Recovered<Executor::Transaction>;
423}
424
425impl<Executor: BlockExecutor> ExecutorTx<Executor>
426 for WithEncoded<Recovered<Executor::Transaction>>
427{
428 fn as_executable(&self) -> impl ExecutableTx<Executor> {
429 self
430 }
431
432 fn into_recovered(self) -> Recovered<Executor::Transaction> {
433 self.1
434 }
435}
436
437impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
438 fn as_executable(&self) -> impl ExecutableTx<Executor> {
439 self
440 }
441
442 fn into_recovered(self) -> Self {
443 self
444 }
445}
446
447impl<T, Executor> ExecutorTx<Executor>
448 for WithTxEnv<<<Executor as BlockExecutor>::Evm as Evm>::Tx, T>
449where
450 T: ExecutorTx<Executor> + Clone,
451 Executor: BlockExecutor,
452 <<Executor as BlockExecutor>::Evm as Evm>::Tx: Clone,
453 Self: RecoveredTx<Executor::Transaction>,
454{
455 fn as_executable(&self) -> impl ExecutableTx<Executor> {
456 self
457 }
458
459 fn into_recovered(self) -> Recovered<Executor::Transaction> {
460 Arc::unwrap_or_clone(self.tx).into_recovered()
461 }
462}
463
464impl<'a, F, DB, Executor, Builder, N> BlockBuilder
465 for BasicBlockBuilder<'a, F, Executor, Builder, N>
466where
467 F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
468 Executor: BlockExecutor<
469 Evm: Evm<
470 Spec = <F::EvmFactory as EvmFactory>::Spec,
471 HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
472 BlockEnv = <F::EvmFactory as EvmFactory>::BlockEnv,
473 DB = &'a mut State<DB>,
474 >,
475 Transaction = N::SignedTx,
476 Receipt = N::Receipt,
477 >,
478 DB: Database + 'a,
479 Builder: BlockAssembler<F, Block = N::Block>,
480 N: NodePrimitives,
481{
482 type Primitives = N;
483 type Executor = Executor;
484
485 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
486 self.executor.apply_pre_execution_changes()
487 }
488
489 fn execute_transaction_with_commit_condition(
490 &mut self,
491 tx: impl ExecutorTx<Self::Executor>,
492 f: impl FnOnce(
493 &ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>,
494 ) -> CommitChanges,
495 ) -> Result<Option<u64>, BlockExecutionError> {
496 if let Some(gas_used) =
497 self.executor.execute_transaction_with_commit_condition(tx.as_executable(), f)?
498 {
499 self.transactions.push(tx.into_recovered());
500 Ok(Some(gas_used))
501 } else {
502 Ok(None)
503 }
504 }
505
506 fn finish(
507 self,
508 state: impl StateProvider,
509 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
510 let (evm, result) = self.executor.finish()?;
511 let (db, evm_env) = evm.finish();
512
513 db.merge_transitions(BundleRetention::Reverts);
515
516 let hashed_state = state.hashed_post_state(&db.bundle_state);
518 let (state_root, trie_updates) = state
519 .state_root_with_updates(hashed_state.clone())
520 .map_err(BlockExecutionError::other)?;
521
522 let (transactions, senders) =
523 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
524
525 let block = self.assembler.assemble_block(BlockAssemblerInput {
526 evm_env,
527 execution_ctx: self.ctx,
528 parent: self.parent,
529 transactions,
530 output: &result,
531 bundle_state: &db.bundle_state,
532 state_provider: &state,
533 state_root,
534 })?;
535
536 let block = RecoveredBlock::new_unhashed(block, senders);
537
538 Ok(BlockBuilderOutcome { execution_result: result, hashed_state, trie_updates, block })
539 }
540
541 fn executor_mut(&mut self) -> &mut Self::Executor {
542 &mut self.executor
543 }
544
545 fn executor(&self) -> &Self::Executor {
546 &self.executor
547 }
548
549 fn into_executor(self) -> Self::Executor {
550 self.executor
551 }
552}
553
554#[expect(missing_debug_implementations)]
557pub struct BasicBlockExecutor<F, DB> {
558 pub(crate) strategy_factory: F,
560 pub(crate) db: State<DB>,
562}
563
564impl<F, DB: Database> BasicBlockExecutor<F, DB> {
565 pub fn new(strategy_factory: F, db: DB) -> Self {
567 let db =
568 State::builder().with_database(db).with_bundle_update().without_state_clear().build();
569 Self { strategy_factory, db }
570 }
571}
572
573impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
574where
575 F: ConfigureEvm,
576 DB: Database,
577{
578 type Primitives = F::Primitives;
579 type Error = BlockExecutionError;
580
581 fn execute_one(
582 &mut self,
583 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
584 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
585 {
586 let result = self
587 .strategy_factory
588 .executor_for_block(&mut self.db, block)
589 .map_err(BlockExecutionError::other)?
590 .execute_block(block.transactions_recovered())?;
591
592 self.db.merge_transitions(BundleRetention::Reverts);
593
594 Ok(result)
595 }
596
597 fn execute_one_with_state_hook<H>(
598 &mut self,
599 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
600 state_hook: H,
601 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
602 where
603 H: OnStateHook + 'static,
604 {
605 let result = self
606 .strategy_factory
607 .executor_for_block(&mut self.db, block)
608 .map_err(BlockExecutionError::other)?
609 .with_state_hook(Some(Box::new(state_hook)))
610 .execute_block(block.transactions_recovered())?;
611
612 self.db.merge_transitions(BundleRetention::Reverts);
613
614 Ok(result)
615 }
616
617 fn into_state(self) -> State<DB> {
618 self.db
619 }
620
621 fn size_hint(&self) -> usize {
622 self.db.bundle_state.size_hint()
623 }
624}
625
626pub trait ExecutableTxFor<Evm: ConfigureEvm>:
629 ToTxEnv<TxEnvFor<Evm>> + RecoveredTx<TxTy<Evm::Primitives>>
630{
631}
632
633impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
634 T: ToTxEnv<TxEnvFor<Evm>> + RecoveredTx<TxTy<Evm::Primitives>>
635{
636}
637
638#[derive(Debug, Clone)]
640pub struct WithTxEnv<TxEnv, T> {
641 pub tx_env: TxEnv,
643 pub tx: Arc<T>,
645}
646
647impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
648 fn tx(&self) -> &Tx {
649 self.tx.tx()
650 }
651
652 fn signer(&self) -> &Address {
653 self.tx.signer()
654 }
655}
656
657impl<TxEnv: Clone, T> ToTxEnv<TxEnv> for WithTxEnv<TxEnv, T> {
658 fn to_tx_env(&self) -> TxEnv {
659 self.tx_env.clone()
660 }
661}
662
663#[cfg(test)]
664mod tests {
665 use super::*;
666 use crate::Address;
667 use alloy_consensus::constants::KECCAK_EMPTY;
668 use alloy_evm::block::state_changes::balance_increment_state;
669 use alloy_primitives::{address, map::HashMap, U256};
670 use core::marker::PhantomData;
671 use reth_ethereum_primitives::EthPrimitives;
672 use revm::{
673 database::{CacheDB, EmptyDB},
674 state::AccountInfo,
675 };
676
677 #[derive(Clone, Debug, Default)]
678 struct TestExecutorProvider;
679
680 impl TestExecutorProvider {
681 fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
682 where
683 DB: Database,
684 {
685 TestExecutor(PhantomData)
686 }
687 }
688
689 struct TestExecutor<DB>(PhantomData<DB>);
690
691 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
692 type Primitives = EthPrimitives;
693 type Error = BlockExecutionError;
694
695 fn execute_one(
696 &mut self,
697 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
698 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
699 {
700 Err(BlockExecutionError::msg("execution unavailable for tests"))
701 }
702
703 fn execute_one_with_state_hook<F>(
704 &mut self,
705 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
706 _state_hook: F,
707 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
708 where
709 F: OnStateHook + 'static,
710 {
711 Err(BlockExecutionError::msg("execution unavailable for tests"))
712 }
713
714 fn into_state(self) -> State<DB> {
715 unreachable!()
716 }
717
718 fn size_hint(&self) -> usize {
719 0
720 }
721 }
722
723 #[test]
724 fn test_provider() {
725 let provider = TestExecutorProvider;
726 let db = CacheDB::<EmptyDB>::default();
727 let executor = provider.executor(db);
728 let _ = executor.execute(&Default::default());
729 }
730
731 fn setup_state_with_account(
732 addr: Address,
733 balance: u128,
734 nonce: u64,
735 ) -> State<CacheDB<EmptyDB>> {
736 let db = CacheDB::<EmptyDB>::default();
737 let mut state = State::builder().with_database(db).with_bundle_update().build();
738
739 let account_info = AccountInfo {
740 balance: U256::from(balance),
741 nonce,
742 code_hash: KECCAK_EMPTY,
743 code: None,
744 };
745 state.insert_account(addr, account_info);
746 state
747 }
748
749 #[test]
750 fn test_balance_increment_state_zero() {
751 let addr = address!("0x1000000000000000000000000000000000000000");
752 let mut state = setup_state_with_account(addr, 100, 1);
753
754 let mut increments = HashMap::default();
755 increments.insert(addr, 0);
756
757 let result = balance_increment_state(&increments, &mut state).unwrap();
758 assert!(result.is_empty(), "Zero increments should be ignored");
759 }
760
761 #[test]
762 fn test_balance_increment_state_empty_increments_map() {
763 let mut state = State::builder()
764 .with_database(CacheDB::<EmptyDB>::default())
765 .with_bundle_update()
766 .build();
767
768 let increments = HashMap::default();
769 let result = balance_increment_state(&increments, &mut state).unwrap();
770 assert!(result.is_empty(), "Empty increments map should return empty state");
771 }
772
773 #[test]
774 fn test_balance_increment_state_multiple_valid_increments() {
775 let addr1 = address!("0x1000000000000000000000000000000000000000");
776 let addr2 = address!("0x2000000000000000000000000000000000000000");
777
778 let mut state = setup_state_with_account(addr1, 100, 1);
779
780 let account2 =
781 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
782 state.insert_account(addr2, account2);
783
784 let mut increments = HashMap::default();
785 increments.insert(addr1, 50);
786 increments.insert(addr2, 100);
787
788 let result = balance_increment_state(&increments, &mut state).unwrap();
789
790 assert_eq!(result.len(), 2);
791 assert_eq!(result.get(&addr1).unwrap().info.balance, U256::from(100));
792 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
793 }
794
795 #[test]
796 fn test_balance_increment_state_mixed_zero_and_nonzero_increments() {
797 let addr1 = address!("0x1000000000000000000000000000000000000000");
798 let addr2 = address!("0x2000000000000000000000000000000000000000");
799
800 let mut state = setup_state_with_account(addr1, 100, 1);
801
802 let account2 =
803 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
804 state.insert_account(addr2, account2);
805
806 let mut increments = HashMap::default();
807 increments.insert(addr1, 0);
808 increments.insert(addr2, 100);
809
810 let result = balance_increment_state(&increments, &mut state).unwrap();
811
812 assert_eq!(result.len(), 1, "Only non-zero increments should be included");
813 assert!(!result.contains_key(&addr1), "Zero increment account should not be included");
814 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
815 }
816}