From 5b9c8b4259c28a1a6b82f5c754df1b8d7fa3ffbf Mon Sep 17 00:00:00 2001 From: Niklas Johansson Date: Fri, 8 Nov 2024 15:38:59 +0100 Subject: [PATCH] feat: Add support for both ip and hostname for peers It is convinent to be able to specify both IPs and hostnames as peers. Especially, when you are building ephemeral testnets and want to specify peers as ``` ./snarkos start --peers "validator0:4130,client0:4130" ``` Where you can populate the `/etc/hosts` file with subnet hostnames. --- cli/src/commands/start.rs | 75 +++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/cli/src/commands/start.rs b/cli/src/commands/start.rs index b87a94dd2b..605b042541 100644 --- a/cli/src/commands/start.rs +++ b/cli/src/commands/start.rs @@ -42,7 +42,7 @@ use rand::{Rng, SeedableRng}; use rand_chacha::ChaChaRng; use serde::{Deserialize, Serialize}; use std::{ - net::SocketAddr, + net::{SocketAddr, ToSocketAddrs}, path::PathBuf, sync::{Arc, atomic::AtomicBool}, }; @@ -228,11 +228,18 @@ impl Start { false => Ok(self .peers .split(',') - .flat_map(|ip| match ip.parse::() { - Ok(ip) => Some(ip), - Err(e) => { - eprintln!("The IP supplied to --peers ('{ip}') is malformed: {e}"); - None + .flat_map(|ip_or_hostname| { + let trimmed = ip_or_hostname.trim(); + match trimmed.to_socket_addrs() { + Ok(mut ip_iter) => { + // A hostname might resolve to multiple IP addresses. We will use only the first one, + // assuming this aligns with the user's expectations. + ip_iter.next() + }, + Err(e) => { + eprintln!("The hostname or IP supplied to --peers ('{trimmed}') is malformed: {e}"); + None + } } }) .collect()), @@ -1039,4 +1046,60 @@ mod tests { panic!("Unexpected result of clap parsing!"); } } + + #[test] + fn parse_peers_when_ips() { + let arg_vec = vec!["snarkos", "start", "--peers", "127.0.0.1:3030,127.0.0.2:3030"]; + let cli = CLI::parse_from(arg_vec); + + if let Command::Start(start) = cli.command { + let peers = start.parse_trusted_peers(); + assert!(peers.is_ok()); + assert_eq!(peers.unwrap().len(), 2, "Expected two peers"); + } else { + panic!("Unexpected result of clap parsing!"); + } + } + + #[test] + fn parse_peers_when_hostnames() { + let arg_vec = vec!["snarkos", "start", "--peers", "www.example.com:4130,www.google.com:4130"]; + let cli = CLI::parse_from(arg_vec); + + if let Command::Start(start) = cli.command { + let peers = start.parse_trusted_peers(); + assert!(peers.is_ok()); + assert_eq!(peers.unwrap().len(), 2, "Expected two peers"); + } else { + panic!("Unexpected result of clap parsing!"); + } + } + + #[test] + fn parse_peers_when_mixed_and_with_whitespaces() { + let arg_vec = vec!["snarkos", "start", "--peers", " 127.0.0.1:3030, www.google.com:4130 "]; + let cli = CLI::parse_from(arg_vec); + + if let Command::Start(start) = cli.command { + let peers = start.parse_trusted_peers(); + assert!(peers.is_ok()); + assert_eq!(peers.unwrap().len(), 2, "Expected two peers"); + } else { + panic!("Unexpected result of clap parsing!"); + } + } + + #[test] + fn parse_peers_when_unknown_hostname_gracefully() { + let arg_vec = vec!["snarkos", "start", "--peers", "banana.cake.eafafdaeefasdfasd.com"]; + let cli = CLI::parse_from(arg_vec); + + if let Command::Start(start) = cli.command { + let peers = start.parse_trusted_peers(); + assert!(peers.is_ok()); + assert!(peers.unwrap().is_empty(), "Expected no peers to be found"); + } else { + panic!("Unexpected result of clap parsing!"); + } + } }