reth_evm/
execute.rs

1//! Traits for execution.
2
3use crate::{ConfigureEvm, Database, OnStateHook};
4use alloc::{boxed::Box, vec::Vec};
5use alloy_consensus::{BlockHeader, Header};
6pub use alloy_evm::block::{BlockExecutor, BlockExecutorFactory};
7use alloy_evm::{Evm, EvmEnv, EvmFactory};
8use alloy_primitives::B256;
9pub use reth_execution_errors::{
10    BlockExecutionError, BlockValidationError, InternalBlockExecutionError,
11};
12use reth_execution_types::BlockExecutionResult;
13pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
14use reth_primitives_traits::{
15    Block, HeaderTy, NodePrimitives, ReceiptTy, Recovered, RecoveredBlock, SealedHeader, TxTy,
16};
17use reth_storage_api::StateProvider;
18pub use reth_storage_errors::provider::ProviderError;
19use reth_trie_common::{updates::TrieUpdates, HashedPostState};
20use revm::context::result::ExecutionResult;
21use revm_database::{states::bundle_state::BundleRetention, BundleState, State};
22
23/// A type that knows how to execute a block. It is assumed to operate on a
24/// [`crate::Evm`] internally and use [`State`] as database.
25pub trait Executor<DB: Database>: Sized {
26    /// The primitive types used by the executor.
27    type Primitives: NodePrimitives;
28    /// The error type returned by the executor.
29    type Error;
30
31    /// Executes a single block and returns [`BlockExecutionResult`], without the state changes.
32    fn execute_one(
33        &mut self,
34        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
35    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
36
37    /// Executes the EVM with the given input and accepts a state hook closure that is invoked with
38    /// the EVM state after execution.
39    fn execute_one_with_state_hook<F>(
40        &mut self,
41        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
42        state_hook: F,
43    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
44    where
45        F: OnStateHook + 'static;
46
47    /// Consumes the type and executes the block.
48    ///
49    /// # Note
50    /// Execution happens without any validation of the output.
51    ///
52    /// # Returns
53    /// The output of the block execution.
54    fn execute(
55        mut self,
56        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
57    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
58    {
59        let result = self.execute_one(block)?;
60        let mut state = self.into_state();
61        Ok(BlockExecutionOutput { state: state.take_bundle(), result })
62    }
63
64    /// Executes multiple inputs in the batch, and returns an aggregated [`ExecutionOutcome`].
65    fn execute_batch<'a, I>(
66        mut self,
67        blocks: I,
68    ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
69    where
70        I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
71    {
72        let mut results = Vec::new();
73        let mut first_block = None;
74        for block in blocks {
75            if first_block.is_none() {
76                first_block = Some(block.header().number());
77            }
78            results.push(self.execute_one(block)?);
79        }
80
81        Ok(ExecutionOutcome::from_blocks(
82            first_block.unwrap_or_default(),
83            self.into_state().take_bundle(),
84            results,
85        ))
86    }
87
88    /// Executes the EVM with the given input and accepts a state closure that is invoked with
89    /// the EVM state after execution.
90    fn execute_with_state_closure<F>(
91        mut self,
92        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
93        mut f: F,
94    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
95    where
96        F: FnMut(&State<DB>),
97    {
98        let result = self.execute_one(block)?;
99        let mut state = self.into_state();
100        f(&state);
101        Ok(BlockExecutionOutput { state: state.take_bundle(), result })
102    }
103
104    /// Executes the EVM with the given input and accepts a state hook closure that is invoked with
105    /// the EVM state after execution.
106    fn execute_with_state_hook<F>(
107        mut self,
108        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
109        state_hook: F,
110    ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
111    where
112        F: OnStateHook + 'static,
113    {
114        let result = self.execute_one_with_state_hook(block, state_hook)?;
115        let mut state = self.into_state();
116        Ok(BlockExecutionOutput { state: state.take_bundle(), result })
117    }
118
119    /// Consumes the executor and returns the [`State`] containing all state changes.
120    fn into_state(self) -> State<DB>;
121
122    /// The size hint of the batch's tracked state size.
123    ///
124    /// This is used to optimize DB commits depending on the size of the state.
125    fn size_hint(&self) -> usize;
126}
127
128/// A type that can create a new executor for block execution.
129pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static {
130    /// Receipt type.
131    type Primitives: NodePrimitives;
132
133    /// An executor that can execute a single block given a database.
134    ///
135    /// # Verification
136    ///
137    /// The on [`Executor::execute`] the executor is expected to validate the execution output of
138    /// the input, this includes:
139    /// - Cumulative gas used must match the input's gas used.
140    /// - Receipts must match the input's receipts root.
141    ///
142    /// It is not expected to validate the state trie root, this must be done by the caller using
143    /// the returned state.
144    type Executor<DB: Database>: Executor<
145        DB,
146        Primitives = Self::Primitives,
147        Error = BlockExecutionError,
148    >;
149
150    /// Creates a new executor for single block execution.
151    ///
152    /// This is used to execute a single block and get the changed state.
153    fn executor<DB>(&self, db: DB) -> Self::Executor<DB>
154    where
155        DB: Database;
156}
157
158/// Helper type for the output of executing a block.
159#[derive(Debug, Clone)]
160pub struct ExecuteOutput<R> {
161    /// Receipts obtained after executing a block.
162    pub receipts: Vec<R>,
163    /// Cumulative gas used in the block execution.
164    pub gas_used: u64,
165}
166
167/// Input for block building. Consumed by [`BlockAssembler`].
168#[derive(derive_more::Debug)]
169#[non_exhaustive]
170pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
171    /// Configuration of EVM used when executing the block.
172    ///
173    /// Contains context relevant to EVM such as [`revm::context::BlockEnv`].
174    pub evm_env: EvmEnv<<F::EvmFactory as EvmFactory>::Spec>,
175    /// [`BlockExecutorFactory::ExecutionCtx`] used to execute the block.
176    pub execution_ctx: F::ExecutionCtx<'a>,
177    /// Parent block header.
178    pub parent: &'a SealedHeader<H>,
179    /// Transactions that were executed in this block.
180    pub transactions: Vec<F::Transaction>,
181    /// Output of block execution.
182    pub output: &'b BlockExecutionResult<F::Receipt>,
183    /// [`BundleState`] after the block execution.
184    pub bundle_state: &'a BundleState,
185    /// Provider with access to state.
186    #[debug(skip)]
187    pub state_provider: &'b dyn StateProvider,
188    /// State root for this block.
189    pub state_root: B256,
190}
191
192/// A type that knows how to assemble a block.
193#[auto_impl::auto_impl(&, Arc)]
194pub trait BlockAssembler<F: BlockExecutorFactory> {
195    /// The block type produced by the assembler.
196    type Block: Block;
197
198    /// Builds a block. see [`BlockAssemblerInput`] documentation for more details.
199    fn assemble_block(
200        &self,
201        input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
202    ) -> Result<Self::Block, BlockExecutionError>;
203}
204
205/// Output of block building.
206#[derive(Debug, Clone)]
207pub struct BlockBuilderOutcome<N: NodePrimitives> {
208    /// Result of block execution.
209    pub execution_result: BlockExecutionResult<N::Receipt>,
210    /// Hashed state after execution.
211    pub hashed_state: HashedPostState,
212    /// Trie updates collected during state root calculation.
213    pub trie_updates: TrieUpdates,
214    /// The built block.
215    pub block: RecoveredBlock<N::Block>,
216}
217
218/// A type that knows how to execute and build a block.
219///
220/// It wraps an inner [`BlockExecutor`] and provides a way to execute transactions and
221/// construct a block.
222///
223/// This is a helper to erase `BasicBlockBuilder` type.
224pub trait BlockBuilder {
225    /// The primitive types used by the inner [`BlockExecutor`].
226    type Primitives: NodePrimitives;
227    /// Inner [`BlockExecutor`].
228    type Executor: BlockExecutor<
229        Transaction = TxTy<Self::Primitives>,
230        Receipt = ReceiptTy<Self::Primitives>,
231    >;
232
233    /// Invokes [`BlockExecutor::apply_pre_execution_changes`].
234    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
235
236    /// Invokes [`BlockExecutor::execute_transaction_with_result_closure`] and saves the
237    /// transaction in internal state.
238    fn execute_transaction_with_result_closure(
239        &mut self,
240        tx: Recovered<TxTy<Self::Primitives>>,
241        f: impl FnOnce(&ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>),
242    ) -> Result<u64, BlockExecutionError>;
243
244    /// Invokes [`BlockExecutor::execute_transaction`] and saves the transaction in
245    /// internal state.
246    fn execute_transaction(
247        &mut self,
248        tx: Recovered<TxTy<Self::Primitives>>,
249    ) -> Result<u64, BlockExecutionError> {
250        self.execute_transaction_with_result_closure(tx, |_| ())
251    }
252
253    /// Completes the block building process and returns the [`BlockBuilderOutcome`].
254    fn finish(
255        self,
256        state_provider: impl StateProvider,
257    ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
258
259    /// Provides mutable access to the inner [`BlockExecutor`].
260    fn executor_mut(&mut self) -> &mut Self::Executor;
261
262    /// Helper to access inner [`BlockExecutor::Evm`].
263    fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
264        self.executor_mut().evm_mut()
265    }
266
267    /// Consumes the type and returns the underlying [`BlockExecutor`].
268    fn into_executor(self) -> Self::Executor;
269}
270
271pub(crate) struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
272where
273    F: BlockExecutorFactory,
274{
275    pub(crate) executor: Executor,
276    pub(crate) transactions: Vec<Recovered<TxTy<N>>>,
277    pub(crate) ctx: F::ExecutionCtx<'a>,
278    pub(crate) parent: &'a SealedHeader<HeaderTy<N>>,
279    pub(crate) assembler: Builder,
280}
281
282impl<'a, F, DB, Executor, Builder, N> BlockBuilder
283    for BasicBlockBuilder<'a, F, Executor, Builder, N>
284where
285    F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
286    Executor: BlockExecutor<
287        Evm: Evm<
288            Spec = <F::EvmFactory as EvmFactory>::Spec,
289            HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
290            DB = &'a mut State<DB>,
291        >,
292        Transaction = N::SignedTx,
293        Receipt = N::Receipt,
294    >,
295    DB: Database + 'a,
296    Builder: BlockAssembler<F, Block = N::Block>,
297    N: NodePrimitives,
298{
299    type Primitives = N;
300    type Executor = Executor;
301
302    fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
303        self.executor.apply_pre_execution_changes()
304    }
305
306    fn execute_transaction_with_result_closure(
307        &mut self,
308        tx: Recovered<TxTy<Self::Primitives>>,
309        f: impl FnOnce(&ExecutionResult<<F::EvmFactory as EvmFactory>::HaltReason>),
310    ) -> Result<u64, BlockExecutionError> {
311        let gas_used =
312            self.executor.execute_transaction_with_result_closure(tx.as_recovered_ref(), f)?;
313        self.transactions.push(tx);
314        Ok(gas_used)
315    }
316
317    fn finish(
318        self,
319        state: impl StateProvider,
320    ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
321        let (evm, result) = self.executor.finish()?;
322        let (db, evm_env) = evm.finish();
323
324        // merge all transitions into bundle state
325        db.merge_transitions(BundleRetention::Reverts);
326
327        // calculate the state root
328        let hashed_state = state.hashed_post_state(&db.bundle_state);
329        let (state_root, trie_updates) = state
330            .state_root_with_updates(hashed_state.clone())
331            .map_err(BlockExecutionError::other)?;
332
333        let (transactions, senders) =
334            self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
335
336        let block = self.assembler.assemble_block(BlockAssemblerInput {
337            evm_env,
338            execution_ctx: self.ctx,
339            parent: self.parent,
340            transactions,
341            output: &result,
342            bundle_state: &db.bundle_state,
343            state_provider: &state,
344            state_root,
345        })?;
346
347        let block = RecoveredBlock::new_unhashed(block, senders);
348
349        Ok(BlockBuilderOutcome { execution_result: result, hashed_state, trie_updates, block })
350    }
351
352    fn executor_mut(&mut self) -> &mut Self::Executor {
353        &mut self.executor
354    }
355
356    fn into_executor(self) -> Self::Executor {
357        self.executor
358    }
359}
360
361impl<F> Clone for BasicBlockExecutorProvider<F>
362where
363    F: Clone,
364{
365    fn clone(&self) -> Self {
366        Self { strategy_factory: self.strategy_factory.clone() }
367    }
368}
369
370/// A generic block executor provider that can create executors using a strategy factory.
371#[allow(missing_debug_implementations)]
372pub struct BasicBlockExecutorProvider<F> {
373    strategy_factory: F,
374}
375
376impl<F> BasicBlockExecutorProvider<F> {
377    /// Creates a new `BasicBlockExecutorProvider` with the given strategy factory.
378    pub const fn new(strategy_factory: F) -> Self {
379        Self { strategy_factory }
380    }
381}
382
383impl<F> BlockExecutorProvider for BasicBlockExecutorProvider<F>
384where
385    F: ConfigureEvm + 'static,
386{
387    type Primitives = F::Primitives;
388
389    type Executor<DB: Database> = BasicBlockExecutor<F, DB>;
390
391    fn executor<DB>(&self, db: DB) -> Self::Executor<DB>
392    where
393        DB: Database,
394    {
395        BasicBlockExecutor::new(self.strategy_factory.clone(), db)
396    }
397}
398
399/// A generic block executor that uses a [`BlockExecutor`] to
400/// execute blocks.
401#[allow(missing_debug_implementations, dead_code)]
402pub struct BasicBlockExecutor<F, DB> {
403    /// Block execution strategy.
404    pub(crate) strategy_factory: F,
405    /// Database.
406    pub(crate) db: State<DB>,
407}
408
409impl<F, DB: Database> BasicBlockExecutor<F, DB> {
410    /// Creates a new `BasicBlockExecutor` with the given strategy.
411    pub fn new(strategy_factory: F, db: DB) -> Self {
412        let db =
413            State::builder().with_database(db).with_bundle_update().without_state_clear().build();
414        Self { strategy_factory, db }
415    }
416}
417
418impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
419where
420    F: ConfigureEvm,
421    DB: Database,
422{
423    type Primitives = F::Primitives;
424    type Error = BlockExecutionError;
425
426    fn execute_one(
427        &mut self,
428        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
429    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
430    {
431        let mut strategy = self.strategy_factory.executor_for_block(&mut self.db, block);
432
433        strategy.apply_pre_execution_changes()?;
434        for tx in block.transactions_recovered() {
435            strategy.execute_transaction(tx)?;
436        }
437        let result = strategy.apply_post_execution_changes()?;
438
439        self.db.merge_transitions(BundleRetention::Reverts);
440
441        Ok(result)
442    }
443
444    fn execute_one_with_state_hook<H>(
445        &mut self,
446        block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
447        state_hook: H,
448    ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
449    where
450        H: OnStateHook + 'static,
451    {
452        let mut strategy = self
453            .strategy_factory
454            .executor_for_block(&mut self.db, block)
455            .with_state_hook(Some(Box::new(state_hook)));
456
457        strategy.apply_pre_execution_changes()?;
458        for tx in block.transactions_recovered() {
459            strategy.execute_transaction(tx)?;
460        }
461        let result = strategy.apply_post_execution_changes()?;
462
463        self.db.merge_transitions(BundleRetention::Reverts);
464
465        Ok(result)
466    }
467
468    fn into_state(self) -> State<DB> {
469        self.db
470    }
471
472    fn size_hint(&self) -> usize {
473        self.db.bundle_state.size_hint()
474    }
475}
476
477#[cfg(test)]
478mod tests {
479    use super::*;
480    use crate::Address;
481    use alloy_consensus::constants::KECCAK_EMPTY;
482    use alloy_evm::block::state_changes::balance_increment_state;
483    use alloy_primitives::{address, map::HashMap, U256};
484    use core::marker::PhantomData;
485    use reth_ethereum_primitives::EthPrimitives;
486    use revm::state::AccountInfo;
487    use revm_database::{CacheDB, EmptyDB};
488
489    #[derive(Clone, Default)]
490    struct TestExecutorProvider;
491
492    impl BlockExecutorProvider for TestExecutorProvider {
493        type Primitives = EthPrimitives;
494        type Executor<DB: Database> = TestExecutor<DB>;
495
496        fn executor<DB>(&self, _db: DB) -> Self::Executor<DB>
497        where
498            DB: Database,
499        {
500            TestExecutor(PhantomData)
501        }
502    }
503
504    struct TestExecutor<DB>(PhantomData<DB>);
505
506    impl<DB: Database> Executor<DB> for TestExecutor<DB> {
507        type Primitives = EthPrimitives;
508        type Error = BlockExecutionError;
509
510        fn execute_one(
511            &mut self,
512            _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
513        ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
514        {
515            Err(BlockExecutionError::msg("execution unavailable for tests"))
516        }
517
518        fn execute_one_with_state_hook<F>(
519            &mut self,
520            _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
521            _state_hook: F,
522        ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
523        where
524            F: OnStateHook + 'static,
525        {
526            Err(BlockExecutionError::msg("execution unavailable for tests"))
527        }
528
529        fn into_state(self) -> State<DB> {
530            unreachable!()
531        }
532
533        fn size_hint(&self) -> usize {
534            0
535        }
536    }
537
538    #[test]
539    fn test_provider() {
540        let provider = TestExecutorProvider;
541        let db = CacheDB::<EmptyDB>::default();
542        let executor = provider.executor(db);
543        let _ = executor.execute(&Default::default());
544    }
545
546    fn setup_state_with_account(
547        addr: Address,
548        balance: u128,
549        nonce: u64,
550    ) -> State<CacheDB<EmptyDB>> {
551        let db = CacheDB::<EmptyDB>::default();
552        let mut state = State::builder().with_database(db).with_bundle_update().build();
553
554        let account_info = AccountInfo {
555            balance: U256::from(balance),
556            nonce,
557            code_hash: KECCAK_EMPTY,
558            code: None,
559        };
560        state.insert_account(addr, account_info);
561        state
562    }
563
564    #[test]
565    fn test_balance_increment_state_zero() {
566        let addr = address!("0x1000000000000000000000000000000000000000");
567        let mut state = setup_state_with_account(addr, 100, 1);
568
569        let mut increments = HashMap::default();
570        increments.insert(addr, 0);
571
572        let result = balance_increment_state(&increments, &mut state).unwrap();
573        assert!(result.is_empty(), "Zero increments should be ignored");
574    }
575
576    #[test]
577    fn test_balance_increment_state_empty_increments_map() {
578        let mut state = State::builder()
579            .with_database(CacheDB::<EmptyDB>::default())
580            .with_bundle_update()
581            .build();
582
583        let increments = HashMap::default();
584        let result = balance_increment_state(&increments, &mut state).unwrap();
585        assert!(result.is_empty(), "Empty increments map should return empty state");
586    }
587
588    #[test]
589    fn test_balance_increment_state_multiple_valid_increments() {
590        let addr1 = address!("0x1000000000000000000000000000000000000000");
591        let addr2 = address!("0x2000000000000000000000000000000000000000");
592
593        let mut state = setup_state_with_account(addr1, 100, 1);
594
595        let account2 =
596            AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
597        state.insert_account(addr2, account2);
598
599        let mut increments = HashMap::default();
600        increments.insert(addr1, 50);
601        increments.insert(addr2, 100);
602
603        let result = balance_increment_state(&increments, &mut state).unwrap();
604
605        assert_eq!(result.len(), 2);
606        assert_eq!(result.get(&addr1).unwrap().info.balance, U256::from(100));
607        assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
608    }
609
610    #[test]
611    fn test_balance_increment_state_mixed_zero_and_nonzero_increments() {
612        let addr1 = address!("0x1000000000000000000000000000000000000000");
613        let addr2 = address!("0x2000000000000000000000000000000000000000");
614
615        let mut state = setup_state_with_account(addr1, 100, 1);
616
617        let account2 =
618            AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
619        state.insert_account(addr2, account2);
620
621        let mut increments = HashMap::default();
622        increments.insert(addr1, 0);
623        increments.insert(addr2, 100);
624
625        let result = balance_increment_state(&increments, &mut state).unwrap();
626
627        assert_eq!(result.len(), 1, "Only non-zero increments should be included");
628        assert!(!result.contains_key(&addr1), "Zero increment account should not be included");
629        assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
630    }
631}