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_hook<F>(
113 mut self,
114 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
115 state_hook: F,
116 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
117 where
118 F: OnStateHook + 'static,
119 {
120 let result = self.execute_one_with_state_hook(block, state_hook)?;
121 let mut state = self.into_state();
122 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
123 }
124
125 fn into_state(self) -> State<DB>;
127
128 fn size_hint(&self) -> usize;
132}
133
134#[derive(Debug, Clone)]
136pub struct ExecuteOutput<R> {
137 pub receipts: Vec<R>,
139 pub gas_used: u64,
141}
142
143#[derive(derive_more::Debug)]
179#[non_exhaustive]
180pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
181 pub evm_env: EvmEnv<<F::EvmFactory as EvmFactory>::Spec>,
185 pub execution_ctx: F::ExecutionCtx<'a>,
187 pub parent: &'a SealedHeader<H>,
189 pub transactions: Vec<F::Transaction>,
191 pub output: &'b BlockExecutionResult<F::Receipt>,
193 pub bundle_state: &'a BundleState,
195 #[debug(skip)]
197 pub state_provider: &'b dyn StateProvider,
198 pub state_root: B256,
200}
201
202#[auto_impl::auto_impl(&, Arc)]
245pub trait BlockAssembler<F: BlockExecutorFactory> {
246 type Block: Block;
248
249 fn assemble_block(
251 &self,
252 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
253 ) -> Result<Self::Block, BlockExecutionError>;
254}
255
256#[derive(Debug, Clone)]
258pub struct BlockBuilderOutcome<N: NodePrimitives> {
259 pub execution_result: BlockExecutionResult<N::Receipt>,
261 pub hashed_state: HashedPostState,
263 pub trie_updates: TrieUpdates,
265 pub block: RecoveredBlock<N::Block>,
267}
268
269pub trait BlockBuilder {
276 type Primitives: NodePrimitives;
278 type Executor: BlockExecutor<
280 Transaction = TxTy<Self::Primitives>,
281 Receipt = ReceiptTy<Self::Primitives>,
282 >;
283
284 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
286
287 fn execute_transaction_with_commit_condition(
290 &mut self,
291 tx: impl ExecutorTx<Self::Executor>,
292 f: impl FnOnce(
293 &ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>,
294 ) -> CommitChanges,
295 ) -> Result<Option<u64>, BlockExecutionError>;
296
297 fn execute_transaction_with_result_closure(
300 &mut self,
301 tx: impl ExecutorTx<Self::Executor>,
302 f: impl FnOnce(&ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>),
303 ) -> Result<u64, BlockExecutionError> {
304 self.execute_transaction_with_commit_condition(tx, |res| {
305 f(res);
306 CommitChanges::Yes
307 })
308 .map(Option::unwrap_or_default)
309 }
310
311 fn execute_transaction(
314 &mut self,
315 tx: impl ExecutorTx<Self::Executor>,
316 ) -> Result<u64, BlockExecutionError> {
317 self.execute_transaction_with_result_closure(tx, |_| ())
318 }
319
320 fn finish(
322 self,
323 state_provider: impl StateProvider,
324 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
325
326 fn executor_mut(&mut self) -> &mut Self::Executor;
328
329 fn executor(&self) -> &Self::Executor;
331
332 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
334 self.executor_mut().evm_mut()
335 }
336
337 fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
339 self.executor().evm()
340 }
341
342 fn into_executor(self) -> Self::Executor;
344}
345
346#[derive(Debug)]
348pub struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
349where
350 F: BlockExecutorFactory,
351{
352 pub executor: Executor,
354 pub transactions: Vec<Recovered<TxTy<N>>>,
356 pub ctx: F::ExecutionCtx<'a>,
358 pub parent: &'a SealedHeader<HeaderTy<N>>,
360 pub assembler: Builder,
362}
363
364pub trait ExecutorTx<Executor: BlockExecutor> {
366 fn as_executable(&self) -> impl ExecutableTx<Executor>;
368
369 fn into_recovered(self) -> Recovered<Executor::Transaction>;
371}
372
373impl<Executor: BlockExecutor> ExecutorTx<Executor>
374 for WithEncoded<Recovered<Executor::Transaction>>
375{
376 fn as_executable(&self) -> impl ExecutableTx<Executor> {
377 self
378 }
379
380 fn into_recovered(self) -> Recovered<Executor::Transaction> {
381 self.1
382 }
383}
384
385impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
386 fn as_executable(&self) -> impl ExecutableTx<Executor> {
387 self
388 }
389
390 fn into_recovered(self) -> Self {
391 self
392 }
393}
394
395impl<'a, F, DB, Executor, Builder, N> BlockBuilder
396 for BasicBlockBuilder<'a, F, Executor, Builder, N>
397where
398 F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
399 Executor: BlockExecutor<
400 Evm: Evm<
401 Spec = <F::EvmFactory as EvmFactory>::Spec,
402 HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
403 DB = &'a mut State<DB>,
404 >,
405 Transaction = N::SignedTx,
406 Receipt = N::Receipt,
407 >,
408 DB: Database + 'a,
409 Builder: BlockAssembler<F, Block = N::Block>,
410 N: NodePrimitives,
411{
412 type Primitives = N;
413 type Executor = Executor;
414
415 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
416 self.executor.apply_pre_execution_changes()
417 }
418
419 fn execute_transaction_with_commit_condition(
420 &mut self,
421 tx: impl ExecutorTx<Self::Executor>,
422 f: impl FnOnce(
423 &ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>,
424 ) -> CommitChanges,
425 ) -> Result<Option<u64>, BlockExecutionError> {
426 if let Some(gas_used) =
427 self.executor.execute_transaction_with_commit_condition(tx.as_executable(), f)?
428 {
429 self.transactions.push(tx.into_recovered());
430 Ok(Some(gas_used))
431 } else {
432 Ok(None)
433 }
434 }
435
436 fn finish(
437 self,
438 state: impl StateProvider,
439 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
440 let (evm, result) = self.executor.finish()?;
441 let (db, evm_env) = evm.finish();
442
443 db.merge_transitions(BundleRetention::Reverts);
445
446 let hashed_state = state.hashed_post_state(&db.bundle_state);
448 let (state_root, trie_updates) = state
449 .state_root_with_updates(hashed_state.clone())
450 .map_err(BlockExecutionError::other)?;
451
452 let (transactions, senders) =
453 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
454
455 let block = self.assembler.assemble_block(BlockAssemblerInput {
456 evm_env,
457 execution_ctx: self.ctx,
458 parent: self.parent,
459 transactions,
460 output: &result,
461 bundle_state: &db.bundle_state,
462 state_provider: &state,
463 state_root,
464 })?;
465
466 let block = RecoveredBlock::new_unhashed(block, senders);
467
468 Ok(BlockBuilderOutcome { execution_result: result, hashed_state, trie_updates, block })
469 }
470
471 fn executor_mut(&mut self) -> &mut Self::Executor {
472 &mut self.executor
473 }
474
475 fn executor(&self) -> &Self::Executor {
476 &self.executor
477 }
478
479 fn into_executor(self) -> Self::Executor {
480 self.executor
481 }
482}
483
484#[expect(missing_debug_implementations)]
487pub struct BasicBlockExecutor<F, DB> {
488 pub(crate) strategy_factory: F,
490 pub(crate) db: State<DB>,
492}
493
494impl<F, DB: Database> BasicBlockExecutor<F, DB> {
495 pub fn new(strategy_factory: F, db: DB) -> Self {
497 let db =
498 State::builder().with_database(db).with_bundle_update().without_state_clear().build();
499 Self { strategy_factory, db }
500 }
501}
502
503impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
504where
505 F: ConfigureEvm,
506 DB: Database,
507{
508 type Primitives = F::Primitives;
509 type Error = BlockExecutionError;
510
511 fn execute_one(
512 &mut self,
513 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
514 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
515 {
516 let result = self
517 .strategy_factory
518 .executor_for_block(&mut self.db, block)
519 .execute_block(block.transactions_recovered())?;
520
521 self.db.merge_transitions(BundleRetention::Reverts);
522
523 Ok(result)
524 }
525
526 fn execute_one_with_state_hook<H>(
527 &mut self,
528 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
529 state_hook: H,
530 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
531 where
532 H: OnStateHook + 'static,
533 {
534 let result = self
535 .strategy_factory
536 .executor_for_block(&mut self.db, block)
537 .with_state_hook(Some(Box::new(state_hook)))
538 .execute_block(block.transactions_recovered())?;
539
540 self.db.merge_transitions(BundleRetention::Reverts);
541
542 Ok(result)
543 }
544
545 fn into_state(self) -> State<DB> {
546 self.db
547 }
548
549 fn size_hint(&self) -> usize {
550 self.db.bundle_state.size_hint()
551 }
552}
553
554pub trait ExecutableTxFor<Evm: ConfigureEvm>:
557 ToTxEnv<TxEnvFor<Evm>> + RecoveredTx<TxTy<Evm::Primitives>>
558{
559}
560
561impl<T, Evm: ConfigureEvm> ExecutableTxFor<Evm> for T where
562 T: ToTxEnv<TxEnvFor<Evm>> + RecoveredTx<TxTy<Evm::Primitives>>
563{
564}
565
566#[derive(Debug, Clone)]
568pub struct WithTxEnv<TxEnv, T> {
569 pub tx_env: TxEnv,
571 pub tx: T,
573}
574
575impl<TxEnv, Tx, T: RecoveredTx<Tx>> RecoveredTx<Tx> for WithTxEnv<TxEnv, T> {
576 fn tx(&self) -> &Tx {
577 self.tx.tx()
578 }
579
580 fn signer(&self) -> &Address {
581 self.tx.signer()
582 }
583}
584
585impl<TxEnv: Clone, T> ToTxEnv<TxEnv> for WithTxEnv<TxEnv, T> {
586 fn to_tx_env(&self) -> TxEnv {
587 self.tx_env.clone()
588 }
589}
590
591#[cfg(test)]
592mod tests {
593 use super::*;
594 use crate::Address;
595 use alloy_consensus::constants::KECCAK_EMPTY;
596 use alloy_evm::block::state_changes::balance_increment_state;
597 use alloy_primitives::{address, map::HashMap, U256};
598 use core::marker::PhantomData;
599 use reth_ethereum_primitives::EthPrimitives;
600 use revm::{
601 database::{CacheDB, EmptyDB},
602 state::AccountInfo,
603 };
604
605 #[derive(Clone, Debug, Default)]
606 struct TestExecutorProvider;
607
608 impl TestExecutorProvider {
609 fn executor<DB>(&self, _db: DB) -> TestExecutor<DB>
610 where
611 DB: Database,
612 {
613 TestExecutor(PhantomData)
614 }
615 }
616
617 struct TestExecutor<DB>(PhantomData<DB>);
618
619 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
620 type Primitives = EthPrimitives;
621 type Error = BlockExecutionError;
622
623 fn execute_one(
624 &mut self,
625 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
626 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
627 {
628 Err(BlockExecutionError::msg("execution unavailable for tests"))
629 }
630
631 fn execute_one_with_state_hook<F>(
632 &mut self,
633 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
634 _state_hook: F,
635 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
636 where
637 F: OnStateHook + 'static,
638 {
639 Err(BlockExecutionError::msg("execution unavailable for tests"))
640 }
641
642 fn into_state(self) -> State<DB> {
643 unreachable!()
644 }
645
646 fn size_hint(&self) -> usize {
647 0
648 }
649 }
650
651 #[test]
652 fn test_provider() {
653 let provider = TestExecutorProvider;
654 let db = CacheDB::<EmptyDB>::default();
655 let executor = provider.executor(db);
656 let _ = executor.execute(&Default::default());
657 }
658
659 fn setup_state_with_account(
660 addr: Address,
661 balance: u128,
662 nonce: u64,
663 ) -> State<CacheDB<EmptyDB>> {
664 let db = CacheDB::<EmptyDB>::default();
665 let mut state = State::builder().with_database(db).with_bundle_update().build();
666
667 let account_info = AccountInfo {
668 balance: U256::from(balance),
669 nonce,
670 code_hash: KECCAK_EMPTY,
671 code: None,
672 };
673 state.insert_account(addr, account_info);
674 state
675 }
676
677 #[test]
678 fn test_balance_increment_state_zero() {
679 let addr = address!("0x1000000000000000000000000000000000000000");
680 let mut state = setup_state_with_account(addr, 100, 1);
681
682 let mut increments = HashMap::default();
683 increments.insert(addr, 0);
684
685 let result = balance_increment_state(&increments, &mut state).unwrap();
686 assert!(result.is_empty(), "Zero increments should be ignored");
687 }
688
689 #[test]
690 fn test_balance_increment_state_empty_increments_map() {
691 let mut state = State::builder()
692 .with_database(CacheDB::<EmptyDB>::default())
693 .with_bundle_update()
694 .build();
695
696 let increments = HashMap::default();
697 let result = balance_increment_state(&increments, &mut state).unwrap();
698 assert!(result.is_empty(), "Empty increments map should return empty state");
699 }
700
701 #[test]
702 fn test_balance_increment_state_multiple_valid_increments() {
703 let addr1 = address!("0x1000000000000000000000000000000000000000");
704 let addr2 = address!("0x2000000000000000000000000000000000000000");
705
706 let mut state = setup_state_with_account(addr1, 100, 1);
707
708 let account2 =
709 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
710 state.insert_account(addr2, account2);
711
712 let mut increments = HashMap::default();
713 increments.insert(addr1, 50);
714 increments.insert(addr2, 100);
715
716 let result = balance_increment_state(&increments, &mut state).unwrap();
717
718 assert_eq!(result.len(), 2);
719 assert_eq!(result.get(&addr1).unwrap().info.balance, U256::from(100));
720 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
721 }
722
723 #[test]
724 fn test_balance_increment_state_mixed_zero_and_nonzero_increments() {
725 let addr1 = address!("0x1000000000000000000000000000000000000000");
726 let addr2 = address!("0x2000000000000000000000000000000000000000");
727
728 let mut state = setup_state_with_account(addr1, 100, 1);
729
730 let account2 =
731 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
732 state.insert_account(addr2, account2);
733
734 let mut increments = HashMap::default();
735 increments.insert(addr1, 0);
736 increments.insert(addr2, 100);
737
738 let result = balance_increment_state(&increments, &mut state).unwrap();
739
740 assert_eq!(result.len(), 1, "Only non-zero increments should be included");
741 assert!(!result.contains_key(&addr1), "Zero increment account should not be included");
742 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
743 }
744}