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
1,861 changes: 1,435 additions & 426 deletions Cargo.lock

Large diffs are not rendered by default.

42 changes: 32 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ codegen-units = 1

[workspace.dependencies]
# Shared
base-access-lists = { path = "crates/shared/access-lists" }
base-bundles = { path = "crates/shared/bundles" }
base-cli-utils = { path = "crates/shared/cli-utils" }
base-flashtypes = { path = "crates/shared/flashtypes" }
base-primitives = { path = "crates/shared/primitives" }
base-access-lists = { path = "crates/shared/access-lists" }
base-reth-rpc-types = { path = "crates/shared/reth-rpc-types" }
base-jwt = { path = "crates/shared/jwt" }
# Client
Expand All @@ -67,6 +67,20 @@ base-metering = { path = "crates/client/metering" }
base-txpool = { path = "crates/client/txpool" }
base-flashblocks = { path = "crates/client/flashblocks" }

# Kona
kona-rpc = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-disc = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-engine = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-derive = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-gossip = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-genesis = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-registry = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-node-service = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-providers-alloy = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-cli = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-peers = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }
kona-sources = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }

# reth
reth-ipc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
Expand Down Expand Up @@ -188,21 +202,23 @@ alloy-rpc-types-beacon = { version = "1.0.41", features = ["ssz"] }
# op-alloy
op-alloy-flz = { version = "0.13.1", default-features = false }
op-alloy-network = { version = "0.22.0", default-features = false }
op-alloy-provider = { version = "0.22.0", default-features = false }
op-alloy-rpc-types = { version = "0.22.0", default-features = false }
op-alloy-consensus = { version = "0.22.0", default-features = false }
op-alloy-rpc-jsonrpsee = { version = "0.22.0", default-features = false }
op-alloy-rpc-types-engine = { version = "0.22.0", default-features = false }
op-alloy-provider = { version = "0.22.0", default-features = false }
alloy-op-evm = { version = "0.23.3", default-features = false }
alloy-op-hardforks = "0.4.4"

# rollup-boost
rollup-boost = { git = "https://github.com/flashbots/rollup-boost", tag = "v0.7.11" }
# rollup-boost version that matches kona's dependency (used for consensus binary)
rollup-boost-kona = { package = "rollup-boost", git = "https://github.com/flashbots/rollup-boost.git", rev = "7fda98f" }


# op-revm
op-revm = { version = "12.0.2", default-features = false }

# kona
kona-registry = "0.4.5"
kona-engine = { git = "https://github.com/op-rs/kona", rev = "24e7e2658e09ac00c8e6cbb48bebe6d10f8fb69d" }

# tokio
tokio = "1.48.0"
tokio-stream = "0.1.17"
Expand All @@ -212,7 +228,6 @@ tokio-tungstenite = { version = "0.28.0", features = ["native-tls"] }
futures = "0.3.31"
reqwest = "0.12.25"
futures-util = "0.3.31"
backon = "1.5"

# rpc
jsonrpsee = "0.26.0"
Expand All @@ -223,6 +238,11 @@ vergen = "9.0.6"
vergen-git2 = "1.0.7"
clap = { version = "4.5.53", features = ["derive", "env", "string"] }

# Tracing
tracing = "0.1.43"
tracing-appender = "0.2.4"
tracing-subscriber = "0.3.22"

# Metrics
metrics = { version = "0.24.3", default-features = false }
prometheus = { version = "0.14.0", default-features = false }
Expand All @@ -233,6 +253,8 @@ metrics-exporter-prometheus = { version = "0.18.1", default-features = false }
url = "2.5.7"
lru = "0.16.3"
rand = "0.9.2"
strum = "0.27.2"
backon = "1.6.0"
uuid = "1.19.0"
time = { version = "0.3.44", features = ["macros", "formatting", "parsing"] }
rayon = "1.11"
Expand All @@ -245,21 +267,21 @@ rstest = "0.26.1"
serde = "1.0.228"
rustls = "0.23.35"
httpmock = "0.8.2"
tracing = "0.1.43"
arc-swap = "1.7.1"
once_cell = "1.21.3"
itertools = "0.14.0"
derive_more = "2.1.0"
serde_json = "1.0.145"
metrics-derive = "0.1.0"
tracing-subscriber = "0.3.22"
tracing-appender = "0.2"
thiserror = "2.0"
async-trait = "0.1.83"
parking_lot = "0.12.3"
auto_impl = "1.2.0"
serde_with = "3.8.1"
secp256k1 = "0.30"
libp2p = "0.56.0"
libp2p-identity = "0.2.12"
discv5 = "0.10"
either = { version = "1.15.0", default-features = false }
tokio-util = { version = "0.7.4", features = ["codec"] }
warp = "0.3.7"
Expand Down
24 changes: 24 additions & 0 deletions bin/consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,34 @@ workspace = true
[dependencies]
# Workspace
base-cli-utils.workspace = true
base-client-cli.workspace = true
base-jwt = { workspace = true, features = ["engine-validation"] }

