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, ExecutableTxParts},
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::database::{states::bundle_state::BundleRetention, BundleState, State};
25
26pub trait Executor<DB: Database>: Sized {
29 type Primitives: NodePrimitives;
31 type Error;
33
34 fn execute_one(
36 &mut self,
37 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
38 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
39
40 fn execute_one_with_state_hook<F>(
43 &mut self,
44 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
45 state_hook: F,
46 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
47 where
48 F: OnStateHook + 'static;
49
50 fn execute(
58 mut self,
59 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
60 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
61 {
62 let result = self.execute_one(block)?;
63 let mut state = self.into_state();
64 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
65 }
66
67 fn execute_batch<'a, I>(
69 mut self,
70 blocks: I,
71 ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
72 where
73 I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
74 {
75 let blocks_iter = blocks.into_iter();
76 let capacity = blocks_iter.size_hint().0;
77 let mut results = Vec::with_capacity(capacity);
78 let mut first_block = None;
79 for block in blocks_iter {
80 if first_block.is_none() {
81 first_block = Some(block.header().number());
82 }
83 results.push(self.execute_one(block)?);
84 }
85
86 Ok(ExecutionOutcome::from_blocks(
87 first_block.unwrap_or_default(),
88 self.into_state().take_bundle(),
89 results,
90 ))
91 }
92
93 fn execute_with_state_closure<F>(
96 mut self,
97 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
98 mut f: F,
99 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
100 where
101 F: FnMut(&State<DB>),
102 {
103 let result = self.execute_one(block)?;
104 let mut state = self.into_state();
105 f(&state);
106 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
107 }
108
109 fn execute_with_state_closure_always<F>(
112 mut self,
113 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
114 mut f: F,
115 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
116 where
117 F: FnMut(&State<DB>),
118 {
119 let result = self.execute_one(block);
120 let mut state = self.into_state();
121 f(&state);
122
123 Ok(BlockExecutionOutput { state: state.take_bundle(), result: result? })
124 }
125
126 fn execute_with_state_hook<F>(
129 mut self,
130 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
131 state_hook: F,
132 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
133 where
134 F: OnStateHook + 'static,
135 {
136 let result = self.execute_one_with_state_hook(block, state_hook)?;
137 let mut state = self.into_state();
138 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
139 }
140
141 fn into_state(self) -> State<DB>;
143
144 fn size_hint(&self) -> usize;
148}
149
150#[derive(derive_more::Debug)]
186#[non_exhaustive]
187pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
188 pub evm_env:
192 EvmEnv<<F::EvmFactory as EvmFactory>::Spec, <F::EvmFactory as EvmFactory>::BlockEnv>,
193 pub execution_ctx: F::ExecutionCtx<'a>,
195 pub parent: &'a SealedHeader<H>,
197 pub transactions: Vec<F::Transaction>,
199 pub output: &'b BlockExecutionResult<F::Receipt>,
201 pub bundle_state: &'a BundleState,
203 #[debug(skip)]
205 pub state_provider: &'b dyn StateProvider,
206 pub state_root: B256,
208}
209
210impl<'a, 'b, F: BlockExecutorFactory, H> BlockAssemblerInput<'a, 'b, F, H> {
211 #[expect(clippy::too_many_arguments)]
213 pub fn new(
214 evm_env: EvmEnv<
215 <F::EvmFactory as EvmFactory>::Spec,
216 <F::EvmFactory as EvmFactory>::BlockEnv,
217 >,
218 execution_ctx: F::ExecutionCtx<'a>,
219 parent: &'a SealedHeader<H>,
220 transactions: Vec<F::Transaction>,
221 output: &'b BlockExecutionResult<F::Receipt>,
222 bundle_state: &'a BundleState,
223 state_provider: &'b dyn StateProvider,
224 state_root: B256,
225 ) -> Self {
226 Self {
227 evm_env,
228 execution_ctx,
229 parent,
230 transactions,
231 output,
232 bundle_state,
233 state_provider,
234 state_root,
235 }
236 }
237}
238
239#[auto_impl::auto_impl(&, Arc)]
282pub trait BlockAssembler<F: BlockExecutorFactory> {
283 type Block: Block;
285
286 fn assemble_block(
288 &self,
289 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
290 ) -> Result<Self::Block, BlockExecutionError>;
291}
292
293#[derive(Debug, Clone)]
295pub struct BlockBuilderOutcome<N: NodePrimitives> {
296 pub execution_result: BlockExecutionResult<N::Receipt>,
298 pub hashed_state: HashedPostState,
300 pub trie_updates: TrieUpdates,
302 pub block: RecoveredBlock<N::Block>,
304}
305
306pub trait BlockBuilder {
313 type Primitives: NodePrimitives;
315 type Executor: BlockExecutor<
317 Transaction = TxTy<Self::Primitives>,
318 Receipt = ReceiptTy<Self::Primitives>,
319 >;
320
321 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
323
324 fn execute_transaction_with_commit_condition(
327 &mut self,
328 tx: impl ExecutorTx<Self::Executor>,
329 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
330 ) -> Result<Option<u64>, BlockExecutionError>;
331
332 fn execute_transaction_with_result_closure(
335 &mut self,
336 tx: impl ExecutorTx<Self::Executor>,
337 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result),
338 ) -> Result<u64, BlockExecutionError> {
339 self.execute_transaction_with_commit_condition(tx, |res| {
340 f(res);
341 CommitChanges::Yes
342 })
343 .map(Option::unwrap_or_default)
344 }
345
346 fn execute_transaction(
349 &mut self,
350 tx: impl ExecutorTx<Self::Executor>,
351 ) -> Result<u64, BlockExecutionError> {
352 self.execute_transaction_with_result_closure(tx, |_| ())
353 }
354
355 fn finish(
361 self,
362 state_provider: impl StateProvider,
363 state_root_precomputed: Option<(B256, TrieUpdates)>,
364 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
365
366 fn executor_mut(&mut self) -> &mut Self::Executor;
368
369 fn executor(&self) -> &Self::Executor;
371
372 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
374 self.executor_mut().evm_mut()
375 }
376
377 fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
379 self.executor().evm()
380 }
381
382 fn into_executor(self) -> Self::Executor;
384}
385
386#[derive(Debug)]
388pub struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
389where
390 F: BlockExecutorFactory,
391{
392 pub executor: Executor,
394 pub transactions: Vec<Recovered<TxTy<N>>>,
396 pub ctx: F::ExecutionCtx<'a>,
398 pub parent: &'a SealedHeader<HeaderTy<N>>,
400 pub assembler: Builder,
402}
403
404pub trait ExecutorTx<Executor: BlockExecutor> {
406 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>);
408}
409
410impl<Executor: BlockExecutor> ExecutorTx<Executor>
411 for WithEncoded<Recovered<Executor::Transaction>>
412{
413 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
414 (self.to_tx_env(), self.1)
415 }
416}
417
418impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
419 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Self) {
420 (self.to_tx_env(), self)
421 }
422}
423
424impl<Executor> ExecutorTx<Executor>
425 for WithTxEnv<<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>>
426where
427 Executor: BlockExecutor<Transaction: Clone>,
428{
429 fn into_parts(self) -> (<Executor::Evm as Evm>::Tx, Recovered<Executor::Transaction>) {
430 (self.tx_env, Arc::unwrap_or_clone(self.tx))
431 }
432}
433
434impl<'a, F, DB, Executor, Builder, N> BlockBuilder
435 for BasicBlockBuilder<'a, F, Executor, Builder, N>
436where
437 F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
438 Executor: BlockExecutor<
439 Evm: Evm<
440 Spec = <F::EvmFactory as EvmFactory>::Spec,
441 HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
442 BlockEnv = <F::EvmFactory as EvmFactory>::BlockEnv,
443 DB = &'a mut State<DB>,
444 >,
445 Transaction = N::SignedTx,
446 Receipt = N::Receipt,
447 >,
448 DB: Database + 'a,
449 Builder: BlockAssembler<F, Block = N::Block>,
450 N: NodePrimitives,
451{
452 type Primitives = N;
453 type Executor = Executor;
454
455 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
456 self.executor.apply_pre_execution_changes()
457 }
458
459 fn execute_transaction_with_commit_condition(
460 &mut self,
461 tx: impl ExecutorTx<Self::Executor>,
462 f: impl FnOnce(&<Self::Executor as BlockExecutor>::Result) -> CommitChanges,
463 ) -> Result<Option<u64>, BlockExecutionError> {
464 let (tx_env, tx) = tx.into_parts();
465 if let Some(gas_used) =
466 self.executor.execute_transaction_with_commit_condition((tx_env, &tx), f)?
467 {
468 self.transactions.push(tx);
469 Ok(Some(gas_used))
470 } else {
471 Ok(None)
472 }
473 }
474
475 fn finish(
476 self,
477 state: impl StateProvider,
478 state_root_precomputed: Option<(B256, TrieUpdates)>,
479 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
480 let (evm, result) = self.executor.finish()?;
481 let (db, evm_env) = evm.finish();
482
483 db.merge_transitions(BundleRetention::Reverts);
485
486 let hashed_state = state.hashed_post_state(&db.bundle_state);
487 let (state_root, trie_updates) = match state_root_precomputed {
488 Some(precomputed) => precomputed,
489 None => state
490 .state_root_with_updates(hashed_state.clone())
491 .map_err(BlockExecutionError::other)?,
492 };
493
494 let (transactions, senders) =
495 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
496
497 let block = self.assembler.assemble_block(BlockAssemblerInput {
498 evm_env,
499 execution_ctx: self.ctx,
500 parent: self.parent,
501 transactions,
502 output: &result,
503 bundle_state: &db.bundle_state,
504 state_provider: &state,
505 state_root,
506 })?;
507
508 let block = RecoveredBlock::new_unhashed(block, senders);
509
510 Ok(BlockBuilderOutcome { execution_result: result, hashed_state, trie_updates, block })
511 }
512
513 fn executor_mut(&mut self) -> &mut Self::Executor {
514 &mut self.executor
515 }
516
517 fn executor(&self) -> &Self::Executor {
518 &self.executor
519 }
520
521 fn into_executor(self) -> Self::Executor {
522 self.executor
523 }
524}
525
526#[expect(missing_debug_implementations)]
529pub struct BasicBlockExecutor<F, DB> {
530 pub(crate) strategy_factory: F,
532 pub(crate) db: State<DB>,
534}
535
536impl<F, DB: Database> BasicBlockExecutor<F, DB> {
537 pub fn new(strategy_factory: F, db: DB) -> Self {
539 let db = State::builder().with_database(db).with_bundle_update().build();
540 Self { strategy_factory, db }
541 }
542}
543
544impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
545where
546 F: ConfigureEvm,
547 DB: Database,
548{
549 type Primitives = F::Primitives;
550 type Error = BlockExecutionError;
551
552 fn execute_one(
553 &mut self,
554 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
555 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
556 {
557 let result = self
558 .strategy_factory
559 .executor_for_block(&mut self.db, block)
560 .map_err(BlockExecutionError::other)?
561 .execute_block(block.transactions_recovered())?;
562
563 self.db.merge_transitions(BundleRetention::Reverts);
564
565 Ok(result)
566 }
567
568 fn execute_one_with_state_hook<H>(
569 &mut self,
570 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
571 state_hook: H,
572 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
573 where
574 H: OnStateHook + 'static,
575 {
576 let result = self
577 .strategy_factory
578 .executor_for_block(&mut self.db, block)
579 .map_err(BlockExecutionError::other)?
580 .with_state_hook(Some(Box::new(state_hook)))
581 .execute_block(block.transactions_recovered())?;
582
583 self.db.merge_transitions(BundleRetention::Reverts);
584
585 Ok(result)
586 }
587
588 fn into_state(self) -> State<DB> {
589 self.db
590 }
591
592 fn size_hint(&self) -> usize {
593 self.db.bundle_state.size_hint()
594 }
595}
596
597pub trait ExecutableTxFor<Evm: ConfigureEvm>:
600 ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
601{
602}
603
604impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
605 T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
606{
607}
608
609#[derive(Debug)]
611pub struct WithTxEnv<TxEnv, T> {
612 pub tx_env: TxEnv,
614 pub tx: Arc<T>,
616}
617
618impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
619 fn clone(&self) -> Self {
620 Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
621 }
622}
623
624impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
625 fn tx(&self) -> &Tx {
626 self.tx.tx()
627 }
628
629 fn signer(&self) -> &Address {
630 self.tx.signer()
631 }
632}
633
634impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
635 type Recovered = Arc<T>;
636
637 fn into_parts(self) -> (TxEnv, Self::Recovered) {
638 (self.tx_env, self.tx)
639 }
640}
641
642#[cfg(test)]
643mod tests {
644 use super::*;
645 use crate::Address;
646 use alloy_consensus::constants::KECCAK_EMPTY;
647 use alloy_evm::block::state_changes::balance_increment_state;
648 use alloy_primitives::{address, map::HashMap, U256};
649 use core::marker::PhantomData;
650 use reth_ethereum_primitives::EthPrimitives;
651 use revm::{
652 database::{CacheDB, EmptyDB},
653 state::AccountInfo,
654 };
655
656 #[derive(Clone, Debug, Default)]
657 struct TestExecutorProvider;
658
659 impl TestExecutorProvider {
660 fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
661 where
662 DB: Database,
663 {
664 TestExecutor(PhantomData)
665 }
666 }
667
668 struct TestExecutor<DB>(PhantomData<DB>);
669
670 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
671 type Primitives = EthPrimitives;
672 type Error = BlockExecutionError;
673
674 fn execute_one(
675 &mut self,
676 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
677 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
678 {
679 Err(BlockExecutionError::msg("execution unavailable for tests"))
680 }
681
682 fn execute_one_with_state_hook<F>(
683 &mut self,
684 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
685 _state_hook: F,
686 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
687 where
688 F: OnStateHook + 'static,
689 {
690 Err(BlockExecutionError::msg("execution unavailable for tests"))
691 }
692
693 fn into_state(self) -> State<DB> {
694 unreachable!()
695 }
696
697 fn size_hint(&self) -> usize {
698 0
699 }
700 }
701
702 #[test]
703 fn test_provider() {
704 let provider = TestExecutorProvider;
705 let db = CacheDB::<EmptyDB>::default();
706 let executor = provider.executor(db);
707 let _ = executor.execute(&Default::default());
708 }
709
710 fn setup_state_with_account(
711 addr: Address,
712 balance: u128,
713 nonce: u64,
714 ) -> State<CacheDB<EmptyDB>> {
715 let db = CacheDB::<EmptyDB>::default();
716 let mut state = State::builder().with_database(db).with_bundle_update().build();
717
718 let account_info = AccountInfo {
719 balance: U256::from(balance),
720 nonce,
721 code_hash: KECCAK_EMPTY,
722 code: None,
723 account_id: None,
724 };
725 state.insert_account(addr, account_info);
726 state
727 }
728
729 #[test]
730 fn test_balance_increment_state_zero() {
731 let addr = address!("0x1000000000000000000000000000000000000000");
732 let mut state = setup_state_with_account(addr, 100, 1);
733
734 let mut increments = HashMap::default();
735 increments.insert(addr, 0);
736
737 let result = balance_increment_state(&increments, &mut state).unwrap();
738 assert!(result.is_empty(), "Zero increments should be ignored");
739 }
740
741 #[test]
742 fn test_balance_increment_state_empty_increments_map() {
743 let mut state = State::builder()
744 .with_database(CacheDB::<EmptyDB>::default())
745 .with_bundle_update()
746 .build();
747
748 let increments = HashMap::default();
749 let result = balance_increment_state(&increments, &mut state).unwrap();
750 assert!(result.is_empty(), "Empty increments map should return empty state");
751 }
752
753 #[test]
754 fn test_balance_increment_state_multiple_valid_increments() {
755 let addr1 = address!("0x1000000000000000000000000000000000000000");
756 let addr2 = address!("0x2000000000000000000000000000000000000000");
757
758 let mut state = setup_state_with_account(addr1, 100, 1);
759
760 let account2 = AccountInfo {
761 balance: U256::from(200),
762 nonce: 1,
763 code_hash: KECCAK_EMPTY,
764 code: None,
765 account_id: None,
766 };
767 state.insert_account(addr2, account2);
768
769 let mut increments = HashMap::default();
770 increments.insert(addr1, 50);
771 increments.insert(addr2, 100);
772
773 let result = balance_increment_state(&increments, &mut state).unwrap();
774
775 assert_eq!(result.len(), 2);
776 assert_eq!(result.get(&addr1).unwrap().info.balance, U256::from(100));
777 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
778 }
779
780 #[test]
781 fn test_balance_increment_state_mixed_zero_and_nonzero_increments() {
782 let addr1 = address!("0x1000000000000000000000000000000000000000");
783 let addr2 = address!("0x2000000000000000000000000000000000000000");
784
785 let mut state = setup_state_with_account(addr1, 100, 1);
786
787 let account2 = AccountInfo {
788 balance: U256::from(200),
789 nonce: 1,
790 code_hash: KECCAK_EMPTY,
791 code: None,
792 account_id: None,
793 };
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}