From 9e0bb80163c62abca86e97bc1aa06a8213d380b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Wed, 24 Mar 2021 10:52:27 +0000 Subject: [PATCH] Feature/topology conversion (#536) * Removed reputation field from existing topology * ibid for registration time * Basic bond to topology conversion * Made existing tests compilable * Added owner and stake fields to mix and gateway topology entries * Moved node conversion to topology crate * Added mixnet contract field to clients configs * topology refresher trying to use new validator * Removed clients depepdency on the old validator client * Removed mixnode dependency on the old validator client * Removed gateway dependency on the old validator client * Removed location field fron mixnode and gateway configs * Removed incentives address from mixnodes and gateways * Cargo.lock changes * Ignoring clippy warnings originating from codegen from JsonSchema * no longer formating string with a literal --- Cargo.lock | 11 +- clients/client-core/Cargo.toml | 2 +- .../src/client/topology_control.rs | 58 ++++-- clients/client-core/src/config/mod.rs | 15 ++ clients/native/Cargo.toml | 2 +- clients/native/src/client/config/template.rs | 3 + clients/native/src/client/mod.rs | 5 +- clients/native/src/commands/init.rs | 38 ++-- clients/native/src/commands/mod.rs | 4 + clients/native/src/commands/run.rs | 5 + clients/socks5/Cargo.toml | 2 +- clients/socks5/src/client/config/template.rs | 3 + clients/socks5/src/client/mod.rs | 5 +- clients/socks5/src/commands/init.rs | 38 ++-- clients/socks5/src/commands/mod.rs | 4 + clients/socks5/src/commands/run.rs | 5 + .../validator-client-rest/Cargo.toml | 1 - .../validator-client-rest/src/lib.rs | 4 +- .../validator-client/src/models/gateway.rs | 38 +--- .../validator-client/src/models/mixnode.rs | 39 +--- .../validator-client/src/models/topology.rs | 2 - common/mixnet-contract/src/gateway.rs | 111 ++++++++++ common/mixnet-contract/src/lib.rs | 194 +----------------- common/mixnet-contract/src/mixnode.rs | 107 ++++++++++ common/nymsphinx/src/receiver.rs | 16 +- common/topology/Cargo.toml | 1 + common/topology/src/gateway.rs | 101 ++++++++- common/topology/src/lib.rs | 60 +++++- common/topology/src/mix.rs | 101 ++++++++- gateway/Cargo.toml | 2 +- gateway/src/commands/init.rs | 12 +- gateway/src/commands/mod.rs | 13 +- gateway/src/commands/run.rs | 12 +- gateway/src/commands/unregister.rs | 59 ------ gateway/src/commands/upgrade.rs | 28 +-- gateway/src/config/mod.rs | 56 ++--- gateway/src/config/template.rs | 12 +- gateway/src/main.rs | 2 - gateway/src/node/mod.rs | 44 ++-- gateway/src/node/presence.rs | 39 ---- mixnode/Cargo.toml | 2 +- mixnode/src/commands/init.rs | 78 ++++--- mixnode/src/commands/mod.rs | 13 +- mixnode/src/commands/run.rs | 15 +- mixnode/src/commands/unregister.rs | 59 ------ mixnode/src/commands/upgrade.rs | 26 +-- mixnode/src/config/mod.rs | 60 ++---- mixnode/src/config/template.rs | 14 +- mixnode/src/main.rs | 2 - mixnode/src/node/mod.rs | 44 ++-- mixnode/src/node/presence.rs | 39 ---- 51 files changed, 792 insertions(+), 814 deletions(-) create mode 100644 common/mixnet-contract/src/gateway.rs create mode 100644 common/mixnet-contract/src/mixnode.rs delete mode 100644 gateway/src/commands/unregister.rs delete mode 100644 gateway/src/node/presence.rs delete mode 100644 mixnode/src/commands/unregister.rs delete mode 100644 mixnode/src/node/presence.rs diff --git a/Cargo.lock b/Cargo.lock index fa998093cc..bf14b97c2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -390,7 +390,7 @@ dependencies = [ "tempfile", "tokio 0.2.22", "topology", - "validator-client", + "validator-client-rest", ] [[package]] @@ -1702,7 +1702,7 @@ dependencies = [ "tokio-tungstenite", "topology", "url", - "validator-client", + "validator-client-rest", "version-checker", "websocket-requests", ] @@ -1754,7 +1754,7 @@ dependencies = [ "tokio-tungstenite", "tokio-util 0.3.1", "tungstenite", - "validator-client", + "validator-client-rest", "version-checker", ] @@ -1784,7 +1784,7 @@ dependencies = [ "tokio 0.2.22", "tokio-util 0.3.1", "topology", - "validator-client", + "validator-client-rest", "version-checker", ] @@ -1857,7 +1857,7 @@ dependencies = [ "socks5-requests", "tokio 0.2.22", "topology", - "validator-client", + "validator-client-rest", "version-checker", ] @@ -3157,6 +3157,7 @@ dependencies = [ "bs58", "crypto", "log", + "mixnet-contract", "nymsphinx-addressing", "nymsphinx-types", "pretty_env_logger", diff --git a/clients/client-core/Cargo.toml b/clients/client-core/Cargo.toml index 0895394e04..4b4cc058cd 100644 --- a/clients/client-core/Cargo.toml +++ b/clients/client-core/Cargo.toml @@ -25,7 +25,7 @@ nonexhaustive-delayqueue = { path = "../../common/nonexhaustive-delayqueue" } nymsphinx = { path = "../../common/nymsphinx" } pemstore = { path = "../../common/pemstore" } topology = { path = "../../common/topology" } -validator-client = { path = "../../common/client-libs/validator-client" } +validator-client-rest = { path = "../../common/client-libs/validator-client-rest" } [dev-dependencies] tempfile = "3.1.0" \ No newline at end of file diff --git a/clients/client-core/src/client/topology_control.rs b/clients/client-core/src/client/topology_control.rs index 7e70636d21..27465f413e 100644 --- a/clients/client-core/src/client/topology_control.rs +++ b/clients/client-core/src/client/topology_control.rs @@ -22,7 +22,7 @@ use std::time::Duration; use tokio::runtime::Handle; use tokio::sync::{RwLock, RwLockReadGuard}; use tokio::task::JoinHandle; -use topology::NymTopology; +use topology::{nym_topology_from_bonds, NymTopology}; // I'm extremely curious why compiler NEVER complained about lack of Debug here before #[derive(Debug)] @@ -136,21 +136,28 @@ impl Default for TopologyAccessor { } pub struct TopologyRefresherConfig { - directory_server: String, + validator_rcp_base_url: String, + mixnet_contract_address: String, refresh_rate: time::Duration, } impl TopologyRefresherConfig { - pub fn new(directory_server: String, refresh_rate: time::Duration) -> Self { + pub fn new( + validator_rcp_base_url: String, + mixnet_contract_address: String, + refresh_rate: time::Duration, + ) -> Self { TopologyRefresherConfig { - directory_server, + validator_rcp_base_url, + mixnet_contract_address, refresh_rate, } } } pub struct TopologyRefresher { - validator_client: validator_client::Client, + validator_client: validator_client_rest::Client, + topology_accessor: TopologyAccessor, refresh_rate: Duration, @@ -158,12 +165,12 @@ pub struct TopologyRefresher { } impl TopologyRefresher { - pub fn new_directory_client( - cfg: TopologyRefresherConfig, - topology_accessor: TopologyAccessor, - ) -> Self { - let validator_client_config = validator_client::Config::new(cfg.directory_server); - let validator_client = validator_client::Client::new(validator_client_config); + pub fn new(cfg: TopologyRefresherConfig, topology_accessor: TopologyAccessor) -> Self { + let validator_client_config = validator_client_rest::Config::new( + cfg.validator_rcp_base_url, + cfg.mixnet_contract_address, + ); + let validator_client = validator_client_rest::Client::new(validator_client_config); TopologyRefresher { validator_client, @@ -174,16 +181,31 @@ impl TopologyRefresher { } async fn get_current_compatible_topology(&self) -> Option { - match self.validator_client.get_active_topology().await { + // TODO: optimization for the future: + // only refresh mixnodes on timer and refresh gateways only when + // we have to send to a new, unknown, gateway + + let mixnodes = match self.validator_client.get_mix_nodes().await { Err(err) => { - error!("failed to get active network topology! - {:?}", err); - None + error!("failed to get network mixnodes - {}", err); + return None; } - Ok(topology) => { - let nym_topology: NymTopology = topology.into(); - Some(nym_topology.filter_system_version(env!("CARGO_PKG_VERSION"))) + Ok(mixes) => mixes, + }; + + let gateways = match self.validator_client.get_gateways().await { + Err(err) => { + error!("failed to get network gateways - {}", err); + return None; } - } + Ok(gateways) => gateways, + }; + + let topology = nym_topology_from_bonds(mixnodes, gateways); + + // TODO: I didn't want to change it now, but the expected system version should rather be put in config + // rather than pulled from package version of `client_core` + Some(topology.filter_system_version(env!("CARGO_PKG_VERSION"))) } pub async fn refresh(&mut self) { diff --git a/clients/client-core/src/config/mod.rs b/clients/client-core/src/config/mod.rs index d15d5b91ff..968f093464 100644 --- a/clients/client-core/src/config/mod.rs +++ b/clients/client-core/src/config/mod.rs @@ -27,6 +27,8 @@ pub const MISSING_VALUE: &str = "MISSING VALUE"; // 'CLIENT' pub const DEFAULT_VALIDATOR_REST_ENDPOINT: &str = "http://testnet-validator1.nymtech.net:8081"; +pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = + "TODO: THIS NEEDS TO BE FILLED WITH SOME REASONABLE VALUE!"; // 'DEBUG' const DEFAULT_ACK_WAIT_MULTIPLIER: f64 = 1.5; @@ -180,6 +182,10 @@ impl Config { self.client.validator_rest_url = validator.into(); } + pub fn set_mixnet_contract>(&mut self, contract_address: S) { + self.client.mixnet_contract_address = contract_address.into(); + } + pub fn set_high_default_traffic_volume(&mut self) { self.debug.average_packet_delay = Duration::from_millis(10); self.debug.loop_cover_traffic_average_delay = Duration::from_millis(2000000); // basically don't really send cover messages @@ -238,6 +244,10 @@ impl Config { self.client.validator_rest_url.clone() } + pub fn get_validator_mixnet_contract_address(&self) -> String { + self.client.mixnet_contract_address.clone() + } + pub fn get_gateway_id(&self) -> String { self.client.gateway_id.clone() } @@ -338,6 +348,10 @@ pub struct Client { #[serde(default = "missing_string_value")] validator_rest_url: String, + /// Address of the validator contract managing the network. + #[serde(default = "missing_string_value")] + mixnet_contract_address: String, + /// Special mode of the system such that all messages are sent as soon as they are received /// and no cover traffic is generated. If set all message delays are set to 0 and overwriting /// 'Debug' values will have no effect. @@ -390,6 +404,7 @@ impl Default for Client { version: env!("CARGO_PKG_VERSION").to_string(), id: "".to_string(), validator_rest_url: DEFAULT_VALIDATOR_REST_ENDPOINT.to_string(), + mixnet_contract_address: DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(), vpn_mode: false, private_identity_key_file: Default::default(), public_identity_key_file: Default::default(), diff --git a/clients/native/Cargo.toml b/clients/native/Cargo.toml index 96c1320d11..fa0949cb60 100644 --- a/clients/native/Cargo.toml +++ b/clients/native/Cargo.toml @@ -39,7 +39,7 @@ nymsphinx = { path = "../../common/nymsphinx" } pemstore = { path = "../../common/pemstore" } topology = { path = "../../common/topology" } websocket-requests = { path = "websocket-requests" } -validator-client = { path = "../../common/client-libs/validator-client" } +validator-client-rest = { path = "../../common/client-libs/validator-client-rest" } version-checker = { path = "../../common/version-checker" } [dev-dependencies] diff --git a/clients/native/src/client/config/template.rs b/clients/native/src/client/config/template.rs index 792c2ac30b..b2d8cd1d0c 100644 --- a/clients/native/src/client/config/template.rs +++ b/clients/native/src/client/config/template.rs @@ -33,6 +33,9 @@ id = '{{ client.id }}' # URL to the validator server for obtaining network topology. validator_rest_url = '{{ client.validator_rest_url }}' +# Address of the validator contract managing the network. +mixnet_contract_address = '{{ client.mixnet_contract_address }} + # Special mode of the system such that all messages are sent as soon as they are received # and no cover traffic is generated. If set all message delays are set to 0 and overwriting # 'Debug' values will have no effect. diff --git a/clients/native/src/client/mod.rs b/clients/native/src/client/mod.rs index a6aabcd92f..8dc7004a4d 100644 --- a/clients/native/src/client/mod.rs +++ b/clients/native/src/client/mod.rs @@ -227,10 +227,13 @@ impl NymClient { fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) { let topology_refresher_config = TopologyRefresherConfig::new( self.config.get_base().get_validator_rest_endpoint(), + self.config + .get_base() + .get_validator_mixnet_contract_address(), self.config.get_base().get_topology_refresh_rate(), ); let mut topology_refresher = - TopologyRefresher::new_directory_client(topology_refresher_config, topology_accessor); + TopologyRefresher::new(topology_refresher_config, topology_accessor); // before returning, block entire runtime to refresh the current network view so that any // components depending on topology would see a non-empty view info!( diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index 2a17b38c76..6a380f2e46 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -23,9 +23,10 @@ use gateway_client::GatewayClient; use gateway_requests::registration::handshake::SharedKeys; use rand::rngs::OsRng; use rand::seq::SliceRandom; +use std::convert::TryInto; use std::sync::Arc; use std::time::Duration; -use topology::{gateway, NymTopology}; +use topology::{filter::VersionFilterable, gateway}; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { App::new("init") @@ -46,6 +47,11 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .help("Address of the validator server the client is getting topology from") .takes_value(true), ) + .arg(Arg::with_name("mixnet-contract") + .long("mixnet-contract") + .help("Address of the validator contract managing the network") + .takes_value(true), + ) .arg(Arg::with_name("disable-socket") .long("disable-socket") .help("Whether to not start the websocket") @@ -95,27 +101,34 @@ async fn register_with_gateway( .expect("failed to register with the gateway!") } -async fn gateway_details(validator_server: &str, chosen_gateway_id: Option<&str>) -> gateway::Node { - let validator_client_config = validator_client::Config::new(validator_server.to_string()); - let validator_client = validator_client::Client::new(validator_client_config); - let topology = validator_client.get_active_topology().await.unwrap(); - let nym_topology: NymTopology = topology.into(); - let version_filtered_topology = nym_topology.filter_system_version(env!("CARGO_PKG_VERSION")); +async fn gateway_details( + validator_server: &str, + mixnet_contract: &str, + chosen_gateway_id: Option<&str>, +) -> gateway::Node { + let validator_client_config = + validator_client_rest::Config::new(validator_server, mixnet_contract); + let validator_client = validator_client_rest::Client::new(validator_client_config); + + let gateways = validator_client.get_gateways().await.unwrap(); + let valid_gateways = gateways + .into_iter() + .filter_map(|gateway| gateway.try_into().ok()) + .collect::>(); + + let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION")); // if we have chosen particular gateway - use it, otherwise choose a random one. // (remember that in active topology all gateways have at least 100 reputation so should // be working correctly) - if let Some(gateway_id) = chosen_gateway_id { - version_filtered_topology - .gateways() + filtered_gateways .iter() .find(|gateway| gateway.identity_key.to_base58_string() == gateway_id) .expect(&*format!("no gateway with id {} exists!", gateway_id)) .clone() } else { - version_filtered_topology - .gateways() + filtered_gateways .choose(&mut rand::thread_rng()) .expect("there are no gateways on the network!") .clone() @@ -156,6 +169,7 @@ pub fn execute(matches: &ArgMatches) { let registration_fut = async { let gate_details = gateway_details( &config.get_base().get_validator_rest_endpoint(), + &config.get_base().get_validator_mixnet_contract_address(), chosen_gateway_id, ) .await; diff --git a/clients/native/src/commands/mod.rs b/clients/native/src/commands/mod.rs index 6523653c36..fd0b17d93c 100644 --- a/clients/native/src/commands/mod.rs +++ b/clients/native/src/commands/mod.rs @@ -24,6 +24,10 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi config.get_base_mut().set_custom_validator(validator); } + if let Some(contract_address) = matches.value_of("mixnet-contract") { + config.get_base_mut().set_mixnet_contract(contract_address) + } + if let Some(gateway_id) = matches.value_of("gateway") { config.get_base_mut().with_gateway_id(gateway_id); } diff --git a/clients/native/src/commands/run.rs b/clients/native/src/commands/run.rs index 0d08129e8c..a40d886ca3 100644 --- a/clients/native/src/commands/run.rs +++ b/clients/native/src/commands/run.rs @@ -35,6 +35,11 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .help("Address of the validator server the client is getting topology from") .takes_value(true), ) + .arg(Arg::with_name("mixnet-contract") + .long("mixnet-contract") + .help("Address of the validator contract managing the network") + .takes_value(true), + ) .arg(Arg::with_name("gateway") .long("gateway") .help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened") diff --git a/clients/socks5/Cargo.toml b/clients/socks5/Cargo.toml index c4ce7ddc41..2dd5d570e0 100644 --- a/clients/socks5/Cargo.toml +++ b/clients/socks5/Cargo.toml @@ -32,5 +32,5 @@ ordered-buffer = {path = "../../common/socks5/ordered-buffer"} socks5-requests = { path = "../../common/socks5/requests" } topology = { path = "../../common/topology" } proxy-helpers = { path = "../../common/socks5/proxy-helpers" } -validator-client = { path = "../../common/client-libs/validator-client" } +validator-client-rest = { path = "../../common/client-libs/validator-client-rest" } version-checker = { path = "../../common/version-checker" } diff --git a/clients/socks5/src/client/config/template.rs b/clients/socks5/src/client/config/template.rs index 939aa5da4e..ca55d1c9dc 100644 --- a/clients/socks5/src/client/config/template.rs +++ b/clients/socks5/src/client/config/template.rs @@ -33,6 +33,9 @@ id = '{{ client.id }}' # URL to the validator server for obtaining network topology. validator_rest_url = '{{ client.validator_rest_url }}' +# Address of the validator contract managing the network. +mixnet_contract_address = '{{ client.mixnet_contract_address }} + # Special mode of the system such that all messages are sent as soon as they are received # and no cover traffic is generated. If set all message delays are set to 0 and overwriting # 'Debug' values will have no effect. diff --git a/clients/socks5/src/client/mod.rs b/clients/socks5/src/client/mod.rs index d21dc5a493..165b42e192 100644 --- a/clients/socks5/src/client/mod.rs +++ b/clients/socks5/src/client/mod.rs @@ -215,10 +215,13 @@ impl NymClient { fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) { let topology_refresher_config = TopologyRefresherConfig::new( self.config.get_base().get_validator_rest_endpoint(), + self.config + .get_base() + .get_validator_mixnet_contract_address(), self.config.get_base().get_topology_refresh_rate(), ); let mut topology_refresher = - TopologyRefresher::new_directory_client(topology_refresher_config, topology_accessor); + TopologyRefresher::new(topology_refresher_config, topology_accessor); // before returning, block entire runtime to refresh the current network view so that any // components depending on topology would see a non-empty view info!( diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 54436ec8f5..ddb75623d8 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -22,9 +22,10 @@ use crypto::asymmetric::identity; use gateway_client::GatewayClient; use gateway_requests::registration::handshake::SharedKeys; use rand::{prelude::SliceRandom, rngs::OsRng}; +use std::convert::TryInto; use std::sync::Arc; use std::time::Duration; -use topology::{gateway, NymTopology}; +use topology::{filter::VersionFilterable, gateway}; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { App::new("init") @@ -51,6 +52,11 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .help("Address of the validator server the client is getting topology from") .takes_value(true), ) + .arg(Arg::with_name("mixnet-contract") + .long("mixnet-contract") + .help("Address of the validator contract managing the network") + .takes_value(true), + ) .arg(Arg::with_name("port") .short("p") .long("port") @@ -96,27 +102,34 @@ async fn register_with_gateway( .expect("failed to register with the gateway!") } -async fn gateway_details(validator_server: &str, chosen_gateway_id: Option<&str>) -> gateway::Node { - let validator_client_config = validator_client::Config::new(validator_server.to_string()); - let validator_client = validator_client::Client::new(validator_client_config); - let topology = validator_client.get_active_topology().await.unwrap(); - let nym_topology: NymTopology = topology.into(); - let version_filtered_topology = nym_topology.filter_system_version(env!("CARGO_PKG_VERSION")); +async fn gateway_details( + validator_server: &str, + mixnet_contract: &str, + chosen_gateway_id: Option<&str>, +) -> gateway::Node { + let validator_client_config = + validator_client_rest::Config::new(validator_server, mixnet_contract); + let validator_client = validator_client_rest::Client::new(validator_client_config); + + let gateways = validator_client.get_gateways().await.unwrap(); + let valid_gateways = gateways + .into_iter() + .filter_map(|gateway| gateway.try_into().ok()) + .collect::>(); + + let filtered_gateways = valid_gateways.filter_by_version(env!("CARGO_PKG_VERSION")); // if we have chosen particular gateway - use it, otherwise choose a random one. // (remember that in active topology all gateways have at least 100 reputation so should // be working correctly) - if let Some(gateway_id) = chosen_gateway_id { - version_filtered_topology - .gateways() + filtered_gateways .iter() .find(|gateway| gateway.identity_key.to_base58_string() == gateway_id) .expect(&*format!("no gateway with id {} exists!", gateway_id)) .clone() } else { - version_filtered_topology - .gateways() + filtered_gateways .choose(&mut rand::thread_rng()) .expect("there are no gateways on the network!") .clone() @@ -158,6 +171,7 @@ pub fn execute(matches: &ArgMatches) { let registration_fut = async { let gate_details = gateway_details( &config.get_base().get_validator_rest_endpoint(), + &config.get_base().get_validator_mixnet_contract_address(), chosen_gateway_id, ) .await; diff --git a/clients/socks5/src/commands/mod.rs b/clients/socks5/src/commands/mod.rs index ef75167f5b..6880939b9d 100644 --- a/clients/socks5/src/commands/mod.rs +++ b/clients/socks5/src/commands/mod.rs @@ -24,6 +24,10 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi config.get_base_mut().set_custom_validator(validator); } + if let Some(contract_address) = matches.value_of("mixnet-contract") { + config.get_base_mut().set_mixnet_contract(contract_address) + } + if let Some(gateway_id) = matches.value_of("gateway") { config.get_base_mut().with_gateway_id(gateway_id); } diff --git a/clients/socks5/src/commands/run.rs b/clients/socks5/src/commands/run.rs index e1f8e840c9..170f445d78 100644 --- a/clients/socks5/src/commands/run.rs +++ b/clients/socks5/src/commands/run.rs @@ -45,6 +45,11 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .help("Address of the validator server the client is getting topology from") .takes_value(true), ) + .arg(Arg::with_name("mixnet-contract") + .long("mixnet-contract") + .help("Address of the validator contract managing the network") + .takes_value(true), + ) .arg(Arg::with_name("gateway") .long("gateway") .help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened") diff --git a/common/client-libs/validator-client-rest/Cargo.toml b/common/client-libs/validator-client-rest/Cargo.toml index 7ce9cde064..d17a4e5365 100644 --- a/common/client-libs/validator-client-rest/Cargo.toml +++ b/common/client-libs/validator-client-rest/Cargo.toml @@ -12,4 +12,3 @@ mixnet-contract = { path = "../../../common/mixnet-contract" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" reqwest = { version = "0.11", features = ["json"] } - diff --git a/common/client-libs/validator-client-rest/src/lib.rs b/common/client-libs/validator-client-rest/src/lib.rs index 8b5f9ba7df..c4a586c8b5 100644 --- a/common/client-libs/validator-client-rest/src/lib.rs +++ b/common/client-libs/validator-client-rest/src/lib.rs @@ -43,9 +43,9 @@ pub struct Config { } impl Config { - pub fn new>(base_url: S, mixnet_contract_address: S) -> Self { + pub fn new>(rpc_server_base_url: S, mixnet_contract_address: S) -> Self { Config { - rpc_server_base_url: base_url.into(), + rpc_server_base_url: rpc_server_base_url.into(), mixnet_contract_address: mixnet_contract_address.into(), mixnode_page_limit: None, gateway_page_limit: None, diff --git a/common/client-libs/validator-client/src/models/gateway.rs b/common/client-libs/validator-client/src/models/gateway.rs index fe65a8dbe6..43d97d42c4 100644 --- a/common/client-libs/validator-client/src/models/gateway.rs +++ b/common/client-libs/validator-client/src/models/gateway.rs @@ -18,25 +18,7 @@ use serde::{Deserialize, Serialize}; use std::convert::TryInto; use std::io; use std::net::{SocketAddr, ToSocketAddrs}; - -#[derive(Debug)] -pub enum ConversionError { - InvalidIdentityKeyError(identity::KeyRecoveryError), - InvalidSphinxKeyError(encryption::KeyRecoveryError), - InvalidAddress(io::Error), -} - -impl From for ConversionError { - fn from(err: encryption::KeyRecoveryError) -> Self { - ConversionError::InvalidSphinxKeyError(err) - } -} - -impl From for ConversionError { - fn from(err: identity::KeyRecoveryError) -> Self { - ConversionError::InvalidIdentityKeyError(err) - } -} +use topology::gateway::GatewayConversionError; // used for gateways to register themselves #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] @@ -102,15 +84,15 @@ impl RegisteredGateway { &self.gateway_info.node_info.version } - fn resolve_hostname(&self) -> Result { + fn resolve_hostname(&self) -> Result { self.gateway_info .node_info .mix_host .to_socket_addrs() - .map_err(ConversionError::InvalidAddress)? + .map_err(GatewayConversionError::InvalidAddress)? .next() .ok_or_else(|| { - ConversionError::InvalidAddress(io::Error::new( + GatewayConversionError::InvalidAddress(io::Error::new( io::ErrorKind::Other, "no valid socket address", )) @@ -119,10 +101,12 @@ impl RegisteredGateway { } impl TryInto for RegisteredGateway { - type Error = ConversionError; + type Error = GatewayConversionError; fn try_into(self) -> Result { Ok(topology::gateway::Node { + owner: "N/A".to_string(), + stake: 0, mixnet_listener: self.resolve_hostname()?, location: self.gateway_info.node_info.location, client_listener: self.gateway_info.clients_host, @@ -132,18 +116,18 @@ impl TryInto for RegisteredGateway { sphinx_key: encryption::PublicKey::from_base58_string( self.gateway_info.node_info.sphinx_key, )?, - registration_time: self.registration_time, - reputation: self.reputation, version: self.gateway_info.node_info.version, }) } } impl<'a> TryInto for &'a RegisteredGateway { - type Error = ConversionError; + type Error = GatewayConversionError; fn try_into(self) -> Result { Ok(topology::gateway::Node { + owner: "N/A".to_string(), + stake: 0, mixnet_listener: self.resolve_hostname()?, location: self.gateway_info.node_info.location.clone(), client_listener: self.gateway_info.clients_host.clone(), @@ -153,8 +137,6 @@ impl<'a> TryInto for &'a RegisteredGateway { sphinx_key: encryption::PublicKey::from_base58_string( &self.gateway_info.node_info.sphinx_key, )?, - registration_time: self.registration_time, - reputation: self.reputation, version: self.gateway_info.node_info.version.clone(), }) } diff --git a/common/client-libs/validator-client/src/models/mixnode.rs b/common/client-libs/validator-client/src/models/mixnode.rs index e45edea01d..68640d03c6 100644 --- a/common/client-libs/validator-client/src/models/mixnode.rs +++ b/common/client-libs/validator-client/src/models/mixnode.rs @@ -19,26 +19,7 @@ use serde::{Deserialize, Serialize}; use std::convert::TryInto; use std::io; use std::net::{SocketAddr, ToSocketAddrs}; -use topology::mix::Node; - -#[derive(Debug)] -pub enum ConversionError { - InvalidIdentityKeyError(identity::KeyRecoveryError), - InvalidSphinxKeyError(encryption::KeyRecoveryError), - InvalidAddress(io::Error), -} - -impl From for ConversionError { - fn from(err: encryption::KeyRecoveryError) -> Self { - ConversionError::InvalidSphinxKeyError(err) - } -} - -impl From for ConversionError { - fn from(err: identity::KeyRecoveryError) -> Self { - ConversionError::InvalidIdentityKeyError(err) - } -} +use topology::mix::{MixnodeConversionError, Node}; // used for mixnode to register themselves #[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] @@ -112,15 +93,15 @@ impl RegisteredMix { self.mix_info.node_info.incentives_address.clone() } - fn resolve_hostname(&self) -> Result { + fn resolve_hostname(&self) -> Result { self.mix_info .node_info .mix_host .to_socket_addrs() - .map_err(ConversionError::InvalidAddress)? + .map_err(MixnodeConversionError::InvalidAddress)? .next() .ok_or_else(|| { - ConversionError::InvalidAddress(io::Error::new( + MixnodeConversionError::InvalidAddress(io::Error::new( io::ErrorKind::Other, "no valid socket address", )) @@ -129,10 +110,12 @@ impl RegisteredMix { } impl TryInto for RegisteredMix { - type Error = ConversionError; + type Error = MixnodeConversionError; fn try_into(self) -> Result { Ok(topology::mix::Node { + owner: "N/A".to_string(), + stake: 0, host: self.resolve_hostname()?, location: self.mix_info.node_info.location, identity_key: identity::PublicKey::from_base58_string( @@ -142,18 +125,18 @@ impl TryInto for RegisteredMix { self.mix_info.node_info.sphinx_key, )?, layer: self.mix_info.layer, - registration_time: self.registration_time, - reputation: self.reputation, version: self.mix_info.node_info.version, }) } } impl<'a> TryInto for &'a RegisteredMix { - type Error = ConversionError; + type Error = MixnodeConversionError; fn try_into(self) -> Result { Ok(topology::mix::Node { + owner: "N/A".to_string(), + stake: 0, host: self.resolve_hostname()?, location: self.mix_info.node_info.location.clone(), identity_key: identity::PublicKey::from_base58_string( @@ -163,8 +146,6 @@ impl<'a> TryInto for &'a RegisteredMix { &self.mix_info.node_info.sphinx_key, )?, layer: self.mix_info.layer, - registration_time: self.registration_time, - reputation: self.reputation, version: self.mix_info.node_info.version.clone(), }) } diff --git a/common/client-libs/validator-client/src/models/topology.rs b/common/client-libs/validator-client/src/models/topology.rs index 96934049ed..f061fcc33f 100644 --- a/common/client-libs/validator-client/src/models/topology.rs +++ b/common/client-libs/validator-client/src/models/topology.rs @@ -14,7 +14,6 @@ use crate::models::gateway::RegisteredGateway; use crate::models::mixnode::RegisteredMix; -use crate::models::validators::ValidatorsOutput; use log::*; use serde::{Deserialize, Serialize}; use std::convert::TryInto; @@ -26,7 +25,6 @@ use topology::{MixLayer, NymTopology}; pub struct Topology { pub mix_nodes: Vec, pub gateways: Vec, - pub validators: ValidatorsOutput, } // changed from `TryInto`. reason being is that we should not fail entire topology diff --git a/common/mixnet-contract/src/gateway.rs b/common/mixnet-contract/src/gateway.rs new file mode 100644 index 0000000000..600842274e --- /dev/null +++ b/common/mixnet-contract/src/gateway.rs @@ -0,0 +1,111 @@ +// due to code generated by JsonSchema +#![allow(clippy::field_reassign_with_default)] + +use cosmwasm_std::{Coin, HumanAddr}; +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 location: String, + pub sphinx_key: String, + /// Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients + pub identity_key: String, + pub version: String, +} + +impl Gateway { + pub fn new( + mix_host: String, + clients_host: String, + location: String, + sphinx_key: String, + identity_key: String, + version: String, + ) -> Self { + Gateway { + mix_host, + clients_host, + location, + sphinx_key, + identity_key, + version, + } + } + + 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)] +pub struct GatewayBond { + pub amount: Vec, + pub owner: HumanAddr, + pub gateway: Gateway, +} + +impl GatewayBond { + pub fn new(amount: Vec, owner: HumanAddr, gateway: Gateway) -> Self { + GatewayBond { + amount, + owner, + gateway, + } + } + + pub fn amount(&self) -> &[Coin] { + &self.amount + } + + pub fn owner(&self) -> &HumanAddr { + &self.owner + } + + pub fn gateway(&self) -> &Gateway { + &self.gateway + } +} + +impl Display for GatewayBond { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if self.amount.len() != 1 { + write!(f, "amount: {:?}, owner: {}", self.amount, self.owner) + } else { + write!( + f, + "amount: {} {}, owner: {}", + self.amount[0].amount, self.amount[0].denom, self.owner + ) + } + } +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +pub struct PagedGatewayResponse { + pub nodes: Vec, + pub per_page: usize, + pub start_next_after: Option, +} + +impl PagedGatewayResponse { + pub fn new( + nodes: Vec, + per_page: usize, + start_next_after: Option, + ) -> Self { + PagedGatewayResponse { + nodes, + per_page, + start_next_after, + } + } +} diff --git a/common/mixnet-contract/src/lib.rs b/common/mixnet-contract/src/lib.rs index c32f358fa9..18a831b6ce 100644 --- a/common/mixnet-contract/src/lib.rs +++ b/common/mixnet-contract/src/lib.rs @@ -1,190 +1,6 @@ +mod gateway; +mod mixnode; + pub use cosmwasm_std::{Coin, HumanAddr}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use std::fmt::Display; - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -pub struct MixNode { - pub(crate) host: String, - pub(crate) layer: u64, - pub(crate) location: String, - pub(crate) sphinx_key: String, - /// Base58 encoded ed25519 EdDSA public key. - pub(crate) identity_key: String, - pub(crate) version: String, -} - -impl MixNode { - pub fn new( - host: String, - layer: u64, - location: String, - sphinx_key: String, - identity_key: String, - version: String, - ) -> Self { - MixNode { - host, - layer, - location, - sphinx_key, - identity_key, - version, - } - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -pub struct MixNodeBond { - pub(crate) amount: Vec, - pub(crate) owner: HumanAddr, - pub(crate) mix_node: MixNode, -} - -impl MixNodeBond { - pub fn new(amount: Vec, owner: HumanAddr, mix_node: MixNode) -> Self { - MixNodeBond { - amount, - owner, - mix_node, - } - } - - pub fn amount(&self) -> &[Coin] { - &self.amount - } - - pub fn owner(&self) -> &HumanAddr { - &self.owner - } - - pub fn mix_node(&self) -> &MixNode { - &self.mix_node - } -} - -impl Display for MixNodeBond { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - // Write strictly the first element into the supplied output - // stream: `f`. Returns `fmt::Result` which indicates whether the - // operation succeeded or failed. Note that `write!` uses syntax which - // is very similar to `println!`. - write!(f, "amount: {:?}, owner: {}", self.amount, self.owner) - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -pub struct Gateway { - pub(crate) mix_host: String, - pub(crate) clients_host: String, - pub(crate) location: String, - pub(crate) sphinx_key: String, - /// Base58 encoded ed25519 EdDSA public key of the gateway used to derive shared keys with clients - pub(crate) identity_key: String, - pub(crate) version: String, -} - -impl Gateway { - pub fn new( - mix_host: String, - clients_host: String, - location: String, - sphinx_key: String, - identity_key: String, - version: String, - ) -> Self { - Gateway { - mix_host, - clients_host, - location, - sphinx_key, - identity_key, - version, - } - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -pub struct GatewayBond { - pub(crate) amount: Vec, - pub(crate) owner: HumanAddr, - pub(crate) gateway: Gateway, -} - -impl GatewayBond { - pub fn new(amount: Vec, owner: HumanAddr, gateway: Gateway) -> Self { - GatewayBond { - amount, - owner, - gateway, - } - } - - pub fn amount(&self) -> &[Coin] { - &self.amount - } - - pub fn owner(&self) -> &HumanAddr { - &self.owner - } - - pub fn gateway(&self) -> &Gateway { - &self.gateway - } -} - -impl Display for GatewayBond { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - if self.amount.len() != 1 { - write!(f, "amount: {:?}, owner: {}", self.amount, self.owner) - } else { - write!( - f, - "amount: {} {}, owner: {}", - self.amount[0].amount, self.amount[0].denom, self.owner - ) - } - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -pub struct PagedResponse { - pub nodes: Vec, - pub per_page: usize, - pub start_next_after: Option, -} - -impl PagedResponse { - pub fn new( - nodes: Vec, - per_page: usize, - start_next_after: Option, - ) -> Self { - PagedResponse { - nodes, - per_page, - start_next_after, - } - } -} - -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] -pub struct PagedGatewayResponse { - pub nodes: Vec, - pub per_page: usize, - pub start_next_after: Option, -} - -impl PagedGatewayResponse { - pub fn new( - nodes: Vec, - per_page: usize, - start_next_after: Option, - ) -> Self { - PagedGatewayResponse { - nodes, - per_page, - start_next_after, - } - } -} +pub use gateway::{Gateway, GatewayBond, PagedGatewayResponse}; +pub use mixnode::{MixNode, MixNodeBond, PagedResponse}; diff --git a/common/mixnet-contract/src/mixnode.rs b/common/mixnet-contract/src/mixnode.rs new file mode 100644 index 0000000000..8e9de8eb4e --- /dev/null +++ b/common/mixnet-contract/src/mixnode.rs @@ -0,0 +1,107 @@ +// due to code generated by JsonSchema +#![allow(clippy::field_reassign_with_default)] + +use cosmwasm_std::{Coin, HumanAddr}; +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 layer: u64, + pub location: String, + pub sphinx_key: String, + /// Base58 encoded ed25519 EdDSA public key. + pub identity_key: String, + pub version: String, +} + +impl MixNode { + pub fn new( + host: String, + layer: u64, + location: String, + sphinx_key: String, + identity_key: String, + version: String, + ) -> Self { + MixNode { + host, + layer, + location, + sphinx_key, + identity_key, + 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")) + } +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +pub struct MixNodeBond { + pub amount: Vec, + pub owner: HumanAddr, + pub mix_node: MixNode, +} + +impl MixNodeBond { + pub fn new(amount: Vec, owner: HumanAddr, mix_node: MixNode) -> Self { + MixNodeBond { + amount, + owner, + mix_node, + } + } + + pub fn amount(&self) -> &[Coin] { + &self.amount + } + + pub fn owner(&self) -> &HumanAddr { + &self.owner + } + + pub fn mix_node(&self) -> &MixNode { + &self.mix_node + } +} + +impl Display for MixNodeBond { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + // Write strictly the first element into the supplied output + // stream: `f`. Returns `fmt::Result` which indicates whether the + // operation succeeded or failed. Note that `write!` uses syntax which + // is very similar to `println!`. + write!(f, "amount: {:?}, owner: {}", self.amount, self.owner) + } +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)] +pub struct PagedResponse { + pub nodes: Vec, + pub per_page: usize, + pub start_next_after: Option, +} + +impl PagedResponse { + pub fn new( + nodes: Vec, + per_page: usize, + start_next_after: Option, + ) -> Self { + PagedResponse { + nodes, + per_page, + start_next_after, + } + } +} diff --git a/common/nymsphinx/src/receiver.rs b/common/nymsphinx/src/receiver.rs index 66d48fc51e..4a6d166274 100644 --- a/common/nymsphinx/src/receiver.rs +++ b/common/nymsphinx/src/receiver.rs @@ -214,6 +214,8 @@ mod message_receiver { mixes.insert( 1, vec![mix::Node { + owner: "foomp1".to_string(), + stake: 123, location: "unknown".to_string(), host: "10.20.30.40:1789".parse().unwrap(), identity_key: identity::PublicKey::from_base58_string( @@ -225,15 +227,15 @@ mod message_receiver { ) .unwrap(), layer: 1, - registration_time: 1594812897745695000, version: "0.8.0-dev".to_string(), - reputation: 100, }], ); mixes.insert( 2, vec![mix::Node { + owner: "foomp2".to_string(), + stake: 123, location: "unknown".to_string(), host: "11.21.31.41:1789".parse().unwrap(), identity_key: identity::PublicKey::from_base58_string( @@ -245,15 +247,15 @@ mod message_receiver { ) .unwrap(), layer: 2, - registration_time: 1594812897745695000, version: "0.8.0-dev".to_string(), - reputation: 100, }], ); mixes.insert( 3, vec![mix::Node { + owner: "foomp3".to_string(), + stake: 123, location: "unknown".to_string(), host: "12.22.32.42:1789".parse().unwrap(), identity_key: identity::PublicKey::from_base58_string( @@ -265,9 +267,7 @@ mod message_receiver { ) .unwrap(), layer: 3, - registration_time: 1594812897745695000, version: "0.8.0-dev".to_string(), - reputation: 100, }], ); @@ -275,6 +275,8 @@ mod message_receiver { // currently coco_nodes don't really exist so this is still to be determined mixes, vec![gateway::Node { + 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(), @@ -286,8 +288,6 @@ mod message_receiver { "EB42xvMFMD5rUCstE2CDazgQQJ22zLv8SPm1Luxni44c", ) .unwrap(), - registration_time: 1594812897745695000, - reputation: 100, version: "0.8.0-dev".to_string(), }], ) diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index b7027cd47b..ac03e868c1 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -14,6 +14,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] } ## internal crypto = { path = "../crypto" } +mixnet-contract = { path = "../mixnet-contract" } nymsphinx-addressing = { path = "../nymsphinx/addressing" } nymsphinx-types = { path = "../nymsphinx/types" } version-checker = { path = "../version-checker" } diff --git a/common/topology/src/gateway.rs b/common/topology/src/gateway.rs index 56d89ad69f..a7acfdfcf9 100644 --- a/common/topology/src/gateway.rs +++ b/common/topology/src/gateway.rs @@ -14,20 +14,80 @@ use crate::filter; use crypto::asymmetric::{encryption, identity}; +use mixnet_contract::GatewayBond; use nymsphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress}; use nymsphinx_types::Node as SphinxNode; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; +use std::fmt::{self, Display, Formatter}; +use std::io; use std::net::SocketAddr; +#[derive(Debug)] +pub enum GatewayConversionError { + InvalidIdentityKey(identity::KeyRecoveryError), + InvalidSphinxKey(encryption::KeyRecoveryError), + InvalidAddress(io::Error), + InvalidStake, + Other(Box), +} + +impl From for GatewayConversionError { + fn from(err: encryption::KeyRecoveryError) -> Self { + GatewayConversionError::InvalidSphinxKey(err) + } +} + +impl From for GatewayConversionError { + fn from(err: identity::KeyRecoveryError) -> Self { + GatewayConversionError::InvalidIdentityKey(err) + } +} + +impl Display for GatewayConversionError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + GatewayConversionError::InvalidIdentityKey(err) => write!( + f, + "failed to convert gateway due to invalid identity key - {}", + err + ), + GatewayConversionError::InvalidSphinxKey(err) => write!( + f, + "failed to convert gateway due to invalid sphinx key - {}", + err + ), + GatewayConversionError::InvalidAddress(err) => { + write!( + f, + "failed to convert gateway due to invalid address - {}", + err + ) + } + GatewayConversionError::InvalidStake => { + write!(f, "failed to convert gateway due to invalid stake") + } + GatewayConversionError::Other(err) => { + write!( + f, + "failed to convert gateway due to another error - {}", + err + ) + } + } + } +} + #[derive(Debug, Clone)] pub struct Node { + pub owner: String, + // somebody correct me if I'm wrong, but we should only ever have a single denom of currency + // on the network at a type, right? + pub stake: u128, pub location: String, pub client_listener: String, pub mixnet_listener: SocketAddr, pub identity_key: identity::PublicKey, pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519 - pub registration_time: i64, - pub reputation: i64, pub version: String, } @@ -52,3 +112,38 @@ impl<'a> From<&'a Node> for SphinxNode { SphinxNode::new(node_address_bytes, (&node.sphinx_key).into()) } } + +impl<'a> TryFrom<&'a GatewayBond> for Node { + type Error = GatewayConversionError; + + fn try_from(bond: &'a GatewayBond) -> Result { + if bond.amount.len() > 1 { + return Err(GatewayConversionError::InvalidStake); + } + Ok(Node { + owner: bond.owner.0.clone(), + stake: bond + .amount + .first() + .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(GatewayConversionError::InvalidAddress)?, + 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(), + }) + } +} + +impl TryFrom for Node { + type Error = GatewayConversionError; + + fn try_from(bond: GatewayBond) -> Result { + Node::try_from(&bond) + } +} diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 9fedb187f5..890d380465 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -13,12 +13,15 @@ // limitations under the License. use crate::filter::VersionFilterable; +use log::warn; +use mixnet_contract::{GatewayBond, MixNodeBond}; use nymsphinx_addressing::nodes::NodeIdentity; use nymsphinx_types::Node as SphinxNode; use rand::Rng; use std::collections::HashMap; +use std::convert::TryInto; -mod filter; +pub mod filter; pub mod gateway; pub mod mix; @@ -58,12 +61,12 @@ impl NymTopology { mixes } - pub fn mixes_in_layer(&self, layer: u8) -> Vec { + pub fn mixes_in_layer(&self, layer: MixLayer) -> Vec { assert!(vec![1, 2, 3].contains(&layer)); self.mixes.get(&layer).unwrap().to_owned() } - pub fn gateways(&self) -> &Vec { + pub fn gateways(&self) -> &[gateway::Node] { &self.gateways } @@ -81,6 +84,8 @@ impl NymTopology { self.gateways = gateways } + /// Returns a vec of size of `num_mix_hops` of mixnodes, such that each subsequent node is on + /// next layer, starting from layer 1 pub fn random_mix_route( &self, rng: &mut R, @@ -117,6 +122,8 @@ impl NymTopology { Ok(route) } + /// Tries to create a route to the specified gateway, such that it goes through mixnode on layer 1, + /// mixnode on layer2, .... mixnode on layer n and finally the target gateway pub fn random_route_to_gateway( &self, rng: &mut R, @@ -138,10 +145,12 @@ impl NymTopology { .collect()) } + /// Overwrites the existing nodes in the specified layer pub fn set_mixes_in_layer(&mut self, layer: u8, mixes: Vec) { self.mixes.insert(layer, mixes); } + /// Checks if a mixnet path can be constructed using the specified number of hops pub fn can_construct_path_through(&self, num_mix_hops: u8) -> bool { // if there are no gateways present, we can't do anything if self.gateways.is_empty() { @@ -183,6 +192,47 @@ impl NymTopology { } } +pub fn nym_topology_from_bonds( + mix_bonds: Vec, + gateway_bonds: Vec, +) -> NymTopology { + let mut mixes = HashMap::new(); + for bond in mix_bonds.into_iter() { + let layer = bond.mix_node.layer as MixLayer; + if layer == 0 || layer > 3 { + warn!( + "{} says it's on invalid layer {}!", + bond.mix_node.identity_key, layer + ); + continue; + } + let mix_id = bond.mix_node.identity_key.clone(); + + let layer_entry = mixes.entry(layer).or_insert_with(Vec::new); + match bond.try_into() { + Ok(mix) => layer_entry.push(mix), + Err(err) => { + warn!("Mix {} is malformed - {}", mix_id, err); + continue; + } + } + } + + let mut gateways = Vec::with_capacity(gateway_bonds.len()); + for bond in gateway_bonds.into_iter() { + let gate_id = bond.gateway.identity_key.clone(); + match bond.try_into() { + Ok(gate) => gateways.push(gate), + Err(err) => { + warn!("Gateway {} is malformed - {}", gate_id, err); + continue; + } + } + } + + NymTopology::new(mixes, gateways) +} + #[cfg(test)] mod converting_mixes_to_vec { use super::*; @@ -196,6 +246,8 @@ mod converting_mixes_to_vec { #[test] fn returns_a_vec_with_hashmap_values() { let node1 = mix::Node { + owner: "N/A".to_string(), + stake: 0, location: "London".to_string(), host: "3.3.3.3:1789".parse().unwrap(), identity_key: identity::PublicKey::from_base58_string( @@ -207,8 +259,6 @@ mod converting_mixes_to_vec { ) .unwrap(), layer: 1, - registration_time: 123, - reputation: 0, version: "0.x.0".to_string(), }; diff --git a/common/topology/src/mix.rs b/common/topology/src/mix.rs index f5783e7cc0..4d741a9ca9 100644 --- a/common/topology/src/mix.rs +++ b/common/topology/src/mix.rs @@ -14,20 +14,80 @@ use crate::filter; use crypto::asymmetric::{encryption, identity}; +use mixnet_contract::MixNodeBond; use nymsphinx_addressing::nodes::NymNodeRoutingAddress; use nymsphinx_types::Node as SphinxNode; -use std::convert::TryInto; +use std::convert::{TryFrom, TryInto}; +use std::fmt::{self, Display, Formatter}; +use std::io; use std::net::SocketAddr; +#[derive(Debug)] +pub enum MixnodeConversionError { + InvalidIdentityKey(identity::KeyRecoveryError), + InvalidSphinxKey(encryption::KeyRecoveryError), + InvalidAddress(io::Error), + InvalidStake, + Other(Box), +} + +impl From for MixnodeConversionError { + fn from(err: encryption::KeyRecoveryError) -> Self { + MixnodeConversionError::InvalidSphinxKey(err) + } +} + +impl From for MixnodeConversionError { + fn from(err: identity::KeyRecoveryError) -> Self { + MixnodeConversionError::InvalidIdentityKey(err) + } +} + +impl Display for MixnodeConversionError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + MixnodeConversionError::InvalidIdentityKey(err) => write!( + f, + "failed to convert mixnode due to invalid identity key - {}", + err + ), + MixnodeConversionError::InvalidSphinxKey(err) => write!( + f, + "failed to convert mixnode due to invalid sphinx key - {}", + err + ), + MixnodeConversionError::InvalidAddress(err) => { + write!( + f, + "failed to convert mixnode due to invalid address - {}", + err + ) + } + MixnodeConversionError::InvalidStake => { + write!(f, "failed to convert mixnode due to invalid stake") + } + MixnodeConversionError::Other(err) => { + write!( + f, + "failed to convert mixnode due to another error - {}", + err + ) + } + } + } +} + #[derive(Debug, Clone)] pub struct Node { + pub owner: String, + // somebody correct me if I'm wrong, but we should only ever have a single denom of currency + // on the network at a type, right? + pub stake: u128, pub location: String, pub host: SocketAddr, pub identity_key: identity::PublicKey, pub sphinx_key: encryption::PublicKey, // TODO: or nymsphinx::PublicKey? both are x25519 pub layer: u64, - pub registration_time: i64, - pub reputation: i64, pub version: String, } @@ -44,3 +104,38 @@ impl<'a> From<&'a Node> for SphinxNode { SphinxNode::new(node_address_bytes, (&node.sphinx_key).into()) } } + +impl<'a> TryFrom<&'a MixNodeBond> for Node { + type Error = MixnodeConversionError; + + fn try_from(bond: &'a MixNodeBond) -> Result { + if bond.amount.len() > 1 { + return Err(MixnodeConversionError::InvalidStake); + } + Ok(Node { + owner: bond.owner.0.clone(), + stake: bond + .amount + .first() + .map(|stake| stake.amount.into()) + .unwrap_or(0), + location: bond.mix_node.location.clone(), + host: bond + .mix_node + .try_resolve_hostname() + .map_err(MixnodeConversionError::InvalidAddress)?, + 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, + version: bond.mix_node.version.clone(), + }) + } +} + +impl TryFrom for Node { + type Error = MixnodeConversionError; + + fn try_from(bond: MixNodeBond) -> Result { + Node::try_from(&bond) + } +} diff --git a/gateway/Cargo.toml b/gateway/Cargo.toml index b63b612348..acb53efccd 100644 --- a/gateway/Cargo.toml +++ b/gateway/Cargo.toml @@ -33,7 +33,7 @@ mixnet-client = { path = "../common/client-libs/mixnet-client" } mixnode-common = { path = "../common/mixnode-common" } nymsphinx = { path = "../common/nymsphinx" } pemstore = { path = "../common/pemstore" } -validator-client = { path = "../common/client-libs/validator-client" } +validator-client-rest = { path = "../common/client-libs/validator-client-rest" } version-checker = { path = "../common/version-checker" } [dependencies.tungstenite] diff --git a/gateway/src/commands/init.rs b/gateway/src/commands/init.rs index 67a962e1c0..6997a7d8b6 100644 --- a/gateway/src/commands/init.rs +++ b/gateway/src/commands/init.rs @@ -18,12 +18,6 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .takes_value(true) .required(true), ) - .arg( - Arg::with_name("location") - .long("location") - .help("Optional geographical location of this provider") - .takes_value(true), - ) .arg( Arg::with_name("mix-host") .long("mix-host") @@ -93,9 +87,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .takes_value(true), ) .arg( - Arg::with_name("incentives-address") - .long("incentives-address") - .help("Optional, if participating in the incentives program, payment address") + Arg::with_name("mixnet-contract") + .long("mixnet-contract") + .help("Address of the validator contract managing the network") .takes_value(true), ) } diff --git a/gateway/src/commands/mod.rs b/gateway/src/commands/mod.rs index 159ef36a38..60d0a124ba 100644 --- a/gateway/src/commands/mod.rs +++ b/gateway/src/commands/mod.rs @@ -6,7 +6,6 @@ use clap::ArgMatches; pub(crate) mod init; pub(crate) mod run; -pub(crate) mod unregister; pub(crate) mod upgrade; pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { @@ -80,6 +79,10 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi config = config.with_custom_validator(validator); } + if let Some(contract_address) = matches.value_of("mixnet-contract") { + config = config.with_custom_mixnet_contract(contract_address) + } + if let Some(inboxes_dir) = matches.value_of("inboxes") { config = config.with_custom_clients_inboxes(inboxes_dir); } @@ -88,13 +91,5 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi config = config.with_custom_clients_ledger(clients_ledger); } - if let Some(location) = matches.value_of("location") { - config = config.with_location(location); - } - - if let Some(incentives_address) = matches.value_of("incentives-address") { - config = config.with_incentives_address(incentives_address); - } - config } diff --git a/gateway/src/commands/run.rs b/gateway/src/commands/run.rs index a6704716bd..0b92605631 100644 --- a/gateway/src/commands/run.rs +++ b/gateway/src/commands/run.rs @@ -22,12 +22,6 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .required(true), ) // the rest of arguments are optional, they are used to override settings in config file - .arg( - Arg::with_name("location") - .long("location") - .help("Optional geographical location of this gateway") - .takes_value(true), - ) .arg( Arg::with_name("config") .long("config") @@ -100,6 +94,12 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .help("REST endpoint of the validator the node is registering presence with") .takes_value(true), ) + .arg( + Arg::with_name("mixnet-contract") + .long("mixnet-contract") + .help("Address of the validator contract managing the network") + .takes_value(true), + ) } fn show_binding_warning(address: String) { diff --git a/gateway/src/commands/unregister.rs b/gateway/src/commands/unregister.rs deleted file mode 100644 index 7c6723cd69..0000000000 --- a/gateway/src/commands/unregister.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::config::{persistence::pathfinder::GatewayPathfinder, Config}; -use clap::{App, Arg, ArgMatches}; -use config::NymConfig; -use crypto::asymmetric::identity; -use log::*; -use tokio::runtime::Runtime; - -pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { - App::new("unregister").about("Unregister the gateway").arg( - Arg::with_name("id") - .long("id") - .help("Id of the nym-gateway we want to explicitly unregister") - .takes_value(true) - .required(true), - ) -} - -fn load_identity_keys(pathfinder: &GatewayPathfinder) -> identity::KeyPair { - pemstore::load_keypair(&pemstore::KeyPairPath::new( - pathfinder.private_identity_key().to_owned(), - pathfinder.public_identity_key().to_owned(), - )) - .expect("Failed to read stored identity key files") -} - -pub fn execute(matches: &ArgMatches) { - // TODO: this should probably be made implicit by slapping `#[tokio::main]` on our main method - // and then removing runtime from gateway itself in `run` - let mut rt = Runtime::new().unwrap(); - rt.block_on(async { - let id = matches.value_of("id").unwrap(); - - println!("Attempting to unregister gateway {}...", id); - - let config = match Config::load_from_file(id) { - Ok(cfg) => cfg, - Err(err) => { - error!("Failed to load config for {}. Are you sure you have run provided correct id? (Error was: {})", id, err); - return; - } - }; - - // we need to load identity keys to be able to grab node's public key - let pathfinder = GatewayPathfinder::new_from_config(&config); - let identity_keypair = load_identity_keys(&pathfinder); - - // now attempt to unregister - let validator_client_config = validator_client::Config::new(config.get_validator_rest_endpoint()); - let validator_client = validator_client::Client::new(validator_client_config); - - match validator_client.unregister_node(&*identity_keypair.public_key().to_base58_string()).await { - Err(err) => error!("failed to unregister node '{}'. Error: {:?}", id, err), - Ok(_) => info!("managed to successfully unregister node '{}'!", id) - } - }) -} diff --git a/gateway/src/commands/upgrade.rs b/gateway/src/commands/upgrade.rs index a3b61f8f7b..942049d8bd 100644 --- a/gateway/src/commands/upgrade.rs +++ b/gateway/src/commands/upgrade.rs @@ -30,7 +30,7 @@ fn print_successful_upgrade(from: D1, to: D2) { ); } -fn pre_090_upgrade(from: &str, config: Config, matches: &ArgMatches) -> Config { +fn pre_090_upgrade(from: &str, config: Config, _matches: &ArgMatches) -> Config { // this is not extracted to separate function as you only have to manually pass version // if upgrading from pre090 version let from = match from.strip_prefix("v") { @@ -82,15 +82,10 @@ fn pre_090_upgrade(from: &str, config: Config, matches: &ArgMatches) -> Config { DEFAULT_VALIDATOR_REST_ENDPOINT ); - let mut upgraded_config = config + let upgraded_config = config .with_custom_version(to_version.to_string().as_ref()) .with_custom_validator(DEFAULT_VALIDATOR_REST_ENDPOINT); - if let Some(incentives_address) = matches.value_of("incentives address") { - upgraded_config = upgraded_config.with_incentives_address(incentives_address); - println!("Setting incentives address to {}", incentives_address); - } - upgraded_config.save_to_file(None).unwrap_or_else(|err| { eprintln!("failed to overwrite config file! - {:?}", err); print_failed_upgrade(&from_version, &to_version); @@ -102,7 +97,7 @@ fn pre_090_upgrade(from: &str, config: Config, matches: &ArgMatches) -> Config { upgraded_config } -fn patch_09x_upgrade(config: Config, matches: &ArgMatches) -> Config { +fn patch_09x_upgrade(config: Config, _matches: &ArgMatches) -> Config { // this call must succeed as it was already called before let from_version = Version::parse(config.get_version()).unwrap(); let to_version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); @@ -110,17 +105,7 @@ fn patch_09x_upgrade(config: Config, matches: &ArgMatches) -> Config { print_start_upgrade(&from_version, &to_version); // 0.9.1 upgrade: - let mut upgraded_config = config.with_custom_version(to_version.to_string().as_ref()); - - // not strictly part of the upgrade, but since people had problems with it and I've got a feeling - // they might try to use it, just allow changing incentives address here again... - if let Some(incentives_address) = matches.value_of("incentives address") { - upgraded_config = upgraded_config.with_incentives_address(incentives_address); - println!( - "Setting incentives address to {}. Old value will be overwritten", - incentives_address - ); - } + let upgraded_config = config.with_custom_version(to_version.to_string().as_ref()); upgraded_config.save_to_file(None).unwrap_or_else(|err| { eprintln!("failed to overwrite config file! - {:?}", err); @@ -148,11 +133,6 @@ 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("incentives address") - .long("incentives-address") - .help("Optional, if participating in the incentives program, payment address") - .takes_value(true) - ) } fn unsupported_upgrade(current_version: Version, config_version: Version) -> ! { diff --git a/gateway/src/config/mod.rs b/gateway/src/config/mod.rs index b5188a72c8..2fe110ee72 100644 --- a/gateway/src/config/mod.rs +++ b/gateway/src/config/mod.rs @@ -23,6 +23,8 @@ const DEFAULT_MIX_LISTENING_PORT: u16 = 1789; const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000; pub(crate) const DEFAULT_VALIDATOR_REST_ENDPOINT: &str = "http://testnet-validator1.nymtech.net:8081"; +pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = + "TODO: THIS NEEDS TO BE FILLED WITH SOME REASONABLE VALUE!"; // 'DEBUG' // where applicable, the below are defined in milliseconds @@ -129,18 +131,6 @@ where deserializer.deserialize_any(DurationVisitor) } -fn deserialize_option_string<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - if s.is_empty() { - Ok(None) - } else { - Ok(Some(s)) - } -} - pub fn missing_string_value() -> String { MISSING_VALUE.to_string() } @@ -196,8 +186,8 @@ impl Config { self } - pub fn with_location>(mut self, location: S) -> Self { - self.gateway.location = location.into(); + pub fn with_custom_mixnet_contract>(mut self, mixnet_contract: S) -> Self { + self.gateway.mixnet_contract_address = mixnet_contract.into(); self } @@ -382,20 +372,11 @@ impl Config { self } - pub fn with_incentives_address>(mut self, incentives_address: S) -> Self { - self.gateway.incentives_address = Some(incentives_address.into()); - self - } - // getters pub fn get_config_file_save_location(&self) -> PathBuf { self.config_directory().join(Self::config_file_name()) } - pub fn get_location(&self) -> String { - self.gateway.location.clone() - } - pub fn get_private_identity_key_file(&self) -> PathBuf { self.gateway.private_identity_key_file.clone() } @@ -416,6 +397,10 @@ impl Config { self.gateway.validator_rest_url.clone() } + pub fn get_validator_mixnet_contract_address(&self) -> String { + self.gateway.mixnet_contract_address.clone() + } + pub fn get_mix_listening_address(&self) -> SocketAddr { self.mixnet_endpoint.listening_address } @@ -471,10 +456,6 @@ impl Config { pub fn get_version(&self) -> &str { &self.gateway.version } - - pub fn get_incentives_address(&self) -> Option { - self.gateway.incentives_address.clone() - } } #[derive(Debug, Deserialize, PartialEq, Serialize)] @@ -486,12 +467,6 @@ pub struct Gateway { /// ID specifies the human readable ID of this particular gateway. id: String, - /// Completely optional value specifying geographical location of this particular gateway. - /// Currently it's used entirely for debug purposes, as there are no mechanisms implemented - /// to verify correctness of the information provided. However, feel free to fill in - /// this field with as much accuracy as you wish to share. - location: String, - /// Path to file containing private identity key. private_identity_key_file: PathBuf, @@ -508,13 +483,13 @@ pub struct Gateway { #[serde(default = "missing_string_value")] validator_rest_url: String, + /// Address of the validator contract managing the network. + #[serde(default = "missing_string_value")] + mixnet_contract_address: String, + /// nym_home_directory specifies absolute path to the home nym gateways directory. /// It is expected to use default value and hence .toml file should not redefine this field. nym_root_directory: PathBuf, - - /// Optional, if participating in the incentives program, payment address. - #[serde(deserialize_with = "deserialize_option_string", default)] - incentives_address: Option, } impl Gateway { @@ -533,10 +508,6 @@ impl Gateway { fn default_public_identity_key_file(id: &str) -> PathBuf { Config::default_data_directory(id).join("public_identity.pem") } - - fn default_location() -> String { - "unknown".into() - } } impl Default for Gateway { @@ -544,14 +515,13 @@ impl Default for Gateway { Gateway { version: env!("CARGO_PKG_VERSION").to_string(), id: "".to_string(), - location: Self::default_location(), private_identity_key_file: Default::default(), public_identity_key_file: Default::default(), private_sphinx_key_file: Default::default(), public_sphinx_key_file: Default::default(), validator_rest_url: DEFAULT_VALIDATOR_REST_ENDPOINT.to_string(), + mixnet_contract_address: DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(), nym_root_directory: Config::default_root_directory(), - incentives_address: None, } } } diff --git a/gateway/src/config/template.rs b/gateway/src/config/template.rs index 5bd828f611..45907d2f9a 100644 --- a/gateway/src/config/template.rs +++ b/gateway/src/config/template.rs @@ -19,12 +19,6 @@ version = '{{ gateway.version }}' # Human readable ID of this particular gateway. id = '{{ gateway.id }}' -# Completely optional value specifying geographical location of this particular node. -# Currently it's used entirely for debug purposes, as there are no mechanisms implemented -# to verify correctness of the information provided. However, feel free to fill in -# this field with as much accuracy as you wish to share. -location = '{{ gateway.location }}' - # Path to file containing private identity key. private_identity_key_file = '{{ gateway.private_identity_key_file }}' @@ -37,12 +31,12 @@ 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 }}' -# Optional, if participating in the incentives program, payment address. -incentives_address = '{{ gateway.incentives_address }}' - # Validator server to which the node will be reporting their presence data. validator_rest_url = '{{ gateway.validator_rest_url }}' +# Address of the validator contract managing the network. +mixnet_contract_address = '{{ gateway.mixnet_contract_address }}' + # 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 }}' diff --git a/gateway/src/main.rs b/gateway/src/main.rs index 34bdd40a51..30b358b3a0 100644 --- a/gateway/src/main.rs +++ b/gateway/src/main.rs @@ -19,7 +19,6 @@ fn main() { .subcommand(commands::init::command_args()) .subcommand(commands::run::command_args()) .subcommand(commands::upgrade::command_args()) - .subcommand(commands::unregister::command_args()) .get_matches(); execute(arg_matches); @@ -30,7 +29,6 @@ fn execute(matches: ArgMatches) { ("init", Some(m)) => commands::init::execute(m), ("run", Some(m)) => commands::run::execute(m), ("upgrade", Some(m)) => commands::upgrade::execute(m), - ("unregister", Some(m)) => commands::unregister::execute(m), _ => println!("{}", usage()), } } diff --git a/gateway/src/node/mod.rs b/gateway/src/node/mod.rs index a34e04d23d..9a11560c0e 100644 --- a/gateway/src/node/mod.rs +++ b/gateway/src/node/mod.rs @@ -15,7 +15,6 @@ use tokio::runtime::Runtime; pub(crate) mod client_handling; pub(crate) mod mixnet_handling; -mod presence; pub(crate) mod storage; pub struct Gateway { @@ -124,40 +123,34 @@ impl Gateway { println!( "Received SIGINT - the gateway will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)." ); - if let Err(err) = presence::unregister_with_validator( - self.config.get_validator_rest_endpoint(), - self.identity.public_key().to_base58_string(), - ) - .await - { - error!("failed to unregister with validator... - {}", err) - } else { - info!("unregistration was successful!") - } } + // 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_endpoint()); - let validator_client = validator_client::Client::new(validator_client_config); - let topology = match validator_client.get_topology().await { - Ok(topology) => topology, + + let validator_client_config = validator_client_rest::Config::new( + self.config.get_validator_rest_endpoint(), + self.config.get_validator_mixnet_contract_address(), + ); + let validator_client = validator_client_rest::Client::new(validator_client_config); + + let existing_gateways = match validator_client.get_gateways().await { + Ok(gateways) => gateways, Err(err) => { - error!("failed to grab initial network topology - {}\n Please try to startup again in few minutes", err); + error!("failed to grab initial network gateways - {}\n Please try to startup again in few minutes", err); process::exit(1); } }; - let existing_gateways = topology.gateways; existing_gateways .iter() .find(|node| { - node.mixnet_listener() == announced_mix_host - || node.clients_listener() == announced_clients_host + node.gateway.mix_host == announced_mix_host + || node.gateway.clients_host == announced_clients_host }) - .map(|node| node.identity()) + .map(|node| node.gateway().identity_key.clone()) } // Rather than starting all futures with explicit `&Handle` argument, let's see how it works @@ -181,15 +174,6 @@ impl Gateway { } } - if let Err(err) = presence::register_with_validator( - &self.config, - self.identity.public_key().to_base58_string(), - self.encryption_keys.public_key().to_base58_string(), - ).await { - error!("failed to register with the validator - {}.\nPlease try again in few minutes.", err); - return - } - let mix_forwarding_channel = self.start_packet_forwarder(); let clients_handler_sender = self.start_clients_handler(); diff --git a/gateway/src/node/presence.rs b/gateway/src/node/presence.rs deleted file mode 100644 index dfcff83478..0000000000 --- a/gateway/src/node/presence.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::config::Config; -use validator_client::models::gateway::GatewayRegistrationInfo; -use validator_client::ValidatorClientError; - -// there's no point in keeping the validator client persistently as it might be literally hours or days -// before it's used again -pub(crate) async fn register_with_validator( - gateway_config: &Config, - identity_key: String, - sphinx_key: String, -) -> Result<(), ValidatorClientError> { - let config = validator_client::Config::new(gateway_config.get_validator_rest_endpoint()); - let validator_client = validator_client::Client::new(config); - - let registration_info = GatewayRegistrationInfo::new( - gateway_config.get_mix_announce_address(), - gateway_config.get_clients_announce_address(), - identity_key, - sphinx_key, - gateway_config.get_version().to_string(), - gateway_config.get_location(), - gateway_config.get_incentives_address(), - ); - - validator_client.register_gateway(registration_info).await -} - -pub(crate) async fn unregister_with_validator( - validator_endpoint: String, - identity_key: String, -) -> Result<(), ValidatorClientError> { - let config = validator_client::Config::new(validator_endpoint); - let validator_client = validator_client::Client::new(config); - - validator_client.unregister_node(&identity_key).await -} diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index c40c870010..eab6af6a7b 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -34,5 +34,5 @@ nonexhaustive-delayqueue = { path = "../common/nonexhaustive-delayqueue" } nymsphinx = { path = "../common/nymsphinx" } pemstore = { path = "../common/pemstore" } topology = { path = "../common/topology" } -validator-client = { path = "../common/client-libs/validator-client" } +validator-client-rest = { path = "../common/client-libs/validator-client-rest" } version-checker = { path = "../common/version-checker" } diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs index 740a484473..2b6046231e 100644 --- a/mixnode/src/commands/init.rs +++ b/mixnode/src/commands/init.rs @@ -9,8 +9,8 @@ use config::NymConfig; use crypto::asymmetric::{encryption, identity}; use log::*; use nymsphinx::params::DEFAULT_NUM_MIX_HOPS; +use std::collections::HashMap; use tokio::runtime::Runtime; -use topology::NymTopology; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { App::new("init") @@ -22,12 +22,6 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .takes_value(true) .required(true), ) - .arg( - Arg::with_name("location") - .long("location") - .help("Optional geographical location of this node") - .takes_value(true), - ) .arg( Arg::with_name("layer") .long("layer") @@ -65,21 +59,25 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .help("REST endpoint of the validator the node is registering presence with") .takes_value(true), ) + .arg( + Arg::with_name("mixnet-contract") + .long("mixnet-contract") + .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), ) - .arg( - Arg::with_name("incentives-address") - .long("incentives-address") - .help("Optional, if participating in the incentives program, payment address") - .takes_value(true), - ) } -async fn choose_layer(matches: &ArgMatches<'_>, validator_server: String) -> u64 { +async fn choose_layer( + matches: &ArgMatches<'_>, + validator_server: String, + mixnet_contract: String, +) -> u64 { let max_layer = DEFAULT_NUM_MIX_HOPS; if let Some(layer) = matches.value_of("layer").map(|layer| layer.parse::()) { if let Err(err) = layer { @@ -92,30 +90,42 @@ async fn choose_layer(matches: &ArgMatches<'_>, validator_server: String) -> u64 } } - let validator_client_config = validator_client::Config::new(validator_server); - let validator_client = validator_client::Client::new(validator_client_config); - let topology: NymTopology = validator_client - .get_topology() + let validator_client_config = + validator_client_rest::Config::new(validator_server, mixnet_contract); + let validator_client = validator_client_rest::Client::new(validator_client_config); + + let mixnodes = validator_client + .get_mix_nodes() .await - .expect("failed to obtain initial network topology!") - .into(); - - let mut lowest_layer = (0, usize::max_value()); + .expect("failed to obtain initial network mixnodes"); + let mut nodes_distribution = HashMap::new(); + // initialise with 0 for each possible layer for layer in 1..=max_layer { - let nodes_count = topology - .mixes() - .get(&layer) - .map(|layer_mixes| layer_mixes.len()) - .unwrap_or_else(|| 0); - trace!("There are {} nodes on layer {}", nodes_count, layer); - if nodes_count < lowest_layer.1 { - lowest_layer.0 = layer; - lowest_layer.1 = nodes_count; - } + nodes_distribution.insert(layer as u64, 0); } - lowest_layer.0 as u64 + for node in mixnodes { + if node.mix_node.layer < 1 || node.mix_node.layer > max_layer as u64 { + warn!( + "one of bonded mixnodes is on invalid layer {}", + node.mix_node.layer + ); + continue; + } + + *nodes_distribution.entry(node.mix_node.layer).or_insert(0) += 1; + } + + // this can't be None as the hashmap is guaranteed to be non-empty since we initialised it + // with zeroes for each possible layer + let layer_with_fewest = nodes_distribution + .iter() + .min_by(|a, b| a.1.cmp(&b.1)) + .map(|(k, _v)| k) + .unwrap(); + + *layer_with_fewest } pub fn execute(matches: &ArgMatches) { @@ -135,7 +145,7 @@ pub fn execute(matches: &ArgMatches) { let mut config = Config::new(id); config = override_config(config, matches); - let layer = choose_layer(matches, config.get_validator_rest_endpoint()).await; + let layer = choose_layer(matches, config.get_validator_rest_endpoint(), config.get_validator_mixnet_contract_address()).await; // TODO: I really don't like how we override config and are presumably done with it // only to change it here config = config.with_layer(layer); diff --git a/mixnode/src/commands/mod.rs b/mixnode/src/commands/mod.rs index 2357fc9adc..c3eb045732 100644 --- a/mixnode/src/commands/mod.rs +++ b/mixnode/src/commands/mod.rs @@ -7,7 +7,6 @@ use nymsphinx::params::DEFAULT_NUM_MIX_HOPS; pub(crate) mod init; pub(crate) mod run; -pub(crate) mod unregister; pub(crate) mod upgrade; pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { @@ -41,6 +40,10 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi config = config.with_custom_validator(validator); } + if let Some(contract_address) = matches.value_of("mixnet-contract") { + config = config.with_custom_mixnet_contract(contract_address) + } + if let Some(metrics_server) = matches.value_of("metrics-server") { config = config.with_custom_metrics_server(metrics_server); } @@ -63,13 +66,5 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Confi config = config.with_announce_port(announce_port.unwrap()); } - if let Some(location) = matches.value_of("location") { - config = config.with_location(location); - } - - if let Some(incentives_address) = matches.value_of("incentives-address") { - config = config.with_incentives_address(incentives_address); - } - config } diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index 17c6c9d6de..e555bb4700 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -21,12 +21,6 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> { .required(true), ) // the rest of arguments are optional, they are used to override settings in config file - .arg( - Arg::with_name("location") - .long("location") - .help("Optional geographical location of this node") - .takes_value(true), - ) .arg( Arg::with_name("layer") .long("layer") @@ -63,6 +57,12 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> { .help("REST endpoint of the validator the node is registering presence with") .takes_value(true), ) + .arg( + Arg::with_name("mixnet-contract") + .long("mixnet-contract") + .help("Address of the validator contract managing the network") + .takes_value(true), + ) .arg( Arg::with_name("metrics-server") .long("metrics-server") @@ -176,14 +176,13 @@ pub fn execute(matches: &ArgMatches) { Sphinx key: {} Host: {} Layer: {} - Location: {} + 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_announce_address(), config.get_layer(), - config.get_location(), config.get_version(), ); diff --git a/mixnode/src/commands/unregister.rs b/mixnode/src/commands/unregister.rs deleted file mode 100644 index 4626412194..0000000000 --- a/mixnode/src/commands/unregister.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::config::{persistence::pathfinder::MixNodePathfinder, Config}; -use clap::{App, Arg, ArgMatches}; -use config::NymConfig; -use crypto::asymmetric::identity; -use log::*; -use tokio::runtime::Runtime; - -pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { - App::new("unregister").about("Unregister the mixnode").arg( - Arg::with_name("id") - .long("id") - .help("Id of the nym-mixnode we want to explicitly unregister") - .takes_value(true) - .required(true), - ) -} - -fn load_identity_keys(pathfinder: &MixNodePathfinder) -> identity::KeyPair { - pemstore::load_keypair(&pemstore::KeyPairPath::new( - pathfinder.private_identity_key().to_owned(), - pathfinder.public_identity_key().to_owned(), - )) - .expect("Failed to read stored identity key files") -} - -pub fn execute(matches: &ArgMatches) { - // TODO: this should probably be made implicit by slapping `#[tokio::main]` on our main method - // and then removing runtime from mixnode itself in `run` - let mut rt = Runtime::new().unwrap(); - rt.block_on(async { - let id = matches.value_of("id").unwrap(); - - println!("Attempting to unregister mixnode {}...", id); - - let config = match Config::load_from_file(id) { - Ok(cfg) => cfg, - Err(err) => { - error!("Failed to load config for {}. Are you sure you have run provided correct id? (Error was: {})", id, err); - return; - } - }; - - // we need to load identity keys to be able to grab node's public key - let pathfinder = MixNodePathfinder::new_from_config(&config); - let identity_keypair = load_identity_keys(&pathfinder); - - // now attempt to unregister - let validator_client_config = validator_client::Config::new(config.get_validator_rest_endpoint()); - let validator_client = validator_client::Client::new(validator_client_config); - - match validator_client.unregister_node(&*identity_keypair.public_key().to_base58_string()).await { - Err(err) => error!("failed to unregister node '{}'. Error: {:?}", id, err), - Ok(_) => info!("managed to successfully unregister node '{}'!", id) - } - }) -} diff --git a/mixnode/src/commands/upgrade.rs b/mixnode/src/commands/upgrade.rs index cb76f6b6bb..78c3988a47 100644 --- a/mixnode/src/commands/upgrade.rs +++ b/mixnode/src/commands/upgrade.rs @@ -33,7 +33,7 @@ fn print_successful_upgrade(from: D1, to: D2) { ); } -fn pre_090_upgrade(from: &str, config: Config, matches: &ArgMatches) -> Config { +fn pre_090_upgrade(from: &str, config: Config, _matches: &ArgMatches) -> Config { // note: current is guaranteed to not have any `build` information suffix (nor pre-release // information), as this was asserted at the beginning of this command) // @@ -101,11 +101,6 @@ fn pre_090_upgrade(from: &str, config: Config, matches: &ArgMatches) -> Config { .with_custom_metrics_server(DEFAULT_METRICS_SERVER) .with_custom_validator(DEFAULT_VALIDATOR_REST_ENDPOINT); - if let Some(incentives_address) = matches.value_of("incentives address") { - upgraded_config = upgraded_config.with_incentives_address(incentives_address); - println!("Setting incentives address to {}", incentives_address); - } - println!("Setting metrics server to {}", DEFAULT_METRICS_SERVER); println!( "Setting validator REST endpoint to {}", @@ -140,7 +135,7 @@ fn pre_090_upgrade(from: &str, config: Config, matches: &ArgMatches) -> Config { upgraded_config } -fn patch_09x_upgrade(config: Config, matches: &ArgMatches) -> Config { +fn patch_09x_upgrade(config: Config, _matches: &ArgMatches) -> Config { // this call must succeed as it was already called before let from_version = Version::parse(config.get_version()).unwrap(); let to_version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); @@ -148,17 +143,7 @@ fn patch_09x_upgrade(config: Config, matches: &ArgMatches) -> Config { print_start_upgrade(&from_version, &to_version); // 0.9.1 upgrade: - let mut upgraded_config = config.with_custom_version(to_version.to_string().as_ref()); - - // not strictly part of the upgrade, but since people had problems with it and I've got a feeling - // they might try to use it, just allow changing incentives address here again... - if let Some(incentives_address) = matches.value_of("incentives address") { - upgraded_config = upgraded_config.with_incentives_address(incentives_address); - println!( - "Setting incentives address to {}. Old value will be overwritten", - incentives_address - ); - } + let upgraded_config = config.with_custom_version(to_version.to_string().as_ref()); upgraded_config.save_to_file(None).unwrap_or_else(|err| { eprintln!("failed to overwrite config file! - {:?}", err); @@ -186,11 +171,6 @@ 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("incentives address") - .long("incentives-address") - .help("Optional, if participating in the incentives program, payment address") - .takes_value(true) - ) } fn unsupported_upgrade(current_version: Version, config_version: Version) -> ! { diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index dade7d5ef9..ca686b92df 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -23,6 +23,8 @@ const DEFAULT_LISTENING_PORT: u16 = 1789; pub(crate) const DEFAULT_VALIDATOR_REST_ENDPOINT: &str = "http://testnet-validator1.nymtech.net:8081"; pub(crate) const DEFAULT_METRICS_SERVER: &str = "http://testnet-metrics.nymtech.net:8080"; +pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = + "TODO: THIS NEEDS TO BE FILLED WITH SOME REASONABLE VALUE!"; // 'DEBUG' const DEFAULT_METRICS_RUNNING_STATS_LOGGING_DELAY: Duration = Duration::from_millis(60_000); @@ -121,18 +123,6 @@ where deserializer.deserialize_any(DurationVisitor) } -fn deserialize_option_string<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - if s.is_empty() { - Ok(None) - } else { - Ok(Some(s)) - } -} - pub fn missing_string_value>() -> T { MISSING_VALUE.to_string().into() } @@ -177,13 +167,13 @@ impl Config { self } - pub fn with_location>(mut self, location: S) -> Self { - self.mixnode.location = location.into(); + pub fn with_custom_validator>(mut self, validator: S) -> Self { + self.mixnode.validator_rest_url = validator.into(); self } - pub fn with_custom_validator>(mut self, validator: S) -> Self { - self.mixnode.validator_rest_url = validator.into(); + pub fn with_custom_mixnet_contract>(mut self, mixnet_contract: S) -> Self { + self.mixnode.mixnet_contract_address = mixnet_contract.into(); self } @@ -272,20 +262,11 @@ impl Config { self } - pub fn with_incentives_address>(mut self, incentives_address: S) -> Self { - self.mixnode.incentives_address = Some(incentives_address.into()); - self - } - // getters pub fn get_config_file_save_location(&self) -> PathBuf { self.config_directory().join(Self::config_file_name()) } - pub fn get_location(&self) -> String { - self.mixnode.location.clone() - } - pub fn get_private_identity_key_file(&self) -> PathBuf { self.mixnode.private_identity_key_file.clone() } @@ -306,6 +287,10 @@ impl Config { self.mixnode.validator_rest_url.clone() } + pub fn get_validator_mixnet_contract_address(&self) -> String { + self.mixnode.mixnet_contract_address.clone() + } + pub fn get_metrics_server(&self) -> String { self.mixnode.metrics_server_url.clone() } @@ -350,10 +335,6 @@ impl Config { &self.mixnode.version } - pub fn get_incentives_address(&self) -> Option { - self.mixnode.incentives_address.clone() - } - // upgrade-specific pub(crate) fn set_default_identity_keypair_paths(&mut self) { self.mixnode.private_identity_key_file = @@ -372,12 +353,6 @@ pub struct MixNode { /// ID specifies the human readable ID of this particular mixnode. id: String, - /// Completely optional value specifying geographical location of this particular node. - /// Currently it's used entirely for debug purposes, as there are no mechanisms implemented - /// to verify correctness of the information provided. However, feel free to fill in - /// this field with as much accuracy as you wish to share. - location: String, - /// Layer of this particular mixnode determining its position in the network. layer: u64, @@ -410,6 +385,10 @@ pub struct MixNode { #[serde(default = "missing_string_value")] validator_rest_url: String, + /// Address of the validator contract managing the network. + #[serde(default = "missing_string_value")] + mixnet_contract_address: String, + /// Metrics server to which the node will be reporting their metrics data. #[serde(default = "missing_string_value")] metrics_server_url: String, @@ -417,10 +396,6 @@ pub struct MixNode { /// nym_home_directory specifies absolute path to the home nym MixNodes directory. /// It is expected to use default value and hence .toml file should not redefine this field. nym_root_directory: PathBuf, - - /// Optional, if participating in the incentives program, payment address. - #[serde(deserialize_with = "deserialize_option_string", default)] - incentives_address: Option, } impl MixNode { @@ -439,10 +414,6 @@ impl MixNode { fn default_public_sphinx_key_file(id: &str) -> PathBuf { Config::default_data_directory(id).join("public_sphinx.pem") } - - fn default_location() -> String { - "unknown".into() - } } impl Default for MixNode { @@ -450,7 +421,6 @@ impl Default for MixNode { MixNode { version: env!("CARGO_PKG_VERSION").to_string(), id: "".to_string(), - location: Self::default_location(), layer: 0, listening_address: format!("0.0.0.0:{}", DEFAULT_LISTENING_PORT) .parse() @@ -461,9 +431,9 @@ impl Default for MixNode { private_sphinx_key_file: Default::default(), public_sphinx_key_file: Default::default(), validator_rest_url: DEFAULT_VALIDATOR_REST_ENDPOINT.to_string(), + mixnet_contract_address: DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string(), metrics_server_url: DEFAULT_METRICS_SERVER.to_string(), nym_root_directory: Config::default_root_directory(), - incentives_address: None, } } } diff --git a/mixnode/src/config/template.rs b/mixnode/src/config/template.rs index 853844600c..080db9b3e3 100644 --- a/mixnode/src/config/template.rs +++ b/mixnode/src/config/template.rs @@ -18,13 +18,7 @@ version = '{{ mixnode.version }}' # Human readable ID of this particular mixnode. id = '{{ mixnode.id }}' - -# Completely optional value specifying geographical location of this particular node. -# Currently it's used entirely for debug purposes, as there are no mechanisms implemented -# to verify correctness of the information provided. However, feel free to fill in -# this field with as much accuracy as you wish to share. -location = '{{ mixnode.location }}' - + # Layer of this particular mixnode determining its position in the network. layer = {{ mixnode.layer }} @@ -43,9 +37,6 @@ private_sphinx_key_file = '{{ mixnode.private_sphinx_key_file }}' # Path to file containing public sphinx key. public_sphinx_key_file = '{{ mixnode.public_sphinx_key_file }}' -# Optional, if participating in the incentives program, payment address. -incentives_address = '{{ mixnode.incentives_address }}' - ##### additional mixnode config options ##### # Optional address announced to the directory server for the clients to connect to. @@ -62,6 +53,9 @@ validator_rest_url = '{{ mixnode.validator_rest_url }}' # Metrics server to which the node will be reporting their metrics data. metrics_server_url = '{{ mixnode.metrics_server_url }}' +# Address of the validator contract managing the network. +mixnet_contract_address = '{{ mixnode.mixnet_contract_address }}' + ##### advanced configuration options ##### # Absolute path to the home Nym Clients directory. diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index 6210d5613d..e25210f9d2 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -19,7 +19,6 @@ fn main() { .subcommand(commands::init::command_args()) .subcommand(commands::run::command_args()) .subcommand(commands::upgrade::command_args()) - .subcommand(commands::unregister::command_args()) .get_matches(); execute(arg_matches); @@ -30,7 +29,6 @@ fn execute(matches: ArgMatches) { ("init", Some(m)) => commands::init::execute(m), ("run", Some(m)) => commands::run::execute(m), ("upgrade", Some(m)) => commands::upgrade::execute(m), - ("unregister", Some(m)) => commands::unregister::execute(m), _ => println!("{}", usage()), } } diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 675de269ac..bdbfaa6244 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -15,7 +15,6 @@ use tokio::runtime::Runtime; mod listener; mod metrics; pub(crate) mod packet_delayforwarder; -mod presence; // the MixNode will live for whole duration of this program pub struct MixNode { @@ -87,23 +86,26 @@ impl MixNode { packet_sender } + // TODO: ask DH whether this function still makes sense in ^0.10 async fn check_if_same_ip_node_exists(&mut self) -> Option { - let validator_client_config = - validator_client::Config::new(self.config.get_validator_rest_endpoint()); - let validator_client = validator_client::Client::new(validator_client_config); - let topology = match validator_client.get_topology().await { - Ok(topology) => topology, + let validator_client_config = validator_client_rest::Config::new( + self.config.get_validator_rest_endpoint(), + self.config.get_validator_mixnet_contract_address(), + ); + let validator_client = validator_client_rest::Client::new(validator_client_config); + + let existing_nodes = match validator_client.get_mix_nodes().await { + Ok(nodes) => nodes, Err(err) => { - error!("failed to grab initial network topology - {}\n Please try to startup again in few minutes", err); + error!("failed to grab initial network mixnodes - {}\n Please try to startup again in few minutes", err); process::exit(1); } }; - let existing_mixes_presence = topology.mix_nodes; - existing_mixes_presence + existing_nodes .iter() - .find(|node| node.mix_host() == self.config.get_announce_address()) - .map(|node| node.identity()) + .find(|node| node.mix_node.host == self.config.get_announce_address()) + .map(|node| node.mix_node.identity_key.clone()) } async fn wait_for_interrupt(&self) { @@ -116,17 +118,6 @@ impl MixNode { println!( "Received SIGINT - the mixnode will terminate now (threads are not yet nicely stopped, if you see stack traces that's alright)." ); - info!("Trying to unregister with the validator..."); - if let Err(err) = presence::unregister_with_validator( - self.config.get_validator_rest_endpoint(), - self.identity_keypair.public_key().to_base58_string(), - ) - .await - { - error!("failed to unregister with validator... - {}", err) - } else { - info!("unregistration was successful!") - } } pub fn run(&mut self) { @@ -147,15 +138,6 @@ impl MixNode { } } - if let Err(err) = presence::register_with_validator( - &self.config, - self.identity_keypair.public_key().to_base58_string(), - self.sphinx_keypair.public_key().to_base58_string(), - ).await { - error!("failed to register with the validator - {}.\nPlease try again in few minutes.", err); - return; - } - let metrics_reporter = self.start_metrics_reporter(); let delay_forwarding_channel = self.start_packet_delay_forwarder(metrics_reporter.clone()); self.start_socket_listener(metrics_reporter, delay_forwarding_channel); diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs deleted file mode 100644 index 4be4758aaa..0000000000 --- a/mixnode/src/node/presence.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2020 - Nym Technologies SA -// SPDX-License-Identifier: Apache-2.0 - -use crate::config::Config; -use validator_client::models::mixnode::MixRegistrationInfo; -use validator_client::ValidatorClientError; - -// there's no point in keeping the validator client persistently as it might be literally hours or days -// before it's used again -pub(crate) async fn register_with_validator( - mixnode_config: &Config, - identity_key: String, - sphinx_key: String, -) -> Result<(), ValidatorClientError> { - let config = validator_client::Config::new(mixnode_config.get_validator_rest_endpoint()); - let validator_client = validator_client::Client::new(config); - - let registration_info = MixRegistrationInfo::new( - mixnode_config.get_announce_address(), - identity_key, - sphinx_key, - mixnode_config.get_version().to_string(), - mixnode_config.get_location(), - mixnode_config.get_layer(), - mixnode_config.get_incentives_address(), - ); - - validator_client.register_mix(registration_info).await -} - -pub(crate) async fn unregister_with_validator( - validator_endpoint: String, - identity_key: String, -) -> Result<(), ValidatorClientError> { - let config = validator_client::Config::new(validator_endpoint); - let validator_client = validator_client::Client::new(config); - - validator_client.unregister_node(&identity_key).await -}