reth_cli_commands/init_state/
without_evm.rs

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