1use 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
23pub trait Executor<DB: Database>: Sized {
26 type Primitives: NodePrimitives;
28 type Error;
30
31 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 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 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 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 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 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 fn into_state(self) -> State<DB>;
121
122 fn size_hint(&self) -> usize;
126}
127
128pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static {
130 type Primitives: NodePrimitives;
132
133 type Executor<DB: Database>: Executor<
145 DB,
146 Primitives = Self::Primitives,
147 Error = BlockExecutionError,
148 >;
149
150 fn executor<DB>(&self, db: DB) -> Self::Executor<DB>
154 where
155 DB: Database;
156}
157
158#[derive(Debug, Clone)]
160pub struct ExecuteOutput<R> {
161 pub receipts: Vec<R>,
163 pub gas_used: u64,
165}
166
167#[derive(derive_more::Debug)]
169#[non_exhaustive]
170pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> {
171 pub evm_env: EvmEnv<<F::EvmFactory as EvmFactory>::Spec>,
175 pub execution_ctx: F::ExecutionCtx<'a>,
177 pub parent: &'a SealedHeader<H>,
179 pub transactions: Vec<F::Transaction>,
181 pub output: &'b BlockExecutionResult<F::Receipt>,
183 pub bundle_state: &'a BundleState,
185 #[debug(skip)]
187 pub state_provider: &'b dyn StateProvider,
188 pub state_root: B256,
190}
191
192#[auto_impl::auto_impl(&, Arc)]
194pub trait BlockAssembler<F: BlockExecutorFactory> {
195 type Block: Block;
197
198 fn assemble_block(
200 &self,
201 input: BlockAssemblerInput<'_, '_, F, <Self::Block as Block>::Header>,
202 ) -> Result<Self::Block, BlockExecutionError>;
203}
204
205#[derive(Debug, Clone)]
207pub struct BlockBuilderOutcome<N: NodePrimitives> {
208 pub execution_result: BlockExecutionResult<N::Receipt>,
210 pub hashed_state: HashedPostState,
212 pub trie_updates: TrieUpdates,
214 pub block: RecoveredBlock<N::Block>,
216}
217
218pub trait BlockBuilder {
225 type Primitives: NodePrimitives;
227 type Executor: BlockExecutor<
229 Transaction = TxTy<Self::Primitives>,
230 Receipt = ReceiptTy<Self::Primitives>,
231 >;
232
233 fn apply_pre_execution_changes(&mut self) -> Result<(), BlockExecutionError>;
235
236 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 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 fn finish(
255 self,
256 state_provider: impl StateProvider,
257 ) -> Result<BlockBuilderOutcome<Self::Primitives>, BlockExecutionError>;
258
259 fn executor_mut(&mut self) -> &mut Self::Executor;
261
262 fn evm_mut(&mut self) -> &mut <Self::Executor as BlockExecutor>::Evm {
264 self.executor_mut().evm_mut()
265 }
266
267 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 db.merge_transitions(BundleRetention::Reverts);
326
327 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#[allow(missing_debug_implementations)]
372pub struct BasicBlockExecutorProvider<F> {
373 strategy_factory: F,
374}
375
376impl<F> BasicBlockExecutorProvider<F> {
377 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#[allow(missing_debug_implementations, dead_code)]
402pub struct BasicBlockExecutor<F, DB> {
403 pub(crate) strategy_factory: F,
405 pub(crate) db: State<DB>,
407}
408
409impl<F, DB: Database> BasicBlockExecutor<F, DB> {
410 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}