# Kona
kona-genesis.workspace = true
kona-registry.workspace = true
kona-disc = { workspace = true, features = ["metrics"] }
kona-derive = { workspace = true, features = ["metrics"] }
kona-engine = { workspace = true, features = ["metrics"] }
kona-gossip = { workspace = true, features = ["metrics"] }
kona-node-service = { workspace = true, features = ["metrics"] }
kona-providers-alloy = { workspace = true, features = ["metrics"] }

# Reth (for serde feature on transitive dependencies)
reth-optimism-node.workspace = true

# CLI
clap.workspace = true
eyre.workspace = true
strum.workspace = true
serde_json.workspace = true

# Tracing
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["fmt", "env-filter"] }

# Metrics
metrics.workspace = true


[build-dependencies]
Expand Down
221 changes: 214 additions & 7 deletions bin/consensus/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
//! Contains the CLI entry point for the Base consensus binary.

use base_cli_utils::GlobalArgs;
use std::{fs::File, path::PathBuf, sync::Arc, time::Duration};

use base_cli_utils::{CliStyles, GlobalArgs, LogConfig, RuntimeManager};
use base_client_cli::{
BuilderClientArgs, JwtSecret, L1ClientArgs, L2ClientArgs, P2PArgs, RollupBoostFlags, RpcArgs,
SequencerArgs,
};
use base_jwt::JwtValidator;
use clap::Parser;
use eyre::bail;
use kona_genesis::{L1ChainConfig, RollupConfig};
use kona_node_service::{EngineConfig, L1ConfigBuilder, NodeMode, RollupNodeBuilder};
use kona_registry::{L1Config, scr_rollup_config_by_alloy_ident};
use serde_json::from_reader;
use strum::IntoEnumIterator;
use tracing::{debug, error, info};

use crate::version;

Expand All @@ -11,24 +25,217 @@ use crate::version;
author,
version = version::SHORT_VERSION,
long_version = version::LONG_VERSION,
styles = CliStyles::init(),
about,
long_about = None
)]
pub struct Cli {
/// Global arguments for the Base Consensus CLI.
#[command(flatten)]
pub global: GlobalArgs,
/// The mode to run the node in.
#[arg(
long = "mode",
default_value_t = NodeMode::Validator,
env = "KONA_NODE_MODE",
help = format!(
"The mode to run the node in. Supported modes are: {}",
NodeMode::iter()
.map(|mode| format!("\"{}\"", mode.to_string()))
.collect::<Vec<_>>()
.join(", ")
)
)]
pub node_mode: NodeMode,

/// L1 RPC CLI arguments.
#[clap(flatten)]
pub l1_rpc_args: L1ClientArgs,

/// L2 engine CLI arguments.
#[clap(flatten)]
pub l2_client_args: L2ClientArgs,

/// Optional block builder client.
#[clap(flatten)]
pub builder_client_args: BuilderClientArgs,

/// Path to a custom L2 rollup configuration file
/// (overrides the default rollup configuration from the registry)
#[arg(long, visible_alias = "rollup-cfg", env = "KONA_NODE_ROLLUP_CONFIG")]
pub l2_config_file: Option<PathBuf>,
/// Path to a custom L1 rollup configuration file
/// (overrides the default rollup configuration from the registry)
#[arg(long, visible_alias = "rollup-l1-cfg", env = "KONA_NODE_L1_CHAIN_CONFIG")]
pub l1_config_file: Option<PathBuf>,
/// P2P CLI arguments.
#[command(flatten)]
pub p2p_flags: P2PArgs,
/// RPC CLI arguments.
#[command(flatten)]
pub rpc_flags: RpcArgs,
/// SEQUENCER CLI arguments.
#[command(flatten)]
pub sequencer_flags: SequencerArgs,
/// Rollup Boost CLI arguments.
#[command(flatten)]
pub rollup_boost_flags: RollupBoostFlags,
}

