diff --git a/ci/localnet-sanity.sh b/ci/localnet-sanity.sh index b598cca5d..dc3e187bd 100755 --- a/ci/localnet-sanity.sh +++ b/ci/localnet-sanity.sh @@ -324,7 +324,7 @@ while [[ $iteration -le $iterations ]]; do set -x client_keypair=/tmp/client-id.json-$$ $solana_keygen new -f -o $client_keypair || exit $? - $solana_gossip spy --num-nodes-exactly $numNodes || exit $? + $solana_gossip spy -n 127.0.0.1:8001 --num-nodes-exactly $numNodes || exit $? rm -rf $client_keypair ) || flag_error diff --git a/core/src/gossip_service.rs b/core/src/gossip_service.rs index 843852fa4..e9cda405b 100644 --- a/core/src/gossip_service.rs +++ b/core/src/gossip_service.rs @@ -68,26 +68,36 @@ impl GossipService { /// Discover Nodes and Archivers in a cluster pub fn discover_cluster( - entry_point: &SocketAddr, + entrypoint: &SocketAddr, num_nodes: usize, ) -> std::io::Result<(Vec, Vec)> { - discover(entry_point, Some(num_nodes), Some(30), None, None, None) + discover( + Some(entrypoint), + Some(num_nodes), + Some(30), + None, + None, + None, + ) } pub fn discover( - entry_point: &SocketAddr, + entrypoint: Option<&SocketAddr>, num_nodes: Option, timeout: Option, find_node_by_pubkey: Option, - find_node_by_gossip_addr: Option, + find_node_by_gossip_addr: Option<&SocketAddr>, my_gossip_addr: Option<&SocketAddr>, ) -> std::io::Result<(Vec, Vec)> { let exit = Arc::new(AtomicBool::new(false)); - let (gossip_service, ip_echo, spy_ref) = make_gossip_node(entry_point, &exit, my_gossip_addr); + let (gossip_service, ip_echo, spy_ref) = make_gossip_node(entrypoint, &exit, my_gossip_addr); let id = spy_ref.read().unwrap().keypair.pubkey(); - info!("Gossip entry point: {:?}", entry_point); - info!("Spy node id: {:?}", id); + info!("Entrypoint: {:?}", entrypoint); + info!("Node Id: {:?}", id); + if let Some(my_gossip_addr) = my_gossip_addr { + info!("Gossip Address: {:?}", my_gossip_addr); + } let _ip_echo_server = ip_echo.map(solana_net_utils::ip_echo_server); @@ -169,7 +179,7 @@ fn spy( num_nodes: Option, timeout: Option, find_node_by_pubkey: Option, - find_node_by_gossip_addr: Option, + find_node_by_gossip_addr: Option<&SocketAddr>, ) -> (bool, u64, Vec, Vec) { let now = Instant::now(); let mut met_criteria = false; @@ -198,7 +208,7 @@ fn spy( if tvu_peers .iter() .chain(archivers.iter()) - .any(|x| x.gossip == gossip_addr) + .any(|x| x.gossip == *gossip_addr) { met_criteria = true; break; @@ -235,7 +245,7 @@ fn spy( if tvu_peers .iter() .chain(archivers.iter()) - .any(|x| x.gossip == gossip_addr) + .any(|x| x.gossip == *gossip_addr) { met_criteria = true; break; @@ -259,7 +269,7 @@ fn spy( /// Makes a spy or gossip node based on whether or not a gossip_addr was passed in /// Pass in a gossip addr to fully participate in gossip instead of relying on just pulls fn make_gossip_node( - entry_point: &SocketAddr, + entrypoint: Option<&SocketAddr>, exit: &Arc, gossip_addr: Option<&SocketAddr>, ) -> (GossipService, Option, Arc>) { @@ -270,7 +280,9 @@ fn make_gossip_node( ClusterInfo::spy_node(&keypair.pubkey()) }; let mut cluster_info = ClusterInfo::new(node, keypair); - cluster_info.set_entrypoint(ContactInfo::new_gossip_entry_point(entry_point)); + if let Some(entrypoint) = entrypoint { + cluster_info.set_entrypoint(ContactInfo::new_gossip_entry_point(entrypoint)); + } let cluster_info = Arc::new(RwLock::new(cluster_info)); let gossip_service = GossipService::new(&cluster_info.clone(), None, None, gossip_socket, &exit); @@ -350,14 +362,15 @@ mod tests { // Find specific node by gossip address let (met_criteria, _, _, _) = - spy(spy_ref.clone(), None, None, None, Some(peer0_info.gossip)); + spy(spy_ref.clone(), None, None, None, Some(&peer0_info.gossip)); assert_eq!(met_criteria, true); + let (met_criteria, _, _, _) = spy( spy_ref.clone(), None, Some(0), None, - Some("1.1.1.1:1234".parse().unwrap()), + Some(&"1.1.1.1:1234".parse().unwrap()), ); assert_eq!(met_criteria, false); } diff --git a/gossip/src/main.rs b/gossip/src/main.rs index aece2b7bd..b329e813f 100644 --- a/gossip/src/main.rs +++ b/gossip/src/main.rs @@ -1,9 +1,11 @@ //! A command-line executable for monitoring a cluster's gossip plane. -use clap::{crate_description, crate_name, value_t_or_exit, App, AppSettings, Arg, SubCommand}; +use clap::{ + crate_description, crate_name, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand, +}; use solana_clap_utils::input_validators::is_pubkey; use solana_client::rpc_client::RpcClient; -use solana_core::{contact_info::ContactInfo, gossip_service::discover}; +use solana_core::{contact_info::ContactInfo, gossip_service::discover, socketaddr}; use solana_sdk::pubkey::Pubkey; use std::error; use std::net::SocketAddr; @@ -12,26 +14,23 @@ use std::process::exit; fn main() -> Result<(), Box> { solana_logger::setup_with_filter("solana=info"); - let mut entrypoint_addr = SocketAddr::from(([127, 0, 0, 1], 8001)); - let entrypoint_string = entrypoint_addr.to_string(); let matches = App::new(crate_name!()) .about(crate_description!()) .version(solana_clap_utils::version!()) .setting(AppSettings::SubcommandRequiredElseHelp) - .arg( - Arg::with_name("entrypoint") - .short("n") - .long("entrypoint") - .value_name("HOST:PORT") - .takes_value(true) - .default_value(&entrypoint_string) - .validator(solana_net_utils::is_host_port) - .global(true) - .help("Rendezvous with the cluster at this entry point"), - ) .subcommand( SubCommand::with_name("get-rpc-url") .about("Get an RPC URL for the cluster") + .arg( + Arg::with_name("entrypoint") + .short("n") + .long("entrypoint") + .value_name("HOST:PORT") + .takes_value(true) + .required(true) + .validator(solana_net_utils::is_host_port) + .help("Rendezvous with the cluster at this entry point"), + ) .arg( Arg::with_name("all") .long("all") @@ -52,12 +51,21 @@ fn main() -> Result<(), Box> { SubCommand::with_name("spy") .about("Monitor the gossip entrypoint") .setting(AppSettings::DisableVersion) + .arg( + Arg::with_name("entrypoint") + .short("n") + .long("entrypoint") + .value_name("HOST:PORT") + .takes_value(true) + .required_unless("gossip_port") + .validator(solana_net_utils::is_host_port) + .help("Rendezvous with the cluster at this entry point"), + ) .arg( clap::Arg::with_name("gossip_port") .long("gossip-port") - .value_name("PORT") + .value_name("HOST:PORT") .takes_value(true) - .default_value("0") .help("Gossip port number for the node"), ) .arg( @@ -98,6 +106,16 @@ fn main() -> Result<(), Box> { SubCommand::with_name("stop") .about("Send stop request to a node") .setting(AppSettings::DisableVersion) + .arg( + Arg::with_name("entrypoint") + .short("n") + .long("entrypoint") + .value_name("HOST:PORT") + .takes_value(true) + .required(true) + .validator(solana_net_utils::is_host_port) + .help("Rendezvous with the cluster at this entry point"), + ) .arg( Arg::with_name("node_pubkey") .index(1) @@ -109,11 +127,13 @@ fn main() -> Result<(), Box> { ) .get_matches(); - if let Some(addr) = matches.value_of("entrypoint") { - entrypoint_addr = solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| { - eprintln!("failed to parse entrypoint address: {}", e); - exit(1); - }); + fn parse_entrypoint(matches: &ArgMatches) -> Option { + matches.value_of("entrypoint").map(|entrypoint| { + solana_net_utils::parse_host_port(entrypoint).unwrap_or_else(|e| { + eprintln!("failed to parse entrypoint address: {}", e); + exit(1); + }) + }) } match matches.subcommand() { @@ -132,16 +152,30 @@ fn main() -> Result<(), Box> { .value_of("node_pubkey") .map(|pubkey_str| pubkey_str.parse::().unwrap()); - let gossip_addr = SocketAddr::new( - solana_net_utils::get_public_ip_addr(&entrypoint_addr).unwrap_or_else(|err| { - eprintln!("failed to contact {}: {}", entrypoint_addr, err); - exit(1); - }), - value_t_or_exit!(matches, "gossip_port", u16), + let mut gossip_addr = solana_net_utils::parse_port_or_addr( + matches.value_of("gossip_port"), + socketaddr!( + [127, 0, 0, 1], + solana_net_utils::find_available_port_in_range((0, 1)) + .expect("unable to find an available gossip port") + ), ); + let entrypoint_addr = parse_entrypoint(&matches); + if let Some(entrypoint_addr) = entrypoint_addr { + gossip_addr.set_ip( + solana_net_utils::get_public_ip_addr(&entrypoint_addr).unwrap_or_else(|err| { + eprintln!( + "Failed to contact cluster entrypoint {}: {}", + entrypoint_addr, err + ); + exit(1); + }), + ); + } + let (nodes, _archivers) = discover( - &entrypoint_addr, + entrypoint_addr.as_ref(), num_nodes, timeout, pubkey, @@ -182,20 +216,21 @@ fn main() -> Result<(), Box> { } } ("get-rpc-url", Some(matches)) => { + let entrypoint_addr = parse_entrypoint(&matches); let timeout = value_t_or_exit!(matches, "timeout", u64); let (nodes, _archivers) = discover( - &entrypoint_addr, + entrypoint_addr.as_ref(), Some(1), Some(timeout), None, - Some(entrypoint_addr), + entrypoint_addr.as_ref(), None, )?; let rpc_addrs: Vec<_> = nodes .iter() .filter_map(|contact_info| { - if (matches.is_present("all") || contact_info.gossip == entrypoint_addr) + if (matches.is_present("all") || Some(contact_info.gossip) == entrypoint_addr) && ContactInfo::is_valid_address(&contact_info.rpc) { return Some(contact_info.rpc); @@ -214,13 +249,20 @@ fn main() -> Result<(), Box> { } } ("stop", Some(matches)) => { + let entrypoint_addr = parse_entrypoint(&matches); let pubkey = matches .value_of("node_pubkey") .unwrap() .parse::() .unwrap(); - let (nodes, _archivers) = - discover(&entrypoint_addr, None, None, Some(pubkey), None, None)?; + let (nodes, _archivers) = discover( + entrypoint_addr.as_ref(), + None, + None, + Some(pubkey), + None, + None, + )?; let node = nodes.iter().find(|x| x.id == pubkey).unwrap(); if !ContactInfo::is_valid_address(&node.rpc) { diff --git a/validator/src/main.rs b/validator/src/main.rs index 558b8c411..dc78b71b3 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -172,11 +172,11 @@ fn create_rpc_client( entrypoint: &ContactInfo, ) -> Result<(std::net::SocketAddr, RpcClient), String> { let (nodes, _archivers) = discover( - &entrypoint.gossip, + Some(&entrypoint.gossip), Some(1), Some(60), None, - Some(entrypoint.gossip), + Some(&entrypoint.gossip), None, ) .map_err(|err| err.to_string())?;