reth_cli_commands/
import_era.rs1use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
3use alloy_chains::{ChainKind, NamedChain};
4use clap::{Args, Parser};
5use eyre::{eyre, OptionExt};
6use reqwest::{Client, Url};
7use reth_chainspec::{EthChainSpec, EthereumHardforks};
8use reth_cli::chainspec::ChainSpecParser;
9use reth_era_downloader::{read_dir, EraClient, EraStream, EraStreamConfig};
10use reth_era_utils as era;
11use reth_etl::Collector;
12use reth_node_core::{dirs::data_dir, version::SHORT_VERSION};
13use std::{path::PathBuf, sync::Arc};
14use tracing::info;
15
16#[derive(Debug, Parser)]
18pub struct ImportEraCommand<C: ChainSpecParser> {
19 #[command(flatten)]
20 env: EnvironmentArgs<C>,
21
22 #[clap(flatten)]
23 import: ImportArgs,
24}
25
26#[derive(Debug, Args)]
27#[group(required = false, multiple = false)]
28pub struct ImportArgs {
29 #[arg(long, value_name = "IMPORT_ERA_PATH", verbatim_doc_comment)]
33 path: Option<PathBuf>,
34
35 #[arg(long, value_name = "IMPORT_ERA_URL", verbatim_doc_comment)]
40 url: Option<Url>,
41}
42
43trait TryFromChain {
44 fn try_to_url(&self) -> eyre::Result<Url>;
45}
46
47impl TryFromChain for ChainKind {
48 fn try_to_url(&self) -> eyre::Result<Url> {
49 Ok(match self {
50 ChainKind::Named(NamedChain::Mainnet) => {
51 Url::parse("https://era.ithaca.xyz/era1/").expect("URL should be valid")
52 }
53 ChainKind::Named(NamedChain::Sepolia) => {
54 Url::parse("https://era.ithaca.xyz/sepolia-era1/").expect("URL should be valid")
55 }
56 chain => return Err(eyre!("No known host for ERA files on chain {chain:?}")),
57 })
58 }
59}
60
61impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> ImportEraCommand<C> {
62 pub async fn execute<N>(self) -> eyre::Result<()>
64 where
65 N: CliNodeTypes<ChainSpec = C::ChainSpec>,
66 {
67 info!(target: "reth::cli", "reth {} starting", SHORT_VERSION);
68
69 let Environment { provider_factory, config, .. } = self.env.init::<N>(AccessRights::RW)?;
70
71 let hash_collector = Collector::new(config.stages.etl.file_size, config.stages.etl.dir);
72 let provider_factory = &provider_factory.provider_rw()?.0;
73
74 if let Some(path) = self.import.path {
75 let stream = read_dir(path)?;
76
77 era::import(stream, provider_factory, hash_collector)?;
78 } else {
79 let url = match self.import.url {
80 Some(url) => url,
81 None => self.env.chain.chain().kind().try_to_url()?,
82 };
83 let folder = data_dir().ok_or_eyre("Missing data directory")?.join("era");
84 let folder = folder.into_boxed_path();
85 let client = EraClient::new(Client::new(), url, folder);
86 let stream = EraStream::new(client, EraStreamConfig::default());
87
88 era::import(stream, provider_factory, hash_collector)?;
89 }
90
91 Ok(())
92 }
93}
94
95impl<C: ChainSpecParser> ImportEraCommand<C> {
96 pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
98 Some(&self.env.chain)
99 }
100}