reth_cli_commands/init_state/
without_evm.rs

1use alloy_consensus::BlockHeader;
2use alloy_primitives::{BlockNumber, B256, U256};
3use alloy_rlp::Decodable;
4use reth_codecs::Compact;
5use reth_node_builder::NodePrimitives;
6use reth_primitives_traits::{SealedBlock, SealedHeader, SealedHeaderFor};
7use reth_provider::{
8    providers::StaticFileProvider, BlockWriter, ProviderResult, StageCheckpointWriter,
9    StaticFileProviderFactory, StaticFileWriter, StorageLocation,
10};
11use reth_stages::{StageCheckpoint, StageId};
12use reth_static_file_types::StaticFileSegment;
13use std::{fs::File, io::Read, path::PathBuf};
14use tracing::info;
15/// Reads the header RLP from a file and returns the Header.
16pub(crate) fn read_header_from_file<H>(path: PathBuf) -> Result<H, eyre::Error>
17where
18    H: Decodable,
19{
20    let mut file = File::open(path)?;
21    let mut buf = Vec::new();
22    file.read_to_end(&mut buf)?;
23
24    let header = H::decode(&mut &buf[..])?;
25    Ok(header)
26}
27
28/// Creates a dummy chain (with no transactions) up to the last EVM block and appends the
29/// first valid block.
30pub fn setup_without_evm<Provider, F>(
31    provider_rw: &Provider,
32    header: SealedHeader<<Provider::Primitives as NodePrimitives>::BlockHeader>,
33    total_difficulty: U256,
34    header_factory: F,
35) -> ProviderResult<()>
36where
37    Provider: StaticFileProviderFactory
38        + StageCheckpointWriter
39        + BlockWriter<Block = <Provider::Primitives as NodePrimitives>::Block>,
40    F: Fn(BlockNumber) -> <Provider::Primitives as NodePrimitives>::BlockHeader
41        + Send
42        + Sync
43        + 'static,
44{
45    info!(target: "reth::cli", new_tip = ?header.num_hash(), "Setting up dummy EVM chain before importing state.");
46
47    let static_file_provider = provider_rw.static_file_provider();
48    // Write EVM dummy data up to `header - 1` block
49    append_dummy_chain(&static_file_provider, header.number() - 1, header_factory)?;
50
51    info!(target: "reth::cli", "Appending first valid block.");
52
53    append_first_block(provider_rw, &header, total_difficulty)?;
54
55    for stage in StageId::ALL {
56        provider_rw.save_stage_checkpoint(stage, StageCheckpoint::new(header.number()))?;
57    }
58
59    info!(target: "reth::cli", "Set up finished.");
60
61    Ok(())
62}
63
64/// Appends the first block.
65///
66/// By appending it, static file writer also verifies that all segments are at the same
67/// height.
68fn append_first_block<Provider>(
69    provider_rw: &Provider,
70    header: &SealedHeaderFor<Provider::Primitives>,
71    total_difficulty: U256,
72) -> ProviderResult<()>
73where
74    Provider: BlockWriter<Block = <Provider::Primitives as NodePrimitives>::Block>
75        + StaticFileProviderFactory<Primitives: NodePrimitives<BlockHeader: Compact>>,
76{
77    provider_rw.insert_block(
78        SealedBlock::<<Provider::Primitives as NodePrimitives>::Block>::from_sealed_parts(
79            header.clone(),
80            Default::default(),
81        )
82        .try_recover()
83        .expect("no senders or txes"),
84        StorageLocation::Database,
85    )?;
86
87    let sf_provider = provider_rw.static_file_provider();
88
89    sf_provider.latest_writer(StaticFileSegment::Headers)?.append_header(
90        header,
91        total_difficulty,
92        &header.hash(),
93    )?;
94
95    sf_provider.latest_writer(StaticFileSegment::Receipts)?.increment_block(header.number())?;
96
97    sf_provider.latest_writer(StaticFileSegment::Transactions)?.increment_block(header.number())?;
98
99    Ok(())
100}
101
102/// Creates a dummy chain with no transactions/receipts up to `target_height` block inclusive.
103///
104/// * Headers: It will push an empty block.
105/// * Transactions: It will not push any tx, only increments the end block range.
106/// * Receipts: It will not push any receipt, only increments the end block range.
107fn append_dummy_chain<N, F>(
108    sf_provider: &StaticFileProvider<N>,
109    target_height: BlockNumber,
110    header_factory: F,
111) -> ProviderResult<()>
112where
113    N: NodePrimitives,
114    F: Fn(BlockNumber) -> N::BlockHeader + Send + Sync + 'static,
115{
116    let (tx, rx) = std::sync::mpsc::channel();
117
118    // Spawn jobs for incrementing the block end range of transactions and receipts
119    for segment in [StaticFileSegment::Transactions, StaticFileSegment::Receipts] {
120        let tx_clone = tx.clone();
121        let provider = sf_provider.clone();
122        std::thread::spawn(move || {
123            let result = provider.latest_writer(segment).and_then(|mut writer| {
124                for block_num in 1..=target_height {
125                    writer.increment_block(block_num)?;
126                }
127                Ok(())
128            });
129
130            tx_clone.send(result).unwrap();
131        });
132    }
133
134    // Spawn job for appending empty headers
135    let provider = sf_provider.clone();
136    std::thread::spawn(move || {
137        let result = provider.latest_writer(StaticFileSegment::Headers).and_then(|mut writer| {
138            for block_num in 1..=target_height {
139                // TODO: should we fill with real parent_hash?
140                let header = header_factory(block_num);
141                writer.append_header(&header, U256::ZERO, &B256::ZERO)?;
142            }
143            Ok(())
144        });
145
146        tx.send(result).unwrap();
147    });
148
149    // Catches any StaticFileWriter error.
150    while let Ok(append_result) = rx.recv() {
151        if let Err(err) = append_result {
152            tracing::error!(target: "reth::cli", "Error appending dummy chain: {err}");
153            return Err(err)
154        }
155    }
156
157    // If, for any reason, rayon crashes this verifies if all segments are at the same
158    // target_height.
159    for segment in
160        [StaticFileSegment::Headers, StaticFileSegment::Receipts, StaticFileSegment::Transactions]
161    {
162        assert_eq!(
163            sf_provider.latest_writer(segment)?.user_header().block_end(),
164            Some(target_height),
165            "Static file segment {segment} was unsuccessful advancing its block height."
166        );
167    }
168
169    Ok(())
170}