From 41d72ae949e677e4e22638f37899a59bb4a78476 Mon Sep 17 00:00:00 2001 From: sprites0 <199826320+sprites0@users.noreply.github.com> Date: Tue, 29 Apr 2025 03:57:09 +0000 Subject: [PATCH] feat: Enable testnet block recovery --- crates/cli/commands/src/init_state/mod.rs | 72 +++++++++++-------- .../commands/src/init_state/without_evm.rs | 8 ++- .../optimism/cli/src/commands/init_state.rs | 1 + 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/crates/cli/commands/src/init_state/mod.rs b/crates/cli/commands/src/init_state/mod.rs index 07e5a7c86..9ccfd13ab 100644 --- a/crates/cli/commands/src/init_state/mod.rs +++ b/crates/cli/commands/src/init_state/mod.rs @@ -12,7 +12,7 @@ use reth_provider::{ BlockNumReader, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter, }; use std::{io::BufReader, path::PathBuf, str::FromStr}; -use tracing::info; +use tracing::{info, warn}; pub mod without_evm; @@ -40,7 +40,7 @@ pub struct InitStateCommand { /// Allows init at a non-genesis block. Caution! Blocks must be manually imported up until /// and including the non-genesis block to init chain at. See 'import' command. #[arg(value_name = "STATE_DUMP_FILE", verbatim_doc_comment)] - pub state: PathBuf, + pub state: Option, /// Specifies whether to initialize the state without relying on EVM historical data. /// @@ -63,6 +63,10 @@ pub struct InitStateCommand { /// Hash of the header. #[arg(long, value_name = "HEADER_HASH", verbatim_doc_comment)] pub header_hash: Option, + + /// Force the initialization of the state even if the data directory is not empty. + #[arg(long)] + pub force: bool, } impl> InitStateCommand { @@ -98,36 +102,48 @@ impl> InitStateC let last_block_number = provider_rw.last_block_number()?; if last_block_number == 0 { - without_evm::setup_without_evm( - &provider_rw, - // &header, - // header_hash, - SealedHeader::new(header, header_hash), - total_difficulty, - )?; - - // SAFETY: it's safe to commit static files, since in the event of a crash, they - // will be unwound according to database checkpoints. - // - // Necessary to commit, so the header is accessible to provider_rw and - // init_state_dump - static_file_provider.commit()?; + info!(target: "reth::cli", "Data directory is empty, setting up dummy chain"); } else if last_block_number > 0 && last_block_number < header.number { - return Err(eyre::eyre!( - "Data directory should be empty when calling init-state with --without-evm-history." - )); + if !self.force { + return Err(eyre::eyre!( + "Data directory is not empty, use --force to override" + )); + } else { + warn!(target: "reth::cli", "Data directory is not empty, setting up dummy chain"); + } } + + info!(target: "reth::cli", "Setting up dummy chain from block {} to {}", + last_block_number + 1, + header.number); + without_evm::setup_without_evm( + &provider_rw, + // &header, + // header_hash, + last_block_number + 1, + SealedHeader::new(header, header_hash), + total_difficulty, + )?; + + // SAFETY: it's safe to commit static files, since in the event of a crash, they + // will be unwound according to database checkpoints. + // + // Necessary to commit, so the header is accessible to provider_rw and + // init_state_dump + static_file_provider.commit()?; } - info!(target: "reth::cli", "Initiating state dump"); + if let Some(state) = self.state { + info!(target: "reth::cli", "Initiating state dump"); + let reader = BufReader::new(reth_fs_util::open(state)?); + let hash = init_from_state_dump(reader, &provider_rw, config.stages.etl)?; + provider_rw.commit()?; - let reader = BufReader::new(reth_fs_util::open(self.state)?); - - let hash = init_from_state_dump(reader, &provider_rw, config.stages.etl)?; - - provider_rw.commit()?; - - info!(target: "reth::cli", hash = ?hash, "Genesis block written"); - Ok(()) + info!(target: "reth::cli", hash = ?hash, "Genesis block written"); + Ok(()) + } else { + provider_rw.commit()?; + Ok(()) + } } } diff --git a/crates/cli/commands/src/init_state/without_evm.rs b/crates/cli/commands/src/init_state/without_evm.rs index 56182dd14..db009d5b9 100644 --- a/crates/cli/commands/src/init_state/without_evm.rs +++ b/crates/cli/commands/src/init_state/without_evm.rs @@ -27,6 +27,7 @@ pub(crate) fn read_header_from_file(path: PathBuf) -> Result( provider_rw: &Provider, + start_height: BlockNumber, header: SealedHeader<::BlockHeader>, total_difficulty: U256, ) -> Result<(), eyre::Error> @@ -39,7 +40,7 @@ where let static_file_provider = provider_rw.static_file_provider(); // Write EVM dummy data up to `header - 1` block - append_dummy_chain(&static_file_provider, header.number() - 1)?; + append_dummy_chain(&static_file_provider, start_height, header.number() - 1)?; info!(target: "reth::cli", "Appending first valid block."); @@ -99,6 +100,7 @@ where /// * Receipts: It will not push any receipt, only increments the end block range. fn append_dummy_chain>( sf_provider: &StaticFileProvider, + start_height: BlockNumber, target_height: BlockNumber, ) -> Result<(), eyre::Error> { let (tx, rx) = std::sync::mpsc::channel(); @@ -109,7 +111,7 @@ fn append_dummy_chain>( let provider = sf_provider.clone(); std::thread::spawn(move || { let result = provider.latest_writer(segment).and_then(|mut writer| { - for block_num in 1..=target_height { + for block_num in start_height..=target_height { writer.increment_block(block_num)?; } Ok(()) @@ -124,7 +126,7 @@ fn append_dummy_chain>( std::thread::spawn(move || { let mut empty_header = Header::default(); let result = provider.latest_writer(StaticFileSegment::Headers).and_then(|mut writer| { - for block_num in 1..=target_height { + for block_num in start_height..=target_height { // TODO: should we fill with real parent_hash? empty_header.number = block_num; writer.append_header(&empty_header, U256::ZERO, &B256::ZERO)?; diff --git a/crates/optimism/cli/src/commands/init_state.rs b/crates/optimism/cli/src/commands/init_state.rs index 80e5c18cb..6d566930e 100644 --- a/crates/optimism/cli/src/commands/init_state.rs +++ b/crates/optimism/cli/src/commands/init_state.rs @@ -56,6 +56,7 @@ impl> InitStateCommandOp { if last_block_number == 0 { reth_cli_commands::init_state::without_evm::setup_without_evm( &provider_rw, + last_block_number + 1, SealedHeader::new(BEDROCK_HEADER, BEDROCK_HEADER_HASH), BEDROCK_HEADER_TTD, )?;