diff --git a/Cargo.lock b/Cargo.lock index 674c2087f..7f4ca8f67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1772,6 +1772,7 @@ dependencies = [ "http_metrics", "hyper 1.6.0", "keygen", + "keysplit", "logging 0.1.0", "message_receiver", "message_sender", diff --git a/anchor/client/Cargo.toml b/anchor/client/Cargo.toml index 73f65221f..fc4ba16ba 100644 --- a/anchor/client/Cargo.toml +++ b/anchor/client/Cargo.toml @@ -24,6 +24,7 @@ http_api = { workspace = true } http_metrics = { workspace = true } hyper = { workspace = true } keygen = { workspace = true } +keysplit = { workspace = true } logging = { workspace = true } message_receiver = { workspace = true } message_sender = { workspace = true } diff --git a/anchor/client/src/cli.rs b/anchor/client/src/cli.rs index c01af5500..bf26ea1cb 100644 --- a/anchor/client/src/cli.rs +++ b/anchor/client/src/cli.rs @@ -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 ", - 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, - - #[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, - - // External APIs +pub struct ExternalApis { #[clap( long, value_name = "NETWORK_ADDRESSES", @@ -137,8 +102,10 @@ pub struct Node { display_order = 0 )] pub execution_nodes_tls_certs: Option>, +} - // REST API related arguments +#[derive(Parser, Clone, Debug)] +pub struct HttpApi { #[clap( long, help = "Enable the RESTful HTTP API server. Disabled by default.", @@ -198,8 +165,52 @@ pub struct Node { requires = "http" )] pub http_allow_origin: Option, +} + +#[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, + + #[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, + + #[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", @@ -272,66 +283,6 @@ pub struct Node { )] pub quic_port6: Option, - #[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, - - #[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, - - #[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, - #[clap( long, global = true, @@ -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, + 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, + pub key_file: Option, #[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, + pub password_file: Option, +} +#[derive(Parser, Clone, Debug)] +pub struct PayloadBuildingOptions { #[clap( long, value_name = "INTEGER", @@ -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, @@ -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 ", + 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, + + #[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, + + // Performance options + #[clap( + long, + help = "The number of maximum concurrent workers. Defaults to logical cores.", + hide = true, + display_order = 0 + )] + pub max_workers: Option, + + #[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, #[clap(long, help = "Disables gossipsub topic scoring.", hide = true)] pub disable_gossipsub_topic_scoring: bool, diff --git a/anchor/client/src/config.rs b/anchor/client/src/config.rs index a909f389f..dc7573e3d 100644 --- a/anchor/client/src/config.rs +++ b/anchor/client/src/config.rs @@ -124,16 +124,16 @@ impl Config { pub fn from_cli(cli_args: &Node, global_config: GlobalConfig) -> Result { let mut config = Config::new(global_config); - config.key_file = cli_args.key_file.clone(); - config.password_file = cli_args.password_file.clone(); + config.key_file = cli_args.security_options.key_file.clone(); + config.password_file = cli_args.security_options.password_file.clone(); - if let Some(ref beacon_nodes) = cli_args.beacon_nodes { + if let Some(ref beacon_nodes) = cli_args.external_apis.beacon_nodes { parse_urls(&mut config.beacon_nodes, beacon_nodes, "beacon node")?; } - if let Some(ref execution_rpc) = cli_args.execution_rpc { + if let Some(ref execution_rpc) = cli_args.external_apis.execution_rpc { parse_urls(&mut config.execution_nodes, execution_rpc, "execution RPC")?; } - if let Some(ref execution_ws) = cli_args.execution_ws { + if let Some(ref execution_ws) = cli_args.external_apis.execution_ws { let ws = SensitiveUrl::parse(execution_ws).map_err(|e| format!("Unable to parse URL: {e:?}"))?; config.execution_nodes_websocket = ws; @@ -145,7 +145,7 @@ pub fn from_cli(cli_args: &Node, global_config: GlobalConfig) -> Result config.network.boot_nodes_enr.push(enr), Err(_) => { @@ -163,7 +163,7 @@ pub fn from_cli(cli_args: &Node, global_config: GlobalConfig) -> Result Result Result Result Result match &maybe_ipv4 { Some(first_ipv4_addr) => { return Err(format!( - "When setting the --listen-address option twice, use an IpV4 address and an Ipv6 address. \ + "When setting the --listen-addresses option twice, use an IpV4 address and an Ipv6 address. \ Got two IpV4 addresses {first_ipv4_addr} and {v4_addr}" )); } @@ -292,7 +298,7 @@ pub fn parse_listening_addresses(cli_args: &Node) -> Result match &maybe_ipv6 { Some(first_ipv6_addr) => { return Err(format!( - "When setting the --listen-address option twice, use an IpV4 address and an Ipv6 address. \ + "When setting the --listen-addresses option twice, use an IpV4 address and an Ipv6 address. \ Got two IpV6 addresses {first_ipv6_addr} and {v6_addr}" )); } @@ -309,19 +315,19 @@ pub fn parse_listening_addresses(cli_args: &Node) -> Result { // A single ipv6 address was provided. Set the ports - if cli_args.port6.is_some() { + if cli_args.network_options.port6.is_some() { warn!( "When listening only over IPv6, use the --port flag. The value of --port6 will be ignored." ); } - if cli_args.discovery_port6.is_some() { + if cli_args.network_options.discovery_port6.is_some() { warn!( "When listening only over IPv6, use the --discovery-port flag. The value of --discovery-port6 will be ignored." ) } - if cli_args.quic_port6.is_some() { + if cli_args.network_options.quic_port6.is_some() { warn!( "When listening only over IPv6, use the --quic-port flag. The value of --quic-port6 will be ignored." ) @@ -335,7 +341,7 @@ pub fn parse_listening_addresses(cli_args: &Node) -> Result Result Result Result Result Result Result Result Result<(), Box> { + let current_dir = env::current_dir()?; + let docs_path = current_dir.join("docs").join("docs").join("pages"); + + fs::create_dir_all(&docs_path)?; + + let node_path = docs_path.join("cli-node.mdx"); + let keygen_path = docs_path.join("cli-keygen.mdx"); + let keysplit_path = docs_path.join("cli-keysplit.mdx"); + + fs::write(&node_path, Self::generate_node_docs())?; + fs::write(&keygen_path, Self::generate_keygen_docs())?; + fs::write(&keysplit_path, Self::generate_keysplit_docs())?; + + info!("Documentation files generated:"); + info!("- {}", node_path.display()); + info!("- {}", keygen_path.display()); + info!("- {}", keysplit_path.display()); + + Ok(()) + } + + /// Generate node documentation + fn generate_node_docs() -> String { + let mut doc = String::new(); + + doc.push_str( + r#"# Node Command + +The `node` command starts the anchor client as a SSV operator node. + +```bash +anchor node [OPTIONS] +``` + +## Options + +"#, + ); + + let sections = [ + ("External APIs", ExternalApis::command()), + ("HTTP API", HttpApi::command()), + ("Metrics Options", MetricsOptions::command()), + ("Network Options", NetworkOptions::command()), + ("Security Options", SecurityOptions::command()), + ( + "Payload Building Options", + PayloadBuildingOptions::command(), + ), + ("Logging Options", FileLoggingFlags::command()), + ]; + + for (section_name, command) in sections { + doc.push_str(&Self::build_options_table(command, section_name)); + } + + doc.push_str( + r#"## Examples + +```bash +anchor node \ + --network hoodi \ + --datadir /data/anchor \ + --beacon-nodes https://beacon1.example.com,https://beacon2.example.com \ + --execution-rpc https://execution1.example.com,https://execution2.example.com \ + --execution-ws wss://execution1.example.com \ + --listen-addresses 10.0.0.10 \ + --port 9100 \ + --http \ + --http-address 127.0.0.1 \ + --http-port 9200 \ + --unencrypted-http-transport \ + --metrics \ + --metrics-address 127.0.0.1 \ + --metrics-port 9300 \ + --password-file /path/to/your/password +``` +"#, + ); + + doc + } + + /// Generate keygen documentation + fn generate_keygen_docs() -> String { + let mut doc = String::new(); + + doc.push_str( + r#"# Keygen Command + +The `keygen` command generates RSA keys for SSV operator identification. + +```bash +anchor keygen [OPTIONS] +``` + +"#, + ); + + doc.push_str(&Self::build_options_table(Keygen::command(), "Options")); + + doc.push_str(r#"## Examples + +### Basic key generation + +This will create an unencrypted `private_key.txt` file containing the newly generated private key and a `public_key.txt` file with the BASE64 encoded public key used for registering the operator. + +```bash +anchor keygen +``` + +### Encrypted key generation + +This will create a `encrypted_private_key.json` file encrypted with the provided password and a `public_key.txt` file with the BASE64 encoded public key used for registering the operator. The password must be provided via `--password-file` or interactively when running Anchor. + +```bash +anchor keygen --encrypt --output-path /path/to/keys +```"#); + + doc + } + + /// Generate keysplit documentation + fn generate_keysplit_docs() -> String { + let mut doc = String::new(); + + doc.push_str(r#"# Keysplit Command + +The `keysplit` command is used to split validator keys for distributed validation on the SSV network. + +```bash +anchor keysplit [OPTIONS] +``` + +## Subcommands + +- `manual` - Split keys with manually provided operator data +- `onchain` - Split keys using operator data from the blockchain + +"#); + + doc.push_str(&Self::build_options_table( + SharedKeygenOptions::command(), + "Shared Options", + )); + + doc.push_str( + r#"## Manual Keysplit Subcommand + +```bash +anchor keysplit manual [OPTIONS] +``` + +"#, + ); + + doc.push_str(&Self::build_options_table( + Manual::command(), + "Manual-specific Options", + )); + + doc.push_str( + r#"### Example + +```bash +anchor keysplit manual \ + --keystore-path /path/to/validator_keystore.json \ + --password "your_keystore_password" \ + --owner 0x123abc... \ + --operators 1,2,3,4 \ + --output-path /path/to/output.json \ + --nonce 0 \ + --public-keys key1,key2,key3,key4 +``` + +"#, + ); + + doc.push_str( + r#"## Onchain Keysplit Subcommand + +```bash +anchor keysplit onchain [OPTIONS] +``` + +"#, + ); + + doc.push_str(&Self::build_options_table( + Onchain::command(), + "Onchain-specific Options", + )); + + doc.push_str(r#"### Example + +```bash +anchor keysplit onchain \ + --keystore-path /path/to/validator_keystore.json \ + --password "your_keystore_password" \ + --owner 0x123abc... \ + --operators 1,2,3,4 \ + --output-path /path/to/output.json \ + --rpc https://eth-mainnet.provider.com \ + --network mainnet +``` + +## Output + +These commands will generate a JSON file to be uploaded to the SSV network webapp when registering a validator. +"#); + + doc + } + + fn build_options_table(command: Command, section_name: &str) -> String { + let mut table = format!( + r#"### {} + +| Option | Description | Default | +| --- | --- | --- | +"#, + section_name + ); + + for arg in command.get_arguments() { + if matches!(arg.get_id().as_str(), "help" | "version") { + continue; + } + + let id = arg.get_id().as_str(); + let option_name = Self::format_option_name(id, arg); + let description = arg + .get_help() + .map(|h| { + h.to_string() + .replace("<", "<") + .replace(">", ">") + .replace("\n", "
") + }) + .unwrap_or_else(|| "No description available".to_string()); + + let default = if arg.get_default_values().is_empty() { + "None".to_string() + } else { + let values: Vec = arg + .get_default_values() + .iter() + .map(|v| v.to_string_lossy().to_string()) + .collect(); + format!("`{}`", values.join(", ")) + }; + + table.push_str(&format!( + "| `{}` | {} | {} |\n", + option_name, description, default + )); + } + + table.push('\n'); + table + } + + fn format_option_name(id: &str, arg: &clap::Arg) -> String { + let mut name = format!("--{}", id.replace('_', "-")); + + if arg.get_action().takes_values() { + let type_hint = match id { + id if id.contains("address") => "ADDRESS", + id if id.contains("port") => "PORT", + id if id.contains("url") || id.contains("nodes") => "URLS", + id if id.contains("file") || id.contains("path") => "PATH", + id if id.contains("dir") => "DIR", + id if id.contains("level") => "LEVEL", + id if id.contains("size") => "SIZE", + id if id.contains("number") => "NUMBER", + id if id.contains("factor") => "FACTOR", + id if id.contains("origin") => "ORIGIN", + _ => "VALUE", + }; + name.push_str(&format!(" <{}>", type_hint)); + } + + name + } +} diff --git a/anchor/client/src/lib.rs b/anchor/client/src/lib.rs index bdb0554a1..6f85592f8 100644 --- a/anchor/client/src/lib.rs +++ b/anchor/client/src/lib.rs @@ -1,5 +1,6 @@ pub mod cli; pub mod config; +pub mod docs; mod key; mod notifier; diff --git a/anchor/keysplit/src/lib.rs b/anchor/keysplit/src/lib.rs index 8b8455b87..c188e58cd 100644 --- a/anchor/keysplit/src/lib.rs +++ b/anchor/keysplit/src/lib.rs @@ -1,6 +1,6 @@ use std::fs; -pub use cli::{KeygenSubcommands, Keysplit, Manual, Onchain}; +pub use cli::{KeygenSubcommands, Keysplit, Manual, Onchain, SharedKeygenOptions}; use error::KeysplitError; use global_config::GlobalConfig; use openssl::{pkey::Public, rsa::Rsa}; diff --git a/anchor/src/main.rs b/anchor/src/main.rs index f8c9795a7..998caed50 100644 --- a/anchor/src/main.rs +++ b/anchor/src/main.rs @@ -1,5 +1,5 @@ use clap::Parser; -use client::{Client, Node, config}; +use client::{Client, Node, config, docs::DocGenerator}; use environment::Environment; use global_config::{GlobalConfig, GlobalFlags}; use keygen::Keygen; @@ -30,6 +30,13 @@ pub enum AnchorSubcommands { Node(Box), Keysplit(Keysplit), Keygen(Keygen), + + #[clap( + name = "docgen", + about = "Generate documentation for CLI options", + hide = true + )] + Docgen, } fn main() { @@ -80,6 +87,11 @@ fn main() { error!("Keygen error: {:?}", e); } } + AnchorSubcommands::Docgen => { + if let Err(e) = DocGenerator::generate_and_write_docs() { + error!("Failed to generate documentation: {}", e); + } + } } } diff --git a/docs/docs/pages/cli-keygen.mdx b/docs/docs/pages/cli-keygen.mdx index e2a2da63a..57bac91f7 100644 --- a/docs/docs/pages/cli-keygen.mdx +++ b/docs/docs/pages/cli-keygen.mdx @@ -1,6 +1,6 @@ -## Keygen Command +# Keygen Command -The `keygen` command generates RSA keys for SSV operator identification +The `keygen` command generates RSA keys for SSV operator identification. ```bash anchor keygen [OPTIONS] @@ -9,30 +9,25 @@ anchor keygen [OPTIONS] ### Options | Option | Description | Default | -| --- | --- | ---| -|`--output-path ` | Directory to store generated keys | Current Directory | -|`--force` | Force overwrite of existing key files | Disabled | -|`--encrypt` | Encrypt the private key | Disabled | -|`--password-file ` | Path to a file containing the key password | None | -|`--help` | Display help information | | +| --- | --- | --- | +| `--force` | Force file overwrite | `false` | +| `--encrypt` | Enable password encryption. Password is read from terminal or via --password-file | None | +| `--password-file ` | Path to a file containing the password to use | None | -### Examples +## Examples -This will create an unencrypted `private_key.txt` file containing the newly generated -private key and a `public_key.txt` file with the BASE64 encoded public key used for -registering the operator. +### Basic key generation + +This will create an unencrypted `private_key.txt` file containing the newly generated private key and a `public_key.txt` file with the BASE64 encoded public key used for registering the operator. ```bash anchor keygen ``` -This will create a `encrypted_private_key.json` file encrypted with the provided password -and a `public_key.txt` file with the BASE64 encoded public key used for registering the -operator. The password must be provided via `--password-file` or interactively when running -Anchor. +### Encrypted key generation + +This will create a `encrypted_private_key.json` file encrypted with the provided password and a `public_key.txt` file with the BASE64 encoded public key used for registering the operator. The password must be provided via `--password-file` or interactively when running Anchor. ```bash anchor keygen --encrypt --output-path /path/to/keys -``` - -Anchor will look for the key file in the default directory `~/.anchor/{network}`, or the directory specified by `--datadir`. \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/docs/pages/cli-keysplit.mdx b/docs/docs/pages/cli-keysplit.mdx index 58ed94e32..59626c499 100644 --- a/docs/docs/pages/cli-keysplit.mdx +++ b/docs/docs/pages/cli-keysplit.mdx @@ -1,4 +1,4 @@ -## Keysplit Command +# Keysplit Command The `keysplit` command is used to split validator keys for distributed validation on the SSV network. @@ -6,50 +6,40 @@ The `keysplit` command is used to split validator keys for distributed validatio anchor keysplit [OPTIONS] ``` -Where `` is one of: +## Subcommands - `manual` - Split keys with manually provided operator data - `onchain` - Split keys using operator data from the blockchain -Both subcommands share these Options +### Shared Options | Option | Description | Default | -| --- | --- | ---| -| `--keystore-path ` | Path to validator keystore file | Required | -| `--password ` | Password for the validator keystore | Required | -| `--owner
` | ETH address that owns the validator | Required | -| `--output-path ` | Path for output file | Required | -| `--operators ` | Comma-separated list of operator IDs (must be 4, 7, 10, or 13) | Required| -| `--help` | Display help information | | +| --- | --- | --- | +| `--keystore-path ` | Path to the validator keystore file | None | +| `--password ` | Password for the validator keystore | None | +| `--owner ` | EOA address that owns the validator | None | +| `--output-path ` | Path for output | None | +| `--operators ` | Operators to split key among | None | -### Manual Keysplit Subcommand +## Manual Keysplit Subcommand ```bash anchor keysplit manual [OPTIONS] ``` -Additional Options: +### Manual-specific Options | Option | Description | Default | -| --- | --- | ---| -| `--nonce ` | Nonce for the owner address | Required | -| `--public-keys ` | Comma-separated RSA public keys for operators | Required| +| --- | --- | --- | +| `--keystore-path ` | Path to the validator keystore file | None | +| `--password ` | Password for the validator keystore | None | +| `--owner ` | EOA address that owns the validator | None | +| `--output-path ` | Path for output | None | +| `--operators ` | Operators to split key among | None | +| `--nonce ` | Nonce for the owner address | None | +| `--public-keys ` | RSA public keys for the operators | None | -### Onchain Keysplit Subcommand - -```bash -anchor keysplit onchain [OPTIONS] -``` - -Additional Options: - -| Option | Description | Default | -| --- | --- | ---| -| `--rpc ` | RPC endpoint to access L1 data | Required| - -#### Examples - -Manual key splitting +### Example ```bash anchor keysplit manual \ @@ -62,7 +52,24 @@ anchor keysplit manual \ --public-keys key1,key2,key3,key4 ``` -Onchain key splitting +## Onchain Keysplit Subcommand + +```bash +anchor keysplit onchain [OPTIONS] +``` + +### Onchain-specific Options + +| Option | Description | Default | +| --- | --- | --- | +| `--keystore-path ` | Path to the validator keystore file | None | +| `--password ` | Password for the validator keystore | None | +| `--owner ` | EOA address that owns the validator | None | +| `--output-path ` | Path for output | None | +| `--operators ` | Operators to split key among | None | +| `--rpc ` | RPC endpoint to access L1 data | None | + +### Example ```bash anchor keysplit onchain \ @@ -75,5 +82,6 @@ anchor keysplit onchain \ --network mainnet ``` -These commands will generate a json file to be uploaded to the SSV network webapp when -registering a validator. +## Output + +These commands will generate a JSON file to be uploaded to the SSV network webapp when registering a validator. diff --git a/docs/docs/pages/cli-node.mdx b/docs/docs/pages/cli-node.mdx index db7aa9ecc..f89c49ff6 100644 --- a/docs/docs/pages/cli-node.mdx +++ b/docs/docs/pages/cli-node.mdx @@ -1,4 +1,4 @@ -## Node Command +# Node Command The `node` command starts the anchor client as a SSV operator node. @@ -6,86 +6,89 @@ The `node` command starts the anchor client as a SSV operator node. anchor node [OPTIONS] ``` -### Options +## Options -#### External APIs +### External APIs | Option | Description | Default | -| --- | --- | ---| -| `--beacon-nodes ` | Comma-separated beacon node HTTP URLs | `http://localhost:5052`| -| `--execution-rpc ` | Comma-separated execution node RPC URLs | `http://localhost:8545` | -| `--execution-ws ` | Execution node websocket URL | `ws://localhost:8546` | -| `--beacon-node-tls-certs ` | Certificate files for beacon node connections | None | -| `--execution-node-tls-certs ` | Certificate files for execution node connections | None | +| --- | --- | --- | +| `--beacon-nodes ` | Comma-separated addresses to one or more beacon node HTTP APIs. Default is http://localhost:5052. | None | +| `--execution-rpc ` | Comma-separated addresses to one or more execution node JSON-RPC APIs. Default is http://localhost:8545. | None | +| `--execution-ws ` | Address of execution node WS API. Default is ws://localhost:8546. | None | +| `--beacon-nodes-tls-certs ` | 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. | None | +| `--execution-nodes-tls-certs ` | Comma-separated paths to custom TLS certificates to use when connecting to an execution 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 | None | -#### HTTP API +### HTTP API | Option | Description | Default | -| --- | --- | ---| -| `--http` | Enable the HTTP API sever | Disabled | -| `--http-address
` | Listen address for HTTP API | None | -| `--http-port ` | Listen port for HTTP API | `5062` if `--http` is set | -| `--http-allow-origin ` | Set CORS allowed origin | None | -| `--unencrypted-http-transport` | Safety flag to acknowledge HTTP is unencrypted | Required if `--http-address` is set| +| --- | --- | --- | +| `--http` | Enable the RESTful HTTP API server. Disabled by default. | None | +| `--http-address
` | 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 tunneling. | None | +| `--unencrypted-http-transport` | 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. | None | +| `--http-port ` | Set the listen TCP port for the RESTful HTTP API server. | None | +| `--http-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 is supplied, the CORS allowed origin is set to the listen address of this server (e.g., http://localhost:5062). | None | -#### Metrics Options +### Metrics Options | Option | Description | Default | -| --- | --- | ---| -| `--metrics` | Enable metrics server | Disabled | -| `--metrics-address
` | Listen address for metrics server | `127.0.0.1` if `--metrics` is set | -| `--metrics-port ` | Listen port for metrics server | `5164` if `--metrics` is set | +| --- | --- | --- | +| `--metrics` | Enable the Prometheus metrics HTTP server. Disabled by default. | None | +| `--metrics-address
` | Set the listen address for the Prometheus metrics HTTP server. | None | +| `--metrics-port ` | Set the listen TCP port for the Prometheus metrics HTTP server. | None | +| `--enable-high-validator-count-metrics` | 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. | None | -#### Network Options +### Network Options | Option | Description | Default | -| --- | --- | ---| -| `--listen-address
` | Network address to listen for UDP & TCP connections | `0.0.0.0` | -| `--port ` | Base port for all network connections | `13001` | -| `--port6 ` | Base port for IPv6 network connections | Same as `--port` | -| `--discovery-port ` | UDP port for discovery | Same as `--port` if specified, otherwise `12001` | -| `--discovery-port6 ` | UDP port for IPv6 discovery | Same as `--discovery-port` | -| `--quic-port ` | UDP port for QUIC protocol | `--port` + 1 | -| `--quic-port6 ` | UDP port for IPv6 QUIC protocol | `--port6` + 1 | -| `--boot-nodes ` | Comma-separated ENRs or Multiaddrs to bootstrap the network | None| -| `--enr-address
` | IPv4 address to broadcast in the node's ENR | None | -| `--enr-address6
` | IPv6 address to broadcast in the node's ENR | None | -| `--enr-udp-port ` | UDP port to advertise in the node's ENR | None | -| `--enr-tcp-port ` | TCP port to advertise in the node's ENR | None | -| `--enr-quic-port ` | QUIC port to advertise in the node's ENR | None | -| `--enr-udp6-port ` | IPv6 UDP port to advertise in the node's ENR | None | -| `--enr-tcp6-port ` | IPv6 TCP port to advertise in the node's ENR | None | -| `--enr-quic6-port ` | IPv6 QUIC port to advertise in the node's ENR | None | -| `--subscribe-all-subnets` | Subscribe to all subnets regardless of committee membership | Disabled| - -#### Security Options +| --- | --- | --- | +| `--listen-addresses
` | The address anchor will listen for UDP and TCP connections. To listen over IpV4 and IpV6 set this flag twice with the different values.
Examples:
- --listen-addresses '0.0.0.0' will listen over IPv4.
- --listen-addresses '::' will listen over IPv6.
- --listen-addresses '0.0.0.0' --listen-addresses '::' will listen over both IPv4 and IPv6. The order of the given addresses is not relevant. However, multiple IPv4, or multiple IPv6 addresses will not be accepted. | `0.0.0.0` | +| `--port ` | The TCP/UDP ports to listen on. There are two UDP ports. The discovery UDP and TCP port will be set to this value. The Quic UDP port will be set to this value + 1. The discovery port can be modified by the --discovery-port flag and the quic port can be modified by the --quic-port flag. If listening over both IPv4 and IPv6 the --port flag will apply to the IPv4 address and --port6 to the IPv6 address. If this flag is not set, the default values will be 12001 for discovery and 13001 for TCP. | None | +| `--port6 ` | The TCP/UDP ports to listen on over IPv6 when listening over both IPv4 and IPv6. The Quic UDP port will be set to this value + 1. | None | +| `--discovery-port ` | The UDP port that discovery will listen on. Defaults to --port if --port is explicitly specified, and `12001` otherwise. | None | +| `--discovery-port6 ` | The UDP port that discovery will listen on over IPv6 if listening over both IPv4 and IPv6. Defaults to `discovery_port` | None | +| `--quic-port ` | The UDP port that quic will listen on. Defaults to `port` + 1 | None | +| `--quic-port6 ` | The UDP port that quic will listen on over IPv6 if listening over both IPv4 and IPv6. Defaults to `port6` + 1 | None | +| `--boot-nodes ` | One or more comma-delimited ENRs or Multiaddrs to bootstrap the p2p network | None | +| `--enr-address
` | The IPv4 address to broadcast to other peers on how to reach this node. Set this only if you are sure other nodes can connect to your local node on this address. This will update the `ip4` ENR field accordingly. | None | +| `--enr-address6
` | The IPv6 address to broadcast to other peers on how to reach this node. Set this only if you are sure other nodes can connect to your local node on this address. This will update the `ip6` ENR field accordingly. | None | +| `--enr-udp-port ` | The UDP4 port of the local ENR. Set this only if you are sure other nodes can connect to your local node on this port over IPv4. | None | +| `--enr-tcp-port ` | The TCP4 port of the local ENR. Set this only if you are sure other nodes can connect to your local node on this port over IPv4. The --port flag is used if this is not set. | None | +| `--enr-quic-port ` | The quic UDP4 port that will be set on the local ENR. Set this only if you are sure other nodes can connect to your local node on this port over IPv4. | None | +| `--enr-udp6-port ` | The UDP6 port of the local ENR. Set this only if you are sure other nodes can connect to your local node on this port over IPv6. | None | +| `--enr-tcp6-port ` | The TCP6 port of the local ENR. Set this only if you are sure other nodes can connect to your local node on this port over IPv6. The --port6 flag is used if this is not set. | None | +| `--enr-quic6-port ` | The quic UDP6 port that will be set on the local ENR. Set this only if you are sure other nodes can connect to your local node on this port over IPv6. | None | +| `--subscribe-all-subnets` | Subscribe to all subnets, regardless of committee membership. | None | +| `--disable-latency-measurement-service` | Disable the latency measurement service. | None | +| `--disable-gossipsub-peer-scoring` | Disables gossipsub peer scoring. | None | + +### Security Options | Option | Description | Default | -| --- | --- | ---| -| `--key-file ` | Path to the operator key | Detected in data dir | -| `--password-file ` | Path to a file containing the key password | None | -| `--disable-slashing-protection` | Disable slashing protection (NOT RECOMMENDED) | None | +| --- | --- | --- | +| `--key-file ` | 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. | None | +| `--password-file ` | Path to the password used to decrypt the operator private key. If not provided but required, Anchor will request the password interactively. | None | -#### Payload Building Options +### Payload Building Options | Option | Description | Default | -| --- | --- | ---| -| `--builder-proposals` | Use external block building | Disabled | -| `--builder-boost-factor ` | Percentage multiplier for builder payload value | None | -| `--prefer-builder-proposals` | Always prefer builder blocks regardless of value | Disabled | +| --- | --- | --- | +| `--gas-limit ` | The gas limit to be used in all builder proposals for all validators managed. Note this will not necessarily be used if the gas limit set here moves too far from the previous block's gas limit. | `36000000` | +| `--builder-proposals` | If this flag is set, Anchor will query the Beacon Node for only block headers during proposals and will sign over headers. Useful for outsourcing execution payload construction during proposals. | None | +| `--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. | None | +| `--prefer-builder-proposals` | If this flag is set, Anchor will always prefer blocks constructed by builders, regardless of payload value. | None | -#### Logging Options +### Logging Options | Option | Description | Default | -| --- | --- | ---| -| `--debug-level ` | Verbosity for terminal logs | `info` | -| `--logfile-debug-level ` | Verbosity for file logs | `debug` | -| `--logfile-max-size ` | Maximum size of each log file in MB | `50` | -| `--logfile-max-number ` | Maximum number of log files to keep | `100` | -| `--logfile-dir ` | Directory to store log files | Same as `--datadir` | -| `--logfile-compression` | Compress old log files | Disabled | +| --- | --- | --- | +| `--logfile-debug-level ` | Specifies the verbosity level used when emitting logs to the log file | `DEBUG` | +| `--logfile-max-size ` | Maximum size of each log file in MB. Set to 0 to disable file logging. | `50` | +| `--logfile-max-number ` | Maximum number of log files to keep. Set to 0 to disable file logging. | `100` | +| `--logfile-dir ` | Directory path where the log file will be stored | None | +| `--logfile-compression` | If present, compress old log files. This can help reduce the space needed to store old logs. | None | +| `--logfile-color` | Enables colors in logfile. | None | -#### Examples +## Examples ```bash anchor node \ @@ -105,5 +108,3 @@ anchor node \ --metrics-port 9300 \ --password-file /path/to/your/password ``` - - diff --git a/docs/docs/pages/index.mdx b/docs/docs/pages/index.mdx index 18e9aa070..6a56007b5 100644 --- a/docs/docs/pages/index.mdx +++ b/docs/docs/pages/index.mdx @@ -57,7 +57,7 @@ anchor --help ```bash [From Release] # Specify the platform i.e aarch64-apple-darwin (for apple) x86_64-unknown-linux-gnu.tar.gz -wget https://github.com/sigp/anchor/releases/download/v0.2.0/anchor-.tar.gz +wget https://github.com/sigp/anchor/releases/download/v0.3.1/anchor-.tar.gz # Extract the file tar -xvf anchor-.tar.gz # Make it executable @@ -74,15 +74,15 @@ chmod +x anchor
-
46
+
47
Stars
-
12
+
13
Contributors
-
v0.2.0
+
v0.3.1
Version
diff --git a/docs/vocs.config.ts b/docs/vocs.config.ts index 0f2166db3..a5b2e2354 100644 --- a/docs/vocs.config.ts +++ b/docs/vocs.config.ts @@ -34,7 +34,7 @@ export default defineConfig({ { text: 'Documentation', link: '/introduction' }, { text: 'GitHub', link: 'https://github.com/sigp/anchor' }, { - text: 'v0.2.0', + text: 'v0.3.1', items: [ { text: 'Releases',