chore(cli): move utils to reth-cli-utils crate (#9297)

This commit is contained in:
Luca Provini
2024-07-04 14:34:55 +02:00
committed by GitHub
parent 610110bb67
commit f759ca6036
20 changed files with 234 additions and 180 deletions

View File

@ -46,6 +46,7 @@ reth-consensus.workspace = true
reth-consensus-debug-client.workspace = true
reth-rpc-types.workspace = true
reth-engine-util.workspace = true
reth-cli-util.workspace = true
## async
futures.workspace = true

View File

@ -11,6 +11,7 @@ use crate::{
};
use futures::Future;
use reth_chainspec::ChainSpec;
use reth_cli_util::get_secret_key;
use reth_db::{
test_utils::{create_test_rw_db_with_path, tempdir_path, TempDatabase},
DatabaseEnv,
@ -25,7 +26,7 @@ use reth_network::{
};
use reth_node_api::{FullNodeTypes, FullNodeTypesAdapter, NodeTypes};
use reth_node_core::{
args::{get_secret_key, DatadirArgs},
args::DatadirArgs,
cli::config::{PayloadBuilderConfig, RethTransactionPoolConfig},
dirs::{ChainPath, DataDirPath, MaybePlatformPath},
node_config::NodeConfig,

View File

@ -14,6 +14,7 @@ workspace = true
# reth
reth-chainspec.workspace = true
reth-primitives.workspace = true
reth-cli-util.workspace = true
reth-fs-util.workspace = true
reth-db = { workspace = true, features = ["mdbx"] }
reth-db-api.workspace = true
@ -57,7 +58,6 @@ metrics-util.workspace = true
eyre.workspace = true
clap = { workspace = true, features = ["derive"] }
humantime.workspace = true
thiserror.workspace = true
const_format.workspace = true
rand.workspace = true
derive_more.workspace = true

View File

@ -24,9 +24,6 @@ pub use database::DatabaseArgs;
mod log;
pub use log::{ColorMode, LogArgs};
mod secret_key;
pub use secret_key::{get_secret_key, SecretKeyError};
/// `PayloadBuilderArgs` struct for configuring the payload builder
mod payload_builder;
pub use payload_builder::PayloadBuilderArgs;

View File

@ -1,11 +1,9 @@
use crate::{
args::utils::parse_duration_from_secs, cli::config::PayloadBuilderConfig,
version::default_extradata,
};
use crate::{cli::config::PayloadBuilderConfig, version::default_extradata};
use clap::{
builder::{RangedU64ValueParser, TypedValueParser},
Arg, Args, Command,
};
use reth_cli_util::parse_duration_from_secs;
use reth_primitives::constants::{
ETHEREUM_BLOCK_GAS_LIMIT, MAXIMUM_EXTRA_DATA_SIZE, SLOT_DURATION,
};

View File

@ -1,62 +0,0 @@
use reth_fs_util::{self as fs, FsPathError};
use reth_network::config::rng_secret_key;
use reth_primitives::hex::encode as hex_encode;
use secp256k1::{Error as SecretKeyBaseError, SecretKey};
use std::{
io,
path::{Path, PathBuf},
};
use thiserror::Error;
/// Errors returned by loading a [`SecretKey`], including IO errors.
#[derive(Error, Debug)]
pub enum SecretKeyError {
/// Error encountered during decoding of the secret key.
#[error(transparent)]
SecretKeyDecodeError(#[from] SecretKeyBaseError),
/// Error related to file system path operations.
#[error(transparent)]
SecretKeyFsPathError(#[from] FsPathError),
/// Represents an error when failed to access the key file.
#[error("failed to access key file {secret_file:?}: {error}")]
FailedToAccessKeyFile {
/// The encountered IO error.
error: io::Error,
/// Path to the secret key file.
secret_file: PathBuf,
},
}
/// Attempts to load a [`SecretKey`] from a specified path. If no file exists there, then it
/// generates a secret key and stores it in the provided path. I/O errors might occur during write
/// operations in the form of a [`SecretKeyError`]
pub fn get_secret_key(secret_key_path: &Path) -> Result<SecretKey, SecretKeyError> {
let exists = secret_key_path.try_exists();
match exists {
Ok(true) => {
let contents = fs::read_to_string(secret_key_path)?;
Ok(contents
.as_str()
.parse::<SecretKey>()
.map_err(SecretKeyError::SecretKeyDecodeError)?)
}
Ok(false) => {
if let Some(dir) = secret_key_path.parent() {
// Create parent directory
fs::create_dir_all(dir)?;
}
let secret = rng_secret_key();
let hex = hex_encode(secret.as_ref());
fs::write(secret_key_path, hex)?;
Ok(secret)
}
Err(error) => Err(SecretKeyError::FailedToAccessKeyFile {
error,
secret_file: secret_key_path.to_path_buf(),
}),
}
}

View File

@ -3,14 +3,7 @@
use alloy_genesis::Genesis;
use reth_chainspec::ChainSpec;
use reth_fs_util as fs;
use reth_primitives::{BlockHashOrNumber, B256};
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs},
path::PathBuf,
str::FromStr,
sync::Arc,
time::Duration,
};
use std::{path::PathBuf, sync::Arc};
use reth_chainspec::DEV;
@ -27,12 +20,6 @@ pub const SUPPORTED_CHAINS: &[&str] = &["optimism", "optimism-sepolia", "base",
/// Chains supported by reth. First value should be used as the default.
pub const SUPPORTED_CHAINS: &[&str] = &["mainnet", "sepolia", "goerli", "holesky", "dev"];
/// Helper to parse a [Duration] from seconds
pub fn parse_duration_from_secs(arg: &str) -> eyre::Result<Duration, std::num::ParseIntError> {
let seconds = arg.parse()?;
Ok(Duration::from_secs(seconds))
}
/// The help info for the --chain flag
pub fn chain_help() -> String {
format!("The chain this node is running.\nPossible values are either a built-in chain or the path to a chain specification file.\n\nBuilt-in chains:\n {}", SUPPORTED_CHAINS.join(", "))
@ -83,64 +70,9 @@ pub fn chain_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error>
})
}
/// Parse [`BlockHashOrNumber`]
pub fn hash_or_num_value_parser(value: &str) -> eyre::Result<BlockHashOrNumber, eyre::Error> {
match B256::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("could not 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 `<host>:<port>`
/// - 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<SocketAddr, SocketAddressParsingError> {
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::<u16>() {
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_known_chain_spec() {
@ -148,28 +80,4 @@ mod tests {
chain_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);
}
}
}