mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: parsers (#9366)
This commit is contained in:
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -7182,6 +7182,15 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "reth-ethereum-cli"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"alloy-genesis",
|
||||
"clap",
|
||||
"eyre",
|
||||
"reth-chainspec",
|
||||
"reth-cli",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-ethereum-consensus"
|
||||
@ -7910,10 +7919,13 @@ dependencies = [
|
||||
name = "reth-optimism-cli"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"alloy-genesis",
|
||||
"alloy-primitives",
|
||||
"clap",
|
||||
"eyre",
|
||||
"futures-util",
|
||||
"reth-chainspec",
|
||||
"reth-cli",
|
||||
"reth-cli-commands",
|
||||
"reth-config",
|
||||
"reth-consensus",
|
||||
@ -7934,6 +7946,8 @@ dependencies = [
|
||||
"reth-stages-types",
|
||||
"reth-static-file",
|
||||
"reth-static-file-types",
|
||||
"serde_json",
|
||||
"shellexpand",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@ -467,6 +467,7 @@ paste = "1.0"
|
||||
url = "2.3"
|
||||
backon = "0.4"
|
||||
boyer-moore-magiclen = "0.2.16"
|
||||
shellexpand = "3.0.0"
|
||||
|
||||
# metrics
|
||||
metrics = "0.23.0"
|
||||
|
||||
@ -8,13 +8,17 @@ homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-cli-runner.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
eyre.workspace = true
|
||||
|
||||
# misc
|
||||
clap.workspace = true
|
||||
eyre.workspace = true
|
||||
|
||||
|
||||
|
||||
|
||||
@ -21,5 +21,5 @@ pub trait ChainSpecParser: TypedValueParser<Value = Arc<ChainSpec>> + Default {
|
||||
///
|
||||
/// This function will return an error if the input string cannot be parsed into a valid
|
||||
/// [`ChainSpec`].
|
||||
fn parse(&self, s: &str) -> eyre::Result<Arc<ChainSpec>>;
|
||||
fn parse(s: &str) -> eyre::Result<Arc<ChainSpec>>;
|
||||
}
|
||||
|
||||
@ -8,12 +8,11 @@
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
use clap::{Error, Parser};
|
||||
use reth_cli_runner::CliRunner;
|
||||
use std::{borrow::Cow, ffi::OsString};
|
||||
|
||||
use reth_cli_runner::CliRunner;
|
||||
|
||||
use clap::{Error, Parser};
|
||||
|
||||
/// The chainspec module defines the different chainspecs that can be used by the node.
|
||||
pub mod chainspec;
|
||||
|
||||
/// Reth based node cli.
|
||||
@ -32,7 +31,7 @@ pub trait RethCli: Sized {
|
||||
/// Parse args from iterator from [`std::env::args_os()`].
|
||||
fn parse_args() -> Result<Self, Error>
|
||||
where
|
||||
Self: Parser + Sized,
|
||||
Self: Parser,
|
||||
{
|
||||
<Self as RethCli>::try_parse_from(std::env::args_os())
|
||||
}
|
||||
@ -40,7 +39,7 @@ pub trait RethCli: Sized {
|
||||
/// Parse args from the given iterator.
|
||||
fn try_parse_from<I, T>(itr: I) -> Result<Self, Error>
|
||||
where
|
||||
Self: Parser + Sized,
|
||||
Self: Parser,
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<OsString> + Clone,
|
||||
{
|
||||
@ -60,7 +59,7 @@ pub trait RethCli: Sized {
|
||||
/// Parses and executes a command.
|
||||
fn execute<F, R>(f: F) -> Result<R, Error>
|
||||
where
|
||||
Self: Parser + Sized,
|
||||
Self: Parser,
|
||||
F: FnOnce(Self, CliRunner) -> R,
|
||||
{
|
||||
let cli = Self::parse_args()?;
|
||||
|
||||
@ -8,3 +8,16 @@ homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
reth-cli.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
|
||||
alloy-genesis.workspace = true
|
||||
|
||||
eyre.workspace = true
|
||||
|
||||
shellexpand.workspace = true
|
||||
serde_json.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
90
crates/ethereum/cli/src/chainspec.rs
Normal file
90
crates/ethereum/cli/src/chainspec.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use alloy_genesis::Genesis;
|
||||
use clap::{builder::TypedValueParser, error::Result, Arg, Command};
|
||||
use reth_chainspec::{ChainSpec, DEV, HOLESKY, MAINNET, SEPOLIA};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use std::{ffi::OsStr, fs, path::PathBuf, sync::Arc};
|
||||
|
||||
/// 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 needs to be a Genesis struct.
|
||||
fn chain_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error> {
|
||||
Ok(match s {
|
||||
"mainnet" => MAINNET.clone(),
|
||||
"sepolia" => SEPOLIA.clone(),
|
||||
"holesky" => HOLESKY.clone(),
|
||||
"dev" => DEV.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())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Ethereum chain specification parser.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EthChainSpecParser;
|
||||
|
||||
impl ChainSpecParser for EthChainSpecParser {
|
||||
const SUPPORTED_CHAINS: &'static [&'static str] = &["mainnet", "sepolia", "holesky", "dev"];
|
||||
|
||||
fn parse(s: &str) -> eyre::Result<Arc<ChainSpec>> {
|
||||
chain_value_parser(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedValueParser for EthChainSpecParser {
|
||||
type Value = Arc<ChainSpec>;
|
||||
|
||||
fn parse_ref(
|
||||
&self,
|
||||
_cmd: &Command,
|
||||
arg: Option<&Arg>,
|
||||
value: &OsStr,
|
||||
) -> Result<Self::Value, clap::Error> {
|
||||
let val =
|
||||
value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
|
||||
<Self as ChainSpecParser>::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!(
|
||||
"Invalid value '{val}' for {arg}: {err}.\n [possible values: {possible_values}]"
|
||||
);
|
||||
clap::Error::raw(clap::error::ErrorKind::InvalidValue, msg)
|
||||
})
|
||||
}
|
||||
|
||||
fn possible_values(
|
||||
&self,
|
||||
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
|
||||
let values = Self::SUPPORTED_CHAINS.iter().map(clap::builder::PossibleValue::new);
|
||||
Some(Box::new(values))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_known_chain_spec() {
|
||||
for &chain in EthChainSpecParser::SUPPORTED_CHAINS {
|
||||
assert!(<EthChainSpecParser as ChainSpecParser>::parse(chain).is_ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,3 +7,6 @@
|
||||
)]
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
/// Chain specification parser.
|
||||
pub mod chainspec;
|
||||
|
||||
@ -65,7 +65,7 @@ once_cell.workspace = true
|
||||
|
||||
# io
|
||||
dirs-next = "2.0.0"
|
||||
shellexpand = "3.0.0"
|
||||
shellexpand.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
# http/rpc
|
||||
|
||||
@ -12,7 +12,6 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
reth-static-file-types = { workspace = true, features = ["clap"] }
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
reth-cli-commands.workspace = true
|
||||
reth-consensus.workspace = true
|
||||
reth-db = { workspace = true, features = ["mdbx"] }
|
||||
@ -26,18 +25,25 @@ reth-static-file.workspace = true
|
||||
reth-execution-types.workspace = true
|
||||
reth-node-core.workspace = true
|
||||
reth-primitives.workspace = true
|
||||
|
||||
|
||||
reth-stages-types.workspace = true
|
||||
reth-node-events.workspace = true
|
||||
reth-network-p2p.workspace = true
|
||||
reth-errors.workspace = true
|
||||
|
||||
reth-config.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
futures-util.workspace = true
|
||||
reth-evm-optimism.workspace = true
|
||||
reth-cli.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
|
||||
# eth
|
||||
alloy-genesis.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
|
||||
|
||||
# misc
|
||||
shellexpand.workspace = true
|
||||
serde_json.workspace = true
|
||||
futures-util.workspace = true
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
|
||||
|
||||
tokio = { workspace = true, features = [
|
||||
|
||||
100
crates/optimism/cli/src/chainspec.rs
Normal file
100
crates/optimism/cli/src/chainspec.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use alloy_genesis::Genesis;
|
||||
use clap::{builder::TypedValueParser, error::Result, Arg, Command};
|
||||
use reth_chainspec::{ChainSpec, BASE_MAINNET, BASE_SEPOLIA, DEV, OP_MAINNET, OP_SEPOLIA};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use std::{ffi::OsStr, fs, path::PathBuf, sync::Arc};
|
||||
|
||||
/// 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 needs to be a Genesis struct.
|
||||
fn chain_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error> {
|
||||
Ok(match s {
|
||||
"dev" => 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())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Optimism chain specification parser.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OpChainSpecParser;
|
||||
|
||||
impl ChainSpecParser for OpChainSpecParser {
|
||||
const SUPPORTED_CHAINS: &'static [&'static str] = &[
|
||||
"optimism",
|
||||
"optimism_sepolia",
|
||||
"optimism-sepolia",
|
||||
"base",
|
||||
"base_sepolia",
|
||||
"base-sepolia",
|
||||
];
|
||||
|
||||
fn parse(s: &str) -> eyre::Result<Arc<ChainSpec>> {
|
||||
chain_value_parser(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedValueParser for OpChainSpecParser {
|
||||
type Value = Arc<ChainSpec>;
|
||||
|
||||
fn parse_ref(
|
||||
&self,
|
||||
_cmd: &Command,
|
||||
arg: Option<&Arg>,
|
||||
value: &OsStr,
|
||||
) -> Result<Self::Value, clap::Error> {
|
||||
let val =
|
||||
value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
|
||||
<Self as ChainSpecParser>::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(
|
||||
clap::error::ErrorKind::InvalidValue,
|
||||
format!(
|
||||
"Invalid value '{val}' for {arg}: {err}. [possible values: {possible_values}]"
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn possible_values(
|
||||
&self,
|
||||
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
|
||||
let values = Self::SUPPORTED_CHAINS.iter().map(clap::builder::PossibleValue::new);
|
||||
Some(Box::new(values))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_known_chain_spec() {
|
||||
for &chain in OpChainSpecParser::SUPPORTED_CHAINS {
|
||||
assert!(<OpChainSpecParser as ChainSpecParser>::parse(chain).is_ok());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,8 @@
|
||||
// The `optimism` feature must be enabled to use this crate.
|
||||
#![cfg(feature = "optimism")]
|
||||
|
||||
/// Optimism chain specification parser.
|
||||
pub mod chainspec;
|
||||
/// Optimism CLI commands.
|
||||
pub mod commands;
|
||||
pub use commands::{import::ImportOpCommand, import_receipts::ImportReceiptsOpCommand};
|
||||
|
||||
Reference in New Issue
Block a user