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::{
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 blocks_iter = blocks.into_iter();
79 let capacity = blocks_iter.size_hint().0;
80 let mut results = Vec::with_capacity(capacity);
81 let mut first_block = None;
82 for block in blocks_iter {
83 if first_block.is_none() {
84 first_block = Some(block.header().number());
85 }
86 results.push(self.execute_one(block)?);
87 }
88
89 Ok(ExecutionOutcome::from_blocks(
90 first_block.unwrap_or_default(),
91 self.into_state().take_bundle(),
92 results,
93 ))
94 }
95
96 fn execute_with_state_closure<F>(
99 mut self,
100 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
101 mut f: F,
102 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
103 where
104 F: FnMut(&State<DB>),
105 {
106 let result = self.execute_one(block)?;
107 let mut state = self.into_state();
108 f(&state);
109 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
110 }
111
112 fn execute_with_state_closure_always<F>(
115 mut self,
116 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
117 mut f: F,
118 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
119 where
120 F: FnMut(&State<DB>),
121 {
122 let result = self.execute_one(block);
123 let mut state = self.into_state();
124 f(&state);
125
126 Ok(BlockExecutionOutput { state: state.take_bundle(), result: result? })
127 }
128
129 fn execute_with_state_hook<F>(
132 mut self,
133 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
134 state_hook: F,
135 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
136 where
137 F: OnStateHook + 'static,
138 {
139 let result = self.execute_one_with_state_hook(block, state_hook)?;
140 let mut state = self.into_state();
141 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
142 }
143
144 fn into_state(self) -> State<DB>;
146
147 fn size_hint(&self) -> usize;
151}
152
153#[derive(derive_more::Debug)]
189#[non_exhaustive]
190pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
191 pub evm_env:
195 EvmEnv<<F::EvmFactory as EvmFactory>::Spec, <F::EvmFactory as EvmFactory>::BlockEnv>,
196 pub execution_ctx: F::ExecutionCtx<'a>,
198 pub parent: &'a SealedHeader<H>,
200 pub transactions: Vec<F::Transaction>,
202 pub output: &'b BlockExecutionResult<F::Receipt>,
204 pub bundle_state: &'a BundleState,
206 #[debug(skip)]
208 pub state_provider: &'b dyn StateProvider,
209 pub state_root: B256,
211}
212
213impl<'a, 'b, F: BlockExecutorFactory, H> BlockAssemblerInput<'a, 'b, F, H> {
214 #[expect(clippy::too_many_arguments)]
216 pub fn new(
217 evm_env: EvmEnv<
218 <F::EvmFactory as EvmFactory>::Spec,
219 <F::EvmFactory as EvmFactory>::BlockEnv,
220 >,
221 execution_ctx: F::ExecutionCtx<'a>,
222 parent: &'a SealedHeader<H>,
223 transactions: Vec<F::Transaction>,
224 output: &'b BlockExecutionResult<F::Receipt>,
225 bundle_state: &'a BundleState,
226 state_provider: &'b dyn StateProvider,
227 state_root: B256,
228 ) -> Self {
229 Self {
230 evm_env,
231 execution_ctx,
232 parent,
233 transactions,
234 output,
235 bundle_state,
236 state_provider,
237 state_root,
238 }
239 }
240}
241
242#[auto_impl::auto_impl(&, Arc)]
285pub trait BlockAssembler<F: BlockExecutorFactory> {
286 type Block: Block;
288
289 fn assemble_block(
291 &self,
292 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
293 ) -> Result<Self::Block, BlockExecutionError>;
294}
295
296#[derive(Debug, Clone)]
298pub struct BlockBuilderOutcome<N: NodePrimitives> {
299 pub execution_result: BlockExecutionResult<N::Receipt>,
301 pub hashed_state: HashedPostState,
303 pub trie_updates: TrieUpdates,
305 pub block: RecoveredBlock<N::Block>,
307}
308
309pub trait BlockBuilder {
316 type Primitives: NodePrimitives;
318 type Executor: BlockExecutor<
320 Transaction = TxTy<Self::Primitives>,
321 Receipt = ReceiptTy<Self::Primitives>,
322 >;
323
324 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
326
327 fn execute_transaction_with_commit_condition(
330 &mut self,
331 tx: impl ExecutorTx<Self::Executor>,
332 f: impl FnOnce(
333 &ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>,
334 ) -> CommitChanges,
335 ) -> Result<Option<u64>, BlockExecutionError>;
336
337 fn execute_transaction_with_result_closure(
340 &mut self,
341 tx: impl ExecutorTx<Self::Executor>,
342 f: impl FnOnce(&ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>),
343 ) -> Result<u64, BlockExecutionError> {
344 self.execute_transaction_with_commit_condition(tx, |res| {
345 f(res);
346 CommitChanges::Yes
347 })
348 .map(Option::unwrap_or_default)
349 }
350
351 fn execute_transaction(
354 &mut self,
355 tx: impl ExecutorTx<Self::Executor>,
356 ) -> Result<u64, BlockExecutionError> {
357 self.execute_transaction_with_result_closure(tx, |_| ())
358 }
359
360 fn finish(
362 self,
363 state_provider: impl StateProvider,
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(
463 &ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>,
464 ) -> CommitChanges,
465 ) -> Result<Option<u64>, BlockExecutionError> {
466 let (tx_env, tx) = tx.into_parts();
467 if let Some(gas_used) =
468 self.executor.execute_transaction_with_commit_condition((tx_env, &tx), f)?
469 {
470 self.transactions.push(tx);
471 Ok(Some(gas_used))
472 } else {
473 Ok(None)
474 }
475 }
476
477 fn finish(
478 self,
479 state: impl StateProvider,
480 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
481 let (evm, result) = self.executor.finish()?;
482 let (db, evm_env) = evm.finish();
483
484 db.merge_transitions(BundleRetention::Reverts);
486
487 let hashed_state = state.hashed_post_state(&db.bundle_state);
489 let (state_root, trie_updates) = state
490 .state_root_with_updates(hashed_state.clone())
491 .map_err(BlockExecutionError::other)?;
492
493 let (transactions, senders) =
494 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
495
496 let block = self.assembler.assemble_block(BlockAssemblerInput {
497 evm_env,
498 execution_ctx: self.ctx,
499 parent: self.parent,
500 transactions,
501 output: &result,
502 bundle_state: &db.bundle_state,
503 state_provider: &state,
504 state_root,
505 })?;
506
507 let block = RecoveredBlock::new_unhashed(block, senders);
508
509 Ok(BlockBuilderOutcome { execution_result: result, hashed_state, trie_updates, block })
510 }
511
512 fn executor_mut(&mut self) -> &mut Self::Executor {
513 &mut self.executor
514 }
515
516 fn executor(&self) -> &Self::Executor {
517 &self.executor
518 }
519
520 fn into_executor(self) -> Self::Executor {
521 self.executor
522 }
523}
524
525#[expect(missing_debug_implementations)]
528pub struct BasicBlockExecutor<F, DB> {
529 pub(crate) strategy_factory: F,
531 pub(crate) db: State<DB>,
533}
534
535impl<F, DB: Database> BasicBlockExecutor<F, DB> {
536 pub fn new(strategy_factory: F, db: DB) -> Self {
538 let db = State::builder().with_database(db).with_bundle_update().build();
539 Self { strategy_factory, db }
540 }
541}
542
543impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
544where
545 F: ConfigureEvm,
546 DB: Database,
547{
548 type Primitives = F::Primitives;
549 type Error = BlockExecutionError;
550
551 fn execute_one(
552 &mut self,
553 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
554 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
555 {
556 let result = self
557 .strategy_factory
558 .executor_for_block(&mut self.db, block)
559 .map_err(BlockExecutionError::other)?
560 .execute_block(block.transactions_recovered())?;
561
562 self.db.merge_transitions(BundleRetention::Reverts);
563
564 Ok(result)
565 }
566
567 fn execute_one_with_state_hook<H>(
568 &mut self,
569 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
570 state_hook: H,
571 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
572 where
573 H: OnStateHook + 'static,
574 {
575 let result = self
576 .strategy_factory
577 .executor_for_block(&mut self.db, block)
578 .map_err(BlockExecutionError::other)?
579 .with_state_hook(Some(Box::new(state_hook)))
580 .execute_block(block.transactions_recovered())?;
581
582 self.db.merge_transitions(BundleRetention::Reverts);
583
584 Ok(result)
585 }
586
587 fn into_state(self) -> State<DB> {
588 self.db
589 }
590
591 fn size_hint(&self) -> usize {
592 self.db.bundle_state.size_hint()
593 }
594}
595
596pub trait ExecutableTxFor<Evm: ConfigureEvm>:
599 ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
600{
601}
602
603impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
604 T: ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>> + RecoveredTx<TxTy<Evm::Primitives>>
605{
606}
607
608#[derive(Debug)]
610pub struct WithTxEnv<TxEnv, T> {
611 pub tx_env: TxEnv,
613 pub tx: Arc<T>,
615}
616
617impl<TxEnv: Clone, T> Clone for WithTxEnv<TxEnv, T> {
618 fn clone(&self) -> Self {
619 Self { tx_env: self.tx_env.clone(), tx: self.tx.clone() }
620 }
621}
622
623impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
624 fn tx(&self) -> &Tx {
625 self.tx.tx()
626 }
627
628 fn signer(&self) -> &Address {
629 self.tx.signer()
630 }
631}
632
633impl<TxEnv, T: RecoveredTx<Tx>, Tx> ExecutableTxParts<TxEnv, Tx> for WithTxEnv<TxEnv, T> {
634 type Recovered = Arc<T>;
635
636 fn into_parts(self) -> (TxEnv, Self::Recovered) {
637 (self.tx_env, self.tx)
638 }
639}
640
641#[cfg(test)]
642mod tests {
643 use super::*;
644 use crate::Address;
645 use alloy_consensus::constants::KECCAK_EMPTY;
646 use alloy_evm::block::state_changes::balance_increment_state;
647 use alloy_primitives::{address, map::HashMap, U256};
648 use core::marker::PhantomData;
649 use reth_ethereum_primitives::EthPrimitives;
650 use revm::{
651 database::{CacheDB, EmptyDB},
652 state::AccountInfo,
653 };
654
655 #[derive(Clone, Debug, Default)]
656 struct TestExecutorProvider;
657
658 impl TestExecutorProvider {
659 fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
660 where
661 DB: Database,
662 {
663 TestExecutor(PhantomData)
664 }
665 }
666
667 struct TestExecutor<DB>(PhantomData<DB>);
668
669 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
670 type Primitives = EthPrimitives;
671 type Error = BlockExecutionError;
672
673 fn execute_one(
674 &mut self,
675 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
676 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
677 {
678 Err(BlockExecutionError::msg("execution unavailable for tests"))
679 }
680
681 fn execute_one_with_state_hook<F>(
682 &mut self,
683 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
684 _state_hook: F,
685 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
686 where
687 F: OnStateHook + 'static,
688 {
689 Err(BlockExecutionError::msg("execution unavailable for tests"))
690 }
691
692 fn into_state(self) -> State<DB> {
693 unreachable!()
694 }
695
696 fn size_hint(&self) -> usize {
697 0
698 }
699 }
700
701 #[test]
702 fn test_provider() {
703 let provider = TestExecutorProvider;
704 let db = CacheDB::<EmptyDB>::default();
705 let executor = provider.executor(db);
706 let _ = executor.execute(&Default::default());
707 }
708
709 fn setup_state_with_account(
710 addr: Address,
711 balance: u128,
712 nonce: u64,
713 ) -> State<CacheDB<EmptyDB>> {
714 let db = CacheDB::<EmptyDB>::default();
715 let mut state = State::builder().with_database(db).with_bundle_update().build();
716
717 let account_info = AccountInfo {
718 balance: U256::from(balance),
719 nonce,
720 code_hash: KECCAK_EMPTY,
721 code: None,
722 account_id: None,
723 };
724 state.insert_account(addr, account_info);
725 state
726 }
727
728 #[test]
729 fn test_balance_increment_state_zero() {
730 let addr = address!("0x1000000000000000000000000000000000000000");
731 let mut state = setup_state_with_account(addr, 100, 1);
732
733 let mut increments = HashMap::default();
734 increments.insert(addr, 0);
735
736 let result = balance_increment_state(&increments, &mut state).unwrap();
737 assert!(result.is_empty(), "Zero increments should be ignored");
738 }
739
740 #[test]
741 fn test_balance_increment_state_empty_increments_map() {
742 let mut state = State::builder()
743 .with_database(CacheDB::<EmptyDB>::default())
744 .with_bundle_update()
745 .build();
746
747 let increments = HashMap::default();
748 let result = balance_increment_state(&increments, &mut state).unwrap();
749 assert!(result.is_empty(), "Empty increments map should return empty state");
750 }
751
752 #[test]
753 fn test_balance_increment_state_multiple_valid_increments() {
754 let addr1 = address!("0x1000000000000000000000000000000000000000");
755 let addr2 = address!("0x2000000000000000000000000000000000000000");
756
757 let mut state = setup_state_with_account(addr1, 100, 1);
758
759 let account2 = AccountInfo {
760 balance: U256::from(200),
761 nonce: 1,
762 code_hash: KECCAK_EMPTY,
763 code: None,
764 account_id: None,
765 };
766 state.insert_account(addr2, account2);
767
768 let mut increments = HashMap::default();
769 increments.insert(addr1, 50);
770 increments.insert(addr2, 100);
771
772 let result = balance_increment_state(&increments, &mut state).unwrap();
773
774 assert_eq!(result.len(), 2);
775 assert_eq!(result.get(&addr1).unwrap().info.balance, U256::from(100));
776 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
777 }
778
779 #[test]
780 fn test_balance_increment_state_mixed_zero_and_nonzero_increments() {
781 let addr1 = address!("0x1000000000000000000000000000000000000000");
782 let addr2 = address!("0x2000000000000000000000000000000000000000");
783
784 let mut state = setup_state_with_account(addr1, 100, 1);
785
786 let account2 = AccountInfo {
787 balance: U256::from(200),
788 nonce: 1,
789 code_hash: KECCAK_EMPTY,
790 code: None,
791 account_id: None,
792 };
793 state.insert_account(addr2, account2);
794
795 let mut increments = HashMap::default();
796 increments.insert(addr1, 0);
797 increments.insert(addr2, 100);
798
799 let result = balance_increment_state(&increments, &mut state).unwrap();
800
801 assert_eq!(result.len(), 1, "Only non-zero increments should be included");
802 assert!(!result.contains_key(&addr1), "Zero increment account should not be included");
803 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
804 }
805}