From 58f386b614eb07910620b2724904de37459c9348 Mon Sep 17 00:00:00 2001 From: 7suyash7 Date: Sun, 22 Dec 2024 16:47:15 +0530 Subject: [PATCH] feat: add CLI flag support to builder config Signed-off-by: 7suyash7 --- .../rbuilder/src/live_builder/base_config.rs | 95 +++++++++++++++- crates/rbuilder/src/live_builder/cli.rs | 102 +++++++++++++++++- crates/rbuilder/src/live_builder/config.rs | 97 ++++++++++++++++- 3 files changed, 288 insertions(+), 6 deletions(-) diff --git a/crates/rbuilder/src/live_builder/base_config.rs b/crates/rbuilder/src/live_builder/base_config.rs index 21a3b8e8..835c4b19 100644 --- a/crates/rbuilder/src/live_builder/base_config.rs +++ b/crates/rbuilder/src/live_builder/base_config.rs @@ -36,7 +36,11 @@ use std::{ use tokio::sync::mpsc; use tracing::{error, warn}; -use super::SlotSource; +use super::{ + cli::{BaseCliArgs, L1CliArgs}, + config::L1Config, + SlotSource, +}; /// Prefix for env variables in config const ENV_PREFIX: &str = "env:"; @@ -469,6 +473,95 @@ impl Default for BaseConfig { } } +pub trait MergeFromCli { + fn merge(&mut self, cli: &T); +} + +impl MergeFromCli for BaseConfig { + fn merge(&mut self, cli: &BaseCliArgs) { + if let Some(log_json) = cli.log_json { + self.log_json = log_json; + } + if let Some(log_level) = &cli.log_level { + self.log_level = EnvOrValue(log_level.clone(), std::marker::PhantomData); + } + if let Some(port) = cli.full_telemetry_server_port { + self.full_telemetry_server_port = port; + } + if let Some(ip) = &cli.full_telemetry_server_ip { + self.full_telemetry_server_ip = Some(ip.clone()); + } + if let Some(port) = cli.redacted_telemetry_server_port { + self.redacted_telemetry_server_port = port; + } + if let Some(ip) = &cli.redacted_telemetry_server_ip { + self.redacted_telemetry_server_ip = Some(ip.clone()); + } + if let Some(log_color) = cli.log_color { + self.log_color = log_color; + } + if let Some(enable_dynamic) = cli.log_enable_dynamic { + self.log_enable_dynamic = enable_dynamic; + } + if let Some(path) = &cli.error_storage_path { + self.error_storage_path = Some(path.clone()); + } + if let Some(db) = &cli.flashbots_db { + self.flashbots_db = Some(EnvOrValue(db.clone(), std::marker::PhantomData)); + } + if let Some(port) = cli.jsonrpc_server_port { + self.jsonrpc_server_port = port; + } + if let Some(ip) = &cli.jsonrpc_server_ip { + self.jsonrpc_server_ip = Some(ip.clone()); + } + if let Some(ignore) = cli.ignore_cancellable_orders { + self.ignore_cancellable_orders = ignore; + } + if let Some(ignore) = cli.ignore_blobs { + self.ignore_blobs = ignore; + } + if let Some(chain) = &cli.chain { + self.chain = chain.clone(); + } + if let Some(dir) = &cli.reth_datadir { + self.reth_datadir = Some(dir.clone()); + } + } +} + +impl MergeFromCli for L1Config { + fn merge(&mut self, cli: &L1CliArgs) { + if let Some(dry_run) = cli.dry_run { + self.dry_run = dry_run; + } + if let Some(urls) = &cli.dry_run_validation_url { + self.dry_run_validation_url = urls.clone(); + } + if let Some(enabled) = cli.optimistic_enabled { + self.optimistic_enabled = enabled; + } + if let Some(val) = &cli.optimistic_max_bid_value_eth { + self.optimistic_max_bid_value_eth = val.clone(); + } + if let Some(validate) = cli.optimistic_prevalidate_optimistic_blocks { + self.optimistic_prevalidate_optimistic_blocks = validate; + } + if let Some(seals) = cli.max_concurrent_seals { + self.max_concurrent_seals = seals; + } + if let Some(urls) = &cli.cl_node_url { + self.cl_node_url = urls + .iter() + .map(|u| EnvOrValue(u.clone(), std::marker::PhantomData)) + .collect(); + } + if let Some(version) = &cli.genesis_fork_version { + self.genesis_fork_version = Some(version.clone()); + } + } +} + /// Open reth db and DB should be opened once per process but it can be cloned and moved to different threads. pub fn create_provider_factory( reth_datadir: Option<&Path>, diff --git a/crates/rbuilder/src/live_builder/cli.rs b/crates/rbuilder/src/live_builder/cli.rs index 9763b438..a5a200c3 100644 --- a/crates/rbuilder/src/live_builder/cli.rs +++ b/crates/rbuilder/src/live_builder/cli.rs @@ -19,7 +19,10 @@ use crate::{ utils::build_info::Version, }; -use super::{base_config::BaseConfig, LiveBuilder}; +use super::{ + base_config::{BaseConfig, MergeFromCli}, + LiveBuilder, +}; #[derive(Parser, Debug)] enum Cli { @@ -40,6 +43,93 @@ enum Cli { struct RunCmd { #[clap(env = "RBUILDER_CONFIG", help = "Config file path")] config: PathBuf, + + #[command(flatten)] + base: BaseCliArgs, + + #[command(flatten)] + l1: L1CliArgs, +} + +#[derive(Parser, Debug, Default)] +pub struct BaseCliArgs { + #[arg(long, help = "Enable JSON logging format")] + pub log_json: Option, + + #[arg(long, help = "Log level configuration string")] + pub log_level: Option, + + #[arg(long, help = "Port for full telemetry server")] + pub full_telemetry_server_port: Option, + + #[arg(long, help = "IP address for full telemetry server")] + pub full_telemetry_server_ip: Option, + + #[arg(long, help = "Port for redacted telemetry server")] + pub redacted_telemetry_server_port: Option, + + #[arg(long, help = "IP address for redacted telemetry server")] + pub redacted_telemetry_server_ip: Option, + + #[arg(long, help = "Enable colored log output")] + pub log_color: Option, + + #[arg(long, help = "Enable dynamic logging to file")] + pub log_enable_dynamic: Option, + + #[arg(long, help = "Path to store error logs")] + pub error_storage_path: Option, + + #[arg(long, help = "Coinbase signer secret key")] + pub coinbase_secret_key: Option, + + #[arg(long, help = "Flashbots database URL")] + pub flashbots_db: Option, + + #[arg(long, help = "JSON-RPC server port")] + pub jsonrpc_server_port: Option, + + #[arg(long, help = "JSON-RPC server IP address")] + pub jsonrpc_server_ip: Option, + + #[arg(long, help = "Ignore cancellable orders")] + pub ignore_cancellable_orders: Option, + + #[arg(long, help = "Ignore blob transactions")] + pub ignore_blobs: Option, + + #[arg(long, help = "Chain identifier (mainnet/goerli/etc)")] + pub chain: Option, + + #[arg(long, help = "Path to reth data directory")] + pub reth_datadir: Option, +} + +#[derive(Parser, Debug, Default)] +pub struct L1CliArgs { + #[arg(long, help = "Enable dry run mode")] + pub dry_run: Option, + + #[arg(long, help = "URLs for dry run validation")] + pub dry_run_validation_url: Option>, + + #[arg(long, help = "Enable optimistic submission mode")] + pub optimistic_enabled: Option, + + #[arg(long, help = "Maximum bid value (ETH) for optimistic mode")] + pub optimistic_max_bid_value_eth: Option, + + #[arg(long, help = "Pre-validate optimistic blocks")] + pub optimistic_prevalidate_optimistic_blocks: Option, + + #[arg(long, help = "Maximum concurrent block sealing operations")] + pub max_concurrent_seals: Option, + + #[arg(long, help = "Consensus layer node URLs")] + pub cl_node_url: Option>, + + #[arg(long, help = "Genesis fork version override")] + pub genesis_fork_version: Option, } /// Basic stuff needed to call cli::run @@ -84,13 +174,15 @@ pub trait LiveBuilderConfig: Debug + DeserializeOwned + Sync { /// on_run func that will be called on command Cli::Run just before running pub async fn run(print_version_info: fn(), on_run: Option) -> eyre::Result<()> where - ConfigType: LiveBuilderConfig, + ConfigType: LiveBuilderConfig + MergeFromCli + MergeFromCli, { let cli = Cli::parse(); let cli = match cli { Cli::Run(cli) => cli, Cli::Config(cli) => { - let config: ConfigType = load_config_toml_and_env(cli.config)?; + let mut config: ConfigType = load_config_toml_and_env(cli.config)?; + config.merge(&cli.base); + config.merge(&cli.l1); println!("{:#?}", config); return Ok(()); } @@ -108,7 +200,9 @@ where } }; - let config: ConfigType = load_config_toml_and_env(cli.config)?; + let mut config: ConfigType = load_config_toml_and_env(cli.config)?; + config.merge(&cli.base); + config.merge(&cli.l1); config.base_config().setup_tracing_subscriber()?; let cancel = CancellationToken::new(); diff --git a/crates/rbuilder/src/live_builder/config.rs b/crates/rbuilder/src/live_builder/config.rs index bf17e5c0..0fccd4ec 100644 --- a/crates/rbuilder/src/live_builder/config.rs +++ b/crates/rbuilder/src/live_builder/config.rs @@ -2,7 +2,7 @@ //! //! use super::{ - base_config::BaseConfig, + base_config::{BaseConfig, MergeFromCli}, block_output::{ bid_observer::{BidObserver, NullBidObserver}, bid_value_source::null_bid_value_source::NullBidValueSource, @@ -13,6 +13,7 @@ use super::{ block_sealing_bidder_factory::BlockSealingBidderFactory, relay_submit::{RelaySubmitSinkFactory, SubmissionConfig}, }, + cli::{BaseCliArgs, L1CliArgs}, }; use crate::{ beacon_api_client::Client, @@ -464,6 +465,18 @@ impl Default for Config { } } +impl MergeFromCli for Config { + fn merge(&mut self, cli: &BaseCliArgs) { + self.base_config.merge(cli); + } +} + +impl MergeFromCli for Config { + fn merge(&mut self, cli: &L1CliArgs) { + self.l1_config.merge(cli); + } +} + /// Open reth db and DB should be opened once per process but it can be cloned and moved to different threads. pub fn create_provider_factory( reth_datadir: Option<&Path>, @@ -715,4 +728,86 @@ mod test { fixed_bytes!("00000001aaf2630a2874a74199f4b5d11a7d6377f363a236271bff4bf8eb4ab3") ); } + + #[test] + fn test_cli_overrides_config() { + let config_str = r#" + log_json = false + chain = "mainnet" + dry_run = false + "#; + let mut config: Config = toml::from_str(config_str).unwrap(); + + let cli_args = BaseCliArgs { + log_json: Some(true), + chain: Some("goerli".to_string()), + ..Default::default() + }; + + let l1_args = L1CliArgs { + dry_run: Some(true), + ..Default::default() + }; + + config.merge(&cli_args); + config.merge(&l1_args); + + assert_eq!(config.base_config.log_json, true); + assert_eq!(config.base_config.chain, "goerli"); + assert_eq!(config.l1_config.dry_run, true); + } + + #[test] + fn test_multiple_cli_overrides() { + let config_str = r#" + log_json = false + full_telemetry_server_port = 6060 + redacted_telemetry_server_port = 6070 + dry_run_validation_url = ["http://localhost:8545"] + "#; + let mut config: Config = toml::from_str(config_str).unwrap(); + + let cli_args = BaseCliArgs { + log_json: Some(true), + full_telemetry_server_port: Some(7000), + redacted_telemetry_server_port: Some(7001), + ..Default::default() + }; + + let l1_args = L1CliArgs { + dry_run_validation_url: Some(vec!["http://localhost:9545".to_string()]), + ..Default::default() + }; + + config.merge(&cli_args); + config.merge(&l1_args); + + assert_eq!(config.base_config.log_json, true); + assert_eq!(config.base_config.full_telemetry_server_port, 7000); + assert_eq!(config.base_config.redacted_telemetry_server_port, 7001); + assert_eq!( + config.l1_config.dry_run_validation_url, + vec!["http://localhost:9545"] + ); + } + + #[test] + fn test_unset_cli_values_preserve_config() { + let config_str = r#" + chain = "mainnet" + max_concurrent_seals = 4 + optimistic_enabled = true + "#; + let mut config: Config = toml::from_str(config_str).unwrap(); + + let cli_args = BaseCliArgs::default(); + let l1_args = L1CliArgs::default(); + + config.merge(&cli_args); + config.merge(&l1_args); + + assert_eq!(config.base_config.chain, "mainnet"); + assert_eq!(config.l1_config.max_concurrent_seals, 4); + assert_eq!(config.l1_config.optimistic_enabled, true); + } }