diff --git a/Cargo.lock b/Cargo.lock index 9b14a7496eb9..2dd41ff110d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2188,6 +2188,7 @@ version = "0.1.0-alpha.11" dependencies = [ "alloy-rlp", "reth-db", + "reth-ethereum-forks", "reth-interfaces", "reth-primitives", "reth-provider", @@ -2615,6 +2616,7 @@ dependencies = [ "reth-beacon-consensus", "reth-blockchain-tree", "reth-db", + "reth-ethereum-forks", "reth-network", "reth-network-api", "reth-primitives", @@ -4145,6 +4147,7 @@ dependencies = [ "reth-discv4", "reth-ecies", "reth-eth-wire", + "reth-ethereum-forks", "reth-network", "reth-primitives", "secp256k1 0.27.0", @@ -5648,6 +5651,7 @@ dependencies = [ "reth-db", "reth-discv4", "reth-downloaders", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-net-nat", @@ -5695,6 +5699,7 @@ dependencies = [ "jsonrpsee", "reth", "reth-beacon-consensus", + "reth-ethereum-forks", "reth-interfaces", "reth-primitives", "reth-provider", @@ -5716,6 +5721,7 @@ dependencies = [ "futures-core", "futures-util", "metrics", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-payload-builder", @@ -5741,6 +5747,7 @@ dependencies = [ "reth-consensus-common", "reth-db", "reth-downloaders", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-payload-builder", @@ -5773,6 +5780,7 @@ dependencies = [ "metrics", "parking_lot 0.12.1", "reth-db", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-primitives", @@ -5804,6 +5812,7 @@ dependencies = [ "confy", "reth-discv4", "reth-downloaders", + "reth-ethereum-forks", "reth-net-nat", "reth-network", "reth-primitives", @@ -5821,6 +5830,7 @@ dependencies = [ "assert_matches", "cfg-if", "mockall", + "reth-ethereum-forks", "reth-interfaces", "reth-primitives", "reth-provider", @@ -5854,6 +5864,7 @@ dependencies = [ "rand 0.8.5", "rayon", "reth-codecs", + "reth-ethereum-forks", "reth-interfaces", "reth-libmdbx", "reth-metrics", @@ -5881,6 +5892,7 @@ dependencies = [ "generic-array", "parking_lot 0.12.1", "rand 0.8.5", + "reth-ethereum-forks", "reth-net-common", "reth-net-nat", "reth-primitives", @@ -5904,6 +5916,7 @@ dependencies = [ "enr", "linked_hash_set", "parking_lot 0.12.1", + "reth-ethereum-forks", "reth-net-common", "reth-primitives", "reth-tracing", @@ -5931,6 +5944,7 @@ dependencies = [ "pin-project", "rayon", "reth-db", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-primitives", @@ -5993,6 +6007,7 @@ dependencies = [ "reth-codecs", "reth-discv4", "reth-ecies", + "reth-ethereum-forks", "reth-metrics", "reth-primitives", "reth-tracing", @@ -6007,6 +6022,55 @@ dependencies = [ "tracing", ] +[[package]] +name = "reth-ethereum-forks" +version = "0.1.0-alpha.11" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "arbitrary", + "assert_matches", + "byteorder", + "bytes", + "c-kzg", + "clap", + "crc", + "criterion", + "derive_more", + "ethers-core", + "hash-db", + "itertools 0.11.0", + "modular-bitfield", + "num_enum 0.7.1", + "once_cell", + "plain_hasher", + "pprof", + "proptest", + "proptest-derive", + "rand 0.8.5", + "rayon", + "reth-codecs", + "reth-primitives", + "reth-rpc-types", + "revm", + "revm-primitives", + "secp256k1 0.27.0", + "serde", + "serde_json", + "serde_with", + "sha2", + "strum", + "sucds 0.6.0", + "tempfile", + "test-fuzz", + "thiserror", + "toml 0.8.8", + "tracing", + "triehash", + "url", + "zstd 0.12.4", +] + [[package]] name = "reth-interfaces" version = "0.1.0-alpha.11" @@ -6022,6 +6086,7 @@ dependencies = [ "reth-codecs", "reth-db", "reth-eth-wire", + "reth-ethereum-forks", "reth-network-api", "reth-nippy-jar", "reth-primitives", @@ -6155,6 +6220,7 @@ dependencies = [ "reth-dns-discovery", "reth-ecies", "reth-eth-wire", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-net-common", @@ -6186,6 +6252,7 @@ dependencies = [ "async-trait", "reth-discv4", "reth-eth-wire", + "reth-ethereum-forks", "reth-primitives", "reth-rpc-types", "serde", @@ -6220,6 +6287,7 @@ dependencies = [ "alloy-rlp", "futures-util", "metrics", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-primitives", @@ -6298,6 +6366,7 @@ dependencies = [ "rand 0.8.5", "rayon", "reth-db", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-nippy-jar", @@ -6319,6 +6388,7 @@ dependencies = [ "metrics", "rayon", "reth-db", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-primitives", @@ -6337,6 +6407,7 @@ name = "reth-revm" version = "0.1.0-alpha.11" dependencies = [ "reth-consensus-common", + "reth-ethereum-forks", "reth-interfaces", "reth-primitives", "reth-provider", @@ -6387,6 +6458,7 @@ dependencies = [ "rayon", "reqwest", "reth-consensus-common", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-network-api", @@ -6420,6 +6492,7 @@ name = "reth-rpc-api" version = "0.1.0-alpha.11" dependencies = [ "jsonrpsee", + "reth-ethereum-forks", "reth-primitives", "reth-rpc-types", "serde_json", @@ -6432,6 +6505,7 @@ dependencies = [ "async-trait", "futures", "jsonrpsee", + "reth-ethereum-forks", "reth-primitives", "reth-rpc-api", "reth-rpc-types", @@ -6448,6 +6522,7 @@ dependencies = [ "jsonrpsee", "metrics", "reth-beacon-consensus", + "reth-ethereum-forks", "reth-interfaces", "reth-ipc", "reth-metrics", @@ -6484,6 +6559,7 @@ dependencies = [ "jsonrpsee-types", "metrics", "reth-beacon-consensus", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-payload-builder", @@ -6528,6 +6604,7 @@ name = "reth-rpc-types-compat" version = "0.1.0-alpha.11" dependencies = [ "alloy-rlp", + "reth-ethereum-forks", "reth-primitives", "reth-rpc-types", ] @@ -6539,6 +6616,7 @@ dependencies = [ "assert_matches", "clap", "reth-db", + "reth-ethereum-forks", "reth-interfaces", "reth-nippy-jar", "reth-primitives", @@ -6574,6 +6652,7 @@ dependencies = [ "reth-db", "reth-downloaders", "reth-eth-wire", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-primitives", @@ -6641,6 +6720,7 @@ dependencies = [ "paste", "proptest", "rand 0.8.5", + "reth-ethereum-forks", "reth-interfaces", "reth-metrics", "reth-primitives", @@ -6669,6 +6749,7 @@ dependencies = [ "pretty_assertions", "proptest", "reth-db", + "reth-ethereum-forks", "reth-interfaces", "reth-primitives", "reth-provider", diff --git a/Cargo.toml b/Cargo.toml index d5a275a3fe69..7f8133f9e1d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "crates/payload/basic/", "crates/payload/builder/", "crates/primitives/", + "crates/ethereum-forks/", "crates/prune/", "crates/revm/", "crates/revm/revm-inspectors/", @@ -102,6 +103,7 @@ reth-dns-discovery = { path = "crates/net/dns" } reth-downloaders = { path = "crates/net/downloaders" } reth-ecies = { path = "crates/net/ecies" } reth-eth-wire = { path = "crates/net/eth-wire" } +reth-ethereum-forks = { path = "crates/ethereum-forks" } reth-interfaces = { path = "crates/interfaces" } reth-ipc = { path = "crates/rpc/ipc" } reth-libmdbx = { path = "crates/storage/libmdbx-rs" } diff --git a/bin/reth/Cargo.toml b/bin/reth/Cargo.toml index 273466aa145c..801c79508dbc 100644 --- a/bin/reth/Cargo.toml +++ b/bin/reth/Cargo.toml @@ -18,6 +18,7 @@ normal = [ # reth reth-config.workspace = true reth-primitives = { workspace = true, features = ["arbitrary", "clap"] } +reth-ethereum-forks = { workspace = true, features = ["arbitrary", "clap"] } reth-db = { workspace = true, features = ["mdbx", "test-utils"] } # TODO: Temporary use of the test-utils feature reth-provider = { workspace = true, features = ["test-utils"] } @@ -130,6 +131,7 @@ optimism = [ "reth-basic-payload-builder/optimism", "reth-network/optimism", "reth-network-api/optimism", + "reth-ethereum-forks/optimism" ] # no-op feature flag for switching between the `optimism` and default functionality in CI matrices ethereum = [] diff --git a/bin/reth/src/args/network_args.rs b/bin/reth/src/args/network_args.rs index 44fd62282599..7ea39ff311e4 100644 --- a/bin/reth/src/args/network_args.rs +++ b/bin/reth/src/args/network_args.rs @@ -4,9 +4,10 @@ use crate::version::P2P_CLIENT_VERSION; use clap::Args; use reth_config::Config; use reth_discv4::{DEFAULT_DISCOVERY_ADDR, DEFAULT_DISCOVERY_PORT}; +use reth_ethereum_forks::ChainSpec; use reth_net_nat::NatResolver; use reth_network::{HelloMessageWithProtocols, NetworkConfigBuilder}; -use reth_primitives::{mainnet_nodes, ChainSpec, NodeRecord}; +use reth_primitives::{mainnet_nodes, NodeRecord}; use secp256k1::SecretKey; use std::{net::Ipv4Addr, path::PathBuf, sync::Arc}; diff --git a/bin/reth/src/args/pruning_args.rs b/bin/reth/src/args/pruning_args.rs index 251e1a918669..dae9994e2f96 100644 --- a/bin/reth/src/args/pruning_args.rs +++ b/bin/reth/src/args/pruning_args.rs @@ -2,9 +2,8 @@ use clap::Args; use reth_config::config::PruneConfig; -use reth_primitives::{ - ChainSpec, PruneMode, PruneModes, ReceiptsLogPruneConfig, MINIMUM_PRUNING_DISTANCE, -}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::{PruneMode, PruneModes, ReceiptsLogPruneConfig, MINIMUM_PRUNING_DISTANCE}; use std::sync::Arc; /// Parameters for pruning and full node diff --git a/bin/reth/src/args/utils.rs b/bin/reth/src/args/utils.rs index 29ccdce3ec8f..b842841f1d6d 100644 --- a/bin/reth/src/args/utils.rs +++ b/bin/reth/src/args/utils.rs @@ -1,6 +1,7 @@ //! Clap parser utilities -use reth_primitives::{fs, AllGenesisFormats, BlockHashOrNumber, ChainSpec, B256}; +use reth_ethereum_forks::{AllGenesisFormats, ChainSpec}; +use reth_primitives::{fs, BlockHashOrNumber, B256}; use std::{ net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs}, path::PathBuf, @@ -10,10 +11,10 @@ use std::{ }; #[cfg(feature = "optimism")] -use reth_primitives::{BASE_GOERLI, BASE_MAINNET}; +use reth_ethereum_forks::{BASE_GOERLI, BASE_MAINNET}; #[cfg(not(feature = "optimism"))] -use reth_primitives::{DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA}; +use reth_ethereum_forks::{DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA}; #[cfg(feature = "optimism")] /// Chains supported by op-reth. First value should be used as the default. @@ -158,9 +159,8 @@ pub fn parse_socket_address(value: &str) -> eyre::Result::parse_from(["reth", "--dev"]); - let chain = reth_primitives::DEV.clone(); + let chain = DEV.clone(); assert_eq!(cmd.chain.chain, chain.chain); assert_eq!(cmd.chain.genesis_hash, chain.genesis_hash); assert_eq!( diff --git a/bin/reth/src/p2p/mod.rs b/bin/reth/src/p2p/mod.rs index 6c59b5853170..fbe864893472 100644 --- a/bin/reth/src/p2p/mod.rs +++ b/bin/reth/src/p2p/mod.rs @@ -13,8 +13,9 @@ use clap::{Parser, Subcommand}; use reth_config::Config; use reth_db::open_db; use reth_discv4::NatResolver; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::p2p::bodies::client::BodiesClient; -use reth_primitives::{BlockHashOrNumber, ChainSpec, NodeRecord}; +use reth_primitives::{BlockHashOrNumber, NodeRecord}; use reth_provider::ProviderFactory; use std::{path::PathBuf, sync::Arc}; diff --git a/bin/reth/src/recover/storage_tries.rs b/bin/reth/src/recover/storage_tries.rs index f1ee84474d31..006d4ea46f74 100644 --- a/bin/reth/src/recover/storage_tries.rs +++ b/bin/reth/src/recover/storage_tries.rs @@ -10,7 +10,7 @@ use reth_db::{ init_db, tables, transaction::DbTx, }; -use reth_primitives::ChainSpec; +use reth_ethereum_forks::ChainSpec; use reth_provider::{BlockNumReader, HeaderProvider, ProviderError, ProviderFactory}; use reth_trie::StateRoot; use std::{fs, sync::Arc}; diff --git a/bin/reth/src/stage/drop.rs b/bin/reth/src/stage/drop.rs index dc8dac8a0829..7626ca5a8510 100644 --- a/bin/reth/src/stage/drop.rs +++ b/bin/reth/src/stage/drop.rs @@ -10,10 +10,10 @@ use crate::{ }; use clap::Parser; use reth_db::{database::Database, open_db, tables, transaction::DbTxMut, DatabaseEnv}; -use reth_primitives::{fs, stage::StageId, ChainSpec}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::{fs, stage::StageId}; use std::sync::Arc; use tracing::info; - /// `reth drop-stage` command #[derive(Debug, Parser)] pub struct Command { diff --git a/bin/reth/src/stage/dump/execution.rs b/bin/reth/src/stage/dump/execution.rs index d0ce96ce5dfc..d06b227e9ebc 100644 --- a/bin/reth/src/stage/dump/execution.rs +++ b/bin/reth/src/stage/dump/execution.rs @@ -5,7 +5,8 @@ use reth_db::{ cursor::DbCursorRO, database::Database, table::TableImporter, tables, transaction::DbTx, DatabaseEnv, }; -use reth_primitives::{stage::StageCheckpoint, ChainSpec}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::stage::StageCheckpoint; use reth_provider::ProviderFactory; use reth_revm::EvmProcessorFactory; use reth_stages::{stages::ExecutionStage, Stage, UnwindInput}; diff --git a/bin/reth/src/stage/dump/hashing_account.rs b/bin/reth/src/stage/dump/hashing_account.rs index 7fe723257f69..987693c11075 100644 --- a/bin/reth/src/stage/dump/hashing_account.rs +++ b/bin/reth/src/stage/dump/hashing_account.rs @@ -2,7 +2,8 @@ use super::setup; use crate::utils::DbTool; use eyre::Result; use reth_db::{database::Database, table::TableImporter, tables, DatabaseEnv}; -use reth_primitives::{stage::StageCheckpoint, BlockNumber, ChainSpec}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::{stage::StageCheckpoint, BlockNumber}; use reth_provider::ProviderFactory; use reth_stages::{stages::AccountHashingStage, Stage, UnwindInput}; use std::{path::PathBuf, sync::Arc}; diff --git a/bin/reth/src/stage/dump/hashing_storage.rs b/bin/reth/src/stage/dump/hashing_storage.rs index 373818072529..adb0ca60b111 100644 --- a/bin/reth/src/stage/dump/hashing_storage.rs +++ b/bin/reth/src/stage/dump/hashing_storage.rs @@ -2,7 +2,8 @@ use super::setup; use crate::utils::DbTool; use eyre::Result; use reth_db::{database::Database, table::TableImporter, tables, DatabaseEnv}; -use reth_primitives::{stage::StageCheckpoint, ChainSpec}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::stage::StageCheckpoint; use reth_provider::ProviderFactory; use reth_stages::{stages::StorageHashingStage, Stage, UnwindInput}; use std::{path::PathBuf, sync::Arc}; diff --git a/bin/reth/src/stage/dump/merkle.rs b/bin/reth/src/stage/dump/merkle.rs index c57a85c597b9..3a2e72209f0d 100644 --- a/bin/reth/src/stage/dump/merkle.rs +++ b/bin/reth/src/stage/dump/merkle.rs @@ -2,7 +2,8 @@ use super::setup; use crate::utils::DbTool; use eyre::Result; use reth_db::{database::Database, table::TableImporter, tables, DatabaseEnv}; -use reth_primitives::{stage::StageCheckpoint, BlockNumber, ChainSpec, PruneModes}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::{stage::StageCheckpoint, BlockNumber, PruneModes}; use reth_provider::ProviderFactory; use reth_stages::{ stages::{ diff --git a/bin/reth/src/stage/dump/mod.rs b/bin/reth/src/stage/dump/mod.rs index d424b523fec8..b6209f7201bc 100644 --- a/bin/reth/src/stage/dump/mod.rs +++ b/bin/reth/src/stage/dump/mod.rs @@ -8,7 +8,7 @@ use reth_db::{ cursor::DbCursorRO, database::Database, init_db, table::TableImporter, tables, transaction::DbTx, DatabaseEnv, }; -use reth_primitives::ChainSpec; +use reth_ethereum_forks::ChainSpec; use std::{path::PathBuf, sync::Arc}; use tracing::info; diff --git a/bin/reth/src/stage/run.rs b/bin/reth/src/stage/run.rs index d1b0cbf670cd..dd8ef6babbc3 100644 --- a/bin/reth/src/stage/run.rs +++ b/bin/reth/src/stage/run.rs @@ -16,7 +16,7 @@ use reth_beacon_consensus::BeaconConsensus; use reth_config::Config; use reth_db::init_db; use reth_downloaders::bodies::bodies::BodiesDownloaderBuilder; -use reth_primitives::ChainSpec; +use reth_ethereum_forks::ChainSpec; use reth_provider::{ProviderFactory, StageCheckpointReader}; use reth_stages::{ stages::{ diff --git a/bin/reth/src/stage/unwind.rs b/bin/reth/src/stage/unwind.rs index 60c5e35de923..862f66bdcc56 100644 --- a/bin/reth/src/stage/unwind.rs +++ b/bin/reth/src/stage/unwind.rs @@ -9,7 +9,8 @@ use crate::{ }; use clap::{Parser, Subcommand}; use reth_db::{cursor::DbCursorRO, database::Database, open_db, tables, transaction::DbTx}; -use reth_primitives::{BlockHashOrNumber, ChainSpec}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::BlockHashOrNumber; use reth_provider::{BlockExecutionWriter, ProviderFactory}; use std::{ops::RangeInclusive, sync::Arc}; diff --git a/bin/reth/src/utils.rs b/bin/reth/src/utils.rs index 6994fd81e8c1..27ae202e8143 100644 --- a/bin/reth/src/utils.rs +++ b/bin/reth/src/utils.rs @@ -10,14 +10,13 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, DatabaseError, RawTable, TableRawRow, }; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::p2p::{ bodies::client::BodiesClient, headers::client::{HeadersClient, HeadersRequest}, priority::Priority, }; -use reth_primitives::{ - fs, BlockHashOrNumber, ChainSpec, HeadersDirection, SealedBlock, SealedHeader, -}; +use reth_primitives::{fs, BlockHashOrNumber, HeadersDirection, SealedBlock, SealedHeader}; use reth_rpc::{JwtError, JwtSecret}; use std::{ env::VarError, diff --git a/codecov.yml b/codecov.yml index 5bd75590b504..f49ede1eafbb 100644 --- a/codecov.yml +++ b/codecov.yml @@ -63,6 +63,7 @@ component_management: name: primitives paths: - crates/primitives/** + - crates/ethereum-forks/** - crates/tasks/** - crates/rlp/** - - crates/interfaces/** \ No newline at end of file + - crates/interfaces/** diff --git a/crates/blockchain-tree/Cargo.toml b/crates/blockchain-tree/Cargo.toml index b3c554cdc913..1c8cde8bffaf 100644 --- a/crates/blockchain-tree/Cargo.toml +++ b/crates/blockchain-tree/Cargo.toml @@ -16,6 +16,7 @@ normal = [ [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-db.workspace = true reth-provider.workspace = true @@ -39,6 +40,7 @@ linked_hash_set = "0.1.4" reth-db = { workspace = true, features = ["test-utils"] } reth-interfaces = { workspace = true, features = ["test-utils"] } reth-primitives = { workspace = true , features = ["test-utils"] } +reth-ethereum-forks = { workspace = true , features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } parking_lot.workspace = true assert_matches.workspace = true diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 8d399dda2599..33ae83390a3e 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -7,6 +7,7 @@ use crate::{ AppendableChain, BlockIndices, BlockchainTreeConfig, BundleStateData, TreeExternals, }; use reth_db::database::Database; +use reth_ethereum_forks::Hardfork; use reth_interfaces::{ blockchain_tree::{ error::{BlockchainTreeError, CanonicalError, InsertBlockError, InsertBlockErrorKind}, @@ -17,7 +18,7 @@ use reth_interfaces::{ RethResult, }; use reth_primitives::{ - BlockHash, BlockNumHash, BlockNumber, ForkBlock, Hardfork, PruneModes, Receipt, SealedBlock, + BlockHash, BlockNumHash, BlockNumber, ForkBlock, PruneModes, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, U256, }; use reth_provider::{ @@ -1205,10 +1206,9 @@ mod tests { use assert_matches::assert_matches; use linked_hash_set::LinkedHashSet; use reth_db::{tables, test_utils::TempDatabase, transaction::DbTxMut, DatabaseEnv}; + use reth_ethereum_forks::{ChainSpecBuilder, MAINNET}; use reth_interfaces::test_utils::TestConsensus; - use reth_primitives::{ - constants::EMPTY_ROOT_HASH, stage::StageCheckpoint, ChainSpecBuilder, B256, MAINNET, - }; + use reth_primitives::{constants::EMPTY_ROOT_HASH, stage::StageCheckpoint, B256}; use reth_provider::{ test_utils::{ blocks::BlockChainTestData, create_test_provider_factory_with_chain_spec, diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index ed8eba877351..749eb39de97b 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -14,6 +14,7 @@ reth-net-nat.workspace = true reth-discv4.workspace = true reth-downloaders.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true # io serde.workspace = true diff --git a/crates/consensus/auto-seal/Cargo.toml b/crates/consensus/auto-seal/Cargo.toml index 704b7a97ae10..a8b26fd48fb2 100644 --- a/crates/consensus/auto-seal/Cargo.toml +++ b/crates/consensus/auto-seal/Cargo.toml @@ -12,6 +12,7 @@ description = "A consensus impl for local testing purposes" # reth reth-beacon-consensus.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-provider.workspace = true reth-stages.workspace = true diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 2065f2e188f9..81603c12bf4c 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -17,15 +17,16 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use reth_beacon_consensus::BeaconEngineMessage; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::{ consensus::{Consensus, ConsensusError}, executor::{BlockExecutionError, BlockValidationError}, }; use reth_primitives::{ constants::{EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, ETHEREUM_BLOCK_GAS_LIMIT}, - proofs, Address, Block, BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, Bloom, ChainSpec, - Header, ReceiptWithBloom, SealedBlock, SealedHeader, TransactionSigned, B256, - EMPTY_OMMER_ROOT_HASH, U256, + proofs, Address, Block, BlockBody, BlockHash, BlockHashOrNumber, BlockNumber, Bloom, Header, + ReceiptWithBloom, SealedBlock, SealedHeader, TransactionSigned, B256, EMPTY_OMMER_ROOT_HASH, + U256, }; use reth_provider::{ BlockExecutor, BlockReaderIdExt, BundleStateWithReceipts, CanonStateNotificationSender, diff --git a/crates/consensus/auto-seal/src/task.rs b/crates/consensus/auto-seal/src/task.rs index 2ace064890e2..439ec5333220 100644 --- a/crates/consensus/auto-seal/src/task.rs +++ b/crates/consensus/auto-seal/src/task.rs @@ -1,8 +1,9 @@ use crate::{mode::MiningMode, Storage}; use futures_util::{future::BoxFuture, FutureExt}; use reth_beacon_consensus::{BeaconEngineMessage, ForkchoiceStatus}; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::consensus::ForkchoiceState; -use reth_primitives::{Block, ChainSpec, IntoRecoveredTransaction, SealedBlockWithSenders}; +use reth_primitives::{Block, IntoRecoveredTransaction, SealedBlockWithSenders}; use reth_provider::{CanonChainTracker, CanonStateNotificationSender, Chain, StateProviderFactory}; use reth_stages::PipelineEvent; use reth_transaction_pool::{TransactionPool, ValidPoolTransaction}; diff --git a/crates/consensus/auto-seal/tests/it/auto_mine.rs b/crates/consensus/auto-seal/tests/it/auto_mine.rs index 8fe3809eb44f..df7f130b9dbe 100644 --- a/crates/consensus/auto-seal/tests/it/auto_mine.rs +++ b/crates/consensus/auto-seal/tests/it/auto_mine.rs @@ -10,7 +10,8 @@ use reth::{ runner::CliRunner, tasks::TaskSpawner, }; -use reth_primitives::{hex, revm_primitives::FixedBytes, ChainSpec, Genesis}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::{hex, revm_primitives::FixedBytes, Genesis}; use reth_provider::CanonStateSubscriptions; use reth_transaction_pool::TransactionPool; use std::{sync::Arc, time::Duration}; diff --git a/crates/consensus/beacon/Cargo.toml b/crates/consensus/beacon/Cargo.toml index 0feec340020a..d2ca5f5cca29 100644 --- a/crates/consensus/beacon/Cargo.toml +++ b/crates/consensus/beacon/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true # reth reth-consensus-common.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-stages.workspace = true reth-db.workspace = true @@ -55,6 +56,7 @@ assert_matches.workspace = true optimism = [ "reth-consensus-common/optimism", "reth-primitives/optimism", + "reth-ethereum-forks/optimism", "reth-interfaces/optimism", "reth-provider/optimism", "reth-rpc-types/optimism", diff --git a/crates/consensus/beacon/src/beacon_consensus.rs b/crates/consensus/beacon/src/beacon_consensus.rs index e54fec714a99..a3f52af2ab90 100644 --- a/crates/consensus/beacon/src/beacon_consensus.rs +++ b/crates/consensus/beacon/src/beacon_consensus.rs @@ -1,10 +1,12 @@ //! Consensus for ethereum network use reth_consensus_common::validation; +use reth_ethereum_forks::{Chain, ChainSpec, Hardfork}; use reth_interfaces::consensus::{Consensus, ConsensusError}; use reth_primitives::{ constants::{ALLOWED_FUTURE_BLOCK_TIME_SECONDS, MAXIMUM_EXTRA_DATA_SIZE}, - Chain, ChainSpec, Hardfork, Header, SealedBlock, SealedHeader, EMPTY_OMMER_ROOT_HASH, U256, + Header, SealedBlock, SealedHeader, EMPTY_OMMER_ROOT_HASH, U256, }; + use std::{sync::Arc, time::SystemTime}; /// Ethereum beacon consensus diff --git a/crates/consensus/beacon/src/engine/mod.rs b/crates/consensus/beacon/src/engine/mod.rs index b7242f3358a5..eea94b561723 100644 --- a/crates/consensus/beacon/src/engine/mod.rs +++ b/crates/consensus/beacon/src/engine/mod.rs @@ -9,6 +9,7 @@ use crate::{ }; use futures::{Future, StreamExt}; use reth_db::database::Database; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::{ blockchain_tree::{ error::{BlockchainTreeError, CanonicalError, InsertBlockError, InsertBlockErrorKind}, @@ -22,8 +23,8 @@ use reth_interfaces::{ }; use reth_payload_builder::{PayloadBuilderAttributes, PayloadBuilderHandle}; use reth_primitives::{ - constants::EPOCH_SLOTS, stage::StageId, BlockNumHash, BlockNumber, ChainSpec, Head, Header, - SealedBlock, SealedHeader, B256, U256, + constants::EPOCH_SLOTS, stage::StageId, BlockNumHash, BlockNumber, Head, Header, SealedBlock, + SealedHeader, B256, U256, }; use reth_provider::{ BlockIdReader, BlockReader, BlockSource, CanonChainTracker, ChainSpecProvider, ProviderError, @@ -1952,8 +1953,9 @@ mod tests { BeaconForkChoiceUpdateError, }; use assert_matches::assert_matches; + use reth_ethereum_forks::{ChainSpec, ChainSpecBuilder, MAINNET}; use reth_interfaces::test_utils::generators::{self, Rng}; - use reth_primitives::{stage::StageCheckpoint, ChainSpec, ChainSpecBuilder, B256, MAINNET}; + use reth_primitives::{stage::StageCheckpoint, B256}; use reth_provider::{BlockWriter, ProviderFactory}; use reth_rpc_types::engine::{ForkchoiceState, ForkchoiceUpdated, PayloadStatus}; use reth_rpc_types_compat::engine::payload::try_block_to_payload_v1; @@ -2407,11 +2409,12 @@ mod tests { mod new_payload { use super::*; + use reth_ethereum_forks::Hardfork; use reth_interfaces::test_utils::{ generators, generators::{generate_keys, random_block}, }; - use reth_primitives::{public_key_to_address, Genesis, GenesisAccount, Hardfork, U256}; + use reth_primitives::{public_key_to_address, Genesis, GenesisAccount, U256}; use reth_provider::test_utils::blocks::BlockChainTestData; #[tokio::test] diff --git a/crates/consensus/beacon/src/engine/sync.rs b/crates/consensus/beacon/src/engine/sync.rs index 10c18e742089..62af2d3aa414 100644 --- a/crates/consensus/beacon/src/engine/sync.rs +++ b/crates/consensus/beacon/src/engine/sync.rs @@ -3,12 +3,13 @@ use crate::{engine::metrics::EngineSyncMetrics, BeaconConsensus}; use futures::FutureExt; use reth_db::database::Database; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::p2p::{ bodies::client::BodiesClient, full_block::{FetchFullBlockFuture, FetchFullBlockRangeFuture, FullBlockClient}, headers::client::HeadersClient, }; -use reth_primitives::{BlockNumber, ChainSpec, SealedBlock, B256}; +use reth_primitives::{BlockNumber, SealedBlock, B256}; use reth_stages::{ControlFlow, Pipeline, PipelineError, PipelineWithResult}; use reth_tasks::TaskSpawner; use std::{ @@ -394,11 +395,15 @@ mod tests { use super::*; use assert_matches::assert_matches; use futures::poll; - use reth_db::{mdbx::DatabaseEnv, test_utils::TempDatabase}; + use reth_db::{ + mdbx::DatabaseEnv, + test_utils::{create_test_rw_db, TempDatabase}, + }; + use reth_ethereum_forks::{ChainSpec, ChainSpecBuilder, MAINNET}; use reth_interfaces::{p2p::either::EitherDownloader, test_utils::TestFullBlockClient}; use reth_primitives::{ - constants::ETHEREUM_BLOCK_GAS_LIMIT, stage::StageCheckpoint, BlockBody, ChainSpec, - ChainSpecBuilder, Header, SealedHeader, MAINNET, + constants::ETHEREUM_BLOCK_GAS_LIMIT, stage::StageCheckpoint, BlockBody, Header, + SealedHeader, }; use reth_provider::{ test_utils::{create_test_provider_factory_with_chain_spec, TestExecutorFactory}, diff --git a/crates/consensus/beacon/src/engine/test_utils.rs b/crates/consensus/beacon/src/engine/test_utils.rs index 781161bb3a02..a2a545a09a14 100644 --- a/crates/consensus/beacon/src/engine/test_utils.rs +++ b/crates/consensus/beacon/src/engine/test_utils.rs @@ -15,6 +15,7 @@ use reth_downloaders::{ bodies::bodies::BodiesDownloaderBuilder, headers::reverse_headers::ReverseHeadersDownloaderBuilder, }; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::{ consensus::Consensus, executor::BlockExecutionError, @@ -23,7 +24,7 @@ use reth_interfaces::{ test_utils::{NoopFullBlockClient, TestConsensus}, }; use reth_payload_builder::test_utils::spawn_test_payload_service; -use reth_primitives::{BlockNumber, ChainSpec, PruneModes, Receipt, B256, U256}; +use reth_primitives::{BlockNumber, PruneModes, Receipt, B256, U256}; use reth_provider::{ providers::BlockchainProvider, test_utils::TestExecutorFactory, BlockExecutor, BundleStateWithReceipts, ExecutorFactory, HeaderSyncMode, ProviderFactory, diff --git a/crates/consensus/common/Cargo.toml b/crates/consensus/common/Cargo.toml index 0a32714919d3..79e11d12c354 100644 --- a/crates/consensus/common/Cargo.toml +++ b/crates/consensus/common/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-provider.workspace = true @@ -23,4 +24,4 @@ assert_matches.workspace = true mockall = "0.11.3" [features] -optimism = ["reth-primitives/optimism"] +optimism = ["reth-primitives/optimism", "reth-ethereum-forks/optimism"] diff --git a/crates/consensus/common/src/calc.rs b/crates/consensus/common/src/calc.rs index 26974d6fc5b6..bd53a2ff7a4d 100644 --- a/crates/consensus/common/src/calc.rs +++ b/crates/consensus/common/src/calc.rs @@ -1,4 +1,5 @@ -use reth_primitives::{constants::ETH_TO_WEI, BlockNumber, Chain, ChainSpec, Hardfork, U256}; +use reth_ethereum_forks::{Chain, ChainSpec, Hardfork}; +use reth_primitives::{constants::ETH_TO_WEI, BlockNumber, U256}; /// Calculates the base block reward. /// @@ -48,7 +49,8 @@ pub fn base_block_reward( /// ``` /// # use reth_consensus_common::calc::{base_block_reward, block_reward}; /// # use reth_primitives::constants::ETH_TO_WEI; -/// # use reth_primitives::{MAINNET, U256}; +/// # use reth_primitives::U256; +/// # use reth_ethereum_forks::MAINNET; /// # /// // This is block 126 on mainnet. /// let block_number = 126; diff --git a/crates/consensus/common/src/validation.rs b/crates/consensus/common/src/validation.rs index 8ba4e535fa1e..419256b4d0e5 100644 --- a/crates/consensus/common/src/validation.rs +++ b/crates/consensus/common/src/validation.rs @@ -1,4 +1,5 @@ //! Collection of methods for block validation. +use reth_ethereum_forks::{ChainSpec, Hardfork}; use reth_interfaces::{consensus::ConsensusError, RethResult}; use reth_primitives::{ constants::{ @@ -6,9 +7,8 @@ use reth_primitives::{ eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK}, }, eip4844::calculate_excess_blob_gas, - BlockNumber, ChainSpec, GotExpected, Hardfork, Header, InvalidTransactionError, SealedBlock, - SealedHeader, Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxEip4844, - TxLegacy, + BlockNumber, GotExpected, Header, InvalidTransactionError, SealedBlock, SealedHeader, + Transaction, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxEip4844, TxLegacy, }; use reth_provider::{AccountReader, HeaderProvider, WithdrawalsProvider}; use std::collections::{hash_map::Entry, HashMap}; @@ -483,14 +483,15 @@ pub fn validate_4844_header_standalone(header: &SealedHeader) -> Result<(), Cons mod tests { use super::*; use mockall::mock; + use reth_ethereum_forks::{ChainSpecBuilder, MAINNET}; use reth_interfaces::{ provider::ProviderResult, test_utils::generators::{self, Rng}, }; use reth_primitives::{ constants::eip4844::DATA_GAS_PER_BLOB, hex_literal::hex, proofs, Account, Address, - BlockBody, BlockHash, BlockHashOrNumber, Bytes, ChainSpecBuilder, Header, Signature, - TransactionKind, TransactionSigned, Withdrawal, MAINNET, U256, + BlockBody, BlockHash, BlockHashOrNumber, Bytes, Signature, TransactionKind, + TransactionSigned, Withdrawal, MAINNET, U256, }; use std::ops::RangeBounds; diff --git a/crates/ethereum-forks/Cargo.toml b/crates/ethereum-forks/Cargo.toml new file mode 100644 index 000000000000..86ee66c4c2f3 --- /dev/null +++ b/crates/ethereum-forks/Cargo.toml @@ -0,0 +1,94 @@ +[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" + +[dependencies] +# reth +reth-codecs.workspace = true +reth-rpc-types.workspace = true +revm-primitives.workspace = true +reth-primitives.workspace = true +revm.workspace = true + +# ethereum +alloy-primitives = { workspace = true, features = ["rand", "rlp"] } +alloy-rlp = { workspace = true, features = ["arrayvec"] } +ethers-core = { workspace = true, default-features = false, optional = true } + +# crypto +secp256k1 = { workspace = true, features = ["global-context", "recovery"] } + +# for eip-4844 +c-kzg = { workspace = true, features = ["serde"], optional = true } + +# used for forkid +crc = "3" + +# tracing +tracing.workspace = true + +# misc +bytes.workspace = true +byteorder = "1" +clap = { workspace = true, features = ["derive"], optional = 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" + +# `test-utils` feature +plain_hasher = { version = "0.2", optional = true } +hash-db = { version = "~0.15", optional = true } + +# arbitrary utils +arbitrary = { workspace = true, features = ["derive"], optional = true } +proptest = { workspace = true, optional = true } +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"] } +reth-primitives = { workspace = true, features = ["arbitrary"] } +arbitrary = { workspace = true, features = ["derive"] } +proptest.workspace = true +proptest-derive.workspace = true +assert_matches.workspace = true +toml.workspace = true +triehash = "0.8" + +plain_hasher = "0.2" +hash-db = "~0.15" + +# 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"] } + +[features] +default = ["c-kzg"] +arbitrary = ["revm-primitives/arbitrary", "reth-primitives/arbitrary", "reth-rpc-types/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] +c-kzg = ["dep:c-kzg", "revm/c-kzg", "revm-primitives/c-kzg", "reth-primitives/c-kzg"] +clap = ["dep:clap"] +optimism = ["reth-codecs/optimism", "revm-primitives/optimism", "reth-primitives/optimism", "revm/optimism"] +test-utils = ["dep:plain_hasher", "dep:hash-db", "dep:ethers-core"] diff --git a/crates/primitives/res/genesis/base.json b/crates/ethereum-forks/res/genesis/base.json similarity index 100% rename from crates/primitives/res/genesis/base.json rename to crates/ethereum-forks/res/genesis/base.json diff --git a/crates/primitives/res/genesis/dev.json b/crates/ethereum-forks/res/genesis/dev.json similarity index 100% rename from crates/primitives/res/genesis/dev.json rename to crates/ethereum-forks/res/genesis/dev.json diff --git a/crates/primitives/res/genesis/goerli.json b/crates/ethereum-forks/res/genesis/goerli.json similarity index 100% rename from crates/primitives/res/genesis/goerli.json rename to crates/ethereum-forks/res/genesis/goerli.json diff --git a/crates/primitives/res/genesis/goerli_base.json b/crates/ethereum-forks/res/genesis/goerli_base.json similarity index 100% rename from crates/primitives/res/genesis/goerli_base.json rename to crates/ethereum-forks/res/genesis/goerli_base.json diff --git a/crates/primitives/res/genesis/goerli_op.json b/crates/ethereum-forks/res/genesis/goerli_op.json similarity index 100% rename from crates/primitives/res/genesis/goerli_op.json rename to crates/ethereum-forks/res/genesis/goerli_op.json diff --git a/crates/primitives/res/genesis/holesky.json b/crates/ethereum-forks/res/genesis/holesky.json similarity index 100% rename from crates/primitives/res/genesis/holesky.json rename to crates/ethereum-forks/res/genesis/holesky.json diff --git a/crates/primitives/res/genesis/mainnet.json b/crates/ethereum-forks/res/genesis/mainnet.json similarity index 100% rename from crates/primitives/res/genesis/mainnet.json rename to crates/ethereum-forks/res/genesis/mainnet.json diff --git a/crates/primitives/res/genesis/sepolia.json b/crates/ethereum-forks/res/genesis/sepolia.json similarity index 100% rename from crates/primitives/res/genesis/sepolia.json rename to crates/ethereum-forks/res/genesis/sepolia.json diff --git a/crates/primitives/src/chain/info.rs b/crates/ethereum-forks/src/chain/info.rs similarity index 100% rename from crates/primitives/src/chain/info.rs rename to crates/ethereum-forks/src/chain/info.rs diff --git a/crates/ethereum-forks/src/chain/mod.rs b/crates/ethereum-forks/src/chain/mod.rs new file mode 100644 index 000000000000..cd3f588b994a --- /dev/null +++ b/crates/ethereum-forks/src/chain/mod.rs @@ -0,0 +1,451 @@ +use alloy_rlp::{Decodable, Encodable}; +use num_enum::TryFromPrimitive; +use reth_codecs::add_arbitrary_tests; +use reth_primitives::{ + goerli_nodes, holesky_nodes, mainnet_nodes, sepolia_nodes, NodeRecord, U256, U64, +}; +use serde::{Deserialize, Serialize}; +use std::{fmt, str::FromStr}; +use strum::{AsRefStr, EnumCount, EnumIter, EnumString, EnumVariantNames}; + +// The chain spec module. +mod spec; +pub use spec::{ + AllGenesisFormats, ChainSpec, ChainSpecBuilder, DisplayHardforks, ForkCondition, + ForkTimestamps, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, +}; + +#[cfg(feature = "optimism")] +pub use spec::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI}; + +// The chain info module. +mod info; +pub use info::ChainInfo; + +/// An Ethereum EIP-155 chain. +#[derive( + Clone, + Copy, + Debug, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + AsRefStr, // AsRef, fmt::Display + EnumVariantNames, // Chain::VARIANTS + EnumString, // FromStr, TryFrom<&str> + EnumIter, // Chain::iter + EnumCount, // Chain::COUNT + TryFromPrimitive, // TryFrom + Deserialize, + Serialize, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +#[repr(u64)] +#[allow(missing_docs)] +pub enum NamedChain { + Mainnet = 1, + Morden = 2, + Ropsten = 3, + Rinkeby = 4, + Goerli = 5, + Kovan = 42, + Holesky = 17000, + Sepolia = 11155111, + + Optimism = 10, + OptimismKovan = 69, + OptimismGoerli = 420, + + Base = 8453, + BaseGoerli = 84531, + + Arbitrum = 42161, + ArbitrumTestnet = 421611, + ArbitrumGoerli = 421613, + ArbitrumNova = 42170, + + #[serde(alias = "bsc")] + #[strum(to_string = "bsc")] + BinanceSmartChain = 56, + #[serde(alias = "bsc_testnet")] + #[strum(to_string = "bsc_testnet")] + BinanceSmartChainTestnet = 97, + + Dev = 1337, +} + +impl From for u64 { + fn from(value: NamedChain) -> Self { + value as u64 + } +} + +impl fmt::Display for NamedChain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_ref().fmt(f) + } +} + +/// Either a named or chain id or the actual id value +#[add_arbitrary_tests(rlp)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Chain { + /// Contains a known chain + Named(NamedChain), + /// Contains the id of a chain + Id(u64), +} + +impl Chain { + /// Returns the mainnet chain. + pub const fn mainnet() -> Self { + Chain::Named(NamedChain::Mainnet) + } + + /// Returns the goerli chain. + pub const fn goerli() -> Self { + Chain::Named(NamedChain::Goerli) + } + + /// Returns the sepolia chain. + pub const fn sepolia() -> Self { + Chain::Named(NamedChain::Sepolia) + } + + /// Returns the holesky chain. + pub const fn holesky() -> Self { + Chain::Named(NamedChain::Holesky) + } + + /// Returns the optimism goerli chain. + pub const fn optimism_goerli() -> Self { + Chain::Named(NamedChain::OptimismGoerli) + } + + /// Returns the optimism mainnet chain. + pub const fn optimism_mainnet() -> Self { + Chain::Named(NamedChain::Optimism) + } + + /// Returns the base goerli chain. + pub const fn base_goerli() -> Self { + Chain::Named(NamedChain::BaseGoerli) + } + + /// Returns the base mainnet chain. + pub const fn base_mainnet() -> Self { + Chain::Named(NamedChain::Base) + } + + /// Returns the dev chain. + pub const fn dev() -> Self { + Chain::Named(NamedChain::Dev) + } + + /// Returns true if the chain contains Optimism configuration. + pub fn is_optimism(self) -> bool { + self.named().map_or(false, |c| { + matches!( + c, + NamedChain::Optimism | + NamedChain::OptimismGoerli | + NamedChain::OptimismKovan | + NamedChain::Base | + NamedChain::BaseGoerli + ) + }) + } + + /// Attempts to convert the chain into a named chain. + pub fn named(&self) -> Option { + match self { + Chain::Named(chain) => Some(*chain), + Chain::Id(id) => NamedChain::try_from(*id).ok(), + } + } + + /// The id of the chain + pub fn id(&self) -> u64 { + match self { + Chain::Named(chain) => *chain as u64, + Chain::Id(id) => *id, + } + } + + /// Returns the address of the public DNS node list for the given chain. + /// + /// See also + pub fn public_dns_network_protocol(self) -> Option { + use NamedChain as C; + const DNS_PREFIX: &str = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@"; + + let named: NamedChain = self.try_into().ok()?; + + if matches!(named, C::Mainnet | C::Goerli | C::Sepolia | C::Ropsten | C::Rinkeby) { + return Some(format!("{DNS_PREFIX}all.{}.ethdisco.net", named.as_ref().to_lowercase())) + } + None + } + + /// Returns bootnodes for the given chain. + pub fn bootnodes(self) -> Option> { + use NamedChain as C; + match self.try_into().ok()? { + C::Mainnet => Some(mainnet_nodes()), + C::Goerli => Some(goerli_nodes()), + C::Sepolia => Some(sepolia_nodes()), + C::Holesky => Some(holesky_nodes()), + _ => None, + } + } +} + +impl fmt::Display for Chain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Chain::Named(chain) => chain.fmt(f), + Chain::Id(id) => { + if let Ok(chain) = NamedChain::try_from(*id) { + chain.fmt(f) + } else { + id.fmt(f) + } + } + } + } +} + +impl From for Chain { + fn from(id: NamedChain) -> Self { + Chain::Named(id) + } +} + +impl From for Chain { + fn from(id: u64) -> Self { + NamedChain::try_from(id).map(Chain::Named).unwrap_or_else(|_| Chain::Id(id)) + } +} + +impl From for Chain { + fn from(id: U256) -> Self { + id.to::().into() + } +} + +impl From for u64 { + fn from(c: Chain) -> Self { + match c { + Chain::Named(c) => c as u64, + Chain::Id(id) => id, + } + } +} + +impl From for U64 { + fn from(c: Chain) -> Self { + U64::from(u64::from(c)) + } +} + +impl From for U256 { + fn from(c: Chain) -> Self { + U256::from(u64::from(c)) + } +} + +impl TryFrom for NamedChain { + type Error = >::Error; + + fn try_from(chain: Chain) -> Result { + match chain { + Chain::Named(chain) => Ok(chain), + Chain::Id(id) => id.try_into(), + } + } +} + +impl FromStr for Chain { + type Err = String; + + fn from_str(s: &str) -> Result { + if let Ok(chain) = NamedChain::from_str(s) { + Ok(Chain::Named(chain)) + } else { + s.parse::() + .map(Chain::Id) + .map_err(|_| format!("Expected known chain or integer, found: {s}")) + } + } +} + +impl Encodable for Chain { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + match self { + Self::Named(chain) => u64::from(*chain).encode(out), + Self::Id(id) => id.encode(out), + } + } + fn length(&self) -> usize { + match self { + Self::Named(chain) => u64::from(*chain).length(), + Self::Id(id) => id.length(), + } + } +} + +impl Decodable for Chain { + fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { + Ok(u64::decode(buf)?.into()) + } +} + +impl Default for Chain { + fn default() -> Self { + NamedChain::Mainnet.into() + } +} + +#[cfg(any(test, feature = "arbitrary"))] +impl<'a> arbitrary::Arbitrary<'a> for Chain { + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + if u.ratio(1, 2)? { + let chain = u.int_in_range(0..=(NamedChain::COUNT - 1))?; + + return Ok(Chain::Named(NamedChain::iter().nth(chain).expect("in range"))) + } + + Ok(Self::Id(u64::arbitrary(u)?)) + } +} + +#[cfg(any(test, feature = "arbitrary"))] +use strum::IntoEnumIterator; + +#[cfg(any(test, feature = "arbitrary"))] +use proptest::{ + arbitrary::ParamsFor, + prelude::{any, Strategy}, + sample::Selector, + strategy::BoxedStrategy, +}; + +#[cfg(any(test, feature = "arbitrary"))] +impl proptest::arbitrary::Arbitrary for Chain { + type Parameters = ParamsFor; + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + let named = + any::().prop_map(move |sel| Chain::Named(sel.select(NamedChain::iter()))); + let id = any::().prop_map(Chain::from); + proptest::strategy::Union::new_weighted(vec![(50, named.boxed()), (50, id.boxed())]).boxed() + } + + type Strategy = BoxedStrategy; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_id() { + let chain = Chain::Id(1234); + assert_eq!(chain.id(), 1234); + } + + #[test] + fn test_named_id() { + let chain = Chain::Named(NamedChain::Goerli); + assert_eq!(chain.id(), 5); + } + + #[test] + fn test_display_named_chain() { + let chain = Chain::Named(NamedChain::Mainnet); + assert_eq!(format!("{chain}"), "mainnet"); + } + + #[test] + fn test_display_id_chain() { + let chain = Chain::Id(1234); + assert_eq!(format!("{chain}"), "1234"); + } + + #[test] + fn test_from_u256() { + let n = U256::from(1234); + let chain = Chain::from(n); + let expected = Chain::Id(1234); + + assert_eq!(chain, expected); + } + + #[test] + fn test_into_u256() { + let chain = Chain::Named(NamedChain::Goerli); + let n: U256 = chain.into(); + let expected = U256::from(5); + + assert_eq!(n, expected); + } + + #[test] + #[allow(non_snake_case)] + fn test_into_U64() { + let chain = Chain::Named(NamedChain::Goerli); + let n: U64 = chain.into(); + let expected = U64::from(5); + + assert_eq!(n, expected); + } + + #[test] + fn test_from_str_named_chain() { + let result = Chain::from_str("mainnet"); + let expected = Chain::Named(NamedChain::Mainnet); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected); + } + + #[test] + fn test_from_str_named_chain_error() { + let result = Chain::from_str("chain"); + + assert!(result.is_err()); + } + + #[test] + fn test_from_str_id_chain() { + let result = Chain::from_str("1234"); + let expected = Chain::Id(1234); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), expected); + } + + #[test] + fn test_default() { + let default = Chain::default(); + let expected = Chain::Named(NamedChain::Mainnet); + + assert_eq!(default, expected); + } + + #[test] + fn test_id_chain_encodable_length() { + let chain = Chain::Id(1234); + + assert_eq!(chain.length(), 3); + } + + #[test] + fn test_dns_network() { + let s = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.mainnet.ethdisco.net"; + let chain: Chain = NamedChain::Mainnet.into(); + assert_eq!(s, chain.public_dns_network_protocol().unwrap().as_str()); + } +} diff --git a/crates/ethereum-forks/src/chain/spec.rs b/crates/ethereum-forks/src/chain/spec.rs new file mode 100644 index 000000000000..9fdf859733da --- /dev/null +++ b/crates/ethereum-forks/src/chain/spec.rs @@ -0,0 +1,2528 @@ +use crate::{forkid::ForkFilterKey, Chain, ForkFilter, ForkHash, ForkId, Hardfork}; +use once_cell::sync::Lazy; +use reth_primitives::{ + constants::{EIP1559_INITIAL_BASE_FEE, EMPTY_RECEIPTS, EMPTY_TRANSACTIONS, EMPTY_WITHDRAWALS}, + proofs::genesis_state_root, + revm_primitives::{address, b256}, + Address, BaseFeeParams, BlockNumber, Genesis, Head, Header, SealedHeader, B256, + EMPTY_OMMER_ROOT_HASH, U256, +}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::BTreeMap, + fmt::{Display, Formatter}, + sync::Arc, +}; + +/// The Ethereum mainnet spec +pub static MAINNET: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::mainnet(), + genesis: serde_json::from_str(include_str!("../../res/genesis/mainnet.json")) + .expect("Can't deserialize Mainnet genesis json"), + genesis_hash: Some(b256!( + "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" + )), + // + paris_block_and_final_difficulty: Some(( + 15537394, + U256::from(58_750_003_716_598_352_816_469u128), + )), + fork_timestamps: ForkTimestamps::default().shanghai(1681338455), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(1150000)), + (Hardfork::Dao, ForkCondition::Block(1920000)), + (Hardfork::Tangerine, ForkCondition::Block(2463000)), + (Hardfork::SpuriousDragon, ForkCondition::Block(2675000)), + (Hardfork::Byzantium, ForkCondition::Block(4370000)), + (Hardfork::Constantinople, ForkCondition::Block(7280000)), + (Hardfork::Petersburg, ForkCondition::Block(7280000)), + (Hardfork::Istanbul, ForkCondition::Block(9069000)), + (Hardfork::MuirGlacier, ForkCondition::Block(9200000)), + (Hardfork::Berlin, ForkCondition::Block(12244000)), + (Hardfork::London, ForkCondition::Block(12965000)), + (Hardfork::ArrowGlacier, ForkCondition::Block(13773000)), + (Hardfork::GrayGlacier, ForkCondition::Block(15050000)), + ( + Hardfork::Paris, + ForkCondition::TTD { + fork_block: None, + total_difficulty: U256::from(58_750_000_000_000_000_000_000_u128), + }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(1681338455)), + ]), + // https://etherscan.io/tx/0xe75fb554e433e03763a1560646ee22dcb74e5274b34c5ad644e7c0f619a7e1d0 + deposit_contract: Some(DepositContract::new( + address!("00000000219ab540356cbb839cbe05303d7705fa"), + 11052984, + b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), + )), + base_fee_params: BaseFeeParams::ethereum(), + prune_delete_limit: 3500, + snapshot_block_interval: 500_000, + } + .into() +}); + +/// The Goerli spec +pub static GOERLI: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::goerli(), + genesis: serde_json::from_str(include_str!("../../res/genesis/goerli.json")) + .expect("Can't deserialize Goerli genesis json"), + genesis_hash: Some(b256!( + "bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" + )), + // + paris_block_and_final_difficulty: Some((7382818, U256::from(10_790_000))), + fork_timestamps: ForkTimestamps::default().shanghai(1678832736), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Dao, ForkCondition::Block(0)), + (Hardfork::Tangerine, ForkCondition::Block(0)), + (Hardfork::SpuriousDragon, ForkCondition::Block(0)), + (Hardfork::Byzantium, ForkCondition::Block(0)), + (Hardfork::Constantinople, ForkCondition::Block(0)), + (Hardfork::Petersburg, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(1561651)), + (Hardfork::Berlin, ForkCondition::Block(4460644)), + (Hardfork::London, ForkCondition::Block(5062605)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: None, total_difficulty: U256::from(10_790_000) }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(1678832736)), + ]), + // https://goerli.etherscan.io/tx/0xa3c07dc59bfdb1bfc2d50920fed2ef2c1c4e0a09fe2325dbc14e07702f965a78 + deposit_contract: Some(DepositContract::new( + address!("ff50ed3d0ec03ac01d4c79aad74928bff48a7b2b"), + 4367322, + b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), + )), + base_fee_params: BaseFeeParams::ethereum(), + prune_delete_limit: 1700, + snapshot_block_interval: 1_000_000, + } + .into() +}); + +/// The Sepolia spec +pub static SEPOLIA: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::sepolia(), + genesis: serde_json::from_str(include_str!("../../res/genesis/sepolia.json")) + .expect("Can't deserialize Sepolia genesis json"), + genesis_hash: Some(b256!( + "25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" + )), + // + paris_block_and_final_difficulty: Some((1450409, U256::from(17_000_018_015_853_232u128))), + fork_timestamps: ForkTimestamps::default().shanghai(1677557088), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Dao, ForkCondition::Block(0)), + (Hardfork::Tangerine, ForkCondition::Block(0)), + (Hardfork::SpuriousDragon, ForkCondition::Block(0)), + (Hardfork::Byzantium, ForkCondition::Block(0)), + (Hardfork::Constantinople, ForkCondition::Block(0)), + (Hardfork::Petersburg, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(0)), + (Hardfork::MuirGlacier, ForkCondition::Block(0)), + (Hardfork::Berlin, ForkCondition::Block(0)), + (Hardfork::London, ForkCondition::Block(0)), + ( + Hardfork::Paris, + ForkCondition::TTD { + fork_block: Some(1735371), + total_difficulty: U256::from(17_000_000_000_000_000u64), + }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(1677557088)), + ]), + // https://sepolia.etherscan.io/tx/0x025ecbf81a2f1220da6285d1701dc89fb5a956b62562ee922e1a9efd73eb4b14 + deposit_contract: Some(DepositContract::new( + address!("7f02c3e3c98b133055b8b348b2ac625669ed295d"), + 1273020, + b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), + )), + + base_fee_params: BaseFeeParams::ethereum(), + prune_delete_limit: 1700, + snapshot_block_interval: 1_000_000, + } + .into() +}); + +/// The Holesky spec +pub static HOLESKY: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::holesky(), + genesis: serde_json::from_str(include_str!("../../res/genesis/holesky.json")) + .expect("Can't deserialize Holesky genesis json"), + genesis_hash: Some(b256!( + "b5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4" + )), + paris_block_and_final_difficulty: Some((0, U256::from(1))), + fork_timestamps: ForkTimestamps::default().shanghai(1696000704), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Dao, ForkCondition::Block(0)), + (Hardfork::Tangerine, ForkCondition::Block(0)), + (Hardfork::SpuriousDragon, ForkCondition::Block(0)), + (Hardfork::Byzantium, ForkCondition::Block(0)), + (Hardfork::Constantinople, ForkCondition::Block(0)), + (Hardfork::Petersburg, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(0)), + (Hardfork::MuirGlacier, ForkCondition::Block(0)), + (Hardfork::Berlin, ForkCondition::Block(0)), + (Hardfork::London, ForkCondition::Block(0)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(1696000704)), + ]), + deposit_contract: Some(DepositContract::new( + address!("4242424242424242424242424242424242424242"), + 0, + b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), + )), + base_fee_params: BaseFeeParams::ethereum(), + prune_delete_limit: 1700, + snapshot_block_interval: 1_000_000, + } + .into() +}); + +/// Dev testnet specification +/// +/// Includes 20 prefunded accounts with 10_000 ETH each derived from mnemonic "test test test test +/// test test test test test test test junk". +pub static DEV: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::dev(), + genesis: serde_json::from_str(include_str!("../../res/genesis/dev.json")) + .expect("Can't deserialize Dev testnet genesis json"), + genesis_hash: Some(b256!( + "2f980576711e3617a5e4d83dd539548ec0f7792007d505a3d2e9674833af2d7c" + )), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + fork_timestamps: ForkTimestamps::default().shanghai(0), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Dao, ForkCondition::Block(0)), + (Hardfork::Tangerine, ForkCondition::Block(0)), + (Hardfork::SpuriousDragon, ForkCondition::Block(0)), + (Hardfork::Byzantium, ForkCondition::Block(0)), + (Hardfork::Constantinople, ForkCondition::Block(0)), + (Hardfork::Petersburg, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(0)), + (Hardfork::MuirGlacier, ForkCondition::Block(0)), + (Hardfork::Berlin, ForkCondition::Block(0)), + (Hardfork::London, ForkCondition::Block(0)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, + ), + (Hardfork::Shanghai, ForkCondition::Timestamp(0)), + ]), + deposit_contract: None, // TODO: do we even have? + ..Default::default() + } + .into() +}); + +/// The Optimism Goerli spec +#[cfg(feature = "optimism")] +pub static OP_GOERLI: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::optimism_goerli(), + genesis: serde_json::from_str(include_str!("../../res/genesis/goerli_op.json")) + .expect("Can't deserialize Optimism Goerli genesis json"), + genesis_hash: Some(b256!( + "c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" + )), + fork_timestamps: ForkTimestamps::default(), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Tangerine, ForkCondition::Block(0)), + (Hardfork::SpuriousDragon, ForkCondition::Block(0)), + (Hardfork::Byzantium, ForkCondition::Block(0)), + (Hardfork::Constantinople, ForkCondition::Block(0)), + (Hardfork::Petersburg, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(0)), + (Hardfork::MuirGlacier, ForkCondition::Block(0)), + (Hardfork::Berlin, ForkCondition::Block(0)), + (Hardfork::London, ForkCondition::Block(0)), + (Hardfork::ArrowGlacier, ForkCondition::Block(0)), + (Hardfork::GrayGlacier, ForkCondition::Block(0)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, + ), + (Hardfork::Bedrock, ForkCondition::Block(4061224)), + (Hardfork::Regolith, ForkCondition::Timestamp(1679079600)), + ]), + base_fee_params: BaseFeeParams::optimism(), + prune_delete_limit: 1700, + snapshot_block_interval: 1_000_000, + ..Default::default() + } + .into() +}); + +/// The Base Goerli spec +#[cfg(feature = "optimism")] +pub static BASE_GOERLI: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::base_goerli(), + genesis: serde_json::from_str(include_str!("../../res/genesis/goerli_base.json")) + .expect("Can't deserialize Base Goerli genesis json"), + genesis_hash: Some(b256!( + "a3ab140f15ea7f7443a4702da64c10314eb04d488e72974e02e2d728096b4f76" + )), + fork_timestamps: ForkTimestamps::default(), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Tangerine, ForkCondition::Block(0)), + (Hardfork::SpuriousDragon, ForkCondition::Block(0)), + (Hardfork::Byzantium, ForkCondition::Block(0)), + (Hardfork::Constantinople, ForkCondition::Block(0)), + (Hardfork::Petersburg, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(0)), + (Hardfork::MuirGlacier, ForkCondition::Block(0)), + (Hardfork::Berlin, ForkCondition::Block(0)), + (Hardfork::London, ForkCondition::Block(0)), + (Hardfork::ArrowGlacier, ForkCondition::Block(0)), + (Hardfork::GrayGlacier, ForkCondition::Block(0)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, + ), + (Hardfork::Bedrock, ForkCondition::Block(0)), + (Hardfork::Regolith, ForkCondition::Timestamp(1683219600)), + ]), + base_fee_params: BaseFeeParams::optimism_goerli(), + prune_delete_limit: 1700, + snapshot_block_interval: 1_000_000, + ..Default::default() + } + .into() +}); + +/// The Base mainnet spec +#[cfg(feature = "optimism")] +pub static BASE_MAINNET: Lazy> = Lazy::new(|| { + ChainSpec { + chain: Chain::base_mainnet(), + genesis: serde_json::from_str(include_str!("../../res/genesis/base.json")) + .expect("Can't deserialize Base genesis json"), + genesis_hash: Some(b256!( + "f712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd" + )), + fork_timestamps: ForkTimestamps::default(), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: BTreeMap::from([ + (Hardfork::Frontier, ForkCondition::Block(0)), + (Hardfork::Homestead, ForkCondition::Block(0)), + (Hardfork::Tangerine, ForkCondition::Block(0)), + (Hardfork::SpuriousDragon, ForkCondition::Block(0)), + (Hardfork::Byzantium, ForkCondition::Block(0)), + (Hardfork::Constantinople, ForkCondition::Block(0)), + (Hardfork::Petersburg, ForkCondition::Block(0)), + (Hardfork::Istanbul, ForkCondition::Block(0)), + (Hardfork::MuirGlacier, ForkCondition::Block(0)), + (Hardfork::Berlin, ForkCondition::Block(0)), + (Hardfork::London, ForkCondition::Block(0)), + (Hardfork::ArrowGlacier, ForkCondition::Block(0)), + (Hardfork::GrayGlacier, ForkCondition::Block(0)), + ( + Hardfork::Paris, + ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, + ), + (Hardfork::Bedrock, ForkCondition::Block(0)), + (Hardfork::Regolith, ForkCondition::Timestamp(0)), + ]), + base_fee_params: BaseFeeParams::optimism(), + prune_delete_limit: 1700, + snapshot_block_interval: 1_000_000, + ..Default::default() + } + .into() +}); + +/// An Ethereum chain specification. +/// +/// A chain specification describes: +/// +/// - Meta-information about the chain (the chain ID) +/// - The genesis block of the chain ([`Genesis`]) +/// - What hardforks are activated, and under which conditions +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ChainSpec { + /// The chain ID + pub chain: Chain, + + /// The hash of the genesis block. + /// + /// This acts as a small cache for known chains. If the chain is known, then the genesis hash + /// is also known ahead of time, and this will be `Some`. + #[serde(skip, default)] + pub genesis_hash: Option, + + /// The genesis block + pub genesis: Genesis, + + /// The block at which [Hardfork::Paris] was activated and the final difficulty at this block. + #[serde(skip, default)] + pub paris_block_and_final_difficulty: Option<(u64, U256)>, + + /// Timestamps of various hardforks + /// + /// This caches entries in `hardforks` map + #[serde(skip, default)] + pub fork_timestamps: ForkTimestamps, + + /// The active hard forks and their activation conditions + pub hardforks: BTreeMap, + + /// The deposit contract deployed for PoS + #[serde(skip, default)] + pub deposit_contract: Option, + + /// The parameters that configure how a block's base fee is computed + pub base_fee_params: BaseFeeParams, + + /// The delete limit for pruner, per block. In the actual pruner run it will be multiplied by + /// the amount of blocks between pruner runs to account for the difference in amount of new + /// data coming in. + #[serde(default)] + pub prune_delete_limit: usize, + + /// The block interval for creating snapshots. Each snapshot will have that much blocks in it. + pub snapshot_block_interval: u64, +} + +impl Default for ChainSpec { + fn default() -> ChainSpec { + ChainSpec { + chain: Default::default(), + genesis_hash: Default::default(), + genesis: Default::default(), + paris_block_and_final_difficulty: Default::default(), + fork_timestamps: Default::default(), + hardforks: Default::default(), + deposit_contract: Default::default(), + base_fee_params: BaseFeeParams::ethereum(), + prune_delete_limit: MAINNET.prune_delete_limit, + snapshot_block_interval: Default::default(), + } + } +} + +impl ChainSpec { + /// Get information about the chain itself + pub fn chain(&self) -> Chain { + self.chain + } + + /// Returns `true` if this chain contains Optimism configuration. + pub fn is_optimism(&self) -> bool { + self.chain.is_optimism() + } + + /// Get the genesis block specification. + /// + /// To get the header for the genesis block, use [`Self::genesis_header`] instead. + pub fn genesis(&self) -> &Genesis { + &self.genesis + } + + /// Get the header for the genesis block. + pub fn genesis_header(&self) -> Header { + // If London is activated at genesis, we set the initial base fee as per EIP-1559. + let base_fee_per_gas = self.initial_base_fee(); + + // If shanghai is activated, initialize the header with an empty withdrawals hash, and + // empty withdrawals list. + let withdrawals_root = + (self.fork(Hardfork::Shanghai).active_at_timestamp(self.genesis.timestamp)) + .then_some(EMPTY_WITHDRAWALS); + + // If Cancun is activated at genesis, we set: + // * parent beacon block root to 0x0 + // * blob gas used to provided genesis or 0x0 + // * excess blob gas to provided genesis or 0x0 + let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) = + if self.fork(Hardfork::Cancun).active_at_timestamp(self.genesis.timestamp) { + let blob_gas_used = self.genesis.blob_gas_used.unwrap_or(0); + let excess_blob_gas = self.genesis.excess_blob_gas.unwrap_or(0); + (Some(B256::ZERO), Some(blob_gas_used), Some(excess_blob_gas)) + } else { + (None, None, None) + }; + + Header { + parent_hash: B256::ZERO, + number: 0, + transactions_root: EMPTY_TRANSACTIONS, + ommers_hash: EMPTY_OMMER_ROOT_HASH, + receipts_root: EMPTY_RECEIPTS, + logs_bloom: Default::default(), + gas_limit: self.genesis.gas_limit, + difficulty: self.genesis.difficulty, + nonce: self.genesis.nonce, + extra_data: self.genesis.extra_data.clone(), + state_root: genesis_state_root(&self.genesis.alloc), + timestamp: self.genesis.timestamp, + mix_hash: self.genesis.mix_hash, + beneficiary: self.genesis.coinbase, + gas_used: Default::default(), + base_fee_per_gas, + withdrawals_root, + parent_beacon_block_root, + blob_gas_used, + excess_blob_gas, + } + } + + /// Get the sealed header for the genesis block. + pub fn sealed_genesis_header(&self) -> SealedHeader { + SealedHeader { header: self.genesis_header(), hash: self.genesis_hash() } + } + + /// Get the initial base fee of the genesis block. + pub fn initial_base_fee(&self) -> Option { + // If the base fee is set in the genesis block, we use that instead of the default. + let genesis_base_fee = self.genesis.base_fee_per_gas.unwrap_or(EIP1559_INITIAL_BASE_FEE); + + // If London is activated at genesis, we set the initial base fee as per EIP-1559. + (self.fork(Hardfork::London).active_at_block(0)).then_some(genesis_base_fee) + } + + /// Get the hash of the genesis block. + pub fn genesis_hash(&self) -> B256 { + if let Some(hash) = self.genesis_hash { + hash + } else { + self.genesis_header().hash_slow() + } + } + + /// Get the timestamp of the genesis block. + pub fn genesis_timestamp(&self) -> u64 { + self.genesis.timestamp + } + + /// Returns the final total difficulty if the given block number is after the Paris hardfork. + /// + /// Note: technically this would also be valid for the block before the paris upgrade, but this + /// edge case is omitted here. + pub fn final_paris_total_difficulty(&self, block_number: u64) -> Option { + self.paris_block_and_final_difficulty.and_then(|(activated_at, final_difficulty)| { + if block_number >= activated_at { + Some(final_difficulty) + } else { + None + } + }) + } + + /// Returns the forks in this specification and their activation conditions. + pub fn hardforks(&self) -> &BTreeMap { + &self.hardforks + } + + /// Get the fork id for the given hardfork. + pub fn hardfork_fork_id(&self, fork: Hardfork) -> Option { + fork.fork_id(self) + } + + /// Convenience method to get the fork id for [Hardfork::Shanghai] from a given chainspec. + pub fn shanghai_fork_id(&self) -> Option { + Hardfork::Shanghai.fork_id(self) + } + + /// Get the fork condition for the given fork. + pub fn fork(&self, fork: Hardfork) -> ForkCondition { + self.hardforks.get(&fork).copied().unwrap_or(ForkCondition::Never) + } + + /// Get an iterator of all hardforks with their respective activation conditions. + pub fn forks_iter(&self) -> impl Iterator + '_ { + self.hardforks.iter().map(|(f, b)| (*f, *b)) + } + + /// Convenience method to check if a fork is active at a given timestamp. + #[inline] + pub fn is_fork_active_at_timestamp(&self, fork: Hardfork, timestamp: u64) -> bool { + self.fork(fork).active_at_timestamp(timestamp) + } + + /// Convenience method to check if [Hardfork::Shanghai] is active at a given timestamp. + #[inline] + pub fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool { + self.fork_timestamps + .shanghai + .map(|shanghai| timestamp >= shanghai) + .unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Shanghai, timestamp)) + } + + /// Convenience method to check if [Hardfork::Cancun] is active at a given timestamp. + #[inline] + pub fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool { + self.fork_timestamps + .cancun + .map(|cancun| timestamp >= cancun) + .unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Cancun, timestamp)) + } + + /// Creates a [`ForkFilter`] for the block described by [Head]. + pub fn fork_filter(&self, head: Head) -> ForkFilter { + let forks = self.forks_iter().filter_map(|(_, condition)| { + // We filter out TTD-based forks w/o a pre-known block since those do not show up in the + // fork filter. + Some(match condition { + ForkCondition::Block(block) => ForkFilterKey::Block(block), + ForkCondition::Timestamp(time) => ForkFilterKey::Time(time), + ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block), + _ => return None, + }) + }); + + ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks) + } + + /// Compute the [`ForkId`] for the given [`Head`] folowing eip-6122 spec + pub fn fork_id(&self, head: &Head) -> ForkId { + let mut forkhash = ForkHash::from(self.genesis_hash()); + let mut current_applied = 0; + + // handle all block forks before handling timestamp based forks. see: https://eips.ethereum.org/EIPS/eip-6122 + for (_, cond) in self.forks_iter() { + // handle block based forks and the sepolia merge netsplit block edge case (TTD + // ForkCondition with Some(block)) + if let ForkCondition::Block(block) | + ForkCondition::TTD { fork_block: Some(block), .. } = cond + { + if cond.active_at_head(head) { + if block != current_applied { + forkhash += block; + current_applied = block; + } + } else { + // we can return here because this block fork is not active, so we set the + // `next` value + return ForkId { hash: forkhash, next: block } + } + } + } + + // timestamp are ALWAYS applied after the merge. + // + // this filter ensures that no block-based forks are returned + for timestamp in self.forks_iter().filter_map(|(_, cond)| { + cond.as_timestamp().filter(|time| time > &self.genesis.timestamp) + }) { + let cond = ForkCondition::Timestamp(timestamp); + if cond.active_at_head(head) { + if timestamp != current_applied { + forkhash += timestamp; + current_applied = timestamp; + } + } else { + // can safely return here because we have already handled all block forks and + // have handled all active timestamp forks, and set the next value to the + // timestamp that is known but not active yet + return ForkId { hash: forkhash, next: timestamp } + } + } + + ForkId { hash: forkhash, next: 0 } + } + + /// An internal helper function that returns a head block that satisfies a given Fork condition. + pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head { + match cond { + ForkCondition::Block(number) => Head { number, ..Default::default() }, + ForkCondition::Timestamp(timestamp) => { + // to satisfy every timestamp ForkCondition, we find the last ForkCondition::Block + // if one exists, and include its block_num in the returned Head + if let Some(last_block_num) = self.last_block_fork_before_merge_or_timestamp() { + return Head { timestamp, number: last_block_num, ..Default::default() } + } + Head { timestamp, ..Default::default() } + } + ForkCondition::TTD { total_difficulty, .. } => { + Head { total_difficulty, ..Default::default() } + } + ForkCondition::Never => unreachable!(), + } + } + + /// An internal helper function that returns the block number of the last block-based + /// fork that occurs before any existing TTD (merge)/timestamp based forks. + /// + /// Note: this returns None if the ChainSpec is not configured with a TTD/Timestamp fork. + pub(crate) fn last_block_fork_before_merge_or_timestamp(&self) -> Option { + let mut hardforks_iter = self.forks_iter().peekable(); + while let Some((_, curr_cond)) = hardforks_iter.next() { + if let Some((_, next_cond)) = hardforks_iter.peek() { + // peek and find the first occurence of ForkCondition::TTD (merge) , or in + // custom ChainSpecs, the first occurence of + // ForkCondition::Timestamp. If curr_cond is ForkCondition::Block at + // this point, which it should be in most "normal" ChainSpecs, + // return its block_num + match next_cond { + ForkCondition::TTD { fork_block, .. } => { + // handle Sepolia merge netsplit case + if fork_block.is_some() { + return *fork_block + } + // ensure curr_cond is indeed ForkCondition::Block and return block_num + if let ForkCondition::Block(block_num) = curr_cond { + return Some(block_num) + } + } + ForkCondition::Timestamp(_) => { + // ensure curr_cond is indeed ForkCondition::Block and return block_num + if let ForkCondition::Block(block_num) = curr_cond { + return Some(block_num) + } + } + ForkCondition::Block(_) | ForkCondition::Never => continue, + } + } + } + None + } + + /// Build a chainspec using [`ChainSpecBuilder`] + pub fn builder() -> ChainSpecBuilder { + ChainSpecBuilder::default() + } +} + +impl From for ChainSpec { + fn from(genesis: Genesis) -> Self { + // Block-based hardforks + let hardfork_opts = [ + (Hardfork::Homestead, genesis.config.homestead_block), + (Hardfork::Dao, genesis.config.dao_fork_block), + (Hardfork::Tangerine, genesis.config.eip150_block), + (Hardfork::SpuriousDragon, genesis.config.eip155_block), + (Hardfork::Byzantium, genesis.config.byzantium_block), + (Hardfork::Constantinople, genesis.config.constantinople_block), + (Hardfork::Petersburg, genesis.config.petersburg_block), + (Hardfork::Istanbul, genesis.config.istanbul_block), + (Hardfork::MuirGlacier, genesis.config.muir_glacier_block), + (Hardfork::Berlin, genesis.config.berlin_block), + (Hardfork::London, genesis.config.london_block), + (Hardfork::ArrowGlacier, genesis.config.arrow_glacier_block), + (Hardfork::GrayGlacier, genesis.config.gray_glacier_block), + ]; + let mut hardforks = hardfork_opts + .iter() + .filter_map(|(hardfork, opt)| opt.map(|block| (*hardfork, ForkCondition::Block(block)))) + .collect::>(); + + // Paris + if let Some(ttd) = genesis.config.terminal_total_difficulty { + hardforks.insert( + Hardfork::Paris, + ForkCondition::TTD { + total_difficulty: ttd, + fork_block: genesis.config.merge_netsplit_block, + }, + ); + } + + // Time-based hardforks + let time_hardfork_opts = [ + (Hardfork::Shanghai, genesis.config.shanghai_time), + (Hardfork::Cancun, genesis.config.cancun_time), + ]; + + let time_hardforks = time_hardfork_opts + .iter() + .filter_map(|(hardfork, opt)| { + opt.map(|time| (*hardfork, ForkCondition::Timestamp(time))) + }) + .collect::>(); + + hardforks.extend(time_hardforks); + + Self { + chain: genesis.config.chain_id.into(), + genesis, + genesis_hash: None, + fork_timestamps: ForkTimestamps::from_hardforks(&hardforks), + hardforks, + paris_block_and_final_difficulty: None, + deposit_contract: None, + ..Default::default() + } + } +} + +/// Various timestamps of forks +#[derive(Debug, Clone, Default, Eq, PartialEq)] +pub struct ForkTimestamps { + /// The timestamp of the shanghai fork + pub shanghai: Option, + /// The timestamp of the cancun fork + pub cancun: Option, +} + +impl ForkTimestamps { + /// Creates a new [`ForkTimestamps`] from the given hardforks by extracing the timestamps + fn from_hardforks(forks: &BTreeMap) -> Self { + let mut timestamps = ForkTimestamps::default(); + if let Some(shanghai) = forks.get(&Hardfork::Shanghai).and_then(|f| f.as_timestamp()) { + timestamps = timestamps.shanghai(shanghai); + } + if let Some(cancun) = forks.get(&Hardfork::Cancun).and_then(|f| f.as_timestamp()) { + timestamps = timestamps.cancun(cancun); + } + timestamps + } + + /// Sets the given shanghai timestamp + pub fn shanghai(mut self, shanghai: u64) -> Self { + self.shanghai = Some(shanghai); + self + } + + /// Sets the given cancun timestamp + pub fn cancun(mut self, cancun: u64) -> Self { + self.cancun = Some(cancun); + self + } +} + +/// A helper type for compatibility with geth's config +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(untagged)] +pub enum AllGenesisFormats { + /// The reth genesis format + Reth(ChainSpec), + /// The geth genesis format + Geth(Genesis), +} + +impl From for AllGenesisFormats { + fn from(genesis: Genesis) -> Self { + Self::Geth(genesis) + } +} + +impl From for AllGenesisFormats { + fn from(genesis: ChainSpec) -> Self { + Self::Reth(genesis) + } +} + +impl From> for AllGenesisFormats { + fn from(genesis: Arc) -> Self { + Arc::try_unwrap(genesis).unwrap_or_else(|arc| (*arc).clone()).into() + } +} + +impl From for ChainSpec { + fn from(genesis: AllGenesisFormats) -> Self { + match genesis { + AllGenesisFormats::Geth(genesis) => genesis.into(), + AllGenesisFormats::Reth(genesis) => genesis, + } + } +} + +/// A helper to build custom chain specs +#[derive(Debug, Default, Clone)] +pub struct ChainSpecBuilder { + chain: Option, + genesis: Option, + hardforks: BTreeMap, +} + +impl ChainSpecBuilder { + /// Construct a new builder from the mainnet chain spec. + pub fn mainnet() -> Self { + Self { + chain: Some(MAINNET.chain), + genesis: Some(MAINNET.genesis.clone()), + hardforks: MAINNET.hardforks.clone(), + } + } + + /// Set the chain ID + pub fn chain(mut self, chain: Chain) -> Self { + self.chain = Some(chain); + self + } + + /// Set the genesis block. + pub fn genesis(mut self, genesis: Genesis) -> Self { + self.genesis = Some(genesis); + self + } + + /// Add the given fork with the given activation condition to the spec. + pub fn with_fork(mut self, fork: Hardfork, condition: ForkCondition) -> Self { + self.hardforks.insert(fork, condition); + self + } + + /// Enable the Paris hardfork at the given TTD. + /// + /// Does not set the merge netsplit block. + pub fn paris_at_ttd(self, ttd: U256) -> Self { + self.with_fork( + Hardfork::Paris, + ForkCondition::TTD { total_difficulty: ttd, fork_block: None }, + ) + } + + /// Enable Frontier at genesis. + pub fn frontier_activated(mut self) -> Self { + self.hardforks.insert(Hardfork::Frontier, ForkCondition::Block(0)); + self + } + + /// Enable Homestead at genesis. + pub fn homestead_activated(mut self) -> Self { + self = self.frontier_activated(); + self.hardforks.insert(Hardfork::Homestead, ForkCondition::Block(0)); + self + } + + /// Enable Tangerine at genesis. + pub fn tangerine_whistle_activated(mut self) -> Self { + self = self.homestead_activated(); + self.hardforks.insert(Hardfork::Tangerine, ForkCondition::Block(0)); + self + } + + /// Enable Spurious Dragon at genesis. + pub fn spurious_dragon_activated(mut self) -> Self { + self = self.tangerine_whistle_activated(); + self.hardforks.insert(Hardfork::SpuriousDragon, ForkCondition::Block(0)); + self + } + + /// Enable Byzantium at genesis. + pub fn byzantium_activated(mut self) -> Self { + self = self.spurious_dragon_activated(); + self.hardforks.insert(Hardfork::Byzantium, ForkCondition::Block(0)); + self + } + + /// Enable Petersburg at genesis. + pub fn petersburg_activated(mut self) -> Self { + self = self.byzantium_activated(); + self.hardforks.insert(Hardfork::Petersburg, ForkCondition::Block(0)); + self + } + + /// Enable Istanbul at genesis. + pub fn istanbul_activated(mut self) -> Self { + self = self.petersburg_activated(); + self.hardforks.insert(Hardfork::Istanbul, ForkCondition::Block(0)); + self + } + + /// Enable Berlin at genesis. + pub fn berlin_activated(mut self) -> Self { + self = self.istanbul_activated(); + self.hardforks.insert(Hardfork::Berlin, ForkCondition::Block(0)); + self + } + + /// Enable London at genesis. + pub fn london_activated(mut self) -> Self { + self = self.berlin_activated(); + self.hardforks.insert(Hardfork::London, ForkCondition::Block(0)); + self + } + + /// Enable Paris at genesis. + pub fn paris_activated(mut self) -> Self { + self = self.london_activated(); + self.hardforks.insert( + Hardfork::Paris, + ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }, + ); + self + } + + /// Enable Shanghai at genesis. + pub fn shanghai_activated(mut self) -> Self { + self = self.paris_activated(); + self.hardforks.insert(Hardfork::Shanghai, ForkCondition::Timestamp(0)); + self + } + + /// Enable Cancun at genesis. + pub fn cancun_activated(mut self) -> Self { + self = self.shanghai_activated(); + self.hardforks.insert(Hardfork::Cancun, ForkCondition::Timestamp(0)); + self + } + + /// Enable Bedrock at genesis + #[cfg(feature = "optimism")] + pub fn bedrock_activated(mut self) -> Self { + self = self.paris_activated(); + self.hardforks.insert(Hardfork::Bedrock, ForkCondition::Block(0)); + self + } + + /// Enable Regolith at the timestamp of activation. + /// For post-bedrock op-stack chains, this will be at genesis. + /// For pre-bedrock op-stack chains, this will be at the timestamp of regolith activation. + #[cfg(feature = "optimism")] + pub fn regolith_activated(mut self) -> Self { + self = self.bedrock_activated(); + self.hardforks.insert(Hardfork::Regolith, ForkCondition::Timestamp(0)); + self + } + + /// Build the resulting [`ChainSpec`]. + /// + /// # Panics + /// + /// This function panics if the chain ID and genesis is not set ([`Self::chain`] and + /// [`Self::genesis`]) + pub fn build(self) -> ChainSpec { + ChainSpec { + chain: self.chain.expect("The chain is required"), + genesis: self.genesis.expect("The genesis is required"), + genesis_hash: None, + fork_timestamps: ForkTimestamps::from_hardforks(&self.hardforks), + hardforks: self.hardforks, + paris_block_and_final_difficulty: None, + deposit_contract: None, + ..Default::default() + } + } +} + +impl From<&Arc> for ChainSpecBuilder { + fn from(value: &Arc) -> Self { + Self { + chain: Some(value.chain), + genesis: Some(value.genesis.clone()), + hardforks: value.hardforks.clone(), + } + } +} + +/// The condition at which a fork is activated. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] +pub enum ForkCondition { + /// The fork is activated after a certain block. + Block(BlockNumber), + /// The fork is activated after a total difficulty has been reached. + TTD { + /// The block number at which TTD is reached, if it is known. + /// + /// This should **NOT** be set unless you want this block advertised as [EIP-2124][eip2124] + /// `FORK_NEXT`. This is currently only the case for Sepolia and Holesky. + /// + /// [eip2124]: https://eips.ethereum.org/EIPS/eip-2124 + fork_block: Option, + /// The total difficulty after which the fork is activated. + total_difficulty: U256, + }, + /// The fork is activated after a specific timestamp. + Timestamp(u64), + /// The fork is never activated + #[default] + Never, +} + +impl ForkCondition { + /// Returns true if the fork condition is timestamp based. + pub fn is_timestamp(&self) -> bool { + matches!(self, ForkCondition::Timestamp(_)) + } + + /// Checks whether the fork condition is satisfied at the given block. + /// + /// For TTD conditions, this will only return true if the activation block is already known. + /// + /// For timestamp conditions, this will always return false. + pub fn active_at_block(&self, current_block: BlockNumber) -> bool { + match self { + ForkCondition::Block(block) => current_block >= *block, + ForkCondition::TTD { fork_block: Some(block), .. } => current_block >= *block, + _ => false, + } + } + + /// Checks if the given block is the first block that satisfies the fork condition. + /// + /// This will return false for any condition that is not block based. + pub fn transitions_at_block(&self, current_block: BlockNumber) -> bool { + match self { + ForkCondition::Block(block) => current_block == *block, + _ => false, + } + } + + /// Checks whether the fork condition is satisfied at the given total difficulty and difficulty + /// of a current block. + /// + /// The fork is considered active if the _previous_ total difficulty is above the threshold. + /// To achieve that, we subtract the passed `difficulty` from the current block's total + /// difficulty, and check if it's above the Fork Condition's total difficulty (here: + /// 58_750_000_000_000_000_000_000) + /// + /// This will return false for any condition that is not TTD-based. + pub fn active_at_ttd(&self, ttd: U256, difficulty: U256) -> bool { + if let ForkCondition::TTD { total_difficulty, .. } = self { + ttd.saturating_sub(difficulty) >= *total_difficulty + } else { + false + } + } + + /// Checks whether the fork condition is satisfied at the given timestamp. + /// + /// This will return false for any condition that is not timestamp-based. + pub fn active_at_timestamp(&self, timestamp: u64) -> bool { + if let ForkCondition::Timestamp(time) = self { + timestamp >= *time + } else { + false + } + } + + /// Checks whether the fork condition is satisfied at the given head block. + /// + /// This will return true if: + /// + /// - The condition is satisfied by the block number; + /// - The condition is satisfied by the timestamp; + /// - or the condition is satisfied by the total difficulty + pub fn active_at_head(&self, head: &Head) -> bool { + self.active_at_block(head.number) || + self.active_at_timestamp(head.timestamp) || + self.active_at_ttd(head.total_difficulty, head.difficulty) + } + + /// Get the total terminal difficulty for this fork condition. + /// + /// Returns `None` for fork conditions that are not TTD based. + pub fn ttd(&self) -> Option { + match self { + ForkCondition::TTD { total_difficulty, .. } => Some(*total_difficulty), + _ => None, + } + } + + /// Returns the timestamp of the fork condition, if it is timestamp based. + pub fn as_timestamp(&self) -> Option { + match self { + ForkCondition::Timestamp(timestamp) => Some(*timestamp), + _ => None, + } + } +} + +/// A container to pretty-print a hardfork. +/// +/// The fork is formatted depending on its fork condition: +/// +/// - Block and timestamp based forks are formatted in the same manner (`{name} <({eip})> +/// @{condition}`) +/// - TTD based forks are formatted separately as `{name} <({eip})> @{ttd} (network is known +/// to be merged)` +/// +/// An optional EIP can be attached to the fork to display as well. This should generally be in the +/// form of just `EIP-x`, e.g. `EIP-1559`. +#[derive(Debug)] +struct DisplayFork { + /// The name of the hardfork (e.g. Frontier) + name: String, + /// The fork condition + activated_at: ForkCondition, + /// An optional EIP (e.g. `EIP-1559`). + eip: Option, +} + +impl Display for DisplayFork { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let name_with_eip = if let Some(eip) = &self.eip { + format!("{} ({})", self.name, eip) + } else { + self.name.clone() + }; + + match self.activated_at { + ForkCondition::Block(at) | ForkCondition::Timestamp(at) => { + write!(f, "{:32} @{}", name_with_eip, at)?; + } + ForkCondition::TTD { fork_block, total_difficulty } => { + writeln!( + f, + "{:32} @{} ({})", + name_with_eip, + total_difficulty, + if fork_block.is_some() { + "network is known to be merged" + } else { + "network is not known to be merged" + } + )?; + } + ForkCondition::Never => unreachable!(), + } + + Ok(()) + } +} + +/// A container for pretty-printing a list of hardforks. +/// +/// # Examples +/// +/// ``` +/// # use reth_ethereum_forks::MAINNET; +/// # use reth_ethereum_forks::DisplayHardforks; +/// println!("{}", DisplayHardforks::new(MAINNET.hardforks())); +/// ``` +/// +/// An example of the output: +/// +/// ```text +/// Pre-merge hard forks (block based): +// - Frontier @0 +// - Homestead @1150000 +// - Dao @1920000 +// - Tangerine @2463000 +// - SpuriousDragon @2675000 +// - Byzantium @4370000 +// - Constantinople @7280000 +// - Petersburg @7280000 +// - Istanbul @9069000 +// - MuirGlacier @9200000 +// - Berlin @12244000 +// - London @12965000 +// - ArrowGlacier @13773000 +// - GrayGlacier @15050000 +// Merge hard forks: +// - Paris @58750000000000000000000 (network is not known to be merged) +// +// Post-merge hard forks (timestamp based): +// - Shanghai @1681338455 +/// ``` +#[derive(Debug)] +pub struct DisplayHardforks { + /// A list of pre-merge (block based) hardforks + pre_merge: Vec, + /// A list of merge (TTD based) hardforks + with_merge: Vec, + /// A list of post-merge (timestamp based) hardforks + post_merge: Vec, +} + +impl Display for DisplayHardforks { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "Pre-merge hard forks (block based):")?; + for fork in self.pre_merge.iter() { + writeln!(f, "- {fork}")?; + } + + if !self.with_merge.is_empty() { + writeln!(f, "Merge hard forks:")?; + for fork in self.with_merge.iter() { + writeln!(f, "- {fork}")?; + } + } + + if !self.post_merge.is_empty() { + writeln!(f, "Post-merge hard forks (timestamp based):")?; + for fork in self.post_merge.iter() { + writeln!(f, "- {fork}")?; + } + } + + Ok(()) + } +} + +impl DisplayHardforks { + /// Creates a new [`DisplayHardforks`] from an iterator of hardforks. + pub fn new(hardforks: &BTreeMap) -> Self { + Self::from_iter(hardforks.iter()) + } +} + +impl<'a, 'b> FromIterator<(&'a Hardfork, &'b ForkCondition)> for DisplayHardforks { + fn from_iter>(iter: T) -> Self { + let mut pre_merge = Vec::new(); + let mut with_merge = Vec::new(); + let mut post_merge = Vec::new(); + + for (fork, condition) in iter { + let display_fork = + DisplayFork { name: fork.to_string(), activated_at: *condition, eip: None }; + + match condition { + ForkCondition::Block(_) => { + pre_merge.push(display_fork); + } + ForkCondition::TTD { .. } => { + with_merge.push(display_fork); + } + ForkCondition::Timestamp(_) => { + post_merge.push(display_fork); + } + ForkCondition::Never => continue, + } + } + + Self { pre_merge, with_merge, post_merge } + } +} + +/// PoS deposit contract details. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DepositContract { + /// Deposit Contract Address + pub address: Address, + /// Deployment Block + pub block: BlockNumber, + /// `DepositEvent` event signature + pub topic: B256, +} + +impl DepositContract { + fn new(address: Address, block: BlockNumber, topic: B256) -> Self { + DepositContract { address, block, topic } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{b256, hex, NamedChain, B256, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, U256}; + use alloy_rlp::Encodable; + use bytes::BytesMut; + use reth_primitives::{ChainConfig, GenesisAccount}; + use std::{collections::HashMap, str::FromStr}; + + #[cfg(feature = "optimism")] + use crate::OP_GOERLI; + + fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) { + for (block, expected_id) in cases { + let computed_id = spec.fork_id(block); + assert_eq!( + expected_id, &computed_id, + "Expected fork ID {:?}, computed fork ID {:?} at block {}", + expected_id, computed_id, block.number + ); + } + } + + fn test_hardfork_fork_ids(spec: &ChainSpec, cases: &[(Hardfork, ForkId)]) { + for (hardfork, expected_id) in cases { + if let Some(computed_id) = spec.hardfork_fork_id(*hardfork) { + assert_eq!( + expected_id, &computed_id, + "Expected fork ID {:?}, computed fork ID {:?} for hardfork {}", + expected_id, computed_id, hardfork + ); + if let Hardfork::Shanghai = hardfork { + if let Some(shangai_id) = spec.shanghai_fork_id() { + assert_eq!( + expected_id, &shangai_id, + "Expected fork ID {:?}, computed fork ID {:?} for Shanghai hardfork", + expected_id, computed_id + ); + } else { + panic!("Expected ForkCondition to return Some for Hardfork::Shanghai"); + } + } + } + } + } + + #[test] + fn test_hardfork_list_display_mainnet() { + assert_eq!( + DisplayHardforks::new(MAINNET.hardforks()).to_string(), + "Pre-merge hard forks (block based): +- Frontier @0 +- Homestead @1150000 +- Dao @1920000 +- Tangerine @2463000 +- SpuriousDragon @2675000 +- Byzantium @4370000 +- Constantinople @7280000 +- Petersburg @7280000 +- Istanbul @9069000 +- MuirGlacier @9200000 +- Berlin @12244000 +- London @12965000 +- ArrowGlacier @13773000 +- GrayGlacier @15050000 +Merge hard forks: +- Paris @58750000000000000000000 (network is not known to be merged) + +Post-merge hard forks (timestamp based): +- Shanghai @1681338455 +" + ); + } + + #[test] + fn test_hardfork_list_ignores_disabled_forks() { + let spec = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(Genesis::default()) + .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) + .with_fork(Hardfork::Shanghai, ForkCondition::Never) + .build(); + assert_eq!( + DisplayHardforks::new(spec.hardforks()).to_string(), + "Pre-merge hard forks (block based): +- Frontier @0 +" + ); + } + + // Tests that the ForkTimestamps are correctly set up. + #[test] + fn test_fork_timestamps() { + let spec = ChainSpec::builder().chain(Chain::mainnet()).genesis(Genesis::default()).build(); + assert!(spec.fork_timestamps.shanghai.is_none()); + + let spec = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(Genesis::default()) + .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(1337)) + .build(); + assert_eq!(spec.fork_timestamps.shanghai, Some(1337)); + assert!(spec.is_shanghai_active_at_timestamp(1337)); + assert!(!spec.is_shanghai_active_at_timestamp(1336)); + } + + // Tests that all predefined timestamps are correctly set up in the chainspecs + #[test] + fn test_predefined_chain_spec_fork_timestamps() { + fn ensure_timestamp_fork_conditions(spec: &ChainSpec) { + // This is a sanity test that ensures we always set all currently known fork timestamps, + // this will fail if a new timestamp based fork condition has added to the hardforks but + // no corresponding entry in the ForkTimestamp types, See also + // [ForkTimestamps::from_hardforks] + + // currently there are only 1 timestamps known: shanghai + let known_timestamp_based_forks = 1; + let num_timestamp_based_forks = + spec.hardforks.values().copied().filter(ForkCondition::is_timestamp).count(); + assert_eq!(num_timestamp_based_forks, known_timestamp_based_forks); + + // ensures all timestamp forks are set + assert!(spec.fork_timestamps.shanghai.is_some()); + } + + for spec in [&*MAINNET, &*SEPOLIA] { + ensure_timestamp_fork_conditions(spec); + } + } + + // Tests that we skip any fork blocks in block #0 (the genesis ruleset) + #[test] + fn ignores_genesis_fork_blocks() { + let spec = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(Genesis::default()) + .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) + .with_fork(Hardfork::Homestead, ForkCondition::Block(0)) + .with_fork(Hardfork::Tangerine, ForkCondition::Block(0)) + .with_fork(Hardfork::SpuriousDragon, ForkCondition::Block(0)) + .with_fork(Hardfork::Byzantium, ForkCondition::Block(0)) + .with_fork(Hardfork::Constantinople, ForkCondition::Block(0)) + .with_fork(Hardfork::Istanbul, ForkCondition::Block(0)) + .with_fork(Hardfork::MuirGlacier, ForkCondition::Block(0)) + .with_fork(Hardfork::Berlin, ForkCondition::Block(0)) + .with_fork(Hardfork::London, ForkCondition::Block(0)) + .with_fork(Hardfork::ArrowGlacier, ForkCondition::Block(0)) + .with_fork(Hardfork::GrayGlacier, ForkCondition::Block(0)) + .build(); + + assert_eq!(spec.hardforks().len(), 12, "12 forks should be active."); + assert_eq!( + spec.fork_id(&Head { number: 1, ..Default::default() }), + ForkId { hash: ForkHash::from(spec.genesis_hash()), next: 0 }, + "the fork ID should be the genesis hash; forks at genesis are ignored for fork filters" + ); + } + + #[test] + fn ignores_duplicate_fork_blocks() { + let empty_genesis = Genesis::default(); + let unique_spec = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(empty_genesis.clone()) + .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) + .with_fork(Hardfork::Homestead, ForkCondition::Block(1)) + .build(); + + let duplicate_spec = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(empty_genesis) + .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) + .with_fork(Hardfork::Homestead, ForkCondition::Block(1)) + .with_fork(Hardfork::Tangerine, ForkCondition::Block(1)) + .build(); + + assert_eq!( + unique_spec.fork_id(&Head { number: 2, ..Default::default() }), + duplicate_spec.fork_id(&Head { number: 2, ..Default::default() }), + "duplicate fork blocks should be deduplicated for fork filters" + ); + } + + #[test] + fn test_chainspec_satisfy() { + let empty_genesis = Genesis::default(); + // happy path test case + let happy_path_case = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(empty_genesis.clone()) + .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) + .with_fork(Hardfork::Homestead, ForkCondition::Block(73)) + .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123)) + .build(); + let happy_path_head = happy_path_case.satisfy(ForkCondition::Timestamp(11313123)); + let happy_path_expected = Head { number: 73, timestamp: 11313123, ..Default::default() }; + assert_eq!( + happy_path_head, happy_path_expected, + "expected satisfy() to return {:#?}, but got {:#?} ", + happy_path_expected, happy_path_head + ); + // multiple timestamp test case (i.e Shanghai -> Cancun) + let multiple_timestamp_fork_case = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(empty_genesis.clone()) + .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) + .with_fork(Hardfork::Homestead, ForkCondition::Block(73)) + .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123)) + .with_fork(Hardfork::Cancun, ForkCondition::Timestamp(11313398)) + .build(); + let multi_timestamp_head = + multiple_timestamp_fork_case.satisfy(ForkCondition::Timestamp(11313398)); + let mult_timestamp_expected = + Head { number: 73, timestamp: 11313398, ..Default::default() }; + assert_eq!( + multi_timestamp_head, mult_timestamp_expected, + "expected satisfy() to return {:#?}, but got {:#?} ", + mult_timestamp_expected, multi_timestamp_head + ); + // no ForkCondition::Block test case + let no_block_fork_case = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(empty_genesis.clone()) + .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123)) + .build(); + let no_block_fork_head = no_block_fork_case.satisfy(ForkCondition::Timestamp(11313123)); + let no_block_fork_expected = Head { number: 0, timestamp: 11313123, ..Default::default() }; + assert_eq!( + no_block_fork_head, no_block_fork_expected, + "expected satisfy() to return {:#?}, but got {:#?} ", + no_block_fork_expected, no_block_fork_head + ); + // spec w/ ForkCondition::TTD with block_num test case (Sepolia merge netsplit edge case) + let fork_cond_ttd_blocknum_case = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(empty_genesis.clone()) + .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) + .with_fork(Hardfork::Homestead, ForkCondition::Block(73)) + .with_fork( + Hardfork::Paris, + ForkCondition::TTD { + fork_block: Some(101), + total_difficulty: U256::from(10_790_000), + }, + ) + .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123)) + .build(); + let fork_cond_ttd_blocknum_head = + fork_cond_ttd_blocknum_case.satisfy(ForkCondition::Timestamp(11313123)); + let fork_cond_ttd_blocknum_expected = + Head { number: 101, timestamp: 11313123, ..Default::default() }; + assert_eq!( + fork_cond_ttd_blocknum_head, fork_cond_ttd_blocknum_expected, + "expected satisfy() to return {:#?}, but got {:#?} ", + fork_cond_ttd_blocknum_expected, fork_cond_ttd_blocknum_head + ); + + // spec w/ only ForkCondition::Block - test the match arm for ForkCondition::Block to ensure + // no regressions, for these ForkConditions(Block/TTD) - a separate chain spec definition is + // technically unecessary - but we include it here for thoroughness + let fork_cond_block_only_case = ChainSpec::builder() + .chain(Chain::mainnet()) + .genesis(empty_genesis) + .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) + .with_fork(Hardfork::Homestead, ForkCondition::Block(73)) + .build(); + let fork_cond_block_only_head = fork_cond_block_only_case.satisfy(ForkCondition::Block(73)); + let fork_cond_block_only_expected = Head { number: 73, ..Default::default() }; + assert_eq!( + fork_cond_block_only_head, fork_cond_block_only_expected, + "expected satisfy() to return {:#?}, but got {:#?} ", + fork_cond_block_only_expected, fork_cond_block_only_head + ); + // Fork::ConditionTTD test case without a new chain spec to demonstrate ChainSpec::satisfy + // is independent of ChainSpec for this(these - including ForkCondition::Block) match arm(s) + let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD { + fork_block: None, + total_difficulty: U256::from(10_790_000), + }); + let fork_cond_ttd_no_new_spec_expected = + Head { total_difficulty: U256::from(10_790_000), ..Default::default() }; + assert_eq!( + fork_cond_ttd_no_new_spec, fork_cond_ttd_no_new_spec_expected, + "expected satisfy() to return {:#?}, but got {:#?} ", + fork_cond_ttd_no_new_spec_expected, fork_cond_ttd_no_new_spec + ); + } + + #[test] + fn mainnet_hardfork_fork_ids() { + test_hardfork_fork_ids( + &MAINNET, + &[ + ( + Hardfork::Frontier, + ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }, + ), + ( + Hardfork::Homestead, + ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }, + ), + (Hardfork::Dao, ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }), + ( + Hardfork::Tangerine, + ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }, + ), + ( + Hardfork::SpuriousDragon, + ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }, + ), + ( + Hardfork::Byzantium, + ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }, + ), + ( + Hardfork::Constantinople, + ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, + ), + ( + Hardfork::Petersburg, + ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, + ), + ( + Hardfork::Istanbul, + ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }, + ), + ( + Hardfork::MuirGlacier, + ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }, + ), + ( + Hardfork::Berlin, + ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, + ), + ( + Hardfork::London, + ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, + ), + ( + Hardfork::ArrowGlacier, + ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }, + ), + ( + Hardfork::GrayGlacier, + ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 }, + ), + (Hardfork::Shanghai, ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 0 }), + ], + ); + } + + #[test] + fn goerli_hardfork_fork_ids() { + test_hardfork_fork_ids( + &GOERLI, + &[ + ( + Hardfork::Frontier, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Hardfork::Homestead, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Hardfork::Tangerine, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Hardfork::SpuriousDragon, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Hardfork::Byzantium, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Hardfork::Constantinople, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Hardfork::Petersburg, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Hardfork::Istanbul, + ForkId { hash: ForkHash([0xc2, 0x5e, 0xfa, 0x5c]), next: 4460644 }, + ), + ( + Hardfork::Berlin, + ForkId { hash: ForkHash([0x75, 0x7a, 0x1c, 0x47]), next: 5062605 }, + ), + ( + Hardfork::London, + ForkId { hash: ForkHash([0xb8, 0xc6, 0x29, 0x9d]), next: 1678832736 }, + ), + (Hardfork::Shanghai, ForkId { hash: ForkHash([0xf9, 0x84, 0x3a, 0xbf]), next: 0 }), + ], + ); + } + + #[test] + fn sepolia_hardfork_fork_ids() { + test_hardfork_fork_ids( + &SEPOLIA, + &[ + ( + Hardfork::Frontier, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::Homestead, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::Tangerine, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::SpuriousDragon, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::Byzantium, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::Constantinople, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::Petersburg, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::Istanbul, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::Berlin, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::London, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Hardfork::Paris, + ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 }, + ), + (Hardfork::Shanghai, ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 0 }), + ], + ); + } + + #[test] + fn mainnet_forkids() { + test_fork_ids( + &MAINNET, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }, + ), + ( + Head { number: 1150000, ..Default::default() }, + ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }, + ), + ( + Head { number: 1920000, ..Default::default() }, + ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }, + ), + ( + Head { number: 2463000, ..Default::default() }, + ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }, + ), + ( + Head { number: 2675000, ..Default::default() }, + ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }, + ), + ( + Head { number: 4370000, ..Default::default() }, + ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }, + ), + ( + Head { number: 7280000, ..Default::default() }, + ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, + ), + ( + Head { number: 9069000, ..Default::default() }, + ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }, + ), + ( + Head { number: 9200000, ..Default::default() }, + ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }, + ), + ( + Head { number: 12244000, ..Default::default() }, + ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, + ), + ( + Head { number: 12965000, ..Default::default() }, + ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, + ), + ( + Head { number: 13773000, ..Default::default() }, + ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }, + ), + ( + Head { number: 15050000, ..Default::default() }, + ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 }, + ), + // First Shanghai block + ( + Head { number: 20000000, timestamp: 1681338455, ..Default::default() }, + ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 0 }, + ), + // Future Shanghai block + ( + Head { number: 20000000, timestamp: 2000000000, ..Default::default() }, + ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 0 }, + ), + ], + ); + } + + #[test] + fn goerli_forkids() { + test_fork_ids( + &GOERLI, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Head { number: 1561650, ..Default::default() }, + ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, + ), + ( + Head { number: 1561651, ..Default::default() }, + ForkId { hash: ForkHash([0xc2, 0x5e, 0xfa, 0x5c]), next: 4460644 }, + ), + ( + Head { number: 4460643, ..Default::default() }, + ForkId { hash: ForkHash([0xc2, 0x5e, 0xfa, 0x5c]), next: 4460644 }, + ), + ( + Head { number: 4460644, ..Default::default() }, + ForkId { hash: ForkHash([0x75, 0x7a, 0x1c, 0x47]), next: 5062605 }, + ), + ( + Head { number: 5062605, ..Default::default() }, + ForkId { hash: ForkHash([0xb8, 0xc6, 0x29, 0x9d]), next: 1678832736 }, + ), + ( + Head { number: 6000000, timestamp: 1678832735, ..Default::default() }, + ForkId { hash: ForkHash([0xb8, 0xc6, 0x29, 0x9d]), next: 1678832736 }, + ), + // First Shanghai block + ( + Head { number: 6000001, timestamp: 1678832736, ..Default::default() }, + ForkId { hash: ForkHash([0xf9, 0x84, 0x3a, 0xbf]), next: 0 }, + ), + // Future Shanghai block + ( + Head { number: 6500000, timestamp: 1678832736, ..Default::default() }, + ForkId { hash: ForkHash([0xf9, 0x84, 0x3a, 0xbf]), next: 0 }, + ), + ], + ); + } + + #[test] + fn sepolia_forkids() { + test_fork_ids( + &SEPOLIA, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Head { number: 1735370, ..Default::default() }, + ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, + ), + ( + Head { number: 1735371, ..Default::default() }, + ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 }, + ), + ( + Head { number: 1735372, timestamp: 1677557087, ..Default::default() }, + ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 }, + ), + ( + Head { number: 1735372, timestamp: 1677557088, ..Default::default() }, + ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 0 }, + ), + ], + ); + } + + #[test] + fn dev_forkids() { + test_fork_ids( + &DEV, + &[( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x45, 0xb8, 0x36, 0x12]), next: 0 }, + )], + ) + } + + #[cfg(feature = "optimism")] + #[test] + fn optimism_goerli_forkids() { + test_fork_ids( + &OP_GOERLI, + &[ + ( + Head { number: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x6d, 0x63, 0x76, 0xbe]), next: 4061224 }, + ), + ( + Head { number: 4061224, timestamp: 1679079599, ..Default::default() }, + ForkId { hash: ForkHash([0x03, 0x47, 0x85, 0x69]), next: 1679079600 }, + ), + ( + Head { number: 4061224, timestamp: 1679079600, ..Default::default() }, + ForkId { hash: ForkHash([0x6d, 0x43, 0x1d, 0x6c]), next: 0 }, + ), + ], + ); + } + + /// Checks that time-based forks work + /// + /// This is based off of the test vectors here: https://github.com/ethereum/go-ethereum/blob/5c8cc10d1e05c23ff1108022f4150749e73c0ca1/core/forkid/forkid_test.go#L155-L188 + #[test] + fn timestamped_forks() { + let mainnet_with_shanghai = ChainSpecBuilder::mainnet() + .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(1668000000)) + .build(); + test_fork_ids( + &mainnet_with_shanghai, + &[ + ( + Head { number: 0, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }, + ), // Unsynced + ( + Head { number: 1149999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }, + ), // Last Frontier block + ( + Head { number: 1150000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }, + ), // First Homestead block + ( + Head { number: 1919999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }, + ), // Last Homestead block + ( + Head { number: 1920000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }, + ), // First DAO block + ( + Head { number: 2462999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }, + ), // Last DAO block + ( + Head { number: 2463000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }, + ), // First Tangerine block + ( + Head { number: 2674999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }, + ), // Last Tangerine block + ( + Head { number: 2675000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }, + ), // First Spurious block + ( + Head { number: 4369999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }, + ), // Last Spurious block + ( + Head { number: 4370000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }, + ), // First Byzantium block + ( + Head { number: 7279999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }, + ), // Last Byzantium block + ( + Head { number: 7280000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, + ), // First and last Constantinople, first Petersburg block + ( + Head { number: 9068999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, + ), // Last Petersburg block + ( + Head { number: 9069000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }, + ), // First Istanbul and first Muir Glacier block + ( + Head { number: 9199999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }, + ), // Last Istanbul and first Muir Glacier block + ( + Head { number: 9200000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }, + ), // First Muir Glacier block + ( + Head { number: 12243999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }, + ), // Last Muir Glacier block + ( + Head { number: 12244000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, + ), // First Berlin block + ( + Head { number: 12964999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, + ), // Last Berlin block + ( + Head { number: 12965000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, + ), // First London block + ( + Head { number: 13772999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, + ), // Last London block + ( + Head { number: 13773000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }, + ), // First Arrow Glacier block + ( + Head { number: 15049999, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }, + ), // Last Arrow Glacier block + ( + Head { number: 15050000, timestamp: 0, ..Default::default() }, + ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1668000000 }, + ), // First Gray Glacier block + ( + Head { number: 19999999, timestamp: 1667999999, ..Default::default() }, + ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1668000000 }, + ), // Last Gray Glacier block + ( + Head { number: 20000000, timestamp: 1668000000, ..Default::default() }, + ForkId { hash: ForkHash([0x71, 0x14, 0x76, 0x44]), next: 0 }, + ), // First Shanghai block + ( + Head { number: 20000000, timestamp: 2668000000, ..Default::default() }, + ForkId { hash: ForkHash([0x71, 0x14, 0x76, 0x44]), next: 0 }, + ), // Future Shanghai block + ], + ); + } + + /// Constructs a [ChainSpec] with the given [ChainSpecBuilder], shanghai, and cancun fork + /// timestamps. + fn construct_chainspec( + builder: ChainSpecBuilder, + shanghai_time: u64, + cancun_time: u64, + ) -> ChainSpec { + builder + .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(shanghai_time)) + .with_fork(Hardfork::Cancun, ForkCondition::Timestamp(cancun_time)) + .build() + } + + /// Tests that time-based forks which are active at genesis are not included in forkid hash. + /// + /// This is based off of the test vectors here: + /// + #[test] + fn test_timestamp_fork_in_genesis() { + let timestamp = 1690475657u64; + let default_spec_builder = ChainSpecBuilder::default() + .chain(Chain::Id(1337)) + .genesis(Genesis::default().with_timestamp(timestamp)) + .paris_activated(); + + // test format: (chain spec, expected next value) - the forkhash will be determined by the + // genesis hash of the constructed chainspec + let tests = [ + ( + construct_chainspec(default_spec_builder.clone(), timestamp - 1, timestamp + 1), + timestamp + 1, + ), + ( + construct_chainspec(default_spec_builder.clone(), timestamp, timestamp + 1), + timestamp + 1, + ), + ( + construct_chainspec(default_spec_builder, timestamp + 1, timestamp + 2), + timestamp + 1, + ), + ]; + + for (spec, expected_timestamp) in tests { + let got_forkid = spec.fork_id(&Head { number: 0, timestamp: 0, ..Default::default() }); + // This is slightly different from the geth test because we use the shanghai timestamp + // to determine whether or not to include a withdrawals root in the genesis header. + // This makes the genesis hash different, and as a result makes the ChainSpec fork hash + // different. + let genesis_hash = spec.genesis_hash(); + let expected_forkid = + ForkId { hash: ForkHash::from(genesis_hash), next: expected_timestamp }; + assert_eq!(got_forkid, expected_forkid); + } + } + + /// Checks that the fork is not active at a terminal ttd block. + #[test] + fn check_terminal_ttd() { + let chainspec = ChainSpecBuilder::mainnet().build(); + + // Check that Paris is not active on terminal PoW block #15537393. + let terminal_block_ttd = U256::from(58750003716598352816469_u128); + let terminal_block_difficulty = U256::from(11055787484078698_u128); + assert!(!chainspec + .fork(Hardfork::Paris) + .active_at_ttd(terminal_block_ttd, terminal_block_difficulty)); + + // Check that Paris is active on first PoS block #15537394. + let first_pos_block_ttd = U256::from(58750003716598352816469_u128); + let first_pos_difficulty = U256::ZERO; + assert!(chainspec + .fork(Hardfork::Paris) + .active_at_ttd(first_pos_block_ttd, first_pos_difficulty)); + } + + #[test] + fn geth_genesis_with_shanghai() { + let geth_genesis = r#" + { + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true, + "ethash": {} + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x", + "gasLimit": "0x4c4b40", + "difficulty": "0x1", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "658bdf435d810c91414ec09147daa6db62406379": { + "balance": "0x487a9a304539440000" + }, + "aa00000000000000000000000000000000000000": { + "code": "0x6042", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000", + "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000", + "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303" + }, + "balance": "0x1", + "nonce": "0x1" + }, + "bb00000000000000000000000000000000000000": { + "code": "0x600154600354", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000", + "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000", + "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303" + }, + "balance": "0x2", + "nonce": "0x1" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas": "0x3b9aca00" + } + "#; + + let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); + let chainspec = ChainSpec::from(genesis); + + // assert a bunch of hardforks that should be set + assert_eq!( + chainspec.hardforks.get(&Hardfork::Homestead).unwrap(), + &ForkCondition::Block(0) + ); + assert_eq!( + chainspec.hardforks.get(&Hardfork::Tangerine).unwrap(), + &ForkCondition::Block(0) + ); + assert_eq!( + chainspec.hardforks.get(&Hardfork::SpuriousDragon).unwrap(), + &ForkCondition::Block(0) + ); + assert_eq!( + chainspec.hardforks.get(&Hardfork::Byzantium).unwrap(), + &ForkCondition::Block(0) + ); + assert_eq!( + chainspec.hardforks.get(&Hardfork::Constantinople).unwrap(), + &ForkCondition::Block(0) + ); + assert_eq!( + chainspec.hardforks.get(&Hardfork::Petersburg).unwrap(), + &ForkCondition::Block(0) + ); + assert_eq!(chainspec.hardforks.get(&Hardfork::Istanbul).unwrap(), &ForkCondition::Block(0)); + assert_eq!( + chainspec.hardforks.get(&Hardfork::MuirGlacier).unwrap(), + &ForkCondition::Block(0) + ); + assert_eq!(chainspec.hardforks.get(&Hardfork::Berlin).unwrap(), &ForkCondition::Block(0)); + assert_eq!(chainspec.hardforks.get(&Hardfork::London).unwrap(), &ForkCondition::Block(0)); + assert_eq!( + chainspec.hardforks.get(&Hardfork::ArrowGlacier).unwrap(), + &ForkCondition::Block(0) + ); + assert_eq!( + chainspec.hardforks.get(&Hardfork::GrayGlacier).unwrap(), + &ForkCondition::Block(0) + ); + + // including time based hardforks + assert_eq!( + chainspec.hardforks.get(&Hardfork::Shanghai).unwrap(), + &ForkCondition::Timestamp(0) + ); + + // alloc key -> expected rlp mapping + let key_rlp = vec![ + (hex!("658bdf435d810c91414ec09147daa6db62406379"), &hex!("f84d8089487a9a304539440000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")[..]), + (hex!("aa00000000000000000000000000000000000000"), &hex!("f8440101a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0ce92c756baff35fa740c3557c1a971fd24d2d35b7c8e067880d50cd86bb0bc99")[..]), + (hex!("bb00000000000000000000000000000000000000"), &hex!("f8440102a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0e25a53cbb501cec2976b393719c63d832423dd70a458731a0b64e4847bbca7d2")[..]), + ]; + + for (key, expected_rlp) in key_rlp { + let account = chainspec.genesis.alloc.get(&key).expect("account should exist"); + let mut account_rlp = BytesMut::new(); + account.encode(&mut account_rlp); + assert_eq!(account_rlp, expected_rlp) + } + + assert_eq!(chainspec.genesis_hash, None); + let expected_state_root: B256 = + hex!("078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into(); + assert_eq!(chainspec.genesis_header().state_root, expected_state_root); + + let expected_withdrawals_hash: B256 = + hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(); + assert_eq!(chainspec.genesis_header().withdrawals_root, Some(expected_withdrawals_hash)); + + let expected_hash: B256 = + hex!("1fc027d65f820d3eef441ebeec139ebe09e471cf98516dce7b5643ccb27f418c").into(); + let hash = chainspec.genesis_hash(); + assert_eq!(hash, expected_hash); + } + + #[test] + fn hive_geth_json() { + let hive_json = r#" + { + "nonce": "0x0000000000000042", + "difficulty": "0x2123456", + "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234", + "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "timestamp": "0x123456", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0xfafbfcfd", + "gasLimit": "0x2fefd8", + "alloc": { + "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": { + "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, + "e6716f9544a56c530d868e4bfbacb172315bdead": { + "balance": "0x11", + "code": "0x12" + }, + "b9c015918bdaba24b4ff057a92a3873d6eb201be": { + "balance": "0x21", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22" + } + }, + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": { + "balance": "0x31", + "nonce": "0x32" + }, + "0000000000000000000000000000000000000001": { + "balance": "0x41" + }, + "0000000000000000000000000000000000000002": { + "balance": "0x51" + }, + "0000000000000000000000000000000000000003": { + "balance": "0x61" + }, + "0000000000000000000000000000000000000004": { + "balance": "0x71" + } + }, + "config": { + "ethash": {}, + "chainId": 10, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0 + } + } + "#; + + let _genesis = serde_json::from_str::(hive_json).unwrap(); + let genesis = serde_json::from_str::(hive_json).unwrap(); + let chainspec: ChainSpec = genesis.into(); + assert_eq!(chainspec.genesis_hash, None); + assert_eq!(chainspec.chain, Chain::Named(NamedChain::Optimism)); + let expected_state_root: B256 = + hex!("9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into(); + assert_eq!(chainspec.genesis_header().state_root, expected_state_root); + let hard_forks = vec![ + Hardfork::Byzantium, + Hardfork::Homestead, + Hardfork::Istanbul, + Hardfork::Petersburg, + Hardfork::Constantinople, + ]; + for ref fork in hard_forks { + assert_eq!(chainspec.hardforks.get(fork).unwrap(), &ForkCondition::Block(0)); + } + + let expected_hash: B256 = + hex!("5ae31c6522bd5856129f66be3d582b842e4e9faaa87f21cce547128339a9db3c").into(); + let hash = chainspec.genesis_header().hash_slow(); + assert_eq!(hash, expected_hash); + } + + #[test] + fn test_parse_genesis_json() { + let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x1337"}"#; + let genesis: Genesis = serde_json::from_str(s).unwrap(); + let acc = genesis + .alloc + .get(&"0xaa00000000000000000000000000000000000000".parse::
().unwrap()) + .unwrap(); + assert_eq!(acc.balance, U256::from(1)); + assert_eq!(genesis.base_fee_per_gas, Some(0x1337)); + } + + #[test] + fn test_parse_cancun_genesis_json() { + let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0,"cancunTime":4661},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x3b9aca00"}"#; + let genesis: Genesis = serde_json::from_str(s).unwrap(); + let acc = genesis + .alloc + .get(&"0xaa00000000000000000000000000000000000000".parse::
().unwrap()) + .unwrap(); + assert_eq!(acc.balance, U256::from(1)); + // assert that the cancun time was picked up + assert_eq!(genesis.config.cancun_time, Some(4661)); + } + + #[test] + fn test_parse_cancun_genesis_all_formats() { + let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0,"cancunTime":4661},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x3b9aca00"}"#; + let genesis: AllGenesisFormats = serde_json::from_str(s).unwrap(); + + // this should be the genesis format + let genesis = match genesis { + AllGenesisFormats::Geth(genesis) => genesis, + _ => panic!("expected geth genesis format"), + }; + + // assert that the alloc was picked up + let acc = genesis + .alloc + .get(&"0xaa00000000000000000000000000000000000000".parse::
().unwrap()) + .unwrap(); + assert_eq!(acc.balance, U256::from(1)); + // assert that the cancun time was picked up + assert_eq!(genesis.config.cancun_time, Some(4661)); + } + + #[test] + fn test_default_cancun_header_forkhash() { + // set the gas limit from the hive test genesis according to the hash + let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() }; + let default_chainspec = ChainSpecBuilder::default() + .chain(Chain::Id(1337)) + .genesis(genesis) + .cancun_activated() + .build(); + let mut header = default_chainspec.genesis_header(); + + // set the state root to the same as in the hive test the hash was pulled from + header.state_root = + B256::from_str("0x62e2595e017f0ca23e08d17221010721a71c3ae932f4ea3cb12117786bb392d4") + .unwrap(); + + // shanghai is activated so we should have a withdrawals root + assert_eq!(header.withdrawals_root, Some(EMPTY_WITHDRAWALS)); + + // cancun is activated so we should have a zero parent beacon block root, zero blob gas + // used, and zero excess blob gas + assert_eq!(header.parent_beacon_block_root, Some(B256::ZERO)); + assert_eq!(header.blob_gas_used, Some(0)); + assert_eq!(header.excess_blob_gas, Some(0)); + println!("header: {:?}", header); + + // check the genesis hash + let genesis_hash = header.hash_slow(); + let expected_hash = + b256!("16bb7c59613a5bad3f7c04a852fd056545ade2483968d9a25a1abb05af0c4d37"); + assert_eq!(genesis_hash, expected_hash); + + // check that the forkhash is correct + let expected_forkhash = ForkHash(hex!("8062457a")); + assert_eq!(ForkHash::from(genesis_hash), expected_forkhash); + } + + #[test] + fn holesky_paris_activated_at_genesis() { + assert!(HOLESKY + .fork(Hardfork::Paris) + .active_at_ttd(HOLESKY.genesis.difficulty, HOLESKY.genesis.difficulty)); + } + + #[test] + fn test_all_genesis_formats_deserialization() { + // custom genesis with chain config + let config = ChainConfig { + chain_id: 2600, + homestead_block: Some(0), + eip150_block: Some(0), + eip155_block: Some(0), + eip158_block: Some(0), + byzantium_block: Some(0), + constantinople_block: Some(0), + petersburg_block: Some(0), + istanbul_block: Some(0), + berlin_block: Some(0), + london_block: Some(0), + shanghai_time: Some(0), + terminal_total_difficulty: Some(U256::ZERO), + terminal_total_difficulty_passed: true, + ..Default::default() + }; + // genesis + let genesis = Genesis { + config, + nonce: 0, + timestamp: 1698688670, + gas_limit: 5000, + difficulty: U256::ZERO, + mix_hash: B256::ZERO, + coinbase: Address::ZERO, + ..Default::default() + }; + + // seed accounts after genesis struct created + let address = hex!("6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b").into(); + let account = GenesisAccount::default().with_balance(U256::from(33)); + let genesis = genesis.extend_accounts(HashMap::from([(address, account)])); + + // ensure genesis is deserialized correctly + let serialized_genesis = serde_json::to_string(&genesis).unwrap(); + let deserialized_genesis: AllGenesisFormats = + serde_json::from_str(&serialized_genesis).unwrap(); + assert!(matches!(deserialized_genesis, AllGenesisFormats::Geth(_))); + + // build chain + let chain_spec = ChainSpecBuilder::default() + .chain(2600.into()) + .genesis(genesis) + .cancun_activated() + .build(); + + // ensure chain spec is deserialized correctly + let serialized_chain_spec = serde_json::to_string(&chain_spec).unwrap(); + let deserialized_chain_spec: AllGenesisFormats = + serde_json::from_str(&serialized_chain_spec).unwrap(); + assert!(matches!(deserialized_chain_spec, AllGenesisFormats::Reth(_))) + } +} diff --git a/crates/primitives/src/forkid.rs b/crates/ethereum-forks/src/forkid.rs similarity index 99% rename from crates/primitives/src/forkid.rs rename to crates/ethereum-forks/src/forkid.rs index 89ef34a52bdb..e1d76974aac3 100644 --- a/crates/primitives/src/forkid.rs +++ b/crates/ethereum-forks/src/forkid.rs @@ -4,10 +4,11 @@ #![deny(missing_docs)] -use crate::{hex, BlockNumber, Head, B256}; +use crate::{hex, BlockNumber, B256}; use alloy_rlp::*; use crc::*; use reth_codecs::derive_arbitrary; +use reth_primitives::Head; use serde::{Deserialize, Serialize}; use std::{ cmp::Ordering, @@ -379,7 +380,8 @@ impl Cache { #[cfg(test)] mod tests { use super::*; - use crate::{hex_literal::hex, revm_primitives::b256}; + use crate::hex_literal::hex; + use reth_primitives::revm_primitives::b256; const GENESIS_HASH: B256 = b256!("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"); diff --git a/crates/primitives/src/hardfork.rs b/crates/ethereum-forks/src/hardfork.rs similarity index 99% rename from crates/primitives/src/hardfork.rs rename to crates/ethereum-forks/src/hardfork.rs index e1e29fc3673c..0c5f269b67ef 100644 --- a/crates/primitives/src/hardfork.rs +++ b/crates/ethereum-forks/src/hardfork.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ChainSpec, ForkCondition, ForkFilter, ForkId}; + use std::{fmt::Display, str::FromStr}; /// The name of an Ethereum hardfork. @@ -110,7 +111,8 @@ impl Display for Hardfork { #[cfg(test)] mod tests { use super::*; - use crate::{Chain, Genesis}; + use crate::Chain; + use reth_primitives::Genesis; use std::collections::BTreeMap; #[test] diff --git a/crates/ethereum-forks/src/lib.rs b/crates/ethereum-forks/src/lib.rs new file mode 100644 index 000000000000..3cfb7f4011de --- /dev/null +++ b/crates/ethereum-forks/src/lib.rs @@ -0,0 +1,64 @@ +//! This crate contains ethereum fork types and helper functions. +//! +//! ## Feature Flags +//! +//! - `arbitrary`: Adds `proptest` and `arbitrary` support for ethereum fork types. +//! - `test-utils`: Export utilities for testing + +#![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)] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![allow(clippy::non_canonical_clone_impl)] + +mod chain; +mod forkid; +mod hardfork; +pub mod proofs; +mod revm; + +pub use crate::revm::{config, env}; +pub use chain::{ + AllGenesisFormats, Chain, ChainInfo, ChainSpec, ChainSpecBuilder, DisplayHardforks, + ForkCondition, ForkTimestamps, NamedChain, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, +}; +pub use forkid::{ForkFilter, ForkHash, ForkId, ForkTransition, ValidationError}; +pub use hardfork::Hardfork; + +#[cfg(feature = "optimism")] +pub use chain::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI}; + +// Re-exports +pub use self::ruint::UintTryTo; +pub use alloy_primitives::{ + self, address, b256, bloom, bytes, eip191_hash_message, hex, hex_literal, keccak256, ruint, + Address, BlockHash, BlockNumber, Bloom, BloomInput, Bytes, ChainId, Selector, StorageKey, + StorageValue, TxHash, TxIndex, TxNumber, B128, B256, B512, B64, U128, U256, U64, U8, +}; +pub use reth_primitives::BaseFeeParams; + +#[doc(hidden)] +#[deprecated = "use B64 instead"] +pub type H64 = B64; +#[doc(hidden)] +#[deprecated = "use B128 instead"] +pub type H128 = B128; +#[doc(hidden)] +#[deprecated = "use Address instead"] +pub type H160 = Address; +#[doc(hidden)] +#[deprecated = "use B256 instead"] +pub type H256 = B256; +#[doc(hidden)] +#[deprecated = "use B512 instead"] +pub type H512 = B512; + +#[cfg(any(test, feature = "arbitrary"))] +pub use arbitrary; + +#[cfg(feature = "c-kzg")] +pub use c_kzg as kzg; diff --git a/crates/ethereum-forks/src/proofs.rs b/crates/ethereum-forks/src/proofs.rs new file mode 100644 index 000000000000..6b3fdfcd0706 --- /dev/null +++ b/crates/ethereum-forks/src/proofs.rs @@ -0,0 +1,71 @@ +//! Helper function for calculating Merkle proofs and hashes. + +use alloy_rlp::Encodable; +use itertools::Itertools; +use reth_primitives::{ + keccak256, + trie::{HashBuilder, Nibbles}, + Address, GenesisAccount, B256, +}; +use std::collections::HashMap; + +/// Calculates the root hash for the state, this corresponds to [geth's +/// `deriveHash`](https://github.com/ethereum/go-ethereum/blob/6c149fd4ad063f7c24d726a73bc0546badd1bc73/core/genesis.go#L119). +pub fn genesis_state_root(genesis_alloc: &HashMap) -> B256 { + let accounts_with_sorted_hashed_keys = genesis_alloc + .iter() + .map(|(address, account)| (keccak256(address), account)) + .sorted_by_key(|(key, _)| *key); + + let mut hb = HashBuilder::default(); + let mut account_rlp_buf = Vec::new(); + for (hashed_key, account) in accounts_with_sorted_hashed_keys { + account_rlp_buf.clear(); + account.encode(&mut account_rlp_buf); + hb.add_leaf(Nibbles::unpack(hashed_key), &account_rlp_buf); + } + + hb.root() +} + +#[cfg(test)] +mod tests { + use crate::{GOERLI, HOLESKY, MAINNET, SEPOLIA}; + use alloy_primitives::b256; + use reth_primitives::proofs::genesis_state_root; + + #[test] + fn test_chain_state_roots() { + let expected_mainnet_state_root = + b256!("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"); + let calculated_mainnet_state_root = genesis_state_root(&MAINNET.genesis.alloc); + assert_eq!( + expected_mainnet_state_root, calculated_mainnet_state_root, + "mainnet state root mismatch" + ); + + let expected_goerli_state_root = + b256!("5d6cded585e73c4e322c30c2f782a336316f17dd85a4863b9d838d2d4b8b3008"); + let calculated_goerli_state_root = genesis_state_root(&GOERLI.genesis.alloc); + assert_eq!( + expected_goerli_state_root, calculated_goerli_state_root, + "goerli state root mismatch" + ); + + let expected_sepolia_state_root = + b256!("5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494"); + let calculated_sepolia_state_root = genesis_state_root(&SEPOLIA.genesis.alloc); + assert_eq!( + expected_sepolia_state_root, calculated_sepolia_state_root, + "sepolia state root mismatch" + ); + + let expected_holesky_state_root = + b256!("69d8c9d72f6fa4ad42d4702b433707212f90db395eb54dc20bc85de253788783"); + let calculated_holesky_state_root = genesis_state_root(&HOLESKY.genesis.alloc); + assert_eq!( + expected_holesky_state_root, calculated_holesky_state_root, + "holesky state root mismatch" + ); + } +} diff --git a/crates/primitives/src/revm/config.rs b/crates/ethereum-forks/src/revm/config.rs similarity index 97% rename from crates/primitives/src/revm/config.rs rename to crates/ethereum-forks/src/revm/config.rs index 33848a844298..298a741e8aa6 100644 --- a/crates/primitives/src/revm/config.rs +++ b/crates/ethereum-forks/src/revm/config.rs @@ -1,4 +1,5 @@ -use crate::{revm_primitives, ChainSpec, Hardfork, Head}; +use crate::{ChainSpec, Hardfork}; +use reth_primitives::{revm_primitives, Head}; /// Returns the spec id at the given timestamp. /// @@ -72,7 +73,8 @@ pub fn revm_spec(chain_spec: &ChainSpec, block: Head) -> revm_primitives::SpecId #[cfg(test)] mod tests { use super::*; - use crate::{ChainSpecBuilder, Head, MAINNET, U256}; + use crate::{ChainSpecBuilder, MAINNET, U256}; + use reth_primitives::Head; #[test] fn test_to_revm_spec() { diff --git a/crates/primitives/src/revm/env.rs b/crates/ethereum-forks/src/revm/env.rs similarity index 98% rename from crates/primitives/src/revm/env.rs rename to crates/ethereum-forks/src/revm/env.rs index 26b87a159b40..65cc55a1ce91 100644 --- a/crates/primitives/src/revm/env.rs +++ b/crates/ethereum-forks/src/revm/env.rs @@ -1,10 +1,10 @@ -use crate::{ +use crate::{revm::config::revm_spec, Chain, ChainSpec}; +use reth_primitives::{ constants::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS}, recover_signer, - revm::config::revm_spec, revm_primitives::{AnalysisKind, BlockEnv, CfgEnv, Env, SpecId, TransactTo, TxEnv}, - Address, Bytes, Chain, ChainSpec, Head, Header, Transaction, TransactionKind, - TransactionSignedEcRecovered, B256, U256, + Address, Bytes, Head, Header, Transaction, TransactionKind, TransactionSignedEcRecovered, B256, + U256, }; #[cfg(feature = "optimism")] diff --git a/crates/ethereum-forks/src/revm/mod.rs b/crates/ethereum-forks/src/revm/mod.rs new file mode 100644 index 000000000000..90d046d172b1 --- /dev/null +++ b/crates/ethereum-forks/src/revm/mod.rs @@ -0,0 +1,8 @@ +/// Reth block execution/validation configuration and constants +pub mod config; +/// The `env` module provides essential utilities for managing Ethereum transaction and block +/// environments. +/// +/// It includes functions to fill transaction and block environments with relevant data, handle +/// system contract calls, and recover the signer of Ethereum headers. +pub mod env; diff --git a/crates/interfaces/Cargo.toml b/crates/interfaces/Cargo.toml index 3071fc5926e6..40e3d69579b0 100644 --- a/crates/interfaces/Cargo.toml +++ b/crates/interfaces/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true reth-codecs.workspace = true reth-nippy-jar.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-rpc-types.workspace = true reth-network-api.workspace = true # TODO(onbjerg): We only need this for [BlockBody] diff --git a/crates/net/discv4/Cargo.toml b/crates/net/discv4/Cargo.toml index 274a32893ea4..1e327ac95445 100644 --- a/crates/net/discv4/Cargo.toml +++ b/crates/net/discv4/Cargo.toml @@ -11,6 +11,7 @@ description = "Ethereum network discovery" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-net-common.workspace = true reth-net-nat.workspace = true diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index 734bb2433a60..b5f7074b7341 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -41,9 +41,10 @@ use discv5::{ use enr::{Enr, EnrBuilder}; use parking_lot::Mutex; use proto::{EnrRequest, EnrResponse, EnrWrapper}; +use reth_ethereum_forks::ForkId; use reth_primitives::{ bytes::{Bytes, BytesMut}, - hex, ForkId, PeerId, B256, + hex, PeerId, B256, }; use secp256k1::SecretKey; use std::{ @@ -2128,7 +2129,8 @@ mod tests { use crate::test_utils::{create_discv4, create_discv4_with_config, rng_endpoint, rng_record}; use alloy_rlp::{Decodable, Encodable}; use rand::{thread_rng, Rng}; - use reth_primitives::{hex, mainnet_nodes, ForkHash}; + use reth_ethereum_forks::ForkHash; + use reth_primitives::{hex, mainnet_nodes}; use std::{future::poll_fn, net::Ipv4Addr}; #[tokio::test] diff --git a/crates/net/discv4/src/proto.rs b/crates/net/discv4/src/proto.rs index 0c4816d259db..19f034848811 100644 --- a/crates/net/discv4/src/proto.rs +++ b/crates/net/discv4/src/proto.rs @@ -7,9 +7,10 @@ use alloy_rlp::{ length_of_length, Decodable, Encodable, Error as RlpError, Header, RlpDecodable, RlpEncodable, }; use enr::{Enr, EnrKey}; +use reth_ethereum_forks::ForkId; use reth_primitives::{ bytes::{Buf, BufMut, Bytes, BytesMut}, - keccak256, ForkId, NodeRecord, B256, + keccak256, NodeRecord, B256, }; use secp256k1::{ ecdsa::{RecoverableSignature, RecoveryId}, @@ -509,7 +510,8 @@ mod tests { }; use enr::{EnrBuilder, EnrPublicKey}; use rand::{thread_rng, Rng, RngCore}; - use reth_primitives::{hex, ForkHash}; + use reth_ethereum_forks::ForkHash; + use reth_primitives::hex; #[test] fn test_endpoint_ipv_v4() { diff --git a/crates/net/discv4/src/test_utils.rs b/crates/net/discv4/src/test_utils.rs index 893b8abfec4c..23e21c084930 100644 --- a/crates/net/discv4/src/test_utils.rs +++ b/crates/net/discv4/src/test_utils.rs @@ -8,7 +8,8 @@ use crate::{ IngressReceiver, PeerId, SAFE_MAX_DATAGRAM_NEIGHBOUR_RECORDS, }; use rand::{thread_rng, Rng, RngCore}; -use reth_primitives::{hex, ForkHash, ForkId, NodeRecord, B256}; +use reth_ethereum_forks::{ForkHash, ForkId}; +use reth_primitives::{hex, NodeRecord, B256}; use secp256k1::{SecretKey, SECP256K1}; use std::{ collections::{HashMap, HashSet}, @@ -283,7 +284,8 @@ pub fn rng_message(rng: &mut impl RngCore) -> Message { mod tests { use super::*; use crate::{Discv4Event, PingReason}; - use reth_primitives::{hex_literal::hex, ForkHash, ForkId}; + use reth_ethereum_forks::{ForkHash, ForkId}; + use reth_primitives::hex_literal::hex; use std::net::{IpAddr, Ipv4Addr}; /// This test creates two local UDP sockets. The mocked discovery service responds to specific diff --git a/crates/net/dns/Cargo.toml b/crates/net/dns/Cargo.toml index 02dd202d7bb5..d035717f0abb 100644 --- a/crates/net/dns/Cargo.toml +++ b/crates/net/dns/Cargo.toml @@ -11,6 +11,7 @@ description = "Support for EIP-1459 Node Discovery via DNS" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-net-common.workspace = true # ethereum diff --git a/crates/net/dns/src/lib.rs b/crates/net/dns/src/lib.rs index cc33e4d9f769..9561ed5bbe8d 100644 --- a/crates/net/dns/src/lib.rs +++ b/crates/net/dns/src/lib.rs @@ -23,7 +23,8 @@ use crate::{ pub use config::DnsDiscoveryConfig; use enr::Enr; use error::ParseDnsEntryError; -use reth_primitives::{ForkId, NodeRecord, PeerId}; +use reth_ethereum_forks::ForkId; +use reth_primitives::{NodeRecord, PeerId}; use schnellru::{ByLength, LruMap}; use secp256k1::SecretKey; use std::{ @@ -413,7 +414,7 @@ mod tests { use crate::tree::TreeRootEntry; use alloy_rlp::Encodable; use enr::{EnrBuilder, EnrKey}; - use reth_primitives::{Chain, Hardfork, MAINNET}; + use reth_ethereum_forks::{Chain, Hardfork, MAINNET}; use secp256k1::rand::thread_rng; use std::{future::poll_fn, net::Ipv4Addr}; use tokio_stream::StreamExt; diff --git a/crates/net/downloaders/Cargo.toml b/crates/net/downloaders/Cargo.toml index 3a50908b13d2..349aaf132647 100644 --- a/crates/net/downloaders/Cargo.toml +++ b/crates/net/downloaders/Cargo.toml @@ -12,6 +12,7 @@ description = "Implementations of various block downloaders" # reth reth-interfaces.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-tasks.workspace = true reth-provider.workspace = true diff --git a/crates/net/downloaders/src/bodies/bodies.rs b/crates/net/downloaders/src/bodies/bodies.rs index b601865d3a6c..fb59100aaa6a 100644 --- a/crates/net/downloaders/src/bodies/bodies.rs +++ b/crates/net/downloaders/src/bodies/bodies.rs @@ -582,8 +582,9 @@ mod tests { use assert_matches::assert_matches; use futures_util::stream::StreamExt; use reth_db::test_utils::create_test_rw_db; + use reth_ethereum_forks::MAINNET; use reth_interfaces::test_utils::{generators, generators::random_block_range, TestConsensus}; - use reth_primitives::{BlockBody, B256, MAINNET}; + use reth_primitives::{BlockBody, B256}; use reth_provider::ProviderFactory; use std::{collections::HashMap, sync::Arc}; diff --git a/crates/net/downloaders/src/bodies/task.rs b/crates/net/downloaders/src/bodies/task.rs index 9a713a8539bc..09c0741d7084 100644 --- a/crates/net/downloaders/src/bodies/task.rs +++ b/crates/net/downloaders/src/bodies/task.rs @@ -170,8 +170,8 @@ mod tests { }; use assert_matches::assert_matches; use reth_db::test_utils::create_test_rw_db; + use reth_ethereum_forks::MAINNET; use reth_interfaces::{p2p::error::DownloadError, test_utils::TestConsensus}; - use reth_primitives::MAINNET; use reth_provider::ProviderFactory; use std::sync::Arc; diff --git a/crates/net/downloaders/src/test_utils/file_client.rs b/crates/net/downloaders/src/test_utils/file_client.rs index 69320fe4b171..63c0b18e363e 100644 --- a/crates/net/downloaders/src/test_utils/file_client.rs +++ b/crates/net/downloaders/src/test_utils/file_client.rs @@ -260,6 +260,7 @@ mod tests { use futures::SinkExt; use futures_util::stream::StreamExt; use reth_db::test_utils::create_test_rw_db; + use reth_ethereum_forks::MAINNET; use reth_interfaces::{ p2p::{ bodies::downloader::BodyDownloader, @@ -267,7 +268,7 @@ mod tests { }, test_utils::TestConsensus, }; - use reth_primitives::{SealedHeader, MAINNET}; + use reth_primitives::SealedHeader; use reth_provider::ProviderFactory; use std::{ io::{Read, Seek, SeekFrom, Write}, diff --git a/crates/net/eth-wire/Cargo.toml b/crates/net/eth-wire/Cargo.toml index a5f691ef383f..b6bb1d9d3f56 100644 --- a/crates/net/eth-wire/Cargo.toml +++ b/crates/net/eth-wire/Cargo.toml @@ -12,6 +12,7 @@ repository.workspace = true # reth reth-codecs.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-ecies.workspace = true alloy-rlp = { workspace = true, features = ["derive"] } reth-discv4.workspace = true @@ -39,6 +40,7 @@ proptest-derive = { workspace = true, optional = true } [dev-dependencies] reth-primitives = { workspace = true, features = ["arbitrary"] } +reth-ethereum-forks = { workspace = true, features = ["arbitrary"] } reth-tracing.workspace = true ethers-core = { workspace = true, default-features = false } @@ -54,8 +56,8 @@ proptest-derive.workspace = true [features] default = ["serde"] serde = ["dep:serde"] -arbitrary = ["reth-primitives/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] -optimism = ["reth-primitives/optimism"] +arbitrary = ["reth-primitives/arbitrary", "reth-ethereum-forks/arbitrary", "dep:arbitrary", "dep:proptest", "dep:proptest-derive"] +optimism = ["reth-primitives/optimism", "reth-ethereum-forks/optimism"] [[test]] name = "fuzz_roundtrip" diff --git a/crates/net/eth-wire/src/builder.rs b/crates/net/eth-wire/src/builder.rs index 0d360a4dd558..fbc2c7e1c736 100644 --- a/crates/net/eth-wire/src/builder.rs +++ b/crates/net/eth-wire/src/builder.rs @@ -1,14 +1,16 @@ //! Builder structs for messages. use crate::Status; -use reth_primitives::{Chain, ForkId, B256, U256}; +use reth_ethereum_forks::{Chain, ForkId}; +use reth_primitives::{B256, U256}; /// Builder for [`Status`] messages. /// /// # Example /// ``` /// use reth_eth_wire::{types::Status, EthVersion}; -/// use reth_primitives::{Chain, Hardfork, B256, MAINNET, MAINNET_GENESIS_HASH, U256}; +/// use reth_ethereum_forks::{Chain, Hardfork, MAINNET}; +/// use reth_primitives::{B256, MAINNET_GENESIS_HASH, U256}; /// /// // this is just an example status message! /// let status = Status::builder() diff --git a/crates/net/eth-wire/src/errors/eth.rs b/crates/net/eth-wire/src/errors/eth.rs index 21645def4a62..7a28b1990b49 100644 --- a/crates/net/eth-wire/src/errors/eth.rs +++ b/crates/net/eth-wire/src/errors/eth.rs @@ -2,7 +2,8 @@ use crate::{ errors::P2PStreamError, version::ParseVersionError, DisconnectReason, EthMessageID, EthVersion, }; -use reth_primitives::{Chain, GotExpected, GotExpectedBoxed, ValidationError, B256}; +use reth_ethereum_forks::{Chain, ValidationError}; +use reth_primitives::{GotExpected, GotExpectedBoxed, B256}; use std::io; /// Errors when sending/receiving messages diff --git a/crates/net/eth-wire/src/ethstream.rs b/crates/net/eth-wire/src/ethstream.rs index 23f64040a438..2854e4012304 100644 --- a/crates/net/eth-wire/src/ethstream.rs +++ b/crates/net/eth-wire/src/ethstream.rs @@ -7,9 +7,10 @@ use crate::{ use alloy_rlp::Encodable; use futures::{ready, Sink, SinkExt, StreamExt}; use pin_project::pin_project; +use reth_ethereum_forks::ForkFilter; use reth_primitives::{ bytes::{Bytes, BytesMut}, - ForkFilter, GotExpected, + GotExpected, }; use std::{ pin::Pin, @@ -332,7 +333,8 @@ mod tests { use futures::{SinkExt, StreamExt}; use reth_discv4::DEFAULT_DISCOVERY_PORT; use reth_ecies::{stream::ECIESStream, util::pk2id}; - use reth_primitives::{ForkFilter, Head, NamedChain, B256, U256}; + use reth_ethereum_forks::{ForkFilter, NamedChain}; + use reth_primitives::{Head, B256, U256}; use secp256k1::{SecretKey, SECP256K1}; use tokio::net::{TcpListener, TcpStream}; use tokio_util::codec::Decoder; diff --git a/crates/net/eth-wire/src/test_utils.rs b/crates/net/eth-wire/src/test_utils.rs index 01bd9a048dc3..426d12fa5249 100644 --- a/crates/net/eth-wire/src/test_utils.rs +++ b/crates/net/eth-wire/src/test_utils.rs @@ -5,7 +5,8 @@ use crate::{ }; use reth_discv4::DEFAULT_DISCOVERY_PORT; use reth_ecies::util::pk2id; -use reth_primitives::{Chain, ForkFilter, Head, B256, U256}; +use reth_ethereum_forks::{Chain, ForkFilter}; +use reth_primitives::{Head, B256, U256}; use secp256k1::{SecretKey, SECP256K1}; use std::net::SocketAddr; use tokio::net::TcpStream; diff --git a/crates/net/eth-wire/src/types/status.rs b/crates/net/eth-wire/src/types/status.rs index c112fe58ccb5..0c0b729a0cae 100644 --- a/crates/net/eth-wire/src/types/status.rs +++ b/crates/net/eth-wire/src/types/status.rs @@ -1,9 +1,8 @@ use crate::{EthVersion, StatusBuilder}; use alloy_rlp::{RlpDecodable, RlpEncodable}; use reth_codecs::derive_arbitrary; -use reth_primitives::{ - hex, Chain, ChainSpec, ForkId, Genesis, Hardfork, Head, NamedChain, B256, MAINNET, U256, -}; +use reth_ethereum_forks::{Chain, ChainSpec, ForkId, Hardfork, NamedChain, MAINNET}; +use reth_primitives::{hex, Genesis, Head, B256, U256}; use std::fmt::{Debug, Display}; #[cfg(feature = "serde")] @@ -149,10 +148,10 @@ mod tests { use crate::types::{EthVersion, Status}; use alloy_rlp::{Decodable, Encodable}; use rand::Rng; - use reth_primitives::{ - hex, Chain, ChainSpec, ForkCondition, ForkHash, ForkId, Genesis, Hardfork, Head, - NamedChain, B256, U256, + use reth_ethereum_forks::{ + Chain, ChainSpec, ForkCondition, ForkHash, ForkId, Hardfork, NamedChain, }; + use reth_primitives::{hex, Genesis, Head, B256, U256}; use std::str::FromStr; #[test] diff --git a/crates/net/network-api/Cargo.toml b/crates/net/network-api/Cargo.toml index 51bd3a58a21a..8fe012af5987 100644 --- a/crates/net/network-api/Cargo.toml +++ b/crates/net/network-api/Cargo.toml @@ -11,6 +11,7 @@ description = "Network interfaces" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-eth-wire.workspace = true reth-rpc-types.workspace = true reth-discv4.workspace = true diff --git a/crates/net/network-api/src/noop.rs b/crates/net/network-api/src/noop.rs index fee69c3ab635..b1a3ce7e610b 100644 --- a/crates/net/network-api/src/noop.rs +++ b/crates/net/network-api/src/noop.rs @@ -10,7 +10,8 @@ use crate::{ use async_trait::async_trait; use reth_discv4::DEFAULT_DISCOVERY_PORT; use reth_eth_wire::{DisconnectReason, ProtocolVersion}; -use reth_primitives::{Chain, NodeRecord, PeerId}; +use reth_ethereum_forks::Chain; +use reth_primitives::{NodeRecord, PeerId}; use reth_rpc_types::{EthProtocolInfo, NetworkStatus}; use std::net::{IpAddr, SocketAddr}; diff --git a/crates/net/network/Cargo.toml b/crates/net/network/Cargo.toml index b63b6637f718..79e2002c7a5d 100644 --- a/crates/net/network/Cargo.toml +++ b/crates/net/network/Cargo.toml @@ -18,6 +18,7 @@ normal = [ # reth reth-interfaces.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-net-common.workspace = true reth-network-api.workspace = true reth-discv4.workspace = true @@ -69,6 +70,7 @@ tempfile = { workspace = true, optional = true } reth-discv4 = { workspace = true, features = ["test-utils"] } reth-interfaces = { workspace = true, features = ["test-utils"] } reth-primitives = { workspace = true, features = ["test-utils"] } +reth-ethereum-forks = { workspace = true, features = ["test-utils"] } # we need to enable the test-utils feature in our own crate to use utils in # integration tests @@ -96,6 +98,7 @@ test-utils = ["reth-provider/test-utils", "dep:enr", "dep:tempfile", "reth-trans geth-tests = [] optimism = [ "reth-primitives/optimism", + "reth-ethereum-forks/optimism", "reth-transaction-pool/optimism", "reth-provider/optimism", "reth-network-api/optimism", diff --git a/crates/net/network/src/config.rs b/crates/net/network/src/config.rs index f97f4004f949..846e246ad3d3 100644 --- a/crates/net/network/src/config.rs +++ b/crates/net/network/src/config.rs @@ -11,9 +11,8 @@ use reth_discv4::{Discv4Config, Discv4ConfigBuilder, DEFAULT_DISCOVERY_ADDRESS}; use reth_dns_discovery::DnsDiscoveryConfig; use reth_ecies::util::pk2id; use reth_eth_wire::{HelloMessage, HelloMessageWithProtocols, Status}; -use reth_primitives::{ - mainnet_nodes, sepolia_nodes, ChainSpec, ForkFilter, Head, NodeRecord, PeerId, MAINNET, -}; +use reth_ethereum_forks::{ChainSpec, ForkFilter, MAINNET}; +use reth_primitives::{mainnet_nodes, sepolia_nodes, Head, NodeRecord, PeerId}; use reth_provider::{BlockReader, HeaderProvider}; use reth_tasks::{TaskSpawner, TokioTaskExecutor}; use secp256k1::SECP256K1; @@ -516,7 +515,7 @@ mod tests { use super::*; use rand::thread_rng; use reth_dns_discovery::tree::LinkEntry; - use reth_primitives::{Chain, ForkHash}; + use reth_ethereum_forks::{Chain, ForkHash}; use reth_provider::test_utils::NoopProvider; use std::collections::BTreeMap; diff --git a/crates/net/network/src/discovery.rs b/crates/net/network/src/discovery.rs index 72583f01bb2d..12cc6fbab77d 100644 --- a/crates/net/network/src/discovery.rs +++ b/crates/net/network/src/discovery.rs @@ -9,7 +9,8 @@ use reth_discv4::{DiscoveryUpdate, Discv4, Discv4Config, EnrForkIdEntry}; use reth_dns_discovery::{ DnsDiscoveryConfig, DnsDiscoveryHandle, DnsDiscoveryService, DnsNodeRecordUpdate, DnsResolver, }; -use reth_primitives::{ForkId, NodeRecord, PeerId}; +use reth_ethereum_forks::ForkId; +use reth_primitives::{NodeRecord, PeerId}; use secp256k1::SecretKey; use std::{ collections::{hash_map::Entry, HashMap, VecDeque}, diff --git a/crates/net/network/src/manager.rs b/crates/net/network/src/manager.rs index bd96ba28072c..305ffb203baa 100644 --- a/crates/net/network/src/manager.rs +++ b/crates/net/network/src/manager.rs @@ -39,10 +39,11 @@ use reth_eth_wire::{ capability::{Capabilities, CapabilityMessage}, DisconnectReason, EthVersion, Status, }; +use reth_ethereum_forks::ForkId; use reth_metrics::common::mpsc::UnboundedMeteredSender; use reth_net_common::bandwidth_meter::BandwidthMeter; use reth_network_api::ReputationChangeKind; -use reth_primitives::{ForkId, NodeRecord, PeerId, B256}; +use reth_primitives::{NodeRecord, PeerId, B256}; use reth_provider::{BlockNumReader, BlockReader}; use reth_rpc_types::{EthProtocolInfo, NetworkStatus}; use reth_tokio_util::EventListeners; diff --git a/crates/net/network/src/peers/manager.rs b/crates/net/network/src/peers/manager.rs index a74db69c1f04..032697df57b9 100644 --- a/crates/net/network/src/peers/manager.rs +++ b/crates/net/network/src/peers/manager.rs @@ -9,9 +9,10 @@ use crate::{ }; use futures::StreamExt; use reth_eth_wire::{errors::EthStreamError, DisconnectReason}; +use reth_ethereum_forks::ForkId; use reth_net_common::ban_list::BanList; use reth_network_api::{PeerKind, ReputationChangeKind}; -use reth_primitives::{ForkId, NodeRecord, PeerId}; +use reth_primitives::{NodeRecord, PeerId}; use std::{ collections::{hash_map::Entry, HashMap, HashSet, VecDeque}, fmt::Display, diff --git a/crates/net/network/src/session/active.rs b/crates/net/network/src/session/active.rs index a7a731a128bc..45038877513c 100644 --- a/crates/net/network/src/session/active.rs +++ b/crates/net/network/src/session/active.rs @@ -776,8 +776,9 @@ mod tests { GetBlockBodies, HelloMessageWithProtocols, Status, StatusBuilder, UnauthedEthStream, UnauthedP2PStream, }; + use reth_ethereum_forks::{Hardfork, MAINNET}; use reth_net_common::bandwidth_meter::BandwidthMeter; - use reth_primitives::{ForkFilter, Hardfork, MAINNET}; + use reth_primitives::ForkFilter; use secp256k1::{SecretKey, SECP256K1}; use std::time::Duration; use tokio::{net::TcpListener, sync::mpsc}; diff --git a/crates/net/network/src/session/mod.rs b/crates/net/network/src/session/mod.rs index 863964ac97ed..c87bbc82490c 100644 --- a/crates/net/network/src/session/mod.rs +++ b/crates/net/network/src/session/mod.rs @@ -13,12 +13,13 @@ use reth_eth_wire::{ DisconnectReason, EthVersion, HelloMessageWithProtocols, Status, UnauthedEthStream, UnauthedP2PStream, }; +use reth_ethereum_forks::{ForkFilter, ForkId, ForkTransition}; use reth_metrics::common::mpsc::MeteredPollSender; use reth_net_common::{ bandwidth_meter::{BandwidthMeter, MeteredStream}, stream::HasRemoteAddr, }; -use reth_primitives::{ForkFilter, ForkId, ForkTransition, Head, PeerId}; +use reth_primitives::{Head, PeerId}; use reth_tasks::TaskSpawner; use secp256k1::SecretKey; use std::{ diff --git a/crates/net/network/src/state.rs b/crates/net/network/src/state.rs index 63351f5f9967..646e488e8629 100644 --- a/crates/net/network/src/state.rs +++ b/crates/net/network/src/state.rs @@ -15,8 +15,9 @@ use crate::{ use reth_eth_wire::{ capability::Capabilities, BlockHashNumber, DisconnectReason, NewBlockHashes, Status, }; +use reth_ethereum_forks::ForkId; use reth_network_api::PeerKind; -use reth_primitives::{ForkId, PeerId, B256}; +use reth_primitives::{PeerId, B256}; use reth_provider::BlockNumReader; use std::{ collections::{HashMap, VecDeque}, diff --git a/crates/net/network/src/test_utils/testnet.rs b/crates/net/network/src/test_utils/testnet.rs index 7ad4ae867e22..8d4fc2e32066 100644 --- a/crates/net/network/src/test_utils/testnet.rs +++ b/crates/net/network/src/test_utils/testnet.rs @@ -11,8 +11,9 @@ use crate::{ use futures::{FutureExt, StreamExt}; use pin_project::pin_project; use reth_eth_wire::{protocol::Protocol, DisconnectReason, HelloMessageWithProtocols}; +use reth_ethereum_forks::MAINNET; use reth_network_api::{NetworkInfo, Peers}; -use reth_primitives::{PeerId, MAINNET}; +use reth_primitives::PeerId; use reth_provider::{ test_utils::NoopProvider, BlockReader, BlockReaderIdExt, HeaderProvider, StateProviderFactory, }; diff --git a/crates/net/network/tests/it/geth.rs b/crates/net/network/tests/it/geth.rs index 473b1bef49f4..18cccee26e36 100644 --- a/crates/net/network/tests/it/geth.rs +++ b/crates/net/network/tests/it/geth.rs @@ -4,12 +4,13 @@ use ethers_core::{ utils::Geth, }; use ethers_providers::Middleware; +use reth_ethereum_forks::ChainSpec; use reth_network::{ test_utils::{unused_tcp_and_udp_port, unused_tcp_udp, NetworkEventStream}, NetworkConfig, NetworkEvents, NetworkManager, }; use reth_network_api::Peers; -use reth_primitives::{ChainSpec, Genesis, PeerId, SealedHeader}; +use reth_primitives::{Genesis, PeerId, SealedHeader}; use reth_provider::test_utils::NoopProvider; use secp256k1::SecretKey; use std::{net::SocketAddr, sync::Arc}; diff --git a/crates/payload/basic/Cargo.toml b/crates/payload/basic/Cargo.toml index 6d06aa4b032f..0f292a56b041 100644 --- a/crates/payload/basic/Cargo.toml +++ b/crates/payload/basic/Cargo.toml @@ -11,6 +11,7 @@ description = "A basic payload builder for reth that uses the txpool API to buil [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-revm.workspace = true reth-transaction-pool.workspace = true reth-provider.workspace = true @@ -37,6 +38,7 @@ tracing.workspace = true [features] optimism = [ "reth-primitives/optimism", + "reth-ethereum-forks/optimism", "reth-revm/optimism", "reth-transaction-pool/optimism", "reth-provider/optimism", diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 0bc30049f5ab..5c0a813ae88b 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -13,6 +13,7 @@ use crate::metrics::PayloadBuilderMetrics; use alloy_rlp::Encodable; use futures_core::ready; use futures_util::FutureExt; +use reth_ethereum_forks::{env::tx_env_with_recovered, ChainSpec}; use reth_interfaces::{RethError, RethResult}; use reth_payload_builder::{ database::CachedReads, error::PayloadBuilderError, BuiltPayload, KeepPayloadJobAlive, @@ -26,8 +27,8 @@ use reth_primitives::{ }, eip4844::calculate_excess_blob_gas, proofs, - revm::{compat::into_reth_log, env::tx_env_with_recovered}, - Block, BlockNumberOrTag, Bytes, ChainSpec, Header, IntoRecoveredTransaction, Receipt, Receipts, + revm::compat::into_reth_log, + Block, BlockNumberOrTag, Bytes, Header, IntoRecoveredTransaction, Receipt, Receipts, SealedBlock, Withdrawal, B256, EMPTY_OMMER_ROOT_HASH, U256, }; use reth_provider::{BlockReaderIdExt, BlockSource, BundleStateWithReceipts, StateProviderFactory}; diff --git a/crates/payload/basic/src/optimism.rs b/crates/payload/basic/src/optimism.rs index 19f62db1dedd..2a695e73c2b6 100644 --- a/crates/payload/basic/src/optimism.rs +++ b/crates/payload/basic/src/optimism.rs @@ -1,8 +1,8 @@ //! Optimism's [PayloadBuilder] implementation. use super::*; +use reth_ethereum_forks::Hardfork; use reth_payload_builder::error::OptimismPayloadBuilderError; -use reth_primitives::Hardfork; /// Constructs an Ethereum transaction payload from the transactions sent through the /// Payload attributes by the sequencer. If the `no_tx_pool` argument is passed in diff --git a/crates/payload/builder/Cargo.toml b/crates/payload/builder/Cargo.toml index 056629ce69dd..d2d80d1fc7dc 100644 --- a/crates/payload/builder/Cargo.toml +++ b/crates/payload/builder/Cargo.toml @@ -11,6 +11,7 @@ description = "reth payload builder" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-rpc-types.workspace = true reth-transaction-pool.workspace = true reth-interfaces.workspace = true @@ -41,6 +42,7 @@ revm.workspace = true test-utils = [] optimism = [ "reth-primitives/optimism", + "reth-ethereum-forks/optimism", "reth-rpc-types/optimism", "reth-rpc-types-compat/optimism", "reth-interfaces/optimism", diff --git a/crates/payload/builder/src/payload.rs b/crates/payload/builder/src/payload.rs index 919c0705afcd..71631465319d 100644 --- a/crates/payload/builder/src/payload.rs +++ b/crates/payload/builder/src/payload.rs @@ -1,10 +1,10 @@ //! Contains types required for building a payload. use alloy_rlp::{Encodable, Error as DecodeError}; +use reth_ethereum_forks::{config::revm_spec_by_timestamp_after_merge, ChainSpec}; use reth_primitives::{ - revm::config::revm_spec_by_timestamp_after_merge, revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, SpecId}, - Address, BlobTransactionSidecar, ChainSpec, Header, SealedBlock, Withdrawal, B256, U256, + Address, BlobTransactionSidecar, Header, SealedBlock, Withdrawal, B256, U256, }; use reth_rpc_types::engine::{ ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadV1, PayloadAttributes, diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 8204636cec5b..c0afefabc046 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -98,4 +98,4 @@ harness = false [[bench]] name = "trie_root" required-features = ["arbitrary", "test-utils"] -harness = false +harness = false \ No newline at end of file diff --git a/crates/primitives/src/chain/mod.rs b/crates/primitives/src/chain/mod.rs index c78d365fde9b..bcd5396cfa66 100644 --- a/crates/primitives/src/chain/mod.rs +++ b/crates/primitives/src/chain/mod.rs @@ -1,453 +1,3 @@ -use crate::{ - holesky_nodes, - net::{goerli_nodes, mainnet_nodes, sepolia_nodes}, - NodeRecord, U256, U64, -}; -use alloy_rlp::{Decodable, Encodable}; -use num_enum::TryFromPrimitive; -use reth_codecs::add_arbitrary_tests; -use serde::{Deserialize, Serialize}; -use std::{fmt, str::FromStr}; -use strum::{AsRefStr, EnumCount, EnumIter, EnumString, EnumVariantNames}; - // The chain spec module. mod spec; -pub use spec::{ - AllGenesisFormats, BaseFeeParams, ChainSpec, ChainSpecBuilder, DisplayHardforks, ForkCondition, - ForkTimestamps, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, -}; - -#[cfg(feature = "optimism")] -pub use spec::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI}; - -// The chain info module. -mod info; -pub use info::ChainInfo; - -/// An Ethereum EIP-155 chain. -#[derive( - Clone, - Copy, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - AsRefStr, // AsRef, fmt::Display - EnumVariantNames, // Chain::VARIANTS - EnumString, // FromStr, TryFrom<&str> - EnumIter, // Chain::iter - EnumCount, // Chain::COUNT - TryFromPrimitive, // TryFrom - Deserialize, - Serialize, -)] -#[serde(rename_all = "snake_case")] -#[strum(serialize_all = "snake_case")] -#[repr(u64)] -#[allow(missing_docs)] -pub enum NamedChain { - Mainnet = 1, - Morden = 2, - Ropsten = 3, - Rinkeby = 4, - Goerli = 5, - Kovan = 42, - Holesky = 17000, - Sepolia = 11155111, - - Optimism = 10, - OptimismKovan = 69, - OptimismGoerli = 420, - - Base = 8453, - BaseGoerli = 84531, - - Arbitrum = 42161, - ArbitrumTestnet = 421611, - ArbitrumGoerli = 421613, - ArbitrumNova = 42170, - - #[serde(alias = "bsc")] - #[strum(to_string = "bsc")] - BinanceSmartChain = 56, - #[serde(alias = "bsc_testnet")] - #[strum(to_string = "bsc_testnet")] - BinanceSmartChainTestnet = 97, - - Dev = 1337, -} - -impl From for u64 { - fn from(value: NamedChain) -> Self { - value as u64 - } -} - -impl fmt::Display for NamedChain { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.as_ref().fmt(f) - } -} - -/// Either a named or chain id or the actual id value -#[add_arbitrary_tests(rlp)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Chain { - /// Contains a known chain - Named(NamedChain), - /// Contains the id of a chain - Id(u64), -} - -impl Chain { - /// Returns the mainnet chain. - pub const fn mainnet() -> Self { - Chain::Named(NamedChain::Mainnet) - } - - /// Returns the goerli chain. - pub const fn goerli() -> Self { - Chain::Named(NamedChain::Goerli) - } - - /// Returns the sepolia chain. - pub const fn sepolia() -> Self { - Chain::Named(NamedChain::Sepolia) - } - - /// Returns the holesky chain. - pub const fn holesky() -> Self { - Chain::Named(NamedChain::Holesky) - } - - /// Returns the optimism goerli chain. - pub const fn optimism_goerli() -> Self { - Chain::Named(NamedChain::OptimismGoerli) - } - - /// Returns the optimism mainnet chain. - pub const fn optimism_mainnet() -> Self { - Chain::Named(NamedChain::Optimism) - } - - /// Returns the base goerli chain. - pub const fn base_goerli() -> Self { - Chain::Named(NamedChain::BaseGoerli) - } - - /// Returns the base mainnet chain. - pub const fn base_mainnet() -> Self { - Chain::Named(NamedChain::Base) - } - - /// Returns the dev chain. - pub const fn dev() -> Self { - Chain::Named(NamedChain::Dev) - } - - /// Returns true if the chain contains Optimism configuration. - pub fn is_optimism(self) -> bool { - self.named().map_or(false, |c| { - matches!( - c, - NamedChain::Optimism | - NamedChain::OptimismGoerli | - NamedChain::OptimismKovan | - NamedChain::Base | - NamedChain::BaseGoerli - ) - }) - } - - /// Attempts to convert the chain into a named chain. - pub fn named(&self) -> Option { - match self { - Chain::Named(chain) => Some(*chain), - Chain::Id(id) => NamedChain::try_from(*id).ok(), - } - } - - /// The id of the chain - pub fn id(&self) -> u64 { - match self { - Chain::Named(chain) => *chain as u64, - Chain::Id(id) => *id, - } - } - - /// Returns the address of the public DNS node list for the given chain. - /// - /// See also - pub fn public_dns_network_protocol(self) -> Option { - use NamedChain as C; - const DNS_PREFIX: &str = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@"; - - let named: NamedChain = self.try_into().ok()?; - - if matches!(named, C::Mainnet | C::Goerli | C::Sepolia | C::Ropsten | C::Rinkeby) { - return Some(format!("{DNS_PREFIX}all.{}.ethdisco.net", named.as_ref().to_lowercase())) - } - None - } - - /// Returns bootnodes for the given chain. - pub fn bootnodes(self) -> Option> { - use NamedChain as C; - match self.try_into().ok()? { - C::Mainnet => Some(mainnet_nodes()), - C::Goerli => Some(goerli_nodes()), - C::Sepolia => Some(sepolia_nodes()), - C::Holesky => Some(holesky_nodes()), - _ => None, - } - } -} - -impl fmt::Display for Chain { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Chain::Named(chain) => chain.fmt(f), - Chain::Id(id) => { - if let Ok(chain) = NamedChain::try_from(*id) { - chain.fmt(f) - } else { - id.fmt(f) - } - } - } - } -} - -impl From for Chain { - fn from(id: NamedChain) -> Self { - Chain::Named(id) - } -} - -impl From for Chain { - fn from(id: u64) -> Self { - NamedChain::try_from(id).map(Chain::Named).unwrap_or_else(|_| Chain::Id(id)) - } -} - -impl From for Chain { - fn from(id: U256) -> Self { - id.to::().into() - } -} - -impl From for u64 { - fn from(c: Chain) -> Self { - match c { - Chain::Named(c) => c as u64, - Chain::Id(id) => id, - } - } -} - -impl From for U64 { - fn from(c: Chain) -> Self { - U64::from(u64::from(c)) - } -} - -impl From for U256 { - fn from(c: Chain) -> Self { - U256::from(u64::from(c)) - } -} - -impl TryFrom for NamedChain { - type Error = >::Error; - - fn try_from(chain: Chain) -> Result { - match chain { - Chain::Named(chain) => Ok(chain), - Chain::Id(id) => id.try_into(), - } - } -} - -impl FromStr for Chain { - type Err = String; - - fn from_str(s: &str) -> Result { - if let Ok(chain) = NamedChain::from_str(s) { - Ok(Chain::Named(chain)) - } else { - s.parse::() - .map(Chain::Id) - .map_err(|_| format!("Expected known chain or integer, found: {s}")) - } - } -} - -impl Encodable for Chain { - fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { - match self { - Self::Named(chain) => u64::from(*chain).encode(out), - Self::Id(id) => id.encode(out), - } - } - fn length(&self) -> usize { - match self { - Self::Named(chain) => u64::from(*chain).length(), - Self::Id(id) => id.length(), - } - } -} - -impl Decodable for Chain { - fn decode(buf: &mut &[u8]) -> alloy_rlp::Result { - Ok(u64::decode(buf)?.into()) - } -} - -impl Default for Chain { - fn default() -> Self { - NamedChain::Mainnet.into() - } -} - -#[cfg(any(test, feature = "arbitrary"))] -impl<'a> arbitrary::Arbitrary<'a> for Chain { - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - if u.ratio(1, 2)? { - let chain = u.int_in_range(0..=(NamedChain::COUNT - 1))?; - - return Ok(Chain::Named(NamedChain::iter().nth(chain).expect("in range"))) - } - - Ok(Self::Id(u64::arbitrary(u)?)) - } -} - -#[cfg(any(test, feature = "arbitrary"))] -use strum::IntoEnumIterator; - -#[cfg(any(test, feature = "arbitrary"))] -use proptest::{ - arbitrary::ParamsFor, - prelude::{any, Strategy}, - sample::Selector, - strategy::BoxedStrategy, -}; - -#[cfg(any(test, feature = "arbitrary"))] -impl proptest::arbitrary::Arbitrary for Chain { - type Parameters = ParamsFor; - fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - let named = - any::().prop_map(move |sel| Chain::Named(sel.select(NamedChain::iter()))); - let id = any::().prop_map(Chain::from); - proptest::strategy::Union::new_weighted(vec![(50, named.boxed()), (50, id.boxed())]).boxed() - } - - type Strategy = BoxedStrategy; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_id() { - let chain = Chain::Id(1234); - assert_eq!(chain.id(), 1234); - } - - #[test] - fn test_named_id() { - let chain = Chain::Named(NamedChain::Goerli); - assert_eq!(chain.id(), 5); - } - - #[test] - fn test_display_named_chain() { - let chain = Chain::Named(NamedChain::Mainnet); - assert_eq!(format!("{chain}"), "mainnet"); - } - - #[test] - fn test_display_id_chain() { - let chain = Chain::Id(1234); - assert_eq!(format!("{chain}"), "1234"); - } - - #[test] - fn test_from_u256() { - let n = U256::from(1234); - let chain = Chain::from(n); - let expected = Chain::Id(1234); - - assert_eq!(chain, expected); - } - - #[test] - fn test_into_u256() { - let chain = Chain::Named(NamedChain::Goerli); - let n: U256 = chain.into(); - let expected = U256::from(5); - - assert_eq!(n, expected); - } - - #[test] - #[allow(non_snake_case)] - fn test_into_U64() { - let chain = Chain::Named(NamedChain::Goerli); - let n: U64 = chain.into(); - let expected = U64::from(5); - - assert_eq!(n, expected); - } - - #[test] - fn test_from_str_named_chain() { - let result = Chain::from_str("mainnet"); - let expected = Chain::Named(NamedChain::Mainnet); - - assert!(result.is_ok()); - assert_eq!(result.unwrap(), expected); - } - - #[test] - fn test_from_str_named_chain_error() { - let result = Chain::from_str("chain"); - - assert!(result.is_err()); - } - - #[test] - fn test_from_str_id_chain() { - let result = Chain::from_str("1234"); - let expected = Chain::Id(1234); - - assert!(result.is_ok()); - assert_eq!(result.unwrap(), expected); - } - - #[test] - fn test_default() { - let default = Chain::default(); - let expected = Chain::Named(NamedChain::Mainnet); - - assert_eq!(default, expected); - } - - #[test] - fn test_id_chain_encodable_length() { - let chain = Chain::Id(1234); - - assert_eq!(chain.length(), 3); - } - - #[test] - fn test_dns_network() { - let s = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@all.mainnet.ethdisco.net"; - let chain: Chain = NamedChain::Mainnet.into(); - assert_eq!(s, chain.public_dns_network_protocol().unwrap().as_str()); - } -} +pub use spec::BaseFeeParams; diff --git a/crates/primitives/src/chain/spec.rs b/crates/primitives/src/chain/spec.rs index 4f5e4f6374c4..9d1dd0fa5e4f 100644 --- a/crates/primitives/src/chain/spec.rs +++ b/crates/primitives/src/chain/spec.rs @@ -1,369 +1,7 @@ -use crate::{ - constants::{ - 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, +use crate::constants::{ + EIP1559_DEFAULT_BASE_FEE_MAX_CHANGE_DENOMINATOR, EIP1559_DEFAULT_ELASTICITY_MULTIPLIER, }; -use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -use std::{ - collections::BTreeMap, - fmt::{Display, Formatter}, - sync::Arc, -}; - -/// The Ethereum mainnet spec -pub static MAINNET: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::mainnet(), - genesis: serde_json::from_str(include_str!("../../res/genesis/mainnet.json")) - .expect("Can't deserialize Mainnet genesis json"), - genesis_hash: Some(b256!( - "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3" - )), - // - paris_block_and_final_difficulty: Some(( - 15537394, - U256::from(58_750_003_716_598_352_816_469u128), - )), - fork_timestamps: ForkTimestamps::default().shanghai(1681338455), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(1150000)), - (Hardfork::Dao, ForkCondition::Block(1920000)), - (Hardfork::Tangerine, ForkCondition::Block(2463000)), - (Hardfork::SpuriousDragon, ForkCondition::Block(2675000)), - (Hardfork::Byzantium, ForkCondition::Block(4370000)), - (Hardfork::Constantinople, ForkCondition::Block(7280000)), - (Hardfork::Petersburg, ForkCondition::Block(7280000)), - (Hardfork::Istanbul, ForkCondition::Block(9069000)), - (Hardfork::MuirGlacier, ForkCondition::Block(9200000)), - (Hardfork::Berlin, ForkCondition::Block(12244000)), - (Hardfork::London, ForkCondition::Block(12965000)), - (Hardfork::ArrowGlacier, ForkCondition::Block(13773000)), - (Hardfork::GrayGlacier, ForkCondition::Block(15050000)), - ( - Hardfork::Paris, - ForkCondition::TTD { - fork_block: None, - total_difficulty: U256::from(58_750_000_000_000_000_000_000_u128), - }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(1681338455)), - ]), - // https://etherscan.io/tx/0xe75fb554e433e03763a1560646ee22dcb74e5274b34c5ad644e7c0f619a7e1d0 - deposit_contract: Some(DepositContract::new( - address!("00000000219ab540356cbb839cbe05303d7705fa"), - 11052984, - b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), - )), - base_fee_params: BaseFeeParams::ethereum(), - prune_delete_limit: 3500, - snapshot_block_interval: 500_000, - } - .into() -}); - -/// The Goerli spec -pub static GOERLI: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::goerli(), - genesis: serde_json::from_str(include_str!("../../res/genesis/goerli.json")) - .expect("Can't deserialize Goerli genesis json"), - genesis_hash: Some(b256!( - "bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a" - )), - // - paris_block_and_final_difficulty: Some((7382818, U256::from(10_790_000))), - fork_timestamps: ForkTimestamps::default().shanghai(1678832736), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Dao, ForkCondition::Block(0)), - (Hardfork::Tangerine, ForkCondition::Block(0)), - (Hardfork::SpuriousDragon, ForkCondition::Block(0)), - (Hardfork::Byzantium, ForkCondition::Block(0)), - (Hardfork::Constantinople, ForkCondition::Block(0)), - (Hardfork::Petersburg, ForkCondition::Block(0)), - (Hardfork::Istanbul, ForkCondition::Block(1561651)), - (Hardfork::Berlin, ForkCondition::Block(4460644)), - (Hardfork::London, ForkCondition::Block(5062605)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: None, total_difficulty: U256::from(10_790_000) }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(1678832736)), - ]), - // https://goerli.etherscan.io/tx/0xa3c07dc59bfdb1bfc2d50920fed2ef2c1c4e0a09fe2325dbc14e07702f965a78 - deposit_contract: Some(DepositContract::new( - address!("ff50ed3d0ec03ac01d4c79aad74928bff48a7b2b"), - 4367322, - b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), - )), - base_fee_params: BaseFeeParams::ethereum(), - prune_delete_limit: 1700, - snapshot_block_interval: 1_000_000, - } - .into() -}); - -/// The Sepolia spec -pub static SEPOLIA: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::sepolia(), - genesis: serde_json::from_str(include_str!("../../res/genesis/sepolia.json")) - .expect("Can't deserialize Sepolia genesis json"), - genesis_hash: Some(b256!( - "25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9" - )), - // - paris_block_and_final_difficulty: Some((1450409, U256::from(17_000_018_015_853_232u128))), - fork_timestamps: ForkTimestamps::default().shanghai(1677557088), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Dao, ForkCondition::Block(0)), - (Hardfork::Tangerine, ForkCondition::Block(0)), - (Hardfork::SpuriousDragon, ForkCondition::Block(0)), - (Hardfork::Byzantium, ForkCondition::Block(0)), - (Hardfork::Constantinople, ForkCondition::Block(0)), - (Hardfork::Petersburg, ForkCondition::Block(0)), - (Hardfork::Istanbul, ForkCondition::Block(0)), - (Hardfork::MuirGlacier, ForkCondition::Block(0)), - (Hardfork::Berlin, ForkCondition::Block(0)), - (Hardfork::London, ForkCondition::Block(0)), - ( - Hardfork::Paris, - ForkCondition::TTD { - fork_block: Some(1735371), - total_difficulty: U256::from(17_000_000_000_000_000u64), - }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(1677557088)), - ]), - // https://sepolia.etherscan.io/tx/0x025ecbf81a2f1220da6285d1701dc89fb5a956b62562ee922e1a9efd73eb4b14 - deposit_contract: Some(DepositContract::new( - address!("7f02c3e3c98b133055b8b348b2ac625669ed295d"), - 1273020, - b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), - )), - - base_fee_params: BaseFeeParams::ethereum(), - prune_delete_limit: 1700, - snapshot_block_interval: 1_000_000, - } - .into() -}); - -/// The Holesky spec -pub static HOLESKY: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::holesky(), - genesis: serde_json::from_str(include_str!("../../res/genesis/holesky.json")) - .expect("Can't deserialize Holesky genesis json"), - genesis_hash: Some(b256!( - "b5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4" - )), - paris_block_and_final_difficulty: Some((0, U256::from(1))), - fork_timestamps: ForkTimestamps::default().shanghai(1696000704), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Dao, ForkCondition::Block(0)), - (Hardfork::Tangerine, ForkCondition::Block(0)), - (Hardfork::SpuriousDragon, ForkCondition::Block(0)), - (Hardfork::Byzantium, ForkCondition::Block(0)), - (Hardfork::Constantinople, ForkCondition::Block(0)), - (Hardfork::Petersburg, ForkCondition::Block(0)), - (Hardfork::Istanbul, ForkCondition::Block(0)), - (Hardfork::MuirGlacier, ForkCondition::Block(0)), - (Hardfork::Berlin, ForkCondition::Block(0)), - (Hardfork::London, ForkCondition::Block(0)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(1696000704)), - ]), - deposit_contract: Some(DepositContract::new( - address!("4242424242424242424242424242424242424242"), - 0, - b256!("649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5"), - )), - base_fee_params: BaseFeeParams::ethereum(), - prune_delete_limit: 1700, - snapshot_block_interval: 1_000_000, - } - .into() -}); - -/// Dev testnet specification -/// -/// Includes 20 prefunded accounts with 10_000 ETH each derived from mnemonic "test test test test -/// test test test test test test test junk". -pub static DEV: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::dev(), - genesis: serde_json::from_str(include_str!("../../res/genesis/dev.json")) - .expect("Can't deserialize Dev testnet genesis json"), - genesis_hash: Some(b256!( - "2f980576711e3617a5e4d83dd539548ec0f7792007d505a3d2e9674833af2d7c" - )), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - fork_timestamps: ForkTimestamps::default().shanghai(0), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Dao, ForkCondition::Block(0)), - (Hardfork::Tangerine, ForkCondition::Block(0)), - (Hardfork::SpuriousDragon, ForkCondition::Block(0)), - (Hardfork::Byzantium, ForkCondition::Block(0)), - (Hardfork::Constantinople, ForkCondition::Block(0)), - (Hardfork::Petersburg, ForkCondition::Block(0)), - (Hardfork::Istanbul, ForkCondition::Block(0)), - (Hardfork::MuirGlacier, ForkCondition::Block(0)), - (Hardfork::Berlin, ForkCondition::Block(0)), - (Hardfork::London, ForkCondition::Block(0)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, - ), - (Hardfork::Shanghai, ForkCondition::Timestamp(0)), - ]), - deposit_contract: None, // TODO: do we even have? - ..Default::default() - } - .into() -}); - -/// The Optimism Goerli spec -#[cfg(feature = "optimism")] -pub static OP_GOERLI: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::optimism_goerli(), - genesis: serde_json::from_str(include_str!("../../res/genesis/goerli_op.json")) - .expect("Can't deserialize Optimism Goerli genesis json"), - genesis_hash: Some(b256!( - "c1fc15cd51159b1f1e5cbc4b82e85c1447ddfa33c52cf1d98d14fba0d6354be1" - )), - fork_timestamps: ForkTimestamps::default(), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Tangerine, ForkCondition::Block(0)), - (Hardfork::SpuriousDragon, ForkCondition::Block(0)), - (Hardfork::Byzantium, ForkCondition::Block(0)), - (Hardfork::Constantinople, ForkCondition::Block(0)), - (Hardfork::Petersburg, ForkCondition::Block(0)), - (Hardfork::Istanbul, ForkCondition::Block(0)), - (Hardfork::MuirGlacier, ForkCondition::Block(0)), - (Hardfork::Berlin, ForkCondition::Block(0)), - (Hardfork::London, ForkCondition::Block(0)), - (Hardfork::ArrowGlacier, ForkCondition::Block(0)), - (Hardfork::GrayGlacier, ForkCondition::Block(0)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, - ), - (Hardfork::Bedrock, ForkCondition::Block(4061224)), - (Hardfork::Regolith, ForkCondition::Timestamp(1679079600)), - ]), - base_fee_params: BaseFeeParams::optimism(), - prune_delete_limit: 1700, - snapshot_block_interval: 1_000_000, - ..Default::default() - } - .into() -}); - -/// The Base Goerli spec -#[cfg(feature = "optimism")] -pub static BASE_GOERLI: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::base_goerli(), - genesis: serde_json::from_str(include_str!("../../res/genesis/goerli_base.json")) - .expect("Can't deserialize Base Goerli genesis json"), - genesis_hash: Some(b256!( - "a3ab140f15ea7f7443a4702da64c10314eb04d488e72974e02e2d728096b4f76" - )), - fork_timestamps: ForkTimestamps::default(), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Tangerine, ForkCondition::Block(0)), - (Hardfork::SpuriousDragon, ForkCondition::Block(0)), - (Hardfork::Byzantium, ForkCondition::Block(0)), - (Hardfork::Constantinople, ForkCondition::Block(0)), - (Hardfork::Petersburg, ForkCondition::Block(0)), - (Hardfork::Istanbul, ForkCondition::Block(0)), - (Hardfork::MuirGlacier, ForkCondition::Block(0)), - (Hardfork::Berlin, ForkCondition::Block(0)), - (Hardfork::London, ForkCondition::Block(0)), - (Hardfork::ArrowGlacier, ForkCondition::Block(0)), - (Hardfork::GrayGlacier, ForkCondition::Block(0)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, - ), - (Hardfork::Bedrock, ForkCondition::Block(0)), - (Hardfork::Regolith, ForkCondition::Timestamp(1683219600)), - ]), - base_fee_params: BaseFeeParams::optimism_goerli(), - prune_delete_limit: 1700, - snapshot_block_interval: 1_000_000, - ..Default::default() - } - .into() -}); - -/// The Base mainnet spec -#[cfg(feature = "optimism")] -pub static BASE_MAINNET: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::base_mainnet(), - genesis: serde_json::from_str(include_str!("../../res/genesis/base.json")) - .expect("Can't deserialize Base genesis json"), - genesis_hash: Some(b256!( - "f712aa9241cc24369b143cf6dce85f0902a9731e70d66818a3a5845b296c73dd" - )), - fork_timestamps: ForkTimestamps::default(), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - hardforks: BTreeMap::from([ - (Hardfork::Frontier, ForkCondition::Block(0)), - (Hardfork::Homestead, ForkCondition::Block(0)), - (Hardfork::Tangerine, ForkCondition::Block(0)), - (Hardfork::SpuriousDragon, ForkCondition::Block(0)), - (Hardfork::Byzantium, ForkCondition::Block(0)), - (Hardfork::Constantinople, ForkCondition::Block(0)), - (Hardfork::Petersburg, ForkCondition::Block(0)), - (Hardfork::Istanbul, ForkCondition::Block(0)), - (Hardfork::MuirGlacier, ForkCondition::Block(0)), - (Hardfork::Berlin, ForkCondition::Block(0)), - (Hardfork::London, ForkCondition::Block(0)), - (Hardfork::ArrowGlacier, ForkCondition::Block(0)), - (Hardfork::GrayGlacier, ForkCondition::Block(0)), - ( - Hardfork::Paris, - ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::from(0) }, - ), - (Hardfork::Bedrock, ForkCondition::Block(0)), - (Hardfork::Regolith, ForkCondition::Timestamp(0)), - ]), - base_fee_params: BaseFeeParams::optimism(), - prune_delete_limit: 1700, - snapshot_block_interval: 1_000_000, - ..Default::default() - } - .into() -}); /// BaseFeeParams contains the config parameters that control block base fee computation #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)] @@ -405,2171 +43,3 @@ impl BaseFeeParams { } } } - -/// An Ethereum chain specification. -/// -/// A chain specification describes: -/// -/// - Meta-information about the chain (the chain ID) -/// - The genesis block of the chain ([`Genesis`]) -/// - What hardforks are activated, and under which conditions -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ChainSpec { - /// The chain ID - pub chain: Chain, - - /// The hash of the genesis block. - /// - /// This acts as a small cache for known chains. If the chain is known, then the genesis hash - /// is also known ahead of time, and this will be `Some`. - #[serde(skip, default)] - pub genesis_hash: Option, - - /// The genesis block - pub genesis: Genesis, - - /// The block at which [Hardfork::Paris] was activated and the final difficulty at this block. - #[serde(skip, default)] - pub paris_block_and_final_difficulty: Option<(u64, U256)>, - - /// Timestamps of various hardforks - /// - /// This caches entries in `hardforks` map - #[serde(skip, default)] - pub fork_timestamps: ForkTimestamps, - - /// The active hard forks and their activation conditions - pub hardforks: BTreeMap, - - /// The deposit contract deployed for PoS - #[serde(skip, default)] - pub deposit_contract: Option, - - /// The parameters that configure how a block's base fee is computed - pub base_fee_params: BaseFeeParams, - - /// The delete limit for pruner, per block. In the actual pruner run it will be multiplied by - /// the amount of blocks between pruner runs to account for the difference in amount of new - /// data coming in. - #[serde(default)] - pub prune_delete_limit: usize, - - /// The block interval for creating snapshots. Each snapshot will have that much blocks in it. - pub snapshot_block_interval: u64, -} - -impl Default for ChainSpec { - fn default() -> ChainSpec { - ChainSpec { - chain: Default::default(), - genesis_hash: Default::default(), - genesis: Default::default(), - paris_block_and_final_difficulty: Default::default(), - fork_timestamps: Default::default(), - hardforks: Default::default(), - deposit_contract: Default::default(), - base_fee_params: BaseFeeParams::ethereum(), - prune_delete_limit: MAINNET.prune_delete_limit, - snapshot_block_interval: Default::default(), - } - } -} - -impl ChainSpec { - /// Get information about the chain itself - pub fn chain(&self) -> Chain { - self.chain - } - - /// Returns `true` if this chain contains Optimism configuration. - pub fn is_optimism(&self) -> bool { - self.chain.is_optimism() - } - - /// Get the genesis block specification. - /// - /// To get the header for the genesis block, use [`Self::genesis_header`] instead. - pub fn genesis(&self) -> &Genesis { - &self.genesis - } - - /// Get the header for the genesis block. - pub fn genesis_header(&self) -> Header { - // If London is activated at genesis, we set the initial base fee as per EIP-1559. - let base_fee_per_gas = self.initial_base_fee(); - - // If shanghai is activated, initialize the header with an empty withdrawals hash, and - // empty withdrawals list. - let withdrawals_root = - (self.fork(Hardfork::Shanghai).active_at_timestamp(self.genesis.timestamp)) - .then_some(EMPTY_WITHDRAWALS); - - // If Cancun is activated at genesis, we set: - // * parent beacon block root to 0x0 - // * blob gas used to provided genesis or 0x0 - // * excess blob gas to provided genesis or 0x0 - let (parent_beacon_block_root, blob_gas_used, excess_blob_gas) = - if self.fork(Hardfork::Cancun).active_at_timestamp(self.genesis.timestamp) { - let blob_gas_used = self.genesis.blob_gas_used.unwrap_or(0); - let excess_blob_gas = self.genesis.excess_blob_gas.unwrap_or(0); - (Some(B256::ZERO), Some(blob_gas_used), Some(excess_blob_gas)) - } else { - (None, None, None) - }; - - Header { - parent_hash: B256::ZERO, - number: 0, - transactions_root: EMPTY_TRANSACTIONS, - ommers_hash: EMPTY_OMMER_ROOT_HASH, - receipts_root: EMPTY_RECEIPTS, - logs_bloom: Default::default(), - gas_limit: self.genesis.gas_limit, - difficulty: self.genesis.difficulty, - nonce: self.genesis.nonce, - extra_data: self.genesis.extra_data.clone(), - state_root: genesis_state_root(&self.genesis.alloc), - timestamp: self.genesis.timestamp, - mix_hash: self.genesis.mix_hash, - beneficiary: self.genesis.coinbase, - gas_used: Default::default(), - base_fee_per_gas, - withdrawals_root, - parent_beacon_block_root, - blob_gas_used, - excess_blob_gas, - } - } - - /// Get the sealed header for the genesis block. - pub fn sealed_genesis_header(&self) -> SealedHeader { - SealedHeader { header: self.genesis_header(), hash: self.genesis_hash() } - } - - /// Get the initial base fee of the genesis block. - pub fn initial_base_fee(&self) -> Option { - // If the base fee is set in the genesis block, we use that instead of the default. - let genesis_base_fee = self.genesis.base_fee_per_gas.unwrap_or(EIP1559_INITIAL_BASE_FEE); - - // If London is activated at genesis, we set the initial base fee as per EIP-1559. - (self.fork(Hardfork::London).active_at_block(0)).then_some(genesis_base_fee) - } - - /// Get the hash of the genesis block. - pub fn genesis_hash(&self) -> B256 { - if let Some(hash) = self.genesis_hash { - hash - } else { - self.genesis_header().hash_slow() - } - } - - /// Get the timestamp of the genesis block. - pub fn genesis_timestamp(&self) -> u64 { - self.genesis.timestamp - } - - /// Returns the final total difficulty if the given block number is after the Paris hardfork. - /// - /// Note: technically this would also be valid for the block before the paris upgrade, but this - /// edge case is omitted here. - pub fn final_paris_total_difficulty(&self, block_number: u64) -> Option { - self.paris_block_and_final_difficulty.and_then(|(activated_at, final_difficulty)| { - if block_number >= activated_at { - Some(final_difficulty) - } else { - None - } - }) - } - - /// Returns the forks in this specification and their activation conditions. - pub fn hardforks(&self) -> &BTreeMap { - &self.hardforks - } - - /// Get the fork id for the given hardfork. - pub fn hardfork_fork_id(&self, fork: Hardfork) -> Option { - fork.fork_id(self) - } - - /// Convenience method to get the fork id for [Hardfork::Shanghai] from a given chainspec. - pub fn shanghai_fork_id(&self) -> Option { - Hardfork::Shanghai.fork_id(self) - } - - /// Get the fork condition for the given fork. - pub fn fork(&self, fork: Hardfork) -> ForkCondition { - self.hardforks.get(&fork).copied().unwrap_or(ForkCondition::Never) - } - - /// Get an iterator of all hardforks with their respective activation conditions. - pub fn forks_iter(&self) -> impl Iterator + '_ { - self.hardforks.iter().map(|(f, b)| (*f, *b)) - } - - /// Convenience method to check if a fork is active at a given timestamp. - #[inline] - pub fn is_fork_active_at_timestamp(&self, fork: Hardfork, timestamp: u64) -> bool { - self.fork(fork).active_at_timestamp(timestamp) - } - - /// Convenience method to check if [Hardfork::Shanghai] is active at a given timestamp. - #[inline] - pub fn is_shanghai_active_at_timestamp(&self, timestamp: u64) -> bool { - self.fork_timestamps - .shanghai - .map(|shanghai| timestamp >= shanghai) - .unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Shanghai, timestamp)) - } - - /// Convenience method to check if [Hardfork::Cancun] is active at a given timestamp. - #[inline] - pub fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool { - self.fork_timestamps - .cancun - .map(|cancun| timestamp >= cancun) - .unwrap_or_else(|| self.is_fork_active_at_timestamp(Hardfork::Cancun, timestamp)) - } - - /// Creates a [`ForkFilter`] for the block described by [Head]. - pub fn fork_filter(&self, head: Head) -> ForkFilter { - let forks = self.forks_iter().filter_map(|(_, condition)| { - // We filter out TTD-based forks w/o a pre-known block since those do not show up in the - // fork filter. - Some(match condition { - ForkCondition::Block(block) => ForkFilterKey::Block(block), - ForkCondition::Timestamp(time) => ForkFilterKey::Time(time), - ForkCondition::TTD { fork_block: Some(block), .. } => ForkFilterKey::Block(block), - _ => return None, - }) - }); - - ForkFilter::new(head, self.genesis_hash(), self.genesis_timestamp(), forks) - } - - /// Compute the [`ForkId`] for the given [`Head`] folowing eip-6122 spec - pub fn fork_id(&self, head: &Head) -> ForkId { - let mut forkhash = ForkHash::from(self.genesis_hash()); - let mut current_applied = 0; - - // handle all block forks before handling timestamp based forks. see: https://eips.ethereum.org/EIPS/eip-6122 - for (_, cond) in self.forks_iter() { - // handle block based forks and the sepolia merge netsplit block edge case (TTD - // ForkCondition with Some(block)) - if let ForkCondition::Block(block) | - ForkCondition::TTD { fork_block: Some(block), .. } = cond - { - if cond.active_at_head(head) { - if block != current_applied { - forkhash += block; - current_applied = block; - } - } else { - // we can return here because this block fork is not active, so we set the - // `next` value - return ForkId { hash: forkhash, next: block } - } - } - } - - // timestamp are ALWAYS applied after the merge. - // - // this filter ensures that no block-based forks are returned - for timestamp in self.forks_iter().filter_map(|(_, cond)| { - cond.as_timestamp().filter(|time| time > &self.genesis.timestamp) - }) { - let cond = ForkCondition::Timestamp(timestamp); - if cond.active_at_head(head) { - if timestamp != current_applied { - forkhash += timestamp; - current_applied = timestamp; - } - } else { - // can safely return here because we have already handled all block forks and - // have handled all active timestamp forks, and set the next value to the - // timestamp that is known but not active yet - return ForkId { hash: forkhash, next: timestamp } - } - } - - ForkId { hash: forkhash, next: 0 } - } - - /// An internal helper function that returns a head block that satisfies a given Fork condition. - pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head { - match cond { - ForkCondition::Block(number) => Head { number, ..Default::default() }, - ForkCondition::Timestamp(timestamp) => { - // to satisfy every timestamp ForkCondition, we find the last ForkCondition::Block - // if one exists, and include its block_num in the returned Head - if let Some(last_block_num) = self.last_block_fork_before_merge_or_timestamp() { - return Head { timestamp, number: last_block_num, ..Default::default() } - } - Head { timestamp, ..Default::default() } - } - ForkCondition::TTD { total_difficulty, .. } => { - Head { total_difficulty, ..Default::default() } - } - ForkCondition::Never => unreachable!(), - } - } - - /// An internal helper function that returns the block number of the last block-based - /// fork that occurs before any existing TTD (merge)/timestamp based forks. - /// - /// Note: this returns None if the ChainSpec is not configured with a TTD/Timestamp fork. - pub(crate) fn last_block_fork_before_merge_or_timestamp(&self) -> Option { - let mut hardforks_iter = self.forks_iter().peekable(); - while let Some((_, curr_cond)) = hardforks_iter.next() { - if let Some((_, next_cond)) = hardforks_iter.peek() { - // peek and find the first occurence of ForkCondition::TTD (merge) , or in - // custom ChainSpecs, the first occurence of - // ForkCondition::Timestamp. If curr_cond is ForkCondition::Block at - // this point, which it should be in most "normal" ChainSpecs, - // return its block_num - match next_cond { - ForkCondition::TTD { fork_block, .. } => { - // handle Sepolia merge netsplit case - if fork_block.is_some() { - return *fork_block - } - // ensure curr_cond is indeed ForkCondition::Block and return block_num - if let ForkCondition::Block(block_num) = curr_cond { - return Some(block_num) - } - } - ForkCondition::Timestamp(_) => { - // ensure curr_cond is indeed ForkCondition::Block and return block_num - if let ForkCondition::Block(block_num) = curr_cond { - return Some(block_num) - } - } - ForkCondition::Block(_) | ForkCondition::Never => continue, - } - } - } - None - } - - /// Build a chainspec using [`ChainSpecBuilder`] - pub fn builder() -> ChainSpecBuilder { - ChainSpecBuilder::default() - } -} - -impl From for ChainSpec { - fn from(genesis: Genesis) -> Self { - // Block-based hardforks - let hardfork_opts = [ - (Hardfork::Homestead, genesis.config.homestead_block), - (Hardfork::Dao, genesis.config.dao_fork_block), - (Hardfork::Tangerine, genesis.config.eip150_block), - (Hardfork::SpuriousDragon, genesis.config.eip155_block), - (Hardfork::Byzantium, genesis.config.byzantium_block), - (Hardfork::Constantinople, genesis.config.constantinople_block), - (Hardfork::Petersburg, genesis.config.petersburg_block), - (Hardfork::Istanbul, genesis.config.istanbul_block), - (Hardfork::MuirGlacier, genesis.config.muir_glacier_block), - (Hardfork::Berlin, genesis.config.berlin_block), - (Hardfork::London, genesis.config.london_block), - (Hardfork::ArrowGlacier, genesis.config.arrow_glacier_block), - (Hardfork::GrayGlacier, genesis.config.gray_glacier_block), - ]; - let mut hardforks = hardfork_opts - .iter() - .filter_map(|(hardfork, opt)| opt.map(|block| (*hardfork, ForkCondition::Block(block)))) - .collect::>(); - - // Paris - if let Some(ttd) = genesis.config.terminal_total_difficulty { - hardforks.insert( - Hardfork::Paris, - ForkCondition::TTD { - total_difficulty: ttd, - fork_block: genesis.config.merge_netsplit_block, - }, - ); - } - - // Time-based hardforks - let time_hardfork_opts = [ - (Hardfork::Shanghai, genesis.config.shanghai_time), - (Hardfork::Cancun, genesis.config.cancun_time), - ]; - - let time_hardforks = time_hardfork_opts - .iter() - .filter_map(|(hardfork, opt)| { - opt.map(|time| (*hardfork, ForkCondition::Timestamp(time))) - }) - .collect::>(); - - hardforks.extend(time_hardforks); - - Self { - chain: genesis.config.chain_id.into(), - genesis, - genesis_hash: None, - fork_timestamps: ForkTimestamps::from_hardforks(&hardforks), - hardforks, - paris_block_and_final_difficulty: None, - deposit_contract: None, - ..Default::default() - } - } -} - -/// Various timestamps of forks -#[derive(Debug, Clone, Default, Eq, PartialEq)] -pub struct ForkTimestamps { - /// The timestamp of the shanghai fork - pub shanghai: Option, - /// The timestamp of the cancun fork - pub cancun: Option, -} - -impl ForkTimestamps { - /// Creates a new [`ForkTimestamps`] from the given hardforks by extracing the timestamps - fn from_hardforks(forks: &BTreeMap) -> Self { - let mut timestamps = ForkTimestamps::default(); - if let Some(shanghai) = forks.get(&Hardfork::Shanghai).and_then(|f| f.as_timestamp()) { - timestamps = timestamps.shanghai(shanghai); - } - if let Some(cancun) = forks.get(&Hardfork::Cancun).and_then(|f| f.as_timestamp()) { - timestamps = timestamps.cancun(cancun); - } - timestamps - } - - /// Sets the given shanghai timestamp - pub fn shanghai(mut self, shanghai: u64) -> Self { - self.shanghai = Some(shanghai); - self - } - - /// Sets the given cancun timestamp - pub fn cancun(mut self, cancun: u64) -> Self { - self.cancun = Some(cancun); - self - } -} - -/// A helper type for compatibility with geth's config -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(untagged)] -pub enum AllGenesisFormats { - /// The reth genesis format - Reth(ChainSpec), - /// The geth genesis format - Geth(Genesis), -} - -impl From for AllGenesisFormats { - fn from(genesis: Genesis) -> Self { - Self::Geth(genesis) - } -} - -impl From for AllGenesisFormats { - fn from(genesis: ChainSpec) -> Self { - Self::Reth(genesis) - } -} - -impl From> for AllGenesisFormats { - fn from(genesis: Arc) -> Self { - Arc::try_unwrap(genesis).unwrap_or_else(|arc| (*arc).clone()).into() - } -} - -impl From for ChainSpec { - fn from(genesis: AllGenesisFormats) -> Self { - match genesis { - AllGenesisFormats::Geth(genesis) => genesis.into(), - AllGenesisFormats::Reth(genesis) => genesis, - } - } -} - -/// A helper to build custom chain specs -#[derive(Debug, Default, Clone)] -pub struct ChainSpecBuilder { - chain: Option, - genesis: Option, - hardforks: BTreeMap, -} - -impl ChainSpecBuilder { - /// Construct a new builder from the mainnet chain spec. - pub fn mainnet() -> Self { - Self { - chain: Some(MAINNET.chain), - genesis: Some(MAINNET.genesis.clone()), - hardforks: MAINNET.hardforks.clone(), - } - } - - /// Set the chain ID - pub fn chain(mut self, chain: Chain) -> Self { - self.chain = Some(chain); - self - } - - /// Set the genesis block. - pub fn genesis(mut self, genesis: Genesis) -> Self { - self.genesis = Some(genesis); - self - } - - /// Add the given fork with the given activation condition to the spec. - pub fn with_fork(mut self, fork: Hardfork, condition: ForkCondition) -> Self { - self.hardforks.insert(fork, condition); - self - } - - /// Enable the Paris hardfork at the given TTD. - /// - /// Does not set the merge netsplit block. - pub fn paris_at_ttd(self, ttd: U256) -> Self { - self.with_fork( - Hardfork::Paris, - ForkCondition::TTD { total_difficulty: ttd, fork_block: None }, - ) - } - - /// Enable Frontier at genesis. - pub fn frontier_activated(mut self) -> Self { - self.hardforks.insert(Hardfork::Frontier, ForkCondition::Block(0)); - self - } - - /// Enable Homestead at genesis. - pub fn homestead_activated(mut self) -> Self { - self = self.frontier_activated(); - self.hardforks.insert(Hardfork::Homestead, ForkCondition::Block(0)); - self - } - - /// Enable Tangerine at genesis. - pub fn tangerine_whistle_activated(mut self) -> Self { - self = self.homestead_activated(); - self.hardforks.insert(Hardfork::Tangerine, ForkCondition::Block(0)); - self - } - - /// Enable Spurious Dragon at genesis. - pub fn spurious_dragon_activated(mut self) -> Self { - self = self.tangerine_whistle_activated(); - self.hardforks.insert(Hardfork::SpuriousDragon, ForkCondition::Block(0)); - self - } - - /// Enable Byzantium at genesis. - pub fn byzantium_activated(mut self) -> Self { - self = self.spurious_dragon_activated(); - self.hardforks.insert(Hardfork::Byzantium, ForkCondition::Block(0)); - self - } - - /// Enable Petersburg at genesis. - pub fn petersburg_activated(mut self) -> Self { - self = self.byzantium_activated(); - self.hardforks.insert(Hardfork::Petersburg, ForkCondition::Block(0)); - self - } - - /// Enable Istanbul at genesis. - pub fn istanbul_activated(mut self) -> Self { - self = self.petersburg_activated(); - self.hardforks.insert(Hardfork::Istanbul, ForkCondition::Block(0)); - self - } - - /// Enable Berlin at genesis. - pub fn berlin_activated(mut self) -> Self { - self = self.istanbul_activated(); - self.hardforks.insert(Hardfork::Berlin, ForkCondition::Block(0)); - self - } - - /// Enable London at genesis. - pub fn london_activated(mut self) -> Self { - self = self.berlin_activated(); - self.hardforks.insert(Hardfork::London, ForkCondition::Block(0)); - self - } - - /// Enable Paris at genesis. - pub fn paris_activated(mut self) -> Self { - self = self.london_activated(); - self.hardforks.insert( - Hardfork::Paris, - ForkCondition::TTD { fork_block: Some(0), total_difficulty: U256::ZERO }, - ); - self - } - - /// Enable Shanghai at genesis. - pub fn shanghai_activated(mut self) -> Self { - self = self.paris_activated(); - self.hardforks.insert(Hardfork::Shanghai, ForkCondition::Timestamp(0)); - self - } - - /// Enable Cancun at genesis. - pub fn cancun_activated(mut self) -> Self { - self = self.shanghai_activated(); - self.hardforks.insert(Hardfork::Cancun, ForkCondition::Timestamp(0)); - self - } - - /// Enable Bedrock at genesis - #[cfg(feature = "optimism")] - pub fn bedrock_activated(mut self) -> Self { - self = self.paris_activated(); - self.hardforks.insert(Hardfork::Bedrock, ForkCondition::Block(0)); - self - } - - /// Enable Regolith at the timestamp of activation. - /// For post-bedrock op-stack chains, this will be at genesis. - /// For pre-bedrock op-stack chains, this will be at the timestamp of regolith activation. - #[cfg(feature = "optimism")] - pub fn regolith_activated(mut self) -> Self { - self = self.bedrock_activated(); - self.hardforks.insert(Hardfork::Regolith, ForkCondition::Timestamp(0)); - self - } - - /// Build the resulting [`ChainSpec`]. - /// - /// # Panics - /// - /// This function panics if the chain ID and genesis is not set ([`Self::chain`] and - /// [`Self::genesis`]) - pub fn build(self) -> ChainSpec { - ChainSpec { - chain: self.chain.expect("The chain is required"), - genesis: self.genesis.expect("The genesis is required"), - genesis_hash: None, - fork_timestamps: ForkTimestamps::from_hardforks(&self.hardforks), - hardforks: self.hardforks, - paris_block_and_final_difficulty: None, - deposit_contract: None, - ..Default::default() - } - } -} - -impl From<&Arc> for ChainSpecBuilder { - fn from(value: &Arc) -> Self { - Self { - chain: Some(value.chain), - genesis: Some(value.genesis.clone()), - hardforks: value.hardforks.clone(), - } - } -} - -/// The condition at which a fork is activated. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Default)] -pub enum ForkCondition { - /// The fork is activated after a certain block. - Block(BlockNumber), - /// The fork is activated after a total difficulty has been reached. - TTD { - /// The block number at which TTD is reached, if it is known. - /// - /// This should **NOT** be set unless you want this block advertised as [EIP-2124][eip2124] - /// `FORK_NEXT`. This is currently only the case for Sepolia and Holesky. - /// - /// [eip2124]: https://eips.ethereum.org/EIPS/eip-2124 - fork_block: Option, - /// The total difficulty after which the fork is activated. - total_difficulty: U256, - }, - /// The fork is activated after a specific timestamp. - Timestamp(u64), - /// The fork is never activated - #[default] - Never, -} - -impl ForkCondition { - /// Returns true if the fork condition is timestamp based. - pub fn is_timestamp(&self) -> bool { - matches!(self, ForkCondition::Timestamp(_)) - } - - /// Checks whether the fork condition is satisfied at the given block. - /// - /// For TTD conditions, this will only return true if the activation block is already known. - /// - /// For timestamp conditions, this will always return false. - pub fn active_at_block(&self, current_block: BlockNumber) -> bool { - match self { - ForkCondition::Block(block) => current_block >= *block, - ForkCondition::TTD { fork_block: Some(block), .. } => current_block >= *block, - _ => false, - } - } - - /// Checks if the given block is the first block that satisfies the fork condition. - /// - /// This will return false for any condition that is not block based. - pub fn transitions_at_block(&self, current_block: BlockNumber) -> bool { - match self { - ForkCondition::Block(block) => current_block == *block, - _ => false, - } - } - - /// Checks whether the fork condition is satisfied at the given total difficulty and difficulty - /// of a current block. - /// - /// The fork is considered active if the _previous_ total difficulty is above the threshold. - /// To achieve that, we subtract the passed `difficulty` from the current block's total - /// difficulty, and check if it's above the Fork Condition's total difficulty (here: - /// 58_750_000_000_000_000_000_000) - /// - /// This will return false for any condition that is not TTD-based. - pub fn active_at_ttd(&self, ttd: U256, difficulty: U256) -> bool { - if let ForkCondition::TTD { total_difficulty, .. } = self { - ttd.saturating_sub(difficulty) >= *total_difficulty - } else { - false - } - } - - /// Checks whether the fork condition is satisfied at the given timestamp. - /// - /// This will return false for any condition that is not timestamp-based. - pub fn active_at_timestamp(&self, timestamp: u64) -> bool { - if let ForkCondition::Timestamp(time) = self { - timestamp >= *time - } else { - false - } - } - - /// Checks whether the fork condition is satisfied at the given head block. - /// - /// This will return true if: - /// - /// - The condition is satisfied by the block number; - /// - The condition is satisfied by the timestamp; - /// - or the condition is satisfied by the total difficulty - pub fn active_at_head(&self, head: &Head) -> bool { - self.active_at_block(head.number) || - self.active_at_timestamp(head.timestamp) || - self.active_at_ttd(head.total_difficulty, head.difficulty) - } - - /// Get the total terminal difficulty for this fork condition. - /// - /// Returns `None` for fork conditions that are not TTD based. - pub fn ttd(&self) -> Option { - match self { - ForkCondition::TTD { total_difficulty, .. } => Some(*total_difficulty), - _ => None, - } - } - - /// Returns the timestamp of the fork condition, if it is timestamp based. - pub fn as_timestamp(&self) -> Option { - match self { - ForkCondition::Timestamp(timestamp) => Some(*timestamp), - _ => None, - } - } -} - -/// A container to pretty-print a hardfork. -/// -/// The fork is formatted depending on its fork condition: -/// -/// - Block and timestamp based forks are formatted in the same manner (`{name} <({eip})> -/// @{condition}`) -/// - TTD based forks are formatted separately as `{name} <({eip})> @{ttd} (network is known -/// to be merged)` -/// -/// An optional EIP can be attached to the fork to display as well. This should generally be in the -/// form of just `EIP-x`, e.g. `EIP-1559`. -#[derive(Debug)] -struct DisplayFork { - /// The name of the hardfork (e.g. Frontier) - name: String, - /// The fork condition - activated_at: ForkCondition, - /// An optional EIP (e.g. `EIP-1559`). - eip: Option, -} - -impl Display for DisplayFork { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let name_with_eip = if let Some(eip) = &self.eip { - format!("{} ({})", self.name, eip) - } else { - self.name.clone() - }; - - match self.activated_at { - ForkCondition::Block(at) | ForkCondition::Timestamp(at) => { - write!(f, "{:32} @{}", name_with_eip, at)?; - } - ForkCondition::TTD { fork_block, total_difficulty } => { - writeln!( - f, - "{:32} @{} ({})", - name_with_eip, - total_difficulty, - if fork_block.is_some() { - "network is known to be merged" - } else { - "network is not known to be merged" - } - )?; - } - ForkCondition::Never => unreachable!(), - } - - Ok(()) - } -} - -/// A container for pretty-printing a list of hardforks. -/// -/// # Examples -/// -/// ``` -/// # use reth_primitives::MAINNET; -/// # use reth_primitives::DisplayHardforks; -/// println!("{}", DisplayHardforks::new(MAINNET.hardforks())); -/// ``` -/// -/// An example of the output: -/// -/// ```text -/// Pre-merge hard forks (block based): -// - Frontier @0 -// - Homestead @1150000 -// - Dao @1920000 -// - Tangerine @2463000 -// - SpuriousDragon @2675000 -// - Byzantium @4370000 -// - Constantinople @7280000 -// - Petersburg @7280000 -// - Istanbul @9069000 -// - MuirGlacier @9200000 -// - Berlin @12244000 -// - London @12965000 -// - ArrowGlacier @13773000 -// - GrayGlacier @15050000 -// Merge hard forks: -// - Paris @58750000000000000000000 (network is not known to be merged) -// -// Post-merge hard forks (timestamp based): -// - Shanghai @1681338455 -/// ``` -#[derive(Debug)] -pub struct DisplayHardforks { - /// A list of pre-merge (block based) hardforks - pre_merge: Vec, - /// A list of merge (TTD based) hardforks - with_merge: Vec, - /// A list of post-merge (timestamp based) hardforks - post_merge: Vec, -} - -impl Display for DisplayHardforks { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - writeln!(f, "Pre-merge hard forks (block based):")?; - for fork in self.pre_merge.iter() { - writeln!(f, "- {fork}")?; - } - - if !self.with_merge.is_empty() { - writeln!(f, "Merge hard forks:")?; - for fork in self.with_merge.iter() { - writeln!(f, "- {fork}")?; - } - } - - if !self.post_merge.is_empty() { - writeln!(f, "Post-merge hard forks (timestamp based):")?; - for fork in self.post_merge.iter() { - writeln!(f, "- {fork}")?; - } - } - - Ok(()) - } -} - -impl DisplayHardforks { - /// Creates a new [`DisplayHardforks`] from an iterator of hardforks. - pub fn new(hardforks: &BTreeMap) -> Self { - Self::from_iter(hardforks.iter()) - } -} - -impl<'a, 'b> FromIterator<(&'a Hardfork, &'b ForkCondition)> for DisplayHardforks { - fn from_iter>(iter: T) -> Self { - let mut pre_merge = Vec::new(); - let mut with_merge = Vec::new(); - let mut post_merge = Vec::new(); - - for (fork, condition) in iter { - let display_fork = - DisplayFork { name: fork.to_string(), activated_at: *condition, eip: None }; - - match condition { - ForkCondition::Block(_) => { - pre_merge.push(display_fork); - } - ForkCondition::TTD { .. } => { - with_merge.push(display_fork); - } - ForkCondition::Timestamp(_) => { - post_merge.push(display_fork); - } - ForkCondition::Never => continue, - } - } - - Self { pre_merge, with_merge, post_merge } - } -} - -/// PoS deposit contract details. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DepositContract { - /// Deposit Contract Address - pub address: Address, - /// Deployment Block - pub block: BlockNumber, - /// `DepositEvent` event signature - pub topic: B256, -} - -impl DepositContract { - fn new(address: Address, block: BlockNumber, topic: B256) -> Self { - DepositContract { address, block, topic } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - b256, hex, ChainConfig, GenesisAccount, NamedChain, B256, DEV, GOERLI, HOLESKY, MAINNET, - SEPOLIA, U256, - }; - use alloy_rlp::Encodable; - use bytes::BytesMut; - use std::{collections::HashMap, str::FromStr}; - - #[cfg(feature = "optimism")] - use crate::OP_GOERLI; - - fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) { - for (block, expected_id) in cases { - let computed_id = spec.fork_id(block); - assert_eq!( - expected_id, &computed_id, - "Expected fork ID {:?}, computed fork ID {:?} at block {}", - expected_id, computed_id, block.number - ); - } - } - - fn test_hardfork_fork_ids(spec: &ChainSpec, cases: &[(Hardfork, ForkId)]) { - for (hardfork, expected_id) in cases { - if let Some(computed_id) = spec.hardfork_fork_id(*hardfork) { - assert_eq!( - expected_id, &computed_id, - "Expected fork ID {:?}, computed fork ID {:?} for hardfork {}", - expected_id, computed_id, hardfork - ); - if let Hardfork::Shanghai = hardfork { - if let Some(shangai_id) = spec.shanghai_fork_id() { - assert_eq!( - expected_id, &shangai_id, - "Expected fork ID {:?}, computed fork ID {:?} for Shanghai hardfork", - expected_id, computed_id - ); - } else { - panic!("Expected ForkCondition to return Some for Hardfork::Shanghai"); - } - } - } - } - } - - #[test] - fn test_hardfork_list_display_mainnet() { - assert_eq!( - DisplayHardforks::new(MAINNET.hardforks()).to_string(), - "Pre-merge hard forks (block based): -- Frontier @0 -- Homestead @1150000 -- Dao @1920000 -- Tangerine @2463000 -- SpuriousDragon @2675000 -- Byzantium @4370000 -- Constantinople @7280000 -- Petersburg @7280000 -- Istanbul @9069000 -- MuirGlacier @9200000 -- Berlin @12244000 -- London @12965000 -- ArrowGlacier @13773000 -- GrayGlacier @15050000 -Merge hard forks: -- Paris @58750000000000000000000 (network is not known to be merged) - -Post-merge hard forks (timestamp based): -- Shanghai @1681338455 -" - ); - } - - #[test] - fn test_hardfork_list_ignores_disabled_forks() { - let spec = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(Genesis::default()) - .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) - .with_fork(Hardfork::Shanghai, ForkCondition::Never) - .build(); - assert_eq!( - DisplayHardforks::new(spec.hardforks()).to_string(), - "Pre-merge hard forks (block based): -- Frontier @0 -" - ); - } - - // Tests that the ForkTimestamps are correctly set up. - #[test] - fn test_fork_timestamps() { - let spec = ChainSpec::builder().chain(Chain::mainnet()).genesis(Genesis::default()).build(); - assert!(spec.fork_timestamps.shanghai.is_none()); - - let spec = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(Genesis::default()) - .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(1337)) - .build(); - assert_eq!(spec.fork_timestamps.shanghai, Some(1337)); - assert!(spec.is_shanghai_active_at_timestamp(1337)); - assert!(!spec.is_shanghai_active_at_timestamp(1336)); - } - - // Tests that all predefined timestamps are correctly set up in the chainspecs - #[test] - fn test_predefined_chain_spec_fork_timestamps() { - fn ensure_timestamp_fork_conditions(spec: &ChainSpec) { - // This is a sanity test that ensures we always set all currently known fork timestamps, - // this will fail if a new timestamp based fork condition has added to the hardforks but - // no corresponding entry in the ForkTimestamp types, See also - // [ForkTimestamps::from_hardforks] - - // currently there are only 1 timestamps known: shanghai - let known_timestamp_based_forks = 1; - let num_timestamp_based_forks = - spec.hardforks.values().copied().filter(ForkCondition::is_timestamp).count(); - assert_eq!(num_timestamp_based_forks, known_timestamp_based_forks); - - // ensures all timestamp forks are set - assert!(spec.fork_timestamps.shanghai.is_some()); - } - - for spec in [&*MAINNET, &*SEPOLIA] { - ensure_timestamp_fork_conditions(spec); - } - } - - // Tests that we skip any fork blocks in block #0 (the genesis ruleset) - #[test] - fn ignores_genesis_fork_blocks() { - let spec = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(Genesis::default()) - .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) - .with_fork(Hardfork::Homestead, ForkCondition::Block(0)) - .with_fork(Hardfork::Tangerine, ForkCondition::Block(0)) - .with_fork(Hardfork::SpuriousDragon, ForkCondition::Block(0)) - .with_fork(Hardfork::Byzantium, ForkCondition::Block(0)) - .with_fork(Hardfork::Constantinople, ForkCondition::Block(0)) - .with_fork(Hardfork::Istanbul, ForkCondition::Block(0)) - .with_fork(Hardfork::MuirGlacier, ForkCondition::Block(0)) - .with_fork(Hardfork::Berlin, ForkCondition::Block(0)) - .with_fork(Hardfork::London, ForkCondition::Block(0)) - .with_fork(Hardfork::ArrowGlacier, ForkCondition::Block(0)) - .with_fork(Hardfork::GrayGlacier, ForkCondition::Block(0)) - .build(); - - assert_eq!(spec.hardforks().len(), 12, "12 forks should be active."); - assert_eq!( - spec.fork_id(&Head { number: 1, ..Default::default() }), - ForkId { hash: ForkHash::from(spec.genesis_hash()), next: 0 }, - "the fork ID should be the genesis hash; forks at genesis are ignored for fork filters" - ); - } - - #[test] - fn ignores_duplicate_fork_blocks() { - let empty_genesis = Genesis::default(); - let unique_spec = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(empty_genesis.clone()) - .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) - .with_fork(Hardfork::Homestead, ForkCondition::Block(1)) - .build(); - - let duplicate_spec = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(empty_genesis) - .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) - .with_fork(Hardfork::Homestead, ForkCondition::Block(1)) - .with_fork(Hardfork::Tangerine, ForkCondition::Block(1)) - .build(); - - assert_eq!( - unique_spec.fork_id(&Head { number: 2, ..Default::default() }), - duplicate_spec.fork_id(&Head { number: 2, ..Default::default() }), - "duplicate fork blocks should be deduplicated for fork filters" - ); - } - - #[test] - fn test_chainspec_satisfy() { - let empty_genesis = Genesis::default(); - // happy path test case - let happy_path_case = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(empty_genesis.clone()) - .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) - .with_fork(Hardfork::Homestead, ForkCondition::Block(73)) - .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123)) - .build(); - let happy_path_head = happy_path_case.satisfy(ForkCondition::Timestamp(11313123)); - let happy_path_expected = Head { number: 73, timestamp: 11313123, ..Default::default() }; - assert_eq!( - happy_path_head, happy_path_expected, - "expected satisfy() to return {:#?}, but got {:#?} ", - happy_path_expected, happy_path_head - ); - // multiple timestamp test case (i.e Shanghai -> Cancun) - let multiple_timestamp_fork_case = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(empty_genesis.clone()) - .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) - .with_fork(Hardfork::Homestead, ForkCondition::Block(73)) - .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123)) - .with_fork(Hardfork::Cancun, ForkCondition::Timestamp(11313398)) - .build(); - let multi_timestamp_head = - multiple_timestamp_fork_case.satisfy(ForkCondition::Timestamp(11313398)); - let mult_timestamp_expected = - Head { number: 73, timestamp: 11313398, ..Default::default() }; - assert_eq!( - multi_timestamp_head, mult_timestamp_expected, - "expected satisfy() to return {:#?}, but got {:#?} ", - mult_timestamp_expected, multi_timestamp_head - ); - // no ForkCondition::Block test case - let no_block_fork_case = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(empty_genesis.clone()) - .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123)) - .build(); - let no_block_fork_head = no_block_fork_case.satisfy(ForkCondition::Timestamp(11313123)); - let no_block_fork_expected = Head { number: 0, timestamp: 11313123, ..Default::default() }; - assert_eq!( - no_block_fork_head, no_block_fork_expected, - "expected satisfy() to return {:#?}, but got {:#?} ", - no_block_fork_expected, no_block_fork_head - ); - // spec w/ ForkCondition::TTD with block_num test case (Sepolia merge netsplit edge case) - let fork_cond_ttd_blocknum_case = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(empty_genesis.clone()) - .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) - .with_fork(Hardfork::Homestead, ForkCondition::Block(73)) - .with_fork( - Hardfork::Paris, - ForkCondition::TTD { - fork_block: Some(101), - total_difficulty: U256::from(10_790_000), - }, - ) - .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(11313123)) - .build(); - let fork_cond_ttd_blocknum_head = - fork_cond_ttd_blocknum_case.satisfy(ForkCondition::Timestamp(11313123)); - let fork_cond_ttd_blocknum_expected = - Head { number: 101, timestamp: 11313123, ..Default::default() }; - assert_eq!( - fork_cond_ttd_blocknum_head, fork_cond_ttd_blocknum_expected, - "expected satisfy() to return {:#?}, but got {:#?} ", - fork_cond_ttd_blocknum_expected, fork_cond_ttd_blocknum_head - ); - - // spec w/ only ForkCondition::Block - test the match arm for ForkCondition::Block to ensure - // no regressions, for these ForkConditions(Block/TTD) - a separate chain spec definition is - // technically unecessary - but we include it here for thoroughness - let fork_cond_block_only_case = ChainSpec::builder() - .chain(Chain::mainnet()) - .genesis(empty_genesis) - .with_fork(Hardfork::Frontier, ForkCondition::Block(0)) - .with_fork(Hardfork::Homestead, ForkCondition::Block(73)) - .build(); - let fork_cond_block_only_head = fork_cond_block_only_case.satisfy(ForkCondition::Block(73)); - let fork_cond_block_only_expected = Head { number: 73, ..Default::default() }; - assert_eq!( - fork_cond_block_only_head, fork_cond_block_only_expected, - "expected satisfy() to return {:#?}, but got {:#?} ", - fork_cond_block_only_expected, fork_cond_block_only_head - ); - // Fork::ConditionTTD test case without a new chain spec to demonstrate ChainSpec::satisfy - // is independent of ChainSpec for this(these - including ForkCondition::Block) match arm(s) - let fork_cond_ttd_no_new_spec = fork_cond_block_only_case.satisfy(ForkCondition::TTD { - fork_block: None, - total_difficulty: U256::from(10_790_000), - }); - let fork_cond_ttd_no_new_spec_expected = - Head { total_difficulty: U256::from(10_790_000), ..Default::default() }; - assert_eq!( - fork_cond_ttd_no_new_spec, fork_cond_ttd_no_new_spec_expected, - "expected satisfy() to return {:#?}, but got {:#?} ", - fork_cond_ttd_no_new_spec_expected, fork_cond_ttd_no_new_spec - ); - } - - #[test] - fn mainnet_hardfork_fork_ids() { - test_hardfork_fork_ids( - &MAINNET, - &[ - ( - Hardfork::Frontier, - ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }, - ), - ( - Hardfork::Homestead, - ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }, - ), - (Hardfork::Dao, ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }), - ( - Hardfork::Tangerine, - ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }, - ), - ( - Hardfork::SpuriousDragon, - ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }, - ), - ( - Hardfork::Byzantium, - ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }, - ), - ( - Hardfork::Constantinople, - ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, - ), - ( - Hardfork::Petersburg, - ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, - ), - ( - Hardfork::Istanbul, - ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }, - ), - ( - Hardfork::MuirGlacier, - ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }, - ), - ( - Hardfork::Berlin, - ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, - ), - ( - Hardfork::London, - ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, - ), - ( - Hardfork::ArrowGlacier, - ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }, - ), - ( - Hardfork::GrayGlacier, - ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 }, - ), - (Hardfork::Shanghai, ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 0 }), - ], - ); - } - - #[test] - fn goerli_hardfork_fork_ids() { - test_hardfork_fork_ids( - &GOERLI, - &[ - ( - Hardfork::Frontier, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Hardfork::Homestead, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Hardfork::Tangerine, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Hardfork::SpuriousDragon, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Hardfork::Byzantium, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Hardfork::Constantinople, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Hardfork::Petersburg, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Hardfork::Istanbul, - ForkId { hash: ForkHash([0xc2, 0x5e, 0xfa, 0x5c]), next: 4460644 }, - ), - ( - Hardfork::Berlin, - ForkId { hash: ForkHash([0x75, 0x7a, 0x1c, 0x47]), next: 5062605 }, - ), - ( - Hardfork::London, - ForkId { hash: ForkHash([0xb8, 0xc6, 0x29, 0x9d]), next: 1678832736 }, - ), - (Hardfork::Shanghai, ForkId { hash: ForkHash([0xf9, 0x84, 0x3a, 0xbf]), next: 0 }), - ], - ); - } - - #[test] - fn sepolia_hardfork_fork_ids() { - test_hardfork_fork_ids( - &SEPOLIA, - &[ - ( - Hardfork::Frontier, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::Homestead, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::Tangerine, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::SpuriousDragon, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::Byzantium, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::Constantinople, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::Petersburg, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::Istanbul, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::Berlin, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::London, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Hardfork::Paris, - ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 }, - ), - (Hardfork::Shanghai, ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 0 }), - ], - ); - } - - #[test] - fn mainnet_forkids() { - test_fork_ids( - &MAINNET, - &[ - ( - Head { number: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }, - ), - ( - Head { number: 1150000, ..Default::default() }, - ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }, - ), - ( - Head { number: 1920000, ..Default::default() }, - ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }, - ), - ( - Head { number: 2463000, ..Default::default() }, - ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }, - ), - ( - Head { number: 2675000, ..Default::default() }, - ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }, - ), - ( - Head { number: 4370000, ..Default::default() }, - ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }, - ), - ( - Head { number: 7280000, ..Default::default() }, - ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, - ), - ( - Head { number: 9069000, ..Default::default() }, - ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }, - ), - ( - Head { number: 9200000, ..Default::default() }, - ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }, - ), - ( - Head { number: 12244000, ..Default::default() }, - ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, - ), - ( - Head { number: 12965000, ..Default::default() }, - ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, - ), - ( - Head { number: 13773000, ..Default::default() }, - ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }, - ), - ( - Head { number: 15050000, ..Default::default() }, - ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 }, - ), - // First Shanghai block - ( - Head { number: 20000000, timestamp: 1681338455, ..Default::default() }, - ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 0 }, - ), - // Future Shanghai block - ( - Head { number: 20000000, timestamp: 2000000000, ..Default::default() }, - ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 0 }, - ), - ], - ); - } - - #[test] - fn goerli_forkids() { - test_fork_ids( - &GOERLI, - &[ - ( - Head { number: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Head { number: 1561650, ..Default::default() }, - ForkId { hash: ForkHash([0xa3, 0xf5, 0xab, 0x08]), next: 1561651 }, - ), - ( - Head { number: 1561651, ..Default::default() }, - ForkId { hash: ForkHash([0xc2, 0x5e, 0xfa, 0x5c]), next: 4460644 }, - ), - ( - Head { number: 4460643, ..Default::default() }, - ForkId { hash: ForkHash([0xc2, 0x5e, 0xfa, 0x5c]), next: 4460644 }, - ), - ( - Head { number: 4460644, ..Default::default() }, - ForkId { hash: ForkHash([0x75, 0x7a, 0x1c, 0x47]), next: 5062605 }, - ), - ( - Head { number: 5062605, ..Default::default() }, - ForkId { hash: ForkHash([0xb8, 0xc6, 0x29, 0x9d]), next: 1678832736 }, - ), - ( - Head { number: 6000000, timestamp: 1678832735, ..Default::default() }, - ForkId { hash: ForkHash([0xb8, 0xc6, 0x29, 0x9d]), next: 1678832736 }, - ), - // First Shanghai block - ( - Head { number: 6000001, timestamp: 1678832736, ..Default::default() }, - ForkId { hash: ForkHash([0xf9, 0x84, 0x3a, 0xbf]), next: 0 }, - ), - // Future Shanghai block - ( - Head { number: 6500000, timestamp: 1678832736, ..Default::default() }, - ForkId { hash: ForkHash([0xf9, 0x84, 0x3a, 0xbf]), next: 0 }, - ), - ], - ); - } - - #[test] - fn sepolia_forkids() { - test_fork_ids( - &SEPOLIA, - &[ - ( - Head { number: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Head { number: 1735370, ..Default::default() }, - ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 }, - ), - ( - Head { number: 1735371, ..Default::default() }, - ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 }, - ), - ( - Head { number: 1735372, timestamp: 1677557087, ..Default::default() }, - ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 }, - ), - ( - Head { number: 1735372, timestamp: 1677557088, ..Default::default() }, - ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 0 }, - ), - ], - ); - } - - #[test] - fn dev_forkids() { - test_fork_ids( - &DEV, - &[( - Head { number: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x45, 0xb8, 0x36, 0x12]), next: 0 }, - )], - ) - } - - #[cfg(feature = "optimism")] - #[test] - fn optimism_goerli_forkids() { - test_fork_ids( - &OP_GOERLI, - &[ - ( - Head { number: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x6d, 0x63, 0x76, 0xbe]), next: 4061224 }, - ), - ( - Head { number: 4061224, timestamp: 1679079599, ..Default::default() }, - ForkId { hash: ForkHash([0x03, 0x47, 0x85, 0x69]), next: 1679079600 }, - ), - ( - Head { number: 4061224, timestamp: 1679079600, ..Default::default() }, - ForkId { hash: ForkHash([0x6d, 0x43, 0x1d, 0x6c]), next: 0 }, - ), - ], - ); - } - - /// Checks that time-based forks work - /// - /// This is based off of the test vectors here: https://github.com/ethereum/go-ethereum/blob/5c8cc10d1e05c23ff1108022f4150749e73c0ca1/core/forkid/forkid_test.go#L155-L188 - #[test] - fn timestamped_forks() { - let mainnet_with_shanghai = ChainSpecBuilder::mainnet() - .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(1668000000)) - .build(); - test_fork_ids( - &mainnet_with_shanghai, - &[ - ( - Head { number: 0, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }, - ), // Unsynced - ( - Head { number: 1149999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 }, - ), // Last Frontier block - ( - Head { number: 1150000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }, - ), // First Homestead block - ( - Head { number: 1919999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 }, - ), // Last Homestead block - ( - Head { number: 1920000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }, - ), // First DAO block - ( - Head { number: 2462999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 }, - ), // Last DAO block - ( - Head { number: 2463000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }, - ), // First Tangerine block - ( - Head { number: 2674999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 }, - ), // Last Tangerine block - ( - Head { number: 2675000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }, - ), // First Spurious block - ( - Head { number: 4369999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 }, - ), // Last Spurious block - ( - Head { number: 4370000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }, - ), // First Byzantium block - ( - Head { number: 7279999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 }, - ), // Last Byzantium block - ( - Head { number: 7280000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, - ), // First and last Constantinople, first Petersburg block - ( - Head { number: 9068999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 }, - ), // Last Petersburg block - ( - Head { number: 9069000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }, - ), // First Istanbul and first Muir Glacier block - ( - Head { number: 9199999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 }, - ), // Last Istanbul and first Muir Glacier block - ( - Head { number: 9200000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }, - ), // First Muir Glacier block - ( - Head { number: 12243999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 }, - ), // Last Muir Glacier block - ( - Head { number: 12244000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, - ), // First Berlin block - ( - Head { number: 12964999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 }, - ), // Last Berlin block - ( - Head { number: 12965000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, - ), // First London block - ( - Head { number: 13772999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 }, - ), // Last London block - ( - Head { number: 13773000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }, - ), // First Arrow Glacier block - ( - Head { number: 15049999, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 }, - ), // Last Arrow Glacier block - ( - Head { number: 15050000, timestamp: 0, ..Default::default() }, - ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1668000000 }, - ), // First Gray Glacier block - ( - Head { number: 19999999, timestamp: 1667999999, ..Default::default() }, - ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1668000000 }, - ), // Last Gray Glacier block - ( - Head { number: 20000000, timestamp: 1668000000, ..Default::default() }, - ForkId { hash: ForkHash([0x71, 0x14, 0x76, 0x44]), next: 0 }, - ), // First Shanghai block - ( - Head { number: 20000000, timestamp: 2668000000, ..Default::default() }, - ForkId { hash: ForkHash([0x71, 0x14, 0x76, 0x44]), next: 0 }, - ), // Future Shanghai block - ], - ); - } - - /// Constructs a [ChainSpec] with the given [ChainSpecBuilder], shanghai, and cancun fork - /// timestamps. - fn construct_chainspec( - builder: ChainSpecBuilder, - shanghai_time: u64, - cancun_time: u64, - ) -> ChainSpec { - builder - .with_fork(Hardfork::Shanghai, ForkCondition::Timestamp(shanghai_time)) - .with_fork(Hardfork::Cancun, ForkCondition::Timestamp(cancun_time)) - .build() - } - - /// Tests that time-based forks which are active at genesis are not included in forkid hash. - /// - /// This is based off of the test vectors here: - /// - #[test] - fn test_timestamp_fork_in_genesis() { - let timestamp = 1690475657u64; - let default_spec_builder = ChainSpecBuilder::default() - .chain(Chain::Id(1337)) - .genesis(Genesis::default().with_timestamp(timestamp)) - .paris_activated(); - - // test format: (chain spec, expected next value) - the forkhash will be determined by the - // genesis hash of the constructed chainspec - let tests = [ - ( - construct_chainspec(default_spec_builder.clone(), timestamp - 1, timestamp + 1), - timestamp + 1, - ), - ( - construct_chainspec(default_spec_builder.clone(), timestamp, timestamp + 1), - timestamp + 1, - ), - ( - construct_chainspec(default_spec_builder, timestamp + 1, timestamp + 2), - timestamp + 1, - ), - ]; - - for (spec, expected_timestamp) in tests { - let got_forkid = spec.fork_id(&Head { number: 0, timestamp: 0, ..Default::default() }); - // This is slightly different from the geth test because we use the shanghai timestamp - // to determine whether or not to include a withdrawals root in the genesis header. - // This makes the genesis hash different, and as a result makes the ChainSpec fork hash - // different. - let genesis_hash = spec.genesis_hash(); - let expected_forkid = - ForkId { hash: ForkHash::from(genesis_hash), next: expected_timestamp }; - assert_eq!(got_forkid, expected_forkid); - } - } - - /// Checks that the fork is not active at a terminal ttd block. - #[test] - fn check_terminal_ttd() { - let chainspec = ChainSpecBuilder::mainnet().build(); - - // Check that Paris is not active on terminal PoW block #15537393. - let terminal_block_ttd = U256::from(58750003716598352816469_u128); - let terminal_block_difficulty = U256::from(11055787484078698_u128); - assert!(!chainspec - .fork(Hardfork::Paris) - .active_at_ttd(terminal_block_ttd, terminal_block_difficulty)); - - // Check that Paris is active on first PoS block #15537394. - let first_pos_block_ttd = U256::from(58750003716598352816469_u128); - let first_pos_difficulty = U256::ZERO; - assert!(chainspec - .fork(Hardfork::Paris) - .active_at_ttd(first_pos_block_ttd, first_pos_difficulty)); - } - - #[test] - fn geth_genesis_with_shanghai() { - let geth_genesis = r#" - { - "config": { - "chainId": 1337, - "homesteadBlock": 0, - "eip150Block": 0, - "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0, - "muirGlacierBlock": 0, - "berlinBlock": 0, - "londonBlock": 0, - "arrowGlacierBlock": 0, - "grayGlacierBlock": 0, - "shanghaiTime": 0, - "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true, - "ethash": {} - }, - "nonce": "0x0", - "timestamp": "0x0", - "extraData": "0x", - "gasLimit": "0x4c4b40", - "difficulty": "0x1", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": "0x0000000000000000000000000000000000000000", - "alloc": { - "658bdf435d810c91414ec09147daa6db62406379": { - "balance": "0x487a9a304539440000" - }, - "aa00000000000000000000000000000000000000": { - "code": "0x6042", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000", - "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000", - "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303" - }, - "balance": "0x1", - "nonce": "0x1" - }, - "bb00000000000000000000000000000000000000": { - "code": "0x600154600354", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000", - "0x0100000000000000000000000000000000000000000000000000000000000000": "0x0100000000000000000000000000000000000000000000000000000000000000", - "0x0200000000000000000000000000000000000000000000000000000000000000": "0x0200000000000000000000000000000000000000000000000000000000000000", - "0x0300000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000303" - }, - "balance": "0x2", - "nonce": "0x1" - } - }, - "number": "0x0", - "gasUsed": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "baseFeePerGas": "0x3b9aca00" - } - "#; - - let genesis: Genesis = serde_json::from_str(geth_genesis).unwrap(); - let chainspec = ChainSpec::from(genesis); - - // assert a bunch of hardforks that should be set - assert_eq!( - chainspec.hardforks.get(&Hardfork::Homestead).unwrap(), - &ForkCondition::Block(0) - ); - assert_eq!( - chainspec.hardforks.get(&Hardfork::Tangerine).unwrap(), - &ForkCondition::Block(0) - ); - assert_eq!( - chainspec.hardforks.get(&Hardfork::SpuriousDragon).unwrap(), - &ForkCondition::Block(0) - ); - assert_eq!( - chainspec.hardforks.get(&Hardfork::Byzantium).unwrap(), - &ForkCondition::Block(0) - ); - assert_eq!( - chainspec.hardforks.get(&Hardfork::Constantinople).unwrap(), - &ForkCondition::Block(0) - ); - assert_eq!( - chainspec.hardforks.get(&Hardfork::Petersburg).unwrap(), - &ForkCondition::Block(0) - ); - assert_eq!(chainspec.hardforks.get(&Hardfork::Istanbul).unwrap(), &ForkCondition::Block(0)); - assert_eq!( - chainspec.hardforks.get(&Hardfork::MuirGlacier).unwrap(), - &ForkCondition::Block(0) - ); - assert_eq!(chainspec.hardforks.get(&Hardfork::Berlin).unwrap(), &ForkCondition::Block(0)); - assert_eq!(chainspec.hardforks.get(&Hardfork::London).unwrap(), &ForkCondition::Block(0)); - assert_eq!( - chainspec.hardforks.get(&Hardfork::ArrowGlacier).unwrap(), - &ForkCondition::Block(0) - ); - assert_eq!( - chainspec.hardforks.get(&Hardfork::GrayGlacier).unwrap(), - &ForkCondition::Block(0) - ); - - // including time based hardforks - assert_eq!( - chainspec.hardforks.get(&Hardfork::Shanghai).unwrap(), - &ForkCondition::Timestamp(0) - ); - - // alloc key -> expected rlp mapping - let key_rlp = vec![ - (hex!("658bdf435d810c91414ec09147daa6db62406379"), &hex!("f84d8089487a9a304539440000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")[..]), - (hex!("aa00000000000000000000000000000000000000"), &hex!("f8440101a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0ce92c756baff35fa740c3557c1a971fd24d2d35b7c8e067880d50cd86bb0bc99")[..]), - (hex!("bb00000000000000000000000000000000000000"), &hex!("f8440102a08afc95b7d18a226944b9c2070b6bda1c3a36afcc3730429d47579c94b9fe5850a0e25a53cbb501cec2976b393719c63d832423dd70a458731a0b64e4847bbca7d2")[..]), - ]; - - for (key, expected_rlp) in key_rlp { - let account = chainspec.genesis.alloc.get(&key).expect("account should exist"); - let mut account_rlp = BytesMut::new(); - account.encode(&mut account_rlp); - assert_eq!(account_rlp, expected_rlp) - } - - assert_eq!(chainspec.genesis_hash, None); - let expected_state_root: B256 = - hex!("078dc6061b1d8eaa8493384b59c9c65ceb917201221d08b80c4de6770b6ec7e7").into(); - assert_eq!(chainspec.genesis_header().state_root, expected_state_root); - - let expected_withdrawals_hash: B256 = - hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(); - assert_eq!(chainspec.genesis_header().withdrawals_root, Some(expected_withdrawals_hash)); - - let expected_hash: B256 = - hex!("1fc027d65f820d3eef441ebeec139ebe09e471cf98516dce7b5643ccb27f418c").into(); - let hash = chainspec.genesis_hash(); - assert_eq!(hash, expected_hash); - } - - #[test] - fn hive_geth_json() { - let hive_json = r#" - { - "nonce": "0x0000000000000042", - "difficulty": "0x2123456", - "mixHash": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234", - "coinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "timestamp": "0x123456", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "extraData": "0xfafbfcfd", - "gasLimit": "0x2fefd8", - "alloc": { - "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": { - "balance": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - }, - "e6716f9544a56c530d868e4bfbacb172315bdead": { - "balance": "0x11", - "code": "0x12" - }, - "b9c015918bdaba24b4ff057a92a3873d6eb201be": { - "balance": "0x21", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22" - } - }, - "1a26338f0d905e295fccb71fa9ea849ffa12aaf4": { - "balance": "0x31", - "nonce": "0x32" - }, - "0000000000000000000000000000000000000001": { - "balance": "0x41" - }, - "0000000000000000000000000000000000000002": { - "balance": "0x51" - }, - "0000000000000000000000000000000000000003": { - "balance": "0x61" - }, - "0000000000000000000000000000000000000004": { - "balance": "0x71" - } - }, - "config": { - "ethash": {}, - "chainId": 10, - "homesteadBlock": 0, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0 - } - } - "#; - - let _genesis = serde_json::from_str::(hive_json).unwrap(); - let genesis = serde_json::from_str::(hive_json).unwrap(); - let chainspec: ChainSpec = genesis.into(); - assert_eq!(chainspec.genesis_hash, None); - assert_eq!(chainspec.chain, Chain::Named(NamedChain::Optimism)); - let expected_state_root: B256 = - hex!("9a6049ac535e3dc7436c189eaa81c73f35abd7f282ab67c32944ff0301d63360").into(); - assert_eq!(chainspec.genesis_header().state_root, expected_state_root); - let hard_forks = vec![ - Hardfork::Byzantium, - Hardfork::Homestead, - Hardfork::Istanbul, - Hardfork::Petersburg, - Hardfork::Constantinople, - ]; - for ref fork in hard_forks { - assert_eq!(chainspec.hardforks.get(fork).unwrap(), &ForkCondition::Block(0)); - } - - let expected_hash: B256 = - hex!("5ae31c6522bd5856129f66be3d582b842e4e9faaa87f21cce547128339a9db3c").into(); - let hash = chainspec.genesis_header().hash_slow(); - assert_eq!(hash, expected_hash); - } - - #[test] - fn test_parse_genesis_json() { - let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x1337"}"#; - let genesis: Genesis = serde_json::from_str(s).unwrap(); - let acc = genesis - .alloc - .get(&"0xaa00000000000000000000000000000000000000".parse::
().unwrap()) - .unwrap(); - assert_eq!(acc.balance, U256::from(1)); - assert_eq!(genesis.base_fee_per_gas, Some(0x1337)); - } - - #[test] - fn test_parse_cancun_genesis_json() { - let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0,"cancunTime":4661},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x3b9aca00"}"#; - let genesis: Genesis = serde_json::from_str(s).unwrap(); - let acc = genesis - .alloc - .get(&"0xaa00000000000000000000000000000000000000".parse::
().unwrap()) - .unwrap(); - assert_eq!(acc.balance, U256::from(1)); - // assert that the cancun time was picked up - assert_eq!(genesis.config.cancun_time, Some(4661)); - } - - #[test] - fn test_parse_cancun_genesis_all_formats() { - let s = r#"{"config":{"ethash":{},"chainId":1337,"homesteadBlock":0,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"berlinBlock":0,"londonBlock":0,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true,"shanghaiTime":0,"cancunTime":4661},"nonce":"0x0","timestamp":"0x0","extraData":"0x","gasLimit":"0x4c4b40","difficulty":"0x1","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","alloc":{"658bdf435d810c91414ec09147daa6db62406379":{"balance":"0x487a9a304539440000"},"aa00000000000000000000000000000000000000":{"code":"0x6042","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x1","nonce":"0x1"},"bb00000000000000000000000000000000000000":{"code":"0x600154600354","storage":{"0x0000000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000000","0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000","0x0200000000000000000000000000000000000000000000000000000000000000":"0x0200000000000000000000000000000000000000000000000000000000000000","0x0300000000000000000000000000000000000000000000000000000000000000":"0x0000000000000000000000000000000000000000000000000000000000000303"},"balance":"0x2","nonce":"0x1"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeePerGas":"0x3b9aca00"}"#; - let genesis: AllGenesisFormats = serde_json::from_str(s).unwrap(); - - // this should be the genesis format - let genesis = match genesis { - AllGenesisFormats::Geth(genesis) => genesis, - _ => panic!("expected geth genesis format"), - }; - - // assert that the alloc was picked up - let acc = genesis - .alloc - .get(&"0xaa00000000000000000000000000000000000000".parse::
().unwrap()) - .unwrap(); - assert_eq!(acc.balance, U256::from(1)); - // assert that the cancun time was picked up - assert_eq!(genesis.config.cancun_time, Some(4661)); - } - - #[test] - fn test_default_cancun_header_forkhash() { - // set the gas limit from the hive test genesis according to the hash - let genesis = Genesis { gas_limit: 0x2fefd8u64, ..Default::default() }; - let default_chainspec = ChainSpecBuilder::default() - .chain(Chain::Id(1337)) - .genesis(genesis) - .cancun_activated() - .build(); - let mut header = default_chainspec.genesis_header(); - - // set the state root to the same as in the hive test the hash was pulled from - header.state_root = - B256::from_str("0x62e2595e017f0ca23e08d17221010721a71c3ae932f4ea3cb12117786bb392d4") - .unwrap(); - - // shanghai is activated so we should have a withdrawals root - assert_eq!(header.withdrawals_root, Some(EMPTY_WITHDRAWALS)); - - // cancun is activated so we should have a zero parent beacon block root, zero blob gas - // used, and zero excess blob gas - assert_eq!(header.parent_beacon_block_root, Some(B256::ZERO)); - assert_eq!(header.blob_gas_used, Some(0)); - assert_eq!(header.excess_blob_gas, Some(0)); - println!("header: {:?}", header); - - // check the genesis hash - let genesis_hash = header.hash_slow(); - let expected_hash = - b256!("16bb7c59613a5bad3f7c04a852fd056545ade2483968d9a25a1abb05af0c4d37"); - assert_eq!(genesis_hash, expected_hash); - - // check that the forkhash is correct - let expected_forkhash = ForkHash(hex!("8062457a")); - assert_eq!(ForkHash::from(genesis_hash), expected_forkhash); - } - - #[test] - fn holesky_paris_activated_at_genesis() { - assert!(HOLESKY - .fork(Hardfork::Paris) - .active_at_ttd(HOLESKY.genesis.difficulty, HOLESKY.genesis.difficulty)); - } - - #[test] - fn test_all_genesis_formats_deserialization() { - // custom genesis with chain config - let config = ChainConfig { - chain_id: 2600, - homestead_block: Some(0), - eip150_block: Some(0), - eip155_block: Some(0), - eip158_block: Some(0), - byzantium_block: Some(0), - constantinople_block: Some(0), - petersburg_block: Some(0), - istanbul_block: Some(0), - berlin_block: Some(0), - london_block: Some(0), - shanghai_time: Some(0), - terminal_total_difficulty: Some(U256::ZERO), - terminal_total_difficulty_passed: true, - ..Default::default() - }; - // genesis - let genesis = Genesis { - config, - nonce: 0, - timestamp: 1698688670, - gas_limit: 5000, - difficulty: U256::ZERO, - mix_hash: B256::ZERO, - coinbase: Address::ZERO, - ..Default::default() - }; - - // seed accounts after genesis struct created - let address = hex!("6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b").into(); - let account = GenesisAccount::default().with_balance(U256::from(33)); - let genesis = genesis.extend_accounts(HashMap::from([(address, account)])); - - // ensure genesis is deserialized correctly - let serialized_genesis = serde_json::to_string(&genesis).unwrap(); - let deserialized_genesis: AllGenesisFormats = - serde_json::from_str(&serialized_genesis).unwrap(); - assert!(matches!(deserialized_genesis, AllGenesisFormats::Geth(_))); - - // build chain - let chain_spec = ChainSpecBuilder::default() - .chain(2600.into()) - .genesis(genesis) - .cancun_activated() - .build(); - - // ensure chain spec is deserialized correctly - let serialized_chain_spec = serde_json::to_string(&chain_spec).unwrap(); - let deserialized_chain_spec: AllGenesisFormats = - serde_json::from_str(&serialized_chain_spec).unwrap(); - assert!(matches!(deserialized_chain_spec, AllGenesisFormats::Reth(_))) - } -} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 63ad9c14a062..3a0a8a865d38 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -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; @@ -53,23 +51,16 @@ pub use block::{ Block, BlockBody, BlockBodyRoots, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag, BlockWithSenders, ForkBlock, RpcBlockHash, SealedBlock, SealedBlockWithSenders, }; -pub use bytes::{self, Buf, BufMut, BytesMut}; -pub use chain::{ - AllGenesisFormats, BaseFeeParams, Chain, ChainInfo, ChainSpec, ChainSpecBuilder, - DisplayHardforks, ForkCondition, ForkTimestamps, NamedChain, DEV, GOERLI, HOLESKY, MAINNET, - SEPOLIA, -}; +pub use bytes::{Buf, BufMut, BytesMut}; +pub use chain::BaseFeeParams; #[cfg(feature = "optimism")] -pub use chain::{BASE_GOERLI, BASE_MAINNET, OP_GOERLI}; pub use compression::*; pub use constants::{ DEV_GENESIS_HASH, EMPTY_OMMER_ROOT_HASH, GOERLI_GENESIS_HASH, HOLESKY_GENESIS_HASH, 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 integer_list::IntegerList; pub use log::{logs_bloom, Log}; diff --git a/crates/primitives/src/proofs.rs b/crates/primitives/src/proofs.rs index 6bf4ab8f0eac..cef0540d27ca 100644 --- a/crates/primitives/src/proofs.rs +++ b/crates/primitives/src/proofs.rs @@ -187,8 +187,7 @@ mod tests { constants::EMPTY_ROOT_HASH, hex_literal::hex, proofs::{calculate_receipt_root, calculate_transaction_root, genesis_state_root}, - Address, Block, GenesisAccount, Log, Receipt, ReceiptWithBloom, TxType, B256, GOERLI, - HOLESKY, MAINNET, SEPOLIA, U256, + Address, Block, GenesisAccount, Log, Receipt, ReceiptWithBloom, TxType, B256, U256, }; use alloy_primitives::b256; use alloy_rlp::Decodable; @@ -508,39 +507,4 @@ b256!("000000000000000000000000000000000000000000000000000000000011a1d3"), assert_eq!(root, expected_root); } } - - #[test] - fn test_chain_state_roots() { - let expected_mainnet_state_root = - b256!("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544"); - let calculated_mainnet_state_root = genesis_state_root(&MAINNET.genesis.alloc); - assert_eq!( - expected_mainnet_state_root, calculated_mainnet_state_root, - "mainnet state root mismatch" - ); - - let expected_goerli_state_root = - b256!("5d6cded585e73c4e322c30c2f782a336316f17dd85a4863b9d838d2d4b8b3008"); - let calculated_goerli_state_root = genesis_state_root(&GOERLI.genesis.alloc); - assert_eq!( - expected_goerli_state_root, calculated_goerli_state_root, - "goerli state root mismatch" - ); - - let expected_sepolia_state_root = - b256!("5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494"); - let calculated_sepolia_state_root = genesis_state_root(&SEPOLIA.genesis.alloc); - assert_eq!( - expected_sepolia_state_root, calculated_sepolia_state_root, - "sepolia state root mismatch" - ); - - let expected_holesky_state_root = - b256!("69d8c9d72f6fa4ad42d4702b433707212f90db395eb54dc20bc85de253788783"); - let calculated_holesky_state_root = genesis_state_root(&HOLESKY.genesis.alloc); - assert_eq!( - expected_holesky_state_root, calculated_holesky_state_root, - "holesky state root mismatch" - ); - } } diff --git a/crates/primitives/src/revm/mod.rs b/crates/primitives/src/revm/mod.rs index 14bafa18e75a..674afbd5eece 100644 --- a/crates/primitives/src/revm/mod.rs +++ b/crates/primitives/src/revm/mod.rs @@ -10,11 +10,3 @@ /// These utilities facilitate interoperability and data exchange between Revm and Reth /// implementations. pub mod compat; -/// Reth block execution/validation configuration and constants -pub mod config; -/// The `env` module provides essential utilities for managing Ethereum transaction and block -/// environments. -/// -/// It includes functions to fill transaction and block environments with relevant data, handle -/// system contract calls, and recover the signer of Ethereum headers. -pub mod env; diff --git a/crates/prune/Cargo.toml b/crates/prune/Cargo.toml index c451667a30ab..04a9ff3d9533 100644 --- a/crates/prune/Cargo.toml +++ b/crates/prune/Cargo.toml @@ -11,6 +11,7 @@ description = "Pruning implementation" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-db.workspace = true reth-provider.workspace = true reth-interfaces.workspace = true diff --git a/crates/prune/src/pruner.rs b/crates/prune/src/pruner.rs index 12214d6d3e26..12fafb6ce021 100644 --- a/crates/prune/src/pruner.rs +++ b/crates/prune/src/pruner.rs @@ -6,7 +6,8 @@ use crate::{ Metrics, PrunerError, PrunerEvent, }; use reth_db::database::Database; -use reth_primitives::{BlockNumber, ChainSpec, PruneMode, PruneProgress, PruneSegment}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::{BlockNumber, PruneMode, PruneProgress, PruneSegment}; use reth_provider::{ProviderFactory, PruneCheckpointReader}; use reth_snapshot::HighestSnapshotsTracker; use reth_tokio_util::EventListeners; @@ -253,7 +254,7 @@ impl Pruner { mod tests { use crate::Pruner; use reth_db::test_utils::create_test_rw_db; - use reth_primitives::MAINNET; + use reth_ethereum_forks::MAINNET; use tokio::sync::watch; #[test] diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml index 18f74ecf222f..d0f0335f3f9e 100644 --- a/crates/revm/Cargo.toml +++ b/crates/revm/Cargo.toml @@ -11,6 +11,7 @@ description = "reth specific revm utilities" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-provider.workspace = true reth-revm-inspectors.workspace = true @@ -29,6 +30,7 @@ reth-trie.workspace = true optimism = [ "revm/optimism", "reth-primitives/optimism", + "reth-ethereum-forks/optimism", "reth-consensus-common/optimism", "reth-interfaces/optimism", ] diff --git a/crates/revm/src/factory.rs b/crates/revm/src/factory.rs index c35b1ad11af6..659e07a4b871 100644 --- a/crates/revm/src/factory.rs +++ b/crates/revm/src/factory.rs @@ -3,7 +3,7 @@ use crate::{ processor::EVMProcessor, stack::{InspectorStack, InspectorStackConfig}, }; -use reth_primitives::ChainSpec; +use reth_ethereum_forks::ChainSpec; use reth_provider::{ExecutorFactory, PrunableBlockExecutor, StateProvider}; use std::sync::Arc; diff --git a/crates/revm/src/optimism/mod.rs b/crates/revm/src/optimism/mod.rs index ced44bd91ca9..0b8d18991cfb 100644 --- a/crates/revm/src/optimism/mod.rs +++ b/crates/revm/src/optimism/mod.rs @@ -1,5 +1,6 @@ +use reth_ethereum_forks::{ChainSpec, Hardfork}; use reth_interfaces::executor::{self as reth_executor, BlockExecutionError}; -use reth_primitives::{Block, Bytes, ChainSpec, Hardfork, TransactionKind, U256}; +use reth_primitives::{Block, Bytes, TransactionKind, U256}; use revm::{ primitives::{BedrockSpec, RegolithSpec}, L1BlockInfo, diff --git a/crates/revm/src/optimism/processor.rs b/crates/revm/src/optimism/processor.rs index cfdc0660d141..3718d6306a5d 100644 --- a/crates/revm/src/optimism/processor.rs +++ b/crates/revm/src/optimism/processor.rs @@ -1,7 +1,7 @@ +use reth_ethereum_forks::Hardfork; use reth_interfaces::executor::{BlockExecutionError, BlockValidationError}; use reth_primitives::{ - revm::compat::into_reth_log, revm_primitives::ResultAndState, Address, Block, Hardfork, - Receipt, U256, + revm::compat::into_reth_log, revm_primitives::ResultAndState, Address, Block, Receipt, U256, }; use reth_provider::{BlockExecutor, BlockExecutorStats, BundleStateWithReceipts}; use revm::DatabaseCommit; diff --git a/crates/revm/src/processor.rs b/crates/revm/src/processor.rs index 1f30ef4dd985..4116c5e73604 100644 --- a/crates/revm/src/processor.rs +++ b/crates/revm/src/processor.rs @@ -4,14 +4,17 @@ use crate::{ stack::{InspectorStack, InspectorStackConfig}, state_change::{apply_beacon_root_contract_call, post_block_balance_increments}, }; +use reth_ethereum_forks::{ + env::{fill_cfg_and_block_env, fill_tx_env}, + ChainSpec, Hardfork, +}; use reth_interfaces::{ executor::{BlockExecutionError, BlockValidationError}, RethError, }; use reth_primitives::{ - revm::env::{fill_cfg_and_block_env, fill_tx_env}, - Address, Block, BlockNumber, Bloom, ChainSpec, GotExpected, Hardfork, Header, PruneMode, - PruneModes, PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, B256, + Address, Block, BlockNumber, Bloom, GotExpected, Header, PruneMode, PruneModes, + PruneSegmentError, Receipt, ReceiptWithBloom, Receipts, TransactionSigned, B256, MINIMUM_PRUNING_DISTANCE, U256, }; use reth_provider::{BlockExecutor, BlockExecutorStats, PrunableBlockExecutor, StateProvider}; @@ -557,13 +560,14 @@ pub fn verify_receipt<'a>( #[cfg(test)] mod tests { use super::*; + use reth_ethereum_forks::{ChainSpecBuilder, ForkCondition, MAINNET}; use reth_interfaces::provider::ProviderResult; use reth_primitives::{ bytes, constants::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS}, keccak256, trie::AccountProof, - Account, Bytecode, Bytes, ChainSpecBuilder, ForkCondition, StorageKey, MAINNET, + Account, Bytecode, Bytes, StorageKey, }; use reth_provider::{ AccountReader, BlockHashReader, BundleStateWithReceipts, StateRootProvider, diff --git a/crates/revm/src/state_change.rs b/crates/revm/src/state_change.rs index 6fe24790f401..a771a6f45831 100644 --- a/crates/revm/src/state_change.rs +++ b/crates/revm/src/state_change.rs @@ -1,9 +1,7 @@ use reth_consensus_common::calc; +use reth_ethereum_forks::{env::fill_tx_env_with_beacon_root_contract_call, ChainSpec}; use reth_interfaces::executor::{BlockExecutionError, BlockValidationError}; -use reth_primitives::{ - constants::SYSTEM_ADDRESS, revm::env::fill_tx_env_with_beacon_root_contract_call, Address, - ChainSpec, Header, Withdrawal, B256, U256, -}; +use reth_primitives::{constants::SYSTEM_ADDRESS, Address, Header, Withdrawal, B256, U256}; use revm::{Database, DatabaseCommit, EVM}; use std::collections::HashMap; diff --git a/crates/rpc/rpc-api/Cargo.toml b/crates/rpc/rpc-api/Cargo.toml index 58d0349d388a..3efbefdc4fba 100644 --- a/crates/rpc/rpc-api/Cargo.toml +++ b/crates/rpc/rpc-api/Cargo.toml @@ -11,6 +11,7 @@ description = "Reth RPC interfaces" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-rpc-types.workspace = true # misc diff --git a/crates/rpc/rpc-builder/Cargo.toml b/crates/rpc/rpc-builder/Cargo.toml index 6ae2e8a1dc46..9c06c9ad904f 100644 --- a/crates/rpc/rpc-builder/Cargo.toml +++ b/crates/rpc/rpc-builder/Cargo.toml @@ -11,6 +11,7 @@ description = "Helpers for configuring RPC" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-ipc.workspace = true reth-interfaces.workspace = true reth-network-api.workspace = true diff --git a/crates/rpc/rpc-engine-api/Cargo.toml b/crates/rpc/rpc-engine-api/Cargo.toml index b718862a9ccb..a9d70f540a1a 100644 --- a/crates/rpc/rpc-engine-api/Cargo.toml +++ b/crates/rpc/rpc-engine-api/Cargo.toml @@ -11,6 +11,7 @@ description = "Implementation of Engine API" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-provider.workspace = true reth-rpc-types.workspace = true @@ -42,4 +43,4 @@ reth-payload-builder = { workspace = true, features = ["test-utils"] } assert_matches.workspace = true [features] -optimism = ["reth-primitives/optimism", "reth-rpc-types/optimism"] +optimism = ["reth-primitives/optimism", "reth-ethereum-forks/optimism", "reth-rpc-types/optimism"] diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index a000a00ab2e5..196552c2f73a 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -5,9 +5,10 @@ use crate::{ use async_trait::async_trait; use jsonrpsee_core::RpcResult; use reth_beacon_consensus::BeaconConsensusEngineHandle; +use reth_ethereum_forks::{ChainSpec, Hardfork}; use reth_interfaces::consensus::ForkchoiceState; use reth_payload_builder::PayloadStore; -use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, ChainSpec, Hardfork, B256, U64}; +use reth_primitives::{BlockHash, BlockHashOrNumber, BlockNumber, B256, U64}; use reth_provider::{BlockReader, EvmEnvProvider, HeaderProvider, StateProviderFactory}; use reth_rpc_api::EngineApiServer; use reth_rpc_types::engine::{ @@ -808,9 +809,10 @@ mod tests { use super::*; use assert_matches::assert_matches; use reth_beacon_consensus::BeaconEngineMessage; + use reth_ethereum_forks::MAINNET; use reth_interfaces::test_utils::generators::random_block; use reth_payload_builder::test_utils::spawn_test_payload_service; - use reth_primitives::{SealedBlock, B256, MAINNET}; + use reth_primitives::{SealedBlock, B256}; use reth_provider::test_utils::MockEthProvider; use reth_rpc_types_compat::engine::payload::execution_payload_from_sealed_block; use reth_tasks::TokioTaskExecutor; diff --git a/crates/rpc/rpc-testing-util/Cargo.toml b/crates/rpc/rpc-testing-util/Cargo.toml index 53b04c7a0e99..ad445df740b0 100644 --- a/crates/rpc/rpc-testing-util/Cargo.toml +++ b/crates/rpc/rpc-testing-util/Cargo.toml @@ -11,6 +11,7 @@ description = "Reth RPC testing helpers" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-rpc-types.workspace = true reth-rpc-api = { workspace = true, features = ["client"] } diff --git a/crates/rpc/rpc-types-compat/Cargo.toml b/crates/rpc/rpc-types-compat/Cargo.toml index e85060334f54..f690600fc153 100644 --- a/crates/rpc/rpc-types-compat/Cargo.toml +++ b/crates/rpc/rpc-types-compat/Cargo.toml @@ -10,8 +10,9 @@ description = "Compatibility layer for reth-primitives and ethereum RPC types" [dependencies] reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-rpc-types.workspace = true alloy-rlp.workspace = true [features] -optimism = ["reth-primitives/optimism", "reth-rpc-types/optimism"] +optimism = ["reth-primitives/optimism", "reth-ethereum-forks/optimism", "reth-rpc-types/optimism"] diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 44c90408830b..eeba18b3ee99 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -11,6 +11,7 @@ description = "Reth RPC implementation" # reth reth-interfaces.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-rpc-api.workspace = true reth-rpc-types.workspace = true reth-provider = { workspace = true, features = ["test-utils"] } @@ -79,6 +80,7 @@ reth-interfaces = { workspace = true, features = ["test-utils"] } optimism = [ "dep:reqwest", "reth-primitives/optimism", + "reth-ethereum-forks/optimism", "reth-rpc-types-compat/optimism", "reth-network-api/optimism", "reth-provider/optimism", diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index 392c2172161e..8a4541c717e7 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -13,8 +13,8 @@ use crate::{ use alloy_rlp::{Decodable, Encodable}; use async_trait::async_trait; use jsonrpsee::core::RpcResult; +use reth_ethereum_forks::env::tx_env_with_recovered; use reth_primitives::{ - revm::env::tx_env_with_recovered, revm_primitives::{ db::{DatabaseCommit, DatabaseRef}, BlockEnv, CfgEnv, diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index a90d64ecc5cf..4798fe6fadd7 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -11,8 +11,9 @@ use crate::{ }, EthApi, }; +use reth_ethereum_forks::env::tx_env_with_recovered; use reth_network_api::NetworkInfo; -use reth_primitives::{revm::env::tx_env_with_recovered, BlockId, BlockNumberOrTag, Bytes, U256}; +use reth_primitives::{BlockId, BlockNumberOrTag, Bytes, U256}; use reth_provider::{ BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProvider, StateProviderFactory, }; diff --git a/crates/rpc/rpc/src/eth/api/mod.rs b/crates/rpc/rpc/src/eth/api/mod.rs index 4ff22571dec9..41966bf8dae5 100644 --- a/crates/rpc/rpc/src/eth/api/mod.rs +++ b/crates/rpc/rpc/src/eth/api/mod.rs @@ -11,11 +11,12 @@ use crate::eth::{ signer::EthSigner, }; use async_trait::async_trait; +use reth_ethereum_forks::ChainInfo; use reth_interfaces::RethResult; use reth_network_api::NetworkInfo; use reth_primitives::{ revm_primitives::{BlockEnv, CfgEnv}, - Address, BlockId, BlockNumberOrTag, ChainInfo, SealedBlockWithSenders, B256, U256, U64, + Address, BlockId, BlockNumberOrTag, SealedBlockWithSenders, B256, U256, U64, }; use reth_provider::{ BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderBox, StateProviderFactory, diff --git a/crates/rpc/rpc/src/eth/api/pending_block.rs b/crates/rpc/rpc/src/eth/api/pending_block.rs index 3c6f6b58793c..557f2ec66204 100644 --- a/crates/rpc/rpc/src/eth/api/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/pending_block.rs @@ -1,15 +1,16 @@ //! Support for building a pending block via local txpool. use crate::eth::error::{EthApiError, EthResult}; +use reth_ethereum_forks::{env::tx_env_with_recovered, ChainSpec}; use reth_primitives::{ constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE}, proofs, - revm::{compat::into_reth_log, env::tx_env_with_recovered}, + revm::compat::into_reth_log, revm_primitives::{ BlockEnv, CfgEnv, EVMError, Env, InvalidTransaction, ResultAndState, SpecId, }, - Block, BlockId, BlockNumberOrTag, ChainSpec, Header, IntoRecoveredTransaction, Receipt, - Receipts, SealedBlockWithSenders, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256, + Block, BlockId, BlockNumberOrTag, Header, IntoRecoveredTransaction, Receipt, Receipts, + SealedBlockWithSenders, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256, }; use reth_provider::{BundleStateWithReceipts, ChainSpecProvider, StateProviderFactory}; use reth_revm::{ diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index 58af7c7697a9..cb7082b0b939 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -12,10 +12,10 @@ use crate::{ EthApi, EthApiSpec, }; use async_trait::async_trait; +use reth_ethereum_forks::env::{fill_block_env_with_coinbase, tx_env_with_recovered}; use reth_network_api::NetworkInfo; use reth_primitives::{ eip4844::calc_blob_gasprice, - revm::env::{fill_block_env_with_coinbase, tx_env_with_recovered}, revm_primitives::{db::DatabaseCommit, Env, ExecutionResult, ResultAndState, SpecId, State}, Address, BlockId, BlockNumberOrTag, Bytes, FromRecoveredPooledTransaction, Header, IntoRecoveredTransaction, Receipt, SealedBlock, diff --git a/crates/rpc/rpc/src/eth/logs_utils.rs b/crates/rpc/rpc/src/eth/logs_utils.rs index 2fdd711356d4..793cd10d0dd8 100644 --- a/crates/rpc/rpc/src/eth/logs_utils.rs +++ b/crates/rpc/rpc/src/eth/logs_utils.rs @@ -1,4 +1,5 @@ -use reth_primitives::{BlockNumHash, ChainInfo, Receipt, TxHash, U256}; +use reth_ethereum_forks::ChainInfo; +use reth_primitives::{BlockNumHash, Receipt, TxHash, U256}; use reth_rpc_types::{FilteredParams, Log}; use reth_rpc_types_compat::log::from_primitive_log; diff --git a/crates/rpc/rpc/src/eth/revm_utils.rs b/crates/rpc/rpc/src/eth/revm_utils.rs index bbfb3b2d1753..ac9b2f7be991 100644 --- a/crates/rpc/rpc/src/eth/revm_utils.rs +++ b/crates/rpc/rpc/src/eth/revm_utils.rs @@ -1,8 +1,8 @@ //! utilities for working with revm use crate::eth::error::{EthApiError, EthResult, RpcInvalidTransactionError}; +use reth_ethereum_forks::env::{fill_tx_env, fill_tx_env_with_recovered}; use reth_primitives::{ - revm::env::{fill_tx_env, fill_tx_env_with_recovered}, Address, TransactionSigned, TransactionSignedEcRecovered, TxHash, B256, U256, }; use reth_rpc_types::{ diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index 7f0e8c5e543a..6b67fc0878f1 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -10,9 +10,9 @@ use crate::{ use async_trait::async_trait; use jsonrpsee::core::RpcResult as Result; use reth_consensus_common::calc::{base_block_reward, block_reward}; +use reth_ethereum_forks::env::tx_env_with_recovered; use reth_primitives::{ - revm::env::tx_env_with_recovered, revm_primitives::db::DatabaseCommit, BlockId, - BlockNumberOrTag, Bytes, SealedHeader, B256, U256, + revm_primitives::db::DatabaseCommit, BlockId, BlockNumberOrTag, Bytes, SealedHeader, B256, U256, }; use reth_provider::{BlockReader, ChainSpecProvider, EvmEnvProvider, StateProviderFactory}; use reth_revm::{ diff --git a/crates/snapshot/Cargo.toml b/crates/snapshot/Cargo.toml index 0eed6732161b..6c60f4bba6d6 100644 --- a/crates/snapshot/Cargo.toml +++ b/crates/snapshot/Cargo.toml @@ -11,6 +11,7 @@ description = "Snapshotting implementation" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-db.workspace = true reth-provider.workspace = true reth-interfaces.workspace = true diff --git a/crates/stages/Cargo.toml b/crates/stages/Cargo.toml index 890bc135ed02..1792b14c89d1 100644 --- a/crates/stages/Cargo.toml +++ b/crates/stages/Cargo.toml @@ -17,6 +17,7 @@ normal = [ [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-db.workspace = true reth-codecs.workspace = true @@ -55,6 +56,7 @@ auto_impl = "1" [dev-dependencies] # reth reth-primitives = { workspace = true, features = ["test-utils", "arbitrary"] } +reth-ethereum-forks = { workspace = true, features = ["test-utils", "arbitrary"] } reth-db = { workspace = true, features = ["test-utils", "mdbx"] } reth-interfaces = { workspace = true, features = ["test-utils"] } reth-downloaders.workspace = true diff --git a/crates/stages/benches/criterion.rs b/crates/stages/benches/criterion.rs index 2f73ec71f9f1..7c068c6a1800 100644 --- a/crates/stages/benches/criterion.rs +++ b/crates/stages/benches/criterion.rs @@ -4,8 +4,9 @@ use criterion::{ }; use pprof::criterion::{Output, PProfProfiler}; use reth_db::DatabaseEnv; +use reth_ethereum_forks::MAINNET; use reth_interfaces::test_utils::TestConsensus; -use reth_primitives::{stage::StageCheckpoint, MAINNET}; +use reth_primitives::stage::StageCheckpoint; use reth_provider::ProviderFactory; use reth_stages::{ stages::{MerkleStage, SenderRecoveryStage, TotalDifficultyStage, TransactionLookupStage}, diff --git a/crates/stages/benches/setup/mod.rs b/crates/stages/benches/setup/mod.rs index 3850ca44fa55..dad125f09f03 100644 --- a/crates/stages/benches/setup/mod.rs +++ b/crates/stages/benches/setup/mod.rs @@ -5,6 +5,7 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, DatabaseEnv, }; +use reth_ethereum_forks::MAINNET; use reth_interfaces::test_utils::{ generators, generators::{ @@ -12,7 +13,7 @@ use reth_interfaces::test_utils::{ random_eoa_account_range, }, }; -use reth_primitives::{Account, Address, SealedBlock, B256, MAINNET}; +use reth_primitives::{Account, Address, SealedBlock, B256}; use reth_provider::ProviderFactory; use reth_stages::{ stages::{AccountHashingStage, StorageHashingStage}, diff --git a/crates/stages/src/lib.rs b/crates/stages/src/lib.rs index e59597ebabc1..126d7377b077 100644 --- a/crates/stages/src/lib.rs +++ b/crates/stages/src/lib.rs @@ -18,7 +18,8 @@ //! # use reth_interfaces::consensus::Consensus; //! # use reth_interfaces::test_utils::{TestBodiesClient, TestConsensus, TestHeadersClient}; //! # use reth_revm::EvmProcessorFactory; -//! # use reth_primitives::{PeerId, MAINNET, B256}; +//! # use reth_primitives::{PeerId, B256}; +//! # use reth_ethereum_forks::MAINNET; //! # use reth_stages::Pipeline; //! # use reth_stages::sets::DefaultStages; //! # use tokio::sync::watch; diff --git a/crates/stages/src/sets.rs b/crates/stages/src/sets.rs index ac97fd5c742c..3a85ffbaf4a9 100644 --- a/crates/stages/src/sets.rs +++ b/crates/stages/src/sets.rs @@ -13,7 +13,7 @@ //! # use reth_stages::Pipeline; //! # use reth_stages::sets::{OfflineStages}; //! # use reth_revm::EvmProcessorFactory; -//! # use reth_primitives::MAINNET; +//! # use reth_ethereum_forks::MAINNET; //! # use reth_provider::test_utils::create_test_provider_factory; //! //! # let executor_factory = EvmProcessorFactory::new(MAINNET.clone()); @@ -26,7 +26,7 @@ //! # use reth_stages::Pipeline; //! # use reth_stages::{StageSet, sets::OfflineStages}; //! # use reth_revm::EvmProcessorFactory; -//! # use reth_primitives::MAINNET; +//! # use reth_ethereum_forks::MAINNET; //! // Build a pipeline with all offline stages and a custom stage at the end. //! # let executor_factory = EvmProcessorFactory::new(MAINNET.clone()); //! Pipeline::builder() diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index 41a26165c9bb..a4b3e527ab49 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -495,9 +495,10 @@ mod tests { use alloy_rlp::Decodable; use assert_matches::assert_matches; use reth_db::{models::AccountBeforeTx, test_utils::create_test_rw_db}; + use reth_ethereum_forks::{ChainSpecBuilder, MAINNET}; use reth_primitives::{ address, hex_literal::hex, keccak256, stage::StageUnitCheckpoint, Account, Bytecode, - ChainSpecBuilder, PruneModes, SealedBlock, StorageEntry, B256, MAINNET, U256, + PruneModes, SealedBlock, StorageEntry, B256, U256, }; use reth_provider::{AccountReader, BlockWriter, ProviderFactory, ReceiptProvider}; use reth_revm::EvmProcessorFactory; diff --git a/crates/stages/src/stages/mod.rs b/crates/stages/src/stages/mod.rs index ffe8ae1da1f6..8fb16d23d267 100644 --- a/crates/stages/src/stages/mod.rs +++ b/crates/stages/src/stages/mod.rs @@ -54,10 +54,11 @@ mod tests { transaction::{DbTx, DbTxMut}, AccountHistory, DatabaseEnv, }; + use reth_ethereum_forks::ChainSpecBuilder; use reth_interfaces::test_utils::generators::{self, random_block}; use reth_primitives::{ - address, hex_literal::hex, keccak256, Account, Bytecode, ChainSpecBuilder, PruneMode, - PruneModes, SealedBlock, U256, + address, hex_literal::hex, keccak256, Account, Bytecode, PruneMode, PruneModes, + SealedBlock, U256, }; use reth_provider::{ AccountExtReader, BlockWriter, ProviderFactory, ReceiptProvider, StorageReader, diff --git a/crates/stages/src/test_utils/runner.rs b/crates/stages/src/test_utils/runner.rs index 17289b9cf20b..2c754014dd9e 100644 --- a/crates/stages/src/test_utils/runner.rs +++ b/crates/stages/src/test_utils/runner.rs @@ -1,8 +1,8 @@ use super::TestStageDB; use crate::{ExecInput, ExecOutput, Stage, StageError, StageExt, UnwindInput, UnwindOutput}; use reth_db::{test_utils::TempDatabase, DatabaseEnv}; +use reth_ethereum_forks::MAINNET; use reth_interfaces::db::DatabaseError; -use reth_primitives::MAINNET; use reth_provider::{ProviderError, ProviderFactory}; use std::{borrow::Borrow, sync::Arc}; use tokio::sync::oneshot; diff --git a/crates/stages/src/test_utils/test_db.rs b/crates/stages/src/test_utils/test_db.rs index 4582bb86acf4..51cbdf44a8a7 100644 --- a/crates/stages/src/test_utils/test_db.rs +++ b/crates/stages/src/test_utils/test_db.rs @@ -9,10 +9,11 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, DatabaseEnv, DatabaseError as DbError, }; +use reth_ethereum_forks::MAINNET; use reth_interfaces::{provider::ProviderResult, test_utils::generators::ChangeSet, RethResult}; use reth_primitives::{ keccak256, Account, Address, BlockNumber, Receipt, SealedBlock, SealedHeader, StorageEntry, - TxHash, TxNumber, B256, MAINNET, U256, + TxHash, TxNumber, B256, U256, }; use reth_provider::{DatabaseProviderRO, DatabaseProviderRW, HistoryWriter, ProviderFactory}; use std::{ diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index 60e13ad2f67f..db467cf0c482 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -11,6 +11,7 @@ description = "Staged syncing primitives used in reth." [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-codecs.workspace = true reth-libmdbx = { workspace = true, optional = true, features = ["return-borrowed"] } @@ -56,6 +57,7 @@ proptest-derive = { workspace = true, optional = true } [dev-dependencies] # reth libs with arbitrary reth-primitives = { workspace = true, features = ["arbitrary"] } +reth-ethereum-forks = { workspace = true, features = ["arbitrary"] } reth-codecs = { workspace = true, features = ["arbitrary"] } reth-interfaces.workspace = true @@ -93,6 +95,7 @@ mdbx = ["reth-libmdbx"] bench = [] arbitrary = [ "reth-primitives/arbitrary", + "reth-ethereum-forks/arbitrary", "reth-codecs/arbitrary", "dep:arbitrary", "dep:proptest", diff --git a/crates/storage/provider/Cargo.toml b/crates/storage/provider/Cargo.toml index 1cecabfce4a2..2510d463ec18 100644 --- a/crates/storage/provider/Cargo.toml +++ b/crates/storage/provider/Cargo.toml @@ -11,6 +11,7 @@ description = "Reth storage provider." [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-db.workspace = true reth-trie.workspace = true @@ -45,6 +46,7 @@ rayon.workspace = true [dev-dependencies] reth-db = { workspace = true, features = ["test-utils"] } reth-primitives = { workspace = true, features = ["arbitrary", "test-utils"] } +reth-ethereum-forks = { workspace = true, features = ["arbitrary", "test-utils"] } reth-trie = { workspace = true, features = ["test-utils"] } reth-interfaces = { workspace = true, features = ["test-utils"] } @@ -58,5 +60,6 @@ rand.workspace = true test-utils = ["alloy-rlp", "reth-db/test-utils"] optimism = [ "reth-primitives/optimism", + "reth-ethereum-forks/optimism", "reth-interfaces/optimism" ] diff --git a/crates/storage/provider/src/providers/chain_info.rs b/crates/storage/provider/src/providers/chain_info.rs index 2532ad866deb..967163dda7f7 100644 --- a/crates/storage/provider/src/providers/chain_info.rs +++ b/crates/storage/provider/src/providers/chain_info.rs @@ -1,5 +1,6 @@ use parking_lot::RwLock; -use reth_primitives::{BlockNumHash, BlockNumber, ChainInfo, SealedHeader}; +use reth_ethereum_forks::ChainInfo; +use reth_primitives::{BlockNumHash, BlockNumber, SealedHeader}; use std::{ sync::{ atomic::{AtomicU64, Ordering}, diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index 0d1ca70ab465..621b80e59cfe 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -10,14 +10,15 @@ use crate::{ TransactionsProvider, WithdrawalsProvider, }; use reth_db::{database::Database, init_db, models::StoredBlockBodyIndices, DatabaseEnv}; +use reth_ethereum_forks::{ChainInfo, ChainSpec}; use reth_interfaces::{db::LogLevel, provider::ProviderResult, RethError, RethResult}; use reth_primitives::{ snapshot::HighestSnapshots, stage::{StageCheckpoint, StageId}, - Address, Block, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, ChainInfo, - ChainSpec, Header, PruneCheckpoint, PruneSegment, Receipt, SealedBlock, SealedBlockWithSenders, - SealedHeader, TransactionMeta, TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, - Withdrawal, B256, U256, + Address, Block, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, Header, + PruneCheckpoint, PruneSegment, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, + TransactionMeta, TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, + B256, U256, }; use revm::primitives::{BlockEnv, CfgEnv}; use std::{ @@ -497,6 +498,7 @@ mod tests { use assert_matches::assert_matches; use rand::Rng; use reth_db::{tables, test_utils::ERROR_TEMPDIR, transaction::DbTxMut, DatabaseEnv}; + use reth_ethereum_forks::ChainSpecBuilder; use reth_interfaces::{ provider::ProviderError, test_utils::{ @@ -505,9 +507,7 @@ mod tests { }, RethError, }; - use reth_primitives::{ - hex_literal::hex, ChainSpecBuilder, PruneMode, PruneModes, SealedBlock, TxNumber, B256, - }; + use reth_primitives::{hex_literal::hex, PruneMode, PruneModes, SealedBlock, TxNumber, B256}; use std::{ops::RangeInclusive, sync::Arc}; use tokio::sync::watch; diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 244011c7b619..5b03383af8d2 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -24,6 +24,11 @@ use reth_db::{ transaction::{DbTx, DbTxMut}, BlockNumberList, DatabaseError, }; +use reth_ethereum_forks::{ + config::revm_spec, + env::{fill_block_env, fill_cfg_and_block_env, fill_cfg_env}, + ChainInfo, ChainSpec, Hardfork, +}; use reth_interfaces::{ p2p::headers::downloader::SyncTarget, provider::{ProviderResult, RootMismatch}, @@ -31,17 +36,13 @@ use reth_interfaces::{ }; use reth_primitives::{ keccak256, - revm::{ - config::revm_spec, - env::{fill_block_env, fill_cfg_and_block_env, fill_cfg_env}, - }, stage::{StageCheckpoint, StageId}, trie::Nibbles, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockNumber, BlockWithSenders, - ChainInfo, ChainSpec, GotExpected, Hardfork, Head, Header, PruneCheckpoint, PruneModes, - PruneSegment, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, StorageEntry, - TransactionMeta, TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, - TxHash, TxNumber, Withdrawal, B256, U256, + GotExpected, Head, Header, PruneCheckpoint, PruneModes, PruneSegment, Receipt, SealedBlock, + SealedBlockWithSenders, SealedHeader, StorageEntry, TransactionMeta, TransactionSigned, + TransactionSignedEcRecovered, TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, B256, + U256, }; use reth_trie::{prefix_set::PrefixSetMut, StateRoot}; use revm::primitives::{BlockEnv, CfgEnv, SpecId}; diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index 11dba5e6a6c4..a01be1adbaea 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -7,6 +7,7 @@ use crate::{ TransactionVariant, TransactionsProvider, WithdrawalsProvider, }; use reth_db::{database::Database, models::StoredBlockBodyIndices}; +use reth_ethereum_forks::{ChainInfo, ChainSpec}; use reth_interfaces::{ blockchain_tree::{BlockchainTreeEngine, BlockchainTreeViewer}, consensus::ForkchoiceState, @@ -16,9 +17,9 @@ use reth_interfaces::{ use reth_primitives::{ stage::{StageCheckpoint, StageId}, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumHash, BlockNumber, - BlockNumberOrTag, BlockWithSenders, ChainInfo, ChainSpec, Header, PruneCheckpoint, - PruneSegment, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, TransactionMeta, - TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, B256, U256, + BlockNumberOrTag, BlockWithSenders, Header, PruneCheckpoint, PruneSegment, Receipt, + SealedBlock, SealedBlockWithSenders, SealedHeader, TransactionMeta, TransactionSigned, + TransactionSignedNoHash, TxHash, TxNumber, Withdrawal, B256, U256, }; use revm::primitives::{BlockEnv, CfgEnv}; use std::{ diff --git a/crates/storage/provider/src/providers/snapshot/jar.rs b/crates/storage/provider/src/providers/snapshot/jar.rs index 9d996d9e1798..ef72b7fed644 100644 --- a/crates/storage/provider/src/providers/snapshot/jar.rs +++ b/crates/storage/provider/src/providers/snapshot/jar.rs @@ -6,9 +6,10 @@ use reth_db::{ codecs::CompactU256, snapshot::{HeaderMask, ReceiptMask, SnapshotCursor, TransactionMask}, }; +use reth_ethereum_forks::ChainInfo; use reth_interfaces::provider::{ProviderError, ProviderResult}; use reth_primitives::{ - Address, BlockHash, BlockHashOrNumber, BlockNumber, ChainInfo, Header, Receipt, SealedHeader, + Address, BlockHash, BlockHashOrNumber, BlockNumber, Header, Receipt, SealedHeader, TransactionMeta, TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, B256, U256, }; use std::ops::{Deref, Range, RangeBounds}; diff --git a/crates/storage/provider/src/providers/snapshot/manager.rs b/crates/storage/provider/src/providers/snapshot/manager.rs index 972df40408a5..f6f184049b19 100644 --- a/crates/storage/provider/src/providers/snapshot/manager.rs +++ b/crates/storage/provider/src/providers/snapshot/manager.rs @@ -6,12 +6,13 @@ use reth_db::{ codecs::CompactU256, snapshot::{HeaderMask, TransactionMask}, }; +use reth_ethereum_forks::ChainInfo; use reth_interfaces::provider::{ProviderError, ProviderResult}; use reth_nippy_jar::NippyJar; use reth_primitives::{ - snapshot::HighestSnapshots, Address, BlockHash, BlockHashOrNumber, BlockNumber, ChainInfo, - Header, SealedHeader, SnapshotSegment, TransactionMeta, TransactionSigned, - TransactionSignedNoHash, TxHash, TxNumber, B256, U256, + snapshot::HighestSnapshots, Address, BlockHash, BlockHashOrNumber, BlockNumber, Header, + SealedHeader, SnapshotSegment, TransactionMeta, TransactionSigned, TransactionSignedNoHash, + TxHash, TxNumber, B256, U256, }; use revm::primitives::HashMap; use std::{ diff --git a/crates/storage/provider/src/test_utils/executor.rs b/crates/storage/provider/src/test_utils/executor.rs index 05d406cd6f97..481b5871776c 100644 --- a/crates/storage/provider/src/test_utils/executor.rs +++ b/crates/storage/provider/src/test_utils/executor.rs @@ -3,8 +3,9 @@ use crate::{ PrunableBlockExecutor, StateProvider, }; use parking_lot::Mutex; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::executor::BlockExecutionError; -use reth_primitives::{Address, Block, BlockNumber, ChainSpec, PruneModes, Receipt, U256}; +use reth_primitives::{Address, Block, BlockNumber, PruneModes, Receipt, U256}; use std::sync::Arc; /// Test executor with mocked result. #[derive(Debug)] diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index e7f8726664a1..6db564d570b3 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -8,11 +8,12 @@ use crate::{ }; use parking_lot::Mutex; use reth_db::models::{AccountBeforeTx, StoredBlockBodyIndices}; +use reth_ethereum_forks::{ChainInfo, ChainSpec}; use reth_interfaces::provider::{ProviderError, ProviderResult}; use reth_primitives::{ keccak256, trie::AccountProof, Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, - BlockNumber, BlockWithSenders, Bytecode, Bytes, ChainInfo, ChainSpec, Header, Receipt, - SealedBlock, SealedBlockWithSenders, SealedHeader, StorageKey, StorageValue, TransactionMeta, + BlockNumber, BlockWithSenders, Bytecode, Bytes, Header, Receipt, SealedBlock, + SealedBlockWithSenders, SealedHeader, StorageKey, StorageValue, TransactionMeta, TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, B256, U256, }; use reth_trie::updates::TrieUpdates; @@ -42,7 +43,7 @@ impl Default for MockEthProvider { blocks: Default::default(), headers: Default::default(), accounts: Default::default(), - chain_spec: Arc::new(reth_primitives::ChainSpecBuilder::mainnet().build()), + chain_spec: Arc::new(reth_ethereum_forks::ChainSpecBuilder::mainnet().build()), } } } diff --git a/crates/storage/provider/src/test_utils/mod.rs b/crates/storage/provider/src/test_utils/mod.rs index 0da47c47940b..87fd82158f2c 100644 --- a/crates/storage/provider/src/test_utils/mod.rs +++ b/crates/storage/provider/src/test_utils/mod.rs @@ -3,7 +3,7 @@ use reth_db::{ test_utils::{create_test_rw_db, TempDatabase}, DatabaseEnv, }; -use reth_primitives::{ChainSpec, MAINNET}; +use reth_ethereum_forks::{ChainSpec, MAINNET}; use std::sync::Arc; pub mod blocks; diff --git a/crates/storage/provider/src/test_utils/noop.rs b/crates/storage/provider/src/test_utils/noop.rs index dc36ac948fc3..ce22cdb9954d 100644 --- a/crates/storage/provider/src/test_utils/noop.rs +++ b/crates/storage/provider/src/test_utils/noop.rs @@ -8,14 +8,15 @@ use crate::{ WithdrawalsProvider, }; use reth_db::models::{AccountBeforeTx, StoredBlockBodyIndices}; +use reth_ethereum_forks::{ChainInfo, ChainSpec, MAINNET}; use reth_interfaces::provider::ProviderResult; use reth_primitives::{ stage::{StageCheckpoint, StageId}, trie::AccountProof, - Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, Bytecode, - ChainInfo, ChainSpec, Header, PruneCheckpoint, PruneSegment, Receipt, SealedBlock, - SealedBlockWithSenders, SealedHeader, StorageKey, StorageValue, TransactionMeta, - TransactionSigned, TransactionSignedNoHash, TxHash, TxNumber, B256, MAINNET, U256, + Account, Address, Block, BlockHash, BlockHashOrNumber, BlockId, BlockNumber, Bytecode, Header, + PruneCheckpoint, PruneSegment, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, + StorageKey, StorageValue, TransactionMeta, TransactionSigned, TransactionSignedNoHash, TxHash, + TxNumber, B256, U256, }; use reth_trie::updates::TrieUpdates; use revm::primitives::{BlockEnv, CfgEnv}; diff --git a/crates/storage/provider/src/traits/block.rs b/crates/storage/provider/src/traits/block.rs index 44951a3fcac8..afe500bfbbea 100644 --- a/crates/storage/provider/src/traits/block.rs +++ b/crates/storage/provider/src/traits/block.rs @@ -4,11 +4,11 @@ use crate::{ }; use auto_impl::auto_impl; use reth_db::models::StoredBlockBodyIndices; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::provider::ProviderResult; use reth_primitives::{ Address, Block, BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, BlockWithSenders, - ChainSpec, Header, PruneModes, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, - B256, + Header, PruneModes, Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader, B256, }; use std::ops::RangeInclusive; diff --git a/crates/storage/provider/src/traits/block_id.rs b/crates/storage/provider/src/traits/block_id.rs index fd52f6c326b2..f9ed9171cc2a 100644 --- a/crates/storage/provider/src/traits/block_id.rs +++ b/crates/storage/provider/src/traits/block_id.rs @@ -1,6 +1,7 @@ use super::BlockHashReader; +use reth_ethereum_forks::ChainInfo; use reth_interfaces::provider::{ProviderError, ProviderResult}; -use reth_primitives::{BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, ChainInfo, B256}; +use reth_primitives::{BlockHashOrNumber, BlockId, BlockNumber, BlockNumberOrTag, B256}; /// Client trait for getting important block numbers (such as the latest block number), converting /// block hashes to numbers, and fetching a block hash from its block number. diff --git a/crates/storage/provider/src/traits/executor.rs b/crates/storage/provider/src/traits/executor.rs index bec270faaf50..0f296b230648 100644 --- a/crates/storage/provider/src/traits/executor.rs +++ b/crates/storage/provider/src/traits/executor.rs @@ -1,8 +1,9 @@ //! Executor Factory use crate::{bundle_state::BundleStateWithReceipts, StateProvider}; +use reth_ethereum_forks::ChainSpec; use reth_interfaces::executor::BlockExecutionError; -use reth_primitives::{Address, Block, BlockNumber, ChainSpec, PruneModes, Receipt, U256}; +use reth_primitives::{Address, Block, BlockNumber, PruneModes, Receipt, U256}; use std::time::Duration; use tracing::debug; diff --git a/crates/storage/provider/src/traits/spec.rs b/crates/storage/provider/src/traits/spec.rs index 47d95fbd586b..a922df1ba1d0 100644 --- a/crates/storage/provider/src/traits/spec.rs +++ b/crates/storage/provider/src/traits/spec.rs @@ -1,4 +1,4 @@ -use reth_primitives::ChainSpec; +use reth_ethereum_forks::ChainSpec; use std::sync::Arc; /// A trait for reading the current chainspec. diff --git a/crates/transaction-pool/Cargo.toml b/crates/transaction-pool/Cargo.toml index 52ceaf937447..12d3bbe2a0b9 100644 --- a/crates/transaction-pool/Cargo.toml +++ b/crates/transaction-pool/Cargo.toml @@ -17,6 +17,7 @@ normal = [ [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-provider.workspace = true reth-interfaces.workspace = true reth-tasks.workspace = true @@ -52,6 +53,7 @@ proptest = { workspace = true, optional = true } [dev-dependencies] reth-primitives = { workspace = true, features = ["arbitrary"] } +reth-ethereum-forks = { workspace = true, features = ["arbitrary"] } reth-provider = { workspace = true, features = ["test-utils"] } paste = "1.0" rand = "0.8" @@ -64,11 +66,12 @@ tempfile.workspace = true default = ["serde"] serde = ["dep:serde"] test-utils = ["rand", "paste", "serde"] -arbitrary = ["proptest", "reth-primitives/arbitrary"] +arbitrary = ["proptest", "reth-primitives/arbitrary", "reth-ethereum-forks/arbitrary"] optimism = [ "dep:reth-revm", "reth-revm?/optimism", "reth-primitives/optimism", + "reth-ethereum-forks/optimism", "reth-provider/test-utils", "reth-provider/optimism", "revm/optimism", diff --git a/crates/transaction-pool/src/lib.rs b/crates/transaction-pool/src/lib.rs index 1621c6ffe179..004d6e981457 100644 --- a/crates/transaction-pool/src/lib.rs +++ b/crates/transaction-pool/src/lib.rs @@ -79,7 +79,7 @@ //! Listen for new transactions and print them: //! //! ``` -//! use reth_primitives::MAINNET; +//! use reth_ethereum_forks::MAINNET; //! use reth_provider::{BlockReaderIdExt, ChainSpecProvider, StateProviderFactory}; //! use reth_tasks::TokioTaskExecutor; //! use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool, TransactionPool}; @@ -107,7 +107,7 @@ //! //! ``` //! use futures_util::Stream; -//! use reth_primitives::MAINNET; +//! use reth_ethereum_forks::MAINNET; //! use reth_provider::{BlockReaderIdExt, CanonStateNotification, ChainSpecProvider, StateProviderFactory}; //! use reth_tasks::TokioTaskExecutor; //! use reth_transaction_pool::{TransactionValidationTaskExecutor, Pool}; @@ -279,7 +279,7 @@ where /// # Example /// /// ``` - /// use reth_primitives::MAINNET; + /// use reth_ethereum_forks::MAINNET; /// use reth_provider::{BlockReaderIdExt, StateProviderFactory}; /// use reth_tasks::TokioTaskExecutor; /// use reth_transaction_pool::{ diff --git a/crates/transaction-pool/src/test_utils/gen.rs b/crates/transaction-pool/src/test_utils/gen.rs index 1f2a44bcdaf5..cf36295ab970 100644 --- a/crates/transaction-pool/src/test_utils/gen.rs +++ b/crates/transaction-pool/src/test_utils/gen.rs @@ -5,10 +5,11 @@ use crate::{ EthPooledTransaction, }; use rand::Rng; +use reth_ethereum_forks::MAINNET; use reth_primitives::{ constants::MIN_PROTOCOL_BASE_FEE, sign_message, AccessList, Address, Bytes, FromRecoveredTransaction, Transaction, TransactionKind, TransactionSigned, TxEip1559, TxLegacy, - TxValue, B256, MAINNET, + TxValue, B256, }; /// A generator for transactions for testing purposes diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 340258c2f7c2..5c73ed1c91a6 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -8,6 +8,7 @@ use crate::{ EthBlobTransactionSidecar, EthPoolTransaction, LocalTransactionConfig, PoolTransaction, TransactionValidationOutcome, TransactionValidationTaskExecutor, TransactionValidator, }; +use reth_ethereum_forks::ChainSpec; use reth_primitives::{ constants::{ eip4844::{MAINNET_KZG_TRUSTED_SETUP, MAX_BLOBS_PER_BLOCK}, @@ -15,8 +16,8 @@ use reth_primitives::{ }, kzg::KzgSettings, revm::compat::calculate_intrinsic_gas_after_merge, - ChainSpec, GotExpected, InvalidTransactionError, SealedBlock, EIP1559_TX_TYPE_ID, - EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, + GotExpected, InvalidTransactionError, SealedBlock, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, + EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, }; use reth_provider::{AccountReader, BlockReaderIdExt, StateProviderFactory}; use reth_tasks::TaskSpawner; @@ -777,8 +778,9 @@ mod tests { blobstore::InMemoryBlobStore, CoinbaseTipOrdering, EthPooledTransaction, Pool, TransactionPool, }; + use reth_ethereum_forks::MAINNET; use reth_primitives::{ - hex, FromRecoveredPooledTransaction, PooledTransactionsElement, MAINNET, U256, + hex, FromRecoveredPooledTransaction, PooledTransactionsElement, U256, }; use reth_provider::test_utils::{ExtendedAccount, MockEthProvider}; diff --git a/crates/transaction-pool/src/validate/task.rs b/crates/transaction-pool/src/validate/task.rs index cc50277bfd07..3bbfaea5cda0 100644 --- a/crates/transaction-pool/src/validate/task.rs +++ b/crates/transaction-pool/src/validate/task.rs @@ -7,7 +7,8 @@ use crate::{ TransactionValidator, }; use futures_util::{lock::Mutex, StreamExt}; -use reth_primitives::{ChainSpec, SealedBlock}; +use reth_ethereum_forks::ChainSpec; +use reth_primitives::SealedBlock; use reth_provider::BlockReaderIdExt; use reth_tasks::TaskSpawner; use std::{future::Future, pin::Pin, sync::Arc}; diff --git a/crates/trie/Cargo.toml b/crates/trie/Cargo.toml index 43b87026c83f..2cc29554e781 100644 --- a/crates/trie/Cargo.toml +++ b/crates/trie/Cargo.toml @@ -11,6 +11,7 @@ description = "Merkle trie implementation" [dependencies] # reth reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-interfaces.workspace = true reth-db.workspace = true @@ -33,6 +34,7 @@ triehash = { version = "0.8", optional = true } [dev-dependencies] # reth reth-primitives = { workspace = true, features = ["test-utils", "arbitrary"] } +reth-ethereum-forks = { workspace = true, features = ["test-utils", "arbitrary"] } reth-db = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } diff --git a/crates/trie/src/proof.rs b/crates/trie/src/proof.rs index eb7c438c4b55..92a8a1cd409c 100644 --- a/crates/trie/src/proof.rs +++ b/crates/trie/src/proof.rs @@ -167,8 +167,9 @@ mod tests { use crate::StateRoot; use once_cell::sync::Lazy; use reth_db::database::Database; + use reth_ethereum_forks::{Chain, ChainSpec, HOLESKY, MAINNET}; use reth_interfaces::RethResult; - use reth_primitives::{Account, Bytes, Chain, ChainSpec, StorageEntry, HOLESKY, MAINNET, U256}; + use reth_primitives::{Account, Bytes, StorageEntry, U256}; use reth_provider::{test_utils::create_test_provider_factory, HashingWriter, ProviderFactory}; use std::{str::FromStr, sync::Arc}; diff --git a/docs/repo/layout.md b/docs/repo/layout.md index d7e0c430adc3..ed2955cfceab 100644 --- a/docs/repo/layout.md +++ b/docs/repo/layout.md @@ -141,6 +141,7 @@ Crates related to building and validating payloads (blocks). These crates define primitive types or algorithms such as RLP. - [`primitives`](../../crates/primitives): Commonly used types in Reth. +- [`ethereum-forks`](../../crates/ethereum-forks): Helper crate for ethereum fork types used in Reth. - [`rlp`](../../crates/rlp): An implementation of RLP, forked from an earlier Apache-licensed version of [`fastrlp`][fastrlp] - [`rlp/rlp-derive`](../../crates/rlp/rlp-derive): Forked from an earlier Apache licenced version of the [`fastrlp-derive`][fastrlp-derive] crate, before it changed licence to GPL. - [`trie`](../../crates/trie): An implementation of a Merkle Patricia Trie used for various roots (e.g. the state root) in Ethereum. diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 92bb0f1f1ea8..e89d85ecfdbe 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -7,6 +7,7 @@ license.workspace = true [dev-dependencies] reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-db.workspace = true reth-provider.workspace = true diff --git a/examples/db-access.rs b/examples/db-access.rs index 235193a38f64..5c8e6f707683 100644 --- a/examples/db-access.rs +++ b/examples/db-access.rs @@ -1,5 +1,6 @@ use reth_db::open_db_read_only; -use reth_primitives::{Address, ChainSpecBuilder, B256, U256}; +use reth_ethereum_forks::ChainSpecBuilder; +use reth_primitives::{Address, B256, U256}; use reth_provider::{ AccountReader, BlockReader, BlockSource, HeaderProvider, ProviderFactory, ReceiptProvider, StateProvider, TransactionsProvider, diff --git a/examples/manual-p2p/Cargo.toml b/examples/manual-p2p/Cargo.toml index 9e463ae8cd40..65964a6a9d26 100644 --- a/examples/manual-p2p/Cargo.toml +++ b/examples/manual-p2p/Cargo.toml @@ -10,6 +10,7 @@ once_cell.workspace = true eyre.workspace = true reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-network.workspace = true reth-discv4.workspace = true reth-eth-wire.workspace = true diff --git a/examples/manual-p2p/src/main.rs b/examples/manual-p2p/src/main.rs index d8b98875d204..f43e8d8b7a5c 100644 --- a/examples/manual-p2p/src/main.rs +++ b/examples/manual-p2p/src/main.rs @@ -15,10 +15,9 @@ use reth_ecies::{stream::ECIESStream, util::pk2id}; use reth_eth_wire::{ EthMessage, EthStream, HelloMessage, P2PStream, Status, UnauthedEthStream, UnauthedP2PStream, }; +use reth_ethereum_forks::{Chain, Hardfork, MAINNET}; use reth_network::config::rng_secret_key; -use reth_primitives::{ - mainnet_nodes, Chain, Hardfork, Head, NodeRecord, MAINNET, MAINNET_GENESIS_HASH, -}; +use reth_primitives::{mainnet_nodes, Head, NodeRecord, MAINNET_GENESIS_HASH}; use secp256k1::{SecretKey, SECP256K1}; use tokio::net::TcpStream; diff --git a/examples/rpc-db/src/main.rs b/examples/rpc-db/src/main.rs index aa97f82b6257..1e9a41228609 100644 --- a/examples/rpc-db/src/main.rs +++ b/examples/rpc-db/src/main.rs @@ -12,7 +12,7 @@ //! cast rpc myrpcExt_customMethod //! ``` use reth::{ - primitives::ChainSpecBuilder, + ethereum_forks::ChainSpecBuilder, providers::{providers::BlockchainProvider, ProviderFactory}, utils::db::open_db_read_only, }; diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index 29813d1dbeca..9c51f3cd8eb2 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -13,6 +13,7 @@ ef-tests = [] [dependencies] reth-primitives.workspace = true +reth-ethereum-forks.workspace = true reth-db = { workspace = true, features = ["mdbx", "test-utils"] } reth-provider.workspace = true reth-stages.workspace = true diff --git a/testing/ef-tests/src/models.rs b/testing/ef-tests/src/models.rs index af1e02bd0746..e2ab781869fe 100644 --- a/testing/ef-tests/src/models.rs +++ b/testing/ef-tests/src/models.rs @@ -6,10 +6,10 @@ use reth_db::{ tables, transaction::{DbTx, DbTxMut}, }; +use reth_ethereum_forks::{ChainSpec, ChainSpecBuilder}; use reth_primitives::{ - keccak256, Account as RethAccount, Address, Bloom, Bytecode, Bytes, ChainSpec, - ChainSpecBuilder, Header as RethHeader, JsonU256, SealedHeader, StorageEntry, Withdrawal, B256, - B64, U256, + keccak256, Account as RethAccount, Address, Bloom, Bytecode, Bytes, Header as RethHeader, + JsonU256, SealedHeader, StorageEntry, Withdrawal, B256, B64, U256, }; use serde::{self, Deserialize}; use std::{collections::BTreeMap, ops::Deref};