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
use alloy_primitives::B256;
use reth_optimism_primitives::bedrock::{BEDROCK_HEADER, BEDROCK_HEADER_HASH, BEDROCK_HEADER_TTD};
use reth_primitives::{
    BlockBody, BlockNumber, Header, SealedBlock, SealedBlockWithSenders, SealedHeader,
    StaticFileSegment, U256,
};
use reth_provider::{
    providers::StaticFileProvider, BlockWriter, StageCheckpointWriter, StaticFileWriter,
};
use reth_stages::{StageCheckpoint, StageId};
use tracing::info;

/// Creates a dummy chain (with no transactions) up to the last OVM block and appends the
/// first valid Bedrock block.
pub(crate) fn setup_op_mainnet_without_ovm<Provider>(
    provider_rw: &Provider,
    static_file_provider: &StaticFileProvider,
) -> Result<(), eyre::Error>
where
    Provider: StageCheckpointWriter + BlockWriter,
{
    info!(target: "reth::cli", "Setting up dummy OVM chain before importing state.");

    // Write OVM dummy data up to `BEDROCK_HEADER - 1` block
    append_dummy_chain(static_file_provider, BEDROCK_HEADER.number - 1)?;

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

    append_bedrock_block(provider_rw, static_file_provider)?;

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

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

    Ok(())
}

/// Appends the first bedrock block.
///
/// By appending it, static file writer also verifies that all segments are at the same
/// height.
fn append_bedrock_block(
    provider_rw: impl BlockWriter,
    sf_provider: &StaticFileProvider,
) -> Result<(), eyre::Error> {
    provider_rw.insert_block(
        SealedBlockWithSenders::new(
            SealedBlock::new(
                SealedHeader::new(BEDROCK_HEADER, BEDROCK_HEADER_HASH),
                BlockBody::default(),
            ),
            vec![],
        )
        .expect("no senders or txes"),
    )?;

    sf_provider.latest_writer(StaticFileSegment::Headers)?.append_header(
        &BEDROCK_HEADER,
        BEDROCK_HEADER_TTD,
        &BEDROCK_HEADER_HASH,
    )?;

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

    sf_provider
        .latest_writer(StaticFileSegment::Transactions)?
        .increment_block(BEDROCK_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(
    sf_provider: &StaticFileProvider,
    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(())
}