refactor: reward calc cleanup (#2075)

This commit is contained in:
Bjerg
2023-04-01 22:22:22 +02:00
committed by GitHub
parent ee81e4f035
commit 7c6c0b41e8
7 changed files with 176 additions and 51 deletions

1
Cargo.lock generated
View File

@@ -4727,6 +4727,7 @@ dependencies = [
"hash-db",
"parking_lot 0.12.1",
"plain_hasher",
"reth-consensus-common",
"reth-db",
"reth-interfaces",
"reth-primitives",

View File

@@ -0,0 +1,143 @@
use reth_primitives::{constants::ETH_TO_WEI, BlockNumber, ChainSpec, Hardfork, U256};
/// Calculates the base block reward.
///
/// The base block reward is defined as:
///
/// - For Paris and later: `None`
/// - For Petersburg and later: `Some(2 ETH)`
/// - For Byzantium and later: `Some(3 ETH)`
/// - Otherwise: `Some(5 ETH)`
///
/// # Note
///
/// This does not include the reward for including ommers. To calculate the full block reward, see
/// [`block_reward`].
///
/// # References
///
/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
///
/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
pub fn base_block_reward(
chain_spec: &ChainSpec,
block_number: BlockNumber,
block_difficulty: U256,
total_difficulty: U256,
) -> Option<u128> {
if chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty, block_difficulty) {
None
} else if chain_spec.fork(Hardfork::Petersburg).active_at_block(block_number) {
Some(ETH_TO_WEI * 2)
} else if chain_spec.fork(Hardfork::Byzantium).active_at_block(block_number) {
Some(ETH_TO_WEI * 3)
} else {
Some(ETH_TO_WEI * 5)
}
}
/// Calculates the reward for a block, including the reward for ommer inclusion.
///
/// The base reward should be calculated using [`base_block_reward`]. `ommers` represents the number
/// of ommers included in the block.
///
/// # Examples
///
/// ```
/// # use reth_consensus_common::calc::{base_block_reward, block_reward};
/// # use reth_primitives::constants::ETH_TO_WEI;
/// # use reth_primitives::{MAINNET, U256};
/// #
/// // This is block 126 on mainnet.
/// let block_number = 126;
/// let block_difficulty = U256::from(18_145_285_642usize);
/// let total_difficulty = U256::from(2_235_668_675_900usize);
/// let number_of_ommers = 1;
///
/// let reward = base_block_reward(&MAINNET, block_number, block_difficulty, total_difficulty).map(|reward| block_reward(reward, 1));
///
/// // The base block reward is 5 ETH, and the ommer inclusion reward is 1/32th of 5 ETH.
/// assert_eq!(
/// reward.unwrap(),
/// U256::from(ETH_TO_WEI * 5 + ((ETH_TO_WEI * 5) >> 5))
/// );
/// ```
///
/// # References
///
/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
///
/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
pub fn block_reward(base_block_reward: u128, ommers: usize) -> U256 {
U256::from(base_block_reward + (base_block_reward >> 5) * ommers as u128)
}
/// Calculate the reward for an ommer.
///
/// # Application
///
/// Rewards are accumulative, so they should be added to the beneficiary addresses in addition to
/// any other rewards from the same block.
///
/// From the yellow paper (page 15):
///
/// > If there are collissions of the beneficiary addresses between ommers and the block (i.e. two
/// > ommers with the same beneficiary address or an ommer with the same beneficiary address as the
/// > present block), additions are applied cumulatively.
///
/// # References
///
/// - Implementation: [OpenEthereum][oe]
/// - Definition: [Yellow Paper][yp] (page 15, 11.3)
///
/// [oe]: https://github.com/openethereum/openethereum/blob/6c2d392d867b058ff867c4373e40850ca3f96969/crates/ethcore/src/ethereum/ethash.rs#L319-L333
/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
pub fn ommer_reward(
base_block_reward: u128,
block_number: BlockNumber,
ommer_block_number: BlockNumber,
) -> U256 {
U256::from(((8 + ommer_block_number - block_number) as u128 * base_block_reward) >> 3)
}
#[cfg(test)]
mod tests {
use super::*;
use reth_primitives::{MAINNET, U256};
#[test]
fn calc_base_block_reward() {
// ((block number, td), reward)
let cases = [
// Pre-byzantium
((0, U256::ZERO), Some(ETH_TO_WEI * 5)),
// Byzantium
((4370000, U256::ZERO), Some(ETH_TO_WEI * 3)),
// Petersburg
((7280000, U256::ZERO), Some(ETH_TO_WEI * 2)),
// Merge
((10000000, U256::from(58_750_000_000_000_000_000_000_u128)), None),
];
for ((block_number, td), expected_reward) in cases {
assert_eq!(base_block_reward(&MAINNET, block_number, U256::ZERO, td), expected_reward);
}
}
#[test]
fn calc_full_block_reward() {
let base_reward = ETH_TO_WEI * 1;
let one_thirty_twoth_reward = base_reward >> 5;
// (num_ommers, reward)
let cases = [
(0, base_reward),
(1, base_reward + one_thirty_twoth_reward),
(2, base_reward + one_thirty_twoth_reward * 2),
];
for (num_ommers, expected_reward) in cases {
assert_eq!(block_reward(base_reward, num_ommers), U256::from(expected_reward));
}
}
}

View File

@@ -9,3 +9,6 @@
/// Collection of consensus validation methods.
pub mod validation;
/// Various calculation methods (e.g. block rewards)
pub mod calc;

View File

@@ -21,6 +21,7 @@ reth-revm-inspectors = { path = "../revm/revm-inspectors" }
reth-rlp = { path = "../rlp" }
reth-db = { path = "../storage/db" }
reth-provider = { path = "../storage/provider" }
reth-consensus-common = { path = "../consensus/common" }
# revm
revm = { version = "3.0.0" }

View File

@@ -1,4 +1,5 @@
use crate::post_state::PostState;
use reth_consensus_common::calc;
use reth_interfaces::executor::Error;
use reth_primitives::{
Account, Address, Block, Bloom, Bytecode, ChainSpec, Hardfork, Header, Log, Receipt,
@@ -6,7 +7,6 @@ use reth_primitives::{
};
use reth_provider::{BlockExecutor, StateProvider};
use reth_revm::{
config::{WEI_2ETH, WEI_3ETH, WEI_5ETH},
database::SubState,
env::{fill_cfg_and_block_env, fill_tx_env},
into_reth_log, to_reth_acc,
@@ -216,26 +216,22 @@ where
) -> Result<HashMap<Address, U256>, Error> {
let mut balance_increments = HashMap::<Address, U256>::default();
// Collect balance increments for block and uncle rewards.
if let Some(reward) = self.get_block_reward(block, td) {
// Calculate Uncle reward
// OpenEthereum code: https://github.com/openethereum/openethereum/blob/6c2d392d867b058ff867c4373e40850ca3f96969/crates/ethcore/src/ethereum/ethash.rs#L319-L333
// Add block rewards if they are enabled.
if let Some(base_block_reward) =
calc::base_block_reward(&self.chain_spec, block.number, block.difficulty, td)
{
// Ommer rewards
for ommer in block.ommers.iter() {
let ommer_reward =
U256::from(((8 + ommer.number - block.number) as u128 * reward) >> 3);
// From yellowpaper Page 15:
// If there are collisions of the beneficiary addresses between ommers and the
// block (i.e. two ommers with the same beneficiary address
// or an ommer with the same beneficiary address as the
// present block), additions are applied cumulatively
*balance_increments.entry(ommer.beneficiary).or_default() += ommer_reward;
*balance_increments.entry(ommer.beneficiary).or_default() +=
calc::ommer_reward(base_block_reward, block.number, ommer.number);
}
// Increment balance for main block reward.
let block_reward = U256::from(reward + (reward >> 5) * block.ommers.len() as u128);
*balance_increments.entry(block.beneficiary).or_default() += block_reward;
// Full block reward
*balance_increments.entry(block.beneficiary).or_default() +=
calc::block_reward(base_block_reward, block.ommers.len());
}
// Process withdrawals
if self.chain_spec.fork(Hardfork::Shanghai).active_at_timestamp(block.timestamp) {
if let Some(withdrawals) = block.withdrawals.as_ref() {
for withdrawal in withdrawals {
@@ -248,29 +244,6 @@ where
Ok(balance_increments)
}
/// From yellowpapper Page 15:
/// 11.3. Reward Application. The application of rewards to a block involves raising the
/// balance of the accounts of the beneficiary address of the block and each ommer by
/// a certain amount. We raise the blocks beneficiary account by Rblock; for each
/// ommer, we raise the blocks beneficiary by an additional 1/32 of the block reward
/// and the beneficiary of the ommer gets rewarded depending on the blocknumber.
/// Formally we define the function Ω.
///
/// NOTE: Related to Ethereum reward change, for other network this is probably going to be
/// moved to config.
fn get_block_reward(&self, header: &Header, total_difficulty: U256) -> Option<u128> {
if self.chain_spec.fork(Hardfork::Paris).active_at_ttd(total_difficulty, header.difficulty)
{
None
} else if self.chain_spec.fork(Hardfork::Petersburg).active_at_block(header.number) {
Some(WEI_2ETH)
} else if self.chain_spec.fork(Hardfork::Byzantium).active_at_block(header.number) {
Some(WEI_3ETH)
} else {
Some(WEI_5ETH)
}
}
/// Irregular state change at Ethereum DAO hardfork
fn apply_dao_fork_changes(&mut self, post_state: &mut PostState) -> Result<(), Error> {
let db = self.db();
@@ -521,9 +494,10 @@ pub fn verify_receipt<'a>(
#[cfg(test)]
mod tests {
use super::*;
use reth_consensus_common::calc;
use reth_primitives::{
hex_literal::hex, keccak256, Account, Address, BlockNumber, Bytecode, Bytes,
ChainSpecBuilder, ForkCondition, StorageKey, H256, MAINNET, U256,
constants::ETH_TO_WEI, hex_literal::hex, keccak256, Account, Address, BlockNumber,
Bytecode, Bytes, ChainSpecBuilder, ForkCondition, StorageKey, H256, MAINNET, U256,
};
use reth_provider::{
post_state::{Change, Storage},
@@ -669,7 +643,8 @@ mod tests {
"Should executed two transitions (1 tx and 1 block reward)"
);
let block_reward = U256::from(WEI_2ETH + (WEI_2ETH >> 5));
let base_block_reward = ETH_TO_WEI * 2;
let block_reward = calc::block_reward(base_block_reward, 1);
let account1_info = Account { balance: U256::ZERO, nonce: 0x00, bytecode_hash: None };
let account2_info = Account {
@@ -688,8 +663,11 @@ mod tests {
nonce: 0x01,
bytecode_hash: None,
};
let ommer_beneficiary_info =
Account { nonce: 0, balance: U256::from((8 * WEI_2ETH) >> 3), bytecode_hash: None };
let ommer_beneficiary_info = Account {
nonce: 0,
balance: calc::ommer_reward(base_block_reward, block.number, block.ommers[0].number),
bytecode_hash: None,
};
// Check if cache is set
// account1

View File

@@ -18,6 +18,12 @@ pub const EIP1559_ELASTICITY_MULTIPLIER: u64 = 2;
/// Multiplier for converting gwei to wei.
pub const GWEI_TO_WEI: u64 = 1_000_000_000;
/// Multiplier for converting finney (milliether) to wei.
pub const FINNEY_TO_WEI: u128 = (GWEI_TO_WEI as u128) * 1_000_000;
/// Multiplier for converting ether to wei.
pub const ETH_TO_WEI: u128 = FINNEY_TO_WEI * 1000;
/// The Ethereum mainnet genesis hash.
pub const MAINNET_GENESIS: H256 =
H256(hex!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"));

View File

@@ -2,13 +2,6 @@
use reth_primitives::{ChainSpec, Hardfork, Head};
/// Two ethereum worth of wei
pub const WEI_2ETH: u128 = 2000000000000000000u128;
/// Three ethereum worth of wei
pub const WEI_3ETH: u128 = 3000000000000000000u128;
/// Five ethereum worth of wei
pub const WEI_5ETH: u128 = 5000000000000000000u128;
/// return revm_spec from spec configuration.
pub fn revm_spec(chain_spec: &ChainSpec, block: Head) -> revm::primitives::SpecId {
if chain_spec.fork(Hardfork::Shanghai).active_at_head(&block) {