1use crate::{ConfigureEvm, Database, OnStateHook};
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::{block::ExecutableTx, Evm, EvmEnv, EvmFactory};
9use alloy_primitives::B256;
10use core::fmt::Debug;
11pub use reth_execution_errors::{
12 BlockExecutionError, BlockValidationError, InternalBlockExecutionError,
13};
14use reth_execution_types::BlockExecutionResult;
15pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
16use reth_primitives_traits::{
17 Block, HeaderTy, NodePrimitives, ReceiptTy, Recovered, RecoveredBlock, SealedHeader, TxTy,
18};
19use reth_storage_api::StateProvider;
20pub use reth_storage_errors::provider::ProviderError;
21use reth_trie_common::{updates::TrieUpdates, HashedPostState};
22use revm::{
23 context::result::ExecutionResult,
24 database::{states::bundle_state::BundleRetention, BundleState, State},
25};
26
27pub trait Executor<DB: Database>: Sized {
30 type Primitives: NodePrimitives;
32 type Error;
34
35 fn execute_one(
37 &mut self,
38 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
39 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
40
41 fn execute_one_with_state_hook<F>(
44 &mut self,
45 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
46 state_hook: F,
47 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
48 where
49 F: OnStateHook + 'static;
50
51 fn execute(
59 mut self,
60 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
61 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
62 {
63 let result = self.execute_one(block)?;
64 let mut state = self.into_state();
65 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
66 }
67
68 fn execute_batch<'a, I>(
70 mut self,
71 blocks: I,
72 ) -> Result<ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
73 where
74 I: IntoIterator<Item = &'a RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>>,
75 {
76 let mut results = Vec::new();
77 let mut first_block = None;
78 for block in blocks {
79 if first_block.is_none() {
80 first_block = Some(block.header().number());
81 }
82 results.push(self.execute_one(block)?);
83 }
84
85 Ok(ExecutionOutcome::from_blocks(
86 first_block.unwrap_or_default(),
87 self.into_state().take_bundle(),
88 results,
89 ))
90 }
91
92 fn execute_with_state_closure<F>(
95 mut self,
96 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
97 mut f: F,
98 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
99 where
100 F: FnMut(&State<DB>),
101 {
102 let result = self.execute_one(block)?;
103 let mut state = self.into_state();
104 f(&state);
105 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
106 }
107
108 fn execute_with_state_hook<F>(
111 mut self,
112 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
113 state_hook: F,
114 ) -> Result<BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
115 where
116 F: OnStateHook + 'static,
117 {
118 let result = self.execute_one_with_state_hook(block, state_hook)?;
119 let mut state = self.into_state();
120 Ok(BlockExecutionOutput { state: state.take_bundle(), result })
121 }
122
123 fn into_state(self) -> State<DB>;
125
126 fn size_hint(&self) -> usize;
130}
131
132pub trait BlockExecutorProvider: Clone + Debug + Send + Sync + Unpin + 'static {
134 type Primitives: NodePrimitives;
136
137 type Executor<DB: Database>: Executor<
149 DB,
150 Primitives = Self::Primitives,
151 Error = BlockExecutionError,
152 >;
153
154 fn executor<DB>(&self, db: DB) -> Self::Executor<DB>
158 where
159 DB: Database;
160}
161
162#[derive(Debug, Clone)]
164pub struct ExecuteOutput<R> {
165 pub receipts: Vec<R>,
167 pub gas_used: u64,
169}
170
171#[derive(derive_more::Debug)]
173#[non_exhaustive]
174pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
175 pub evm_env: EvmEnv<<F::EvmFactory as EvmFactory>::Spec>,
179 pub execution_ctx: F::ExecutionCtx<'a>,
181 pub parent: &'a SealedHeader<H>,
183 pub transactions: Vec<F::Transaction>,
185 pub output: &'b BlockExecutionResult<F::Receipt>,
187 pub bundle_state: &'a BundleState,
189 #[debug(skip)]
191 pub state_provider: &'b dyn StateProvider,
192 pub state_root: B256,
194}
195
196#[auto_impl::auto_impl(&, Arc)]
198pub trait BlockAssembler<F: BlockExecutorFactory> {
199 type Block: Block;
201
202 fn assemble_block(
204 &self,
205 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
206 ) -> Result<Self::Block, BlockExecutionError>;
207}
208
209#[derive(Debug, Clone)]
211pub struct BlockBuilderOutcome<N: NodePrimitives> {
212 pub execution_result: BlockExecutionResult<N::Receipt>,
214 pub hashed_state: HashedPostState,
216 pub trie_updates: TrieUpdates,
218 pub block: RecoveredBlock<N::Block>,
220}
221
222pub trait BlockBuilder {
229 type Primitives: NodePrimitives;
231 type Executor: BlockExecutor<
233 Transaction = TxTy<Self::Primitives>,
234 Receipt = ReceiptTy<Self::Primitives>,
235 >;
236
237 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
239
240 fn execute_transaction_with_result_closure(
243 &mut self,
244 tx: impl ExecutorTx<Self::Executor>,
245 f: impl FnOnce(&ExecutionResult<<<Self::Executor as BlockExecutor>::Evm as Evm>::HaltReason>),
246 ) -> Result<u64, BlockExecutionError>;
247
248 fn execute_transaction(
251 &mut self,
252 tx: Recovered<TxTy<Self::Primitives>>,
253 ) -> Result<u64, BlockExecutionError> {
254 self.execute_transaction_with_result_closure(tx, |_| ())
255 }
256
257 fn finish(
259 self,
260 state_provider: impl StateProvider,
261 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
262
263 fn executor_mut(&mut self) -> &mut Self::Executor;
265
266 fn executor(&self) -> &Self::Executor;
268
269 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
271 self.executor_mut().evm_mut()
272 }
273
274 fn evm(&self) -> &<Self::Executor as BlockExecutor>::Evm {
276 self.executor().evm()
277 }
278
279 fn into_executor(self) -> Self::Executor;
281}
282
283pub(crate) struct BasicBlockBuilder<'a, F, Executor, Builder, N: NodePrimitives>
284where
285 F: BlockExecutorFactory,
286{
287 pub(crate) executor: Executor,
288 pub(crate) transactions: Vec<Recovered<TxTy<N>>>,
289 pub(crate) ctx: F::ExecutionCtx<'a>,
290 pub(crate) parent: &'a SealedHeader<HeaderTy<N>>,
291 pub(crate) assembler: Builder,
292}
293
294pub trait ExecutorTx<Executor: BlockExecutor> {
296 fn as_executable(&self) -> impl ExecutableTx<Executor>;
298
299 fn into_recovered(self) -> Recovered<Executor::Transaction>;
301}
302
303impl<Executor: BlockExecutor> ExecutorTx<Executor>
304 for WithEncoded<Recovered<Executor::Transaction>>
305{
306 fn as_executable(&self) -> impl ExecutableTx<Executor> {
307 self
308 }
309
310 fn into_recovered(self) -> Recovered<Executor::Transaction> {
311 self.1
312 }
313}
314
315impl<Executor: BlockExecutor> ExecutorTx<Executor> for Recovered<Executor::Transaction> {
316 fn as_executable(&self) -> impl ExecutableTx<Executor> {
317 self
318 }
319
320 fn into_recovered(self) -> Self {
321 self
322 }
323}
324
325impl<'a, F, DB, Executor, Builder, N> BlockBuilder
326 for BasicBlockBuilder<'a, F, Executor, Builder, N>
327where
328 F: BlockExecutorFactory<Transaction = N::SignedTx, Receipt = N::Receipt>,
329 Executor: BlockExecutor<
330 Evm: Evm<
331 Spec = <F::EvmFactory as EvmFactory>::Spec,
332 HaltReason = <F::EvmFactory as EvmFactory>::HaltReason,
333 DB = &'a mut State<DB>,
334 >,
335 Transaction = N::SignedTx,
336 Receipt = N::Receipt,
337 >,
338 DB: Database + 'a,
339 Builder: BlockAssembler<F, Block = N::Block>,
340 N: NodePrimitives,
341{
342 type Primitives = N;
343 type Executor = Executor;
344
345 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError> {
346 self.executor.apply_pre_execution_changes()
347 }
348
349 fn execute_transaction_with_result_closure(
350 &mut self,
351 tx: impl ExecutorTx<Self::Executor>,
352 f: impl FnOnce(&ExecutionResult<<F::EvmFactory as EvmFactory>::HaltReason>),
353 ) -> Result<u64, BlockExecutionError> {
354 let gas_used =
355 self.executor.execute_transaction_with_result_closure(tx.as_executable(), f)?;
356 self.transactions.push(tx.into_recovered());
357 Ok(gas_used)
358 }
359
360 fn finish(
361 self,
362 state: impl StateProvider,
363 ) -> Result<BlockBuilderOutcome<N>, BlockExecutionError> {
364 let (evm, result) = self.executor.finish()?;
365 let (db, evm_env) = evm.finish();
366
367 db.merge_transitions(BundleRetention::Reverts);
369
370 let hashed_state = state.hashed_post_state(&db.bundle_state);
372 let (state_root, trie_updates) = state
373 .state_root_with_updates(hashed_state.clone())
374 .map_err(BlockExecutionError::other)?;
375
376 let (transactions, senders) =
377 self.transactions.into_iter().map(|tx| tx.into_parts()).unzip();
378
379 let block = self.assembler.assemble_block(BlockAssemblerInput {
380 evm_env,
381 execution_ctx: self.ctx,
382 parent: self.parent,
383 transactions,
384 output: &result,
385 bundle_state: &db.bundle_state,
386 state_provider: &state,
387 state_root,
388 })?;
389
390 let block = RecoveredBlock::new_unhashed(block, senders);
391
392 Ok(BlockBuilderOutcome { execution_result: result, hashed_state, trie_updates, block })
393 }
394
395 fn executor_mut(&mut self) -> &mut Self::Executor {
396 &mut self.executor
397 }
398
399 fn executor(&self) -> &Self::Executor {
400 &self.executor
401 }
402
403 fn into_executor(self) -> Self::Executor {
404 self.executor
405 }
406}
407
408impl<F> Clone for BasicBlockExecutorProvider<F>
409where
410 F: Clone,
411{
412 fn clone(&self) -> Self {
413 Self { strategy_factory: self.strategy_factory.clone() }
414 }
415}
416
417#[derive(Debug)]
419pub struct BasicBlockExecutorProvider<F> {
420 strategy_factory: F,
421}
422
423impl<F> BasicBlockExecutorProvider<F> {
424 pub const fn new(strategy_factory: F) -> Self {
426 Self { strategy_factory }
427 }
428}
429
430impl<F> BlockExecutorProvider for BasicBlockExecutorProvider<F>
431where
432 F: ConfigureEvm + 'static,
433{
434 type Primitives = F::Primitives;
435
436 type Executor<DB: Database> = BasicBlockExecutor<F, DB>;
437
438 fn executor<DB>(&self, db: DB) -> Self::Executor<DB>
439 where
440 DB: Database,
441 {
442 BasicBlockExecutor::new(self.strategy_factory.clone(), db)
443 }
444}
445
446#[expect(missing_debug_implementations)]
449pub struct BasicBlockExecutor<F, DB> {
450 pub(crate) strategy_factory: F,
452 pub(crate) db: State<DB>,
454}
455
456impl<F, DB: Database> BasicBlockExecutor<F, DB> {
457 pub fn new(strategy_factory: F, db: DB) -> Self {
459 let db =
460 State::builder().with_database(db).with_bundle_update().without_state_clear().build();
461 Self { strategy_factory, db }
462 }
463}
464
465impl<F, DB> Executor<DB> for BasicBlockExecutor<F, DB>
466where
467 F: ConfigureEvm,
468 DB: Database,
469{
470 type Primitives = F::Primitives;
471 type Error = BlockExecutionError;
472
473 fn execute_one(
474 &mut self,
475 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
476 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
477 {
478 let mut strategy = self.strategy_factory.executor_for_block(&mut self.db, block);
479
480 strategy.apply_pre_execution_changes()?;
481 for tx in block.transactions_recovered() {
482 strategy.execute_transaction(tx)?;
483 }
484 let result = strategy.apply_post_execution_changes()?;
485
486 self.db.merge_transitions(BundleRetention::Reverts);
487
488 Ok(result)
489 }
490
491 fn execute_one_with_state_hook<H>(
492 &mut self,
493 block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
494 state_hook: H,
495 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
496 where
497 H: OnStateHook + 'static,
498 {
499 let mut strategy = self
500 .strategy_factory
501 .executor_for_block(&mut self.db, block)
502 .with_state_hook(Some(Box::new(state_hook)));
503
504 strategy.apply_pre_execution_changes()?;
505 for tx in block.transactions_recovered() {
506 strategy.execute_transaction(tx)?;
507 }
508 let result = strategy.apply_post_execution_changes()?;
509
510 self.db.merge_transitions(BundleRetention::Reverts);
511
512 Ok(result)
513 }
514
515 fn into_state(self) -> State<DB> {
516 self.db
517 }
518
519 fn size_hint(&self) -> usize {
520 self.db.bundle_state.size_hint()
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use super::*;
527 use crate::Address;
528 use alloy_consensus::constants::KECCAK_EMPTY;
529 use alloy_evm::block::state_changes::balance_increment_state;
530 use alloy_primitives::{address, map::HashMap, U256};
531 use core::marker::PhantomData;
532 use reth_ethereum_primitives::EthPrimitives;
533 use revm::{
534 database::{CacheDB, EmptyDB},
535 state::AccountInfo,
536 };
537
538 #[derive(Clone, Debug, Default)]
539 struct TestExecutorProvider;
540
541 impl BlockExecutorProvider for TestExecutorProvider {
542 type Primitives = EthPrimitives;
543 type Executor<DB: Database> = TestExecutor<DB>;
544
545 fn executor<DB>(&self, _db: DB) -> Self::Executor<DB>
546 where
547 DB: Database,
548 {
549 TestExecutor(PhantomData)
550 }
551 }
552
553 struct TestExecutor<DB>(PhantomData<DB>);
554
555 impl<DB: Database> Executor<DB> for TestExecutor<DB> {
556 type Primitives = EthPrimitives;
557 type Error = BlockExecutionError;
558
559 fn execute_one(
560 &mut self,
561 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
562 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
563 {
564 Err(BlockExecutionError::msg("execution unavailable for tests"))
565 }
566
567 fn execute_one_with_state_hook<F>(
568 &mut self,
569 _block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
570 _state_hook: F,
571 ) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
572 where
573 F: OnStateHook + 'static,
574 {
575 Err(BlockExecutionError::msg("execution unavailable for tests"))
576 }
577
578 fn into_state(self) -> State<DB> {
579 unreachable!()
580 }
581
582 fn size_hint(&self) -> usize {
583 0
584 }
585 }
586
587 #[test]
588 fn test_provider() {
589 let provider = TestExecutorProvider;
590 let db = CacheDB::<EmptyDB>::default();
591 let executor = provider.executor(db);
592 let _ = executor.execute(&Default::default());
593 }
594
595 fn setup_state_with_account(
596 addr: Address,
597 balance: u128,
598 nonce: u64,
599 ) -> State<CacheDB<EmptyDB>> {
600 let db = CacheDB::<EmptyDB>::default();
601 let mut state = State::builder().with_database(db).with_bundle_update().build();
602
603 let account_info = AccountInfo {
604 balance: U256::from(balance),
605 nonce,
606 code_hash: KECCAK_EMPTY,
607 code: None,
608 };
609 state.insert_account(addr, account_info);
610 state
611 }
612
613 #[test]
614 fn test_balance_increment_state_zero() {
615 let addr = address!("0x1000000000000000000000000000000000000000");
616 let mut state = setup_state_with_account(addr, 100, 1);
617
618 let mut increments = HashMap::default();
619 increments.insert(addr, 0);
620
621 let result = balance_increment_state(&increments, &mut state).unwrap();
622 assert!(result.is_empty(), "Zero increments should be ignored");
623 }
624
625 #[test]
626 fn test_balance_increment_state_empty_increments_map() {
627 let mut state = State::builder()
628 .with_database(CacheDB::<EmptyDB>::default())
629 .with_bundle_update()
630 .build();
631
632 let increments = HashMap::default();
633 let result = balance_increment_state(&increments, &mut state).unwrap();
634 assert!(result.is_empty(), "Empty increments map should return empty state");
635 }
636
637 #[test]
638 fn test_balance_increment_state_multiple_valid_increments() {
639 let addr1 = address!("0x1000000000000000000000000000000000000000");
640 let addr2 = address!("0x2000000000000000000000000000000000000000");
641
642 let mut state = setup_state_with_account(addr1, 100, 1);
643
644 let account2 =
645 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
646 state.insert_account(addr2, account2);
647
648 let mut increments = HashMap::default();
649 increments.insert(addr1, 50);
650 increments.insert(addr2, 100);
651
652 let result = balance_increment_state(&increments, &mut state).unwrap();
653
654 assert_eq!(result.len(), 2);
655 assert_eq!(result.get(&addr1).unwrap().info.balance, U256::from(100));
656 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
657 }
658
659 #[test]
660 fn test_balance_increment_state_mixed_zero_and_nonzero_increments() {
661 let addr1 = address!("0x1000000000000000000000000000000000000000");
662 let addr2 = address!("0x2000000000000000000000000000000000000000");
663
664 let mut state = setup_state_with_account(addr1, 100, 1);
665
666 let account2 =
667 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
668 state.insert_account(addr2, account2);
669
670 let mut increments = HashMap::default();
671 increments.insert(addr1, 0);
672 increments.insert(addr2, 100);
673
674 let result = balance_increment_state(&increments, &mut state).unwrap();
675
676 assert_eq!(result.len(), 1, "Only non-zero increments should be included");
677 assert!(!result.contains_key(&addr1), "Zero increment account should not be included");
678 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
679 }
680}