diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 63ce3098ab..fde831ea5f 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -1267,14 +1267,16 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces .into_iter() .map(|node| { format!( - "{:15} | {:44} | {:6} | {:5} | {:5} | {}", + "{:15} | {:44} | {:6} | {:5} | {:21} | {}", node.gossip .map(|addr| addr.ip().to_string()) .unwrap_or_else(|| "none".to_string()), format_labeled_address(&node.pubkey, &config.address_labels), format_port(node.gossip), format_port(node.tpu), - format_port(node.rpc), + node.rpc + .map(|addr| addr.to_string()) + .unwrap_or_else(|| "none".to_string()), node.version.unwrap_or_else(|| "unknown".to_string()), ) }) @@ -1282,9 +1284,9 @@ pub fn process_show_gossip(rpc_client: &RpcClient, config: &CliConfig) -> Proces Ok(format!( "IP Address | Node identifier \ - | Gossip | TPU | RPC | Version\n\ + | Gossip | TPU | RPC Address | Version\n\ ----------------+----------------------------------------------+\ - --------+-------+-------+----------------\n\ + --------+-------+-----------------------+----------------\n\ {}\n\ Nodes: {}", s.join("\n"), diff --git a/core/src/cluster_info.rs b/core/src/cluster_info.rs index 940214104f..a300351118 100644 --- a/core/src/cluster_info.rs +++ b/core/src/cluster_info.rs @@ -530,6 +530,70 @@ impl ClusterInfo { .unwrap_or_else(|| EpochSlots::new(self.id(), timestamp())) } + pub fn rpc_info_trace(&self) -> String { + let now = timestamp(); + let my_pubkey = self.id(); + let my_shred_version = self.my_shred_version(); + let nodes: Vec<_> = self + .all_peers() + .into_iter() + .filter_map(|(node, last_updated)| { + if !ContactInfo::is_valid_address(&node.rpc) { + info!("invalid rpc: {}", node.rpc.to_string()); + return None; + } + + let node_version = self.get_node_version(&node.id); + if my_shred_version != 0 + && (node.shred_version != 0 && node.shred_version != my_shred_version) + { + return None; + } + + fn addr_to_string(default_ip: &IpAddr, addr: &SocketAddr) -> String { + if ContactInfo::is_valid_address(addr) { + if &addr.ip() == default_ip { + addr.port().to_string() + } else { + addr.to_string() + } + } else { + "none".to_string() + } + } + + let rpc_addr = node.rpc.ip(); + Some(format!( + "{:15} {:2}| {:5} | {:44} |{:^9}| {:5}| {:5}| {:5}| {}\n", + rpc_addr.to_string(), + if node.id == my_pubkey { "me" } else { "" }.to_string(), + now.saturating_sub(last_updated), + node.id.to_string(), + if let Some(node_version) = node_version { + node_version.to_string() + } else { + "-".to_string() + }, + addr_to_string(&rpc_addr, &node.rpc), + addr_to_string(&rpc_addr, &node.rpc_pubsub), + addr_to_string(&rpc_addr, &node.rpc_banks), + node.shred_version, + )) + }) + .collect(); + + format!( + "RPC Address |Age(ms)| Node identifier \ + | Version | RPC |PubSub| Banks|ShredVer\n\ + ------------------+-------+----------------------------------------------+---------+\ + ------+------+------+--------\n\ + {}\ + RPC Enabled Nodes: {}", + nodes.join(""), + nodes.len(), + ) + } + pub fn contact_info_trace(&self) -> String { let now = timestamp(); let mut spy_nodes = 0; @@ -562,7 +626,7 @@ impl ClusterInfo { } let ip_addr = node.gossip.ip(); Some(format!( - "{:15} {:2}| {:5} | {:44} |{:^9}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {}\n", + "{:15} {:2}| {:5} | {:44} |{:^9}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {}\n", if ContactInfo::is_valid_address(&node.gossip) { ip_addr.to_string() } else { @@ -583,9 +647,6 @@ impl ClusterInfo { addr_to_string(&ip_addr, &node.tvu_forwards), addr_to_string(&ip_addr, &node.repair), addr_to_string(&ip_addr, &node.serve_repair), - addr_to_string(&ip_addr, &node.rpc), - addr_to_string(&ip_addr, &node.rpc_pubsub), - addr_to_string(&ip_addr, &node.rpc_banks), node.shred_version, )) } @@ -594,9 +655,9 @@ impl ClusterInfo { format!( "IP Address |Age(ms)| Node identifier \ - | Version |Gossip| TPU |TPUfwd| TVU |TVUfwd|Repair|ServeR| RPC |PubSub|ShredVer\n\ + | Version |Gossip| TPU |TPUfwd| TVU |TVUfwd|Repair|ServeR|ShredVer\n\ ------------------+-------+----------------------------------------------+---------+\ - ------+------+------+------+------+------+------+------+------+--------\n\ + ------+------+------+------+------+------+------+--------\n\ {}\ Nodes: {}{}{}", nodes.join(""), @@ -1580,7 +1641,11 @@ impl ClusterInfo { thread_mem_usage::datapoint("solana-gossip"); if start - last_contact_info_trace > 10000 { // Log contact info every 10 seconds - info!("\n{}", self.contact_info_trace()); + info!( + "\n{}\n\n{}", + self.contact_info_trace(), + self.rpc_info_trace() + ); last_contact_info_trace = start; } diff --git a/core/src/validator.rs b/core/src/validator.rs index 0d233a32e7..21a6f713cb 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -386,11 +386,10 @@ impl Validator { if let Some((rpc_addr, rpc_pubsub_addr, rpc_banks_addr)) = config.rpc_addrs { if ContactInfo::is_valid_address(&node.info.rpc) { assert!(ContactInfo::is_valid_address(&node.info.rpc_pubsub)); - assert_eq!(rpc_addr.port(), node.info.rpc.port()); - assert_eq!(rpc_pubsub_addr.port(), node.info.rpc_pubsub.port()); - assert_eq!(rpc_banks_addr.port(), node.info.rpc_banks.port()); + assert!(ContactInfo::is_valid_address(&node.info.rpc_banks)); } else { assert!(!ContactInfo::is_valid_address(&node.info.rpc_pubsub)); + assert!(!ContactInfo::is_valid_address(&node.info.rpc_banks)); } let tpu_address = cluster_info.my_contact_info().tpu; let (bank_notification_sender, bank_notification_receiver) = unbounded(); diff --git a/validator/src/main.rs b/validator/src/main.rs index ec38afd1cf..927636166c 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -154,7 +154,7 @@ fn get_rpc_node( let mut retry_reason = None; loop { sleep(Duration::from_secs(1)); - info!("\n{}", cluster_info.contact_info_trace()); + info!("\n{}", cluster_info.rpc_info_trace()); let shred_version = validator_config .expected_shred_version @@ -1058,6 +1058,17 @@ pub fn main() { .help("IP address for the node to advertise in gossip when \ --entrypoint is not provided [default: 127.0.0.1]"), ) + .arg( + Arg::with_name("public_rpc_addr") + .long("public-rpc-address") + .value_name("HOST:PORT") + .takes_value(true) + .conflicts_with("private_rpc") + .validator(solana_net_utils::is_host_port) + .help("RPC address for the node to advertise publicly in gossip. \ + Useful for nodes running behind a load balancer or proxy \ + [default: use --rpc-bind-address / --rpc-port]"), + ) .arg( Arg::with_name("dynamic_port_range") .long("dynamic-port-range") @@ -1593,6 +1604,13 @@ pub fn main() { }) }); + let public_rpc_addr = matches.value_of("public_rpc_addr").map(|addr| { + solana_net_utils::parse_host_port(addr).unwrap_or_else(|e| { + eprintln!("failed to parse public rpc address: {}", e); + exit(1); + }) + }); + let logfile = { let logfile = matches .value_of("logfile") @@ -1668,7 +1686,12 @@ pub fn main() { } if !private_rpc { - if let Some((rpc_addr, rpc_pubsub_addr, rpc_banks_addr)) = validator_config.rpc_addrs { + if let Some(public_rpc_addr) = public_rpc_addr { + node.info.rpc = public_rpc_addr; + node.info.rpc_pubsub = public_rpc_addr; + node.info.rpc_banks = public_rpc_addr; + } else if let Some((rpc_addr, rpc_pubsub_addr, rpc_banks_addr)) = validator_config.rpc_addrs + { node.info.rpc = SocketAddr::new(node.info.gossip.ip(), rpc_addr.port()); node.info.rpc_pubsub = SocketAddr::new(node.info.gossip.ip(), rpc_pubsub_addr.port()); node.info.rpc_banks = SocketAddr::new(node.info.gossip.ip(), rpc_banks_addr.port());