mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +00:00
feat(op): import below bedrock blocks (#7555)
Co-authored-by: Atris <vacekj@outlook.com>
This commit is contained in:
@ -85,7 +85,7 @@ futures.workspace = true
|
||||
# misc
|
||||
aquamarine.workspace = true
|
||||
eyre.workspace = true
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
tempfile.workspace = true
|
||||
backon = "0.4"
|
||||
similar-asserts.workspace = true
|
||||
|
||||
@ -21,7 +21,7 @@ use reth_downloaders::{
|
||||
use reth_interfaces::consensus::Consensus;
|
||||
use reth_node_core::{events::node::NodeEvent, init::init_genesis};
|
||||
use reth_node_ethereum::EthEvmConfig;
|
||||
use reth_primitives::{stage::StageId, ChainSpec, PruneModes, B256};
|
||||
use reth_primitives::{stage::StageId, ChainSpec, PruneModes, B256, OP_RETH_MAINNET_BELOW_BEDROCK};
|
||||
use reth_provider::{HeaderSyncMode, ProviderFactory, StageCheckpointReader};
|
||||
use reth_stages::{
|
||||
prelude::*,
|
||||
@ -61,6 +61,15 @@ pub struct ImportCommand {
|
||||
)]
|
||||
chain: Arc<ChainSpec>,
|
||||
|
||||
/// Disables execution stage.
|
||||
#[arg(long, verbatim_doc_comment)]
|
||||
disable_execution: bool,
|
||||
|
||||
/// Import OP Mainnet chain below Bedrock. Caution! Flag must be set as env var, since the env
|
||||
/// var is read by another process too, in order to make below Bedrock import work.
|
||||
#[arg(long, verbatim_doc_comment, env = OP_RETH_MAINNET_BELOW_BEDROCK)]
|
||||
op_mainnet_below_bedrock: bool,
|
||||
|
||||
#[command(flatten)]
|
||||
db: DatabaseArgs,
|
||||
|
||||
@ -74,9 +83,18 @@ pub struct ImportCommand {
|
||||
|
||||
impl ImportCommand {
|
||||
/// Execute `import` command
|
||||
pub async fn execute(self) -> eyre::Result<()> {
|
||||
pub async fn execute(mut self) -> eyre::Result<()> {
|
||||
info!(target: "reth::cli", "reth {} starting", SHORT_VERSION);
|
||||
|
||||
if self.op_mainnet_below_bedrock {
|
||||
self.disable_execution = true;
|
||||
debug!(target: "reth::cli", "Importing OP mainnet below bedrock");
|
||||
}
|
||||
|
||||
if self.disable_execution {
|
||||
debug!(target: "reth::cli", "Execution stage disabled");
|
||||
}
|
||||
|
||||
// add network name to data dir
|
||||
let data_dir = self.datadir.unwrap_or_chain_default(self.chain.chain);
|
||||
let config_path = self.config.clone().unwrap_or_else(|| data_dir.config_path());
|
||||
@ -118,6 +136,7 @@ impl ImportCommand {
|
||||
provider_factory.static_file_provider(),
|
||||
PruneModes::default(),
|
||||
),
|
||||
self.disable_execution,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -154,6 +173,7 @@ impl ImportCommand {
|
||||
consensus: &Arc<C>,
|
||||
file_client: Arc<FileClient>,
|
||||
static_file_producer: StaticFileProducer<DB>,
|
||||
disable_execution: bool,
|
||||
) -> eyre::Result<(Pipeline<DB>, impl Stream<Item = NodeEvent>)>
|
||||
where
|
||||
DB: Database + Clone + Unpin + 'static,
|
||||
@ -209,7 +229,8 @@ impl ImportCommand {
|
||||
.max(config.stages.account_hashing.clean_threshold)
|
||||
.max(config.stages.storage_hashing.clean_threshold),
|
||||
config.prune.map(|prune| prune.segments).unwrap_or_default(),
|
||||
)),
|
||||
))
|
||||
.disable_if(StageId::Execution, || disable_execution),
|
||||
)
|
||||
.build(provider_factory, static_file_producer);
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ pub mod db;
|
||||
pub mod debug_cmd;
|
||||
pub mod dump_genesis;
|
||||
pub mod import;
|
||||
|
||||
pub mod init_cmd;
|
||||
|
||||
pub mod node;
|
||||
|
||||
@ -65,7 +65,8 @@ impl FileClient {
|
||||
let metadata = file.metadata().await?;
|
||||
let file_len = metadata.len();
|
||||
|
||||
// read the entire file into memory
|
||||
// todo: read chunks into memory. for op mainnet 1/8 th of blocks below bedrock can be
|
||||
// decoded at once
|
||||
let mut reader = vec![];
|
||||
file.read_to_end(&mut reader).await.unwrap();
|
||||
|
||||
@ -76,8 +77,12 @@ impl FileClient {
|
||||
// use with_capacity to make sure the internal buffer contains the entire file
|
||||
let mut stream = FramedRead::with_capacity(&reader[..], BlockFileCodec, file_len as usize);
|
||||
|
||||
let mut log_interval = 0;
|
||||
let mut log_interval_start_block = 0;
|
||||
|
||||
while let Some(block_res) = stream.next().await {
|
||||
let block = block_res?;
|
||||
let block_number = block.header.number;
|
||||
let block_hash = block.header.hash_slow();
|
||||
|
||||
// add to the internal maps
|
||||
@ -91,6 +96,17 @@ impl FileClient {
|
||||
withdrawals: block.withdrawals,
|
||||
},
|
||||
);
|
||||
|
||||
if log_interval == 0 {
|
||||
log_interval_start_block = block_number;
|
||||
} else if log_interval % 100000 == 0 {
|
||||
trace!(target: "downloaders::file",
|
||||
blocks=?log_interval_start_block..=block_number,
|
||||
"inserted blocks into db"
|
||||
);
|
||||
log_interval_start_block = block_number + 1;
|
||||
}
|
||||
log_interval += 1;
|
||||
}
|
||||
|
||||
trace!(blocks = headers.len(), "Initialized file client");
|
||||
|
||||
@ -10,14 +10,14 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
use reth_primitives::{BASE_MAINNET, BASE_SEPOLIA, OP_SEPOLIA};
|
||||
use reth_primitives::{BASE_MAINNET, BASE_SEPOLIA, OP_MAINNET, OP_SEPOLIA};
|
||||
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
use reth_primitives::{DEV, GOERLI, HOLESKY, MAINNET, SEPOLIA};
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
/// Chains supported by op-reth. First value should be used as the default.
|
||||
pub const SUPPORTED_CHAINS: &[&str] = &["base", "base-sepolia", "optimism-sepolia"];
|
||||
pub const SUPPORTED_CHAINS: &[&str] = &["base", "base-sepolia", "optimism", "optimism-sepolia"];
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
/// Chains supported by reth. First value should be used as the default.
|
||||
pub const SUPPORTED_CHAINS: &[&str] = &["mainnet", "sepolia", "goerli", "holesky", "dev"];
|
||||
@ -43,11 +43,13 @@ pub fn chain_spec_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Er
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
"dev" => DEV.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"optimism" => OP_MAINNET.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"base_sepolia" | "base-sepolia" => BASE_SEPOLIA.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"base" => BASE_MAINNET.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"base_sepolia" | "base-sepolia" => BASE_SEPOLIA.clone(),
|
||||
_ => {
|
||||
let raw = fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned()))?;
|
||||
serde_json::from_str(&raw)?
|
||||
@ -78,11 +80,13 @@ pub fn genesis_value_parser(s: &str) -> eyre::Result<Arc<ChainSpec>, eyre::Error
|
||||
#[cfg(not(feature = "optimism"))]
|
||||
"dev" => DEV.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"optimism" => OP_MAINNET.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"optimism_sepolia" | "optimism-sepolia" => OP_SEPOLIA.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"base_sepolia" | "base-sepolia" => BASE_SEPOLIA.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"base" => BASE_MAINNET.clone(),
|
||||
#[cfg(feature = "optimism")]
|
||||
"base_sepolia" | "base-sepolia" => BASE_SEPOLIA.clone(),
|
||||
_ => {
|
||||
// try to read json from path first
|
||||
let raw = match fs::read_to_string(PathBuf::from(shellexpand::full(s)?.into_owned())) {
|
||||
|
||||
32
crates/primitives/res/genesis/optimism.json
Normal file
32
crates/primitives/res/genesis/optimism.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"config": {
|
||||
"ChainName": "optimism-mainnet",
|
||||
"chainId": 10,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"eip158Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"muirGlacierBlock": 0,
|
||||
"berlinBlock": 3950000,
|
||||
"londonBlock": 3950000,
|
||||
"arrowGlacierBlock": 3950000,
|
||||
"grayGlacierBlock": 3950000,
|
||||
"mergeNetsplitBlock": 3950000,
|
||||
"bedrockBlock": 105235063,
|
||||
"terminalTotalDifficulty": 0,
|
||||
"terminalTotalDifficultyPassed": true,
|
||||
"optimism": {
|
||||
"eip1559Elasticity": 6,
|
||||
"eip1559Denominator": 50
|
||||
},
|
||||
"regolithTime": 0
|
||||
},
|
||||
"difficulty": "1",
|
||||
"gasLimit": "15000000",
|
||||
"extradata": "0x000000000000000000000000000000000000000000000000000000000000000000000398232e2064f896018496b4b44b3d62751f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"alloc": {}
|
||||
}
|
||||
@ -6,7 +6,7 @@ pub use spec::{
|
||||
MAINNET, SEPOLIA,
|
||||
};
|
||||
#[cfg(feature = "optimism")]
|
||||
pub use spec::{BASE_MAINNET, BASE_SEPOLIA, OP_SEPOLIA};
|
||||
pub use spec::{BASE_MAINNET, BASE_SEPOLIA, OP_MAINNET, OP_SEPOLIA};
|
||||
|
||||
// The chain spec module.
|
||||
mod spec;
|
||||
|
||||
@ -243,6 +243,58 @@ pub static DEV: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
.into()
|
||||
});
|
||||
|
||||
/// The Optimism Mainnet spec
|
||||
#[cfg(feature = "optimism")]
|
||||
pub static OP_MAINNET: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
ChainSpec {
|
||||
chain: Chain::optimism_mainnet(),
|
||||
// genesis contains empty alloc field because state at first bedrock block is imported
|
||||
// manually from trusted source
|
||||
genesis: serde_json::from_str(include_str!("../../res/genesis/optimism.json"))
|
||||
.expect("Can't deserialize Optimism Mainnet genesis json"),
|
||||
genesis_hash: Some(b256!(
|
||||
"7ca38a1916c42007829c55e69d3e9a73265554b586a499015373241b8a3fa48b"
|
||||
)),
|
||||
fork_timestamps: ForkTimestamps::default()
|
||||
.shanghai(1699981200)
|
||||
.canyon(1699981200)
|
||||
.cancun(1707238800)
|
||||
.ecotone(1707238800),
|
||||
paris_block_and_final_difficulty: Some((0, U256::from(0))),
|
||||
hardforks: BTreeMap::from([
|
||||
(Hardfork::Frontier, ForkCondition::Block(0)),
|
||||
(Hardfork::Homestead, ForkCondition::Block(0)),
|
||||
(Hardfork::Tangerine, ForkCondition::Block(0)),
|
||||
(Hardfork::SpuriousDragon, ForkCondition::Block(0)),
|
||||
(Hardfork::Byzantium, ForkCondition::Block(0)),
|
||||
(Hardfork::Constantinople, ForkCondition::Block(0)),
|
||||
(Hardfork::Petersburg, ForkCondition::Block(0)),
|
||||
(Hardfork::Istanbul, ForkCondition::Block(0)),
|
||||
(Hardfork::MuirGlacier, ForkCondition::Block(0)),
|
||||
(Hardfork::Berlin, ForkCondition::Block(3950000)),
|
||||
(Hardfork::London, ForkCondition::Block(3950000)),
|
||||
(Hardfork::ArrowGlacier, ForkCondition::Block(3950000)),
|
||||
(Hardfork::GrayGlacier, ForkCondition::Block(3950000)),
|
||||
(
|
||||
Hardfork::Paris,
|
||||
ForkCondition::TTD { fork_block: Some(3950000), total_difficulty: U256::from(0) },
|
||||
),
|
||||
(Hardfork::Bedrock, ForkCondition::Block(105235063)),
|
||||
(Hardfork::Regolith, ForkCondition::Timestamp(0)),
|
||||
]),
|
||||
base_fee_params: BaseFeeParamsKind::Variable(
|
||||
vec![
|
||||
(Hardfork::London, BaseFeeParams::optimism()),
|
||||
(Hardfork::Canyon, BaseFeeParams::optimism_canyon()),
|
||||
]
|
||||
.into(),
|
||||
),
|
||||
prune_delete_limit: 1700,
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
});
|
||||
|
||||
/// The OP Sepolia spec
|
||||
#[cfg(feature = "optimism")]
|
||||
pub static OP_SEPOLIA: Lazy<Arc<ChainSpec>> = Lazy::new(|| {
|
||||
@ -469,7 +521,7 @@ impl BaseFeeParams {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the base fee parameters for optimism goerli (post Canyon)
|
||||
/// Get the base fee parameters for optimism sepolia (post Canyon)
|
||||
#[cfg(feature = "optimism")]
|
||||
pub const fn optimism_sepolia_canyon() -> BaseFeeParams {
|
||||
BaseFeeParams {
|
||||
@ -3176,7 +3228,7 @@ Post-merge hard forks (timestamp based):
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
#[test]
|
||||
fn latest_op_mainnet_fork_id() {
|
||||
fn latest_base_mainnet_fork_id() {
|
||||
assert_eq!(
|
||||
ForkId { hash: ForkHash([0x51, 0xcc, 0x98, 0xb3]), next: 0 },
|
||||
BASE_MAINNET.latest_fork_id()
|
||||
|
||||
@ -98,8 +98,9 @@ pub use transaction::{
|
||||
InvalidTransactionError, Signature, Transaction, TransactionKind, TransactionMeta,
|
||||
TransactionSigned, TransactionSignedEcRecovered, TransactionSignedNoHash, TxEip1559, TxEip2930,
|
||||
TxEip4844, TxHashOrNumber, TxLegacy, TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID,
|
||||
EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID,
|
||||
EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, OP_RETH_MAINNET_BELOW_BEDROCK,
|
||||
};
|
||||
|
||||
pub use withdrawal::{Withdrawal, Withdrawals};
|
||||
|
||||
// Re-exports
|
||||
@ -141,7 +142,7 @@ pub use c_kzg as kzg;
|
||||
#[cfg(feature = "optimism")]
|
||||
mod optimism {
|
||||
pub use crate::{
|
||||
chain::{BASE_MAINNET, BASE_SEPOLIA, OP_SEPOLIA},
|
||||
chain::{BASE_MAINNET, BASE_SEPOLIA, OP_MAINNET, OP_SEPOLIA},
|
||||
transaction::{TxDeposit, DEPOSIT_TX_TYPE_ID},
|
||||
};
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ pub use sidecar::generate_blob_sidecar;
|
||||
#[cfg(feature = "c-kzg")]
|
||||
pub use sidecar::{BlobTransaction, BlobTransactionSidecar, BlobTransactionValidationError};
|
||||
|
||||
pub use signature::Signature;
|
||||
pub use signature::{Signature, OP_RETH_MAINNET_BELOW_BEDROCK};
|
||||
pub use tx_type::{
|
||||
TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID,
|
||||
};
|
||||
|
||||
@ -14,6 +14,9 @@ const SECP256K1N_HALF: U256 = U256::from_be_bytes([
|
||||
0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0,
|
||||
]);
|
||||
|
||||
/// Running OP Mainnet migration for chain below bedrock.]
|
||||
pub const OP_RETH_MAINNET_BELOW_BEDROCK: &str = "OP_RETH_MAINNET_BELOW_BEDROCK";
|
||||
|
||||
/// r, s: Values corresponding to the signature of the
|
||||
/// transaction and used to determine the sender of
|
||||
/// the transaction; formally Tr and Ts. This is expanded in Appendix F of yellow paper.
|
||||
@ -94,9 +97,14 @@ impl Signature {
|
||||
let v = u64::decode(buf)?;
|
||||
let r = Decodable::decode(buf)?;
|
||||
let s = Decodable::decode(buf)?;
|
||||
|
||||
if v < 35 {
|
||||
// non-EIP-155 legacy scheme, v = 27 for even y-parity, v = 28 for odd y-parity
|
||||
if v != 27 && v != 28 {
|
||||
#[cfg(feature = "optimism")]
|
||||
if std::env::var(OP_RETH_MAINNET_BELOW_BEDROCK) == Ok(true.to_string()) && v == 0 {
|
||||
return Ok((Signature { r, s, odd_y_parity: false }, None))
|
||||
}
|
||||
return Err(RlpError::Custom("invalid Ethereum signature (V is not 27 or 28)"))
|
||||
}
|
||||
let odd_y_parity = v == 28;
|
||||
|
||||
Reference in New Issue
Block a user