diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index a4e9f30027..8558648244 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -88,7 +88,7 @@ async fn register_with_gateway( ) -> SharedKeys { let timeout = Duration::from_millis(1500); let mut gateway_client = GatewayClient::new_init( - gateway.client_listener.clone(), + gateway.clients_address(), gateway.identity_key, our_identity.clone(), timeout, @@ -215,7 +215,7 @@ pub fn execute(matches: &ArgMatches) { .with_gateway_id(gate_details.identity_key.to_base58_string()); let shared_keys = register_with_gateway(&gate_details, key_manager.identity_keypair()).await; - (shared_keys, gate_details.client_listener) + (shared_keys, gate_details.clients_address()) }; // TODO: is there perhaps a way to make it work without having to spawn entire runtime? diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 9fbb003e35..0a73d524bf 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -89,7 +89,7 @@ async fn register_with_gateway( ) -> SharedKeys { let timeout = Duration::from_millis(1500); let mut gateway_client = GatewayClient::new_init( - gateway.client_listener.clone(), + gateway.clients_address(), gateway.identity_key, our_identity.clone(), timeout, @@ -217,7 +217,7 @@ pub fn execute(matches: &ArgMatches) { .with_gateway_id(gate_details.identity_key.to_base58_string()); let shared_keys = register_with_gateway(&gate_details, key_manager.identity_keypair()).await; - (shared_keys, gate_details.client_listener) + (shared_keys, gate_details.clients_address()) }; // TODO: is there perhaps a way to make it work without having to spawn entire runtime? diff --git a/clients/webassembly/src/client/mod.rs b/clients/webassembly/src/client/mod.rs index 3785cc3ee5..4f9fc1e404 100644 --- a/clients/webassembly/src/client/mod.rs +++ b/clients/webassembly/src/client/mod.rs @@ -120,7 +120,7 @@ impl NymClient { let (ack_sender, ack_receiver) = mpsc::unbounded(); let mut gateway_client = GatewayClient::new( - gateway.client_listener.clone(), + gateway.clients_address(), Arc::clone(&client.identity), gateway.identity_key, None, diff --git a/common/mixnet-contract/src/gateway.rs b/common/mixnet-contract/src/gateway.rs index d4bae81d17..8285c4a641 100644 --- a/common/mixnet-contract/src/gateway.rs +++ b/common/mixnet-contract/src/gateway.rs @@ -6,13 +6,12 @@ use cosmwasm_std::{Addr, Coin}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt::Display; -use std::io; -use std::net::{SocketAddr, ToSocketAddrs}; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct Gateway { - pub mix_host: String, - pub clients_host: String, + pub host: String, + pub mix_port: u16, + pub clients_port: u16, pub location: String, pub sphinx_key: SphinxKey, /// Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients @@ -22,16 +21,18 @@ pub struct Gateway { impl Gateway { pub fn new( - mix_host: String, - clients_host: String, + host: String, + mix_port: u16, + clients_port: u16, location: String, sphinx_key: SphinxKey, identity_key: IdentityKey, version: String, ) -> Self { Gateway { - mix_host, - clients_host, + host, + mix_port, + clients_port, location, sphinx_key, identity_key, @@ -39,12 +40,12 @@ impl Gateway { } } - pub fn try_resolve_hostname(&self) -> Result { - self.mix_host - .to_socket_addrs()? - .next() - .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no valid socket address")) - } + // pub fn try_resolve_hostname(&self) -> Result { + // self.mix_host + // .to_socket_addrs()? + // .next() + // .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no valid socket address")) + // } } #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] diff --git a/common/mixnet-contract/src/mixnode.rs b/common/mixnet-contract/src/mixnode.rs index c4bfa7da41..eb7687ad17 100644 --- a/common/mixnet-contract/src/mixnode.rs +++ b/common/mixnet-contract/src/mixnode.rs @@ -6,12 +6,11 @@ use cosmwasm_std::{Addr, Coin}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt::Display; -use std::io; -use std::net::{SocketAddr, ToSocketAddrs}; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct MixNode { pub host: String, + pub mix_port: u16, pub layer: u64, pub location: String, pub sphinx_key: SphinxKey, @@ -23,6 +22,7 @@ pub struct MixNode { impl MixNode { pub fn new( host: String, + mix_port: u16, layer: u64, location: String, sphinx_key: SphinxKey, @@ -31,6 +31,7 @@ impl MixNode { ) -> Self { MixNode { host, + mix_port, layer, location, sphinx_key, @@ -38,20 +39,8 @@ impl MixNode { version, } } - - pub fn try_resolve_hostname(&self) -> Result { - self.host - .to_socket_addrs()? - .next() - .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no valid socket address")) - } } -/* -By the way, I've also been thinking of some potential changes in the contract we could introduce once we have to make an incompatible upgrade (say to cosmwasm 0.14+). Is there some place I could write them down so that we would not forget about them? Should I make a GitHub issue, a google doc or maybe put it on GitLab? - - */ - #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] pub struct MixNodeBond { // TODO: @@ -62,8 +51,6 @@ pub struct MixNodeBond { // // I would also modify the `MixNode` struct: // - remove `location` field - // - repurpose `host` field to hold either ip or hostname (WITHOUT port information) - // - introduce `mix_port` field // - introduce `rest_api_port` field // - [POTENTIALLY] introduce `verloc_port` field or keep it accessible via http api // diff --git a/common/topology/src/gateway.rs b/common/topology/src/gateway.rs index 4c62db62f7..b285c9d48e 100644 --- a/common/topology/src/gateway.rs +++ b/common/topology/src/gateway.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::filter; +use crate::{filter, NetworkAddress}; use crypto::asymmetric::{encryption, identity}; use mixnet_contract::GatewayBond; use nymsphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress}; @@ -84,8 +84,11 @@ pub struct Node { // on the network at a type, right? pub stake: u128, pub location: String, - pub client_listener: String, - pub mixnet_listener: SocketAddr, + pub host: NetworkAddress, + // we're keeping this as separate resolved field since we do not want to be resolving the potential + // hostname every time we want to construct a path via this node + pub mix_host: SocketAddr, + pub clients_port: u16, pub identity_key: identity::PublicKey, pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519 pub version: String, @@ -95,6 +98,10 @@ impl Node { pub fn identity(&self) -> &NodeIdentity { &self.identity_key } + + pub fn clients_address(&self) -> String { + format!("ws://{}:{}", self.host, self.clients_port) + } } impl filter::Versioned for Node { @@ -105,7 +112,7 @@ impl filter::Versioned for Node { impl<'a> From<&'a Node> for SphinxNode { fn from(node: &'a Node) -> Self { - let node_address_bytes = NymNodeRoutingAddress::from(node.mixnet_listener) + let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host) .try_into() .unwrap(); @@ -120,6 +127,17 @@ impl<'a> TryFrom<&'a GatewayBond> for Node { if bond.amount.len() > 1 { return Err(GatewayConversionError::InvalidStake); } + + let host: NetworkAddress = bond.gateway.host.parse().map_err(|err| { + GatewayConversionError::InvalidAddress(bond.gateway.host.clone(), err) + })?; + + // try to completely resolve the host in the mix situation to avoid doing it every + // single time we want to construct a path + let mix_host = host.to_socket_addrs(bond.gateway.mix_port).map_err(|err| { + GatewayConversionError::InvalidAddress(bond.gateway.host.clone(), err) + })?[0]; + Ok(Node { owner: bond.owner.as_str().to_owned(), stake: bond @@ -128,10 +146,9 @@ impl<'a> TryFrom<&'a GatewayBond> for Node { .map(|stake| stake.amount.into()) .unwrap_or(0), location: bond.gateway.location.clone(), - client_listener: bond.gateway.clients_host.clone(), - mixnet_listener: bond.gateway.try_resolve_hostname().map_err(|err| { - GatewayConversionError::InvalidAddress(bond.gateway.mix_host.clone(), err) - })?, + host, + mix_host, + clients_port: bond.gateway.clients_port, identity_key: identity::PublicKey::from_base58_string(&bond.gateway.identity_key)?, sphinx_key: encryption::PublicKey::from_base58_string(&bond.gateway.sphinx_key)?, version: bond.gateway.version.clone(), diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 666d5e3003..316d8c90c3 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -20,6 +20,10 @@ use nymsphinx_types::Node as SphinxNode; use rand::Rng; use std::collections::HashMap; use std::convert::TryInto; +use std::fmt::{self, Display, Formatter}; +use std::io; +use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; +use std::str::FromStr; pub mod filter; pub mod gateway; @@ -35,6 +39,43 @@ pub enum NymTopologyError { NoMixesOnLayerAvailable(MixLayer), } +#[derive(Debug, Clone)] +pub enum NetworkAddress { + IpAddr(IpAddr), + Hostname(String), +} + +impl NetworkAddress { + pub fn to_socket_addrs(&self, port: u16) -> io::Result> { + match self { + NetworkAddress::IpAddr(addr) => Ok(vec![SocketAddr::new(*addr, port)]), + NetworkAddress::Hostname(hostname) => { + Ok((hostname.as_str(), port).to_socket_addrs()?.collect()) + } + } + } +} + +impl FromStr for NetworkAddress { + type Err = std::io::Error; + + fn from_str(s: &str) -> Result { + if let Ok(ip_addr) = s.parse() { + return Ok(NetworkAddress::IpAddr(ip_addr)); + } + todo!() + } +} + +impl Display for NetworkAddress { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + NetworkAddress::IpAddr(ip_addr) => ip_addr.fmt(f), + NetworkAddress::Hostname(hostname) => hostname.fmt(f), + } + } +} + pub type MixLayer = u8; #[derive(Debug, Clone)] diff --git a/common/topology/src/mix.rs b/common/topology/src/mix.rs index a97e342745..291b7fabdc 100644 --- a/common/topology/src/mix.rs +++ b/common/topology/src/mix.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::filter; +use crate::{filter, NetworkAddress}; use crypto::asymmetric::{encryption, identity}; use mixnet_contract::MixNodeBond; use nymsphinx_addressing::nodes::NymNodeRoutingAddress; @@ -84,7 +84,10 @@ pub struct Node { // on the network at a type, right? pub stake: u128, pub location: String, - pub host: SocketAddr, + pub host: NetworkAddress, + // we're keeping this as separate resolved field since we do not want to be resolving the potential + // hostname every time we want to construct a path via this node + pub mix_host: SocketAddr, pub identity_key: identity::PublicKey, pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519 pub layer: u64, @@ -99,7 +102,9 @@ impl filter::Versioned for Node { impl<'a> From<&'a Node> for SphinxNode { fn from(node: &'a Node) -> Self { - let node_address_bytes = NymNodeRoutingAddress::from(node.host).try_into().unwrap(); + let node_address_bytes = NymNodeRoutingAddress::from(node.mix_host) + .try_into() + .unwrap(); SphinxNode::new(node_address_bytes, (&node.sphinx_key).into()) } @@ -112,6 +117,19 @@ impl<'a> TryFrom<&'a MixNodeBond> for Node { if bond.amount.len() > 1 { return Err(MixnodeConversionError::InvalidStake); } + + let host: NetworkAddress = bond.mix_node.host.parse().map_err(|err| { + MixnodeConversionError::InvalidAddress(bond.mix_node.host.clone(), err) + })?; + + // try to completely resolve the host in the mix situation to avoid doing it every + // single time we want to construct a path + let mix_host = host + .to_socket_addrs(bond.mix_node.mix_port) + .map_err(|err| { + MixnodeConversionError::InvalidAddress(bond.mix_node.host.clone(), err) + })?[0]; + Ok(Node { owner: bond.owner.as_str().to_owned(), stake: bond @@ -120,9 +138,8 @@ impl<'a> TryFrom<&'a MixNodeBond> for Node { .map(|stake| stake.amount.into()) .unwrap_or(0), location: bond.mix_node.location.clone(), - host: bond.mix_node.try_resolve_hostname().map_err(|err| { - MixnodeConversionError::InvalidAddress(bond.mix_node.host.clone(), err) - })?, + host, + mix_host, identity_key: identity::PublicKey::from_base58_string(&bond.mix_node.identity_key)?, sphinx_key: encryption::PublicKey::from_base58_string(&bond.mix_node.sphinx_key)?, layer: bond.mix_node.layer, diff --git a/gateway/src/commands/init.rs b/gateway/src/commands/init.rs index f5ad70456c..3e285b65ad 100644 --- a/gateway/src/commands/init.rs +++ b/gateway/src/commands/init.rs @@ -106,21 +106,23 @@ fn show_bonding_info(config: &Config) { "\nTo bond your gateway you will [most likely] need to provide the following: Identity key: {} Sphinx key: {} - Mix Host: {} - Clients Host: {} + Host: {} + Mix Port: {} + Clients Port: {} Location: [physical location of your node's server] Version: {} ", identity_keypair.public_key().to_base58_string(), sphinx_keypair.public_key().to_base58_string(), - config.get_mix_announce_address(), - config.get_clients_announce_address(), + config.get_announce_address(), + config.get_mix_port(), + config.get_clients_port(), config.get_version(), ); } pub fn execute(matches: &ArgMatches) { - let id = matches.value_of("id").unwrap(); + let id = matches.value_of(ID_ARG_NAME).unwrap(); println!("Initialising gateway {}...", id); let already_init = if Config::default_config_file_path(id).exists() { diff --git a/gateway/src/commands/run.rs b/gateway/src/commands/run.rs index e83645c724..6c1b8e2041 100644 --- a/gateway/src/commands/run.rs +++ b/gateway/src/commands/run.rs @@ -156,14 +156,8 @@ pub fn execute(matches: &ArgMatches) { let sphinx_keypair = load_sphinx_keys(&pathfinder); let identity = load_identity_keys(&pathfinder); - let mix_listening_ip_string = config.get_mix_listening_address().ip().to_string(); - if special_addresses().contains(&mix_listening_ip_string.as_ref()) { - show_binding_warning(mix_listening_ip_string); - } - - let clients_listening_ip_string = config.get_clients_listening_address().ip().to_string(); - if special_addresses().contains(&clients_listening_ip_string.as_ref()) { - show_binding_warning(clients_listening_ip_string); + if special_addresses().contains(&&*config.get_listening_address()) { + show_binding_warning(config.get_listening_address()); } println!( @@ -172,21 +166,12 @@ pub fn execute(matches: &ArgMatches) { ); println!( - "Listening for incoming sphinx packets on {}", - config.get_mix_listening_address() + "Listening for incoming packets on {}", + config.get_listening_address() ); println!( - "Announcing the following socket address for sphinx packets: {}", - config.get_mix_announce_address() - ); - - println!( - "Listening for incoming clients packets on {}", - config.get_clients_listening_address() - ); - println!( - "Announcing the following socket address for clients packets: {}", - config.get_clients_announce_address() + "Announcing the following address: {}", + config.get_announce_address() ); println!( diff --git a/gateway/src/config/mod.rs b/gateway/src/config/mod.rs index 1dad80b084..398bb4e8fb 100644 --- a/gateway/src/config/mod.rs +++ b/gateway/src/config/mod.rs @@ -3,11 +3,8 @@ use crate::config::template::config_template; use config::{deserialize_duration, deserialize_validators, NymConfig}; -use log::*; use serde::{Deserialize, Serialize}; -use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; -use std::str::FromStr; use std::time::Duration; pub mod persistence; @@ -58,8 +55,6 @@ pub fn missing_vec_string_value() -> Vec { pub struct Config { gateway: Gateway, - mixnet_endpoint: MixnetEndpoint, - clients_endpoint: ClientsEndpoint, #[serde(default)] @@ -155,169 +150,38 @@ impl Config { self } - pub fn with_mix_listening_host>(mut self, host: S) -> Self { - // see if the provided `host` is just an ip address or ip:port - let host = host.into(); - - // is it ip:port? - match SocketAddr::from_str(host.as_ref()) { - Ok(socket_addr) => { - self.mixnet_endpoint.listening_address = socket_addr; - self - } - // try just for ip - Err(_) => match IpAddr::from_str(host.as_ref()) { - Ok(ip_addr) => { - self.mixnet_endpoint.listening_address.set_ip(ip_addr); - self - } - Err(_) => { - error!( - "failed to make any changes to config - invalid host {}", - host - ); - self - } - }, - } - } - - pub fn with_mix_listening_port(mut self, port: u16) -> Self { - self.mixnet_endpoint.listening_address.set_port(port); + pub fn with_listening_address>(mut self, listening_address: S) -> Self { + self.gateway.listening_address = listening_address.into(); self } - pub fn with_mix_announce_host>(mut self, host: S) -> Self { - // this is slightly more complicated as we store announce information as String, - // since it might not necessarily be a valid SocketAddr (say `nymtech.net:8080` is a valid - // announce address, yet invalid SocketAddr` - - // first let's see if we received host:port or just host part of an address - let host = host.into(); - match host.split(':').count() { - 1 => { - // we provided only 'host' part so we are going to reuse existing port - self.mixnet_endpoint.announce_address = - format!("{}:{}", host, self.mixnet_endpoint.listening_address.port()); - self - } - 2 => { - // we provided 'host:port' so just put the whole thing there - self.mixnet_endpoint.announce_address = host; - self - } - _ => { - // we provided something completely invalid, so don't try to parse it - error!( - "failed to make any changes to config - invalid announce host {}", - host - ); - self - } - } - } - - pub fn mix_announce_host_from_listening_host(mut self) -> Self { - self.mixnet_endpoint.announce_address = self.mixnet_endpoint.listening_address.to_string(); + pub fn with_announce_address>(mut self, announce_address: S) -> Self { + self.gateway.announce_address = announce_address.into(); self } - pub fn with_mix_announce_port(mut self, port: u16) -> Self { - let current_host: Vec<_> = self.mixnet_endpoint.announce_address.split(':').collect(); - debug_assert_eq!(current_host.len(), 2); - self.mixnet_endpoint.announce_address = format!("{}:{}", current_host[0], port); + pub fn with_mix_port(mut self, port: u16) -> Self { + self.gateway.mix_port = port; self } - pub fn with_clients_listening_host>(mut self, host: S) -> Self { - // see if the provided `host` is just an ip address or ip:port - let host = host.into(); - - // is it ip:port? - match SocketAddr::from_str(host.as_ref()) { - Ok(socket_addr) => { - self.clients_endpoint.listening_address = socket_addr; - self - } - // try just for ip - Err(_) => match IpAddr::from_str(host.as_ref()) { - Ok(ip_addr) => { - self.clients_endpoint.listening_address.set_ip(ip_addr); - self - } - Err(_) => { - error!( - "failed to make any changes to config - invalid host {}", - host - ); - self - } - }, - } - } - - pub fn clients_announce_host_from_listening_host(mut self) -> Self { - self.clients_endpoint.announce_address = format!( - "ws://{}", - self.clients_endpoint.listening_address.to_string() - ); + pub fn with_clients_port(mut self, port: u16) -> Self { + self.gateway.clients_port = port; self } - pub fn with_clients_listening_port(mut self, port: u16) -> Self { - self.clients_endpoint.listening_address.set_port(port); + pub fn announce_host_from_listening_host(mut self) -> Self { + self.gateway.announce_address = self.gateway.listening_address.clone(); self } - pub fn with_clients_announce_host>(mut self, host: S) -> Self { - // this is slightly more complicated as we store announce information as String, - // since it might not necessarily be a valid SocketAddr (say `nymtech.net:8080` is a valid - // announce address, yet invalid SocketAddr` - - // first let's see if we received host:port or just host part of an address - let host = host.into(); - match host.split(':').count() { - 1 => { - // we provided only 'host' part so we are going to reuse existing port - self.clients_endpoint.announce_address = format!( - "{}:{}", - host, - self.clients_endpoint.listening_address.port() - ); - // make sure it has 'ws' prefix (by extension it also includes 'wss') - if !self.clients_endpoint.announce_address.starts_with("ws") { - self.clients_endpoint.announce_address = - format!("ws://{}", self.clients_endpoint.announce_address); - } - self - } - 2 => { - // we provided 'host:port' so just put the whole thing there - self.clients_endpoint.announce_address = host; - // make sure it has 'ws' prefix (by extension it also includes 'wss') - if !self.clients_endpoint.announce_address.starts_with("ws") { - self.clients_endpoint.announce_address = - format!("ws://{}", self.clients_endpoint.announce_address); - } - self - } - _ => { - // we provided something completely invalid, so don't try to parse it - error!( - "failed to make any changes to config - invalid announce host {}", - host - ); - self - } - } - } - - pub fn with_clients_announce_port(mut self, port: u16) -> Self { - let current_host: Vec<_> = self.clients_endpoint.announce_address.split(':').collect(); - debug_assert_eq!(current_host.len(), 2); - self.clients_endpoint.announce_address = format!("{}:{}", current_host[0], port); - self - } + // pub fn clients_announce_host_from_listening_host(mut self) -> Self { + // self.clients_endpoint.announce_address = format!( + // "ws://{}", + // self.clients_endpoint.listening_address.to_string() + // ); + // self + // } pub fn with_custom_clients_inboxes>(mut self, inboxes_dir: S) -> Self { self.clients_endpoint.inboxes_directory = PathBuf::from(inboxes_dir.into()); @@ -363,20 +227,20 @@ impl Config { self.gateway.mixnet_contract_address.clone() } - pub fn get_mix_listening_address(&self) -> SocketAddr { - self.mixnet_endpoint.listening_address + pub fn get_listening_address(&self) -> String { + self.gateway.listening_address.clone() } - pub fn get_mix_announce_address(&self) -> String { - self.mixnet_endpoint.announce_address.clone() + pub fn get_announce_address(&self) -> String { + self.gateway.announce_address.clone() } - pub fn get_clients_listening_address(&self) -> SocketAddr { - self.clients_endpoint.listening_address + pub fn get_mix_port(&self) -> u16 { + self.gateway.mix_port } - pub fn get_clients_announce_address(&self) -> String { - self.clients_endpoint.announce_address.clone() + pub fn get_clients_port(&self) -> u16 { + self.gateway.clients_port } pub fn get_clients_inboxes_dir(&self) -> PathBuf { @@ -429,6 +293,22 @@ pub struct Gateway { /// ID specifies the human readable ID of this particular gateway. id: String, + /// Address to which this mixnode will bind to and will be listening for packets. + listening_address: String, + + /// Optional address announced to the validator for the clients to connect to. + /// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address + /// later on by using name resolvable with a DNS query, such as `nymtech.net`. + announce_address: String, + + /// Port used for listening for all mixnet traffic. + /// (default: 1789) + mix_port: u16, + + /// Port used for listening for all client-related traffic. + /// (default: 9000) + clients_port: u16, + /// Path to file containing private identity key. private_identity_key_file: PathBuf, @@ -481,6 +361,10 @@ impl Default for Gateway { Gateway { version: env!("CARGO_PKG_VERSION").to_string(), id: "".to_string(), + listening_address: "0.0.0.0".to_string(), + announce_address: "127.0.0.1".to_string(), + mix_port: DEFAULT_MIX_LISTENING_PORT, + clients_port: DEFAULT_CLIENT_LISTENING_PORT, private_identity_key_file: Default::default(), public_identity_key_file: Default::default(), private_sphinx_key_file: Default::default(), @@ -492,48 +376,9 @@ impl Default for Gateway { } } -#[derive(Debug, Deserialize, PartialEq, Serialize)] -#[serde(deny_unknown_fields)] -pub struct MixnetEndpoint { - /// Socket address to which this gateway will bind to - /// and will be listening for sphinx packets coming from the mixnet. - listening_address: SocketAddr, - - /// Optional address announced to the directory server for the clients to connect to. - /// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address - /// later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. - /// Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` - /// are valid announce addresses, while the later will default to whatever port is used for - /// `listening_address`. - announce_address: String, -} - -impl Default for MixnetEndpoint { - fn default() -> Self { - MixnetEndpoint { - listening_address: format!("0.0.0.0:{}", DEFAULT_MIX_LISTENING_PORT) - .parse() - .unwrap(), - announce_address: format!("127.0.0.1:{}", DEFAULT_MIX_LISTENING_PORT), - } - } -} - #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct ClientsEndpoint { - /// Socket address to which this gateway will bind to - /// and will be listening for data packets coming from the clients. - listening_address: SocketAddr, - - /// Optional address announced to the directory server for the clients to connect to. - /// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address - /// later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. - /// Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` - /// are valid announce addresses, while the later will default to whatever port is used for - /// `listening_address`. - announce_address: String, - /// Path to the directory with clients inboxes containing messages stored for them. inboxes_directory: PathBuf, @@ -555,10 +400,6 @@ impl ClientsEndpoint { impl Default for ClientsEndpoint { fn default() -> Self { ClientsEndpoint { - listening_address: format!("0.0.0.0:{}", DEFAULT_CLIENT_LISTENING_PORT) - .parse() - .unwrap(), - announce_address: format!("ws://127.0.0.1:{}", DEFAULT_CLIENT_LISTENING_PORT), inboxes_directory: Default::default(), ledger_path: Default::default(), } diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index 9d4793e4d4..c5b07d7101 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -70,9 +70,21 @@ impl Gateway { ack_sender, ); - let listener = mixnet_handling::Listener::new(self.config.get_mix_listening_address()); + let listening_address = match format!( + "{}:{}", + self.config.get_listening_address(), + self.config.get_mix_port() + ) + .parse() + { + Ok(addr) => addr, + Err(err) => { + error!("Failed to correctly parse socket address - {}", err); + process::exit(1); + } + }; - listener.start(connection_handler); + mixnet_handling::Listener::new(listening_address).start(connection_handler); } fn start_client_websocket_listener( @@ -82,11 +94,22 @@ impl Gateway { ) { info!("Starting client [web]socket listener..."); - websocket::Listener::new( - self.config.get_clients_listening_address(), - Arc::clone(&self.identity), + let listening_address = match format!( + "{}:{}", + self.config.get_listening_address(), + self.config.get_clients_port() ) - .start(clients_handler_sender, forwarding_channel); + .parse() + { + Ok(addr) => addr, + Err(err) => { + error!("Failed to correctly parse socket address - {}", err); + process::exit(1); + } + }; + + websocket::Listener::new(listening_address, Arc::clone(&self.identity)) + .start(clients_handler_sender, forwarding_channel); } fn start_packet_forwarder(&self) -> MixForwardingSender { @@ -127,9 +150,6 @@ impl Gateway { // TODO: ask DH whether this function still makes sense in ^0.10 async fn check_if_same_ip_gateway_exists(&self) -> Option { - let announced_mix_host = self.config.get_mix_announce_address(); - let announced_clients_host = self.config.get_clients_announce_address(); - let validator_client_config = validator_client::Config::new( self.config.get_validator_rest_endpoints(), self.config.get_validator_mixnet_contract_address(), @@ -144,12 +164,11 @@ impl Gateway { } }; + let our_host = self.config.get_announce_address(); + existing_gateways .iter() - .find(|node| { - node.gateway.mix_host == announced_mix_host - || node.gateway.clients_host == announced_clients_host - }) + .find(|node| node.gateway.host == our_host) .map(|node| node.gateway().identity_key.clone()) } diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index 68c07296ba..cffa00393f 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -136,9 +136,8 @@ pub fn execute(matches: &ArgMatches) { let identity_keypair = load_identity_keys(&pathfinder); let sphinx_keypair = load_sphinx_keys(&pathfinder); - let listening_ip_string = config.get_listening_address().ip().to_string(); - if special_addresses().contains(&listening_ip_string.as_ref()) { - show_binding_warning(listening_ip_string); + if special_addresses().contains(&&*config.get_listening_address()) { + show_binding_warning(config.get_listening_address()); } println!( @@ -150,7 +149,7 @@ pub fn execute(matches: &ArgMatches) { config.get_listening_address() ); println!( - "Announcing the following socket address: {}", + "Announcing the following address: {}", config.get_announce_address() ); @@ -158,7 +157,8 @@ pub fn execute(matches: &ArgMatches) { "\nTo bond your mixnode, go to https://web-wallet-finney.nymtech.net/. You will need to provide the following: Identity key: {} Sphinx key: {} - Host: {} + Address: {} + Mix port: {} Layer: {} Location: [physical location of your node's server] Version: {} @@ -166,6 +166,7 @@ pub fn execute(matches: &ArgMatches) { identity_keypair.public_key().to_base58_string(), sphinx_keypair.public_key().to_base58_string(), config.get_announce_address(), + config.get_mix_port(), config.get_layer(), config.get_version(), ); diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 10d84ed849..e5af5ea822 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -3,11 +3,8 @@ use crate::config::template::config_template; use config::{deserialize_duration, deserialize_validators, NymConfig}; -use log::error; use serde::{Deserialize, Serialize}; -use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; -use std::str::FromStr; use std::time::Duration; pub mod persistence; @@ -16,7 +13,7 @@ mod template; pub(crate) const MISSING_VALUE: &str = "MISSING VALUE"; // 'MIXNODE' -const DEFAULT_LISTENING_PORT: u16 = 1789; +const DEFAULT_MIX_LISTENING_PORT: u16 = 1789; pub(crate) const DEFAULT_VALIDATOR_REST_ENDPOINTS: &[&str] = &[ "http://testnet-finney-validator.nymtech.net:1317", "http://testnet-finney-validator2.nymtech.net:1317", @@ -152,77 +149,23 @@ impl Config { self } - pub fn with_listening_host>(mut self, host: S) -> Self { - // see if the provided `host` is just an ip address or ip:port - let host = host.into(); - - // is it ip:port? - match SocketAddr::from_str(host.as_ref()) { - Ok(socket_addr) => { - self.mixnode.listening_address = socket_addr; - self - } - // try just for ip - Err(_) => match IpAddr::from_str(host.as_ref()) { - Ok(ip_addr) => { - self.mixnode.listening_address.set_ip(ip_addr); - self - } - Err(_) => { - error!( - "failed to make any changes to config - invalid host {}", - host - ); - self - } - }, - } - } - - pub fn with_listening_port(mut self, port: u16) -> Self { - self.mixnode.listening_address.set_port(port); + pub fn with_listening_address>(mut self, listening_address: S) -> Self { + self.mixnode.listening_address = listening_address.into(); self } - pub fn with_announce_host>(mut self, host: S) -> Self { - // this is slightly more complicated as we store announce information as String, - // since it might not necessarily be a valid SocketAddr (say `nymtech.net:8080` is a valid - // announce address, yet invalid SocketAddr` - - // first lets see if we received host:port or just host part of an address - let host = host.into(); - match host.split(':').count() { - 1 => { - // we provided only 'host' part so we are going to reuse existing port - self.mixnode.announce_address = - format!("{}:{}", host, self.mixnode.listening_address.port()); - self - } - 2 => { - // we provided 'host:port' so just put the whole thing there - self.mixnode.announce_address = host; - self - } - _ => { - // we provided something completely invalid, so don't try to parse it - error!( - "failed to make any changes to config - invalid announce host {}", - host - ); - self - } - } - } - - pub fn announce_host_from_listening_host(mut self) -> Self { - self.mixnode.announce_address = self.mixnode.listening_address.to_string(); + pub fn with_announce_address>(mut self, announce_address: S) -> Self { + self.mixnode.announce_address = announce_address.into(); self } - pub fn with_announce_port(mut self, port: u16) -> Self { - let current_host: Vec<_> = self.mixnode.announce_address.split(':').collect(); - debug_assert_eq!(current_host.len(), 2); - self.mixnode.announce_address = format!("{}:{}", current_host[0], port); + pub fn with_mix_port(mut self, port: u16) -> Self { + self.mixnode.mix_port = port; + self + } + + pub fn announce_address_from_listening_address(mut self) -> Self { + self.mixnode.announce_address = self.mixnode.listening_address.clone(); self } @@ -272,14 +215,18 @@ impl Config { self.mixnode.layer } - pub fn get_listening_address(&self) -> SocketAddr { - self.mixnode.listening_address + pub fn get_listening_address(&self) -> String { + self.mixnode.listening_address.clone() } pub fn get_announce_address(&self) -> String { self.mixnode.announce_address.clone() } + pub fn get_mix_port(&self) -> u16 { + self.mixnode.mix_port + } + pub fn get_packet_forwarding_initial_backoff(&self) -> Duration { self.debug.packet_forwarding_initial_backoff } @@ -349,17 +296,18 @@ pub struct MixNode { /// Layer of this particular mixnode determining its position in the network. layer: u64, - /// Socket address to which this mixnode will bind to and will be listening for packets. - listening_address: SocketAddr, + /// Address to which this mixnode will bind to and will be listening for packets. + listening_address: String, /// Optional address announced to the validator for the clients to connect to. /// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address - /// later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. - /// Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` - /// are valid announce addresses, while the later will default to whatever port is used for - /// `listening_address`. + /// later on by using name resolvable with a DNS query, such as `nymtech.net`. announce_address: String, + /// Port used for listening for all mixnet traffic. + /// (default: 1789) + mix_port: u16, + /// Path to file containing private identity key. #[serde(default = "missing_string_value")] private_identity_key_file: PathBuf, @@ -374,7 +322,7 @@ pub struct MixNode { /// Path to file containing public sphinx key. public_sphinx_key_file: PathBuf, - /// Validator server to which the node will be reporting their presence data. + /// Validator server from which the node gets the view on the network. #[serde( deserialize_with = "deserialize_validators", default = "missing_vec_string_value", @@ -415,10 +363,9 @@ impl Default for MixNode { version: env!("CARGO_PKG_VERSION").to_string(), id: "".to_string(), layer: 0, - listening_address: format!("0.0.0.0:{}", DEFAULT_LISTENING_PORT) - .parse() - .unwrap(), - announce_address: format!("127.0.0.1:{}", DEFAULT_LISTENING_PORT), + listening_address: "0.0.0.0".to_string(), + announce_address: "127.0.0.1".to_string(), + mix_port: DEFAULT_MIX_LISTENING_PORT, private_identity_key_file: Default::default(), public_identity_key_file: Default::default(), private_sphinx_key_file: Default::default(), diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 8582c15d0d..7d0dac15d0 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -61,7 +61,7 @@ impl MixNode { let mut config = rocket::config::Config::release_default(); // bind to the same address as we are using for mixnodes - config.address = self.config.get_listening_address().ip(); + config.address = self.config.get_listening_address().parse().unwrap(); let verloc_state = VerlocState::new(atomic_verloc_result); let descriptor = self.descriptor.clone(); @@ -106,9 +106,21 @@ impl MixNode { let connection_handler = ConnectionHandler::new(packet_processor, delay_forwarding_channel); - let listener = Listener::new(self.config.get_listening_address()); + let listening_address = match format!( + "{}:{}", + self.config.get_listening_address(), + self.config.get_mix_port() + ) + .parse() + { + Ok(addr) => addr, + Err(err) => { + error!("Failed to correctly parse socket address - {}", err); + process::exit(1); + } + }; - listener.start(connection_handler); + Listener::new(listening_address).start(connection_handler); } fn start_packet_delay_forwarder( @@ -147,10 +159,20 @@ impl MixNode { // use the same binding address with the HARDCODED port for time being (I don't like that approach personally) - let listening_address = rtt_measurement::replace_port( + let listening_address = match format!( + "{}:{}", self.config.get_listening_address(), - rtt_measurement::DEFAULT_MEASUREMENT_PORT, - ); + rtt_measurement::DEFAULT_MEASUREMENT_PORT + ) + .parse() + { + Ok(addr) => addr, + Err(err) => { + error!("Failed to correctly parse socket address - {}", err); + process::exit(1); + } + }; + let config = rtt_measurement::ConfigBuilder::new() .listening_address(listening_address) .packets_per_node(self.config.get_measurement_packets_per_node()) @@ -186,9 +208,11 @@ impl MixNode { } }; + let our_host = self.config.get_announce_address(); + existing_nodes .iter() - .find(|node| node.mix_node.host == self.config.get_announce_address()) + .find(|node| node.mix_node.host == our_host) .map(|node| node.mix_node.identity_key.clone()) } diff --git a/network-monitor/src/monitor/preparer.rs b/network-monitor/src/monitor/preparer.rs index 8905f5d8b5..a1aba7b924 100644 --- a/network-monitor/src/monitor/preparer.rs +++ b/network-monitor/src/monitor/preparer.rs @@ -408,7 +408,7 @@ impl PacketPreparer { gateway_packets.push(mix_packet.pop().unwrap()); } packets.push(GatewayPackets::new( - node.client_listener, + node.clients_address(), node.identity_key, gateway_packets, )) @@ -461,10 +461,7 @@ impl PacketPreparer { // we are not testing the gateway from our 'good' topology -> it's probably // situation similar to using 'good' qa-topology but testing testnet nodes. let main_gateway_packets = GatewayPackets::new( - self.tested_network - .main_v4_gateway() - .client_listener - .clone(), + self.tested_network.main_v4_gateway().clients_address(), main_gateway_id, mix_packets, );