reth_cli_commands/
export_era.rs1use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
4use clap::{Args, Parser};
5use reth_chainspec::{EthChainSpec, EthereumHardforks};
6use reth_cli::chainspec::ChainSpecParser;
7use reth_era::era1::types::execution::MAX_BLOCKS_PER_ERA1;
8use reth_era_utils as era;
9use reth_provider::DatabaseProviderFactory;
10use std::{path::PathBuf, sync::Arc};
11use tracing::info;
12
13#[derive(Debug, Parser)]
14pub struct ExportEraCommand<C: ChainSpecParser> {
15 #[command(flatten)]
16 env: EnvironmentArgs<C>,
17
18 #[clap(flatten)]
19 export: ExportArgs,
20}
21
22#[derive(Debug, Args)]
23pub struct ExportArgs {
24 #[arg(long, value_enum, default_value_t = ExportFileType::Era1, verbatim_doc_comment)]
26 file_type: ExportFileType,
27 #[arg(long, value_name = "first-block-number", verbatim_doc_comment)]
30 first_block_number: Option<u64>,
31 #[arg(long, value_name = "last-block-number", verbatim_doc_comment)]
34 last_block_number: Option<u64>,
35 #[arg(long, value_name = "max-blocks-per-file", verbatim_doc_comment)]
38 max_blocks_per_file: Option<u64>,
39 #[arg(long, value_name = "EXPORT_PATH", verbatim_doc_comment)]
42 path: Option<PathBuf>,
43}
44
45#[derive(Debug, Clone, Copy, clap::ValueEnum)]
49enum ExportFileType {
50 Era1,
52 Ere,
54 Era,
57}
58
59impl ExportFileType {
60 const fn format(&self) -> &'static str {
62 match self {
63 Self::Era1 => "era1",
64 Self::Ere => "ere",
65 Self::Era => "era",
66 }
67 }
68
69 fn ensure_exportable(self) -> eyre::Result<()> {
71 if matches!(self, Self::Era) {
72 return Err(era_not_exportable());
73 }
74 Ok(())
75 }
76}
77
78fn era_not_exportable() -> eyre::Report {
81 eyre::eyre!(
82 "Consensus-layer ERA (.era) files cannot be exported: they require beacon blocks and \
83 state that the execution database does not store. Export `era1` or `ere` instead."
84 )
85}
86
87impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> ExportEraCommand<C> {
88 pub async fn execute<N>(self, runtime: reth_tasks::Runtime) -> eyre::Result<()>
90 where
91 N: CliNodeTypes<ChainSpec = C::ChainSpec>,
92 {
93 let file_type = self.export.file_type;
94 file_type.ensure_exportable()?;
95 let format = file_type.format();
96
97 let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RO, runtime)?;
98
99 let data_dir = match &self.export.path {
101 Some(path) => path.clone(),
102 None => self
103 .env
104 .datadir
105 .resolve_datadir(self.env.chain.chain())
106 .data_dir()
107 .join(format!("{format}-export")),
108 };
109
110 let export_config = era::ExportConfig {
111 network: self.env.chain.chain().to_string(),
112 first_block_number: self.export.first_block_number.unwrap_or(0),
113 last_block_number: self
114 .export
115 .last_block_number
116 .unwrap_or(MAX_BLOCKS_PER_ERA1 as u64 - 1),
117 max_blocks_per_file: self
118 .export
119 .max_blocks_per_file
120 .unwrap_or(MAX_BLOCKS_PER_ERA1 as u64),
121 dir: data_dir,
122 };
123
124 export_config.validate()?;
125
126 info!(
127 target: "reth::cli",
128 "Starting {format} block export: blocks {}-{} to {}",
129 export_config.first_block_number,
130 export_config.last_block_number,
131 export_config.dir.display()
132 );
133
134 let provider = provider_factory.database_provider_ro()?;
136
137 let exported_files = match file_type {
138 ExportFileType::Era1 => era::export::<era::Era1, _>(&provider, &export_config)?,
139 ExportFileType::Ere => era::export::<era::Ere, _>(&provider, &export_config)?,
140 ExportFileType::Era => return Err(era_not_exportable()),
142 };
143
144 info!(
145 target: "reth::cli",
146 "Successfully exported {} {format} files to {}",
147 exported_files.len(),
148 export_config.dir.display()
149 );
150
151 Ok(())
152 }
153}
154
155impl<C: ChainSpecParser> ExportEraCommand<C> {
156 pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
158 Some(&self.env.chain)
159 }
160}