Feature/wasm topology duplication (#265)
* Removed constructor from NymTopology trait + split directory_client * Removed bunch of duplicate wasm topology code * Post-merge changes
This commit is contained in:
committed by
GitHub
parent
b57a548350
commit
fcfe463efa
Generated
+10
-2
@@ -634,6 +634,7 @@ dependencies = [
|
||||
name = "directory-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"directory-client-models",
|
||||
"futures 0.3.4",
|
||||
"log 0.4.8",
|
||||
"mockito",
|
||||
@@ -641,6 +642,13 @@ dependencies = [
|
||||
"reqwest 0.10.4",
|
||||
"serde",
|
||||
"tokio 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "directory-client-models"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"topology",
|
||||
]
|
||||
|
||||
@@ -1761,10 +1769,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym-client-wasm"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"crypto",
|
||||
"directory-client-models",
|
||||
"log 0.4.8",
|
||||
"nymsphinx",
|
||||
"rand 0.7.3",
|
||||
@@ -3271,7 +3280,6 @@ name = "topology"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"futures 0.3.4",
|
||||
"itertools",
|
||||
"log 0.4.8",
|
||||
"nymsphinx-addressing",
|
||||
|
||||
@@ -8,6 +8,7 @@ members = [
|
||||
"clients/native",
|
||||
"clients/webassembly",
|
||||
"common/client-libs/directory-client",
|
||||
"common/client-libs/directory-client/models",
|
||||
"common/client-libs/gateway-client",
|
||||
"common/client-libs/mixnet-client",
|
||||
"common/client-libs/validator-client",
|
||||
|
||||
@@ -25,7 +25,6 @@ use crate::client::topology_control::{
|
||||
use crate::config::{Config, SocketType};
|
||||
use crate::websocket;
|
||||
use crypto::identity::MixIdentityKeyPair;
|
||||
use directory_client::presence;
|
||||
use futures::channel::mpsc;
|
||||
use gateway_client::{
|
||||
AcknowledgementReceiver, AcknowledgementSender, GatewayClient, MixnetMessageReceiver,
|
||||
@@ -221,16 +220,16 @@ impl NymClient {
|
||||
|
||||
// future responsible for periodically polling directory server and updating
|
||||
// the current global view of topology
|
||||
fn start_topology_refresher<T: 'static + NymTopology>(
|
||||
fn start_topology_refresher(
|
||||
&mut self,
|
||||
topology_accessor: TopologyAccessor<T>,
|
||||
topology_accessor: TopologyAccessor<directory_client::Topology>,
|
||||
) {
|
||||
let topology_refresher_config = TopologyRefresherConfig::new(
|
||||
self.config.get_directory_server(),
|
||||
self.config.get_topology_refresh_rate(),
|
||||
);
|
||||
let mut topology_refresher =
|
||||
TopologyRefresher::new(topology_refresher_config, topology_accessor);
|
||||
TopologyRefresher::new_directory_client(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!(
|
||||
@@ -354,9 +353,8 @@ impl NymClient {
|
||||
|
||||
// channels responsible for controlling ack messages
|
||||
let (ack_sender, ack_receiver) = mpsc::unbounded();
|
||||
let shared_topology_accessor = TopologyAccessor::<directory_client::Topology>::new();
|
||||
|
||||
// TODO: when we switch to our graph topology, we need to remember to change 'presence::Topology' type
|
||||
let shared_topology_accessor = TopologyAccessor::<presence::Topology>::new();
|
||||
// the components are started in very specific order. Unless you know what you are doing,
|
||||
// do not change that.
|
||||
self.start_topology_refresher(shared_topology_accessor.clone());
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::built_info;
|
||||
use directory_client::DirectoryClient;
|
||||
use log::*;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use std::ops::Deref;
|
||||
@@ -167,34 +168,40 @@ impl TopologyRefresherConfig {
|
||||
}
|
||||
|
||||
pub(crate) struct TopologyRefresher<T: NymTopology> {
|
||||
directory_server: String,
|
||||
directory_client: directory_client::Client,
|
||||
topology_accessor: TopologyAccessor<T>,
|
||||
refresh_rate: Duration,
|
||||
}
|
||||
|
||||
impl<T: 'static + NymTopology> TopologyRefresher<T> {
|
||||
pub(crate) fn new(
|
||||
// TODO: consider (or maybe not) restoring generic TopologyRefresher<T>
|
||||
impl TopologyRefresher<directory_client::Topology> {
|
||||
pub(crate) fn new_directory_client(
|
||||
cfg: TopologyRefresherConfig,
|
||||
topology_accessor: TopologyAccessor<T>,
|
||||
topology_accessor: TopologyAccessor<directory_client::Topology>,
|
||||
) -> Self {
|
||||
let directory_client_config = directory_client::Config::new(cfg.directory_server);
|
||||
let directory_client = directory_client::Client::new(directory_client_config);
|
||||
|
||||
TopologyRefresher {
|
||||
directory_server: cfg.directory_server,
|
||||
directory_client,
|
||||
topology_accessor,
|
||||
refresh_rate: cfg.refresh_rate,
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_current_compatible_topology(&self) -> T {
|
||||
// note: this call makes it necessary that `T::new()`does *not* have 'static lifetime
|
||||
let full_topology = T::new(self.directory_server.clone()).await;
|
||||
// just filter by version and assume the validators will remove all bad behaving
|
||||
// nodes with the staking
|
||||
full_topology.filter_system_version(built_info::PKG_VERSION)
|
||||
async fn get_current_compatible_topology(&self) -> Option<directory_client::Topology> {
|
||||
match self.directory_client.get_topology().await {
|
||||
Err(err) => {
|
||||
error!("failed to get network topology! - {:?}", err);
|
||||
None
|
||||
}
|
||||
Ok(topology) => Some(topology.filter_system_version(built_info::PKG_VERSION)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn refresh(&mut self) {
|
||||
trace!("Refreshing the topology");
|
||||
let new_topology = Some(self.get_current_compatible_topology().await);
|
||||
let new_topology = self.get_current_compatible_topology().await;
|
||||
|
||||
self.topology_accessor
|
||||
.update_global_topology(new_topology)
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::config::persistence::pathfinder::ClientPathfinder;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use config::NymConfig;
|
||||
use crypto::identity::MixIdentityKeyPair;
|
||||
use directory_client::presence::Topology;
|
||||
use directory_client::DirectoryClient;
|
||||
use gateway_client::GatewayClient;
|
||||
use gateway_requests::AuthToken;
|
||||
use nymsphinx::DestinationAddressBytes;
|
||||
@@ -90,8 +90,10 @@ async fn choose_gateway(
|
||||
directory_server: String,
|
||||
our_address: DestinationAddressBytes,
|
||||
) -> (String, AuthToken) {
|
||||
// TODO: once we change to graph topology this here will need to be updated!
|
||||
let topology = Topology::new(directory_server.clone()).await;
|
||||
let directory_client_config = directory_client::Config::new(directory_server.clone());
|
||||
let directory_client = directory_client::Client::new(directory_client_config);
|
||||
let topology = directory_client.get_topology().await.unwrap();
|
||||
|
||||
let version_filtered_topology = topology.filter_system_version(built_info::PKG_VERSION);
|
||||
// don't care about health of the networks as mixes can go up and down any time,
|
||||
// but DO care about gateways
|
||||
|
||||
@@ -26,6 +26,7 @@ rand = "0.7.2"
|
||||
crypto = { path = "../../common/crypto" }
|
||||
nymsphinx = { path = "../../common/nymsphinx" }
|
||||
topology = { path = "../../common/topology" }
|
||||
directory-client-models = { path = "../../common/client-libs/directory-client/models" }
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use crypto::identity::MixIdentityPublicKey;
|
||||
use models::Topology;
|
||||
use models::topology::Topology;
|
||||
use nymsphinx::addressing::nodes::NymNodeRoutingAddress;
|
||||
use nymsphinx::Node as SphinxNode;
|
||||
use nymsphinx::{delays, Destination, NodeAddressBytes, SphinxPacket};
|
||||
@@ -28,6 +28,7 @@ mod utils;
|
||||
|
||||
pub use models::keys::keygen;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use topology::NymTopology;
|
||||
|
||||
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||||
// allocator.
|
||||
@@ -198,8 +199,8 @@ mod building_a_topology_from_json {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn panics_when_there_are_no_mixnodes() {
|
||||
let mut topology: Topology = serde_json::from_str(topology_fixture()).unwrap();
|
||||
topology.mix_nodes = vec![];
|
||||
let mut topology = Topology::new(topology_fixture());
|
||||
topology.set_mixnodes(vec![]);
|
||||
let json = serde_json::to_string(&topology).unwrap();
|
||||
sphinx_route_to(
|
||||
&json,
|
||||
@@ -213,9 +214,9 @@ mod building_a_topology_from_json {
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn panics_when_there_are_not_enough_mixnodes() {
|
||||
let mut topology: Topology = serde_json::from_str(topology_fixture()).unwrap();
|
||||
let node = topology.mix_nodes.first().unwrap().clone();
|
||||
topology.mix_nodes = vec![node]; // 1 mixnode isn't enough. Panic!
|
||||
let mut topology = Topology::new(topology_fixture());
|
||||
let node = topology.get_current_raw_mixnodes().first().unwrap().clone();
|
||||
topology.set_mixnodes(vec![node]); // 1 mixnode isn't enough. Panic!
|
||||
let json = serde_json::to_string(&topology).unwrap();
|
||||
sphinx_route_to(
|
||||
&json,
|
||||
@@ -226,6 +227,7 @@ mod building_a_topology_from_json {
|
||||
);
|
||||
}
|
||||
|
||||
// JS: why is this an "offline-test" feature? It makes no network requests?
|
||||
#[test]
|
||||
#[cfg_attr(feature = "offline-test", ignore)]
|
||||
fn test_works_on_happy_json() {
|
||||
@@ -238,6 +240,20 @@ mod building_a_topology_from_json {
|
||||
);
|
||||
assert_eq!(4, route.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_works_on_happy_json_when_serialized() {
|
||||
let topology = Topology::new(topology_fixture());
|
||||
let json = serde_json::to_string(&topology).unwrap();
|
||||
let route = sphinx_route_to(
|
||||
&json,
|
||||
&NodeAddressBytes::try_from_base58_string(
|
||||
"7vhgER4Gz789QHNTSu4apMpTcpTuUaRiLxJnbz1g2HFh",
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
assert_eq!(4, route.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crypto::identity::MixIdentityKeyPair;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
@@ -12,176 +12,5 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use nymsphinx::Node as SphinxNode;
|
||||
use nymsphinx::NodeAddressBytes;
|
||||
use rand::seq::IteratorRandom;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use topology::{gateway, mix, provider};
|
||||
|
||||
pub mod coconodes;
|
||||
pub mod gateways;
|
||||
pub mod keys;
|
||||
pub mod mixnodes;
|
||||
pub mod providers;
|
||||
|
||||
// JS: can we just get rid of this? It's mostly just copied (and un-updated) code from NymTopology trait
|
||||
// Topology shows us the current state of the overall Nym network
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Topology {
|
||||
pub coco_nodes: Vec<coconodes::CocoPresence>,
|
||||
pub mix_nodes: Vec<mixnodes::MixNodePresence>,
|
||||
pub mix_provider_nodes: Vec<providers::MixProviderPresence>,
|
||||
pub gateway_nodes: Vec<gateways::GatewayPresence>,
|
||||
}
|
||||
|
||||
impl Topology {
|
||||
pub fn new(json: &str) -> Self {
|
||||
if json.is_empty() {
|
||||
panic!("empty json passed");
|
||||
}
|
||||
serde_json::from_str(json).unwrap()
|
||||
}
|
||||
|
||||
fn make_layered_topology(&self) -> Result<HashMap<u64, Vec<mix::Node>>, NymTopologyError> {
|
||||
let mut layered_topology: HashMap<u64, Vec<mix::Node>> = HashMap::new();
|
||||
let mut highest_layer = 0;
|
||||
for mix in self.mix_nodes() {
|
||||
// we need to have extra space for provider
|
||||
if mix.layer > nymsphinx::MAX_PATH_LENGTH as u64 {
|
||||
return Err(NymTopologyError::InvalidMixLayerError);
|
||||
}
|
||||
highest_layer = max(highest_layer, mix.layer);
|
||||
|
||||
let layer_nodes = layered_topology.entry(mix.layer).or_insert_with(Vec::new);
|
||||
layer_nodes.push(mix);
|
||||
}
|
||||
|
||||
// verify the topology - make sure there are no gaps and there is at least one node per layer
|
||||
let mut missing_layers = Vec::new();
|
||||
for layer in 1..=highest_layer {
|
||||
if !layered_topology.contains_key(&layer) {
|
||||
missing_layers.push(layer);
|
||||
}
|
||||
if layered_topology[&layer].is_empty() {
|
||||
missing_layers.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
if !missing_layers.is_empty() {
|
||||
return Err(NymTopologyError::MissingLayerError(missing_layers));
|
||||
}
|
||||
|
||||
Ok(layered_topology)
|
||||
}
|
||||
|
||||
// Tries to get a route through the mix network
|
||||
fn random_mix_route(&self) -> Result<Vec<SphinxNode>, NymTopologyError> {
|
||||
let mut layered_topology = self.make_layered_topology()?;
|
||||
let num_layers = layered_topology.len();
|
||||
let route = (1..=num_layers as u64)
|
||||
// unwrap is safe for 'remove' as it it failed, it implied the entry never existed
|
||||
// in the map in the first place which would contradict what we've just done
|
||||
.map(|layer| layered_topology.remove(&layer).unwrap()) // for each layer
|
||||
.map(|nodes| nodes.into_iter().choose(&mut rand::thread_rng()).unwrap()) // choose random node
|
||||
.map(|random_node| random_node.into()) // and convert it into sphinx specific node format
|
||||
.collect();
|
||||
|
||||
Ok(route)
|
||||
}
|
||||
|
||||
// Sets up a route to a specific gateway
|
||||
pub fn random_route_to_gateway(
|
||||
&self,
|
||||
gateway_address: &NodeAddressBytes,
|
||||
) -> Result<Vec<SphinxNode>, NymTopologyError> {
|
||||
let b58_address = gateway_address.to_base58_string();
|
||||
let gateway_node = self
|
||||
.gateways()
|
||||
.iter()
|
||||
.cloned()
|
||||
.find(|gateway| gateway.pub_key == b58_address.clone())
|
||||
.ok_or_else(|| NymTopologyError::InvalidMixLayerError)?;
|
||||
|
||||
Ok(self
|
||||
.random_mix_route()?
|
||||
.into_iter()
|
||||
.chain(std::iter::once(gateway_node.into()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn mix_nodes(&self) -> Vec<mix::Node> {
|
||||
self.mix_nodes
|
||||
.iter()
|
||||
.filter_map(|x| x.clone().try_into().ok())
|
||||
.collect()
|
||||
}
|
||||
|
||||
// it's intesting how compiler knows this function is unused even though it's public
|
||||
// but it will be removed once our topology is refactored and the concept of "provider"
|
||||
// goes away.
|
||||
#[allow(dead_code)]
|
||||
pub fn providers(&self) -> Vec<provider::Node> {
|
||||
self.mix_provider_nodes
|
||||
.iter()
|
||||
.map(|x| x.clone().into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn gateways(&self) -> Vec<gateway::Node> {
|
||||
self.gateway_nodes
|
||||
.iter()
|
||||
.map(|x| x.clone().into())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NymTopologyError {
|
||||
InvalidMixLayerError,
|
||||
MissingLayerError(Vec<u64>),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod converting_mixnode_presence_into_topology_mixnode {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_returns_error_on_unresolvable_hostname() {
|
||||
let unresolvable_hostname = "foomp.foomp.foomp:1234";
|
||||
|
||||
let mix_presence = mixnodes::MixNodePresence {
|
||||
location: "".to_string(),
|
||||
host: unresolvable_hostname.to_string(),
|
||||
pub_key: "".to_string(),
|
||||
layer: 0,
|
||||
last_seen: 0,
|
||||
version: "".to_string(),
|
||||
};
|
||||
|
||||
let _result: Result<mix::Node, std::io::Error> = mix_presence.try_into();
|
||||
// assert!(result.is_err()) // This fails only for me. Why?
|
||||
// ¯\_(ツ)_/¯ - works on my machine (and travis)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature = "offline-test", ignore)]
|
||||
fn it_returns_resolved_ip_on_resolvable_hostname() {
|
||||
let resolvable_hostname = "nymtech.net:1234";
|
||||
|
||||
let mix_presence = mixnodes::MixNodePresence {
|
||||
location: "".to_string(),
|
||||
host: resolvable_hostname.to_string(),
|
||||
pub_key: "".to_string(),
|
||||
layer: 0,
|
||||
last_seen: 0,
|
||||
version: "".to_string(),
|
||||
};
|
||||
|
||||
let result: Result<topology::mix::Node, std::io::Error> = mix_presence.try_into();
|
||||
assert!(result.is_ok())
|
||||
}
|
||||
}
|
||||
pub mod topology;
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use serde::Serializer;
|
||||
use topology::{coco, gateway, mix, provider, NymTopology};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Topology {
|
||||
inner: directory_client_models::presence::Topology,
|
||||
}
|
||||
|
||||
impl serde::Serialize for Topology {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.inner.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Topology {
|
||||
pub fn new(json: &str) -> Self {
|
||||
if json.is_empty() {
|
||||
panic!("empty json passed");
|
||||
}
|
||||
|
||||
Topology {
|
||||
inner: serde_json::from_str(json).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn set_mixnodes(
|
||||
&mut self,
|
||||
mix_nodes: Vec<directory_client_models::presence::mixnodes::MixNodePresence>,
|
||||
) {
|
||||
self.inner.mix_nodes = mix_nodes
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn get_current_raw_mixnodes(
|
||||
&self,
|
||||
) -> Vec<directory_client_models::presence::mixnodes::MixNodePresence> {
|
||||
self.inner.mix_nodes.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl NymTopology for Topology {
|
||||
fn new_from_nodes(
|
||||
mix_nodes: Vec<mix::Node>,
|
||||
mix_provider_nodes: Vec<provider::Node>,
|
||||
coco_nodes: Vec<coco::Node>,
|
||||
gateway_nodes: Vec<gateway::Node>,
|
||||
) -> Self {
|
||||
Topology {
|
||||
inner: directory_client_models::presence::Topology::new_from_nodes(
|
||||
mix_nodes,
|
||||
mix_provider_nodes,
|
||||
coco_nodes,
|
||||
gateway_nodes,
|
||||
),
|
||||
}
|
||||
}
|
||||
fn mix_nodes(&self) -> Vec<mix::Node> {
|
||||
self.inner.mix_nodes()
|
||||
}
|
||||
|
||||
fn providers(&self) -> Vec<provider::Node> {
|
||||
self.inner.providers()
|
||||
}
|
||||
|
||||
fn gateways(&self) -> Vec<gateway::Node> {
|
||||
self.inner.gateways()
|
||||
}
|
||||
|
||||
fn coco_nodes(&self) -> Vec<topology::coco::Node> {
|
||||
self.inner.coco_nodes()
|
||||
}
|
||||
}
|
||||
@@ -13,11 +13,11 @@ offline-test = []
|
||||
log = "0.4"
|
||||
futures = "0.3"
|
||||
pretty_env_logger = "0.3"
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
||||
serde = { version = "1.0.104", features = ["derive"] }
|
||||
reqwest = { version = "0.10", features = ["json"] }
|
||||
|
||||
## internal
|
||||
topology = {path = "../../topology"}
|
||||
directory-client-models = { path = "models" }
|
||||
|
||||
[dev-dependencies]
|
||||
mockito = "0.23.0"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "directory-client-models"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.104", features = ["derive"] }
|
||||
|
||||
topology = { path = "../../../topology" }
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub mod metrics;
|
||||
pub mod presence;
|
||||
+1
-21
@@ -13,12 +13,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
use super::{coconodes, gateways, mixnodes, providers};
|
||||
use crate::{Client, Config, DirectoryClient};
|
||||
use futures::future::BoxFuture;
|
||||
use log::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use topology::{coco, gateway, mix, provider, NymTopology};
|
||||
|
||||
// Topology shows us the current state of the overall Nym network
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -30,24 +28,6 @@ pub struct Topology {
|
||||
}
|
||||
|
||||
impl NymTopology for Topology {
|
||||
// TODO: this will need some changes to not imply having to make an HTTP request in constructor
|
||||
// TODO2: and also be reworked/removed with the topology re-work
|
||||
fn new<'a>(directory_server: String) -> BoxFuture<'a, Self> {
|
||||
debug!("Using directory server: {:?}", directory_server);
|
||||
let directory_config = Config {
|
||||
base_url: directory_server,
|
||||
};
|
||||
let directory = Client::new(directory_config);
|
||||
Box::pin({
|
||||
async move {
|
||||
directory
|
||||
.get_topology()
|
||||
.await
|
||||
.expect("Failed to retrieve network topology.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn new_from_nodes(
|
||||
mix_nodes: Vec<mix::Node>,
|
||||
mix_provider_nodes: Vec<provider::Node>,
|
||||
@@ -20,16 +20,18 @@ use crate::requests::presence_gateways_post::Request as PresenceGatewayPost;
|
||||
use crate::requests::presence_mixnodes_post::Request as PresenceMixNodesPost;
|
||||
use crate::requests::presence_providers_post::Request as PresenceProvidersPost;
|
||||
use crate::requests::presence_topology_get::Request as PresenceTopologyRequest;
|
||||
|
||||
use metrics::{MixMetric, PersistedMixMetric};
|
||||
use presence::{
|
||||
use directory_client_models::metrics::{MixMetric, PersistedMixMetric};
|
||||
use directory_client_models::presence::{
|
||||
coconodes::CocoPresence, gateways::GatewayPresence, mixnodes::MixNodePresence,
|
||||
providers::MixProviderPresence, Topology,
|
||||
providers::MixProviderPresence,
|
||||
};
|
||||
use requests::{health_check_get::HealthCheckResponse, DirectoryGetRequest, DirectoryPostRequest};
|
||||
|
||||
pub mod metrics;
|
||||
pub mod presence;
|
||||
pub use directory_client_models::{
|
||||
metrics,
|
||||
presence::{self, Topology},
|
||||
};
|
||||
|
||||
pub mod requests;
|
||||
|
||||
pub struct Config {
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use topology::coco;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CocoPresence {
|
||||
pub location: String,
|
||||
pub host: String,
|
||||
pub pub_key: String,
|
||||
pub last_seen: u64,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Into<topology::coco::Node> for CocoPresence {
|
||||
fn into(self) -> topology::coco::Node {
|
||||
topology::coco::Node {
|
||||
location: self.location,
|
||||
host: self.host,
|
||||
pub_key: self.pub_key,
|
||||
last_seen: self.last_seen,
|
||||
version: self.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<topology::coco::Node> for CocoPresence {
|
||||
fn from(cn: coco::Node) -> Self {
|
||||
CocoPresence {
|
||||
location: cn.location,
|
||||
host: cn.host,
|
||||
pub_key: cn.pub_key,
|
||||
last_seen: cn.last_seen,
|
||||
version: cn.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use topology::gateway;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GatewayPresence {
|
||||
pub location: String,
|
||||
pub client_listener: String,
|
||||
pub mixnet_listener: String,
|
||||
pub pub_key: String,
|
||||
pub registered_clients: Vec<GatewayClient>,
|
||||
pub last_seen: u64,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Into<topology::gateway::Node> for GatewayPresence {
|
||||
fn into(self) -> topology::gateway::Node {
|
||||
topology::gateway::Node {
|
||||
location: self.location,
|
||||
client_listener: self.client_listener.parse().unwrap(),
|
||||
mixnet_listener: self.mixnet_listener.parse().unwrap(),
|
||||
pub_key: self.pub_key,
|
||||
registered_clients: self
|
||||
.registered_clients
|
||||
.into_iter()
|
||||
.map(|c| c.into())
|
||||
.collect(),
|
||||
last_seen: self.last_seen,
|
||||
version: self.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<topology::gateway::Node> for GatewayPresence {
|
||||
fn from(mpn: gateway::Node) -> Self {
|
||||
GatewayPresence {
|
||||
location: mpn.location,
|
||||
client_listener: mpn.client_listener.to_string(),
|
||||
mixnet_listener: mpn.mixnet_listener.to_string(),
|
||||
pub_key: mpn.pub_key,
|
||||
registered_clients: mpn
|
||||
.registered_clients
|
||||
.into_iter()
|
||||
.map(|c| c.into())
|
||||
.collect(),
|
||||
last_seen: mpn.last_seen,
|
||||
version: mpn.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GatewayClient {
|
||||
pub pub_key: String,
|
||||
}
|
||||
|
||||
impl Into<topology::gateway::Client> for GatewayClient {
|
||||
fn into(self) -> topology::gateway::Client {
|
||||
topology::gateway::Client {
|
||||
pub_key: self.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<topology::gateway::Client> for GatewayClient {
|
||||
fn from(mpc: topology::gateway::Client) -> Self {
|
||||
GatewayClient {
|
||||
pub_key: mpc.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use std::net::ToSocketAddrs;
|
||||
use topology::mix;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MixNodePresence {
|
||||
pub location: String,
|
||||
pub host: String,
|
||||
pub pub_key: String,
|
||||
pub layer: u64,
|
||||
pub last_seen: u64,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl TryInto<topology::mix::Node> for MixNodePresence {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_into(self) -> Result<topology::mix::Node, Self::Error> {
|
||||
let resolved_hostname = self.host.to_socket_addrs()?.next();
|
||||
if resolved_hostname.is_none() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"no valid socket address",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(topology::mix::Node {
|
||||
location: self.location,
|
||||
host: resolved_hostname.unwrap(),
|
||||
pub_key: self.pub_key,
|
||||
layer: self.layer,
|
||||
last_seen: self.last_seen,
|
||||
version: self.version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<topology::mix::Node> for MixNodePresence {
|
||||
fn from(mn: mix::Node) -> Self {
|
||||
MixNodePresence {
|
||||
location: mn.location,
|
||||
host: mn.host.to_string(),
|
||||
pub_key: mn.pub_key,
|
||||
layer: mn.layer,
|
||||
last_seen: mn.last_seen,
|
||||
version: mn.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2020 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use topology::provider;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MixProviderPresence {
|
||||
pub location: String,
|
||||
pub client_listener: String,
|
||||
pub mixnet_listener: String,
|
||||
pub pub_key: String,
|
||||
pub registered_clients: Vec<MixProviderClient>,
|
||||
pub last_seen: u64,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Into<topology::provider::Node> for MixProviderPresence {
|
||||
fn into(self) -> topology::provider::Node {
|
||||
topology::provider::Node {
|
||||
location: self.location,
|
||||
client_listener: self.client_listener.parse().unwrap(),
|
||||
mixnet_listener: self.mixnet_listener.parse().unwrap(),
|
||||
pub_key: self.pub_key,
|
||||
registered_clients: self
|
||||
.registered_clients
|
||||
.into_iter()
|
||||
.map(|c| c.into())
|
||||
.collect(),
|
||||
last_seen: self.last_seen,
|
||||
version: self.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<topology::provider::Node> for MixProviderPresence {
|
||||
fn from(mpn: provider::Node) -> Self {
|
||||
MixProviderPresence {
|
||||
location: mpn.location,
|
||||
client_listener: mpn.client_listener.to_string(),
|
||||
mixnet_listener: mpn.mixnet_listener.to_string(),
|
||||
pub_key: mpn.pub_key,
|
||||
registered_clients: mpn
|
||||
.registered_clients
|
||||
.into_iter()
|
||||
.map(|c| c.into())
|
||||
.collect(),
|
||||
last_seen: mpn.last_seen,
|
||||
version: mpn.version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MixProviderClient {
|
||||
pub pub_key: String,
|
||||
}
|
||||
|
||||
impl Into<topology::provider::Client> for MixProviderClient {
|
||||
fn into(self) -> topology::provider::Client {
|
||||
topology::provider::Client {
|
||||
pub_key: self.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<topology::provider::Client> for MixProviderClient {
|
||||
fn from(mpc: topology::provider::Client) -> Self {
|
||||
MixProviderClient {
|
||||
pub_key: mpc.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ edition = "2018"
|
||||
[dependencies]
|
||||
bs58 = "0.3.0"
|
||||
itertools = "0.8.2"
|
||||
futures = "0.3"
|
||||
log = "0.4"
|
||||
pretty_env_logger = "0.3"
|
||||
rand = "0.7.2"
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::filter::VersionFilterable;
|
||||
use futures::future::BoxFuture;
|
||||
use itertools::Itertools;
|
||||
use nymsphinx_types::{Node as SphinxNode, NodeAddressBytes};
|
||||
use rand::seq::IteratorRandom;
|
||||
@@ -29,10 +28,6 @@ pub mod provider;
|
||||
// TODO: Figure out why 'Clone' was required to have 'TopologyAccessor<T>' working
|
||||
// even though it only contains an Arc
|
||||
pub trait NymTopology: Sized + std::fmt::Debug + Send + Sync + Clone {
|
||||
// this is just a temporary work-around to make current code work without having to
|
||||
// do major topology rewrites now.
|
||||
// This will be removed once topology is re-worked
|
||||
fn new<'a>(directory_server: String) -> BoxFuture<'a, Self>;
|
||||
fn new_from_nodes(
|
||||
mix_nodes: Vec<mix::Node>,
|
||||
mix_provider_nodes: Vec<provider::Node>,
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
use crate::config::Config;
|
||||
use crate::node::packet_processing::PacketProcessor;
|
||||
use crypto::encryption;
|
||||
use directory_client::presence::Topology;
|
||||
use directory_client::DirectoryClient;
|
||||
use futures::channel::mpsc;
|
||||
use log::*;
|
||||
use nymsphinx::SphinxPacket;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::runtime::Runtime;
|
||||
use topology::NymTopology;
|
||||
|
||||
mod listener;
|
||||
mod metrics;
|
||||
@@ -102,10 +101,13 @@ impl MixNode {
|
||||
}
|
||||
|
||||
fn check_if_same_ip_node_exists(&mut self) -> Option<String> {
|
||||
// TODO: once we change to graph topology this here will need to be updated!
|
||||
let directory_client_config =
|
||||
directory_client::Config::new(self.config.get_presence_directory_server());
|
||||
let directory_client = directory_client::Client::new(directory_client_config);
|
||||
let topology = self
|
||||
.runtime
|
||||
.block_on(Topology::new(self.config.get_presence_directory_server()));
|
||||
.block_on(directory_client.get_topology())
|
||||
.ok()?;
|
||||
let existing_mixes_presence = topology.mix_nodes;
|
||||
existing_mixes_presence
|
||||
.iter()
|
||||
|
||||
Reference in New Issue
Block a user