reth_consensus_common/calc.rs
1use alloy_consensus::constants::ETH_TO_WEI;
2use alloy_primitives::{BlockNumber, U256};
3use reth_chainspec::{EthereumHardfork, Hardforks};
4
5/// Calculates the base block reward.
6///
7/// The base block reward is defined as:
8///
9/// - For Paris and later: `None`
10/// - For Petersburg and later: `Some(2 ETH)`
11/// - For Byzantium and later: `Some(3 ETH)`
12/// - Otherwise: `Some(5 ETH)`
13///
14/// # Note
15///
16/// This does not include the reward for including ommers. To calculate the full block reward, see
17/// [`block_reward`].
18///
19/// # References
20///
21/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
22///
23/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
24pub fn base_block_reward(
25 chain_spec: impl Hardforks,
26 block_number: BlockNumber,
27 block_difficulty: U256,
28 total_difficulty: U256,
29) -> Option<u128> {
30 if chain_spec.fork(EthereumHardfork::Paris).active_at_ttd(total_difficulty, block_difficulty) {
31 None
32 } else {
33 Some(base_block_reward_pre_merge(chain_spec, block_number))
34 }
35}
36
37/// Calculates the base block reward __before__ the merge (Paris hardfork).
38///
39/// Caution: The caller must ensure that the block number is before the merge.
40pub fn base_block_reward_pre_merge(chain_spec: impl Hardforks, block_number: BlockNumber) -> u128 {
41 if chain_spec.fork(EthereumHardfork::Constantinople).active_at_block(block_number) {
42 ETH_TO_WEI * 2
43 } else if chain_spec.fork(EthereumHardfork::Byzantium).active_at_block(block_number) {
44 ETH_TO_WEI * 3
45 } else {
46 ETH_TO_WEI * 5
47 }
48}
49
50/// Calculates the reward for a block, including the reward for ommer inclusion.
51///
52/// The base reward should be calculated using [`base_block_reward`]. `ommers` represents the number
53/// of ommers included in the block.
54///
55/// # Examples
56///
57/// ```
58/// # use reth_chainspec::MAINNET;
59/// # use reth_consensus_common::calc::{base_block_reward, block_reward};
60/// # use alloy_consensus::constants::ETH_TO_WEI;
61/// # use alloy_primitives::U256;
62/// #
63/// // This is block 126 on mainnet.
64/// let block_number = 126;
65/// let block_difficulty = U256::from(18_145_285_642usize);
66/// let total_difficulty = U256::from(2_235_668_675_900usize);
67/// let number_of_ommers = 1;
68///
69/// let reward = base_block_reward(&*MAINNET, block_number, block_difficulty, total_difficulty)
70/// .map(|reward| block_reward(reward, 1));
71///
72/// // The base block reward is 5 ETH, and the ommer inclusion reward is 1/32th of 5 ETH.
73/// assert_eq!(reward.unwrap(), ETH_TO_WEI * 5 + ((ETH_TO_WEI * 5) >> 5));
74/// ```
75///
76/// # References
77///
78/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
79///
80/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
81pub const fn block_reward(base_block_reward: u128, ommers: usize) -> u128 {
82 base_block_reward + (base_block_reward >> 5) * ommers as u128
83}
84
85/// Calculate the reward for an ommer.
86///
87/// # Application
88///
89/// Rewards are accumulative, so they should be added to the beneficiary addresses in addition to
90/// any other rewards from the same block.
91///
92/// From the yellow paper (page 15):
93///
94/// > If there are collisions of the beneficiary addresses between ommers and the block (i.e. two
95/// > ommers with the same beneficiary address or an ommer with the same beneficiary address as the
96/// > present block), additions are applied cumulatively.
97///
98/// # References
99///
100/// - Implementation: [OpenEthereum][oe]
101/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
102///
103/// [oe]: https://github.com/openethereum/openethereum/blob/6c2d392d867b058ff867c4373e40850ca3f96969/crates/ethcore/src/ethereum/ethash.rs#L319-L333
104/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
105pub const fn ommer_reward(
106 base_block_reward: u128,
107 block_number: BlockNumber,
108 ommer_block_number: BlockNumber,
109) -> u128 {
110 ((8 + ommer_block_number - block_number) as u128 * base_block_reward) >> 3
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use reth_chainspec::MAINNET;
117
118 #[test]
119 fn calc_base_block_reward() {
120 // ((block number, td), reward)
121 let cases = [
122 // Pre-byzantium
123 ((0, U256::ZERO), Some(ETH_TO_WEI * 5)),
124 // Byzantium
125 ((4370000, U256::ZERO), Some(ETH_TO_WEI * 3)),
126 // Petersburg
127 ((7280000, U256::ZERO), Some(ETH_TO_WEI * 2)),
128 // Merge
129 ((10000000, U256::from(58_750_000_000_000_000_000_000_u128)), None),
130 ];
131
132 for ((block_number, td), expected_reward) in cases {
133 assert_eq!(base_block_reward(&*MAINNET, block_number, U256::ZERO, td), expected_reward);
134 }
135 }
136
137 #[test]
138 fn calc_full_block_reward() {
139 let base_reward = ETH_TO_WEI;
140 let one_thirty_twoth_reward = base_reward >> 5;
141
142 // (num_ommers, reward)
143 let cases = [
144 (0, base_reward),
145 (1, base_reward + one_thirty_twoth_reward),
146 (2, base_reward + one_thirty_twoth_reward * 2),
147 ];
148
149 for (num_ommers, expected_reward) in cases {
150 assert_eq!(block_reward(base_reward, num_ommers), expected_reward);
151 }
152 }
153}