Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions anchor/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ http_api = { workspace = true }
http_metrics = { workspace = true }
hyper = { workspace = true }
keygen = { workspace = true }
keysplit = { workspace = true }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to add keysplit?

Copy link
Member

@dknopik dknopik Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. It would be nicer to have the docgen as a separate binary target of the top level crate IMO.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi what would be better, a separate binary target for docgen or have a top level library crate that it then called into the main anchor binary as a CLI command?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My recommendation: make docgen a separate workspace binary (e.g. tools/docgen) rather than an anchor client subcommand. This keeps the shipping CLI lean, avoids linking extra deps (e.g., keysplit) into the client crate.

Sketch:
• tools/docgen/Cargo.toml (bin = docgen)
• Depends on the crates that define the clap trees (e.g. anchor-client, keygen, keysplit) via path = "../anchor/client" etc.
• Each of those crates exposes a small pub fn cli() -> clap::Command (or impl CommandFactory) so docgen can introspect without booting the whole app.

logging = { workspace = true }
message_receiver = { workspace = true }
message_sender = { workspace = true }
Expand Down
277 changes: 155 additions & 122 deletions anchor/client/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,42 +48,7 @@ fn build_profile_name() -> &'static str {
}

#[derive(Parser, Clone, Debug)]
#[clap(
name = "ssv",
about = "SSV Validator client. Maintained by Sigma Prime.",
author = "Sigma Prime <[email protected]>",
long_version = LONG_VERSION.as_str(),
version = SHORT_VERSION.as_str(),
styles = get_color_style(),
disable_help_flag = true,
next_line_help = true,
term_width = 80,
display_order = 0,
)]
pub struct Node {
#[clap(
long,
global = true,
value_name = "PATH",
help = "Path to the operator key file. File name needs to end in \
`.txt` for unencrypted keys, or `.json` for encrypted keys. \
If not provided, Anchor will look for the key in the data dir. \
If provided and the file does not exist, Anchor will exit.",
display_order = 0
)]
pub key_file: Option<PathBuf>,

#[clap(
long,
global = true,
value_name = "PATH",
help = "Path to the password used to decrypt the operator private key. \
If not provided but required, Anchor will request the password interactively.",
display_order = 0
)]
pub password_file: Option<PathBuf>,

// External APIs
pub struct ExternalApis {
#[clap(
long,
value_name = "NETWORK_ADDRESSES",
Expand Down Expand Up @@ -137,8 +102,10 @@ pub struct Node {
display_order = 0
)]
pub execution_nodes_tls_certs: Option<Vec<PathBuf>>,
}

// REST API related arguments
#[derive(Parser, Clone, Debug)]
pub struct HttpApi {
#[clap(
long,
help = "Enable the RESTful HTTP API server. Disabled by default.",
Expand Down Expand Up @@ -198,8 +165,52 @@ pub struct Node {
requires = "http"
)]
pub http_allow_origin: Option<String>,
}

#[derive(Parser, Clone, Debug)]
pub struct MetricsOptions {
#[clap(
long,
help = "Enable the Prometheus metrics HTTP server. Disabled by default.",
display_order = 0,
help_heading = FLAG_HEADER,
)]
pub metrics: bool,

// Network related arguments
#[clap(
long,
value_name = "ADDRESS",
help = "Set the listen address for the Prometheus metrics HTTP server.",
default_value_if("metrics", ArgPredicate::IsPresent, "127.0.0.1"),
display_order = 0,
requires = "metrics"
)]
pub metrics_address: Option<IpAddr>,

#[clap(
long,
value_name = "PORT",
help = "Set the listen TCP port for the Prometheus metrics HTTP server.",
display_order = 0,
default_value_if("metrics", ArgPredicate::IsPresent, "5164"),
requires = "metrics"
)]
pub metrics_port: Option<u16>,

#[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,
}

#[derive(Parser, Clone, Debug)]
pub struct NetworkOptions {
#[clap(
long,
value_name = "ADDRESS",
Expand Down Expand Up @@ -272,66 +283,6 @@ pub struct Node {
)]
pub quic_port6: Option<u16>,

#[clap(
long,
help = "Sets all listening TCP/UDP ports to 0, allowing the OS to choose some \
arbitrary free ports.",
action = ArgAction::SetTrue,
hide = true,
)]
pub use_zero_ports: 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",
help = "Set the listen address for the Prometheus metrics HTTP server.",
default_value_if("metrics", ArgPredicate::IsPresent, "127.0.0.1"),
display_order = 0,
requires = "metrics"
)]
pub metrics_address: Option<IpAddr>,

#[clap(
long,
value_name = "PORT",
help = "Set the listen TCP port for the Prometheus metrics HTTP server.",
display_order = 0,
default_value_if("metrics", ArgPredicate::IsPresent, "5164"),
requires = "metrics"
)]
pub metrics_port: Option<u16>,

#[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,
// TODO: Metrics CORS Origin
// https://github.com/sigp/anchor/issues/249
#[clap(
long,
global = true,
help = "Prints help information",
action = clap::ArgAction::HelpLong,
display_order = 0,
help_heading = FLAG_HEADER
)]
help: Option<bool>,

