use crate::crds_value::MAX_WALLCLOCK; use solana_sdk::pubkey::Pubkey; #[cfg(test)] use solana_sdk::rpc_port; use solana_sdk::sanitize::{Sanitize, SanitizeError}; #[cfg(test)] use solana_sdk::signature::{Keypair, Signer}; use solana_sdk::timing::timestamp; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::net::{IpAddr, SocketAddr}; /// Structure representing a node on the network #[derive(Serialize, Deserialize, Clone, Debug, AbiExample)] pub struct ContactInfo { pub id: Pubkey, /// gossip address pub gossip: SocketAddr, /// address to connect to for replication pub tvu: SocketAddr, /// address to forward shreds to pub tvu_forwards: SocketAddr, /// address to send repair responses to pub repair: SocketAddr, /// transactions address pub tpu: SocketAddr, /// address to forward unprocessed transactions to pub tpu_forwards: SocketAddr, /// address to which to send bank state requests pub rpc_banks: SocketAddr, /// address to which to send JSON-RPC requests pub rpc: SocketAddr, /// websocket for JSON-RPC push notifications pub rpc_pubsub: SocketAddr, /// address to send repair requests to pub serve_repair: SocketAddr, /// latest wallclock picked pub wallclock: u64, /// node shred version pub shred_version: u16, } impl Sanitize for ContactInfo { fn sanitize(&self) -> std::result::Result<(), SanitizeError> { if self.wallclock >= MAX_WALLCLOCK { return Err(SanitizeError::ValueOutOfBounds); } Ok(()) } } impl Ord for ContactInfo { fn cmp(&self, other: &Self) -> Ordering { self.id.cmp(&other.id) } } impl PartialOrd for ContactInfo { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl PartialEq for ContactInfo { fn eq(&self, other: &Self) -> bool { self.id == other.id } } impl Eq for ContactInfo {} #[macro_export] macro_rules! socketaddr { ($ip:expr, $port:expr) => { std::net::SocketAddr::from((std::net::Ipv4Addr::from($ip), $port)) }; ($str:expr) => {{ let a: std::net::SocketAddr = $str.parse().unwrap(); a }}; } #[macro_export] macro_rules! socketaddr_any { () => { socketaddr!(0, 0) }; } impl Default for ContactInfo { fn default() -> Self { ContactInfo { id: Pubkey::default(), gossip: socketaddr_any!(), tvu: socketaddr_any!(), tvu_forwards: socketaddr_any!(), repair: socketaddr_any!(), tpu: socketaddr_any!(), tpu_forwards: socketaddr_any!(), rpc_banks: socketaddr_any!(), rpc: socketaddr_any!(), rpc_pubsub: socketaddr_any!(), serve_repair: socketaddr_any!(), wallclock: 0, shred_version: 0, } } } impl ContactInfo { pub fn new_localhost(id: &Pubkey, now: u64) -> Self { Self { id: *id, gossip: socketaddr!("127.0.0.1:1234"), tvu: socketaddr!("127.0.0.1:1235"), tvu_forwards: socketaddr!("127.0.0.1:1236"), repair: socketaddr!("127.0.0.1:1237"), tpu: socketaddr!("127.0.0.1:1238"), tpu_forwards: socketaddr!("127.0.0.1:1239"), rpc_banks: socketaddr!("127.0.0.1:1240"), rpc: socketaddr!("127.0.0.1:1241"), rpc_pubsub: socketaddr!("127.0.0.1:1242"), serve_repair: socketaddr!("127.0.0.1:1243"), wallclock: now, shred_version: 0, } } #[cfg(test)] /// ContactInfo with multicast addresses for adversarial testing. pub fn new_multicast() -> Self { let addr = socketaddr!("224.0.1.255:1000"); assert!(addr.ip().is_multicast()); Self { id: Pubkey::new_rand(), gossip: addr, tvu: addr, tvu_forwards: addr, repair: addr, tpu: addr, tpu_forwards: addr, rpc_banks: addr, rpc: addr, rpc_pubsub: addr, serve_repair: addr, wallclock: 0, shred_version: 0, } } #[cfg(test)] pub(crate) fn new_with_pubkey_socketaddr(pubkey: &Pubkey, bind_addr: &SocketAddr) -> Self { fn next_port(addr: &SocketAddr, nxt: u16) -> SocketAddr { let mut nxt_addr = *addr; nxt_addr.set_port(addr.port() + nxt); nxt_addr } let tpu = *bind_addr; let gossip = next_port(&bind_addr, 1); let tvu = next_port(&bind_addr, 2); let tpu_forwards = next_port(&bind_addr, 3); let tvu_forwards = next_port(&bind_addr, 4); let repair = next_port(&bind_addr, 5); let rpc = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_PORT); let rpc_pubsub = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_PUBSUB_PORT); let rpc_banks = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_BANKS_PORT); let serve_repair = next_port(&bind_addr, 6); Self { id: *pubkey, gossip, tvu, tvu_forwards, repair, tpu, tpu_forwards, rpc_banks, rpc, rpc_pubsub, serve_repair, wallclock: timestamp(), shred_version: 0, } } #[cfg(test)] pub(crate) fn new_with_socketaddr(bind_addr: &SocketAddr) -> Self { let keypair = Keypair::new(); Self::new_with_pubkey_socketaddr(&keypair.pubkey(), bind_addr) } // Construct a ContactInfo that's only usable for gossip pub fn new_gossip_entry_point(gossip_addr: &SocketAddr) -> Self { Self { id: Pubkey::default(), gossip: *gossip_addr, wallclock: timestamp(), ..ContactInfo::default() } } fn is_valid_ip(addr: IpAddr) -> bool { !(addr.is_unspecified() || addr.is_multicast()) // || (addr.is_loopback() && !cfg_test)) // TODO: boot loopback in production networks } /// port must not be 0 /// ip must be specified and not multicast /// loopback ip is only allowed in tests pub fn is_valid_address(addr: &SocketAddr) -> bool { (addr.port() != 0) && Self::is_valid_ip(addr.ip()) } pub fn client_facing_addr(&self) -> (SocketAddr, SocketAddr) { (self.rpc, self.tpu) } pub fn valid_client_facing_addr(&self) -> Option<(SocketAddr, SocketAddr)> { if ContactInfo::is_valid_address(&self.rpc) && ContactInfo::is_valid_address(&self.tpu) { Some((self.rpc, self.tpu)) } else { None } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_valid_address() { let bad_address_port = socketaddr!("127.0.0.1:0"); assert!(!ContactInfo::is_valid_address(&bad_address_port)); let bad_address_unspecified = socketaddr!(0, 1234); assert!(!ContactInfo::is_valid_address(&bad_address_unspecified)); let bad_address_multicast = socketaddr!([224, 254, 0, 0], 1234); assert!(!ContactInfo::is_valid_address(&bad_address_multicast)); let loopback = socketaddr!("127.0.0.1:1234"); assert!(ContactInfo::is_valid_address(&loopback)); // assert!(!ContactInfo::is_valid_ip_internal(loopback.ip(), false)); } #[test] fn test_default() { let ci = ContactInfo::default(); assert!(ci.gossip.ip().is_unspecified()); assert!(ci.tvu.ip().is_unspecified()); assert!(ci.tpu_forwards.ip().is_unspecified()); assert!(ci.rpc.ip().is_unspecified()); assert!(ci.rpc_pubsub.ip().is_unspecified()); assert!(ci.tpu.ip().is_unspecified()); assert!(ci.rpc_banks.ip().is_unspecified()); assert!(ci.serve_repair.ip().is_unspecified()); } #[test] fn test_multicast() { let ci = ContactInfo::new_multicast(); assert!(ci.gossip.ip().is_multicast()); assert!(ci.tvu.ip().is_multicast()); assert!(ci.tpu_forwards.ip().is_multicast()); assert!(ci.rpc.ip().is_multicast()); assert!(ci.rpc_pubsub.ip().is_multicast()); assert!(ci.tpu.ip().is_multicast()); assert!(ci.rpc_banks.ip().is_multicast()); assert!(ci.serve_repair.ip().is_multicast()); } #[test] fn test_entry_point() { let addr = socketaddr!("127.0.0.1:10"); let ci = ContactInfo::new_gossip_entry_point(&addr); assert_eq!(ci.gossip, addr); assert!(ci.tvu.ip().is_unspecified()); assert!(ci.tpu_forwards.ip().is_unspecified()); assert!(ci.rpc.ip().is_unspecified()); assert!(ci.rpc_pubsub.ip().is_unspecified()); assert!(ci.tpu.ip().is_unspecified()); assert!(ci.rpc_banks.ip().is_unspecified()); assert!(ci.serve_repair.ip().is_unspecified()); } #[test] fn test_socketaddr() { let addr = socketaddr!("127.0.0.1:10"); let ci = ContactInfo::new_with_socketaddr(&addr); assert_eq!(ci.tpu, addr); assert_eq!(ci.gossip.port(), 11); assert_eq!(ci.tvu.port(), 12); assert_eq!(ci.tpu_forwards.port(), 13); assert_eq!(ci.rpc.port(), rpc_port::DEFAULT_RPC_PORT); assert_eq!(ci.rpc_pubsub.port(), rpc_port::DEFAULT_RPC_PUBSUB_PORT); assert_eq!(ci.rpc_banks.port(), rpc_port::DEFAULT_RPC_BANKS_PORT); assert_eq!(ci.serve_repair.port(), 16); } #[test] fn replayed_data_new_with_socketaddr_with_pubkey() { let keypair = Keypair::new(); let d1 = ContactInfo::new_with_pubkey_socketaddr( &keypair.pubkey(), &socketaddr!("127.0.0.1:1234"), ); assert_eq!(d1.id, keypair.pubkey()); assert_eq!(d1.gossip, socketaddr!("127.0.0.1:1235")); assert_eq!(d1.tvu, socketaddr!("127.0.0.1:1236")); assert_eq!(d1.tpu_forwards, socketaddr!("127.0.0.1:1237")); assert_eq!(d1.tpu, socketaddr!("127.0.0.1:1234")); assert_eq!( d1.rpc, socketaddr!(format!("127.0.0.1:{}", rpc_port::DEFAULT_RPC_PORT)) ); assert_eq!( d1.rpc_pubsub, socketaddr!(format!("127.0.0.1:{}", rpc_port::DEFAULT_RPC_PUBSUB_PORT)) ); assert_eq!( d1.rpc_banks, socketaddr!(format!("127.0.0.1:{}", rpc_port::DEFAULT_RPC_BANKS_PORT)) ); assert_eq!(d1.tvu_forwards, socketaddr!("127.0.0.1:1238")); assert_eq!(d1.repair, socketaddr!("127.0.0.1:1239")); assert_eq!(d1.serve_repair, socketaddr!("127.0.0.1:1240")); } #[test] fn test_valid_client_facing() { let mut ci = ContactInfo::default(); assert_eq!(ci.valid_client_facing_addr(), None); ci.tpu = socketaddr!("127.0.0.1:123"); assert_eq!(ci.valid_client_facing_addr(), None); ci.rpc = socketaddr!("127.0.0.1:234"); assert!(ci.valid_client_facing_addr().is_some()); } #[test] fn test_sanitize() { let mut ci = ContactInfo::default(); assert_eq!(ci.sanitize(), Ok(())); ci.wallclock = MAX_WALLCLOCK; assert_eq!(ci.sanitize(), Err(SanitizeError::ValueOutOfBounds)); } }