diff --git a/book/src/help_vc.md b/book/src/help_vc.md index 2cfbfbc857..d4c6e03eb6 100644 --- a/book/src/help_vc.md +++ b/book/src/help_vc.md @@ -20,14 +20,15 @@ Options: Comma-separated list of beacon API topics to broadcast to all beacon nodes. Possible values are: none, attestations, blocks, subscriptions, sync-committee. Default (when flag is omitted) is to broadcast - subscriptions only. + subscriptions only. [possible values: none, attestations, blocks, + subscriptions, sync-committee] --builder-boost-factor Defines the boost factor, a percentage multiplier to apply to the builder's payload value when choosing between a builder payload header and payload from the local execution node. - --builder-registration-timestamp-override + --builder-registration-timestamp-override This flag takes a unix timestamp value that will be used to override - the timestamp used in the builder api registration + the timestamp used in the builder api registration. -d, --datadir Used to specify a custom root data directory for lighthouse keys and databases. Defaults to $HOME/.lighthouse/{network} where network is @@ -41,7 +42,7 @@ Options: The gas limit to be used in all builder proposals for all validators managed by this validator client. Note this will not necessarily be used if the gas limit set here moves too far from the previous block's - gas limit. [default: 30,000,000] + gas limit. [default: 30000000] --genesis-state-url A URL of a beacon-API compatible server from which to download the genesis state. Checkpoint sync server URLs can generally be used with @@ -68,7 +69,8 @@ Options: is supplied, the CORS allowed origin is set to the listen address of this server (e.g., http://localhost:5062). --http-port - Set the listen TCP port for the RESTful HTTP API server. + Set the listen TCP port for the RESTful HTTP API server. [default: + 5062] --log-format Specifies the log format used when emitting logs to the terminal. [possible values: JSON] @@ -92,6 +94,7 @@ Options: set to 0, background file logging is disabled. [default: 200] --metrics-address
Set the listen address for the Prometheus metrics HTTP server. + [default: 127.0.0.1] --metrics-allow-origin Set the value of the Access-Control-Allow-Origin response HTTP header. Use * to allow any origin (not recommended in production). If no value @@ -99,6 +102,7 @@ Options: this server (e.g., http://localhost:5064). --metrics-port Set the listen TCP port for the Prometheus metrics HTTP server. + [default: 5064] --monitoring-endpoint
Enables the monitoring service for sending system metrics to a remote endpoint. This can be used to monitor your setup on certain services @@ -109,7 +113,7 @@ Options: provide an untrusted URL. --monitoring-endpoint-period Defines how many seconds to wait between each message sent to the - monitoring-endpoint. Default: 60s + monitoring-endpoint. [default: 60] --network Name of the Eth2 chain Lighthouse will sync and follow. [possible values: mainnet, gnosis, chiado, sepolia, holesky] @@ -141,8 +145,8 @@ Options: each validator along with the common slashing protection database and the validator_definitions.yml --web3-signer-keep-alive-timeout - Keep-alive timeout for each web3signer connection. Set to 'null' to - never timeout [default: 20000] + Keep-alive timeout for each web3signer connection. Set to '0' to never + timeout. [default: 20000] --web3-signer-max-idle-connections Maximum number of idle connections to maintain per web3signer host. Default is unlimited. diff --git a/lighthouse/src/cli.rs b/lighthouse/src/cli.rs index 90d3e811eb..ed665d2a47 100644 --- a/lighthouse/src/cli.rs +++ b/lighthouse/src/cli.rs @@ -1,9 +1,12 @@ use clap::Parser; use database_manager::cli::DatabaseManager; use serde::{Deserialize, Serialize}; +use validator_client::cli::ValidatorClient; #[derive(Parser, Clone, Deserialize, Serialize, Debug)] pub enum LighthouseSubcommands { #[clap(name = "database_manager")] - DatabaseManager(DatabaseManager), + DatabaseManager(Box), + #[clap(name = "validator_client")] + ValidatorClient(Box), } diff --git a/lighthouse/src/main.rs b/lighthouse/src/main.rs index e33e4cb9b8..7e648d0932 100644 --- a/lighthouse/src/main.rs +++ b/lighthouse/src/main.rs @@ -395,10 +395,10 @@ fn main() { .action(ArgAction::HelpLong) .display_order(0) .help_heading(FLAG_HEADER) + .global(true) ) .subcommand(beacon_node::cli_app()) .subcommand(boot_node::cli_app()) - .subcommand(validator_client::cli_app()) .subcommand(account_manager::cli_app()) .subcommand(validator_manager::cli_app()); @@ -669,12 +669,49 @@ fn run( return Ok(()); } - if let Ok(LighthouseSubcommands::DatabaseManager(db_manager_config)) = - LighthouseSubcommands::from_arg_matches(matches) - { - info!(log, "Running database manager for {} network", network_name); - database_manager::run(matches, &db_manager_config, environment)?; - return Ok(()); + match LighthouseSubcommands::from_arg_matches(matches) { + Ok(LighthouseSubcommands::DatabaseManager(db_manager_config)) => { + info!(log, "Running database manager for {} network", network_name); + database_manager::run(matches, &db_manager_config, environment)?; + return Ok(()); + } + Ok(LighthouseSubcommands::ValidatorClient(validator_client_config)) => { + let context = environment.core_context(); + let log = context.log().clone(); + let executor = context.executor.clone(); + let config = validator_client::Config::from_cli( + matches, + &validator_client_config, + context.log(), + ) + .map_err(|e| format!("Unable to initialize validator config: {}", e))?; + // Dump configs if `dump-config` or `dump-chain-config` flags are set + clap_utils::check_dump_configs::<_, E>(matches, &config, &context.eth2_config.spec)?; + + let shutdown_flag = matches.get_flag("immediate-shutdown"); + if shutdown_flag { + info!(log, "Validator client immediate shutdown triggered."); + return Ok(()); + } + + executor.clone().spawn( + async move { + if let Err(e) = ProductionValidatorClient::new(context, config) + .and_then(|mut vc| async move { vc.start_service().await }) + .await + { + crit!(log, "Failed to start validator client"; "reason" => e); + // Ignore the error since it always occurs during normal operation when + // shutting down. + let _ = executor + .shutdown_sender() + .try_send(ShutdownReason::Failure("Failed to start validator client")); + } + }, + "validator_client", + ); + } + Err(_) => (), }; info!(log, "Lighthouse started"; "version" => VERSION); @@ -729,38 +766,9 @@ fn run( "beacon_node", ); } - Some(("validator_client", matches)) => { - let context = environment.core_context(); - let log = context.log().clone(); - let executor = context.executor.clone(); - let config = validator_client::Config::from_cli(matches, context.log()) - .map_err(|e| format!("Unable to initialize validator config: {}", e))?; - // Dump configs if `dump-config` or `dump-chain-config` flags are set - clap_utils::check_dump_configs::<_, E>(matches, &config, &context.eth2_config.spec)?; - - let shutdown_flag = matches.get_flag("immediate-shutdown"); - if shutdown_flag { - info!(log, "Validator client immediate shutdown triggered."); - return Ok(()); - } - - executor.clone().spawn( - async move { - if let Err(e) = ProductionValidatorClient::new(context, config) - .and_then(|mut vc| async move { vc.start_service().await }) - .await - { - crit!(log, "Failed to start validator client"; "reason" => e); - // Ignore the error since it always occurs during normal operation when - // shutting down. - let _ = executor - .shutdown_sender() - .try_send(ShutdownReason::Failure("Failed to start validator client")); - } - }, - "validator_client", - ); - } + // TODO(clap-derive) delete this once we've fully migrated to clap derive. + // Qt the moment this needs to exist so that we dont trigger a crit. + Some(("validator_client", _)) => (), _ => { crit!(log, "No subcommand supplied. See --help ."); return Err("No subcommand supplied.".into()); diff --git a/lighthouse/tests/validator_client.rs b/lighthouse/tests/validator_client.rs index 147a371f0e..40e98ecc73 100644 --- a/lighthouse/tests/validator_client.rs +++ b/lighthouse/tests/validator_client.rs @@ -379,6 +379,13 @@ fn metrics_port_flag() { .with_config(|config| assert_eq!(config.http_metrics.listen_port, 9090)); } #[test] +fn metrics_port_flag_default() { + CommandLineTest::new() + .flag("metrics", None) + .run() + .with_config(|config| assert_eq!(config.http_metrics.listen_port, 5064)); +} +#[test] fn metrics_allow_origin_flag() { CommandLineTest::new() .flag("metrics", None) @@ -430,7 +437,7 @@ fn no_doppelganger_protection_flag() { fn no_gas_limit_flag() { CommandLineTest::new() .run() - .with_config(|config| assert!(config.gas_limit.is_none())); + .with_config(|config| assert!(config.gas_limit == Some(30_000_000))); } #[test] fn gas_limit_flag() { @@ -532,7 +539,7 @@ fn broadcast_flag() { }); // Other valid variants CommandLineTest::new() - .flag("broadcast", Some("blocks, subscriptions")) + .flag("broadcast", Some("blocks,subscriptions")) .run() .with_config(|config| { assert_eq!( @@ -577,7 +584,7 @@ fn beacon_nodes_sync_tolerances_flag() { } #[test] -#[should_panic(expected = "Unknown API topic")] +#[should_panic(expected = "invalid value")] fn wrong_broadcast_flag() { CommandLineTest::new() .flag("broadcast", Some("foo, subscriptions")) diff --git a/validator_client/src/beacon_node_fallback.rs b/validator_client/src/beacon_node_fallback.rs index e5fe419983..3537bd9f68 100644 --- a/validator_client/src/beacon_node_fallback.rs +++ b/validator_client/src/beacon_node_fallback.rs @@ -8,6 +8,7 @@ use crate::beacon_node_health::{ }; use crate::check_synced::check_node_health; use crate::http_metrics::metrics::{inc_counter_vec, ENDPOINT_ERRORS, ENDPOINT_REQUESTS}; +use clap::ValueEnum; use environment::RuntimeContext; use eth2::BeaconNodeHttpClient; use futures::future; @@ -21,7 +22,8 @@ use std::future::Future; use std::marker::PhantomData; use std::sync::Arc; use std::time::{Duration, Instant}; -use strum::{EnumString, EnumVariantNames}; +use std::vec::Vec; +use strum::EnumVariantNames; use tokio::{sync::RwLock, time::sleep}; use types::{ChainSpec, Config as ConfigSpec, EthSpec, Slot}; @@ -719,9 +721,10 @@ async fn sort_nodes_by_health(nodes: &mut Vec } /// Serves as a cue for `BeaconNodeFallback` to tell which requests need to be broadcasted. -#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, EnumString, EnumVariantNames)] +#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize, EnumVariantNames, ValueEnum)] #[strum(serialize_all = "kebab-case")] pub enum ApiTopic { + None, Attestations, Blocks, Subscriptions, @@ -741,7 +744,6 @@ mod tests { use crate::beacon_node_health::BeaconNodeHealthTier; use crate::SensitiveUrl; use eth2::Timeouts; - use std::str::FromStr; use strum::VariantNames; use types::{MainnetEthSpec, Slot}; @@ -750,10 +752,13 @@ mod tests { #[test] fn api_topic_all() { let all = ApiTopic::all(); - assert_eq!(all.len(), ApiTopic::VARIANTS.len()); - assert!(ApiTopic::VARIANTS + // ignore NONE variant + let mut variants = ApiTopic::VARIANTS.to_vec(); + variants.retain(|s| *s != "none"); + assert_eq!(all.len(), variants.len()); + assert!(variants .iter() - .map(|topic| ApiTopic::from_str(topic).unwrap()) + .map(|topic| ApiTopic::from_str(topic, true).unwrap()) .eq(all.into_iter())); } diff --git a/validator_client/src/beacon_node_health.rs b/validator_client/src/beacon_node_health.rs index 1783bb312c..c7e9369b7a 100644 --- a/validator_client/src/beacon_node_health.rs +++ b/validator_client/src/beacon_node_health.rs @@ -1,8 +1,6 @@ -use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fmt::{Debug, Display, Formatter}; -use std::str::FromStr; use types::Slot; /// Sync distances between 0 and DEFAULT_SYNC_TOLERANCE are considered `synced`. @@ -50,29 +48,6 @@ impl Default for BeaconNodeSyncDistanceTiers { } } -impl FromStr for BeaconNodeSyncDistanceTiers { - type Err = String; - - fn from_str(s: &str) -> Result { - let values: (u64, u64, u64) = s - .split(',') - .map(|s| { - s.parse() - .map_err(|e| format!("Invalid sync distance modifier: {e:?}")) - }) - .collect::, _>>()? - .into_iter() - .collect_tuple() - .ok_or("Invalid number of sync distance modifiers".to_string())?; - - Ok(BeaconNodeSyncDistanceTiers { - synced: Slot::new(values.0), - small: Slot::new(values.0 + values.1), - medium: Slot::new(values.0 + values.1 + values.2), - }) - } -} - impl BeaconNodeSyncDistanceTiers { /// Takes a given sync distance and determines its tier based on the `sync_tolerance` defined by /// the CLI. @@ -87,6 +62,17 @@ impl BeaconNodeSyncDistanceTiers { SyncDistanceTier::Large } } + + pub fn from_vec(tiers: &[u64]) -> Result { + if tiers.len() != 3 { + return Err("Invalid number of sync distance modifiers".to_string()); + } + Ok(BeaconNodeSyncDistanceTiers { + synced: Slot::new(tiers[0]), + small: Slot::new(tiers[0] + tiers[1]), + medium: Slot::new(tiers[0] + tiers[1] + tiers[2]), + }) + } } /// Execution Node health metrics. @@ -293,7 +279,6 @@ mod tests { SyncDistanceTier, }; use crate::beacon_node_fallback::Config; - use std::str::FromStr; use types::Slot; #[test] @@ -396,7 +381,7 @@ mod tests { // medium 9..=12 // large: 13.. - let distance_tiers = BeaconNodeSyncDistanceTiers::from_str("4,4,4").unwrap(); + let distance_tiers = BeaconNodeSyncDistanceTiers::from_vec(&[4, 4, 4]).unwrap(); let synced_low = new_distance_tier(0, &distance_tiers); let synced_high = new_distance_tier(4, &distance_tiers); diff --git a/validator_client/src/cli.rs b/validator_client/src/cli.rs index 209876f07b..3a0f09bbcf 100644 --- a/validator_client/src/cli.rs +++ b/validator_client/src/cli.rs @@ -1,478 +1,466 @@ -use clap::{builder::ArgPredicate, Arg, ArgAction, Command}; -use clap_utils::{get_color_style, FLAG_HEADER}; - -pub fn cli_app() -> Command { - Command::new("validator_client") - .visible_aliases(["v", "vc", "validator"]) - .styles(get_color_style()) - .display_order(0) - .about( - "When connected to a beacon node, performs the duties of a staked \ +use crate::ApiTopic; +use clap::builder::ArgPredicate; +pub use clap::{Arg, ArgAction, Args, Command, FromArgMatches, Parser}; +use clap_utils::get_color_style; +use clap_utils::FLAG_HEADER; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use types::Address; + +#[derive(Parser, Clone, Deserialize, Serialize, Debug)] +#[clap( + name = "validator_client", + visible_aliases = &["v", "vc", "validator"], + about = "When connected to a beacon node, performs the duties of a staked \ validator (e.g., proposing blocks and attestations).", - ) - .arg( - Arg::new("help") - .long("help") - .short('h') - .help("Prints help information") - .action(ArgAction::HelpLong) - .display_order(0) - .help_heading(FLAG_HEADER) - ) - .arg( - Arg::new("beacon-nodes") - .long("beacon-nodes") - .value_name("NETWORK_ADDRESSES") - .help("Comma-separated addresses to one or more beacon node HTTP APIs. \ - Default is http://localhost:5052." - ) - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("proposer-nodes") - .long("proposer-nodes") - .value_name("NETWORK_ADDRESSES") - .help("Comma-separated addresses to one or more beacon node HTTP APIs. \ - These specify nodes that are used to send beacon block proposals. A failure will revert back to the standard beacon nodes specified in --beacon-nodes." - ) - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("broadcast") - .long("broadcast") - .value_name("API_TOPICS") - .help("Comma-separated list of beacon API topics to broadcast to all beacon nodes. \ - Possible values are: none, attestations, blocks, subscriptions, \ - sync-committee. Default (when flag is omitted) is to broadcast \ - subscriptions only." - ) - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("validators-dir") - .long("validators-dir") - .alias("validator-dir") - .value_name("VALIDATORS_DIR") - .help( - "The directory which contains the validator keystores, deposit data for \ - each validator along with the common slashing protection database \ - and the validator_definitions.yml" - ) - .action(ArgAction::Set) - .conflicts_with("datadir") - .display_order(0) - ) - .arg( - Arg::new("secrets-dir") - .long("secrets-dir") - .value_name("SECRETS_DIRECTORY") - .help( - "The directory which contains the password to unlock the validator \ - voting keypairs. Each password should be contained in a file where the \ - name is the 0x-prefixed hex representation of the validators voting public \ - key. Defaults to ~/.lighthouse/{network}/secrets.", - ) - .action(ArgAction::Set) - .conflicts_with("datadir") - .display_order(0) - ) - .arg( - Arg::new("init-slashing-protection") - .long("init-slashing-protection") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .help( - "If present, do not require the slashing protection database to exist before \ - running. You SHOULD NOT use this flag unless you're certain that a new \ - slashing protection database is required. Usually, your database \ - will have been initialized when you imported your validator keys. If you \ - misplace your database and then run with this flag you risk being slashed." - ) - .display_order(0) - ) - .arg( - Arg::new("disable-auto-discover") - .long("disable-auto-discover") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .help( - "If present, do not attempt to discover new validators in the validators-dir. Validators \ - will need to be manually added to the validator_definitions.yml file." - ) - .display_order(0) - ) - .arg( - Arg::new("use-long-timeouts") - .long("use-long-timeouts") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .help("If present, the validator client will use longer timeouts for requests \ - made to the beacon node. This flag is generally not recommended, \ - longer timeouts can cause missed duties when fallbacks are used.") - .display_order(0) - ) - .arg( - Arg::new("beacon-nodes-tls-certs") - .long("beacon-nodes-tls-certs") - .value_name("CERTIFICATE-FILES") - .action(ArgAction::Set) - .help("Comma-separated paths to custom TLS certificates to use when connecting \ - to a beacon node (and/or proposer node). These certificates must be in PEM format and are used \ - in addition to the OS trust store. Commas must only be used as a \ - delimiter, and must not be part of the certificate path.") - .display_order(0) - ) - // This overwrites the graffiti configured in the beacon node. - .arg( - Arg::new("graffiti") - .long("graffiti") - .help("Specify your custom graffiti to be included in blocks.") - .value_name("GRAFFITI") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("graffiti-file") - .long("graffiti-file") - .help("Specify a graffiti file to load validator graffitis from.") - .value_name("GRAFFITI-FILE") - .action(ArgAction::Set) - .conflicts_with("graffiti") - .display_order(0) - ) - .arg( - Arg::new("suggested-fee-recipient") - .long("suggested-fee-recipient") - .help("Once the merge has happened, this address will receive transaction fees \ - from blocks proposed by this validator client. If a fee recipient is \ - configured in the validator definitions it takes priority over this value.") - .value_name("FEE-RECIPIENT") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("distributed") - .long("distributed") - .help("Enables functionality required for running the validator in a distributed validator cluster.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - /* REST API related arguments */ - .arg( - Arg::new("http") - .long("http") - .help("Enable the RESTful HTTP API server. Disabled by default.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - /* - * Note: The HTTP server is **not** encrypted (i.e., not HTTPS) and therefore it is - * unsafe to publish on a public network. - * - * If the `--http-address` flag is used, the `--unencrypted-http-transport` flag - * must also be used in order to make it clear to the user that this is unsafe. - */ - .arg( - Arg::new("http-address") - .long("http-address") - .requires("http") - .value_name("ADDRESS") - .help("Set the address for the HTTP address. The HTTP server is not encrypted \ - and therefore it is unsafe to publish on a public network. When this \ - flag is used, it additionally requires the explicit use of the \ - `--unencrypted-http-transport` flag to ensure the user is aware of the \ - risks involved. For access via the Internet, users should apply \ - transport-layer security like a HTTPS reverse-proxy or SSH tunnelling.") - .requires("unencrypted-http-transport") - .display_order(0) - ) - .arg( - Arg::new("unencrypted-http-transport") - .long("unencrypted-http-transport") - .help("This is a safety flag to ensure that the user is aware that the http \ - transport is unencrypted and using a custom HTTP address is unsafe.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .requires("http-address") - .display_order(0) - ) - .arg( - Arg::new("http-port") - .long("http-port") - .requires("http") - .value_name("PORT") - .help("Set the listen TCP port for the RESTful HTTP API server.") - .default_value_if("http", ArgPredicate::IsPresent, "5062") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("http-allow-origin") - .long("http-allow-origin") - .requires("http") - .value_name("ORIGIN") - .help("Set the value of the Access-Control-Allow-Origin response HTTP header. \ - Use * to allow any origin (not recommended in production). \ - If no value is supplied, the CORS allowed origin is set to the listen \ - address of this server (e.g., http://localhost:5062).") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("http-allow-keystore-export") - .long("http-allow-keystore-export") - .requires("http") - .help("If present, allow access to the DELETE /lighthouse/keystores HTTP \ - API method, which allows exporting keystores and passwords to HTTP API \ - consumers who have access to the API token. This method is useful for \ - exporting validators, however it should be used with caution since it \ - exposes private key data to authorized users.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - .arg( - Arg::new("http-store-passwords-in-secrets-dir") - .long("http-store-passwords-in-secrets-dir") - .requires("http") - .help("If present, any validators created via the HTTP will have keystore \ - passwords stored in the secrets-dir rather than the validator \ - definitions file.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - /* Prometheus metrics HTTP server related arguments */ - .arg( - Arg::new("metrics") - .long("metrics") - .help("Enable the Prometheus metrics HTTP server. Disabled by default.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - .arg( - Arg::new("metrics-address") - .long("metrics-address") - .requires("metrics") - .value_name("ADDRESS") - .help("Set the listen address for the Prometheus metrics HTTP server.") - .default_value_if("metrics", ArgPredicate::IsPresent, "127.0.0.1") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("metrics-port") - .long("metrics-port") - .requires("metrics") - .value_name("PORT") - .help("Set the listen TCP port for the Prometheus metrics HTTP server.") - .default_value_if("metrics", ArgPredicate::IsPresent, "5064") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("metrics-allow-origin") - .long("metrics-allow-origin") - .requires("metrics") - .value_name("ORIGIN") - .help("Set the value of the Access-Control-Allow-Origin response HTTP header. \ + styles = get_color_style(), + next_line_help = true, + term_width = 80, + disable_help_flag = true, + disable_help_subcommand = true, + display_order = 0, +)] +pub struct ValidatorClient { + #[clap( + long, + value_name = "NETWORK_ADDRESSES", + value_delimiter = ',', + help = "Comma-separated addresses to one or more beacon node HTTP APIs. \ + Default is http://localhost:5052.", + display_order = 0 + )] + pub beacon_nodes: Option>, + + #[clap( + long, + value_name = "NETWORK_ADDRESSES", + value_delimiter = ',', + help = "Comma-separated addresses to one or more beacon node HTTP APIs. \ + These specify nodes that are used to send beacon block proposals. \ + A failure will revert back to the standard beacon nodes specified in --beacon-nodes.", + display_order = 0 + )] + pub proposer_nodes: Option>, + + #[clap( + long, + value_name = "API_TOPICS", + value_delimiter = ',', + help = "Comma-separated list of beacon API topics to broadcast to all beacon nodes. \ + Possible values are: none, attestations, blocks, subscriptions, \ + sync-committee. Default (when flag is omitted) is to broadcast \ + subscriptions only.", + display_order = 0 + )] + pub broadcast: Option>, + + #[clap( + long, + alias = "validator-dir", + value_name = "VALIDATORS_DIR", + conflicts_with = "datadir", + help = "The directory which contains the validator keystores, deposit data for \ + each validator along with the common slashing protection database \ + and the validator_definitions.yml", + display_order = 0 + )] + pub validators_dir: Option, + + #[clap( + long, + value_name = "SECRETS_DIRECTORY", + conflicts_with = "datadir", + help = "The directory which contains the password to unlock the validator \ + voting keypairs. Each password should be contained in a file where the \ + name is the 0x-prefixed hex representation of the validators voting public \ + key. Defaults to ~/.lighthouse/{network}/secrets.", + display_order = 0 + )] + pub secrets_dir: Option, + + #[clap( + long, + help = "If present, do not require the slashing protection database to exist before \ + running. You SHOULD NOT use this flag unless you're certain that a new \ + slashing protection database is required. Usually, your database \ + will have been initialized when you imported your validator keys. If you \ + misplace your database and then run with this flag you risk being slashed.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub init_slashing_protection: bool, + + #[clap( + long, + help = "If present, do not attempt to discover new validators in the validators-dir. Validators \ + will need to be manually added to the validator_definitions.yml file.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub disable_auto_discover: bool, + + #[clap( + long, + help = "If present, the validator client will use longer timeouts for requests \ + made to the beacon node. This flag is generally not recommended, \ + longer timeouts can cause missed duties when fallbacks are used.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub use_long_timeouts: bool, + + #[clap( + long, + value_name = "CERTIFICATE-FILES", + value_delimiter = ',', + help = "Comma-separated paths to custom TLS certificates to use when connecting \ + to a beacon node (and/or proposer node). These certificates must be in PEM format and are used \ + in addition to the OS trust store. Commas must only be used as a \ + delimiter, and must not be part of the certificate path.", + display_order = 0 + )] + pub beacon_nodes_tls_certs: Option>, + + // This overwrites the graffiti configured in the beacon node. + #[clap( + long, + value_name = "GRAFFITI", + help = "Specify your custom graffiti to be included in blocks.", + display_order = 0 + )] + pub graffiti: Option, + + #[clap( + long, + value_name = "GRAFFITI-FILE", + conflicts_with = "graffiti", + help = "Specify a graffiti file to load validator graffitis from.", + display_order = 0 + )] + pub graffiti_file: Option, + + #[clap( + long, + value_name = "FEE-RECIPIENT", + help = "Once the merge has happened, this address will receive transaction fees \ + from blocks proposed by this validator client. If a fee recipient is \ + configured in the validator definitions it takes priority over this value.", + display_order = 0 + )] + pub suggested_fee_recipient: Option
, + + #[clap( + long, + help = "Enables functionality required for running the validator in a distributed validator cluster.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub distributed: bool, + + /* REST API related arguments */ + #[clap( + long, + help = "Enable the RESTful HTTP API server. Disabled by default.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub http: bool, + + /* + * Note: The HTTP server is **not** encrypted (i.e., not HTTPS) and therefore it is + * unsafe to publish on a public network. + * + * If the `--http-address` flag is used, the `--unencrypted-http-transport` flag + * must also be used in order to make it clear to the user that this is unsafe. + */ + #[clap( + long, + value_name = "ADDRESS", + requires = "unencrypted_http_transport", + help = "Set the address for the HTTP address. The HTTP server is not encrypted \ + and therefore it is unsafe to publish on a public network. When this \ + flag is used, it additionally requires the explicit use of the \ + `--unencrypted-http-transport` flag to ensure the user is aware of the \ + risks involved. For access via the Internet, users should apply \ + transport-layer security like a HTTPS reverse-proxy or SSH tunnelling.", + display_order = 0 + )] + pub http_address: Option, + + #[clap( + long, + requires = "http_address", + help = "This is a safety flag to ensure that the user is aware that the http \ + transport is unencrypted and using a custom HTTP address is unsafe.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub unencrypted_http_transport: bool, + + #[clap( + long, + value_name = "PORT", + default_value_t = 5062, + help = "Set the listen TCP port for the RESTful HTTP API server.", + display_order = 0 + )] + pub http_port: u16, + + #[clap( + long, + value_name = "ORIGIN", + help = "Set the value of the Access-Control-Allow-Origin response HTTP header. \ Use * to allow any origin (not recommended in production). \ If no value is supplied, the CORS allowed origin is set to the listen \ - address of this server (e.g., http://localhost:5064).") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("enable-high-validator-count-metrics") - .long("enable-high-validator-count-metrics") - .help("Enable per validator metrics for > 64 validators. \ - Note: This flag is automatically enabled for <= 64 validators. \ - Enabling this metric for higher validator counts will lead to higher volume \ - of prometheus metrics being collected.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - /* - * Explorer metrics - */ - .arg( - Arg::new("monitoring-endpoint") - .long("monitoring-endpoint") - .value_name("ADDRESS") - .help("Enables the monitoring service for sending system metrics to a remote endpoint. \ + address of this server (e.g., http://localhost:5062).", + display_order = 0 + )] + pub http_allow_origin: Option, + + #[clap( + long, + requires = "http", + help = "If present, allow access to the DELETE /lighthouse/keystores HTTP \ + API method, which allows exporting keystores and passwords to HTTP API \ + consumers who have access to the API token. This method is useful for \ + exporting validators, however it should be used with caution since it \ + exposes private key data to authorized users.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub http_allow_keystore_export: bool, + + #[clap( + long, + requires = "http", + help = "If present, any validators created via the HTTP will have keystore \ + passwords stored in the secrets-dir rather than the validator \ + definitions file.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub http_store_passwords_in_secrets_dir: bool, + + /* Prometheus metrics HTTP server related arguments */ + #[clap( + long, + help = "Enable the Prometheus metrics HTTP server. Disabled by default.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub metrics: bool, + + #[clap( + long, + value_name = "ADDRESS", + default_value_if("metrics", ArgPredicate::IsPresent, "127.0.0.1"), + help = "Set the listen address for the Prometheus metrics HTTP server. [default: 127.0.0.1]", + display_order = 0 + )] + pub metrics_address: Option, + + #[clap( + long, + value_name = "PORT", + default_value_t = 5064, + help = "Set the listen TCP port for the Prometheus metrics HTTP server.", + display_order = 0 + )] + pub metrics_port: u16, + + #[clap( + long, + value_name = "ORIGIN", + help = "Set the value of the Access-Control-Allow-Origin response HTTP header. \ + Use * to allow any origin (not recommended in production). \ + If no value is supplied, the CORS allowed origin is set to the listen \ + address of this server (e.g., http://localhost:5064).", + display_order = 0 + )] + pub metrics_allow_origin: Option, + + #[clap( + long, + help = "Enable per validator metrics for > 64 validators. \ + Note: This flag is automatically enabled for <= 64 validators. \ + Enabling this metric for higher validator counts will lead to higher volume \ + of prometheus metrics being collected.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub enable_high_validator_count_metrics: bool, + + /* Explorer metrics */ + #[clap( + long, + value_name = "ADDRESS", + help = "Enables the monitoring service for sending system metrics to a remote endpoint. \ This can be used to monitor your setup on certain services (e.g. beaconcha.in). \ This flag sets the endpoint where the beacon node metrics will be sent. \ Note: This will send information to a remote sever which may identify and associate your \ validators, IP address and other personal information. Always use a HTTPS connection \ - and never provide an untrusted URL.") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("monitoring-endpoint-period") - .long("monitoring-endpoint-period") - .value_name("SECONDS") - .help("Defines how many seconds to wait between each message sent to \ - the monitoring-endpoint. Default: 60s") - .requires("monitoring-endpoint") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("enable-doppelganger-protection") - .long("enable-doppelganger-protection") - .value_name("ENABLE_DOPPELGANGER_PROTECTION") - .help("If this flag is set, Lighthouse will delay startup for three epochs and \ - monitor for messages on the network by any of the validators managed by this \ - client. This will result in three (possibly four) epochs worth of missed \ - attestations. If an attestation is detected during this period, it means it is \ - very likely that you are running a second validator client with the same keys. \ - This validator client will immediately shutdown if this is detected in order \ - to avoid potentially committing a slashable offense. Use this flag in order to \ - ENABLE this functionality, without this flag Lighthouse will begin attesting \ - immediately.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - .arg( - Arg::new("builder-proposals") - .long("builder-proposals") - .alias("private-tx-proposals") - .help("If this flag is set, Lighthouse will query the Beacon Node for only block \ - headers during proposals and will sign over headers. Useful for outsourcing \ - execution payload construction during proposals.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - .arg( - Arg::new("builder-registration-timestamp-override") - .long("builder-registration-timestamp-override") - .alias("builder-registration-timestamp-override") - .help("This flag takes a unix timestamp value that will be used to override the \ - timestamp used in the builder api registration") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("gas-limit") - .long("gas-limit") - .value_name("INTEGER") - .action(ArgAction::Set) - .help("The gas limit to be used in all builder proposals for all validators managed \ - by this validator client. Note this will not necessarily be used if the gas limit \ - set here moves too far from the previous block's gas limit. [default: 30,000,000]") - .requires("builder-proposals") - .display_order(0) - ) - .arg( - Arg::new("disable-latency-measurement-service") - .long("disable-latency-measurement-service") - .help("Disables the service that periodically attempts to measure latency to BNs.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - .arg( - Arg::new("validator-registration-batch-size") - .long("validator-registration-batch-size") - .value_name("INTEGER") - .help("Defines the number of validators per \ - validator/register_validator request sent to the BN. This value \ - can be reduced to avoid timeouts from builders.") - .default_value("500") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("builder-boost-factor") - .long("builder-boost-factor") - .value_name("UINT64") - .help("Defines the boost factor, \ - a percentage multiplier to apply to the builder's payload value \ - when choosing between a builder payload header and payload from \ - the local execution node.") - .conflicts_with("prefer-builder-proposals") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("prefer-builder-proposals") - .long("prefer-builder-proposals") - .help("If this flag is set, Lighthouse will always prefer blocks \ - constructed by builders, regardless of payload value.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - .arg( - Arg::new("beacon-nodes-sync-tolerances") - .long("beacon-nodes-sync-tolerances") - .value_name("SYNC_TOLERANCES") - .help("A comma-separated list of 3 values which sets the size of each sync distance range when \ - determining the health of each connected beacon node. \ - The first value determines the `Synced` range. \ - If a connected beacon node is synced to within this number of slots it is considered 'Synced'. \ - The second value determines the `Small` sync distance range. \ - This range starts immediately after the `Synced` range. \ - The third value determines the `Medium` sync distance range. \ - This range starts immediately after the `Small` range. \ - Any sync distance value beyond that is considered `Large`. \ - For example, a value of `8,8,48` would have ranges like the following: \ - `Synced`: 0..=8 \ - `Small`: 9..=16 \ - `Medium`: 17..=64 \ - `Large`: 65.. \ - These values are used to determine what ordering beacon node fallbacks are used in. \ - Generally, `Synced` nodes are preferred over `Small` and so on. \ - Nodes in the `Synced` range will tie-break based on their ordering in `--beacon-nodes`. \ - This ensures the primary beacon node is prioritised. \ - [default: 8,8,48]") - .action(ArgAction::Set) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - .arg( - Arg::new("disable-slashing-protection-web3signer") - .long("disable-slashing-protection-web3signer") - .help("Disable Lighthouse's slashing protection for all web3signer keys. This can \ - reduce the I/O burden on the VC but is only safe if slashing protection \ - is enabled on the remote signer and is implemented correctly. DO NOT ENABLE \ - THIS FLAG UNLESS YOU ARE CERTAIN THAT SLASHING PROTECTION IS ENABLED ON \ - THE REMOTE SIGNER. YOU WILL GET SLASHED IF YOU USE THIS FLAG WITHOUT \ - ENABLING WEB3SIGNER'S SLASHING PROTECTION.") - .action(ArgAction::SetTrue) - .help_heading(FLAG_HEADER) - .display_order(0) - ) - /* - * Experimental/development options. - */ - .arg( - Arg::new("web3-signer-keep-alive-timeout") - .long("web3-signer-keep-alive-timeout") - .value_name("MILLIS") - .default_value("20000") - .help("Keep-alive timeout for each web3signer connection. Set to 'null' to never \ - timeout") - .action(ArgAction::Set) - .display_order(0) - ) - .arg( - Arg::new("web3-signer-max-idle-connections") - .long("web3-signer-max-idle-connections") - .value_name("COUNT") - .help("Maximum number of idle connections to maintain per web3signer host. Default \ - is unlimited.") - .action(ArgAction::Set) - .display_order(0) - ) + and never provide an untrusted URL.", + display_order = 0 + )] + pub monitoring_endpoint: Option, + + #[clap( + long, + value_name = "SECONDS", + requires = "monitoring_endpoint", + default_value_t = 60, + help = "Defines how many seconds to wait between each message sent to \ + the monitoring-endpoint.", + display_order = 0 + )] + pub monitoring_endpoint_period: u64, + + #[clap( + long, + value_name = "ENABLE_DOPPELGANGER_PROTECTION", + help = "If this flag is set, Lighthouse will delay startup for three epochs and \ + monitor for messages on the network by any of the validators managed by this \ + client. This will result in three (possibly four) epochs worth of missed \ + attestations. If an attestation is detected during this period, it means it is \ + very likely that you are running a second validator client with the same keys. \ + This validator client will immediately shutdown if this is detected in order \ + to avoid potentially committing a slashable offense. Use this flag in order to \ + ENABLE this functionality, without this flag Lighthouse will begin attesting \ + immediately.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub enable_doppelganger_protection: bool, + + #[clap( + long, + alias = "private-tx-proposals", + help = "If this flag is set, Lighthouse will query the Beacon Node for only block \ + headers during proposals and will sign over headers. Useful for outsourcing \ + execution payload construction during proposals.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub builder_proposals: bool, + + #[clap( + long, + value_name = "UNIX-TIMESTAMP", + help = "This flag takes a unix timestamp value that will be used to override the \ + timestamp used in the builder api registration.", + display_order = 0 + )] + pub builder_registration_timestamp_override: Option, + + #[clap( + long, + value_name = "INTEGER", + default_value_t = 30_000_000, + help = "The gas limit to be used in all builder proposals for all validators managed \ + by this validator client. Note this will not necessarily be used if the gas limit \ + set here moves too far from the previous block's gas limit.", + display_order = 0 + )] + pub gas_limit: u64, + + #[clap( + long, + value_name = "BOOLEAN", + help = "Disables the service that periodically attempts to measure latency to BNs.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub disable_latency_measurement_service: bool, + + #[clap( + long, + value_name = "INTEGER", + default_value_t = 500, + help = "Defines the number of validators per \ + validator/register_validator request sent to the BN. This value \ + can be reduced to avoid timeouts from builders.", + display_order = 0 + )] + pub validator_registration_batch_size: usize, + + #[clap( + long, + value_name = "UINT64", + help = "Defines the boost factor, \ + a percentage multiplier to apply to the builder's payload value \ + when choosing between a builder payload header and payload from \ + the local execution node.", + display_order = 0 + )] + pub builder_boost_factor: Option, + + #[clap( + long, + help = "If this flag is set, Lighthouse will always prefer blocks \ + constructed by builders, regardless of payload value.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub prefer_builder_proposals: bool, + + #[clap( + long, + help = "A comma-separated list of 3 values which sets the size of each sync distance range when \ + determining the health of each connected beacon node. \ + The first value determines the `Synced` range. \ + If a connected beacon node is synced to within this number of slots it is considered 'Synced'. \ + The second value determines the `Small` sync distance range. \ + This range starts immediately after the `Synced` range. \ + The third value determines the `Medium` sync distance range. \ + This range starts immediately after the `Small` range. \ + Any sync distance value beyond that is considered `Large`. \ + For example, a value of `8,8,48` would have ranges like the following: \ + `Synced`: 0..=8 \ + `Small`: 9..=16 \ + `Medium`: 17..=64 \ + `Large`: 65.. \ + These values are used to determine what ordering beacon node fallbacks are used in. \ + Generally, `Synced` nodes are preferred over `Small` and so on. \ + Nodes in the `Synced` range will tie-break based on their ordering in `--beacon-nodes`. \ + This ensures the primary beacon node is prioritised.", + display_order = 0, + value_delimiter = ',', + default_value = "8,8,48", + help_heading = FLAG_HEADER, + value_name = "SYNC_TOLERANCES" + )] + pub beacon_nodes_sync_tolerances: Vec, + + #[clap( + long, + help = "Disable Lighthouse's slashing protection for all web3signer keys. This can \ + reduce the I/O burden on the VC but is only safe if slashing protection \ + is enabled on the remote signer and is implemented correctly. DO NOT ENABLE \ + THIS FLAG UNLESS YOU ARE CERTAIN THAT SLASHING PROTECTION IS ENABLED ON \ + THE REMOTE SIGNER. YOU WILL GET SLASHED IF YOU USE THIS FLAG WITHOUT \ + ENABLING WEB3SIGNER'S SLASHING PROTECTION.", + display_order = 0, + help_heading = FLAG_HEADER + )] + pub disable_slashing_protection_web3signer: bool, + + /* Experimental/development options */ + #[clap( + long, + value_name = "MILLIS", + default_value_t = 20000, + help = "Keep-alive timeout for each web3signer connection. Set to '0' to never \ + timeout.", + display_order = 0 + )] + pub web3_signer_keep_alive_timeout: u64, + + #[clap( + long, + value_name = "COUNT", + help = "Maximum number of idle connections to maintain per web3signer host. Default \ + is unlimited.", + display_order = 0 + )] + pub web3_signer_max_idle_connections: Option, } diff --git a/validator_client/src/config.rs b/validator_client/src/config.rs index f42ed55146..53da1bc43c 100644 --- a/validator_client/src/config.rs +++ b/validator_client/src/config.rs @@ -1,10 +1,10 @@ use crate::beacon_node_fallback::ApiTopic; +use crate::cli::ValidatorClient; use crate::graffiti_file::GraffitiFile; -use crate::{ - beacon_node_fallback, beacon_node_health::BeaconNodeSyncDistanceTiers, http_api, http_metrics, -}; +use crate::validator_store::DEFAULT_GAS_LIMIT; +use crate::{beacon_node_fallback, http_api, http_metrics, BeaconNodeSyncDistanceTiers}; use clap::ArgMatches; -use clap_utils::{flags::DISABLE_MALLOC_TUNING_FLAG, parse_optional, parse_required}; +use clap_utils::{flags::DISABLE_MALLOC_TUNING_FLAG, parse_required}; use directory::{ get_network_dir, DEFAULT_HARDCODED_NETWORK, DEFAULT_ROOT_DIR, DEFAULT_SECRET_DIR, DEFAULT_VALIDATOR_DIR, @@ -16,7 +16,6 @@ use slog::{info, warn, Logger}; use std::fs; use std::net::IpAddr; use std::path::PathBuf; -use std::str::FromStr; use std::time::Duration; use types::{Address, GRAFFITI_BYTES_LEN}; @@ -72,7 +71,7 @@ pub struct Config { /// Overrides the timestamp field in builder api ValidatorRegistrationV1 pub builder_registration_timestamp_override: Option, /// Fallback gas limit. - pub gas_limit: Option, + pub gas_limit: u64, /// A list of custom certificates that the validator client will additionally use when /// connecting to a beacon node over SSL/TLS. pub beacon_nodes_tls_certs: Option>, @@ -129,7 +128,7 @@ impl Default for Config { beacon_nodes_tls_certs: None, builder_proposals: false, builder_registration_timestamp_override: None, - gas_limit: None, + gas_limit: DEFAULT_GAS_LIMIT, broadcast_topics: vec![ApiTopic::Subscriptions], enable_latency_measurement_service: true, validator_registration_batch_size: 500, @@ -146,7 +145,11 @@ impl Default for Config { impl Config { /// Returns a `Default` implementation of `Self` with some parameters modified by the supplied /// `cli_args`. - pub fn from_cli(cli_args: &ArgMatches, log: &Logger) -> Result { + pub fn from_cli( + cli_args: &ArgMatches, + validator_client_config: &ValidatorClient, + log: &Logger, + ) -> Result { let mut config = Config::default(); let default_root_dir = dirs::home_dir() @@ -159,11 +162,12 @@ impl Config { validator_dir = Some(base_dir.join(DEFAULT_VALIDATOR_DIR)); secrets_dir = Some(base_dir.join(DEFAULT_SECRET_DIR)); } - if cli_args.get_one::("validators-dir").is_some() { - validator_dir = Some(parse_required(cli_args, "validators-dir")?); + + if let Some(validator_dir_path) = validator_client_config.validators_dir.as_ref() { + validator_dir = Some(validator_dir_path.clone()); } - if cli_args.get_one::("secrets-dir").is_some() { - secrets_dir = Some(parse_required(cli_args, "secrets-dir")?); + if let Some(secrets_dir_path) = validator_client_config.secrets_dir.as_ref() { + secrets_dir = Some(secrets_dir_path.clone()); } config.validator_dir = validator_dir.unwrap_or_else(|| { @@ -183,35 +187,36 @@ impl Config { .map_err(|e| format!("Failed to create {:?}: {:?}", config.validator_dir, e))?; } - if let Some(beacon_nodes) = parse_optional::(cli_args, "beacon-nodes")? { + if let Some(beacon_nodes) = validator_client_config.beacon_nodes.as_ref() { config.beacon_nodes = beacon_nodes - .split(',') - .map(SensitiveUrl::parse) + .iter() + .map(|s| SensitiveUrl::parse(s)) .collect::>() .map_err(|e| format!("Unable to parse beacon node URL: {:?}", e))?; } - if let Some(proposer_nodes) = parse_optional::(cli_args, "proposer-nodes")? { + + if let Some(proposer_nodes) = validator_client_config.proposer_nodes.as_ref() { config.proposer_nodes = proposer_nodes - .split(',') - .map(SensitiveUrl::parse) + .iter() + .map(|s| SensitiveUrl::parse(s)) .collect::>() .map_err(|e| format!("Unable to parse proposer node URL: {:?}", e))?; } - config.disable_auto_discover = cli_args.get_flag("disable-auto-discover"); - config.init_slashing_protection = cli_args.get_flag("init-slashing-protection"); - config.use_long_timeouts = cli_args.get_flag("use-long-timeouts"); + config.disable_auto_discover = validator_client_config.disable_auto_discover; + config.init_slashing_protection = validator_client_config.init_slashing_protection; + config.use_long_timeouts = validator_client_config.use_long_timeouts; - if let Some(graffiti_file_path) = cli_args.get_one::("graffiti-file") { + if let Some(graffiti_file_path) = validator_client_config.graffiti_file.as_ref() { let mut graffiti_file = GraffitiFile::new(graffiti_file_path.into()); graffiti_file .read_graffiti_file() .map_err(|e| format!("Error reading graffiti file: {:?}", e))?; config.graffiti_file = Some(graffiti_file); - info!(log, "Successfully loaded graffiti file"; "path" => graffiti_file_path); + info!(log, "Successfully loaded graffiti file"; "path" => graffiti_file_path.to_str()); } - if let Some(input_graffiti) = cli_args.get_one::("graffiti") { + if let Some(input_graffiti) = validator_client_config.graffiti.as_ref() { let graffiti_bytes = input_graffiti.as_bytes(); if graffiti_bytes.len() > GRAFFITI_BYTES_LEN { return Err(format!( @@ -230,55 +235,40 @@ impl Config { } } - if let Some(input_fee_recipient) = - parse_optional::
(cli_args, "suggested-fee-recipient")? - { + if let Some(input_fee_recipient) = validator_client_config.suggested_fee_recipient { config.fee_recipient = Some(input_fee_recipient); } - if let Some(tls_certs) = parse_optional::(cli_args, "beacon-nodes-tls-certs")? { - config.beacon_nodes_tls_certs = Some(tls_certs.split(',').map(PathBuf::from).collect()); + if let Some(tls_certs) = validator_client_config.beacon_nodes_tls_certs.as_ref() { + config.beacon_nodes_tls_certs = Some(tls_certs.iter().map(PathBuf::from).collect()); } - if cli_args.get_flag("distributed") { - config.distributed = true; - } + config.distributed = validator_client_config.distributed; - if let Some(broadcast_topics) = cli_args.get_one::("broadcast") { - config.broadcast_topics = broadcast_topics - .split(',') - .filter(|t| *t != "none") - .map(|t| { - t.trim() - .parse::() - .map_err(|_| format!("Unknown API topic to broadcast: {t}")) - }) - .collect::>()?; + if let Some(mut broadcast_topics) = validator_client_config.broadcast.clone() { + broadcast_topics.retain(|topic| *topic != ApiTopic::None); + config.broadcast_topics = broadcast_topics; } /* * Beacon node fallback */ - if let Some(sync_tolerance) = cli_args.get_one::("beacon-nodes-sync-tolerances") { - config.beacon_node_fallback.sync_tolerances = - BeaconNodeSyncDistanceTiers::from_str(sync_tolerance)?; - } else { - config.beacon_node_fallback.sync_tolerances = BeaconNodeSyncDistanceTiers::default(); - } + config.beacon_node_fallback.sync_tolerances = BeaconNodeSyncDistanceTiers::from_vec( + &validator_client_config.beacon_nodes_sync_tolerances, + )?; /* * Web3 signer */ - if let Some(s) = parse_optional::(cli_args, "web3-signer-keep-alive-timeout")? { - config.web3_signer_keep_alive_timeout = if s == "null" { - None - } else { - Some(Duration::from_millis( - s.parse().map_err(|_| "invalid timeout value".to_string())?, - )) - } + if validator_client_config.web3_signer_keep_alive_timeout == 0 { + config.web3_signer_keep_alive_timeout = None + } else { + config.web3_signer_keep_alive_timeout = Some(Duration::from_millis( + validator_client_config.web3_signer_keep_alive_timeout, + )); } - if let Some(n) = parse_optional::(cli_args, "web3-signer-max-idle-connections")? { + + if let Some(n) = validator_client_config.web3_signer_max_idle_connections { config.web3_signer_max_idle_connections = Some(n); } @@ -286,12 +276,10 @@ impl Config { * Http API server */ - if cli_args.get_flag("http") { - config.http_api.enabled = true; - } + config.http_api.enabled = validator_client_config.http; - if let Some(address) = cli_args.get_one::("http-address") { - if cli_args.get_flag("unencrypted-http-transport") { + if let Some(address) = &validator_client_config.http_address { + if validator_client_config.unencrypted_http_transport { config.http_api.listen_addr = address .parse::() .map_err(|_| "http-address is not a valid IP address.")?; @@ -303,13 +291,9 @@ impl Config { } } - if let Some(port) = cli_args.get_one::("http-port") { - config.http_api.listen_port = port - .parse::() - .map_err(|_| "http-port is not a valid u16.")?; - } + config.http_api.listen_port = validator_client_config.http_port; - if let Some(allow_origin) = cli_args.get_one::("http-allow-origin") { + if let Some(allow_origin) = validator_client_config.http_allow_origin.as_ref() { // Pre-validate the config value to give feedback to the user on node startup, instead of // as late as when the first API response is produced. hyper::header::HeaderValue::from_str(allow_origin) @@ -318,39 +302,26 @@ impl Config { config.http_api.allow_origin = Some(allow_origin.to_string()); } - if cli_args.get_flag("http-allow-keystore-export") { - config.http_api.allow_keystore_export = true; - } - - if cli_args.get_flag("http-store-passwords-in-secrets-dir") { - config.http_api.store_passwords_in_secrets_dir = true; - } - + config.http_api.allow_keystore_export = validator_client_config.http_allow_keystore_export; + config.http_api.store_passwords_in_secrets_dir = + validator_client_config.http_store_passwords_in_secrets_dir; /* * Prometheus metrics HTTP server */ - if cli_args.get_flag("metrics") { - config.http_metrics.enabled = true; - } + config.http_metrics.enabled = validator_client_config.metrics; + config.enable_high_validator_count_metrics = + validator_client_config.enable_high_validator_count_metrics; - if cli_args.get_flag("enable-high-validator-count-metrics") { - config.enable_high_validator_count_metrics = true; - } - - if let Some(address) = cli_args.get_one::("metrics-address") { - config.http_metrics.listen_addr = address + if let Some(metrics_address) = &validator_client_config.metrics_address { + config.http_metrics.listen_addr = metrics_address .parse::() .map_err(|_| "metrics-address is not a valid IP address.")?; } - if let Some(port) = cli_args.get_one::("metrics-port") { - config.http_metrics.listen_port = port - .parse::() - .map_err(|_| "metrics-port is not a valid u16.")?; - } + config.http_metrics.listen_port = validator_client_config.metrics_port; - if let Some(allow_origin) = cli_args.get_one::("metrics-allow-origin") { + if let Some(allow_origin) = validator_client_config.metrics_allow_origin.as_ref() { // Pre-validate the config value to give feedback to the user on node startup, instead of // as late as when the first API response is produced. hyper::header::HeaderValue::from_str(allow_origin) @@ -366,9 +337,8 @@ impl Config { /* * Explorer metrics */ - if let Some(monitoring_endpoint) = cli_args.get_one::("monitoring-endpoint") { - let update_period_secs = - clap_utils::parse_optional(cli_args, "monitoring-endpoint-period")?; + if let Some(monitoring_endpoint) = validator_client_config.monitoring_endpoint.as_ref() { + let update_period_secs = Some(validator_client_config.monitoring_endpoint_period); config.monitoring_api = Some(monitoring_api::Config { db_path: None, freezer_db_path: None, @@ -377,55 +347,33 @@ impl Config { }); } - if cli_args.get_flag("enable-doppelganger-protection") { - config.enable_doppelganger_protection = true; - } - - if cli_args.get_flag("builder-proposals") { - config.builder_proposals = true; - } + config.enable_doppelganger_protection = + validator_client_config.enable_doppelganger_protection; + config.builder_proposals = validator_client_config.builder_proposals; + config.prefer_builder_proposals = validator_client_config.prefer_builder_proposals; + config.gas_limit = validator_client_config.gas_limit; - if cli_args.get_flag("prefer-builder-proposals") { - config.prefer_builder_proposals = true; - } - - config.gas_limit = cli_args - .get_one::("gas-limit") - .map(|gas_limit| { - gas_limit - .parse::() - .map_err(|_| "gas-limit is not a valid u64.") - }) - .transpose()?; - - if let Some(registration_timestamp_override) = - cli_args.get_one::("builder-registration-timestamp-override") - { - config.builder_registration_timestamp_override = Some( - registration_timestamp_override - .parse::() - .map_err(|_| "builder-registration-timestamp-override is not a valid u64.")?, - ); - } - - config.builder_boost_factor = parse_optional(cli_args, "builder-boost-factor")?; + config.builder_registration_timestamp_override = + validator_client_config.builder_registration_timestamp_override; + config.builder_boost_factor = validator_client_config.builder_boost_factor; config.enable_latency_measurement_service = - !cli_args.get_flag("disable-latency-measurement-service"); + !validator_client_config.disable_latency_measurement_service; config.validator_registration_batch_size = - parse_required(cli_args, "validator-registration-batch-size")?; + validator_client_config.validator_registration_batch_size; + if config.validator_registration_batch_size == 0 { return Err("validator-registration-batch-size cannot be 0".to_string()); } config.enable_web3signer_slashing_protection = - if cli_args.get_flag("disable-slashing-protection-web3signer") { + if validator_client_config.disable_slashing_protection_web3signer { warn!( log, "Slashing protection for remote keys disabled"; "info" => "ensure slashing protection on web3signer is enabled or you WILL \ - get slashed" + get slashed" ); false } else { diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs index 9a02ffdefb..fb3ace8d01 100644 --- a/validator_client/src/lib.rs +++ b/validator_client/src/lib.rs @@ -3,7 +3,7 @@ mod beacon_node_fallback; mod beacon_node_health; mod block_service; mod check_synced; -mod cli; +pub mod cli; mod duties_service; mod graffiti_file; mod http_metrics; @@ -22,7 +22,6 @@ pub mod validator_store; pub use beacon_node_fallback::ApiTopic; pub use beacon_node_health::BeaconNodeSyncDistanceTiers; -pub use cli::cli_app; pub use config::Config; use initialized_validators::InitializedValidators; use lighthouse_metrics::set_gauge; @@ -33,6 +32,7 @@ pub use slashing_protection::{SlashingDatabase, SLASHING_PROTECTION_FILENAME}; use crate::beacon_node_fallback::{ start_fallback_updater_service, BeaconNodeFallback, CandidateBeaconNode, }; +use crate::cli::ValidatorClient; use crate::doppelganger_service::DoppelgangerService; use crate::graffiti_file::GraffitiFile; use crate::initialized_validators::Error::UnableToOpenVotingKeystore; @@ -112,8 +112,9 @@ impl ProductionValidatorClient { pub async fn new_from_cli( context: RuntimeContext, cli_args: &ArgMatches, + validator_client_config: &ValidatorClient, ) -> Result { - let config = Config::from_cli(cli_args, context.log()) + let config = Config::from_cli(cli_args, validator_client_config, context.log()) .map_err(|e| format!("Unable to initialize config: {}", e))?; Self::new(context, config).await } diff --git a/validator_client/src/validator_store.rs b/validator_client/src/validator_store.rs index af59ad9892..998686d77c 100644 --- a/validator_client/src/validator_store.rs +++ b/validator_client/src/validator_store.rs @@ -102,7 +102,7 @@ impl ValidatorStore { doppelganger_service, slot_clock, fee_recipient_process: config.fee_recipient, - gas_limit: config.gas_limit, + gas_limit: Some(config.gas_limit), builder_proposals: config.builder_proposals, enable_web3signer_slashing_protection: config.enable_web3signer_slashing_protection, prefer_builder_proposals: config.prefer_builder_proposals,