From 044e42705d97599693c5a158df0dfb36ee950ad7 Mon Sep 17 00:00:00 2001 From: int88 Date: Wed, 24 Jul 2024 10:59:25 +0000 Subject: [PATCH 1/6] implement `reth p2p rlpx ping` --- Cargo.lock | 6 +++ crates/cli/commands/Cargo.toml | 6 +++ .../cli/commands/src/{p2p.rs => p2p/mod.rs} | 9 +++- crates/cli/commands/src/p2p/rlpx.rs | 54 +++++++++++++++++++ 4 files changed, 74 insertions(+), 1 deletion(-) rename crates/cli/commands/src/{p2p.rs => p2p/mod.rs} (96%) create mode 100644 crates/cli/commands/src/p2p/rlpx.rs diff --git a/Cargo.lock b/Cargo.lock index 81b806f81836..233a67640edc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6552,6 +6552,7 @@ dependencies = [ "comfy-table", "confy", "crossterm", + "discv5", "eyre", "fdlimit", "futures", @@ -6570,12 +6571,16 @@ dependencies = [ "reth-db-api", "reth-db-common", "reth-discv4", + "reth-discv5", "reth-downloaders", + "reth-ecies", + "reth-eth-wire", "reth-evm", "reth-exex", "reth-fs-util", "reth-network", "reth-network-p2p", + "reth-network-peers", "reth-node-builder", "reth-node-core", "reth-node-events", @@ -6588,6 +6593,7 @@ dependencies = [ "reth-static-file-types", "reth-trie", "reth-trie-db", + "secp256k1", "serde", "serde_json", "tokio", diff --git a/crates/cli/commands/Cargo.toml b/crates/cli/commands/Cargo.toml index 1d9983ad8d45..bb343c79d4bf 100644 --- a/crates/cli/commands/Cargo.toml +++ b/crates/cli/commands/Cargo.toml @@ -20,11 +20,15 @@ reth-db = { workspace = true, features = ["mdbx"] } reth-db-api.workspace = true reth-db-common.workspace = true reth-downloaders.workspace = true +reth-discv5.workspace = true +reth-ecies.workspace = true +reth-eth-wire.workspace = true reth-evm.workspace = true reth-exex.workspace = true reth-fs-util.workspace = true reth-network = { workspace = true, features = ["serde"] } reth-network-p2p.workspace = true +reth-network-peers = { workspace = true, features = ["secp256k1"] } reth-node-builder.workspace = true reth-node-core.workspace = true reth-node-events.workspace = true @@ -41,6 +45,7 @@ reth-trie-db = { workspace = true, features = ["metrics"] } itertools.workspace = true futures.workspace = true tokio.workspace = true +discv5 = { workspace = true, features = ["libp2p"] } # misc ahash = "0.8" @@ -51,6 +56,7 @@ serde.workspace = true serde_json.workspace = true tracing.workspace = true backon.workspace = true +secp256k1 = { workspace = true, features = ["global-context", "rand-std", "recovery"] } # io fdlimit.workspace = true diff --git a/crates/cli/commands/src/p2p.rs b/crates/cli/commands/src/p2p/mod.rs similarity index 96% rename from crates/cli/commands/src/p2p.rs rename to crates/cli/commands/src/p2p/mod.rs index 659e63387b0d..9c1b45eb20dd 100644 --- a/crates/cli/commands/src/p2p.rs +++ b/crates/cli/commands/src/p2p/mod.rs @@ -18,6 +18,8 @@ use reth_node_core::{ }; use reth_primitives::BlockHashOrNumber; +mod rlpx; + /// `reth p2p` command #[derive(Debug, Parser)] pub struct Command { @@ -69,10 +71,12 @@ pub enum Subcommands { #[arg(value_parser = hash_or_num_value_parser)] id: BlockHashOrNumber, }, + // RLPx utilities + Rlpx(rlpx::Command), } impl Command { /// Execute `p2p` command - pub async fn execute(&self) -> eyre::Result<()> { + pub async fn execute(self) -> eyre::Result<()> { let data_dir = self.datadir.clone().resolve_datadir(self.chain.chain); let config_path = self.config.clone().unwrap_or_else(|| data_dir.config()); @@ -152,6 +156,9 @@ impl Command { let body = result.into_iter().next().unwrap(); println!("Successfully downloaded body: {body:?}") } + Subcommands::Rlpx(command) => { + command.execute().await?; + } } Ok(()) diff --git a/crates/cli/commands/src/p2p/rlpx.rs b/crates/cli/commands/src/p2p/rlpx.rs new file mode 100644 index 000000000000..fa667120ba0d --- /dev/null +++ b/crates/cli/commands/src/p2p/rlpx.rs @@ -0,0 +1,54 @@ +//! RLPx subcommand of P2P Debugging tool. + +use clap::{Parser, Subcommand}; +use discv5::Enr; +use reth_discv5::enr::EnrCombinedKeyWrapper; +use reth_ecies::stream::ECIESStream; +use reth_eth_wire::{HelloMessage, UnauthedP2PStream}; +use reth_network::config::rng_secret_key; +use reth_network_peers::{pk2id, NodeRecord}; +use secp256k1::SECP256K1; +use tokio::net::TcpStream; + +/// The arguments for the `reth p2p rlpx` command +#[derive(Parser, Debug)] +pub struct Command { + #[clap(subcommand)] + subcommand: Subcommands, +} + +impl Command { + // Execute `p2p rlpx` command. + pub async fn execute(self) -> eyre::Result<()> { + match self.subcommand { + Subcommands::Ping { node } => { + let key = rng_secret_key(); + let enr = node.parse::().unwrap(); + let enr = EnrCombinedKeyWrapper(enr).into(); + let node_record = NodeRecord::try_from(&enr).unwrap(); + let outgoing = + TcpStream::connect((node_record.address, node_record.tcp_port)).await?; + let ecies_stream = ECIESStream::connect(outgoing, key, node_record.id).await?; + + let peer_id = pk2id(&key.public_key(SECP256K1)); + let hello = HelloMessage::builder(peer_id).build(); + + let (_, their_hello) = + UnauthedP2PStream::new(ecies_stream).handshake(hello).await?; + + println!("{:#?}", their_hello); + } + } + Ok(()) + } +} + +#[derive(Subcommand, Debug)] +enum Subcommands { + /// ping node + Ping { + /// The node to ping. + #[arg(long, short)] + node: String, + }, +} From 03bb395fe573f5fc42ed6080d0def44e8ddac0df Mon Sep 17 00:00:00 2001 From: int88 Date: Wed, 24 Jul 2024 13:50:38 +0000 Subject: [PATCH 2/6] fix comments --- book/cli/reth/p2p.md | 1 + crates/cli/commands/src/p2p/rlpx.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/book/cli/reth/p2p.md b/book/cli/reth/p2p.md index 5bc48aaf02b8..1b031049fac8 100644 --- a/book/cli/reth/p2p.md +++ b/book/cli/reth/p2p.md @@ -9,6 +9,7 @@ Usage: reth p2p [OPTIONS] Commands: header Download block header body Download block body + rlpx RLPx commands help Print this message or the help of the given subcommand(s) Options: diff --git a/crates/cli/commands/src/p2p/rlpx.rs b/crates/cli/commands/src/p2p/rlpx.rs index fa667120ba0d..bf8aee6ac404 100644 --- a/crates/cli/commands/src/p2p/rlpx.rs +++ b/crates/cli/commands/src/p2p/rlpx.rs @@ -10,7 +10,7 @@ use reth_network_peers::{pk2id, NodeRecord}; use secp256k1::SECP256K1; use tokio::net::TcpStream; -/// The arguments for the `reth p2p rlpx` command +/// RLPx commands #[derive(Parser, Debug)] pub struct Command { #[clap(subcommand)] @@ -25,7 +25,7 @@ impl Command { let key = rng_secret_key(); let enr = node.parse::().unwrap(); let enr = EnrCombinedKeyWrapper(enr).into(); - let node_record = NodeRecord::try_from(&enr).unwrap(); + let node_record = NodeRecord::try_from(&enr)?; let outgoing = TcpStream::connect((node_record.address, node_record.tcp_port)).await?; let ecies_stream = ECIESStream::connect(outgoing, key, node_record.id).await?; @@ -47,8 +47,8 @@ impl Command { enum Subcommands { /// ping node Ping { - /// The node to ping. #[arg(long, short)] + /// The node to ping. node: String, }, } From 241de6a9f1dd323be06bc9e3289554310238cc81 Mon Sep 17 00:00:00 2001 From: int88 Date: Thu, 25 Jul 2024 10:46:52 +0000 Subject: [PATCH 3/6] fix comments --- Cargo.lock | 3 +-- crates/cli/commands/Cargo.toml | 3 +-- crates/cli/commands/src/p2p/rlpx.rs | 11 ++++++----- crates/net/eth-wire/src/p2pstream.rs | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 233a67640edc..5d5572f95820 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6552,7 +6552,7 @@ dependencies = [ "comfy-table", "confy", "crossterm", - "discv5", + "enr", "eyre", "fdlimit", "futures", @@ -6571,7 +6571,6 @@ dependencies = [ "reth-db-api", "reth-db-common", "reth-discv4", - "reth-discv5", "reth-downloaders", "reth-ecies", "reth-eth-wire", diff --git a/crates/cli/commands/Cargo.toml b/crates/cli/commands/Cargo.toml index bb343c79d4bf..c43d6121e93b 100644 --- a/crates/cli/commands/Cargo.toml +++ b/crates/cli/commands/Cargo.toml @@ -20,7 +20,6 @@ reth-db = { workspace = true, features = ["mdbx"] } reth-db-api.workspace = true reth-db-common.workspace = true reth-downloaders.workspace = true -reth-discv5.workspace = true reth-ecies.workspace = true reth-eth-wire.workspace = true reth-evm.workspace = true @@ -45,11 +44,11 @@ reth-trie-db = { workspace = true, features = ["metrics"] } itertools.workspace = true futures.workspace = true tokio.workspace = true -discv5 = { workspace = true, features = ["libp2p"] } # misc ahash = "0.8" human_bytes = "0.4.1" +enr.workspace = true eyre.workspace = true clap = { workspace = true, features = ["derive", "env"] } serde.workspace = true diff --git a/crates/cli/commands/src/p2p/rlpx.rs b/crates/cli/commands/src/p2p/rlpx.rs index bf8aee6ac404..181c45581c3b 100644 --- a/crates/cli/commands/src/p2p/rlpx.rs +++ b/crates/cli/commands/src/p2p/rlpx.rs @@ -1,8 +1,8 @@ //! RLPx subcommand of P2P Debugging tool. use clap::{Parser, Subcommand}; -use discv5::Enr; -use reth_discv5::enr::EnrCombinedKeyWrapper; +use enr::Enr; +use futures::StreamExt; use reth_ecies::stream::ECIESStream; use reth_eth_wire::{HelloMessage, UnauthedP2PStream}; use reth_network::config::rng_secret_key; @@ -23,8 +23,7 @@ impl Command { match self.subcommand { Subcommands::Ping { node } => { let key = rng_secret_key(); - let enr = node.parse::().unwrap(); - let enr = EnrCombinedKeyWrapper(enr).into(); + let enr = node.parse::>().unwrap(); let node_record = NodeRecord::try_from(&enr)?; let outgoing = TcpStream::connect((node_record.address, node_record.tcp_port)).await?; @@ -33,9 +32,11 @@ impl Command { let peer_id = pk2id(&key.public_key(SECP256K1)); let hello = HelloMessage::builder(peer_id).build(); - let (_, their_hello) = + let (p2p_stream, their_hello) = UnauthedP2PStream::new(ecies_stream).handshake(hello).await?; + p2p_stream.send_ping(); + println!("{:#?}", their_hello); } } diff --git a/crates/net/eth-wire/src/p2pstream.rs b/crates/net/eth-wire/src/p2pstream.rs index 466987768ead..01394e8648ce 100644 --- a/crates/net/eth-wire/src/p2pstream.rs +++ b/crates/net/eth-wire/src/p2pstream.rs @@ -302,7 +302,7 @@ impl P2PStream { } /// Queues in a _snappy_ encoded [`P2PMessage::Ping`] message. - fn send_ping(&mut self) { + pub fn send_ping(&mut self) { self.outgoing_messages.push_back(Bytes::from(alloy_rlp::encode(P2PMessage::Ping))); } } From 7eb0cfe6a8b2717edd2f3f6424021f305f6a7966 Mon Sep 17 00:00:00 2001 From: int88 Date: Fri, 2 Aug 2024 06:34:15 +0000 Subject: [PATCH 4/6] fix comments --- book/SUMMARY.md | 2 + book/cli/SUMMARY.md | 2 + book/cli/reth/p2p/rlpx.md | 104 +++++++++++++++++++++++++++++++++ book/cli/reth/p2p/rlpx/ping.md | 103 ++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+) create mode 100644 book/cli/reth/p2p/rlpx.md create mode 100644 book/cli/reth/p2p/rlpx/ping.md diff --git a/book/SUMMARY.md b/book/SUMMARY.md index 69f88b285349..f93daeaba397 100644 --- a/book/SUMMARY.md +++ b/book/SUMMARY.md @@ -62,6 +62,8 @@ - [`reth p2p`](./cli/reth/p2p.md) - [`reth p2p header`](./cli/reth/p2p/header.md) - [`reth p2p body`](./cli/reth/p2p/body.md) + - [`reth p2p rlpx`](./cli/reth/p2p/rlpx.md) + - [`reth p2p rlpx ping`](./cli/reth/p2p/rlpx/ping.md) - [`reth config`](./cli/reth/config.md) - [`reth debug`](./cli/reth/debug.md) - [`reth debug execution`](./cli/reth/debug/execution.md) diff --git a/book/cli/SUMMARY.md b/book/cli/SUMMARY.md index 5f02f1e9ee04..4cc86c7a3649 100644 --- a/book/cli/SUMMARY.md +++ b/book/cli/SUMMARY.md @@ -32,6 +32,8 @@ - [`reth p2p`](./reth/p2p.md) - [`reth p2p header`](./reth/p2p/header.md) - [`reth p2p body`](./reth/p2p/body.md) + - [`reth p2p rlpx`](./reth/p2p/rlpx.md) + - [`reth p2p rlpx ping`](./reth/p2p/rlpx/ping.md) - [`reth config`](./reth/config.md) - [`reth debug`](./reth/debug.md) - [`reth debug execution`](./reth/debug/execution.md) diff --git a/book/cli/reth/p2p/rlpx.md b/book/cli/reth/p2p/rlpx.md new file mode 100644 index 000000000000..dd73e437aa42 --- /dev/null +++ b/book/cli/reth/p2p/rlpx.md @@ -0,0 +1,104 @@ +# reth p2p rlpx + +RLPx commands + +```bash +$ reth p2p rlpx --help +Usage: reth p2p rlpx [OPTIONS] + +Commands: + ping ping node + help Print this message or the help of the given subcommand(s) + +Options: + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.stdout.format + The format to use for logs written to stdout + + [default: terminal] + + Possible values: + - json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging + - log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications + - terminal: Represents terminal-friendly formatting for logs + + --log.stdout.filter + The filter to use for logs written to stdout + + [default: ] + + --log.file.format + The format to use for logs written to the log file + + [default: terminal] + + Possible values: + - json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging + - log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications + - terminal: Represents terminal-friendly formatting for logs + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file diff --git a/book/cli/reth/p2p/rlpx/ping.md b/book/cli/reth/p2p/rlpx/ping.md new file mode 100644 index 000000000000..32f1495b4a72 --- /dev/null +++ b/book/cli/reth/p2p/rlpx/ping.md @@ -0,0 +1,103 @@ +# reth p2p rlpx ping + +ping node + +```bash +$ reth p2p rlpx ping --help +Usage: reth p2p rlpx ping [OPTIONS] --node + +Options: + -n, --node + The node to ping + + --instance + Add a new instance of a node. + + Configures the ports of the node to avoid conflicts with the defaults. This is useful for running multiple nodes on the same machine. + + Max number of instances is 200. It is chosen in a way so that it's not possible to have port numbers that conflict with each other. + + Changes to the following port numbers: - `DISCOVERY_PORT`: default + `instance` - 1 - `AUTH_PORT`: default + `instance` * 100 - 100 - `HTTP_RPC_PORT`: default - `instance` + 1 - `WS_RPC_PORT`: default + `instance` * 2 - 2 + + [default: 1] + + -h, --help + Print help (see a summary with '-h') + +Logging: + --log.stdout.format + The format to use for logs written to stdout + + [default: terminal] + + Possible values: + - json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging + - log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications + - terminal: Represents terminal-friendly formatting for logs + + --log.stdout.filter + The filter to use for logs written to stdout + + [default: ] + + --log.file.format + The format to use for logs written to the log file + + [default: terminal] + + Possible values: + - json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging + - log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications + - terminal: Represents terminal-friendly formatting for logs + + --log.file.filter + The filter to use for logs written to the log file + + [default: debug] + + --log.file.directory + The path to put log files in + + [default: /logs] + + --log.file.max-size + The maximum size (in MB) of one log file + + [default: 200] + + --log.file.max-files + The maximum amount of log files that will be stored. If set to 0, background file logging is disabled + + [default: 5] + + --log.journald + Write logs to journald + + --log.journald.filter + The filter to use for logs written to journald + + [default: error] + + --color + Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting + + [default: always] + + Possible values: + - always: Colors on + - auto: Colors on + - never: Colors off + +Display: + -v, --verbosity... + Set the minimum log level. + + -v Errors + -vv Warnings + -vvv Info + -vvvv Debug + -vvvvv Traces (warning: very verbose!) + + -q, --quiet + Silence all log output +``` \ No newline at end of file From d41355d877cbe74e3413e698a119491f1e35b14e Mon Sep 17 00:00:00 2001 From: int88 Date: Fri, 2 Aug 2024 07:03:35 +0000 Subject: [PATCH 5/6] fix --- crates/cli/commands/src/p2p/rlpx.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/cli/commands/src/p2p/rlpx.rs b/crates/cli/commands/src/p2p/rlpx.rs index 181c45581c3b..7c071f315cca 100644 --- a/crates/cli/commands/src/p2p/rlpx.rs +++ b/crates/cli/commands/src/p2p/rlpx.rs @@ -2,7 +2,6 @@ use clap::{Parser, Subcommand}; use enr::Enr; -use futures::StreamExt; use reth_ecies::stream::ECIESStream; use reth_eth_wire::{HelloMessage, UnauthedP2PStream}; use reth_network::config::rng_secret_key; @@ -32,11 +31,9 @@ impl Command { let peer_id = pk2id(&key.public_key(SECP256K1)); let hello = HelloMessage::builder(peer_id).build(); - let (p2p_stream, their_hello) = + let (_, their_hello) = UnauthedP2PStream::new(ecies_stream).handshake(hello).await?; - p2p_stream.send_ping(); - println!("{:#?}", their_hello); } } From 4d473f063b2328670d170cac88d163260787c9f0 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 3 Aug 2024 06:23:32 +0200 Subject: [PATCH 6/6] touchup --- Cargo.lock | 1 - book/cli/reth/p2p/rlpx/ping.md | 7 ++++--- crates/cli/commands/Cargo.toml | 1 - crates/cli/commands/src/p2p/rlpx.rs | 11 +++++------ 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d5572f95820..77429b025fb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6552,7 +6552,6 @@ dependencies = [ "comfy-table", "confy", "crossterm", - "enr", "eyre", "fdlimit", "futures", diff --git a/book/cli/reth/p2p/rlpx/ping.md b/book/cli/reth/p2p/rlpx/ping.md index 32f1495b4a72..222b57735bc3 100644 --- a/book/cli/reth/p2p/rlpx/ping.md +++ b/book/cli/reth/p2p/rlpx/ping.md @@ -4,12 +4,13 @@ ping node ```bash $ reth p2p rlpx ping --help -Usage: reth p2p rlpx ping [OPTIONS] --node +Usage: reth p2p rlpx ping [OPTIONS] -Options: - -n, --node +Arguments: + The node to ping +Options: --instance Add a new instance of a node. diff --git a/crates/cli/commands/Cargo.toml b/crates/cli/commands/Cargo.toml index c43d6121e93b..673cd179934c 100644 --- a/crates/cli/commands/Cargo.toml +++ b/crates/cli/commands/Cargo.toml @@ -48,7 +48,6 @@ tokio.workspace = true # misc ahash = "0.8" human_bytes = "0.4.1" -enr.workspace = true eyre.workspace = true clap = { workspace = true, features = ["derive", "env"] } serde.workspace = true diff --git a/crates/cli/commands/src/p2p/rlpx.rs b/crates/cli/commands/src/p2p/rlpx.rs index 7c071f315cca..f0bd5e6328bb 100644 --- a/crates/cli/commands/src/p2p/rlpx.rs +++ b/crates/cli/commands/src/p2p/rlpx.rs @@ -1,11 +1,10 @@ //! RLPx subcommand of P2P Debugging tool. use clap::{Parser, Subcommand}; -use enr::Enr; use reth_ecies::stream::ECIESStream; use reth_eth_wire::{HelloMessage, UnauthedP2PStream}; use reth_network::config::rng_secret_key; -use reth_network_peers::{pk2id, NodeRecord}; +use reth_network_peers::{pk2id, AnyNode}; use secp256k1::SECP256K1; use tokio::net::TcpStream; @@ -22,8 +21,9 @@ impl Command { match self.subcommand { Subcommands::Ping { node } => { let key = rng_secret_key(); - let enr = node.parse::>().unwrap(); - let node_record = NodeRecord::try_from(&enr)?; + let node_record = node + .node_record() + .ok_or_else(|| eyre::eyre!("failed to parse node {}", node))?; let outgoing = TcpStream::connect((node_record.address, node_record.tcp_port)).await?; let ecies_stream = ECIESStream::connect(outgoing, key, node_record.id).await?; @@ -45,8 +45,7 @@ impl Command { enum Subcommands { /// ping node Ping { - #[arg(long, short)] /// The node to ping. - node: String, + node: AnyNode, }, }