reth_cli_commands/init_state/
mod.rs

1//! Command that initializes the node from a genesis file.
2
3use crate::common::{AccessRights, CliHeader, 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::{BlockHeader, 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/// Initializes the database with the genesis block.
22#[derive(Debug, Parser)]
23pub struct InitStateCommand<C: ChainSpecParser> {
24    #[command(flatten)]
25    pub env: EnvironmentArgs<C>,
26
27    /// JSONL file with state dump.
28    ///
29    /// Must contain accounts in following format, additional account fields are ignored. Must
30    /// also contain { "root": \<state-root\> } as first line.
31    /// {
32    ///     "balance": "\<balance\>",
33    ///     "nonce": \<nonce\>,
34    ///     "code": "\<bytecode\>",
35    ///     "storage": {
36    ///         "\<key\>": "\<value\>",
37    ///         ..
38    ///     },
39    ///     "address": "\<address\>",
40    /// }
41    ///
42    /// Allows init at a non-genesis block. Caution! Blocks must be manually imported up until
43    /// and including the non-genesis block to init chain at. See 'import' command.
44    #[arg(value_name = "STATE_DUMP_FILE", verbatim_doc_comment)]
45    pub state: PathBuf,
46
47    /// Specifies whether to initialize the state without relying on EVM historical data.
48    ///
49    /// When enabled, and before inserting the state, it creates a dummy chain up to the last EVM
50    /// block specified. It then, appends the first block provided block.
51    ///
52    /// - **Note**: **Do not** import receipts and blocks beforehand, or this will fail or be
53    ///   ignored.
54    #[arg(long, default_value = "false")]
55    pub without_evm: bool,
56
57    /// Header file containing the header in an RLP encoded format.
58    #[arg(long, value_name = "HEADER_FILE", verbatim_doc_comment)]
59    pub header: Option<PathBuf>,
60
61    /// Hash of the header.
62    #[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    /// Execute the `init` command
68    pub async fn execute<N>(self) -> eyre::Result<()>
69    where
70        N: CliNodeTypes<
71            ChainSpec = C::ChainSpec,
72            Primitives: NodePrimitives<BlockHeader: BlockHeader + CliHeader>,
73        >,
74    {
75        info!(target: "reth::cli", "Reth init-state starting");
76
77        let Environment { config, provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
78
79        let static_file_provider = provider_factory.static_file_provider();
80        let provider_rw = provider_factory.database_provider_rw()?;
81
82        if self.without_evm {
83            // ensure header, total difficulty and header hash are provided
84            let header = self.header.ok_or_else(|| eyre::eyre!("Header file must be provided"))?;
85            let header = without_evm::read_header_from_file::<
86                <N::Primitives as NodePrimitives>::BlockHeader,
87            >(&header)?;
88
89            let header_hash = self.header_hash.unwrap_or_else(|| header.hash_slow());
90
91            let last_block_number = provider_rw.last_block_number()?;
92
93            if last_block_number == 0 {
94                without_evm::setup_without_evm(
95                    &provider_rw,
96                    SealedHeader::new(header, header_hash),
97                    |number| {
98                        let mut header =
99                            <<N::Primitives as NodePrimitives>::BlockHeader>::default();
100                        header.set_number(number);
101                        header
102                    },
103                )?;
104
105                // SAFETY: it's safe to commit static files, since in the event of a crash, they
106                // will be unwound according to database checkpoints.
107                //
108                // Necessary to commit, so the header is accessible to provider_rw and
109                // init_state_dump
110                static_file_provider.commit()?;
111            } else if last_block_number > 0 && last_block_number < header.number() {
112                return Err(eyre::eyre!(
113                    "Data directory should be empty when calling init-state with --without-evm-history."
114                ));
115            }
116        }
117
118        info!(target: "reth::cli", "Initiating state dump");
119
120        let reader = BufReader::new(reth_fs_util::open(self.state)?);
121
122        let hash = init_from_state_dump(reader, &provider_rw, config.stages.etl)?;
123
124        provider_rw.commit()?;
125
126        info!(target: "reth::cli", hash = ?hash, "Genesis block written");
127        Ok(())
128    }
129}
130
131impl<C: ChainSpecParser> InitStateCommand<C> {
132    /// Returns the underlying chain being used to run this command
133    pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
134        Some(&self.env.chain)
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use alloy_primitives::b256;
142    use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
143
144    #[test]
145    fn parse_init_state_command_with_without_evm() {
146        let cmd: InitStateCommand<EthereumChainSpecParser> = InitStateCommand::parse_from([
147            "reth",
148            "--chain",
149            "sepolia",
150            "--without-evm",
151            "--header",
152            "header.rlp",
153            "--header-hash",
154            "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
155            "state.jsonl",
156        ]);
157        assert_eq!(cmd.state.to_str().unwrap(), "state.jsonl");
158        assert!(cmd.without_evm);
159        assert_eq!(cmd.header.unwrap().to_str().unwrap(), "header.rlp");
160        assert_eq!(
161            cmd.header_hash.unwrap(),
162            b256!("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
163        );
164    }
165}