1use alloy_consensus::BlockHeader;
4use alloy_eips::eip4895::Withdrawal;
5use alloy_primitives::{map::HashMap, Address, U256};
6use reth_chainspec::EthereumHardforks;
7use reth_consensus_common::calc;
8use reth_primitives_traits::BlockBody;
9
10#[inline]
15pub fn post_block_balance_increments<ChainSpec, Block>(
16 chain_spec: &ChainSpec,
17 block: &Block,
18 total_difficulty: U256,
19) -> HashMap<Address, u128>
20where
21 ChainSpec: EthereumHardforks,
22 Block: reth_primitives_traits::Block,
23{
24 let mut balance_increments = HashMap::default();
25
26 if let Some(base_block_reward) = calc::base_block_reward(
28 chain_spec,
29 block.header().number(),
30 block.header().difficulty(),
31 total_difficulty,
32 ) {
33 if let Some(ommers) = block.body().ommers() {
35 for ommer in ommers {
36 *balance_increments.entry(ommer.beneficiary()).or_default() +=
37 calc::ommer_reward(base_block_reward, block.header().number(), ommer.number());
38 }
39 }
40
41 *balance_increments.entry(block.header().beneficiary()).or_default() += calc::block_reward(
43 base_block_reward,
44 block.body().ommers().map(|s| s.len()).unwrap_or(0),
45 );
46 }
47
48 insert_post_block_withdrawals_balance_increments(
50 chain_spec,
51 block.header().timestamp(),
52 block.body().withdrawals().as_ref().map(|w| w.as_slice()),
53 &mut balance_increments,
54 );
55
56 balance_increments
57}
58
59#[inline]
64pub fn post_block_withdrawals_balance_increments<ChainSpec: EthereumHardforks>(
65 chain_spec: &ChainSpec,
66 block_timestamp: u64,
67 withdrawals: &[Withdrawal],
68) -> HashMap<Address, u128> {
69 let mut balance_increments =
70 HashMap::with_capacity_and_hasher(withdrawals.len(), Default::default());
71 insert_post_block_withdrawals_balance_increments(
72 chain_spec,
73 block_timestamp,
74 Some(withdrawals),
75 &mut balance_increments,
76 );
77 balance_increments
78}
79
80#[inline]
85pub fn insert_post_block_withdrawals_balance_increments<ChainSpec: EthereumHardforks>(
86 chain_spec: &ChainSpec,
87 block_timestamp: u64,
88 withdrawals: Option<&[Withdrawal]>,
89 balance_increments: &mut HashMap<Address, u128>,
90) {
91 if chain_spec.is_shanghai_active_at_timestamp(block_timestamp) {
93 if let Some(withdrawals) = withdrawals {
94 for withdrawal in withdrawals {
95 if withdrawal.amount > 0 {
96 *balance_increments.entry(withdrawal.address).or_default() +=
97 withdrawal.amount_wei().to::<u128>();
98 }
99 }
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use alloy_consensus::constants::GWEI_TO_WEI;
108 use reth_chainspec::ChainSpec;
109 use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition};
110
111 #[test]
114 fn test_insert_post_block_withdrawals_balance_increments_shanghai_active_with_withdrawals() {
115 let chain_spec = ChainSpec {
118 hardforks: ChainHardforks::new(vec![(
119 Box::new(EthereumHardfork::Shanghai),
120 ForkCondition::Timestamp(100),
121 )]),
122 ..Default::default()
123 };
124
125 let block_timestamp = 1000;
127 let withdrawals = vec![
128 Withdrawal {
129 address: Address::from([1; 20]),
130 amount: 1000,
131 index: 45,
132 validator_index: 12,
133 },
134 Withdrawal {
135 address: Address::from([2; 20]),
136 amount: 500,
137 index: 412,
138 validator_index: 123,
139 },
140 ];
141
142 let mut balance_increments = HashMap::default();
144
145 insert_post_block_withdrawals_balance_increments(
148 &chain_spec,
149 block_timestamp,
150 Some(&withdrawals),
151 &mut balance_increments,
152 );
153
154 assert_eq!(balance_increments.len(), 2);
157 assert_eq!(
159 *balance_increments.get(&Address::from([1; 20])).unwrap(),
160 (1000 * GWEI_TO_WEI).into()
161 );
162 assert_eq!(
163 *balance_increments.get(&Address::from([2; 20])).unwrap(),
164 (500 * GWEI_TO_WEI).into()
165 );
166 }
167
168 #[test]
171 fn test_insert_post_block_withdrawals_balance_increments_shanghai_active_no_withdrawals() {
172 let chain_spec = ChainSpec {
175 hardforks: ChainHardforks::new(vec![(
176 Box::new(EthereumHardfork::Shanghai),
177 ForkCondition::Timestamp(100),
178 )]),
179 ..Default::default()
180 };
181
182 let block_timestamp = 1000;
184 let withdrawals = Vec::<Withdrawal>::new();
185
186 let mut balance_increments = HashMap::default();
188
189 insert_post_block_withdrawals_balance_increments(
192 &chain_spec,
193 block_timestamp,
194 Some(&withdrawals),
195 &mut balance_increments,
196 );
197
198 assert!(balance_increments.is_empty());
201 }
202
203 #[test]
206 fn test_insert_post_block_withdrawals_balance_increments_shanghai_not_active_with_withdrawals()
207 {
208 let chain_spec = ChainSpec::default(); let block_timestamp = 1000;
214 let withdrawals = vec![
215 Withdrawal {
216 address: Address::from([1; 20]),
217 amount: 1000,
218 index: 45,
219 validator_index: 12,
220 },
221 Withdrawal {
222 address: Address::from([2; 20]),
223 amount: 500,
224 index: 412,
225 validator_index: 123,
226 },
227 ];
228
229 let mut balance_increments = HashMap::default();
231
232 insert_post_block_withdrawals_balance_increments(
235 &chain_spec,
236 block_timestamp,
237 Some(&withdrawals),
238 &mut balance_increments,
239 );
240
241 assert!(balance_increments.is_empty());
244 }
245
246 #[test]
249 fn test_insert_post_block_withdrawals_balance_increments_shanghai_active_with_zero_withdrawals()
250 {
251 let chain_spec = ChainSpec {
254 hardforks: ChainHardforks::new(vec![(
255 Box::new(EthereumHardfork::Shanghai),
256 ForkCondition::Timestamp(100),
257 )]),
258 ..Default::default()
259 };
260
261 let block_timestamp = 1000;
263 let withdrawals = vec![
264 Withdrawal {
265 address: Address::from([1; 20]),
266 amount: 0, index: 45,
268 validator_index: 12,
269 },
270 Withdrawal {
271 address: Address::from([2; 20]),
272 amount: 0, index: 412,
274 validator_index: 123,
275 },
276 ];
277
278 let mut balance_increments = HashMap::default();
280
281 insert_post_block_withdrawals_balance_increments(
284 &chain_spec,
285 block_timestamp,
286 Some(&withdrawals),
287 &mut balance_increments,
288 );
289
290 assert!(balance_increments.is_empty());
293 }
294
295 #[test]
298 fn test_insert_post_block_withdrawals_balance_increments_shanghai_active_with_empty_withdrawals(
299 ) {
300 let chain_spec = ChainSpec {
303 hardforks: ChainHardforks::new(vec![(
304 Box::new(EthereumHardfork::Shanghai),
305 ForkCondition::Timestamp(100),
306 )]),
307 ..Default::default()
308 };
309
310 let block_timestamp = 1000;
312 let withdrawals = None; let mut balance_increments = HashMap::default();
316
317 insert_post_block_withdrawals_balance_increments(
320 &chain_spec,
321 block_timestamp,
322 withdrawals,
323 &mut balance_increments,
324 );
325
326 assert!(balance_increments.is_empty());
329 }
330}