diff --git a/Cargo.lock b/Cargo.lock index 7628d91e43..b3780d799b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,6 +449,7 @@ version = "0.1.0" dependencies = [ "reqwest 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "topology 0.1.0", ] [[package]] @@ -2056,7 +2057,6 @@ dependencies = [ "addressing 0.1.0", "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "curve25519-dalek 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "directory_client 0.1.0", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/client/directory_client/Cargo.toml b/client/directory_client/Cargo.toml index 43750353a9..c9c9fa886d 100644 --- a/client/directory_client/Cargo.toml +++ b/client/directory_client/Cargo.toml @@ -8,4 +8,7 @@ edition = "2018" [dependencies] reqwest = "0.9.22" -serde = { version = "1.0.104", features = ["derive"] } \ No newline at end of file +serde = { version = "1.0.104", features = ["derive"] } + +## internal +topology = {path = "../../common/topology"} \ No newline at end of file diff --git a/client/directory_client/src/presence.rs b/client/directory_client/src/presence.rs index c251a3d96c..5aa9cfe8de 100644 --- a/client/directory_client/src/presence.rs +++ b/client/directory_client/src/presence.rs @@ -1,11 +1,24 @@ +use crate::requests::presence_topology_get::PresenceTopologyGetRequester; +use crate::{Client, Config, DirectoryClient}; use serde::{Deserialize, Serialize}; +use topology::NymTopology; #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct CocoPresence { pub host: String, pub pub_key: String, - pub last_seen: i64, + pub last_seen: u64, +} + +impl Into for CocoPresence { + fn into(self) -> topology::CocoNode { + topology::CocoNode { + host: self.host, + pub_key: self.pub_key, + last_seen: self.last_seen, + } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -18,21 +31,54 @@ pub struct MixNodePresence { pub version: String, } +impl Into for MixNodePresence { + fn into(self) -> topology::MixNode { + topology::MixNode { + host: self.host, + pub_key: self.pub_key, + layer: self.layer, + last_seen: self.last_seen, + version: self.version, + } + } +} + #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct MixProviderPresence { pub host: String, pub pub_key: String, pub registered_clients: Vec, + pub last_seen: u64, pub version: String, } +impl Into for MixProviderPresence { + fn into(self) -> topology::MixProviderNode { + topology::MixProviderNode { + host: self.host, + 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, + } + } +} + #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub struct MixProviderClient { pub pub_key: String, } +impl Into for MixProviderClient { + fn into(self) -> topology::MixProviderClient { + topology::MixProviderClient { + pub_key: self.pub_key, + } + } +} + // Topology shows us the current state of the overall Nym network #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] @@ -41,3 +87,31 @@ pub struct Topology { pub mix_nodes: Vec, pub mix_provider_nodes: Vec, } + +impl NymTopology for Topology { + fn new(directory_server: String) -> Self { + println!("Using directory server: {:?}", directory_server); + let directory_config = Config { + base_url: directory_server, + }; + let directory = Client::new(directory_config); + + let topology = directory + .presence_topology + .get() + .expect("Failed to retrieve network topology."); + topology + } + + fn get_mix_nodes(&self) -> Vec { + self.mix_nodes.iter().map(|x| x.clone().into()).collect() + } + + fn get_mix_provider_nodes(&self) -> Vec { + self.mix_provider_nodes.iter().map(|x| x.clone().into()).collect() + } + + fn get_coco_nodes(&self) -> Vec { + self.coco_nodes.iter().map(|x| x.clone().into()).collect() + } +} diff --git a/client/src/clients/mod.rs b/client/src/clients/mod.rs index 6e8f907a84..f466e6875c 100644 --- a/client/src/clients/mod.rs +++ b/client/src/clients/mod.rs @@ -14,7 +14,7 @@ use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; use tokio::runtime::Runtime; -use topology::get_topology; +use topology::NymTopology; const LOOP_COVER_AVERAGE_DELAY: f64 = 0.5; // seconds @@ -223,10 +223,10 @@ impl NymClient { println!("Starting nym client"); let mut rt = Runtime::new()?; - let topology = get_topology(self.directory.clone()); + let topology = Topology::new(self.directory.clone()); // this is temporary and assumes there exists only a single provider. let provider_address: SocketAddr = topology - .mix_provider_nodes + .get_mix_provider_nodes() .first() .unwrap() .host diff --git a/client/src/utils/sphinx.rs b/client/src/utils/sphinx.rs index b087616992..98df4b9976 100644 --- a/client/src/utils/sphinx.rs +++ b/client/src/utils/sphinx.rs @@ -5,6 +5,7 @@ use directory_client::presence::Topology; use sphinx::route::{Destination, DestinationAddressBytes, Node, SURBIdentifier}; use sphinx::SphinxPacket; use std::net::SocketAddr; +use topology::NymTopology; pub const LOOP_COVER_MESSAGE_PAYLOAD: &[u8] = b"The cake is a lie!"; @@ -23,7 +24,7 @@ pub fn encapsulate_message( message: Vec, topology: &Topology, ) -> (SocketAddr, SphinxPacket) { - let mixes_route = topology::route_from(&topology); + let mixes_route = topology.route_from(); let first_provider = topology.mix_provider_nodes.first().unwrap(); let decoded_key_bytes = base64::decode_config(&first_provider.pub_key, base64::URL_SAFE).unwrap(); diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index 7e42bb15ec..2fe226e5fc 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -15,7 +15,6 @@ serde = { version = "1.0.104", features = ["derive"] } ## internal addressing = {path = "../addressing"} -directory_client = { path = "../../client/directory_client" } ## will be moved to proper dependencies once released sphinx = { path = "../../../sphinx" } \ No newline at end of file diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 6fc522c426..5f6f15516c 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -1,56 +1,75 @@ use addressing; use curve25519_dalek::montgomery::MontgomeryPoint; -use directory_client::presence::{MixNodePresence, Topology}; -use directory_client::requests::presence_topology_get::PresenceTopologyGetRequester; -use directory_client::DirectoryClient; use rand::seq::SliceRandom; use sphinx::route::Node as SphinxNode; use std::collections::HashMap; -pub fn get_topology(directory_server: String) -> Topology { - println!("Using directory server: {:?}", directory_server); - let directory_config = directory_client::Config { - base_url: directory_server, - }; - let directory = directory_client::Client::new(directory_config); - - let topology = directory - .presence_topology - .get() - .expect("Failed to retrieve network topology."); - topology +#[derive(Debug)] +pub struct MixNode { + pub host: String, + pub pub_key: String, + pub layer: u64, + pub last_seen: u64, + pub version: String, } -pub fn route_from(topology: &Topology) -> Vec { - let mut layered_topology: HashMap> = HashMap::new(); - let mixes = topology.mix_nodes.iter(); - for mix in mixes { - let layer_nodes = layered_topology.entry(mix.layer).or_insert(Vec::new()); - layer_nodes.push(mix.clone()); - } - - let num_layers = layered_topology.len() as u64; - let mut route = vec![]; - - for x in 1..=num_layers { - let nodes = &layered_topology[&x]; - let the_node = nodes.choose(&mut rand::thread_rng()).unwrap(); - route.push(the_node); - } - - route - .iter() - .map(|mix| { - let address_bytes = - addressing::encoded_bytes_from_socket_address(mix.host.clone().parse().unwrap()); - let decoded_key_bytes = base64::decode_config(&mix.pub_key, base64::URL_SAFE).unwrap(); - let mut key_bytes = [0; 32]; - key_bytes.copy_from_slice(&decoded_key_bytes[..]); - let key = MontgomeryPoint(key_bytes); - SphinxNode { - address: address_bytes, - pub_key: key, - } - }) - .collect() +#[derive(Debug)] +pub struct MixProviderClient { + pub pub_key: String, +} + +#[derive(Debug)] +pub struct MixProviderNode { + pub host: String, + pub pub_key: String, + pub registered_clients: Vec, + pub last_seen: u64, + pub version: String, +} + +#[derive(Debug)] +pub struct CocoNode { + pub host: String, + pub pub_key: String, + pub last_seen: u64, +} + +pub trait NymTopology { + fn new(directory_server: String) -> Self; + fn get_mix_nodes(&self) -> Vec; + fn get_mix_provider_nodes(&self) -> Vec; + fn get_coco_nodes(&self) -> Vec; + fn route_from(&self) -> Vec { + let mut layered_topology: HashMap> = HashMap::new(); + for mix in self.get_mix_nodes() { + let layer_nodes = layered_topology.entry(mix.layer).or_insert(Vec::new()); + layer_nodes.push(mix); + } + + // TODO: assertion that num_layers is a sane number + let num_layers = layered_topology.len() as u64; + + let route: Vec<_> = (1..=num_layers) + .map(|layer| &layered_topology[&layer]) // for each layer + .map(|nodes| nodes.choose(&mut rand::thread_rng()).unwrap()) // choose random node + .collect(); + + route + .iter() + .map(|mix| { + let address_bytes = addressing::encoded_bytes_from_socket_address( + mix.host.clone().parse().unwrap(), + ); + let decoded_key_bytes = + base64::decode_config(&mix.pub_key, base64::URL_SAFE).unwrap(); + let mut key_bytes = [0; 32]; + key_bytes.copy_from_slice(&decoded_key_bytes[..]); + let key = MontgomeryPoint(key_bytes); + SphinxNode { + address: address_bytes, + pub_key: key, + } + }) + .collect() + } } diff --git a/sfw-provider/src/provider/presence.rs b/sfw-provider/src/provider/presence.rs index bc99d8b2a9..982c1d15cf 100644 --- a/sfw-provider/src/provider/presence.rs +++ b/sfw-provider/src/provider/presence.rs @@ -42,6 +42,7 @@ impl Notifier { host: self.host.clone(), pub_key: self.pub_key.clone(), registered_clients: unlocked_ledger.current_clients(), + last_seen: 0, version: env!("CARGO_PKG_VERSION").to_string(), } }