Feature/host ip split (#654)

* Updated smart contract to work with future cosmwasm 0.14.0 update

* Updated the rest of the codebase

* Reordered imports

* Missing imports

* cargo fmt

* More cargo fmt action

* Introduced type alias for IdentityKey reference

* Constant defined arguments for gateway and mixnode

* Spliting host into explicit address and port(s)

* Using more type restrictive IpAddr rather than String for listening address

* Updated config templates

* Tentative upgrade commands, probably to be further changed before release

* Fixed mixnet contract test fixtures

* Further missing test adjustments
This commit is contained in:
Jędrzej Stuczyński
2021-06-25 10:33:12 +01:00
committed by GitHub
parent 5ba20268dd
commit b7f197d278
28 changed files with 661 additions and 722 deletions
+2 -2
View File
@@ -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?
+2 -2
View File
@@ -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?
+1 -1
View File
@@ -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,
+9 -15
View File
@@ -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,29 +21,24 @@ 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,
version,
}
}
pub fn try_resolve_hostname(&self) -> Result<SocketAddr, io::Error> {
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)]
+3 -16
View File
@@ -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<SocketAddr, io::Error> {
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
//
+9 -5
View File
@@ -217,7 +217,8 @@ mod message_receiver {
owner: "foomp1".to_string(),
stake: 123,
location: "unknown".to_string(),
host: "10.20.30.40:1789".parse().unwrap(),
host: "10.20.30.40".parse().unwrap(),
mix_host: "10.20.30.40:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
"3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7",
)
@@ -237,7 +238,8 @@ mod message_receiver {
owner: "foomp2".to_string(),
stake: 123,
location: "unknown".to_string(),
host: "11.21.31.41:1789".parse().unwrap(),
host: "11.21.31.41".parse().unwrap(),
mix_host: "11.21.31.41:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
"D6YaMzLSY7mANtSQRKXsmMZpqgqiVkeiagKM4V4oFPFr",
)
@@ -257,7 +259,8 @@ mod message_receiver {
owner: "foomp3".to_string(),
stake: 123,
location: "unknown".to_string(),
host: "12.22.32.42:1789".parse().unwrap(),
host: "12.22.32.42".parse().unwrap(),
mix_host: "12.22.32.42:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
"GkWDysw4AjESv1KiAiVn7JzzCMJeksxNSXVfr1PpX8wD",
)
@@ -278,8 +281,9 @@ mod message_receiver {
owner: "foomp4".to_string(),
stake: 123,
location: "unknown".to_string(),
client_listener: "ws://1.2.3.4:9000".to_string(),
mixnet_listener: "1.2.3.4:1789".parse().unwrap(),
host: "1.2.3.4".parse().unwrap(),
mix_host: "1.2.3.4:1789".parse().unwrap(),
clients_port: 9000,
identity_key: identity::PublicKey::from_base58_string(
"FioFa8nMmPpQnYi7JyojoTuwGLeyNS8BF4ChPr29zUML",
)
+25 -8
View File
@@ -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(),
+44 -1
View File
@@ -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,44 @@ 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<Vec<SocketAddr>> {
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<Self, Self::Err> {
if let Ok(ip_addr) = s.parse() {
Ok(NetworkAddress::IpAddr(ip_addr))
} else {
Ok(NetworkAddress::Hostname(s.to_string()))
}
}
}
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)]
@@ -248,7 +290,8 @@ mod converting_mixes_to_vec {
owner: "N/A".to_string(),
stake: 0,
location: "London".to_string(),
host: "3.3.3.3:1789".parse().unwrap(),
host: "3.3.3.3".parse().unwrap(),
mix_host: "3.3.3.3:1789".parse().unwrap(),
identity_key: identity::PublicKey::from_base58_string(
"3ebjp1Fb9hdcS1AR6AZihgeJiMHkB5jjJUsvqNnfQwU7",
)
+23 -6
View File
@@ -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,
+8 -4
View File
@@ -107,6 +107,7 @@ pub mod helpers {
pub fn mix_node_fixture() -> MixNode {
MixNode::new(
"mix.node.org".to_string(),
1789,
1,
"Sweden".to_string(),
"sphinx".to_string(),
@@ -118,6 +119,7 @@ pub mod helpers {
pub fn mixnode_bond_fixture() -> MixNodeBond {
let mix_node = MixNode::new(
"1.1.1.1".to_string(),
1789,
1,
"London".to_string(),
"1234".to_string(),
@@ -129,8 +131,9 @@ pub mod helpers {
pub fn gateway_fixture() -> Gateway {
Gateway::new(
"1.1.1.1:1234".to_string(),
"ws://1.1.1.1:1235".to_string(),
"1.1.1.1".to_string(),
1789,
9000,
"Sweden".to_string(),
"sphinx".to_string(),
"identity".to_string(),
@@ -140,8 +143,9 @@ pub mod helpers {
pub fn gateway_bond_fixture() -> GatewayBond {
let gateway = Gateway::new(
"1.1.1.1:1234".to_string(),
"ws://1.1.1.1:1235".to_string(),
"1.1.1.1".to_string(),
1789,
9000,
"London".to_string(),
"sphinx".to_string(),
"identity".to_string(),
+4 -4
View File
@@ -1210,7 +1210,7 @@ pub mod tests {
let msg = ExecuteMsg::BondGateway {
gateway: Gateway {
identity_key: "myAwesomeGateway".to_string(),
mix_host: "1.1.1.1:1789".into(),
host: "1.1.1.1".into(),
..helpers::gateway_fixture()
},
};
@@ -1221,7 +1221,7 @@ pub mod tests {
let msg = ExecuteMsg::BondGateway {
gateway: Gateway {
identity_key: "myAwesomeGateway".to_string(),
mix_host: "2.2.2.2:1789".into(),
host: "2.2.2.2".into(),
..helpers::gateway_fixture()
},
};
@@ -1230,12 +1230,12 @@ pub mod tests {
// make sure the host information was updated
assert_eq!(
"2.2.2.2:1789".to_string(),
"2.2.2.2".to_string(),
gateways_read(deps.as_ref().storage)
.load("myAwesomeGateway".as_bytes())
.unwrap()
.gateway
.mix_host
.host
);
}
+27 -50
View File
@@ -1,7 +1,7 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::override_config;
use crate::commands::*;
use crate::config::persistence::pathfinder::GatewayPathfinder;
use crate::config::Config;
use clap::{App, Arg, ArgMatches};
@@ -12,83 +12,58 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
App::new("init")
.about("Initialise the gateway")
.arg(
Arg::with_name("id")
.long("id")
Arg::with_name(ID_ARG_NAME)
.long(ID_ARG_NAME)
.help("Id of the gateway we want to create config for.")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("mix-host")
.long("mix-host")
Arg::with_name(HOST_ARG_NAME)
.long(HOST_ARG_NAME)
.help("The custom host on which the gateway will be running for receiving sphinx packets")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("mix-port")
.long("mix-port")
Arg::with_name(MIX_PORT_ARG_NAME)
.long(MIX_PORT_ARG_NAME)
.help("The port on which the gateway will be listening for sphinx packets")
.takes_value(true)
)
.arg(
Arg::with_name("clients-host")
.long("clients-host")
.help("The custom host on which the gateway will be running for receiving clients gateway-requests")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("clients-port")
.long("clients-port")
Arg::with_name(CLIENTS_PORT_ARG_NAME)
.long(CLIENTS_PORT_ARG_NAME)
.help("The port on which the gateway will be listening for clients gateway-requests")
.takes_value(true)
)
.arg(
Arg::with_name("mix-announce-host")
.long("mix-announce-host")
Arg::with_name(ANNOUNCE_HOST_ARG_NAME)
.long(ANNOUNCE_HOST_ARG_NAME)
.help("The host that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("mix-announce-port")
.long("mix-announce-port")
.help("The port that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("clients-announce-host")
.long("clients-announce-host")
.help("The host that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("clients-announce-port")
.long("clients-announce-port")
.help("The port that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("inboxes")
.long("inboxes")
Arg::with_name(INBOXES_ARG_NAME)
.long(INBOXES_ARG_NAME)
.help("Directory with inboxes where all packets for the clients are stored")
.takes_value(true)
)
.arg(
Arg::with_name("clients-ledger")
.long("clients-ledger")
.help("Ledger directory containing registered clients")
Arg::with_name(CLIENTS_LEDGER_ARG_NAME)
.long(CLIENTS_LEDGER_ARG_NAME)
.help("Ledger file containing registered clients")
.takes_value(true)
)
.arg(
Arg::with_name("validators")
.long("validators")
Arg::with_name(VALIDATORS_ARG_NAME)
.long(VALIDATORS_ARG_NAME)
.help("Comma separated list of rest endpoints of the validators")
.takes_value(true),
)
.arg(
Arg::with_name("mixnet-contract")
.long("mixnet-contract")
Arg::with_name(CONTRACT_ARG_NAME)
.long(CONTRACT_ARG_NAME)
.help("Address of the validator contract managing the network")
.takes_value(true),
)
@@ -131,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() {
+29 -50
View File
@@ -8,6 +8,16 @@ pub(crate) mod init;
pub(crate) mod run;
pub(crate) mod upgrade;
pub(crate) const ID_ARG_NAME: &str = "id";
pub(crate) const HOST_ARG_NAME: &str = "host";
pub(crate) const MIX_PORT_ARG_NAME: &str = "mix-port";
pub(crate) const CLIENTS_PORT_ARG_NAME: &str = "clients-port";
pub(crate) const VALIDATORS_ARG_NAME: &str = "validators";
pub(crate) const CONTRACT_ARG_NAME: &str = "mixnet-contract";
pub(crate) const ANNOUNCE_HOST_ARG_NAME: &str = "announce-host";
pub(crate) const INBOXES_ARG_NAME: &str = "inboxes";
pub(crate) const CLIENTS_LEDGER_ARG_NAME: &str = "clients-ledger";
fn parse_validators(raw: &str) -> Vec<String> {
raw.split(',')
.map(|raw_validator| raw_validator.trim().into())
@@ -15,85 +25,54 @@ fn parse_validators(raw: &str) -> Vec<String> {
}
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
let mut was_mix_host_overridden = false;
if let Some(mix_host) = matches.value_of("mix-host") {
config = config.with_mix_listening_host(mix_host);
was_mix_host_overridden = true;
let mut was_host_overridden = false;
if let Some(host) = matches.value_of(HOST_ARG_NAME) {
config = config.with_listening_address(host);
was_host_overridden = true;
}
if let Some(mix_port) = matches.value_of("mix-port").map(|port| port.parse::<u16>()) {
if let Some(mix_port) = matches
.value_of(MIX_PORT_ARG_NAME)
.map(|port| port.parse::<u16>())
{
if let Err(err) = mix_port {
// if port was overridden, it must be parsable
panic!("Invalid port value provided - {:?}", err);
}
config = config.with_mix_listening_port(mix_port.unwrap());
}
let mut was_clients_host_overridden = false;
if let Some(clients_host) = matches.value_of("clients-host") {
config = config.with_clients_listening_host(clients_host);
was_clients_host_overridden = true;
config = config.with_mix_port(mix_port.unwrap());
}
if let Some(clients_port) = matches
.value_of("clients-port")
.value_of(CLIENTS_PORT_ARG_NAME)
.map(|port| port.parse::<u16>())
{
if let Err(err) = clients_port {
// if port was overridden, it must be parsable
panic!("Invalid port value provided - {:?}", err);
}
config = config.with_clients_listening_port(clients_port.unwrap());
config = config.with_clients_port(clients_port.unwrap());
}
if let Some(mix_announce_host) = matches.value_of("mix-announce-host") {
config = config.with_mix_announce_host(mix_announce_host);
} else if was_mix_host_overridden {
if let Some(announce_host) = matches.value_of(ANNOUNCE_HOST_ARG_NAME) {
config = config.with_announce_address(announce_host);
} else if was_host_overridden {
// make sure our 'mix-announce-host' always defaults to 'mix-host'
config = config.mix_announce_host_from_listening_host()
config = config.announce_host_from_listening_host()
}
if let Some(mix_announce_port) = matches
.value_of("mix-announce-port")
.map(|port| port.parse::<u16>())
{
if let Err(err) = mix_announce_port {
// if port was overridden, it must be parsable
panic!("Invalid port value provided - {:?}", err);
}
config = config.with_mix_announce_port(mix_announce_port.unwrap());
}
if let Some(clients_announce_host) = matches.value_of("clients-announce-host") {
config = config.with_clients_announce_host(clients_announce_host);
} else if was_clients_host_overridden {
// make sure our 'clients-announce-host' always defaults to 'clients-host'
config = config.clients_announce_host_from_listening_host()
}
if let Some(clients_announce_port) = matches
.value_of("clients-announce-port")
.map(|port| port.parse::<u16>())
{
if let Err(err) = clients_announce_port {
// if port was overridden, it must be parsable
panic!("Invalid port value provided - {:?}", err);
}
config = config.with_clients_announce_port(clients_announce_port.unwrap());
}
if let Some(raw_validators) = matches.value_of("validators") {
if let Some(raw_validators) = matches.value_of(VALIDATORS_ARG_NAME) {
config = config.with_custom_validators(parse_validators(raw_validators));
}
if let Some(contract_address) = matches.value_of("mixnet-contract") {
if let Some(contract_address) = matches.value_of(CONTRACT_ARG_NAME) {
config = config.with_custom_mixnet_contract(contract_address)
}
if let Some(inboxes_dir) = matches.value_of("inboxes") {
if let Some(inboxes_dir) = matches.value_of(INBOXES_ARG_NAME) {
config = config.with_custom_clients_inboxes(inboxes_dir);
}
if let Some(clients_ledger) = matches.value_of("clients-ledger") {
if let Some(clients_ledger) = matches.value_of(CLIENTS_LEDGER_ARG_NAME) {
config = config.with_custom_clients_ledger(clients_ledger);
}
+26 -71
View File
@@ -1,7 +1,7 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::override_config;
use crate::commands::*;
use crate::config::persistence::pathfinder::GatewayPathfinder;
use crate::config::Config;
use crate::node::Gateway;
@@ -15,88 +15,58 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
App::new("run")
.about("Starts the gateway")
.arg(
Arg::with_name("id")
.long("id")
Arg::with_name(ID_ARG_NAME)
.long(ID_ARG_NAME)
.help("Id of the gateway we want to run")
.takes_value(true)
.required(true),
)
// the rest of arguments are optional, they are used to override settings in config file
.arg(
Arg::with_name("config")
.long("config")
.help("Custom path to the nym gateway configuration file")
.takes_value(true),
)
.arg(
Arg::with_name("mix-host")
.long("mix-host")
Arg::with_name(HOST_ARG_NAME)
.long(HOST_ARG_NAME)
.help("The custom host on which the gateway will be running for receiving sphinx packets")
.takes_value(true)
)
.arg(
Arg::with_name("mix-port")
.long("mix-port")
Arg::with_name(MIX_PORT_ARG_NAME)
.long(MIX_PORT_ARG_NAME)
.help("The port on which the gateway will be listening for sphinx packets")
.takes_value(true)
)
.arg(
Arg::with_name("clients-host")
.long("clients-host")
.help("The custom host on which the gateway will be running for receiving clients gateway-requests")
.takes_value(true)
)
.arg(
Arg::with_name("clients-port")
.long("clients-port")
Arg::with_name(CLIENTS_PORT_ARG_NAME)
.long(CLIENTS_PORT_ARG_NAME)
.help("The port on which the gateway will be listening for clients gateway-requests")
.takes_value(true)
)
.arg(
Arg::with_name("mix-announce-host")
.long("mix-announce-host")
Arg::with_name(ANNOUNCE_HOST_ARG_NAME)
.long(ANNOUNCE_HOST_ARG_NAME)
.help("The host that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("mix-announce-port")
.long("mix-announce-port")
.help("The port that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("clients-announce-host")
.long("clients-announce-host")
.help("The host that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("clients-announce-port")
.long("clients-announce-port")
.help("The port that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("inboxes")
.long("inboxes")
Arg::with_name(INBOXES_ARG_NAME)
.long(INBOXES_ARG_NAME)
.help("Directory with inboxes where all packets for the clients are stored")
.takes_value(true)
)
.arg(
Arg::with_name("clients-ledger")
.long("clients-ledger")
Arg::with_name(CLIENTS_LEDGER_ARG_NAME)
.long(CLIENTS_LEDGER_ARG_NAME)
.help("Ledger file containing registered clients")
.takes_value(true)
)
.arg(
Arg::with_name("validators")
.long("validators")
Arg::with_name(VALIDATORS_ARG_NAME)
.long(VALIDATORS_ARG_NAME)
.help("Comma separated list of rest endpoints of the validators")
.takes_value(true),
)
.arg(
Arg::with_name("mixnet-contract")
.long("mixnet-contract")
Arg::with_name(CONTRACT_ARG_NAME)
.long(CONTRACT_ARG_NAME)
.help("Address of the validator contract managing the network")
.takes_value(true),
)
@@ -163,7 +133,7 @@ fn version_check(cfg: &Config) -> bool {
}
pub fn execute(matches: &ArgMatches) {
let id = matches.value_of("id").unwrap();
let id = matches.value_of(ID_ARG_NAME).unwrap();
println!("Starting gateway {}...", id);
@@ -186,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().to_string()) {
show_binding_warning(config.get_listening_address().to_string());
}
println!(
@@ -202,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!(
+85 -11
View File
@@ -1,14 +1,22 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::DEFAULT_MIXNET_CONTRACT_ADDRESS;
use crate::config::{default_validator_rest_endpoints, Config, MISSING_VALUE};
use clap::{App, Arg, ArgMatches};
use config::NymConfig;
use std::fmt::Display;
use std::process;
use clap::{App, Arg, ArgMatches};
use config::NymConfig;
use version_checker::{parse_version, Version};
use crate::config::DEFAULT_MIXNET_CONTRACT_ADDRESS;
use crate::config::{default_validator_rest_endpoints, Config, MISSING_VALUE};
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
print_failed_upgrade(from_version, to_version);
process::exit(1)
}
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
println!(
"\n==================\nTrying to upgrade gateway from {} to {} ...",
@@ -45,6 +53,16 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> {
.help("REQUIRED FOR PRE-0.9.0 UPGRADES. Specifies current version of the configuration file to help to determine a valid upgrade path. Valid formats include '0.8.1', 'v0.8.1' or 'V0.8.1'")
.takes_value(true)
)
.arg(Arg::with_name("listening-address")
.long("listening-address")
.help("REQUIRED FOR 0.Y.Z UPGRADE. Specifies the listening address of this gateway")
.takes_value(true)
)
.arg(Arg::with_name("announce-address")
.long("announce-address")
.help("OPTIONAL FOR 0.Y.Z UPGRADE. Specifies the announce address of this gateway. If not provided, it will be set to the same value as listening address")
.takes_value(true)
)
}
fn unsupported_upgrade(current_version: Version, config_version: Version) -> ! {
@@ -169,8 +187,7 @@ fn minor_010_upgrade(
if config.get_validator_mixnet_contract_address() != MISSING_VALUE {
eprintln!("existing config seems to have specified mixnet contract address which was only introduced in 0.10.0! Can't perform upgrade.");
print_failed_upgrade(&config_version, &to_version);
process::exit(1);
fail_upgrade(&config_version, &to_version)
}
println!(
@@ -190,8 +207,7 @@ fn minor_010_upgrade(
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
print_failed_upgrade(&config_version, &to_version);
process::exit(1);
fail_upgrade(&config_version, &to_version)
});
print_successful_upgrade(config_version, to_version);
@@ -214,8 +230,58 @@ fn patch_010_upgrade(
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
print_failed_upgrade(&config_version, &to_version);
process::exit(1);
fail_upgrade(&config_version, &to_version)
});
print_successful_upgrade(config_version, to_version);
upgraded_config
}
// TODO: to be renamed once the release version is decided (so presumably either 0.10.2 or 0.11.0)
fn undetermined_version_upgrade(
config: Config,
matches: &ArgMatches,
config_version: &Version,
package_version: &Version,
) -> Config {
// If we decide this version should be tagged with 0.11.0, then the following code will be used instead:
// let to_version = if package_version.major == 0 && package_version.minor == 11 {
// package_version.clone()
// } else {
// Version::new(0, 11, 0)
// };
let to_version = package_version;
print_start_upgrade(&config_version, &to_version);
// normally the proper approach would have been to keep old structs, parse file according to old structs
// and move it to the new ones
// however, considering that at this point of time we don't have many gateways and all
// are run by us, this would be an unnecessary code complication and so just providing
// addresses again during upgrade is I think a better approach.
let listening_address = matches.value_of("listening-address").unwrap_or_else(|| {
eprintln!(
"trying to upgrade to {} without providing proper listening address!",
to_version
);
fail_upgrade(&config_version, &to_version)
});
let announce_address = if let Some(announce_addr) = matches.value_of("announce-address") {
announce_addr.to_string()
} else {
listening_address.to_string()
};
let upgraded_config = config
.with_custom_version(to_version.to_string().as_ref())
.with_listening_address(listening_address)
.with_announce_address(announce_address);
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
fail_upgrade(&config_version, &to_version)
});
print_successful_upgrade(config_version, to_version);
@@ -235,7 +301,15 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version
config = match config_version.major {
0 => match config_version.minor {
9 => minor_010_upgrade(config, matches, &config_version, &package_version),
10 => patch_010_upgrade(config, matches, &config_version, &package_version),
10 => match config_version.patch {
0 => patch_010_upgrade(config, matches, &config_version, &package_version),
_ => undetermined_version_upgrade(
config,
matches,
&config_version,
&package_version,
),
},
_ => unsupported_upgrade(config_version, package_version),
},
_ => unsupported_upgrade(config_version, package_version),
+63 -207
View File
@@ -3,11 +3,10 @@
use crate::config::template::config_template;
use config::{deserialize_duration, deserialize_validators, NymConfig};
use log::*;
use log::error;
use serde::{Deserialize, Serialize};
use std::net::{IpAddr, SocketAddr};
use std::net::IpAddr;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Duration;
pub mod persistence;
@@ -21,7 +20,6 @@ const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000;
pub(crate) const DEFAULT_VALIDATOR_REST_ENDPOINTS: &[&str] = &[
"http://testnet-finney-validator.nymtech.net:1317",
"http://testnet-finney-validator2.nymtech.net:1317",
"http://mixnet.club:1317",
];
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "hal1k0jntykt7e4g3y88ltc60czgjuqdy4c9c6gv94";
@@ -53,13 +51,22 @@ pub fn missing_vec_string_value() -> Vec<String> {
vec![missing_string_value()]
}
fn bind_all_address() -> IpAddr {
"0.0.0.0".parse().unwrap()
}
fn default_mix_port() -> u16 {
DEFAULT_MIX_LISTENING_PORT
}
fn default_clients_port() -> u16 {
DEFAULT_CLIENT_LISTENING_PORT
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
gateway: Gateway,
mixnet_endpoint: MixnetEndpoint,
clients_endpoint: ClientsEndpoint,
#[serde(default)]
@@ -155,167 +162,36 @@ impl Config {
self
}
pub fn with_mix_listening_host<S: Into<String>>(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_listening_address<S: Into<String>>(mut self, listening_address: S) -> Self {
let listening_address_string = listening_address.into();
if let Ok(ip_addr) = listening_address_string.parse() {
self.gateway.listening_address = ip_addr
} else {
error!(
"failed to change listening address. the provided value ({}) was invalid",
listening_address_string
)
}
}
pub fn with_mix_listening_port(mut self, port: u16) -> Self {
self.mixnet_endpoint.listening_address.set_port(port);
self
}
pub fn with_mix_announce_host<S: Into<String>>(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<S: Into<String>>(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<S: Into<String>>(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);
self
}
pub fn with_clients_announce_host<S: Into<String>>(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);
pub fn announce_host_from_listening_host(mut self) -> Self {
self.gateway.announce_address = self.gateway.listening_address.to_string();
self
}
@@ -363,20 +239,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) -> IpAddr {
self.gateway.listening_address
}
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 +305,26 @@ 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.
#[serde(default = "bind_all_address")]
listening_address: IpAddr,
/// 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`.
#[serde(default = "missing_string_value")]
announce_address: String,
/// Port used for listening for all mixnet traffic.
/// (default: 1789)
#[serde(default = "default_mix_port")]
mix_port: u16,
/// Port used for listening for all client-related traffic.
/// (default: 9000)
#[serde(default = "default_clients_port")]
clients_port: u16,
/// Path to file containing private identity key.
private_identity_key_file: PathBuf,
@@ -481,6 +377,10 @@ impl Default for Gateway {
Gateway {
version: env!("CARGO_PKG_VERSION").to_string(),
id: "".to_string(),
listening_address: bind_all_address(),
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(),
@@ -493,47 +393,7 @@ 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 +415,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(),
}
+21 -29
View File
@@ -19,6 +19,9 @@ version = '{{ gateway.version }}'
# Human readable ID of this particular gateway.
id = '{{ gateway.id }}'
# Socket address to which this gateway will bind to and will be listening for packets.
listening_address = '{{ gateway.listening_address }}'
# Path to file containing private identity key.
private_identity_key_file = '{{ gateway.private_identity_key_file }}'
@@ -31,7 +34,22 @@ private_sphinx_key_file = '{{ gateway.private_sphinx_key_file }}'
# Path to file containing public sphinx key.
public_sphinx_key_file = '{{ gateway.public_sphinx_key_file }}'
# Validator server to which the node will be reporting their presence data.
##### additional gateway config options #####
# 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`.
announce_address = '{{ gateway.announce_address }}'
# Port used for listening for all mixnet traffic.
# (default: 1789)
mix_port = {{ gateway.mix_port }}
# Port used for listening for all client websocket traffic.
# (default: 9000)
clients_port = {{ gateway.clients_port }}
# Validator server to which the node will be getting information about the network.
validator_rest_urls = [
{{#each gateway.validator_rest_urls }}
'{{this}}',
@@ -41,41 +59,15 @@ validator_rest_urls = [
# Address of the validator contract managing the network.
mixnet_contract_address = '{{ gateway.mixnet_contract_address }}'
##### advanced configuration options #####
# nym_home_directory specifies absolute path to the home nym gateway directory.
# It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory = '{{ gateway.nym_root_directory }}'
##### Mixnet endpoint config options #####
[mixnet_endpoint]
# Socket address to which this gateway will bind to
# and will be listening for sphinx packets coming from the mixnet.
listening_address = '{{ mixnet_endpoint.listening_address }}'
# 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 = '{{ mixnet_endpoint.announce_address }}'
#### Clients endpoint config options #####
[clients_endpoint]
# Socket address to which this gateway will bind to
# and will be listening for sphinx packets coming from the mixnet.
listening_address = '{{ clients_endpoint.listening_address }}'
# 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 = '{{ clients_endpoint.announce_address }}'
# Path to the directory with clients inboxes containing messages stored for them.
inboxes_directory = '{{ clients_endpoint.inboxes_directory }}'
+16 -14
View File
@@ -9,6 +9,7 @@ use crate::node::storage::{inboxes, ClientLedger};
use crypto::asymmetric::{encryption, identity};
use log::*;
use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
use std::net::SocketAddr;
use std::process;
use std::sync::Arc;
use tokio::runtime::Runtime;
@@ -70,9 +71,12 @@ impl Gateway {
ack_sender,
);
let listener = mixnet_handling::Listener::new(self.config.get_mix_listening_address());
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_mix_port(),
);
listener.start(connection_handler);
mixnet_handling::Listener::new(listening_address).start(connection_handler);
}
fn start_client_websocket_listener(
@@ -82,11 +86,13 @@ impl Gateway {
) {
info!("Starting client [web]socket listener...");
websocket::Listener::new(
self.config.get_clients_listening_address(),
Arc::clone(&self.identity),
)
.start(clients_handler_sender, forwarding_channel);
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_clients_port(),
);
websocket::Listener::new(listening_address, Arc::clone(&self.identity))
.start(clients_handler_sender, forwarding_channel);
}
fn start_packet_forwarder(&self) -> MixForwardingSender {
@@ -127,9 +133,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<String> {
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 +147,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())
}
+4 -3
View File
@@ -1,3 +1,4 @@
use crate::commands::*;
use crate::config::Config;
use crate::node::node_description::NodeDescription;
use clap::{App, Arg, ArgMatches};
@@ -10,8 +11,8 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> {
App::new("describe")
.about("Describe your mixnode and tell people why they should delegate stake to you")
.arg(
Arg::with_name("id")
.long("id")
Arg::with_name(ID_ARG_NAME)
.long(ID_ARG_NAME)
.help("The id of the mixnode you want to describe")
.takes_value(true)
.required(true),
@@ -21,7 +22,7 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> {
pub fn execute(matches: &ArgMatches) {
// figure out which node the user is describing
let id = matches
.value_of("id")
.value_of(ID_ARG_NAME)
.expect("Please provide the id of your mixnode");
// ensure that the mixnode has in fact been initialized
+23 -30
View File
@@ -1,7 +1,7 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::override_config;
use crate::commands::*;
use crate::config::persistence::pathfinder::MixNodePathfinder;
use crate::config::Config;
use clap::{App, Arg, ArgMatches};
@@ -15,61 +15,49 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
App::new("init")
.about("Initialise the mixnode")
.arg(
Arg::with_name("id")
.long("id")
Arg::with_name(ID_ARG_NAME)
.long(ID_ARG_NAME)
.help("Id of the nym-mixnode we want to create config for.")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("layer")
.long("layer")
Arg::with_name(LAYER_ARG_NAME)
.long(LAYER_ARG_NAME)
.help("The mixnet layer of this particular node")
.takes_value(true),
)
.arg(
Arg::with_name("host")
.long("host")
Arg::with_name(HOST_ARG_NAME)
.long(HOST_ARG_NAME)
.help("The host on which the mixnode will be running")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("port")
.long("port")
Arg::with_name(MIX_PORT_ARG_NAME)
.long(MIX_PORT_ARG_NAME)
.help("The port on which the mixnode will be listening")
.takes_value(true),
)
.arg(
Arg::with_name("announce-host")
.long("announce-host")
Arg::with_name(ANNOUNCE_HOST_ARG_NAME)
.long(ANNOUNCE_HOST_ARG_NAME)
.help("The custom host that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("announce-port")
.long("announce-port")
.help("The custom port that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("validators")
.long("validators")
Arg::with_name(VALIDATORS_ARG_NAME)
.long(VALIDATORS_ARG_NAME)
.help("Comma separated list of rest endpoints of the validators")
.takes_value(true),
)
.arg(
Arg::with_name("mixnet-contract")
.long("mixnet-contract")
Arg::with_name(CONTRACT_ARG_NAME)
.long(CONTRACT_ARG_NAME)
.help("Address of the validator contract managing the network")
.takes_value(true),
)
.arg(
Arg::with_name("metrics-server")
.long("metrics-server")
.help("Server to which the node is sending all metrics data")
.takes_value(true),
)
}
async fn choose_layer(
@@ -78,7 +66,10 @@ async fn choose_layer(
mixnet_contract: String,
) -> u64 {
let max_layer = DEFAULT_NUM_MIX_HOPS;
if let Some(layer) = matches.value_of("layer").map(|layer| layer.parse::<u64>()) {
if let Some(layer) = matches
.value_of(LAYER_ARG_NAME)
.map(|layer| layer.parse::<u64>())
{
if let Err(err) = layer {
// if layer was overridden, it must be parsable
panic!("Invalid layer value provided - {:?}", err);
@@ -139,7 +130,8 @@ fn show_bonding_info(config: &Config) {
"\nTo bond your mixnode 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: {}
@@ -147,6 +139,7 @@ fn show_bonding_info(config: &Config) {
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(),
);
@@ -157,7 +150,7 @@ pub fn execute(matches: &ArgMatches) {
// and then removing runtime from mixnode itself in `run`
let rt = Runtime::new().unwrap();
rt.block_on(async {
let id = matches.value_of("id").unwrap();
let id = matches.value_of(ID_ARG_NAME).unwrap();
println!("Initialising mixnode {}...", id);
let already_init = if Config::default_config_file_path(id).exists() {
+25 -22
View File
@@ -11,6 +11,14 @@ pub(crate) mod run;
pub(crate) mod sign;
pub(crate) mod upgrade;
pub(crate) const ID_ARG_NAME: &str = "id";
pub(crate) const HOST_ARG_NAME: &str = "host";
pub(crate) const LAYER_ARG_NAME: &str = "layer";
pub(crate) const MIX_PORT_ARG_NAME: &str = "mix-port";
pub(crate) const VALIDATORS_ARG_NAME: &str = "validators";
pub(crate) const CONTRACT_ARG_NAME: &str = "mixnet-contract";
pub(crate) const ANNOUNCE_HOST_ARG_NAME: &str = "announce-host";
fn parse_validators(raw: &str) -> Vec<String> {
raw.split(',')
.map(|raw_validator| raw_validator.trim().into())
@@ -20,12 +28,15 @@ fn parse_validators(raw: &str) -> Vec<String> {
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
let max_layer = DEFAULT_NUM_MIX_HOPS;
let mut was_host_overridden = false;
if let Some(host) = matches.value_of("host") {
config = config.with_listening_host(host);
if let Some(host) = matches.value_of(HOST_ARG_NAME) {
config = config.with_listening_address(host);
was_host_overridden = true;
}
if let Some(layer) = matches.value_of("layer").map(|layer| layer.parse::<u64>()) {
if let Some(layer) = matches
.value_of(LAYER_ARG_NAME)
.map(|layer| layer.parse::<u64>())
{
if let Err(err) = layer {
// if layer was overridden, it must be parsable
panic!("Invalid layer value provided - {:?}", err);
@@ -36,38 +47,30 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi
}
}
if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
if let Some(port) = matches
.value_of(MIX_PORT_ARG_NAME)
.map(|port| port.parse::<u16>())
{
if let Err(err) = port {
// if port was overridden, it must be parsable
panic!("Invalid port value provided - {:?}", err);
panic!("Invalid mix port value provided - {:?}", err);
}
config = config.with_listening_port(port.unwrap());
config = config.with_mix_port(port.unwrap());
}
if let Some(raw_validators) = matches.value_of("validators") {
if let Some(raw_validators) = matches.value_of(VALIDATORS_ARG_NAME) {
config = config.with_custom_validators(parse_validators(raw_validators));
}
if let Some(contract_address) = matches.value_of("mixnet-contract") {
if let Some(contract_address) = matches.value_of(CONTRACT_ARG_NAME) {
config = config.with_custom_mixnet_contract(contract_address)
}
if let Some(announce_host) = matches.value_of("announce-host") {
config = config.with_announce_host(announce_host);
if let Some(announce_host) = matches.value_of(ANNOUNCE_HOST_ARG_NAME) {
config = config.with_announce_address(announce_host);
} else if was_host_overridden {
// make sure our 'announce-host' always defaults to 'host'
config = config.announce_host_from_listening_host()
}
if let Some(announce_port) = matches
.value_of("announce-port")
.map(|port| port.parse::<u16>())
{
if let Err(err) = announce_port {
// if port was overridden, it must be parsable
panic!("Invalid port value provided - {:?}", err);
}
config = config.with_announce_port(announce_port.unwrap());
config = config.announce_address_from_listening_address()
}
config
+22 -33
View File
@@ -1,7 +1,7 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::override_config;
use crate::commands::*;
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
use crate::node::node_description::NodeDescription;
use crate::node::MixNode;
@@ -15,61 +15,49 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> {
App::new("run")
.about("Starts the mixnode")
.arg(
Arg::with_name("id")
.long("id")
Arg::with_name(ID_ARG_NAME)
.long(ID_ARG_NAME)
.help("Id of the nym-mixnode we want to run")
.takes_value(true)
.required(true),
)
// the rest of arguments are optional, they are used to override settings in config file
.arg(
Arg::with_name("layer")
.long("layer")
Arg::with_name(LAYER_ARG_NAME)
.long(LAYER_ARG_NAME)
.help("The mixnet layer of this particular node")
.takes_value(true),
)
.arg(
Arg::with_name("host")
.long("host")
Arg::with_name(HOST_ARG_NAME)
.long(HOST_ARG_NAME)
.help("The custom host on which the mixnode will be running")
.takes_value(true),
)
.arg(
Arg::with_name("port")
.long("port")
Arg::with_name(MIX_PORT_ARG_NAME)
.long(MIX_PORT_ARG_NAME)
.help("The port on which the mixnode will be listening")
.takes_value(true),
)
.arg(
Arg::with_name("announce-host")
.long("announce-host")
Arg::with_name(ANNOUNCE_HOST_ARG_NAME)
.long(ANNOUNCE_HOST_ARG_NAME)
.help("The host that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("announce-port")
.long("announce-port")
.help("The port that will be reported to the directory server")
.takes_value(true),
)
.arg(
Arg::with_name("validators")
.long("validators")
Arg::with_name(VALIDATORS_ARG_NAME)
.long(VALIDATORS_ARG_NAME)
.help("Comma separated list of rest endpoints of the validators")
.takes_value(true),
)
.arg(
Arg::with_name("mixnet-contract")
.long("mixnet-contract")
Arg::with_name(CONTRACT_ARG_NAME)
.long(CONTRACT_ARG_NAME)
.help("Address of the validator contract managing the network")
.takes_value(true),
)
.arg(
Arg::with_name("metrics-server")
.long("metrics-server")
.help("Server to which the node is sending all metrics data")
.takes_value(true),
)
}
fn show_binding_warning(address: String) {
@@ -125,7 +113,7 @@ fn version_check(cfg: &Config) -> bool {
}
pub fn execute(matches: &ArgMatches) {
let id = matches.value_of("id").unwrap();
let id = matches.value_of(ID_ARG_NAME).unwrap();
println!("Starting mixnode {}...", id);
@@ -148,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().to_string()) {
show_binding_warning(config.get_listening_address().to_string());
}
println!(
@@ -162,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()
);
@@ -170,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: {}
@@ -178,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(),
);
+9 -6
View File
@@ -1,3 +1,4 @@
use crate::commands::*;
use crate::config::{persistence::pathfinder::MixNodePathfinder, Config};
use clap::{App, Arg, ArgMatches};
use colored::*;
@@ -5,19 +6,21 @@ use config::NymConfig;
use crypto::asymmetric::identity;
use log::error;
const SIGN_TEXT_ARG_NAME: &str = "text";
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
App::new("sign")
.about("Sign text to prove ownership of this mixnode")
.arg(
Arg::with_name("id")
.long("id")
Arg::with_name(ID_ARG_NAME)
.long(ID_ARG_NAME)
.help("The id of the mixnode you want to sign with")
.takes_value(true)
.required(true),
)
.arg(
Arg::with_name("text")
.long("text")
Arg::with_name(SIGN_TEXT_ARG_NAME)
.long(SIGN_TEXT_ARG_NAME)
.help("The text to sign")
.takes_value(true)
.required(true),
@@ -34,8 +37,8 @@ fn load_identity_keys(pathfinder: &MixNodePathfinder) -> identity::KeyPair {
}
pub fn execute(matches: &ArgMatches) {
let id = matches.value_of("id").unwrap();
let text = matches.value_of("text").unwrap();
let id = matches.value_of(ID_ARG_NAME).unwrap();
let text = matches.value_of(SIGN_TEXT_ARG_NAME).unwrap();
let config = match Config::load_from_file(id) {
Ok(cfg) => cfg,
+88 -26
View File
@@ -1,6 +1,7 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::commands::*;
use crate::config::{
default_validator_rest_endpoints, missing_string_value, Config,
DEFAULT_MIXNET_CONTRACT_ADDRESS, MISSING_VALUE,
@@ -9,10 +10,19 @@ use clap::{App, Arg, ArgMatches};
use config::NymConfig;
use crypto::asymmetric::identity;
use std::fmt::Display;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::process;
use std::str::FromStr;
use version_checker::{parse_version, Version};
const CURRENT_VERSION_ARG_NAME: &str = "current-version";
fn fail_upgrade<D1: Display, D2: Display>(from_version: D1, to_version: D2) -> ! {
print_failed_upgrade(from_version, to_version);
process::exit(1)
}
fn print_start_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
println!(
"\n==================\nTrying to upgrade mixnode from {} to {} ...",
@@ -37,15 +47,15 @@ fn print_successful_upgrade<D1: Display, D2: Display>(from: D1, to: D2) {
pub fn command_args<'a, 'b>() -> App<'a, 'b> {
App::new("upgrade").about("Try to upgrade the mixnode")
.arg(
Arg::with_name("id")
.long("id")
Arg::with_name(ID_ARG_NAME)
.long(ID_ARG_NAME)
.help("Id of the nym-mixnode we want to upgrade")
.takes_value(true)
.required(true),
)
// the rest of arguments depend on the upgrade path
.arg(Arg::with_name("current version")
.long("current-version")
.arg(Arg::with_name(CURRENT_VERSION_ARG_NAME)
.long(CURRENT_VERSION_ARG_NAME)
.help("REQUIRED FOR PRE-0.9.0 UPGRADES. Specifies current version of the configuration file to help to determine a valid upgrade path. Valid formats include '0.8.1', 'v0.8.1' or 'V0.8.1'")
.takes_value(true)
)
@@ -123,28 +133,24 @@ fn pre_090_upgrade(from: &str, config: Config) -> Config {
if from_version.major == 0 && from_version.minor < 8 {
// technically this could be implemented, but is there any point in that?
eprintln!("upgrading node from before v0.8.0 is not supported. Please run `init` with new binary instead");
print_failed_upgrade(&from_version, &to_version);
process::exit(1)
fail_upgrade(&from_version, &to_version)
}
if (from_version.major == 0 && from_version.minor >= 9) || from_version.major >= 1 {
eprintln!("self reported version is higher than 0.9.0. Those releases should have already contained version numbers in config! Make sure you provided correct version");
print_failed_upgrade(&from_version, &to_version);
process::exit(1)
fail_upgrade(&from_version, &to_version)
}
if config.get_private_identity_key_file() != missing_string_value::<PathBuf>()
|| config.get_public_identity_key_file() != missing_string_value::<PathBuf>()
{
eprintln!("existing config seems to have specified identity keys which were only introduced in 0.9.0! Can't perform upgrade.");
print_failed_upgrade(&from_version, &to_version);
process::exit(1);
fail_upgrade(&from_version, &to_version)
}
if config.get_validator_rest_endpoints()[0] != missing_string_value::<String>() {
eprintln!("existing config seems to have specified new validator rest endpoint which was only introduced in 0.9.0! Can't perform upgrade.");
print_failed_upgrade(&from_version, &to_version);
process::exit(1);
fail_upgrade(&from_version, &to_version)
}
let mut upgraded_config = config
@@ -175,8 +181,7 @@ fn pre_090_upgrade(from: &str, config: Config) -> Config {
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
print_failed_upgrade(&from_version, &to_version);
process::exit(1);
fail_upgrade(&from_version, &to_version)
});
print_successful_upgrade(from_version, to_version);
@@ -200,8 +205,7 @@ fn minor_010_upgrade(
if config.get_validator_mixnet_contract_address() != MISSING_VALUE {
eprintln!("existing config seems to have specified mixnet contract address which was only introduced in 0.10.0! Can't perform upgrade.");
print_failed_upgrade(&config_version, &to_version);
process::exit(1);
fail_upgrade(&config_version, to_version)
}
println!(
@@ -221,8 +225,7 @@ fn minor_010_upgrade(
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
print_failed_upgrade(&config_version, &to_version);
process::exit(1);
fail_upgrade(&config_version, &to_version)
});
print_successful_upgrade(config_version, to_version);
@@ -246,8 +249,56 @@ fn patch_010_upgrade(
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
print_failed_upgrade(&config_version, &to_version);
process::exit(1);
fail_upgrade(&config_version, to_version)
});
print_successful_upgrade(config_version, to_version);
upgraded_config
}
// TODO: to be renamed once the release version is decided (so presumably either 0.10.2 or 0.11.0)
fn undetermined_version_upgrade(
config: Config,
_matches: &ArgMatches,
config_version: &Version,
package_version: &Version,
) -> Config {
// If we decide this version should be tagged with 0.11.0, then the following code will be used instead:
// let to_version = if package_version.major == 0 && package_version.minor == 11 {
// package_version.clone()
// } else {
// Version::new(0, 11, 0)
// };
let to_version = package_version;
print_start_upgrade(&config_version, &to_version);
let current_annnounce_addr = config.get_announce_address();
// try to parse it as socket address directly
let (announce_address, custom_mix_port) = match SocketAddr::from_str(&current_annnounce_addr) {
Ok(addr) => (addr.ip().to_string(), addr.port()),
Err(_) => {
let announce_split = current_annnounce_addr.split(':').collect::<Vec<_>>();
if announce_split.len() != 2 {
eprintln!("failed to correctly parse current announce host");
fail_upgrade(&config_version, &to_version)
}
(
announce_split[0].to_string(),
announce_split[1].parse().unwrap(),
)
}
};
let upgraded_config = config
.with_custom_version(to_version.to_string().as_ref())
.with_announce_address(announce_address)
.with_mix_port(custom_mix_port);
upgraded_config.save_to_file(None).unwrap_or_else(|err| {
eprintln!("failed to overwrite config file! - {:?}", err);
fail_upgrade(&config_version, to_version)
});
print_successful_upgrade(config_version, to_version);
@@ -267,7 +318,15 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version
config = match config_version.major {
0 => match config_version.minor {
9 => minor_010_upgrade(config, matches, &config_version, &package_version),
10 => patch_010_upgrade(config, matches, &config_version, &package_version),
10 => match config_version.patch {
0 => patch_010_upgrade(config, matches, &config_version, &package_version),
_ => undetermined_version_upgrade(
config,
matches,
&config_version,
&package_version,
),
},
_ => unsupported_upgrade(config_version, package_version),
},
_ => unsupported_upgrade(config_version, package_version),
@@ -278,7 +337,7 @@ fn do_upgrade(mut config: Config, matches: &ArgMatches, package_version: Version
pub fn execute(matches: &ArgMatches) {
let package_version = parse_package_version();
let id = matches.value_of("id").unwrap();
let id = matches.value_of(ID_ARG_NAME).unwrap();
let mut existing_config = Config::load_from_file(id).unwrap_or_else(|err| {
eprintln!("failed to load existing config file! - {:?}", err);
@@ -287,12 +346,15 @@ pub fn execute(matches: &ArgMatches) {
// versions fields were added in 0.9.0
if existing_config.get_version() == missing_string_value::<String>() {
let self_reported_version = matches.value_of("current version").unwrap_or_else(|| {
eprintln!(
let self_reported_version =
matches
.value_of(CURRENT_VERSION_ARG_NAME)
.unwrap_or_else(|| {
eprintln!(
"trying to upgrade from pre v0.9.0 without providing current system version!"
);
process::exit(1)
});
process::exit(1)
});
// upgrades up to 0.9.0
existing_config = pre_090_upgrade(self_reported_version, existing_config);
+73 -91
View File
@@ -3,8 +3,7 @@
use crate::config::template::config_template;
use config::{deserialize_duration, deserialize_validators, NymConfig};
use log::error;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::str::FromStr;
@@ -16,11 +15,10 @@ 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",
"http://mixnet.club:1317",
];
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "hal1k0jntykt7e4g3y88ltc60czgjuqdy4c9c6gv94";
@@ -58,13 +56,37 @@ pub fn missing_vec_string_value() -> Vec<String> {
vec![missing_string_value()]
}
fn bind_all_address() -> IpAddr {
"0.0.0.0".parse().unwrap()
}
fn default_mix_port() -> u16 {
DEFAULT_MIX_LISTENING_PORT
}
// basically a migration helper that deserialises string representation of a maybe socket addr (like "1.1.1.1:1234")
// into just the ipaddr (like "1.1.1.1")
pub(super) fn de_ipaddr_from_maybe_str_socks_addr<'de, D>(
deserializer: D,
) -> Result<IpAddr, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if let Ok(socket_addr) = SocketAddr::from_str(&s) {
Ok(socket_addr.ip())
} else {
IpAddr::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
mixnode: MixNode,
#[serde(default)]
rtt_measurement: RttMeasurement,
verloc: Verloc,
#[serde(default)]
logging: Logging,
#[serde(default)]
@@ -152,80 +174,34 @@ impl Config {
self
}
pub fn with_listening_host<S: Into<String>>(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_address<S: Into<String>>(mut self, listening_address: S) -> Self {
let listening_address_string = listening_address.into();
if let Ok(ip_addr) = listening_address_string.parse() {
self.mixnode.listening_address = ip_addr
} else {
error!(
"failed to change listening address. the provided value ({}) was invalid",
listening_address_string
)
}
}
pub fn with_listening_port(mut self, port: u16) -> Self {
self.mixnode.listening_address.set_port(port);
self
}
pub fn with_announce_host<S: Into<String>>(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 with_announce_address<S: Into<String>>(mut self, announce_address: S) -> Self {
self.mixnode.announce_address = announce_address.into();
self
}
pub fn announce_host_from_listening_host(mut self) -> Self {
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.to_string();
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);
self
}
pub fn with_custom_version(mut self, version: &str) -> Self {
self.mixnode.version = version.to_string();
self
@@ -272,7 +248,7 @@ impl Config {
self.mixnode.layer
}
pub fn get_listening_address(&self) -> SocketAddr {
pub fn get_listening_address(&self) -> IpAddr {
self.mixnode.listening_address
}
@@ -280,6 +256,10 @@ impl Config {
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
}
@@ -305,27 +285,27 @@ impl Config {
}
pub fn get_measurement_packets_per_node(&self) -> usize {
self.rtt_measurement.packets_per_node
self.verloc.packets_per_node
}
pub fn get_measurement_packet_timeout(&self) -> Duration {
self.rtt_measurement.packet_timeout
self.verloc.packet_timeout
}
pub fn get_measurement_connection_timeout(&self) -> Duration {
self.rtt_measurement.connection_timeout
self.verloc.connection_timeout
}
pub fn get_measurement_delay_between_packets(&self) -> Duration {
self.rtt_measurement.delay_between_packets
self.verloc.delay_between_packets
}
pub fn get_measurement_tested_nodes_batch_size(&self) -> usize {
self.rtt_measurement.tested_nodes_batch_size
self.verloc.tested_nodes_batch_size
}
pub fn get_measurement_testing_interval(&self) -> Duration {
self.rtt_measurement.testing_interval
self.verloc.testing_interval
}
pub fn get_measurement_retry_timeout(&self) -> Duration {
self.rtt_measurement.retry_timeout
self.verloc.retry_timeout
}
// upgrade-specific
@@ -349,17 +329,20 @@ 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.
#[serde(deserialize_with = "de_ipaddr_from_maybe_str_socks_addr")]
listening_address: IpAddr,
/// 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)
#[serde(default = "default_mix_port")]
mix_port: u16,
/// Path to file containing private identity key.
#[serde(default = "missing_string_value")]
private_identity_key_file: PathBuf,
@@ -374,7 +357,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 +398,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: bind_all_address(),
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(),
@@ -442,7 +424,7 @@ impl Default for Logging {
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct RttMeasurement {
pub struct Verloc {
/// Specifies number of echo packets sent to each node during a measurement run.
packets_per_node: usize,
@@ -466,9 +448,9 @@ pub struct RttMeasurement {
retry_timeout: Duration,
}
impl Default for RttMeasurement {
impl Default for Verloc {
fn default() -> Self {
RttMeasurement {
Verloc {
packets_per_node: DEFAULT_PACKETS_PER_NODE,
connection_timeout: DEFAULT_CONNECTION_TIMEOUT,
packet_timeout: DEFAULT_PACKET_TIMEOUT,
+6 -5
View File
@@ -41,13 +41,14 @@ public_sphinx_key_file = '{{ mixnode.public_sphinx_key_file }}'
# 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`.
# later on by using name resolvable with a DNS query, such as `nymtech.net`.
announce_address = '{{ mixnode.announce_address }}'
# Validator server to which the node will be reporting their presence data.
# Port used for listening for all mixnet traffic.
# (default: 1789)
mix_port = {{ mixnode.mix_port }}
# Validator server to which the node will be getting information about the network.
validator_rest_urls = [
{{#each mixnode.validator_rest_urls }}
'{{this}}',
+12 -5
View File
@@ -17,6 +17,7 @@ use crate::node::packet_delayforwarder::{DelayForwarder, PacketDelayForwardSende
use crypto::asymmetric::{encryption, identity};
use log::{error, info, warn};
use mixnode_common::rtt_measurement::{self, AtomicVerlocResult, RttMeasurer};
use std::net::SocketAddr;
use std::process;
use std::sync::Arc;
use tokio::runtime::Runtime;
@@ -61,7 +62,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();
let verloc_state = VerlocState::new(atomic_verloc_result);
let descriptor = self.descriptor.clone();
@@ -106,9 +107,12 @@ impl MixNode {
let connection_handler = ConnectionHandler::new(packet_processor, delay_forwarding_channel);
let listener = Listener::new(self.config.get_listening_address());
let listening_address = SocketAddr::new(
self.config.get_listening_address(),
self.config.get_mix_port(),
);
listener.start(connection_handler);
Listener::new(listening_address).start(connection_handler);
}
fn start_packet_delay_forwarder(
@@ -147,10 +151,11 @@ 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 = SocketAddr::new(
self.config.get_listening_address(),
rtt_measurement::DEFAULT_MEASUREMENT_PORT,
);
let config = rtt_measurement::ConfigBuilder::new()
.listening_address(listening_address)
.packets_per_node(self.config.get_measurement_packets_per_node())
@@ -186,9 +191,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())
}
+2 -5
View File
@@ -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,
);