reth-ethereum-forks crate (#5621)

Co-authored-by: root <root@Arindam>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Arindam Singh
2023-12-01 12:07:32 +00:00
committed by GitHub
parent 1067bb32d0
commit 2144b97df0
16 changed files with 359 additions and 200 deletions

View File

@ -0,0 +1,41 @@
[package]
name = "reth-ethereum-forks"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
description = "Ethereum fork types used in reth."
[dependencies]
# reth
reth-codecs.workspace = true
# ethereum
alloy-primitives = { workspace = true, features = ["rand", "rlp"] }
alloy-rlp = { workspace = true, features = ["arrayvec"] }
# used for forkid
crc = "3"
# misc
serde.workspace = true
thiserror.workspace = true
# arbitrary utils
arbitrary = { workspace = true, features = ["derive"], optional = true }
proptest = { workspace = true, optional = true }
proptest-derive = { workspace = true, optional = true }
[dev-dependencies]
rand.workspace = true
arbitrary = { workspace = true, features = ["derive"] }
proptest.workspace = true
proptest-derive.workspace = true
[features]
arbitrary = ["dep:arbitrary", "dep:proptest", "dep:proptest-derive"]
optimism = ["reth-codecs/optimism"]

View File

@ -2,9 +2,8 @@
//!
//! Previously version of Apache licenced [`ethereum-forkid`](https://crates.io/crates/ethereum-forkid).
#![deny(missing_docs)]
use crate::{hex, BlockNumber, Head, B256};
use crate::Head;
use alloy_primitives::{hex, BlockNumber, B256};
use alloy_rlp::*;
use crc::*;
use reth_codecs::derive_arbitrary;
@ -72,10 +71,12 @@ where
}
}
// TODO: Move
/// How to filter forks.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum ForkFilterKey {
/// By block number activation.
Block(BlockNumber),
/// By timestamp activation.
Time(u64),
}
@ -379,7 +380,7 @@ impl Cache {
#[cfg(test)]
mod tests {
use super::*;
use crate::{hex_literal::hex, revm_primitives::b256};
use alloy_primitives::b256;
const GENESIS_HASH: B256 =
b256!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3");

View File

@ -1,6 +1,5 @@
use serde::{Deserialize, Serialize};
use crate::{ChainSpec, ForkCondition, ForkFilter, ForkId};
use std::{fmt::Display, str::FromStr};
/// The name of an Ethereum hardfork.
@ -52,25 +51,6 @@ pub enum Hardfork {
Cancun,
}
impl Hardfork {
/// Get the [ForkId] for this hardfork in the given spec, if the fork is activated at any point.
pub fn fork_id(&self, spec: &ChainSpec) -> Option<ForkId> {
match spec.fork(*self) {
ForkCondition::Never => None,
_ => Some(spec.fork_id(&spec.satisfy(spec.fork(*self)))),
}
}
/// Get the [ForkFilter] for this hardfork in the given spec, if the fork is activated at any
/// point.
pub fn fork_filter(&self, spec: &ChainSpec) -> Option<ForkFilter> {
match spec.fork(*self) {
ForkCondition::Never => None,
_ => Some(spec.fork_filter(spec.satisfy(spec.fork(*self)))),
}
}
}
impl FromStr for Hardfork {
type Err = String;
@ -115,8 +95,6 @@ impl Display for Hardfork {
#[cfg(test)]
mod tests {
use super::*;
use crate::{Chain, Genesis};
use std::collections::BTreeMap;
#[test]
fn check_hardfork_from_str() {
@ -181,34 +159,4 @@ mod tests {
fn check_nonexistent_hardfork_from_str() {
assert!(Hardfork::from_str("not a hardfork").is_err());
}
#[test]
fn check_fork_id_chainspec_with_fork_condition_never() {
let spec = ChainSpec {
chain: Chain::mainnet(),
genesis: Genesis::default(),
genesis_hash: None,
hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]),
paris_block_and_final_difficulty: None,
deposit_contract: None,
..Default::default()
};
assert_eq!(Hardfork::Frontier.fork_id(&spec), None);
}
#[test]
fn check_fork_filter_chainspec_with_fork_condition_never() {
let spec = ChainSpec {
chain: Chain::mainnet(),
genesis: Genesis::default(),
genesis_hash: None,
hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]),
paris_block_and_final_difficulty: None,
deposit_contract: None,
..Default::default()
};
assert_eq!(Hardfork::Shanghai.fork_filter(&spec), None);
}
}

View File

@ -0,0 +1,24 @@
use alloy_primitives::{BlockNumber, B256, U256};
use serde::{Deserialize, Serialize};
/// Describes the current head block.
///
/// The head block is the highest fully synced block.
///
/// Note: This is a slimmed down version of Header, primarily for communicating the highest block
/// with the P2P network and the RPC.
#[derive(
Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
pub struct Head {
/// The number of the head block.
pub number: BlockNumber,
/// The hash of the head block.
pub hash: B256,
/// The difficulty of the head block.
pub difficulty: U256,
/// The total difficulty at the head block.
pub total_difficulty: U256,
/// The timestamp of the head block.
pub timestamp: u64,
}

View File

@ -0,0 +1,28 @@
//! Ethereum fork types used in reth.
//!
//! This crate contains Ethereum fork types and helper functions.
//!
//! ## Feature Flags
//!
//! - `arbitrary`: Adds `proptest` and `arbitrary` support for primitive types.
#![doc(
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![warn(missing_debug_implementations, missing_docs, unreachable_pub, rustdoc::all)]
#![deny(unused_must_use, rust_2018_idioms, unused_crate_dependencies)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![allow(clippy::non_canonical_clone_impl)]
mod forkid;
mod hardfork;
mod head;
pub use forkid::{ForkFilter, ForkFilterKey, ForkHash, ForkId, ForkTransition, ValidationError};
pub use hardfork::Hardfork;
pub use head::Head;
#[cfg(any(test, feature = "arbitrary"))]
pub use arbitrary;

View File

@ -462,7 +462,7 @@ mod tests {
let mut builder = EnrBuilder::new("v4");
let mut buf = Vec::new();
let fork_id = Hardfork::Frontier.fork_id(&MAINNET).unwrap();
let fork_id = MAINNET.hardfork_fork_id(Hardfork::Frontier).unwrap();
fork_id.encode(&mut buf);
builder.ip4(Ipv4Addr::LOCALHOST).udp4(30303).tcp4(30303).add_value(b"eth", &buf);
let enr = builder.build(&secret_key).unwrap();

View File

@ -17,7 +17,7 @@ use reth_primitives::{Chain, ForkId, B256, U256};
/// .total_difficulty(U256::from(100))
/// .blockhash(B256::from(MAINNET_GENESIS_HASH))
/// .genesis(B256::from(MAINNET_GENESIS_HASH))
/// .forkid(Hardfork::Paris.fork_id(&MAINNET).unwrap())
/// .forkid(MAINNET.hardfork_fork_id(Hardfork::Paris).unwrap())
/// .build();
///
/// assert_eq!(
@ -28,7 +28,7 @@ use reth_primitives::{Chain, ForkId, B256, U256};
/// total_difficulty: U256::from(100),
/// blockhash: B256::from(MAINNET_GENESIS_HASH),
/// genesis: B256::from(MAINNET_GENESIS_HASH),
/// forkid: Hardfork::Paris.fork_id(&MAINNET).unwrap(),
/// forkid: MAINNET.hardfork_fork_id(Hardfork::Paris).unwrap(),
/// }
/// );
/// ```

View File

@ -137,8 +137,8 @@ impl Default for Status {
total_difficulty: U256::from(17_179_869_184u64),
blockhash: mainnet_genesis,
genesis: mainnet_genesis,
forkid: Hardfork::Frontier
.fork_id(&MAINNET)
forkid: MAINNET
.hardfork_fork_id(Hardfork::Frontier)
.expect("The Frontier hardfork should always exist"),
}
}

View File

@ -926,8 +926,8 @@ mod tests {
secret_key,
local_peer_id,
status: StatusBuilder::default().build(),
fork_filter: Hardfork::Frontier
.fork_filter(&MAINNET)
fork_filter: MAINNET
.hardfork_fork_filter(Hardfork::Frontier)
.expect("The Frontier fork filter should exist on mainnet"),
bandwidth_meter: BandwidthMeter::default(),
}

View File

@ -2,18 +2,19 @@
name = "reth-primitives"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
description = "Commonly used types in reth."
[dependencies]
# reth
reth-codecs.workspace = true
reth-ethereum-forks.workspace = true
reth-rpc-types.workspace = true
revm-primitives.workspace = true
revm.workspace = true
revm-primitives.workspace = true
# ethereum
alloy-primitives = { workspace = true, features = ["rand", "rlp"] }
@ -33,28 +34,28 @@ crc = "3"
tracing.workspace = true
# misc
bytes.workspace = true
byteorder = "1"
bytes.workspace = true
clap = { workspace = true, features = ["derive"], optional = true }
derive_more = "0.99"
itertools = "0.11"
modular-bitfield = "0.11.2"
num_enum = "0.7"
once_cell.workspace = true
rayon.workspace = true
serde.workspace = true
serde_json.workspace = true
serde_with = "3.3.0"
thiserror.workspace = true
sucds = "~0.6"
modular-bitfield = "0.11.2"
derive_more = "0.99"
url = "2.3"
once_cell.workspace = true
zstd = { version = "0.12", features = ["experimental"] }
rayon.workspace = true
tempfile.workspace = true
sha2 = "0.10.7"
itertools = "0.11"
num_enum = "0.7"
sucds = "~0.6"
tempfile.workspace = true
thiserror.workspace = true
url = "2.3"
zstd = { version = "0.12", features = ["experimental"] }
# `test-utils` feature
plain_hasher = { version = "0.2", optional = true }
hash-db = { version = "~0.15", optional = true }
plain_hasher = { version = "0.2", optional = true }
# arbitrary utils
arbitrary = { workspace = true, features = ["derive"], optional = true }
@ -63,32 +64,39 @@ proptest-derive = { workspace = true, optional = true }
strum = { workspace = true, features = ["derive"] }
[dev-dependencies]
serde_json.workspace = true
test-fuzz = "4"
rand.workspace = true
revm-primitives = { workspace = true, features = ["arbitrary"] }
arbitrary = { workspace = true, features = ["derive"] }
assert_matches.workspace = true
proptest.workspace = true
proptest-derive.workspace = true
assert_matches.workspace = true
rand.workspace = true
revm-primitives = { workspace = true, features = ["arbitrary"] }
serde_json.workspace = true
test-fuzz = "4"
toml.workspace = true
triehash = "0.8"
plain_hasher = "0.2"
hash-db = "~0.15"
plain_hasher = "0.2"
# necessary so we don't hit a "undeclared 'std'":
# https://github.com/paradigmxyz/reth/pull/177#discussion_r1021172198
secp256k1.workspace = true
criterion.workspace = true
pprof = { workspace = true, features = ["flamegraph", "frame-pointer", "criterion"] }
secp256k1.workspace = true
[features]
default = ["c-kzg"]
arbitrary = ["revm-primitives/arbitrary", "reth-rpc-types/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"]
arbitrary = [
"revm-primitives/arbitrary",
"reth-rpc-types/arbitrary",
"reth-ethereum-forks/arbitrary",
"dep:arbitrary",
"dep:proptest",
"dep:proptest-derive",
]
c-kzg = ["dep:c-kzg", "revm/c-kzg", "revm-primitives/c-kzg"]
clap = ["dep:clap"]
optimism = ["reth-codecs/optimism", "revm-primitives/optimism", "revm/optimism"]
optimism = ["reth-codecs/optimism", "revm-primitives/optimism", "reth-ethereum-forks/optimism", "revm/optimism"]
test-utils = ["dep:plain_hasher", "dep:hash-db", "dep:ethers-core"]
[[bench]]

View File

@ -3,12 +3,10 @@ use crate::{
EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR, EIP1559_DEFAULT_ELASTICITY_MULTIPLIER,
EIP1559_INITIAL_BASE_FEE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, EMPTY_WITHDRAWALS,
},
forkid::ForkFilterKey,
header::Head,
proofs::genesis_state_root,
revm_primitives::{address, b256},
Address, BlockNumber, Chain, ForkFilter, ForkHash, ForkId, Genesis, Hardfork, Header,
SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256,
Address, BlockNumber, Chain, ForkFilter, ForkFilterKey, ForkHash, ForkId, Genesis, Hardfork,
Head, Header, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256,
};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
@ -561,6 +559,7 @@ impl ChainSpec {
}
/// Returns `true` if this chain contains Optimism configuration.
#[inline]
pub fn is_optimism(&self) -> bool {
self.chain.is_optimism()
}
@ -671,6 +670,7 @@ impl ChainSpec {
///
/// Note: technically this would also be valid for the block before the paris upgrade, but this
/// edge case is omitted here.
#[inline]
pub fn final_paris_total_difficulty(&self, block_number: u64) -> Option<U256> {
self.paris_block_and_final_difficulty.and_then(|(activated_at, final_difficulty)| {
if block_number >= activated_at {
@ -681,19 +681,38 @@ impl ChainSpec {
})
}
/// Get the fork filter for the given hardfork
pub fn hardfork_fork_filter(&self, fork: Hardfork) -> Option<ForkFilter> {
match self.fork(fork) {
ForkCondition::Never => None,
_ => Some(self.fork_filter(self.satisfy(self.fork(fork)))),
}
}
/// Returns the forks in this specification and their activation conditions.
pub fn hardforks(&self) -> &BTreeMap<Hardfork, ForkCondition> {
&self.hardforks
}
/// Get the fork id for the given hardfork.
#[inline]
pub fn hardfork_fork_id(&self, fork: Hardfork) -> Option<ForkId> {
fork.fork_id(self)
match self.fork(fork) {
ForkCondition::Never => None,
_ => Some(self.fork_id(&self.satisfy(self.fork(fork)))),
}
}
/// Convenience method to get the fork id for [Hardfork::Shanghai] from a given chainspec.
#[inline]
pub fn shanghai_fork_id(&self) -> Option<ForkId> {
Hardfork::Shanghai.fork_id(self)
self.hardfork_fork_id(Hardfork::Shanghai)
}
/// Convenience method to get the fork id for [Hardfork::Cancun] from a given chainspec.
#[inline]
pub fn cancun_fork_id(&self) -> Option<ForkId> {
self.hardfork_fork_id(Hardfork::Cancun)
}
/// Get the fork condition for the given fork.
@ -2749,4 +2768,34 @@ Post-merge hard forks (timestamp based):
serde_json::from_str(&serialized_chain_spec).unwrap();
assert!(matches!(deserialized_chain_spec, AllGenesisFormats::Reth(_)))
}
#[test]
fn check_fork_id_chainspec_with_fork_condition_never() {
let spec = ChainSpec {
chain: Chain::mainnet(),
genesis: Genesis::default(),
genesis_hash: None,
hardforks: BTreeMap::from([(Hardfork::Frontier, ForkCondition::Never)]),
paris_block_and_final_difficulty: None,
deposit_contract: None,
..Default::default()
};
assert_eq!(spec.hardfork_fork_id(Hardfork::Frontier), None);
}
#[test]
fn check_fork_filter_chainspec_with_fork_condition_never() {
let spec = ChainSpec {
chain: Chain::mainnet(),
genesis: Genesis::default(),
genesis_hash: None,
hardforks: BTreeMap::from([(Hardfork::Shanghai, ForkCondition::Never)]),
paris_block_and_final_difficulty: None,
deposit_contract: None,
..Default::default()
};
assert_eq!(spec.hardfork_fork_filter(Hardfork::Shanghai), None);
}
}

View File

@ -14,28 +14,6 @@ use std::{
ops::{Deref, DerefMut},
};
/// Describes the current head block.
///
/// The head block is the highest fully synced block.
///
/// Note: This is a slimmed down version of [Header], primarily for communicating the highest block
/// with the P2P network and the RPC.
#[derive(
Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
pub struct Head {
/// The number of the head block.
pub number: BlockNumber,
/// The hash of the head block.
pub hash: B256,
/// The difficulty of the head block.
pub difficulty: U256,
/// The total difficulty at the head block.
pub total_difficulty: U256,
/// The timestamp of the head block.
pub timestamp: u64,
}
/// Block header
#[main_codec]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

View File

@ -25,10 +25,8 @@ mod compression;
pub mod constants;
pub mod eip4844;
mod error;
mod forkid;
pub mod fs;
mod genesis;
mod hardfork;
mod header;
mod integer_list;
mod log;
@ -67,10 +65,8 @@ pub use constants::{
KECCAK_EMPTY, MAINNET_GENESIS_HASH, SEPOLIA_GENESIS_HASH,
};
pub use error::{GotExpected, GotExpectedBoxed};
pub use forkid::{ForkFilter, ForkHash, ForkId, ForkTransition, ValidationError};
pub use genesis::{ChainConfig, Genesis, GenesisAccount};
pub use hardfork::Hardfork;
pub use header::{Head, Header, HeadersDirection, SealedHeader};
pub use header::{Header, HeadersDirection, SealedHeader};
pub use integer_list::IntegerList;
pub use log::{logs_bloom, Log};
pub use net::{
@ -113,6 +109,7 @@ pub use alloy_primitives::{
Address, BlockHash, BlockNumber, Bloom, BloomInput, Bytes, ChainId, Selector, StorageKey,
StorageValue, TxHash, TxIndex, TxNumber, B128, B256, B512, B64, U128, U256, U64, U8,
};
pub use reth_ethereum_forks::*;
pub use revm_primitives::{self, JumpMap};
#[doc(hidden)]