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}