#[clap(
long,
global = true,
Expand Down Expand Up @@ -435,38 +386,48 @@ pub struct Node {

#[clap(
long,
help = "Disable slashing protection for all validator clients. DO NOT ENABLE THIS UNLESS YOU HAVE A MORE THAN SUFFICIENT REASON TO",
hide = true,
display_order = 0
help = "Disable the latency measurement service.",
display_order = 0,
help_heading = FLAG_HEADER,
)]
pub disable_slashing_protection: bool,
pub disable_latency_measurement_service: bool,

// debugging stuff
#[clap(
long,
hide = true,
help = "Act as if we were a certain operator, except for sending messages."
help = "Disables gossipsub peer scoring.",
display_order = 0,
help_heading = FLAG_HEADER,
)]
pub impostor: Option<u64>,
pub disable_gossipsub_peer_scoring: bool,
}

// Performance options
#[derive(Parser, Clone, Debug)]
pub struct SecurityOptions {
#[clap(
long,
help = "The number of maximum concurrent workers. Defaults to logical cores.",
hide = true,
global = true,
value_name = "PATH",
help = "Path to the operator key file. File name needs to end in \
`.txt` for unencrypted keys, or `.json` for encrypted keys. \
If not provided, Anchor will look for the key in the data dir. \
If provided and the file does not exist, Anchor will exit.",
display_order = 0
)]
pub max_workers: Option<usize>,
pub key_file: Option<PathBuf>,

#[clap(
long,
value_delimiter = ',',
help = "Override size for a specific queue. Needs to be of the format \"queue_name=42\".",
hide = true,
global = true,
value_name = "PATH",
help = "Path to the password used to decrypt the operator private key. \
If not provided but required, Anchor will request the password interactively.",
display_order = 0
)]
pub work_queue_size: Vec<String>,
pub password_file: Option<PathBuf>,
}

#[derive(Parser, Clone, Debug)]
pub struct PayloadBuildingOptions {
#[clap(
long,
value_name = "INTEGER",
Expand All @@ -486,7 +447,7 @@ pub struct Node {
headers during proposals and will sign over headers. Useful for outsourcing \
execution payload construction during proposals.",
display_order = 0,
help_heading = FLAG_HEADER
help_heading = FLAG_HEADER,
)]
pub builder_proposals: bool,

Expand All @@ -507,25 +468,97 @@ pub struct Node {
help = "If this flag is set, Anchor will always prefer blocks \
constructed by builders, regardless of payload value.",
display_order = 0,
help_heading = FLAG_HEADER
help_heading = FLAG_HEADER,
)]
pub prefer_builder_proposals: bool,
}

#[derive(Parser, Clone, Debug)]
#[clap(
name = "ssv",
about = "SSV Validator client. Maintained by Sigma Prime.",
author = "Sigma Prime <[email protected]>",
long_version = LONG_VERSION.as_str(),
version = SHORT_VERSION.as_str(),
styles = get_color_style(),
disable_help_flag = true,
next_line_help = true,
term_width = 80,
display_order = 0,
)]
pub struct Node {
#[clap(flatten)]
pub security_options: SecurityOptions,

#[clap(flatten)]
pub external_apis: ExternalApis,

#[clap(flatten)]
pub http_api: HttpApi,

#[clap(flatten)]
pub metrics_options: MetricsOptions,

#[clap(flatten)]
pub network_options: NetworkOptions,

#[clap(flatten)]
pub payload_building_options: PayloadBuildingOptions,

#[clap(
long,
help = "Disable the latency measurement service.",
display_order = 0,
help_heading = FLAG_HEADER
help = "Sets all listening TCP/UDP ports to 0, allowing the OS to choose some \
arbitrary free ports.",
action = ArgAction::SetTrue,
hide = true,
)]
pub disable_latency_measurement_service: bool,
pub use_zero_ports: bool,

// TODO: Metrics CORS Origin
// https://github.com/sigp/anchor/issues/249
#[clap(
long,
help = "Disables gossipsub peer scoring.",
global = true,
help = "Prints help information",
action = clap::ArgAction::HelpLong,
display_order = 0,
help_heading = FLAG_HEADER
)]
pub disable_gossipsub_peer_scoring: bool,
help: Option<bool>,

#[clap(
long,
help = "Disable slashing protection for all validator clients. DO NOT ENABLE THIS UNLESS YOU HAVE A MORE THAN SUFFICIENT REASON TO",
hide = true,
display_order = 0
)]
pub disable_slashing_protection: bool,

// debugging stuff
#[clap(
long,
hide = true,
help = "Act as if we were a certain operator, except for sending messages."
)]
pub impostor: Option<u64>,

// Performance options
#[clap(
long,
help = "The number of maximum concurrent workers. Defaults to logical cores.",
hide = true,
display_order = 0
)]
pub max_workers: Option<usize>,

#[clap(
long,
value_delimiter = ',',
help = "Override size for a specific queue. Needs to be of the format \"queue_name=42\".",
hide = true,
display_order = 0
)]
pub work_queue_size: Vec<String>,

#[clap(long, help = "Disables gossipsub topic scoring.", hide = true)]
pub disable_gossipsub_topic_scoring: bool,
Expand Down
Loading