reth_cli_commands/init_state/
without_evm.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use alloy_primitives::{BlockNumber, B256, U256};
use alloy_rlp::Decodable;

use alloy_consensus::{BlockHeader, Header};
use reth_codecs::Compact;
use reth_node_builder::NodePrimitives;
use reth_primitives::{SealedBlock, SealedBlockWithSenders, SealedHeader, StaticFileSegment};
use reth_provider::{
    providers::StaticFileProvider, BlockWriter, StageCheckpointWriter, StaticFileProviderFactory,
    StaticFileWriter, StorageLocation,
};
use reth_stages::{StageCheckpoint, StageId};

use std::{fs::File, io::Read, path::PathBuf};
use tracing::info;

/// Reads the header RLP from a file and returns the Header.
pub(crate) fn read_header_from_file(path: PathBuf) -> Result<Header, eyre::Error> {
    let mut file = File::open(path)?;
    let mut buf = Vec::new();
    file.read_to_end(&mut buf)?;

    let header = Header::decode(&mut &buf[..])?;
    Ok(header)
}

/// Creates a dummy chain (with no transactions) up to the last EVM block and appends the
/// first valid block.
pub fn setup_without_evm<Provider>(
    provider_rw: &Provider,
    header: SealedHeader<<Provider::Primitives as NodePrimitives>::BlockHeader>,
    total_difficulty: U256,
) -> Result<(), eyre::Error>
where
    Provider: StaticFileProviderFactory<Primitives: NodePrimitives<BlockHeader = Header>>
        + StageCheckpointWriter
        + BlockWriter<Block = <Provider::Primitives as NodePrimitives>::Block>,
{
    info!(target: "reth::cli", "Setting up dummy EVM chain before importing state.");

    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)?;

    info!(target: "reth::cli", "Appending first valid block.");

    append_first_block(provider_rw, &header, total_difficulty)?;

    for stage in StageId::ALL {
        provider_rw.save_stage_checkpoint(stage, StageCheckpoint::new(header.number()))?;
    }

    info!(target: "reth::cli", "Set up finished.");

    Ok(())
}

/// Appends the first block.
///
/// By appending it, static file writer also verifies that all segments are at the same
/// height.
fn append_first_block<Provider>(
    provider_rw: &Provider,
    header: &SealedHeader<<Provider::Primitives as NodePrimitives>::BlockHeader>,
    total_difficulty: U256,
) -> Result<(), eyre::Error>
where
    Provider: BlockWriter<Block = <Provider::Primitives as NodePrimitives>::Block>
        + StaticFileProviderFactory<Primitives: NodePrimitives<BlockHeader: Compact>>,
{
    provider_rw.insert_block(
        SealedBlockWithSenders::new(SealedBlock::new(header.clone(), Default::default()), vec![])
            .expect("no senders or txes"),
        StorageLocation::Database,
    )?;

    let sf_provider = provider_rw.static_file_provider();

    sf_provider.latest_writer(StaticFileSegment::Headers)?.append_header(
        header,
        total_difficulty,
        &header.hash(),
    )?;

    sf_provider.latest_writer(StaticFileSegment::Receipts)?.increment_block(header.number())?;

    sf_provider.latest_writer(StaticFileSegment::Transactions)?.increment_block(header.number())?;

    Ok(())
}

/// Creates a dummy chain with no transactions/receipts up to `target_height` block inclusive.
///
/// * Headers: It will push an empty block.
/// * Transactions: It will not push any tx, 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>>(
    sf_provider: &StaticFileProvider<N>,
    target_height: BlockNumber,
) -> Result<(), eyre::Error> {
    let (tx, rx) = std::sync::mpsc::channel();

    // Spawn jobs for incrementing the block end range of transactions and receipts
    for segment in [StaticFileSegment::Transactions, StaticFileSegment::Receipts] {
        let tx_clone = tx.clone();
        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 {
                    writer.increment_block(block_num)?;
                }
                Ok(())
            });

            tx_clone.send(result).unwrap();
        });
    }

    // Spawn job for appending empty headers
    let provider = sf_provider.clone();
    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 {
                // TODO: should we fill with real parent_hash?
                empty_header.number = block_num;
                writer.append_header(&empty_header, U256::ZERO, &B256::ZERO)?;
            }
            Ok(())
        });

        tx.send(result).unwrap();
    });

    // Catches any StaticFileWriter error.
    while let Ok(r) = rx.recv() {
        r?;
    }

    // If, for any reason, rayon crashes this verifies if all segments are at the same
    // target_height.
    for segment in
        [StaticFileSegment::Headers, StaticFileSegment::Receipts, StaticFileSegment::Transactions]
    {
        assert_eq!(
            sf_provider.latest_writer(segment)?.user_header().block_end(),
            Some(target_height),
            "Static file segment {segment} was unsuccessful advancing its block height."
        );
    }

    Ok(())
}