mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: allow syncing op-mainnet with only state and without importing blocks/receipts (#10850)
Co-authored-by: Alexey Shekhirin <a.shekhirin@gmail.com>
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -8027,6 +8027,11 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "reth-optimism-primitives"
|
||||
version = "1.0.7"
|
||||
dependencies = [
|
||||
"alloy-primitives",
|
||||
"reth-primitives",
|
||||
"reth-primitives-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reth-optimism-rpc"
|
||||
|
||||
@ -1,5 +1,25 @@
|
||||
# Sync OP Mainnet
|
||||
|
||||
To sync OP mainnet, bedrock state needs to be imported as a starting point. There are currently two ways:
|
||||
|
||||
* Minimal bootstrap: only state snapshot at Bedrock block is imported without any OVM historical data.
|
||||
* Full bootstrap: state, blocks and receipts are imported.
|
||||
|
||||
## Minimal bootstrap
|
||||
|
||||
**The state snapshot at Bedrock block is required.** It can be exported from [op-geth](https://github.com/testinprod-io/op-erigon/blob/pcw109550/bedrock-db-migration/bedrock-migration.md#export-state) (**.jsonl**) or downloaded directly from [here](https://mega.nz/file/GdZ1xbAT#a9cBv3AqzsTGXYgX7nZc_3fl--tcBmOAIwIA5ND6kwc).
|
||||
|
||||
```sh
|
||||
$ op-reth init-state --without-ovm --chain optimism --datadir op-mainnet world_trie_state.jsonl
|
||||
|
||||
$ op-reth node --chain optimism --datadir op-mainnet --debug.tip 0x098f87b75c8b861c775984f9d5dbe7b70cbbbc30fc15adb03a5044de0144f2d0 # block #125200000
|
||||
```
|
||||
|
||||
|
||||
## Full bootstrap
|
||||
|
||||
### Import state
|
||||
|
||||
To sync OP mainnet, the Bedrock datadir needs to be imported to use as starting point.
|
||||
Blocks lower than the OP mainnet Bedrock fork, are built on the OVM and cannot be executed on the EVM.
|
||||
For this reason, the chain segment from genesis until Bedrock, must be manually imported to circumvent
|
||||
@ -10,7 +30,7 @@ Importing OP mainnet Bedrock datadir requires exported data:
|
||||
- Blocks [and receipts] below Bedrock
|
||||
- State snapshot at first Bedrock block
|
||||
|
||||
## Manual Export Steps
|
||||
### Manual Export Steps
|
||||
|
||||
The `op-geth` Bedrock datadir can be downloaded from <https://datadirs.optimism.io/mainnet-bedrock.tar.zst>.
|
||||
|
||||
@ -18,9 +38,9 @@ To export the OVM chain from `op-geth`, clone the `testinprod-io/op-geth` repo a
|
||||
<https://github.com/testinprod-io/op-geth/pull/1>. Commands to export blocks, receipts and state dump can be
|
||||
found in `op-geth/migrate.sh`.
|
||||
|
||||
## Manual Import Steps
|
||||
### Manual Import Steps
|
||||
|
||||
### 1. Import Blocks
|
||||
#### 1. Import Blocks
|
||||
|
||||
Imports a `.rlp` file of blocks.
|
||||
|
||||
@ -30,7 +50,7 @@ Import of >100 million OVM blocks, from genesis to Bedrock, completes in 45 minu
|
||||
$ op-reth import-op <exported-blocks>
|
||||
```
|
||||
|
||||
### 2. Import Receipts
|
||||
#### 2. Import Receipts
|
||||
|
||||
This step is optional. To run a full node, skip this step. If however receipts are to be imported, the
|
||||
corresponding transactions must already be imported (see [step 1](#1-import-blocks)).
|
||||
@ -44,7 +64,7 @@ Import of >100 million OVM receipts, from genesis to Bedrock, completes in 30 mi
|
||||
$ op-reth import-receipts-op <exported-receipts>
|
||||
```
|
||||
|
||||
### 3. Import State
|
||||
#### 3. Import State
|
||||
|
||||
Imports a `.jsonl` state dump. The block at which the state dump is made, must be the latest block in
|
||||
reth's database. This should be block 105 235 063, the first Bedrock block (see [step 1](#1-import-blocks)).
|
||||
@ -61,4 +81,4 @@ Running the node with `--debug.tip <block-hash>`syncs the node without help from
|
||||
block hash can be taken from the latest block on <https://optimistic.etherscan.io>.
|
||||
|
||||
Use `op-node` to track the tip. Start `op-node` with `--syncmode=execution-layer` and `--l2.enginekind=reth`. If `op-node`'s RPC
|
||||
connection to L1 is over localhost, `--l1.trustrpc` can be set to improve performance.
|
||||
connection to L1 is over localhost, `--l1.trustrpc` can be set to improve performance.
|
||||
|
||||
@ -17,7 +17,7 @@ use tracing::info;
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct InitStateCommand<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
env: EnvironmentArgs<C>,
|
||||
pub env: EnvironmentArgs<C>,
|
||||
|
||||
/// JSONL file with state dump.
|
||||
///
|
||||
@ -37,7 +37,7 @@ pub struct InitStateCommand<C: ChainSpecParser> {
|
||||
/// Allows init at a non-genesis block. Caution! Blocks must be manually imported up until
|
||||
/// and including the non-genesis block to init chain at. See 'import' command.
|
||||
#[arg(value_name = "STATE_DUMP_FILE", verbatim_doc_comment)]
|
||||
state: PathBuf,
|
||||
pub state: PathBuf,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>> InitStateCommand<C> {
|
||||
@ -71,5 +71,9 @@ pub fn init_at_state<N: NodeTypesWithDB<ChainSpec = ChainSpec>>(
|
||||
let file = File::open(state_dump_path)?;
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
init_from_state_dump(reader, factory, etl_config)
|
||||
let provider_rw = factory.provider_rw()?;
|
||||
let hash = init_from_state_dump(reader, &provider_rw.0, etl_config)?;
|
||||
provider_rw.commit()?;
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ reth-cli-commands.workspace = true
|
||||
reth-consensus.workspace = true
|
||||
reth-db = { workspace = true, features = ["mdbx"] }
|
||||
reth-db-api.workspace = true
|
||||
reth-db-common.workspace = true
|
||||
reth-downloaders.workspace = true
|
||||
reth-provider.workspace = true
|
||||
reth-prune.workspace = true
|
||||
|
||||
@ -12,7 +12,7 @@ use reth_downloaders::file_client::{
|
||||
};
|
||||
use reth_node_builder::NodeTypesWithEngine;
|
||||
use reth_node_core::version::SHORT_VERSION;
|
||||
use reth_optimism_primitives::bedrock_import::is_dup_tx;
|
||||
use reth_optimism_primitives::bedrock::is_dup_tx;
|
||||
use reth_provider::StageCheckpointReader;
|
||||
use reth_prune::PruneModes;
|
||||
use reth_stages::StageId;
|
||||
|
||||
@ -15,7 +15,7 @@ use reth_downloaders::{
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_node_builder::{NodeTypesWithDB, NodeTypesWithEngine};
|
||||
use reth_node_core::version::SHORT_VERSION;
|
||||
use reth_optimism_primitives::bedrock_import::is_dup_tx;
|
||||
use reth_optimism_primitives::bedrock::is_dup_tx;
|
||||
use reth_primitives::Receipts;
|
||||
use reth_provider::{
|
||||
writer::UnifiedStorageWriter, DatabaseProviderFactory, OriginalValuesKnown, ProviderFactory,
|
||||
|
||||
136
crates/optimism/cli/src/commands/init_state/bedrock.rs
Normal file
136
crates/optimism/cli/src/commands/init_state/bedrock.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use alloy_primitives::B256;
|
||||
use reth_db::Database;
|
||||
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, DatabaseProviderRW, 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<DB: Database>(
|
||||
provider_rw: &DatabaseProviderRW<DB>,
|
||||
static_file_provider: &StaticFileProvider,
|
||||
) -> Result<(), eyre::Error> {
|
||||
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<DB: Database>(
|
||||
provider_rw: &DatabaseProviderRW<DB>,
|
||||
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(())
|
||||
}
|
||||
80
crates/optimism/cli/src/commands/init_state/mod.rs
Normal file
80
crates/optimism/cli/src/commands/init_state/mod.rs
Normal file
@ -0,0 +1,80 @@
|
||||
//! Command that initializes the node from a genesis file.
|
||||
|
||||
use clap::Parser;
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::common::{AccessRights, Environment};
|
||||
use reth_db_common::init::init_from_state_dump;
|
||||
use reth_node_builder::NodeTypesWithEngine;
|
||||
use reth_optimism_primitives::bedrock::BEDROCK_HEADER;
|
||||
use reth_provider::{
|
||||
BlockNumReader, ChainSpecProvider, StaticFileProviderFactory, StaticFileWriter,
|
||||
};
|
||||
use std::{fs::File, io::BufReader};
|
||||
use tracing::info;
|
||||
|
||||
mod bedrock;
|
||||
|
||||
/// Initializes the database with the genesis block.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct InitStateCommandOp<C: ChainSpecParser> {
|
||||
#[command(flatten)]
|
||||
init_state: reth_cli_commands::init_state::InitStateCommand<C>,
|
||||
|
||||
/// **Optimism Mainnet Only**
|
||||
///
|
||||
/// Specifies whether to initialize the state without relying on OVM historical data.
|
||||
///
|
||||
/// When enabled, and before inserting the state, it creates a dummy chain up to the last OVM
|
||||
/// block (#105235062) (14GB / 90 seconds). It then, appends the Bedrock block.
|
||||
///
|
||||
/// - **Note**: **Do not** import receipts and blocks beforehand, or this will fail or be
|
||||
/// ignored.
|
||||
#[arg(long, default_value = "false")]
|
||||
without_ovm: bool,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser<ChainSpec = ChainSpec>> InitStateCommandOp<C> {
|
||||
/// Execute the `init` command
|
||||
pub async fn execute<N: NodeTypesWithEngine<ChainSpec = C::ChainSpec>>(
|
||||
self,
|
||||
) -> eyre::Result<()> {
|
||||
info!(target: "reth::cli", "Reth init-state starting");
|
||||
|
||||
let Environment { config, provider_factory, .. } =
|
||||
self.init_state.env.init::<N>(AccessRights::RW)?;
|
||||
|
||||
let static_file_provider = provider_factory.static_file_provider();
|
||||
let provider_rw = provider_factory.provider_rw()?;
|
||||
|
||||
// OP-Mainnet may want to bootstrap a chain without OVM historical data
|
||||
if provider_factory.chain_spec().is_optimism_mainnet() && self.without_ovm {
|
||||
let last_block_number = provider_rw.last_block_number()?;
|
||||
|
||||
if last_block_number == 0 {
|
||||
bedrock::setup_op_mainnet_without_ovm(&provider_rw, &static_file_provider)?;
|
||||
|
||||
// SAFETY: it's safe to commit static files, since in the event of a crash, they
|
||||
// will be unwinded according to database checkpoints.
|
||||
//
|
||||
// Necessary to commit, so the BEDROCK_HEADER is accessible to provider_rw and
|
||||
// init_state_dump
|
||||
static_file_provider.commit()?;
|
||||
} else if last_block_number > 0 && last_block_number < BEDROCK_HEADER.number {
|
||||
return Err(eyre::eyre!(
|
||||
"Data directory should be empty when calling init-state with --without-ovm."
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
info!(target: "reth::cli", "Initiating state dump");
|
||||
|
||||
let reader = BufReader::new(File::open(self.init_state.state)?);
|
||||
let hash = init_from_state_dump(reader, &provider_rw.0, config.stages.etl)?;
|
||||
|
||||
provider_rw.commit()?;
|
||||
|
||||
info!(target: "reth::cli", hash = ?hash, "Genesis block written");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ use import_receipts::ImportReceiptsOpCommand;
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_commands::{
|
||||
config_cmd, db, dump_genesis, init_cmd, init_state,
|
||||
config_cmd, db, dump_genesis, init_cmd,
|
||||
node::{self, NoArgs},
|
||||
p2p, prune, recover, stage,
|
||||
};
|
||||
@ -15,6 +15,7 @@ use std::fmt;
|
||||
mod build_pipeline;
|
||||
pub mod import;
|
||||
pub mod import_receipts;
|
||||
pub mod init_state;
|
||||
|
||||
/// Commands to be executed
|
||||
#[derive(Debug, Subcommand)]
|
||||
@ -30,7 +31,7 @@ pub enum Commands<
|
||||
Init(init_cmd::InitCommand<Spec>),
|
||||
/// Initialize the database from a state dump file.
|
||||
#[command(name = "init-state")]
|
||||
InitState(init_state::InitStateCommand<Spec>),
|
||||
InitState(init_state::InitStateCommandOp<Spec>),
|
||||
/// This syncs RLP encoded OP blocks below Bedrock from a file, without executing.
|
||||
#[command(name = "import-op")]
|
||||
ImportOp(ImportOpCommand<Spec>),
|
||||
|
||||
@ -9,4 +9,9 @@ repository.workspace = true
|
||||
description = "OP primitive types"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
reth-primitives.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
|
||||
98
crates/optimism/primitives/src/bedrock.rs
Normal file
98
crates/optimism/primitives/src/bedrock.rs
Normal file
@ -0,0 +1,98 @@
|
||||
//! OP mainnet bedrock related data.
|
||||
|
||||
use alloy_primitives::{b256, bloom, bytes, B256, U256};
|
||||
use reth_primitives::{address, Header};
|
||||
use reth_primitives_traits::constants::EMPTY_OMMER_ROOT_HASH;
|
||||
|
||||
/// Transaction 0x9ed8f713b2cc6439657db52dcd2fdb9cc944915428f3c6e2a7703e242b259cb9 in block 985,
|
||||
/// replayed in blocks:
|
||||
///
|
||||
/// 19 022
|
||||
/// 45 036
|
||||
pub const TX_BLOCK_985: [u64; 2] = [19_022, 45_036];
|
||||
|
||||
/// Transaction 0xc033250c5a45f9d104fc28640071a776d146d48403cf5e95ed0015c712e26cb6 in block
|
||||
/// 123 322, replayed in block:
|
||||
///
|
||||
/// 123 542
|
||||
pub const TX_BLOCK_123_322: u64 = 123_542;
|
||||
|
||||
/// Transaction 0x86f8c77cfa2b439e9b4e92a10f6c17b99fce1220edf4001e4158b57f41c576e5 in block
|
||||
/// 1 133 328, replayed in blocks:
|
||||
///
|
||||
/// 1 135 391
|
||||
/// 1 144 468
|
||||
pub const TX_BLOCK_1_133_328: [u64; 2] = [1_135_391, 1_144_468];
|
||||
|
||||
/// Transaction 0x3cc27e7cc8b7a9380b2b2f6c224ea5ef06ade62a6af564a9dd0bcca92131cd4e in block
|
||||
/// 1 244 152, replayed in block:
|
||||
///
|
||||
/// 1 272 994
|
||||
pub const TX_BLOCK_1_244_152: u64 = 1_272_994;
|
||||
|
||||
/// The six blocks with replayed transactions.
|
||||
pub const BLOCK_NUMS_REPLAYED_TX: [u64; 6] = [
|
||||
TX_BLOCK_985[0],
|
||||
TX_BLOCK_985[1],
|
||||
TX_BLOCK_123_322,
|
||||
TX_BLOCK_1_133_328[0],
|
||||
TX_BLOCK_1_133_328[1],
|
||||
TX_BLOCK_1_244_152,
|
||||
];
|
||||
|
||||
/// Returns `true` if transaction is the second or third appearance of the transaction. The blocks
|
||||
/// with replayed transaction happen to only contain the single transaction.
|
||||
pub fn is_dup_tx(block_number: u64) -> bool {
|
||||
if block_number > BLOCK_NUMS_REPLAYED_TX[5] {
|
||||
return false
|
||||
}
|
||||
|
||||
// these blocks just have one transaction!
|
||||
if BLOCK_NUMS_REPLAYED_TX.contains(&block_number) {
|
||||
return true
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Bedrock hash on Optimism Mainnet.
|
||||
pub const BEDROCK_HEADER_HASH: B256 =
|
||||
b256!("dbf6a80fef073de06add9b0d14026d6e5a86c85f6d102c36d3d8e9cf89c2afd3");
|
||||
|
||||
/// Bedrock on Optimism Mainnet. (`105_235_063`)
|
||||
pub const BEDROCK_HEADER: Header = Header {
|
||||
difficulty: U256::ZERO,
|
||||
extra_data: bytes!("424544524f434b"),
|
||||
gas_limit: 30000000,
|
||||
gas_used: 0,
|
||||
logs_bloom: bloom!("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
nonce: 0,
|
||||
number: 105235063,
|
||||
parent_hash: b256!("21a168dfa5e727926063a28ba16fd5ee84c814e847c81a699c7a0ea551e4ca50"),
|
||||
receipts_root: b256!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
||||
state_root: b256!("920314c198da844a041d63bf6cbe8b59583165fd2229d1b3f599da812fd424cb"),
|
||||
timestamp: 1686068903,
|
||||
transactions_root: b256!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"),
|
||||
ommers_hash: EMPTY_OMMER_ROOT_HASH,
|
||||
beneficiary: address!("4200000000000000000000000000000000000011"),
|
||||
withdrawals_root: None,
|
||||
mix_hash: B256::ZERO,
|
||||
base_fee_per_gas: Some(0x3b9aca00),
|
||||
blob_gas_used: None,
|
||||
excess_blob_gas: None,
|
||||
parent_beacon_block_root: None,
|
||||
requests_root: None,
|
||||
};
|
||||
|
||||
/// Bedrock total difficulty on Optimism Mainnet.
|
||||
pub const BEDROCK_HEADER_TTD: U256 = U256::ZERO;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_bedrock_header() {
|
||||
assert_eq!(BEDROCK_HEADER.hash_slow(), BEDROCK_HEADER_HASH);
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
//! Replayed OP mainnet OVM transactions (in blocks below Bedrock).
|
||||
|
||||
/// Transaction 0x9ed8f713b2cc6439657db52dcd2fdb9cc944915428f3c6e2a7703e242b259cb9 in block 985,
|
||||
/// replayed in blocks:
|
||||
///
|
||||
/// 19 022
|
||||
/// 45 036
|
||||
pub const TX_BLOCK_985: [u64; 2] = [19_022, 45_036];
|
||||
|
||||
/// Transaction 0xc033250c5a45f9d104fc28640071a776d146d48403cf5e95ed0015c712e26cb6 in block
|
||||
/// 123 322, replayed in block:
|
||||
///
|
||||
/// 123 542
|
||||
pub const TX_BLOCK_123_322: u64 = 123_542;
|
||||
|
||||
/// Transaction 0x86f8c77cfa2b439e9b4e92a10f6c17b99fce1220edf4001e4158b57f41c576e5 in block
|
||||
/// 1 133 328, replayed in blocks:
|
||||
///
|
||||
/// 1 135 391
|
||||
/// 1 144 468
|
||||
pub const TX_BLOCK_1_133_328: [u64; 2] = [1_135_391, 1_144_468];
|
||||
|
||||
/// Transaction 0x3cc27e7cc8b7a9380b2b2f6c224ea5ef06ade62a6af564a9dd0bcca92131cd4e in block
|
||||
/// 1 244 152, replayed in block:
|
||||
///
|
||||
/// 1 272 994
|
||||
pub const TX_BLOCK_1_244_152: u64 = 1_272_994;
|
||||
|
||||
/// The six blocks with replayed transactions.
|
||||
pub const BLOCK_NUMS_REPLAYED_TX: [u64; 6] = [
|
||||
TX_BLOCK_985[0],
|
||||
TX_BLOCK_985[1],
|
||||
TX_BLOCK_123_322,
|
||||
TX_BLOCK_1_133_328[0],
|
||||
TX_BLOCK_1_133_328[1],
|
||||
TX_BLOCK_1_244_152,
|
||||
];
|
||||
|
||||
/// Returns `true` if transaction is the second or third appearance of the transaction. The blocks
|
||||
/// with replayed transaction happen to only contain the single transaction.
|
||||
pub fn is_dup_tx(block_number: u64) -> bool {
|
||||
if block_number > BLOCK_NUMS_REPLAYED_TX[5] {
|
||||
return false
|
||||
}
|
||||
|
||||
// these blocks just have one transaction!
|
||||
if BLOCK_NUMS_REPLAYED_TX.contains(&block_number) {
|
||||
return true
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
@ -7,4 +7,4 @@
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
|
||||
pub mod bedrock_import;
|
||||
pub mod bedrock;
|
||||
|
||||
@ -326,29 +326,27 @@ where
|
||||
/// It's similar to [`init_genesis`] but supports importing state too big to fit in memory, and can
|
||||
/// be set to the highest block present. One practical usecase is to import OP mainnet state at
|
||||
/// bedrock transition block.
|
||||
pub fn init_from_state_dump<PF>(
|
||||
pub fn init_from_state_dump<Provider>(
|
||||
mut reader: impl BufRead,
|
||||
factory: PF,
|
||||
provider_rw: &Provider,
|
||||
etl_config: EtlConfig,
|
||||
) -> eyre::Result<B256>
|
||||
where
|
||||
PF: DatabaseProviderFactory
|
||||
+ StaticFileProviderFactory
|
||||
+ ChainSpecProvider<ChainSpec = ChainSpec>
|
||||
+ BlockHashReader
|
||||
Provider: DBProvider<Tx: DbTxMut>
|
||||
+ BlockNumReader
|
||||
+ HeaderProvider,
|
||||
PF::ProviderRW: StageCheckpointWriter
|
||||
+ BlockHashReader
|
||||
+ ChainSpecProvider<ChainSpec = ChainSpec>
|
||||
+ StageCheckpointWriter
|
||||
+ HistoryWriter
|
||||
+ HeaderProvider
|
||||
+ HashingWriter
|
||||
+ StateChangeWriter
|
||||
+ TrieWriter
|
||||
+ AsRef<PF::ProviderRW>,
|
||||
+ AsRef<Provider>,
|
||||
{
|
||||
let block = factory.last_block_number()?;
|
||||
let hash = factory.block_hash(block)?.unwrap();
|
||||
let expected_state_root = factory
|
||||
let block = provider_rw.last_block_number()?;
|
||||
let hash = provider_rw.block_hash(block)?.unwrap();
|
||||
let expected_state_root = provider_rw
|
||||
.header_by_number(block)?
|
||||
.ok_or(ProviderError::HeaderNotFound(block.into()))?
|
||||
.state_root;
|
||||
@ -370,7 +368,7 @@ where
|
||||
|
||||
debug!(target: "reth::cli",
|
||||
block,
|
||||
chain=%factory.chain_spec().chain,
|
||||
chain=%provider_rw.chain_spec().chain,
|
||||
"Initializing state at block"
|
||||
);
|
||||
|
||||
@ -378,11 +376,10 @@ where
|
||||
let collector = parse_accounts(&mut reader, etl_config)?;
|
||||
|
||||
// write state to db
|
||||
let provider_rw = factory.database_provider_rw()?;
|
||||
dump_state(collector, &provider_rw, block)?;
|
||||
dump_state(collector, provider_rw, block)?;
|
||||
|
||||
// compute and compare state root. this advances the stage checkpoints.
|
||||
let computed_state_root = compute_state_root(&provider_rw)?;
|
||||
let computed_state_root = compute_state_root(provider_rw)?;
|
||||
if computed_state_root == expected_state_root {
|
||||
info!(target: "reth::cli",
|
||||
?computed_state_root,
|
||||
@ -407,8 +404,6 @@ where
|
||||
provider_rw.save_stage_checkpoint(stage, StageCheckpoint::new(block))?;
|
||||
}
|
||||
|
||||
provider_rw.commit()?;
|
||||
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ use crate::{
|
||||
use alloy_primitives::{keccak256, Address, BlockHash, BlockNumber, TxHash, TxNumber, B256, U256};
|
||||
use itertools::{izip, Itertools};
|
||||
use rayon::slice::ParallelSliceMut;
|
||||
use reth_chainspec::{ChainInfo, ChainSpec, EthereumHardforks};
|
||||
use reth_chainspec::{ChainInfo, ChainSpec, ChainSpecProvider, EthereumHardforks};
|
||||
use reth_db::{
|
||||
cursor::DbDupCursorRW, tables, BlockNumberList, PlainAccountState, PlainStorageState,
|
||||
};
|
||||
@ -1595,6 +1595,14 @@ impl<TX: DbTxMut + DbTx> DatabaseProvider<TX> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX: DbTx> ChainSpecProvider for DatabaseProvider<TX> {
|
||||
type ChainSpec = ChainSpec;
|
||||
|
||||
fn chain_spec(&self) -> Arc<ChainSpec> {
|
||||
self.chain_spec.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<TX: DbTx> AccountReader for DatabaseProvider<TX> {
|
||||
fn basic_account(&self, address: Address) -> ProviderResult<Option<Account>> {
|
||||
Ok(self.tx.get::<tables::PlainAccountState>(address)?)
|
||||
|
||||
Reference in New Issue
Block a user