Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
367 changes: 361 additions & 6 deletions Cargo.lock

Large diffs are not rendered by default.

27 changes: 26 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ exclude = [".github/"]

[workspace]
resolver = "2"
members = ["bin/*", "crates/client/*", "crates/shared/*", "crates/builder/*"]
members = ["bin/*", "crates/client/*", "crates/shared/*", "crates/builder/*", "crates/optimism/*"]
default-members = ["bin/node"]

[workspace.metadata.cargo-udeps.ignore]
Expand Down Expand Up @@ -65,6 +65,9 @@ base-metering = { path = "crates/client/metering" }
base-txpool = { path = "crates/client/txpool" }
base-flashblocks = { path = "crates/client/flashblocks" }

reth-optimism-exex = { path = "crates/optimism/exex" }
reth-optimism-trie = { path = "crates/optimism/trie" }

# reth
reth = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-ipc = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
Expand Down Expand Up @@ -99,6 +102,16 @@ reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", rev =
reth-db = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8", features = [
"op",
] }
reth-cli-runner = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-consensus = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-downloaders = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-fs-util = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-node-events = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-node-metrics = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-prune = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-stages = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-static-file = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-static-file-types = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-chain-state = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-errors = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
reth-payload-builder = { git = "https://github.com/paradigmxyz/reth", rev = "6d952f39fe47411164ec0b3978c43fdc7eefe3a8" }
Expand Down Expand Up @@ -185,6 +198,8 @@ alloy-network-primitives = "1.2.1"
alloy-transport = "1.2.1"
alloy-node-bindings = "1.2.1"
alloy-rpc-types-beacon = { version = "1.2.1", features = ["ssz"] }
alloy-rpc-types-debug = { version = "1.2.1", default-features = false }


# op-alloy
op-alloy-flz = { version = "0.13.1", default-features = false }
Expand Down Expand Up @@ -264,6 +279,16 @@ opentelemetry = { version = "0.31", features = ["trace"] }
jsonrpsee-core = "0.26.0"
ethereum_ssz = "0.9.0"
ethereum_ssz_derive = "0.9.0"
strum = { version = "0.27", default-features = false }
tower = "0.5"
bincode = "1.3"
mockall = "0.13.1"
serial_test = "3.2.0"
tempfile = "3.20"
test-case = "3"
humantime = "2.1"
proptest = "1.7"


# base
concurrent-queue = "2.5.0"
3 changes: 2 additions & 1 deletion bin/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ base-txpool.workspace = true

# reth
reth-optimism-node.workspace = true
reth-optimism-cli.workspace = true
reth-optimism-cli = { path = "../../crates/optimism/cli" }
reth-cli-util.workspace = true
reth-optimism-exex.workspace = true

# misc
clap.workspace = true
Expand Down
5 changes: 5 additions & 0 deletions bin/node/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use base_flashblocks::FlashblocksConfig;
use base_txpool::TxpoolConfig;
use reth_optimism_exex::ProofsHistoryConfig;
use reth_optimism_node::args::RollupArgs;

