Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add CLI flag support to builder config #315

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
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
95 changes: 94 additions & 1 deletion crates/rbuilder/src/live_builder/base_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:";
Expand Down Expand Up @@ -469,6 +473,95 @@ impl Default for BaseConfig {
}
}

pub trait MergeFromCli<T> {
fn merge(&mut self, cli: &T);
}

impl MergeFromCli<BaseCliArgs> 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<L1CliArgs> 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>,
Expand Down
102 changes: 98 additions & 4 deletions crates/rbuilder/src/live_builder/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<bool>,

#[arg(long, help = "Log level configuration string")]
pub log_level: Option<String>,

#[arg(long, help = "Port for full telemetry server")]
pub full_telemetry_server_port: Option<u16>,

#[arg(long, help = "IP address for full telemetry server")]
pub full_telemetry_server_ip: Option<String>,

#[arg(long, help = "Port for redacted telemetry server")]
pub redacted_telemetry_server_port: Option<u16>,

#[arg(long, help = "IP address for redacted telemetry server")]
pub redacted_telemetry_server_ip: Option<String>,

#[arg(long, help = "Enable colored log output")]
pub log_color: Option<bool>,

#[arg(long, help = "Enable dynamic logging to file")]
pub log_enable_dynamic: Option<bool>,

#[arg(long, help = "Path to store error logs")]
pub error_storage_path: Option<PathBuf>,

#[arg(long, help = "Coinbase signer secret key")]
pub coinbase_secret_key: Option<String>,

#[arg(long, help = "Flashbots database URL")]
pub flashbots_db: Option<String>,

#[arg(long, help = "JSON-RPC server port")]
pub jsonrpc_server_port: Option<u16>,

#[arg(long, help = "JSON-RPC server IP address")]
pub jsonrpc_server_ip: Option<String>,

#[arg(long, help = "Ignore cancellable orders")]
pub ignore_cancellable_orders: Option<bool>,

#[arg(long, help = "Ignore blob transactions")]
pub ignore_blobs: Option<bool>,

#[arg(long, help = "Chain identifier (mainnet/goerli/etc)")]
pub chain: Option<String>,

#[arg(long, help = "Path to reth data directory")]
pub reth_datadir: Option<PathBuf>,
}

#[derive(Parser, Debug, Default)]
pub struct L1CliArgs {
#[arg(long, help = "Enable dry run mode")]
pub dry_run: Option<bool>,

#[arg(long, help = "URLs for dry run validation")]
pub dry_run_validation_url: Option<Vec<String>>,

#[arg(long, help = "Enable optimistic submission mode")]
pub optimistic_enabled: Option<bool>,

#[arg(long, help = "Maximum bid value (ETH) for optimistic mode")]
pub optimistic_max_bid_value_eth: Option<String>,

#[arg(long, help = "Pre-validate optimistic blocks")]
pub optimistic_prevalidate_optimistic_blocks: Option<bool>,

#[arg(long, help = "Maximum concurrent block sealing operations")]
pub max_concurrent_seals: Option<u64>,

#[arg(long, help = "Consensus layer node URLs")]
pub cl_node_url: Option<Vec<String>>,

#[arg(long, help = "Genesis fork version override")]
pub genesis_fork_version: Option<String>,
}

/// Basic stuff needed to call cli::run
Expand Down Expand Up @@ -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<ConfigType>(print_version_info: fn(), on_run: Option<fn()>) -> eyre::Result<()>
where
ConfigType: LiveBuilderConfig,
ConfigType: LiveBuilderConfig + MergeFromCli<BaseCliArgs> + MergeFromCli<L1CliArgs>,
{
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(());
}
Expand All @@ -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();
Expand Down
97 changes: 96 additions & 1 deletion crates/rbuilder/src/live_builder/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -13,6 +13,7 @@ use super::{
block_sealing_bidder_factory::BlockSealingBidderFactory,
relay_submit::{RelaySubmitSinkFactory, SubmissionConfig},
},
cli::{BaseCliArgs, L1CliArgs},
};
use crate::{
beacon_api_client::Client,
Expand Down Expand Up @@ -464,6 +465,18 @@ impl Default for Config {
}
}

impl MergeFromCli<BaseCliArgs> for Config {
fn merge(&mut self, cli: &BaseCliArgs) {
self.base_config.merge(cli);
}
}

impl MergeFromCli<L1CliArgs> 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>,
Expand Down Expand Up @@ -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);
}
}