From e6995e5cdf3099fd1d492e63d599d11cae77b142 Mon Sep 17 00:00:00 2001 From: grantkee <49913008+grantkee@users.noreply.github.com> Date: Thu, 2 Nov 2023 21:58:29 -0500 Subject: [PATCH] Parse Genesis Value from Memory (#5238) --- bin/reth/src/args/utils.rs | 120 ++++++++++++++++++++++++++-- crates/primitives/src/chain/spec.rs | 68 +++++++++++++++- crates/primitives/src/lib.rs | 2 +- 3 files changed, 180 insertions(+), 10 deletions(-) diff --git a/bin/reth/src/args/utils.rs b/bin/reth/src/args/utils.rs index b0035f1c64..0f9ef5c6bb 100644 --- a/bin/reth/src/args/utils.rs +++ b/bin/reth/src/args/utils.rs @@ -34,8 +34,11 @@ pub fn chain_spec_value_parser(s: &str) -> eyre::Result, eyre::Er }) } -/// Clap value parser for [ChainSpec]s that takes either a built-in genesis format or the path -/// to a custom one. +/// Clap value parser for [ChainSpec]s. +/// +/// The value parser matches either a known chain, the path +/// to a json file, or a json formatted string in-memory. The json can be either +/// a serialized [ChainSpec] or Genesis struct. pub fn genesis_value_parser(s: &str) -> eyre::Result, eyre::Error> { Ok(match s { "mainnet" => MAINNET.clone(), @@ -44,8 +47,21 @@ pub fn genesis_value_parser(s: &str) -> eyre::Result, eyre::Error "holesky" => HOLESKY.clone(), "dev" => DEV.clone(), _ => { - let raw = fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; - let genesis: AllGenesisFormats = serde_json::from_str(&raw)?; + // both serialized Genesis and ChainSpec structs supported + let genesis: AllGenesisFormats = + // try to read json from path first + match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) { + Ok(raw) => serde_json::from_str(&raw)?, + Err(io_err) => { + // valid json may start with "\n", but must contain "{" + if s.contains('{') { + serde_json::from_str(s)? + } else { + return Err(io_err.into()) // assume invalid path + } + } + }; + Arc::new(genesis.into()) } }) @@ -108,16 +124,110 @@ pub fn parse_socket_address(value: &str) -> eyre::Result for AllGenesisFormats { @@ -1189,10 +1189,13 @@ impl DepositContract { #[cfg(test)] mod tests { use super::*; - use crate::{b256, hex, NamedChain, B256, DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA, U256}; + use crate::{ + b256, hex, ChainConfig, GenesisAccount, NamedChain, B256, DEV, GOERLI, HOLESKY, MAINNET, + SEPOLIA, U256, + }; use alloy_rlp::Encodable; use bytes::BytesMut; - use std::str::FromStr; + use std::{collections::HashMap, str::FromStr}; fn test_fork_ids(spec: &ChainSpec, cases: &[(Head, ForkId)]) { for (block, expected_id) in cases { @@ -2293,4 +2296,61 @@ Post-merge hard forks (timestamp based): .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 379ace2dc4..36fba28daa 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -67,7 +67,7 @@ pub use constants::{ #[cfg(feature = "c-kzg")] pub use eip4844::{calculate_excess_blob_gas, kzg_to_versioned_hash}; pub use forkid::{ForkFilter, ForkHash, ForkId, ForkTransition, ValidationError}; -pub use genesis::{Genesis, GenesisAccount}; +pub use genesis::{ChainConfig, Genesis, GenesisAccount}; pub use hardfork::Hardfork; pub use header::{Head, Header, HeadersDirection, SealedHeader}; pub use integer_list::IntegerList;