feat: Enable testnet block recovery

This commit is contained in:
sprites0
2025-04-29 03:57:09 +00:00
parent db419400c2
commit 41d72ae949
3 changed files with 50 additions and 31 deletions

View File

@ -12,7 +12,7 @@ use reth_provider::{
BlockNumReader, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter, BlockNumReader, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter,
}; };
use std::{io::BufReader, path::PathBuf, str::FromStr}; use std::{io::BufReader, path::PathBuf, str::FromStr};
use tracing::info; use tracing::{info, warn};
pub mod without_evm; pub mod without_evm;
@ -40,7 +40,7 @@ pub struct InitStateCommand<C: ChainSpecParser> {
/// Allows init at a non-genesis block. Caution! Blocks must be manually imported up until /// 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. /// and including the non-genesis block to init chain at. See 'import' command.
#[arg(value_name = "STATE_DUMP_FILE", verbatim_doc_comment)] #[arg(value_name = "STATE_DUMP_FILE", verbatim_doc_comment)]
pub state: PathBuf, pub state: Option<PathBuf>,
/// Specifies whether to initialize the state without relying on EVM historical data. /// Specifies whether to initialize the state without relying on EVM historical data.
/// ///
@ -63,6 +63,10 @@ pub struct InitStateCommand<C: ChainSpecParser> {
/// Hash of the header. /// Hash of the header.
#[arg(long, value_name = "HEADER_HASH", verbatim_doc_comment)] #[arg(long, value_name = "HEADER_HASH", verbatim_doc_comment)]
pub header_hash: Option<String>, pub header_hash: Option<String>,
/// Force the initialization of the state even if the data directory is not empty.
#[arg(long)]
pub force: bool,
} }
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateCommand<C> { impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateCommand<C> {
@ -98,36 +102,48 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
let last_block_number = provider_rw.last_block_number()?; let last_block_number = provider_rw.last_block_number()?;
if last_block_number == 0 { if last_block_number == 0 {
without_evm::setup_without_evm( info!(target: "reth::cli", "Data directory is empty, setting up dummy chain");
&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()?;
} else if last_block_number > 0 && last_block_number < header.number { } else if last_block_number > 0 && last_block_number < header.number {
return Err(eyre::eyre!( if !self.force {
"Data directory should be empty when calling init-state with --without-evm-history." 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)?); info!(target: "reth::cli", hash = ?hash, "Genesis block written");
Ok(())
let hash = init_from_state_dump(reader, &provider_rw, config.stages.etl)?; } else {
provider_rw.commit()?;
provider_rw.commit()?; Ok(())
}
info!(target: "reth::cli", hash = ?hash, "Genesis block written");
Ok(())
} }
} }

View File

@ -27,6 +27,7 @@ pub(crate) fn read_header_from_file(path: PathBuf) -> Result<Header, eyre::Error
/// first valid block. /// first valid block.
pub fn setup_without_evm<Provider>( pub fn setup_without_evm<Provider>(
provider_rw: &Provider, provider_rw: &Provider,
start_height: BlockNumber,
header: SealedHeader<<Provider::Primitives as NodePrimitives>::BlockHeader>, header: SealedHeader<<Provider::Primitives as NodePrimitives>::BlockHeader>,
total_difficulty: U256, total_difficulty: U256,
) -> Result<(), eyre::Error> ) -> Result<(), eyre::Error>
@ -39,7 +40,7 @@ where
let static_file_provider = provider_rw.static_file_provider(); let static_file_provider = provider_rw.static_file_provider();
// Write EVM dummy data up to `header - 1` block // 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."); 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. /// * Receipts: It will not push any receipt, only increments the end block range.
fn append_dummy_chain<N: NodePrimitives<BlockHeader = Header>>( fn append_dummy_chain<N: NodePrimitives<BlockHeader = Header>>(
sf_provider: &StaticFileProvider<N>, sf_provider: &StaticFileProvider<N>,
start_height: BlockNumber,
target_height: BlockNumber, target_height: BlockNumber,
) -> Result<(), eyre::Error> { ) -> Result<(), eyre::Error> {
let (tx, rx) = std::sync::mpsc::channel(); let (tx, rx) = std::sync::mpsc::channel();
@ -109,7 +111,7 @@ fn append_dummy_chain<N: NodePrimitives<BlockHeader = Header>>(
let provider = sf_provider.clone(); let provider = sf_provider.clone();
std::thread::spawn(move || { std::thread::spawn(move || {
let result = provider.latest_writer(segment).and_then(|mut writer| { 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)?; writer.increment_block(block_num)?;
} }
Ok(()) Ok(())
@ -124,7 +126,7 @@ fn append_dummy_chain<N: NodePrimitives<BlockHeader = Header>>(
std::thread::spawn(move || { std::thread::spawn(move || {
let mut empty_header = Header::default(); let mut empty_header = Header::default();
let result = provider.latest_writer(StaticFileSegment::Headers).and_then(|mut writer| { 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? // TODO: should we fill with real parent_hash?
empty_header.number = block_num; empty_header.number = block_num;
writer.append_header(&empty_header, U256::ZERO, &B256::ZERO)?; writer.append_header(&empty_header, U256::ZERO, &B256::ZERO)?;

View File

@ -56,6 +56,7 @@ impl<C: ChainSpecParser<ChainSpec = OpChainSpec>> InitStateCommandOp<C> {
if last_block_number == 0 { if last_block_number == 0 {
reth_cli_commands::init_state::without_evm::setup_without_evm( reth_cli_commands::init_state::without_evm::setup_without_evm(
&provider_rw, &provider_rw,
last_block_number + 1,
SealedHeader::new(BEDROCK_HEADER, BEDROCK_HEADER_HASH), SealedHeader::new(BEDROCK_HEADER, BEDROCK_HEADER_HASH),
BEDROCK_HEADER_TTD, BEDROCK_HEADER_TTD,
)?; )?;