diff --git a/Cargo.lock b/Cargo.lock index 80a251524a..780ca757cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2301,6 +2301,16 @@ dependencies = [ "solana-vote-api 0.13.0", ] +[[package]] +name = "solana-gossip" +version = "0.13.0" +dependencies = [ + "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", + "solana 0.13.0", + "solana-logger 0.13.0", + "solana-sdk 0.13.0", +] + [[package]] name = "solana-install" version = "0.13.0" diff --git a/Cargo.toml b/Cargo.toml index 89c7fa83c1..fde5106873 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "drone", "fullnode", "genesis", + "gossip", "install", "keygen", "kvstore", diff --git a/ci/publish-crate.sh b/ci/publish-crate.sh index fb0a1b7a85..3e077049d6 100755 --- a/ci/publish-crate.sh +++ b/ci/publish-crate.sh @@ -26,6 +26,7 @@ CRATES=( core fullnode genesis + gossip ledger-tool wallet install diff --git a/gossip/Cargo.toml b/gossip/Cargo.toml new file mode 100644 index 0000000000..43dbe91faf --- /dev/null +++ b/gossip/Cargo.toml @@ -0,0 +1,15 @@ +[package] +authors = ["Solana Maintainers "] +edition = "2018" +name = "solana-gossip" +description = "Blockchain, Rebuilt for Scale" +version = "0.13.0" +repository = "https://github.com/solana-labs/solana" +license = "Apache-2.0" +homepage = "https://solana.com/" + +[dependencies] +clap = "2.32.0" +solana = { path = "../core", version = "0.13.0" } +solana-logger = { path = "../logger", version = "0.13.0" } +solana-sdk = { path = "../sdk", version = "0.13.0" } diff --git a/gossip/src/main.rs b/gossip/src/main.rs new file mode 100644 index 0000000000..5f664f688f --- /dev/null +++ b/gossip/src/main.rs @@ -0,0 +1,118 @@ +//! A command-line executable for monitoring a network's gossip plane. + +use clap::{crate_description, crate_name, crate_version, App, Arg}; +use solana::gossip_service::discover; +use solana_sdk::pubkey::Pubkey; +use std::error; +use std::net::SocketAddr; +use std::process::exit; + +fn pubkey_validator(pubkey: String) -> Result<(), String> { + match pubkey.parse::() { + Ok(_) => Ok(()), + Err(err) => Err(format!("{:?}", err)), + } +} + +fn main() -> Result<(), Box> { + solana_logger::setup(); + let mut network_addr = SocketAddr::from(([127, 0, 0, 1], 8001)); + let network_string = network_addr.to_string(); + let matches = App::new(crate_name!()) + .about(crate_description!()) + .version(crate_version!()) + .arg( + Arg::with_name("network") + .short("n") + .long("network") + .value_name("HOST:PORT") + .takes_value(true) + .default_value(&network_string) + .help("Rendezvous with the network at this gossip entry point; defaults to 127.0.0.1:8001"), + ) + .arg( + Arg::with_name("num_nodes") + .short("N") + .long("num-nodes") + .value_name("NUM") + .takes_value(true) + .conflicts_with("num_nodes_exactly") + .help("Wait for at least NUM nodes to converge"), + ) + .arg( + Arg::with_name("num_nodes_exactly") + .short("E") + .long("num-nodes-exactly") + .value_name("NUM") + .takes_value(true) + .help("Wait for exactly NUM nodes to converge"), + ) + .arg( + Arg::with_name("node_pubkey") + .short("p") + .long("pubkey") + .value_name("PUBKEY") + .takes_value(true) + .validator(pubkey_validator) + .help("Public key of a specific node to wait for"), + ) + .arg( + Arg::with_name("timeout") + .long("timeout") + .value_name("SECS") + .takes_value(true) + .help("Seconds to wait for cluster to converge, then exit; default is forever"), + ) + .get_matches(); + + if let Some(addr) = matches.value_of("network") { + network_addr = addr.parse().unwrap_or_else(|e| { + eprintln!("failed to parse network: {}", e); + exit(1) + }); + } + + let num_nodes_exactly = matches + .value_of("num_nodes_exactly") + .map(|num| num.to_string().parse().unwrap()); + let num_nodes = matches + .value_of("num_nodes") + .map(|num| num.to_string().parse().unwrap()) + .or(num_nodes_exactly); + let timeout = matches + .value_of("timeout") + .map(|secs| secs.to_string().parse().unwrap()); + let pubkey = matches + .value_of("node_pubkey") + .map(|pubkey_str| pubkey_str.parse::().unwrap()); + + let nodes = discover(&network_addr, num_nodes, timeout, pubkey)?; + + if timeout.is_some() { + if let Some(num) = num_nodes { + if nodes.len() < num { + let add = if num_nodes_exactly.is_some() { + "" + } else { + " or more" + }; + eprintln!( + "Error: Insufficient nodes discovered. Expecting {}{}", + num, add, + ); + } + } + if let Some(node) = pubkey { + if nodes.iter().find(|x| x.id == node).is_none() { + eprintln!("Error: Could not find node {:?}", node); + } + } + } + if num_nodes_exactly.is_some() && nodes.len() > num_nodes_exactly.unwrap() { + eprintln!( + "Error: Extra nodes discovered. Expecting exactly {}", + num_nodes_exactly.unwrap() + ); + } + Ok(()) +}