diff --git a/Cargo.lock b/Cargo.lock index 082fe9fbd792..e5beda6176a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8236,6 +8236,7 @@ dependencies = [ "alloy-eips", "reth-chainspec", "reth-consensus-common", + "reth-ethereum-forks", "reth-execution-errors", "reth-primitives", "reth-prune-types", diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index bbb60b293edd..665d61a37d8a 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -33,6 +33,7 @@ tracing.workspace = true [dev-dependencies] reth-trie.workspace = true +reth-ethereum-forks.workspace = true [features] default = ["std", "c-kzg"] diff --git a/crates/revm/src/state_change.rs b/crates/revm/src/state_change.rs index 519f9704d702..f0ca1255771c 100644 --- a/crates/revm/src/state_change.rs +++ b/crates/revm/src/state_change.rs @@ -168,3 +168,230 @@ pub fn insert_post_block_withdrawals_balance_increments( } } } + +#[cfg(test)] +mod tests { + use super::*; + use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition}; + use reth_primitives::constants::GWEI_TO_WEI; + + /// Tests that the function correctly inserts balance increments when the Shanghai hardfork is + /// active and there are withdrawals. + #[test] + fn test_insert_post_block_withdrawals_balance_increments_shanghai_active_with_withdrawals() { + // Arrange + // Create a ChainSpec with the Shanghai hardfork active at timestamp 100 + let chain_spec = ChainSpec { + hardforks: ChainHardforks::new(vec![( + Box::new(EthereumHardfork::Shanghai), + ForkCondition::Timestamp(100), + )]), + ..Default::default() + }; + + // Define the block timestamp and withdrawals + let block_timestamp = 1000; + let withdrawals = vec![ + Withdrawal { + address: Address::from([1; 20]), + amount: 1000, + index: 45, + validator_index: 12, + }, + Withdrawal { + address: Address::from([2; 20]), + amount: 500, + index: 412, + validator_index: 123, + }, + ]; + + // Create an empty HashMap to hold the balance increments + let mut balance_increments = HashMap::new(); + + // Act + // Call the function with the prepared inputs + insert_post_block_withdrawals_balance_increments( + &chain_spec, + block_timestamp, + Some(&withdrawals), + &mut balance_increments, + ); + + // Assert + // Verify that the balance increments map has the correct number of entries + assert_eq!(balance_increments.len(), 2); + // Verify that the balance increments map contains the correct values for each address + assert_eq!( + *balance_increments.get(&Address::from([1; 20])).unwrap(), + (1000 * GWEI_TO_WEI).into() + ); + assert_eq!( + *balance_increments.get(&Address::from([2; 20])).unwrap(), + (500 * GWEI_TO_WEI).into() + ); + } + + /// Tests that the function correctly handles the case when Shanghai is active but there are no + /// withdrawals. + #[test] + fn test_insert_post_block_withdrawals_balance_increments_shanghai_active_no_withdrawals() { + // Arrange + // Create a ChainSpec with the Shanghai hardfork active + let chain_spec = ChainSpec { + hardforks: ChainHardforks::new(vec![( + Box::new(EthereumHardfork::Shanghai), + ForkCondition::Timestamp(100), + )]), + ..Default::default() + }; + + // Define the block timestamp and an empty list of withdrawals + let block_timestamp = 1000; + let withdrawals = Vec::::new(); + + // Create an empty HashMap to hold the balance increments + let mut balance_increments = HashMap::new(); + + // Act + // Call the function with the prepared inputs + insert_post_block_withdrawals_balance_increments( + &chain_spec, + block_timestamp, + Some(&withdrawals), + &mut balance_increments, + ); + + // Assert + // Verify that the balance increments map is empty + assert!(balance_increments.is_empty()); + } + + /// Tests that the function correctly handles the case when Shanghai is not active even if there + /// are withdrawals. + #[test] + fn test_insert_post_block_withdrawals_balance_increments_shanghai_not_active_with_withdrawals() + { + // Arrange + // Create a ChainSpec without the Shanghai hardfork active + let chain_spec = ChainSpec::default(); // Mock chain spec with Shanghai not active + + // Define the block timestamp and withdrawals + let block_timestamp = 1000; + let withdrawals = vec![ + Withdrawal { + address: Address::from([1; 20]), + amount: 1000, + index: 45, + validator_index: 12, + }, + Withdrawal { + address: Address::from([2; 20]), + amount: 500, + index: 412, + validator_index: 123, + }, + ]; + + // Create an empty HashMap to hold the balance increments + let mut balance_increments = HashMap::new(); + + // Act + // Call the function with the prepared inputs + insert_post_block_withdrawals_balance_increments( + &chain_spec, + block_timestamp, + Some(&withdrawals), + &mut balance_increments, + ); + + // Assert + // Verify that the balance increments map is empty + assert!(balance_increments.is_empty()); + } + + /// Tests that the function correctly handles the case when Shanghai is active but all + /// withdrawals have zero amounts. + #[test] + fn test_insert_post_block_withdrawals_balance_increments_shanghai_active_with_zero_withdrawals() + { + // Arrange + // Create a ChainSpec with the Shanghai hardfork active + let chain_spec = ChainSpec { + hardforks: ChainHardforks::new(vec![( + Box::new(EthereumHardfork::Shanghai), + ForkCondition::Timestamp(100), + )]), + ..Default::default() + }; + + // Define the block timestamp and withdrawals with zero amounts + let block_timestamp = 1000; + let withdrawals = vec![ + Withdrawal { + address: Address::from([1; 20]), + amount: 0, // Zero withdrawal amount + index: 45, + validator_index: 12, + }, + Withdrawal { + address: Address::from([2; 20]), + amount: 0, // Zero withdrawal amount + index: 412, + validator_index: 123, + }, + ]; + + // Create an empty HashMap to hold the balance increments + let mut balance_increments = HashMap::new(); + + // Act + // Call the function with the prepared inputs + insert_post_block_withdrawals_balance_increments( + &chain_spec, + block_timestamp, + Some(&withdrawals), + &mut balance_increments, + ); + + // Assert + // Verify that the balance increments map is empty + assert!(balance_increments.is_empty()); + } + + /// Tests that the function correctly handles the case when Shanghai is active but there are no + /// withdrawals provided. + #[test] + fn test_insert_post_block_withdrawals_balance_increments_shanghai_active_with_empty_withdrawals( + ) { + // Arrange + // Create a ChainSpec with the Shanghai hardfork active + let chain_spec = ChainSpec { + hardforks: ChainHardforks::new(vec![( + Box::new(EthereumHardfork::Shanghai), + ForkCondition::Timestamp(100), + )]), + ..Default::default() + }; + + // Define the block timestamp and no withdrawals + let block_timestamp = 1000; + let withdrawals = None; // No withdrawals provided + + // Create an empty HashMap to hold the balance increments + let mut balance_increments = HashMap::new(); + + // Act + // Call the function with the prepared inputs + insert_post_block_withdrawals_balance_increments( + &chain_spec, + block_timestamp, + withdrawals, + &mut balance_increments, + ); + + // Assert + // Verify that the balance increments map is empty + assert!(balance_increments.is_empty()); + } +}