From 7df7bc2c1a83df550c24e3f4a26c067c4690b9eb Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Tue, 27 Aug 2024 14:25:38 +0200 Subject: [PATCH] chore(op): Add `OpChainSpec` (#10516) --- Cargo.lock | 6 +- crates/cli/cli/Cargo.toml | 1 - crates/cli/cli/src/chainspec.rs | 12 +-- crates/ethereum/cli/src/chainspec.rs | 6 +- crates/node/core/Cargo.toml | 2 +- crates/node/core/src/args/utils.rs | 78 +++++++++++-------- crates/optimism/chainspec/Cargo.toml | 4 +- .../optimism/chainspec/res/genesis/dev.json | 1 + crates/optimism/chainspec/src/base.rs | 44 ++++++----- crates/optimism/chainspec/src/base_sepolia.rs | 44 ++++++----- crates/optimism/chainspec/src/dev.rs | 38 +++++++++ crates/optimism/chainspec/src/lib.rs | 15 +++- crates/optimism/chainspec/src/op.rs | 48 ++++++------ crates/optimism/chainspec/src/op_sepolia.rs | 44 ++++++----- crates/optimism/cli/Cargo.toml | 5 +- crates/optimism/cli/src/chainspec.rs | 46 ++++------- crates/optimism/evm/Cargo.toml | 1 + crates/optimism/evm/src/execute.rs | 14 +++- crates/optimism/node/Cargo.toml | 2 +- crates/primitives/Cargo.toml | 2 +- 20 files changed, 236 insertions(+), 177 deletions(-) create mode 100644 crates/optimism/chainspec/res/genesis/dev.json create mode 100644 crates/optimism/chainspec/src/dev.rs diff --git a/Cargo.lock b/Cargo.lock index e4f48ea83..7a8545600 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6568,7 +6568,6 @@ version = "1.0.5" dependencies = [ "clap", "eyre", - "reth-chainspec", "reth-cli-runner", ] @@ -7344,6 +7343,7 @@ dependencies = [ "reth-evm", "reth-execution-errors", "reth-execution-types", + "reth-optimism-chainspec", "reth-optimism-consensus", "reth-primitives", "reth-prune-types", @@ -7971,6 +7971,7 @@ dependencies = [ "alloy-chains", "alloy-genesis", "alloy-primitives", + "derive_more 1.0.0", "once_cell", "op-alloy-rpc-types", "reth-chainspec", @@ -7983,7 +7984,6 @@ dependencies = [ name = "reth-optimism-cli" version = "1.0.5" dependencies = [ - "alloy-genesis", "alloy-primitives", "alloy-rlp", "clap", @@ -8013,8 +8013,6 @@ dependencies = [ "reth-stages-types", "reth-static-file", "reth-static-file-types", - "serde_json", - "shellexpand", "tempfile", "tokio", "tokio-util", diff --git a/crates/cli/cli/Cargo.toml b/crates/cli/cli/Cargo.toml index 8ddd9b301..e8f7a1dcb 100644 --- a/crates/cli/cli/Cargo.toml +++ b/crates/cli/cli/Cargo.toml @@ -14,7 +14,6 @@ workspace = true [dependencies] # reth reth-cli-runner.workspace = true -reth-chainspec.workspace = true # misc clap.workspace = true diff --git a/crates/cli/cli/src/chainspec.rs b/crates/cli/cli/src/chainspec.rs index 97d9cf471..f8caca779 100644 --- a/crates/cli/cli/src/chainspec.rs +++ b/crates/cli/cli/src/chainspec.rs @@ -1,17 +1,17 @@ -use clap::builder::TypedValueParser; -use reth_chainspec::ChainSpec; use std::sync::Arc; +use clap::builder::TypedValueParser; + /// Trait for parsing chain specifications. /// /// This trait extends [`clap::builder::TypedValueParser`] to provide a parser for chain /// specifications. Implementers of this trait must provide a list of supported chains and a -/// function to parse a given string into a [`ChainSpec`]. -pub trait ChainSpecParser: TypedValueParser> + Default { +/// function to parse a given string into a chain spec. +pub trait ChainSpecParser: TypedValueParser> + Default { /// List of supported chains. const SUPPORTED_CHAINS: &'static [&'static str]; - /// Parses the given string into a [`ChainSpec`]. + /// Parses the given string into a chain spec. /// /// # Arguments /// @@ -20,6 +20,6 @@ pub trait ChainSpecParser: TypedValueParser> + Default { /// # Errors /// /// This function will return an error if the input string cannot be parsed into a valid - /// [`ChainSpec`]. + /// chain spec. fn parse(s: &str) -> eyre::Result>; } diff --git a/crates/ethereum/cli/src/chainspec.rs b/crates/ethereum/cli/src/chainspec.rs index 6269e5c8e..a092994ae 100644 --- a/crates/ethereum/cli/src/chainspec.rs +++ b/crates/ethereum/cli/src/chainspec.rs @@ -40,7 +40,7 @@ fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { #[derive(Debug, Clone, Default)] pub struct EthChainSpecParser; -impl ChainSpecParser for EthChainSpecParser { +impl ChainSpecParser for EthChainSpecParser { const SUPPORTED_CHAINS: &'static [&'static str] = &["mainnet", "sepolia", "holesky", "dev"]; fn parse(s: &str) -> eyre::Result> { @@ -59,7 +59,7 @@ impl TypedValueParser for EthChainSpecParser { ) -> Result { let val = value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?; - ::parse(val).map_err(|err| { + >::parse(val).map_err(|err| { let arg = arg.map(|a| a.to_string()).unwrap_or_else(|| "...".to_owned()); let possible_values = Self::SUPPORTED_CHAINS.join(","); let msg = format!( @@ -84,7 +84,7 @@ mod tests { #[test] fn parse_known_chain_spec() { for &chain in EthChainSpecParser::SUPPORTED_CHAINS { - assert!(::parse(chain).is_ok()); + assert!(>::parse(chain).is_ok()); } } } diff --git a/crates/node/core/Cargo.toml b/crates/node/core/Cargo.toml index f9881b0e7..a3768a889 100644 --- a/crates/node/core/Cargo.toml +++ b/crates/node/core/Cargo.toml @@ -38,7 +38,7 @@ reth-network-peers.workspace = true reth-consensus-common.workspace = true reth-prune-types.workspace = true reth-stages-types.workspace = true -reth-optimism-chainspec = { workspace = true, features = ["optimism"], optional = true } +reth-optimism-chainspec = { workspace = true, optional = true } # ethereum alloy-genesis.workspace = true diff --git a/crates/node/core/src/args/utils.rs b/crates/node/core/src/args/utils.rs index 53962f382..1edbc7e09 100644 --- a/crates/node/core/src/args/utils.rs +++ b/crates/node/core/src/args/utils.rs @@ -3,16 +3,17 @@ use std::{path::PathBuf, sync::Arc}; use alloy_genesis::Genesis; -use reth_chainspec::{ChainSpec, DEV}; +use reth_chainspec::ChainSpec; #[cfg(not(feature = "optimism"))] -use reth_chainspec::{HOLESKY, MAINNET, SEPOLIA}; +use reth_chainspec::{DEV, HOLESKY, MAINNET, SEPOLIA}; use reth_fs_util as fs; #[cfg(feature = "optimism")] -use reth_optimism_chainspec::{BASE_MAINNET, BASE_SEPOLIA, OP_MAINNET, OP_SEPOLIA}; +use reth_optimism_chainspec::{BASE_MAINNET, BASE_SEPOLIA, OP_DEV, OP_MAINNET, OP_SEPOLIA}; #[cfg(feature = "optimism")] /// Chains supported by op-reth. First value should be used as the default. -pub const SUPPORTED_CHAINS: &[&str] = &["optimism", "optimism-sepolia", "base", "base-sepolia"]; +pub const SUPPORTED_CHAINS: &[&str] = + &["optimism", "optimism-sepolia", "base", "base-sepolia", "dev"]; #[cfg(not(feature = "optimism"))] /// Chains supported by reth. First value should be used as the default. pub const SUPPORTED_CHAINS: &[&str] = &["mainnet", "sepolia", "holesky", "dev"]; @@ -26,45 +27,54 @@ pub fn chain_help() -> String { /// /// The value parser matches either a known chain, the path /// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct. +#[cfg(not(feature = "optimism"))] pub fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { Ok(match s { - #[cfg(not(feature = "optimism"))] "mainnet" => MAINNET.clone(), - #[cfg(not(feature = "optimism"))] "sepolia" => SEPOLIA.clone(), - #[cfg(not(feature = "optimism"))] "holesky" => HOLESKY.clone(), "dev" => DEV.clone(), - #[cfg(feature = "optimism")] - "optimism" => OP_MAINNET.clone(), - #[cfg(feature = "optimism")] - "optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.clone(), - #[cfg(feature = "optimism")] - "base" => BASE_MAINNET.clone(), - #[cfg(feature = "optimism")] - "base_sepolia" | "base-sepolia" => BASE_SEPOLIA.clone(), - _ => { - // try to read json from path first - let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) { - Ok(raw) => raw, - Err(io_err) => { - // valid json may start with "\n", but must contain "{" - if s.contains('{') { - s.to_string() - } else { - return Err(io_err.into()) // assume invalid path - } - } - }; - - // both serialized Genesis and ChainSpec structs supported - let genesis: Genesis = serde_json::from_str(&raw)?; - - Arc::new(genesis.into()) - } + _ => Arc::new(parse_custom_chain_spec(s)?), }) } +/// Clap value parser for [`OpChainSpec`](reth_optimism_chainspec::OpChainSpec)s. +/// +/// The value parser matches either a known chain, the path +/// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct. +#[cfg(feature = "optimism")] +pub fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { + Ok(Arc::new(match s { + "optimism" => OP_MAINNET.inner.clone(), + "optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.inner.clone(), + "base" => BASE_MAINNET.inner.clone(), + "base_sepolia" | "base-sepolia" => BASE_SEPOLIA.inner.clone(), + "dev" => OP_DEV.inner.clone(), + _ => parse_custom_chain_spec(s)?, + })) +} + +/// Parses a custom [`ChainSpec`]. +pub fn parse_custom_chain_spec(s: &str) -> eyre::Result { + // try to read json from path first + let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) { + Ok(raw) => raw, + Err(io_err) => { + // valid json may start with "\n", but must contain "{" + if s.contains('{') { + s.to_string() + } else { + return Err(io_err.into()) // assume invalid path + } + } + }; + + // both serialized Genesis and ChainSpec structs supported + let genesis: Genesis = serde_json::from_str(&raw)?; + + Ok(genesis.into()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/optimism/chainspec/Cargo.toml b/crates/optimism/chainspec/Cargo.toml index a33dc0885..d4d9eb87f 100644 --- a/crates/optimism/chainspec/Cargo.toml +++ b/crates/optimism/chainspec/Cargo.toml @@ -26,6 +26,7 @@ serde_json.workspace = true # misc once_cell.workspace = true +derive_more.workspace = true [dev-dependencies] reth-chainspec = { workspace = true, features = ["test-utils"] } @@ -34,5 +35,4 @@ op-alloy-rpc-types.workspace = true [features] default = ["std"] -std = [] -optimism = [] \ No newline at end of file +std = [] \ No newline at end of file diff --git a/crates/optimism/chainspec/res/genesis/dev.json b/crates/optimism/chainspec/res/genesis/dev.json new file mode 100644 index 000000000..ed0522167 --- /dev/null +++ b/crates/optimism/chainspec/res/genesis/dev.json @@ -0,0 +1 @@ +{"nonce":"0x0","timestamp":"0x6490fdd2","extraData":"0x","gasLimit":"0x1c9c380","difficulty":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","coinbase":"0x0000000000000000000000000000000000000000","stateRoot":"0x5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494","alloc":{"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266":{"balance":"0xD3C21BCECCEDA1000000"},"0x70997970C51812dc3A010C7d01b50e0d17dc79C8":{"balance":"0xD3C21BCECCEDA1000000"},"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC":{"balance":"0xD3C21BCECCEDA1000000"},"0x90F79bf6EB2c4f870365E785982E1f101E93b906":{"balance":"0xD3C21BCECCEDA1000000"},"0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65":{"balance":"0xD3C21BCECCEDA1000000"},"0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc":{"balance":"0xD3C21BCECCEDA1000000"},"0x976EA74026E726554dB657fA54763abd0C3a0aa9":{"balance":"0xD3C21BCECCEDA1000000"},"0x14dC79964da2C08b23698B3D3cc7Ca32193d9955":{"balance":"0xD3C21BCECCEDA1000000"},"0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f":{"balance":"0xD3C21BCECCEDA1000000"},"0xa0Ee7A142d267C1f36714E4a8F75612F20a79720":{"balance":"0xD3C21BCECCEDA1000000"},"0xBcd4042DE499D14e55001CcbB24a551F3b954096":{"balance":"0xD3C21BCECCEDA1000000"},"0x71bE63f3384f5fb98995898A86B02Fb2426c5788":{"balance":"0xD3C21BCECCEDA1000000"},"0xFABB0ac9d68B0B445fB7357272Ff202C5651694a":{"balance":"0xD3C21BCECCEDA1000000"},"0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec":{"balance":"0xD3C21BCECCEDA1000000"},"0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097":{"balance":"0xD3C21BCECCEDA1000000"},"0xcd3B766CCDd6AE721141F452C550Ca635964ce71":{"balance":"0xD3C21BCECCEDA1000000"},"0x2546BcD3c84621e976D8185a91A922aE77ECEc30":{"balance":"0xD3C21BCECCEDA1000000"},"0xbDA5747bFD65F08deb54cb465eB87D40e51B197E":{"balance":"0xD3C21BCECCEDA1000000"},"0xdD2FD4581271e230360230F9337D5c0430Bf44C0":{"balance":"0xD3C21BCECCEDA1000000"},"0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199":{"balance":"0xD3C21BCECCEDA1000000"}},"number":"0x0","gasUsed":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"} \ No newline at end of file diff --git a/crates/optimism/chainspec/src/base.rs b/crates/optimism/chainspec/src/base.rs index 0ecc62415..d14a6b3d6 100644 --- a/crates/optimism/chainspec/src/base.rs +++ b/crates/optimism/chainspec/src/base.rs @@ -11,27 +11,31 @@ use once_cell::sync::Lazy; use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::{EthereumHardfork, OptimismHardfork}; +use crate::OpChainSpec; + /// The Base mainnet spec -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" - )), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - hardforks: OptimismHardfork::base_mainnet(), - base_fee_params: BaseFeeParamsKind::Variable( - vec![ - (EthereumHardfork::London.boxed(), BaseFeeParams::optimism()), - (OptimismHardfork::Canyon.boxed(), BaseFeeParams::optimism_canyon()), - ] - .into(), - ), - max_gas_limit: crate::constants::BASE_MAINNET_MAX_GAS_LIMIT, - prune_delete_limit: 10000, - ..Default::default() +pub static BASE_MAINNET: Lazy> = Lazy::new(|| { + OpChainSpec { + inner: 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" + )), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: OptimismHardfork::base_mainnet(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![ + (EthereumHardfork::London.boxed(), BaseFeeParams::optimism()), + (OptimismHardfork::Canyon.boxed(), BaseFeeParams::optimism_canyon()), + ] + .into(), + ), + max_gas_limit: crate::constants::BASE_MAINNET_MAX_GAS_LIMIT, + prune_delete_limit: 10000, + ..Default::default() + }, } .into() }); diff --git a/crates/optimism/chainspec/src/base_sepolia.rs b/crates/optimism/chainspec/src/base_sepolia.rs index 8a12617af..916e47cf9 100644 --- a/crates/optimism/chainspec/src/base_sepolia.rs +++ b/crates/optimism/chainspec/src/base_sepolia.rs @@ -11,27 +11,31 @@ use once_cell::sync::Lazy; use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::{EthereumHardfork, OptimismHardfork}; +use crate::OpChainSpec; + /// The Base Sepolia spec -pub static BASE_SEPOLIA: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::base_sepolia(), - genesis: serde_json::from_str(include_str!("../res/genesis/sepolia_base.json")) - .expect("Can't deserialize Base Sepolia genesis json"), - genesis_hash: Some(b256!( - "0dcc9e089e30b90ddfc55be9a37dd15bc551aeee999d2e2b51414c54eaf934e4" - )), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - hardforks: OptimismHardfork::base_sepolia(), - base_fee_params: BaseFeeParamsKind::Variable( - vec![ - (EthereumHardfork::London.boxed(), BaseFeeParams::base_sepolia()), - (OptimismHardfork::Canyon.boxed(), BaseFeeParams::base_sepolia_canyon()), - ] - .into(), - ), - max_gas_limit: crate::constants::BASE_SEPOLIA_MAX_GAS_LIMIT, - prune_delete_limit: 10000, - ..Default::default() +pub static BASE_SEPOLIA: Lazy> = Lazy::new(|| { + OpChainSpec { + inner: ChainSpec { + chain: Chain::base_sepolia(), + genesis: serde_json::from_str(include_str!("../res/genesis/sepolia_base.json")) + .expect("Can't deserialize Base Sepolia genesis json"), + genesis_hash: Some(b256!( + "0dcc9e089e30b90ddfc55be9a37dd15bc551aeee999d2e2b51414c54eaf934e4" + )), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: OptimismHardfork::base_sepolia(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![ + (EthereumHardfork::London.boxed(), BaseFeeParams::base_sepolia()), + (OptimismHardfork::Canyon.boxed(), BaseFeeParams::base_sepolia_canyon()), + ] + .into(), + ), + max_gas_limit: crate::constants::BASE_SEPOLIA_MAX_GAS_LIMIT, + prune_delete_limit: 10000, + ..Default::default() + }, } .into() }); diff --git a/crates/optimism/chainspec/src/dev.rs b/crates/optimism/chainspec/src/dev.rs new file mode 100644 index 000000000..3351ee03b --- /dev/null +++ b/crates/optimism/chainspec/src/dev.rs @@ -0,0 +1,38 @@ +//! Chain specification in dev mode for custom chain. + +#[cfg(not(feature = "std"))] +use alloc::sync::Arc; +#[cfg(feature = "std")] +use std::sync::Arc; + +use alloy_chains::Chain; +use alloy_primitives::U256; +use once_cell::sync::Lazy; +use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; +use reth_ethereum_forks::DEV_HARDFORKS; +use reth_primitives_traits::constants::DEV_GENESIS_HASH; + +use crate::OpChainSpec; + +/// OP 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 OP_DEV: Lazy> = Lazy::new(|| { + { + OpChainSpec { + inner: 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(DEV_GENESIS_HASH), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: DEV_HARDFORKS.clone(), + base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), + deposit_contract: None, // TODO: do we even have? + ..Default::default() + }, + } + } + .into() +}); diff --git a/crates/optimism/chainspec/src/lib.rs b/crates/optimism/chainspec/src/lib.rs index 711c904e8..fd290448d 100644 --- a/crates/optimism/chainspec/src/lib.rs +++ b/crates/optimism/chainspec/src/lib.rs @@ -5,10 +5,7 @@ html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] -#![cfg_attr(all(not(test), feature = "optimism"), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] -// The `optimism` feature must be enabled to use this crate. -#![cfg(feature = "optimism")] #[cfg(not(feature = "std"))] extern crate alloc; @@ -17,14 +14,26 @@ pub mod constants; mod base; mod base_sepolia; +mod dev; mod op; mod op_sepolia; pub use base::BASE_MAINNET; pub use base_sepolia::BASE_SEPOLIA; +pub use dev::OP_DEV; pub use op::OP_MAINNET; pub use op_sepolia::OP_SEPOLIA; +use derive_more::{Constructor, Deref, Into}; +use reth_chainspec::ChainSpec; + +/// OP stack chain spec type. +#[derive(Debug, Deref, Into, Constructor)] +pub struct OpChainSpec { + /// [`ChainSpec`]. + pub inner: ChainSpec, +} + #[cfg(test)] mod tests { use alloy_genesis::Genesis; diff --git a/crates/optimism/chainspec/src/op.rs b/crates/optimism/chainspec/src/op.rs index 902085157..15a952ae6 100644 --- a/crates/optimism/chainspec/src/op.rs +++ b/crates/optimism/chainspec/src/op.rs @@ -12,29 +12,33 @@ use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::{EthereumHardfork, OptimismHardfork}; use reth_primitives_traits::constants::ETHEREUM_BLOCK_GAS_LIMIT; +use crate::OpChainSpec; + /// The Optimism Mainnet spec -pub static OP_MAINNET: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::optimism_mainnet(), - // genesis contains empty alloc field because state at first bedrock block is imported - // manually from trusted source - genesis: serde_json::from_str(include_str!("../res/genesis/optimism.json")) - .expect("Can't deserialize Optimism Mainnet genesis json"), - genesis_hash: Some(b256!( - "7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" - )), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - hardforks: OptimismHardfork::op_mainnet(), - base_fee_params: BaseFeeParamsKind::Variable( - vec![ - (EthereumHardfork::London.boxed(), BaseFeeParams::optimism()), - (OptimismHardfork::Canyon.boxed(), BaseFeeParams::optimism_canyon()), - ] - .into(), - ), - max_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, - prune_delete_limit: 10000, - ..Default::default() +pub static OP_MAINNET: Lazy> = Lazy::new(|| { + OpChainSpec { + inner: ChainSpec { + chain: Chain::optimism_mainnet(), + // genesis contains empty alloc field because state at first bedrock block is imported + // manually from trusted source + genesis: serde_json::from_str(include_str!("../res/genesis/optimism.json")) + .expect("Can't deserialize Optimism Mainnet genesis json"), + genesis_hash: Some(b256!( + "7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b" + )), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: OptimismHardfork::op_mainnet(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![ + (EthereumHardfork::London.boxed(), BaseFeeParams::optimism()), + (OptimismHardfork::Canyon.boxed(), BaseFeeParams::optimism_canyon()), + ] + .into(), + ), + max_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, + prune_delete_limit: 10000, + ..Default::default() + }, } .into() }); diff --git a/crates/optimism/chainspec/src/op_sepolia.rs b/crates/optimism/chainspec/src/op_sepolia.rs index 213b10aad..9f223f7e0 100644 --- a/crates/optimism/chainspec/src/op_sepolia.rs +++ b/crates/optimism/chainspec/src/op_sepolia.rs @@ -12,27 +12,31 @@ use reth_chainspec::{BaseFeeParams, BaseFeeParamsKind, ChainSpec}; use reth_ethereum_forks::{EthereumHardfork, OptimismHardfork}; use reth_primitives_traits::constants::ETHEREUM_BLOCK_GAS_LIMIT; +use crate::OpChainSpec; + /// The OP Sepolia spec -pub static OP_SEPOLIA: Lazy> = Lazy::new(|| { - ChainSpec { - chain: Chain::from_named(NamedChain::OptimismSepolia), - genesis: serde_json::from_str(include_str!("../res/genesis/sepolia_op.json")) - .expect("Can't deserialize OP Sepolia genesis json"), - genesis_hash: Some(b256!( - "102de6ffb001480cc9b8b548fd05c34cd4f46ae4aa91759393db90ea0409887d" - )), - paris_block_and_final_difficulty: Some((0, U256::from(0))), - hardforks: OptimismHardfork::op_sepolia(), - base_fee_params: BaseFeeParamsKind::Variable( - vec![ - (EthereumHardfork::London.boxed(), BaseFeeParams::optimism_sepolia()), - (OptimismHardfork::Canyon.boxed(), BaseFeeParams::optimism_sepolia_canyon()), - ] - .into(), - ), - max_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, - prune_delete_limit: 10000, - ..Default::default() +pub static OP_SEPOLIA: Lazy> = Lazy::new(|| { + OpChainSpec { + inner: ChainSpec { + chain: Chain::from_named(NamedChain::OptimismSepolia), + genesis: serde_json::from_str(include_str!("../res/genesis/sepolia_op.json")) + .expect("Can't deserialize OP Sepolia genesis json"), + genesis_hash: Some(b256!( + "102de6ffb001480cc9b8b548fd05c34cd4f46ae4aa91759393db90ea0409887d" + )), + paris_block_and_final_difficulty: Some((0, U256::from(0))), + hardforks: OptimismHardfork::op_sepolia(), + base_fee_params: BaseFeeParamsKind::Variable( + vec![ + (EthereumHardfork::London.boxed(), BaseFeeParams::optimism_sepolia()), + (OptimismHardfork::Canyon.boxed(), BaseFeeParams::optimism_sepolia_canyon()), + ] + .into(), + ), + max_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT, + prune_delete_limit: 10000, + ..Default::default() + }, } .into() }); diff --git a/crates/optimism/cli/Cargo.toml b/crates/optimism/cli/Cargo.toml index 346ffbb6a..9f4be1586 100644 --- a/crates/optimism/cli/Cargo.toml +++ b/crates/optimism/cli/Cargo.toml @@ -27,7 +27,7 @@ reth-primitives.workspace = true ## optimisim reth-optimism-primitives.workspace = true -reth-optimism-chainspec = { workspace = true, features = ["optimism"] } +reth-optimism-chainspec.workspace = true reth-chainspec.workspace = true reth-stages-types.workspace = true @@ -39,14 +39,11 @@ reth-evm-optimism.workspace = true reth-cli.workspace = true # eth -alloy-genesis.workspace = true alloy-primitives.workspace = true alloy-rlp.workspace = true # misc -shellexpand.workspace = true -serde_json.workspace = true futures-util.workspace = true clap = { workspace = true, features = ["derive", "env"] } diff --git a/crates/optimism/cli/src/chainspec.rs b/crates/optimism/cli/src/chainspec.rs index 020a9e6bf..bee5124a3 100644 --- a/crates/optimism/cli/src/chainspec.rs +++ b/crates/optimism/cli/src/chainspec.rs @@ -1,41 +1,24 @@ -use std::{ffi::OsStr, fs, path::PathBuf, sync::Arc}; +use std::{ffi::OsStr, sync::Arc}; -use alloy_genesis::Genesis; use clap::{builder::TypedValueParser, error::Result, Arg, Command}; -use reth_chainspec::{ChainSpec, DEV}; use reth_cli::chainspec::ChainSpecParser; -use reth_optimism_chainspec::{BASE_MAINNET, BASE_SEPOLIA, OP_MAINNET, OP_SEPOLIA}; +use reth_node_core::args::utils::parse_custom_chain_spec; +use reth_optimism_chainspec::{ + OpChainSpec, BASE_MAINNET, BASE_SEPOLIA, OP_DEV, OP_MAINNET, OP_SEPOLIA, +}; -/// Clap value parser for [`ChainSpec`]s. +/// Clap value parser for [`OpChainSpec`]s. /// /// The value parser matches either a known chain, the path /// to a json file, or a json formatted string in-memory. The json needs to be a Genesis struct. -fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { +fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { Ok(match s { - "dev" => DEV.clone(), + "dev" => OP_DEV.clone(), "optimism" => OP_MAINNET.clone(), "optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.clone(), "base" => BASE_MAINNET.clone(), "base_sepolia" | "base-sepolia" => BASE_SEPOLIA.clone(), - _ => { - // try to read json from path first - let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) { - Ok(raw) => raw, - Err(io_err) => { - // valid json may start with "\n", but must contain "{" - if s.contains('{') { - s.to_string() - } else { - return Err(io_err.into()) // assume invalid path - } - } - }; - - // both serialized Genesis and ChainSpec structs supported - let genesis: Genesis = serde_json::from_str(&raw)?; - - Arc::new(genesis.into()) - } + _ => Arc::new(OpChainSpec { inner: parse_custom_chain_spec(s)? }), }) } @@ -43,8 +26,9 @@ fn chain_value_parser(s: &str) -> eyre::Result, eyre::Error> { #[derive(Debug, Clone, Default)] pub struct OpChainSpecParser; -impl ChainSpecParser for OpChainSpecParser { +impl ChainSpecParser for OpChainSpecParser { const SUPPORTED_CHAINS: &'static [&'static str] = &[ + "dev", "optimism", "optimism_sepolia", "optimism-sepolia", @@ -53,13 +37,13 @@ impl ChainSpecParser for OpChainSpecParser { "base-sepolia", ]; - fn parse(s: &str) -> eyre::Result> { + fn parse(s: &str) -> eyre::Result> { chain_value_parser(s) } } impl TypedValueParser for OpChainSpecParser { - type Value = Arc; + type Value = Arc; fn parse_ref( &self, @@ -69,7 +53,7 @@ impl TypedValueParser for OpChainSpecParser { ) -> Result { let val = value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?; - ::parse(val).map_err(|err| { + >::parse(val).map_err(|err| { let arg = arg.map(|a| a.to_string()).unwrap_or_else(|| "...".to_owned()); let possible_values = Self::SUPPORTED_CHAINS.join(", "); clap::Error::raw( @@ -96,7 +80,7 @@ mod tests { #[test] fn parse_known_chain_spec() { for &chain in OpChainSpecParser::SUPPORTED_CHAINS { - assert!(::parse(chain).is_ok()); + assert!(>::parse(chain).is_ok()); } } } diff --git a/crates/optimism/evm/Cargo.toml b/crates/optimism/evm/Cargo.toml index 0720aaf18..6e06a280a 100644 --- a/crates/optimism/evm/Cargo.toml +++ b/crates/optimism/evm/Cargo.toml @@ -34,6 +34,7 @@ tracing.workspace = true [dev-dependencies] reth-revm = { workspace = true, features = ["test-utils"] } +reth-optimism-chainspec.workspace = true [features] optimism = [ diff --git a/crates/optimism/evm/src/execute.rs b/crates/optimism/evm/src/execute.rs index 8d800778d..3b18f4389 100644 --- a/crates/optimism/evm/src/execute.rs +++ b/crates/optimism/evm/src/execute.rs @@ -501,8 +501,11 @@ mod tests { let account = Account { balance: U256::MAX, ..Account::default() }; db.insert_account(addr, account, None, HashMap::new()); - let chain_spec = - Arc::new(ChainSpecBuilder::from(&*BASE_MAINNET).regolith_activated().build()); + let chain_spec = Arc::new( + ChainSpecBuilder::from(&Arc::new(BASE_MAINNET.inner.clone())) + .regolith_activated() + .build(), + ); let tx = TransactionSigned::from_transaction_and_signature( Transaction::Eip1559(TxEip1559 { @@ -582,8 +585,11 @@ mod tests { db.insert_account(addr, account, None, HashMap::new()); - let chain_spec = - Arc::new(ChainSpecBuilder::from(&*BASE_MAINNET).canyon_activated().build()); + let chain_spec = Arc::new( + ChainSpecBuilder::from(&Arc::new(BASE_MAINNET.inner.clone())) + .canyon_activated() + .build(), + ); let tx = TransactionSigned::from_transaction_and_signature( Transaction::Eip1559(TxEip1559 { diff --git a/crates/optimism/node/Cargo.toml b/crates/optimism/node/Cargo.toml index 9ff578027..670b392dd 100644 --- a/crates/optimism/node/Cargo.toml +++ b/crates/optimism/node/Cargo.toml @@ -37,7 +37,7 @@ reth-rpc-eth-types.workspace = true reth-rpc-eth-api.workspace = true reth-optimism-rpc.workspace = true reth-rpc.workspace = true -reth-optimism-chainspec = { workspace = true, features = ["optimism"] } +reth-optimism-chainspec.workspace = true # async async-trait.workspace = true diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index fdcca363b..2c50ad77f 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -20,7 +20,7 @@ reth-trie-common.workspace = true revm-primitives = { workspace = true, features = ["serde"] } reth-chainspec = { workspace = true, optional = true } reth-codecs = { workspace = true, optional = true } -reth-optimism-chainspec = { workspace = true, optional = true, features = ["optimism"] } +reth-optimism-chainspec = { workspace = true, optional = true } # ethereum alloy-primitives = { workspace = true, features = ["rand", "rlp"] }