/// CLI Arguments
Expand All @@ -12,6 +13,10 @@ pub struct Args {
#[command(flatten)]
pub rollup_args: RollupArgs,

/// Proofs history arguments
#[command(flatten)]
pub proofs_history_args: ProofsHistoryConfig,

/// The websocket url used for flashblocks.
#[arg(long = "websocket-url", value_name = "WEBSOCKET_URL")]
pub websocket_url: Option<String>,
Expand Down
4 changes: 3 additions & 1 deletion bin/node/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use base_client_node::BaseNodeRunner;
use base_flashblocks::FlashblocksExtension;
use base_metering::MeteringExtension;
use base_txpool::TxPoolExtension;
use reth_optimism_exex::ProofsHistoryExtension;

#[global_allocator]
static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator();
Expand All @@ -29,7 +30,8 @@ fn main() {
// Feature extensions (FlashblocksExtension must be last - uses replace_configured)
runner.install_ext::<TxPoolExtension>(args.clone().into());
runner.install_ext::<MeteringExtension>(args.enable_metering);
runner.install_ext::<FlashblocksExtension>(args.into());
runner.install_ext::<FlashblocksExtension>(args.clone().into());
runner.install_ext::<ProofsHistoryExtension>(args.proofs_history_args.clone());

let handle = runner.run(builder);
handle.await
Expand Down
103 changes: 103 additions & 0 deletions crates/optimism/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
[package]
name = "reth-optimism-cli"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[lints]
workspace = true

[dependencies]
reth-static-file-types = { workspace = true, features = ["clap"] }
reth-cli.workspace = true
reth-cli-commands.workspace = true
reth-consensus.workspace = true
reth-rpc-server-types.workspace = true
reth-primitives-traits.workspace = true
reth-db = { workspace = true, features = ["mdbx", "op"] }
reth-db-api.workspace = true
reth-db-common.workspace = true
reth-downloaders.workspace = true
reth-provider.workspace = true
reth-prune.workspace = true
reth-stages.workspace = true
reth-static-file.workspace = true
reth-execution-types.workspace = true
reth-node-core.workspace = true
reth-optimism-node.workspace = true
reth-fs-util.workspace = true

# so jemalloc metrics can be included
reth-node-metrics.workspace = true

## optimism
reth-optimism-primitives.workspace = true
reth-optimism-chainspec = { workspace = true, features = ["superchain-configs"] }
reth-optimism-consensus.workspace = true
reth-optimism-trie.workspace = true

reth-chainspec.workspace = true
reth-node-events.workspace = true
reth-optimism-evm.workspace = true
reth-cli-runner.workspace = true
reth-node-builder = { workspace = true, features = ["op"] }
reth-tracing.workspace = true

# eth
alloy-eips.workspace = true
alloy-consensus.workspace = true
alloy-primitives.workspace = true
alloy-rlp.workspace = true

# misc
futures-util.workspace = true
derive_more.workspace = true
serde.workspace = true
clap = { workspace = true, features = ["derive", "env"] }

tokio = { workspace = true, features = ["sync", "macros", "time", "rt-multi-thread"] }
tokio-util = { workspace = true, features = ["codec"] }
tracing.workspace = true
eyre.workspace = true

# reth test-vectors
proptest = { workspace = true, optional = true }
op-alloy-consensus.workspace = true

[dev-dependencies]
tempfile.workspace = true
reth-stages = { workspace = true, features = ["test-utils"] }

[build-dependencies]
reth-optimism-chainspec = { workspace = true, features = ["std", "superchain-configs"] }

[features]
default = []

# Opentelemtry feature to activate metrics export
otlp = ["reth-tracing/otlp", "reth-node-core/otlp"]

asm-keccak = [
"alloy-primitives/asm-keccak",
"reth-node-core/asm-keccak",
"reth-optimism-node/asm-keccak",
]

# Jemalloc feature for vergen to generate correct env vars
jemalloc = ["reth-node-core/jemalloc", "reth-node-metrics/jemalloc"]

dev = ["dep:proptest", "reth-cli-commands/arbitrary"]

serde = [
"alloy-consensus/serde",
"alloy-eips/serde",
"alloy-primitives/serde",
"op-alloy-consensus/serde",
"reth-execution-types/serde",
"reth-optimism-primitives/serde",
"reth-primitives-traits/serde",
"reth-optimism-chainspec/serde",
]
145 changes: 145 additions & 0 deletions crates/optimism/cli/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use crate::{Cli, Commands};
use eyre::{eyre, Result};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_commands::launcher::Launcher;
use reth_cli_runner::CliRunner;
use reth_node_core::args::OtlpInitStatus;
use reth_node_metrics::recorder::install_prometheus_recorder;
use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_consensus::OpBeaconConsensus;
use reth_optimism_node::{OpExecutorProvider, OpNode};
use reth_rpc_server_types::RpcModuleValidator;
use reth_tracing::{FileWorkerGuard, Layers};
use std::{fmt, sync::Arc};
use tracing::{info, warn};

/// A wrapper around a parsed CLI that handles command execution.
#[derive(Debug)]
pub struct CliApp<Spec: ChainSpecParser, Ext: clap::Args + fmt::Debug, Rpc: RpcModuleValidator> {
cli: Cli<Spec, Ext, Rpc>,
runner: Option<CliRunner>,
layers: Option<Layers>,
guard: Option<FileWorkerGuard>,
}

impl<C, Ext, Rpc> CliApp<C, Ext, Rpc>
where
C: ChainSpecParser<ChainSpec = OpChainSpec>,
Ext: clap::Args + fmt::Debug,
Rpc: RpcModuleValidator,
{
pub(crate) fn new(cli: Cli<C, Ext, Rpc>) -> Self {
Self { cli, runner: None, layers: Some(Layers::new()), guard: None }
}

/// Sets the runner for the CLI commander.
///
/// This replaces any existing runner with the provided one.
pub fn set_runner(&mut self, runner: CliRunner) {
self.runner = Some(runner);
}

/// Access to tracing layers.
///
/// Returns a mutable reference to the tracing layers, or error
/// if tracing initialized and layers have detached already.
pub fn access_tracing_layers(&mut self) -> Result<&mut Layers> {
self.layers.as_mut().ok_or_else(|| eyre!("Tracing already initialized"))
}

/// Execute the configured cli command.
///
/// This accepts a closure that is used to launch the node via the
/// [`NodeCommand`](reth_cli_commands::node::NodeCommand).
pub fn run(mut self, launcher: impl Launcher<C, Ext>) -> Result<()> {
let runner = match self.runner.take() {
Some(runner) => runner,
None => CliRunner::try_default_runtime()?,
};

// add network name to logs dir
// Add network name if available to the logs dir
if let Some(chain_spec) = self.cli.command.chain_spec() {
self.cli.logs.log_file_directory =
self.cli.logs.log_file_directory.join(chain_spec.chain.to_string());
}

self.init_tracing(&runner)?;

// Install the prometheus recorder to be sure to record all metrics
let _ = install_prometheus_recorder();

let components = |spec: Arc<OpChainSpec>| {
(OpExecutorProvider::optimism(spec.clone()), Arc::new(OpBeaconConsensus::new(spec)))
};

match self.cli.command {
Commands::Node(command) => {
// Validate RPC modules using the configured validator
if let Some(http_api) = &command.rpc.http_api {
Rpc::validate_selection(http_api, "http.api").map_err(|e| eyre!("{e}"))?;
}
if let Some(ws_api) = &command.rpc.ws_api {
Rpc::validate_selection(ws_api, "ws.api").map_err(|e| eyre!("{e}"))?;
}

runner.run_command_until_exit(|ctx| command.execute(ctx, launcher))
}
Commands::Init(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
Commands::InitState(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
Commands::ImportOp(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
Commands::ImportReceiptsOp(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
Commands::Db(command) => {
runner.run_blocking_command_until_exit(|ctx| command.execute::<OpNode>(ctx))
}
Commands::Stage(command) => {
runner.run_command_until_exit(|ctx| command.execute::<OpNode, _>(ctx, components))
}
Commands::P2P(command) => runner.run_until_ctrl_c(command.execute::<OpNode>()),
Commands::Config(command) => runner.run_until_ctrl_c(command.execute()),
Commands::Prune(command) => runner.run_until_ctrl_c(command.execute::<OpNode>()),
#[cfg(feature = "dev")]
Commands::TestVectors(command) => runner.run_until_ctrl_c(command.execute()),
Commands::ReExecute(command) => {
runner.run_until_ctrl_c(command.execute::<OpNode>(components))
}
Commands::OpProofs(command) => {
runner.run_blocking_until_ctrl_c(command.execute::<OpNode>())
}
}
}

/// Initializes tracing with the configured options.
///
/// If file logging is enabled, this function stores guard to the struct.
/// For gRPC OTLP, it requires tokio runtime context.
pub fn init_tracing(&mut self, runner: &CliRunner) -> Result<()> {
if self.guard.is_none() {
let mut layers = self.layers.take().unwrap_or_default();

let otlp_status = runner.block_on(self.cli.traces.init_otlp_tracing(&mut layers))?;

self.guard = self.cli.logs.init_tracing_with_layers(layers)?;
info!(target: "reth::cli", "Initialized tracing, debug log directory: {}", self.cli.logs.log_file_directory);
match otlp_status {
OtlpInitStatus::Started(endpoint) => {
info!(target: "reth::cli", "Started OTLP {:?} tracing export to {endpoint}", self.cli.traces.protocol);
}
OtlpInitStatus::NoFeature => {
warn!(target: "reth::cli", "Provided OTLP tracing arguments do not have effect, compile with the `otlp` feature")
}
OtlpInitStatus::Disabled => {}
}
}
Ok(())
}
}
Loading
Loading