reth_cli_commands/init_state/
without_evm.rs

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