reth_cli_commands/init_state/
mod.rs1use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
4use alloy_consensus::BlockHeader as AlloyBlockHeader;
5use alloy_primitives::{Sealable, B256};
6use clap::Parser;
7use reth_chainspec::{EthChainSpec, EthereumHardforks};
8use reth_cli::chainspec::ChainSpecParser;
9use reth_db_common::init::init_from_state_dump;
10use reth_node_api::NodePrimitives;
11use reth_primitives_traits::{header::HeaderMut, SealedHeader};
12use reth_provider::{
13 BlockNumReader, DBProvider, DatabaseProviderFactory, StaticFileProviderFactory,
14 StaticFileWriter,
15};
16use std::{io::BufReader, path::PathBuf, sync::Arc};
17use tracing::info;
18
19pub mod without_evm;
20
21#[derive(Debug, Parser)]
23pub struct InitStateCommand<C: ChainSpecParser> {
24 #[command(flatten)]
25 pub env: EnvironmentArgs<C>,
26
27 #[arg(value_name = "STATE_DUMP_FILE", verbatim_doc_comment)]
45 pub state: PathBuf,
46
47 #[arg(long, default_value = "false")]
55 pub without_evm: bool,
56
57 #[arg(long, value_name = "HEADER_FILE", verbatim_doc_comment)]
59 pub header: Option<PathBuf>,
60
61 #[arg(long, value_name = "HEADER_HASH", verbatim_doc_comment)]
63 pub header_hash: Option<B256>,
64}
65
66impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateCommand<C> {
67 pub async fn execute<N>(self, runtime: reth_tasks::Runtime) -> eyre::Result<()>
69 where
70 N: CliNodeTypes<
71 ChainSpec = C::ChainSpec,
72 Primitives: NodePrimitives<BlockHeader: HeaderMut>,
73 >,
74 {
75 info!(target: "reth::cli", "Reth init-state starting");
76
77 let Environment { config, provider_factory, .. } =
78 self.env.init::<N>(AccessRights::RW, runtime)?;
79
80 let static_file_provider = provider_factory.static_file_provider();
81
82 if self.without_evm {
83 let provider_rw = provider_factory.database_provider_rw()?;
84
85 let header = self.header.ok_or_else(|| eyre::eyre!("Header file must be provided"))?;
87 let header = without_evm::read_header_from_file::<
88 <N::Primitives as NodePrimitives>::BlockHeader,
89 >(&header)?;
90
91 let header_hash = self.header_hash.unwrap_or_else(|| header.hash_slow());
92
93 let last_block_number = provider_rw.last_block_number()?;
94
95 if last_block_number == 0 {
96 without_evm::setup_without_evm(
97 &provider_rw,
98 SealedHeader::new(header, header_hash),
99 |number| {
100 let mut header =
101 <<N::Primitives as NodePrimitives>::BlockHeader>::default();
102 header.set_number(number);
103 header
104 },
105 )?;
106
107 static_file_provider.commit()?;
112 } else if last_block_number > 0 && last_block_number < header.number() {
113 return Err(eyre::eyre!(
114 "Data directory should be empty when calling init-state with --without-evm."
115 ));
116 }
117
118 provider_rw.commit()?;
119 }
120
121 info!(target: "reth::cli", "Initiating state dump");
122
123 let reader = BufReader::new(reth_fs_util::open(self.state)?);
124
125 let hash = init_from_state_dump(reader, &provider_factory, config.stages.etl)?;
126
127 info!(target: "reth::cli", hash = ?hash, "Genesis block written");
128 Ok(())
129 }
130}
131
132impl<C: ChainSpecParser> InitStateCommand<C> {
133 pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
135 Some(&self.env.chain)
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use alloy_primitives::b256;
143 use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
144
145 #[test]
146 fn parse_init_state_command_with_without_evm() {
147 let cmd: InitStateCommand<EthereumChainSpecParser> = InitStateCommand::parse_from([
148 "reth",
149 "--chain",
150 "sepolia",
151 "--without-evm",
152 "--header",
153 "header.rlp",
154 "--header-hash",
155 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
156 "state.jsonl",
157 ]);
158 assert_eq!(cmd.state.to_str().unwrap(), "state.jsonl");
159 assert!(cmd.without_evm);
160 assert_eq!(cmd.header.unwrap().to_str().unwrap(), "header.rlp");
161 assert_eq!(
162 cmd.header_hash.unwrap(),
163 b256!("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
164 );
165 }
166}