1use alloy_consensus::BlockHeader;
4pub use reth_execution_errors::{
6 BlockExecutionError, BlockValidationError, InternalBlockExecutionError,
7};
8pub use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput, ExecutionOutcome};
9use reth_primitives_traits::Block as _;
10pub use reth_storage_errors::provider::ProviderError;
11
12use crate::{system_calls::OnStateHook, TxEnvOverrides};
13use alloc::{boxed::Box, vec::Vec};
14use alloy_eips::eip7685::Requests;
15use alloy_primitives::{
16 map::{DefaultHashBuilder, HashMap},
17 Address, BlockNumber,
18};
19use core::fmt::Display;
20use reth_consensus::ConsensusError;
21use reth_primitives::{BlockWithSenders, NodePrimitives, Receipt};
22use reth_prune_types::PruneModes;
23use reth_revm::batch::BlockBatchRecord;
24use revm::{
25 db::{states::bundle_state::BundleRetention, BundleState},
26 State,
27};
28use revm_primitives::{db::Database, Account, AccountStatus, EvmState, U256};
29
30pub trait Executor<DB> {
35 type Input<'a>;
37 type Output;
39 type Error;
41
42 fn init(&mut self, _tx_env_overrides: Box<dyn TxEnvOverrides>) {}
44
45 fn execute(self, input: Self::Input<'_>) -> Result<Self::Output, Self::Error>;
54
55 fn execute_with_state_closure<F>(
58 self,
59 input: Self::Input<'_>,
60 state: F,
61 ) -> Result<Self::Output, Self::Error>
62 where
63 F: FnMut(&State<DB>);
64
65 fn execute_with_state_hook<F>(
68 self,
69 input: Self::Input<'_>,
70 state_hook: F,
71 ) -> Result<Self::Output, Self::Error>
72 where
73 F: OnStateHook + 'static;
74}
75
76pub trait BatchExecutor<DB> {
79 type Input<'a>;
81 type Output;
83 type Error;
85
86 fn execute_and_verify_one(&mut self, input: Self::Input<'_>) -> Result<(), Self::Error>;
88
89 fn execute_and_verify_many<'a, I>(&mut self, inputs: I) -> Result<(), Self::Error>
95 where
96 I: IntoIterator<Item = Self::Input<'a>>,
97 {
98 for input in inputs {
99 self.execute_and_verify_one(input)?;
100 }
101 Ok(())
102 }
103
104 fn execute_and_verify_batch<'a, I>(mut self, batch: I) -> Result<Self::Output, Self::Error>
109 where
110 I: IntoIterator<Item = Self::Input<'a>>,
111 Self: Sized,
112 {
113 self.execute_and_verify_many(batch)?;
114 Ok(self.finalize())
115 }
116
117 fn finalize(self) -> Self::Output;
119
120 fn set_tip(&mut self, tip: BlockNumber);
124
125 fn set_prune_modes(&mut self, prune_modes: PruneModes);
129
130 fn size_hint(&self) -> Option<usize>;
134}
135
136pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static {
138 type Primitives: NodePrimitives;
140
141 type Executor<DB: Database<Error: Into<ProviderError> + Display>>: for<'a> Executor<
153 DB,
154 Input<'a> = BlockExecutionInput<
155 'a,
156 BlockWithSenders<<Self::Primitives as NodePrimitives>::Block>,
157 >,
158 Output = BlockExecutionOutput<<Self::Primitives as NodePrimitives>::Receipt>,
159 Error = BlockExecutionError,
160 >;
161
162 type BatchExecutor<DB: Database<Error: Into<ProviderError> + Display>>: for<'a> BatchExecutor<
164 DB,
165 Input<'a> = BlockExecutionInput<
166 'a,
167 BlockWithSenders<<Self::Primitives as NodePrimitives>::Block>,
168 >,
169 Output = ExecutionOutcome<<Self::Primitives as NodePrimitives>::Receipt>,
170 Error = BlockExecutionError,
171 >;
172
173 fn executor<DB>(&self, db: DB) -> Self::Executor<DB>
177 where
178 DB: Database<Error: Into<ProviderError> + Display>;
179
180 fn batch_executor<DB>(&self, db: DB) -> Self::BatchExecutor<DB>
185 where
186 DB: Database<Error: Into<ProviderError> + Display>;
187}
188
189#[derive(Debug, Clone)]
191pub struct ExecuteOutput<R = Receipt> {
192 pub receipts: Vec<R>,
194 pub gas_used: u64,
196}
197
198pub trait BlockExecutionStrategy {
200 type DB: Database;
202
203 type Primitives: NodePrimitives;
205
206 type Error: From<ProviderError> + core::error::Error;
208
209 fn init(&mut self, _tx_env_overrides: Box<dyn TxEnvOverrides>) {}
211
212 fn apply_pre_execution_changes(
214 &mut self,
215 block: &BlockWithSenders<<Self::Primitives as NodePrimitives>::Block>,
216 total_difficulty: U256,
217 ) -> Result<(), Self::Error>;
218
219 fn execute_transactions(
221 &mut self,
222 block: &BlockWithSenders<<Self::Primitives as NodePrimitives>::Block>,
223 total_difficulty: U256,
224 ) -> Result<ExecuteOutput<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>;
225
226 fn apply_post_execution_changes(
228 &mut self,
229 block: &BlockWithSenders<<Self::Primitives as NodePrimitives>::Block>,
230 total_difficulty: U256,
231 receipts: &[<Self::Primitives as NodePrimitives>::Receipt],
232 ) -> Result<Requests, Self::Error>;
233
234 fn state_ref(&self) -> &State<Self::DB>;
236
237 fn state_mut(&mut self) -> &mut State<Self::DB>;
239
240 fn with_state_hook(&mut self, _hook: Option<Box<dyn OnStateHook>>) {}
242
243 fn finish(&mut self) -> BundleState {
245 self.state_mut().merge_transitions(BundleRetention::Reverts);
246 self.state_mut().take_bundle()
247 }
248
249 fn validate_block_post_execution(
251 &self,
252 _block: &BlockWithSenders<<Self::Primitives as NodePrimitives>::Block>,
253 _receipts: &[<Self::Primitives as NodePrimitives>::Receipt],
254 _requests: &Requests,
255 ) -> Result<(), ConsensusError> {
256 Ok(())
257 }
258}
259
260pub trait BlockExecutionStrategyFactory: Send + Sync + Clone + Unpin + 'static {
262 type Primitives: NodePrimitives;
264
265 type Strategy<DB: Database<Error: Into<ProviderError> + Display>>: BlockExecutionStrategy<
267 DB = DB,
268 Primitives = Self::Primitives,
269 Error = BlockExecutionError,
270 >;
271
272 fn create_strategy<DB>(&self, db: DB) -> Self::Strategy<DB>
274 where
275 DB: Database<Error: Into<ProviderError> + Display>;
276}
277
278impl<F> Clone for BasicBlockExecutorProvider<F>
279where
280 F: Clone,
281{
282 fn clone(&self) -> Self {
283 Self { strategy_factory: self.strategy_factory.clone() }
284 }
285}
286
287#[allow(missing_debug_implementations)]
289pub struct BasicBlockExecutorProvider<F> {
290 strategy_factory: F,
291}
292
293impl<F> BasicBlockExecutorProvider<F> {
294 pub const fn new(strategy_factory: F) -> Self {
296 Self { strategy_factory }
297 }
298}
299
300impl<F> BlockExecutorProvider for BasicBlockExecutorProvider<F>
301where
302 F: BlockExecutionStrategyFactory,
303{
304 type Primitives = F::Primitives;
305
306 type Executor<DB: Database<Error: Into<ProviderError> + Display>> =
307 BasicBlockExecutor<F::Strategy<DB>>;
308
309 type BatchExecutor<DB: Database<Error: Into<ProviderError> + Display>> =
310 BasicBatchExecutor<F::Strategy<DB>>;
311
312 fn executor<DB>(&self, db: DB) -> Self::Executor<DB>
313 where
314 DB: Database<Error: Into<ProviderError> + Display>,
315 {
316 let strategy = self.strategy_factory.create_strategy(db);
317 BasicBlockExecutor::new(strategy)
318 }
319
320 fn batch_executor<DB>(&self, db: DB) -> Self::BatchExecutor<DB>
321 where
322 DB: Database<Error: Into<ProviderError> + Display>,
323 {
324 let strategy = self.strategy_factory.create_strategy(db);
325 let batch_record = BlockBatchRecord::default();
326 BasicBatchExecutor::new(strategy, batch_record)
327 }
328}
329
330#[allow(missing_debug_implementations, dead_code)]
333pub struct BasicBlockExecutor<S> {
334 pub(crate) strategy: S,
336}
337
338impl<S> BasicBlockExecutor<S> {
339 pub const fn new(strategy: S) -> Self {
341 Self { strategy }
342 }
343}
344
345impl<S, DB> Executor<DB> for BasicBlockExecutor<S>
346where
347 S: BlockExecutionStrategy<DB = DB>,
348 DB: Database<Error: Into<ProviderError> + Display>,
349{
350 type Input<'a> =
351 BlockExecutionInput<'a, BlockWithSenders<<S::Primitives as NodePrimitives>::Block>>;
352 type Output = BlockExecutionOutput<<S::Primitives as NodePrimitives>::Receipt>;
353 type Error = S::Error;
354
355 fn init(&mut self, env_overrides: Box<dyn TxEnvOverrides>) {
356 self.strategy.init(env_overrides);
357 }
358
359 fn execute(mut self, input: Self::Input<'_>) -> Result<Self::Output, Self::Error> {
360 let BlockExecutionInput { block, total_difficulty } = input;
361
362 self.strategy.apply_pre_execution_changes(block, total_difficulty)?;
363 let ExecuteOutput { receipts, gas_used } =
364 self.strategy.execute_transactions(block, total_difficulty)?;
365 let requests =
366 self.strategy.apply_post_execution_changes(block, total_difficulty, &receipts)?;
367 let state = self.strategy.finish();
368
369 Ok(BlockExecutionOutput { state, receipts, requests, gas_used })
370 }
371
372 fn execute_with_state_closure<F>(
373 mut self,
374 input: Self::Input<'_>,
375 mut state: F,
376 ) -> Result<Self::Output, Self::Error>
377 where
378 F: FnMut(&State<DB>),
379 {
380 let BlockExecutionInput { block, total_difficulty } = input;
381
382 self.strategy.apply_pre_execution_changes(block, total_difficulty)?;
383 let ExecuteOutput { receipts, gas_used } =
384 self.strategy.execute_transactions(block, total_difficulty)?;
385 let requests =
386 self.strategy.apply_post_execution_changes(block, total_difficulty, &receipts)?;
387
388 state(self.strategy.state_ref());
389
390 let state = self.strategy.finish();
391
392 Ok(BlockExecutionOutput { state, receipts, requests, gas_used })
393 }
394
395 fn execute_with_state_hook<H>(
396 mut self,
397 input: Self::Input<'_>,
398 state_hook: H,
399 ) -> Result<Self::Output, Self::Error>
400 where
401 H: OnStateHook + 'static,
402 {
403 let BlockExecutionInput { block, total_difficulty } = input;
404
405 self.strategy.with_state_hook(Some(Box::new(state_hook)));
406
407 self.strategy.apply_pre_execution_changes(block, total_difficulty)?;
408 let ExecuteOutput { receipts, gas_used } =
409 self.strategy.execute_transactions(block, total_difficulty)?;
410 let requests =
411 self.strategy.apply_post_execution_changes(block, total_difficulty, &receipts)?;
412
413 let state = self.strategy.finish();
414
415 Ok(BlockExecutionOutput { state, receipts, requests, gas_used })
416 }
417}
418
419#[allow(missing_debug_implementations)]
422pub struct BasicBatchExecutor<S>
423where
424 S: BlockExecutionStrategy,
425{
426 pub(crate) strategy: S,
428 pub(crate) batch_record: BlockBatchRecord<<S::Primitives as NodePrimitives>::Receipt>,
430}
431
432impl<S> BasicBatchExecutor<S>
433where
434 S: BlockExecutionStrategy,
435{
436 pub const fn new(
438 strategy: S,
439 batch_record: BlockBatchRecord<<S::Primitives as NodePrimitives>::Receipt>,
440 ) -> Self {
441 Self { strategy, batch_record }
442 }
443}
444
445impl<S, DB> BatchExecutor<DB> for BasicBatchExecutor<S>
446where
447 S: BlockExecutionStrategy<DB = DB, Error = BlockExecutionError>,
448 DB: Database<Error: Into<ProviderError> + Display>,
449{
450 type Input<'a> =
451 BlockExecutionInput<'a, BlockWithSenders<<S::Primitives as NodePrimitives>::Block>>;
452 type Output = ExecutionOutcome<<S::Primitives as NodePrimitives>::Receipt>;
453 type Error = BlockExecutionError;
454
455 fn execute_and_verify_one(&mut self, input: Self::Input<'_>) -> Result<(), Self::Error> {
456 let BlockExecutionInput { block, total_difficulty } = input;
457
458 if self.batch_record.first_block().is_none() {
459 self.batch_record.set_first_block(block.header().number());
460 }
461
462 self.strategy.apply_pre_execution_changes(block, total_difficulty)?;
463 let ExecuteOutput { receipts, .. } =
464 self.strategy.execute_transactions(block, total_difficulty)?;
465 let requests =
466 self.strategy.apply_post_execution_changes(block, total_difficulty, &receipts)?;
467
468 self.strategy.validate_block_post_execution(block, &receipts, &requests)?;
469
470 let retention = self.batch_record.bundle_retention(block.header().number());
472 self.strategy.state_mut().merge_transitions(retention);
473
474 self.batch_record.save_receipts(receipts)?;
476
477 self.batch_record.save_requests(requests);
479
480 Ok(())
481 }
482
483 fn finalize(mut self) -> Self::Output {
484 ExecutionOutcome::new(
485 self.strategy.state_mut().take_bundle(),
486 self.batch_record.take_receipts(),
487 self.batch_record.first_block().unwrap_or_default(),
488 self.batch_record.take_requests(),
489 )
490 }
491
492 fn set_tip(&mut self, tip: BlockNumber) {
493 self.batch_record.set_tip(tip);
494 }
495
496 fn set_prune_modes(&mut self, prune_modes: PruneModes) {
497 self.batch_record.set_prune_modes(prune_modes);
498 }
499
500 fn size_hint(&self) -> Option<usize> {
501 Some(self.strategy.state_ref().bundle_state.size_hint())
502 }
503}
504
505pub fn balance_increment_state<DB>(
509 balance_increments: &HashMap<Address, u128, DefaultHashBuilder>,
510 state: &mut State<DB>,
511) -> Result<EvmState, BlockExecutionError>
512where
513 DB: Database,
514{
515 let mut load_account = |address: &Address| -> Result<(Address, Account), BlockExecutionError> {
516 let cache_account = state.load_cache_account(*address).map_err(|_| {
517 BlockExecutionError::msg("could not load account for balance increment")
518 })?;
519
520 let account = cache_account.account.as_ref().ok_or_else(|| {
521 BlockExecutionError::msg("could not load account for balance increment")
522 })?;
523
524 Ok((
525 *address,
526 Account {
527 info: account.info.clone(),
528 storage: Default::default(),
529 status: AccountStatus::Touched,
530 },
531 ))
532 };
533
534 balance_increments
535 .iter()
536 .filter(|(_, &balance)| balance != 0)
537 .map(|(addr, _)| load_account(addr))
538 .collect::<Result<EvmState, _>>()
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
544 use alloy_primitives::U256;
545 use core::marker::PhantomData;
546 use reth_chainspec::{ChainSpec, MAINNET};
547 use reth_primitives::EthPrimitives;
548 use revm::db::{CacheDB, EmptyDBTyped};
549 use revm_primitives::{address, bytes, AccountInfo, TxEnv, KECCAK_EMPTY};
550 use std::sync::Arc;
551
552 #[derive(Clone, Default)]
553 struct TestExecutorProvider;
554
555 impl BlockExecutorProvider for TestExecutorProvider {
556 type Primitives = EthPrimitives;
557 type Executor<DB: Database<Error: Into<ProviderError> + Display>> = TestExecutor<DB>;
558 type BatchExecutor<DB: Database<Error: Into<ProviderError> + Display>> = TestExecutor<DB>;
559
560 fn executor<DB>(&self, _db: DB) -> Self::Executor<DB>
561 where
562 DB: Database<Error: Into<ProviderError> + Display>,
563 {
564 TestExecutor(PhantomData)
565 }
566
567 fn batch_executor<DB>(&self, _db: DB) -> Self::BatchExecutor<DB>
568 where
569 DB: Database<Error: Into<ProviderError> + Display>,
570 {
571 TestExecutor(PhantomData)
572 }
573 }
574
575 struct TestExecutor<DB>(PhantomData<DB>);
576
577 impl<DB> Executor<DB> for TestExecutor<DB> {
578 type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>;
579 type Output = BlockExecutionOutput<Receipt>;
580 type Error = BlockExecutionError;
581
582 fn execute(self, _input: Self::Input<'_>) -> Result<Self::Output, Self::Error> {
583 Err(BlockExecutionError::msg("execution unavailable for tests"))
584 }
585
586 fn execute_with_state_closure<F>(
587 self,
588 _: Self::Input<'_>,
589 _: F,
590 ) -> Result<Self::Output, Self::Error>
591 where
592 F: FnMut(&State<DB>),
593 {
594 Err(BlockExecutionError::msg("execution unavailable for tests"))
595 }
596
597 fn execute_with_state_hook<F>(
598 self,
599 _: Self::Input<'_>,
600 _: F,
601 ) -> Result<Self::Output, Self::Error>
602 where
603 F: OnStateHook,
604 {
605 Err(BlockExecutionError::msg("execution unavailable for tests"))
606 }
607 }
608
609 impl<DB> BatchExecutor<DB> for TestExecutor<DB> {
610 type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>;
611 type Output = ExecutionOutcome;
612 type Error = BlockExecutionError;
613
614 fn execute_and_verify_one(&mut self, _input: Self::Input<'_>) -> Result<(), Self::Error> {
615 Ok(())
616 }
617
618 fn finalize(self) -> Self::Output {
619 todo!()
620 }
621
622 fn set_tip(&mut self, _tip: BlockNumber) {
623 todo!()
624 }
625
626 fn set_prune_modes(&mut self, _prune_modes: PruneModes) {
627 todo!()
628 }
629
630 fn size_hint(&self) -> Option<usize> {
631 None
632 }
633 }
634
635 struct TestExecutorStrategy<DB, EvmConfig> {
636 _chain_spec: Arc<ChainSpec>,
639 _evm_config: EvmConfig,
640 state: State<DB>,
641 execute_transactions_result: ExecuteOutput<Receipt>,
642 apply_post_execution_changes_result: Requests,
643 finish_result: BundleState,
644 }
645
646 #[derive(Clone)]
647 struct TestExecutorStrategyFactory {
648 execute_transactions_result: ExecuteOutput<Receipt>,
649 apply_post_execution_changes_result: Requests,
650 finish_result: BundleState,
651 }
652
653 impl BlockExecutionStrategyFactory for TestExecutorStrategyFactory {
654 type Primitives = EthPrimitives;
655 type Strategy<DB: Database<Error: Into<ProviderError> + Display>> =
656 TestExecutorStrategy<DB, TestEvmConfig>;
657
658 fn create_strategy<DB>(&self, db: DB) -> Self::Strategy<DB>
659 where
660 DB: Database<Error: Into<ProviderError> + Display>,
661 {
662 let state = State::builder()
663 .with_database(db)
664 .with_bundle_update()
665 .without_state_clear()
666 .build();
667
668 TestExecutorStrategy {
669 _chain_spec: MAINNET.clone(),
670 _evm_config: TestEvmConfig {},
671 execute_transactions_result: self.execute_transactions_result.clone(),
672 apply_post_execution_changes_result: self
673 .apply_post_execution_changes_result
674 .clone(),
675 finish_result: self.finish_result.clone(),
676 state,
677 }
678 }
679 }
680
681 impl<DB> BlockExecutionStrategy for TestExecutorStrategy<DB, TestEvmConfig>
682 where
683 DB: Database,
684 {
685 type DB = DB;
686 type Primitives = EthPrimitives;
687 type Error = BlockExecutionError;
688
689 fn apply_pre_execution_changes(
690 &mut self,
691 _block: &BlockWithSenders,
692 _total_difficulty: U256,
693 ) -> Result<(), Self::Error> {
694 Ok(())
695 }
696
697 fn execute_transactions(
698 &mut self,
699 _block: &BlockWithSenders,
700 _total_difficulty: U256,
701 ) -> Result<ExecuteOutput<Receipt>, Self::Error> {
702 Ok(self.execute_transactions_result.clone())
703 }
704
705 fn apply_post_execution_changes(
706 &mut self,
707 _block: &BlockWithSenders,
708 _total_difficulty: U256,
709 _receipts: &[Receipt],
710 ) -> Result<Requests, Self::Error> {
711 Ok(self.apply_post_execution_changes_result.clone())
712 }
713
714 fn state_ref(&self) -> &State<DB> {
715 &self.state
716 }
717
718 fn state_mut(&mut self) -> &mut State<DB> {
719 &mut self.state
720 }
721
722 fn with_state_hook(&mut self, _hook: Option<Box<dyn OnStateHook>>) {}
723
724 fn finish(&mut self) -> BundleState {
725 self.finish_result.clone()
726 }
727
728 fn validate_block_post_execution(
729 &self,
730 _block: &BlockWithSenders,
731 _receipts: &[Receipt],
732 _requests: &Requests,
733 ) -> Result<(), ConsensusError> {
734 Ok(())
735 }
736 }
737
738 #[derive(Clone)]
739 struct TestEvmConfig {}
740
741 #[test]
742 fn test_provider() {
743 let provider = TestExecutorProvider;
744 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
745 let executor = provider.executor(db);
746 let _ = executor.execute(BlockExecutionInput::new(&Default::default(), U256::ZERO));
747 }
748
749 #[test]
750 fn test_strategy() {
751 let expected_gas_used = 10;
752 let expected_receipts = vec![Receipt::default()];
753 let expected_execute_transactions_result = ExecuteOutput::<Receipt> {
754 receipts: expected_receipts.clone(),
755 gas_used: expected_gas_used,
756 };
757 let expected_apply_post_execution_changes_result = Requests::new(vec![bytes!("deadbeef")]);
758 let expected_finish_result = BundleState::default();
759
760 let strategy_factory = TestExecutorStrategyFactory {
761 execute_transactions_result: expected_execute_transactions_result,
762 apply_post_execution_changes_result: expected_apply_post_execution_changes_result
763 .clone(),
764 finish_result: expected_finish_result.clone(),
765 };
766 let provider = BasicBlockExecutorProvider::new(strategy_factory);
767 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
768 let executor = provider.executor(db);
769 let result = executor.execute(BlockExecutionInput::new(&Default::default(), U256::ZERO));
770
771 assert!(result.is_ok());
772 let block_execution_output = result.unwrap();
773 assert_eq!(block_execution_output.gas_used, expected_gas_used);
774 assert_eq!(block_execution_output.receipts, expected_receipts);
775 assert_eq!(block_execution_output.requests, expected_apply_post_execution_changes_result);
776 assert_eq!(block_execution_output.state, expected_finish_result);
777 }
778
779 #[test]
780 fn test_tx_env_overrider() {
781 let strategy_factory = TestExecutorStrategyFactory {
782 execute_transactions_result: ExecuteOutput {
783 receipts: vec![Receipt::default()],
784 gas_used: 10,
785 },
786 apply_post_execution_changes_result: Requests::new(vec![bytes!("deadbeef")]),
787 finish_result: BundleState::default(),
788 };
789 let provider = BasicBlockExecutorProvider::new(strategy_factory);
790 let db = CacheDB::<EmptyDBTyped<ProviderError>>::default();
791
792 let mut executor = provider.executor(db);
794 executor.init(Box::new(|tx_env: &mut TxEnv| {
797 tx_env.nonce.take();
798 }));
799 let result = executor.execute(BlockExecutionInput::new(&Default::default(), U256::ZERO));
800 assert!(result.is_ok());
801 }
802
803 fn setup_state_with_account(
804 addr: Address,
805 balance: u128,
806 nonce: u64,
807 ) -> State<CacheDB<EmptyDBTyped<BlockExecutionError>>> {
808 let db = CacheDB::<EmptyDBTyped<BlockExecutionError>>::default();
809 let mut state = State::builder().with_database(db).with_bundle_update().build();
810
811 let account_info = AccountInfo {
812 balance: U256::from(balance),
813 nonce,
814 code_hash: KECCAK_EMPTY,
815 code: None,
816 };
817 state.insert_account(addr, account_info);
818 state
819 }
820
821 #[test]
822 fn test_balance_increment_state_zero() {
823 let addr = address!("1000000000000000000000000000000000000000");
824 let mut state = setup_state_with_account(addr, 100, 1);
825
826 let mut increments = HashMap::<Address, u128, DefaultHashBuilder>::default();
827 increments.insert(addr, 0);
828
829 let result = balance_increment_state(&increments, &mut state).unwrap();
830 assert!(result.is_empty(), "Zero increments should be ignored");
831 }
832
833 #[test]
834 fn test_balance_increment_state_empty_increments_map() {
835 let mut state = State::builder()
836 .with_database(CacheDB::<EmptyDBTyped<BlockExecutionError>>::default())
837 .with_bundle_update()
838 .build();
839
840 let increments = HashMap::<Address, u128, DefaultHashBuilder>::default();
841 let result = balance_increment_state(&increments, &mut state).unwrap();
842 assert!(result.is_empty(), "Empty increments map should return empty state");
843 }
844
845 #[test]
846 fn test_balance_increment_state_multiple_valid_increments() {
847 let addr1 = address!("1000000000000000000000000000000000000000");
848 let addr2 = address!("2000000000000000000000000000000000000000");
849
850 let mut state = setup_state_with_account(addr1, 100, 1);
851
852 let account2 =
853 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
854 state.insert_account(addr2, account2);
855
856 let mut increments = HashMap::<Address, u128, DefaultHashBuilder>::default();
857 increments.insert(addr1, 50);
858 increments.insert(addr2, 100);
859
860 let result = balance_increment_state(&increments, &mut state).unwrap();
861
862 assert_eq!(result.len(), 2);
863 assert_eq!(result.get(&addr1).unwrap().info.balance, U256::from(100));
864 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
865 }
866
867 #[test]
868 fn test_balance_increment_state_mixed_zero_and_nonzero_increments() {
869 let addr1 = address!("1000000000000000000000000000000000000000");
870 let addr2 = address!("2000000000000000000000000000000000000000");
871
872 let mut state = setup_state_with_account(addr1, 100, 1);
873
874 let account2 =
875 AccountInfo { balance: U256::from(200), nonce: 1, code_hash: KECCAK_EMPTY, code: None };
876 state.insert_account(addr2, account2);
877
878 let mut increments = HashMap::<Address, u128, DefaultHashBuilder>::default();
879 increments.insert(addr1, 0);
880 increments.insert(addr2, 100);
881
882 let result = balance_increment_state(&increments, &mut state).unwrap();
883
884 assert_eq!(result.len(), 1, "Only non-zero increments should be included");
885 assert!(!result.contains_key(&addr1), "Zero increment account should not be included");
886 assert_eq!(result.get(&addr2).unwrap().info.balance, U256::from(200));
887 }
888}