reth_optimism_cli/commands/
init_state.rs

1//! Command that initializes the node from a genesis file.
2
3use alloy_consensus::Header;
4use clap::Parser;
5use reth_cli::chainspec::ChainSpecParser;
6use reth_cli_commands::common::{AccessRights, CliHeader, CliNodeTypes, Environment};
7use reth_db_common::init::init_from_state_dump;
8use reth_optimism_chainspec::OpChainSpec;
9use reth_optimism_primitives::{
10    bedrock::{BEDROCK_HEADER, BEDROCK_HEADER_HASH},
11    OpPrimitives,
12};
13use reth_primitives_traits::SealedHeader;
14use reth_provider::{
15    BlockNumReader, DBProvider, DatabaseProviderFactory, StaticFileProviderFactory,
16    StaticFileWriter,
17};
18use std::{io::BufReader, sync::Arc};
19use tracing::info;
20
21/// Initializes the database with the genesis block.
22#[derive(Debug, Parser)]
23pub struct InitStateCommandOp<C: ChainSpecParser> {
24    #[command(flatten)]
25    init_state: reth_cli_commands::init_state::InitStateCommand<C>,
26
27    /// Specifies whether to initialize the state without relying on OVM or EVM historical data.
28    ///
29    /// When enabled, and before inserting the state, it creates a dummy chain up to the last OVM
30    /// block (#105235062) (14GB / 90 seconds). It then, appends the Bedrock block. This is
31    /// hardcoded for OP mainnet, for other OP chains you will need to pass in a header.
32    ///
33    /// - **Note**: **Do not** import receipts and blocks beforehand, or this will fail or be
34    ///   ignored.
35    #[arg(long, default_value = "false")]
36    without_ovm: bool,
37}
38
39impl<C: ChainSpecParser<ChainSpec = OpChainSpec>> InitStateCommandOp<C> {
40    /// Execute the `init` command
41    pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = OpPrimitives>>(
42        mut self,
43    ) -> eyre::Result<()> {
44        // If using --without-ovm for OP mainnet, handle the special case with hardcoded Bedrock
45        // header. Otherwise delegate to the base InitStateCommand implementation.
46        if self.without_ovm {
47            if self.init_state.env.chain.is_optimism_mainnet() {
48                return self.execute_with_bedrock_header::<N>();
49            }
50
51            // For non-mainnet OP chains with --without-ovm, use the base implementation
52            // by setting the without_evm flag
53            self.init_state.without_evm = true;
54        }
55
56        self.init_state.execute::<N>().await
57    }
58
59    /// Execute init-state with hardcoded Bedrock header for OP mainnet.
60    fn execute_with_bedrock_header<
61        N: CliNodeTypes<ChainSpec = C::ChainSpec, Primitives = OpPrimitives>,
62    >(
63        self,
64    ) -> eyre::Result<()> {
65        info!(target: "reth::cli", "Reth init-state starting for OP mainnet");
66        let env = self.init_state.env.init::<N>(AccessRights::RW)?;
67
68        let Environment { config, provider_factory, .. } = env;
69        let static_file_provider = provider_factory.static_file_provider();
70        let provider_rw = provider_factory.database_provider_rw()?;
71
72        let last_block_number = provider_rw.last_block_number()?;
73
74        if last_block_number == 0 {
75            reth_cli_commands::init_state::without_evm::setup_without_evm(
76                &provider_rw,
77                SealedHeader::new(BEDROCK_HEADER, BEDROCK_HEADER_HASH),
78                |number| {
79                    let mut header = Header::default();
80                    header.set_number(number);
81                    header
82                },
83            )?;
84
85            // SAFETY: it's safe to commit static files, since in the event of a crash, they
86            // will be unwound according to database checkpoints.
87            //
88            // Necessary to commit, so the BEDROCK_HEADER is accessible to provider_rw and
89            // init_state_dump
90            static_file_provider.commit()?;
91        } else if last_block_number > 0 && last_block_number < BEDROCK_HEADER.number {
92            return Err(eyre::eyre!(
93                "Data directory should be empty when calling init-state with --without-ovm."
94            ))
95        }
96
97        info!(target: "reth::cli", "Initiating state dump");
98
99        let reader = BufReader::new(reth_fs_util::open(self.init_state.state)?);
100        let hash = init_from_state_dump(reader, &provider_rw, config.stages.etl)?;
101
102        provider_rw.commit()?;
103
104        info!(target: "reth::cli", hash = ?hash, "Genesis block written");
105        Ok(())
106    }
107}
108
109impl<C: ChainSpecParser> InitStateCommandOp<C> {
110    /// Returns the underlying chain being used to run this command
111    pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
112        self.init_state.chain_spec()
113    }
114}