impl Cli {
/// Parse the CLI arguments.
pub fn parse() -> Self {
<Self as Parser>::parse()
/// Runs the CLI.
pub fn run(self) -> eyre::Result<()> {
// Initialize telemetry - allow subcommands to customize the filter.
Self::init_logs(&self.global)?;

// Initialize unified metrics
self.global.metrics.init_with(|| {
kona_gossip::Metrics::init();
kona_disc::Metrics::init();
kona_engine::Metrics::init();
kona_node_service::Metrics::init();
kona_derive::Metrics::init();
kona_providers_alloy::Metrics::init();
version::VersionInfo::from_build().register_version_metrics();
})?;

// Run the subcommand.
RuntimeManager::run_until_ctrl_c(self.exec(&self.global))
}

/// Run the CLI.
pub fn run(self) -> eyre::Result<()> {
// TODO: Implement the CLI logic
/// Run the Node subcommand.
pub async fn exec(&self, args: &GlobalArgs) -> eyre::Result<()> {
let cfg = self.get_l2_config(args)?;

info!(
target: "rollup_node",
chain_id = cfg.l2_chain_id.id(),
"Starting rollup node services"
);
for hf in cfg.hardforks.to_string().lines() {
info!(target: "rollup_node", "{hf}");
}

let l1_config = L1ConfigBuilder {
chain_config: self.get_l1_config(cfg.l1_chain_id)?,
trust_rpc: self.l1_rpc_args.l1_trust_rpc,
beacon: self.l1_rpc_args.l1_beacon.clone(),
rpc_url: self.l1_rpc_args.l1_eth_rpc.clone(),
slot_duration_override: self.l1_rpc_args.l1_slot_duration_override,
};

// TODO: If metrics are enabled, initialize the global cli metrics.
// args.metrics.enabled.then(|| init_rollup_config_metrics(&cfg));

let jwt_secret = self.validate_jwt().await?;

self.p2p_flags.check_ports()?;
let genesis_signer = args.genesis_signer().ok();
let p2p_config = self
.p2p_flags
.clone()
.config(
&cfg,
args.l2_chain_id.into(),
Some(self.l1_rpc_args.l1_eth_rpc.clone()),
genesis_signer,
)
.await?;
let rpc_config = self.rpc_flags.clone().into();

let engine_config = EngineConfig {
config: Arc::new(cfg.clone()),
builder_url: self.builder_client_args.l2_builder_rpc.clone(),
builder_jwt_secret: self
.builder_client_args
.jwt_secret()
.map_err(|e| eyre::eyre!(e))?,
builder_timeout: Duration::from_millis(self.builder_client_args.builder_timeout),
l2_url: self.l2_client_args.l2_engine_rpc.clone(),
l2_jwt_secret: jwt_secret,
l2_timeout: Duration::from_millis(self.l2_client_args.l2_engine_timeout),
l1_url: self.l1_rpc_args.l1_eth_rpc.clone(),
mode: self.node_mode,
rollup_boost: self.rollup_boost_flags.clone().as_rollup_boost_args(),
};

RollupNodeBuilder::new(
cfg,
l1_config,
self.l2_client_args.l2_trust_rpc,
engine_config,
p2p_config,
rpc_config,
)
.with_sequencer_config(self.sequencer_flags.config())
.build()
.start()
.await
.map_err(|e| {
error!(target: "rollup_node", "Failed to start rollup node service: {e}");
eyre::eyre!("{e}")
})?;

Ok(())
}

/// Validate the jwt secret if specified by exchanging capabilities with the engine.
/// Since the engine client will fail if the jwt token is invalid, this allows to ensure
/// that the jwt token passed as a cli arg is correct.
pub async fn validate_jwt(&self) -> eyre::Result<JwtSecret> {
let jwt_secret = self.l2_client_args.jwt_secret().map_err(|e| eyre::eyre!(e))?;
let validator = JwtValidator::new(jwt_secret);
validator
.validate_with_engine(self.l2_client_args.l2_engine_rpc.clone())
.await
.map_err(|e| eyre::eyre!(e))
}

/// Get the L1 config, either from a file or the known chains.
pub fn get_l1_config(&self, l1_chain_id: u64) -> eyre::Result<L1ChainConfig> {
match &self.l1_config_file {
Some(path) => {
debug!("Loading l1 config from file: {:?}", path);
let file = File::open(path)
.map_err(|e| eyre::eyre!("Failed to open l1 config file: {e}"))?;
from_reader(file).map_err(|e| eyre::eyre!("Failed to parse l1 config: {e}"))
}
None => {
debug!("Loading l1 config from known chains");
let cfg = L1Config::get_l1_genesis(l1_chain_id).map_err(|e| {
eyre::eyre!("Failed to find l1 config for chain ID {l1_chain_id}: {e}")
})?;
Ok(cfg.into())
}
}
}

/// Get the L2 rollup config, either from a file or the superchain registry.
pub fn get_l2_config(&self, args: &GlobalArgs) -> eyre::Result<RollupConfig> {
match &self.l2_config_file {
Some(path) => {
debug!("Loading l2 config from file: {:?}", path);
let file = File::open(path)
.map_err(|e| eyre::eyre!("Failed to open l2 config file: {e}"))?;
from_reader(file).map_err(|e| eyre::eyre!("Failed to parse l2 config: {e}"))
}
None => {
debug!("Loading l2 config from superchain registry");
let Some(cfg) = scr_rollup_config_by_alloy_ident(&args.l2_chain_id) else {
bail!("Failed to find l2 config for chain ID {}", args.l2_chain_id);
};
Ok(cfg.clone())
}
}
}

/// Initializes the logging system based on global arguments.
pub fn init_logs(args: &GlobalArgs) -> eyre::Result<()> {
// Filter out discovery warnings since they're very very noisy.
let filter = tracing_subscriber::EnvFilter::from_default_env()
.add_directive("discv5=error".parse()?);

let config: LogConfig = args.logging.clone().into();
config.init_tracing_subscriber_with_filter(filter)
}
}
Loading
Loading