diff --git a/bin/reth/src/args/mod.rs b/bin/reth/src/args/mod.rs index a77e1e446..ba26ce04b 100644 --- a/bin/reth/src/args/mod.rs +++ b/bin/reth/src/args/mod.rs @@ -24,4 +24,6 @@ mod stage_args; pub use stage_args::StageEnum; mod gas_price_oracle_args; +pub mod utils; + pub use gas_price_oracle_args::GasPriceOracleArgs; diff --git a/bin/reth/src/args/payload_builder_args.rs b/bin/reth/src/args/payload_builder_args.rs index c56239dd7..1957c8f65 100644 --- a/bin/reth/src/args/payload_builder_args.rs +++ b/bin/reth/src/args/payload_builder_args.rs @@ -1,4 +1,4 @@ -use crate::{utils::parse_duration_from_secs, version::default_extradata}; +use crate::{args::utils::parse_duration_from_secs, version::default_extradata}; use clap::{ builder::{RangedU64ValueParser, TypedValueParser}, Arg, Args, Command, diff --git a/bin/reth/src/args/utils.rs b/bin/reth/src/args/utils.rs new file mode 100644 index 000000000..3978ebe92 --- /dev/null +++ b/bin/reth/src/args/utils.rs @@ -0,0 +1,138 @@ +//! Clap parser utilities + +use reth_primitives::{AllGenesisFormats, BlockHashOrNumber, ChainSpec, GOERLI, MAINNET, SEPOLIA}; +use reth_revm::primitives::B256 as H256; +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs}, + path::PathBuf, + str::FromStr, + sync::Arc, + time::Duration, +}; + +/// Helper to parse a [Duration] from seconds +pub fn parse_duration_from_secs(arg: &str) -> eyre::Result { + let seconds = arg.parse()?; + Ok(Duration::from_secs(seconds)) +} + +/// Clap value parser for [ChainSpec]s that takes either a built-in chainspec or the path +/// to a custom one. +pub fn chain_spec_value_parser(s: &str) -> eyre::Result, eyre::Error> { + Ok(Arc::new(match s { + "mainnet" => MAINNET.clone(), + "goerli" => GOERLI.clone(), + "sepolia" => SEPOLIA.clone(), + _ => { + let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; + serde_json::from_str(&raw)? + } + })) +} + +/// Clap value parser for [ChainSpec]s that takes either a built-in genesis format or the path +/// to a custom one. +pub fn genesis_value_parser(s: &str) -> eyre::Result, eyre::Error> { + Ok(Arc::new(match s { + "mainnet" => MAINNET.clone(), + "goerli" => GOERLI.clone(), + "sepolia" => SEPOLIA.clone(), + _ => { + let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; + let genesis: AllGenesisFormats = serde_json::from_str(&raw)?; + genesis.into() + } + })) +} + +/// Parse [BlockHashOrNumber] +pub fn hash_or_num_value_parser(value: &str) -> eyre::Result { + match H256::from_str(value) { + Ok(hash) => Ok(BlockHashOrNumber::Hash(hash)), + Err(_) => Ok(BlockHashOrNumber::Number(value.parse()?)), + } +} + +/// Error thrown while parsing a socket address. +#[derive(thiserror::Error, Debug)] +pub enum SocketAddressParsingError { + /// Failed to convert the string into a socket addr + #[error("Cannot parse socket address: {0}")] + Io(#[from] std::io::Error), + /// Input must not be empty + #[error("Cannot parse socket address from empty string")] + Empty, + /// Failed to parse the address + #[error("Could not parse socket address from {0}")] + Parse(String), + /// Failed to parse port + #[error("Could not parse port: {0}")] + Port(#[from] std::num::ParseIntError), +} + +/// Parse a [SocketAddr] from a `str`. +/// +/// The following formats are checked: +/// +/// - If the value can be parsed as a `u16` or starts with `:` it is considered a port, and the +/// hostname is set to `localhost`. +/// - If the value contains `:` it is assumed to be the format `:` +/// - Otherwise it is assumed to be a hostname +/// +/// An error is returned if the value is empty. +pub fn parse_socket_address(value: &str) -> eyre::Result { + if value.is_empty() { + return Err(SocketAddressParsingError::Empty) + } + + if let Some(port) = value.strip_prefix(':').or_else(|| value.strip_prefix("localhost:")) { + let port: u16 = port.parse()?; + return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)) + } + if let Ok(port) = value.parse::() { + return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)) + } + value + .to_socket_addrs()? + .next() + .ok_or_else(|| SocketAddressParsingError::Parse(value.to_string())) +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::prelude::Rng; + use secp256k1::rand::thread_rng; + + #[test] + fn parse_chain_spec() { + for chain in ["mainnet", "sepolia", "goerli"] { + chain_spec_value_parser(chain).unwrap(); + genesis_value_parser(chain).unwrap(); + } + } + + #[test] + fn parse_socket_addresses() { + for value in ["localhost:9000", ":9000", "9000"] { + let socket_addr = parse_socket_address(value) + .unwrap_or_else(|_| panic!("could not parse socket address: {value}")); + + assert!(socket_addr.ip().is_loopback()); + assert_eq!(socket_addr.port(), 9000); + } + } + + #[test] + fn parse_socket_address_random() { + let port: u16 = thread_rng().gen(); + + for value in [format!("localhost:{port}"), format!(":{port}"), port.to_string()] { + let socket_addr = parse_socket_address(&value) + .unwrap_or_else(|_| panic!("could not parse socket address: {value}")); + + assert!(socket_addr.ip().is_loopback()); + assert_eq!(socket_addr.port(), port); + } + } +} diff --git a/bin/reth/src/chain/import.rs b/bin/reth/src/chain/import.rs index 5f3f9ea64..cdfb741ec 100644 --- a/bin/reth/src/chain/import.rs +++ b/bin/reth/src/chain/import.rs @@ -8,6 +8,7 @@ use eyre::Context; use futures::{Stream, StreamExt}; use reth_beacon_consensus::BeaconConsensus; +use crate::args::utils::genesis_value_parser; use reth_config::Config; use reth_db::database::Database; use reth_downloaders::{ @@ -16,10 +17,7 @@ use reth_downloaders::{ }; use reth_interfaces::consensus::Consensus; use reth_primitives::{ChainSpec, H256}; -use reth_staged_sync::utils::{ - chainspec::genesis_value_parser, - init::{init_db, init_genesis}, -}; +use reth_staged_sync::utils::init::{init_db, init_genesis}; use reth_stages::{ prelude::*, stages::{ diff --git a/bin/reth/src/chain/init.rs b/bin/reth/src/chain/init.rs index c3ac2c002..9be1ae163 100644 --- a/bin/reth/src/chain/init.rs +++ b/bin/reth/src/chain/init.rs @@ -1,10 +1,10 @@ -use crate::dirs::{DataDirPath, MaybePlatformPath}; +use crate::{ + args::utils::genesis_value_parser, + dirs::{DataDirPath, MaybePlatformPath}, +}; use clap::Parser; use reth_primitives::ChainSpec; -use reth_staged_sync::utils::{ - chainspec::genesis_value_parser, - init::{init_db, init_genesis}, -}; +use reth_staged_sync::utils::init::{init_db, init_genesis}; use std::sync::Arc; use tracing::info; diff --git a/bin/reth/src/db/mod.rs b/bin/reth/src/db/mod.rs index 5634b2e0e..30b0b2ec4 100644 --- a/bin/reth/src/db/mod.rs +++ b/bin/reth/src/db/mod.rs @@ -1,5 +1,6 @@ //! Database debugging tool use crate::{ + args::utils::genesis_value_parser, dirs::{DataDirPath, MaybePlatformPath}, utils::DbTool, }; @@ -9,7 +10,6 @@ use eyre::WrapErr; use human_bytes::human_bytes; use reth_db::{database::Database, tables}; use reth_primitives::ChainSpec; -use reth_staged_sync::utils::chainspec::genesis_value_parser; use std::sync::Arc; use tracing::error; diff --git a/bin/reth/src/debug_cmd/execution.rs b/bin/reth/src/debug_cmd/execution.rs index 1fcd0daeb..e2211804a 100644 --- a/bin/reth/src/debug_cmd/execution.rs +++ b/bin/reth/src/debug_cmd/execution.rs @@ -27,10 +27,9 @@ use reth_network::NetworkHandle; use reth_network_api::NetworkInfo; use reth_primitives::{stage::StageId, BlockHashOrNumber, BlockNumber, ChainSpec, H256}; use reth_provider::{providers::get_stage_checkpoint, ShareableDatabase, Transaction}; -use reth_staged_sync::utils::{ - chainspec::genesis_value_parser, - init::{init_db, init_genesis}, -}; +use reth_staged_sync::utils::init::{init_db, init_genesis}; + +use crate::args::utils::genesis_value_parser; use reth_stages::{ sets::DefaultStages, stages::{ diff --git a/bin/reth/src/debug_cmd/merkle.rs b/bin/reth/src/debug_cmd/merkle.rs index 64c5093d3..1b90fb1a2 100644 --- a/bin/reth/src/debug_cmd/merkle.rs +++ b/bin/reth/src/debug_cmd/merkle.rs @@ -1,5 +1,8 @@ //! Command for debugging merkle trie calculation. -use crate::dirs::{DataDirPath, MaybePlatformPath}; +use crate::{ + args::utils::genesis_value_parser, + dirs::{DataDirPath, MaybePlatformPath}, +}; use clap::Parser; use reth_db::{cursor::DbCursorRO, tables, transaction::DbTx}; use reth_primitives::{ @@ -7,7 +10,7 @@ use reth_primitives::{ ChainSpec, }; use reth_provider::Transaction; -use reth_staged_sync::utils::{chainspec::genesis_value_parser, init::init_db}; +use reth_staged_sync::utils::init::init_db; use reth_stages::{ stages::{ AccountHashingStage, ExecutionStage, ExecutionStageThresholds, MerkleStage, diff --git a/bin/reth/src/dirs.rs b/bin/reth/src/dirs.rs index 7d25cba63..796377b6a 100644 --- a/bin/reth/src/dirs.rs +++ b/bin/reth/src/dirs.rs @@ -1,6 +1,6 @@ //! reth data directories. +use crate::utils::parse_path; use reth_primitives::Chain; -use reth_staged_sync::utils::parse_path; use std::{ env::VarError, fmt::{Debug, Display, Formatter}, diff --git a/bin/reth/src/node/mod.rs b/bin/reth/src/node/mod.rs index d77b8519f..63deba362 100644 --- a/bin/reth/src/node/mod.rs +++ b/bin/reth/src/node/mod.rs @@ -51,11 +51,7 @@ use reth_provider::{ use reth_revm::Factory; use reth_revm_inspectors::stack::Hook; use reth_rpc_engine_api::EngineApi; -use reth_staged_sync::utils::{ - chainspec::genesis_value_parser, - init::{init_db, init_genesis}, - parse_socket_address, -}; +use reth_staged_sync::utils::init::{init_db, init_genesis}; use reth_stages::{ prelude::*, stages::{ @@ -74,7 +70,13 @@ use std::{ use tokio::sync::{mpsc::unbounded_channel, oneshot, watch}; use tracing::*; -use crate::{args::PayloadBuilderArgs, dirs::MaybePlatformPath}; +use crate::{ + args::{ + utils::{genesis_value_parser, parse_socket_address}, + PayloadBuilderArgs, + }, + dirs::MaybePlatformPath, +}; use reth_interfaces::p2p::headers::client::HeadersClient; use reth_payload_builder::PayloadBuilderService; use reth_primitives::bytes::BytesMut; diff --git a/bin/reth/src/p2p/mod.rs b/bin/reth/src/p2p/mod.rs index 8c8811d0b..ef940f96f 100644 --- a/bin/reth/src/p2p/mod.rs +++ b/bin/reth/src/p2p/mod.rs @@ -1,6 +1,10 @@ //! P2P Debugging tool use crate::{ - args::{get_secret_key, DiscoveryArgs}, + args::{ + get_secret_key, + utils::{chain_spec_value_parser, hash_or_num_value_parser}, + DiscoveryArgs, + }, dirs::{DataDirPath, MaybePlatformPath}, utils::get_single_header, }; @@ -12,7 +16,6 @@ use reth_discv4::NatResolver; use reth_interfaces::p2p::bodies::client::BodiesClient; use reth_primitives::{BlockHashOrNumber, ChainSpec, NodeRecord}; use reth_provider::ShareableDatabase; -use reth_staged_sync::utils::{chainspec::chain_spec_value_parser, hash_or_num_value_parser}; use std::{path::PathBuf, sync::Arc}; /// `reth p2p` command diff --git a/bin/reth/src/stage/drop.rs b/bin/reth/src/stage/drop.rs index bb8e6ff17..61882a95d 100644 --- a/bin/reth/src/stage/drop.rs +++ b/bin/reth/src/stage/drop.rs @@ -1,6 +1,6 @@ //! Database debugging tool use crate::{ - args::StageEnum, + args::{utils::genesis_value_parser, StageEnum}, dirs::{DataDirPath, MaybePlatformPath}, utils::DbTool, }; @@ -12,7 +12,7 @@ use reth_db::{ transaction::DbTxMut, }; use reth_primitives::{stage::StageId, ChainSpec}; -use reth_staged_sync::utils::{chainspec::genesis_value_parser, init::insert_genesis_state}; +use reth_staged_sync::utils::init::insert_genesis_state; use std::sync::Arc; use tracing::info; diff --git a/bin/reth/src/stage/dump/mod.rs b/bin/reth/src/stage/dump/mod.rs index ab6881ca2..116a777ce 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, table::TableImporter, tables, transaction::DbTx, }; use reth_primitives::ChainSpec; -use reth_staged_sync::utils::{chainspec::genesis_value_parser, init::init_db}; +use reth_staged_sync::utils::init::init_db; use std::{path::PathBuf, sync::Arc}; use tracing::info; @@ -22,6 +22,7 @@ mod execution; use execution::dump_execution_stage; mod merkle; +use crate::args::utils::genesis_value_parser; use merkle::dump_merkle_stage; /// `reth dump-stage` command diff --git a/bin/reth/src/stage/run.rs b/bin/reth/src/stage/run.rs index f763eba74..f8a45cdf3 100644 --- a/bin/reth/src/stage/run.rs +++ b/bin/reth/src/stage/run.rs @@ -2,7 +2,7 @@ //! //! Stage debugging tool use crate::{ - args::{get_secret_key, NetworkArgs, StageEnum}, + args::{get_secret_key, utils::chain_spec_value_parser, NetworkArgs, StageEnum}, dirs::{DataDirPath, MaybePlatformPath}, prometheus_exporter, version::SHORT_VERSION, @@ -16,7 +16,7 @@ use reth_primitives::{ ChainSpec, }; use reth_provider::{ShareableDatabase, Transaction}; -use reth_staged_sync::utils::{chainspec::chain_spec_value_parser, init::init_db}; +use reth_staged_sync::utils::init::init_db; use reth_stages::{ stages::{ BodyStage, ExecutionStage, ExecutionStageThresholds, IndexAccountHistoryStage, diff --git a/bin/reth/src/stage/unwind.rs b/bin/reth/src/stage/unwind.rs index d709ddaeb..c26c31748 100644 --- a/bin/reth/src/stage/unwind.rs +++ b/bin/reth/src/stage/unwind.rs @@ -10,9 +10,9 @@ use reth_db::{ }; use reth_primitives::{BlockHashOrNumber, ChainSpec}; use reth_provider::Transaction; -use reth_staged_sync::utils::chainspec::genesis_value_parser; use std::{ops::RangeInclusive, sync::Arc}; +use crate::args::utils::genesis_value_parser; use reth_db::cursor::DbCursorRO; /// `reth stage unwind` command diff --git a/bin/reth/src/utils.rs b/bin/reth/src/utils.rs index 943c2724f..b0321949d 100644 --- a/bin/reth/src/utils.rs +++ b/bin/reth/src/utils.rs @@ -12,14 +12,17 @@ use reth_interfaces::p2p::{ priority::Priority, }; use reth_primitives::{BlockHashOrNumber, HeadersDirection, SealedHeader}; -use std::{path::Path, time::Duration}; +use std::{ + env::VarError, + path::{Path, PathBuf}, +}; use tracing::info; /// Get a single header from network pub async fn get_single_header( client: Client, id: BlockHashOrNumber, -) -> eyre::Result +) -> Result where Client: HeadersClient, { @@ -104,8 +107,8 @@ impl<'a, DB: Database> DbTool<'a, DB> { } } -/// Helper to parse a [Duration] from seconds -pub fn parse_duration_from_secs(arg: &str) -> Result { - let seconds = arg.parse()?; - Ok(Duration::from_secs(seconds)) +/// Parses a user-specified path with support for environment variables and common shorthands (e.g. +/// ~ for the user's home directory). +pub fn parse_path(value: &str) -> Result> { + shellexpand::full(value).map(|path| PathBuf::from(path.into_owned())) } diff --git a/crates/staged-sync/src/utils/chainspec.rs b/crates/staged-sync/src/utils/chainspec.rs deleted file mode 100644 index 6d0897412..000000000 --- a/crates/staged-sync/src/utils/chainspec.rs +++ /dev/null @@ -1,44 +0,0 @@ -use reth_primitives::{AllGenesisFormats, ChainSpec, GOERLI, MAINNET, SEPOLIA}; -use std::{path::PathBuf, sync::Arc}; - -/// Clap value parser for [ChainSpec]s that takes either a built-in chainspec or the path -/// to a custom one. -pub fn chain_spec_value_parser(s: &str) -> Result, eyre::Error> { - Ok(Arc::new(match s { - "mainnet" => MAINNET.clone(), - "goerli" => GOERLI.clone(), - "sepolia" => SEPOLIA.clone(), - _ => { - let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; - serde_json::from_str(&raw)? - } - })) -} - -/// Clap value parser for [ChainSpec]s that takes either a built-in genesis format or the path -/// to a custom one. -pub fn genesis_value_parser(s: &str) -> Result, eyre::Error> { - Ok(Arc::new(match s { - "mainnet" => MAINNET.clone(), - "goerli" => GOERLI.clone(), - "sepolia" => SEPOLIA.clone(), - _ => { - let raw = std::fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?; - let genesis: AllGenesisFormats = serde_json::from_str(&raw)?; - genesis.into() - } - })) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_chain_spec() { - for chain in ["mainnet", "sepolia", "goerli"] { - chain_spec_value_parser(chain).unwrap(); - genesis_value_parser(chain).unwrap(); - } - } -} diff --git a/crates/staged-sync/src/utils/mod.rs b/crates/staged-sync/src/utils/mod.rs index c189bb2b5..8c15ca28e 100644 --- a/crates/staged-sync/src/utils/mod.rs +++ b/crates/staged-sync/src/utils/mod.rs @@ -1,114 +1,4 @@ //! Utility functions. -use reth_primitives::{BlockHashOrNumber, H256}; -use std::{ - env::VarError, - net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs}, - path::{Path, PathBuf}, - str::FromStr, -}; -use walkdir::{DirEntry, WalkDir}; - -/// Utilities for parsing chainspecs -pub mod chainspec; /// Utilities for initializing parts of the chain pub mod init; - -/// Finds all files in a directory with a given postfix. -pub fn find_all_files_with_postfix(path: &Path, postfix: &str) -> Vec { - WalkDir::new(path) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| e.file_name().to_string_lossy().ends_with(postfix)) - .map(DirEntry::into_path) - .collect::>() -} - -/// Parses a user-specified path with support for environment variables and common shorthands (e.g. -/// ~ for the user's home directory). -pub fn parse_path(value: &str) -> Result> { - shellexpand::full(value).map(|path| PathBuf::from(path.into_owned())) -} - -/// Parse [BlockHashOrNumber] -pub fn hash_or_num_value_parser(value: &str) -> Result { - match H256::from_str(value) { - Ok(hash) => Ok(BlockHashOrNumber::Hash(hash)), - Err(_) => Ok(BlockHashOrNumber::Number(value.parse()?)), - } -} - -/// Error thrown while parsing a socket address. -#[derive(thiserror::Error, Debug)] -pub enum SocketAddressParsingError { - /// Failed to convert the string into a socket addr - #[error("Cannot parse socket address: {0}")] - Io(#[from] std::io::Error), - /// Input must not be empty - #[error("Cannot parse socket address from empty string")] - Empty, - /// Failed to parse the address - #[error("Could not parse socket address from {0}")] - Parse(String), - /// Failed to parse port - #[error("Could not parse port: {0}")] - Port(#[from] std::num::ParseIntError), -} - -/// Parse a [SocketAddr] from a `str`. -/// -/// The following formats are checked: -/// -/// - If the value can be parsed as a `u16` or starts with `:` it is considered a port, and the -/// hostname is set to `localhost`. -/// - If the value contains `:` it is assumed to be the format `:` -/// - Otherwise it is assumed to be a hostname -/// -/// An error is returned if the value is empty. -pub fn parse_socket_address(value: &str) -> Result { - if value.is_empty() { - return Err(SocketAddressParsingError::Empty) - } - - if let Some(port) = value.strip_prefix(':').or_else(|| value.strip_prefix("localhost:")) { - let port: u16 = port.parse()?; - return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)) - } - if let Ok(port) = value.parse::() { - return Ok(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)) - } - value - .to_socket_addrs()? - .next() - .ok_or_else(|| SocketAddressParsingError::Parse(value.to_string())) -} - -#[cfg(test)] -mod tests { - use super::*; - use rand::{thread_rng, Rng}; - - #[test] - fn parse_socket_addresses() { - for value in ["localhost:9000", ":9000", "9000"] { - let socket_addr = parse_socket_address(value) - .unwrap_or_else(|_| panic!("could not parse socket address: {value}")); - - assert!(socket_addr.ip().is_loopback()); - assert_eq!(socket_addr.port(), 9000); - } - } - - #[test] - fn parse_socket_address_random() { - let port: u16 = thread_rng().gen(); - - for value in [format!("localhost:{port}"), format!(":{port}"), port.to_string()] { - let socket_addr = parse_socket_address(&value) - .unwrap_or_else(|_| panic!("could not parse socket address: {value}")); - - assert!(socket_addr.ip().is_loopback()); - assert_eq!(socket_addr.port(), port); - } - } -}