From 0785ad67f5f7d19e9ec3fbf3fecf363f618a22aa Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 28 Jan 2020 10:10:52 +0000 Subject: [PATCH 001/273] Merge with develop --- Cargo.lock | 71 +++++++++++++++++- nym-client/Cargo.toml | 1 + nym-client/src/config/mod.rs | 81 +++++++++++++++++++++ nym-client/src/config/template.rs | 68 +++++++++++++++++ sample-configs/validator-config.toml.sample | 2 +- validator/Cargo.toml | 1 - validator/src/validator/config/mod.rs | 2 +- 7 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 nym-client/src/config/template.rs diff --git a/Cargo.lock b/Cargo.lock index 3af9d02bc2..f1a86d084a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -847,6 +847,20 @@ dependencies = [ "tokio-io", ] +[[package]] +name = "handlebars" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba758d094d31274eb49d15da6f326b96bf3185239a6359bf684f3d5321148900" +dependencies = [ + "log", + "pest", + "pest_derive", + "quick-error", + "serde", + "serde_json", +] + [[package]] name = "healthcheck" version = "0.1.0" @@ -1135,6 +1149,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matches" version = "0.1.8" @@ -1355,6 +1375,7 @@ dependencies = [ "dirs", "dotenv", "futures 0.3.1", + "handlebars", "healthcheck", "log", "mix-client", @@ -1428,7 +1449,6 @@ dependencies = [ "log", "pretty_env_logger", "serde", - "serde_derive", "tokio 0.2.10", "toml", ] @@ -1531,6 +1551,49 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + [[package]] name = "pin-project" version = "0.4.7" @@ -2474,6 +2537,12 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +[[package]] +name = "ucd-trie" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" + [[package]] name = "unicase" version = "2.6.0" diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 52f5cb89da..e9c7e7e053 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -18,6 +18,7 @@ curve25519-dalek = "1.2.3" dirs = "2.0.2" dotenv = "0.15.0" futures = "0.3.1" +handlebars = "3.0.1" log = "0.4" pem = "0.7.0" pretty_env_logger = "0.3" diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 4bf0f95209..93c8b0af32 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -1 +1,82 @@ +use crate::config::persistance::pathfinder::ClientPathfinder; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + pub mod persistance; +mod template; + +#[derive(Serialize, Deserialize)] +pub struct Config { + client: Client, +} + +impl Default for Config { + fn default() -> Self { + unimplemented!() + } +} + +#[derive(Serialize, Deserialize)] +pub struct Client { + // nym_home_directory specifies absolute path to the home nym Clients directory. + // It is expected to use default value and hence .toml file should not redefine this field. + nym_home_directory: String, + + // ID specifies the human readable ID of this particular client. + // If not provided a pseudorandom id will be generated instead. + id: String, + + // URL to the directory server. + directory_server: String, + + // Path to file containing private identity key. + private_identity_key_file: PathBuf, + + // Path to file containing public identity key. + public_identity_key_file: PathBuf, + + // mix_apps_directory specifies directory for mixapps, such as a chat client, + // to store their app-specific data. + mix_apps_directory: String, + + // provider_id specifies ID of the provider to which the client should send messages. + // If initially omitted, a random provider will be chosen from the available topology. + provider_id: String, +} + +impl Default for Client { + fn default() -> Self { + unimplemented!() + } +} + +impl Client { + fn new() -> Self { + Default::default() + } + + fn with_id(mut self, id: String) -> Self { + self.id = id; + self + } + + fn with_pathfinder(mut self, pathfinder: ClientPathfinder) -> Self { + // pub config_dir: PathBuf, + // pub private_mix_key: PathBuf, + // pub public_mix_key: PathBuf, + self + } +} + +pub struct Logging {} + +impl Default for Logging { + fn default() -> Self { + unimplemented!() + } +} + +#[cfg(test)] +mod client_config { + use super::*; +} diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs new file mode 100644 index 0000000000..9233c2b5fe --- /dev/null +++ b/nym-client/src/config/template.rs @@ -0,0 +1,68 @@ +use super::Config; +use handlebars::{Handlebars, TemplateRenderError}; + +fn config_template() -> &'static str { + // While using normal toml marshalling would have been way simpler with less overhead, + // I think it's useful to have comments attached to the saved config file to explain behaviour of + // particular fields. + // Note: any changes to the template must be reflected in the appropriate structs in mod.rs. + r#" +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base client config options ##### + +[client] +# Human readable ID of this particular client. +id = "{{ client.id }}" + +# URL to the directory server. +directory_server = "{{ client.directory_server }}" + +# Path to file containing private identity key. +private_identity_key_file = "{{ client.private_identity_key }}" + +# Path to file containing public identity key. +public_identity_key_file = "{{ client.public_identity_key }}" + +##### additional client config options ##### + +# ID of the provider to which the client should send messages. +provider_id = "{{ client.provider_id }}" + +# directory for mixapps, such as a chat client, to store their app-specific data. +mixapps_directory = "{{ client.mix_apps_directory }}" + +##### advanced configuration options ##### + +# Absolute path to the home Nym Clients directory. +nym_home_directory = "{{ client.home_directory }}" + "# +} + +fn render_template(config: &Config) -> Result { + let reg = Handlebars::new(); + reg.render_template(config_template(), &config) +} + +#[cfg(test)] +mod config_template { + use super::*; + + #[test] + fn it_works_for_default_config() { + render_template(&Default::default()).unwrap(); + } + + #[test] + fn it_works_for_dummy_config() { + let dummy_cfg = Config { + client: crate::config::Client { + id: "foomp".to_string(), + }, + }; + let render = render_template(&dummy_cfg).unwrap(); + + println!("{}", render); + } +} diff --git a/sample-configs/validator-config.toml.sample b/sample-configs/validator-config.toml.sample index c60c223287..2997abea29 100644 --- a/sample-configs/validator-config.toml.sample +++ b/sample-configs/validator-config.toml.sample @@ -1,7 +1,7 @@ [healthcheck] #directory-server = "http://localhost:8080" -directory-server = "https://qa-directory.nymtech.net" +directory-server = "https://directory.nymtech.net" interval = 10.0 test-packets-per-node = 2 # in seconds resolution-timeout = 5 # in seconds \ No newline at end of file diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 6a6e83066c..99c880b52c 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -15,7 +15,6 @@ futures = "0.3.1" log = "0.4" pretty_env_logger = "0.3" serde = "1.0.104" -serde_derive = "1.0.104" tokio = { version = "0.2", features = ["full"] } toml = "0.5.5" diff --git a/validator/src/validator/config/mod.rs b/validator/src/validator/config/mod.rs index 4ef70b9c7e..a6ec068a4f 100644 --- a/validator/src/validator/config/mod.rs +++ b/validator/src/validator/config/mod.rs @@ -1,4 +1,4 @@ -use serde_derive::Deserialize; +use serde::Deserialize; #[derive(Deserialize, Debug)] pub struct Config { From 6a3316897308fc7670c166f9dbd878c49e6f9a9c Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 28 Jan 2020 12:46:53 +0000 Subject: [PATCH 002/273] validator: adding the abci crate --- Cargo.lock | 76 +++++++++++++++++++++++++++++++++++++++++++- validator/Cargo.toml | 1 + 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 14b3f014ca..cf783a09b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,22 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "abci" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfb90d266d6ca61cea4d155dd5033c9516495f4f74f82faf1ca5c4feeb82577" +dependencies = [ + "byteorder", + "bytes 0.4.12", + "env_logger 0.7.1", + "futures 0.3.1", + "integer-encoding", + "log", + "protobuf", + "protobuf-codegen-pure", + "tokio 0.1.22", +] + [[package]] name = "addressing" version = "0.1.0" @@ -597,6 +614,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "error-chain" version = "0.12.1" @@ -1020,6 +1050,12 @@ dependencies = [ "bytes 0.4.12", ] +[[package]] +name = "integer-encoding" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aec89c15e2cfa0f0eae8ca60e03cb10b30d25ea2c0ad7d6be60a95e32729994" + [[package]] name = "iovec" version = "0.1.4" @@ -1419,6 +1455,7 @@ dependencies = [ name = "nym-validator" version = "0.4.0-rc.1" dependencies = [ + "abci", "built", "clap", "crypto", @@ -1582,7 +1619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074" dependencies = [ "chrono", - "env_logger", + "env_logger 0.6.2", "log", ] @@ -1612,6 +1649,31 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "protobuf" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6563a657a014b771e7f69f06447d88d8fbb5a215ffc4cab724afb3acedcc7701" + +[[package]] +name = "protobuf-codegen" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f1bbc6db30d5d3e730b6e2326e9a64a75ca9c80d6427d6f054dc8cacc79d225" +dependencies = [ + "protobuf", +] + +[[package]] +name = "protobuf-codegen-pure" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5473ffa23d2ea3b9046764f1a22149791967aad946b6cbd99601e720afc4d0" +dependencies = [ + "protobuf", + "protobuf-codegen", +] + [[package]] name = "provider-client" version = "0.1.0" @@ -2240,6 +2302,7 @@ dependencies = [ "futures 0.1.29", "mio", "num_cpus", + "tokio-codec", "tokio-current-thread", "tokio-executor", "tokio-io", @@ -2284,6 +2347,17 @@ dependencies = [ "futures 0.1.29", ] +[[package]] +name = "tokio-codec" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" +dependencies = [ + "bytes 0.4.12", + "futures 0.1.29", + "tokio-io", +] + [[package]] name = "tokio-current-thread" version = "0.1.6" diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 5bf0cf583a..49c827ec8b 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +abci = "0.6.4" clap = "2.33.0" # Read notes https://crates.io/crates/dotenv - tl;dr: don't use in production, set environmental variables properly. dotenv = "0.15.0" From ee8da3a019e57b8a07e08579dfb91857087da095 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 28 Jan 2020 12:47:16 +0000 Subject: [PATCH 003/273] validator: starting a README so docs will be easier to write when we switch from Go --- validator/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 validator/README.md diff --git a/validator/README.md b/validator/README.md new file mode 100644 index 0000000000..3f6dcb7b2a --- /dev/null +++ b/validator/README.md @@ -0,0 +1,18 @@ +Nym Validator +============= + +The Nym Validator has several jobs: + +* use Tendermint (v0.33.0) to maintain a total global ordering of incoming transactions +* track quality of service for mixnet nodes (mixmining) +* generate Coconut credentials and ensure they're not double spent +* maintain a decentralized directory of all Nym nodes that have staked into the system + +Some of these functions may be moved away to their own node types in the future, for example to increase scalability or performance. At the moment, we'd like to keep deployments simple, so they're all in the validator node. + +Running the validator +--------------------- + +1. Download and install [Tendermint 0.33.0](https://github.com/tendermint/tendermint/releases/tag/v0.33.0) +2. `tendermint init` +3. `tendermint run` \ No newline at end of file From 74bf2ed9f43d100f7d63d99554e3529755d524d2 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 28 Jan 2020 13:03:24 +0000 Subject: [PATCH 004/273] validator: using exact Tendermint version specified by rust-abci crate docs --- validator/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/README.md b/validator/README.md index 3f6dcb7b2a..e3a5722acf 100644 --- a/validator/README.md +++ b/validator/README.md @@ -13,6 +13,6 @@ Some of these functions may be moved away to their own node types in the future, Running the validator --------------------- -1. Download and install [Tendermint 0.33.0](https://github.com/tendermint/tendermint/releases/tag/v0.33.0) +1. Download and install [Tendermint 0.32.7](https://github.com/tendermint/tendermint/releases/tag/v0.32.7) 2. `tendermint init` -3. `tendermint run` \ No newline at end of file +3. `tendermint node` \ No newline at end of file From dd2e4f153a60b06a62948c3a844c6c4beba90fdf Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 28 Jan 2020 13:28:11 +0000 Subject: [PATCH 005/273] validator: adding byteorder crate --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index cf783a09b9..97d346cbc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1457,6 +1457,7 @@ version = "0.4.0-rc.1" dependencies = [ "abci", "built", + "byteorder", "clap", "crypto", "dotenv", From 58f839ecd8caddd0436b92938d01664cba941d4e Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 28 Jan 2020 13:28:20 +0000 Subject: [PATCH 006/273] ibid --- validator/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 49c827ec8b..d59ebdbf06 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" [dependencies] abci = "0.6.4" +byteorder = "1.3.2" clap = "2.33.0" # Read notes https://crates.io/crates/dotenv - tl;dr: don't use in production, set environmental variables properly. dotenv = "0.15.0" From 742c428a251ffc442bfad52fb4fa44d6c6e8c0d1 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 28 Jan 2020 13:28:42 +0000 Subject: [PATCH 007/273] validator: sample tendermint counter app is breathing. --- validator/src/main.rs | 8 ++++ validator/src/tendermint_abci/mod.rs | 65 ++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 validator/src/tendermint_abci/mod.rs diff --git a/validator/src/main.rs b/validator/src/main.rs index 39d0d375f3..3e9a27705c 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -5,6 +5,7 @@ use log::{error, trace}; use std::process; use toml; +mod tendermint_abci; mod validator; fn main() { @@ -26,6 +27,7 @@ fn main() { .required(true), ), ) + .subcommand(SubCommand::with_name("tm").about("Starts the Tendermint test app")) .get_matches(); if let Err(e) = execute(arg_matches) { @@ -42,6 +44,11 @@ fn run(matches: &ArgMatches) { validator.start() } +fn tm(_matches: &ArgMatches) { + println!("Starting Tendermint app."); + abci::run_local(tendermint_abci::CounterApp::new()); +} + fn parse_config(matches: &ArgMatches) -> Config { let config_file_path = matches.value_of("config").unwrap(); // since this is happening at the very startup, it's fine to panic if file doesn't exist @@ -57,6 +64,7 @@ pub mod built_info { fn execute(matches: ArgMatches) -> Result<(), String> { match matches.subcommand() { ("run", Some(m)) => Ok(run(m)), + ("tm", Some(m)) => Ok(tm(m)), _ => Err(usage()), } } diff --git a/validator/src/tendermint_abci/mod.rs b/validator/src/tendermint_abci/mod.rs new file mode 100644 index 0000000000..69f859bd9e --- /dev/null +++ b/validator/src/tendermint_abci/mod.rs @@ -0,0 +1,65 @@ +use abci::*; +use byteorder::{BigEndian, ByteOrder}; + +// Simple counter application. Its only state is a u64 count +// We use BigEndian to serialize the data across transactions calls +pub struct CounterApp { + count: u64, +} + +impl CounterApp { + pub fn new() -> CounterApp { + CounterApp { count: 0 } + } +} + +// Convert incoming tx data to the proper BigEndian size. txs.len() > 8 will return 0 +fn convert_tx(tx: &[u8]) -> u64 { + if tx.len() < 8 { + let pad = 8 - tx.len(); + let mut x = vec![0; pad]; + x.extend_from_slice(tx); + return BigEndian::read_u64(x.as_slice()); + } + BigEndian::read_u64(tx) +} + +impl abci::Application for CounterApp { + // Validate transactions. Rule: Transactions must be incremental: 1,2,3,4... + fn check_tx(&mut self, req: &RequestCheckTx) -> ResponseCheckTx { + // Get the Tx [u8] and convert to u64 + let c = convert_tx(req.get_tx()); + let mut resp = ResponseCheckTx::new(); + + // Validation logic + if c != self.count + 1 { + resp.set_code(1); + resp.set_log(String::from("Count must be incremental!")); + return resp; + } + + // Update state to keep state correct for next check_tx call + self.count = c; + resp + } + + fn deliver_tx(&mut self, req: &RequestDeliverTx) -> ResponseDeliverTx { + // Get the Tx [u8] + let c = convert_tx(req.get_tx()); + // Update state + self.count = c; + // Return default code 0 == bueno + ResponseDeliverTx::new() + } + + fn commit(&mut self, _req: &RequestCommit) -> ResponseCommit { + // Create the response + let mut resp = ResponseCommit::new(); + // Convert count to bits + let mut buf = [0; 8]; + BigEndian::write_u64(&mut buf, self.count); + // Set data so last state is included in the block + resp.set_data(buf.to_vec()); + resp + } +} From b20555c622537c85d240d663b55ad026c9ff1f4b Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Tue, 28 Jan 2020 13:31:13 +0000 Subject: [PATCH 008/273] validator: slight comment change --- validator/src/validator/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/validator/mod.rs b/validator/src/validator/mod.rs index 6ed56fa42b..98c51880e6 100644 --- a/validator/src/validator/mod.rs +++ b/validator/src/validator/mod.rs @@ -42,6 +42,6 @@ impl Validator Date: Wed, 29 Jan 2020 08:20:06 +0000 Subject: [PATCH 009/273] validator: using info logging --- validator/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/src/main.rs b/validator/src/main.rs index 3e9a27705c..5e3701d0f2 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -1,7 +1,7 @@ use crate::validator::config::Config; use crate::validator::Validator; use clap::{App, Arg, ArgMatches, SubCommand}; -use log::{error, trace}; +use log::{error, info, trace}; use std::process; use toml; @@ -45,7 +45,7 @@ fn run(matches: &ArgMatches) { } fn tm(_matches: &ArgMatches) { - println!("Starting Tendermint app."); + info!("Starting Tendermint app."); abci::run_local(tendermint_abci::CounterApp::new()); } From 482deb718c9f8312bec11b6101dcfc785cd4255e Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 08:45:51 +0000 Subject: [PATCH 010/273] validator: more docs --- validator/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/validator/README.md b/validator/README.md index e3a5722acf..1c6467e22d 100644 --- a/validator/README.md +++ b/validator/README.md @@ -10,9 +10,11 @@ The Nym Validator has several jobs: Some of these functions may be moved away to their own node types in the future, for example to increase scalability or performance. At the moment, we'd like to keep deployments simple, so they're all in the validator node. -Running the validator ---------------------- +Running the validator on your local machine +------------------------------------------- 1. Download and install [Tendermint 0.32.7](https://github.com/tendermint/tendermint/releases/tag/v0.32.7) -2. `tendermint init` -3. `tendermint node` \ No newline at end of file +2. `tendermint init` sets up Tendermint for use +3. `tendermint node` runs Tendermint +4. `cargo run -- run` builds the Nym Validator and runs it + From 79f9372ebbef736e4f695df6367861e45ddc1b02 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 08:46:09 +0000 Subject: [PATCH 011/273] validator: banging structure around a bit --- validator/src/main.rs | 5 +++-- validator/src/network/mod.rs | 1 + validator/src/{ => network}/tendermint_abci/mod.rs | 0 validator/src/validator/config/mod.rs | 7 ------- validator/src/validator/mod.rs | 9 +++++++-- 5 files changed, 11 insertions(+), 11 deletions(-) create mode 100644 validator/src/network/mod.rs rename validator/src/{ => network}/tendermint_abci/mod.rs (100%) delete mode 100644 validator/src/validator/config/mod.rs diff --git a/validator/src/main.rs b/validator/src/main.rs index 5e3701d0f2..d964d8991a 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -1,11 +1,12 @@ -use crate::validator::config::Config; +use crate::validator::Config; use crate::validator::Validator; use clap::{App, Arg, ArgMatches, SubCommand}; use log::{error, info, trace}; +use network::tendermint_abci; use std::process; use toml; -mod tendermint_abci; +mod network; mod validator; fn main() { diff --git a/validator/src/network/mod.rs b/validator/src/network/mod.rs new file mode 100644 index 0000000000..30db3a456d --- /dev/null +++ b/validator/src/network/mod.rs @@ -0,0 +1 @@ +pub mod tendermint_abci; diff --git a/validator/src/tendermint_abci/mod.rs b/validator/src/network/tendermint_abci/mod.rs similarity index 100% rename from validator/src/tendermint_abci/mod.rs rename to validator/src/network/tendermint_abci/mod.rs diff --git a/validator/src/validator/config/mod.rs b/validator/src/validator/config/mod.rs deleted file mode 100644 index 4ef70b9c7e..0000000000 --- a/validator/src/validator/config/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde_derive::Deserialize; - -#[derive(Deserialize, Debug)] -pub struct Config { - #[serde(rename(deserialize = "healthcheck"))] - pub health_check: healthcheck::config::HealthCheck, -} diff --git a/validator/src/validator/mod.rs b/validator/src/validator/mod.rs index 98c51880e6..d9c220318f 100644 --- a/validator/src/validator/mod.rs +++ b/validator/src/validator/mod.rs @@ -1,4 +1,3 @@ -use crate::validator::config::Config; use crypto::identity::{ DummyMixIdentityKeyPair, DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey, MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey, @@ -7,7 +6,13 @@ use healthcheck::HealthChecker; use log::debug; use tokio::runtime::Runtime; -pub mod config; +use serde_derive::Deserialize; + +#[derive(Deserialize, Debug)] +pub struct Config { + #[serde(rename(deserialize = "healthcheck"))] + pub health_check: healthcheck::config::HealthCheck, +} // allow for a generic validator pub struct Validator From 4ab9b8414d050f046b16309a6fe3aa2da787ab19 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 08:49:13 +0000 Subject: [PATCH 012/273] Removing the validator folder, having two seems overly nested --- validator/src/{validator/mod.rs => validator.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename validator/src/{validator/mod.rs => validator.rs} (100%) diff --git a/validator/src/validator/mod.rs b/validator/src/validator.rs similarity index 100% rename from validator/src/validator/mod.rs rename to validator/src/validator.rs From 80f92254e00e1fc404d8c98cea3ccfe1924348d9 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 09:25:34 +0000 Subject: [PATCH 013/273] Ignoring validator config file inside repo --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 74a82c4e13..0e2a851081 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ target .env /.vscode/settings.json +sample-configs/validator-config.toml \ No newline at end of file From ae7c1467aea22db2fb863d495f40b8a2f413256b Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 09:26:08 +0000 Subject: [PATCH 014/273] validator: simplifying structure of networking interface files --- validator/src/network/ethereum.rs | 0 validator/src/network/mod.rs | 3 +++ .../mod.rs => tendermint_abci.rs} | 20 +++++++------------ 3 files changed, 10 insertions(+), 13 deletions(-) create mode 100644 validator/src/network/ethereum.rs rename validator/src/network/{tendermint_abci/mod.rs => tendermint_abci.rs} (84%) diff --git a/validator/src/network/ethereum.rs b/validator/src/network/ethereum.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/validator/src/network/mod.rs b/validator/src/network/mod.rs index 30db3a456d..cc58e96b21 100644 --- a/validator/src/network/mod.rs +++ b/validator/src/network/mod.rs @@ -1 +1,4 @@ +//! The `network` module provides interfaces to external systems via network +//! connectivity. +//! pub mod tendermint_abci; diff --git a/validator/src/network/tendermint_abci/mod.rs b/validator/src/network/tendermint_abci.rs similarity index 84% rename from validator/src/network/tendermint_abci/mod.rs rename to validator/src/network/tendermint_abci.rs index 69f859bd9e..90236fbe9a 100644 --- a/validator/src/network/tendermint_abci/mod.rs +++ b/validator/src/network/tendermint_abci.rs @@ -1,17 +1,9 @@ +use crate::validator::Validator; use abci::*; use byteorder::{BigEndian, ByteOrder}; - -// Simple counter application. Its only state is a u64 count -// We use BigEndian to serialize the data across transactions calls -pub struct CounterApp { - count: u64, -} - -impl CounterApp { - pub fn new() -> CounterApp { - CounterApp { count: 0 } - } -} +use crypto::identity::{ + DummyMixIdentityKeyPair, DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey, +}; // Convert incoming tx data to the proper BigEndian size. txs.len() > 8 will return 0 fn convert_tx(tx: &[u8]) -> u64 { @@ -24,7 +16,9 @@ fn convert_tx(tx: &[u8]) -> u64 { BigEndian::read_u64(tx) } -impl abci::Application for CounterApp { +impl abci::Application + for Validator +{ // Validate transactions. Rule: Transactions must be incremental: 1,2,3,4... fn check_tx(&mut self, req: &RequestCheckTx) -> ResponseCheckTx { // Get the Tx [u8] and convert to u64 From 17a513d5fb39fb3bf4b6a103a0ac3ead4472ee04 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 09:31:01 +0000 Subject: [PATCH 015/273] validator: Tendermint ABCI works with Validator struct. --- validator/src/main.rs | 20 ++++++++++++++++---- validator/src/validator.rs | 8 +++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/validator/src/main.rs b/validator/src/main.rs index d964d8991a..832173d6d8 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -2,7 +2,6 @@ use crate::validator::Config; use crate::validator::Validator; use clap::{App, Arg, ArgMatches, SubCommand}; use log::{error, info, trace}; -use network::tendermint_abci; use std::process; use toml; @@ -28,7 +27,17 @@ fn main() { .required(true), ), ) - .subcommand(SubCommand::with_name("tm").about("Starts the Tendermint test app")) + .subcommand( + SubCommand::with_name("tm") + .about("Starts the Tendermint test app") + .arg( + Arg::with_name("config") + .long("config") + .help("Location of the validator configuration file") + .takes_value(true) + .required(true), + ), + ) .get_matches(); if let Err(e) = execute(arg_matches) { @@ -45,9 +54,12 @@ fn run(matches: &ArgMatches) { validator.start() } -fn tm(_matches: &ArgMatches) { +fn tm(matches: &ArgMatches) { + let config = parse_config(matches); + trace!("read config: {:?}", config); info!("Starting Tendermint app."); - abci::run_local(tendermint_abci::CounterApp::new()); + let validator = Validator::new(config); + abci::run_local(validator); } fn parse_config(matches: &ArgMatches) -> Config { diff --git a/validator/src/validator.rs b/validator/src/validator.rs index d9c220318f..1725def40b 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -21,9 +21,10 @@ where Priv: MixnetIdentityPrivateKey, Pub: MixnetIdentityPublicKey, { + pub count: u64, + heath_check: HealthChecker, #[allow(dead_code)] identity_keypair: IDPair, - heath_check: HealthChecker, } // but for time being, since it's a dummy one, have it use dummy keys @@ -34,8 +35,9 @@ impl Validator Date: Wed, 29 Jan 2020 09:33:38 +0000 Subject: [PATCH 016/273] validator: removed separate "tm" command --- validator/src/main.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/validator/src/main.rs b/validator/src/main.rs index 832173d6d8..db7b56a87c 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -27,17 +27,6 @@ fn main() { .required(true), ), ) - .subcommand( - SubCommand::with_name("tm") - .about("Starts the Tendermint test app") - .arg( - Arg::with_name("config") - .long("config") - .help("Location of the validator configuration file") - .takes_value(true) - .required(true), - ), - ) .get_matches(); if let Err(e) = execute(arg_matches) { @@ -54,14 +43,6 @@ fn run(matches: &ArgMatches) { validator.start() } -fn tm(matches: &ArgMatches) { - let config = parse_config(matches); - trace!("read config: {:?}", config); - info!("Starting Tendermint app."); - let validator = Validator::new(config); - abci::run_local(validator); -} - fn parse_config(matches: &ArgMatches) -> Config { let config_file_path = matches.value_of("config").unwrap(); // since this is happening at the very startup, it's fine to panic if file doesn't exist @@ -77,7 +58,6 @@ pub mod built_info { fn execute(matches: ArgMatches) -> Result<(), String> { match matches.subcommand() { ("run", Some(m)) => Ok(run(m)), - ("tm", Some(m)) => Ok(tm(m)), _ => Err(usage()), } } From b9f3000dcc412dd6c11bfad483ac7f1d34b5da22 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 29 Jan 2020 10:17:47 +0000 Subject: [PATCH 017/273] Updated changelog --- CHANGELOG.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f60174ed7..983dcc4625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [v0.4.1](https://github.com/nymtech/nym/tree/v0.4.1) (2020-01-29) + +[Full Changelog](https://github.com/nymtech/nym/compare/v0.4.0...v0.4.1) + +**Closed issues:** + +- Change healthcheck to run on provided topology rather than pull one itself [\#95](https://github.com/nymtech/nym/issues/95) + +**Merged pull requests:** + +- Bugfix/healthcheck on provided topology [\#108](https://github.com/nymtech/nym/pull/108) ([jstuczyn](https://github.com/jstuczyn)) + ## [v0.4.0](https://github.com/nymtech/nym/tree/v0.4.0) (2020-01-28) [Full Changelog](https://github.com/nymtech/nym/compare/v0.4.0-rc.2...v0.4.0) @@ -82,10 +94,6 @@ This release also integrates a health-checker and network topology refresh into [Full Changelog](https://github.com/nymtech/nym/compare/v0.3.1...v0.3.2) -**Merged pull requests:** - -- Feature/separate presence address [\#59](https://github.com/nymtech/nym/pull/59) ([jstuczyn](https://github.com/jstuczyn)) - ## [v0.3.1](https://github.com/nymtech/nym/tree/v0.3.1) (2020-01-16) [Full Changelog](https://github.com/nymtech/nym/compare/v0.3.0...v0.3.1) @@ -112,6 +120,7 @@ This release also integrates a health-checker and network topology refresh into **Merged pull requests:** +- Feature/separate presence address [\#59](https://github.com/nymtech/nym/pull/59) ([jstuczyn](https://github.com/jstuczyn)) - Feature/client topology filtering [\#54](https://github.com/nymtech/nym/pull/54) ([jstuczyn](https://github.com/jstuczyn)) - print public key for nym client tools [\#53](https://github.com/nymtech/nym/pull/53) ([mileschet](https://github.com/mileschet)) - Showing binding warning on binding to localhost, 0.0.0.0 or 127.0.0.1 [\#52](https://github.com/nymtech/nym/pull/52) ([jstuczyn](https://github.com/jstuczyn)) From 657e09288714a44322ae022c88a3cdb9a6d27562 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 10:47:46 +0000 Subject: [PATCH 018/273] mixnode: minor re-ordering of start() operations --- mixnode/src/node/mod.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 0432dec413..502ff77843 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -242,17 +242,20 @@ impl MixNode { } pub fn start(&self, config: node::Config) -> Result<(), Box> { - // Create the runtime, probably later move it to MixNode itself? - let mut rt = Runtime::new()?; - - let (received_tx, received_rx) = mpsc::channel(1024); - let (sent_tx, sent_rx) = mpsc::channel(1024); - + // Set up config and public key for this node let directory_cfg = directory_client::Config { base_url: self.directory_server.clone(), }; let pub_key_str = bs58::encode(&self.public_key.to_bytes().to_vec()).into_string(); + // Set up channels + let (received_tx, received_rx) = mpsc::channel(1024); + let (sent_tx, sent_rx) = mpsc::channel(1024); + + // Create the runtime, probably later move it to MixNode itself? + let mut rt = Runtime::new()?; + + // Spawn Tokio tasks as necessary for node functionality rt.spawn({ let presence_notifier = presence::Notifier::new(&config); presence_notifier.run() From 81ed98d3ec13f72e3af628245dfeda16e09a8528 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 11:33:23 +0000 Subject: [PATCH 019/273] validator: pulling all log levels into main --- validator/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/main.rs b/validator/src/main.rs index db7b56a87c..335c3b0845 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -1,7 +1,7 @@ use crate::validator::Config; use crate::validator::Validator; use clap::{App, Arg, ArgMatches, SubCommand}; -use log::{error, info, trace}; +use log::*; use std::process; use toml; From ef64871f78aee96e8f7fcf5fe4d75b70e20e0e2a Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 11:33:47 +0000 Subject: [PATCH 020/273] validator: adding an async start() method to abci network interface --- validator/src/network/tendermint_abci.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/validator/src/network/tendermint_abci.rs b/validator/src/network/tendermint_abci.rs index 90236fbe9a..deea902fa6 100644 --- a/validator/src/network/tendermint_abci.rs +++ b/validator/src/network/tendermint_abci.rs @@ -57,3 +57,8 @@ impl abci::Application resp } } +pub async fn start( + app: Validator, +) { + abci::run_local(app); +} From 7a68499c1179b79a2f93df667afff5b3adbc1eda Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 11:34:18 +0000 Subject: [PATCH 021/273] validator: easier log level switches --- validator/src/validator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 1725def40b..dcbc11b470 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -3,7 +3,7 @@ use crypto::identity::{ MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey, }; use healthcheck::HealthChecker; -use log::debug; +use log::*; use tokio::runtime::Runtime; use serde_derive::Deserialize; From 62f4c8bebde88b4fc822236b011907565feabc6e Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 11:34:49 +0000 Subject: [PATCH 022/273] validator: import the abci interface for use by the runner --- validator/src/validator.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index dcbc11b470..f75281d027 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -1,3 +1,4 @@ +use crate::network::tendermint_abci; use crypto::identity::{ DummyMixIdentityKeyPair, DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey, MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey, From 4c4ec081229adbc2b8102862811ebca4a4a4cb16 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 11:35:39 +0000 Subject: [PATCH 023/273] validator: fixing health_check typo in Validator struct --- validator/src/validator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index f75281d027..1f94185e92 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -23,7 +23,7 @@ where Pub: MixnetIdentityPublicKey, { pub count: u64, - heath_check: HealthChecker, + health_check: HealthChecker, #[allow(dead_code)] identity_keypair: IDPair, } From d9613043ecf30136736df54bb6b2e1a6b67fe92b Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 12:28:29 +0000 Subject: [PATCH 024/273] validator: comment on networks --- validator/src/network/tendermint_abci.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/network/tendermint_abci.rs b/validator/src/network/tendermint_abci.rs index deea902fa6..68358e08c2 100644 --- a/validator/src/network/tendermint_abci.rs +++ b/validator/src/network/tendermint_abci.rs @@ -5,7 +5,7 @@ use crypto::identity::{ DummyMixIdentityKeyPair, DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey, }; -// Convert incoming tx data to the proper BigEndian size. txs.len() > 8 will return 0 +// Convert incoming tx network data to the proper BigEndian size. txs.len() > 8 will return 0 fn convert_tx(tx: &[u8]) -> u64 { if tx.len() < 8 { let pad = 8 - tx.len(); From 8c9d68041c417281c9df96b5a262a85cd00a0c80 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 12:49:51 +0000 Subject: [PATCH 025/273] validator: renamed tendermint integration --- validator/src/network/mod.rs | 2 +- .../{tendermint_abci.rs => tendermint.rs} | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) rename validator/src/network/{tendermint_abci.rs => tendermint.rs} (80%) diff --git a/validator/src/network/mod.rs b/validator/src/network/mod.rs index cc58e96b21..6ad3fa6cb5 100644 --- a/validator/src/network/mod.rs +++ b/validator/src/network/mod.rs @@ -1,4 +1,4 @@ //! The `network` module provides interfaces to external systems via network //! connectivity. //! -pub mod tendermint_abci; +pub mod tendermint; diff --git a/validator/src/network/tendermint_abci.rs b/validator/src/network/tendermint.rs similarity index 80% rename from validator/src/network/tendermint_abci.rs rename to validator/src/network/tendermint.rs index 68358e08c2..c493dbb407 100644 --- a/validator/src/network/tendermint_abci.rs +++ b/validator/src/network/tendermint.rs @@ -1,9 +1,6 @@ -use crate::validator::Validator; use abci::*; use byteorder::{BigEndian, ByteOrder}; -use crypto::identity::{ - DummyMixIdentityKeyPair, DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey, -}; +use serde_derive::Deserialize; // Convert incoming tx network data to the proper BigEndian size. txs.len() > 8 will return 0 fn convert_tx(tx: &[u8]) -> u64 { @@ -16,9 +13,18 @@ fn convert_tx(tx: &[u8]) -> u64 { BigEndian::read_u64(tx) } -impl abci::Application - for Validator -{ +#[derive(Debug, Deserialize)] +pub struct Abci { + count: u64, +} + +impl Abci { + pub fn new() -> Abci { + Abci { count: 0 } + } +} + +impl abci::Application for Abci { // Validate transactions. Rule: Transactions must be incremental: 1,2,3,4... fn check_tx(&mut self, req: &RequestCheckTx) -> ResponseCheckTx { // Get the Tx [u8] and convert to u64 @@ -57,8 +63,6 @@ impl abci::Application resp } } -pub async fn start( - app: Validator, -) { +pub async fn start(app: Abci) { abci::run_local(app); } From f02b37f5c932d8e8166ef0ed308db56c8efa7a20 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 12:50:09 +0000 Subject: [PATCH 026/273] validator: pulling tendermint ABCI out into its own type. --- validator/src/validator.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 1f94185e92..f2c668336a 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -1,4 +1,4 @@ -use crate::network::tendermint_abci; +use crate::network::tendermint; use crypto::identity::{ DummyMixIdentityKeyPair, DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey, MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey, @@ -22,7 +22,7 @@ where Priv: MixnetIdentityPrivateKey, Pub: MixnetIdentityPublicKey, { - pub count: u64, + tendermint_abci: tendermint::Abci, health_check: HealthChecker, #[allow(dead_code)] identity_keypair: IDPair, @@ -36,18 +36,20 @@ impl Validator Date: Wed, 29 Jan 2020 12:54:30 +0000 Subject: [PATCH 027/273] validator: changing tendermint start to run() --- validator/src/network/tendermint.rs | 7 ++++--- validator/src/validator.rs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/validator/src/network/tendermint.rs b/validator/src/network/tendermint.rs index c493dbb407..b2960a6f40 100644 --- a/validator/src/network/tendermint.rs +++ b/validator/src/network/tendermint.rs @@ -22,6 +22,10 @@ impl Abci { pub fn new() -> Abci { Abci { count: 0 } } + + pub async fn run(self) { + abci::run_local(self); + } } impl abci::Application for Abci { @@ -63,6 +67,3 @@ impl abci::Application for Abci { resp } } -pub async fn start(app: Abci) { - abci::run_local(app); -} diff --git a/validator/src/validator.rs b/validator/src/validator.rs index f2c668336a..ec6c1b4aa3 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -46,7 +46,7 @@ impl Validator Date: Wed, 29 Jan 2020 12:55:04 +0000 Subject: [PATCH 028/273] ibid --- validator/src/validator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index ec6c1b4aa3..1b27c4344b 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -46,7 +46,7 @@ impl Validator Date: Wed, 29 Jan 2020 12:58:40 +0000 Subject: [PATCH 029/273] validator: removing serde stuff in abci --- validator/src/network/tendermint.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/validator/src/network/tendermint.rs b/validator/src/network/tendermint.rs index b2960a6f40..4a581774e6 100644 --- a/validator/src/network/tendermint.rs +++ b/validator/src/network/tendermint.rs @@ -1,6 +1,5 @@ use abci::*; use byteorder::{BigEndian, ByteOrder}; -use serde_derive::Deserialize; // Convert incoming tx network data to the proper BigEndian size. txs.len() > 8 will return 0 fn convert_tx(tx: &[u8]) -> u64 { @@ -13,7 +12,6 @@ fn convert_tx(tx: &[u8]) -> u64 { BigEndian::read_u64(tx) } -#[derive(Debug, Deserialize)] pub struct Abci { count: u64, } From 536b84c55cb808ea5ce901a1b951da6b94859de9 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 12:59:02 +0000 Subject: [PATCH 030/273] validator: removing logging calls --- validator/src/validator.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 1b27c4344b..4bf44d82ad 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -31,8 +31,6 @@ where // but for time being, since it's a dummy one, have it use dummy keys impl Validator { pub fn new(config: Config) -> Self { - debug!("validator new"); - let dummy_keypair = DummyMixIdentityKeyPair::new(); Validator { @@ -43,7 +41,6 @@ impl Validator Date: Wed, 29 Jan 2020 13:01:00 +0000 Subject: [PATCH 031/273] validator: removing logging for now --- validator/src/validator.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 4bf44d82ad..99d462317d 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -4,7 +4,6 @@ use crypto::identity::{ MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey, }; use healthcheck::HealthChecker; -use log::*; use tokio::runtime::Runtime; use serde_derive::Deserialize; From aa251c245039ca04acc8927752a6e31c8b322824 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Wed, 29 Jan 2020 13:02:48 +0000 Subject: [PATCH 032/273] validator: multi-threading tendermint so that health check also works --- validator/src/validator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 99d462317d..343b8f54cd 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -45,7 +45,7 @@ impl Validator Date: Wed, 29 Jan 2020 13:05:16 +0000 Subject: [PATCH 033/273] validator: updating run instructions --- validator/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/validator/README.md b/validator/README.md index 1c6467e22d..c8bbad5042 100644 --- a/validator/README.md +++ b/validator/README.md @@ -15,6 +15,7 @@ Running the validator on your local machine 1. Download and install [Tendermint 0.32.7](https://github.com/tendermint/tendermint/releases/tag/v0.32.7) 2. `tendermint init` sets up Tendermint for use -3. `tendermint node` runs Tendermint -4. `cargo run -- run` builds the Nym Validator and runs it +3. `tendermint node` runs Tendermint. You'll get errors until you run the Nym validator, this is normal :). +4. `cp sample-configs/validator-config.toml.sample sample-configs/validator-config.toml` +5. `cargo run -- run --config ../sample-configs/validator-config.toml` builds the Nym Validator and runs it From a73585e13aeddfac4c419db8eabee9dda0fef556 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 29 Jan 2020 15:30:20 +0000 Subject: [PATCH 034/273] Simplified the use of generics on identity keypair by using output types --- common/crypto/src/identity/mod.rs | 20 ++++++++--------- common/healthcheck/src/lib.rs | 27 ++++------------------- common/healthcheck/src/path_check.rs | 11 +++------ common/healthcheck/src/result.rs | 8 +++---- common/pemstore/src/pemstore.rs | 19 ++++++---------- nym-client/src/client/mod.rs | 22 +++++------------- nym-client/src/client/topology_control.rs | 16 +++++--------- 7 files changed, 39 insertions(+), 84 deletions(-) diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index cf39000e5d..3c920f84dc 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -6,14 +6,13 @@ use bs58; use curve25519_dalek::scalar::Scalar; use sphinx::route::DestinationAddressBytes; -pub trait MixnetIdentityKeyPair: Clone -where - Priv: MixnetIdentityPrivateKey, - Pub: MixnetIdentityPublicKey, -{ +pub trait MixnetIdentityKeyPair: Clone { + type PublicKeyMaterial: MixnetIdentityPublicKey; + type PrivateKeyMaterial: MixnetIdentityPrivateKey; + fn new() -> Self; - fn private_key(&self) -> &Priv; - fn public_key(&self) -> &Pub; + fn private_key(&self) -> &Self::PrivateKeyMaterial; + fn public_key(&self) -> &Self::PublicKeyMaterial; fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self; // TODO: signing related methods @@ -56,9 +55,10 @@ pub struct DummyMixIdentityKeyPair { pub public_key: DummyMixIdentityPublicKey, } -impl MixnetIdentityKeyPair - for DummyMixIdentityKeyPair -{ +impl MixnetIdentityKeyPair for DummyMixIdentityKeyPair { + type PublicKeyMaterial = DummyMixIdentityPublicKey; + type PrivateKeyMaterial = DummyMixIdentityPrivateKey; + fn new() -> Self { let keypair = encryption::x25519::KeyPair::new(); DummyMixIdentityKeyPair { diff --git a/common/healthcheck/src/lib.rs b/common/healthcheck/src/lib.rs index 7cd97692db..5554b2bb97 100644 --- a/common/healthcheck/src/lib.rs +++ b/common/healthcheck/src/lib.rs @@ -1,10 +1,7 @@ use crate::result::HealthCheckResult; -use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey}; -use directory_client::requests::presence_topology_get::PresenceTopologyGetRequester; -use directory_client::DirectoryClient; -use log::{debug, error, info, trace}; +use crypto::identity::MixnetIdentityKeyPair; +use log::trace; use std::fmt::{Error, Formatter}; -use std::marker::PhantomData; use std::time::Duration; use topology::{NymTopology, NymTopologyError}; @@ -36,26 +33,13 @@ impl From for HealthCheckerError { } } -pub struct HealthChecker -where - IDPair: MixnetIdentityKeyPair, - Priv: MixnetIdentityPrivateKey, - Pub: MixnetIdentityPublicKey, -{ +pub struct HealthChecker { num_test_packets: usize, resolution_timeout: Duration, identity_keypair: IDPair, - - _phantom_private: PhantomData, - _phantom_public: PhantomData, } -impl HealthChecker -where - IDPair: crypto::identity::MixnetIdentityKeyPair, - Priv: crypto::identity::MixnetIdentityPrivateKey, - Pub: crypto::identity::MixnetIdentityPublicKey, -{ +impl HealthChecker { pub fn new( resolution_timeout_f64: f64, num_test_packets: usize, @@ -65,9 +49,6 @@ where resolution_timeout: Duration::from_secs_f64(resolution_timeout_f64), num_test_packets, identity_keypair, - - _phantom_private: PhantomData, - _phantom_public: PhantomData, } } diff --git a/common/healthcheck/src/path_check.rs b/common/healthcheck/src/path_check.rs index b1d2617ad8..a345e82d99 100644 --- a/common/healthcheck/src/path_check.rs +++ b/common/healthcheck/src/path_check.rs @@ -1,4 +1,4 @@ -use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey}; +use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPublicKey}; use itertools::Itertools; use log::{debug, error, info, trace, warn}; use mix_client::MixClient; @@ -27,16 +27,11 @@ pub(crate) struct PathChecker { } impl PathChecker { - pub(crate) async fn new( + pub(crate) async fn new( providers: Vec, identity_keys: &IDPair, check_id: [u8; 16], - ) -> Self - where - IDPair: MixnetIdentityKeyPair, - Priv: MixnetIdentityPrivateKey, - Pub: MixnetIdentityPublicKey, - { + ) -> Self { let mut provider_clients = HashMap::new(); let address = identity_keys.public_key().derive_address(); diff --git a/common/healthcheck/src/result.rs b/common/healthcheck/src/result.rs index 9eda0885f5..2dd199ffde 100644 --- a/common/healthcheck/src/result.rs +++ b/common/healthcheck/src/result.rs @@ -1,6 +1,6 @@ use crate::path_check::{PathChecker, PathStatus}; use crate::score::NodeScore; -use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey}; +use crypto::identity::MixnetIdentityKeyPair; use log::{debug, error, info, warn}; use rand_os::rand_core::RngCore; use sphinx::route::NodeAddressBytes; @@ -102,7 +102,7 @@ impl HealthCheckResult { id } - pub async fn calculate( + pub async fn calculate( topology: &T, iterations: usize, resolution_timeout: Duration, @@ -110,9 +110,7 @@ impl HealthCheckResult { ) -> Self where T: NymTopology, - IDPair: MixnetIdentityKeyPair, - Priv: MixnetIdentityPrivateKey, - Pub: MixnetIdentityPublicKey, + IDPair: MixnetIdentityKeyPair, { // currently healthchecker supports only up to 255 iterations - if we somehow // find we need more, it's relatively easy change diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index 9c7a914d02..84bc8cf60a 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -1,4 +1,6 @@ use crate::pathfinder::PathFinder; +use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey}; +use crypto::PemStorable; use pem::{encode, parse, Pem}; use std::fs::File; use std::io; @@ -25,12 +27,7 @@ impl PemStore { } } - pub fn read_identity(&self) -> io::Result - where - IDPair: crypto::identity::MixnetIdentityKeyPair, - Priv: crypto::identity::MixnetIdentityPrivateKey, - Pub: crypto::identity::MixnetIdentityPublicKey, - { + pub fn read_identity(&self) -> io::Result { let private_pem = self.read_pem_file(self.private_mix_key.clone())?; let public_pem = self.read_pem_file(self.public_mix_key.clone())?; @@ -62,12 +59,10 @@ impl PemStore { // This should be refactored and made more generic for when we have other kinds of // KeyPairs that we want to persist (e.g. validator keypairs, or keys for // signing vs encryption). However, for the moment, it does the job. - pub fn write_identity(&self, key_pair: IDPair) -> io::Result<()> - where - IDPair: crypto::identity::MixnetIdentityKeyPair, - Priv: crypto::identity::MixnetIdentityPrivateKey, - Pub: crypto::identity::MixnetIdentityPublicKey, - { + pub fn write_identity( + &self, + key_pair: IDPair, + ) -> io::Result<()> { std::fs::create_dir_all(self.config_dir.clone())?; let private_key = key_pair.private_key(); diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 4d4e0da408..8bb090b95f 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -3,14 +3,13 @@ use crate::client::received_buffer::ReceivedMessagesBuffer; use crate::client::topology_control::TopologyInnerRef; use crate::sockets::tcp; use crate::sockets::ws; -use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey}; +use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPublicKey}; use directory_client::presence::Topology; use futures::channel::mpsc; use futures::join; use log::*; use sfw_provider_requests::AuthToken; use sphinx::route::Destination; -use std::marker::PhantomData; use std::net::SocketAddr; use tokio::runtime::Runtime; use topology::NymTopology; @@ -37,11 +36,9 @@ pub enum SocketType { None, } -pub struct NymClient +pub struct NymClient where - IDPair: MixnetIdentityKeyPair + Send + Sync, - Priv: MixnetIdentityPrivateKey + Send + Sync, - Pub: MixnetIdentityPublicKey + Send + Sync, + IDPair: MixnetIdentityKeyPair + Send + Sync, { keypair: IDPair, @@ -53,19 +50,14 @@ where directory: String, auth_token: Option, socket_type: SocketType, - - _phantom_private: PhantomData, - _phantom_public: PhantomData, } #[derive(Debug)] pub struct InputMessage(pub Destination, pub Vec); -impl NymClient +impl NymClient where - IDPair: MixnetIdentityKeyPair + Send + Sync, - Priv: MixnetIdentityPrivateKey + Send + Sync, - Pub: MixnetIdentityPublicKey + Send + Sync, + IDPair: 'static + MixnetIdentityKeyPair + Send + Sync, { pub fn new( keypair: IDPair, @@ -84,8 +76,6 @@ where directory, auth_token, socket_type, - _phantom_private: PhantomData, - _phantom_public: PhantomData, } } @@ -129,7 +119,7 @@ where // TODO: when we switch to our graph topology, we need to remember to change 'Topology' type let topology_controller = - rt.block_on(topology_control::TopologyControl::::new( + rt.block_on(topology_control::TopologyControl::::new( self.directory.clone(), TOPOLOGY_REFRESH_RATE, healthcheck_keys, diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index 9833f81166..5ec9422782 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -1,5 +1,5 @@ use crate::built_info; -use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey}; +use crypto::identity::MixnetIdentityKeyPair; use healthcheck::HealthChecker; use log::{error, info, trace, warn}; use std::sync::Arc; @@ -12,16 +12,14 @@ const NODE_HEALTH_THRESHOLD: f64 = 0.0; // auxiliary type for ease of use pub type TopologyInnerRef = Arc>>; -pub(crate) struct TopologyControl +pub(crate) struct TopologyControl where T: NymTopology, - IDPair: MixnetIdentityKeyPair, - Priv: MixnetIdentityPrivateKey, - Pub: MixnetIdentityPublicKey, + IDPair: MixnetIdentityKeyPair, { directory_server: String, inner: Arc>>, - health_checker: HealthChecker, + health_checker: HealthChecker, refresh_rate: f64, } @@ -31,12 +29,10 @@ enum TopologyError { NoValidPathsError, } -impl TopologyControl +impl TopologyControl where T: NymTopology, - IDPair: MixnetIdentityKeyPair, - Priv: MixnetIdentityPrivateKey, - Pub: MixnetIdentityPublicKey, + IDPair: MixnetIdentityKeyPair, { pub(crate) async fn new( directory_server: String, From 00dc25a94fe20ed3f5bfa2223c998f35ca0381fe Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 29 Jan 2020 16:04:27 +0000 Subject: [PATCH 035/273] Definition of NymConfig trait + default methods --- Cargo.lock | 11 +++++++++- Cargo.toml | 1 + common/config/Cargo.toml | 12 +++++++++++ common/config/src/lib.rs | 46 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 common/config/Cargo.toml create mode 100644 common/config/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d97c0c9bfb..4b78ab0517 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,15 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "config" +version = "0.1.0" +dependencies = [ + "handlebars", + "serde", + "toml", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -1369,13 +1378,13 @@ dependencies = [ "bs58", "built", "clap", + "config", "crypto", "curve25519-dalek", "directory-client", "dirs", "dotenv", "futures 0.3.1", - "handlebars", "healthcheck", "log", "mix-client", diff --git a/Cargo.toml b/Cargo.toml index 22e94b3a0a..a5ce1313ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "common/clients/provider-client", "common/clients/validator-client", "common/addressing", + "common/config", "common/crypto", "common/healthcheck", "common/pemstore", diff --git a/common/config/Cargo.toml b/common/config/Cargo.toml new file mode 100644 index 0000000000..d955cd93d8 --- /dev/null +++ b/common/config/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "config" +version = "0.1.0" +authors = ["Jedrzej Stuczynski "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +handlebars = "3.0.1" +serde = { version = "1.0.104", features = ["derive"] } +toml = "0.5.6" diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs new file mode 100644 index 0000000000..18ab5b2297 --- /dev/null +++ b/common/config/src/lib.rs @@ -0,0 +1,46 @@ +use handlebars::Handlebars; +use serde::de::DeserializeOwned; +use serde::Serialize; +use std::path::{Path, PathBuf}; +use std::{fs, io}; + +pub trait NymConfig: Default + Serialize + DeserializeOwned { + fn template() -> &'static str; + + fn default_root_directory() -> PathBuf; + + // default, most probable, implementations; can be easily overridden where required + fn default_config_directory() -> PathBuf { + Self::default_root_directory().join("config") + } + + fn default_data_directory() -> PathBuf { + Self::default_root_directory().join("data") + } + + fn root_directory(&self) -> PathBuf; + fn config_directory(&self) -> PathBuf; + fn data_directory(&self) -> PathBuf; + + fn save_to_file(&self, custom_location: Option) -> io::Result<()> { + let reg = Handlebars::new(); + // it's whoever is implementing the trait responsibility to make sure you can execute your own template on your data + let templated_config = reg.render_template(Self::template(), self).unwrap(); + + fs::write( + custom_location.unwrap_or(self.config_directory().join("config.toml")), + templated_config, + ) + } + + fn load_from_file(custom_location: Option) -> io::Result { + let config_contents = fs::read_to_string( + custom_location.unwrap_or(Self::default_config_directory().join("config.toml")), + )?; + + let parsing_result = toml::from_str(&config_contents) + .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)); + + parsing_result + } +} From 713a7a6fe8599ebbf207544f55b50bc6616d5f3e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 29 Jan 2020 16:04:57 +0000 Subject: [PATCH 036/273] Initial fields for the client config --- nym-client/Cargo.toml | 6 +- nym-client/src/client/mod.rs | 3 + nym-client/src/config/mod.rs | 227 +++++++++++++++++++++++++----- nym-client/src/config/template.rs | 109 +++++++++----- 4 files changed, 273 insertions(+), 72 deletions(-) diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 9a8873d7af..09939462d3 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -18,7 +18,6 @@ curve25519-dalek = "1.2.3" dirs = "2.0.2" dotenv = "0.15.0" futures = "0.3.1" -handlebars = "3.0.1" log = "0.4" pem = "0.7.0" pretty_env_logger = "0.3" @@ -30,6 +29,7 @@ tungstenite = "0.9.2" ## internal addressing = {path = "../common/addressing" } +config = {path = "../common/config"} crypto = {path = "../common/crypto"} directory-client = { path = "../common/clients/directory-client" } healthcheck = { path = "../common/healthcheck" } @@ -48,3 +48,7 @@ tokio-tungstenite = { git = "https://github.com/snapview/tokio-tungstenite", rev [build-dependencies] built = "0.3.2" + +[features] +qa = [] +local = [] \ No newline at end of file diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 4d4e0da408..daa7865271 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -8,6 +8,7 @@ use directory_client::presence::Topology; use futures::channel::mpsc; use futures::join; use log::*; +use serde::{Deserialize, Serialize}; use sfw_provider_requests::AuthToken; use sphinx::route::Destination; use std::marker::PhantomData; @@ -31,6 +32,8 @@ const FETCH_MESSAGES_DELAY: f64 = 1.0; // seconds; const TOPOLOGY_REFRESH_RATE: f64 = 10.0; // seconds +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] pub enum SocketType { TCP, WebSocket, diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 93c8b0af32..5ae2bd1e96 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -1,82 +1,243 @@ -use crate::config::persistance::pathfinder::ClientPathfinder; +use crate::client::SocketType; +use crate::config::template::config_template; +use config::NymConfig; use serde::{Deserialize, Serialize}; use std::path::PathBuf; pub mod persistance; mod template; -#[derive(Serialize, Deserialize)] +// all of the below are defined in seconds +const DEFAULT_LOOP_COVER_SENDING_AVERAGE_DELAY: f64 = 1.0; +const DEFAULT_MESSAGE_SENDING_AVERAGE_DELAY: f64 = 0.5; +const DEFAULT_AVERAGE_PACKET_DELAY: f64 = 0.2; +const DEFAULT_FETCH_MESSAGES_DELAY: f64 = 1.0; +const DEFAULT_TOPOLOGY_REFRESH_RATE: f64 = 10.0; + +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] pub struct Config { client: Client, + socket: Socket, + + #[serde(default)] + logging: Logging, + #[serde(default)] + debug: Debug, } -impl Default for Config { - fn default() -> Self { - unimplemented!() +impl NymConfig for Config { + fn template() -> &'static str { + config_template() + } + + fn default_root_directory() -> PathBuf { + dirs::home_dir() + .expect("Failed to evaluate $HOME value") + .join(".nym") + .join("clients") + } + + fn root_directory(&self) -> PathBuf { + self.client.nym_root_directory.clone() + } + + fn config_directory(&self) -> PathBuf { + self.client + .nym_root_directory + .join(&self.client.id) + .join("config") + } + + fn data_directory(&self) -> PathBuf { + self.client + .nym_root_directory + .join(&self.client.id) + .join("data") } } -#[derive(Serialize, Deserialize)] -pub struct Client { - // nym_home_directory specifies absolute path to the home nym Clients directory. - // It is expected to use default value and hence .toml file should not redefine this field. - nym_home_directory: String, +impl Config { + pub fn new(id: String) -> Self { + Config::default().with_id(id) + } - // ID specifies the human readable ID of this particular client. - // If not provided a pseudorandom id will be generated instead. + pub fn with_id(mut self, id: String) -> Self { + if self.client.private_identity_key_file.as_os_str().is_empty() { + self.client.private_identity_key_file = + self::Client::default_private_identity_key_file(&id); + } + if self.client.public_identity_key_file.as_os_str().is_empty() { + self.client.public_identity_key_file = + self::Client::default_public_identity_key_file(&id); + } + self.client.id = id; + self + } + + pub fn with_provider_id(mut self, id: String) -> Self { + self.client.provider_id = id; + self + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Client { + /// ID specifies the human readable ID of this particular client. id: String, - // URL to the directory server. + /// URL to the directory server. directory_server: String, - // Path to file containing private identity key. + /// Path to file containing private identity key. private_identity_key_file: PathBuf, - // Path to file containing public identity key. + /// Path to file containing public identity key. public_identity_key_file: PathBuf, - // mix_apps_directory specifies directory for mixapps, such as a chat client, - // to store their app-specific data. - mix_apps_directory: String, - - // provider_id specifies ID of the provider to which the client should send messages. - // If initially omitted, a random provider will be chosen from the available topology. + /// provider_id specifies ID of the provider to which the client should send messages. + /// If initially omitted, a random provider will be chosen from the available topology. provider_id: String, + + /// nym_home_directory specifies absolute path to the home nym Clients directory. + /// It is expected to use default value and hence .toml file should not redefine this field. + nym_root_directory: PathBuf, } impl Default for Client { fn default() -> Self { - unimplemented!() + // there must be explicit checks for whether id is not empty later + Client { + id: "".to_string(), + directory_server: Self::default_directory_server(), + private_identity_key_file: Default::default(), + public_identity_key_file: Default::default(), + provider_id: "".to_string(), + nym_root_directory: Config::default_root_directory(), + } } } impl Client { - fn new() -> Self { - Default::default() + fn default_directory_server() -> String { + #[cfg(feature = "qa")] + return "https://qa-directory.nymtech.net".to_string(); + #[cfg(feature = "local")] + return "http://localhost:8080".to_string(); + + "https://directory.nymtech.net".to_string() } - fn with_id(mut self, id: String) -> Self { - self.id = id; - self + fn default_private_identity_key_file(id: &str) -> PathBuf { + Config::default_data_directory() + .join(id) + .join("private_identity.pem") } - fn with_pathfinder(mut self, pathfinder: ClientPathfinder) -> Self { - // pub config_dir: PathBuf, - // pub private_mix_key: PathBuf, - // pub public_mix_key: PathBuf, - self + fn default_public_identity_key_file(id: &str) -> PathBuf { + Config::default_data_directory() + .join(id) + .join("public_identity.pem") } } +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Socket { + socket_type: SocketType, + listening_port: u64, +} + +impl Default for Socket { + fn default() -> Self { + Socket { + socket_type: SocketType::None, + listening_port: 0, + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] pub struct Logging {} impl Default for Logging { fn default() -> Self { - unimplemented!() + Logging {} + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Debug { + /// The parameter of Poisson distribution determining how long, on average, + /// sent packet is going to be delayed at any given mix node. + /// So for a packet going through three mix nodes, on average, it will take three times this value + /// until the packet reaches its destination. + /// The provided value is interpreted as seconds. + average_packet_delay: f64, + + /// The parameter of Poisson distribution determining how long, on average, + /// it is going to take for another loop cover traffic message to be sent. + /// If set to a negative value, the loop cover traffic stream will be disabled. + /// The provided value is interpreted as seconds. + loop_cover_traffic_average_delay: f64, + + /// The uniform delay every which clients are querying the providers for received packets. + /// If set to a negative value, client will never try to fetch their messages. + /// The provided value is interpreted as seconds. + fetch_message_delay: f64, + + /// The parameter of Poisson distribution determining how long, on average, + /// it is going to take another 'real traffic stream' message to be sent. + /// If no real packets are available and cover traffic is enabled, + /// a loop cover message is sent instead in order to preserve the rate. + /// If set to a negative value, client will never try to send real traffic data. + /// The provided value is interpreted as seconds. + message_sending_average_delay: f64, + + /// Whether loop cover messages should be sent to respect message_sending_rate. + /// In the case of it being disabled and not having enough real traffic + /// waiting to be sent the actual sending rate is going be lower than the desired value + /// thus decreasing the anonymity. + rate_compliant_cover_messages_disabled: bool, + + /// The uniform delay every which clients are querying the directory server + /// to try to obtain a compatible network topology to send sphinx packets through. + /// If set to a negative value, client will never try to refresh its topology, + /// meaning it will always try to use whatever it obtained on startup. + /// The provided value is interpreted as seconds. + topology_refresh_rate: f64, +} + +impl Default for Debug { + fn default() -> Self { + Debug { + average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY, + loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_SENDING_AVERAGE_DELAY, + fetch_message_delay: DEFAULT_FETCH_MESSAGES_DELAY, + message_sending_average_delay: DEFAULT_MESSAGE_SENDING_AVERAGE_DELAY, + rate_compliant_cover_messages_disabled: false, + topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, + } } } #[cfg(test)] mod client_config { use super::*; + + #[test] + fn after_saving_default_config_the_loaded_one_is_identical() { + let temp_location = std::env::temp_dir().join("config.toml"); + let default_config = Config::default().with_id("foomp".to_string()); + default_config + .save_to_file(Some(temp_location.clone())) + .unwrap(); + + let loaded_config = Config::load_from_file(Some(temp_location)).unwrap(); + + assert_eq!(default_config, loaded_config); + } } diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index 9233c2b5fe..65f7cb8cc7 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -1,7 +1,4 @@ -use super::Config; -use handlebars::{Handlebars, TemplateRenderError}; - -fn config_template() -> &'static str { +pub(crate) fn config_template() -> &'static str { // While using normal toml marshalling would have been way simpler with less overhead, // I think it's useful to have comments attached to the saved config file to explain behaviour of // particular fields. @@ -20,49 +17,85 @@ id = "{{ client.id }}" directory_server = "{{ client.directory_server }}" # Path to file containing private identity key. -private_identity_key_file = "{{ client.private_identity_key }}" +private_identity_key_file = "{{ client.private_identity_key_file }}" # Path to file containing public identity key. -public_identity_key_file = "{{ client.public_identity_key }}" +public_identity_key_file = "{{ client.public_identity_key_file }}" ##### additional client config options ##### -# ID of the provider to which the client should send messages. +# ID of the provider from which the client should be fetching messages. provider_id = "{{ client.provider_id }}" -# directory for mixapps, such as a chat client, to store their app-specific data. -mixapps_directory = "{{ client.mix_apps_directory }}" - ##### advanced configuration options ##### # Absolute path to the home Nym Clients directory. -nym_home_directory = "{{ client.home_directory }}" +nym_root_directory = "{{ client.nym_root_directory }}" + + +##### socket config options ##### + +[socket] + +# allowed values are 'TCP', 'WebSocket' or 'None' +socket_type = "{{ socket.socket_type }}" + +# if applicable (for the case of 'TCP' or 'WebSocket'), the port on which the client +# will be listening for incoming requests +listening_port = {{ socket.listening_port }} + + +##### logging configuration options ##### + +[logging] + +# TODO + + +##### debug configuration options ##### +# The following options should not be modified unless you know EXACTLY what you are doing +# as if set incorrectly, they may impact your anonymity. + +[debug] + +# The parameter of Poisson distribution determining how long, on average, +# sent packet is going to be delayed at any given mix node. +# So for a packet going through three mix nodes, on average, it will take three times this value +# until the packet reaches its destination. +# The provided value is interpreted as seconds. +average_packet_delay = {{ debug.average_packet_delay }} + +# The parameter of Poisson distribution determining how long, on average, +# it is going to take for another loop cover traffic message to be sent. +# If set to a negative value, the loop cover traffic stream will be disabled. +# The provided value is interpreted as seconds. +loop_cover_traffic_average_delay = {{ debug.loop_cover_traffic_average_delay }} + +# The uniform delay every which clients are querying the providers for received packets. +# If set to a negative value, client will never try to fetch their messages. +# The provided value is interpreted as seconds. +fetch_message_delay = {{ debug.fetch_message_delay }} + +# The parameter of Poisson distribution determining how long, on average, +# it is going to take another 'real traffic stream' message to be sent. +# If no real packets are available and cover traffic is enabled, +# a loop cover message is sent instead in order to preserve the rate. +# If set to a negative value, client will never try to send real traffic data. +# The provided value is interpreted as seconds. +message_sending_average_delay = {{ debug.message_sending_average_delay }} + +# Whether loop cover messages should be sent to respect message_sending_rate. +# In the case of it being disabled and not having enough real traffic +# waiting to be sent the actual sending rate is going be lower than the desired value +# thus decreasing the anonymity. +rate_compliant_cover_messages_disabled = {{ debug.rate_compliant_cover_messages_disabled }} + +# The uniform delay every which clients are querying the directory server +# to try to obtain a compatible network topology to send sphinx packets through. +# If set to a negative value, client will never try to refresh its topology, +# meaning it will always try to use whatever it obtained on startup. +# The provided value is interpreted as seconds. +topology_refresh_rate = {{ debug.topology_refresh_rate }} + "# } - -fn render_template(config: &Config) -> Result { - let reg = Handlebars::new(); - reg.render_template(config_template(), &config) -} - -#[cfg(test)] -mod config_template { - use super::*; - - #[test] - fn it_works_for_default_config() { - render_template(&Default::default()).unwrap(); - } - - #[test] - fn it_works_for_dummy_config() { - let dummy_cfg = Config { - client: crate::config::Client { - id: "foomp".to_string(), - }, - }; - let render = render_template(&dummy_cfg).unwrap(); - - println!("{}", render); - } -} From 2d99b2b93ac9872ed2425286397a9e6b6c845c39 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 10:19:55 +0000 Subject: [PATCH 037/273] Fixed validator compilation error --- validator/src/validator/mod.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/validator/src/validator/mod.rs b/validator/src/validator/mod.rs index 26f031fab5..4cd4891255 100644 --- a/validator/src/validator/mod.rs +++ b/validator/src/validator/mod.rs @@ -1,8 +1,5 @@ use crate::validator::config::Config; -use crypto::identity::{ - DummyMixIdentityKeyPair, DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey, - MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey, -}; +use crypto::identity::{DummyMixIdentityKeyPair, MixnetIdentityKeyPair}; use directory_client::presence::Topology; use healthcheck::HealthChecker; use log::{debug, error, info}; @@ -13,20 +10,15 @@ use topology::NymTopology; pub mod config; // allow for a generic validator -pub struct Validator -where - IDPair: MixnetIdentityKeyPair, - Priv: MixnetIdentityPrivateKey, - Pub: MixnetIdentityPublicKey, -{ +pub struct Validator { config: Config, #[allow(dead_code)] identity_keypair: IDPair, - heath_check: HealthChecker, + heath_check: HealthChecker, } // but for time being, since it's a dummy one, have it use dummy keys -impl Validator { +impl Validator { pub fn new(config: Config) -> Self { debug!("validator new"); From d75ac203d2c370416afec19950dfb61c9b6f024a Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 30 Jan 2020 11:32:06 +0000 Subject: [PATCH 038/273] validator: simplified task spawning --- validator/src/network/tendermint.rs | 4 +- .../services/mixmining/health_check_runner.rs | 42 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/validator/src/network/tendermint.rs b/validator/src/network/tendermint.rs index 2f3d2b924a..4a581774e6 100644 --- a/validator/src/network/tendermint.rs +++ b/validator/src/network/tendermint.rs @@ -21,8 +21,8 @@ impl Abci { Abci { count: 0 } } - pub fn run(self) { - tokio::spawn(async { abci::run_local(self) }) + pub async fn run(self) { + abci::run_local(self); } } diff --git a/validator/src/services/mixmining/health_check_runner.rs b/validator/src/services/mixmining/health_check_runner.rs index c2bd6c7b18..42a3821cea 100644 --- a/validator/src/services/mixmining/health_check_runner.rs +++ b/validator/src/services/mixmining/health_check_runner.rs @@ -24,27 +24,25 @@ impl HealthCheckRunner { } pub async fn run(self) { - tokio::spawn(async move { - let healthcheck_interval = Duration::from_secs_f64(self.interval); - debug!("healthcheck will run every {:?}", healthcheck_interval); - loop { - let full_topology = - directory_client::presence::Topology::new(self.directory_server.clone()); - let version_filtered_topology = full_topology.filter_node_versions( - crate::built_info::PKG_VERSION, - crate::built_info::PKG_VERSION, - crate::built_info::PKG_VERSION, - ); - match self - .health_checker - .do_check(&version_filtered_topology) - .await - { - Ok(health) => info!("current network health: \n{}", health), - Err(err) => error!("failed to perform healthcheck - {:?}", err), - }; - tokio::time::delay_for(healthcheck_interval).await; - } - }); + let healthcheck_interval = Duration::from_secs_f64(self.interval); + debug!("healthcheck will run every {:?}", healthcheck_interval); + loop { + let full_topology = + directory_client::presence::Topology::new(self.directory_server.clone()); + let version_filtered_topology = full_topology.filter_node_versions( + crate::built_info::PKG_VERSION, + crate::built_info::PKG_VERSION, + crate::built_info::PKG_VERSION, + ); + match self + .health_checker + .do_check(&version_filtered_topology) + .await + { + Ok(health) => info!("current network health: \n{}", health), + Err(err) => error!("failed to perform healthcheck - {:?}", err), + }; + tokio::time::delay_for(healthcheck_interval).await; + } } } From 46055fe8c494cdc997304a5690619a9662d4ab1a Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 30 Jan 2020 11:33:04 +0000 Subject: [PATCH 039/273] validator: removing unused config --- validator/src/validator.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 1ba280dae7..b4a3e2d0e0 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -14,7 +14,6 @@ pub struct Config { // allow for a generic validator pub struct Validator { - config: Config, #[allow(dead_code)] identity_keypair: IDPair, health_check_runner: health_check_runner::HealthCheckRunner, @@ -40,7 +39,6 @@ impl Validator { Validator { identity_keypair: dummy_keypair, health_check_runner, - config, tendermint_abci: tendermint::Abci::new(), } } From c426b09b07f40363674bc6bd6ea9ef07ff5e7e8e Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 30 Jan 2020 11:50:37 +0000 Subject: [PATCH 040/273] validator: placeholder comment for Ethereum integration --- validator/src/network/ethereum.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/validator/src/network/ethereum.rs b/validator/src/network/ethereum.rs index e69de29bb2..9aa85267fe 100644 --- a/validator/src/network/ethereum.rs +++ b/validator/src/network/ethereum.rs @@ -0,0 +1 @@ +// placeholder for Ethereum / ERC20 bridge integration From d3199778e4745424a00b9ef1902b320fe0c0d76d Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 30 Jan 2020 11:55:15 +0000 Subject: [PATCH 041/273] validator: starting removal of keypair generics --- .../src/services/mixmining/health_check_runner.rs | 12 ++++++------ validator/src/validator.rs | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/validator/src/services/mixmining/health_check_runner.rs b/validator/src/services/mixmining/health_check_runner.rs index 42a3821cea..b53162273f 100644 --- a/validator/src/services/mixmining/health_check_runner.rs +++ b/validator/src/services/mixmining/health_check_runner.rs @@ -1,21 +1,21 @@ -use crypto::identity::MixnetIdentityKeyPair; +use crypto::identity::DummyMixIdentityKeyPair; use healthcheck::HealthChecker; use log::*; use std::time::Duration; use topology::NymTopology; -pub struct HealthCheckRunner { +pub struct HealthCheckRunner { directory_server: String, - health_checker: HealthChecker, + health_checker: HealthChecker, interval: f64, } -impl HealthCheckRunner { +impl HealthCheckRunner { pub fn new( directory_server: String, interval: f64, - health_checker: HealthChecker, - ) -> HealthCheckRunner { + health_checker: HealthChecker, + ) -> HealthCheckRunner { HealthCheckRunner { directory_server, health_checker, diff --git a/validator/src/validator.rs b/validator/src/validator.rs index b4a3e2d0e0..6cdc6f6fc3 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -13,15 +13,15 @@ pub struct Config { } // allow for a generic validator -pub struct Validator { +pub struct Validator { #[allow(dead_code)] - identity_keypair: IDPair, - health_check_runner: health_check_runner::HealthCheckRunner, + identity_keypair: DummyMixIdentityKeyPair, + health_check_runner: health_check_runner::HealthCheckRunner, tendermint_abci: tendermint::Abci, } // but for time being, since it's a dummy one, have it use dummy keys -impl Validator { +impl Validator { pub fn new(config: Config) -> Self { let dummy_keypair = DummyMixIdentityKeyPair::new(); let hc = HealthChecker::new( From 6b879e88e77ffb0137192c99453533c74dfbc3a7 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 30 Jan 2020 12:19:23 +0000 Subject: [PATCH 042/273] nym: removing type parameters on keypairs --- common/crypto/src/identity/mod.rs | 125 ++++++------------ common/healthcheck/src/lib.rs | 10 +- common/healthcheck/src/path_check.rs | 6 +- common/healthcheck/src/result.rs | 12 +- common/pemstore/src/pemstore.rs | 12 +- nym-client/src/client/mod.rs | 29 ++-- nym-client/src/client/topology_control.rs | 18 +-- nym-client/src/commands/init.rs | 4 +- nym-client/src/commands/tcpsocket.rs | 4 +- nym-client/src/commands/websocket.rs | 4 +- sfw-provider/src/main.rs | 4 +- .../src/provider/client_handling/mod.rs | 14 +- sfw-provider/src/provider/mix_handling/mod.rs | 6 +- sfw-provider/src/provider/mod.rs | 14 +- sfw-provider/src/provider/presence.rs | 4 +- .../services/mixmining/health_check_runner.rs | 5 +- validator/src/validator.rs | 6 +- 17 files changed, 102 insertions(+), 175 deletions(-) diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index 3c920f84dc..a966ec4cb9 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -6,90 +6,44 @@ use bs58; use curve25519_dalek::scalar::Scalar; use sphinx::route::DestinationAddressBytes; -pub trait MixnetIdentityKeyPair: Clone { - type PublicKeyMaterial: MixnetIdentityPublicKey; - type PrivateKeyMaterial: MixnetIdentityPrivateKey; - - fn new() -> Self; - fn private_key(&self) -> &Self::PrivateKeyMaterial; - fn public_key(&self) -> &Self::PublicKeyMaterial; - fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self; - - // TODO: signing related methods -} - -pub trait MixnetIdentityPublicKey: - Sized - + PemStorable - + Clone - + for<'a> From<&'a ::PrivateKeyMaterial> -{ - // we need to couple public and private keys together - type PrivateKeyMaterial: MixnetIdentityPrivateKey; - - fn derive_address(&self) -> DestinationAddressBytes; - fn to_bytes(&self) -> Vec; - fn from_bytes(b: &[u8]) -> Self; -} - -pub trait MixnetIdentityPrivateKey: Sized + PemStorable + Clone { - // we need to couple public and private keys together - type PublicKeyMaterial: MixnetIdentityPublicKey; - - /// Returns the associated public key - fn public_key(&self) -> Self::PublicKeyMaterial { - self.into() - } - - fn to_bytes(&self) -> Vec; - fn from_bytes(b: &[u8]) -> Self; -} - -// same for validator - // for time being define a dummy identity using x25519 encryption keys (as we've done so far) // and replace it with proper keys, like ed25519 later on #[derive(Clone)] -pub struct DummyMixIdentityKeyPair { - pub private_key: DummyMixIdentityPrivateKey, - pub public_key: DummyMixIdentityPublicKey, +pub struct MixIdentityKeyPair { + pub private_key: MixIdentityPrivateKey, + pub public_key: MixIdentityPublicKey, } -impl MixnetIdentityKeyPair for DummyMixIdentityKeyPair { - type PublicKeyMaterial = DummyMixIdentityPublicKey; - type PrivateKeyMaterial = DummyMixIdentityPrivateKey; - - fn new() -> Self { +impl MixIdentityKeyPair { + pub fn new() -> Self { let keypair = encryption::x25519::KeyPair::new(); - DummyMixIdentityKeyPair { - private_key: DummyMixIdentityPrivateKey(keypair.private_key), - public_key: DummyMixIdentityPublicKey(keypair.public_key), + MixIdentityKeyPair { + private_key: MixIdentityPrivateKey(keypair.private_key), + public_key: MixIdentityPublicKey(keypair.public_key), } } - fn private_key(&self) -> &DummyMixIdentityPrivateKey { + pub fn private_key(&self) -> &MixIdentityPrivateKey { &self.private_key } - fn public_key(&self) -> &DummyMixIdentityPublicKey { + pub fn public_key(&self) -> &MixIdentityPublicKey { &self.public_key } - fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self { - DummyMixIdentityKeyPair { - private_key: DummyMixIdentityPrivateKey::from_bytes(priv_bytes), - public_key: DummyMixIdentityPublicKey::from_bytes(pub_bytes), + pub fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self { + MixIdentityKeyPair { + private_key: MixIdentityPrivateKey::from_bytes(priv_bytes), + public_key: MixIdentityPublicKey::from_bytes(pub_bytes), } } } #[derive(Debug, Clone, Eq, PartialEq)] -pub struct DummyMixIdentityPublicKey(encryption::x25519::PublicKey); +pub struct MixIdentityPublicKey(encryption::x25519::PublicKey); -impl MixnetIdentityPublicKey for DummyMixIdentityPublicKey { - type PrivateKeyMaterial = DummyMixIdentityPrivateKey; - - fn derive_address(&self) -> DestinationAddressBytes { +impl MixIdentityPublicKey { + pub fn derive_address(&self) -> DestinationAddressBytes { let mut temporary_address = [0u8; 32]; let public_key_bytes = self.to_bytes(); temporary_address.copy_from_slice(&public_key_bytes[..]); @@ -97,65 +51,60 @@ impl MixnetIdentityPublicKey for DummyMixIdentityPublicKey { temporary_address } - fn to_bytes(&self) -> Vec { + pub fn to_bytes(&self) -> Vec { self.0.to_bytes() } - fn from_bytes(b: &[u8]) -> Self { + pub fn from_bytes(b: &[u8]) -> Self { Self(encryption::x25519::PublicKey::from_bytes(b)) } + + pub fn to_base58_string(&self) -> String { + bs58::encode(&self.to_bytes()).into_string() + } + + pub fn from_base58_string(val: String) -> Self { + Self::from_bytes(&bs58::decode(&val).into_vec().unwrap()) + } } -impl PemStorable for DummyMixIdentityPublicKey { +impl PemStorable for MixIdentityPublicKey { fn pem_type(&self) -> String { format!("DUMMY KEY BASED ON {}", self.0.pem_type()) } } -impl DummyMixIdentityPublicKey { - pub fn to_base58_string(&self) -> String { - bs58::encode(&self.to_bytes()).into_string() - } - - #[allow(dead_code)] - fn from_base58_string(val: String) -> Self { - Self::from_bytes(&bs58::decode(&val).into_vec().unwrap()) - } -} - // COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed #[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub struct DummyMixIdentityPrivateKey(pub encryption::x25519::PrivateKey); +pub struct MixIdentityPrivateKey(pub encryption::x25519::PrivateKey); -impl<'a> From<&'a DummyMixIdentityPrivateKey> for DummyMixIdentityPublicKey { - fn from(pk: &'a DummyMixIdentityPrivateKey) -> Self { +impl<'a> From<&'a MixIdentityPrivateKey> for MixIdentityPublicKey { + fn from(pk: &'a MixIdentityPrivateKey) -> Self { let private_ref = &pk.0; let public: encryption::x25519::PublicKey = private_ref.into(); - DummyMixIdentityPublicKey(public) + MixIdentityPublicKey(public) } } -impl MixnetIdentityPrivateKey for DummyMixIdentityPrivateKey { - type PublicKeyMaterial = DummyMixIdentityPublicKey; - - fn to_bytes(&self) -> Vec { +impl MixIdentityPrivateKey { + pub fn to_bytes(&self) -> Vec { self.0.to_bytes() } - fn from_bytes(b: &[u8]) -> Self { + pub fn from_bytes(b: &[u8]) -> Self { Self(encryption::x25519::PrivateKey::from_bytes(b)) } } // TODO: this will be implemented differently by using the proper trait -impl DummyMixIdentityPrivateKey { +impl MixIdentityPrivateKey { pub fn as_scalar(self) -> Scalar { let encryption_key = self.0; encryption_key.0 } } -impl PemStorable for DummyMixIdentityPrivateKey { +impl PemStorable for MixIdentityPrivateKey { fn pem_type(&self) -> String { format!("DUMMY KEY BASED ON {}", self.0.pem_type()) } diff --git a/common/healthcheck/src/lib.rs b/common/healthcheck/src/lib.rs index 5554b2bb97..fc4be5850e 100644 --- a/common/healthcheck/src/lib.rs +++ b/common/healthcheck/src/lib.rs @@ -1,5 +1,5 @@ use crate::result::HealthCheckResult; -use crypto::identity::MixnetIdentityKeyPair; +use crypto::identity::MixIdentityKeyPair; use log::trace; use std::fmt::{Error, Formatter}; use std::time::Duration; @@ -33,17 +33,17 @@ impl From for HealthCheckerError { } } -pub struct HealthChecker { +pub struct HealthChecker { num_test_packets: usize, resolution_timeout: Duration, - identity_keypair: IDPair, + identity_keypair: MixIdentityKeyPair, } -impl HealthChecker { +impl HealthChecker { pub fn new( resolution_timeout_f64: f64, num_test_packets: usize, - identity_keypair: IDPair, + identity_keypair: MixIdentityKeyPair, ) -> Self { HealthChecker { resolution_timeout: Duration::from_secs_f64(resolution_timeout_f64), diff --git a/common/healthcheck/src/path_check.rs b/common/healthcheck/src/path_check.rs index a345e82d99..074df7da44 100644 --- a/common/healthcheck/src/path_check.rs +++ b/common/healthcheck/src/path_check.rs @@ -1,4 +1,4 @@ -use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPublicKey}; +use crypto::identity::MixIdentityKeyPair; use itertools::Itertools; use log::{debug, error, info, trace, warn}; use mix_client::MixClient; @@ -27,9 +27,9 @@ pub(crate) struct PathChecker { } impl PathChecker { - pub(crate) async fn new( + pub(crate) async fn new( providers: Vec, - identity_keys: &IDPair, + identity_keys: &MixIdentityKeyPair, check_id: [u8; 16], ) -> Self { let mut provider_clients = HashMap::new(); diff --git a/common/healthcheck/src/result.rs b/common/healthcheck/src/result.rs index 2dd199ffde..677114a457 100644 --- a/common/healthcheck/src/result.rs +++ b/common/healthcheck/src/result.rs @@ -1,6 +1,6 @@ use crate::path_check::{PathChecker, PathStatus}; use crate::score::NodeScore; -use crypto::identity::MixnetIdentityKeyPair; +use crypto::identity::MixIdentityKeyPair; use log::{debug, error, info, warn}; use rand_os::rand_core::RngCore; use sphinx::route::NodeAddressBytes; @@ -102,16 +102,12 @@ impl HealthCheckResult { id } - pub async fn calculate( + pub async fn calculate( topology: &T, iterations: usize, resolution_timeout: Duration, - identity_keys: &IDPair, - ) -> Self - where - T: NymTopology, - IDPair: MixnetIdentityKeyPair, - { + identity_keys: &MixIdentityKeyPair, + ) -> Self { // currently healthchecker supports only up to 255 iterations - if we somehow // find we need more, it's relatively easy change assert!(iterations <= 255); diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index 84bc8cf60a..a8d7b4b239 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -1,5 +1,5 @@ use crate::pathfinder::PathFinder; -use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPrivateKey, MixnetIdentityPublicKey}; +use crypto::identity::MixIdentityKeyPair; use crypto::PemStorable; use pem::{encode, parse, Pem}; use std::fs::File; @@ -27,11 +27,12 @@ impl PemStore { } } - pub fn read_identity(&self) -> io::Result { + pub fn read_identity(&self) -> io::Result { let private_pem = self.read_pem_file(self.private_mix_key.clone())?; let public_pem = self.read_pem_file(self.public_mix_key.clone())?; - let key_pair = IDPair::from_bytes(&private_pem.contents, &public_pem.contents); + let key_pair = + MixIdentityKeyPair::from_bytes(&private_pem.contents, &public_pem.contents); if key_pair.private_key().pem_type() != private_pem.tag { return Err(io::Error::new( @@ -59,10 +60,7 @@ impl PemStore { // This should be refactored and made more generic for when we have other kinds of // KeyPairs that we want to persist (e.g. validator keypairs, or keys for // signing vs encryption). However, for the moment, it does the job. - pub fn write_identity( - &self, - key_pair: IDPair, - ) -> io::Result<()> { + pub fn write_identity(&self, key_pair: MixIdentityKeyPair) -> io::Result<()> { std::fs::create_dir_all(self.config_dir.clone())?; let private_key = key_pair.private_key(); diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 8bb090b95f..af074e3b4f 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -3,7 +3,7 @@ use crate::client::received_buffer::ReceivedMessagesBuffer; use crate::client::topology_control::TopologyInnerRef; use crate::sockets::tcp; use crate::sockets::ws; -use crypto::identity::{MixnetIdentityKeyPair, MixnetIdentityPublicKey}; +use crypto::identity::MixIdentityKeyPair; use directory_client::presence::Topology; use futures::channel::mpsc; use futures::join; @@ -36,11 +36,8 @@ pub enum SocketType { None, } -pub struct NymClient -where - IDPair: MixnetIdentityKeyPair + Send + Sync, -{ - keypair: IDPair, +pub struct NymClient { + keypair: MixIdentityKeyPair, // to be used by "send" function or socket, etc pub input_tx: mpsc::UnboundedSender, @@ -55,12 +52,9 @@ where #[derive(Debug)] pub struct InputMessage(pub Destination, pub Vec); -impl NymClient -where - IDPair: 'static + MixnetIdentityKeyPair + Send + Sync, -{ +impl NymClient { pub fn new( - keypair: IDPair, + keypair: MixIdentityKeyPair, socket_listening_address: SocketAddr, directory: String, auth_token: Option, @@ -115,15 +109,14 @@ where let self_address = self.keypair.public_key().derive_address(); // generate same type of keys we have as our identity - let healthcheck_keys = IDPair::new(); + let healthcheck_keys = MixIdentityKeyPair::new(); // TODO: when we switch to our graph topology, we need to remember to change 'Topology' type - let topology_controller = - rt.block_on(topology_control::TopologyControl::::new( - self.directory.clone(), - TOPOLOGY_REFRESH_RATE, - healthcheck_keys, - )); + let topology_controller = rt.block_on(topology_control::TopologyControl::::new( + self.directory.clone(), + TOPOLOGY_REFRESH_RATE, + healthcheck_keys, + )); let provider_client_listener_address = rt.block_on(self.get_provider_socket_address(topology_controller.get_inner_ref())); diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index 5ec9422782..90c84679cc 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -1,5 +1,5 @@ use crate::built_info; -use crypto::identity::MixnetIdentityKeyPair; +use crypto::identity::MixIdentityKeyPair; use healthcheck::HealthChecker; use log::{error, info, trace, warn}; use std::sync::Arc; @@ -12,14 +12,10 @@ const NODE_HEALTH_THRESHOLD: f64 = 0.0; // auxiliary type for ease of use pub type TopologyInnerRef = Arc>>; -pub(crate) struct TopologyControl -where - T: NymTopology, - IDPair: MixnetIdentityKeyPair, -{ +pub(crate) struct TopologyControl { directory_server: String, inner: Arc>>, - health_checker: HealthChecker, + health_checker: HealthChecker, refresh_rate: f64, } @@ -29,15 +25,11 @@ enum TopologyError { NoValidPathsError, } -impl TopologyControl -where - T: NymTopology, - IDPair: MixnetIdentityKeyPair, -{ +impl TopologyControl { pub(crate) async fn new( directory_server: String, refresh_rate: f64, - identity_keypair: IDPair, + identity_keypair: MixIdentityKeyPair, ) -> Self { // this is a temporary solution as the healthcheck will eventually be moved to validators let health_checker = healthcheck::HealthChecker::new(5.0, 2, identity_keypair); diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index bc4403e6b9..1ee2385ded 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -1,6 +1,6 @@ use crate::config::persistance::pathfinder::ClientPathfinder; use clap::ArgMatches; -use crypto::identity::MixnetIdentityKeyPair; +use crypto::identity::MixIdentityKeyPair; use pemstore::pemstore::PemStore; pub fn execute(matches: &ArgMatches) { @@ -10,7 +10,7 @@ pub fn execute(matches: &ArgMatches) { let pathfinder = ClientPathfinder::new(id); println!("Writing keypairs to {:?}...", pathfinder.config_dir); - let mix_keys = crypto::identity::DummyMixIdentityKeyPair::new(); + let mix_keys = MixIdentityKeyPair::new(); let pem_store = PemStore::new(pathfinder); pem_store.write_identity(mix_keys).unwrap(); diff --git a/nym-client/src/commands/tcpsocket.rs b/nym-client/src/commands/tcpsocket.rs index 7a98a91681..5dce285c0b 100644 --- a/nym-client/src/commands/tcpsocket.rs +++ b/nym-client/src/commands/tcpsocket.rs @@ -1,7 +1,7 @@ use crate::client::{NymClient, SocketType}; use crate::config::persistance::pathfinder::ClientPathfinder; use clap::ArgMatches; -use crypto::identity::DummyMixIdentityKeyPair; +use crypto::identity::MixIdentityKeyPair; use pemstore::pemstore::PemStore; use std::net::ToSocketAddrs; @@ -27,7 +27,7 @@ pub fn execute(matches: &ArgMatches) { .expect("Failed to extract the socket address from the iterator"); // TODO: currently we know we are reading the 'DummyMixIdentityKeyPair', but how to properly assert the type? - let keypair: DummyMixIdentityKeyPair = PemStore::new(ClientPathfinder::new(id)) + let keypair: MixIdentityKeyPair = PemStore::new(ClientPathfinder::new(id)) .read_identity() .unwrap(); // TODO: reading auth_token from disk (if exists); diff --git a/nym-client/src/commands/websocket.rs b/nym-client/src/commands/websocket.rs index 3193c1ffde..bcb375761e 100644 --- a/nym-client/src/commands/websocket.rs +++ b/nym-client/src/commands/websocket.rs @@ -1,7 +1,7 @@ use crate::client::{NymClient, SocketType}; use crate::config::persistance::pathfinder::ClientPathfinder; use clap::ArgMatches; -use crypto::identity::DummyMixIdentityKeyPair; +use crypto::identity::MixIdentityKeyPair; use pemstore::pemstore::PemStore; use std::net::ToSocketAddrs; @@ -27,7 +27,7 @@ pub fn execute(matches: &ArgMatches) { .expect("Failed to extract the socket address from the iterator"); // TODO: currently we know we are reading the 'DummyMixIdentityKeyPair', but how to properly assert the type? - let keypair: DummyMixIdentityKeyPair = PemStore::new(ClientPathfinder::new(id)) + let keypair: MixIdentityKeyPair = PemStore::new(ClientPathfinder::new(id)) .read_identity() .unwrap(); diff --git a/sfw-provider/src/main.rs b/sfw-provider/src/main.rs index 6cd945a2c0..ec2dee141e 100644 --- a/sfw-provider/src/main.rs +++ b/sfw-provider/src/main.rs @@ -1,6 +1,6 @@ use crate::provider::ServiceProvider; use clap::{App, Arg, ArgMatches, SubCommand}; -use crypto::identity::MixnetIdentityKeyPair; +use crypto::identity::MixIdentityKeyPair; use log::error; use std::net::ToSocketAddrs; use std::path::PathBuf; @@ -121,7 +121,7 @@ fn new_config(matches: &ArgMatches) -> provider::Config { print_binding_warning(client_host); } - let key_pair = crypto::identity::DummyMixIdentityKeyPair::new(); // TODO: persist this so keypairs don't change every restart + let key_pair = crypto::identity::MixIdentityKeyPair::new(); // TODO: persist this so keypairs don't change every restart let store_dir = PathBuf::from( matches .value_of("storeDir") diff --git a/sfw-provider/src/provider/client_handling/mod.rs b/sfw-provider/src/provider/client_handling/mod.rs index e3b9dcf86e..67a2658ebe 100644 --- a/sfw-provider/src/provider/client_handling/mod.rs +++ b/sfw-provider/src/provider/client_handling/mod.rs @@ -1,6 +1,6 @@ use crate::provider::storage::{ClientStorage, StoreError}; use crate::provider::ClientLedger; -use crypto::identity::{DummyMixIdentityPrivateKey, MixnetIdentityPrivateKey}; +use crypto::identity::MixIdentityPrivateKey; use futures::lock::Mutex as FMutex; use hmac::{Hmac, Mac}; use log::*; @@ -54,14 +54,14 @@ impl From for ClientProcessingError { pub(crate) struct ClientProcessingData { store_dir: PathBuf, registered_clients_ledger: Arc>, - secret_key: DummyMixIdentityPrivateKey, + secret_key: MixIdentityPrivateKey, } impl ClientProcessingData { pub(crate) fn new( store_dir: PathBuf, registered_clients_ledger: Arc>, - secret_key: DummyMixIdentityPrivateKey, + secret_key: MixIdentityPrivateKey, ) -> Self { ClientProcessingData { store_dir, @@ -153,7 +153,7 @@ impl ClientRequestProcessor { std::fs::create_dir_all(full_store_dir) } - fn generate_new_auth_token(data: Vec, key: DummyMixIdentityPrivateKey) -> AuthToken { + fn generate_new_auth_token(data: Vec, key: MixIdentityPrivateKey) -> AuthToken { // also note that `new_varkey` doesn't even have an execution branch returning an error let mut auth_token_raw = HmacSha256::new_varkey(&key.to_bytes()) .expect("HMAC should be able take key of any size"); @@ -249,7 +249,7 @@ mod generating_new_auth_token { fn for_the_same_input_generates_the_same_auth_token() { let data1 = vec![1u8; 55]; let data2 = vec![1u8; 55]; - let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]); + let key = MixIdentityPrivateKey::from_bytes(&[1u8; 32]); let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key); let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key); assert_eq!(token1, token2); @@ -259,14 +259,14 @@ mod generating_new_auth_token { fn for_different_inputs_generates_different_auth_tokens() { let data1 = vec![1u8; 55]; let data2 = vec![2u8; 55]; - let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]); + let key = MixIdentityPrivateKey::from_bytes(&[1u8; 32]); let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key); let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key); assert_ne!(token1, token2); let data1 = vec![1u8; 50]; let data2 = vec![2u8; 55]; - let key = DummyMixIdentityPrivateKey::from_bytes(&[1u8; 32]); + let key = MixIdentityPrivateKey::from_bytes(&[1u8; 32]); let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key); let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key); assert_ne!(token1, token2); diff --git a/sfw-provider/src/provider/mix_handling/mod.rs b/sfw-provider/src/provider/mix_handling/mod.rs index 338943efd1..60ceaf700e 100644 --- a/sfw-provider/src/provider/mix_handling/mod.rs +++ b/sfw-provider/src/provider/mix_handling/mod.rs @@ -1,5 +1,5 @@ use crate::provider::storage::StoreData; -use crypto::identity::DummyMixIdentityPrivateKey; +use crypto::identity::MixIdentityPrivateKey; use log::{error, warn}; use sphinx::{ProcessedPacket, SphinxPacket}; use std::path::PathBuf; @@ -37,12 +37,12 @@ impl From for MixProcessingError { // ProcessingData defines all data required to correctly unwrap sphinx packets #[derive(Debug, Clone)] pub(crate) struct MixProcessingData { - secret_key: DummyMixIdentityPrivateKey, + secret_key: MixIdentityPrivateKey, pub(crate) store_dir: PathBuf, } impl MixProcessingData { - pub(crate) fn new(secret_key: DummyMixIdentityPrivateKey, store_dir: PathBuf) -> Self { + pub(crate) fn new(secret_key: MixIdentityPrivateKey, store_dir: PathBuf) -> Self { MixProcessingData { secret_key, store_dir, diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 4e16e5a2a2..56085917aa 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -1,7 +1,7 @@ use crate::provider::client_handling::{ClientProcessingData, ClientRequestProcessor}; use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData}; use crate::provider::storage::ClientStorage; -use crypto::identity::{DummyMixIdentityPrivateKey, DummyMixIdentityPublicKey}; +use crypto::identity::{MixIdentityPrivateKey, MixIdentityPublicKey}; use directory_client::presence::providers::MixProviderClient; use futures::io::Error; use futures::lock::Mutex as FMutex; @@ -29,8 +29,8 @@ pub struct Config { pub client_socket_address: SocketAddr, pub directory_server: String, pub mix_socket_address: SocketAddr, - pub public_key: DummyMixIdentityPublicKey, - pub secret_key: DummyMixIdentityPrivateKey, + pub public_key: MixIdentityPublicKey, + pub secret_key: MixIdentityPrivateKey, pub store_dir: PathBuf, } @@ -102,8 +102,8 @@ pub struct ServiceProvider { directory_server: String, mix_network_address: SocketAddr, client_network_address: SocketAddr, - public_key: DummyMixIdentityPublicKey, - secret_key: DummyMixIdentityPrivateKey, + public_key: MixIdentityPublicKey, + secret_key: MixIdentityPrivateKey, store_dir: PathBuf, registered_clients_ledger: ClientLedger, } @@ -236,7 +236,7 @@ impl ServiceProvider { async fn start_mixnet_listening( address: SocketAddr, - secret_key: DummyMixIdentityPrivateKey, + secret_key: MixIdentityPrivateKey, store_dir: PathBuf, ) -> Result<(), ProviderError> { let mut listener = tokio::net::TcpListener::bind(address).await?; @@ -258,7 +258,7 @@ impl ServiceProvider { address: SocketAddr, store_dir: PathBuf, client_ledger: Arc>, - secret_key: DummyMixIdentityPrivateKey, + secret_key: MixIdentityPrivateKey, ) -> Result<(), ProviderError> { let mut listener = tokio::net::TcpListener::bind(address).await?; let processing_data = diff --git a/sfw-provider/src/provider/presence.rs b/sfw-provider/src/provider/presence.rs index adfcebaddc..77945cb096 100644 --- a/sfw-provider/src/provider/presence.rs +++ b/sfw-provider/src/provider/presence.rs @@ -1,5 +1,5 @@ use crate::provider::ClientLedger; -use crypto::identity::DummyMixIdentityPublicKey; +use crypto::identity::MixIdentityPublicKey; use directory_client::presence::providers::MixProviderPresence; use directory_client::requests::presence_providers_post::PresenceMixProviderPoster; use directory_client::DirectoryClient; @@ -22,7 +22,7 @@ impl Notifier { directory_server_address: String, client_listener: SocketAddr, mixnet_listener: SocketAddr, - pub_key: DummyMixIdentityPublicKey, + pub_key: MixIdentityPublicKey, client_ledger: Arc>, ) -> Notifier { let directory_config = directory_client::Config { diff --git a/validator/src/services/mixmining/health_check_runner.rs b/validator/src/services/mixmining/health_check_runner.rs index b53162273f..1200c6fe4f 100644 --- a/validator/src/services/mixmining/health_check_runner.rs +++ b/validator/src/services/mixmining/health_check_runner.rs @@ -1,4 +1,3 @@ -use crypto::identity::DummyMixIdentityKeyPair; use healthcheck::HealthChecker; use log::*; use std::time::Duration; @@ -6,7 +5,7 @@ use topology::NymTopology; pub struct HealthCheckRunner { directory_server: String, - health_checker: HealthChecker, + health_checker: HealthChecker, interval: f64, } @@ -14,7 +13,7 @@ impl HealthCheckRunner { pub fn new( directory_server: String, interval: f64, - health_checker: HealthChecker, + health_checker: HealthChecker, ) -> HealthCheckRunner { HealthCheckRunner { directory_server, diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 6cdc6f6fc3..f793440bcc 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -1,6 +1,6 @@ use crate::network::tendermint; use crate::services::mixmining::health_check_runner; -use crypto::identity::{DummyMixIdentityKeyPair, MixnetIdentityKeyPair}; +use crypto::identity::MixIdentityKeyPair; use healthcheck::HealthChecker; use tokio::runtime::Runtime; @@ -15,7 +15,7 @@ pub struct Config { // allow for a generic validator pub struct Validator { #[allow(dead_code)] - identity_keypair: DummyMixIdentityKeyPair, + identity_keypair: MixIdentityKeyPair, health_check_runner: health_check_runner::HealthCheckRunner, tendermint_abci: tendermint::Abci, } @@ -23,7 +23,7 @@ pub struct Validator { // but for time being, since it's a dummy one, have it use dummy keys impl Validator { pub fn new(config: Config) -> Self { - let dummy_keypair = DummyMixIdentityKeyPair::new(); + let dummy_keypair = MixIdentityKeyPair::new(); let hc = HealthChecker::new( config.health_check.resolution_timeout, config.health_check.num_test_packets, From 9797b8da74072b044a3a193b93b1fa3a48403bc1 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 30 Jan 2020 12:27:00 +0000 Subject: [PATCH 043/273] crypto: simplified keypairs a bit --- common/crypto/src/encryption/mod.rs | 111 ++++++++++++++++++------- common/crypto/src/encryption/x25519.rs | 99 ---------------------- common/crypto/src/identity/mod.rs | 15 ++-- common/pemstore/src/pemstore.rs | 5 +- sfw-provider/src/main.rs | 1 - 5 files changed, 90 insertions(+), 141 deletions(-) delete mode 100644 common/crypto/src/encryption/x25519.rs diff --git a/common/crypto/src/encryption/mod.rs b/common/crypto/src/encryption/mod.rs index b36ddf1af7..4a38cd89ed 100644 --- a/common/crypto/src/encryption/mod.rs +++ b/common/crypto/src/encryption/mod.rs @@ -1,39 +1,92 @@ use crate::PemStorable; +use curve25519_dalek::montgomery::MontgomeryPoint; +use curve25519_dalek::scalar::Scalar; -pub mod x25519; +// TODO: ensure this is a proper name for this considering we are not implementing entire DH here -pub trait MixnetEncryptionKeyPair -where - Priv: MixnetEncryptionPrivateKey, - Pub: MixnetEncryptionPublicKey, -{ - fn new() -> Self; - fn private_key(&self) -> &Priv; - fn public_key(&self) -> &Pub; - fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self; +const CURVE_GENERATOR: MontgomeryPoint = curve25519_dalek::constants::X25519_BASEPOINT; - // TODO: encryption related methods +pub struct KeyPair { + pub(crate) private_key: PrivateKey, + pub(crate) public_key: PublicKey, } -pub trait MixnetEncryptionPublicKey: - Sized + PemStorable + for<'a> From<&'a ::PrivateKeyMaterial> -{ - // we need to couple public and private keys together - type PrivateKeyMaterial: MixnetEncryptionPrivateKey; +impl KeyPair { + pub fn new() -> Self { + let mut rng = rand_os::OsRng::new().unwrap(); + let private_key_value = Scalar::random(&mut rng); + let public_key_value = CURVE_GENERATOR * private_key_value; - fn to_bytes(&self) -> Vec; - fn from_bytes(b: &[u8]) -> Self; -} - -pub trait MixnetEncryptionPrivateKey: Sized + PemStorable { - // we need to couple public and private keys together - type PublicKeyMaterial: MixnetEncryptionPublicKey; - - /// Returns the associated public key - fn public_key(&self) -> Self::PublicKeyMaterial { - self.into() + KeyPair { + private_key: PrivateKey(private_key_value), + public_key: PublicKey(public_key_value), + } } - fn to_bytes(&self) -> Vec; - fn from_bytes(b: &[u8]) -> Self; + pub fn private_key(&self) -> &PrivateKey { + &self.private_key + } + + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + + pub fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self { + KeyPair { + private_key: PrivateKey::from_bytes(priv_bytes), + public_key: PublicKey::from_bytes(pub_bytes), + } + } +} + +// COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct PrivateKey(pub Scalar); + +impl PrivateKey { + pub fn to_bytes(&self) -> Vec { + self.0.to_bytes().to_vec() + } + + pub fn from_bytes(b: &[u8]) -> Self { + let mut bytes = [0; 32]; + bytes.copy_from_slice(&b[..]); + // due to trait restriction we have no choice but to panic if this fails + let key = Scalar::from_canonical_bytes(bytes).unwrap(); + Self(key) + } +} + +impl PemStorable for PrivateKey { + fn pem_type(&self) -> String { + String::from("X25519 PRIVATE KEY") + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct PublicKey(pub MontgomeryPoint); + +impl<'a> From<&'a PrivateKey> for PublicKey { + fn from(pk: &'a PrivateKey) -> Self { + PublicKey(CURVE_GENERATOR * pk.0) + } +} + +impl PublicKey { + pub fn to_bytes(&self) -> Vec { + self.0.to_bytes().to_vec() + } + + pub fn from_bytes(b: &[u8]) -> Self { + let mut bytes = [0; 32]; + bytes.copy_from_slice(&b[..]); + let key = MontgomeryPoint(bytes); + Self(key) + } +} + +impl PemStorable for PublicKey { + fn pem_type(&self) -> String { + String::from("X25519 PUBLIC KEY") + } } diff --git a/common/crypto/src/encryption/x25519.rs b/common/crypto/src/encryption/x25519.rs deleted file mode 100644 index 44d86783da..0000000000 --- a/common/crypto/src/encryption/x25519.rs +++ /dev/null @@ -1,99 +0,0 @@ -use crate::encryption::{ - MixnetEncryptionKeyPair, MixnetEncryptionPrivateKey, MixnetEncryptionPublicKey, -}; -use crate::PemStorable; -use curve25519_dalek::montgomery::MontgomeryPoint; -use curve25519_dalek::scalar::Scalar; - -// TODO: ensure this is a proper name for this considering we are not implementing entire DH here - -const CURVE_GENERATOR: MontgomeryPoint = curve25519_dalek::constants::X25519_BASEPOINT; - -pub struct KeyPair { - pub(crate) private_key: PrivateKey, - pub(crate) public_key: PublicKey, -} - -impl MixnetEncryptionKeyPair for KeyPair { - fn new() -> Self { - let mut rng = rand_os::OsRng::new().unwrap(); - let private_key_value = Scalar::random(&mut rng); - let public_key_value = CURVE_GENERATOR * private_key_value; - - KeyPair { - private_key: PrivateKey(private_key_value), - public_key: PublicKey(public_key_value), - } - } - - fn private_key(&self) -> &PrivateKey { - &self.private_key - } - - fn public_key(&self) -> &PublicKey { - &self.public_key - } - - fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self { - KeyPair { - private_key: PrivateKey::from_bytes(priv_bytes), - public_key: PublicKey::from_bytes(pub_bytes), - } - } -} - -// COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub struct PrivateKey(pub Scalar); - -impl MixnetEncryptionPrivateKey for PrivateKey { - type PublicKeyMaterial = PublicKey; - - fn to_bytes(&self) -> Vec { - self.0.to_bytes().to_vec() - } - - fn from_bytes(b: &[u8]) -> Self { - let mut bytes = [0; 32]; - bytes.copy_from_slice(&b[..]); - // due to trait restriction we have no choice but to panic if this fails - let key = Scalar::from_canonical_bytes(bytes).unwrap(); - Self(key) - } -} - -impl PemStorable for PrivateKey { - fn pem_type(&self) -> String { - String::from("X25519 PRIVATE KEY") - } -} - -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct PublicKey(pub MontgomeryPoint); - -impl<'a> From<&'a PrivateKey> for PublicKey { - fn from(pk: &'a PrivateKey) -> Self { - PublicKey(CURVE_GENERATOR * pk.0) - } -} - -impl MixnetEncryptionPublicKey for PublicKey { - type PrivateKeyMaterial = PrivateKey; - - fn to_bytes(&self) -> Vec { - self.0.to_bytes().to_vec() - } - - fn from_bytes(b: &[u8]) -> Self { - let mut bytes = [0; 32]; - bytes.copy_from_slice(&b[..]); - let key = MontgomeryPoint(bytes); - Self(key) - } -} - -impl PemStorable for PublicKey { - fn pem_type(&self) -> String { - String::from("X25519 PUBLIC KEY") - } -} diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index a966ec4cb9..270e25dcfb 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -1,6 +1,3 @@ -use crate::encryption::{ - MixnetEncryptionKeyPair, MixnetEncryptionPrivateKey, MixnetEncryptionPublicKey, -}; use crate::{encryption, PemStorable}; use bs58; use curve25519_dalek::scalar::Scalar; @@ -16,7 +13,7 @@ pub struct MixIdentityKeyPair { impl MixIdentityKeyPair { pub fn new() -> Self { - let keypair = encryption::x25519::KeyPair::new(); + let keypair = encryption::KeyPair::new(); MixIdentityKeyPair { private_key: MixIdentityPrivateKey(keypair.private_key), public_key: MixIdentityPublicKey(keypair.public_key), @@ -40,7 +37,7 @@ impl MixIdentityKeyPair { } #[derive(Debug, Clone, Eq, PartialEq)] -pub struct MixIdentityPublicKey(encryption::x25519::PublicKey); +pub struct MixIdentityPublicKey(encryption::PublicKey); impl MixIdentityPublicKey { pub fn derive_address(&self) -> DestinationAddressBytes { @@ -56,7 +53,7 @@ impl MixIdentityPublicKey { } pub fn from_bytes(b: &[u8]) -> Self { - Self(encryption::x25519::PublicKey::from_bytes(b)) + Self(encryption::PublicKey::from_bytes(b)) } pub fn to_base58_string(&self) -> String { @@ -76,12 +73,12 @@ impl PemStorable for MixIdentityPublicKey { // COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed #[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub struct MixIdentityPrivateKey(pub encryption::x25519::PrivateKey); +pub struct MixIdentityPrivateKey(pub encryption::PrivateKey); impl<'a> From<&'a MixIdentityPrivateKey> for MixIdentityPublicKey { fn from(pk: &'a MixIdentityPrivateKey) -> Self { let private_ref = &pk.0; - let public: encryption::x25519::PublicKey = private_ref.into(); + let public: encryption::PublicKey = private_ref.into(); MixIdentityPublicKey(public) } } @@ -92,7 +89,7 @@ impl MixIdentityPrivateKey { } pub fn from_bytes(b: &[u8]) -> Self { - Self(encryption::x25519::PrivateKey::from_bytes(b)) + Self(encryption::PrivateKey::from_bytes(b)) } } diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index a8d7b4b239..0fbc38b71f 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -8,7 +8,7 @@ use std::io::prelude::*; use std::path::PathBuf; #[allow(dead_code)] -pub fn read_mix_encryption_keypair_from_disk(_id: String) -> crypto::encryption::x25519::KeyPair { +pub fn read_mix_encryption_keypair_from_disk(_id: String) -> crypto::encryption::KeyPair { unimplemented!() } @@ -31,8 +31,7 @@ impl PemStore { let private_pem = self.read_pem_file(self.private_mix_key.clone())?; let public_pem = self.read_pem_file(self.public_mix_key.clone())?; - let key_pair = - MixIdentityKeyPair::from_bytes(&private_pem.contents, &public_pem.contents); + let key_pair = MixIdentityKeyPair::from_bytes(&private_pem.contents, &public_pem.contents); if key_pair.private_key().pem_type() != private_pem.tag { return Err(io::Error::new( diff --git a/sfw-provider/src/main.rs b/sfw-provider/src/main.rs index ec2dee141e..408ec221b6 100644 --- a/sfw-provider/src/main.rs +++ b/sfw-provider/src/main.rs @@ -1,6 +1,5 @@ use crate::provider::ServiceProvider; use clap::{App, Arg, ArgMatches, SubCommand}; -use crypto::identity::MixIdentityKeyPair; use log::error; use std::net::ToSocketAddrs; use std::path::PathBuf; From 03f7be502b15c172bb335aa18d4f3696fed682d9 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 12:57:06 +0000 Subject: [PATCH 044/273] Updated import path for serde --- validator/src/validator.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index f793440bcc..1536519b20 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -2,10 +2,9 @@ use crate::network::tendermint; use crate::services::mixmining::health_check_runner; use crypto::identity::MixIdentityKeyPair; use healthcheck::HealthChecker; +use serde::Deserialize; use tokio::runtime::Runtime; -use serde_derive::Deserialize; - #[derive(Deserialize, Debug)] pub struct Config { #[serde(rename(deserialize = "healthcheck"))] From 793d42c33a5b01bbebcff6ac46b7ad91a20da561 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 30 Jan 2020 13:01:26 +0000 Subject: [PATCH 045/273] mixnode: running without args isn't an error --- mixnode/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index f16f460dd3..1235035c5a 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -1,5 +1,4 @@ use clap::{App, Arg, ArgMatches, SubCommand}; -use log::*; use std::process; mod mix_peer; @@ -58,7 +57,7 @@ fn main() { .get_matches(); if let Err(e) = execute(arg_matches) { - error!("{}", e); + println!("{}", e); process::exit(1); } } From a3b3cc1edd0614b2aaf72a198098abaa90a0e87f Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 30 Jan 2020 13:04:43 +0000 Subject: [PATCH 046/273] validator: minor variable name change --- validator/src/validator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/src/validator.rs b/validator/src/validator.rs index f793440bcc..1f2ddd4a30 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -24,7 +24,7 @@ pub struct Validator { impl Validator { pub fn new(config: Config) -> Self { let dummy_keypair = MixIdentityKeyPair::new(); - let hc = HealthChecker::new( + let health_checker = HealthChecker::new( config.health_check.resolution_timeout, config.health_check.num_test_packets, dummy_keypair.clone(), @@ -33,7 +33,7 @@ impl Validator { let health_check_runner = health_check_runner::HealthCheckRunner::new( config.health_check.directory_server.clone(), config.health_check.interval, - hc, + health_checker, ); Validator { From 0ad648703f2a2b38adb7022cd9bfc8e1f0486f11 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 14:21:44 +0000 Subject: [PATCH 047/273] Changed delays/intervals values to use millis rather than seconds --- nym-client/src/config/mod.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 5ae2bd1e96..61ecf79093 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -7,12 +7,12 @@ use std::path::PathBuf; pub mod persistance; mod template; -// all of the below are defined in seconds -const DEFAULT_LOOP_COVER_SENDING_AVERAGE_DELAY: f64 = 1.0; -const DEFAULT_MESSAGE_SENDING_AVERAGE_DELAY: f64 = 0.5; -const DEFAULT_AVERAGE_PACKET_DELAY: f64 = 0.2; -const DEFAULT_FETCH_MESSAGES_DELAY: f64 = 1.0; -const DEFAULT_TOPOLOGY_REFRESH_RATE: f64 = 10.0; +// all of the below are defined in milliseconds +const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: u64 = 1000; +const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: u64 = 500; +const DEFAULT_AVERAGE_PACKET_DELAY: u64 = 200; +const DEFAULT_FETCH_MESSAGES_DELAY: u64 = 1000; +const DEFAULT_TOPOLOGY_REFRESH_RATE: u64 = 10_000; #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] @@ -175,27 +175,27 @@ pub struct Debug { /// sent packet is going to be delayed at any given mix node. /// So for a packet going through three mix nodes, on average, it will take three times this value /// until the packet reaches its destination. - /// The provided value is interpreted as seconds. - average_packet_delay: f64, + /// The provided value is interpreted as milliseconds. + average_packet_delay: u64, /// The parameter of Poisson distribution determining how long, on average, /// it is going to take for another loop cover traffic message to be sent. /// If set to a negative value, the loop cover traffic stream will be disabled. - /// The provided value is interpreted as seconds. - loop_cover_traffic_average_delay: f64, + /// The provided value is interpreted as milliseconds. + loop_cover_traffic_average_delay: u64, /// The uniform delay every which clients are querying the providers for received packets. /// If set to a negative value, client will never try to fetch their messages. - /// The provided value is interpreted as seconds. - fetch_message_delay: f64, + /// The provided value is interpreted as milliseconds. + fetch_message_delay: u64, /// The parameter of Poisson distribution determining how long, on average, /// it is going to take another 'real traffic stream' message to be sent. /// If no real packets are available and cover traffic is enabled, /// a loop cover message is sent instead in order to preserve the rate. /// If set to a negative value, client will never try to send real traffic data. - /// The provided value is interpreted as seconds. - message_sending_average_delay: f64, + /// The provided value is interpreted as milliseconds. + message_sending_average_delay: u64, /// Whether loop cover messages should be sent to respect message_sending_rate. /// In the case of it being disabled and not having enough real traffic @@ -207,17 +207,17 @@ pub struct Debug { /// to try to obtain a compatible network topology to send sphinx packets through. /// If set to a negative value, client will never try to refresh its topology, /// meaning it will always try to use whatever it obtained on startup. - /// The provided value is interpreted as seconds. - topology_refresh_rate: f64, + /// The provided value is interpreted as milliseconds. + topology_refresh_rate: u64, } impl Default for Debug { fn default() -> Self { Debug { average_packet_delay: DEFAULT_AVERAGE_PACKET_DELAY, - loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_SENDING_AVERAGE_DELAY, + loop_cover_traffic_average_delay: DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY, fetch_message_delay: DEFAULT_FETCH_MESSAGES_DELAY, - message_sending_average_delay: DEFAULT_MESSAGE_SENDING_AVERAGE_DELAY, + message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY, rate_compliant_cover_messages_disabled: false, topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, } From f33e9e80f6919c4dc445e0f4a4112b6fa0780954 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 14:54:45 +0000 Subject: [PATCH 048/273] Added config_file_name() to NymConfig trait --- common/config/src/lib.rs | 4 +++- nym-client/src/config/mod.rs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index 18ab5b2297..b9d179942a 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -7,6 +7,8 @@ use std::{fs, io}; pub trait NymConfig: Default + Serialize + DeserializeOwned { fn template() -> &'static str; + fn config_file_name() -> String; + fn default_root_directory() -> PathBuf; // default, most probable, implementations; can be easily overridden where required @@ -28,7 +30,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { let templated_config = reg.render_template(Self::template(), self).unwrap(); fs::write( - custom_location.unwrap_or(self.config_directory().join("config.toml")), + custom_location.unwrap_or(self.config_directory().join(Self::config_file_name())), templated_config, ) } diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 61ecf79093..0754f94c32 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -31,6 +31,10 @@ impl NymConfig for Config { config_template() } + fn config_file_name() -> String { + "config.toml".to_string() + } + fn default_root_directory() -> PathBuf { dirs::home_dir() .expect("Failed to evaluate $HOME value") From 9767dd58cabfa614a60e7dd1d720e8469b494d56 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 14:54:58 +0000 Subject: [PATCH 049/273] Additional methods on client config --- nym-client/src/config/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 0754f94c32..c35680f41a 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -83,6 +83,18 @@ impl Config { self.client.provider_id = id; self } + + pub fn get_private_identity_key_file(&self) -> PathBuf { + self.client.private_identity_key_file.clone() + } + + pub fn get_public_identity_key_file(&self) -> PathBuf { + self.client.public_identity_key_file.clone() + } + + pub fn get_config_file_save_location(&self) -> PathBuf { + self.config_directory().join(Self::config_file_name()) + } } #[derive(Debug, Deserialize, PartialEq, Serialize)] From 728f5e237a20dbcea391e996dd96d5eceb9304ed Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 14:55:10 +0000 Subject: [PATCH 050/273] Ability to create client pathfinder from the config --- nym-client/src/config/persistance/pathfinder.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nym-client/src/config/persistance/pathfinder.rs b/nym-client/src/config/persistance/pathfinder.rs index c71aab5b84..670e71038e 100644 --- a/nym-client/src/config/persistance/pathfinder.rs +++ b/nym-client/src/config/persistance/pathfinder.rs @@ -1,3 +1,5 @@ +use crate::config::Config; +use config::NymConfig; use pemstore::pathfinder::PathFinder; use std::path::PathBuf; @@ -19,6 +21,14 @@ impl ClientPathfinder { public_mix_key, } } + + pub fn new_from_config(config: &Config) -> Self { + ClientPathfinder { + config_dir: config.get_config_file_save_location(), + private_mix_key: config.get_private_identity_key_file(), + public_mix_key: config.get_public_identity_key_file(), + } + } } impl PathFinder for ClientPathfinder { From 851bad7864e604d86d35373ea9b9475d999625fe Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 14:55:21 +0000 Subject: [PATCH 051/273] Extra logging for pemstore --- common/pemstore/src/pemstore.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index 0fbc38b71f..85ab4ea451 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -1,6 +1,7 @@ use crate::pathfinder::PathFinder; use crypto::identity::MixIdentityKeyPair; use crypto::PemStorable; +use log::info; use pem::{encode, parse, Pem}; use std::fs::File; use std::io; @@ -69,11 +70,13 @@ impl PemStore { private_key.to_bytes(), private_key.pem_type(), )?; + info!("Written private key to {:?}", self.private_mix_key.clone()); self.write_pem_file( self.public_mix_key.clone(), public_key.to_bytes(), public_key.pem_type(), )?; + info!("Written public key to {:?}", self.public_mix_key.clone()); Ok(()) } From e97102d3e443a4fa09fb4ce23039b6a0793b35b1 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 14:55:33 +0000 Subject: [PATCH 052/273] Generating and saving client config on client init --- nym-client/src/commands/init.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index 1ee2385ded..a957fe7722 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -1,5 +1,6 @@ use crate::config::persistance::pathfinder::ClientPathfinder; use clap::ArgMatches; +use config::NymConfig; use crypto::identity::MixIdentityKeyPair; use pemstore::pemstore::PemStore; @@ -7,12 +8,21 @@ pub fn execute(matches: &ArgMatches) { println!("Initialising client..."); let id = matches.value_of("id").unwrap().to_string(); // required for now - let pathfinder = ClientPathfinder::new(id); + let mut config = crate::config::Config::new(id); + + if let Some(provider_id) = matches.value_of("provider") { + config = config.with_provider_id(provider_id.to_string()); + } + + let mix_identity_keys = MixIdentityKeyPair::new(); + let pathfinder = ClientPathfinder::new_from_config(&config); - println!("Writing keypairs to {:?}...", pathfinder.config_dir); - let mix_keys = MixIdentityKeyPair::new(); let pem_store = PemStore::new(pathfinder); - pem_store.write_identity(mix_keys).unwrap(); + pem_store.write_identity(mix_identity_keys).unwrap(); + println!("Saved mixnet identity keypair"); + let config_save_location = config.get_config_file_save_location(); + config.save_to_file(None).unwrap(); + println!("Saved configuration file to {:?}", config_save_location); println!("Client configuration completed.\n\n\n") } From 32f067256bab7a0944cfc4f9e3e5acbd545973fe Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 16:08:30 +0000 Subject: [PATCH 053/273] Pemstore no longer creating directory for config file --- common/pemstore/src/pemstore.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index 85ab4ea451..9a2f0782d4 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -61,8 +61,6 @@ impl PemStore { // KeyPairs that we want to persist (e.g. validator keypairs, or keys for // signing vs encryption). However, for the moment, it does the job. pub fn write_identity(&self, key_pair: MixIdentityKeyPair) -> io::Result<()> { - std::fs::create_dir_all(self.config_dir.clone())?; - let private_key = key_pair.private_key(); let public_key = key_pair.public_key(); self.write_pem_file( From 2e63f95b764ea5d35873ab200274d4c7807dfd67 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 16:08:44 +0000 Subject: [PATCH 054/273] More descriptive variable names in pemstore --- common/pemstore/src/pemstore.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index 9a2f0782d4..76ec698e51 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -15,22 +15,22 @@ pub fn read_mix_encryption_keypair_from_disk(_id: String) -> crypto::encryption: pub struct PemStore { config_dir: PathBuf, - private_mix_key: PathBuf, - public_mix_key: PathBuf, + private_mix_key_file: PathBuf, + public_mix_key_file: PathBuf, } impl PemStore { pub fn new(pathfinder: P) -> PemStore { PemStore { config_dir: pathfinder.config_dir(), - private_mix_key: pathfinder.private_identity_key(), - public_mix_key: pathfinder.public_identity_key(), + private_mix_key_file: pathfinder.private_identity_key(), + public_mix_key_file: pathfinder.public_identity_key(), } } pub fn read_identity(&self) -> io::Result { - let private_pem = self.read_pem_file(self.private_mix_key.clone())?; - let public_pem = self.read_pem_file(self.public_mix_key.clone())?; + let private_pem = self.read_pem_file(self.private_mix_key_file.clone())?; + let public_pem = self.read_pem_file(self.public_mix_key_file.clone())?; let key_pair = MixIdentityKeyPair::from_bytes(&private_pem.contents, &public_pem.contents); @@ -63,18 +63,25 @@ impl PemStore { pub fn write_identity(&self, key_pair: MixIdentityKeyPair) -> io::Result<()> { let private_key = key_pair.private_key(); let public_key = key_pair.public_key(); + self.write_pem_file( - self.private_mix_key.clone(), + self.private_mix_key_file.clone(), private_key.to_bytes(), private_key.pem_type(), )?; - info!("Written private key to {:?}", self.private_mix_key.clone()); + info!( + "Written private key to {:?}", + self.private_mix_key_file.clone() + ); self.write_pem_file( - self.public_mix_key.clone(), + self.public_mix_key_file.clone(), public_key.to_bytes(), public_key.pem_type(), )?; - info!("Written public key to {:?}", self.public_mix_key.clone()); + info!( + "Written public key to {:?}", + self.public_mix_key_file.clone() + ); Ok(()) } From 51e43547ed4b2c9f2c79b8fcde8a644971144780 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 16:09:53 +0000 Subject: [PATCH 055/273] Changed NymConfig to take optional id for some of its method --- common/config/src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index b9d179942a..48339be66b 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -12,12 +12,16 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { fn default_root_directory() -> PathBuf; // default, most probable, implementations; can be easily overridden where required - fn default_config_directory() -> PathBuf { - Self::default_root_directory().join("config") + fn default_config_directory(id: Option<&str>) -> PathBuf { + Self::default_root_directory() + .join(id.unwrap_or("")) + .join("config") } - fn default_data_directory() -> PathBuf { - Self::default_root_directory().join("data") + fn default_data_directory(id: Option<&str>) -> PathBuf { + Self::default_root_directory() + .join(id.unwrap_or("")) + .join("data") } fn root_directory(&self) -> PathBuf; @@ -35,9 +39,9 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { ) } - fn load_from_file(custom_location: Option) -> io::Result { + fn load_from_file(custom_location: Option, id: Option<&str>) -> io::Result { let config_contents = fs::read_to_string( - custom_location.unwrap_or(Self::default_config_directory().join("config.toml")), + custom_location.unwrap_or(Self::default_config_directory(id).join("config.toml")), )?; let parsing_result = toml::from_str(&config_contents) From 807e4234d6eed9f4420afc1a33115e9d015d2652 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 16:12:44 +0000 Subject: [PATCH 056/273] Default save_to_file method making sure the directory exists --- common/config/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index 48339be66b..0e129e4d39 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -33,6 +33,8 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { // it's whoever is implementing the trait responsibility to make sure you can execute your own template on your data let templated_config = reg.render_template(Self::template(), self).unwrap(); + // make sure the whole directory structure actually exists + fs::create_dir_all(custom_location.clone().unwrap_or(self.config_directory()))?; fs::write( custom_location.unwrap_or(self.config_directory().join(Self::config_file_name())), templated_config, From 7b0aee7a2cd4528f6b661ee4aecc179ffa1f4414 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 16:13:04 +0000 Subject: [PATCH 057/273] Using correct methods --- nym-client/src/config/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index c35680f41a..1d04fa5cce 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -146,15 +146,11 @@ impl Client { } fn default_private_identity_key_file(id: &str) -> PathBuf { - Config::default_data_directory() - .join(id) - .join("private_identity.pem") + Config::default_data_directory(Some(id)).join("private_identity.pem") } fn default_public_identity_key_file(id: &str) -> PathBuf { - Config::default_data_directory() - .join(id) - .join("public_identity.pem") + Config::default_data_directory(Some(id)).join("public_identity.pem") } } From b27b619412e1c8471f15626d74244fb161abfdca Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 16:13:11 +0000 Subject: [PATCH 058/273] Debug on Pathfinder --- nym-client/src/config/persistance/pathfinder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-client/src/config/persistance/pathfinder.rs b/nym-client/src/config/persistance/pathfinder.rs index 670e71038e..f4847bfcaf 100644 --- a/nym-client/src/config/persistance/pathfinder.rs +++ b/nym-client/src/config/persistance/pathfinder.rs @@ -1,8 +1,8 @@ use crate::config::Config; -use config::NymConfig; use pemstore::pathfinder::PathFinder; use std::path::PathBuf; +#[derive(Debug)] pub struct ClientPathfinder { pub config_dir: PathBuf, pub private_mix_key: PathBuf, From af5c389eeada1bb35717995ce0bb3843a37bbfd8 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 17:06:45 +0000 Subject: [PATCH 059/273] Ability to convert SocketType from &str or String --- nym-client/src/client/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 27c08a012c..4f30068a87 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -39,6 +39,24 @@ pub enum SocketType { None, } +impl From for SocketType { + fn from(v: String) -> Self { + Self::from(v.as_ref()) + } +} + +impl From<&str> for SocketType { + fn from(v: &str) -> Self { + let mut upper = v.to_string(); + upper.make_ascii_uppercase(); + match upper.as_ref() { + "TCP" => SocketType::TCP, + "WEBSOCKET" => SocketType::WebSocket, + _ => SocketType::None, + } + } +} + pub struct NymClient { keypair: MixIdentityKeyPair, From 2ac707d57cfe02d956e326ff3cd6e3248ab7e3c0 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 30 Jan 2020 17:07:05 +0000 Subject: [PATCH 060/273] Constructing client config + printing it --- nym-client/src/commands/mod.rs | 1 + nym-client/src/commands/run.rs | 64 ++++++++++++++++++++++++++++++++++ nym-client/src/config/mod.rs | 21 +++++++++-- nym-client/src/main.rs | 41 ++++++++++++++++++++++ 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 nym-client/src/commands/run.rs diff --git a/nym-client/src/commands/mod.rs b/nym-client/src/commands/mod.rs index 1b0fe864f6..83edeba8a9 100644 --- a/nym-client/src/commands/mod.rs +++ b/nym-client/src/commands/mod.rs @@ -1,3 +1,4 @@ pub mod init; +pub mod run; pub mod tcpsocket; pub mod websocket; diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs new file mode 100644 index 0000000000..ad0abc6f9e --- /dev/null +++ b/nym-client/src/commands/run.rs @@ -0,0 +1,64 @@ +use crate::client::{NymClient, SocketType}; +use crate::config::persistance::pathfinder::ClientPathfinder; +use crate::config::Config; +use clap::ArgMatches; +use config::NymConfig; +use crypto::identity::MixIdentityKeyPair; +use pemstore::pemstore::PemStore; +use std::net::ToSocketAddrs; + +pub fn execute(matches: &ArgMatches) { + let id = matches.value_of("id").unwrap(); + + let mut config_file = + Config::load_from_file(matches.value_of("config").map(|path| path.into()), Some(id)) + .expect("Failed to load config file"); + + if let Some(directory) = matches.value_of("directory") { + config_file = config_file.with_custom_directory(directory.to_string()); + } + + if let Some(provider_id) = matches.value_of("provider") { + config_file = config_file.with_provider_id(provider_id.to_string()); + } + + if let Some(socket_type) = matches.value_of("socket-type") { + config_file = config_file.with_socket(socket_type.into()); + } + + if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { + if let Err(err) = port { + panic!("Invalid port value provided - {:?}", err); + } + config_file = config_file.with_port(port.unwrap()); + } + + println!("Going to use the following config: {:#?}", config_file); + + // + // let socket_address = ("127.0.0.1", port) + // .to_socket_addrs() + // .expect("Failed to combine host and port") + // .next() + // .expect("Failed to extract the socket address from the iterator"); + // + // // TODO: currently we know we are reading the 'DummyMixIdentityKeyPair', but how to properly assert the type? + // let keypair: MixIdentityKeyPair = PemStore::new(ClientPathfinder::new(id)) + // .read_identity() + // .unwrap(); + // + // // TODO: reading auth_token from disk (if exists); + // + // println!("Public key: {}", keypair.public_key.to_base58_string()); + // + // let auth_token = None; + // let client = NymClient::new( + // keypair, + // socket_address, + // directory_server, + // auth_token, + // SocketType::WebSocket, + // ); + // + // client.start().unwrap(); +} diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 1d04fa5cce..09481334cf 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -14,6 +14,8 @@ const DEFAULT_AVERAGE_PACKET_DELAY: u64 = 200; const DEFAULT_FETCH_MESSAGES_DELAY: u64 = 1000; const DEFAULT_TOPOLOGY_REFRESH_RATE: u64 = 10_000; +const DEFAULT_LISTENING_PORT: u16 = 9001; + #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Config { @@ -84,6 +86,21 @@ impl Config { self } + pub fn with_custom_directory(mut self, directory_server: String) -> Self { + self.client.directory_server = directory_server; + self + } + + pub fn with_socket(mut self, socket_type: SocketType) -> Self { + self.socket.socket_type = socket_type; + self + } + + pub fn with_port(mut self, port: u16) -> Self { + self.socket.listening_port = port; + self + } + pub fn get_private_identity_key_file(&self) -> PathBuf { self.client.private_identity_key_file.clone() } @@ -158,14 +175,14 @@ impl Client { #[serde(deny_unknown_fields)] pub struct Socket { socket_type: SocketType, - listening_port: u64, + listening_port: u16, } impl Default for Socket { fn default() -> Self { Socket { socket_type: SocketType::None, - listening_port: 0, + listening_port: DEFAULT_LISTENING_PORT, } } } diff --git a/nym-client/src/main.rs b/nym-client/src/main.rs index 8160641828..844d999bee 100644 --- a/nym-client/src/main.rs +++ b/nym-client/src/main.rs @@ -76,6 +76,43 @@ fn main() { .required(true) ) ) + .subcommand( + SubCommand::with_name("run") + .about("Run the Nym client with provided configuration client optionally overriding set parameters") + .arg(Arg::with_name("id") + .long("id") + .help("Id of the nym-mixnet-client we want to run.") + .takes_value(true) + .required(true) + ) + // the rest of arguments are optional, they are used to override settings in config file + .arg(Arg::with_name("config") + .long("config") + .help("Custom path to the nym-mixnet-client configuration file") + .takes_value(true) + ) + .arg(Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the client is getting topology from") + .takes_value(true), + ) + .arg(Arg::with_name("provider") + .long("provider") + .help("Id of the provider we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened") + .takes_value(true) + ) + .arg(Arg::with_name("socket-type") + .long("socket-type") + .help("Type of socket to use (TCP, WebSocket or None)") + .takes_value(true) + ) + .arg(Arg::with_name("port") + .short("p") + .long("port") + .help("Port for the socket (if applicable) to listen on") + .takes_value(true) + ) + ) .get_matches(); execute(arg_matches); @@ -87,6 +124,10 @@ fn execute(matches: ArgMatches) { println!("{}", banner()); commands::init::execute(m); } + ("run", Some(m)) => { + println!("{}", banner()); + commands::run::execute(m); + } ("tcpsocket", Some(m)) => { println!("{}", banner()); commands::tcpsocket::execute(m); From bb29421854204acabb58f50bf30abc7084eb54bb Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 10:03:51 +0000 Subject: [PATCH 061/273] Ensuring the whole path to pem files exist before saving --- common/pemstore/src/pemstore.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index 76ec698e51..88d0fe3ced 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -86,6 +86,10 @@ impl PemStore { } fn write_pem_file(&self, filepath: PathBuf, data: Vec, tag: String) -> io::Result<()> { + // ensure the whole directory structure exists + if let Some(parent_dir) = filepath.parent() { + std::fs::create_dir_all(parent_dir)?; + } let pem = Pem { tag, contents: data, From 09ac19fb45ee082b0bd6b78d5fb41dae344c0752 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 10:04:10 +0000 Subject: [PATCH 062/273] Using except rather than unwrap during client init --- nym-client/src/commands/init.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index a957fe7722..6888de474e 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -18,11 +18,15 @@ pub fn execute(matches: &ArgMatches) { let pathfinder = ClientPathfinder::new_from_config(&config); let pem_store = PemStore::new(pathfinder); - pem_store.write_identity(mix_identity_keys).unwrap(); + pem_store + .write_identity(mix_identity_keys) + .expect("Failed to save identity keys"); println!("Saved mixnet identity keypair"); let config_save_location = config.get_config_file_save_location(); - config.save_to_file(None).unwrap(); + config + .save_to_file(None) + .expect("Failed to save the config file"); println!("Saved configuration file to {:?}", config_save_location); println!("Client configuration completed.\n\n\n") } From 0780f6c50167a3169bb820babc7f98bfb545d427 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 10:04:29 +0000 Subject: [PATCH 063/273] Added optional provider auth token to config --- nym-client/src/config/mod.rs | 12 +++++++++++- nym-client/src/config/template.rs | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 09481334cf..2e8dd80952 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -86,6 +86,11 @@ impl Config { self } + pub fn with_provider_auth_token(mut self, token: String) -> Self { + self.client.provider_authtoken = Some(token); + self + } + pub fn with_custom_directory(mut self, directory_server: String) -> Self { self.client.directory_server = directory_server; self @@ -133,6 +138,10 @@ pub struct Client { /// If initially omitted, a random provider will be chosen from the available topology. provider_id: String, + /// A provider specific, optional, stringified authentication token used for + /// communication with particular provider. + provider_authtoken: Option, + /// nym_home_directory specifies absolute path to the home nym Clients directory. /// It is expected to use default value and hence .toml file should not redefine this field. nym_root_directory: PathBuf, @@ -147,6 +156,7 @@ impl Default for Client { private_identity_key_file: Default::default(), public_identity_key_file: Default::default(), provider_id: "".to_string(), + provider_authtoken: None, nym_root_directory: Config::default_root_directory(), } } @@ -265,7 +275,7 @@ mod client_config { .save_to_file(Some(temp_location.clone())) .unwrap(); - let loaded_config = Config::load_from_file(Some(temp_location)).unwrap(); + let loaded_config = Config::load_from_file(Some(temp_location), None).unwrap(); assert_eq!(default_config, loaded_config); } diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index 65f7cb8cc7..feb40aca45 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -27,6 +27,10 @@ public_identity_key_file = "{{ client.public_identity_key_file }}" # ID of the provider from which the client should be fetching messages. provider_id = "{{ client.provider_id }}" +# A provider specific, optional, stringified authentication token used for +# communication with particular provider. +provider_authtoken = "{{ client.provider_authtoken }}" + ##### advanced configuration options ##### # Absolute path to the home Nym Clients directory. From f13e5ae186a8cbbdc37eba994c68d3642901a3e6 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 10:07:58 +0000 Subject: [PATCH 064/273] Allow using "ws" alias for websocket --- nym-client/src/client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 4f30068a87..ffcd00f141 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -51,7 +51,7 @@ impl From<&str> for SocketType { upper.make_ascii_uppercase(); match upper.as_ref() { "TCP" => SocketType::TCP, - "WEBSOCKET" => SocketType::WebSocket, + "WEBSOCKET" | "WS" => SocketType::WebSocket, _ => SocketType::None, } } From b8530c91c17c28a151f85d75b893e32b086800fb Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 10:10:52 +0000 Subject: [PATCH 065/273] Made conversion into 'SocketType' enum more generic Now it works for anything that can be converted into String --- nym-client/src/client/mod.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index ffcd00f141..d5be2af964 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -39,15 +39,9 @@ pub enum SocketType { None, } -impl From for SocketType { - fn from(v: String) -> Self { - Self::from(v.as_ref()) - } -} - -impl From<&str> for SocketType { - fn from(v: &str) -> Self { - let mut upper = v.to_string(); +impl SocketType { + pub fn from_string>(val: S) -> Self { + let mut upper = val.into(); upper.make_ascii_uppercase(); match upper.as_ref() { "TCP" => SocketType::TCP, From 3ac46255f003a4a5b14d0650f4c35a3fe9f646b8 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 10:18:40 +0000 Subject: [PATCH 066/273] ibid. --- nym-client/src/commands/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index ad0abc6f9e..d6aadeb965 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -23,7 +23,7 @@ pub fn execute(matches: &ArgMatches) { } if let Some(socket_type) = matches.value_of("socket-type") { - config_file = config_file.with_socket(socket_type.into()); + config_file = config_file.with_socket(SocketType::from_string(socket_type)); } if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { From bc18173ffa6250427462489202cd950daad1f447 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 10:19:10 +0000 Subject: [PATCH 067/273] Made builder methods on config more generic to accept any kind of strings --- nym-client/src/commands/init.rs | 4 ++-- nym-client/src/commands/run.rs | 4 ++-- nym-client/src/config/mod.rs | 17 +++++++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index 6888de474e..f8cea00742 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -7,11 +7,11 @@ use pemstore::pemstore::PemStore; pub fn execute(matches: &ArgMatches) { println!("Initialising client..."); - let id = matches.value_of("id").unwrap().to_string(); // required for now + let id = matches.value_of("id").unwrap(); // required for now let mut config = crate::config::Config::new(id); if let Some(provider_id) = matches.value_of("provider") { - config = config.with_provider_id(provider_id.to_string()); + config = config.with_provider_id(provider_id); } let mix_identity_keys = MixIdentityKeyPair::new(); diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index d6aadeb965..1c5398d255 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -15,11 +15,11 @@ pub fn execute(matches: &ArgMatches) { .expect("Failed to load config file"); if let Some(directory) = matches.value_of("directory") { - config_file = config_file.with_custom_directory(directory.to_string()); + config_file = config_file.with_custom_directory(directory); } if let Some(provider_id) = matches.value_of("provider") { - config_file = config_file.with_provider_id(provider_id.to_string()); + config_file = config_file.with_provider_id(provider_id); } if let Some(socket_type) = matches.value_of("socket-type") { diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 2e8dd80952..ad4f73a2b8 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -64,11 +64,12 @@ impl NymConfig for Config { } impl Config { - pub fn new(id: String) -> Self { + pub fn new>(id: S) -> Self { Config::default().with_id(id) } - pub fn with_id(mut self, id: String) -> Self { + pub fn with_id>(mut self, id: S) -> Self { + let id = id.into(); if self.client.private_identity_key_file.as_os_str().is_empty() { self.client.private_identity_key_file = self::Client::default_private_identity_key_file(&id); @@ -81,18 +82,18 @@ impl Config { self } - pub fn with_provider_id(mut self, id: String) -> Self { - self.client.provider_id = id; + pub fn with_provider_id>(mut self, id: S) -> Self { + self.client.provider_id = id.into(); self } - pub fn with_provider_auth_token(mut self, token: String) -> Self { - self.client.provider_authtoken = Some(token); + pub fn with_provider_auth_token>(mut self, token: S) -> Self { + self.client.provider_authtoken = Some(token.into()); self } - pub fn with_custom_directory(mut self, directory_server: String) -> Self { - self.client.directory_server = directory_server; + pub fn with_custom_directory>(mut self, directory_server: S) -> Self { + self.client.directory_server = directory_server.into(); self } From a5cee1fa6211b7fec71cc596bf5ae4e88a38a75b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 11:47:20 +0000 Subject: [PATCH 068/273] Topology refresh switching to millis interval --- nym-client/src/client/topology_control.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index 90c84679cc..76c0b1aee7 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -16,7 +16,7 @@ pub(crate) struct TopologyControl { directory_server: String, inner: Arc>>, health_checker: HealthChecker, - refresh_rate: f64, + refresh_rate: u64, } #[derive(Debug)] @@ -28,7 +28,7 @@ enum TopologyError { impl TopologyControl { pub(crate) async fn new( directory_server: String, - refresh_rate: f64, + refresh_rate: u64, identity_keypair: MixIdentityKeyPair, ) -> Self { // this is a temporary solution as the healthcheck will eventually be moved to validators @@ -99,7 +99,7 @@ impl TopologyControl { pub(crate) async fn run_refresher(mut self) { info!("Starting topology refresher"); - let delay_duration = Duration::from_secs_f64(self.refresh_rate); + let delay_duration = Duration::from_millis(self.refresh_rate); loop { trace!("Refreshing the topology"); let new_topology_res = self.get_current_compatible_topology().await; From 02add8d416fdef0c59b4da8248f5d2dd38b479e3 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 11:47:46 +0000 Subject: [PATCH 069/273] More config getters + extra comments --- nym-client/src/config/mod.rs | 54 ++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index ad4f73a2b8..06b368c691 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -68,6 +68,7 @@ impl Config { Config::default().with_id(id) } + // builder methods pub fn with_id>(mut self, id: S) -> Self { let id = id.into(); if self.client.private_identity_key_file.as_os_str().is_empty() { @@ -107,6 +108,11 @@ impl Config { self } + // getters + pub fn get_config_file_save_location(&self) -> PathBuf { + self.config_directory().join(Self::config_file_name()) + } + pub fn get_private_identity_key_file(&self) -> PathBuf { self.client.private_identity_key_file.clone() } @@ -115,8 +121,49 @@ impl Config { self.client.public_identity_key_file.clone() } - pub fn get_config_file_save_location(&self) -> PathBuf { - self.config_directory().join(Self::config_file_name()) + pub fn get_directory_server(&self) -> String { + self.client.directory_server.clone() + } + + pub fn get_provider_id(&self) -> String { + self.client.provider_id.clone() + } + + pub fn get_provider_auth_token(&self) -> Option { + self.client.provider_authtoken.clone() + } + + pub fn get_socket_type(&self) -> SocketType { + self.socket.socket_type + } + + pub fn get_listening_port(&self) -> u16 { + self.socket.listening_port + } + + // Debug getters + pub fn get_average_packet_delay(&self) -> u64 { + self.debug.average_packet_delay + } + + pub fn get_loop_cover_traffic_average_delay(&self) -> u64 { + self.debug.loop_cover_traffic_average_delay + } + + pub fn get_fetch_message_delay(&self) -> u64 { + self.debug.fetch_message_delay + } + + pub fn get_message_sending_average_delay(&self) -> u64 { + self.debug.message_sending_average_delay + } + + pub fn get_rate_compliant_cover_messages_disabled(&self) -> bool { + self.debug.rate_compliant_cover_messages_disabled + } + + pub fn get_topology_refresh_rate(&self) -> u64 { + self.debug.topology_refresh_rate } } @@ -139,7 +186,7 @@ pub struct Client { /// If initially omitted, a random provider will be chosen from the available topology. provider_id: String, - /// A provider specific, optional, stringified authentication token used for + /// A provider specific, optional, base58 stringified authentication token used for /// communication with particular provider. provider_authtoken: Option, @@ -249,6 +296,7 @@ pub struct Debug { /// meaning it will always try to use whatever it obtained on startup. /// The provided value is interpreted as milliseconds. topology_refresh_rate: u64, + // topology resolution timeout } impl Default for Debug { From e9f96e030f77c852cb97351bae7168a93d255547 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 11:48:16 +0000 Subject: [PATCH 070/273] Extra bit of information in template indicating AuthToken is expected to be b58 --- nym-client/src/config/template.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index feb40aca45..4192f0954c 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -27,7 +27,7 @@ public_identity_key_file = "{{ client.public_identity_key_file }}" # ID of the provider from which the client should be fetching messages. provider_id = "{{ client.provider_id }}" -# A provider specific, optional, stringified authentication token used for +# A provider specific, optional, base58 stringified authentication token used for # communication with particular provider. provider_authtoken = "{{ client.provider_authtoken }}" From f23fd57e8dc28cc59a1215b82f124a820acb4251 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 11:48:41 +0000 Subject: [PATCH 071/273] TCP and WebSocket taking only port as an argument rather than whole SocketAddr --- nym-client/src/sockets/tcp.rs | 3 ++- nym-client/src/sockets/ws.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nym-client/src/sockets/tcp.rs b/nym-client/src/sockets/tcp.rs index 2b2b309070..9d9f6d3bd1 100644 --- a/nym-client/src/sockets/tcp.rs +++ b/nym-client/src/sockets/tcp.rs @@ -297,12 +297,13 @@ async fn accept_connection( } pub async fn start_tcpsocket( - address: SocketAddr, + listening_port: u16, message_tx: mpsc::UnboundedSender, received_messages_query_tx: mpsc::UnboundedSender, self_address: DestinationAddressBytes, topology: TopologyInnerRef, ) -> Result<(), TCPSocketError> { + let address = SocketAddr::new("127.0.0.1".parse().unwrap(), listening_port); let mut listener = tokio::net::TcpListener::bind(address).await?; while let Ok((stream, _)) = listener.accept().await { diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index 15dedb37c9..c97cb87883 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -387,12 +387,13 @@ async fn accept_connection( } pub async fn start_websocket( - address: SocketAddr, + listening_port: u16, message_tx: mpsc::UnboundedSender, received_messages_query_tx: mpsc::UnboundedSender, self_address: DestinationAddressBytes, topology: TopologyInnerRef, ) -> Result<(), WebSocketError> { + let address = SocketAddr::new("127.0.0.1".parse().unwrap(), listening_port); let mut listener = tokio::net::TcpListener::bind(address).await?; while let Ok((stream, _)) = listener.accept().await { From abd937e308673e5c3c4ff434c7262af7ed6427c0 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 11:49:03 +0000 Subject: [PATCH 072/273] Started modifying NymClient to hold config struct --- nym-client/src/client/mod.rs | 61 +++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index d5be2af964..7868b70ba4 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -1,6 +1,8 @@ use crate::client::mix_traffic::MixTrafficController; use crate::client::received_buffer::ReceivedMessagesBuffer; use crate::client::topology_control::TopologyInnerRef; +use crate::config::persistance::pathfinder::ClientPathfinder; +use crate::config::Config; use crate::sockets::tcp; use crate::sockets::ws; use crypto::identity::MixIdentityKeyPair; @@ -8,6 +10,7 @@ use directory_client::presence::Topology; use futures::channel::mpsc; use futures::join; use log::*; +use pemstore::pemstore::PemStore; use serde::{Deserialize, Serialize}; use sfw_provider_requests::AuthToken; use sphinx::route::Destination; @@ -31,7 +34,7 @@ const FETCH_MESSAGES_DELAY: f64 = 1.0; // seconds; const TOPOLOGY_REFRESH_RATE: f64 = 10.0; // seconds -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)] #[serde(deny_unknown_fields)] pub enum SocketType { TCP, @@ -52,39 +55,40 @@ impl SocketType { } pub struct NymClient { - keypair: MixIdentityKeyPair, + config: Config, + identity_keypair: MixIdentityKeyPair, // to be used by "send" function or socket, etc pub input_tx: mpsc::UnboundedSender, - + // the other end of the above channel input_rx: mpsc::UnboundedReceiver, - socket_listening_address: SocketAddr, - directory: String, - auth_token: Option, - socket_type: SocketType, } #[derive(Debug)] pub struct InputMessage(pub Destination, pub Vec); impl NymClient { - pub fn new( - keypair: MixIdentityKeyPair, - socket_listening_address: SocketAddr, - directory: String, - auth_token: Option, - socket_type: SocketType, - ) -> Self { + fn load_identity_keys(config_file: &Config) -> MixIdentityKeyPair { + let identity_keypair = PemStore::new(ClientPathfinder::new_from_config(&config_file)) + .read_identity() + .expect("Failed to read stored identity key files"); + + println!( + "Public key: {}", + identity_keypair.public_key.to_base58_string() + ); + identity_keypair + } + + pub fn new(config: Config) -> Self { + let identity_keypair = Self::load_identity_keys(&config); let (input_tx, input_rx) = mpsc::unbounded::(); NymClient { - keypair, + config, + identity_keypair, input_tx, input_rx, - socket_listening_address, - directory, - auth_token, - socket_type, } } @@ -121,26 +125,29 @@ impl NymClient { let (received_messages_buffer_output_tx, received_messages_buffer_output_rx) = mpsc::unbounded(); - let self_address = self.keypair.public_key().derive_address(); + let self_address = self.identity_keypair.public_key().derive_address(); // generate same type of keys we have as our identity let healthcheck_keys = MixIdentityKeyPair::new(); // TODO: when we switch to our graph topology, we need to remember to change 'Topology' type let topology_controller = rt.block_on(topology_control::TopologyControl::::new( - self.directory.clone(), - TOPOLOGY_REFRESH_RATE, + self.config.get_directory_server(), + self.config.get_topology_refresh_rate(), healthcheck_keys, )); let provider_client_listener_address = rt.block_on(self.get_provider_socket_address(topology_controller.get_inner_ref())); + // this is temporary until rest of the code compiles so I could work on this + let temp_auth_token = None; + let mut provider_poller = provider_poller::ProviderPoller::new( poller_input_tx, provider_client_listener_address, self_address, - self.auth_token, + temp_auth_token, ); // registration @@ -192,12 +199,10 @@ impl NymClient { // the received messages are sent to ReceivedMessagesBuffer to be available to rest of the system let provider_polling_future = rt.spawn(provider_poller.start_provider_polling()); - // a temporary workaround for starting socket listener of specified type - // in the future the actual socket handler should start THIS client instead - match self.socket_type { + match self.config.get_socket_type() { SocketType::WebSocket => { rt.spawn(ws::start_websocket( - self.socket_listening_address, + self.config.get_listening_port(), self.input_tx, received_messages_buffer_output_tx, self_address, @@ -206,7 +211,7 @@ impl NymClient { } SocketType::TCP => { rt.spawn(tcp::start_tcpsocket( - self.socket_listening_address, + self.config.get_listening_port(), self.input_tx, received_messages_buffer_output_tx, self_address, From dbf3c0d6ea24799b780c7e7084cd9de08d252dfd Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 11:49:36 +0000 Subject: [PATCH 073/273] Created new version of AuthToken that can be converted to and from String --- sfw-provider/sfw-provider-requests/Cargo.toml | 1 + sfw-provider/sfw-provider-requests/src/lib.rs | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/sfw-provider/sfw-provider-requests/Cargo.toml b/sfw-provider/sfw-provider-requests/Cargo.toml index 12a8343bba..06f70f252d 100644 --- a/sfw-provider/sfw-provider-requests/Cargo.toml +++ b/sfw-provider/sfw-provider-requests/Cargo.toml @@ -7,4 +7,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bs58 = "0.3.0" sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } diff --git a/sfw-provider/sfw-provider-requests/src/lib.rs b/sfw-provider/sfw-provider-requests/src/lib.rs index 70e06feaad..c9d872b0e0 100644 --- a/sfw-provider/sfw-provider-requests/src/lib.rs +++ b/sfw-provider/sfw-provider-requests/src/lib.rs @@ -5,3 +5,96 @@ pub const DUMMY_MESSAGE_CONTENT: &[u8] = b"[DUMMY MESSAGE] Wanting something does not give you the right to have it."; pub type AuthToken = [u8; 32]; + +// To be renamed to 'AuthToken' once it is safe to replace it +#[derive(Debug, PartialEq)] +pub struct AuthToken2(pub [u8; 32]); + +#[derive(Debug)] +pub enum AuthTokenConversionError { + InvalidStringError, + StringOfInvalidLengthError, +} + +impl AuthToken2 { + pub fn try_from_base58_string>( + val: S, + ) -> Result { + let decoded = match bs58::decode(val.into()).into_vec() { + Ok(decoded) => decoded, + Err(_) => return Err(AuthTokenConversionError::InvalidStringError), + }; + + if decoded.len() != 32 { + return Err(AuthTokenConversionError::StringOfInvalidLengthError); + } + + let mut token = [0u8; 32]; + token.copy_from_slice(&decoded[..]); + Ok(AuthToken2(token)) + } + + pub fn to_base58_string(&self) -> String { + bs58::encode(self.0).into_string() + } +} + +#[cfg(test)] +mod auth_token_conversion { + use super::*; + + #[test] + fn it_is_possible_to_recover_it_from_valid_b58_string() { + let auth_token = AuthToken2([42; 32]); + let auth_token_string = auth_token.to_base58_string(); + assert_eq!( + auth_token, + AuthToken2::try_from_base58_string(auth_token_string).unwrap() + ) + } + + #[test] + fn it_is_possible_to_recover_it_from_valid_b58_str_ref() { + let auth_token = AuthToken2([42; 32]); + let auth_token_string = auth_token.to_base58_string(); + let auth_token_str_ref: &str = &auth_token_string; + assert_eq!( + auth_token, + AuthToken2::try_from_base58_string(auth_token_str_ref).unwrap() + ) + } + + #[test] + fn it_returns_error_on_b58_with_invalid_characters() { + let auth_token = AuthToken2([42; 32]); + let auth_token_string = auth_token.to_base58_string(); + + let mut chars = auth_token_string.chars(); + let _consumed_first_char = chars.next().unwrap(); + + let invalid_chars_token = "=".to_string() + chars.as_str(); + assert!(AuthToken2::try_from_base58_string(invalid_chars_token).is_err()) + } + + #[test] + fn it_returns_error_on_too_long_b58_string() { + let auth_token = AuthToken2([42; 32]); + let mut auth_token_string = auth_token.to_base58_string(); + auth_token_string.push('f'); + + assert!(AuthToken2::try_from_base58_string(auth_token_string).is_err()) + } + + #[test] + fn it_returns_error_on_too_short_b58_string() { + let auth_token = AuthToken2([42; 32]); + let auth_token_string = auth_token.to_base58_string(); + + let mut chars = auth_token_string.chars(); + let _consumed_first_char = chars.next().unwrap(); + let _consumed_second_char = chars.next().unwrap(); + let invalid_chars_token = chars.as_str(); + + assert!(AuthToken2::try_from_base58_string(invalid_chars_token).is_err()) + } +} From 5a4bf6f08aa517b368367f797fa417ac4db405a7 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 11:49:45 +0000 Subject: [PATCH 074/273] ibid. --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 52ee8ead93..fe73520f9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2164,6 +2164,7 @@ dependencies = [ name = "sfw-provider-requests" version = "0.1.0" dependencies = [ + "bs58", "sphinx", ] From f8a468135ab582c3aed1951a2103dd128a05530a Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 11:59:27 +0000 Subject: [PATCH 075/273] Replaced AuthToken with new implementation --- common/clients/provider-client/src/lib.rs | 4 +-- sfw-provider/sfw-provider-requests/src/lib.rs | 30 +++++++++---------- .../sfw-provider-requests/src/requests.rs | 12 ++++---- .../sfw-provider-requests/src/responses.rs | 6 ++-- .../src/provider/client_handling/mod.rs | 8 ++--- sfw-provider/src/provider/mod.rs | 4 +-- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/common/clients/provider-client/src/lib.rs b/common/clients/provider-client/src/lib.rs index e24f893ac9..72bca9c682 100644 --- a/common/clients/provider-client/src/lib.rs +++ b/common/clients/provider-client/src/lib.rs @@ -82,8 +82,8 @@ impl ProviderClient { } pub async fn retrieve_messages(&self) -> Result>, ProviderClientError> { - let auth_token = match self.auth_token { - Some(token) => token, + let auth_token = match self.auth_token.as_ref() { + Some(token) => token.clone(), None => { return Err(ProviderClientError::EmptyAuthTokenError); } diff --git a/sfw-provider/sfw-provider-requests/src/lib.rs b/sfw-provider/sfw-provider-requests/src/lib.rs index c9d872b0e0..0938750206 100644 --- a/sfw-provider/sfw-provider-requests/src/lib.rs +++ b/sfw-provider/sfw-provider-requests/src/lib.rs @@ -4,11 +4,9 @@ pub mod responses; pub const DUMMY_MESSAGE_CONTENT: &[u8] = b"[DUMMY MESSAGE] Wanting something does not give you the right to have it."; -pub type AuthToken = [u8; 32]; - // To be renamed to 'AuthToken' once it is safe to replace it -#[derive(Debug, PartialEq)] -pub struct AuthToken2(pub [u8; 32]); +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct AuthToken(pub [u8; 32]); #[derive(Debug)] pub enum AuthTokenConversionError { @@ -16,7 +14,7 @@ pub enum AuthTokenConversionError { StringOfInvalidLengthError, } -impl AuthToken2 { +impl AuthToken { pub fn try_from_base58_string>( val: S, ) -> Result { @@ -31,7 +29,7 @@ impl AuthToken2 { let mut token = [0u8; 32]; token.copy_from_slice(&decoded[..]); - Ok(AuthToken2(token)) + Ok(AuthToken(token)) } pub fn to_base58_string(&self) -> String { @@ -45,49 +43,49 @@ mod auth_token_conversion { #[test] fn it_is_possible_to_recover_it_from_valid_b58_string() { - let auth_token = AuthToken2([42; 32]); + let auth_token = AuthToken([42; 32]); let auth_token_string = auth_token.to_base58_string(); assert_eq!( auth_token, - AuthToken2::try_from_base58_string(auth_token_string).unwrap() + AuthToken::try_from_base58_string(auth_token_string).unwrap() ) } #[test] fn it_is_possible_to_recover_it_from_valid_b58_str_ref() { - let auth_token = AuthToken2([42; 32]); + let auth_token = AuthToken([42; 32]); let auth_token_string = auth_token.to_base58_string(); let auth_token_str_ref: &str = &auth_token_string; assert_eq!( auth_token, - AuthToken2::try_from_base58_string(auth_token_str_ref).unwrap() + AuthToken::try_from_base58_string(auth_token_str_ref).unwrap() ) } #[test] fn it_returns_error_on_b58_with_invalid_characters() { - let auth_token = AuthToken2([42; 32]); + let auth_token = AuthToken([42; 32]); let auth_token_string = auth_token.to_base58_string(); let mut chars = auth_token_string.chars(); let _consumed_first_char = chars.next().unwrap(); let invalid_chars_token = "=".to_string() + chars.as_str(); - assert!(AuthToken2::try_from_base58_string(invalid_chars_token).is_err()) + assert!(AuthToken::try_from_base58_string(invalid_chars_token).is_err()) } #[test] fn it_returns_error_on_too_long_b58_string() { - let auth_token = AuthToken2([42; 32]); + let auth_token = AuthToken([42; 32]); let mut auth_token_string = auth_token.to_base58_string(); auth_token_string.push('f'); - assert!(AuthToken2::try_from_base58_string(auth_token_string).is_err()) + assert!(AuthToken::try_from_base58_string(auth_token_string).is_err()) } #[test] fn it_returns_error_on_too_short_b58_string() { - let auth_token = AuthToken2([42; 32]); + let auth_token = AuthToken([42; 32]); let auth_token_string = auth_token.to_base58_string(); let mut chars = auth_token_string.chars(); @@ -95,6 +93,6 @@ mod auth_token_conversion { let _consumed_second_char = chars.next().unwrap(); let invalid_chars_token = chars.as_str(); - assert!(AuthToken2::try_from_base58_string(invalid_chars_token).is_err()) + assert!(AuthToken::try_from_base58_string(invalid_chars_token).is_err()) } } diff --git a/sfw-provider/sfw-provider-requests/src/requests.rs b/sfw-provider/sfw-provider-requests/src/requests.rs index 3dba30c038..c8a15f44dc 100644 --- a/sfw-provider/sfw-provider-requests/src/requests.rs +++ b/sfw-provider/sfw-provider-requests/src/requests.rs @@ -80,7 +80,7 @@ impl ProviderRequest for PullRequest { .to_vec() .into_iter() .chain(self.destination_address.iter().cloned()) - .chain(self.auth_token.iter().cloned()) + .chain(self.auth_token.0.iter().cloned()) .collect() } @@ -102,7 +102,7 @@ impl ProviderRequest for PullRequest { auth_token.copy_from_slice(&bytes[34..]); Ok(PullRequest { - auth_token, + auth_token: AuthToken(auth_token), destination_address, }) } @@ -165,12 +165,12 @@ mod creating_pull_request { 0, 1, 2, ]; let auth_token = [1u8; 32]; - let pull_request = PullRequest::new(address, auth_token); + let pull_request = PullRequest::new(address, AuthToken(auth_token)); let bytes = pull_request.to_bytes(); let recovered = PullRequest::from_bytes(&bytes).unwrap(); assert_eq!(address, recovered.destination_address); - assert_eq!(auth_token, recovered.auth_token); + assert_eq!(AuthToken(auth_token), recovered.auth_token); } #[test] @@ -180,14 +180,14 @@ mod creating_pull_request { 0, 1, 2, ]; let auth_token = [1u8; 32]; - let pull_request = PullRequest::new(address, auth_token); + let pull_request = PullRequest::new(address, AuthToken(auth_token)); let bytes = pull_request.to_bytes(); let recovered = ProviderRequests::from_bytes(&bytes).unwrap(); match recovered { ProviderRequests::PullMessages(req) => { assert_eq!(address, req.destination_address); - assert_eq!(auth_token, req.auth_token); + assert_eq!(AuthToken(auth_token), req.auth_token); } _ => panic!("expected to recover pull request!"), } diff --git a/sfw-provider/sfw-provider-requests/src/responses.rs b/sfw-provider/sfw-provider-requests/src/responses.rs index c15017c76d..6a181ffedc 100644 --- a/sfw-provider/sfw-provider-requests/src/responses.rs +++ b/sfw-provider/sfw-provider-requests/src/responses.rs @@ -109,7 +109,7 @@ impl ProviderResponse for PullResponse { impl ProviderResponse for RegisterResponse { fn to_bytes(&self) -> Vec { - self.auth_token.to_vec() + self.auth_token.0.to_vec() } fn from_bytes(bytes: &[u8]) -> Result { @@ -117,7 +117,9 @@ impl ProviderResponse for RegisterResponse { 32 => { let mut auth_token = [0u8; 32]; auth_token.copy_from_slice(&bytes[..32]); - Ok(RegisterResponse { auth_token }) + Ok(RegisterResponse { + auth_token: AuthToken(auth_token), + }) } _ => Err(ProviderResponseError::UnmarshalErrorInvalidLength), } diff --git a/sfw-provider/src/provider/client_handling/mod.rs b/sfw-provider/src/provider/client_handling/mod.rs index 67a2658ebe..35fbbf5eb9 100644 --- a/sfw-provider/src/provider/client_handling/mod.rs +++ b/sfw-provider/src/provider/client_handling/mod.rs @@ -107,7 +107,7 @@ impl ClientRequestProcessor { // Wait for https://github.com/nymtech/nym-sfw-provider/issues/19 to resolve. let unlocked_ledger = processing_data.registered_clients_ledger.lock().await; - if unlocked_ledger.has_token(req.auth_token) { + if unlocked_ledger.has_token(&req.auth_token) { // drop the mutex so that we could do IO without blocking others wanting to get the lock drop(unlocked_ledger); let retrieved_messages = ClientStorage::retrieve_client_files( @@ -134,8 +134,8 @@ impl ClientRequestProcessor { req.destination_address.to_vec(), processing_data.secret_key, ); - if !unlocked_ledger.has_token(auth_token) { - unlocked_ledger.insert_token(auth_token, req.destination_address); + if !unlocked_ledger.has_token(&auth_token) { + unlocked_ledger.insert_token(auth_token.clone(), req.destination_address); ClientRequestProcessor::create_storage_dir( req.destination_address, processing_data.store_dir.as_path(), @@ -160,7 +160,7 @@ impl ClientRequestProcessor { auth_token_raw.input(&data); let mut auth_token = [0u8; 32]; auth_token.copy_from_slice(&auth_token_raw.result().code().to_vec()); - auth_token + AuthToken(auth_token) } } diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 56085917aa..9637f76668 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -72,8 +72,8 @@ impl ClientLedger { Arc::new(FMutex::new(self)) } - fn has_token(&self, auth_token: AuthToken) -> bool { - return self.0.contains_key(&auth_token); + fn has_token(&self, auth_token: &AuthToken) -> bool { + return self.0.contains_key(auth_token); } fn insert_token( From 65772cdc1e2c75c48840e451137538f16a99b859 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 12:40:16 +0000 Subject: [PATCH 076/273] Fixed how ensuring config save directory exists --- common/config/src/lib.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index 0e129e4d39..edd6a4bdeb 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -34,7 +34,17 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { let templated_config = reg.render_template(Self::template(), self).unwrap(); // make sure the whole directory structure actually exists - fs::create_dir_all(custom_location.clone().unwrap_or(self.config_directory()))?; + match custom_location.clone() { + Some(loc) => { + if let Some(parent_dir) = loc.parent() { + fs::create_dir_all(parent_dir) + } else { + Ok(()) + } + } + None => fs::create_dir_all(self.config_directory()), + }?; + fs::write( custom_location.unwrap_or(self.config_directory().join(Self::config_file_name())), templated_config, From ea5223ddf5e044c25954f0a9628945bb8d68be0c Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 12:50:56 +0000 Subject: [PATCH 077/273] Custom deserializer for Option to fix receiving Some("") rather than None --- nym-client/src/config/mod.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 06b368c691..6eac43c16a 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -1,7 +1,7 @@ use crate::client::SocketType; use crate::config::template::config_template; use config::NymConfig; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use std::path::PathBuf; pub mod persistance; @@ -167,6 +167,18 @@ impl Config { } } +fn de_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)) + } +} + #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Client { @@ -188,6 +200,7 @@ pub struct Client { /// A provider specific, optional, base58 stringified authentication token used for /// communication with particular provider. + #[serde(deserialize_with = "de_option_string")] provider_authtoken: Option, /// nym_home_directory specifies absolute path to the home nym Clients directory. From e48b28eefdb36ab48e160f50a6bd337330f7a087 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 12:51:17 +0000 Subject: [PATCH 078/273] Generating proper tmp random dir on each test run --- Cargo.lock | 1 + nym-client/Cargo.toml | 3 +++ nym-client/src/config/mod.rs | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index fe73520f9d..0d892bb34f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1433,6 +1433,7 @@ dependencies = [ "serde_json", "sfw-provider-requests", "sphinx", + "tempfile", "tokio 0.2.10", "tokio-tungstenite", "topology", diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 09939462d3..3e5922af22 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -49,6 +49,9 @@ tokio-tungstenite = { git = "https://github.com/snapview/tokio-tungstenite", rev [build-dependencies] built = "0.3.2" +[dev-dependencies] +tempfile = "3.1.0" + [features] qa = [] local = [] \ No newline at end of file diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 6eac43c16a..69cb059aee 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -331,7 +331,7 @@ mod client_config { #[test] fn after_saving_default_config_the_loaded_one_is_identical() { - let temp_location = std::env::temp_dir().join("config.toml"); + let temp_location = tempfile::tempdir().unwrap().path().join("config.toml"); let default_config = Config::default().with_id("foomp".to_string()); default_config .save_to_file(Some(temp_location.clone())) From 1e938dfd555fded445a092edcff70ed6f13244a2 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 13:04:39 +0000 Subject: [PATCH 079/273] Provider poller getting rate from the provided config value --- nym-client/src/client/mod.rs | 5 +---- nym-client/src/client/provider_poller.rs | 8 +++++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 7868b70ba4..ec64d47c99 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -12,7 +12,6 @@ use futures::join; use log::*; use pemstore::pemstore::PemStore; use serde::{Deserialize, Serialize}; -use sfw_provider_requests::AuthToken; use sphinx::route::Destination; use std::net::SocketAddr; use tokio::runtime::Runtime; @@ -30,9 +29,6 @@ const LOOP_COVER_AVERAGE_DELAY: f64 = 0.5; // seconds const MESSAGE_SENDING_AVERAGE_DELAY: f64 = 0.5; // seconds; -const FETCH_MESSAGES_DELAY: f64 = 1.0; // seconds; - -const TOPOLOGY_REFRESH_RATE: f64 = 10.0; // seconds #[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)] #[serde(deny_unknown_fields)] @@ -148,6 +144,7 @@ impl NymClient { provider_client_listener_address, self_address, temp_auth_token, + self.config.get_fetch_message_delay(), ); // registration diff --git a/nym-client/src/client/provider_poller.rs b/nym-client/src/client/provider_poller.rs index 71ccd41fdd..2b2e89a847 100644 --- a/nym-client/src/client/provider_poller.rs +++ b/nym-client/src/client/provider_poller.rs @@ -1,4 +1,3 @@ -use crate::client::FETCH_MESSAGES_DELAY; use futures::channel::mpsc; use log::{debug, error, info, trace, warn}; use provider_client::ProviderClientError; @@ -8,6 +7,7 @@ use std::net::SocketAddr; use std::time::Duration; pub(crate) struct ProviderPoller { + average_polling_delay_millis: u64, provider_client: provider_client::ProviderClient, poller_tx: mpsc::UnboundedSender>>, } @@ -18,6 +18,7 @@ impl ProviderPoller { provider_client_listener_address: SocketAddr, client_address: DestinationAddressBytes, auth_token: Option, + average_polling_delay_millis: u64, ) -> Self { ProviderPoller { provider_client: provider_client::ProviderClient::new( @@ -26,6 +27,7 @@ impl ProviderPoller { auth_token, ), poller_tx, + average_polling_delay_millis, } } @@ -55,8 +57,8 @@ impl ProviderPoller { let loop_message = &mix_client::packet::LOOP_COVER_MESSAGE_PAYLOAD.to_vec(); let dummy_message = &sfw_provider_requests::DUMMY_MESSAGE_CONTENT.to_vec(); - let delay_duration = Duration::from_secs_f64(FETCH_MESSAGES_DELAY); - let extended_delay_duration = Duration::from_secs_f64(FETCH_MESSAGES_DELAY * 10.0); + let delay_duration = Duration::from_millis(self.average_polling_delay_millis); + let extended_delay_duration = Duration::from_millis(self.average_polling_delay_millis * 10); loop { debug!("Polling provider..."); From f3da6d4258bd40227d0ec30082f04b9dd269b297 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 15:07:38 +0000 Subject: [PATCH 080/273] Updated used sphinx revision --- Cargo.lock | 2 +- common/clients/mix-client/Cargo.toml | 2 +- common/clients/provider-client/Cargo.toml | 2 +- common/crypto/Cargo.toml | 2 +- common/healthcheck/Cargo.toml | 2 +- common/topology/Cargo.toml | 2 +- mixnode/Cargo.toml | 2 +- nym-client/Cargo.toml | 2 +- sfw-provider/Cargo.toml | 2 +- sfw-provider/sfw-provider-requests/Cargo.toml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d892bb34f..4af4244bdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2239,7 +2239,7 @@ dependencies = [ [[package]] name = "sphinx" version = "0.1.0" -source = "git+https://github.com/nymtech/sphinx?rev=8424f4b0933b4a7f64ae828b2edefc5ba43a9e79#8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" +source = "git+https://github.com/nymtech/sphinx?rev=5862939c52e4dd76f836856a01787faec32152b4#5862939c52e4dd76f836856a01787faec32152b4" dependencies = [ "aes-ctr", "arrayref", diff --git a/common/clients/mix-client/Cargo.toml b/common/clients/mix-client/Cargo.toml index ae7091c62c..2a029d7632 100644 --- a/common/clients/mix-client/Cargo.toml +++ b/common/clients/mix-client/Cargo.toml @@ -18,6 +18,6 @@ addressing = {path = "../../addressing"} topology = {path = "../../topology"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } # sphinx = { path = "../../../../sphinx"} diff --git a/common/clients/provider-client/Cargo.toml b/common/clients/provider-client/Cargo.toml index 786b3c2a29..1d3b68c4f2 100644 --- a/common/clients/provider-client/Cargo.toml +++ b/common/clients/provider-client/Cargo.toml @@ -16,4 +16,4 @@ tokio = { version = "0.2", features = ["full"] } sfw-provider-requests = { path = "../../../sfw-provider/sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index 9cda304ed6..be0cb42b31 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -15,4 +15,4 @@ rand = "0.7.2" rand_os = "0.1" ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } \ No newline at end of file +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } \ No newline at end of file diff --git a/common/healthcheck/Cargo.toml b/common/healthcheck/Cargo.toml index f972b56af1..7af12ace4c 100644 --- a/common/healthcheck/Cargo.toml +++ b/common/healthcheck/Cargo.toml @@ -27,7 +27,7 @@ sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" } topology = {path = "../topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } # sphinx = { path = "../../../sphinx"} [dev-dependencies] diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index c3c23e733d..2c28228bd5 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -19,5 +19,5 @@ addressing = {path = "../addressing"} version-checker = {path = "../version-checker" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } # sphinx = { path = "../../../sphinx"} diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index 4685d6416e..32a9e1aeeb 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -22,7 +22,7 @@ addressing = {path = "../common/addressing" } directory-client = { path = "../common/clients/directory-client" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } [build-dependencies] built = "0.3.2" diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 3e5922af22..4599510abd 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -40,7 +40,7 @@ sfw-provider-requests = { path = "../sfw-provider/sfw-provider-requests" } topology = {path = "../common/topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } # sphinx = { path = "../../sphinx"} # putting this explicitly below everything and most likely, the next time we look into it, it will already have a proper release diff --git a/sfw-provider/Cargo.toml b/sfw-provider/Cargo.toml index d2a7cf2c5c..2cc6afcd12 100644 --- a/sfw-provider/Cargo.toml +++ b/sfw-provider/Cargo.toml @@ -28,7 +28,7 @@ directory-client = { path = "../common/clients/directory-client" } sfw-provider-requests = { path = "./sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } [build-dependencies] built = "0.3.2" diff --git a/sfw-provider/sfw-provider-requests/Cargo.toml b/sfw-provider/sfw-provider-requests/Cargo.toml index 06f70f252d..0507581973 100644 --- a/sfw-provider/sfw-provider-requests/Cargo.toml +++ b/sfw-provider/sfw-provider-requests/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] bs58 = "0.3.0" -sphinx = { git = "https://github.com/nymtech/sphinx", rev="8424f4b0933b4a7f64ae828b2edefc5ba43a9e79" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } From 79b18a4789fd5f5ab7b3856588898f811928d15b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 15:57:45 +0000 Subject: [PATCH 081/273] Fixed initial real packet timing bias + duration parameterization --- nym-client/src/client/real_traffic_stream.rs | 32 ++++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/nym-client/src/client/real_traffic_stream.rs b/nym-client/src/client/real_traffic_stream.rs index fc8069c12b..934cd3522e 100644 --- a/nym-client/src/client/real_traffic_stream.rs +++ b/nym-client/src/client/real_traffic_stream.rs @@ -1,6 +1,6 @@ use crate::client::mix_traffic::MixMessage; use crate::client::topology_control::TopologyInnerRef; -use crate::client::{InputMessage, MESSAGE_SENDING_AVERAGE_DELAY}; +use crate::client::InputMessage; use futures::channel::mpsc; use futures::task::{Context, Poll}; use futures::{Future, Stream, StreamExt}; @@ -11,11 +11,10 @@ use std::time::Duration; use tokio::time; use topology::NymTopology; -// have a rather low value for test sake -const AVERAGE_PACKET_DELAY: f64 = 0.1; - pub(crate) struct OutQueueControl { - delay: time::Delay, + average_packet_delay: Duration, + average_message_sending_delay: Duration, + next_delay: time::Delay, mix_tx: mpsc::UnboundedSender, input_rx: mpsc::UnboundedReceiver, our_info: Destination, @@ -32,21 +31,20 @@ impl Stream for OutQueueControl { fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // it is not yet time to return a message - if Pin::new(&mut self.delay).poll(cx).is_pending() { + if Pin::new(&mut self.next_delay).poll(cx).is_pending() { return Poll::Pending; }; // we know it's time to send a message, so let's prepare delay for the next one // Get the `now` by looking at the current `delay` deadline - let now = self.delay.deadline(); - + let now = self.next_delay.deadline(); let next_poisson_delay = - Duration::from_secs_f64(mix_client::poisson::sample(MESSAGE_SENDING_AVERAGE_DELAY)); + mix_client::poisson::sample_from_duration(self.average_message_sending_delay); // The next interval value is `next_poisson_delay` after the one that just // yielded. let next = now + next_poisson_delay; - self.delay.reset(next); + self.next_delay.reset(next); // decide what kind of message to send match Pin::new(&mut self.input_rx).poll_next(cx) { @@ -69,10 +67,13 @@ impl OutQueueControl { input_rx: mpsc::UnboundedReceiver, our_info: Destination, topology: TopologyInnerRef, + average_packet_delay: Duration, + average_message_sending_delay: Duration, ) -> Self { - let initial_delay = time::delay_for(Duration::from_secs_f64(MESSAGE_SENDING_AVERAGE_DELAY)); OutQueueControl { - delay: initial_delay, + average_packet_delay, + average_message_sending_delay, + next_delay: time::delay_for(Default::default()), mix_tx, input_rx, our_info, @@ -81,6 +82,11 @@ impl OutQueueControl { } pub(crate) async fn run_out_queue_control(mut self) { + // we should set initial delay only when we actually start the stream + self.next_delay = time::delay_for(mix_client::poisson::sample_from_duration( + self.average_message_sending_delay, + )); + info!("starting out queue controller"); while let Some(next_message) = self.next().await { trace!("created new message"); @@ -105,7 +111,7 @@ impl OutQueueControl { real_message.0, real_message.1, topology, - AVERAGE_PACKET_DELAY, + self.average_packet_delay, ), }; From 8c68a812e2f3009f28bb9fc489f27f3b40108497 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:00:17 +0000 Subject: [PATCH 082/273] Made client config delay getters return time::duration --- nym-client/src/config/mod.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 69cb059aee..0402fe70bc 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -3,6 +3,7 @@ use crate::config::template::config_template; use config::NymConfig; use serde::{Deserialize, Deserializer, Serialize}; use std::path::PathBuf; +use std::time; pub mod persistance; mod template; @@ -142,28 +143,28 @@ impl Config { } // Debug getters - pub fn get_average_packet_delay(&self) -> u64 { - self.debug.average_packet_delay + pub fn get_average_packet_delay(&self) -> time::Duration { + time::Duration::from_millis(self.debug.average_packet_delay) } - pub fn get_loop_cover_traffic_average_delay(&self) -> u64 { - self.debug.loop_cover_traffic_average_delay + pub fn get_loop_cover_traffic_average_delay(&self) -> time::Duration { + time::Duration::from_millis(self.debug.loop_cover_traffic_average_delay) } - pub fn get_fetch_message_delay(&self) -> u64 { - self.debug.fetch_message_delay + pub fn get_fetch_message_delay(&self) -> time::Duration { + time::Duration::from_millis(self.debug.fetch_message_delay) } - pub fn get_message_sending_average_delay(&self) -> u64 { - self.debug.message_sending_average_delay + pub fn get_message_sending_average_delay(&self) -> time::Duration { + time::Duration::from_millis(self.debug.message_sending_average_delay) } pub fn get_rate_compliant_cover_messages_disabled(&self) -> bool { self.debug.rate_compliant_cover_messages_disabled } - pub fn get_topology_refresh_rate(&self) -> u64 { - self.debug.topology_refresh_rate + pub fn get_topology_refresh_rate(&self) -> time::Duration { + time::Duration::from_millis(self.debug.topology_refresh_rate) } } From c921223ac29db136594d6e32126a1bf43d00184c Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:02:34 +0000 Subject: [PATCH 083/273] Changed topology control to also use time::duration --- nym-client/src/client/topology_control.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index 76c0b1aee7..f040163d92 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -3,6 +3,7 @@ use crypto::identity::MixIdentityKeyPair; use healthcheck::HealthChecker; use log::{error, info, trace, warn}; use std::sync::Arc; +use std::time; use std::time::Duration; use tokio::sync::RwLock as FRwLock; use topology::NymTopology; @@ -16,7 +17,7 @@ pub(crate) struct TopologyControl { directory_server: String, inner: Arc>>, health_checker: HealthChecker, - refresh_rate: u64, + refresh_rate: time::Duration, } #[derive(Debug)] @@ -28,7 +29,7 @@ enum TopologyError { impl TopologyControl { pub(crate) async fn new( directory_server: String, - refresh_rate: u64, + refresh_rate: time::Duration, identity_keypair: MixIdentityKeyPair, ) -> Self { // this is a temporary solution as the healthcheck will eventually be moved to validators @@ -99,7 +100,6 @@ impl TopologyControl { pub(crate) async fn run_refresher(mut self) { info!("Starting topology refresher"); - let delay_duration = Duration::from_millis(self.refresh_rate); loop { trace!("Refreshing the topology"); let new_topology_res = self.get_current_compatible_topology().await; @@ -113,7 +113,7 @@ impl TopologyControl { }; self.update_global_topology(new_topology).await; - tokio::time::delay_for(delay_duration).await; + tokio::time::delay_for(self.refresh_rate).await; } } } From 409bfb4f08b7816216ba12c72be85a3b6548f382 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:05:22 +0000 Subject: [PATCH 084/273] ibid. for provider poller --- nym-client/src/client/provider_poller.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/nym-client/src/client/provider_poller.rs b/nym-client/src/client/provider_poller.rs index 2b2e89a847..03c76fb8a8 100644 --- a/nym-client/src/client/provider_poller.rs +++ b/nym-client/src/client/provider_poller.rs @@ -4,10 +4,10 @@ use provider_client::ProviderClientError; use sfw_provider_requests::AuthToken; use sphinx::route::DestinationAddressBytes; use std::net::SocketAddr; -use std::time::Duration; +use std::time; pub(crate) struct ProviderPoller { - average_polling_delay_millis: u64, + polling_rate: time::Duration, provider_client: provider_client::ProviderClient, poller_tx: mpsc::UnboundedSender>>, } @@ -18,7 +18,7 @@ impl ProviderPoller { provider_client_listener_address: SocketAddr, client_address: DestinationAddressBytes, auth_token: Option, - average_polling_delay_millis: u64, + polling_rate: time::Duration, ) -> Self { ProviderPoller { provider_client: provider_client::ProviderClient::new( @@ -27,7 +27,7 @@ impl ProviderPoller { auth_token, ), poller_tx, - average_polling_delay_millis, + polling_rate, } } @@ -57,8 +57,7 @@ impl ProviderPoller { let loop_message = &mix_client::packet::LOOP_COVER_MESSAGE_PAYLOAD.to_vec(); let dummy_message = &sfw_provider_requests::DUMMY_MESSAGE_CONTENT.to_vec(); - let delay_duration = Duration::from_millis(self.average_polling_delay_millis); - let extended_delay_duration = Duration::from_millis(self.average_polling_delay_millis * 10); + let extended_delay_duration = self.polling_rate * 10; loop { debug!("Polling provider..."); @@ -84,7 +83,7 @@ impl ProviderPoller { // in either case there's no recovery and we can only panic self.poller_tx.unbounded_send(good_messages).unwrap(); - tokio::time::delay_for(delay_duration).await; + tokio::time::delay_for(self.polling_rate).await; } } } From 6432e708d6303e76386b07cfaa73458ee8d760a4 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:10:00 +0000 Subject: [PATCH 085/273] Extra argument to cover traffic stream --- nym-client/src/client/cover_traffic_stream.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nym-client/src/client/cover_traffic_stream.rs b/nym-client/src/client/cover_traffic_stream.rs index c48c0c4801..c2aa3853da 100644 --- a/nym-client/src/client/cover_traffic_stream.rs +++ b/nym-client/src/client/cover_traffic_stream.rs @@ -1,22 +1,21 @@ use crate::client::mix_traffic::MixMessage; use crate::client::topology_control::TopologyInnerRef; -use crate::client::LOOP_COVER_AVERAGE_DELAY; use futures::channel::mpsc; use log::{error, info, trace, warn}; use sphinx::route::Destination; -use std::time::Duration; +use std::time; use topology::NymTopology; pub(crate) async fn start_loop_cover_traffic_stream( tx: mpsc::UnboundedSender, our_info: Destination, topology_ctrl_ref: TopologyInnerRef, + average_delay_duration: time::Duration, ) { info!("Starting loop cover traffic stream"); loop { trace!("next cover message!"); - let delay = mix_client::poisson::sample(LOOP_COVER_AVERAGE_DELAY); - let delay_duration = Duration::from_secs_f64(delay); + let delay_duration = mix_client::poisson::sample_from_duration(average_delay_duration); tokio::time::delay_for(delay_duration).await; let read_lock = topology_ctrl_ref.read().await; From d9a95917e3da898516484c65682a768964a8e20b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:13:23 +0000 Subject: [PATCH 086/273] Moved SocketType enum to config --- nym-client/src/client/mod.rs | 29 +---------------------------- nym-client/src/config/mod.rs | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index ec64d47c99..433001926e 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -2,7 +2,7 @@ use crate::client::mix_traffic::MixTrafficController; use crate::client::received_buffer::ReceivedMessagesBuffer; use crate::client::topology_control::TopologyInnerRef; use crate::config::persistance::pathfinder::ClientPathfinder; -use crate::config::Config; +use crate::config::{Config, SocketType}; use crate::sockets::tcp; use crate::sockets::ws; use crypto::identity::MixIdentityKeyPair; @@ -11,7 +11,6 @@ use futures::channel::mpsc; use futures::join; use log::*; use pemstore::pemstore::PemStore; -use serde::{Deserialize, Serialize}; use sphinx::route::Destination; use std::net::SocketAddr; use tokio::runtime::Runtime; @@ -24,32 +23,6 @@ mod real_traffic_stream; pub mod received_buffer; pub mod topology_control; -// TODO: all of those constants should probably be moved to config file -const LOOP_COVER_AVERAGE_DELAY: f64 = 0.5; -// seconds -const MESSAGE_SENDING_AVERAGE_DELAY: f64 = 0.5; -// seconds; - -#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)] -#[serde(deny_unknown_fields)] -pub enum SocketType { - TCP, - WebSocket, - None, -} - -impl SocketType { - pub fn from_string>(val: S) -> Self { - let mut upper = val.into(); - upper.make_ascii_uppercase(); - match upper.as_ref() { - "TCP" => SocketType::TCP, - "WEBSOCKET" | "WS" => SocketType::WebSocket, - _ => SocketType::None, - } - } -} - pub struct NymClient { config: Config, identity_keypair: MixIdentityKeyPair, diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 0402fe70bc..f92c11cb4f 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -1,4 +1,3 @@ -use crate::client::SocketType; use crate::config::template::config_template; use config::NymConfig; use serde::{Deserialize, Deserializer, Serialize}; @@ -17,6 +16,26 @@ const DEFAULT_TOPOLOGY_REFRESH_RATE: u64 = 10_000; const DEFAULT_LISTENING_PORT: u16 = 9001; +#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)] +#[serde(deny_unknown_fields)] +pub enum SocketType { + TCP, + WebSocket, + None, +} + +impl SocketType { + pub fn from_string>(val: S) -> Self { + let mut upper = val.into(); + upper.make_ascii_uppercase(); + match upper.as_ref() { + "TCP" => SocketType::TCP, + "WEBSOCKET" | "WS" => SocketType::WebSocket, + _ => SocketType::None, + } + } +} + #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Config { From 98a19e78232326742eba20278fa2ae98459b4175 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:15:09 +0000 Subject: [PATCH 087/273] Removed websocket and tcpsocket commands --- nym-client/src/commands/mod.rs | 2 -- nym-client/src/commands/tcpsocket.rs | 47 --------------------------- nym-client/src/commands/websocket.rs | 48 ---------------------------- nym-client/src/main.rs | 8 ----- 4 files changed, 105 deletions(-) delete mode 100644 nym-client/src/commands/tcpsocket.rs delete mode 100644 nym-client/src/commands/websocket.rs diff --git a/nym-client/src/commands/mod.rs b/nym-client/src/commands/mod.rs index 83edeba8a9..0d06305e61 100644 --- a/nym-client/src/commands/mod.rs +++ b/nym-client/src/commands/mod.rs @@ -1,4 +1,2 @@ pub mod init; pub mod run; -pub mod tcpsocket; -pub mod websocket; diff --git a/nym-client/src/commands/tcpsocket.rs b/nym-client/src/commands/tcpsocket.rs deleted file mode 100644 index 5dce285c0b..0000000000 --- a/nym-client/src/commands/tcpsocket.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::client::{NymClient, SocketType}; -use crate::config::persistance::pathfinder::ClientPathfinder; -use clap::ArgMatches; -use crypto::identity::MixIdentityKeyPair; -use pemstore::pemstore::PemStore; -use std::net::ToSocketAddrs; - -pub fn execute(matches: &ArgMatches) { - let id = matches.value_of("id").unwrap().to_string(); - let port = match matches.value_of("port").unwrap_or("9001").parse::() { - Ok(n) => n, - Err(err) => panic!("Invalid port value provided - {:?}", err), - }; - - let directory_server = matches - .value_of("directory") - .unwrap_or("https://directory.nymtech.net") - .to_string(); - - println!("Starting TCP socket on port: {:?}", port); - println!("Listening for messages..."); - - let socket_address = ("127.0.0.1", port) - .to_socket_addrs() - .expect("Failed to combine host and port") - .next() - .expect("Failed to extract the socket address from the iterator"); - - // TODO: currently we know we are reading the 'DummyMixIdentityKeyPair', but how to properly assert the type? - let keypair: MixIdentityKeyPair = PemStore::new(ClientPathfinder::new(id)) - .read_identity() - .unwrap(); - // TODO: reading auth_token from disk (if exists); - - println!("Public key: {}", keypair.public_key.to_base58_string()); - - let auth_token = None; - let client = NymClient::new( - keypair, - socket_address.clone(), - directory_server, - auth_token, - SocketType::TCP, - ); - - client.start().unwrap(); -} diff --git a/nym-client/src/commands/websocket.rs b/nym-client/src/commands/websocket.rs deleted file mode 100644 index bcb375761e..0000000000 --- a/nym-client/src/commands/websocket.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::client::{NymClient, SocketType}; -use crate::config::persistance::pathfinder::ClientPathfinder; -use clap::ArgMatches; -use crypto::identity::MixIdentityKeyPair; -use pemstore::pemstore::PemStore; -use std::net::ToSocketAddrs; - -pub fn execute(matches: &ArgMatches) { - let id = matches.value_of("id").unwrap().to_string(); - let port = match matches.value_of("port").unwrap_or("9001").parse::() { - Ok(n) => n, - Err(err) => panic!("Invalid port value provided - {:?}", err), - }; - - let directory_server = matches - .value_of("directory") - .unwrap_or("https://directory.nymtech.net") - .to_string(); - - println!("Starting websocket on port: {:?}", port); - println!("Listening for messages..."); - - let socket_address = ("127.0.0.1", port) - .to_socket_addrs() - .expect("Failed to combine host and port") - .next() - .expect("Failed to extract the socket address from the iterator"); - - // TODO: currently we know we are reading the 'DummyMixIdentityKeyPair', but how to properly assert the type? - let keypair: MixIdentityKeyPair = PemStore::new(ClientPathfinder::new(id)) - .read_identity() - .unwrap(); - - // TODO: reading auth_token from disk (if exists); - - println!("Public key: {}", keypair.public_key.to_base58_string()); - - let auth_token = None; - let client = NymClient::new( - keypair, - socket_address, - directory_server, - auth_token, - SocketType::WebSocket, - ); - - client.start().unwrap(); -} diff --git a/nym-client/src/main.rs b/nym-client/src/main.rs index 844d999bee..590d71e45f 100644 --- a/nym-client/src/main.rs +++ b/nym-client/src/main.rs @@ -128,14 +128,6 @@ fn execute(matches: ArgMatches) { println!("{}", banner()); commands::run::execute(m); } - ("tcpsocket", Some(m)) => { - println!("{}", banner()); - commands::tcpsocket::execute(m); - } - ("websocket", Some(m)) => { - println!("{}", banner()); - commands::websocket::execute(m); - } _ => { println!("{}", usage()); } From 199377d466522f4589be37d967a00ee91212618b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:15:52 +0000 Subject: [PATCH 088/273] Made 'run' command compilable again --- nym-client/src/commands/run.rs | 41 ++++++++++------------------------ 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index 1c5398d255..5a982b6daa 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -1,11 +1,9 @@ -use crate::client::{NymClient, SocketType}; +use crate::client::NymClient; use crate::config::persistance::pathfinder::ClientPathfinder; -use crate::config::Config; +use crate::config::{Config, SocketType}; use clap::ArgMatches; use config::NymConfig; -use crypto::identity::MixIdentityKeyPair; use pemstore::pemstore::PemStore; -use std::net::ToSocketAddrs; pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); @@ -28,37 +26,22 @@ pub fn execute(matches: &ArgMatches) { if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { if let Err(err) = port { + // if port was overridden, it must be parsable panic!("Invalid port value provided - {:?}", err); } config_file = config_file.with_port(port.unwrap()); } - println!("Going to use the following config: {:#?}", config_file); + let identity_keypair = PemStore::new(ClientPathfinder::new_from_config(&config_file)) + .read_identity() + .expect("Failed to read stored identity key files"); - // - // let socket_address = ("127.0.0.1", port) - // .to_socket_addrs() - // .expect("Failed to combine host and port") - // .next() - // .expect("Failed to extract the socket address from the iterator"); - // - // // TODO: currently we know we are reading the 'DummyMixIdentityKeyPair', but how to properly assert the type? - // let keypair: MixIdentityKeyPair = PemStore::new(ClientPathfinder::new(id)) - // .read_identity() - // .unwrap(); - // - // // TODO: reading auth_token from disk (if exists); - // - // println!("Public key: {}", keypair.public_key.to_base58_string()); - // - // let auth_token = None; - // let client = NymClient::new( - // keypair, - // socket_address, - // directory_server, - // auth_token, - // SocketType::WebSocket, - // ); + println!( + "Public key: {}", + identity_keypair.public_key.to_base58_string() + ); + + let client = NymClient::new(config_file); // // client.start().unwrap(); } From bcbebe944dfa5f9ee7298e121c9f7d53e78a4fd9 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:16:26 +0000 Subject: [PATCH 089/273] Borrow checker --- nym-client/src/client/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 433001926e..3b46f3960c 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -144,11 +144,14 @@ impl NymClient { mix_tx.clone(), Destination::new(self_address, Default::default()), topology_controller.get_inner_ref(), + self.config.get_loop_cover_traffic_average_delay(), )); // cloning arguments required by OutQueueControl; required due to move let input_rx = self.input_rx; let topology_ref = topology_controller.get_inner_ref(); + let average_packet_delay = self.config.get_average_packet_delay(); + let message_sending_average_delay = self.config.get_message_sending_average_delay(); // future constantly pumping traffic at some specified average rate // if a real message is available on 'input_rx' that might have been received from say @@ -160,6 +163,8 @@ impl NymClient { input_rx, Destination::new(self_address, Default::default()), topology_ref, + average_packet_delay, + message_sending_average_delay, ) .run_out_queue_control() .await From 94479cdeec32b4f5cbecb535206935d59f173fd6 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:20:49 +0000 Subject: [PATCH 090/273] Loop cover messages having parameterized delays --- common/clients/mix-client/src/packet.rs | 10 ++++++---- nym-client/src/client/cover_traffic_stream.rs | 7 +++++-- nym-client/src/client/mod.rs | 1 + nym-client/src/client/real_traffic_stream.rs | 1 + 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/common/clients/mix-client/src/packet.rs b/common/clients/mix-client/src/packet.rs index f1ee832651..d7fb8b242a 100644 --- a/common/clients/mix-client/src/packet.rs +++ b/common/clients/mix-client/src/packet.rs @@ -3,10 +3,10 @@ use addressing::AddressTypeError; use sphinx::route::{Destination, DestinationAddressBytes, SURBIdentifier}; use sphinx::SphinxPacket; use std::net::SocketAddr; +use std::time; use topology::{NymTopology, NymTopologyError}; pub const LOOP_COVER_MESSAGE_PAYLOAD: &[u8] = b"The cake is a lie!"; -pub const LOOP_COVER_MESSAGE_AVERAGE_DELAY: f64 = 2.0; #[derive(Debug)] pub enum SphinxPacketEncapsulationError { @@ -43,6 +43,7 @@ pub fn loop_cover_message( our_address: DestinationAddressBytes, surb_id: SURBIdentifier, topology: &T, + average_delay_duration: time::Duration, ) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> { let destination = Destination::new(our_address, surb_id); @@ -50,7 +51,7 @@ pub fn loop_cover_message( destination, LOOP_COVER_MESSAGE_PAYLOAD.to_vec(), topology, - LOOP_COVER_MESSAGE_AVERAGE_DELAY, + average_delay_duration, ) } @@ -58,7 +59,7 @@ pub fn encapsulate_message( recipient: Destination, message: Vec, topology: &T, - average_delay: f64, + average_delay_duration: time::Duration, ) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> { let mut providers = topology.providers(); if providers.len() == 0 { @@ -69,7 +70,8 @@ pub fn encapsulate_message( let route = topology.route_to(provider)?; - let delays = sphinx::header::delays::generate(route.len(), average_delay); + let delays = + sphinx::header::delays::generate_from_average_duration(route.len(), average_delay_duration); // build the packet let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays)?; diff --git a/nym-client/src/client/cover_traffic_stream.rs b/nym-client/src/client/cover_traffic_stream.rs index c2aa3853da..45fa0aced6 100644 --- a/nym-client/src/client/cover_traffic_stream.rs +++ b/nym-client/src/client/cover_traffic_stream.rs @@ -10,12 +10,14 @@ pub(crate) async fn start_loop_cover_traffic_stream( tx: mpsc::UnboundedSender, our_info: Destination, topology_ctrl_ref: TopologyInnerRef, - average_delay_duration: time::Duration, + average_cover_message_delay_duration: time::Duration, + average_packet_delay_duration: time::Duration, ) { info!("Starting loop cover traffic stream"); loop { trace!("next cover message!"); - let delay_duration = mix_client::poisson::sample_from_duration(average_delay_duration); + let delay_duration = + mix_client::poisson::sample_from_duration(average_cover_message_delay_duration); tokio::time::delay_for(delay_duration).await; let read_lock = topology_ctrl_ref.read().await; @@ -31,6 +33,7 @@ pub(crate) async fn start_loop_cover_traffic_stream( our_info.address, our_info.identifier, topology, + average_packet_delay_duration, ) { Ok(message) => message, Err(err) => { diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 3b46f3960c..f7979445d5 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -145,6 +145,7 @@ impl NymClient { Destination::new(self_address, Default::default()), topology_controller.get_inner_ref(), self.config.get_loop_cover_traffic_average_delay(), + self.config.get_average_packet_delay(), )); // cloning arguments required by OutQueueControl; required due to move diff --git a/nym-client/src/client/real_traffic_stream.rs b/nym-client/src/client/real_traffic_stream.rs index 934cd3522e..f11af86f04 100644 --- a/nym-client/src/client/real_traffic_stream.rs +++ b/nym-client/src/client/real_traffic_stream.rs @@ -106,6 +106,7 @@ impl OutQueueControl { self.our_info.address, self.our_info.identifier, topology, + self.average_packet_delay, ), StreamMessage::Real(real_message) => mix_client::packet::encapsulate_message( real_message.0, From 12fc906542c3ed9fb1eaaffaaa4574bcbad97f3e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:24:14 +0000 Subject: [PATCH 091/273] Changing poisson sampling to sample from time::Duration --- common/clients/mix-client/src/poisson.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/clients/mix-client/src/poisson.rs b/common/clients/mix-client/src/poisson.rs index 6474d3c65b..e4bc83de7a 100644 --- a/common/clients/mix-client/src/poisson.rs +++ b/common/clients/mix-client/src/poisson.rs @@ -1,9 +1,10 @@ use rand_distr::{Distribution, Exp}; +use std::time; -pub fn sample(average_delay: f64) -> f64 { +pub fn sample_from_duration(average_duration: time::Duration) -> time::Duration { // this is our internal code used by our traffic streams // the error is only thrown if average delay is less than 0, which will never happen // so call to unwrap is perfectly safe here - let exp = Exp::new(1.0 / average_delay).unwrap(); - exp.sample(&mut rand::thread_rng()) + let exp = Exp::new(1.0 / average_duration.as_nanos() as f64).unwrap(); + time::Duration::from_nanos(exp.sample(&mut rand::thread_rng()).round() as u64) } From 9e0735498b28fcafe986a9379a26b17caa7cc216 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:27:56 +0000 Subject: [PATCH 092/273] Corrected client config template comments --- nym-client/src/config/template.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index 4192f0954c..e716eca179 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -66,18 +66,18 @@ listening_port = {{ socket.listening_port }} # sent packet is going to be delayed at any given mix node. # So for a packet going through three mix nodes, on average, it will take three times this value # until the packet reaches its destination. -# The provided value is interpreted as seconds. +# The provided value is interpreted as milliseconds. average_packet_delay = {{ debug.average_packet_delay }} # The parameter of Poisson distribution determining how long, on average, # it is going to take for another loop cover traffic message to be sent. # If set to a negative value, the loop cover traffic stream will be disabled. -# The provided value is interpreted as seconds. +# The provided value is interpreted as milliseconds. loop_cover_traffic_average_delay = {{ debug.loop_cover_traffic_average_delay }} # The uniform delay every which clients are querying the providers for received packets. # If set to a negative value, client will never try to fetch their messages. -# The provided value is interpreted as seconds. +# The provided value is interpreted as milliseconds. fetch_message_delay = {{ debug.fetch_message_delay }} # The parameter of Poisson distribution determining how long, on average, @@ -85,7 +85,7 @@ fetch_message_delay = {{ debug.fetch_message_delay }} # If no real packets are available and cover traffic is enabled, # a loop cover message is sent instead in order to preserve the rate. # If set to a negative value, client will never try to send real traffic data. -# The provided value is interpreted as seconds. +# The provided value is interpreted as milliseconds. message_sending_average_delay = {{ debug.message_sending_average_delay }} # Whether loop cover messages should be sent to respect message_sending_rate. @@ -98,8 +98,8 @@ rate_compliant_cover_messages_disabled = {{ debug.rate_compliant_cover_messages_ # to try to obtain a compatible network topology to send sphinx packets through. # If set to a negative value, client will never try to refresh its topology, # meaning it will always try to use whatever it obtained on startup. -# The provided value is interpreted as seconds. +# The provided value is interpreted as milliseconds. topology_refresh_rate = {{ debug.topology_refresh_rate }} - "# +"# } From a4d500f0f5cef1f927ef8fa42d3580119e53b1ee Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 31 Jan 2020 16:32:32 +0000 Subject: [PATCH 093/273] Additional optional arguments for client init --- nym-client/src/commands/init.rs | 19 +++++++++++ nym-client/src/main.rs | 59 ++++++++------------------------- 2 files changed, 33 insertions(+), 45 deletions(-) diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index f8cea00742..6ced36ff39 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -1,4 +1,5 @@ use crate::config::persistance::pathfinder::ClientPathfinder; +use crate::config::SocketType; use clap::ArgMatches; use config::NymConfig; use crypto::identity::MixIdentityKeyPair; @@ -10,10 +11,26 @@ pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); // required for now let mut config = crate::config::Config::new(id); + if let Some(directory) = matches.value_of("directory") { + config = config.with_custom_directory(directory); + } + if let Some(provider_id) = matches.value_of("provider") { config = config.with_provider_id(provider_id); } + if let Some(socket_type) = matches.value_of("socket-type") { + config = config.with_socket(SocketType::from_string(socket_type)); + } + + if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { + if let Err(err) = port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_port(port.unwrap()); + } + let mix_identity_keys = MixIdentityKeyPair::new(); let pathfinder = ClientPathfinder::new_from_config(&config); @@ -23,6 +40,8 @@ pub fn execute(matches: &ArgMatches) { .expect("Failed to save identity keys"); println!("Saved mixnet identity keypair"); + // TODO: perform provider registration here + let config_save_location = config.get_config_file_save_location(); config .save_to_file(None) diff --git a/nym-client/src/main.rs b/nym-client/src/main.rs index 590d71e45f..5b92f6b319 100644 --- a/nym-client/src/main.rs +++ b/nym-client/src/main.rs @@ -28,52 +28,21 @@ fn main() { .help("Id of the provider we have preference to connect to. If left empty, a random provider will be chosen.") .takes_value(true) ) - ) - .subcommand( - SubCommand::with_name("tcpsocket") - .about("Run Nym client that listens for bytes on a TCP socket") - .arg( - Arg::with_name("port") - .short("p") - .long("port") - .help("Port for TCP socket to listen on") - .takes_value(true) - .required(true), + .arg(Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the client is getting topology from") + .takes_value(true), ) - .arg( - Arg::with_name("directory") - .long("directory") - .help("Address of the directory server the client is getting topology from") - .takes_value(true), - ) - .arg(Arg::with_name("id") - .long("id") - .help("Id of the nym-mixnet-client we want to run.") + .arg(Arg::with_name("socket-type") + .long("socket-type") + .help("Type of socket to use (TCP, WebSocket or None) in all subsequent runs") .takes_value(true) - .required(true) ) - ) - .subcommand( - SubCommand::with_name("websocket") - .about("Run Nym client that listens on a websocket") - .arg( - Arg::with_name("port") - .short("p") - .long("port") - .help("Port for websocket to listen on") - .takes_value(true) - ) - .arg( - Arg::with_name("directory") - .long("directory") - .help("Address of the directory server the client is getting topology from") - .takes_value(true), - ) - .arg(Arg::with_name("id") - .long("id") - .help("Id of the nym-mixnet-client we want to run.") + .arg(Arg::with_name("port") + .short("p") + .long("port") + .help("Port for the socket (if applicable) to listen on in all subsequent runs") .takes_value(true) - .required(true) ) ) .subcommand( @@ -92,9 +61,9 @@ fn main() { .takes_value(true) ) .arg(Arg::with_name("directory") - .long("directory") - .help("Address of the directory server the client is getting topology from") - .takes_value(true), + .long("directory") + .help("Address of the directory server the client is getting topology from") + .takes_value(true), ) .arg(Arg::with_name("provider") .long("provider") From 8747dc9d72527938a36aa0d24616db351b427858 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 09:31:31 +0000 Subject: [PATCH 094/273] Unused import --- nym-client/src/client/topology_control.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index f040163d92..22b0a4495b 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -4,7 +4,6 @@ use healthcheck::HealthChecker; use log::{error, info, trace, warn}; use std::sync::Arc; use std::time; -use std::time::Duration; use tokio::sync::RwLock as FRwLock; use topology::NymTopology; From 98ea9dccdf073bbdda459829c8461dc1fa1a18a8 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 09:40:53 +0000 Subject: [PATCH 095/273] Removed duplicate code for overwriting config values --- nym-client/src/commands/init.rs | 22 ++-------------------- nym-client/src/commands/mod.rs | 27 +++++++++++++++++++++++++++ nym-client/src/commands/run.rs | 27 +++++---------------------- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index 6ced36ff39..82c8e3038e 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -1,5 +1,5 @@ +use crate::commands::overwrite_config; use crate::config::persistance::pathfinder::ClientPathfinder; -use crate::config::SocketType; use clap::ArgMatches; use config::NymConfig; use crypto::identity::MixIdentityKeyPair; @@ -11,25 +11,7 @@ pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); // required for now let mut config = crate::config::Config::new(id); - if let Some(directory) = matches.value_of("directory") { - config = config.with_custom_directory(directory); - } - - if let Some(provider_id) = matches.value_of("provider") { - config = config.with_provider_id(provider_id); - } - - if let Some(socket_type) = matches.value_of("socket-type") { - config = config.with_socket(SocketType::from_string(socket_type)); - } - - if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { - if let Err(err) = port { - // if port was overridden, it must be parsable - panic!("Invalid port value provided - {:?}", err); - } - config = config.with_port(port.unwrap()); - } + config = overwrite_config(config, matches); let mix_identity_keys = MixIdentityKeyPair::new(); let pathfinder = ClientPathfinder::new_from_config(&config); diff --git a/nym-client/src/commands/mod.rs b/nym-client/src/commands/mod.rs index 0d06305e61..92e7c3dfe6 100644 --- a/nym-client/src/commands/mod.rs +++ b/nym-client/src/commands/mod.rs @@ -1,2 +1,29 @@ +use crate::config::{Config, SocketType}; +use clap::ArgMatches; + pub mod init; pub mod run; + +pub(crate) fn overwrite_config(mut config: Config, matches: &ArgMatches) -> Config { + if let Some(directory) = matches.value_of("directory") { + config = config.with_custom_directory(directory); + } + + if let Some(provider_id) = matches.value_of("provider") { + config = config.with_provider_id(provider_id); + } + + if let Some(socket_type) = matches.value_of("socket-type") { + config = config.with_socket(SocketType::from_string(socket_type)); + } + + if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { + if let Err(err) = port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_port(port.unwrap()); + } + + config +} diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index 5a982b6daa..4b564b0918 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -1,4 +1,5 @@ use crate::client::NymClient; +use crate::commands::overwrite_config; use crate::config::persistance::pathfinder::ClientPathfinder; use crate::config::{Config, SocketType}; use clap::ArgMatches; @@ -8,31 +9,13 @@ use pemstore::pemstore::PemStore; pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); - let mut config_file = + let mut config = Config::load_from_file(matches.value_of("config").map(|path| path.into()), Some(id)) .expect("Failed to load config file"); - if let Some(directory) = matches.value_of("directory") { - config_file = config_file.with_custom_directory(directory); - } + config = overwrite_config(config, matches); - if let Some(provider_id) = matches.value_of("provider") { - config_file = config_file.with_provider_id(provider_id); - } - - if let Some(socket_type) = matches.value_of("socket-type") { - config_file = config_file.with_socket(SocketType::from_string(socket_type)); - } - - if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { - if let Err(err) = port { - // if port was overridden, it must be parsable - panic!("Invalid port value provided - {:?}", err); - } - config_file = config_file.with_port(port.unwrap()); - } - - let identity_keypair = PemStore::new(ClientPathfinder::new_from_config(&config_file)) + let identity_keypair = PemStore::new(ClientPathfinder::new_from_config(&config)) .read_identity() .expect("Failed to read stored identity key files"); @@ -41,7 +24,7 @@ pub fn execute(matches: &ArgMatches) { identity_keypair.public_key.to_base58_string() ); - let client = NymClient::new(config_file); + let client = NymClient::new(config); // // client.start().unwrap(); } From 0f03221fb9dd87101dad097937736d481580c992 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 09:54:43 +0000 Subject: [PATCH 096/273] Removed redundant key info print --- nym-client/src/client/mod.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index f7979445d5..909c7f83c0 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -38,15 +38,9 @@ pub struct InputMessage(pub Destination, pub Vec); impl NymClient { fn load_identity_keys(config_file: &Config) -> MixIdentityKeyPair { - let identity_keypair = PemStore::new(ClientPathfinder::new_from_config(&config_file)) + PemStore::new(ClientPathfinder::new_from_config(&config_file)) .read_identity() - .expect("Failed to read stored identity key files"); - - println!( - "Public key: {}", - identity_keypair.public_key.to_base58_string() - ); - identity_keypair + .expect("Failed to read stored identity key files") } pub fn new(config: Config) -> Self { From cc5627f0c3791e19e03bee2ed2f67867930c549f Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 09:57:06 +0000 Subject: [PATCH 097/273] Notice regarding client address --- nym-client/src/commands/run.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index 4b564b0918..6568009d70 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -1,7 +1,7 @@ use crate::client::NymClient; use crate::commands::overwrite_config; use crate::config::persistance::pathfinder::ClientPathfinder; -use crate::config::{Config, SocketType}; +use crate::config::Config; use clap::ArgMatches; use config::NymConfig; use pemstore::pemstore::PemStore; @@ -20,7 +20,7 @@ pub fn execute(matches: &ArgMatches) { .expect("Failed to read stored identity key files"); println!( - "Public key: {}", + "Public identity key: {}\nFor time being, it is identical to address", identity_keypair.public_key.to_base58_string() ); From b72ce283b4031e60c1df865044a5e0b352a0433e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:06:43 +0000 Subject: [PATCH 098/273] Derived Into for AuthToken using existing to base58 method --- sfw-provider/sfw-provider-requests/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sfw-provider/sfw-provider-requests/src/lib.rs b/sfw-provider/sfw-provider-requests/src/lib.rs index 0938750206..6d307aaf42 100644 --- a/sfw-provider/sfw-provider-requests/src/lib.rs +++ b/sfw-provider/sfw-provider-requests/src/lib.rs @@ -37,6 +37,12 @@ impl AuthToken { } } +impl Into for AuthToken { + fn into(self) -> String { + self.to_base58_string() + } +} + #[cfg(test)] mod auth_token_conversion { use super::*; From 82232f970acef9cf1fec86fba9066058a69c4199 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:07:13 +0000 Subject: [PATCH 099/273] Choosing provider at startup + (if applicable) performing registration --- nym-client/src/commands/init.rs | 88 +++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index 82c8e3038e..c1569d93d8 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -1,9 +1,66 @@ -use crate::commands::overwrite_config; +use crate::built_info; +use crate::commands::override_config; use crate::config::persistance::pathfinder::ClientPathfinder; use clap::ArgMatches; use config::NymConfig; use crypto::identity::MixIdentityKeyPair; +use directory_client::presence::Topology; use pemstore::pemstore::PemStore; +use sfw_provider_requests::AuthToken; +use sphinx::route::DestinationAddressBytes; +use topology::provider::Node; +use topology::NymTopology; + +async fn try_provider_registrations( + providers: Vec, + our_address: DestinationAddressBytes, +) -> Option<(String, AuthToken)> { + // since the order of providers is non-deterministic we can just try to get a first 'working' provider + for provider in providers { + let provider_client = + provider_client::ProviderClient::new(provider.client_listener, our_address, None); + let auth_token = provider_client.register().await; + if let Ok(token) = auth_token { + return Some((provider.pub_key, token)); + } + } + None +} + +// in the long run this will be provider specific and only really applicable to a +// relatively small subset of all providers +async fn choose_provider( + 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()); + let version_filtered_topology = topology.filter_node_versions( + built_info::PKG_VERSION, + built_info::PKG_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 providers + let providers = version_filtered_topology.providers(); + + // try to perform registration so that we wouldn't need to do it at startup + // + at the same time we'll know if we can actually talk with that provider + let registration_result = try_provider_registrations(providers, our_address).await; + match registration_result { + None => { + // while technically there's no issue client-side, it will be impossible to execute + // `nym-client run` as no provider is available so it might be best to not finalize + // the init and rely on users trying to init another time? + panic!( + "Currently there are no valid providers available on the network ({}). \ + Please try to run `init` again at later time or change your directory server", + directory_server + ) + } + Some((provider_id, auth_token)) => (provider_id, auth_token), + } +} pub fn execute(matches: &ArgMatches) { println!("Initialising client..."); @@ -11,23 +68,44 @@ pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); // required for now let mut config = crate::config::Config::new(id); - config = overwrite_config(config, matches); + config = override_config(config, matches); let mix_identity_keys = MixIdentityKeyPair::new(); - let pathfinder = ClientPathfinder::new_from_config(&config); + // if there is no provider chosen, get a random-ish one from the topology + if config.get_provider_id().is_empty() { + let our_address = mix_identity_keys.public_key().derive_address(); + // TODO: is there perhaps a way to make it work without having to spawn entire runtime? + let mut rt = tokio::runtime::Runtime::new().unwrap(); + let (provider_id, auth_token) = + rt.block_on(choose_provider(config.get_directory_server(), our_address)); + config = config + .with_provider_id(provider_id) + .with_provider_auth_token(auth_token); + } + + let pathfinder = ClientPathfinder::new_from_config(&config); let pem_store = PemStore::new(pathfinder); pem_store .write_identity(mix_identity_keys) .expect("Failed to save identity keys"); println!("Saved mixnet identity keypair"); - // TODO: perform provider registration here - let config_save_location = config.get_config_file_save_location(); config .save_to_file(None) .expect("Failed to save the config file"); println!("Saved configuration file to {:?}", config_save_location); + + println!( + "Unless overridden in all `nym-client run` we will be talking to the following provider: {}...", + config.get_provider_id(), + ); + if config.get_provider_auth_token().is_some() { + println!( + "using optional AuthToken: {:?}", + config.get_provider_auth_token().unwrap() + ) + } println!("Client configuration completed.\n\n\n") } From ce6d858b880cf8bf685b5aac65ea68c62c764200 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:07:31 +0000 Subject: [PATCH 100/273] Additional comments --- nym-client/src/commands/mod.rs | 2 +- nym-client/src/commands/run.rs | 7 +++---- nym-client/src/config/mod.rs | 2 ++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/nym-client/src/commands/mod.rs b/nym-client/src/commands/mod.rs index 92e7c3dfe6..2908199326 100644 --- a/nym-client/src/commands/mod.rs +++ b/nym-client/src/commands/mod.rs @@ -4,7 +4,7 @@ use clap::ArgMatches; pub mod init; pub mod run; -pub(crate) fn overwrite_config(mut config: Config, matches: &ArgMatches) -> Config { +pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { if let Some(directory) = matches.value_of("directory") { config = config.with_custom_directory(directory); } diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index 6568009d70..775c93787e 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -1,5 +1,5 @@ use crate::client::NymClient; -use crate::commands::overwrite_config; +use crate::commands::override_config; use crate::config::persistance::pathfinder::ClientPathfinder; use crate::config::Config; use clap::ArgMatches; @@ -13,7 +13,7 @@ pub fn execute(matches: &ArgMatches) { Config::load_from_file(matches.value_of("config").map(|path| path.into()), Some(id)) .expect("Failed to load config file"); - config = overwrite_config(config, matches); + config = override_config(config, matches); let identity_keypair = PemStore::new(ClientPathfinder::new_from_config(&config)) .read_identity() @@ -25,6 +25,5 @@ pub fn execute(matches: &ArgMatches) { ); let client = NymClient::new(config); - // - // client.start().unwrap(); + client.start().unwrap(); } diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index f92c11cb4f..09ff35e47a 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -351,6 +351,8 @@ mod client_config { #[test] fn after_saving_default_config_the_loaded_one_is_identical() { + // need to figure out how to do something similar but without touching the disk + // or the file system at all... let temp_location = tempfile::tempdir().unwrap().path().join("config.toml"); let default_config = Config::default().with_id("foomp".to_string()); default_config From efda3ee834f3b837d524287120501d08eb5a527d Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:36:02 +0000 Subject: [PATCH 101/273] Further decreased logging level during healthcheck --- common/healthcheck/src/path_check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/healthcheck/src/path_check.rs b/common/healthcheck/src/path_check.rs index 074df7da44..43f4ef329f 100644 --- a/common/healthcheck/src/path_check.rs +++ b/common/healthcheck/src/path_check.rs @@ -252,7 +252,7 @@ impl PathChecker { debug!("sending test packet to {}", first_node_address); match first_node_client.send(packet, first_node_address).await { Err(err) => { - info!("failed to send packet to {} - {}", first_node_address, err); + debug!("failed to send packet to {} - {}", first_node_address, err); if self .paths_status .insert(path_identifier, PathStatus::Unhealthy) From 35f5816da83bd756adc6af54c3b2290264fc4004 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:36:29 +0000 Subject: [PATCH 102/273] Using auth token obtained during init --- nym-client/src/client/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 909c7f83c0..08c3fccea0 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -11,6 +11,7 @@ use futures::channel::mpsc; use futures::join; use log::*; use pemstore::pemstore::PemStore; +use sfw_provider_requests::AuthToken; use sphinx::route::Destination; use std::net::SocketAddr; use tokio::runtime::Runtime; @@ -103,14 +104,14 @@ impl NymClient { let provider_client_listener_address = rt.block_on(self.get_provider_socket_address(topology_controller.get_inner_ref())); - // this is temporary until rest of the code compiles so I could work on this - let temp_auth_token = None; - let mut provider_poller = provider_poller::ProviderPoller::new( poller_input_tx, provider_client_listener_address, self_address, - temp_auth_token, + self.config + .get_provider_auth_token() + .map(|str_token| AuthToken::try_from_base58_string(str_token).ok()) + .unwrap_or(None), self.config.get_fetch_message_delay(), ); From 04e415eb0cd2f86f24e189223c055f30610b6265 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:48:26 +0000 Subject: [PATCH 103/273] No more panics on websocket handshake error --- nym-client/src/sockets/ws.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index c97cb87883..73d8df3898 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -318,9 +318,13 @@ async fn accept_connection( .expect("connected streams should have a peer address"); debug!("Peer address: {}", address); - let mut ws_stream = tokio_tungstenite::accept_async(stream) - .await - .expect("Error during the websocket handshake occurred"); + let mut ws_stream = match tokio_tungstenite::accept_async(stream).await { + Ok(ws_stream) => ws_stream, + Err(e) => { + error!("Error during the websocket handshake occurred - {}", e); + return; + } + }; // Create a channel for our stream, which other sockets will use to // send us messages. Then register our address with the stream to send From e091d30f6ed1c045b8dfb2688baf9b8b9bf287c4 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:51:01 +0000 Subject: [PATCH 104/273] Slightly better logging messages --- common/healthcheck/src/result.rs | 2 +- nym-client/src/client/mod.rs | 3 ++- nym-client/src/client/provider_poller.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/healthcheck/src/result.rs b/common/healthcheck/src/result.rs index 677114a457..3a6826544a 100644 --- a/common/healthcheck/src/result.rs +++ b/common/healthcheck/src/result.rs @@ -144,7 +144,7 @@ impl HealthCheckResult { } } - info!( + debug!( "waiting {:?} for pending requests to resolve", resolution_timeout ); diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 08c3fccea0..eeb9dc8b0f 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -94,6 +94,7 @@ impl NymClient { // generate same type of keys we have as our identity let healthcheck_keys = MixIdentityKeyPair::new(); + info!("Trying to obtain initial compatible network topology before starting up rest of modules"); // TODO: when we switch to our graph topology, we need to remember to change 'Topology' type let topology_controller = rt.block_on(topology_control::TopologyControl::::new( self.config.get_directory_server(), @@ -117,7 +118,7 @@ impl NymClient { // registration if let Err(err) = rt.block_on(provider_poller.perform_initial_registration()) { - panic!("Failed to perform initial registration: {:?}", err); + panic!("Failed to perform initial provider registration: {:?}", err); }; // setup all of futures for the components running on the client diff --git a/nym-client/src/client/provider_poller.rs b/nym-client/src/client/provider_poller.rs index 03c76fb8a8..526acb9e4b 100644 --- a/nym-client/src/client/provider_poller.rs +++ b/nym-client/src/client/provider_poller.rs @@ -45,7 +45,7 @@ impl ProviderPoller { self.provider_client.update_token(auth_token) } else { - warn!("did not perform registration - we were already registered") + warn!("did not perform provider registration - we were already registered") } Ok(()) From 21877c240bf96da08efa4d7669482db71186efbd Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:56:39 +0000 Subject: [PATCH 105/273] Added topology resolution timeout to client debug config --- nym-client/src/config/mod.rs | 9 ++++++++- nym-client/src/config/template.rs | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 09ff35e47a..375085c737 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -13,6 +13,7 @@ const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: u64 = 500; const DEFAULT_AVERAGE_PACKET_DELAY: u64 = 200; const DEFAULT_FETCH_MESSAGES_DELAY: u64 = 1000; const DEFAULT_TOPOLOGY_REFRESH_RATE: u64 = 10_000; +const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: u64 = 5_000; const DEFAULT_LISTENING_PORT: u16 = 9001; @@ -329,7 +330,12 @@ pub struct Debug { /// meaning it will always try to use whatever it obtained on startup. /// The provided value is interpreted as milliseconds. topology_refresh_rate: u64, - // topology resolution timeout + + /// During initial healthcheck, test packets are sent through every single possible network + /// path. This timeout determines waiting period until it is decided that the packet + /// did not reach its destination. + /// The provided value is interpreted as milliseconds. + topology_resolution_timeout: u64, } impl Default for Debug { @@ -341,6 +347,7 @@ impl Default for Debug { message_sending_average_delay: DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY, rate_compliant_cover_messages_disabled: false, topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, + topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, } } } diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index e716eca179..31e739aa3d 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -101,5 +101,11 @@ rate_compliant_cover_messages_disabled = {{ debug.rate_compliant_cover_messages_ # The provided value is interpreted as milliseconds. topology_refresh_rate = {{ debug.topology_refresh_rate }} +# During initial healthcheck, test packets are sent through every single possible network +# path. This timeout determines waiting period until it is decided that the packet +# did not reach its destination. +# The provided value is interpreted as milliseconds. +topology_resolution_timeout = {{ debug.topology_resolution_timeout }} + "# } From ab2703e6c17a518cfe3e230a9f0cfaccef554c1b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 11:57:50 +0000 Subject: [PATCH 106/273] Updated comments on ibid. --- nym-client/src/config/mod.rs | 2 +- nym-client/src/config/template.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 375085c737..561ece60ec 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -331,7 +331,7 @@ pub struct Debug { /// The provided value is interpreted as milliseconds. topology_refresh_rate: u64, - /// During initial healthcheck, test packets are sent through every single possible network + /// During topology refresh, test packets are sent through every single possible network /// path. This timeout determines waiting period until it is decided that the packet /// did not reach its destination. /// The provided value is interpreted as milliseconds. diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index 31e739aa3d..ace8db32a7 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -101,7 +101,7 @@ rate_compliant_cover_messages_disabled = {{ debug.rate_compliant_cover_messages_ # The provided value is interpreted as milliseconds. topology_refresh_rate = {{ debug.topology_refresh_rate }} -# During initial healthcheck, test packets are sent through every single possible network +# During topology refresh, test packets are sent through every single possible network # path. This timeout determines waiting period until it is decided that the packet # did not reach its destination. # The provided value is interpreted as milliseconds. From 9e890da986d1b3182d172ff6a573a25219405d50 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 12:02:43 +0000 Subject: [PATCH 107/273] Additional debug config options --- nym-client/src/config/mod.rs | 7 +++++++ nym-client/src/config/template.rs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 561ece60ec..b086178f80 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -336,6 +336,13 @@ pub struct Debug { /// did not reach its destination. /// The provided value is interpreted as milliseconds. topology_resolution_timeout: u64, + + /// How many packets should be sent through each path during the healthcheck + number_of_healthcheck_test_packets: u64, + + /// In the current healthcheck implementation, minimum percentage of packets node must have + /// received to be considered healthy + node_score_threshold: f64, } impl Default for Debug { diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index ace8db32a7..a6bc525bea 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -106,6 +106,13 @@ topology_refresh_rate = {{ debug.topology_refresh_rate }} # did not reach its destination. # The provided value is interpreted as milliseconds. topology_resolution_timeout = {{ debug.topology_resolution_timeout }} + +# How many packets should be sent through each path during the healthcheck +number_of_healthcheck_test_packets = {{ debug.number_of_healthcheck_test_packets }} + +# In the current healthcheck implementation, minimum percentage of packets node must have +# received to be considered healthy +node_score_threshold = {{ debug.node_score_threshold }} "# } From ac5d16899e18a468449c43289a47db53118618f4 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 12:25:08 +0000 Subject: [PATCH 108/273] Using the new config options + slightly better constructor for topology controller --- common/healthcheck/src/lib.rs | 4 +- nym-client/src/client/mod.rs | 8 +++- nym-client/src/client/topology_control.rs | 45 ++++++++++++++++++++--- nym-client/src/config/mod.rs | 28 ++++++++++++-- nym-client/src/config/template.rs | 5 ++- validator/src/validator.rs | 3 +- 6 files changed, 77 insertions(+), 16 deletions(-) diff --git a/common/healthcheck/src/lib.rs b/common/healthcheck/src/lib.rs index fc4be5850e..04196db3ed 100644 --- a/common/healthcheck/src/lib.rs +++ b/common/healthcheck/src/lib.rs @@ -41,12 +41,12 @@ pub struct HealthChecker { impl HealthChecker { pub fn new( - resolution_timeout_f64: f64, + resolution_timeout: Duration, num_test_packets: usize, identity_keypair: MixIdentityKeyPair, ) -> Self { HealthChecker { - resolution_timeout: Duration::from_secs_f64(resolution_timeout_f64), + resolution_timeout, num_test_packets, identity_keypair, } diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index eeb9dc8b0f..2f2b94cce8 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -96,10 +96,16 @@ impl NymClient { info!("Trying to obtain initial compatible network topology before starting up rest of modules"); // TODO: when we switch to our graph topology, we need to remember to change 'Topology' type - let topology_controller = rt.block_on(topology_control::TopologyControl::::new( + let topology_controller_config = topology_control::TopologyControlConfig::::new( self.config.get_directory_server(), self.config.get_topology_refresh_rate(), healthcheck_keys, + self.config.get_topology_resolution_timeout(), + self.config.get_number_of_healthcheck_test_packets() as usize, + ); + + let topology_controller = rt.block_on(topology_control::TopologyControl::::new( + topology_controller_config, )); let provider_client_listener_address = diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index 22b0a4495b..c7403503c3 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -2,6 +2,7 @@ use crate::built_info; use crypto::identity::MixIdentityKeyPair; use healthcheck::HealthChecker; use log::{error, info, trace, warn}; +use std::marker::PhantomData; use std::sync::Arc; use std::time; use tokio::sync::RwLock as FRwLock; @@ -25,18 +26,50 @@ enum TopologyError { NoValidPathsError, } -impl TopologyControl { - pub(crate) async fn new( +pub(crate) struct TopologyControlConfig { + directory_server: String, + refresh_rate: time::Duration, + identity_keypair: MixIdentityKeyPair, + resolution_timeout: time::Duration, + number_test_packets: usize, + + // the only reason I put phantom data here is so that we would we able to infer type + // of TopologyControl directly from the provided config rather than having to + // specify it during TopologyControl::::new() call + _topology_type_phantom: PhantomData<*const T>, +} + +impl TopologyControlConfig { + pub(crate) fn new( directory_server: String, refresh_rate: time::Duration, identity_keypair: MixIdentityKeyPair, + resolution_timeout: time::Duration, + number_test_packets: usize, ) -> Self { - // this is a temporary solution as the healthcheck will eventually be moved to validators - let health_checker = healthcheck::HealthChecker::new(5.0, 2, identity_keypair); - - let mut topology_control = TopologyControl { + TopologyControlConfig { directory_server, refresh_rate, + identity_keypair, + resolution_timeout, + number_test_packets, + _topology_type_phantom: PhantomData, + } + } +} + +impl TopologyControl { + pub(crate) async fn new(cfg: TopologyControlConfig) -> Self { + // this is a temporary solution as the healthcheck will eventually be moved to validators + let health_checker = healthcheck::HealthChecker::new( + cfg.resolution_timeout, + cfg.number_test_packets, + cfg.identity_keypair, + ); + + let mut topology_control = TopologyControl { + directory_server: cfg.directory_server, + refresh_rate: cfg.refresh_rate, inner: Arc::new(FRwLock::new(Inner::new(None))), health_checker, }; diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index b086178f80..507e9e7dc5 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -7,7 +7,11 @@ use std::time; pub mod persistance; mod template; -// all of the below are defined in milliseconds +// 'CLIENT' +const DEFAULT_LISTENING_PORT: u16 = 9001; + +// 'DEBUG' +// where applicable, the below are defined in milliseconds const DEFAULT_LOOP_COVER_STREAM_AVERAGE_DELAY: u64 = 1000; const DEFAULT_MESSAGE_STREAM_AVERAGE_DELAY: u64 = 500; const DEFAULT_AVERAGE_PACKET_DELAY: u64 = 200; @@ -15,7 +19,8 @@ const DEFAULT_FETCH_MESSAGES_DELAY: u64 = 1000; const DEFAULT_TOPOLOGY_REFRESH_RATE: u64 = 10_000; const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: u64 = 5_000; -const DEFAULT_LISTENING_PORT: u16 = 9001; +const DEFAULT_NUMBER_OF_HEALTHCHECK_TEST_PACKETS: u64 = 2; +const DEFAULT_NODE_SCORE_THRESHOLD: f64 = 0.0; #[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)] #[serde(deny_unknown_fields)] @@ -186,6 +191,18 @@ impl Config { pub fn get_topology_refresh_rate(&self) -> time::Duration { time::Duration::from_millis(self.debug.topology_refresh_rate) } + + pub fn get_topology_resolution_timeout(&self) -> time::Duration { + time::Duration::from_millis(self.debug.topology_resolution_timeout) + } + + pub fn get_number_of_healthcheck_test_packets(&self) -> u64 { + self.debug.number_of_healthcheck_test_packets + } + + pub fn get_node_score_threshold(&self) -> f64 { + self.debug.node_score_threshold + } } fn de_option_string<'de, D>(deserializer: D) -> Result, D::Error> @@ -340,8 +357,9 @@ pub struct Debug { /// How many packets should be sent through each path during the healthcheck number_of_healthcheck_test_packets: u64, - /// In the current healthcheck implementation, minimum percentage of packets node must have - /// received to be considered healthy + /// In the current healthcheck implementation, threshold indicating percentage of packets + /// node received during healthcheck. Node's score must be above that value to be + /// considered healthy. node_score_threshold: f64, } @@ -355,6 +373,8 @@ impl Default for Debug { rate_compliant_cover_messages_disabled: false, topology_refresh_rate: DEFAULT_TOPOLOGY_REFRESH_RATE, topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, + number_of_healthcheck_test_packets: DEFAULT_NUMBER_OF_HEALTHCHECK_TEST_PACKETS, + node_score_threshold: DEFAULT_NODE_SCORE_THRESHOLD, } } } diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index a6bc525bea..78635045c1 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -110,8 +110,9 @@ topology_resolution_timeout = {{ debug.topology_resolution_timeout }} # How many packets should be sent through each path during the healthcheck number_of_healthcheck_test_packets = {{ debug.number_of_healthcheck_test_packets }} -# In the current healthcheck implementation, minimum percentage of packets node must have -# received to be considered healthy +# In the current healthcheck implementation, threshold indicating percentage of packets +# node received during healthcheck. Node's score must be above that value to be +# considered healthy. node_score_threshold = {{ debug.node_score_threshold }} "# diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 1536519b20..c11161711f 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -3,6 +3,7 @@ use crate::services::mixmining::health_check_runner; use crypto::identity::MixIdentityKeyPair; use healthcheck::HealthChecker; use serde::Deserialize; +use std::time; use tokio::runtime::Runtime; #[derive(Deserialize, Debug)] @@ -24,7 +25,7 @@ impl Validator { pub fn new(config: Config) -> Self { let dummy_keypair = MixIdentityKeyPair::new(); let hc = HealthChecker::new( - config.health_check.resolution_timeout, + time::Duration::from_secs_f64(config.health_check.resolution_timeout), config.health_check.num_test_packets, dummy_keypair.clone(), ); From c7c52e68b5dbd18f76da0ebdc6e087f06bb8c2de Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 13:07:45 +0000 Subject: [PATCH 109/273] Moved args definition to specific commands file --- nym-client/src/commands/init.rs | 34 +++++++++++++++- nym-client/src/commands/run.rs | 40 +++++++++++++++++- nym-client/src/main.rs | 72 ++------------------------------- 3 files changed, 75 insertions(+), 71 deletions(-) diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index c1569d93d8..c9f47790c4 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -1,7 +1,7 @@ use crate::built_info; use crate::commands::override_config; use crate::config::persistance::pathfinder::ClientPathfinder; -use clap::ArgMatches; +use clap::{App, Arg, ArgMatches}; use config::NymConfig; use crypto::identity::MixIdentityKeyPair; use directory_client::presence::Topology; @@ -11,6 +11,38 @@ use sphinx::route::DestinationAddressBytes; use topology::provider::Node; use topology::NymTopology; +pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { + App::new("init") + .about("Initialise a Nym client. Do this first!") + .arg(Arg::with_name("id") + .long("id") + .help("Id of the nym-mixnet-client we want to create config for.") + .takes_value(true) + .required(true) + ) + .arg(Arg::with_name("provider") + .long("provider") + .help("Id of the provider we have preference to connect to. If left empty, a random provider will be chosen.") + .takes_value(true) + ) + .arg(Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the client is getting topology from") + .takes_value(true), + ) + .arg(Arg::with_name("socket-type") + .long("socket-type") + .help("Type of socket to use (TCP, WebSocket or None) in all subsequent runs") + .takes_value(true) + ) + .arg(Arg::with_name("port") + .short("p") + .long("port") + .help("Port for the socket (if applicable) to listen on in all subsequent runs") + .takes_value(true) + ) +} + async fn try_provider_registrations( providers: Vec, our_address: DestinationAddressBytes, diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index 775c93787e..d02e834ee9 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -2,10 +2,48 @@ use crate::client::NymClient; use crate::commands::override_config; use crate::config::persistance::pathfinder::ClientPathfinder; use crate::config::Config; -use clap::ArgMatches; +use clap::{App, Arg, ArgMatches}; use config::NymConfig; use pemstore::pemstore::PemStore; +pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { + App::new("run") + .about("Run the Nym client with provided configuration client optionally overriding set parameters") + .arg(Arg::with_name("id") + .long("id") + .help("Id of the nym-mixnet-client we want to run.") + .takes_value(true) + .required(true) + ) + // the rest of arguments are optional, they are used to override settings in config file + .arg(Arg::with_name("config") + .long("config") + .help("Custom path to the nym-mixnet-client configuration file") + .takes_value(true) + ) + .arg(Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the client is getting topology from") + .takes_value(true), + ) + .arg(Arg::with_name("provider") + .long("provider") + .help("Id of the provider we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened") + .takes_value(true) + ) + .arg(Arg::with_name("socket-type") + .long("socket-type") + .help("Type of socket to use (TCP, WebSocket or None)") + .takes_value(true) + ) + .arg(Arg::with_name("port") + .short("p") + .long("port") + .help("Port for the socket (if applicable) to listen on") + .takes_value(true) + ) +} + pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); diff --git a/nym-client/src/main.rs b/nym-client/src/main.rs index 5b92f6b319..5907ab3cea 100644 --- a/nym-client/src/main.rs +++ b/nym-client/src/main.rs @@ -1,4 +1,4 @@ -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{App, ArgMatches}; pub mod built_info; pub mod client; @@ -14,74 +14,8 @@ fn main() { .version(built_info::PKG_VERSION) .author("Nymtech") .about("Implementation of the Nym Client") - .subcommand( - SubCommand::with_name("init") - .about("Initialise a Nym client. Do this first!") - .arg(Arg::with_name("id") - .long("id") - .help("Id of the nym-mixnet-client we want to create config for.") - .takes_value(true) - .required(true) - ) - .arg(Arg::with_name("provider") - .long("provider") - .help("Id of the provider we have preference to connect to. If left empty, a random provider will be chosen.") - .takes_value(true) - ) - .arg(Arg::with_name("directory") - .long("directory") - .help("Address of the directory server the client is getting topology from") - .takes_value(true), - ) - .arg(Arg::with_name("socket-type") - .long("socket-type") - .help("Type of socket to use (TCP, WebSocket or None) in all subsequent runs") - .takes_value(true) - ) - .arg(Arg::with_name("port") - .short("p") - .long("port") - .help("Port for the socket (if applicable) to listen on in all subsequent runs") - .takes_value(true) - ) - ) - .subcommand( - SubCommand::with_name("run") - .about("Run the Nym client with provided configuration client optionally overriding set parameters") - .arg(Arg::with_name("id") - .long("id") - .help("Id of the nym-mixnet-client we want to run.") - .takes_value(true) - .required(true) - ) - // the rest of arguments are optional, they are used to override settings in config file - .arg(Arg::with_name("config") - .long("config") - .help("Custom path to the nym-mixnet-client configuration file") - .takes_value(true) - ) - .arg(Arg::with_name("directory") - .long("directory") - .help("Address of the directory server the client is getting topology from") - .takes_value(true), - ) - .arg(Arg::with_name("provider") - .long("provider") - .help("Id of the provider we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened") - .takes_value(true) - ) - .arg(Arg::with_name("socket-type") - .long("socket-type") - .help("Type of socket to use (TCP, WebSocket or None)") - .takes_value(true) - ) - .arg(Arg::with_name("port") - .short("p") - .long("port") - .help("Port for the socket (if applicable) to listen on") - .takes_value(true) - ) - ) + .subcommand(commands::init::command_args()) + .subcommand(commands::run::command_args()) .get_matches(); execute(arg_matches); From 1e1e1db4f585cbb7a36346b17a306d62524b2355 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 13:15:24 +0000 Subject: [PATCH 110/273] Printing nym banner even in case of invalid command --- nym-client/src/main.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/nym-client/src/main.rs b/nym-client/src/main.rs index 5907ab3cea..c83c375beb 100644 --- a/nym-client/src/main.rs +++ b/nym-client/src/main.rs @@ -10,6 +10,8 @@ fn main() { dotenv::dotenv().ok(); pretty_env_logger::init(); + println!("{}", banner()); + let arg_matches = App::new("Nym Client") .version(built_info::PKG_VERSION) .author("Nymtech") @@ -23,22 +25,14 @@ fn main() { fn execute(matches: ArgMatches) { match matches.subcommand() { - ("init", Some(m)) => { - println!("{}", banner()); - commands::init::execute(m); - } - ("run", Some(m)) => { - println!("{}", banner()); - commands::run::execute(m); - } - _ => { - println!("{}", usage()); - } + ("init", Some(m)) => commands::init::execute(m), + ("run", Some(m)) => commands::run::execute(m), + _ => println!("{}", usage()), } } -fn usage() -> String { - banner() + "usage: --help to see available options.\n\n" +fn usage() -> &'static str { + "usage: --help to see available options.\n\n" } fn banner() -> String { From de2dbf7156c17d66345a676fb5a62cff748c96c5 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 14:15:22 +0000 Subject: [PATCH 111/273] Started creating mixnode commands, similarly to client --- mixnode/src/built_info.rs | 2 + mixnode/src/commands/init.rs | 5 +++ mixnode/src/commands/mod.rs | 4 ++ mixnode/src/commands/run.rs | 44 ++++++++++++++++++++++ mixnode/src/main.rs | 71 +++++++----------------------------- mixnode/src/node/runner.rs | 1 - 6 files changed, 69 insertions(+), 58 deletions(-) create mode 100644 mixnode/src/built_info.rs create mode 100644 mixnode/src/commands/init.rs create mode 100644 mixnode/src/commands/mod.rs create mode 100644 mixnode/src/commands/run.rs diff --git a/mixnode/src/built_info.rs b/mixnode/src/built_info.rs new file mode 100644 index 0000000000..d11fb6389f --- /dev/null +++ b/mixnode/src/built_info.rs @@ -0,0 +1,2 @@ +// The file has been placed there by the build script. +include!(concat!(env!("OUT_DIR"), "/built.rs")); diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs new file mode 100644 index 0000000000..0b06f75d05 --- /dev/null +++ b/mixnode/src/commands/init.rs @@ -0,0 +1,5 @@ +use clap::{App, Arg}; + +pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { + App::new("init").about("Initialise the mixnode") +} diff --git a/mixnode/src/commands/mod.rs b/mixnode/src/commands/mod.rs new file mode 100644 index 0000000000..7cfd48c75a --- /dev/null +++ b/mixnode/src/commands/mod.rs @@ -0,0 +1,4 @@ +use clap::ArgMatches; + +pub mod init; +pub mod run; diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs new file mode 100644 index 0000000000..ac2a20f99c --- /dev/null +++ b/mixnode/src/commands/run.rs @@ -0,0 +1,44 @@ +use clap::{App, Arg}; + +pub fn command_args<'a, 'b>() -> App<'a, 'b> { + App::new("run") + .about("Starts the mixnode") + .arg( + Arg::with_name("host") + .long("host") + .help("The custom host on which the mixnode will be running") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("port") + .long("port") + .help("The port on which the mixnode will be listening") + .takes_value(true), + ) + .arg( + Arg::with_name("layer") + .long("layer") + .help("The mixnet layer of this particular node") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("announce_host") + .long("announce-host") + .help("The host that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("announce_port") + .long("announce-port") + .help("The port that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the node is sending presence and metrics to") + .takes_value(true), + ) +} diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index f16f460dd3..4e80538187 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -1,7 +1,10 @@ -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{App, ArgMatches}; use log::*; use std::process; +pub mod built_info; +mod commands; +mod config; mod mix_peer; mod node; @@ -9,74 +12,28 @@ fn main() { dotenv::dotenv().ok(); pretty_env_logger::init(); + println!("{}", banner()); + let arg_matches = App::new("Nym Mixnode") .version(built_info::PKG_VERSION) .author("Nymtech") .about("Implementation of the Loopix-based Mixnode") - .subcommand( - SubCommand::with_name("run") - .about("Starts the mixnode") - .arg( - Arg::with_name("host") - .long("host") - .help("The custom host on which the mixnode will be running") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("port") - .long("port") - .help("The port on which the mixnode will be listening") - .takes_value(true), - ) - .arg( - Arg::with_name("layer") - .long("layer") - .help("The mixnet layer of this particular node") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("announce_host") - .long("announce-host") - .help("The host that will be reported to the directory server") - .takes_value(true) - ) - .arg( - Arg::with_name("announce_port") - .long("announce-port") - .help("The port that will be reported to the directory server") - .takes_value(true) - ) - .arg( - Arg::with_name("directory") - .long("directory") - .help("Address of the directory server the node is sending presence and metrics to") - .takes_value(true), - ), - ) + .subcommand(commands::init::command_args()) + .subcommand(commands::run::command_args()) .get_matches(); - if let Err(e) = execute(arg_matches) { - error!("{}", e); - process::exit(1); - } + execute(arg_matches); } -pub mod built_info { - // The file has been placed there by the build script. - include!(concat!(env!("OUT_DIR"), "/built.rs")); -} - -fn execute(matches: ArgMatches) -> Result<(), String> { +fn execute(matches: ArgMatches) { match matches.subcommand() { - ("run", Some(m)) => Ok(node::runner::start(m)), - _ => Err(usage()), + ("run", Some(m)) => node::runner::start(m), + _ => println!("{}", usage()), } } -fn usage() -> String { - banner() + "usage: --help to see available options.\n\n" +fn usage() -> &'static str { + "usage: --help to see available options.\n\n" } fn banner() -> String { diff --git a/mixnode/src/node/runner.rs b/mixnode/src/node/runner.rs index a403e02036..669b8d3c58 100644 --- a/mixnode/src/node/runner.rs +++ b/mixnode/src/node/runner.rs @@ -14,7 +14,6 @@ fn print_binding_warning(address: &str) { } pub fn start(matches: &ArgMatches) { - println!("{}", banner()); println!("Starting mixnode..."); let config = new_config(matches); From fad5c9862987fe418c485ceb116719e60c72f68e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 15:02:13 +0000 Subject: [PATCH 112/273] Initial mix config with working default test --- Cargo.lock | 4 + mixnode/Cargo.toml | 10 ++ mixnode/src/config/mod.rs | 169 +++++++++++++++++++++++++++++++++ mixnode/src/config/template.rs | 48 ++++++++++ mixnode/src/node/runner.rs | 1 - 5 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 mixnode/src/config/mod.rs create mode 100644 mixnode/src/config/template.rs diff --git a/Cargo.lock b/Cargo.lock index 4af4244bdd..9ec38d5c16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1448,13 +1448,17 @@ dependencies = [ "bs58", "built", "clap", + "config", "curve25519-dalek", "directory-client", + "dirs", "dotenv", "futures 0.3.1", "log", "pretty_env_logger", + "serde", "sphinx", + "tempfile", "tokio 0.2.10", ] diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index 32a9e1aeeb..26ccefad3d 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -11,14 +11,17 @@ edition = "2018" bs58 = "0.3.0" clap = "2.33.0" curve25519-dalek = "1.2.3" +dirs = "2.0.2" dotenv = "0.15.0" futures = "0.3.1" log = "0.4" pretty_env_logger = "0.3" +serde = { version = "1.0.104", features = ["derive"] } tokio = { version = "0.2", features = ["full"] } ## internal addressing = {path = "../common/addressing" } +config = {path = "../common/config"} directory-client = { path = "../common/clients/directory-client" } ## will be moved to proper dependencies once released @@ -26,3 +29,10 @@ sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f8368 [build-dependencies] built = "0.3.2" + +[dev-dependencies] +tempfile = "3.1.0" + +[features] +qa = [] +local = [] \ No newline at end of file diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs new file mode 100644 index 0000000000..35fefb5473 --- /dev/null +++ b/mixnode/src/config/mod.rs @@ -0,0 +1,169 @@ +use crate::config::template::config_template; +use config::NymConfig; +use serde::{Deserialize, Deserializer, Serialize}; +use std::path::PathBuf; + +mod template; + +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Config { + mixnode: MixNode, + + #[serde(default)] + logging: Logging, + #[serde(default)] + debug: Debug, +} + +impl NymConfig for Config { + fn template() -> &'static str { + config_template() + } + + fn config_file_name() -> String { + "config.toml".to_string() + } + + fn default_root_directory() -> PathBuf { + dirs::home_dir() + .expect("Failed to evaluate $HOME value") + .join(".nym") + .join("mixnodes") + } + + fn root_directory(&self) -> PathBuf { + self.mixnode.nym_root_directory.clone() + } + + fn config_directory(&self) -> PathBuf { + self.mixnode + .nym_root_directory + .join(&self.mixnode.id) + .join("config") + } + + fn data_directory(&self) -> PathBuf { + self.mixnode + .nym_root_directory + .join(&self.mixnode.id) + .join("data") + } +} + +impl Config { + pub fn new>(id: S) -> Self { + Config::default().with_id(id) + } + + // builder methods + pub fn with_id>(mut self, id: S) -> Self { + let id = id.into(); + if self + .mixnode + .private_identity_key_file + .as_os_str() + .is_empty() + { + self.mixnode.private_identity_key_file = + self::MixNode::default_private_identity_key_file(&id); + } + if self.mixnode.public_identity_key_file.as_os_str().is_empty() { + self.mixnode.public_identity_key_file = + self::MixNode::default_public_identity_key_file(&id); + } + self.mixnode.id = id; + self + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct MixNode { + /// ID specifies the human readable ID of this particular mixnode. + id: String, + + /// URL to the directory server. + directory_server: String, + + /// Path to file containing private identity key. + private_identity_key_file: PathBuf, + + /// Path to file containing public identity key. + public_identity_key_file: PathBuf, + + /// 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, +} + +impl MixNode { + fn default_directory_server() -> String { + #[cfg(feature = "qa")] + return "https://qa-directory.nymtech.net".to_string(); + #[cfg(feature = "local")] + return "http://localhost:8080".to_string(); + + "https://directory.nymtech.net".to_string() + } + + fn default_private_identity_key_file(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("private_identity.pem") + } + + fn default_public_identity_key_file(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("public_identity.pem") + } +} + +impl Default for MixNode { + fn default() -> Self { + MixNode { + id: "".to_string(), + directory_server: Self::default_directory_server(), + private_identity_key_file: Default::default(), + public_identity_key_file: Default::default(), + nym_root_directory: Config::default_root_directory(), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Logging {} + +impl Default for Logging { + fn default() -> Self { + Logging {} + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Debug {} + +impl Default for Debug { + fn default() -> Self { + Debug {} + } +} + +#[cfg(test)] +mod mixnode_config { + use super::*; + + #[test] + fn after_saving_default_config_the_loaded_one_is_identical() { + // need to figure out how to do something similar but without touching the disk + // or the file system at all... + let temp_location = tempfile::tempdir().unwrap().path().join("config.toml"); + let default_config = Config::default().with_id("foomp".to_string()); + default_config + .save_to_file(Some(temp_location.clone())) + .unwrap(); + + let loaded_config = Config::load_from_file(Some(temp_location), None).unwrap(); + + assert_eq!(default_config, loaded_config); + } +} diff --git a/mixnode/src/config/template.rs b/mixnode/src/config/template.rs new file mode 100644 index 0000000000..d5170ba6fc --- /dev/null +++ b/mixnode/src/config/template.rs @@ -0,0 +1,48 @@ +pub(crate) fn config_template() -> &'static str { + // While using normal toml marshalling would have been way simpler with less overhead, + // I think it's useful to have comments attached to the saved config file to explain behaviour of + // particular fields. + // Note: any changes to the template must be reflected in the appropriate structs in mod.rs. + r#" +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base mixnode config options ##### + +[mixnode] +# Human readable ID of this particular mixnode. +id = "{{ mixnode.id }}" + +# URL to the directory server. +directory_server = "{{ mixnode.directory_server }}" + +# Path to file containing private identity key. +private_identity_key_file = "{{ mixnode.private_identity_key_file }}" + +# Path to file containing public identity key. +public_identity_key_file = "{{ mixnode.public_identity_key_file }}" + +##### additional mixnode config options ##### + + +##### advanced configuration options ##### + +# Absolute path to the home Nym Clients directory. +nym_root_directory = "{{ mixnode.nym_root_directory }}" + + +##### logging configuration options ##### + +[logging] + +# TODO + + +##### debug configuration options ##### +# The following options should not be modified unless you know EXACTLY what you are doing +# as if set incorrectly, they may impact your anonymity. + +[debug] + +"# +} diff --git a/mixnode/src/node/runner.rs b/mixnode/src/node/runner.rs index 669b8d3c58..eec5496238 100644 --- a/mixnode/src/node/runner.rs +++ b/mixnode/src/node/runner.rs @@ -1,4 +1,3 @@ -use crate::banner; use crate::node; use crate::node::MixNode; use clap::ArgMatches; From 15c0c731e9e7bfc6b8d4f795dc03f41345f181d1 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 15:24:31 +0000 Subject: [PATCH 113/273] Saving mixnode's config on init --- mixnode/src/commands/init.rs | 28 ++++++++++++++++++++++++++-- mixnode/src/config/mod.rs | 17 +++++++++++++++++ mixnode/src/main.rs | 1 + 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs index 0b06f75d05..6b42e22025 100644 --- a/mixnode/src/commands/init.rs +++ b/mixnode/src/commands/init.rs @@ -1,5 +1,29 @@ -use clap::{App, Arg}; +use clap::{App, Arg, ArgMatches}; +use config::NymConfig; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { - App::new("init").about("Initialise the mixnode") + App::new("init").about("Initialise the mixnode").arg( + Arg::with_name("id") + .long("id") + .help("Id of the nym-mixnode we want to create config for.") + .takes_value(true) + .required(true), + ) +} + +pub fn execute(matches: &ArgMatches) { + println!("Initialising mixnode..."); + + let id = matches.value_of("id").unwrap(); // required for now + let mut config = crate::config::Config::new(id); + + // overriding config defaults here + + let config_save_location = config.get_config_file_save_location(); + config + .save_to_file(None) + .expect("Failed to save the config file"); + println!("Saved configuration file to {:?}", config_save_location); + + println!("Mixnode configuration completed.\n\n\n") } diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 35fefb5473..21d5234ce2 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -75,6 +75,23 @@ impl Config { self.mixnode.id = id; self } + + // getters + pub fn get_config_file_save_location(&self) -> PathBuf { + self.config_directory().join(Self::config_file_name()) + } + + pub fn get_private_identity_key_file(&self) -> PathBuf { + self.mixnode.private_identity_key_file.clone() + } + + pub fn get_public_identity_key_file(&self) -> PathBuf { + self.mixnode.public_identity_key_file.clone() + } + + pub fn get_directory_server(&self) -> String { + self.mixnode.directory_server.clone() + } } #[derive(Debug, Deserialize, PartialEq, Serialize)] diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index 4e80538187..4541f60850 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -27,6 +27,7 @@ fn main() { fn execute(matches: ArgMatches) { match matches.subcommand() { + ("init", Some(m)) => commands::init::execute(m), ("run", Some(m)) => node::runner::start(m), _ => println!("{}", usage()), } From 901996345cc2130919dc54ce818b7f2fb2687647 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 17:19:52 +0000 Subject: [PATCH 114/273] announce address in template --- mixnode/src/config/template.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mixnode/src/config/template.rs b/mixnode/src/config/template.rs index d5170ba6fc..4bee88a32e 100644 --- a/mixnode/src/config/template.rs +++ b/mixnode/src/config/template.rs @@ -13,6 +13,12 @@ pub(crate) fn config_template() -> &'static str { # Human readable ID of this particular mixnode. id = "{{ mixnode.id }}" +# Layer of this particular mixnode determining its position in the network. +layer = {{ mixnode.layer }} + +# Socket address to which this mixnode will bind to and will be listening for packets. +listening_address = "{{ mixnode.listening_address }}" + # URL to the directory server. directory_server = "{{ mixnode.directory_server }}" @@ -24,7 +30,14 @@ public_identity_key_file = "{{ mixnode.public_identity_key_file }}" ##### additional mixnode config options ##### - +# Optional address announced to the directory server for the clients to connect to. +# It is useful, say, in NAT scenarios or wanting to more easily update actual IP address +# later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. +# Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` +# are valid announce addresses, while the later will default to whatever port is used for +# `listening_address`. +announce_address = "{{ mixnode.announce_address }}" + ##### advanced configuration options ##### # Absolute path to the home Nym Clients directory. From a491f7543a3fb97a908336ba9ba0956e80c8ba6f Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 3 Feb 2020 17:20:29 +0000 Subject: [PATCH 115/273] More builder methods, incl. announce host/port parsers --- Cargo.lock | 2 + mixnode/Cargo.toml | 2 + mixnode/src/commands/init.rs | 90 ++++++++++++++++++++++++++--- mixnode/src/config/mod.rs | 106 ++++++++++++++++++++++++++++++++++- 4 files changed, 188 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ec38d5c16..e21e731b3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1449,12 +1449,14 @@ dependencies = [ "built", "clap", "config", + "crypto", "curve25519-dalek", "directory-client", "dirs", "dotenv", "futures 0.3.1", "log", + "pemstore", "pretty_env_logger", "serde", "sphinx", diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index 26ccefad3d..f583498830 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -22,7 +22,9 @@ tokio = { version = "0.2", features = ["full"] } ## internal addressing = {path = "../common/addressing" } config = {path = "../common/config"} +crypto = {path = "../common/crypto"} directory-client = { path = "../common/clients/directory-client" } +pemstore = {path = "../common/pemstore"} ## will be moved to proper dependencies once released sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs index 6b42e22025..ccbd1e0251 100644 --- a/mixnode/src/commands/init.rs +++ b/mixnode/src/commands/init.rs @@ -2,23 +2,95 @@ use clap::{App, Arg, ArgMatches}; use config::NymConfig; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { - App::new("init").about("Initialise the mixnode").arg( - Arg::with_name("id") - .long("id") - .help("Id of the nym-mixnode we want to create config for.") - .takes_value(true) - .required(true), - ) + App::new("init") + .about("Initialise the mixnode") + .arg( + Arg::with_name("id") + .long("id") + .help("Id of the nym-mixnode we want to create config for.") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("layer") + .long("layer") + .help("The mixnet layer of this particular node") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("host") + .long("host") + .help("The custom host on which the mixnode will be running") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("port") + .long("port") + .help("The port on which the mixnode will be listening") + .takes_value(true), + ) + .arg( + Arg::with_name("announce-host") + .long("announce-host") + .help("The host that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("announce-port") + .long("announce-port") + .help("The port that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the node is sending presence and metrics to") + .takes_value(true), + ) } pub fn execute(matches: &ArgMatches) { println!("Initialising mixnode..."); - let id = matches.value_of("id").unwrap(); // required for now - let mut config = crate::config::Config::new(id); + let id = matches.value_of("id").unwrap(); + let layer = matches.value_of("layer").unwrap().parse().unwrap(); + let mut config = crate::config::Config::new(id, layer); // overriding config defaults here + if let Some(host) = matches.value_of("host") { + config = config.with_listening_host(host); + } + + if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { + if let Err(err) = port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_listening_port(port.unwrap()); + } + + if let Some(directory) = matches.value_of("directory") { + config = config.with_custom_directory(directory); + } + + if let Some(announce_host) = matches.value_of("announce-host") { + config = config.with_announce_host(announce_host); + } + + if let Some(announce_port) = matches + .value_of("announce-port") + .map(|port| port.parse::()) + { + if let Err(err) = announce_port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_announce_port(announce_port.unwrap()); + } + let config_save_location = config.get_config_file_save_location(); config .save_to_file(None) diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 21d5234ce2..4c5b760e30 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -1,7 +1,10 @@ use crate::config::template::config_template; use config::NymConfig; -use serde::{Deserialize, Deserializer, Serialize}; +use log::*; +use serde::{Deserialize, Serialize}; +use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; +use std::str::FromStr; mod template; @@ -52,8 +55,8 @@ impl NymConfig for Config { } impl Config { - pub fn new>(id: S) -> Self { - Config::default().with_id(id) + pub fn new>(id: S, layer: usize) -> Self { + Config::default().with_id(id).with_layer(layer) } // builder methods @@ -76,6 +79,86 @@ impl Config { self } + pub fn with_layer(mut self, layer: usize) -> Self { + self.mixnode.layer = layer; + self + } + + pub fn with_custom_directory>(mut self, directory_server: S) -> Self { + self.mixnode.directory_server = directory_server.into(); + self + } + + pub fn with_listening_host>(mut self, host: S) -> Self { + // see if the provided `host` is just an ip address or ip:port + let host = host.into(); + + // is it ip:port? + match SocketAddr::from_str(host.as_ref()) { + Ok(socket_addr) => { + self.mixnode.listening_address = socket_addr; + self + } + // try just for ip + Err(_) => match IpAddr::from_str(host.as_ref()) { + Ok(ip_addr) => { + self.mixnode.listening_address.set_ip(ip_addr); + self + } + Err(_) => { + error!( + "failed to make any changes to config - invalid host {}", + host + ); + self + } + }, + } + } + + pub fn with_listening_port(mut self, port: u16) -> Self { + self.mixnode.listening_address.set_port(port); + self + } + + pub fn with_announce_host>(mut self, host: S) -> Self { + // this is slightly more complicated as we store announce information as String, + // since it might not necessarily be a valid SocketAddr (say `nymtech.net:8080` is a valid + // announce address, yet invalid SocketAddr` + + // first lets see if we received host:port or just host part of an address + let host = host.into(); + let split_host: Vec<_> = host.split(':').collect(); + match split_host.len() { + 1 => { + // we provided only 'host' part so we are going to reuse existing port + self.mixnode.announce_address = + format!("{}:{}", host, self.mixnode.listening_address.port()); + self + } + 2 => { + // we provided 'host:port' so just put the whole thing there + self.mixnode.announce_address = host; + self + } + _ => { + // we provided something completely invalid, so don't try to parse it + error!( + "failed to make any changes to config - invalid announce host {}", + host + ); + self + } + } + } + + pub fn with_announce_port(mut self, port: u16) -> Self { + let current_host: Vec<_> = self.mixnode.announce_address.split(':').collect(); + debug_assert_eq!(current_host.len(), 2); + self.mixnode.announce_address = format!("{}:{}", current_host[0], port); + self + } + // getters pub fn get_config_file_save_location(&self) -> PathBuf { self.config_directory().join(Self::config_file_name()) @@ -103,6 +186,20 @@ pub struct MixNode { /// URL to the directory server. directory_server: String, + /// Layer of this particular mixnode determining its position in the network. + layer: usize, + + /// Socket address to which this mixnode will bind to and will be listening for packets. + listening_address: SocketAddr, + + /// Optional address announced to the directory server for the clients to connect to. + /// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address + /// later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. + /// Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` + /// are valid announce addresses, while the later will default to whatever port is used for + /// `listening_address`. + announce_address: String, + /// Path to file containing private identity key. private_identity_key_file: PathBuf, @@ -138,6 +235,9 @@ impl Default for MixNode { MixNode { id: "".to_string(), directory_server: Self::default_directory_server(), + layer: 0, + listening_address: "0.0.0.0:1789".parse().unwrap(), + announce_address: "127.0.0.1789".to_string(), private_identity_key_file: Default::default(), public_identity_key_file: Default::default(), nym_root_directory: Config::default_root_directory(), From 495b0c7ed9b12aac830a662b3fa0ad4f896f79b9 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 11:21:00 +0000 Subject: [PATCH 116/273] Renamed 'PemStorable' trait to 'PemStorableKey' --- common/crypto/src/encryption/mod.rs | 6 +++--- common/crypto/src/identity/mod.rs | 6 +++--- common/crypto/src/lib.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/crypto/src/encryption/mod.rs b/common/crypto/src/encryption/mod.rs index 4a38cd89ed..1ab614e8d4 100644 --- a/common/crypto/src/encryption/mod.rs +++ b/common/crypto/src/encryption/mod.rs @@ -1,4 +1,4 @@ -use crate::PemStorable; +use crate::PemStorableKey; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; @@ -57,7 +57,7 @@ impl PrivateKey { } } -impl PemStorable for PrivateKey { +impl PemStorableKey for PrivateKey { fn pem_type(&self) -> String { String::from("X25519 PRIVATE KEY") } @@ -85,7 +85,7 @@ impl PublicKey { } } -impl PemStorable for PublicKey { +impl PemStorableKey for PublicKey { fn pem_type(&self) -> String { String::from("X25519 PUBLIC KEY") } diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index 270e25dcfb..39c03e8469 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -1,4 +1,4 @@ -use crate::{encryption, PemStorable}; +use crate::{encryption, PemStorableKey}; use bs58; use curve25519_dalek::scalar::Scalar; use sphinx::route::DestinationAddressBytes; @@ -65,7 +65,7 @@ impl MixIdentityPublicKey { } } -impl PemStorable for MixIdentityPublicKey { +impl PemStorableKey for MixIdentityPublicKey { fn pem_type(&self) -> String { format!("DUMMY KEY BASED ON {}", self.0.pem_type()) } @@ -101,7 +101,7 @@ impl MixIdentityPrivateKey { } } -impl PemStorable for MixIdentityPrivateKey { +impl PemStorableKey for MixIdentityPrivateKey { fn pem_type(&self) -> String { format!("DUMMY KEY BASED ON {}", self.0.pem_type()) } diff --git a/common/crypto/src/lib.rs b/common/crypto/src/lib.rs index e1f5f2d17c..c5c3334e47 100644 --- a/common/crypto/src/lib.rs +++ b/common/crypto/src/lib.rs @@ -4,6 +4,6 @@ pub mod identity; // TODO: this trait will need to be moved elsewhere, probably to some 'persistence' crate // but since it will need to be used by all identities, it's not really appropriate if it lived in nym-client -pub trait PemStorable { +pub trait PemStorableKey { fn pem_type(&self) -> String; } From 165d022015ee3418ba10d79f7354e7bb0b31bdc8 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 11:28:49 +0000 Subject: [PATCH 117/273] Defined 'PemStorableKeypair' trait and implemented it on existing keypairs --- common/crypto/src/encryption/mod.rs | 15 ++++++++++++++- common/crypto/src/identity/mod.rs | 15 ++++++++++++++- common/crypto/src/lib.rs | 8 ++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/common/crypto/src/encryption/mod.rs b/common/crypto/src/encryption/mod.rs index 1ab614e8d4..4d94f06f4b 100644 --- a/common/crypto/src/encryption/mod.rs +++ b/common/crypto/src/encryption/mod.rs @@ -1,4 +1,4 @@ -use crate::PemStorableKey; +use crate::{PemStorableKey, PemStorableKeyPair}; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; @@ -39,6 +39,19 @@ impl KeyPair { } } +impl PemStorableKeyPair for KeyPair { + type PrivatePemKey = PrivateKey; + type PublicPemKey = PublicKey; + + fn private_pem_key(&self) -> &Self::PrivatePemKey { + self.private_key() + } + + fn public_pem_key(&self) -> &Self::PublicPemKey { + self.public_key() + } +} + // COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct PrivateKey(pub Scalar); diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index 39c03e8469..b7fe294c78 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -1,4 +1,4 @@ -use crate::{encryption, PemStorableKey}; +use crate::{encryption, PemStorableKey, PemStorableKeyPair}; use bs58; use curve25519_dalek::scalar::Scalar; use sphinx::route::DestinationAddressBytes; @@ -36,6 +36,19 @@ impl MixIdentityKeyPair { } } +impl PemStorableKeyPair for MixIdentityKeyPair { + type PrivatePemKey = MixIdentityPrivateKey; + type PublicPemKey = MixIdentityPublicKey; + + fn private_pem_key(&self) -> &Self::PrivatePemKey { + self.private_key() + } + + fn public_pem_key(&self) -> &Self::PublicPemKey { + self.public_key() + } +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct MixIdentityPublicKey(encryption::PublicKey); diff --git a/common/crypto/src/lib.rs b/common/crypto/src/lib.rs index c5c3334e47..f5401aead5 100644 --- a/common/crypto/src/lib.rs +++ b/common/crypto/src/lib.rs @@ -7,3 +7,11 @@ pub mod identity; pub trait PemStorableKey { fn pem_type(&self) -> String; } + +pub trait PemStorableKeyPair { + type PrivatePemKey: PemStorableKey; + type PublicPemKey: PemStorableKey; + + fn private_pem_key(&self) -> &Self::PrivatePemKey; + fn public_pem_key(&self) -> &Self::PublicPemKey; +} From 4c900bbfff88c35eba38c2b1312851dfce2fd9fa Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:13:49 +0000 Subject: [PATCH 118/273] Extended pemstorable traits with methods required for more generic store --- common/crypto/src/encryption/mod.rs | 16 ++++++++++++++-- common/crypto/src/identity/mod.rs | 16 ++++++++++++++-- common/crypto/src/lib.rs | 13 +++++++++---- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/common/crypto/src/encryption/mod.rs b/common/crypto/src/encryption/mod.rs index 4d94f06f4b..650e221f0e 100644 --- a/common/crypto/src/encryption/mod.rs +++ b/common/crypto/src/encryption/mod.rs @@ -43,13 +43,17 @@ impl PemStorableKeyPair for KeyPair { type PrivatePemKey = PrivateKey; type PublicPemKey = PublicKey; - fn private_pem_key(&self) -> &Self::PrivatePemKey { + fn private_key(&self) -> &Self::PrivatePemKey { self.private_key() } - fn public_pem_key(&self) -> &Self::PublicPemKey { + fn public_key(&self) -> &Self::PublicPemKey { self.public_key() } + + fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self { + Self::from_bytes(priv_bytes, pub_bytes) + } } // COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed @@ -74,6 +78,10 @@ impl PemStorableKey for PrivateKey { fn pem_type(&self) -> String { String::from("X25519 PRIVATE KEY") } + + fn to_bytes(&self) -> Vec { + self.to_bytes() + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -102,4 +110,8 @@ impl PemStorableKey for PublicKey { fn pem_type(&self) -> String { String::from("X25519 PUBLIC KEY") } + + fn to_bytes(&self) -> Vec { + self.to_bytes() + } } diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index b7fe294c78..4c1ea3cf75 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -40,13 +40,17 @@ impl PemStorableKeyPair for MixIdentityKeyPair { type PrivatePemKey = MixIdentityPrivateKey; type PublicPemKey = MixIdentityPublicKey; - fn private_pem_key(&self) -> &Self::PrivatePemKey { + fn private_key(&self) -> &Self::PrivatePemKey { self.private_key() } - fn public_pem_key(&self) -> &Self::PublicPemKey { + fn public_key(&self) -> &Self::PublicPemKey { self.public_key() } + + fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self { + Self::from_bytes(priv_bytes, pub_bytes) + } } #[derive(Debug, Clone, Eq, PartialEq)] @@ -82,6 +86,10 @@ impl PemStorableKey for MixIdentityPublicKey { fn pem_type(&self) -> String { format!("DUMMY KEY BASED ON {}", self.0.pem_type()) } + + fn to_bytes(&self) -> Vec { + self.to_bytes() + } } // COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed @@ -118,4 +126,8 @@ impl PemStorableKey for MixIdentityPrivateKey { fn pem_type(&self) -> String { format!("DUMMY KEY BASED ON {}", self.0.pem_type()) } + + fn to_bytes(&self) -> Vec { + self.to_bytes() + } } diff --git a/common/crypto/src/lib.rs b/common/crypto/src/lib.rs index f5401aead5..bb8a61b20c 100644 --- a/common/crypto/src/lib.rs +++ b/common/crypto/src/lib.rs @@ -1,17 +1,22 @@ pub mod encryption; pub mod identity; -// TODO: this trait will need to be moved elsewhere, probably to some 'persistence' crate -// but since it will need to be used by all identities, it's not really appropriate if it lived in nym-client +// TODO: ideally those trait should be moved to 'pemstore' crate, however, that would cause +// circular dependency. The best solution would be to remove dependency on 'crypto' from +// pemstore by using either dynamic dispatch or generics - perhaps this should be done +// at some point during one of refactors. pub trait PemStorableKey { fn pem_type(&self) -> String; + fn to_bytes(&self) -> Vec; } pub trait PemStorableKeyPair { type PrivatePemKey: PemStorableKey; type PublicPemKey: PemStorableKey; - fn private_pem_key(&self) -> &Self::PrivatePemKey; - fn public_pem_key(&self) -> &Self::PublicPemKey; + fn private_key(&self) -> &Self::PrivatePemKey; + fn public_key(&self) -> &Self::PublicPemKey; + + fn from_bytes(priv_bytes: &[u8], pub_bytes: &[u8]) -> Self; } From c6fa5398451b1ab83b2ff5708188aa0e7e7d965b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:14:09 +0000 Subject: [PATCH 119/273] Pemstore capable of using slightly more generic types --- common/pemstore/src/pemstore.rs | 37 ++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index 88d0fe3ced..b123c1e2ce 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -1,6 +1,7 @@ use crate::pathfinder::PathFinder; use crypto::identity::MixIdentityKeyPair; -use crypto::PemStorable; +use crypto::PemStorableKey; +use crypto::{encryption, PemStorableKeyPair}; use log::info; use pem::{encode, parse, Pem}; use std::fs::File; @@ -8,11 +9,6 @@ use std::io; use std::io::prelude::*; use std::path::PathBuf; -#[allow(dead_code)] -pub fn read_mix_encryption_keypair_from_disk(_id: String) -> crypto::encryption::KeyPair { - unimplemented!() -} - pub struct PemStore { config_dir: PathBuf, private_mix_key_file: PathBuf, @@ -28,11 +24,11 @@ impl PemStore { } } - pub fn read_identity(&self) -> io::Result { + pub fn read_keypair(&self) -> io::Result { let private_pem = self.read_pem_file(self.private_mix_key_file.clone())?; let public_pem = self.read_pem_file(self.public_mix_key_file.clone())?; - let key_pair = MixIdentityKeyPair::from_bytes(&private_pem.contents, &public_pem.contents); + let key_pair = T::from_bytes(&private_pem.contents, &public_pem.contents); if key_pair.private_key().pem_type() != private_pem.tag { return Err(io::Error::new( @@ -51,16 +47,22 @@ impl PemStore { Ok(key_pair) } + pub fn read_encryption(&self) -> io::Result { + self.read_keypair() + } + + pub fn read_identity(&self) -> io::Result { + self.read_keypair() + } + fn read_pem_file(&self, filepath: PathBuf) -> io::Result { let mut pem_bytes = File::open(filepath)?; let mut buf = Vec::new(); pem_bytes.read_to_end(&mut buf)?; parse(&buf).map_err(|e| io::Error::new(io::ErrorKind::Other, e)) } - // This should be refactored and made more generic for when we have other kinds of - // KeyPairs that we want to persist (e.g. validator keypairs, or keys for - // signing vs encryption). However, for the moment, it does the job. - pub fn write_identity(&self, key_pair: MixIdentityKeyPair) -> io::Result<()> { + + fn write_keypair(&self, key_pair: impl PemStorableKeyPair) -> io::Result<()> { let private_key = key_pair.private_key(); let public_key = key_pair.public_key(); @@ -85,6 +87,17 @@ impl PemStore { Ok(()) } + // This should be refactored and made more generic for when we have other kinds of + // KeyPairs that we want to persist (e.g. validator keypairs, or keys for + // signing vs encryption). However, for the moment, it does the job. + pub fn write_identity(&self, key_pair: MixIdentityKeyPair) -> io::Result<()> { + self.write_keypair(key_pair) + } + + pub fn write_encryption_keys(&self, key_pair: encryption::KeyPair) -> io::Result<()> { + self.write_keypair(key_pair) + } + fn write_pem_file(&self, filepath: PathBuf, data: Vec, tag: String) -> io::Result<()> { // ensure the whole directory structure exists if let Some(parent_dir) = filepath.parent() { From 25a8f639126f83a9afc68a9116c45b46399b3c6e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:34:38 +0000 Subject: [PATCH 120/273] Extracted config overriding, similarly to nym-client --- mixnode/src/commands/mod.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/mixnode/src/commands/mod.rs b/mixnode/src/commands/mod.rs index 7cfd48c75a..b7c45f5bd5 100644 --- a/mixnode/src/commands/mod.rs +++ b/mixnode/src/commands/mod.rs @@ -1,4 +1,40 @@ +use crate::config::Config; use clap::ArgMatches; pub mod init; pub mod run; + +pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { + if let Some(host) = matches.value_of("host") { + config = config.with_listening_host(host); + } + + if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { + if let Err(err) = port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_listening_port(port.unwrap()); + } + + if let Some(directory) = matches.value_of("directory") { + config = config.with_custom_directory(directory); + } + + if let Some(announce_host) = matches.value_of("announce-host") { + config = config.with_announce_host(announce_host); + } + + if let Some(announce_port) = matches + .value_of("announce-port") + .map(|port| port.parse::()) + { + if let Err(err) = announce_port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_announce_port(announce_port.unwrap()); + } + + config +} From 7a99905f1d41a0cb1b0150d10a9011d6f5c878d2 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:34:53 +0000 Subject: [PATCH 121/273] Generating and saving keys on mixnode init --- mixnode/src/commands/init.rs | 49 +++++++++++++----------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs index ccbd1e0251..3ebbc018e8 100644 --- a/mixnode/src/commands/init.rs +++ b/mixnode/src/commands/init.rs @@ -1,5 +1,10 @@ +use crate::commands::override_config; +use crate::config::persistance::pathfinder::MixNodePathfinder; use clap::{App, Arg, ArgMatches}; use config::NymConfig; +use crypto::encryption; +use crypto::identity::MixIdentityKeyPair; +use pemstore::pemstore::PemStore; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { App::new("init") @@ -52,44 +57,24 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { } pub fn execute(matches: &ArgMatches) { - println!("Initialising mixnode..."); - let id = matches.value_of("id").unwrap(); + println!("Initialising mixnode {}...", id); + let layer = matches.value_of("layer").unwrap().parse().unwrap(); let mut config = crate::config::Config::new(id, layer); - // overriding config defaults here + config = override_config(config, matches); - if let Some(host) = matches.value_of("host") { - config = config.with_listening_host(host); - } + // TODO: which one should be used? + let sphinx_keys = encryption::KeyPair::new(); + // let alternative_keypair = MixIdentityKeyPair::new(); - if let Some(port) = matches.value_of("port").map(|port| port.parse::()) { - if let Err(err) = port { - // if port was overridden, it must be parsable - panic!("Invalid port value provided - {:?}", err); - } - config = config.with_listening_port(port.unwrap()); - } - - if let Some(directory) = matches.value_of("directory") { - config = config.with_custom_directory(directory); - } - - if let Some(announce_host) = matches.value_of("announce-host") { - config = config.with_announce_host(announce_host); - } - - if let Some(announce_port) = matches - .value_of("announce-port") - .map(|port| port.parse::()) - { - if let Err(err) = announce_port { - // if port was overridden, it must be parsable - panic!("Invalid port value provided - {:?}", err); - } - config = config.with_announce_port(announce_port.unwrap()); - } + let pathfinder = MixNodePathfinder::new_from_config(&config); + let pem_store = PemStore::new(pathfinder); + pem_store + .write_encryption_keys(sphinx_keys) + .expect("Failed to save sphinx keys"); + println!("Saved mixnet sphinx keypair"); let config_save_location = config.get_config_file_save_location(); config From 6c32d64194ae883436f7312c37ed6bc680453696 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:36:06 +0000 Subject: [PATCH 122/273] Changed config to be more explicit about type of keys used --- mixnode/src/config/mod.rs | 54 ++++++++++++++++++---------------- mixnode/src/config/template.rs | 6 ++-- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 4c5b760e30..ca534b9965 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -6,6 +6,7 @@ use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; use std::str::FromStr; +pub mod persistance; mod template; #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] @@ -62,18 +63,13 @@ impl Config { // builder methods pub fn with_id>(mut self, id: S) -> Self { let id = id.into(); - if self - .mixnode - .private_identity_key_file - .as_os_str() - .is_empty() - { - self.mixnode.private_identity_key_file = - self::MixNode::default_private_identity_key_file(&id); + if self.mixnode.private_sphinx_key_file.as_os_str().is_empty() { + self.mixnode.private_sphinx_key_file = + self::MixNode::default_private_sphinx_key_file(&id); } - if self.mixnode.public_identity_key_file.as_os_str().is_empty() { - self.mixnode.public_identity_key_file = - self::MixNode::default_public_identity_key_file(&id); + if self.mixnode.public_sphinx_key_file.as_os_str().is_empty() { + self.mixnode.public_sphinx_key_file = + self::MixNode::default_public_sphinx_key_file(&id); } self.mixnode.id = id; self @@ -164,17 +160,25 @@ impl Config { self.config_directory().join(Self::config_file_name()) } - pub fn get_private_identity_key_file(&self) -> PathBuf { - self.mixnode.private_identity_key_file.clone() + pub fn get_private_sphinx_key_file(&self) -> PathBuf { + self.mixnode.private_sphinx_key_file.clone() } - pub fn get_public_identity_key_file(&self) -> PathBuf { - self.mixnode.public_identity_key_file.clone() + pub fn get_public_sphinx_key_file(&self) -> PathBuf { + self.mixnode.public_sphinx_key_file.clone() } pub fn get_directory_server(&self) -> String { self.mixnode.directory_server.clone() } + + pub fn get_listening_address(&self) -> SocketAddr { + self.mixnode.listening_address + } + + pub fn get_announce_address(&self) -> String { + self.mixnode.announce_address.clone() + } } #[derive(Debug, Deserialize, PartialEq, Serialize)] @@ -200,11 +204,11 @@ pub struct MixNode { /// `listening_address`. announce_address: String, - /// Path to file containing private identity key. - private_identity_key_file: PathBuf, + /// Path to file containing private sphinx key. + private_sphinx_key_file: PathBuf, - /// Path to file containing public identity key. - public_identity_key_file: PathBuf, + /// Path to file containing public sphinx key. + public_sphinx_key_file: PathBuf, /// 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. @@ -221,12 +225,12 @@ impl MixNode { "https://directory.nymtech.net".to_string() } - fn default_private_identity_key_file(id: &str) -> PathBuf { - Config::default_data_directory(Some(id)).join("private_identity.pem") + fn default_private_sphinx_key_file(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("private_sphinx.pem") } - fn default_public_identity_key_file(id: &str) -> PathBuf { - Config::default_data_directory(Some(id)).join("public_identity.pem") + fn default_public_sphinx_key_file(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("public_sphinx.pem") } } @@ -238,8 +242,8 @@ impl Default for MixNode { layer: 0, listening_address: "0.0.0.0:1789".parse().unwrap(), announce_address: "127.0.0.1789".to_string(), - private_identity_key_file: Default::default(), - public_identity_key_file: Default::default(), + private_sphinx_key_file: Default::default(), + public_sphinx_key_file: Default::default(), nym_root_directory: Config::default_root_directory(), } } diff --git a/mixnode/src/config/template.rs b/mixnode/src/config/template.rs index 4bee88a32e..6ce5c38b2d 100644 --- a/mixnode/src/config/template.rs +++ b/mixnode/src/config/template.rs @@ -23,10 +23,10 @@ listening_address = "{{ mixnode.listening_address }}" directory_server = "{{ mixnode.directory_server }}" # Path to file containing private identity key. -private_identity_key_file = "{{ mixnode.private_identity_key_file }}" +private_sphinx_key_file = "{{ mixnode.private_sphinx_key_file }}" -# Path to file containing public identity key. -public_identity_key_file = "{{ mixnode.public_identity_key_file }}" +# Path to file containing public sphinx key. +public_sphinx_key_file = "{{ mixnode.public_sphinx_key_file }}" ##### additional mixnode config options ##### From 8caf218aed281ef8bbe82f8ab8a4b6a7b420c95e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:39:03 +0000 Subject: [PATCH 123/273] To and from b58 string on encryption public key --- common/crypto/src/encryption/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/crypto/src/encryption/mod.rs b/common/crypto/src/encryption/mod.rs index 650e221f0e..d3c8ba3d62 100644 --- a/common/crypto/src/encryption/mod.rs +++ b/common/crypto/src/encryption/mod.rs @@ -104,6 +104,14 @@ impl PublicKey { let key = MontgomeryPoint(bytes); Self(key) } + + pub fn to_base58_string(&self) -> String { + bs58::encode(&self.to_bytes()).into_string() + } + + pub fn from_base58_string(val: String) -> Self { + Self::from_bytes(&bs58::decode(&val).into_vec().unwrap()) + } } impl PemStorableKey for PublicKey { From f858d407ca56738848f3f37e49877df622b0508e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:48:40 +0000 Subject: [PATCH 124/273] Starting the mixnode with commands::run::execute --- mixnode/src/commands/run.rs | 105 +++++++++++++++++++++++++++++++----- mixnode/src/config/mod.rs | 4 ++ mixnode/src/main.rs | 2 +- mixnode/src/node/mod.rs | 12 ++--- 4 files changed, 104 insertions(+), 19 deletions(-) diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index ac2a20f99c..666e4f0c49 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -1,14 +1,40 @@ -use clap::{App, Arg}; +use crate::commands::override_config; +use crate::config::persistance::pathfinder::MixNodePathfinder; +use crate::config::Config; +use crate::node; +use crate::node::MixNode; +use clap::{App, Arg, ArgMatches}; +use config::NymConfig; +use pemstore::pemstore::PemStore; pub fn command_args<'a, 'b>() -> App<'a, 'b> { App::new("run") .about("Starts the mixnode") + .arg( + Arg::with_name("id") + .long("id") + .help("Id of the nym-mixnode we want to create config for.") + .takes_value(true) + .required(true), + ) + // the rest of arguments are optional, they are used to override settings in config file + .arg( + Arg::with_name("config") + .long("config") + .help("Custom path to the nym-mixnet-client configuration file") + .takes_value(true), + ) + .arg( + Arg::with_name("layer") + .long("layer") + .help("The mixnet layer of this particular node") + .takes_value(true), + ) .arg( Arg::with_name("host") .long("host") .help("The custom host on which the mixnode will be running") - .takes_value(true) - .required(true), + .takes_value(true), ) .arg( Arg::with_name("port") @@ -17,20 +43,13 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> { .takes_value(true), ) .arg( - Arg::with_name("layer") - .long("layer") - .help("The mixnet layer of this particular node") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("announce_host") + Arg::with_name("announce-host") .long("announce-host") .help("The host that will be reported to the directory server") .takes_value(true), ) .arg( - Arg::with_name("announce_port") + Arg::with_name("announce-port") .long("announce-port") .help("The port that will be reported to the directory server") .takes_value(true), @@ -42,3 +61,65 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> { .takes_value(true), ) } + +fn show_binding_warning(address: String) { + println!("\n##### WARNING #####"); + println!( + "\nYou are trying to bind to {} - you might not be accessible to other nodes\n\ + You can ignore this warning if you're running setup on a local network \n\ + or have set a custom 'announce-host'", + address + ); + println!("\n##### WARNING #####\n"); +} + +pub fn execute(matches: &ArgMatches) { + let id = matches.value_of("id").unwrap(); + + println!("Starting mixnode {}...", id); + + let mut config = + Config::load_from_file(matches.value_of("config").map(|path| path.into()), Some(id)) + .expect("Failed to load config file"); + + config = override_config(config, matches); + + let listening_ip_string = config.get_listening_address().ip().to_string(); + if listening_ip_string == "localhost" + || listening_ip_string == "127.0.0.1" + || listening_ip_string == "0.0.0.0" + { + show_binding_warning(listening_ip_string); + } + + let sphinx_keypair = PemStore::new(MixNodePathfinder::new_from_config(&config)) + .read_encryption() + .expect("Failed to read stored identity key files"); + + println!( + "Public encryption key: {}\nFor time being, it is identical to identity keys", + sphinx_keypair.public_key().to_base58_string() + ); + + println!("Directory server: {}", config.get_directory_server()); + println!( + "Listening for incoming packets on {}", + config.get_listening_address() + ); + println!( + "Announcing the following socket address: {}", + config.get_announce_address() + ); + + let old_dummy_config = node::Config { + announce_address: config.get_announce_address(), + directory_server: config.get_directory_server(), + layer: config.get_layer(), + public_key: sphinx_keypair.public_key().0, + secret_key: sphinx_keypair.private_key().0, + socket_address: config.get_listening_address(), + }; + + let mix = MixNode::new(&old_dummy_config); + mix.start(old_dummy_config).unwrap(); +} diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index ca534b9965..f1d9dfbd21 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -172,6 +172,10 @@ impl Config { self.mixnode.directory_server.clone() } + pub fn get_layer(&self) -> usize { + self.mixnode.layer + } + pub fn get_listening_address(&self) -> SocketAddr { self.mixnode.listening_address } diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index 4541f60850..212d2d6c3c 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -28,7 +28,7 @@ fn main() { fn execute(matches: ArgMatches) { match matches.subcommand() { ("init", Some(m)) => commands::init::execute(m), - ("run", Some(m)) => node::runner::start(m), + ("run", Some(m)) => commands::run::execute(m), _ => println!("{}", usage()), } } diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 502ff77843..b9085f4c48 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -20,12 +20,12 @@ mod presence; pub mod runner; pub struct Config { - announce_address: String, - directory_server: String, - layer: usize, - public_key: MontgomeryPoint, - secret_key: Scalar, - socket_address: SocketAddr, + pub announce_address: String, + pub directory_server: String, + pub layer: usize, + pub public_key: MontgomeryPoint, + pub secret_key: Scalar, + pub socket_address: SocketAddr, } impl Config { From 5fd8987eb9e0658efcdb7e4e0d9815e86cab6479 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:54:16 +0000 Subject: [PATCH 125/273] More easily expandable list of 'special' addresses that show binding warning --- mixnode/src/commands/run.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index 666e4f0c49..788de52ad3 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -73,6 +73,10 @@ fn show_binding_warning(address: String) { println!("\n##### WARNING #####\n"); } +fn special_addresses() -> Vec<&'static str> { + vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"] +} + pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); @@ -85,10 +89,7 @@ pub fn execute(matches: &ArgMatches) { config = override_config(config, matches); let listening_ip_string = config.get_listening_address().ip().to_string(); - if listening_ip_string == "localhost" - || listening_ip_string == "127.0.0.1" - || listening_ip_string == "0.0.0.0" - { + if special_addresses().contains(&listening_ip_string.as_ref()) { show_binding_warning(listening_ip_string); } From b1207caf7c0225977a5cee017c6fe1d6b8ed1cee Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 12:58:24 +0000 Subject: [PATCH 126/273] Inner methods on private/public encryption keys --- common/crypto/src/encryption/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/crypto/src/encryption/mod.rs b/common/crypto/src/encryption/mod.rs index d3c8ba3d62..4d9d7f6db0 100644 --- a/common/crypto/src/encryption/mod.rs +++ b/common/crypto/src/encryption/mod.rs @@ -72,6 +72,10 @@ impl PrivateKey { let key = Scalar::from_canonical_bytes(bytes).unwrap(); Self(key) } + + pub fn inner(&self) -> Scalar { + self.0 + } } impl PemStorableKey for PrivateKey { @@ -105,6 +109,10 @@ impl PublicKey { Self(key) } + pub fn inner(&self) -> MontgomeryPoint { + self.0 + } + pub fn to_base58_string(&self) -> String { bs58::encode(&self.to_bytes()).into_string() } From fee81aaff796ccb374bb3f9a2e03e5c8dda5cc30 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 13:04:41 +0000 Subject: [PATCH 127/273] Changed layer from usize to u64 for better serialization --- mixnode/src/config/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index f1d9dfbd21..c73bb5f650 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -56,7 +56,7 @@ impl NymConfig for Config { } impl Config { - pub fn new>(id: S, layer: usize) -> Self { + pub fn new>(id: S, layer: u64) -> Self { Config::default().with_id(id).with_layer(layer) } @@ -75,7 +75,7 @@ impl Config { self } - pub fn with_layer(mut self, layer: usize) -> Self { + pub fn with_layer(mut self, layer: u64) -> Self { self.mixnode.layer = layer; self } @@ -172,7 +172,7 @@ impl Config { self.mixnode.directory_server.clone() } - pub fn get_layer(&self) -> usize { + pub fn get_layer(&self) -> u64 { self.mixnode.layer } @@ -195,7 +195,7 @@ pub struct MixNode { directory_server: String, /// Layer of this particular mixnode determining its position in the network. - layer: usize, + layer: u64, /// Socket address to which this mixnode will bind to and will be listening for packets. listening_address: SocketAddr, From fe93a3f4d568ad88b0b8ae9676bb6b3f808c5494 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 13:08:21 +0000 Subject: [PATCH 128/273] Removed client keys being loaded twice --- nym-client/src/client/mod.rs | 9 +++++++-- nym-client/src/commands/run.rs | 9 --------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 2f2b94cce8..2e379e4aac 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -39,9 +39,14 @@ pub struct InputMessage(pub Destination, pub Vec); impl NymClient { fn load_identity_keys(config_file: &Config) -> MixIdentityKeyPair { - PemStore::new(ClientPathfinder::new_from_config(&config_file)) + let identity_keypair = PemStore::new(ClientPathfinder::new_from_config(&config_file)) .read_identity() - .expect("Failed to read stored identity key files") + .expect("Failed to read stored identity key files"); + println!( + "Public identity key: {}\nFor time being, it is identical to address", + identity_keypair.public_key.to_base58_string() + ); + identity_keypair } pub fn new(config: Config) -> Self { diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index d02e834ee9..7d9eeb3f01 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -53,15 +53,6 @@ pub fn execute(matches: &ArgMatches) { config = override_config(config, matches); - let identity_keypair = PemStore::new(ClientPathfinder::new_from_config(&config)) - .read_identity() - .expect("Failed to read stored identity key files"); - - println!( - "Public identity key: {}\nFor time being, it is identical to address", - identity_keypair.public_key.to_base58_string() - ); - let client = NymClient::new(config); client.start().unwrap(); } From 937becb1acb31be10cb69c1934ffa7ffcb36c2c5 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 13:11:37 +0000 Subject: [PATCH 129/273] Removed runner --- mixnode/src/node/mod.rs | 1 - mixnode/src/node/runner.rs | 85 -------------------------------------- 2 files changed, 86 deletions(-) delete mode 100644 mixnode/src/node/runner.rs diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index b9085f4c48..c590081f13 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -17,7 +17,6 @@ use tokio::runtime::Runtime; mod metrics; mod presence; -pub mod runner; pub struct Config { pub announce_address: String, diff --git a/mixnode/src/node/runner.rs b/mixnode/src/node/runner.rs deleted file mode 100644 index eec5496238..0000000000 --- a/mixnode/src/node/runner.rs +++ /dev/null @@ -1,85 +0,0 @@ -use crate::node; -use crate::node::MixNode; -use clap::ArgMatches; -use std::net::ToSocketAddrs; - -fn print_binding_warning(address: &str) { - println!("\n##### WARNING #####"); - println!( - "\nYou are trying to bind to {} - you might not be accessible to other nodes", - address - ); - println!("\n##### WARNING #####\n"); -} - -pub fn start(matches: &ArgMatches) { - println!("Starting mixnode..."); - - let config = new_config(matches); - println!("Public key: {}", config.public_key_string()); - println!("Directory server: {}", config.directory_server); - println!( - "Listening for incoming packets on {}", - config.socket_address - ); - println!( - "Announcing the following socket address: {}", - config.announce_address - ); - - let mix = MixNode::new(&config); - mix.start(config).unwrap(); -} - -fn new_config(matches: &ArgMatches) -> node::Config { - let host = matches.value_of("host").unwrap(); - if host == "localhost" || host == "127.0.0.1" || host == "0.0.0.0" { - print_binding_warning(host); - } - - let port = match matches.value_of("port").unwrap_or("1789").parse::() { - Ok(n) => n, - Err(err) => panic!("Invalid port value provided - {:?}", err), - }; - - let layer = match matches.value_of("layer").unwrap().parse::() { - Ok(n) => n, - Err(err) => panic!("Invalid layer value provided - {:?}", err), - }; - - let socket_address = (host, port) - .to_socket_addrs() - .expect("Failed to combine host and port") - .next() - .expect("Failed to extract the socket address from the iterator"); - - let announce_host = matches.value_of("announce_host").unwrap_or(host); - let announce_port = matches - .value_of("announce_port") - .map(|port| port.parse::().unwrap()) - .unwrap_or(port); - - let _ = (announce_host, announce_port) - .to_socket_addrs() - .expect("Failed to combine announce host and port") - .next() - .expect("Failed to extract the announce socket address from the iterator"); - - let announce_address = format!("{}:{}", announce_host, announce_port); - - let (secret_key, public_key) = sphinx::crypto::keygen(); - - let directory_server = matches - .value_of("directory") - .unwrap_or("https://directory.nymtech.net") - .to_string(); - - node::Config { - directory_server, - layer, - public_key, - socket_address, - announce_address, - secret_key, - } -} From e1cc16fe81cfdf172d6711d049651baf2391de60 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 13:12:17 +0000 Subject: [PATCH 130/273] Using new config in mixnode constructor --- mixnode/src/commands/run.rs | 22 +---------- mixnode/src/node/mod.rs | 76 +++++++++++++++++++----------------- mixnode/src/node/presence.rs | 22 +++++++---- 3 files changed, 57 insertions(+), 63 deletions(-) diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index 788de52ad3..c026bb4e65 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -93,15 +93,6 @@ pub fn execute(matches: &ArgMatches) { show_binding_warning(listening_ip_string); } - let sphinx_keypair = PemStore::new(MixNodePathfinder::new_from_config(&config)) - .read_encryption() - .expect("Failed to read stored identity key files"); - - println!( - "Public encryption key: {}\nFor time being, it is identical to identity keys", - sphinx_keypair.public_key().to_base58_string() - ); - println!("Directory server: {}", config.get_directory_server()); println!( "Listening for incoming packets on {}", @@ -112,15 +103,6 @@ pub fn execute(matches: &ArgMatches) { config.get_announce_address() ); - let old_dummy_config = node::Config { - announce_address: config.get_announce_address(), - directory_server: config.get_directory_server(), - layer: config.get_layer(), - public_key: sphinx_keypair.public_key().0, - secret_key: sphinx_keypair.private_key().0, - socket_address: config.get_listening_address(), - }; - - let mix = MixNode::new(&old_dummy_config); - mix.start(old_dummy_config).unwrap(); + let mix = MixNode::new(config); + mix.start().unwrap(); } diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index c590081f13..3400fd4816 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -1,12 +1,16 @@ +use crate::config::persistance::pathfinder::MixNodePathfinder; +use crate::config::Config; use crate::mix_peer::MixPeer; use crate::node; use crate::node::metrics::MetricsReporter; +use crypto::encryption; use curve25519_dalek::montgomery::MontgomeryPoint; use curve25519_dalek::scalar::Scalar; use futures::channel::mpsc; use futures::lock::Mutex; use futures::SinkExt; use log::*; +use pemstore::pemstore::PemStore; use sphinx::header::delays::Delay as SphinxDelay; use sphinx::{ProcessedPacket, SphinxPacket}; use std::net::SocketAddr; @@ -18,22 +22,6 @@ use tokio::runtime::Runtime; mod metrics; mod presence; -pub struct Config { - pub announce_address: String, - pub directory_server: String, - pub layer: usize, - pub public_key: MontgomeryPoint, - pub secret_key: Scalar, - pub socket_address: SocketAddr, -} - -impl Config { - pub fn public_key_string(&self) -> String { - let key_bytes = self.public_key.to_bytes().to_vec(); - bs58::encode(&key_bytes).into_string() - } -} - #[derive(Debug)] pub enum MixProcessingError { SphinxRecoveryError, @@ -178,22 +166,28 @@ impl PacketProcessor { // the MixNode will live for whole duration of this program pub struct MixNode { - directory_server: String, - network_address: SocketAddr, - public_key: MontgomeryPoint, - secret_key: Scalar, - // TODO: use it later to enforce forward travel - // layer: usize, + config: Config, + sphinx_keypair: encryption::KeyPair, } impl MixNode { - pub fn new(config: &Config) -> Self { + fn load_sphinx_keys(config_file: &Config) -> encryption::KeyPair { + let sphinx_keypair = PemStore::new(MixNodePathfinder::new_from_config(&config_file)) + .read_encryption() + .expect("Failed to read stored sphinx key files"); + println!( + "Public encryption key: {}\nFor time being, it is identical to identity keys", + sphinx_keypair.public_key().to_base58_string() + ); + sphinx_keypair + } + + pub fn new(config: Config) -> Self { + let sphinx_keypair = Self::load_sphinx_keys(&config); + MixNode { - directory_server: config.directory_server.clone(), - network_address: config.socket_address, - secret_key: config.secret_key, - public_key: config.public_key, - // layer: config.layer, + config, + sphinx_keypair, } } @@ -240,12 +234,11 @@ impl MixNode { } } - pub fn start(&self, config: node::Config) -> Result<(), Box> { + pub fn start(&self) -> Result<(), Box> { // Set up config and public key for this node let directory_cfg = directory_client::Config { - base_url: self.directory_server.clone(), + base_url: self.config.get_directory_server(), }; - let pub_key_str = bs58::encode(&self.public_key.to_bytes().to_vec()).into_string(); // Set up channels let (received_tx, received_rx) = mpsc::channel(1024); @@ -255,8 +248,14 @@ impl MixNode { let mut rt = Runtime::new()?; // Spawn Tokio tasks as necessary for node functionality + let tmp_notifier_config = presence::NotifierConfig { + directory_server: self.config.get_directory_server(), + host: self.config.get_announce_address(), + pub_key: self.sphinx_keypair.public_key().to_base58_string(), + layer: self.config.get_layer(), + }; rt.spawn({ - let presence_notifier = presence::Notifier::new(&config); + let presence_notifier = presence::Notifier::new(tmp_notifier_config); presence_notifier.run() }); @@ -272,14 +271,19 @@ impl MixNode { rt.spawn(MetricsReporter::run_metrics_sender( metrics, directory_cfg, - pub_key_str, + self.sphinx_keypair.public_key().to_base58_string(), )); // Spawn the root task rt.block_on(async { - let mut listener = tokio::net::TcpListener::bind(self.network_address).await?; - let processing_data = - ProcessingData::new(self.secret_key, received_tx, sent_tx).add_arc_mutex(); + let mut listener = + tokio::net::TcpListener::bind(self.config.get_listening_address()).await?; + let processing_data = ProcessingData::new( + self.sphinx_keypair.private_key().inner(), + received_tx, + sent_tx, + ) + .add_arc_mutex(); loop { let (socket, _) = listener.accept().await?; diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs index b017bacdea..70dd045d95 100644 --- a/mixnode/src/node/presence.rs +++ b/mixnode/src/node/presence.rs @@ -10,16 +10,24 @@ pub struct Notifier { presence: MixNodePresence, } +pub struct NotifierConfig { + // TEMPORARLY PUBLIC + pub directory_server: String, + pub host: String, + pub pub_key: String, + pub layer: u64, +} + impl Notifier { - pub fn new(node_config: &node::Config) -> Notifier { - let config = directory_client::Config { - base_url: node_config.directory_server.clone(), + pub fn new(config: NotifierConfig) -> Notifier { + let directory_client_cfg = directory_client::Config { + base_url: config.directory_server, }; - let net_client = directory_client::Client::new(config); + let net_client = directory_client::Client::new(directory_client_cfg); let presence = MixNodePresence { - host: node_config.announce_address.clone(), - pub_key: node_config.public_key_string(), - layer: node_config.layer as u64, + host: config.host, + pub_key: config.pub_key, + layer: config.layer, last_seen: 0, version: env!("CARGO_PKG_VERSION").to_string(), }; From ed9ee2734056e99ffa2c6cfffd26f2ec7e61c301 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 13:18:00 +0000 Subject: [PATCH 131/273] Uncommited mixnode persitance module --- mixnode/src/config/persistance/mod.rs | 1 + mixnode/src/config/persistance/pathfinder.rs | 56 ++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 mixnode/src/config/persistance/mod.rs create mode 100644 mixnode/src/config/persistance/pathfinder.rs diff --git a/mixnode/src/config/persistance/mod.rs b/mixnode/src/config/persistance/mod.rs new file mode 100644 index 0000000000..5692e9bc8b --- /dev/null +++ b/mixnode/src/config/persistance/mod.rs @@ -0,0 +1 @@ +pub mod pathfinder; diff --git a/mixnode/src/config/persistance/pathfinder.rs b/mixnode/src/config/persistance/pathfinder.rs new file mode 100644 index 0000000000..d4bfe9da5e --- /dev/null +++ b/mixnode/src/config/persistance/pathfinder.rs @@ -0,0 +1,56 @@ +use crate::config::Config; +use pemstore::pathfinder::PathFinder; +use std::path::PathBuf; + +#[derive(Debug)] +pub struct MixNodePathfinder { + pub config_dir: PathBuf, + pub private_sphinx_key: PathBuf, + pub public_sphinx_key: PathBuf, +} + +impl MixNodePathfinder { + pub fn new(id: String) -> Self { + let os_config_dir = dirs::config_dir().unwrap(); // grabs the OS default config dir + let config_dir = os_config_dir.join("nym").join("mixnodes").join(id); + let private_sphinx_key = config_dir.join("private.pem"); + let public_sphinx_key = config_dir.join("public.pem"); + MixNodePathfinder { + config_dir, + private_sphinx_key, + public_sphinx_key, + } + } + + pub fn new_from_config(config: &Config) -> Self { + MixNodePathfinder { + config_dir: config.get_config_file_save_location(), + private_sphinx_key: config.get_private_sphinx_key_file(), + public_sphinx_key: config.get_public_sphinx_key_file(), + } + } +} + +impl PathFinder for MixNodePathfinder { + fn config_dir(&self) -> PathBuf { + self.config_dir.clone() + } + + fn private_identity_key(&self) -> PathBuf { + // TEMPORARILY USE SAME KEYS AS ENCRYPTION + self.private_sphinx_key.clone() + } + + fn public_identity_key(&self) -> PathBuf { + // TEMPORARILY USE SAME KEYS AS ENCRYPTION + self.public_sphinx_key.clone() + } + + fn private_encryption_key(&self) -> Option { + Some(self.private_sphinx_key.clone()) + } + + fn public_encryption_key(&self) -> Option { + Some(self.public_sphinx_key.clone()) + } +} From b2f41de27bf6a0a171a59a2f2782d549a19e9bf0 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 14:36:02 +0000 Subject: [PATCH 132/273] Constructor for notifier config --- mixnode/src/node/mod.rs | 14 +++++++------- mixnode/src/node/presence.rs | 33 ++++++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 3400fd4816..1eaa2e8d37 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -248,14 +248,14 @@ impl MixNode { let mut rt = Runtime::new()?; // Spawn Tokio tasks as necessary for node functionality - let tmp_notifier_config = presence::NotifierConfig { - directory_server: self.config.get_directory_server(), - host: self.config.get_announce_address(), - pub_key: self.sphinx_keypair.public_key().to_base58_string(), - layer: self.config.get_layer(), - }; + let notifier_config = presence::NotifierConfig::new( + self.config.get_directory_server(), + self.config.get_announce_address(), + self.sphinx_keypair.public_key().to_base58_string(), + self.config.get_layer(), + ); rt.spawn({ - let presence_notifier = presence::Notifier::new(tmp_notifier_config); + let presence_notifier = presence::Notifier::new(notifier_config); presence_notifier.run() }); diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs index 70dd045d95..d97e4ea05d 100644 --- a/mixnode/src/node/presence.rs +++ b/mixnode/src/node/presence.rs @@ -1,4 +1,4 @@ -use crate::node; +use crate::built_info; use directory_client::presence::mixnodes::MixNodePresence; use directory_client::requests::presence_mixnodes_post::PresenceMixNodesPoster; use directory_client::DirectoryClient; @@ -11,11 +11,26 @@ pub struct Notifier { } pub struct NotifierConfig { - // TEMPORARLY PUBLIC - pub directory_server: String, - pub host: String, - pub pub_key: String, - pub layer: u64, + directory_server: String, + announce_host: String, + pub_key_string: String, + layer: u64, +} + +impl NotifierConfig { + pub fn new( + directory_server: String, + announce_host: String, + pub_key_string: String, + layer: u64, + ) -> Self { + NotifierConfig { + directory_server, + announce_host, + pub_key_string, + layer, + } + } } impl Notifier { @@ -25,11 +40,11 @@ impl Notifier { }; let net_client = directory_client::Client::new(directory_client_cfg); let presence = MixNodePresence { - host: config.host, - pub_key: config.pub_key, + host: config.announce_host, + pub_key: config.pub_key_string, layer: config.layer, last_seen: 0, - version: env!("CARGO_PKG_VERSION").to_string(), + version: built_info::PKG_VERSION.to_string(), }; Notifier { net_client, From 7723dc7aedb3a53637d3abcf11ab1c7fe7e574d5 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 14:50:20 +0000 Subject: [PATCH 133/273] constant default port --- mixnode/src/config/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index c73bb5f650..135551f7a7 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -9,6 +9,9 @@ use std::str::FromStr; pub mod persistance; mod template; +// 'MIXNODE' +const DEFAULT_LISTENING_PORT: u16 = 1789; + #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Config { @@ -244,8 +247,10 @@ impl Default for MixNode { id: "".to_string(), directory_server: Self::default_directory_server(), layer: 0, - listening_address: "0.0.0.0:1789".parse().unwrap(), - announce_address: "127.0.0.1789".to_string(), + listening_address: format!("0.0.0.0:{}", DEFAULT_LISTENING_PORT) + .parse() + .unwrap(), + announce_address: format!("127.0.0.1:{}", DEFAULT_LISTENING_PORT), private_sphinx_key_file: Default::default(), public_sphinx_key_file: Default::default(), nym_root_directory: Config::default_root_directory(), From 1080ad5ac1ea531248392ed03ecba77f77044f97 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 15:08:13 +0000 Subject: [PATCH 134/273] Two debug directory servers - presence and metrics --- mixnode/src/commands/run.rs | 10 ++++++- mixnode/src/config/mod.rs | 54 ++++++++++++++++++++++++------------- mixnode/src/node/mod.rs | 12 ++++----- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index c026bb4e65..c00033a61a 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -93,7 +93,15 @@ pub fn execute(matches: &ArgMatches) { show_binding_warning(listening_ip_string); } - println!("Directory server: {}", config.get_directory_server()); + println!( + "Directory server [presence]: {}", + config.get_presence_directory_server() + ); + println!( + "Directory server [metrics]: {}", + config.get_metrics_directory_server() + ); + println!( "Listening for incoming packets on {}", config.get_listening_address() diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 135551f7a7..16974def01 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -83,8 +83,12 @@ impl Config { self } + // if you want to use distinct servers for metrics and presence + // you need to do so in the config.toml file. pub fn with_custom_directory>(mut self, directory_server: S) -> Self { - self.mixnode.directory_server = directory_server.into(); + let directory_server_string = directory_server.into(); + self.debug.presence_directory_server = directory_server_string.clone(); + self.debug.metrics_directory_server = directory_server_string; self } @@ -171,8 +175,15 @@ impl Config { self.mixnode.public_sphinx_key_file.clone() } - pub fn get_directory_server(&self) -> String { - self.mixnode.directory_server.clone() + pub fn get_presence_directory_server(&self) -> String { + self.debug.presence_directory_server.clone() + } + + + pub fn get_metrics_directory_server(&self) -> String { + self.debug.metrics_directory_server.clone() + } + } pub fn get_layer(&self) -> u64 { @@ -194,9 +205,6 @@ pub struct MixNode { /// ID specifies the human readable ID of this particular mixnode. id: String, - /// URL to the directory server. - directory_server: String, - /// Layer of this particular mixnode determining its position in the network. layer: u64, @@ -223,15 +231,6 @@ pub struct MixNode { } impl MixNode { - fn default_directory_server() -> String { - #[cfg(feature = "qa")] - return "https://qa-directory.nymtech.net".to_string(); - #[cfg(feature = "local")] - return "http://localhost:8080".to_string(); - - "https://directory.nymtech.net".to_string() - } - fn default_private_sphinx_key_file(id: &str) -> PathBuf { Config::default_data_directory(Some(id)).join("private_sphinx.pem") } @@ -245,7 +244,6 @@ impl Default for MixNode { fn default() -> Self { MixNode { id: "".to_string(), - directory_server: Self::default_directory_server(), layer: 0, listening_address: format!("0.0.0.0:{}", DEFAULT_LISTENING_PORT) .parse() @@ -270,11 +268,31 @@ impl Default for Logging { #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] -pub struct Debug {} +pub struct Debug { + // The idea of additional 'directory servers' is to let mixes report their presence + // and metrics to separate places + /// Directory server to which the server will be reporting their presence data. + presence_directory_server: String, + + /// Directory server to which the server will be reporting their metrics data. + metrics_directory_server: String, + +impl Debug { + fn default_directory_server() -> String { + #[cfg(feature = "qa")] + return "https://qa-directory.nymtech.net".to_string(); + #[cfg(feature = "local")] + return "http://localhost:8080".to_string(); + + "https://directory.nymtech.net".to_string() + } +} impl Default for Debug { fn default() -> Self { - Debug {} + Debug { + presence_directory_server: Self::default_directory_server(), + metrics_directory_server: Self::default_directory_server(), } } diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 1eaa2e8d37..60a7c5e4f5 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -235,11 +235,6 @@ impl MixNode { } pub fn start(&self) -> Result<(), Box> { - // Set up config and public key for this node - let directory_cfg = directory_client::Config { - base_url: self.config.get_directory_server(), - }; - // Set up channels let (received_tx, received_rx) = mpsc::channel(1024); let (sent_tx, sent_rx) = mpsc::channel(1024); @@ -249,7 +244,7 @@ impl MixNode { // Spawn Tokio tasks as necessary for node functionality let notifier_config = presence::NotifierConfig::new( - self.config.get_directory_server(), + self.config.get_presence_directory_server(), self.config.get_announce_address(), self.sphinx_keypair.public_key().to_base58_string(), self.config.get_layer(), @@ -268,6 +263,11 @@ impl MixNode { metrics.clone(), sent_rx, )); + + let directory_cfg = directory_client::Config { + base_url: self.config.get_metrics_directory_server(), + }; + rt.spawn(MetricsReporter::run_metrics_sender( metrics, directory_cfg, From fd33589326d41cba7166239289dd451cfa0cb61b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 15:14:17 +0000 Subject: [PATCH 135/273] Using sending delay from config for the presence notifier --- mixnode/src/node/presence.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs index d97e4ea05d..55d1c10df2 100644 --- a/mixnode/src/node/presence.rs +++ b/mixnode/src/node/presence.rs @@ -3,11 +3,13 @@ use directory_client::presence::mixnodes::MixNodePresence; use directory_client::requests::presence_mixnodes_post::PresenceMixNodesPoster; use directory_client::DirectoryClient; use log::{debug, error}; +use std::time; use std::time::Duration; pub struct Notifier { pub net_client: directory_client::Client, presence: MixNodePresence, + sending_delay: time::Duration, } pub struct NotifierConfig { @@ -15,6 +17,7 @@ pub struct NotifierConfig { announce_host: String, pub_key_string: String, layer: u64, + sending_delay: time::Duration, } impl NotifierConfig { @@ -23,12 +26,14 @@ impl NotifierConfig { announce_host: String, pub_key_string: String, layer: u64, + sending_delay: time::Duration, ) -> Self { NotifierConfig { directory_server, announce_host, pub_key_string, layer, + sending_delay, } } } @@ -49,6 +54,7 @@ impl Notifier { Notifier { net_client, presence, + sending_delay: config.sending_delay, } } @@ -60,11 +66,9 @@ impl Notifier { } pub async fn run(self) { - let delay_duration = Duration::from_secs(5); - loop { self.notify(); - tokio::time::delay_for(delay_duration).await; + tokio::time::delay_for(self.sending_delay).await; } } } From 448fc4a3618d88c5009c25ca1a6c3ebdfc8281d5 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 15:15:51 +0000 Subject: [PATCH 136/273] ibid. for metrics --- mixnode/src/node/metrics.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mixnode/src/node/metrics.rs b/mixnode/src/node/metrics.rs index adc1d74e48..c29f7fef26 100644 --- a/mixnode/src/node/metrics.rs +++ b/mixnode/src/node/metrics.rs @@ -74,11 +74,11 @@ impl MetricsReporter { metrics: Arc>, cfg: directory_client::Config, pub_key_str: String, + sending_delay: Duration, ) { - let delay_duration = Duration::from_secs(METRICS_INTERVAL); let directory_client = directory_client::Client::new(cfg); loop { - tokio::time::delay_for(delay_duration).await; + tokio::time::delay_for(sending_delay).await; let (received, sent) = MetricsReporter::acquire_and_reset_metrics(metrics.clone()).await; From bed0badc15a195a96eeea6f7bb69d7ccc727471a Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 15:17:25 +0000 Subject: [PATCH 137/273] Defaults in the config + using said fields when starting mixnode --- mixnode/src/config/mod.rs | 21 +++++++++++++++++++++ mixnode/src/node/mod.rs | 2 ++ mixnode/src/node/presence.rs | 9 ++++----- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 16974def01..69e0978975 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -5,6 +5,7 @@ use serde::{Deserialize, Serialize}; use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; use std::str::FromStr; +use std::time; pub mod persistance; mod template; @@ -12,6 +13,11 @@ mod template; // 'MIXNODE' const DEFAULT_LISTENING_PORT: u16 = 1789; +// 'DEBUG' +// where applicable, the below are defined in milliseconds +const DEFAULT_PRESENCE_SENDING_DELAY: u64 = 3000; +const DEFAULT_METRICS_SENDING_DELAY: u64 = 3000; + #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Config { @@ -179,11 +185,16 @@ impl Config { self.debug.presence_directory_server.clone() } + pub fn get_presence_sending_delay(&self) -> time::Duration { + time::Duration::from_millis(self.debug.presence_sending_delay) + } pub fn get_metrics_directory_server(&self) -> String { self.debug.metrics_directory_server.clone() } + pub fn get_metrics_sending_delay(&self) -> time::Duration { + time::Duration::from_millis(self.debug.metrics_sending_delay) } pub fn get_layer(&self) -> u64 { @@ -274,10 +285,16 @@ pub struct Debug { /// Directory server to which the server will be reporting their presence data. presence_directory_server: String, + /// Delay between each subsequent presence data being sent. + presence_sending_delay: u64, /// Directory server to which the server will be reporting their metrics data. metrics_directory_server: String, + /// Delay between each subsequent metrics data being sent. + metrics_sending_delay: u64, +} + impl Debug { fn default_directory_server() -> String { #[cfg(feature = "qa")] @@ -288,11 +305,15 @@ impl Debug { "https://directory.nymtech.net".to_string() } } + impl Default for Debug { fn default() -> Self { Debug { presence_directory_server: Self::default_directory_server(), + presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY, metrics_directory_server: Self::default_directory_server(), + metrics_sending_delay: DEFAULT_METRICS_SENDING_DELAY, + } } } diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 60a7c5e4f5..5bd392e3bb 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -248,6 +248,7 @@ impl MixNode { self.config.get_announce_address(), self.sphinx_keypair.public_key().to_base58_string(), self.config.get_layer(), + self.config.get_presence_sending_delay(), ); rt.spawn({ let presence_notifier = presence::Notifier::new(notifier_config); @@ -272,6 +273,7 @@ impl MixNode { metrics, directory_cfg, self.sphinx_keypair.public_key().to_base58_string(), + self.config.get_metrics_sending_delay(), )); // Spawn the root task diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs index 55d1c10df2..bdcdc3ad70 100644 --- a/mixnode/src/node/presence.rs +++ b/mixnode/src/node/presence.rs @@ -3,13 +3,12 @@ use directory_client::presence::mixnodes::MixNodePresence; use directory_client::requests::presence_mixnodes_post::PresenceMixNodesPoster; use directory_client::DirectoryClient; use log::{debug, error}; -use std::time; use std::time::Duration; pub struct Notifier { pub net_client: directory_client::Client, presence: MixNodePresence, - sending_delay: time::Duration, + sending_delay: Duration, } pub struct NotifierConfig { @@ -17,7 +16,7 @@ pub struct NotifierConfig { announce_host: String, pub_key_string: String, layer: u64, - sending_delay: time::Duration, + sending_delay: Duration, } impl NotifierConfig { @@ -26,7 +25,7 @@ impl NotifierConfig { announce_host: String, pub_key_string: String, layer: u64, - sending_delay: time::Duration, + sending_delay: Duration, ) -> Self { NotifierConfig { directory_server, @@ -68,7 +67,7 @@ impl Notifier { pub async fn run(self) { loop { self.notify(); - tokio::time::delay_for(self.sending_delay).await; + tokio::delay_for(self.sending_delay).await; } } } From 441e586915bdb979d900c5cae7f0b4d81ee5fb4f Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 15:18:52 +0000 Subject: [PATCH 138/273] Accidentally removed import path --- mixnode/src/node/presence.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs index bdcdc3ad70..56feae14ee 100644 --- a/mixnode/src/node/presence.rs +++ b/mixnode/src/node/presence.rs @@ -67,7 +67,7 @@ impl Notifier { pub async fn run(self) { loop { self.notify(); - tokio::delay_for(self.sending_delay).await; + tokio::time::delay_for(self.sending_delay).await; } } } From bc8e5fd6f6471678e9fcfe57fc1663b5949dc9d6 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 15:24:12 +0000 Subject: [PATCH 139/273] Adjusted mixnode config template to new changes --- mixnode/src/config/template.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mixnode/src/config/template.rs b/mixnode/src/config/template.rs index 6ce5c38b2d..b150865248 100644 --- a/mixnode/src/config/template.rs +++ b/mixnode/src/config/template.rs @@ -19,9 +19,6 @@ layer = {{ mixnode.layer }} # Socket address to which this mixnode will bind to and will be listening for packets. listening_address = "{{ mixnode.listening_address }}" -# URL to the directory server. -directory_server = "{{ mixnode.directory_server }}" - # Path to file containing private identity key. private_sphinx_key_file = "{{ mixnode.private_sphinx_key_file }}" @@ -57,5 +54,17 @@ nym_root_directory = "{{ mixnode.nym_root_directory }}" [debug] +# Directory server to which the server will be reporting their presence data. +presence_directory_server = "{{ debug.presence_directory_server}}" + +# Delay between each subsequent presence data being sent. +presence_sending_delay = {{ debug.presence_sending_delay }} + +# Directory server to which the server will be reporting their metrics data. +metrics_directory_server = "{{ debug.metrics_directory_server }}" + +# Delay between each subsequent metrics data being sent. +metrics_sending_delay = {{ debug.metrics_sending_delay }} + "# } From 0d5fe1a788c9710e151ce75421a76cb64d5adf7b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 4 Feb 2020 15:55:36 +0000 Subject: [PATCH 140/273] Starting provider via commands::run::execute --- sfw-provider/src/built_info.rs | 2 + sfw-provider/src/commands/mod.rs | 5 + sfw-provider/src/commands/run.rs | 151 +++++++++++++++++++++++++++ sfw-provider/src/main.rs | 169 ++----------------------------- 4 files changed, 169 insertions(+), 158 deletions(-) create mode 100644 sfw-provider/src/built_info.rs create mode 100644 sfw-provider/src/commands/mod.rs create mode 100644 sfw-provider/src/commands/run.rs diff --git a/sfw-provider/src/built_info.rs b/sfw-provider/src/built_info.rs new file mode 100644 index 0000000000..d11fb6389f --- /dev/null +++ b/sfw-provider/src/built_info.rs @@ -0,0 +1,2 @@ +// The file has been placed there by the build script. +include!(concat!(env!("OUT_DIR"), "/built.rs")); diff --git a/sfw-provider/src/commands/mod.rs b/sfw-provider/src/commands/mod.rs new file mode 100644 index 0000000000..ed633f4b71 --- /dev/null +++ b/sfw-provider/src/commands/mod.rs @@ -0,0 +1,5 @@ +use crate::config::Config; +use clap::ArgMatches; + +pub mod init; +pub mod run; diff --git a/sfw-provider/src/commands/run.rs b/sfw-provider/src/commands/run.rs new file mode 100644 index 0000000000..41a203842e --- /dev/null +++ b/sfw-provider/src/commands/run.rs @@ -0,0 +1,151 @@ +use crate::provider; +use crate::provider::ServiceProvider; +use clap::{App, Arg, ArgMatches}; +use std::net::ToSocketAddrs; +use std::path::PathBuf; + +pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { + App::new("run") + .about("Starts the service provider") + .arg( + Arg::with_name("mixHost") + .long("mixHost") + .help("The custom host on which the service provider will be running for receiving sphinx packets") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("mixPort") + .long("mixPort") + .help("The port on which the service provider will be listening for sphinx packets") + .takes_value(true) + ) + .arg( + Arg::with_name("clientHost") + .long("clientHost") + .help("The custom host on which the service provider will be running for receiving client sfw-provider-requests") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("clientPort") + .long("clientPort") + .help("The port on which the service provider will be listening for client sfw-provider-requests") + .takes_value(true) + ) + .arg( + Arg::with_name("storeDir") + .short("s") + .long("storeDir") + .help("Directory storing all packets for the clients") + .takes_value(true) + ) + .arg( + Arg::with_name("registeredLedger") + .short("r") + .long("registeredLedger") + .help("Directory of the ledger of registered clients") + .takes_value(true) + ) + .arg( + Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the node is sending presence and metrics to") + .takes_value(true), + ) +} + +fn show_binding_warning(address: String) { + println!("\n##### WARNING #####"); + println!( + "\nYou are trying to bind to {} - you might not be accessible to other nodes\n\ + You can ignore this warning if you're running setup on a local network \n\ + or have set a custom 'announce-host'", + address + ); + println!("\n##### WARNING #####\n"); +} + +fn special_addresses() -> Vec<&'static str> { + vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"] +} + +fn new_config(matches: &ArgMatches) -> provider::Config { + let directory_server = matches + .value_of("directory") + .unwrap_or("https://directory.nymtech.net") + .to_string(); + + let mix_host = matches.value_of("mixHost").unwrap(); + let mix_port = match matches.value_of("mixPort").unwrap_or("8085").parse::() { + Ok(n) => n, + Err(err) => panic!("Invalid mix host port value provided - {:?}", err), + }; + + let client_host = matches.value_of("clientHost").unwrap(); + let client_port = match matches + .value_of("clientPort") + .unwrap_or("9000") + .parse::() + { + Ok(n) => n, + Err(err) => panic!("Invalid client port value provided - {:?}", err), + }; + + if special_addresses().contains(&mix_host) { + show_binding_warning(mix_host.parse().unwrap()); + } + + if special_addresses().contains(&client_host) { + show_binding_warning(client_host.parse().unwrap()); + } + + let key_pair = crypto::identity::MixIdentityKeyPair::new(); // TODO: persist this so keypairs don't change every restart + let store_dir = PathBuf::from( + matches + .value_of("storeDir") + .unwrap_or("/tmp/nym-provider/inboxes"), + ); + let registered_client_ledger_dir = PathBuf::from( + matches + .value_of("registeredLedger") + .unwrap_or("/tmp/nym-provider/registered_clients"), + ); + + println!("store_dir is: {:?}", store_dir); + println!( + "registered_client_ledger_dir is: {:?}", + registered_client_ledger_dir + ); + + let mix_socket_address = (mix_host, mix_port) + .to_socket_addrs() + .expect("Failed to combine host and port") + .next() + .expect("Failed to extract the socket address from the iterator"); + + let client_socket_address = (client_host, client_port) + .to_socket_addrs() + .expect("Failed to combine host and port") + .next() + .expect("Failed to extract the socket address from the iterator"); + + println!("Listening for mixnet packets on {}", mix_socket_address); + println!("Listening for client requests on {}", client_socket_address); + + provider::Config { + mix_socket_address, + directory_server, + public_key: key_pair.public_key, + client_socket_address, + secret_key: key_pair.private_key, + store_dir: PathBuf::from(store_dir), + } +} + +pub fn execute(matches: &ArgMatches) { + let config = new_config(matches); + let provider = ServiceProvider::new(config); + + provider.start().unwrap() +} diff --git a/sfw-provider/src/main.rs b/sfw-provider/src/main.rs index 408ec221b6..1c774fb007 100644 --- a/sfw-provider/src/main.rs +++ b/sfw-provider/src/main.rs @@ -1,177 +1,30 @@ -use crate::provider::ServiceProvider; -use clap::{App, Arg, ArgMatches, SubCommand}; -use log::error; -use std::net::ToSocketAddrs; -use std::path::PathBuf; -use std::process; +use clap::{App, ArgMatches, SubCommand}; +pub mod built_info; +mod commands; +mod config; pub mod provider; fn main() { dotenv::dotenv().ok(); pretty_env_logger::init(); + println!("{}", banner()); + let arg_matches = App::new("Nym Service Provider") .version(built_info::PKG_VERSION) .author("Nymtech") .about("Implementation of the Loopix-based Service Provider") - .subcommand( - SubCommand::with_name("run") - .about("Starts the service provider") - .arg( - Arg::with_name("mixHost") - .long("mixHost") - .help("The custom host on which the service provider will be running for receiving sphinx packets") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("mixPort") - .long("mixPort") - .help("The port on which the service provider will be listening for sphinx packets") - .takes_value(true) - ) - .arg( - Arg::with_name("clientHost") - .long("clientHost") - .help("The custom host on which the service provider will be running for receiving client sfw-provider-requests") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("clientPort") - .long("clientPort") - .help("The port on which the service provider will be listening for client sfw-provider-requests") - .takes_value(true) - ) - .arg( - Arg::with_name("storeDir") - .short("s") - .long("storeDir") - .help("Directory storing all packets for the clients") - .takes_value(true) - ) - .arg( - Arg::with_name("registeredLedger") - .short("r") - .long("registeredLedger") - .help("Directory of the ledger of registered clients") - .takes_value(true) - ) - .arg( - Arg::with_name("directory") - .long("directory") - .help("Address of the directory server the node is sending presence and metrics to") - .takes_value(true), - ), - ) + .subcommand(commands::run::command_args()) .get_matches(); - if let Err(e) = execute(arg_matches) { - error!("{}", e); - process::exit(1); - } + execute(arg_matches); } -fn print_binding_warning(address: &str) { - println!("\n##### WARNING #####"); - println!( - "\nYou are trying to bind to {} - you might not be accessible to other nodes", - address - ); - println!("\n##### WARNING #####\n"); -} - -fn run(matches: &ArgMatches) { - println!("{}", banner()); - let config = new_config(matches); - let provider = ServiceProvider::new(config); - - provider.start().unwrap() -} - -fn new_config(matches: &ArgMatches) -> provider::Config { - let directory_server = matches - .value_of("directory") - .unwrap_or("https://directory.nymtech.net") - .to_string(); - - let mix_host = matches.value_of("mixHost").unwrap(); - let mix_port = match matches.value_of("mixPort").unwrap_or("8085").parse::() { - Ok(n) => n, - Err(err) => panic!("Invalid mix host port value provided - {:?}", err), - }; - - let client_host = matches.value_of("clientHost").unwrap(); - let client_port = match matches - .value_of("clientPort") - .unwrap_or("9000") - .parse::() - { - Ok(n) => n, - Err(err) => panic!("Invalid client port value provided - {:?}", err), - }; - - if mix_host == "localhost" || mix_host == "127.0.0.1" || mix_host == "0.0.0.0" { - print_binding_warning(mix_host); - } - - if client_host == "localhost" || client_host == "127.0.0.1" || client_host == "0.0.0.0" { - print_binding_warning(client_host); - } - - let key_pair = crypto::identity::MixIdentityKeyPair::new(); // TODO: persist this so keypairs don't change every restart - let store_dir = PathBuf::from( - matches - .value_of("storeDir") - .unwrap_or("/tmp/nym-provider/inboxes"), - ); - let registered_client_ledger_dir = PathBuf::from( - matches - .value_of("registeredLedger") - .unwrap_or("/tmp/nym-provider/registered_clients"), - ); - - println!("store_dir is: {:?}", store_dir); - println!( - "registered_client_ledger_dir is: {:?}", - registered_client_ledger_dir - ); - - let mix_socket_address = (mix_host, mix_port) - .to_socket_addrs() - .expect("Failed to combine host and port") - .next() - .expect("Failed to extract the socket address from the iterator"); - - let client_socket_address = (client_host, client_port) - .to_socket_addrs() - .expect("Failed to combine host and port") - .next() - .expect("Failed to extract the socket address from the iterator"); - - println!("Listening for mixnet packets on {}", mix_socket_address); - println!("Listening for client requests on {}", client_socket_address); - - provider::Config { - mix_socket_address, - directory_server, - public_key: key_pair.public_key, - client_socket_address, - secret_key: key_pair.private_key, - store_dir: PathBuf::from(store_dir), - } -} - -pub mod built_info { - // The file has been placed there by the build script. - include!(concat!(env!("OUT_DIR"), "/built.rs")); -} - -fn execute(matches: ArgMatches) -> Result<(), String> { +fn execute(matches: ArgMatches) { match matches.subcommand() { - ("run", Some(m)) => Ok(run(m)), - _ => Err(usage()), + ("run", Some(m)) => commands::run::execute(m), + _ => println!("{}", usage()), } } From 61b9fac25b4c447fa183fee0ae5b8ff91ac2f907 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 10:39:27 +0000 Subject: [PATCH 141/273] Initial definition of new sfw provider config --- sfw-provider/src/config/mod.rs | 458 +++++++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) create mode 100644 sfw-provider/src/config/mod.rs diff --git a/sfw-provider/src/config/mod.rs b/sfw-provider/src/config/mod.rs new file mode 100644 index 0000000000..94e5c1c350 --- /dev/null +++ b/sfw-provider/src/config/mod.rs @@ -0,0 +1,458 @@ +use crate::config::template::config_template; +use config::NymConfig; +use log::*; +use serde::{Deserialize, Serialize}; +use std::net::{IpAddr, SocketAddr}; +use std::path::PathBuf; +use std::str::FromStr; +use std::time; + +pub mod persistance; +mod template; + +// 'PROVIDER' +const DEFAULT_MIX_LISTENING_PORT: u16 = 1789; +const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000; +// 'DEBUG' +// where applicable, the below are defined in milliseconds +const DEFAULT_PRESENCE_SENDING_DELAY: u64 = 3000; + +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Config { + provider: Provider, + + mixnet_endpoint: MixnetEndpoint, + + clients_endpoint: ClientsEndpoint, + + #[serde(default)] + logging: Logging, + #[serde(default)] + debug: Debug, +} + +impl NymConfig for Config { + fn template() -> &'static str { + config_template() + } + + fn config_file_name() -> String { + "config.toml".to_string() + } + + fn default_root_directory() -> PathBuf { + dirs::home_dir() + .expect("Failed to evaluate $HOME value") + .join(".nym") + .join("sfw-providers") + } + + fn root_directory(&self) -> PathBuf { + self.provider.nym_root_directory.clone() + } + + fn config_directory(&self) -> PathBuf { + self.provider + .nym_root_directory + .join(&self.provider.id) + .join("config") + } + + fn data_directory(&self) -> PathBuf { + self.provider + .nym_root_directory + .join(&self.provider.id) + .join("data") + } +} + +impl Config { + pub fn new>(id: S) -> Self { + Config::default().with_id(id) + } + + // builder methods + pub fn with_id>(mut self, id: S) -> Self { + let id = id.into(); + if self.provider.private_sphinx_key_file.as_os_str().is_empty() { + self.provider.private_sphinx_key_file = + self::Provider::default_private_sphinx_key_file(&id); + } + if self.provider.public_sphinx_key_file.as_os_str().is_empty() { + self.provider.public_sphinx_key_file = + self::Provider::default_public_sphinx_key_file(&id); + } + if self + .clients_endpoint + .inboxes_directory + .as_os_str() + .is_empty() + { + self.clients_endpoint.inboxes_directory = + self::ClientsEndpoint::default_inboxes_directory(&id); + } + if self.clients_endpoint.ledger_path.as_os_str().is_empty() { + self.clients_endpoint.ledger_path = self::ClientsEndpoint::default_ledger_path(&id); + } + + self.provider.id = id; + self + } + + pub fn with_custom_directory>(mut self, directory_server: S) -> Self { + self.debug.presence_directory_server = directory_server.into(); + self + } + + pub fn with_mix_listening_host>(mut self, host: S) -> Self { + // see if the provided `host` is just an ip address or ip:port + let host = host.into(); + + // is it ip:port? + match SocketAddr::from_str(host.as_ref()) { + Ok(socket_addr) => { + self.mixnet_endpoint.listening_address = socket_addr; + self + } + // try just for ip + Err(_) => match IpAddr::from_str(host.as_ref()) { + Ok(ip_addr) => { + self.mixnet_endpoint.listening_address.set_ip(ip_addr); + self + } + Err(_) => { + error!( + "failed to make any changes to config - invalid host {}", + host + ); + self + } + }, + } + } + + pub fn with_mix_listening_port(mut self, port: u16) -> Self { + self.mixnet_endpoint.listening_address.set_port(port); + self + } + + pub fn with_mix_announce_host>(mut self, host: S) -> Self { + // this is slightly more complicated as we store announce information as String, + // since it might not necessarily be a valid SocketAddr (say `nymtech.net:8080` is a valid + // announce address, yet invalid SocketAddr` + + // first lets see if we received host:port or just host part of an address + let host = host.into(); + let split_host: Vec<_> = host.split(':').collect(); + match split_host.len() { + 1 => { + // we provided only 'host' part so we are going to reuse existing port + self.mixnet_endpoint.announce_address = + format!("{}:{}", host, self.mixnet_endpoint.listening_address.port()); + self + } + 2 => { + // we provided 'host:port' so just put the whole thing there + self.mixnet_endpoint.announce_address = host; + self + } + _ => { + // we provided something completely invalid, so don't try to parse it + error!( + "failed to make any changes to config - invalid announce host {}", + host + ); + self + } + } + } + + pub fn with_mix_announce_port(mut self, port: u16) -> Self { + let current_host: Vec<_> = self.mixnet_endpoint.announce_address.split(':').collect(); + debug_assert_eq!(current_host.len(), 2); + self.mixnet_endpoint.announce_address = format!("{}:{}", current_host[0], port); + self + } + + pub fn with_clients_listening_host>(mut self, host: S) -> Self { + // see if the provided `host` is just an ip address or ip:port + let host = host.into(); + + // is it ip:port? + match SocketAddr::from_str(host.as_ref()) { + Ok(socket_addr) => { + self.clients_endpoint.listening_address = socket_addr; + self + } + // try just for ip + Err(_) => match IpAddr::from_str(host.as_ref()) { + Ok(ip_addr) => { + self.clients_endpoint.listening_address.set_ip(ip_addr); + self + } + Err(_) => { + error!( + "failed to make any changes to config - invalid host {}", + host + ); + self + } + }, + } + } + + pub fn with_clients_listening_port(mut self, port: u16) -> Self { + self.clients_endpoint.listening_address.set_port(port); + self + } + + pub fn with_clients_announce_host>(mut self, host: S) -> Self { + // this is slightly more complicated as we store announce information as String, + // since it might not necessarily be a valid SocketAddr (say `nymtech.net:8080` is a valid + // announce address, yet invalid SocketAddr` + + // first lets see if we received host:port or just host part of an address + let host = host.into(); + let split_host: Vec<_> = host.split(':').collect(); + match split_host.len() { + 1 => { + // we provided only 'host' part so we are going to reuse existing port + self.clients_endpoint.announce_address = format!( + "{}:{}", + host, + self.clients_endpoint.listening_address.port() + ); + self + } + 2 => { + // we provided 'host:port' so just put the whole thing there + self.clients_endpoint.announce_address = host; + self + } + _ => { + // we provided something completely invalid, so don't try to parse it + error!( + "failed to make any changes to config - invalid announce host {}", + host + ); + self + } + } + } + + pub fn with_clients_announce_port(mut self, port: u16) -> Self { + let current_host: Vec<_> = self.clients_endpoint.announce_address.split(':').collect(); + debug_assert_eq!(current_host.len(), 2); + self.clients_endpoint.announce_address = format!("{}:{}", current_host[0], port); + self + } + + // getters + pub fn get_config_file_save_location(&self) -> PathBuf { + self.config_directory().join(Self::config_file_name()) + } + + pub fn get_private_sphinx_key_file(&self) -> PathBuf { + self.provider.private_sphinx_key_file.clone() + } + + pub fn get_public_sphinx_key_file(&self) -> PathBuf { + self.provider.public_sphinx_key_file.clone() + } + + pub fn get_presence_directory_server(&self) -> String { + self.debug.presence_directory_server.clone() + } + + pub fn get_presence_sending_delay(&self) -> time::Duration { + time::Duration::from_millis(self.debug.presence_sending_delay) + } + + pub fn get_mix_listening_address(&self) -> SocketAddr { + self.mixnet_endpoint.listening_address + } + + pub fn get_mix_announce_address(&self) -> String { + self.mixnet_endpoint.announce_address.clone() + } + + pub fn get_clients_listening_address(&self) -> SocketAddr { + self.clients_endpoint.listening_address + } + + pub fn get_clients_announce_address(&self) -> String { + self.clients_endpoint.announce_address.clone() + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Provider { + /// ID specifies the human readable ID of this particular mixnode. + id: String, + + /// Path to file containing private sphinx key. + private_sphinx_key_file: PathBuf, + + /// Path to file containing public sphinx key. + public_sphinx_key_file: PathBuf, + + /// nym_home_directory specifies absolute path to the home nym service providers directory. + /// It is expected to use default value and hence .toml file should not redefine this field. + nym_root_directory: PathBuf, +} + +impl Provider { + fn default_private_sphinx_key_file(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("private_sphinx.pem") + } + + fn default_public_sphinx_key_file(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("public_sphinx.pem") + } +} + +impl Default for Provider { + fn default() -> Self { + Provider { + id: "".to_string(), + private_sphinx_key_file: Default::default(), + public_sphinx_key_file: Default::default(), + nym_root_directory: Config::default_root_directory(), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct MixnetEndpoint { + /// Socket address to which this service provider will bind to + /// and will be listening for sphinx packets coming from the mixnet. + listening_address: SocketAddr, + + /// Optional address announced to the directory server for the clients to connect to. + /// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address + /// later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. + /// Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` + /// are valid announce addresses, while the later will default to whatever port is used for + /// `listening_address`. + announce_address: String, +} + +impl Default for MixnetEndpoint { + fn default() -> Self { + MixnetEndpoint { + listening_address: format!("0.0.0.0:{}", DEFAULT_MIX_LISTENING_PORT) + .parse() + .unwrap(), + announce_address: format!("127.0.0.1:{}", DEFAULT_MIX_LISTENING_PORT), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct ClientsEndpoint { + /// Socket address to which this service provider will bind to + /// and will be listening for data packets coming from the clients. + listening_address: SocketAddr, + + /// Optional address announced to the directory server for the clients to connect to. + /// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address + /// later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. + /// Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` + /// are valid announce addresses, while the later will default to whatever port is used for + /// `listening_address`. + announce_address: String, + + /// Path to the directory with clients inboxes containing messages stored for them. + inboxes_directory: PathBuf, + + /// [TODO: implement its storage] Full path to a aile containing mapping of + /// client addresses to their access tokens. + ledger_path: PathBuf, +} + +impl ClientsEndpoint { + fn default_inboxes_directory(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("inboxes") + } + + fn default_ledger_path(id: &str) -> PathBuf { + Config::default_data_directory(Some(id)).join("client_ledger.dat") + } +} + +impl Default for ClientsEndpoint { + fn default() -> Self { + ClientsEndpoint { + listening_address: format!("0.0.0.0:{}", DEFAULT_CLIENT_LISTENING_PORT) + .parse() + .unwrap(), + announce_address: format!("127.0.0.1:{}", DEFAULT_CLIENT_LISTENING_PORT), + inboxes_directory: Default::default(), + ledger_path: Default::default(), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Logging {} + +impl Default for Logging { + fn default() -> Self { + Logging {} + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Debug { + /// Directory server to which the server will be reporting their presence data. + presence_directory_server: String, + + /// Delay between each subsequent presence data being sent. + presence_sending_delay: u64, +} + +impl Debug { + fn default_directory_server() -> String { + #[cfg(feature = "qa")] + return "https://qa-directory.nymtech.net".to_string(); + #[cfg(feature = "local")] + return "http://localhost:8080".to_string(); + + "https://directory.nymtech.net".to_string() + } +} + +impl Default for Debug { + fn default() -> Self { + Debug { + presence_directory_server: Self::default_directory_server(), + presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY, + } + } +} + +#[cfg(test)] +mod mixnode_config { + use super::*; + + #[test] + fn after_saving_default_config_the_loaded_one_is_identical() { + // need to figure out how to do something similar but without touching the disk + // or the file system at all... + let temp_location = tempfile::tempdir().unwrap().path().join("config.toml"); + let default_config = Config::default().with_id("foomp".to_string()); + default_config + .save_to_file(Some(temp_location.clone())) + .unwrap(); + + let loaded_config = Config::load_from_file(Some(temp_location), None).unwrap(); + + assert_eq!(default_config, loaded_config); + } +} From e8b9d641ba50f86e72aa1153562c8aa223c4311c Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:02:02 +0000 Subject: [PATCH 142/273] Init command for the provider --- sfw-provider/src/commands/init.rs | 115 ++++++++++++++++++++++++++++++ sfw-provider/src/commands/mod.rs | 4 ++ sfw-provider/src/main.rs | 2 + 3 files changed, 121 insertions(+) create mode 100644 sfw-provider/src/commands/init.rs diff --git a/sfw-provider/src/commands/init.rs b/sfw-provider/src/commands/init.rs new file mode 100644 index 0000000000..05007535ef --- /dev/null +++ b/sfw-provider/src/commands/init.rs @@ -0,0 +1,115 @@ +use crate::commands::override_config; +use crate::config::persistance::pathfinder::ProviderPathfinder; +use clap::{App, Arg, ArgMatches}; +use config::NymConfig; +use crypto::encryption; +use crypto::identity::MixIdentityKeyPair; +use pemstore::pemstore::PemStore; + +pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { + App::new("init") + .about("Initialise the store and forward service provider") + .arg( + Arg::with_name("id") + .long("id") + .help("Id of the sfw-provider we want to create config for.") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("mix-host") + .long("mix-host") + .help("The custom host on which the service provider will be running for receiving sphinx packets") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("mix-port") + .long("mix-port") + .help("The port on which the service provider will be listening for sphinx packets") + .takes_value(true) + ) + .arg( + Arg::with_name("client-host") + .long("client-host") + .help("The custom host on which the service provider will be running for receiving client sfw-provider-requests") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("client-port") + .long("client-port") + .help("The port on which the service provider will be listening for client sfw-provider-requests") + .takes_value(true) + ) + .arg( + Arg::with_name("announce-mix-host") + .long("announce-mix-host") + .help("The host that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("announce-mix-port") + .long("announce-mix-port") + .help("The port that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("announce-clients-host") + .long("announce-clients-host") + .help("The host that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("announce-clients-port") + .long("announce-clients-port") + .help("The port that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("inboxes") + .long("inboxes") + .help("Directory with inboxes where all packets for the clients are stored") + .takes_value(true) + ) + .arg( + Arg::with_name("clients-ledger") + .long("clients-ledger") + .help("[UNIMPLEMENTED] Ledger file containing registered clients") + .takes_value(true) + ) + .arg( + Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the node is sending presence data to") + .takes_value(true), + ) +} + +pub fn execute(matches: &ArgMatches) { + let id = matches.value_of("id").unwrap(); + println!("Initialising sfw service provider {}...", id); + + let mut config = crate::config::Config::new(id); + + config = override_config(config, matches); + + // TODO: which one should be used? + let sphinx_keys = encryption::KeyPair::new(); + // let alternative_keypair = MixIdentityKeyPair::new(); + + let pathfinder = ProviderPathfinder::new_from_config(&config); + let pem_store = PemStore::new(pathfinder); + pem_store + .write_encryption_keys(sphinx_keys) + .expect("Failed to save sphinx keys"); + println!("Saved mixnet sphinx keypair"); + + let config_save_location = config.get_config_file_save_location(); + config + .save_to_file(None) + .expect("Failed to save the config file"); + println!("Saved configuration file to {:?}", config_save_location); + + println!("Service provider configuration completed.\n\n\n") +} diff --git a/sfw-provider/src/commands/mod.rs b/sfw-provider/src/commands/mod.rs index ed633f4b71..d21d4a92bc 100644 --- a/sfw-provider/src/commands/mod.rs +++ b/sfw-provider/src/commands/mod.rs @@ -3,3 +3,7 @@ use clap::ArgMatches; pub mod init; pub mod run; + +pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { + config +} diff --git a/sfw-provider/src/main.rs b/sfw-provider/src/main.rs index 1c774fb007..c563f141eb 100644 --- a/sfw-provider/src/main.rs +++ b/sfw-provider/src/main.rs @@ -15,6 +15,7 @@ fn main() { .version(built_info::PKG_VERSION) .author("Nymtech") .about("Implementation of the Loopix-based Service Provider") + .subcommand(commands::init::command_args()) .subcommand(commands::run::command_args()) .get_matches(); @@ -23,6 +24,7 @@ fn main() { fn execute(matches: ArgMatches) { match matches.subcommand() { + ("init", Some(m)) => commands::init::execute(m), ("run", Some(m)) => commands::run::execute(m), _ => println!("{}", usage()), } From 273a61d7def029c8c46734b57d4cfaffd4153514 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:12:22 +0000 Subject: [PATCH 143/273] client => clients --- sfw-provider/src/commands/init.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sfw-provider/src/commands/init.rs b/sfw-provider/src/commands/init.rs index 05007535ef..9ed0508c93 100644 --- a/sfw-provider/src/commands/init.rs +++ b/sfw-provider/src/commands/init.rs @@ -30,16 +30,16 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .takes_value(true) ) .arg( - Arg::with_name("client-host") - .long("client-host") - .help("The custom host on which the service provider will be running for receiving client sfw-provider-requests") + Arg::with_name("clients-host") + .long("clients-host") + .help("The custom host on which the service provider will be running for receiving clients sfw-provider-requests") .takes_value(true) .required(true), ) .arg( - Arg::with_name("client-port") - .long("client-port") - .help("The port on which the service provider will be listening for client sfw-provider-requests") + Arg::with_name("clients-port") + .long("clients-port") + .help("The port on which the service provider will be listening for clients sfw-provider-requests") .takes_value(true) ) .arg( From d6824ccad220ad7f88262494fcd0ed8f651165dc Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:15:14 +0000 Subject: [PATCH 144/273] announce-X-host/port => X-announce-host/port --- sfw-provider/src/commands/init.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sfw-provider/src/commands/init.rs b/sfw-provider/src/commands/init.rs index 9ed0508c93..6973a858e7 100644 --- a/sfw-provider/src/commands/init.rs +++ b/sfw-provider/src/commands/init.rs @@ -43,26 +43,26 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .takes_value(true) ) .arg( - Arg::with_name("announce-mix-host") - .long("announce-mix-host") + Arg::with_name("mix-announce-host") + .long("mix-announce-host") .help("The host that will be reported to the directory server") .takes_value(true), ) .arg( - Arg::with_name("announce-mix-port") - .long("announce-mix-port") + Arg::with_name("mix-announce-port") + .long("mix-announce-port") .help("The port that will be reported to the directory server") .takes_value(true), ) .arg( - Arg::with_name("announce-clients-host") - .long("announce-clients-host") + Arg::with_name("clients-announce-host") + .long("clients-announce-host") .help("The host that will be reported to the directory server") .takes_value(true), ) .arg( - Arg::with_name("announce-clients-port") - .long("announce-clients-port") + Arg::with_name("clients-announce-port") + .long("clients-announce-port") .help("The port that will be reported to the directory server") .takes_value(true), ) From 133d36b559190a6810dceef6c17ebb826aa1fcd0 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:23:54 +0000 Subject: [PATCH 145/273] override config function --- sfw-provider/src/commands/mod.rs | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/sfw-provider/src/commands/mod.rs b/sfw-provider/src/commands/mod.rs index d21d4a92bc..95651ffe13 100644 --- a/sfw-provider/src/commands/mod.rs +++ b/sfw-provider/src/commands/mod.rs @@ -5,5 +5,74 @@ pub mod init; pub mod run; pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { + if let Some(mix_host) = matches.value_of("mix-host") { + config = config.with_mix_listening_host(mix_host); + } + + if let Some(mix_port) = matches.value_of("mix-port").map(|port| port.parse::()) { + if let Err(err) = mix_port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_mix_listening_port(mix_port.unwrap()); + } + + if let Some(clients_host) = matches.value_of("clients-host") { + config = config.with_clients_listening_host(clients_host); + } + + if let Some(clients_port) = matches + .value_of("clients-port") + .map(|port| port.parse::()) + { + if let Err(err) = clients_port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_clients_listening_port(clients_port.unwrap()); + } + + if let Some(mix_announce_host) = matches.value_of("mix-announce-host") { + config = config.with_mix_announce_host(mix_announce_host); + } + + if let Some(mix_announce_port) = matches + .value_of("mix-announce-port") + .map(|port| port.parse::()) + { + if let Err(err) = mix_announce_port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_mix_announce_port(mix_announce_port.unwrap()); + } + + if let Some(clients_announce_host) = matches.value_of("clients-announce-host") { + config = config.with_clients_announce_host(clients_announce_host); + } + + if let Some(clients_announce_port) = matches + .value_of("clients-announce-port") + .map(|port| port.parse::()) + { + if let Err(err) = clients_announce_port { + // if port was overridden, it must be parsable + panic!("Invalid port value provided - {:?}", err); + } + config = config.with_clients_announce_port(clients_announce_port.unwrap()); + } + + if let Some(directory) = matches.value_of("directory") { + config = config.with_custom_directory(directory); + } + + if let Some(inboxes_dir) = matches.value_of("inboxes") { + config = config.with_custom_clients_inboxes(inboxes_dir); + } + + if let Some(clients_ledger) = matches.value_of("clients-ledger") { + config = config.with_custom_clients_ledger(clients_ledger); + } + config } From 851ccfd736796c90808ebab88f535bfca4d8eeab Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:48:33 +0000 Subject: [PATCH 146/273] Ability to start provider with new 'run' method (but still using old config) --- sfw-provider/src/commands/run.rs | 223 ++++++++++++++++++------------- 1 file changed, 129 insertions(+), 94 deletions(-) diff --git a/sfw-provider/src/commands/run.rs b/sfw-provider/src/commands/run.rs index 41a203842e..f6e5b65d5a 100644 --- a/sfw-provider/src/commands/run.rs +++ b/sfw-provider/src/commands/run.rs @@ -1,6 +1,10 @@ +use crate::commands::override_config; +use crate::config::Config; use crate::provider; use crate::provider::ServiceProvider; use clap::{App, Arg, ArgMatches}; +use config::NymConfig; +use crypto::identity::{MixIdentityPrivateKey, MixIdentityPublicKey}; use std::net::ToSocketAddrs; use std::path::PathBuf; @@ -8,49 +12,85 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { App::new("run") .about("Starts the service provider") .arg( - Arg::with_name("mixHost") - .long("mixHost") + Arg::with_name("id") + .long("id") + .help("Id of the nym-mixnode we want to run") + .takes_value(true) + .required(true), + ) + // the rest of arguments are optional, they are used to override settings in config file + .arg( + Arg::with_name("config") + .long("config") + .help("Custom path to the nym-mixnet-client configuration file") + .takes_value(true), + ) + .arg( + Arg::with_name("mix-host") + .long("mix-host") .help("The custom host on which the service provider will be running for receiving sphinx packets") .takes_value(true) .required(true), ) .arg( - Arg::with_name("mixPort") - .long("mixPort") + Arg::with_name("mix-port") + .long("mix-port") .help("The port on which the service provider will be listening for sphinx packets") .takes_value(true) ) .arg( - Arg::with_name("clientHost") - .long("clientHost") - .help("The custom host on which the service provider will be running for receiving client sfw-provider-requests") + Arg::with_name("clients-host") + .long("clients-host") + .help("The custom host on which the service provider will be running for receiving clients sfw-provider-requests") .takes_value(true) .required(true), ) .arg( - Arg::with_name("clientPort") - .long("clientPort") - .help("The port on which the service provider will be listening for client sfw-provider-requests") + Arg::with_name("clients-port") + .long("clients-port") + .help("The port on which the service provider will be listening for clients sfw-provider-requests") .takes_value(true) ) .arg( - Arg::with_name("storeDir") - .short("s") - .long("storeDir") - .help("Directory storing all packets for the clients") + Arg::with_name("mix-announce-host") + .long("mix-announce-host") + .help("The host that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("mix-announce-port") + .long("mix-announce-port") + .help("The port that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("clients-announce-host") + .long("clients-announce-host") + .help("The host that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("clients-announce-port") + .long("clients-announce-port") + .help("The port that will be reported to the directory server") + .takes_value(true), + ) + .arg( + Arg::with_name("inboxes") + .long("inboxes") + .help("Directory with inboxes where all packets for the clients are stored") .takes_value(true) ) .arg( - Arg::with_name("registeredLedger") - .short("r") - .long("registeredLedger") - .help("Directory of the ledger of registered clients") + Arg::with_name("clients-ledger") + .long("clients-ledger") + .help("[UNIMPLEMENTED] Ledger file containing registered clients") .takes_value(true) ) .arg( Arg::with_name("directory") .long("directory") - .help("Address of the directory server the node is sending presence and metrics to") + .help("Address of the directory server the node is sending presence data to") .takes_value(true), ) } @@ -70,82 +110,77 @@ fn special_addresses() -> Vec<&'static str> { vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"] } -fn new_config(matches: &ArgMatches) -> provider::Config { - let directory_server = matches - .value_of("directory") - .unwrap_or("https://directory.nymtech.net") - .to_string(); - - let mix_host = matches.value_of("mixHost").unwrap(); - let mix_port = match matches.value_of("mixPort").unwrap_or("8085").parse::() { - Ok(n) => n, - Err(err) => panic!("Invalid mix host port value provided - {:?}", err), - }; - - let client_host = matches.value_of("clientHost").unwrap(); - let client_port = match matches - .value_of("clientPort") - .unwrap_or("9000") - .parse::() - { - Ok(n) => n, - Err(err) => panic!("Invalid client port value provided - {:?}", err), - }; - - if special_addresses().contains(&mix_host) { - show_binding_warning(mix_host.parse().unwrap()); - } - - if special_addresses().contains(&client_host) { - show_binding_warning(client_host.parse().unwrap()); - } - - let key_pair = crypto::identity::MixIdentityKeyPair::new(); // TODO: persist this so keypairs don't change every restart - let store_dir = PathBuf::from( - matches - .value_of("storeDir") - .unwrap_or("/tmp/nym-provider/inboxes"), - ); - let registered_client_ledger_dir = PathBuf::from( - matches - .value_of("registeredLedger") - .unwrap_or("/tmp/nym-provider/registered_clients"), - ); - - println!("store_dir is: {:?}", store_dir); - println!( - "registered_client_ledger_dir is: {:?}", - registered_client_ledger_dir - ); - - let mix_socket_address = (mix_host, mix_port) - .to_socket_addrs() - .expect("Failed to combine host and port") - .next() - .expect("Failed to extract the socket address from the iterator"); - - let client_socket_address = (client_host, client_port) - .to_socket_addrs() - .expect("Failed to combine host and port") - .next() - .expect("Failed to extract the socket address from the iterator"); - - println!("Listening for mixnet packets on {}", mix_socket_address); - println!("Listening for client requests on {}", client_socket_address); - - provider::Config { - mix_socket_address, - directory_server, - public_key: key_pair.public_key, - client_socket_address, - secret_key: key_pair.private_key, - store_dir: PathBuf::from(store_dir), - } -} - pub fn execute(matches: &ArgMatches) { - let config = new_config(matches); - let provider = ServiceProvider::new(config); + let id = matches.value_of("id").unwrap(); + + println!("Starting mixnode {}...", id); + + let mut config = + Config::load_from_file(matches.value_of("config").map(|path| path.into()), Some(id)) + .expect("Failed to load config file"); + + config = override_config(config, matches); + + let mix_listening_ip_string = config.get_mix_listening_address().ip().to_string(); + if special_addresses().contains(&mix_listening_ip_string.as_ref()) { + show_binding_warning(mix_listening_ip_string); + } + + let clients_listening_ip_string = config.get_clients_listening_address().ip().to_string(); + if special_addresses().contains(&clients_listening_ip_string.as_ref()) { + show_binding_warning(clients_listening_ip_string); + } + + println!( + "Directory server [presence]: {}", + config.get_presence_directory_server() + ); + + println!( + "Listening for incoming sphinx packets on {}", + config.get_mix_listening_address() + ); + println!( + "Announcing the following socket address for sphinx packets: {}", + config.get_mix_announce_address() + ); + + println!( + "Listening for incoming clients packets on {}", + config.get_clients_listening_address() + ); + println!( + "Announcing the following socket address for clients packets: {}", + config.get_clients_announce_address() + ); + + println!( + "Inboxes directory is: {:?}", + config.get_clients_inboxes_dir() + ); + println!( + "[UNIMPLEMENTED] Registered client ledger is: {:?}", + config.get_clients_ledger_path() + ); + + // key will be loaded directly provider in just a moment + let key_pair = ServiceProvider::load_sphinx_keys(&config); + + // stupid temporary hack + let private_bytes = key_pair.private_key().to_bytes(); + let public_bytes = key_pair.public_key().to_bytes(); + + let old_startup_config = provider::Config { + client_socket_address: config.get_clients_listening_address(), + directory_server: config.get_presence_directory_server(), + mix_socket_address: config.get_mix_listening_address(), + // identity keys are wrapper for encryption keys so this temporary hack will work to just make it compile once + public_key: MixIdentityPublicKey::from_bytes(public_bytes.as_ref()), + secret_key: MixIdentityPrivateKey::from_bytes(private_bytes.as_ref()), + store_dir: Default::default(), + }; + + let provider = ServiceProvider::new(old_startup_config); provider.start().unwrap() } From 881ab4f28e23fcd0a5a0bc237fc2ac4deab5b948 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:48:46 +0000 Subject: [PATCH 147/273] config changes required by ibid. --- sfw-provider/src/config/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sfw-provider/src/config/mod.rs b/sfw-provider/src/config/mod.rs index 94e5c1c350..6033f61fe8 100644 --- a/sfw-provider/src/config/mod.rs +++ b/sfw-provider/src/config/mod.rs @@ -248,6 +248,16 @@ impl Config { self } + pub fn with_custom_clients_inboxes>(mut self, inboxes_dir: S) -> Self { + self.clients_endpoint.inboxes_directory = PathBuf::from(inboxes_dir.into()); + self + } + + pub fn with_custom_clients_ledger>(mut self, ledger_path: S) -> Self { + self.clients_endpoint.ledger_path = PathBuf::from(ledger_path.into()); + self + } + // getters pub fn get_config_file_save_location(&self) -> PathBuf { self.config_directory().join(Self::config_file_name()) @@ -284,6 +294,14 @@ impl Config { pub fn get_clients_announce_address(&self) -> String { self.clients_endpoint.announce_address.clone() } + + pub fn get_clients_inboxes_dir(&self) -> PathBuf { + self.clients_endpoint.inboxes_directory.clone() + } + + pub fn get_clients_ledger_path(&self) -> PathBuf { + self.clients_endpoint.ledger_path.clone() + } } #[derive(Debug, Deserialize, PartialEq, Serialize)] From 30bf382d0a67900c0935153ce833d430ea972ad0 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:49:11 +0000 Subject: [PATCH 148/273] Better argument description --- mixnode/src/commands/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index c00033a61a..f72852c271 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -13,7 +13,7 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> { .arg( Arg::with_name("id") .long("id") - .help("Id of the nym-mixnode we want to create config for.") + .help("Id of the nym-mixnode we want to run") .takes_value(true) .required(true), ) From 4c9c7427955b8d6bb4f513e20ed0835d8eca13f2 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:49:27 +0000 Subject: [PATCH 149/273] Updated provider dependencies --- sfw-provider/Cargo.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sfw-provider/Cargo.toml b/sfw-provider/Cargo.toml index 2cc6afcd12..21ee83a326 100644 --- a/sfw-provider/Cargo.toml +++ b/sfw-provider/Cargo.toml @@ -11,6 +11,7 @@ edition = "2018" bs58 = "0.3.0" clap = "2.33.0" curve25519-dalek = "1.2.3" +dirs = "2.0.2" dotenv = "0.15.0" futures = "0.3.1" log = "0.4" @@ -24,7 +25,9 @@ hmac = "0.7.1" ## internal crypto = {path = "../common/crypto"} +config = {path = "../common/config"} directory-client = { path = "../common/clients/directory-client" } +pemstore = {path = "../common/pemstore"} sfw-provider-requests = { path = "./sfw-provider-requests" } ## will be moved to proper dependencies once released @@ -32,3 +35,10 @@ sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f8368 [build-dependencies] built = "0.3.2" + +[dev-dependencies] +tempfile = "3.1.0" + +[features] +qa = [] +local = [] \ No newline at end of file From 75df3a87b8cec549c2e0e1378aa474c84c70d9c2 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:49:40 +0000 Subject: [PATCH 150/273] Provider sphinx key loading --- sfw-provider/src/provider/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 9637f76668..a980c454ed 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -1,11 +1,14 @@ +use crate::config::persistance::pathfinder::ProviderPathfinder; use crate::provider::client_handling::{ClientProcessingData, ClientRequestProcessor}; use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData}; use crate::provider::storage::ClientStorage; +use crypto::encryption; use crypto::identity::{MixIdentityPrivateKey, MixIdentityPublicKey}; use directory_client::presence::providers::MixProviderClient; use futures::io::Error; use futures::lock::Mutex as FMutex; use log::*; +use pemstore::pemstore::PemStore; use sfw_provider_requests::AuthToken; use sphinx::route::DestinationAddressBytes; use std::collections::HashMap; @@ -276,6 +279,18 @@ impl ServiceProvider { } } + // TODO: make private and change config import path + pub fn load_sphinx_keys(config_file: &crate::config::Config) -> encryption::KeyPair { + let sphinx_keypair = PemStore::new(ProviderPathfinder::new_from_config(&config_file)) + .read_encryption() + .expect("Failed to read stored sphinx key files"); + println!( + "Public encryption key: {}\nFor time being, it is identical to identity keys", + sphinx_keypair.public_key().to_base58_string() + ); + sphinx_keypair + } + // Note: this now consumes the provider pub fn start(self) -> Result<(), Box> { // Create the runtime, probably later move it to Provider struct itself? From 7d9e2ef64a3a7d25190d6fd98a95491d57f19514 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 11:58:35 +0000 Subject: [PATCH 151/273] "mixnode" => "provider" --- sfw-provider/src/config/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sfw-provider/src/config/mod.rs b/sfw-provider/src/config/mod.rs index 6033f61fe8..8112d5af46 100644 --- a/sfw-provider/src/config/mod.rs +++ b/sfw-provider/src/config/mod.rs @@ -307,7 +307,7 @@ impl Config { #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Provider { - /// ID specifies the human readable ID of this particular mixnode. + /// ID specifies the human readable ID of this particular provider. id: String, /// Path to file containing private sphinx key. @@ -456,7 +456,7 @@ impl Default for Debug { } #[cfg(test)] -mod mixnode_config { +mod provider_config { use super::*; #[test] From 0084c74f97c676d9e5684b659ec046396a490b11 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:05:46 +0000 Subject: [PATCH 152/273] typo --- sfw-provider/src/config/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sfw-provider/src/config/mod.rs b/sfw-provider/src/config/mod.rs index 8112d5af46..53857a0181 100644 --- a/sfw-provider/src/config/mod.rs +++ b/sfw-provider/src/config/mod.rs @@ -387,7 +387,7 @@ pub struct ClientsEndpoint { /// Path to the directory with clients inboxes containing messages stored for them. inboxes_directory: PathBuf, - /// [TODO: implement its storage] Full path to a aile containing mapping of + /// [TODO: implement its storage] Full path to a file containing mapping of /// client addresses to their access tokens. ledger_path: PathBuf, } From 48c77bcb0d479b7fa668a44515555dd5c65e4496 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:08:01 +0000 Subject: [PATCH 153/273] Template for provider config --- sfw-provider/src/config/template.rs | 84 +++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 sfw-provider/src/config/template.rs diff --git a/sfw-provider/src/config/template.rs b/sfw-provider/src/config/template.rs new file mode 100644 index 0000000000..0ffa09c318 --- /dev/null +++ b/sfw-provider/src/config/template.rs @@ -0,0 +1,84 @@ +pub(crate) fn config_template() -> &'static str { + // While using normal toml marshalling would have been way simpler with less overhead, + // I think it's useful to have comments attached to the saved config file to explain behaviour of + // particular fields. + // Note: any changes to the template must be reflected in the appropriate structs in mod.rs. + r#" +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base mixnode config options ##### + +[provider] +# Human readable ID of this particular service provider. +id = "{{ provider.id }}" + +# Path to file containing private sphinx key. +private_sphinx_key_file = "{{ provider.private_sphinx_key_file }}" + +# Path to file containing public sphinx key. +public_sphinx_key_file = "{{ provider.public_sphinx_key_file }}" + +# nym_home_directory specifies absolute path to the home nym service providers directory. +# It is expected to use default value and hence .toml file should not redefine this field. +nym_root_directory = "{{ provider.nym_root_directory }}" + + +##### Mixnet endpoint config options ##### + +# Socket address to which this service provider will bind to +# and will be listening for sphinx packets coming from the mixnet. +listening_address = "{{ mixnet_endpoint.listening_address }}" + +# Optional address announced to the directory server for the clients to connect to. +# It is useful, say, in NAT scenarios or wanting to more easily update actual IP address +# later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. +# Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` +# are valid announce addresses, while the later will default to whatever port is used for +# `listening_address`. +announce_address = "{{ mixnet_endpoint.announce_address }}" + + +#### Clients endpoint config options ##### + +# Socket address to which this service provider will bind to +# and will be listening for sphinx packets coming from the mixnet. +listening_address = "{{ clients_endpoint.listening_address }}" + +# Optional address announced to the directory server for the clients to connect to. +# It is useful, say, in NAT scenarios or wanting to more easily update actual IP address +# later on by using name resolvable with a DNS query, such as `nymtech.net:8080`. +# Additionally a custom port can be provided, so both `nymtech.net:8080` and `nymtech.net` +# are valid announce addresses, while the later will default to whatever port is used for +# `listening_address`. +announce_address = "{{ clients_endpoint.announce_address }}" + +# Path to the directory with clients inboxes containing messages stored for them. +inboxes_directory = "{{ clients_endpoint.inboxes_directory }}" + +# [TODO: implement its storage] Full path to a file containing mapping of +# client addresses to their access tokens. +ledger_path = {{ clients_endpoint.ledger_path }}" + + +##### logging configuration options ##### + +[logging] + +# TODO + + +##### debug configuration options ##### +# The following options should not be modified unless you know EXACTLY what you are doing +# as if set incorrectly, they may impact your anonymity. + +[debug] + +# Directory server to which the server will be reporting their presence data. +presence_directory_server = "{{ debug.presence_directory_server}}" + +# Delay between each subsequent presence data being sent. +presence_sending_delay = {{ debug.presence_sending_delay }} + +"# +} From 5f393de34bf424b303dc33167904efafd4c0c453 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:08:21 +0000 Subject: [PATCH 154/273] Provider pathfinder (identical to mixnode's) --- sfw-provider/src/config/persistance/mod.rs | 1 + .../src/config/persistance/pathfinder.rs | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 sfw-provider/src/config/persistance/mod.rs create mode 100644 sfw-provider/src/config/persistance/pathfinder.rs diff --git a/sfw-provider/src/config/persistance/mod.rs b/sfw-provider/src/config/persistance/mod.rs new file mode 100644 index 0000000000..5692e9bc8b --- /dev/null +++ b/sfw-provider/src/config/persistance/mod.rs @@ -0,0 +1 @@ +pub mod pathfinder; diff --git a/sfw-provider/src/config/persistance/pathfinder.rs b/sfw-provider/src/config/persistance/pathfinder.rs new file mode 100644 index 0000000000..fd74b03c04 --- /dev/null +++ b/sfw-provider/src/config/persistance/pathfinder.rs @@ -0,0 +1,56 @@ +use crate::config::Config; +use pemstore::pathfinder::PathFinder; +use std::path::PathBuf; + +#[derive(Debug)] +pub struct ProviderPathfinder { + pub config_dir: PathBuf, + pub private_sphinx_key: PathBuf, + pub public_sphinx_key: PathBuf, +} + +impl ProviderPathfinder { + pub fn new(id: String) -> Self { + let os_config_dir = dirs::config_dir().unwrap(); // grabs the OS default config dir + let config_dir = os_config_dir.join("nym").join("mixnodes").join(id); + let private_sphinx_key = config_dir.join("private.pem"); + let public_sphinx_key = config_dir.join("public.pem"); + ProviderPathfinder { + config_dir, + private_sphinx_key, + public_sphinx_key, + } + } + + pub fn new_from_config(config: &Config) -> Self { + ProviderPathfinder { + config_dir: config.get_config_file_save_location(), + private_sphinx_key: config.get_private_sphinx_key_file(), + public_sphinx_key: config.get_public_sphinx_key_file(), + } + } +} + +impl PathFinder for ProviderPathfinder { + fn config_dir(&self) -> PathBuf { + self.config_dir.clone() + } + + fn private_identity_key(&self) -> PathBuf { + // TEMPORARILY USE SAME KEYS AS ENCRYPTION + self.private_sphinx_key.clone() + } + + fn public_identity_key(&self) -> PathBuf { + // TEMPORARILY USE SAME KEYS AS ENCRYPTION + self.public_sphinx_key.clone() + } + + fn private_encryption_key(&self) -> Option { + Some(self.private_sphinx_key.clone()) + } + + fn public_encryption_key(&self) -> Option { + Some(self.public_sphinx_key.clone()) + } +} From 2df3a262ef10fff54526b29de5cef5f75087b5ff Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:11:51 +0000 Subject: [PATCH 155/273] Fixed errors in template causing loading errors --- sfw-provider/src/config/template.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sfw-provider/src/config/template.rs b/sfw-provider/src/config/template.rs index 0ffa09c318..cc95584167 100644 --- a/sfw-provider/src/config/template.rs +++ b/sfw-provider/src/config/template.rs @@ -26,6 +26,7 @@ nym_root_directory = "{{ provider.nym_root_directory }}" ##### Mixnet endpoint config options ##### +[mixnet_endpoint] # Socket address to which this service provider will bind to # and will be listening for sphinx packets coming from the mixnet. listening_address = "{{ mixnet_endpoint.listening_address }}" @@ -41,6 +42,7 @@ announce_address = "{{ mixnet_endpoint.announce_address }}" #### Clients endpoint config options ##### +[clients_endpoint] # Socket address to which this service provider will bind to # and will be listening for sphinx packets coming from the mixnet. listening_address = "{{ clients_endpoint.listening_address }}" @@ -58,7 +60,7 @@ inboxes_directory = "{{ clients_endpoint.inboxes_directory }}" # [TODO: implement its storage] Full path to a file containing mapping of # client addresses to their access tokens. -ledger_path = {{ clients_endpoint.ledger_path }}" +ledger_path = "{{ clients_endpoint.ledger_path }}" ##### logging configuration options ##### From 0ab7e4ddd9fe4649eae3266e61a7559d678317f5 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:15:06 +0000 Subject: [PATCH 156/273] mix-host and client-host no longer required for 'run' --- sfw-provider/src/commands/run.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/sfw-provider/src/commands/run.rs b/sfw-provider/src/commands/run.rs index f6e5b65d5a..1b49cf3ba2 100644 --- a/sfw-provider/src/commands/run.rs +++ b/sfw-provider/src/commands/run.rs @@ -30,7 +30,6 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .long("mix-host") .help("The custom host on which the service provider will be running for receiving sphinx packets") .takes_value(true) - .required(true), ) .arg( Arg::with_name("mix-port") @@ -43,7 +42,6 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .long("clients-host") .help("The custom host on which the service provider will be running for receiving clients sfw-provider-requests") .takes_value(true) - .required(true), ) .arg( Arg::with_name("clients-port") From 82bcb3f79f30547432d1a727cfbfdcd276e00fa4 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:30:39 +0000 Subject: [PATCH 157/273] made net_client private --- mixnode/src/node/presence.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs index 56feae14ee..667b4b0f21 100644 --- a/mixnode/src/node/presence.rs +++ b/mixnode/src/node/presence.rs @@ -5,12 +5,6 @@ use directory_client::DirectoryClient; use log::{debug, error}; use std::time::Duration; -pub struct Notifier { - pub net_client: directory_client::Client, - presence: MixNodePresence, - sending_delay: Duration, -} - pub struct NotifierConfig { directory_server: String, announce_host: String, @@ -37,6 +31,12 @@ impl NotifierConfig { } } +pub struct Notifier { + net_client: directory_client::Client, + presence: MixNodePresence, + sending_delay: Duration, +} + impl Notifier { pub fn new(config: NotifierConfig) -> Notifier { let directory_client_cfg = directory_client::Config { From c5ec0591e1d055fb50cc27db84d6ce4e0f16eb20 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:31:00 +0000 Subject: [PATCH 158/273] Changes to provider presence notifier similar to the ones in mixnode --- sfw-provider/src/provider/presence.rs | 64 ++++++++++++++++++--------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/sfw-provider/src/provider/presence.rs b/sfw-provider/src/provider/presence.rs index 77945cb096..67df2bba08 100644 --- a/sfw-provider/src/provider/presence.rs +++ b/sfw-provider/src/provider/presence.rs @@ -1,41 +1,62 @@ +use crate::built_info; use crate::provider::ClientLedger; -use crypto::identity::MixIdentityPublicKey; use directory_client::presence::providers::MixProviderPresence; use directory_client::requests::presence_providers_post::PresenceMixProviderPoster; use directory_client::DirectoryClient; use futures::lock::Mutex as FMutex; use log::{debug, error}; -use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; +pub struct NotifierConfig { + directory_server: String, + mix_announce_host: String, + clients_announce_host: String, + pub_key_string: String, + sending_delay: Duration, +} + +impl NotifierConfig { + pub fn new( + directory_server: String, + mix_announce_host: String, + clients_announce_host: String, + pub_key_string: String, + sending_delay: Duration, + ) -> Self { + NotifierConfig { + directory_server, + mix_announce_host, + clients_announce_host, + pub_key_string, + sending_delay, + } + } +} + pub struct Notifier { - pub net_client: directory_client::Client, + net_client: directory_client::Client, client_ledger: Arc>, + sending_delay: Duration, client_listener: String, mixnet_listener: String, - pub_key: String, + pub_key_string: String, } impl Notifier { - pub fn new( - directory_server_address: String, - client_listener: SocketAddr, - mixnet_listener: SocketAddr, - pub_key: MixIdentityPublicKey, - client_ledger: Arc>, - ) -> Notifier { - let directory_config = directory_client::Config { - base_url: directory_server_address, + pub fn new(config: NotifierConfig, client_ledger: Arc>) -> Notifier { + let directory_client_cfg = directory_client::Config { + base_url: config.directory_server, }; - let net_client = directory_client::Client::new(directory_config); + let net_client = directory_client::Client::new(directory_client_cfg); Notifier { - net_client, - client_listener: client_listener.to_string(), - mixnet_listener: mixnet_listener.to_string(), - pub_key: pub_key.to_base58_string(), client_ledger, + net_client, + client_listener: config.clients_announce_host, + mixnet_listener: config.mix_announce_host, + pub_key_string: config.pub_key_string, + sending_delay: config.sending_delay, } } @@ -45,10 +66,10 @@ impl Notifier { MixProviderPresence { client_listener: self.client_listener.clone(), mixnet_listener: self.mixnet_listener.clone(), - pub_key: self.pub_key.clone(), + pub_key: self.pub_key_string.clone(), registered_clients: unlocked_ledger.current_clients(), last_seen: 0, - version: env!("CARGO_PKG_VERSION").to_string(), + version: built_info::PKG_VERSION.to_string(), } } @@ -63,8 +84,7 @@ impl Notifier { loop { let presence = self.make_presence().await; self.notify(presence); - let delay_duration = Duration::from_secs(5); - tokio::time::delay_for(delay_duration).await; + tokio::time::delay_for(self.sending_delay).await; } } } From 3fdf7b4f109997f32b95381029126ece04036335 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:38:32 +0000 Subject: [PATCH 159/273] Provider starting to use new config --- sfw-provider/src/commands/run.rs | 20 +---- .../src/provider/client_handling/mod.rs | 9 +- sfw-provider/src/provider/mix_handling/mod.rs | 7 +- sfw-provider/src/provider/mod.rs | 87 ++++++++----------- 4 files changed, 48 insertions(+), 75 deletions(-) diff --git a/sfw-provider/src/commands/run.rs b/sfw-provider/src/commands/run.rs index 1b49cf3ba2..2280ba9e24 100644 --- a/sfw-provider/src/commands/run.rs +++ b/sfw-provider/src/commands/run.rs @@ -161,24 +161,6 @@ pub fn execute(matches: &ArgMatches) { config.get_clients_ledger_path() ); - // key will be loaded directly provider in just a moment - let key_pair = ServiceProvider::load_sphinx_keys(&config); - - // stupid temporary hack - let private_bytes = key_pair.private_key().to_bytes(); - let public_bytes = key_pair.public_key().to_bytes(); - - let old_startup_config = provider::Config { - client_socket_address: config.get_clients_listening_address(), - directory_server: config.get_presence_directory_server(), - mix_socket_address: config.get_mix_listening_address(), - // identity keys are wrapper for encryption keys so this temporary hack will work to just make it compile once - public_key: MixIdentityPublicKey::from_bytes(public_bytes.as_ref()), - secret_key: MixIdentityPrivateKey::from_bytes(private_bytes.as_ref()), - store_dir: Default::default(), - }; - - let provider = ServiceProvider::new(old_startup_config); - + let provider = ServiceProvider::new(config); provider.start().unwrap() } diff --git a/sfw-provider/src/provider/client_handling/mod.rs b/sfw-provider/src/provider/client_handling/mod.rs index 35fbbf5eb9..bdd39f7286 100644 --- a/sfw-provider/src/provider/client_handling/mod.rs +++ b/sfw-provider/src/provider/client_handling/mod.rs @@ -1,5 +1,6 @@ use crate::provider::storage::{ClientStorage, StoreError}; use crate::provider::ClientLedger; +use crypto::encryption; use crypto::identity::MixIdentityPrivateKey; use futures::lock::Mutex as FMutex; use hmac::{Hmac, Mac}; @@ -54,14 +55,14 @@ impl From for ClientProcessingError { pub(crate) struct ClientProcessingData { store_dir: PathBuf, registered_clients_ledger: Arc>, - secret_key: MixIdentityPrivateKey, + secret_key: encryption::PrivateKey, } impl ClientProcessingData { pub(crate) fn new( store_dir: PathBuf, registered_clients_ledger: Arc>, - secret_key: MixIdentityPrivateKey, + secret_key: encryption::PrivateKey, ) -> Self { ClientProcessingData { store_dir, @@ -132,7 +133,7 @@ impl ClientRequestProcessor { let auth_token = ClientRequestProcessor::generate_new_auth_token( req.destination_address.to_vec(), - processing_data.secret_key, + &processing_data.secret_key, ); if !unlocked_ledger.has_token(&auth_token) { unlocked_ledger.insert_token(auth_token.clone(), req.destination_address); @@ -153,7 +154,7 @@ impl ClientRequestProcessor { std::fs::create_dir_all(full_store_dir) } - fn generate_new_auth_token(data: Vec, key: MixIdentityPrivateKey) -> AuthToken { + fn generate_new_auth_token(data: Vec, key: &encryption::PrivateKey) -> AuthToken { // also note that `new_varkey` doesn't even have an execution branch returning an error let mut auth_token_raw = HmacSha256::new_varkey(&key.to_bytes()) .expect("HMAC should be able take key of any size"); diff --git a/sfw-provider/src/provider/mix_handling/mod.rs b/sfw-provider/src/provider/mix_handling/mod.rs index 60ceaf700e..7e8ceaf085 100644 --- a/sfw-provider/src/provider/mix_handling/mod.rs +++ b/sfw-provider/src/provider/mix_handling/mod.rs @@ -1,4 +1,5 @@ use crate::provider::storage::StoreData; +use crypto::encryption; use crypto::identity::MixIdentityPrivateKey; use log::{error, warn}; use sphinx::{ProcessedPacket, SphinxPacket}; @@ -37,12 +38,12 @@ impl From for MixProcessingError { // ProcessingData defines all data required to correctly unwrap sphinx packets #[derive(Debug, Clone)] pub(crate) struct MixProcessingData { - secret_key: MixIdentityPrivateKey, + secret_key: encryption::PrivateKey, pub(crate) store_dir: PathBuf, } impl MixProcessingData { - pub(crate) fn new(secret_key: MixIdentityPrivateKey, store_dir: PathBuf) -> Self { + pub(crate) fn new(secret_key: encryption::PrivateKey, store_dir: PathBuf) -> Self { MixProcessingData { secret_key, store_dir, @@ -70,7 +71,7 @@ impl MixPacketProcessor { } }; let (client_address, client_surb_id, payload) = - match packet.process(read_processing_data.secret_key.as_scalar()) { + match packet.process(read_processing_data.secret_key.inner()) { Ok(ProcessedPacket::ProcessedPacketFinalHop(client_address, surb_id, payload)) => { (client_address, surb_id, payload) } diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index a980c454ed..8636932972 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -1,4 +1,5 @@ use crate::config::persistance::pathfinder::ProviderPathfinder; +use crate::config::Config; use crate::provider::client_handling::{ClientProcessingData, ClientRequestProcessor}; use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData}; use crate::provider::storage::ClientStorage; @@ -28,15 +29,6 @@ mod storage; const STORED_MESSAGE_FILENAME_LENGTH: usize = 16; const MESSAGE_RETRIEVAL_LIMIT: usize = 5; -pub struct Config { - pub client_socket_address: SocketAddr, - pub directory_server: String, - pub mix_socket_address: SocketAddr, - pub public_key: MixIdentityPublicKey, - pub secret_key: MixIdentityPrivateKey, - pub store_dir: PathBuf, -} - #[derive(Debug)] pub enum ProviderError { TcpListenerBindingError, @@ -102,29 +94,34 @@ impl ClientLedger { } pub struct ServiceProvider { - directory_server: String, - mix_network_address: SocketAddr, - client_network_address: SocketAddr, - public_key: MixIdentityPublicKey, - secret_key: MixIdentityPrivateKey, - store_dir: PathBuf, + config: Config, + sphinx_keypair: encryption::KeyPair, registered_clients_ledger: ClientLedger, } impl ServiceProvider { pub fn new(config: Config) -> Self { + let sphinx_keypair = Self::load_sphinx_keys(&config); + ServiceProvider { - mix_network_address: config.mix_socket_address, - client_network_address: config.client_socket_address, - secret_key: config.secret_key, - public_key: config.public_key, - store_dir: PathBuf::from(config.store_dir.clone()), + config, + sphinx_keypair, // TODO: load initial ledger from file registered_clients_ledger: ClientLedger::new(), - directory_server: config.directory_server.clone(), } } + fn load_sphinx_keys(config_file: &Config) -> encryption::KeyPair { + let sphinx_keypair = PemStore::new(ProviderPathfinder::new_from_config(&config_file)) + .read_encryption() + .expect("Failed to read stored sphinx key files"); + println!( + "Public encryption key: {}\nFor time being, it is identical to identity keys", + sphinx_keypair.public_key().to_base58_string() + ); + sphinx_keypair + } + async fn process_mixnet_socket_connection( mut socket: tokio::net::TcpStream, processing_data: Arc>, @@ -239,7 +236,7 @@ impl ServiceProvider { async fn start_mixnet_listening( address: SocketAddr, - secret_key: MixIdentityPrivateKey, + secret_key: encryption::PrivateKey, store_dir: PathBuf, ) -> Result<(), ProviderError> { let mut listener = tokio::net::TcpListener::bind(address).await?; @@ -261,7 +258,7 @@ impl ServiceProvider { address: SocketAddr, store_dir: PathBuf, client_ledger: Arc>, - secret_key: MixIdentityPrivateKey, + secret_key: encryption::PrivateKey, ) -> Result<(), ProviderError> { let mut listener = tokio::net::TcpListener::bind(address).await?; let processing_data = @@ -279,19 +276,6 @@ impl ServiceProvider { } } - // TODO: make private and change config import path - pub fn load_sphinx_keys(config_file: &crate::config::Config) -> encryption::KeyPair { - let sphinx_keypair = PemStore::new(ProviderPathfinder::new_from_config(&config_file)) - .read_encryption() - .expect("Failed to read stored sphinx key files"); - println!( - "Public encryption key: {}\nFor time being, it is identical to identity keys", - sphinx_keypair.public_key().to_base58_string() - ); - sphinx_keypair - } - - // Note: this now consumes the provider pub fn start(self) -> Result<(), Box> { // Create the runtime, probably later move it to Provider struct itself? // TODO: figure out the difference between Runtime and Handle @@ -301,25 +285,30 @@ impl ServiceProvider { let initial_client_ledger = self.registered_clients_ledger; let thread_shareable_ledger = initial_client_ledger.add_arc_futures_mutex(); - let presence_notifier = presence::Notifier::new( - self.directory_server, - self.client_network_address.clone(), - self.mix_network_address.clone(), - self.public_key, - thread_shareable_ledger.clone(), + let notifier_config = presence::NotifierConfig::new( + self.config.get_presence_directory_server(), + self.config.get_mix_announce_address(), + self.config.get_clients_announce_address(), + self.sphinx_keypair.public_key().to_base58_string(), + self.config.get_presence_sending_delay(), ); - let presence_future = rt.spawn(presence_notifier.run()); + let presence_future = rt.spawn({ + let presence_notifier = + presence::Notifier::new(notifier_config, thread_shareable_ledger.clone()); + presence_notifier.run() + }); + let mix_future = rt.spawn(ServiceProvider::start_mixnet_listening( - self.mix_network_address, - self.secret_key.clone(), - self.store_dir.clone(), + self.config.get_mix_listening_address(), + *self.sphinx_keypair.private_key(), + self.config.get_clients_inboxes_dir(), )); let client_future = rt.spawn(ServiceProvider::start_client_listening( - self.client_network_address, - self.store_dir.clone(), + self.config.get_clients_listening_address(), + self.config.get_clients_inboxes_dir(), thread_shareable_ledger, - self.secret_key, + *self.sphinx_keypair.private_key(), )); // Spawn the root task rt.block_on(async { From 95160a2ba309e31f38665c0a33ffcbff32f6be8d Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:45:47 +0000 Subject: [PATCH 160/273] Additional debug options --- sfw-provider/src/config/mod.rs | 21 +++++++++++++++++++++ sfw-provider/src/config/template.rs | 8 ++++++++ 2 files changed, 29 insertions(+) diff --git a/sfw-provider/src/config/mod.rs b/sfw-provider/src/config/mod.rs index 53857a0181..b578ef92b7 100644 --- a/sfw-provider/src/config/mod.rs +++ b/sfw-provider/src/config/mod.rs @@ -17,6 +17,9 @@ const DEFAULT_CLIENT_LISTENING_PORT: u16 = 9000; // where applicable, the below are defined in milliseconds const DEFAULT_PRESENCE_SENDING_DELAY: u64 = 3000; +const DEFAULT_STORED_MESSAGE_FILENAME_LENGTH: u16 = 16; +const DEFAULT_MESSAGE_RETRIEVAL_LIMIT: u16 = 5; + #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] pub struct Config { @@ -302,6 +305,14 @@ impl Config { pub fn get_clients_ledger_path(&self) -> PathBuf { self.clients_endpoint.ledger_path.clone() } + + pub fn get_message_retrieval_limit(&self) -> u16 { + self.debug.message_retrieval_limit + } + + pub fn get_stored_messages_filename_length(&self) -> u16 { + self.debug.stored_messages_filename_length + } } #[derive(Debug, Deserialize, PartialEq, Serialize)] @@ -433,6 +444,14 @@ pub struct Debug { /// Delay between each subsequent presence data being sent. presence_sending_delay: u64, + + /// Length of filenames for new client messages. + stored_messages_filename_length: u16, + + /// number of messages client gets on each request + /// if there are no real messages, dummy ones are create to always return + /// `message_retrieval_limit` total messages + message_retrieval_limit: u16, } impl Debug { @@ -451,6 +470,8 @@ impl Default for Debug { Debug { presence_directory_server: Self::default_directory_server(), presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY, + stored_messages_filename_length: DEFAULT_STORED_MESSAGE_FILENAME_LENGTH, + message_retrieval_limit: DEFAULT_MESSAGE_RETRIEVAL_LIMIT, } } } diff --git a/sfw-provider/src/config/template.rs b/sfw-provider/src/config/template.rs index cc95584167..64208c8ab7 100644 --- a/sfw-provider/src/config/template.rs +++ b/sfw-provider/src/config/template.rs @@ -82,5 +82,13 @@ presence_directory_server = "{{ debug.presence_directory_server}}" # Delay between each subsequent presence data being sent. presence_sending_delay = {{ debug.presence_sending_delay }} +# Length of filenames for new client messages. +stored_messages_filename_length = {{ debug.stored_messages_filename_length }} + +# number of messages client gets on each request +# if there are no real messages, dummy ones are create to always return +# `message_retrieval_limit` total messages +message_retrieval_limit = {{ debug.message_retrieval_limit }} + "# } From 4e4024aba64886449791eebaa8e6d834d2cadaf6 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 12:49:47 +0000 Subject: [PATCH 161/273] Fixed broken tests --- .../src/provider/client_handling/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/sfw-provider/src/provider/client_handling/mod.rs b/sfw-provider/src/provider/client_handling/mod.rs index bdd39f7286..25a5f7c519 100644 --- a/sfw-provider/src/provider/client_handling/mod.rs +++ b/sfw-provider/src/provider/client_handling/mod.rs @@ -250,9 +250,9 @@ mod generating_new_auth_token { fn for_the_same_input_generates_the_same_auth_token() { let data1 = vec![1u8; 55]; let data2 = vec![1u8; 55]; - let key = MixIdentityPrivateKey::from_bytes(&[1u8; 32]); - let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key); - let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key); + let key = encryption::PrivateKey::from_bytes(&[1u8; 32]); + let token1 = ClientRequestProcessor::generate_new_auth_token(data1, &key); + let token2 = ClientRequestProcessor::generate_new_auth_token(data2, &key); assert_eq!(token1, token2); } @@ -260,16 +260,16 @@ mod generating_new_auth_token { fn for_different_inputs_generates_different_auth_tokens() { let data1 = vec![1u8; 55]; let data2 = vec![2u8; 55]; - let key = MixIdentityPrivateKey::from_bytes(&[1u8; 32]); - let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key); - let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key); + let key = encryption::PrivateKey::from_bytes(&[1u8; 32]); + let token1 = ClientRequestProcessor::generate_new_auth_token(data1, &key); + let token2 = ClientRequestProcessor::generate_new_auth_token(data2, &key); assert_ne!(token1, token2); let data1 = vec![1u8; 50]; let data2 = vec![2u8; 55]; - let key = MixIdentityPrivateKey::from_bytes(&[1u8; 32]); - let token1 = ClientRequestProcessor::generate_new_auth_token(data1, key); - let token2 = ClientRequestProcessor::generate_new_auth_token(data2, key); + let key = encryption::PrivateKey::from_bytes(&[1u8; 32]); + let token1 = ClientRequestProcessor::generate_new_auth_token(data1, &key); + let token2 = ClientRequestProcessor::generate_new_auth_token(data2, &key); assert_ne!(token1, token2); } } From 3497ae10434b90290e1f7241737e571459835b13 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 13:00:20 +0000 Subject: [PATCH 162/273] Pushing those new debug options down the provider processing stack --- Cargo.lock | 4 ++++ sfw-provider/src/commands/init.rs | 1 - .../src/provider/client_handling/mod.rs | 4 ++++ sfw-provider/src/provider/mix_handling/mod.rs | 15 +++++++++++-- sfw-provider/src/provider/mod.rs | 21 ++++++++++++------- sfw-provider/src/provider/storage/mod.rs | 15 ++++++++----- 6 files changed, 45 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e21e731b3f..2857c4ec31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1471,13 +1471,16 @@ dependencies = [ "bs58", "built", "clap", + "config", "crypto", "curve25519-dalek", "directory-client", + "dirs", "dotenv", "futures 0.3.1", "hmac", "log", + "pemstore", "pretty_env_logger", "rand 0.7.3", "serde", @@ -1485,6 +1488,7 @@ dependencies = [ "sfw-provider-requests", "sha2", "sphinx", + "tempfile", "tokio 0.2.10", ] diff --git a/sfw-provider/src/commands/init.rs b/sfw-provider/src/commands/init.rs index 6973a858e7..f3ec9624f2 100644 --- a/sfw-provider/src/commands/init.rs +++ b/sfw-provider/src/commands/init.rs @@ -3,7 +3,6 @@ use crate::config::persistance::pathfinder::ProviderPathfinder; use clap::{App, Arg, ArgMatches}; use config::NymConfig; use crypto::encryption; -use crypto::identity::MixIdentityKeyPair; use pemstore::pemstore::PemStore; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { diff --git a/sfw-provider/src/provider/client_handling/mod.rs b/sfw-provider/src/provider/client_handling/mod.rs index 25a5f7c519..7cae0b4894 100644 --- a/sfw-provider/src/provider/client_handling/mod.rs +++ b/sfw-provider/src/provider/client_handling/mod.rs @@ -56,6 +56,7 @@ pub(crate) struct ClientProcessingData { store_dir: PathBuf, registered_clients_ledger: Arc>, secret_key: encryption::PrivateKey, + message_retrieval_limit: u16, } impl ClientProcessingData { @@ -63,11 +64,13 @@ impl ClientProcessingData { store_dir: PathBuf, registered_clients_ledger: Arc>, secret_key: encryption::PrivateKey, + message_retrieval_limit: u16, ) -> Self { ClientProcessingData { store_dir, registered_clients_ledger, secret_key, + message_retrieval_limit, } } @@ -114,6 +117,7 @@ impl ClientRequestProcessor { let retrieved_messages = ClientStorage::retrieve_client_files( req.destination_address, processing_data.store_dir.as_path(), + processing_data.message_retrieval_limit, )?; Ok(PullResponse::new(retrieved_messages)) } else { diff --git a/sfw-provider/src/provider/mix_handling/mod.rs b/sfw-provider/src/provider/mix_handling/mod.rs index 7e8ceaf085..aab17f1dda 100644 --- a/sfw-provider/src/provider/mix_handling/mod.rs +++ b/sfw-provider/src/provider/mix_handling/mod.rs @@ -40,13 +40,19 @@ impl From for MixProcessingError { pub(crate) struct MixProcessingData { secret_key: encryption::PrivateKey, pub(crate) store_dir: PathBuf, + new_messages_filename_length: u16, } impl MixProcessingData { - pub(crate) fn new(secret_key: encryption::PrivateKey, store_dir: PathBuf) -> Self { + pub(crate) fn new( + secret_key: encryption::PrivateKey, + store_dir: PathBuf, + new_messages_filename_length: u16, + ) -> Self { MixProcessingData { secret_key, store_dir, + new_messages_filename_length, } } @@ -91,6 +97,11 @@ impl MixPacketProcessor { return Err(MixProcessingError::NonMatchingRecipient); } - Ok(StoreData::new(client_address, client_surb_id, message)) + Ok(StoreData::new( + client_address, + client_surb_id, + message, + read_processing_data.new_messages_filename_length, + )) } } diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 8636932972..2f709a843e 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -25,10 +25,6 @@ mod mix_handling; pub mod presence; mod storage; -// TODO: if we ever create config file, this should go there -const STORED_MESSAGE_FILENAME_LENGTH: usize = 16; -const MESSAGE_RETRIEVAL_LIMIT: usize = 5; - #[derive(Debug)] pub enum ProviderError { TcpListenerBindingError, @@ -238,9 +234,12 @@ impl ServiceProvider { address: SocketAddr, secret_key: encryption::PrivateKey, store_dir: PathBuf, + new_messages_filename_length: u16, ) -> Result<(), ProviderError> { let mut listener = tokio::net::TcpListener::bind(address).await?; - let processing_data = MixProcessingData::new(secret_key, store_dir).add_arc_rwlock(); + let processing_data = + MixProcessingData::new(secret_key, store_dir, new_messages_filename_length) + .add_arc_rwlock(); loop { let (socket, _) = listener.accept().await?; @@ -259,10 +258,16 @@ impl ServiceProvider { store_dir: PathBuf, client_ledger: Arc>, secret_key: encryption::PrivateKey, + message_retrieval_limit: u16, ) -> Result<(), ProviderError> { let mut listener = tokio::net::TcpListener::bind(address).await?; - let processing_data = - ClientProcessingData::new(store_dir, client_ledger, secret_key).add_arc(); + let processing_data = ClientProcessingData::new( + store_dir, + client_ledger, + secret_key, + message_retrieval_limit, + ) + .add_arc(); loop { let (socket, _) = listener.accept().await?; @@ -303,12 +308,14 @@ impl ServiceProvider { self.config.get_mix_listening_address(), *self.sphinx_keypair.private_key(), self.config.get_clients_inboxes_dir(), + self.config.get_stored_messages_filename_length(), )); let client_future = rt.spawn(ServiceProvider::start_client_listening( self.config.get_clients_listening_address(), self.config.get_clients_inboxes_dir(), thread_shareable_ledger, *self.sphinx_keypair.private_key(), + self.config.get_message_retrieval_limit(), )); // Spawn the root task rt.block_on(async { diff --git a/sfw-provider/src/provider/storage/mod.rs b/sfw-provider/src/provider/storage/mod.rs index 73c32a84a3..6123d5c686 100644 --- a/sfw-provider/src/provider/storage/mod.rs +++ b/sfw-provider/src/provider/storage/mod.rs @@ -1,4 +1,3 @@ -use crate::provider::{MESSAGE_RETRIEVAL_LIMIT, STORED_MESSAGE_FILENAME_LENGTH}; use log::*; use rand::Rng; use sfw_provider_requests::DUMMY_MESSAGE_CONTENT; @@ -26,6 +25,7 @@ pub struct StoreData { #[allow(dead_code)] client_surb_id: SURBIdentifier, message: Vec, + filename_length: u16, } impl StoreData { @@ -33,11 +33,13 @@ impl StoreData { client_address: DestinationAddressBytes, client_surb_id: SURBIdentifier, message: Vec, + filename_length: u16, ) -> Self { StoreData { client_address, client_surb_id, message, + filename_length, } } } @@ -47,10 +49,10 @@ pub struct ClientStorage(()); // TODO: change it to some generic implementation to inject fs impl ClientStorage { - pub(crate) fn generate_random_file_name() -> String { + pub(crate) fn generate_random_file_name(length: usize) -> String { rand::thread_rng() .sample_iter(&rand::distributions::Alphanumeric) - .take(STORED_MESSAGE_FILENAME_LENGTH) + .take(length) .collect::() } @@ -62,7 +64,9 @@ impl ClientStorage { pub fn store_processed_data(store_data: StoreData, store_dir: &Path) -> io::Result<()> { let client_dir_name = bs58::encode(store_data.client_address).into_string(); let full_store_dir = store_dir.join(client_dir_name); - let full_store_path = full_store_dir.join(ClientStorage::generate_random_file_name()); + let full_store_path = full_store_dir.join(ClientStorage::generate_random_file_name( + store_data.filename_length as usize, + )); debug!( "going to store: {:?} in file: {:?}", store_data.message, full_store_path @@ -80,6 +84,7 @@ impl ClientStorage { pub fn retrieve_client_files( client_address: DestinationAddressBytes, store_dir: &Path, + message_retrieval_limit: u16, ) -> Result>, StoreError> { let client_dir_name = bs58::encode(client_address).into_string(); let full_store_dir = store_dir.join(client_dir_name); @@ -99,7 +104,7 @@ impl ClientStorage { content }) // TODO: THIS MAP IS UNSAFE (BOTH FOR READING AND DELETING)!! - in the case where there are multiple requests from the same client being processed in parallel .chain(std::iter::repeat(ClientStorage::dummy_message())) - .take(MESSAGE_RETRIEVAL_LIMIT) + .take(message_retrieval_limit as usize) .collect(); Ok(msgs) From 83e22dbad9d1ca0db6da41fbc9987b0c5847e306 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 15:52:19 +0000 Subject: [PATCH 163/273] Moved built_info module to separate file --- validator/src/built_info.rs | 2 ++ validator/src/main.rs | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 validator/src/built_info.rs diff --git a/validator/src/built_info.rs b/validator/src/built_info.rs new file mode 100644 index 0000000000..d11fb6389f --- /dev/null +++ b/validator/src/built_info.rs @@ -0,0 +1,2 @@ +// The file has been placed there by the build script. +include!(concat!(env!("OUT_DIR"), "/built.rs")); diff --git a/validator/src/main.rs b/validator/src/main.rs index 20d7ee6f2d..eba3636636 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -5,6 +5,7 @@ use log::*; use std::process; use toml; +pub mod built_info; mod network; mod services; mod validator; @@ -51,10 +52,6 @@ fn parse_config(matches: &ArgMatches) -> Config { toml::from_str(&config_content).unwrap() } -pub mod built_info { - // The file has been placed there by the build script. - include!(concat!(env!("OUT_DIR"), "/built.rs")); -} fn execute(matches: ArgMatches) -> Result<(), String> { match matches.subcommand() { From 8ea9b14e9470554bcd2e119d32cbe048509b0879 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 15:52:35 +0000 Subject: [PATCH 164/273] Init command for the validator --- validator/src/commands/init.rs | 38 ++++++++++++++++++++++++++++++++++ validator/src/main.rs | 17 +++++++-------- 2 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 validator/src/commands/init.rs diff --git a/validator/src/commands/init.rs b/validator/src/commands/init.rs new file mode 100644 index 0000000000..b46f24e4bd --- /dev/null +++ b/validator/src/commands/init.rs @@ -0,0 +1,38 @@ +use crate::commands::override_config; +use clap::{App, Arg, ArgMatches}; +use config::NymConfig; + +pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { + App::new("init") + .about("Initialise the validator") + .arg( + Arg::with_name("id") + .long("id") + .help("Id of the nym-mixnode we want to create config for.") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the validator is sending presence to") + .takes_value(true), + ) +} + +pub fn execute(matches: &ArgMatches) { + let id = matches.value_of("id").unwrap(); + println!("Initialising validator {}...", id); + + let mut config = crate::config::Config::new(id); + + config = override_config(config, matches); + + let config_save_location = config.get_config_file_save_location(); + config + .save_to_file(None) + .expect("Failed to save the config file"); + println!("Saved configuration file to {:?}", config_save_location); + + println!("Validator configuration completed.\n\n\n") +} diff --git a/validator/src/main.rs b/validator/src/main.rs index eba3636636..c7b71a3d7b 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -2,10 +2,11 @@ use crate::validator::Config; use crate::validator::Validator; use clap::{App, Arg, ArgMatches, SubCommand}; use log::*; -use std::process; use toml; pub mod built_info; +mod commands; +mod config; mod network; mod services; mod validator; @@ -18,6 +19,7 @@ fn main() { .version(built_info::PKG_VERSION) .author("Nymtech") .about("Implementation of Nym Validator") + .subcommand(commands::init::command_args()) .subcommand( SubCommand::with_name("run") .about("Starts the validator") @@ -31,10 +33,7 @@ fn main() { ) .get_matches(); - if let Err(e) = execute(arg_matches) { - error!("{:?}", e); - process::exit(1); - } + execute(arg_matches); } fn run(matches: &ArgMatches) { @@ -52,11 +51,11 @@ fn parse_config(matches: &ArgMatches) -> Config { toml::from_str(&config_content).unwrap() } - -fn execute(matches: ArgMatches) -> Result<(), String> { +fn execute(matches: ArgMatches) { match matches.subcommand() { - ("run", Some(m)) => Ok(run(m)), - _ => Err(usage()), + ("init", Some(m)) => commands::init::execute(m), + ("run", Some(m)) => run(m), + _ => println!("{}", usage()), } } From 84cb98ca964bc86bf0eed8bd7cce6064f532c167 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 15:52:42 +0000 Subject: [PATCH 165/273] Override config stub --- validator/src/commands/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 validator/src/commands/mod.rs diff --git a/validator/src/commands/mod.rs b/validator/src/commands/mod.rs new file mode 100644 index 0000000000..d21d4a92bc --- /dev/null +++ b/validator/src/commands/mod.rs @@ -0,0 +1,9 @@ +use crate::config::Config; +use clap::ArgMatches; + +pub mod init; +pub mod run; + +pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { + config +} From 5aee54fc1db7a4efac8cc8a6ddce6f9c460321d3 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 15:53:02 +0000 Subject: [PATCH 166/273] Stubs for validator configs --- validator/src/config/mod.rs | 193 +++++++++++++++++++++++++++++++ validator/src/config/template.rs | 39 +++++++ 2 files changed, 232 insertions(+) create mode 100644 validator/src/config/mod.rs create mode 100644 validator/src/config/template.rs diff --git a/validator/src/config/mod.rs b/validator/src/config/mod.rs new file mode 100644 index 0000000000..87af031006 --- /dev/null +++ b/validator/src/config/mod.rs @@ -0,0 +1,193 @@ +use crate::config::template::config_template; +use config::NymConfig; +use log::*; +use serde::{Deserialize, Serialize}; +use std::net::{IpAddr, SocketAddr}; +use std::path::PathBuf; +use std::str::FromStr; +use std::time; + +mod template; + +// 'DEBUG' +// where applicable, the below are defined in milliseconds +const DEFAULT_PRESENCE_SENDING_DELAY: u64 = 3000; + +#[derive(Debug, Default, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Config { + validator: Validator, + + mix_mining: MixMining, + + tendermint: Tendermint, + + #[serde(default)] + logging: Logging, + #[serde(default)] + debug: Debug, +} + +impl NymConfig for Config { + fn template() -> &'static str { + config_template() + } + + fn config_file_name() -> String { + "config.toml".to_string() + } + + fn default_root_directory() -> PathBuf { + dirs::home_dir() + .expect("Failed to evaluate $HOME value") + .join(".nym") + .join("validators") + } + + fn root_directory(&self) -> PathBuf { + self.validator.nym_root_directory.clone() + } + + fn config_directory(&self) -> PathBuf { + self.validator + .nym_root_directory + .join(&self.validator.id) + .join("config") + } + + fn data_directory(&self) -> PathBuf { + self.validator + .nym_root_directory + .join(&self.validator.id) + .join("data") + } +} + +impl Config { + pub fn new>(id: S) -> Self { + Config::default().with_id(id) + } + + // builder methods + pub fn with_id>(mut self, id: S) -> Self { + let id = id.into(); + + // calls to any defaults requiring id (see: client, mixnode, provider): + + self.validator.id = id; + self + } + + pub fn with_custom_directory>(mut self, directory_server: S) -> Self { + let directory_server_string = directory_server.into(); + self.debug.presence_directory_server = directory_server_string.clone(); + self + } + + // getters + pub fn get_config_file_save_location(&self) -> PathBuf { + self.config_directory().join(Self::config_file_name()) + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Validator { + /// ID specifies the human readable ID of this particular validator. + id: String, + + /// 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, +} + +impl Validator {} + +impl Default for Validator { + fn default() -> Self { + Validator { + id: "".to_string(), + nym_root_directory: Config::default_root_directory(), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct MixMining {} + +impl Default for MixMining { + fn default() -> Self { + MixMining {} + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Tendermint {} + +impl Default for Tendermint { + fn default() -> Self { + Tendermint {} + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Logging {} + +impl Default for Logging { + fn default() -> Self { + Logging {} + } +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct Debug { + /// Directory server to which the server will be reporting their presence data. + presence_directory_server: String, + + /// Delay between each subsequent presence data being sent. + presence_sending_delay: u64, +} + +impl Debug { + fn default_directory_server() -> String { + #[cfg(feature = "qa")] + return "https://qa-directory.nymtech.net".to_string(); + #[cfg(feature = "local")] + return "http://localhost:8080".to_string(); + + "https://directory.nymtech.net".to_string() + } +} + +impl Default for Debug { + fn default() -> Self { + Debug { + presence_directory_server: Self::default_directory_server(), + presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY, + } + } +} + +#[cfg(test)] +mod validator_config { + use super::*; + + #[test] + fn after_saving_default_config_the_loaded_one_is_identical() { + // need to figure out how to do something similar but without touching the disk + // or the file system at all... + let temp_location = tempfile::tempdir().unwrap().path().join("config.toml"); + let default_config = Config::default().with_id("foomp".to_string()); + default_config + .save_to_file(Some(temp_location.clone())) + .unwrap(); + + let loaded_config = Config::load_from_file(Some(temp_location), None).unwrap(); + + assert_eq!(default_config, loaded_config); + } +} diff --git a/validator/src/config/template.rs b/validator/src/config/template.rs new file mode 100644 index 0000000000..42fc0c2f19 --- /dev/null +++ b/validator/src/config/template.rs @@ -0,0 +1,39 @@ +pub(crate) fn config_template() -> &'static str { + // While using normal toml marshalling would have been way simpler with less overhead, + // I think it's useful to have comments attached to the saved config file to explain behaviour of + // particular fields. + // Note: any changes to the template must be reflected in the appropriate structs in mod.rs. + r#" +# This is a TOML config file. +# For more information, see https://github.com/toml-lang/toml + +##### main base mixnode config options ##### + +[validator] +# Human readable ID of this particular validator. +id = "{{ provider.id }}" + + + +# nym_home_directory specifies absolute path to the home nym validators directory. +# It is expected to use default value and hence .toml file should not redefine this field. +nym_root_directory = "{{ validator.nym_root_directory }}" + + + +##### logging configuration options ##### + +[logging] + +# TODO + + +##### debug configuration options ##### +# The following options should not be modified unless you know EXACTLY what you are doing +# as if set incorrectly, they may impact your anonymity. + +[debug] + + +"# +} From 28b5673cbf35fc93aa919b4ea509cde81be10c8e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 15:54:31 +0000 Subject: [PATCH 167/273] Corrected description to say validator instead of mixnode --- validator/src/commands/init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/commands/init.rs b/validator/src/commands/init.rs index b46f24e4bd..697edfcdcb 100644 --- a/validator/src/commands/init.rs +++ b/validator/src/commands/init.rs @@ -8,7 +8,7 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .arg( Arg::with_name("id") .long("id") - .help("Id of the nym-mixnode we want to create config for.") + .help("Id of the nym-validator we want to create config for.") .takes_value(true) .required(true), ) From ae27a5e4c35cb9e2c38453a2ffb0b79eb489f7d7 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 15:55:14 +0000 Subject: [PATCH 168/273] Corrected run description for sfw-provider --- sfw-provider/src/commands/run.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sfw-provider/src/commands/run.rs b/sfw-provider/src/commands/run.rs index 2280ba9e24..2041c6f480 100644 --- a/sfw-provider/src/commands/run.rs +++ b/sfw-provider/src/commands/run.rs @@ -111,7 +111,7 @@ fn special_addresses() -> Vec<&'static str> { pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); - println!("Starting mixnode {}...", id); + println!("Starting sfw-provider {}...", id); let mut config = Config::load_from_file(matches.value_of("config").map(|path| path.into()), Some(id)) From 95d1912e18b48591036c1f503ac127fda5dbba94 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 15:57:36 +0000 Subject: [PATCH 169/273] More typos --- mixnode/src/commands/run.rs | 2 +- sfw-provider/src/commands/run.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index f72852c271..a058e7b56a 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -21,7 +21,7 @@ pub fn command_args<'a, 'b>() -> App<'a, 'b> { .arg( Arg::with_name("config") .long("config") - .help("Custom path to the nym-mixnet-client configuration file") + .help("Custom path to the nym-mixnode configuration file") .takes_value(true), ) .arg( diff --git a/sfw-provider/src/commands/run.rs b/sfw-provider/src/commands/run.rs index 2041c6f480..60d51328db 100644 --- a/sfw-provider/src/commands/run.rs +++ b/sfw-provider/src/commands/run.rs @@ -22,7 +22,7 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .arg( Arg::with_name("config") .long("config") - .help("Custom path to the nym-mixnet-client configuration file") + .help("Custom path to the nym-provider configuration file") .takes_value(true), ) .arg( From da8365ee14a9b1aea5271c15bc07fc3f2c518bc6 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:02:44 +0000 Subject: [PATCH 170/273] Running validator via commands::run::execute --- validator/src/commands/run.rs | 55 +++++++++++++++++++++++++++++++++++ validator/src/main.rs | 31 +++----------------- 2 files changed, 59 insertions(+), 27 deletions(-) create mode 100644 validator/src/commands/run.rs diff --git a/validator/src/commands/run.rs b/validator/src/commands/run.rs new file mode 100644 index 0000000000..bca11b7eef --- /dev/null +++ b/validator/src/commands/run.rs @@ -0,0 +1,55 @@ +use crate::commands::override_config; +use crate::config::Config; +use crate::validator; +use crate::validator::Validator; +use clap::{App, Arg, ArgMatches}; +use config::NymConfig; + +pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { + App::new("run") + .about("Starts the validator") + .arg( + Arg::with_name("id") + .long("id") + .help("Id of the nym-validator we want to run") + .takes_value(true) + .required(true), + ) + // the rest of arguments are optional, they are used to override settings in config file + .arg( + Arg::with_name("config") + .long("config") + .help("Custom path to the nym-validator configuration file") + .takes_value(true), + ) + .arg( + Arg::with_name("directory") + .long("directory") + .help("Address of the directory server the validator is sending presence to") + .takes_value(true), + ) +} + +fn parse_old_config(matches: &ArgMatches) -> validator::Config { + let config_file_path = matches.value_of("config").unwrap(); + // since this is happening at the very startup, it's fine to panic if file doesn't exist + let config_content = std::fs::read_to_string(config_file_path).unwrap(); + toml::from_str(&config_content).unwrap() +} + +pub fn execute(matches: &ArgMatches) { + let id = matches.value_of("id").unwrap(); + + println!("Starting sfw-provider {}...", id); + + let mut config = + Config::load_from_file(matches.value_of("config").map(|path| path.into()), Some(id)) + .expect("Failed to load config file"); + + config = override_config(config, matches); + + let old_config = parse_old_config(matches); + + let validator = Validator::new(old_config); + validator.start() +} diff --git a/validator/src/main.rs b/validator/src/main.rs index c7b71a3d7b..4a12f05e5e 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -15,46 +15,23 @@ fn main() { dotenv::dotenv().ok(); pretty_env_logger::init(); + println!("{}", banner()); + let arg_matches = App::new("Nym Validator") .version(built_info::PKG_VERSION) .author("Nymtech") .about("Implementation of Nym Validator") .subcommand(commands::init::command_args()) - .subcommand( - SubCommand::with_name("run") - .about("Starts the validator") - .arg( - Arg::with_name("config") - .long("config") - .help("Location of the validator configuration file") - .takes_value(true) - .required(true), - ), - ) + .subcommand(commands::run::command_args()) .get_matches(); execute(arg_matches); } -fn run(matches: &ArgMatches) { - let config = parse_config(matches); - trace!("read config: {:?}", config); - - let validator = Validator::new(config); - validator.start() -} - -fn parse_config(matches: &ArgMatches) -> Config { - let config_file_path = matches.value_of("config").unwrap(); - // since this is happening at the very startup, it's fine to panic if file doesn't exist - let config_content = std::fs::read_to_string(config_file_path).unwrap(); - toml::from_str(&config_content).unwrap() -} - fn execute(matches: ArgMatches) { match matches.subcommand() { ("init", Some(m)) => commands::init::execute(m), - ("run", Some(m)) => run(m), + ("run", Some(m)) => commands::run::execute(m), _ => println!("{}", usage()), } } From 0d3084b95648a4d00b69acfef9a79de64077ecae Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:02:59 +0000 Subject: [PATCH 171/273] Updated validator dependencies --- Cargo.lock | 3 +++ validator/Cargo.toml | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2857c4ec31..330ecc9529 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1500,14 +1500,17 @@ dependencies = [ "built", "byteorder", "clap", + "config", "crypto", "directory-client", + "dirs", "dotenv", "futures 0.3.1", "healthcheck", "log", "pretty_env_logger", "serde", + "tempfile", "tokio 0.2.10", "toml", "topology", diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 0dfe2ee076..527739df3d 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -11,6 +11,7 @@ edition = "2018" abci = "0.6.4" byteorder = "1.3.2" clap = "2.33.0" +dirs = "2.0.2" # Read notes https://crates.io/crates/dotenv - tl;dr: don't use in production, set environmental variables properly. dotenv = "0.15.0" futures = "0.3.1" @@ -22,6 +23,7 @@ toml = "0.5.5" ## internal crypto = {path = "../common/crypto"} +config = {path = "../common/config"} directory-client = { path = "../common/clients/directory-client" } healthcheck = {path = "../common/healthcheck" } topology = {path = "../common/topology"} @@ -30,3 +32,8 @@ topology = {path = "../common/topology"} built = "0.3.2" [dev-dependencies] +tempfile = "3.1.0" + +[features] +qa = [] +local = [] \ No newline at end of file From 852d127a2a4ace8b8637695663d52e17abdabeaf Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:25:34 +0000 Subject: [PATCH 172/273] Mix mining config section --- validator/src/config/mod.rs | 77 +++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/validator/src/config/mod.rs b/validator/src/config/mod.rs index 87af031006..461690a0e1 100644 --- a/validator/src/config/mod.rs +++ b/validator/src/config/mod.rs @@ -9,8 +9,15 @@ use std::time; mod template; -// 'DEBUG' // where applicable, the below are defined in milliseconds + +// 'MIXMINING' +const DEFAULT_MIX_MINING_DELAY: u64 = 10_000; +const DEFAULT_MIX_MINING_RESOLUTION_TIMEOUT: u64 = 5_000; + +const DEFAULT_NUMBER_OF_MIX_MINING_TEST_PACKETS: u64 = 2; + +// 'DEBUG' const DEFAULT_PRESENCE_SENDING_DELAY: u64 = 3000; #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] @@ -64,6 +71,15 @@ impl NymConfig for Config { } impl Config { + fn default_directory_server() -> String { + #[cfg(feature = "qa")] + return "https://qa-directory.nymtech.net".to_string(); + #[cfg(feature = "local")] + return "http://localhost:8080".to_string(); + + "https://directory.nymtech.net".to_string() + } + pub fn new>(id: S) -> Self { Config::default().with_id(id) } @@ -81,6 +97,7 @@ impl Config { pub fn with_custom_directory>(mut self, directory_server: S) -> Self { let directory_server_string = directory_server.into(); self.debug.presence_directory_server = directory_server_string.clone(); + self.mix_mining.directory_server = directory_server_string; self } @@ -88,6 +105,30 @@ impl Config { pub fn get_config_file_save_location(&self) -> PathBuf { self.config_directory().join(Self::config_file_name()) } + + pub fn get_mix_mining_directory_server(&self) -> String { + self.mix_mining.directory_server.clone() + } + + pub fn get_presence_directory_server(&self) -> String { + self.debug.presence_directory_server.clone() + } + + pub fn get_presence_sending_delay(&self) -> time::Duration { + time::Duration::from_millis(self.debug.presence_sending_delay) + } + + pub fn get_mix_mining_run_delay(&self) -> time::Duration { + time::Duration::from_millis(self.mix_mining.run_delay) + } + + pub fn get_mix_mining_resolution_timeout(&self) -> time::Duration { + time::Duration::from_millis(self.mix_mining.resolution_timeout) + } + + pub fn get_mix_mining_number_of_test_packets(&self) -> u64 { + self.mix_mining.number_of_test_packets + } } #[derive(Debug, Deserialize, PartialEq, Serialize)] @@ -114,11 +155,32 @@ impl Default for Validator { #[derive(Debug, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] -pub struct MixMining {} +pub struct MixMining { + /// Directory server from which the validator will obtain initial topology. + directory_server: String, + + /// The uniform delay every which validator are running their mix-mining procedure. + /// The provided value is interpreted as milliseconds. + run_delay: u64, + + /// During the mix-mining process, test packets are sent through various network + /// paths. This timeout determines waiting period until it is decided that the packet + /// did not reach its destination. + /// The provided value is interpreted as milliseconds. + resolution_timeout: u64, + + /// How many packets should be sent through each path during the mix-mining procedure. + number_of_test_packets: u64, +} impl Default for MixMining { fn default() -> Self { - MixMining {} + MixMining { + directory_server: Config::default_directory_server(), + run_delay: DEFAULT_MIX_MINING_DELAY, + resolution_timeout: DEFAULT_MIX_MINING_RESOLUTION_TIMEOUT, + number_of_test_packets: DEFAULT_NUMBER_OF_MIX_MINING_TEST_PACKETS, + } } } @@ -153,20 +215,13 @@ pub struct Debug { } impl Debug { - fn default_directory_server() -> String { - #[cfg(feature = "qa")] - return "https://qa-directory.nymtech.net".to_string(); - #[cfg(feature = "local")] - return "http://localhost:8080".to_string(); - "https://directory.nymtech.net".to_string() - } } impl Default for Debug { fn default() -> Self { Debug { - presence_directory_server: Self::default_directory_server(), + presence_directory_server: Config::default_directory_server(), presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY, } } From 7f999d5893f710bc417a6b5c1743a2e4ca091c76 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:25:47 +0000 Subject: [PATCH 173/273] Validator using new config --- .../services/mixmining/health_check_runner.rs | 9 +++--- validator/src/validator.rs | 30 ++++++++----------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/validator/src/services/mixmining/health_check_runner.rs b/validator/src/services/mixmining/health_check_runner.rs index 1200c6fe4f..a8f4983958 100644 --- a/validator/src/services/mixmining/health_check_runner.rs +++ b/validator/src/services/mixmining/health_check_runner.rs @@ -6,13 +6,13 @@ use topology::NymTopology; pub struct HealthCheckRunner { directory_server: String, health_checker: HealthChecker, - interval: f64, + interval: Duration, } impl HealthCheckRunner { pub fn new( directory_server: String, - interval: f64, + interval: Duration, health_checker: HealthChecker, ) -> HealthCheckRunner { HealthCheckRunner { @@ -23,8 +23,7 @@ impl HealthCheckRunner { } pub async fn run(self) { - let healthcheck_interval = Duration::from_secs_f64(self.interval); - debug!("healthcheck will run every {:?}", healthcheck_interval); + debug!("healthcheck will run every {:?}", self.interval); loop { let full_topology = directory_client::presence::Topology::new(self.directory_server.clone()); @@ -41,7 +40,7 @@ impl HealthCheckRunner { Ok(health) => info!("current network health: \n{}", health), Err(err) => error!("failed to perform healthcheck - {:?}", err), }; - tokio::time::delay_for(healthcheck_interval).await; + tokio::time::delay_for(self.interval).await; } } } diff --git a/validator/src/validator.rs b/validator/src/validator.rs index c11161711f..1d06347f94 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -1,21 +1,15 @@ +use crate::config::Config; use crate::network::tendermint; use crate::services::mixmining::health_check_runner; use crypto::identity::MixIdentityKeyPair; use healthcheck::HealthChecker; -use serde::Deserialize; -use std::time; use tokio::runtime::Runtime; -#[derive(Deserialize, Debug)] -pub struct Config { - #[serde(rename(deserialize = "healthcheck"))] - pub health_check: healthcheck::config::HealthCheck, -} - // allow for a generic validator pub struct Validator { - #[allow(dead_code)] - identity_keypair: MixIdentityKeyPair, + // when you re-introduce keys, check which ones you want: + // MixIdentityKeyPair (like 'nym-client' ) <- probably that one (after maybe renaming to just identity::KeyPair) + // encryption::KeyPair (like 'nym-mixnode' or 'sfw-provider') health_check_runner: health_check_runner::HealthCheckRunner, tendermint_abci: tendermint::Abci, } @@ -23,22 +17,24 @@ pub struct Validator { // but for time being, since it's a dummy one, have it use dummy keys impl Validator { pub fn new(config: Config) -> Self { - let dummy_keypair = MixIdentityKeyPair::new(); + let dummy_healthcheck_keypair = MixIdentityKeyPair::new(); let hc = HealthChecker::new( - time::Duration::from_secs_f64(config.health_check.resolution_timeout), - config.health_check.num_test_packets, - dummy_keypair.clone(), + config.get_mix_mining_resolution_timeout(), + config.get_mix_mining_number_of_test_packets() as usize, + dummy_healthcheck_keypair, ); let health_check_runner = health_check_runner::HealthCheckRunner::new( - config.health_check.directory_server.clone(), - config.health_check.interval, + config.get_mix_mining_directory_server(), + config.get_mix_mining_run_delay(), hc, ); Validator { - identity_keypair: dummy_keypair, health_check_runner, + + // perhaps you might want to pass &config to the constructor + // there to get the config.tendermint (assuming you create appropriate fields + getters) tendermint_abci: tendermint::Abci::new(), } } From 9f49746b445a4844c439cb1aa9ce1ec7dd61f264 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:27:31 +0000 Subject: [PATCH 174/273] Actually using proper config --- validator/src/commands/run.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/validator/src/commands/run.rs b/validator/src/commands/run.rs index bca11b7eef..3b50bbd1cc 100644 --- a/validator/src/commands/run.rs +++ b/validator/src/commands/run.rs @@ -1,6 +1,5 @@ use crate::commands::override_config; use crate::config::Config; -use crate::validator; use crate::validator::Validator; use clap::{App, Arg, ArgMatches}; use config::NymConfig; @@ -30,17 +29,10 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { ) } -fn parse_old_config(matches: &ArgMatches) -> validator::Config { - let config_file_path = matches.value_of("config").unwrap(); - // since this is happening at the very startup, it's fine to panic if file doesn't exist - let config_content = std::fs::read_to_string(config_file_path).unwrap(); - toml::from_str(&config_content).unwrap() -} - pub fn execute(matches: &ArgMatches) { let id = matches.value_of("id").unwrap(); - println!("Starting sfw-provider {}...", id); + println!("Starting validator {}...", id); let mut config = Config::load_from_file(matches.value_of("config").map(|path| path.into()), Some(id)) @@ -48,8 +40,6 @@ pub fn execute(matches: &ArgMatches) { config = override_config(config, matches); - let old_config = parse_old_config(matches); - - let validator = Validator::new(old_config); + let validator = Validator::new(config); validator.start() } From 1f53dcc56fae03b578c8f049e975df9020c7dd91 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:28:56 +0000 Subject: [PATCH 175/273] Removed no longe used toml dependency from the validator --- Cargo.lock | 1 - validator/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 330ecc9529..29cb23770a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1512,7 +1512,6 @@ dependencies = [ "serde", "tempfile", "tokio 0.2.10", - "toml", "topology", ] diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 527739df3d..ebe0c12ee0 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -19,7 +19,6 @@ log = "0.4" pretty_env_logger = "0.3" serde = "1.0.104" tokio = { version = "0.2", features = ["full"] } -toml = "0.5.5" ## internal crypto = {path = "../common/crypto"} From ab24008c871d6f3438f26a7d55366ba7680e178a Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:29:07 +0000 Subject: [PATCH 176/273] Fixed compilation error due to old imports --- validator/src/main.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/validator/src/main.rs b/validator/src/main.rs index 4a12f05e5e..c47eb5e139 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -1,8 +1,4 @@ -use crate::validator::Config; -use crate::validator::Validator; -use clap::{App, Arg, ArgMatches, SubCommand}; -use log::*; -use toml; +use clap::{App, ArgMatches}; pub mod built_info; mod commands; From a9e0a1d6345c1bca110c542b22b9b2e6a248aae1 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:31:26 +0000 Subject: [PATCH 177/273] Updated config override for validator --- validator/src/commands/init.rs | 2 +- validator/src/commands/mod.rs | 4 ++++ validator/src/commands/run.rs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/validator/src/commands/init.rs b/validator/src/commands/init.rs index 697edfcdcb..c741dca5ee 100644 --- a/validator/src/commands/init.rs +++ b/validator/src/commands/init.rs @@ -15,7 +15,7 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .arg( Arg::with_name("directory") .long("directory") - .help("Address of the directory server the validator is sending presence to") + .help("Address of the directory server the validator is sending presence to and uses for mix mining") .takes_value(true), ) } diff --git a/validator/src/commands/mod.rs b/validator/src/commands/mod.rs index d21d4a92bc..60ec03984d 100644 --- a/validator/src/commands/mod.rs +++ b/validator/src/commands/mod.rs @@ -5,5 +5,9 @@ pub mod init; pub mod run; pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config { + if let Some(directory) = matches.value_of("directory") { + config = config.with_custom_directory(directory); + } + config } diff --git a/validator/src/commands/run.rs b/validator/src/commands/run.rs index 3b50bbd1cc..8f06e0e2d9 100644 --- a/validator/src/commands/run.rs +++ b/validator/src/commands/run.rs @@ -24,7 +24,7 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { .arg( Arg::with_name("directory") .long("directory") - .help("Address of the directory server the validator is sending presence to") + .help("Address of the directory server the validator is sending presence to and uses for mix mining") .takes_value(true), ) } From bf93739ef50c6fb1a2fa1701f21685e0aa0d2e51 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:36:32 +0000 Subject: [PATCH 178/273] Updated validator config template --- validator/src/config/template.rs | 35 ++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/validator/src/config/template.rs b/validator/src/config/template.rs index 42fc0c2f19..3e8b0c1f82 100644 --- a/validator/src/config/template.rs +++ b/validator/src/config/template.rs @@ -11,15 +11,41 @@ pub(crate) fn config_template() -> &'static str { [validator] # Human readable ID of this particular validator. -id = "{{ provider.id }}" - +id = "{{ validator.id }}" +##### advanced configuration options ##### # nym_home_directory specifies absolute path to the home nym validators directory. # It is expected to use default value and hence .toml file should not redefine this field. nym_root_directory = "{{ validator.nym_root_directory }}" +##### mix mining config options ##### + +[mix_mining] + +# Directory server from which the validator will obtain initial topology. +directory_server = "{{ mix_mining.directory_server }}" + +# The uniform delay every which validator are running their mix-mining procedure. +# The provided value is interpreted as milliseconds. +run_delay = {{ mix_mining.run_delay }} + +# During the mix-mining process, test packets are sent through various network +# paths. This timeout determines waiting period until it is decided that the packet +# did not reach its destination. +# The provided value is interpreted as milliseconds. +resolution_timeout = {{ mix_mining.resolution_timeout }} + +# How many packets should be sent through each path during the mix-mining procedure. +number_of_test_packets = {{ mix_mining.number_of_test_packets }} + + +##### tendermint config options ##### + +[tendermint] + + ##### logging configuration options ##### @@ -34,6 +60,11 @@ nym_root_directory = "{{ validator.nym_root_directory }}" [debug] +# Directory server to which the server will be reporting their presence data. +presence_directory_server = "{{ debug.presence_directory_server }}" + +# Delay between each subsequent presence data being sent. +presence_sending_delay = {{ debug.presence_sending_delay }} "# } From 1fa599dd63aebe64b252f4d029c4363c6db59318 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:51:56 +0000 Subject: [PATCH 179/273] Getting rid of direct Scalar and MontgomeryPoint imports --- mixnode/src/node/mod.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 5bd392e3bb..65da46e7f5 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -4,8 +4,6 @@ use crate::mix_peer::MixPeer; use crate::node; use crate::node::metrics::MetricsReporter; use crypto::encryption; -use curve25519_dalek::montgomery::MontgomeryPoint; -use curve25519_dalek::scalar::Scalar; use futures::channel::mpsc; use futures::lock::Mutex; use futures::SinkExt; @@ -65,14 +63,14 @@ impl ForwardingData { // ProcessingData defines all data required to correctly unwrap sphinx packets struct ProcessingData { - secret_key: Scalar, + secret_key: encryption::PrivateKey, received_metrics_tx: mpsc::Sender<()>, sent_metrics_tx: mpsc::Sender, } impl ProcessingData { fn new( - secret_key: Scalar, + secret_key: encryption::PrivateKey, received_metrics_tx: mpsc::Sender<()>, sent_metrics_tx: mpsc::Sender, ) -> Self { @@ -108,7 +106,7 @@ impl PacketProcessor { let packet = SphinxPacket::from_bytes(packet_data.to_vec())?; let (next_packet, next_hop_address, delay) = - match packet.process(processing_data.secret_key) { + match packet.process(processing_data.secret_key.inner()) { Ok(ProcessedPacket::ProcessedPacketForwardHop(packet, address, delay)) => { (packet, address, delay) } @@ -280,12 +278,9 @@ impl MixNode { rt.block_on(async { let mut listener = tokio::net::TcpListener::bind(self.config.get_listening_address()).await?; - let processing_data = ProcessingData::new( - self.sphinx_keypair.private_key().inner(), - received_tx, - sent_tx, - ) - .add_arc_mutex(); + let processing_data = + ProcessingData::new(*self.sphinx_keypair.private_key(), received_tx, sent_tx) + .add_arc_mutex(); loop { let (socket, _) = listener.accept().await?; From 800c41ce97ff417de73b8533931a54af80844c0d Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:52:17 +0000 Subject: [PATCH 180/273] Removed unused imports --- common/config/src/lib.rs | 2 +- common/healthcheck/src/result.rs | 2 +- common/pemstore/src/pemstore.rs | 1 + mixnode/src/commands/init.rs | 1 - mixnode/src/commands/run.rs | 3 -- mixnode/src/config/persistance/pathfinder.rs | 12 -------- mixnode/src/main.rs | 2 -- mixnode/src/node/metrics.rs | 2 -- mixnode/src/node/mod.rs | 2 -- nym-client/src/commands/run.rs | 2 -- sfw-provider/src/commands/run.rs | 4 --- .../src/config/persistance/pathfinder.rs | 12 -------- sfw-provider/src/main.rs | 2 +- .../src/provider/client_handling/mod.rs | 1 - sfw-provider/src/provider/mix_handling/mod.rs | 1 - sfw-provider/src/provider/mod.rs | 2 +- validator/src/config/mod.rs | 30 +++++++++---------- 17 files changed, 19 insertions(+), 62 deletions(-) diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index edd6a4bdeb..4cf0cadc37 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -1,7 +1,7 @@ use handlebars::Handlebars; use serde::de::DeserializeOwned; use serde::Serialize; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::{fs, io}; pub trait NymConfig: Default + Serialize + DeserializeOwned { diff --git a/common/healthcheck/src/result.rs b/common/healthcheck/src/result.rs index 3a6826544a..e1f8ad969c 100644 --- a/common/healthcheck/src/result.rs +++ b/common/healthcheck/src/result.rs @@ -1,7 +1,7 @@ use crate::path_check::{PathChecker, PathStatus}; use crate::score::NodeScore; use crypto::identity::MixIdentityKeyPair; -use log::{debug, error, info, warn}; +use log::{debug, error, warn}; use rand_os::rand_core::RngCore; use sphinx::route::NodeAddressBytes; use std::collections::HashMap; diff --git a/common/pemstore/src/pemstore.rs b/common/pemstore/src/pemstore.rs index b123c1e2ce..f6059b3a06 100644 --- a/common/pemstore/src/pemstore.rs +++ b/common/pemstore/src/pemstore.rs @@ -10,6 +10,7 @@ use std::io::prelude::*; use std::path::PathBuf; pub struct PemStore { + #[allow(dead_code)] config_dir: PathBuf, private_mix_key_file: PathBuf, public_mix_key_file: PathBuf, diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs index 3ebbc018e8..fdbba7dd2d 100644 --- a/mixnode/src/commands/init.rs +++ b/mixnode/src/commands/init.rs @@ -3,7 +3,6 @@ use crate::config::persistance::pathfinder::MixNodePathfinder; use clap::{App, Arg, ArgMatches}; use config::NymConfig; use crypto::encryption; -use crypto::identity::MixIdentityKeyPair; use pemstore::pemstore::PemStore; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index a058e7b56a..36fdb8af4a 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -1,11 +1,8 @@ use crate::commands::override_config; -use crate::config::persistance::pathfinder::MixNodePathfinder; use crate::config::Config; -use crate::node; use crate::node::MixNode; use clap::{App, Arg, ArgMatches}; use config::NymConfig; -use pemstore::pemstore::PemStore; pub fn command_args<'a, 'b>() -> App<'a, 'b> { App::new("run") diff --git a/mixnode/src/config/persistance/pathfinder.rs b/mixnode/src/config/persistance/pathfinder.rs index d4bfe9da5e..a0a7627bf1 100644 --- a/mixnode/src/config/persistance/pathfinder.rs +++ b/mixnode/src/config/persistance/pathfinder.rs @@ -10,18 +10,6 @@ pub struct MixNodePathfinder { } impl MixNodePathfinder { - pub fn new(id: String) -> Self { - let os_config_dir = dirs::config_dir().unwrap(); // grabs the OS default config dir - let config_dir = os_config_dir.join("nym").join("mixnodes").join(id); - let private_sphinx_key = config_dir.join("private.pem"); - let public_sphinx_key = config_dir.join("public.pem"); - MixNodePathfinder { - config_dir, - private_sphinx_key, - public_sphinx_key, - } - } - pub fn new_from_config(config: &Config) -> Self { MixNodePathfinder { config_dir: config.get_config_file_save_location(), diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index 212d2d6c3c..d073b1385c 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -1,6 +1,4 @@ use clap::{App, ArgMatches}; -use log::*; -use std::process; pub mod built_info; mod commands; diff --git a/mixnode/src/node/metrics.rs b/mixnode/src/node/metrics.rs index c29f7fef26..a7913200ef 100644 --- a/mixnode/src/node/metrics.rs +++ b/mixnode/src/node/metrics.rs @@ -9,8 +9,6 @@ use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; -const METRICS_INTERVAL: u64 = 3; - #[derive(Debug)] pub struct MetricsReporter { received: u64, diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 65da46e7f5..2f6f9dccac 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -1,7 +1,6 @@ use crate::config::persistance::pathfinder::MixNodePathfinder; use crate::config::Config; use crate::mix_peer::MixPeer; -use crate::node; use crate::node::metrics::MetricsReporter; use crypto::encryption; use futures::channel::mpsc; @@ -11,7 +10,6 @@ use log::*; use pemstore::pemstore::PemStore; use sphinx::header::delays::Delay as SphinxDelay; use sphinx::{ProcessedPacket, SphinxPacket}; -use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; use tokio::prelude::*; diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index 7d9eeb3f01..9d2918a204 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -1,10 +1,8 @@ use crate::client::NymClient; use crate::commands::override_config; -use crate::config::persistance::pathfinder::ClientPathfinder; use crate::config::Config; use clap::{App, Arg, ArgMatches}; use config::NymConfig; -use pemstore::pemstore::PemStore; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { App::new("run") diff --git a/sfw-provider/src/commands/run.rs b/sfw-provider/src/commands/run.rs index 60d51328db..cabb7bcfd1 100644 --- a/sfw-provider/src/commands/run.rs +++ b/sfw-provider/src/commands/run.rs @@ -1,12 +1,8 @@ use crate::commands::override_config; use crate::config::Config; -use crate::provider; use crate::provider::ServiceProvider; use clap::{App, Arg, ArgMatches}; use config::NymConfig; -use crypto::identity::{MixIdentityPrivateKey, MixIdentityPublicKey}; -use std::net::ToSocketAddrs; -use std::path::PathBuf; pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { App::new("run") diff --git a/sfw-provider/src/config/persistance/pathfinder.rs b/sfw-provider/src/config/persistance/pathfinder.rs index fd74b03c04..a86a967e4e 100644 --- a/sfw-provider/src/config/persistance/pathfinder.rs +++ b/sfw-provider/src/config/persistance/pathfinder.rs @@ -10,18 +10,6 @@ pub struct ProviderPathfinder { } impl ProviderPathfinder { - pub fn new(id: String) -> Self { - let os_config_dir = dirs::config_dir().unwrap(); // grabs the OS default config dir - let config_dir = os_config_dir.join("nym").join("mixnodes").join(id); - let private_sphinx_key = config_dir.join("private.pem"); - let public_sphinx_key = config_dir.join("public.pem"); - ProviderPathfinder { - config_dir, - private_sphinx_key, - public_sphinx_key, - } - } - pub fn new_from_config(config: &Config) -> Self { ProviderPathfinder { config_dir: config.get_config_file_save_location(), diff --git a/sfw-provider/src/main.rs b/sfw-provider/src/main.rs index c563f141eb..9f4d755644 100644 --- a/sfw-provider/src/main.rs +++ b/sfw-provider/src/main.rs @@ -1,4 +1,4 @@ -use clap::{App, ArgMatches, SubCommand}; +use clap::{App, ArgMatches}; pub mod built_info; mod commands; diff --git a/sfw-provider/src/provider/client_handling/mod.rs b/sfw-provider/src/provider/client_handling/mod.rs index 7cae0b4894..3890316548 100644 --- a/sfw-provider/src/provider/client_handling/mod.rs +++ b/sfw-provider/src/provider/client_handling/mod.rs @@ -1,7 +1,6 @@ use crate::provider::storage::{ClientStorage, StoreError}; use crate::provider::ClientLedger; use crypto::encryption; -use crypto::identity::MixIdentityPrivateKey; use futures::lock::Mutex as FMutex; use hmac::{Hmac, Mac}; use log::*; diff --git a/sfw-provider/src/provider/mix_handling/mod.rs b/sfw-provider/src/provider/mix_handling/mod.rs index aab17f1dda..12bd4a6f7e 100644 --- a/sfw-provider/src/provider/mix_handling/mod.rs +++ b/sfw-provider/src/provider/mix_handling/mod.rs @@ -1,6 +1,5 @@ use crate::provider::storage::StoreData; use crypto::encryption; -use crypto::identity::MixIdentityPrivateKey; use log::{error, warn}; use sphinx::{ProcessedPacket, SphinxPacket}; use std::path::PathBuf; diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 2f709a843e..86b19fe9e0 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -4,7 +4,7 @@ use crate::provider::client_handling::{ClientProcessingData, ClientRequestProces use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData}; use crate::provider::storage::ClientStorage; use crypto::encryption; -use crypto::identity::{MixIdentityPrivateKey, MixIdentityPublicKey}; +use crypto::identity::MixIdentityPublicKey; use directory_client::presence::providers::MixProviderClient; use futures::io::Error; use futures::lock::Mutex as FMutex; diff --git a/validator/src/config/mod.rs b/validator/src/config/mod.rs index 461690a0e1..e23c6f86c0 100644 --- a/validator/src/config/mod.rs +++ b/validator/src/config/mod.rs @@ -1,10 +1,7 @@ use crate::config::template::config_template; use config::NymConfig; -use log::*; use serde::{Deserialize, Serialize}; -use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; -use std::str::FromStr; use std::time; mod template; @@ -73,13 +70,13 @@ impl NymConfig for Config { impl Config { fn default_directory_server() -> String { #[cfg(feature = "qa")] - return "https://qa-directory.nymtech.net".to_string(); + return "https://qa-directory.nymtech.net".to_string(); #[cfg(feature = "local")] - return "http://localhost:8080".to_string(); + return "http://localhost:8080".to_string(); "https://directory.nymtech.net".to_string() } - + pub fn new>(id: S) -> Self { Config::default().with_id(id) } @@ -105,28 +102,31 @@ impl Config { pub fn get_config_file_save_location(&self) -> PathBuf { self.config_directory().join(Self::config_file_name()) } - + pub fn get_mix_mining_directory_server(&self) -> String { self.mix_mining.directory_server.clone() } - + + // dead_code until validator actually sends the presence data + #[allow(dead_code)] pub fn get_presence_directory_server(&self) -> String { self.debug.presence_directory_server.clone() } - + + #[allow(dead_code)] pub fn get_presence_sending_delay(&self) -> time::Duration { time::Duration::from_millis(self.debug.presence_sending_delay) } - + pub fn get_mix_mining_run_delay(&self) -> time::Duration { time::Duration::from_millis(self.mix_mining.run_delay) } - + pub fn get_mix_mining_resolution_timeout(&self) -> time::Duration { time::Duration::from_millis(self.mix_mining.resolution_timeout) } - - pub fn get_mix_mining_number_of_test_packets(&self) -> u64 { + + pub fn get_mix_mining_number_of_test_packets(&self) -> u64 { self.mix_mining.number_of_test_packets } } @@ -214,9 +214,7 @@ pub struct Debug { presence_sending_delay: u64, } -impl Debug { - -} +impl Debug {} impl Default for Debug { fn default() -> Self { From 1f06aa2fb95a1916f3a0f3977b3c18fdc02c22f5 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 5 Feb 2020 16:53:31 +0000 Subject: [PATCH 181/273] ibid. --- sfw-provider/src/provider/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 86b19fe9e0..01489211b1 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -4,7 +4,6 @@ use crate::provider::client_handling::{ClientProcessingData, ClientRequestProces use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData}; use crate::provider::storage::ClientStorage; use crypto::encryption; -use crypto::identity::MixIdentityPublicKey; use directory_client::presence::providers::MixProviderClient; use futures::io::Error; use futures::lock::Mutex as FMutex; From ef4929daccd5e37371d1035570adcb141d08d0e4 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 17 Feb 2020 16:32:46 +0000 Subject: [PATCH 182/273] Fixed banner usage --- validator/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/src/main.rs b/validator/src/main.rs index c47eb5e139..fc21b153c3 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -32,8 +32,8 @@ fn execute(matches: ArgMatches) { } } -fn usage() -> String { - banner() + "usage: --help to see available options.\n\n" +fn usage() -> &'static str { + "usage: --help to see available options.\n\n" } fn banner() -> String { From 15b626bb6f3849b06ff7f34de49bd051b4493512 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 11:24:36 +0000 Subject: [PATCH 183/273] Removed comments regarding keypair for mixnode init --- mixnode/src/commands/init.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs index fdbba7dd2d..b7a6c521c7 100644 --- a/mixnode/src/commands/init.rs +++ b/mixnode/src/commands/init.rs @@ -64,10 +64,7 @@ pub fn execute(matches: &ArgMatches) { config = override_config(config, matches); - // TODO: which one should be used? let sphinx_keys = encryption::KeyPair::new(); - // let alternative_keypair = MixIdentityKeyPair::new(); - let pathfinder = MixNodePathfinder::new_from_config(&config); let pem_store = PemStore::new(pathfinder); pem_store From 16c92bebdf2a45072fd4929cacd6d17665bf0eb6 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 11:37:47 +0000 Subject: [PATCH 184/273] Removed the fake-news comments --- nym-client/src/config/mod.rs | 5 ----- nym-client/src/config/template.rs | 5 ----- 2 files changed, 10 deletions(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 507e9e7dc5..b1d6a8b7b2 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -318,12 +318,10 @@ pub struct Debug { /// The parameter of Poisson distribution determining how long, on average, /// it is going to take for another loop cover traffic message to be sent. - /// If set to a negative value, the loop cover traffic stream will be disabled. /// The provided value is interpreted as milliseconds. loop_cover_traffic_average_delay: u64, /// The uniform delay every which clients are querying the providers for received packets. - /// If set to a negative value, client will never try to fetch their messages. /// The provided value is interpreted as milliseconds. fetch_message_delay: u64, @@ -331,7 +329,6 @@ pub struct Debug { /// it is going to take another 'real traffic stream' message to be sent. /// If no real packets are available and cover traffic is enabled, /// a loop cover message is sent instead in order to preserve the rate. - /// If set to a negative value, client will never try to send real traffic data. /// The provided value is interpreted as milliseconds. message_sending_average_delay: u64, @@ -343,8 +340,6 @@ pub struct Debug { /// The uniform delay every which clients are querying the directory server /// to try to obtain a compatible network topology to send sphinx packets through. - /// If set to a negative value, client will never try to refresh its topology, - /// meaning it will always try to use whatever it obtained on startup. /// The provided value is interpreted as milliseconds. topology_refresh_rate: u64, diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index 78635045c1..561ae802ad 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -71,12 +71,10 @@ average_packet_delay = {{ debug.average_packet_delay }} # The parameter of Poisson distribution determining how long, on average, # it is going to take for another loop cover traffic message to be sent. -# If set to a negative value, the loop cover traffic stream will be disabled. # The provided value is interpreted as milliseconds. loop_cover_traffic_average_delay = {{ debug.loop_cover_traffic_average_delay }} # The uniform delay every which clients are querying the providers for received packets. -# If set to a negative value, client will never try to fetch their messages. # The provided value is interpreted as milliseconds. fetch_message_delay = {{ debug.fetch_message_delay }} @@ -84,7 +82,6 @@ fetch_message_delay = {{ debug.fetch_message_delay }} # it is going to take another 'real traffic stream' message to be sent. # If no real packets are available and cover traffic is enabled, # a loop cover message is sent instead in order to preserve the rate. -# If set to a negative value, client will never try to send real traffic data. # The provided value is interpreted as milliseconds. message_sending_average_delay = {{ debug.message_sending_average_delay }} @@ -96,8 +93,6 @@ rate_compliant_cover_messages_disabled = {{ debug.rate_compliant_cover_messages_ # The uniform delay every which clients are querying the directory server # to try to obtain a compatible network topology to send sphinx packets through. -# If set to a negative value, client will never try to refresh its topology, -# meaning it will always try to use whatever it obtained on startup. # The provided value is interpreted as milliseconds. topology_refresh_rate = {{ debug.topology_refresh_rate }} From 6515666b843b4710998e8a307e5e0804202fed39 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 11:41:27 +0000 Subject: [PATCH 185/273] Removed comments regarding keypair for sfw-provider init --- sfw-provider/src/commands/init.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/sfw-provider/src/commands/init.rs b/sfw-provider/src/commands/init.rs index f3ec9624f2..a488cd691f 100644 --- a/sfw-provider/src/commands/init.rs +++ b/sfw-provider/src/commands/init.rs @@ -93,10 +93,7 @@ pub fn execute(matches: &ArgMatches) { config = override_config(config, matches); - // TODO: which one should be used? let sphinx_keys = encryption::KeyPair::new(); - // let alternative_keypair = MixIdentityKeyPair::new(); - let pathfinder = ProviderPathfinder::new_from_config(&config); let pem_store = PemStore::new(pathfinder); pem_store From f72b456cddfa8b3008c07dc012862e3b97c4181b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 15:18:00 +0000 Subject: [PATCH 186/273] Renamed `poisson::sample_from_duration` to `poisson::sample` --- common/clients/mix-client/src/poisson.rs | 2 +- nym-client/src/client/cover_traffic_stream.rs | 3 +-- nym-client/src/client/real_traffic_stream.rs | 5 ++--- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/common/clients/mix-client/src/poisson.rs b/common/clients/mix-client/src/poisson.rs index e4bc83de7a..14aaf7a9d4 100644 --- a/common/clients/mix-client/src/poisson.rs +++ b/common/clients/mix-client/src/poisson.rs @@ -1,7 +1,7 @@ use rand_distr::{Distribution, Exp}; use std::time; -pub fn sample_from_duration(average_duration: time::Duration) -> time::Duration { +pub fn sample(average_duration: time::Duration) -> time::Duration { // this is our internal code used by our traffic streams // the error is only thrown if average delay is less than 0, which will never happen // so call to unwrap is perfectly safe here diff --git a/nym-client/src/client/cover_traffic_stream.rs b/nym-client/src/client/cover_traffic_stream.rs index 45fa0aced6..ac012a9a07 100644 --- a/nym-client/src/client/cover_traffic_stream.rs +++ b/nym-client/src/client/cover_traffic_stream.rs @@ -16,8 +16,7 @@ pub(crate) async fn start_loop_cover_traffic_stream( info!("Starting loop cover traffic stream"); loop { trace!("next cover message!"); - let delay_duration = - mix_client::poisson::sample_from_duration(average_cover_message_delay_duration); + let delay_duration = mix_client::poisson::sample(average_cover_message_delay_duration); tokio::time::delay_for(delay_duration).await; let read_lock = topology_ctrl_ref.read().await; diff --git a/nym-client/src/client/real_traffic_stream.rs b/nym-client/src/client/real_traffic_stream.rs index f11af86f04..5da904a7fe 100644 --- a/nym-client/src/client/real_traffic_stream.rs +++ b/nym-client/src/client/real_traffic_stream.rs @@ -38,8 +38,7 @@ impl Stream for OutQueueControl { // we know it's time to send a message, so let's prepare delay for the next one // Get the `now` by looking at the current `delay` deadline let now = self.next_delay.deadline(); - let next_poisson_delay = - mix_client::poisson::sample_from_duration(self.average_message_sending_delay); + let next_poisson_delay = mix_client::poisson::sample(self.average_message_sending_delay); // The next interval value is `next_poisson_delay` after the one that just // yielded. @@ -83,7 +82,7 @@ impl OutQueueControl { pub(crate) async fn run_out_queue_control(mut self) { // we should set initial delay only when we actually start the stream - self.next_delay = time::delay_for(mix_client::poisson::sample_from_duration( + self.next_delay = time::delay_for(mix_client::poisson::sample( self.average_message_sending_delay, )); From a6c389b5a6f346014c2137d4644d9d64f60a8144 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 15:19:13 +0000 Subject: [PATCH 187/273] Renamed 'average_delay_duration' to just 'average_delay' --- common/clients/mix-client/src/packet.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/common/clients/mix-client/src/packet.rs b/common/clients/mix-client/src/packet.rs index d7fb8b242a..8fab9589c3 100644 --- a/common/clients/mix-client/src/packet.rs +++ b/common/clients/mix-client/src/packet.rs @@ -43,7 +43,7 @@ pub fn loop_cover_message( our_address: DestinationAddressBytes, surb_id: SURBIdentifier, topology: &T, - average_delay_duration: time::Duration, + average_delay: time::Duration, ) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> { let destination = Destination::new(our_address, surb_id); @@ -51,7 +51,7 @@ pub fn loop_cover_message( destination, LOOP_COVER_MESSAGE_PAYLOAD.to_vec(), topology, - average_delay_duration, + average_delay, ) } @@ -59,7 +59,7 @@ pub fn encapsulate_message( recipient: Destination, message: Vec, topology: &T, - average_delay_duration: time::Duration, + average_delay: time::Duration, ) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> { let mut providers = topology.providers(); if providers.len() == 0 { @@ -70,8 +70,7 @@ pub fn encapsulate_message( let route = topology.route_to(provider)?; - let delays = - sphinx::header::delays::generate_from_average_duration(route.len(), average_delay_duration); + let delays = sphinx::header::delays::generate_from_average_duration(route.len(), average_delay); // build the packet let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays)?; From a62744a1773d2cd9dd0e2adb8323e8e759b7e285 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 15:33:37 +0000 Subject: [PATCH 188/273] Fixed clippy warnings --- common/clients/mix-client/src/packet.rs | 2 +- common/healthcheck/src/path_check.rs | 8 ++++---- common/healthcheck/src/result.rs | 10 +++------- common/healthcheck/src/score.rs | 2 +- nym-client/src/sockets/tcp.rs | 4 ++-- nym-client/src/sockets/ws.rs | 7 ++----- sfw-provider/sfw-provider-requests/src/responses.rs | 2 +- sfw-provider/src/provider/mod.rs | 3 +-- 8 files changed, 15 insertions(+), 23 deletions(-) diff --git a/common/clients/mix-client/src/packet.rs b/common/clients/mix-client/src/packet.rs index 8fab9589c3..5132d4cb92 100644 --- a/common/clients/mix-client/src/packet.rs +++ b/common/clients/mix-client/src/packet.rs @@ -62,7 +62,7 @@ pub fn encapsulate_message( average_delay: time::Duration, ) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> { let mut providers = topology.providers(); - if providers.len() == 0 { + if providers.is_empty() { return Err(SphinxPacketEncapsulationError::NoValidProvidersError); } // unwrap is fine here as we asserted there is at least single provider diff --git a/common/healthcheck/src/path_check.rs b/common/healthcheck/src/path_check.rs index 43f4ef329f..79f2f80537 100644 --- a/common/healthcheck/src/path_check.rs +++ b/common/healthcheck/src/path_check.rs @@ -73,7 +73,7 @@ impl PathChecker { // iteration is used to distinguish packets sent through the same path (as the healthcheck // may try to send say 10 packets through given path) - fn unique_path_key(path: &Vec, check_id: [u8; 16], iteration: u8) -> Vec { + fn unique_path_key(path: &[SphinxNode], check_id: [u8; 16], iteration: u8) -> Vec { check_id .iter() .cloned() @@ -173,8 +173,8 @@ impl PathChecker { self.update_path_statuses(provider_messages); } - pub(crate) async fn send_test_packet(&mut self, path: &Vec, iteration: u8) { - if path.len() == 0 { + pub(crate) async fn send_test_packet(&mut self, path: &[SphinxNode], iteration: u8) { + if path.is_empty() { warn!("trying to send test packet through an empty path!"); return; } @@ -221,7 +221,7 @@ impl PathChecker { let first_node_client = self .layer_one_clients .entry(first_node_key) - .or_insert(Some(mix_client::MixClient::new())); + .or_insert_with(|| Some(mix_client::MixClient::new())); if first_node_client.is_none() { debug!("we can ignore this path as layer one mix is inaccessible"); diff --git a/common/healthcheck/src/result.rs b/common/healthcheck/src/result.rs index e1f8ad969c..2db51ddc3a 100644 --- a/common/healthcheck/src/result.rs +++ b/common/healthcheck/src/result.rs @@ -16,7 +16,7 @@ impl std::fmt::Display for HealthCheckResult { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { write!(f, "NETWORK HEALTH\n==============\n")?; for score in self.0.iter() { - write!(f, "{}\n", score)? + writeln!(f, "{}", score)? } Ok(()) } @@ -34,12 +34,8 @@ impl HealthCheckResult { let health = mixes .into_iter() - .map(|node| NodeScore::from_mixnode(node)) - .chain( - providers - .into_iter() - .map(|node| NodeScore::from_provider(node)), - ) + .map(NodeScore::from_mixnode) + .chain(providers.into_iter().map(NodeScore::from_provider)) .collect(); HealthCheckResult(health) diff --git a/common/healthcheck/src/score.rs b/common/healthcheck/src/score.rs index 7262d4ee0d..5cfa6cd811 100644 --- a/common/healthcheck/src/score.rs +++ b/common/healthcheck/src/score.rs @@ -110,7 +110,7 @@ impl NodeScore { pub_key: NodeAddressBytes::from_base58_string(node.pub_key), addresses: vec![node.mixnet_listener, node.client_listener], version: node.version, - layer: format!("provider"), + layer: "provider".to_string(), packets_sent: 0, packets_received: 0, } diff --git a/nym-client/src/sockets/tcp.rs b/nym-client/src/sockets/tcp.rs index 9d9f6d3bd1..28987ad90b 100644 --- a/nym-client/src/sockets/tcp.rs +++ b/nym-client/src/sockets/tcp.rs @@ -125,7 +125,7 @@ impl ClientRequest { Err(e) => { warn!("Failed to fetch client messages - {:?}", e); return ServerResponse::Error { - message: format!("Server failed to receive messages").to_string(), + message: "Server failed to receive messages".to_string(), }; } }; @@ -275,7 +275,7 @@ async fn accept_connection( topology: topology.clone(), msg_input: msg_input.clone(), msg_query: msg_query.clone(), - self_address: self_address.clone(), + self_address, }; match handle_connection(&buf[..n], request_handling_data).await { Ok(res) => res, diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index 73d8df3898..9315ac80a1 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -239,7 +239,7 @@ impl ClientRequest { Err(e) => { warn!("Failed to fetch client messages - {:?}", e); return ServerResponse::Error { - message: format!("Server failed to receive messages").to_string(), + message: "Server failed to receive messages".to_string(), }; } }; @@ -353,10 +353,7 @@ async fn accept_connection( } }; - let mut should_close = false; - if message.is_close() { - should_close = true; - } + let should_close = message.is_close(); if let Err(err) = msg_tx.unbounded_send(message) { error!( diff --git a/sfw-provider/sfw-provider-requests/src/responses.rs b/sfw-provider/sfw-provider-requests/src/responses.rs index 6a181ffedc..4135ed06e4 100644 --- a/sfw-provider/sfw-provider-requests/src/responses.rs +++ b/sfw-provider/sfw-provider-requests/src/responses.rs @@ -72,7 +72,7 @@ impl ProviderResponse for PullResponse { return Err(ProviderResponseError::UnmarshalErrorInvalidLength); } - let mut bytes_copy = bytes.clone(); + let mut bytes_copy = bytes; let num_msgs = read_be_u16(&mut bytes_copy); // can we read all lengths of messages? diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 01489211b1..12673ab126 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -63,7 +63,7 @@ impl ClientLedger { } fn has_token(&self, auth_token: &AuthToken) -> bool { - return self.0.contains_key(auth_token); + self.0.contains_key(auth_token) } fn insert_token( @@ -155,7 +155,6 @@ impl ServiceProvider { ) .unwrap_or_else(|e| { error!("failed to store processed sphinx message; err = {:?}", e); - return; }); } Err(e) => { From 1a8573632f2fca5f1749cac07ef69043b8293ce4 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 15:38:46 +0000 Subject: [PATCH 189/273] 'new_without_default' clippy warnings --- common/crypto/src/encryption/mod.rs | 6 ++++++ common/crypto/src/identity/mod.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/common/crypto/src/encryption/mod.rs b/common/crypto/src/encryption/mod.rs index 4d9d7f6db0..78edbd5c47 100644 --- a/common/crypto/src/encryption/mod.rs +++ b/common/crypto/src/encryption/mod.rs @@ -39,6 +39,12 @@ impl KeyPair { } } +impl Default for KeyPair { + fn default() -> Self { + KeyPair::new() + } +} + impl PemStorableKeyPair for KeyPair { type PrivatePemKey = PrivateKey; type PublicPemKey = PublicKey; diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index 4c1ea3cf75..941f786f67 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -36,6 +36,12 @@ impl MixIdentityKeyPair { } } +impl Default for MixIdentityKeyPair { + fn default() -> Self { + MixIdentityKeyPair::new() + } +} + impl PemStorableKeyPair for MixIdentityKeyPair { type PrivatePemKey = MixIdentityPrivateKey; type PublicPemKey = MixIdentityPublicKey; From bdf1e5bef91f605a711b12da8b8f68d29bb9880d Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 15:47:38 +0000 Subject: [PATCH 190/273] Fixed further linter warnings --- common/clients/directory-client/src/presence/mod.rs | 5 ++--- common/config/src/lib.rs | 12 ++++++------ common/topology/src/lib.rs | 11 ++++------- mixnode/src/mix_peer.rs | 5 ++--- mixnode/src/node/mod.rs | 2 +- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/common/clients/directory-client/src/presence/mod.rs b/common/clients/directory-client/src/presence/mod.rs index b595953758..99d23ca130 100644 --- a/common/clients/directory-client/src/presence/mod.rs +++ b/common/clients/directory-client/src/presence/mod.rs @@ -29,11 +29,10 @@ impl NymTopology for Topology { }; let directory = Client::new(directory_config); - let topology = directory + directory .presence_topology .get() - .expect("Failed to retrieve network topology."); - topology + .expect("Failed to retrieve network topology.") } fn new_from_nodes( diff --git a/common/config/src/lib.rs b/common/config/src/lib.rs index 4cf0cadc37..f5c8c0c032 100644 --- a/common/config/src/lib.rs +++ b/common/config/src/lib.rs @@ -46,19 +46,19 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned { }?; fs::write( - custom_location.unwrap_or(self.config_directory().join(Self::config_file_name())), + custom_location + .unwrap_or_else(|| self.config_directory().join(Self::config_file_name())), templated_config, ) } fn load_from_file(custom_location: Option, id: Option<&str>) -> io::Result { let config_contents = fs::read_to_string( - custom_location.unwrap_or(Self::default_config_directory(id).join("config.toml")), + custom_location + .unwrap_or_else(|| Self::default_config_directory(id).join("config.toml")), )?; - let parsing_result = toml::from_str(&config_contents) - .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)); - - parsing_result + toml::from_str(&config_contents) + .map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err)) } } diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 3067b30b47..5edba93d7d 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -30,7 +30,7 @@ pub trait NymTopology: Sized + std::fmt::Debug + Send + Sync { } highest_layer = max(highest_layer, mix.layer); - let layer_nodes = layered_topology.entry(mix.layer).or_insert(Vec::new()); + let layer_nodes = layered_topology.entry(mix.layer).or_insert_with(Vec::new); layer_nodes.push(mix); } @@ -40,12 +40,12 @@ pub trait NymTopology: Sized + std::fmt::Debug + Send + Sync { if !layered_topology.contains_key(&layer) { missing_layers.push(layer); } - if layered_topology[&layer].len() == 0 { + if layered_topology[&layer].is_empty() { missing_layers.push(layer); } } - if missing_layers.len() > 0 { + if !missing_layers.is_empty() { return Err(NymTopologyError::MissingLayerError(missing_layers)); } @@ -112,10 +112,7 @@ pub trait NymTopology: Sized + std::fmt::Debug + Send + Sync { } fn can_construct_path_through(&self) -> bool { - match self.make_layered_topology() { - Ok(_) => true, - Err(_) => false, - } + self.make_layered_topology().is_ok() } } diff --git a/mixnode/src/mix_peer.rs b/mixnode/src/mix_peer.rs index 8a8579a780..82be36c475 100644 --- a/mixnode/src/mix_peer.rs +++ b/mixnode/src/mix_peer.rs @@ -34,13 +34,12 @@ impl MixPeer { } pub async fn send(&self, bytes: Vec) -> Result<(), Box> { - let next_hop_address = self.connection.clone(); - let mut stream = tokio::net::TcpStream::connect(next_hop_address).await?; + let mut stream = tokio::net::TcpStream::connect(self.connection).await?; stream.write_all(&bytes).await?; Ok(()) } - pub fn to_string(&self) -> String { + pub fn stringify(&self) -> String { self.connection.to_string() } } diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 2f6f9dccac..9244022302 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -135,7 +135,7 @@ impl PacketProcessor { if forwarding_data .sent_metrics_tx - .send(forwarding_data.recipient.to_string()) + .send(forwarding_data.recipient.stringify()) .await .is_err() { From 8229cb2c7484a6b4b9f7f20ae5a4c0e7bbe1ddc6 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 16:01:07 +0000 Subject: [PATCH 191/273] Logging message when binding to socket --- nym-client/src/sockets/tcp.rs | 1 + nym-client/src/sockets/ws.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/nym-client/src/sockets/tcp.rs b/nym-client/src/sockets/tcp.rs index 9d9f6d3bd1..d1c072e178 100644 --- a/nym-client/src/sockets/tcp.rs +++ b/nym-client/src/sockets/tcp.rs @@ -304,6 +304,7 @@ pub async fn start_tcpsocket( topology: TopologyInnerRef, ) -> Result<(), TCPSocketError> { let address = SocketAddr::new("127.0.0.1".parse().unwrap(), listening_port); + info!("Starting tcp socket listener at {:?}", address); let mut listener = tokio::net::TcpListener::bind(address).await?; while let Ok((stream, _)) = listener.accept().await { diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index 73d8df3898..841a0b62ad 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -398,6 +398,7 @@ pub async fn start_websocket( topology: TopologyInnerRef, ) -> Result<(), WebSocketError> { let address = SocketAddr::new("127.0.0.1".parse().unwrap(), listening_port); + info!("Starting websocket listener at {:?}", address); let mut listener = tokio::net::TcpListener::bind(address).await?; while let Ok((stream, _)) = listener.accept().await { From b68689dc4b879643873db392fa971d4ca7dfdc12 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 16:01:39 +0000 Subject: [PATCH 192/273] Using proper release version of tokio-tungstenite rather than pointing to specific commit --- Cargo.lock | 40 ++++++++++++++++++++++-------------- nym-client/Cargo.toml | 5 +---- nym-client/src/sockets/ws.rs | 4 ++-- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29cb23770a..a92233b8ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -878,7 +878,7 @@ dependencies = [ "bytes 0.4.12", "fnv", "futures 0.1.29", - "http", + "http 0.1.21", "indexmap", "log", "slab", @@ -963,6 +963,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" +dependencies = [ + "bytes 0.5.4", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.1.0" @@ -971,7 +982,7 @@ checksum = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" dependencies = [ "bytes 0.4.12", "futures 0.1.29", - "http", + "http 0.1.21", "tokio-buf", ] @@ -1000,7 +1011,7 @@ dependencies = [ "futures 0.1.29", "futures-cpupool", "h2", - "http", + "http 0.1.21", "http-body", "httparse", "iovec", @@ -1066,11 +1077,11 @@ dependencies = [ [[package]] name = "input_buffer" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1b822cc844905551931d6f81608ed5f50a79c1078a4e2b4d42dbc7c1eedfbf" +checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" dependencies = [ - "bytes 0.4.12", + "bytes 0.5.4", ] [[package]] @@ -1437,7 +1448,6 @@ dependencies = [ "tokio 0.2.10", "tokio-tungstenite", "topology", - "tungstenite", ] [[package]] @@ -2024,7 +2034,7 @@ dependencies = [ "encoding_rs", "flate2", "futures 0.1.29", - "http", + "http 0.1.21", "hyper", "hyper-tls", "log", @@ -2562,8 +2572,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.10.0" -source = "git+https://github.com/snapview/tokio-tungstenite?rev=308d9680c0e59dd1e8651659a775c05df937934e#308d9680c0e59dd1e8651659a775c05df937934e" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b8fe88007ebc363512449868d7da4389c9400072a3f666f212c7280082882a" dependencies = [ "futures 0.3.1", "log", @@ -2613,18 +2624,17 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.9.2" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0c2bd5aeb7dcd2bb32e472c8872759308495e5eccc942e929a513cd8d36110" +checksum = "cfea31758bf674f990918962e8e5f07071a3161bd7c4138ed23e416e1ac4264e" dependencies = [ "base64 0.11.0", "byteorder", - "bytes 0.4.12", - "http", + "bytes 0.5.4", + "http 0.2.0", "httparse", "input_buffer", "log", - "native-tls", "rand 0.7.3", "sha-1", "url 2.1.1", diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 4599510abd..60524d2fac 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -25,7 +25,7 @@ reqwest = "0.9.22" serde = { version = "1.0.104", features = ["derive"] } serde_json = "1.0.44" tokio = { version = "0.2", features = ["full"] } -tungstenite = "0.9.2" +tokio-tungstenite = "0.10.1" ## internal addressing = {path = "../common/addressing" } @@ -43,9 +43,6 @@ topology = {path = "../common/topology" } sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } # sphinx = { path = "../../sphinx"} -# putting this explicitly below everything and most likely, the next time we look into it, it will already have a proper release -tokio-tungstenite = { git = "https://github.com/snapview/tokio-tungstenite", rev="308d9680c0e59dd1e8651659a775c05df937934e" } - [build-dependencies] built = "0.3.2" diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index 841a0b62ad..dbc68650e1 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -12,9 +12,9 @@ use sphinx::route::{Destination, DestinationAddressBytes}; use std::convert::TryFrom; use std::io; use std::net::SocketAddr; +use tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode; +use tokio_tungstenite::tungstenite::protocol::{CloseFrame, Message}; use topology::NymTopology; -use tungstenite::protocol::frame::coding::CloseCode; -use tungstenite::protocol::{CloseFrame, Message}; struct Connection { address: SocketAddr, From 0928a5aaeb87d923a8b5c377e5567bd11fd0aa21 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 16:48:53 +0000 Subject: [PATCH 193/273] Making clippy nice and happy --- common/clients/directory-client/src/lib.rs | 2 +- common/healthcheck/src/score.rs | 1 + nym-client/src/sockets/tcp.rs | 3 +-- nym-client/src/sockets/ws.rs | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/common/clients/directory-client/src/lib.rs b/common/clients/directory-client/src/lib.rs index db641b952c..3acc52411d 100644 --- a/common/clients/directory-client/src/lib.rs +++ b/common/clients/directory-client/src/lib.rs @@ -54,7 +54,7 @@ impl DirectoryClient for Client { let presence_mix_nodes_post: PresenceMixNodesPost = PresenceMixNodesPost::new(config.base_url.clone()); let presence_providers_post: PresenceProvidersPost = - PresenceProvidersPost::new(config.base_url.clone()); + PresenceProvidersPost::new(config.base_url); Client { health_check, metrics_mixes, diff --git a/common/healthcheck/src/score.rs b/common/healthcheck/src/score.rs index 5cfa6cd811..9bdbf74bce 100644 --- a/common/healthcheck/src/score.rs +++ b/common/healthcheck/src/score.rs @@ -37,6 +37,7 @@ pub(crate) struct NodeScore { impl Ord for NodeScore { // order by: version, layer, sent, received, pubkey; ignore addresses + #[allow(clippy::comparison_chain)] fn cmp(&self, other: &Self) -> Ordering { if self.typ > other.typ { return Ordering::Greater; diff --git a/nym-client/src/sockets/tcp.rs b/nym-client/src/sockets/tcp.rs index fa6742e5f6..ad6d7ec463 100644 --- a/nym-client/src/sockets/tcp.rs +++ b/nym-client/src/sockets/tcp.rs @@ -101,8 +101,7 @@ impl ClientRequest { "too long message. Sent {} bytes while the maximum is {}", msg.len(), sphinx::constants::MAXIMUM_PLAINTEXT_LENGTH - ) - .to_string(), + ), }; } let dummy_surb = [0; 16]; diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index ff9bc4bbb4..c9ff1ab2be 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -194,8 +194,7 @@ impl ClientRequest { "message too long. Sent {} bytes, but the maximum is {}", message_bytes.len(), sphinx::constants::MAXIMUM_PLAINTEXT_LENGTH - ) - .to_string(), + ), }; } From 285e5dc5ec697e3c4f9b3557ba07e210c62143d2 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 19 Feb 2020 16:49:31 +0000 Subject: [PATCH 194/273] Clippy ignoring new_without_default for MixClient --- common/clients/mix-client/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/common/clients/mix-client/src/lib.rs b/common/clients/mix-client/src/lib.rs index fd883a54a4..2a754034ed 100644 --- a/common/clients/mix-client/src/lib.rs +++ b/common/clients/mix-client/src/lib.rs @@ -9,6 +9,7 @@ pub mod poisson; pub struct MixClient {} impl MixClient { + #[allow(clippy::new_without_default)] pub fn new() -> MixClient { MixClient {} } From d807bcd1eeea767555a5c683c716ff83ca06a984 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 21 Feb 2020 14:29:26 +0000 Subject: [PATCH 195/273] Created the multi-tcp-client crate --- Cargo.lock | 8 ++++++++ Cargo.toml | 1 + common/clients/multi-tcp-client/Cargo.toml | 11 +++++++++++ 3 files changed, 20 insertions(+) create mode 100644 common/clients/multi-tcp-client/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index a92233b8ef..d86f890d7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1359,6 +1359,14 @@ dependencies = [ "serde_json", ] +[[package]] +name = "multi-tcp-client" +version = "0.1.0" +dependencies = [ + "futures 0.3.1", + "tokio 0.2.10", +] + [[package]] name = "native-tls" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index a5ce1313ac..071c877bc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ panic = "abort" members = [ "common/clients/directory-client", "common/clients/mix-client", + "common/clients/multi-tcp-client", "common/clients/provider-client", "common/clients/validator-client", "common/addressing", diff --git a/common/clients/multi-tcp-client/Cargo.toml b/common/clients/multi-tcp-client/Cargo.toml new file mode 100644 index 0000000000..e7bcd83dca --- /dev/null +++ b/common/clients/multi-tcp-client/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "multi-tcp-client" +version = "0.1.0" +authors = ["Jedrzej Stuczynski "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +futures = "0.3" +tokio = { version = "0.2", features = ["full"] } From bfad2168b1b47ece0d27a008ea03de8d3e1caf88 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 21 Feb 2020 14:30:16 +0000 Subject: [PATCH 196/273] Maintaining persistent connection to server (no reconnection yet) --- common/clients/multi-tcp-client/src/lib.rs | 191 +++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 common/clients/multi-tcp-client/src/lib.rs diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs new file mode 100644 index 0000000000..c28cd1919c --- /dev/null +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -0,0 +1,191 @@ +use futures::task::{Context, Poll}; +use futures::{AsyncWrite, AsyncWriteExt}; +use std::collections::HashMap; +use std::io; +use std::net::SocketAddr; +use std::pin::Pin; +use std::str; +use tokio::prelude::*; + +struct ConnectionWriter { + connection: tokio::net::TcpStream, +} + +impl ConnectionWriter { + fn new(conn: tokio::net::TcpStream) -> Self { + ConnectionWriter { connection: conn } + } +} + +impl Drop for ConnectionWriter { + fn drop(&mut self) { + // try to cleanly shutdown connection on going out of scope + if let Err(e) = self.connection.shutdown(std::net::Shutdown::Both) { + eprintln!("Failed to cleanly shutdown the connection - {:?}", e); + } + } +} + +impl AsyncWrite for ConnectionWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + use tokio::io::AsyncWrite; + + let mut read_buf = [0; 1]; + match Pin::new(&mut self.connection).poll_read(cx, &mut read_buf) { + // at least try the obvious check if connection is definitely down + // can't do more than that + Poll::Ready(Ok(n)) if n == 0 => Poll::Ready(Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "trying to write to closed connection", + ))), + _ => Pin::new(&mut self.connection).poll_write(cx, buf), + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + use tokio::io::AsyncWrite; + Pin::new(&mut self.connection).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + use tokio::io::AsyncWrite; + Pin::new(&mut self.connection).poll_shutdown(cx) + } +} + +pub struct Client { + connections_writers: HashMap, +} + +impl Client { + pub async fn new(endpoints: Vec) -> Client { + let mut connections_writers = HashMap::new(); + for endpoint in endpoints { + connections_writers.insert( + endpoint, + ConnectionWriter::new(tokio::net::TcpStream::connect(endpoint).await.unwrap()), + ); + } + + Client { + connections_writers, + } + } + + pub async fn send(&mut self, address: SocketAddr, message: &[u8]) -> io::Result<()> { + println!("sending {:?}", str::from_utf8(message)); + if !self.connections_writers.contains_key(&address) { + return Err(io::Error::new( + io::ErrorKind::AddrNotAvailable, + "address not in the list", + )); + } + + // to optimize later by using channels and separate tokio tasks for each connection handler + // because right now say we want to write to addresses A and B - + // We have to wait until we're done dealing with A before we can do anything with B + if let Err(e) = self + .connections_writers + .get_mut(&address) + .unwrap() + .write_all(&message) + .await + { + println!( + "Failed to write to socket - {:?}. Presumably we need to reconnect!", + e + ); + // TODO: reconnection + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time; + + const CLOSE_MESSAGE: [u8; 3] = [0, 0, 0]; + + struct DummyServer { + received_buf: Vec>, + } + + impl DummyServer { + fn new() -> Self { + DummyServer { + received_buf: Vec::new(), + } + } + + fn get_received(&self) -> Vec> { + self.received_buf.clone() + } + + async fn listen_until(mut self, addr: SocketAddr, close_message: &[u8]) -> Self { + let mut listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + println!("started"); + + let (mut socket, _) = listener.accept().await.unwrap(); + println!("connected"); + loop { + let mut buf = [0u8; 1024]; + match socket.read(&mut buf).await { + Ok(n) if n == 0 => { + println!("Remote connection closed"); + return self; + } + Ok(n) => { + println!("received ({}) - {:?}", n, str::from_utf8(buf[..n].as_ref())); + + if buf[..n].as_ref() == close_message { + println!("closing..."); + socket.shutdown(std::net::Shutdown::Both).unwrap(); + return self; + } else { + self.received_buf.push(buf[..n].to_vec()); + } + } + Err(e) => { + panic!("failed to read from socket; err = {:?}", e); + } + }; + } + } + } + + #[test] + fn server_receives_all_sent_messages_when_up() { + let mut rt = tokio::runtime::Runtime::new().unwrap(); + let addr = "127.0.0.1:5000".parse().unwrap(); + + let messages_to_send = vec![b"foomp1", b"foomp2"]; + let finished_dummy_server_future = + rt.spawn(DummyServer::new().listen_until(addr, CLOSE_MESSAGE.as_ref())); + + let mut c = rt.block_on(Client::new(vec![addr])); + + for msg in &messages_to_send { + rt.block_on(c.send(addr, *msg)).unwrap(); + rt.block_on( + async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }, + ); + } + + rt.block_on(c.send(addr, CLOSE_MESSAGE.as_ref())).unwrap(); + + // the server future should have already been resolved + let received_messages = rt + .block_on(finished_dummy_server_future) + .unwrap() + .get_received(); + + assert_eq!(received_messages, messages_to_send); + } +} From 3f80a00bf1d0370b09ad0a10044d1b79812ddf4b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 21 Feb 2020 14:49:44 +0000 Subject: [PATCH 197/273] Introduced backoff fields to connection writer --- common/clients/multi-tcp-client/src/lib.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index c28cd1919c..7384570788 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -5,15 +5,29 @@ use std::io; use std::net::SocketAddr; use std::pin::Pin; use std::str; +use std::time::Duration; use tokio::prelude::*; struct ConnectionWriter { connection: tokio::net::TcpStream, + + reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, + current_reconnection_backoff: Duration, } impl ConnectionWriter { - fn new(conn: tokio::net::TcpStream) -> Self { - ConnectionWriter { connection: conn } + fn new( + connection: tokio::net::TcpStream, + initial_reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, + ) -> Self { + ConnectionWriter { + connection, + reconnection_backoff: initial_reconnection_backoff, + maximum_reconnection_backoff, + current_reconnection_backoff: initial_reconnection_backoff, + } } } From bd86ef07a9f5b67383881066dae3108778a99305 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 21 Feb 2020 14:49:57 +0000 Subject: [PATCH 198/273] Client is now constructed with a config struct --- common/clients/multi-tcp-client/src/lib.rs | 35 +++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index 7384570788..3e0aef0d8d 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -71,17 +71,41 @@ impl AsyncWrite for ConnectionWriter { } } +pub struct Config { + initial_endpoints: Vec, + initial_reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, +} + +impl Config { + pub fn new( + initial_endpoints: Vec, + initial_reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, + ) -> Self { + Config { + initial_endpoints, + initial_reconnection_backoff, + maximum_reconnection_backoff, + } + } +} + pub struct Client { connections_writers: HashMap, } impl Client { - pub async fn new(endpoints: Vec) -> Client { + pub async fn new(config: Config) -> Client { let mut connections_writers = HashMap::new(); - for endpoint in endpoints { + for endpoint in config.initial_endpoints { connections_writers.insert( endpoint, - ConnectionWriter::new(tokio::net::TcpStream::connect(endpoint).await.unwrap()), + ConnectionWriter::new( + tokio::net::TcpStream::connect(endpoint).await.unwrap(), + config.initial_reconnection_backoff, + config.maximum_reconnection_backoff, + ), ); } @@ -178,12 +202,15 @@ mod tests { fn server_receives_all_sent_messages_when_up() { let mut rt = tokio::runtime::Runtime::new().unwrap(); let addr = "127.0.0.1:5000".parse().unwrap(); + let reconnection_backoff = Duration::from_secs(2); + let client_config = + Config::new(vec![addr], reconnection_backoff, 10 * reconnection_backoff); let messages_to_send = vec![b"foomp1", b"foomp2"]; let finished_dummy_server_future = rt.spawn(DummyServer::new().listen_until(addr, CLOSE_MESSAGE.as_ref())); - let mut c = rt.block_on(Client::new(vec![addr])); + let mut c = rt.block_on(Client::new(client_config)); for msg in &messages_to_send { rt.block_on(c.send(addr, *msg)).unwrap(); From a5c65b27d93f78bc916f50b0c3ea691c62da2afd Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 24 Feb 2020 15:23:29 +0000 Subject: [PATCH 199/273] Experimenting with connection reconnector --- common/clients/multi-tcp-client/src/lib.rs | 71 +++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index 3e0aef0d8d..697acc93fa 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -25,8 +25,35 @@ impl ConnectionWriter { ConnectionWriter { connection, reconnection_backoff: initial_reconnection_backoff, + +struct ConnectionReconnector { + address: SocketAddr, + connection: Pin>>>, + + current_retry_attempt: u32, + maximum_retry_attempts: u32, + + current_backoff_delay: tokio::time::Delay, + maximum_reconnection_backoff: Duration, + + reconnection_backoff: Duration, +} + +impl ConnectionReconnector { + fn new( + address: SocketAddr, + maximum_retry_attempts: u32, + reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, + ) -> Self { + ConnectionReconnector { + address, + connection: Box::pin(tokio::net::TcpStream::connect(address)), + current_backoff_delay: tokio::time::delay_for(Duration::new(0, 0)), // if we can re-establish connection on first try without any backoff that's perfect + current_retry_attempt: 0, maximum_reconnection_backoff, - current_reconnection_backoff: initial_reconnection_backoff, + maximum_retry_attempts, + reconnection_backoff, } } } @@ -36,6 +63,48 @@ impl Drop for ConnectionWriter { // try to cleanly shutdown connection on going out of scope if let Err(e) = self.connection.shutdown(std::net::Shutdown::Both) { eprintln!("Failed to cleanly shutdown the connection - {:?}", e); +impl Future for ConnectionReconnector { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // see if we are still in exponential backoff + if Pin::new(&mut self.current_backoff_delay) + .poll(cx) + .is_pending() + { + return Poll::Pending; + }; + + // see if we managed to resolve the connection yet + match Pin::new(&mut self.connection).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => { + warn!( + "we failed to re-establish connection to {} - {:?}", + self.address, e + ); + self.current_retry_attempt += 1; + + // check if we reached our limit + if self.current_retry_attempt == self.maximum_retry_attempts { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "Reached maximum number of retry attempts", + ))); + } + + // we failed to re-establish connection - continue exponential backoff + let next_delay = std::cmp::min( + self.maximum_reconnection_backoff, + 2_u32.pow(self.current_retry_attempt) * self.reconnection_backoff, + ); + + self.current_backoff_delay + .reset(tokio::time::Instant::now() + next_delay); + + Poll::Pending + } + Poll::Ready(Ok(conn)) => Poll::Ready(Ok(conn)), } } } From f599502eab6000c57444c01c555f554275eda818 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 24 Feb 2020 15:23:52 +0000 Subject: [PATCH 200/273] log dependency required by ibid. --- Cargo.lock | 1 + common/clients/multi-tcp-client/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index d86f890d7d..a27893cff7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1364,6 +1364,7 @@ name = "multi-tcp-client" version = "0.1.0" dependencies = [ "futures 0.3.1", + "log", "tokio 0.2.10", ] diff --git a/common/clients/multi-tcp-client/Cargo.toml b/common/clients/multi-tcp-client/Cargo.toml index e7bcd83dca..4887808519 100644 --- a/common/clients/multi-tcp-client/Cargo.toml +++ b/common/clients/multi-tcp-client/Cargo.toml @@ -8,4 +8,5 @@ edition = "2018" [dependencies] futures = "0.3" +log = "0.4.8" tokio = { version = "0.2", features = ["full"] } From 9b81c2eb685cbd81c1d4e021c8f0e1156d29f507 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 25 Feb 2020 15:54:50 +0000 Subject: [PATCH 201/273] Initial version of the reconnecting tcp client --- .../src/connection_manager/mod.rs | 95 +++++++ .../src/connection_manager/reconnector.rs | 76 ++++++ .../src/connection_manager/writer.rs | 46 ++++ common/clients/multi-tcp-client/src/lib.rs | 247 ++++++------------ common/clients/multi-tcp-client/src/main.rs | 20 ++ 5 files changed, 323 insertions(+), 161 deletions(-) create mode 100644 common/clients/multi-tcp-client/src/connection_manager/mod.rs create mode 100644 common/clients/multi-tcp-client/src/connection_manager/reconnector.rs create mode 100644 common/clients/multi-tcp-client/src/connection_manager/writer.rs create mode 100644 common/clients/multi-tcp-client/src/main.rs diff --git a/common/clients/multi-tcp-client/src/connection_manager/mod.rs b/common/clients/multi-tcp-client/src/connection_manager/mod.rs new file mode 100644 index 0000000000..a3ee712392 --- /dev/null +++ b/common/clients/multi-tcp-client/src/connection_manager/mod.rs @@ -0,0 +1,95 @@ +use crate::connection_manager::reconnector::ConnectionReconnector; +use crate::connection_manager::writer::ConnectionWriter; +use futures::task::Poll; +use futures::AsyncWriteExt; +use log::*; +use std::io; +use std::net::SocketAddr; +use std::time::Duration; + +mod reconnector; +mod writer; + +enum ConnectionState { + Writing(ConnectionWriter), + Reconnecting(ConnectionReconnector), +} + +pub(crate) struct ConnectionManager { + address: SocketAddr, + + maximum_reconnection_backoff: Duration, + reconnection_backoff: Duration, + + state: ConnectionState, +} + +impl ConnectionManager { + pub(crate) async fn new( + address: SocketAddr, + reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, + ) -> Self { + // based on initial connection we will either have a writer or a reconnector + let state = match tokio::net::TcpStream::connect(address).await { + Ok(conn) => ConnectionState::Writing(ConnectionWriter::new(conn)), + Err(e) => { + warn!( + "failed to establish initial connection to {} ({}). Going into reconnection mode", + address, e + ); + ConnectionState::Reconnecting(ConnectionReconnector::new( + address, + reconnection_backoff, + maximum_reconnection_backoff, + )) + } + }; + + ConnectionManager { + address, + maximum_reconnection_backoff, + reconnection_backoff, + state, + } + } + + pub(crate) async fn send(&mut self, msg: &[u8]) -> io::Result<()> { + if let ConnectionState::Reconnecting(conn_reconnector) = &mut self.state { + // do a single poll rather than await for future to completely resolve + // TODO: if we call poll ourselves here, will the Waker still call it itself later on? + let new_connection = match futures::poll!(conn_reconnector) { + Poll::Pending => { + return Err(io::Error::new( + io::ErrorKind::BrokenPipe, + "connection is broken - reconnection is in progress", + )) + } + Poll::Ready(conn) => conn, + }; + + println!("We managed to reconnect!"); + self.state = ConnectionState::Writing(ConnectionWriter::new(new_connection)); + } + + // we must be in writing state if we are here, either by being here from beginning or just + // transitioning from reconnecting + if let ConnectionState::Writing(conn_writer) = &mut self.state { + return match conn_writer.write_all(msg).await { + // if we failed to write to connection we should reconnect + // TODO: is this true? can we fail to write to a connection while it still remains open and valid? + Ok(res) => Ok(res), + Err(e) => { + println!("Creating connection reconnector!"); + self.state = ConnectionState::Reconnecting(ConnectionReconnector::new( + self.address, + self.reconnection_backoff, + self.maximum_reconnection_backoff, + )); + Err(e) + } + }; + }; + unreachable!() + } +} diff --git a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs new file mode 100644 index 0000000000..df8d6c0703 --- /dev/null +++ b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs @@ -0,0 +1,76 @@ +use log::*; +use std::future::Future; +use std::io; +use std::net::SocketAddr; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::time::Duration; + +pub(crate) struct ConnectionReconnector { + address: SocketAddr, + connection: Pin>>>, + + current_retry_attempt: u32, + + current_backoff_delay: tokio::time::Delay, + maximum_reconnection_backoff: Duration, + + reconnection_backoff: Duration, +} + +impl ConnectionReconnector { + pub(crate) fn new( + address: SocketAddr, + reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, + ) -> Self { + ConnectionReconnector { + address, + connection: Box::pin(tokio::net::TcpStream::connect(address)), + current_backoff_delay: tokio::time::delay_for(Duration::new(0, 0)), // if we can re-establish connection on first try without any backoff that's perfect + current_retry_attempt: 0, + maximum_reconnection_backoff, + reconnection_backoff, + } + } +} + +impl Future for ConnectionReconnector { + type Output = tokio::net::TcpStream; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // see if we are still in exponential backoff + if Pin::new(&mut self.current_backoff_delay) + .poll(cx) + .is_pending() + { + return Poll::Pending; + }; + + // see if we managed to resolve the connection yet + match Pin::new(&mut self.connection).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => { + warn!( + "we failed to re-establish connection to {} - {:?} (attempt {})", + self.address, e, self.current_retry_attempt + ); + self.current_retry_attempt += 1; + + // we failed to re-establish connection - continue exponential backoff + let next_delay = std::cmp::min( + self.maximum_reconnection_backoff, + 2_u32.pow(self.current_retry_attempt) * self.reconnection_backoff, + ); + + self.current_backoff_delay + .reset(tokio::time::Instant::now() + next_delay); + + self.connection = Box::pin(tokio::net::TcpStream::connect(self.address)); + + Poll::Pending + } + Poll::Ready(Ok(conn)) => Poll::Ready(conn), + } + } +} diff --git a/common/clients/multi-tcp-client/src/connection_manager/writer.rs b/common/clients/multi-tcp-client/src/connection_manager/writer.rs new file mode 100644 index 0000000000..32d76694f3 --- /dev/null +++ b/common/clients/multi-tcp-client/src/connection_manager/writer.rs @@ -0,0 +1,46 @@ +use futures::task::{Context, Poll}; +use futures::AsyncWrite; +use std::io; +use std::pin::Pin; +use tokio::prelude::*; + +pub(crate) struct ConnectionWriter { + connection: tokio::net::TcpStream, +} + +impl ConnectionWriter { + pub(crate) fn new(connection: tokio::net::TcpStream) -> Self { + ConnectionWriter { connection } + } +} + +impl AsyncWrite for ConnectionWriter { + fn poll_write( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + use tokio::io::AsyncWrite; + + let mut read_buf = [0; 1]; + match Pin::new(&mut self.connection).poll_read(cx, &mut read_buf) { + // at least try the obvious check for if connection is definitely down + // TODO: can we do anything else? + Poll::Ready(Ok(n)) if n == 0 => Poll::Ready(Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "trying to write to closed connection", + ))), + _ => Pin::new(&mut self.connection).poll_write(cx, buf), + } + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + use tokio::io::AsyncWrite; + Pin::new(&mut self.connection).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + use tokio::io::AsyncWrite; + Pin::new(&mut self.connection).poll_shutdown(cx) + } +} diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index 697acc93fa..c5c46bf359 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -1,222 +1,83 @@ -use futures::task::{Context, Poll}; -use futures::{AsyncWrite, AsyncWriteExt}; +use crate::connection_manager::ConnectionManager; use std::collections::HashMap; use std::io; use std::net::SocketAddr; -use std::pin::Pin; use std::str; use std::time::Duration; -use tokio::prelude::*; +use tokio::prelude::*; // this import is actually required for socket.read() call -struct ConnectionWriter { - connection: tokio::net::TcpStream, - - reconnection_backoff: Duration, - maximum_reconnection_backoff: Duration, - current_reconnection_backoff: Duration, -} - -impl ConnectionWriter { - fn new( - connection: tokio::net::TcpStream, - initial_reconnection_backoff: Duration, - maximum_reconnection_backoff: Duration, - ) -> Self { - ConnectionWriter { - connection, - reconnection_backoff: initial_reconnection_backoff, - -struct ConnectionReconnector { - address: SocketAddr, - connection: Pin>>>, - - current_retry_attempt: u32, - maximum_retry_attempts: u32, - - current_backoff_delay: tokio::time::Delay, - maximum_reconnection_backoff: Duration, - - reconnection_backoff: Duration, -} - -impl ConnectionReconnector { - fn new( - address: SocketAddr, - maximum_retry_attempts: u32, - reconnection_backoff: Duration, - maximum_reconnection_backoff: Duration, - ) -> Self { - ConnectionReconnector { - address, - connection: Box::pin(tokio::net::TcpStream::connect(address)), - current_backoff_delay: tokio::time::delay_for(Duration::new(0, 0)), // if we can re-establish connection on first try without any backoff that's perfect - current_retry_attempt: 0, - maximum_reconnection_backoff, - maximum_retry_attempts, - reconnection_backoff, - } - } -} - -impl Drop for ConnectionWriter { - fn drop(&mut self) { - // try to cleanly shutdown connection on going out of scope - if let Err(e) = self.connection.shutdown(std::net::Shutdown::Both) { - eprintln!("Failed to cleanly shutdown the connection - {:?}", e); -impl Future for ConnectionReconnector { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // see if we are still in exponential backoff - if Pin::new(&mut self.current_backoff_delay) - .poll(cx) - .is_pending() - { - return Poll::Pending; - }; - - // see if we managed to resolve the connection yet - match Pin::new(&mut self.connection).poll(cx) { - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => { - warn!( - "we failed to re-establish connection to {} - {:?}", - self.address, e - ); - self.current_retry_attempt += 1; - - // check if we reached our limit - if self.current_retry_attempt == self.maximum_retry_attempts { - return Poll::Ready(Err(io::Error::new( - io::ErrorKind::Other, - "Reached maximum number of retry attempts", - ))); - } - - // we failed to re-establish connection - continue exponential backoff - let next_delay = std::cmp::min( - self.maximum_reconnection_backoff, - 2_u32.pow(self.current_retry_attempt) * self.reconnection_backoff, - ); - - self.current_backoff_delay - .reset(tokio::time::Instant::now() + next_delay); - - Poll::Pending - } - Poll::Ready(Ok(conn)) => Poll::Ready(Ok(conn)), - } - } -} - -impl AsyncWrite for ConnectionWriter { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - use tokio::io::AsyncWrite; - - let mut read_buf = [0; 1]; - match Pin::new(&mut self.connection).poll_read(cx, &mut read_buf) { - // at least try the obvious check if connection is definitely down - // can't do more than that - Poll::Ready(Ok(n)) if n == 0 => Poll::Ready(Err(io::Error::new( - io::ErrorKind::UnexpectedEof, - "trying to write to closed connection", - ))), - _ => Pin::new(&mut self.connection).poll_write(cx, buf), - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - use tokio::io::AsyncWrite; - Pin::new(&mut self.connection).poll_flush(cx) - } - - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - use tokio::io::AsyncWrite; - Pin::new(&mut self.connection).poll_shutdown(cx) - } -} +mod connection_manager; pub struct Config { initial_endpoints: Vec, - initial_reconnection_backoff: Duration, + reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, } impl Config { pub fn new( initial_endpoints: Vec, - initial_reconnection_backoff: Duration, + reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, ) -> Self { Config { initial_endpoints, - initial_reconnection_backoff, + reconnection_backoff, maximum_reconnection_backoff, } } } pub struct Client { - connections_writers: HashMap, + connections_managers: HashMap, } impl Client { pub async fn new(config: Config) -> Client { - let mut connections_writers = HashMap::new(); + let mut connections_managers = HashMap::new(); for endpoint in config.initial_endpoints { - connections_writers.insert( + connections_managers.insert( endpoint, - ConnectionWriter::new( - tokio::net::TcpStream::connect(endpoint).await.unwrap(), - config.initial_reconnection_backoff, + ConnectionManager::new( + endpoint, + config.reconnection_backoff, config.maximum_reconnection_backoff, - ), + ) + .await, ); } Client { - connections_writers, + connections_managers, } } pub async fn send(&mut self, address: SocketAddr, message: &[u8]) -> io::Result<()> { println!("sending {:?}", str::from_utf8(message)); - if !self.connections_writers.contains_key(&address) { + if !self.connections_managers.contains_key(&address) { return Err(io::Error::new( io::ErrorKind::AddrNotAvailable, - "address not in the list", + "address not in the list - dynamic connections not yet supported", )); } + // let (tx, rx) = oneshot::channel(); + // to optimize later by using channels and separate tokio tasks for each connection handler // because right now say we want to write to addresses A and B - // We have to wait until we're done dealing with A before we can do anything with B - if let Err(e) = self - .connections_writers + self.connections_managers .get_mut(&address) .unwrap() - .write_all(&message) + .send(&message) .await - { - println!( - "Failed to write to socket - {:?}. Presumably we need to reconnect!", - e - ); - // TODO: reconnection - } - - Ok(()) } } #[cfg(test)] mod tests { use super::*; - use std::time; + use std::{env, time}; const CLOSE_MESSAGE: [u8; 3] = [0, 0, 0]; @@ -267,8 +128,72 @@ mod tests { } } + #[test] + fn client_reconnects_to_server_after_it_went_down() { + let num_test_threads = env::var("RUST_TEST_THREADS").expect("Number of test threads must be set to 1 to prevent tests interacting with themselves! (RUST_TEST_THREADS=1)"); + assert_eq!(num_test_threads, "1", "Number of test threads must be set to 1 to prevent tests interacting with themselves! (RUST_TEST_THREADS=1)"); + + let mut rt = tokio::runtime::Runtime::new().unwrap(); + let addr = "127.0.0.1:5000".parse().unwrap(); + let reconnection_backoff = Duration::from_secs(1); + let client_config = + Config::new(vec![addr], reconnection_backoff, 10 * reconnection_backoff); + + let messages_to_send = vec![b"foomp1", b"foomp2"]; + let finished_dummy_server_future = + rt.spawn(DummyServer::new().listen_until(addr, CLOSE_MESSAGE.as_ref())); + + let mut c = rt.block_on(Client::new(client_config)); + + for msg in &messages_to_send { + rt.block_on(c.send(addr, *msg)).unwrap(); + rt.block_on( + async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }, + ); + } + + // kill server + rt.block_on(c.send(addr, CLOSE_MESSAGE.as_ref())).unwrap(); + rt.block_on(async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }); + + // try to send - go into reconnection + let post_kill_message = b"new foomp"; + + // we are trying to send to killed server + assert!(rt.block_on(c.send(addr, post_kill_message)).is_err()); + // the server future should have already been resolved + let received_messages = rt + .block_on(finished_dummy_server_future) + .unwrap() + .get_received(); + + assert_eq!(received_messages, messages_to_send); + + let new_server_future = + rt.spawn(DummyServer::new().listen_until(addr, CLOSE_MESSAGE.as_ref())); + + // keep sending after we leave reconnection backoff and reconnect + loop { + if rt.block_on(c.send(addr, post_kill_message)).is_ok() { + break; + } + rt.block_on( + async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }, + ); + } + rt.block_on(async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }); + + // kill the server to ensure it actually got the message + rt.block_on(c.send(addr, CLOSE_MESSAGE.as_ref())).unwrap(); + let new_received_messages = rt.block_on(new_server_future).unwrap().get_received(); + assert_eq!(post_kill_message.to_vec(), new_received_messages[0]); + } + #[test] fn server_receives_all_sent_messages_when_up() { + let num_test_threads = env::var("RUST_TEST_THREADS").expect("Number of test threads must be set to 1 to prevent tests interacting with themselves! (RUST_TEST_THREADS=1)"); + assert_eq!(num_test_threads, "1", "Number of test threads must be set to 1 to prevent tests interacting with themselves! (RUST_TEST_THREADS=1)"); + let mut rt = tokio::runtime::Runtime::new().unwrap(); let addr = "127.0.0.1:5000".parse().unwrap(); let reconnection_backoff = Duration::from_secs(2); diff --git a/common/clients/multi-tcp-client/src/main.rs b/common/clients/multi-tcp-client/src/main.rs new file mode 100644 index 0000000000..669972eae7 --- /dev/null +++ b/common/clients/multi-tcp-client/src/main.rs @@ -0,0 +1,20 @@ +use multi_tcp_client::{Client, Config}; +use std::time; +use std::time::Duration; +use tokio::prelude::*; +use tokio::runtime::Runtime; + +fn main() { + let mut rt = Runtime::new().unwrap(); + let addr = "127.0.0.1:5000".parse().unwrap(); + let reconnection_backoff = Duration::from_secs(1); + + let client_config = Config::new(vec![addr], reconnection_backoff, 10 * reconnection_backoff); + + let mut c = rt.block_on(Client::new(client_config)); + + for _ in 0..50 { + rt.block_on(c.send(addr, b"foomp\n")); + rt.block_on(async move { tokio::time::delay_for(time::Duration::from_millis(250)).await }); + } +} From 04dec97109d0fcf3043f45fd09ea35eb88609ce8 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 25 Feb 2020 15:57:36 +0000 Subject: [PATCH 202/273] Adjusted logging messages --- .../multi-tcp-client/src/connection_manager/mod.rs | 4 ++-- common/clients/multi-tcp-client/src/lib.rs | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/common/clients/multi-tcp-client/src/connection_manager/mod.rs b/common/clients/multi-tcp-client/src/connection_manager/mod.rs index a3ee712392..1ff7cde685 100644 --- a/common/clients/multi-tcp-client/src/connection_manager/mod.rs +++ b/common/clients/multi-tcp-client/src/connection_manager/mod.rs @@ -68,7 +68,7 @@ impl ConnectionManager { Poll::Ready(conn) => conn, }; - println!("We managed to reconnect!"); + debug!("Managed to reconnect to {}!", self.address); self.state = ConnectionState::Writing(ConnectionWriter::new(new_connection)); } @@ -80,7 +80,7 @@ impl ConnectionManager { // TODO: is this true? can we fail to write to a connection while it still remains open and valid? Ok(res) => Ok(res), Err(e) => { - println!("Creating connection reconnector!"); + trace!("Creating connection reconnector!"); self.state = ConnectionState::Reconnecting(ConnectionReconnector::new( self.address, self.reconnection_backoff, diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index c5c46bf359..cb9432f010 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -1,4 +1,5 @@ use crate::connection_manager::ConnectionManager; +use log::*; use std::collections::HashMap; use std::io; use std::net::SocketAddr; @@ -53,7 +54,6 @@ impl Client { } pub async fn send(&mut self, address: SocketAddr, message: &[u8]) -> io::Result<()> { - println!("sending {:?}", str::from_utf8(message)); if !self.connections_managers.contains_key(&address) { return Err(io::Error::new( io::ErrorKind::AddrNotAvailable, @@ -96,24 +96,22 @@ mod tests { self.received_buf.clone() } + // this is only used in tests so slightly higher logging levels are fine async fn listen_until(mut self, addr: SocketAddr, close_message: &[u8]) -> Self { let mut listener = tokio::net::TcpListener::bind(addr).await.unwrap(); - println!("started"); - let (mut socket, _) = listener.accept().await.unwrap(); - println!("connected"); loop { let mut buf = [0u8; 1024]; match socket.read(&mut buf).await { Ok(n) if n == 0 => { - println!("Remote connection closed"); + info!("Remote connection closed"); return self; } Ok(n) => { - println!("received ({}) - {:?}", n, str::from_utf8(buf[..n].as_ref())); + info!("received ({}) - {:?}", n, str::from_utf8(buf[..n].as_ref())); if buf[..n].as_ref() == close_message { - println!("closing..."); + info!("closing..."); socket.shutdown(std::net::Shutdown::Both).unwrap(); return self; } else { From d77f2b20ce8344194a1febe569b3c7ca75932b5c Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 25 Feb 2020 16:01:48 +0000 Subject: [PATCH 203/273] Replaced scary looking Pin>> with LocalBoxFuture<...> --- .../src/connection_manager/reconnector.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs index df8d6c0703..d2343c8234 100644 --- a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs +++ b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs @@ -1,3 +1,4 @@ +use futures::future::LocalBoxFuture; use log::*; use std::future::Future; use std::io; @@ -6,9 +7,9 @@ use std::pin::Pin; use std::task::{Context, Poll}; use std::time::Duration; -pub(crate) struct ConnectionReconnector { +pub(crate) struct ConnectionReconnector<'a> { address: SocketAddr, - connection: Pin>>>, + connection: LocalBoxFuture<'a, io::Result>, current_retry_attempt: u32, @@ -18,7 +19,7 @@ pub(crate) struct ConnectionReconnector { reconnection_backoff: Duration, } -impl ConnectionReconnector { +impl<'a> ConnectionReconnector<'a> { pub(crate) fn new( address: SocketAddr, reconnection_backoff: Duration, @@ -26,7 +27,7 @@ impl ConnectionReconnector { ) -> Self { ConnectionReconnector { address, - connection: Box::pin(tokio::net::TcpStream::connect(address)), + connection: tokio::net::TcpStream::connect(address).boxed_local(), current_backoff_delay: tokio::time::delay_for(Duration::new(0, 0)), // if we can re-establish connection on first try without any backoff that's perfect current_retry_attempt: 0, maximum_reconnection_backoff, @@ -35,7 +36,7 @@ impl ConnectionReconnector { } } -impl Future for ConnectionReconnector { +impl<'a> Future for ConnectionReconnector<'a> { type Output = tokio::net::TcpStream; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -66,7 +67,7 @@ impl Future for ConnectionReconnector { self.current_backoff_delay .reset(tokio::time::Instant::now() + next_delay); - self.connection = Box::pin(tokio::net::TcpStream::connect(self.address)); + self.connection = tokio::net::TcpStream::connect(self.address).boxed_local(); Poll::Pending } From 87bbe9c86479137b82094d2cfbf6678bed5fe118 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 25 Feb 2020 16:04:28 +0000 Subject: [PATCH 204/273] Remaining lifetime changes required by ibid. --- .../multi-tcp-client/src/connection_manager/mod.rs | 12 ++++++------ .../src/connection_manager/reconnector.rs | 3 ++- common/clients/multi-tcp-client/src/lib.rs | 8 ++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/common/clients/multi-tcp-client/src/connection_manager/mod.rs b/common/clients/multi-tcp-client/src/connection_manager/mod.rs index 1ff7cde685..a05cb2fbcf 100644 --- a/common/clients/multi-tcp-client/src/connection_manager/mod.rs +++ b/common/clients/multi-tcp-client/src/connection_manager/mod.rs @@ -10,26 +10,26 @@ use std::time::Duration; mod reconnector; mod writer; -enum ConnectionState { +enum ConnectionState<'a> { Writing(ConnectionWriter), - Reconnecting(ConnectionReconnector), + Reconnecting(ConnectionReconnector<'a>), } -pub(crate) struct ConnectionManager { +pub(crate) struct ConnectionManager<'a> { address: SocketAddr, maximum_reconnection_backoff: Duration, reconnection_backoff: Duration, - state: ConnectionState, + state: ConnectionState<'a>, } -impl ConnectionManager { +impl<'a> ConnectionManager<'a> { pub(crate) async fn new( address: SocketAddr, reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, - ) -> Self { + ) -> ConnectionManager<'a> { // based on initial connection we will either have a writer or a reconnector let state = match tokio::net::TcpStream::connect(address).await { Ok(conn) => ConnectionState::Writing(ConnectionWriter::new(conn)), diff --git a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs index d2343c8234..5017637d10 100644 --- a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs +++ b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs @@ -1,4 +1,5 @@ use futures::future::LocalBoxFuture; +use futures::FutureExt; use log::*; use std::future::Future; use std::io; @@ -24,7 +25,7 @@ impl<'a> ConnectionReconnector<'a> { address: SocketAddr, reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, - ) -> Self { + ) -> ConnectionReconnector<'a> { ConnectionReconnector { address, connection: tokio::net::TcpStream::connect(address).boxed_local(), diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index cb9432f010..38191fee22 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -29,12 +29,12 @@ impl Config { } } -pub struct Client { - connections_managers: HashMap, +pub struct Client<'a> { + connections_managers: HashMap>, } -impl Client { - pub async fn new(config: Config) -> Client { +impl<'a> Client<'a> { + pub async fn new(config: Config) -> Client<'a> { let mut connections_managers = HashMap::new(); for endpoint in config.initial_endpoints { connections_managers.insert( From 732bf7472e91ed54d0d9cdd3a0a8691011c258e8 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 25 Feb 2020 16:05:57 +0000 Subject: [PATCH 205/273] Cleaned some imports by moving them to test module --- common/clients/multi-tcp-client/src/lib.rs | 6 +++--- common/clients/multi-tcp-client/src/main.rs | 20 -------------------- 2 files changed, 3 insertions(+), 23 deletions(-) delete mode 100644 common/clients/multi-tcp-client/src/main.rs diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index 38191fee22..5a460e8e5f 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -1,11 +1,8 @@ use crate::connection_manager::ConnectionManager; -use log::*; use std::collections::HashMap; use std::io; use std::net::SocketAddr; -use std::str; use std::time::Duration; -use tokio::prelude::*; // this import is actually required for socket.read() call mod connection_manager; @@ -77,7 +74,10 @@ impl<'a> Client<'a> { #[cfg(test)] mod tests { use super::*; + use log::*; + use std::str; use std::{env, time}; + use tokio::prelude::*; const CLOSE_MESSAGE: [u8; 3] = [0, 0, 0]; diff --git a/common/clients/multi-tcp-client/src/main.rs b/common/clients/multi-tcp-client/src/main.rs deleted file mode 100644 index 669972eae7..0000000000 --- a/common/clients/multi-tcp-client/src/main.rs +++ /dev/null @@ -1,20 +0,0 @@ -use multi_tcp_client::{Client, Config}; -use std::time; -use std::time::Duration; -use tokio::prelude::*; -use tokio::runtime::Runtime; - -fn main() { - let mut rt = Runtime::new().unwrap(); - let addr = "127.0.0.1:5000".parse().unwrap(); - let reconnection_backoff = Duration::from_secs(1); - - let client_config = Config::new(vec![addr], reconnection_backoff, 10 * reconnection_backoff); - - let mut c = rt.block_on(Client::new(client_config)); - - for _ in 0..50 { - rt.block_on(c.send(addr, b"foomp\n")); - rt.block_on(async move { tokio::time::delay_for(time::Duration::from_millis(250)).await }); - } -} From b27ebc3eed776abb3bf5203d9f49d632735614da Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 25 Feb 2020 16:34:43 +0000 Subject: [PATCH 206/273] Updated travis to run tests in single threaded fashion --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7041ae8d65..06857f4fb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ jobs: allow_failures: - rust: nightly fast_finish: true +env: + - RUST_TEST_THREADS=1 before_script: - rustup component add rustfmt script: From f136f8e75aec321c500bb7d87c30ba0fd462701d Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 26 Feb 2020 11:31:27 +0000 Subject: [PATCH 207/273] Presence reporter responsible for starting own task --- mixnode/src/node/mod.rs | 7 +++---- mixnode/src/node/presence.rs | 8 +++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 9244022302..996c708a22 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -239,6 +239,7 @@ impl MixNode { let mut rt = Runtime::new()?; // Spawn Tokio tasks as necessary for node functionality + pub fn start_presence_notifier(&self) { let notifier_config = presence::NotifierConfig::new( self.config.get_presence_directory_server(), self.config.get_announce_address(), @@ -246,10 +247,8 @@ impl MixNode { self.config.get_layer(), self.config.get_presence_sending_delay(), ); - rt.spawn({ - let presence_notifier = presence::Notifier::new(notifier_config); - presence_notifier.run() - }); + presence::Notifier::new(notifier_config).start(self.runtime.handle()) + } let metrics = MetricsReporter::new().add_arc_mutex(); rt.spawn(MetricsReporter::run_received_metrics_control( diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs index 667b4b0f21..120d6d429b 100644 --- a/mixnode/src/node/presence.rs +++ b/mixnode/src/node/presence.rs @@ -2,8 +2,10 @@ use crate::built_info; use directory_client::presence::mixnodes::MixNodePresence; use directory_client::requests::presence_mixnodes_post::PresenceMixNodesPoster; use directory_client::DirectoryClient; +use futures::task::SpawnExt; use log::{debug, error}; use std::time::Duration; +use tokio::runtime::Handle; pub struct NotifierConfig { directory_server: String, @@ -64,10 +66,14 @@ impl Notifier { } } - pub async fn run(self) { + pub async fn run(&self) { loop { self.notify(); tokio::time::delay_for(self.sending_delay).await; } } + + pub fn start(self, handle: &Handle) { + handle.spawn(async move { self.run() }); + } } From e03fd54739fa923f88d88b96ec779428ee246bbf Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 26 Feb 2020 17:05:18 +0000 Subject: [PATCH 208/273] Updated sphinx version --- Cargo.lock | 2 +- common/clients/mix-client/Cargo.toml | 2 +- common/clients/provider-client/Cargo.toml | 2 +- common/crypto/Cargo.toml | 2 +- common/healthcheck/Cargo.toml | 2 +- common/topology/Cargo.toml | 2 +- mixnode/Cargo.toml | 2 +- nym-client/Cargo.toml | 2 +- sfw-provider/Cargo.toml | 2 +- sfw-provider/sfw-provider-requests/Cargo.toml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a27893cff7..71eeba7ec2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2270,7 +2270,7 @@ dependencies = [ [[package]] name = "sphinx" version = "0.1.0" -source = "git+https://github.com/nymtech/sphinx?rev=5862939c52e4dd76f836856a01787faec32152b4#5862939c52e4dd76f836856a01787faec32152b4" +source = "git+https://github.com/nymtech/sphinx?rev=e5d91f87a6a9dbfc192a029e834b39a42768e9ce#e5d91f87a6a9dbfc192a029e834b39a42768e9ce" dependencies = [ "aes-ctr", "arrayref", diff --git a/common/clients/mix-client/Cargo.toml b/common/clients/mix-client/Cargo.toml index 2a029d7632..55e3730471 100644 --- a/common/clients/mix-client/Cargo.toml +++ b/common/clients/mix-client/Cargo.toml @@ -18,6 +18,6 @@ addressing = {path = "../../addressing"} topology = {path = "../../topology"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } # sphinx = { path = "../../../../sphinx"} diff --git a/common/clients/provider-client/Cargo.toml b/common/clients/provider-client/Cargo.toml index 1d3b68c4f2..3713846eba 100644 --- a/common/clients/provider-client/Cargo.toml +++ b/common/clients/provider-client/Cargo.toml @@ -16,4 +16,4 @@ tokio = { version = "0.2", features = ["full"] } sfw-provider-requests = { path = "../../../sfw-provider/sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index be0cb42b31..22818bd791 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -15,4 +15,4 @@ rand = "0.7.2" rand_os = "0.1" ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } \ No newline at end of file +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } \ No newline at end of file diff --git a/common/healthcheck/Cargo.toml b/common/healthcheck/Cargo.toml index 7af12ace4c..31f0ac787f 100644 --- a/common/healthcheck/Cargo.toml +++ b/common/healthcheck/Cargo.toml @@ -27,7 +27,7 @@ sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" } topology = {path = "../topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } # sphinx = { path = "../../../sphinx"} [dev-dependencies] diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index 2c28228bd5..99c377ebb7 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -19,5 +19,5 @@ addressing = {path = "../addressing"} version-checker = {path = "../version-checker" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } # sphinx = { path = "../../../sphinx"} diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index f583498830..b58ee4a113 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -27,7 +27,7 @@ directory-client = { path = "../common/clients/directory-client" } pemstore = {path = "../common/pemstore"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } [build-dependencies] built = "0.3.2" diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 60524d2fac..60ef9899e3 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -40,7 +40,7 @@ sfw-provider-requests = { path = "../sfw-provider/sfw-provider-requests" } topology = {path = "../common/topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } # sphinx = { path = "../../sphinx"} [build-dependencies] diff --git a/sfw-provider/Cargo.toml b/sfw-provider/Cargo.toml index 21ee83a326..994f197b8a 100644 --- a/sfw-provider/Cargo.toml +++ b/sfw-provider/Cargo.toml @@ -31,7 +31,7 @@ pemstore = {path = "../common/pemstore"} sfw-provider-requests = { path = "./sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } [build-dependencies] built = "0.3.2" diff --git a/sfw-provider/sfw-provider-requests/Cargo.toml b/sfw-provider/sfw-provider-requests/Cargo.toml index 0507581973..2b7bd63c43 100644 --- a/sfw-provider/sfw-provider-requests/Cargo.toml +++ b/sfw-provider/sfw-provider-requests/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] bs58 = "0.3.0" -sphinx = { git = "https://github.com/nymtech/sphinx", rev="5862939c52e4dd76f836856a01787faec32152b4" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } From 3f3dfd404c89b2b9a9d5ea0a9aa0d022994c602e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 26 Feb 2020 17:05:36 +0000 Subject: [PATCH 209/273] Removed derivation of copy on private keys --- common/crypto/src/encryption/mod.rs | 3 +-- common/crypto/src/identity/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/common/crypto/src/encryption/mod.rs b/common/crypto/src/encryption/mod.rs index 78edbd5c47..03ad7de8d1 100644 --- a/common/crypto/src/encryption/mod.rs +++ b/common/crypto/src/encryption/mod.rs @@ -62,8 +62,7 @@ impl PemStorableKeyPair for KeyPair { } } -// COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct PrivateKey(pub Scalar); impl PrivateKey { diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index 941f786f67..8f627e14aa 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -98,8 +98,7 @@ impl PemStorableKey for MixIdentityPublicKey { } } -// COPY IS DERIVED ONLY TEMPORARILY UNTIL https://github.com/nymtech/nym/issues/47 is fixed -#[derive(Debug, Clone, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct MixIdentityPrivateKey(pub encryption::PrivateKey); impl<'a> From<&'a MixIdentityPrivateKey> for MixIdentityPublicKey { From 2eec9dc8a17d5eae4f2e6c5efdd1faef5cfb7b47 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 26 Feb 2020 17:06:17 +0000 Subject: [PATCH 210/273] Required provider adjustments due to previous changes --- sfw-provider/src/provider/mix_handling/mod.rs | 2 +- sfw-provider/src/provider/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sfw-provider/src/provider/mix_handling/mod.rs b/sfw-provider/src/provider/mix_handling/mod.rs index 12bd4a6f7e..b50303331c 100644 --- a/sfw-provider/src/provider/mix_handling/mod.rs +++ b/sfw-provider/src/provider/mix_handling/mod.rs @@ -67,7 +67,7 @@ impl MixPacketProcessor { packet_data: &[u8], processing_data: &RwLock, ) -> Result { - let packet = SphinxPacket::from_bytes(packet_data.to_vec())?; + let packet = SphinxPacket::from_bytes(packet_data)?; let read_processing_data = match processing_data.read() { Ok(guard) => guard, Err(e) => { diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 12673ab126..fa6518e118 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -304,7 +304,7 @@ impl ServiceProvider { let mix_future = rt.spawn(ServiceProvider::start_mixnet_listening( self.config.get_mix_listening_address(), - *self.sphinx_keypair.private_key(), + self.sphinx_keypair.private_key().clone(), // CLONE IS DONE TEMPORARILY UNTIL PROVIDER IS REFACTORED THE MIXNODE STYLE self.config.get_clients_inboxes_dir(), self.config.get_stored_messages_filename_length(), )); @@ -312,7 +312,7 @@ impl ServiceProvider { self.config.get_clients_listening_address(), self.config.get_clients_inboxes_dir(), thread_shareable_ledger, - *self.sphinx_keypair.private_key(), + self.sphinx_keypair.private_key().clone(), // CLONE IS DONE TEMPORARILY UNTIL PROVIDER IS REFACTORED THE MIXNODE STYLE self.config.get_message_retrieval_limit(), )); // Spawn the root task From d808c9dab4e919eff3ab8694d81638e48f64d18e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 26 Feb 2020 17:06:47 +0000 Subject: [PATCH 211/273] changed presence notifier to return JoinHandle --- mixnode/src/node/presence.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/mixnode/src/node/presence.rs b/mixnode/src/node/presence.rs index 120d6d429b..bc851641a4 100644 --- a/mixnode/src/node/presence.rs +++ b/mixnode/src/node/presence.rs @@ -2,10 +2,10 @@ use crate::built_info; use directory_client::presence::mixnodes::MixNodePresence; use directory_client::requests::presence_mixnodes_post::PresenceMixNodesPoster; use directory_client::DirectoryClient; -use futures::task::SpawnExt; use log::{debug, error}; use std::time::Duration; use tokio::runtime::Handle; +use tokio::task::JoinHandle; pub struct NotifierConfig { directory_server: String, @@ -59,21 +59,19 @@ impl Notifier { } } - pub fn notify(&self) { + fn notify(&self) { match self.net_client.presence_mix_nodes_post.post(&self.presence) { Err(err) => error!("failed to send presence - {:?}", err), Ok(_) => debug!("sent presence information"), } } - pub async fn run(&self) { - loop { - self.notify(); - tokio::time::delay_for(self.sending_delay).await; - } - } - - pub fn start(self, handle: &Handle) { - handle.spawn(async move { self.run() }); + pub fn start(self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + loop { + self.notify(); + tokio::time::delay_for(self.sending_delay).await; + } + }) } } From 811e18b36e8377d72a5cb348abf53ac06fd4af4d Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 26 Feb 2020 17:07:18 +0000 Subject: [PATCH 212/273] Refactored how metrics are handled - separate sender, receiver and reporter --- mixnode/src/node/metrics.rs | 212 ++++++++++++++++++++++++++---------- 1 file changed, 156 insertions(+), 56 deletions(-) diff --git a/mixnode/src/node/metrics.rs b/mixnode/src/node/metrics.rs index a7913200ef..3bb5b97c82 100644 --- a/mixnode/src/node/metrics.rs +++ b/mixnode/src/node/metrics.rs @@ -8,58 +8,47 @@ use log::{debug, error}; use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; -#[derive(Debug)] -pub struct MetricsReporter { +pub(crate) enum MetricEvent { + Sent(String), + Received, +} + +#[derive(Debug, Clone)] +struct MixMetrics { + inner: Arc>, +} + +struct MixMetricsInner { received: u64, sent: HashMap, } -impl MetricsReporter { +impl MixMetrics { pub(crate) fn new() -> Self { - MetricsReporter { - received: 0, - sent: HashMap::new(), + MixMetrics { + inner: Arc::new(Mutex::new(MixMetricsInner { + received: 0, + sent: HashMap::new(), + })), } } - pub(crate) fn add_arc_mutex(self) -> Arc> { - Arc::new(Mutex::new(self)) - } - - async fn increment_received_metrics(metrics: Arc>) { - let mut unlocked = metrics.lock().await; + async fn increment_received_metrics(&mut self) { + let mut unlocked = self.inner.lock().await; unlocked.received += 1; } - pub(crate) async fn run_received_metrics_control( - metrics: Arc>, - mut rx: mpsc::Receiver<()>, - ) { - while let Some(_) = rx.next().await { - MetricsReporter::increment_received_metrics(metrics.clone()).await; - } - } - - async fn increment_sent_metrics(metrics: Arc>, sent_to: String) { - let mut unlocked = metrics.lock().await; - let receiver_count = unlocked.sent.entry(sent_to).or_insert(0); + async fn increment_sent_metrics(&mut self, destination: String) { + let mut unlocked = self.inner.lock().await; + let receiver_count = unlocked.sent.entry(destination).or_insert(0); *receiver_count += 1; } - pub(crate) async fn run_sent_metrics_control( - metrics: Arc>, - mut rx: mpsc::Receiver, - ) { - while let Some(sent_metric) = rx.next().await { - MetricsReporter::increment_sent_metrics(metrics.clone(), sent_metric).await; - } - } - - async fn acquire_and_reset_metrics( - metrics: Arc>, - ) -> (u64, HashMap) { - let mut unlocked = metrics.lock().await; + async fn acquire_and_reset_metrics(&mut self) -> (u64, HashMap) { + let mut unlocked = self.inner.lock().await; let received = unlocked.received; let sent = std::mem::replace(&mut unlocked.sent, HashMap::new()); @@ -67,27 +56,138 @@ impl MetricsReporter { (received, sent) } +} - pub(crate) async fn run_metrics_sender( - metrics: Arc>, - cfg: directory_client::Config, - pub_key_str: String, - sending_delay: Duration, - ) { - let directory_client = directory_client::Client::new(cfg); - loop { - tokio::time::delay_for(sending_delay).await; - let (received, sent) = - MetricsReporter::acquire_and_reset_metrics(metrics.clone()).await; +struct MetricsReceiver { + metrics: MixMetrics, + metrics_rx: mpsc::UnboundedReceiver, +} - match directory_client.metrics_post.post(&MixMetric { - pub_key: pub_key_str.clone(), - received, - sent, - }) { - Err(err) => error!("failed to send metrics - {:?}", err), - Ok(_) => debug!("sent metrics information"), - } +impl MetricsReceiver { + fn new(metrics: MixMetrics, metrics_rx: mpsc::UnboundedReceiver) -> Self { + MetricsReceiver { + metrics, + metrics_rx, } } + + fn start(mut self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + while let Some(metrics_data) = self.metrics_rx.next().await { + match metrics_data { + MetricEvent::Received => self.metrics.increment_received_metrics().await, + MetricEvent::Sent(destination) => { + self.metrics.increment_sent_metrics(destination).await + } + } + } + }) + } +} + +struct MetricsSender { + metrics: MixMetrics, + directory_client: directory_client::Client, + pub_key_str: String, + sending_delay: Duration, +} + +impl MetricsSender { + fn new( + metrics: MixMetrics, + directory_server: String, + pub_key_str: String, + sending_delay: Duration, + ) -> Self { + MetricsSender { + metrics, + directory_client: directory_client::Client::new(directory_client::Config::new( + directory_server, + )), + pub_key_str, + sending_delay, + } + } + + fn start(mut self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + loop { + tokio::time::delay_for(self.sending_delay).await; + let (received, sent) = self.metrics.acquire_and_reset_metrics().await; + + match self.directory_client.metrics_post.post(&MixMetric { + pub_key: self.pub_key_str.clone(), + received, + sent, + }) { + Err(err) => error!("failed to send metrics - {:?}", err), + Ok(_) => debug!("sent metrics information"), + } + } + }) + } +} + +#[derive(Clone)] +pub struct MetricsReporter { + metrics_tx: mpsc::UnboundedSender, +} + +impl MetricsReporter { + pub(crate) fn new(metrics_tx: mpsc::UnboundedSender) -> Self { + MetricsReporter { metrics_tx } + } + + pub(crate) fn report_sent(&self, destination: String) { + // in unbounded_send() failed it means that the receiver channel was disconnected + // and hence something weird must have happened without a way of recovering + self.metrics_tx + .unbounded_send(MetricEvent::Sent(destination)) + .unwrap() + } + + pub(crate) fn report_received(&self) { + // in unbounded_send() failed it means that the receiver channel was disconnected + // and hence something weird must have happened without a way of recovering + self.metrics_tx + .unbounded_send(MetricEvent::Received) + .unwrap() + } +} + +// basically an easy single entry point to start all metrics related tasks +pub struct MetricsController { + receiver: MetricsReceiver, + reporter: MetricsReporter, + sender: MetricsSender, +} + +impl MetricsController { + pub(crate) fn new( + directory_server: String, + pub_key_str: String, + sending_delay: Duration, + ) -> Self { + let (metrics_tx, metrics_rx) = mpsc::unbounded(); + let shared_metrics = MixMetrics::new(); + + MetricsController { + sender: MetricsSender::new( + shared_metrics.clone(), + directory_server, + pub_key_str, + sending_delay, + ), + receiver: MetricsReceiver::new(shared_metrics, metrics_rx), + reporter: MetricsReporter::new(metrics_tx), + } + } + + // reporter is how node is going to be accessing the metrics data + pub(crate) fn start(self, handle: &Handle) -> MetricsReporter { + // TODO: should we do anything with JoinHandle(s) returned by start methods? + self.receiver.start(handle); + self.sender.start(handle); + self.reporter + } } From f297ed7323298a9f9780001e843c6f72eadbd226 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 26 Feb 2020 17:07:45 +0000 Subject: [PATCH 213/273] Refactored mixnode startup and moved a lot of code outside of mod.rs --- mixnode/src/commands/run.rs | 3 +- mixnode/src/node/mod.rs | 276 +++++------------------------------- 2 files changed, 39 insertions(+), 240 deletions(-) diff --git a/mixnode/src/commands/run.rs b/mixnode/src/commands/run.rs index 36fdb8af4a..42baaffe01 100644 --- a/mixnode/src/commands/run.rs +++ b/mixnode/src/commands/run.rs @@ -108,6 +108,5 @@ pub fn execute(matches: &ArgMatches) { config.get_announce_address() ); - let mix = MixNode::new(config); - mix.start().unwrap(); + MixNode::new(config).run(); } diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 996c708a22..93aac6fb96 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -1,167 +1,19 @@ use crate::config::persistance::pathfinder::MixNodePathfinder; use crate::config::Config; -use crate::mix_peer::MixPeer; -use crate::node::metrics::MetricsReporter; +use crate::node::packet_processing::PacketProcessor; use crypto::encryption; -use futures::channel::mpsc; -use futures::lock::Mutex; -use futures::SinkExt; use log::*; use pemstore::pemstore::PemStore; -use sphinx::header::delays::Delay as SphinxDelay; -use sphinx::{ProcessedPacket, SphinxPacket}; -use std::sync::Arc; -use std::time::Duration; -use tokio::prelude::*; use tokio::runtime::Runtime; +mod listener; mod metrics; +pub(crate) mod packet_processing; mod presence; -#[derive(Debug)] -pub enum MixProcessingError { - SphinxRecoveryError, - ReceivedFinalHopError, - SphinxProcessingError, - InvalidHopAddress, -} - -impl From for MixProcessingError { - // for time being just have a single error instance for all possible results of sphinx::ProcessingError - fn from(_: sphinx::ProcessingError) -> Self { - use MixProcessingError::*; - - SphinxRecoveryError - } -} - -struct ForwardingData { - packet: SphinxPacket, - delay: SphinxDelay, - recipient: MixPeer, - sent_metrics_tx: mpsc::Sender, -} - -// TODO: this will need to be changed if MixPeer will live longer than our Forwarding Data -impl ForwardingData { - fn new( - packet: SphinxPacket, - delay: SphinxDelay, - recipient: MixPeer, - sent_metrics_tx: mpsc::Sender, - ) -> Self { - ForwardingData { - packet, - delay, - recipient, - sent_metrics_tx, - } - } -} - -// ProcessingData defines all data required to correctly unwrap sphinx packets -struct ProcessingData { - secret_key: encryption::PrivateKey, - received_metrics_tx: mpsc::Sender<()>, - sent_metrics_tx: mpsc::Sender, -} - -impl ProcessingData { - fn new( - secret_key: encryption::PrivateKey, - received_metrics_tx: mpsc::Sender<()>, - sent_metrics_tx: mpsc::Sender, - ) -> Self { - ProcessingData { - secret_key, - received_metrics_tx, - sent_metrics_tx, - } - } - - fn add_arc_mutex(self) -> Arc> { - Arc::new(Mutex::new(self)) - } -} - -struct PacketProcessor; - -impl PacketProcessor { - pub async fn process_sphinx_data_packet( - packet_data: &[u8], - processing_data: Arc>, - ) -> Result { - // we received something resembling a sphinx packet, report it! - let processing_data = processing_data.lock().await; - let mut received_metrics_tx = processing_data.received_metrics_tx.clone(); - - // if unwrap failed it means our metrics reporter died, so we should exit application and - // force restart - if received_metrics_tx.send(()).await.is_err() { - error!("failed to send metrics data to the controller - the underlying thread probably died!"); - std::process::exit(1); - } - - let packet = SphinxPacket::from_bytes(packet_data.to_vec())?; - let (next_packet, next_hop_address, delay) = - match packet.process(processing_data.secret_key.inner()) { - Ok(ProcessedPacket::ProcessedPacketForwardHop(packet, address, delay)) => { - (packet, address, delay) - } - Ok(_) => return Err(MixProcessingError::ReceivedFinalHopError), - Err(e) => { - warn!("Failed to unwrap Sphinx packet: {:?}", e); - return Err(MixProcessingError::SphinxProcessingError); - } - }; - - let next_mix = match MixPeer::new(next_hop_address) { - Ok(next_mix) => next_mix, - Err(_) => return Err(MixProcessingError::InvalidHopAddress), - }; - - let fwd_data = ForwardingData::new( - next_packet, - delay, - next_mix, - processing_data.sent_metrics_tx.clone(), - ); - Ok(fwd_data) - } - - async fn wait_and_forward(mut forwarding_data: ForwardingData) { - let delay_duration = Duration::from_nanos(forwarding_data.delay.get_value()); - tokio::time::delay_for(delay_duration).await; - - if forwarding_data - .sent_metrics_tx - .send(forwarding_data.recipient.stringify()) - .await - .is_err() - { - error!("failed to send metrics data to the controller - the underlying thread probably died!"); - std::process::exit(1); - } - - trace!("RECIPIENT: {:?}", forwarding_data.recipient); - match forwarding_data - .recipient - .send(forwarding_data.packet.to_bytes()) - .await - { - Ok(()) => (), - Err(e) => { - warn!( - "failed to write bytes to next mix peer. err = {:?}", - e.to_string() - ); - } - } - } -} - // the MixNode will live for whole duration of this program pub struct MixNode { + runtime: Runtime, config: Config, sphinx_keypair: encryption::KeyPair, } @@ -182,63 +34,12 @@ impl MixNode { let sphinx_keypair = Self::load_sphinx_keys(&config); MixNode { + runtime: Runtime::new().unwrap(), config, sphinx_keypair, } } - async fn process_socket_connection( - mut socket: tokio::net::TcpStream, - processing_data: Arc>, - ) { - let mut buf = [0u8; sphinx::PACKET_SIZE]; - - // In a loop, read data from the socket and write the data back. - loop { - match socket.read(&mut buf).await { - // socket closed - Ok(n) if n == 0 => { - trace!("Remote connection closed."); - return; - } - Ok(_) => { - let fwd_data = match PacketProcessor::process_sphinx_data_packet( - buf.as_ref(), - processing_data.clone(), - ) - .await - { - Ok(fwd_data) => fwd_data, - Err(e) => { - warn!("failed to process sphinx packet: {:?}", e); - return; - } - }; - PacketProcessor::wait_and_forward(fwd_data).await; - } - Err(e) => { - warn!("failed to read from socket; err = {:?}", e); - return; - } - }; - - // Write the some data back - if let Err(e) = socket.write_all(b"foomp").await { - warn!("failed to write reply to socket; err = {:?}", e); - return; - } - } - } - - pub fn start(&self) -> Result<(), Box> { - // Set up channels - let (received_tx, received_rx) = mpsc::channel(1024); - let (sent_tx, sent_rx) = mpsc::channel(1024); - - // Create the runtime, probably later move it to MixNode itself? - let mut rt = Runtime::new()?; - - // Spawn Tokio tasks as necessary for node functionality pub fn start_presence_notifier(&self) { let notifier_config = presence::NotifierConfig::new( self.config.get_presence_directory_server(), @@ -247,46 +48,45 @@ impl MixNode { self.config.get_layer(), self.config.get_presence_sending_delay(), ); - presence::Notifier::new(notifier_config).start(self.runtime.handle()) + presence::Notifier::new(notifier_config).start(self.runtime.handle()); } - let metrics = MetricsReporter::new().add_arc_mutex(); - rt.spawn(MetricsReporter::run_received_metrics_control( - metrics.clone(), - received_rx, - )); - rt.spawn(MetricsReporter::run_sent_metrics_control( - metrics.clone(), - sent_rx, - )); - - let directory_cfg = directory_client::Config { - base_url: self.config.get_metrics_directory_server(), - }; - - rt.spawn(MetricsReporter::run_metrics_sender( - metrics, - directory_cfg, + pub fn start_metrics_reporter(&self) -> metrics::MetricsReporter { + metrics::MetricsController::new( + self.config.get_metrics_directory_server(), self.sphinx_keypair.public_key().to_base58_string(), self.config.get_metrics_sending_delay(), - )); + ) + .start(self.runtime.handle()) + } - // Spawn the root task - rt.block_on(async { - let mut listener = - tokio::net::TcpListener::bind(self.config.get_listening_address()).await?; - let processing_data = - ProcessingData::new(*self.sphinx_keypair.private_key(), received_tx, sent_tx) - .add_arc_mutex(); + pub fn start_socket_listener(&self, metrics_reporter: metrics::MetricsReporter) { + // this is the only location where our private key is going to be copied + // it will be held in memory owned by `MixNode` and inside an Arc of `PacketProcessor` + let packet_processor = + PacketProcessor::new(self.sphinx_keypair.private_key().clone(), metrics_reporter); - loop { - let (socket, _) = listener.accept().await?; + listener::run_socket_listener( + self.runtime.handle(), + self.config.get_listening_address(), + packet_processor, + ); + } - let thread_processing_data = processing_data.clone(); - tokio::spawn(async move { - MixNode::process_socket_connection(socket, thread_processing_data).await; - }); - } - }) + pub fn run(&mut self) { + self.start_presence_notifier(); + let metrics_reporter = self.start_metrics_reporter(); + self.start_socket_listener(metrics_reporter); + + if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) { + error!( + "There was an error while capturing SIGINT - {:?}. We will terminate regardless", + e + ); + } + + println!( + "Received SIGINT - the mixnode will terminate now (threads are not YET nicely stopped)" + ); } } From 0e6d0c5bda38f56bc63b033952e2fcd4f57cffe8 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 26 Feb 2020 17:08:06 +0000 Subject: [PATCH 214/273] Beginning of listener and packet processing refactoring --- mixnode/src/node/listener.rs | 87 +++++++++++++++ mixnode/src/node/packet_processing.rs | 146 ++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 mixnode/src/node/listener.rs create mode 100644 mixnode/src/node/packet_processing.rs diff --git a/mixnode/src/node/listener.rs b/mixnode/src/node/listener.rs new file mode 100644 index 0000000000..d0d8be2082 --- /dev/null +++ b/mixnode/src/node/listener.rs @@ -0,0 +1,87 @@ +use crate::node::packet_processing::PacketProcessor; +use log::*; +use std::io; +use std::net::SocketAddr; +use tokio::prelude::*; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; + +async fn process_received_packet( + packet_data: [u8; sphinx::PACKET_SIZE], + processing_data: PacketProcessor, +) { + // if we fail then we fail + processing_data.process_sphinx_packet(packet_data); + + // let fwd_data = match PacketProcessor::process_sphinx_data_packet( + // &packet_data, + // processing_data, + // ) + // .await + // { + // Ok(fwd_data) => fwd_data, + // Err(e) => { + // warn!("failed to process sphinx packet: {:?}", e); + // return; + // } + // }; + // PacketProcessor::wait_and_forward(fwd_data).await; +} + +async fn process_socket_connection( + mut socket: tokio::net::TcpStream, + packet_processor: PacketProcessor, +) { + let mut buf = [0u8; sphinx::PACKET_SIZE]; + loop { + match socket.read(&mut buf).await { + // socket closed + Ok(n) if n == 0 => { + trace!("Remote connection closed."); + return; + } + Ok(n) => { + if n != sphinx::PACKET_SIZE { + warn!("read data of different length than expected sphinx packet size - {} (expected {})", n, sphinx::PACKET_SIZE); + continue; + } + + // we must be able to handle multiple packets from same connection independently + tokio::spawn(process_received_packet( + buf.clone(), + // note: processing_data is relatively cheap (and safe) to clone - + // it contains arc to private key and metrics reporter (which is just + // a single mpsc unbounded sender) + // TODO: channel to tcp client + packet_processor.clone(), + )) + } + Err(e) => { + warn!( + "failed to read from socket. Closing the connection; err = {:?}", + e + ); + return; + } + }; + } +} + +pub(crate) fn run_socket_listener( + handle: &Handle, + addr: SocketAddr, + packet_processor: PacketProcessor, +) -> JoinHandle> { + let handle_clone = handle.clone(); + handle.spawn(async move { + let mut listener = tokio::net::TcpListener::bind(addr).await?; + loop { + let (socket, _) = listener.accept().await?; + + let thread_packet_processor = packet_processor.clone(); + handle_clone.spawn(async move { + process_socket_connection(socket, thread_packet_processor).await; + }); + } + }) +} diff --git a/mixnode/src/node/packet_processing.rs b/mixnode/src/node/packet_processing.rs new file mode 100644 index 0000000000..4080af472a --- /dev/null +++ b/mixnode/src/node/packet_processing.rs @@ -0,0 +1,146 @@ +use crate::node::metrics; +use crypto::encryption; +use log::*; +use sphinx::header::delays::Delay as SphinxDelay; +use sphinx::route::NodeAddressBytes; +use sphinx::{ProcessedPacket, SphinxPacket}; +use std::ops::Deref; +use std::sync::Arc; + +#[derive(Debug)] +pub enum MixProcessingError { + SphinxRecoveryError, + ReceivedFinalHopError, + SphinxProcessingError, + InvalidHopAddress, +} + +impl From for MixProcessingError { + // for time being just have a single error instance for all possible results of sphinx::ProcessingError + fn from(_: sphinx::ProcessingError) -> Self { + use MixProcessingError::*; + + SphinxRecoveryError + } +} + +// +// struct ForwardingData { +// packet: SphinxPacket, +// delay: SphinxDelay, +// recipient: MixPeer, +// sent_metrics_tx: mpsc::Sender, +// } +// +// // TODO: this will need to be changed if MixPeer will live longer than our Forwarding Data +// impl ForwardingData { +// fn new( +// packet: SphinxPacket, +// delay: SphinxDelay, +// recipient: MixPeer, +// sent_metrics_tx: mpsc::Sender, +// ) -> Self { +// ForwardingData { +// packet, +// delay, +// recipient, +// sent_metrics_tx, +// } +// } +// } + +// PacketProcessor contains all data required to correctly unwrap and forward sphinx packets +#[derive(Clone)] +pub struct PacketProcessor { + secret_key: Arc, + metrics_reporter: metrics::MetricsReporter, +} + +impl PacketProcessor { + pub(crate) fn new( + secret_key: encryption::PrivateKey, + metrics_reporter: metrics::MetricsReporter, + ) -> Self { + PacketProcessor { + secret_key: Arc::new(secret_key), + metrics_reporter, + } + } + + // async fn wait_and_forward(mut forwarding_data: ForwardingData) { + // let delay_duration = Duration::from_nanos(forwarding_data.delay.get_value()); + // tokio::time::delay_for(delay_duration).await; + // + // if forwarding_data + // .sent_metrics_tx + // .send(forwarding_data.recipient.stringify()) + // .await + // .is_err() + // { + // error!("failed to send metrics data to the controller - the underlying thread probably died!"); + // std::process::exit(1); + // } + // + // trace!("RECIPIENT: {:?}", forwarding_data.recipient); + // match forwarding_data + // .recipient + // .send(forwarding_data.packet.to_bytes()) + // .await + // { + // Ok(()) => (), + // Err(e) => { + // warn!( + // "failed to write bytes to next mix peer. err = {:?}", + // e.to_string() + // ); + // } + // } + // } + + fn process_forward_hop( + &self, + packet: SphinxPacket, + forward_address: NodeAddressBytes, + delay: SphinxDelay, + ) -> Result<(), MixProcessingError> { + unimplemented!() + // let next_mix = match MixPeer::new(next_hop_address) { + // Ok(next_mix) => next_mix, + // Err(_) => return Err(MixProcessingError::InvalidHopAddress), + // }; + // + // let fwd_data = ForwardingData::new( + // next_packet, + // delay, + // next_mix, + // processing_data.sent_metrics_tx.clone(), + // ); + // Ok(fwd_data) + + // TODO: do forwarding here? + } + + pub(crate) fn process_sphinx_packet( + &self, + raw_packet_data: [u8; sphinx::PACKET_SIZE], + ) -> Result<(), MixProcessingError> { + // we received something resembling a sphinx packet, report it! + self.metrics_reporter.report_received(); + + let packet = SphinxPacket::from_bytes(&raw_packet_data)?; + + match packet.process(self.secret_key.deref().inner()) { + Ok(ProcessedPacket::ProcessedPacketForwardHop(packet, address, delay)) => { + self.process_forward_hop(packet, address, delay) + } + Ok(ProcessedPacket::ProcessedPacketFinalHop(_, _, _)) => { + warn!("Received a loop cover message that we haven't implemented yet!"); + Err(MixProcessingError::ReceivedFinalHopError) + } + Err(e) => { + warn!("Failed to unwrap Sphinx packet: {:?}", e); + Err(MixProcessingError::SphinxProcessingError) + } + } + } +} From 8febd5958892a0edc3af8d59bd2ff6ba40ddb03d Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 12:18:38 +0000 Subject: [PATCH 215/273] Changed LocalBoxFuture to BoxFuture in reconnector (adds Send requirement) --- .../src/connection_manager/reconnector.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs index 5017637d10..666e156f99 100644 --- a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs +++ b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs @@ -1,4 +1,4 @@ -use futures::future::LocalBoxFuture; +use futures::future::BoxFuture; use futures::FutureExt; use log::*; use std::future::Future; @@ -10,7 +10,7 @@ use std::time::Duration; pub(crate) struct ConnectionReconnector<'a> { address: SocketAddr, - connection: LocalBoxFuture<'a, io::Result>, + connection: BoxFuture<'a, io::Result>, current_retry_attempt: u32, @@ -28,7 +28,7 @@ impl<'a> ConnectionReconnector<'a> { ) -> ConnectionReconnector<'a> { ConnectionReconnector { address, - connection: tokio::net::TcpStream::connect(address).boxed_local(), + connection: tokio::net::TcpStream::connect(address).boxed(), current_backoff_delay: tokio::time::delay_for(Duration::new(0, 0)), // if we can re-establish connection on first try without any backoff that's perfect current_retry_attempt: 0, maximum_reconnection_backoff, @@ -68,7 +68,7 @@ impl<'a> Future for ConnectionReconnector<'a> { self.current_backoff_delay .reset(tokio::time::Instant::now() + next_delay); - self.connection = tokio::net::TcpStream::connect(self.address).boxed_local(); + self.connection = tokio::net::TcpStream::connect(self.address).boxed(); Poll::Pending } From 1418d068b080c7d02e5eb314d7d6a2b80c55fe7e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 12:18:50 +0000 Subject: [PATCH 216/273] Mixnode packet forwarder --- mixnode/Cargo.toml | 1 + mixnode/src/node/packet_forwarding.rs | 48 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 mixnode/src/node/packet_forwarding.rs diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index b58ee4a113..ec249f8b62 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -24,6 +24,7 @@ addressing = {path = "../common/addressing" } config = {path = "../common/config"} crypto = {path = "../common/crypto"} directory-client = { path = "../common/clients/directory-client" } +multi-tcp-client = { path = "../common/clients/multi-tcp-client" } pemstore = {path = "../common/pemstore"} ## will be moved to proper dependencies once released diff --git a/mixnode/src/node/packet_forwarding.rs b/mixnode/src/node/packet_forwarding.rs new file mode 100644 index 0000000000..15707a7b43 --- /dev/null +++ b/mixnode/src/node/packet_forwarding.rs @@ -0,0 +1,48 @@ +use futures::channel::mpsc; +use futures::StreamExt; +use log::*; +use std::net::SocketAddr; +use std::time::Duration; +use tokio::runtime::Handle; + +pub(crate) struct PacketForwarder<'a> { + tcp_client: multi_tcp_client::Client<'a>, + conn_tx: mpsc::UnboundedSender<(SocketAddr, Vec)>, + conn_rx: mpsc::UnboundedReceiver<(SocketAddr, Vec)>, +} + +impl<'a: 'static> PacketForwarder<'a> { + pub(crate) async fn new( + initial_endpoints: Vec, + reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, + ) -> PacketForwarder<'a> { + let tcp_client_config = multi_tcp_client::Config::new( + initial_endpoints, + reconnection_backoff, + maximum_reconnection_backoff, + ); + + let (conn_tx, conn_rx) = mpsc::unbounded(); + + PacketForwarder { + tcp_client: multi_tcp_client::Client::new(tcp_client_config).await, + conn_tx, + conn_rx, + } + } + + pub(crate) fn start(mut self, handle: &Handle) -> mpsc::UnboundedSender<(SocketAddr, Vec)> { + // TODO: what to do with the lost JoinHandle? + let sender_channel = self.conn_tx.clone(); + handle.spawn(async move { + while let Some((address, packet)) = self.conn_rx.next().await { + match self.tcp_client.send(address, &packet).await { + Err(e) => warn!("Failed to forward packet to {:?} - {:?}", address, e), + Ok(_) => trace!("Forwarded packet to {:?}", address), + } + } + }); + sender_channel + } +} From dfb7c82c310acf7e7f5cd2f95caa0be3425765d6 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 12:19:08 +0000 Subject: [PATCH 217/273] Updated mixnode config with forwarding data --- mixnode/src/config/mod.rs | 24 ++++++++++++++++++++++++ mixnode/src/config/template.rs | 13 ++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 69e0978975..55690e6c23 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -17,6 +17,8 @@ const DEFAULT_LISTENING_PORT: u16 = 1789; // where applicable, the below are defined in milliseconds const DEFAULT_PRESENCE_SENDING_DELAY: u64 = 3000; const DEFAULT_METRICS_SENDING_DELAY: u64 = 3000; +const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: u64 = 10_000; // 10s +const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: u64 = 300_000; // 5min #[derive(Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(deny_unknown_fields)] @@ -208,6 +210,14 @@ impl Config { pub fn get_announce_address(&self) -> String { self.mixnode.announce_address.clone() } + + pub fn get_packet_forwarding_initial_backoff(&self) -> time::Duration { + time::Duration::from_millis(self.debug.packet_forwarding_initial_backoff) + } + + pub fn get_packet_forwarding_maximum_backoff(&self) -> time::Duration { + time::Duration::from_millis(self.debug.packet_forwarding_maximum_backoff) + } } #[derive(Debug, Deserialize, PartialEq, Serialize)] @@ -286,13 +296,25 @@ pub struct Debug { presence_directory_server: String, /// Delay between each subsequent presence data being sent. + /// The provided value is interpreted as milliseconds. presence_sending_delay: u64, /// Directory server to which the server will be reporting their metrics data. metrics_directory_server: String, /// Delay between each subsequent metrics data being sent. + /// The provided value is interpreted as milliseconds. metrics_sending_delay: u64, + + /// Initial value of an exponential backoff to reconnect to dropped TCP connection when + /// forwarding sphinx packets. + /// The provided value is interpreted as milliseconds. + packet_forwarding_initial_backoff: u64, + + /// Maximum value of an exponential backoff to reconnect to dropped TCP connection when + /// forwarding sphinx packets. + /// The provided value is interpreted as milliseconds. + packet_forwarding_maximum_backoff: u64, } impl Debug { @@ -313,6 +335,8 @@ impl Default for Debug { presence_sending_delay: DEFAULT_PRESENCE_SENDING_DELAY, metrics_directory_server: Self::default_directory_server(), metrics_sending_delay: DEFAULT_METRICS_SENDING_DELAY, + packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF, + packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF, } } } diff --git a/mixnode/src/config/template.rs b/mixnode/src/config/template.rs index b150865248..78ec91fa02 100644 --- a/mixnode/src/config/template.rs +++ b/mixnode/src/config/template.rs @@ -58,13 +58,24 @@ nym_root_directory = "{{ mixnode.nym_root_directory }}" presence_directory_server = "{{ debug.presence_directory_server}}" # Delay between each subsequent presence data being sent. +# The provided value is interpreted as milliseconds. presence_sending_delay = {{ debug.presence_sending_delay }} # Directory server to which the server will be reporting their metrics data. metrics_directory_server = "{{ debug.metrics_directory_server }}" # Delay between each subsequent metrics data being sent. +# The provided value is interpreted as milliseconds. metrics_sending_delay = {{ debug.metrics_sending_delay }} - + +# Initial value of an exponential backoff to reconnect to dropped TCP connection when +# forwarding sphinx packets. +# The provided value is interpreted as milliseconds. +packet_forwarding_initial_backoff = {{ debug.packet_forwarding_initial_backoff }} + +# Maximum value of an exponential backoff to reconnect to dropped TCP connection when +# forwarding sphinx packets. +# The provided value is interpreted as milliseconds. +packet_forwarding_maximum_backoff = {{ debug.packet_forwarding_maximum_backoff }} "# } From 594e42793e654570e36e9d79ff4b3c677cf38d25 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 12:19:30 +0000 Subject: [PATCH 218/273] Updated client error message --- nym-client/src/client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 2e379e4aac..e950d806ff 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -69,7 +69,7 @@ impl NymClient { topology_ctrl_ref.read().await.topology.as_ref().unwrap() .providers() .first() - .expect("Could not get a provider from the initial network topology, are you using the right directory server?") + .expect("Could not get a compatible provider from the initial network topology, are you using the right directory server?") .client_listener } From 8f48836fd4ed841821756d991bf864476b1e6501 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 12:19:44 +0000 Subject: [PATCH 219/273] Full sphinx packet processing --- mixnode/src/node/packet_processing.rs | 110 +++++++++----------------- 1 file changed, 37 insertions(+), 73 deletions(-) diff --git a/mixnode/src/node/packet_processing.rs b/mixnode/src/node/packet_processing.rs index 4080af472a..0a8ab92d29 100644 --- a/mixnode/src/node/packet_processing.rs +++ b/mixnode/src/node/packet_processing.rs @@ -1,4 +1,5 @@ use crate::node::metrics; +use addressing::AddressTypeError; use crypto::encryption; use log::*; use sphinx::header::delays::Delay as SphinxDelay; @@ -6,6 +7,8 @@ use sphinx::route::NodeAddressBytes; use sphinx::{ProcessedPacket, SphinxPacket}; use std::ops::Deref; use std::sync::Arc; +use std::time::Duration; +use std::net::SocketAddr; #[derive(Debug)] pub enum MixProcessingError { @@ -15,6 +18,11 @@ pub enum MixProcessingError { InvalidHopAddress, } +pub enum MixProcessingResult { + ForwardHop(SocketAddr, Vec), + LoopMessage, +} + impl From for MixProcessingError { // for time being just have a single error instance for all possible results of sphinx::ProcessingError fn from(_: sphinx::ProcessingError) -> Self { @@ -24,30 +32,14 @@ impl From for MixProcessingError { } } -// -// struct ForwardingData { -// packet: SphinxPacket, -// delay: SphinxDelay, -// recipient: MixPeer, -// sent_metrics_tx: mpsc::Sender, -// } -// -// // TODO: this will need to be changed if MixPeer will live longer than our Forwarding Data -// impl ForwardingData { -// fn new( -// packet: SphinxPacket, -// delay: SphinxDelay, -// recipient: MixPeer, -// sent_metrics_tx: mpsc::Sender, -// ) -> Self { -// ForwardingData { -// packet, -// delay, -// recipient, -// sent_metrics_tx, -// } -// } -// } +impl From for MixProcessingError { + fn from(_: AddressTypeError) -> Self { + use MixProcessingError::*; + + InvalidHopAddress + } +} + // PacketProcessor contains all data required to correctly unwrap and forward sphinx packets #[derive(Clone)] @@ -66,64 +58,33 @@ impl PacketProcessor { metrics_reporter, } } + + pub(crate) fn report_sent(&self, addr: SocketAddr) { + self.metrics_reporter.report_sent(addr.to_string()) + } - // async fn wait_and_forward(mut forwarding_data: ForwardingData) { - // let delay_duration = Duration::from_nanos(forwarding_data.delay.get_value()); - // tokio::time::delay_for(delay_duration).await; - // - // if forwarding_data - // .sent_metrics_tx - // .send(forwarding_data.recipient.stringify()) - // .await - // .is_err() - // { - // error!("failed to send metrics data to the controller - the underlying thread probably died!"); - // std::process::exit(1); - // } - // - // trace!("RECIPIENT: {:?}", forwarding_data.recipient); - // match forwarding_data - // .recipient - // .send(forwarding_data.packet.to_bytes()) - // .await - // { - // Ok(()) => (), - // Err(e) => { - // warn!( - // "failed to write bytes to next mix peer. err = {:?}", - // e.to_string() - // ); - // } - // } - // } - - fn process_forward_hop( + async fn process_forward_hop( &self, packet: SphinxPacket, forward_address: NodeAddressBytes, delay: SphinxDelay, - ) -> Result<(), MixProcessingError> { - unimplemented!() - // let next_mix = match MixPeer::new(next_hop_address) { - // Ok(next_mix) => next_mix, - // Err(_) => return Err(MixProcessingError::InvalidHopAddress), - // }; - // - // let fwd_data = ForwardingData::new( - // next_packet, - // delay, - // next_mix, - // processing_data.sent_metrics_tx.clone(), - // ); - // Ok(fwd_data) + ) -> Result { + let next_hop_address = + addressing::socket_address_from_encoded_bytes(forward_address.to_bytes())?; - // TODO: do forwarding here? + // TODO: change the calls after merge on sphinx PR + let delay_duration = Duration::from_nanos(delay.get_value()); + + // Delay packet for as long as required + tokio::time::delay_for(delay_duration).await; + + Ok(MixProcessingResult::ForwardHop(next_hop_address, packet.to_bytes())) } - pub(crate) fn process_sphinx_packet( + pub(crate) async fn process_sphinx_packet( &self, raw_packet_data: [u8; sphinx::PACKET_SIZE], - ) -> Result<(), MixProcessingError> { + ) -> Result { // we received something resembling a sphinx packet, report it! self.metrics_reporter.report_received(); @@ -131,7 +92,7 @@ impl PacketProcessor { match packet.process(self.secret_key.deref().inner()) { Ok(ProcessedPacket::ProcessedPacketForwardHop(packet, address, delay)) => { - self.process_forward_hop(packet, address, delay) + self.process_forward_hop(packet, address, delay).await } Ok(ProcessedPacket::ProcessedPacketFinalHop(_, _, _)) => { warn!("Received a loop cover message that we haven't implemented yet!"); @@ -144,3 +105,6 @@ impl PacketProcessor { } } } + +// TODO: the test that definitely needs to be written is as follows: +// we are stuck trying to write to mix A, can we still forward just fine to mix B? From c6cceb8d4d039139cb3eae87f346d457fe5ee6e2 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 12:20:00 +0000 Subject: [PATCH 220/273] Starting and using the forwarding channel --- Cargo.lock | 1 + mixnode/src/node/listener.rs | 52 +++++++++++++++++++++++------------- mixnode/src/node/mod.rs | 36 ++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71eeba7ec2..9168cb857f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1475,6 +1475,7 @@ dependencies = [ "dotenv", "futures 0.3.1", "log", + "multi-tcp-client", "pemstore", "pretty_env_logger", "serde", diff --git a/mixnode/src/node/listener.rs b/mixnode/src/node/listener.rs index d0d8be2082..d12e9f4b6d 100644 --- a/mixnode/src/node/listener.rs +++ b/mixnode/src/node/listener.rs @@ -1,4 +1,5 @@ -use crate::node::packet_processing::PacketProcessor; +use crate::node::packet_processing::{MixProcessingResult, PacketProcessor}; +use futures::channel::mpsc; use log::*; use std::io; use std::net::SocketAddr; @@ -9,28 +10,34 @@ use tokio::task::JoinHandle; async fn process_received_packet( packet_data: [u8; sphinx::PACKET_SIZE], processing_data: PacketProcessor, + forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec)>, ) { - // if we fail then we fail - processing_data.process_sphinx_packet(packet_data); - - // let fwd_data = match PacketProcessor::process_sphinx_data_packet( - // &packet_data, - // processing_data, - // ) - // .await - // { - // Ok(fwd_data) => fwd_data, - // Err(e) => { - // warn!("failed to process sphinx packet: {:?}", e); - // return; - // } - // }; - // PacketProcessor::wait_and_forward(fwd_data).await; + // all processing incl. delay was done, the only thing left is to forward it + match processing_data.process_sphinx_packet(packet_data).await { + Err(e) => debug!("We failed to process received sphinx packet - {:?}", e), + Ok(res) => match res { + MixProcessingResult::ForwardHop(hop_address, hop_data) => { + // send our data to tcp client for forwarding. If forwarding fails, then it fails, + // it's not like we can do anything about it + // + // in unbounded_send() failed it means that the receiver channel was disconnected + // and hence something weird must have happened without a way of recovering + forwarding_channel + .unbounded_send((hop_address, hop_data)) + .unwrap(); + processing_data.report_sent(hop_address); + } + MixProcessingResult::LoopMessage => { + warn!("Somehow processed a loop cover message that we haven't implemented yet!") + } + }, + } } async fn process_socket_connection( mut socket: tokio::net::TcpStream, packet_processor: PacketProcessor, + forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec)>, ) { let mut buf = [0u8; sphinx::PACKET_SIZE]; loop { @@ -52,8 +59,8 @@ async fn process_socket_connection( // note: processing_data is relatively cheap (and safe) to clone - // it contains arc to private key and metrics reporter (which is just // a single mpsc unbounded sender) - // TODO: channel to tcp client packet_processor.clone(), + forwarding_channel.clone(), )) } Err(e) => { @@ -71,6 +78,7 @@ pub(crate) fn run_socket_listener( handle: &Handle, addr: SocketAddr, packet_processor: PacketProcessor, + forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec)>, ) -> JoinHandle> { let handle_clone = handle.clone(); handle.spawn(async move { @@ -79,8 +87,14 @@ pub(crate) fn run_socket_listener( let (socket, _) = listener.accept().await?; let thread_packet_processor = packet_processor.clone(); + let forwarding_channel_clone = forwarding_channel.clone(); handle_clone.spawn(async move { - process_socket_connection(socket, thread_packet_processor).await; + process_socket_connection( + socket, + thread_packet_processor, + forwarding_channel_clone, + ) + .await; }); } }) diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 93aac6fb96..74fc8f4d04 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -2,12 +2,15 @@ use crate::config::persistance::pathfinder::MixNodePathfinder; use crate::config::Config; use crate::node::packet_processing::PacketProcessor; use crypto::encryption; +use futures::channel::mpsc; use log::*; use pemstore::pemstore::PemStore; +use std::net::SocketAddr; use tokio::runtime::Runtime; mod listener; mod metrics; +mod packet_forwarding; pub(crate) mod packet_processing; mod presence; @@ -41,6 +44,7 @@ impl MixNode { } pub fn start_presence_notifier(&self) { + info!("Starting presence notifier..."); let notifier_config = presence::NotifierConfig::new( self.config.get_presence_directory_server(), self.config.get_announce_address(), @@ -52,6 +56,7 @@ impl MixNode { } pub fn start_metrics_reporter(&self) -> metrics::MetricsReporter { + info!("Starting metrics reporter..."); metrics::MetricsController::new( self.config.get_metrics_directory_server(), self.sphinx_keypair.public_key().to_base58_string(), @@ -60,7 +65,12 @@ impl MixNode { .start(self.runtime.handle()) } - pub fn start_socket_listener(&self, metrics_reporter: metrics::MetricsReporter) { + pub fn start_socket_listener( + &self, + metrics_reporter: metrics::MetricsReporter, + forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec)>, + ) { + info!("Starting socket listener..."); // this is the only location where our private key is going to be copied // it will be held in memory owned by `MixNode` and inside an Arc of `PacketProcessor` let packet_processor = @@ -70,13 +80,33 @@ impl MixNode { self.runtime.handle(), self.config.get_listening_address(), packet_processor, + forwarding_channel, ); } + pub fn start_packet_forwarder(&mut self) -> mpsc::UnboundedSender<(SocketAddr, Vec)> { + info!("Starting packet forwarder..."); + // only temporary for test sake, will be replaced with dynamic list extremely soon + let initial_addresses = vec![ + "127.0.0.1:4000".parse().unwrap(), + "127.0.0.1:5000".parse().unwrap(), + "127.0.0.1:5001".parse().unwrap(), + "127.0.0.1:5002".parse().unwrap(), + ]; + self.runtime + .block_on(packet_forwarding::PacketForwarder::new( + initial_addresses, + self.config.get_packet_forwarding_initial_backoff(), + self.config.get_packet_forwarding_maximum_backoff(), + )) + .start(self.runtime.handle()) + } + pub fn run(&mut self) { - self.start_presence_notifier(); + let forwarding_channel = self.start_packet_forwarder(); let metrics_reporter = self.start_metrics_reporter(); - self.start_socket_listener(metrics_reporter); + self.start_socket_listener(metrics_reporter, forwarding_channel); + self.start_presence_notifier(); if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) { error!( From 71c465afd544a4fd4696313581134b372805fecd Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 12:49:49 +0000 Subject: [PATCH 221/273] Updated sphinx rev --- common/clients/mix-client/Cargo.toml | 2 +- common/clients/provider-client/Cargo.toml | 2 +- common/crypto/Cargo.toml | 2 +- common/healthcheck/Cargo.toml | 2 +- common/topology/Cargo.toml | 2 +- mixnode/Cargo.toml | 2 +- nym-client/Cargo.toml | 2 +- sfw-provider/Cargo.toml | 2 +- sfw-provider/sfw-provider-requests/Cargo.toml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/common/clients/mix-client/Cargo.toml b/common/clients/mix-client/Cargo.toml index 55e3730471..b269af2d02 100644 --- a/common/clients/mix-client/Cargo.toml +++ b/common/clients/mix-client/Cargo.toml @@ -18,6 +18,6 @@ addressing = {path = "../../addressing"} topology = {path = "../../topology"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } # sphinx = { path = "../../../../sphinx"} diff --git a/common/clients/provider-client/Cargo.toml b/common/clients/provider-client/Cargo.toml index 3713846eba..7897a94c7e 100644 --- a/common/clients/provider-client/Cargo.toml +++ b/common/clients/provider-client/Cargo.toml @@ -16,4 +16,4 @@ tokio = { version = "0.2", features = ["full"] } sfw-provider-requests = { path = "../../../sfw-provider/sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index 22818bd791..7044e14cc9 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -15,4 +15,4 @@ rand = "0.7.2" rand_os = "0.1" ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } \ No newline at end of file +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } \ No newline at end of file diff --git a/common/healthcheck/Cargo.toml b/common/healthcheck/Cargo.toml index 31f0ac787f..0eb6b3c147 100644 --- a/common/healthcheck/Cargo.toml +++ b/common/healthcheck/Cargo.toml @@ -27,7 +27,7 @@ sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" } topology = {path = "../topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } # sphinx = { path = "../../../sphinx"} [dev-dependencies] diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index 99c377ebb7..c3ee8e72e1 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -19,5 +19,5 @@ addressing = {path = "../addressing"} version-checker = {path = "../version-checker" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } # sphinx = { path = "../../../sphinx"} diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index ec249f8b62..277138b2ad 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -28,7 +28,7 @@ multi-tcp-client = { path = "../common/clients/multi-tcp-client" } pemstore = {path = "../common/pemstore"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } [build-dependencies] built = "0.3.2" diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 60ef9899e3..4d32bdba48 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -40,7 +40,7 @@ sfw-provider-requests = { path = "../sfw-provider/sfw-provider-requests" } topology = {path = "../common/topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } # sphinx = { path = "../../sphinx"} [build-dependencies] diff --git a/sfw-provider/Cargo.toml b/sfw-provider/Cargo.toml index 994f197b8a..62b3f15bef 100644 --- a/sfw-provider/Cargo.toml +++ b/sfw-provider/Cargo.toml @@ -31,7 +31,7 @@ pemstore = {path = "../common/pemstore"} sfw-provider-requests = { path = "./sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } [build-dependencies] built = "0.3.2" diff --git a/sfw-provider/sfw-provider-requests/Cargo.toml b/sfw-provider/sfw-provider-requests/Cargo.toml index 2b7bd63c43..9d0cb7e241 100644 --- a/sfw-provider/sfw-provider-requests/Cargo.toml +++ b/sfw-provider/sfw-provider-requests/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] bs58 = "0.3.0" -sphinx = { git = "https://github.com/nymtech/sphinx", rev="e5d91f87a6a9dbfc192a029e834b39a42768e9ce" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } From b489d422a2c12066087e62e3c071bd4947ea885e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 13:07:41 +0000 Subject: [PATCH 222/273] Dynamic list of tcp connections --- common/clients/multi-tcp-client/src/lib.rs | 33 +++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index 5a460e8e5f..6381aa3f8f 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -1,4 +1,5 @@ use crate::connection_manager::ConnectionManager; +use log::*; use std::collections::HashMap; use std::io; use std::net::SocketAddr; @@ -28,16 +29,18 @@ impl Config { pub struct Client<'a> { connections_managers: HashMap>, + maximum_reconnection_backoff: Duration, + reconnection_backoff: Duration, } impl<'a> Client<'a> { pub async fn new(config: Config) -> Client<'a> { let mut connections_managers = HashMap::new(); - for endpoint in config.initial_endpoints { + for initial_endpoint in config.initial_endpoints { connections_managers.insert( - endpoint, + initial_endpoint, ConnectionManager::new( - endpoint, + initial_endpoint, config.reconnection_backoff, config.maximum_reconnection_backoff, ) @@ -47,18 +50,29 @@ impl<'a> Client<'a> { Client { connections_managers, + reconnection_backoff: config.maximum_reconnection_backoff, + maximum_reconnection_backoff: config.reconnection_backoff, } } pub async fn send(&mut self, address: SocketAddr, message: &[u8]) -> io::Result<()> { if !self.connections_managers.contains_key(&address) { - return Err(io::Error::new( - io::ErrorKind::AddrNotAvailable, - "address not in the list - dynamic connections not yet supported", - )); - } + info!( + "There is no existing connection to {:?} - it will be established now", + address + ); - // let (tx, rx) = oneshot::channel(); + // TODO: now we're blocking to establish TCP connection this need to be changed + // so that other connections could progress + let new_manager = ConnectionManager::new( + address, + self.reconnection_backoff, + self.maximum_reconnection_backoff, + ) + .await; + + self.connections_managers.insert(address, new_manager); + } // to optimize later by using channels and separate tokio tasks for each connection handler // because right now say we want to write to addresses A and B - @@ -74,7 +88,6 @@ impl<'a> Client<'a> { #[cfg(test)] mod tests { use super::*; - use log::*; use std::str; use std::{env, time}; use tokio::prelude::*; From 8c6744dd964c1acf14821118f6128a2d025d1b15 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 13:07:55 +0000 Subject: [PATCH 223/273] Mixes starting with empty list of endpoints --- mixnode/src/node/mod.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 74fc8f4d04..35167905df 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -86,13 +86,9 @@ impl MixNode { pub fn start_packet_forwarder(&mut self) -> mpsc::UnboundedSender<(SocketAddr, Vec)> { info!("Starting packet forwarder..."); - // only temporary for test sake, will be replaced with dynamic list extremely soon - let initial_addresses = vec![ - "127.0.0.1:4000".parse().unwrap(), - "127.0.0.1:5000".parse().unwrap(), - "127.0.0.1:5001".parse().unwrap(), - "127.0.0.1:5002".parse().unwrap(), - ]; + + // this can later be replaced with topology information + let initial_addresses = vec![]; self.runtime .block_on(packet_forwarding::PacketForwarder::new( initial_addresses, From c71a1ae71d10c3dca9356fa2c4240800537b4ab8 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 13:08:25 +0000 Subject: [PATCH 224/273] Changes due to updated sphinx --- Cargo.lock | 2 +- common/healthcheck/src/path_check.rs | 2 +- mixnode/src/node/packet_processing.rs | 15 +++++++-------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9168cb857f..1d5206d34b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2271,7 +2271,7 @@ dependencies = [ [[package]] name = "sphinx" version = "0.1.0" -source = "git+https://github.com/nymtech/sphinx?rev=e5d91f87a6a9dbfc192a029e834b39a42768e9ce#e5d91f87a6a9dbfc192a029e834b39a42768e9ce" +source = "git+https://github.com/nymtech/sphinx?rev=4e51ed38a5de6fcb454c8903f1fe5e6b52720c77#4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" dependencies = [ "aes-ctr", "arrayref", diff --git a/common/healthcheck/src/path_check.rs b/common/healthcheck/src/path_check.rs index 79f2f80537..3a64043706 100644 --- a/common/healthcheck/src/path_check.rs +++ b/common/healthcheck/src/path_check.rs @@ -238,7 +238,7 @@ impl PathChecker { // we already checked for 'None' case let first_node_client = first_node_client.as_ref().unwrap(); - let delays: Vec<_> = path.iter().map(|_| Delay::new(0)).collect(); + let delays: Vec<_> = path.iter().map(|_| Delay::new_from_nanos(0)).collect(); // all of the data used to create the packet was created by us let packet = sphinx::SphinxPacket::new( diff --git a/mixnode/src/node/packet_processing.rs b/mixnode/src/node/packet_processing.rs index 0a8ab92d29..ad9eb66730 100644 --- a/mixnode/src/node/packet_processing.rs +++ b/mixnode/src/node/packet_processing.rs @@ -5,10 +5,10 @@ use log::*; use sphinx::header::delays::Delay as SphinxDelay; use sphinx::route::NodeAddressBytes; use sphinx::{ProcessedPacket, SphinxPacket}; +use std::net::SocketAddr; use std::ops::Deref; use std::sync::Arc; use std::time::Duration; -use std::net::SocketAddr; #[derive(Debug)] pub enum MixProcessingError { @@ -40,7 +40,6 @@ impl From for MixProcessingError { } } - // PacketProcessor contains all data required to correctly unwrap and forward sphinx packets #[derive(Clone)] pub struct PacketProcessor { @@ -58,7 +57,7 @@ impl PacketProcessor { metrics_reporter, } } - + pub(crate) fn report_sent(&self, addr: SocketAddr) { self.metrics_reporter.report_sent(addr.to_string()) } @@ -72,13 +71,13 @@ impl PacketProcessor { let next_hop_address = addressing::socket_address_from_encoded_bytes(forward_address.to_bytes())?; - // TODO: change the calls after merge on sphinx PR - let delay_duration = Duration::from_nanos(delay.get_value()); - // Delay packet for as long as required - tokio::time::delay_for(delay_duration).await; + tokio::time::delay_for(delay.to_duration()).await; - Ok(MixProcessingResult::ForwardHop(next_hop_address, packet.to_bytes())) + Ok(MixProcessingResult::ForwardHop( + next_hop_address, + packet.to_bytes(), + )) } pub(crate) async fn process_sphinx_packet( From 511ec78b2ced7785cf816e65af406ff70fa9537f Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 14:22:11 +0000 Subject: [PATCH 225/273] Removed resolved comment --- common/clients/multi-tcp-client/src/connection_manager/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/common/clients/multi-tcp-client/src/connection_manager/mod.rs b/common/clients/multi-tcp-client/src/connection_manager/mod.rs index a05cb2fbcf..a695ca05c9 100644 --- a/common/clients/multi-tcp-client/src/connection_manager/mod.rs +++ b/common/clients/multi-tcp-client/src/connection_manager/mod.rs @@ -57,7 +57,6 @@ impl<'a> ConnectionManager<'a> { pub(crate) async fn send(&mut self, msg: &[u8]) -> io::Result<()> { if let ConnectionState::Reconnecting(conn_reconnector) = &mut self.state { // do a single poll rather than await for future to completely resolve - // TODO: if we call poll ourselves here, will the Waker still call it itself later on? let new_connection = match futures::poll!(conn_reconnector) { Poll::Pending => { return Err(io::Error::new( From 7fd3e28f2a09db378b554787f68bcc1a11ac94c3 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 14:22:36 +0000 Subject: [PATCH 226/273] Renamed reconnection backoff to initial reconnection backoff --- .../src/connection_manager/reconnector.rs | 8 ++++---- common/clients/multi-tcp-client/src/lib.rs | 16 ++++++++-------- mixnode/src/node/packet_forwarding.rs | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs index 666e156f99..7c87113d52 100644 --- a/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs +++ b/common/clients/multi-tcp-client/src/connection_manager/reconnector.rs @@ -17,13 +17,13 @@ pub(crate) struct ConnectionReconnector<'a> { current_backoff_delay: tokio::time::Delay, maximum_reconnection_backoff: Duration, - reconnection_backoff: Duration, + initial_reconnection_backoff: Duration, } impl<'a> ConnectionReconnector<'a> { pub(crate) fn new( address: SocketAddr, - reconnection_backoff: Duration, + initial_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, ) -> ConnectionReconnector<'a> { ConnectionReconnector { @@ -32,7 +32,7 @@ impl<'a> ConnectionReconnector<'a> { current_backoff_delay: tokio::time::delay_for(Duration::new(0, 0)), // if we can re-establish connection on first try without any backoff that's perfect current_retry_attempt: 0, maximum_reconnection_backoff, - reconnection_backoff, + initial_reconnection_backoff, } } } @@ -62,7 +62,7 @@ impl<'a> Future for ConnectionReconnector<'a> { // we failed to re-establish connection - continue exponential backoff let next_delay = std::cmp::min( self.maximum_reconnection_backoff, - 2_u32.pow(self.current_retry_attempt) * self.reconnection_backoff, + 2_u32.pow(self.current_retry_attempt) * self.initial_reconnection_backoff, ); self.current_backoff_delay diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index 6381aa3f8f..c5965f2372 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -9,19 +9,19 @@ mod connection_manager; pub struct Config { initial_endpoints: Vec, - reconnection_backoff: Duration, + initial_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, } impl Config { pub fn new( initial_endpoints: Vec, - reconnection_backoff: Duration, + initial_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, ) -> Self { Config { initial_endpoints, - reconnection_backoff, + initial_reconnection_backoff, maximum_reconnection_backoff, } } @@ -30,7 +30,7 @@ impl Config { pub struct Client<'a> { connections_managers: HashMap>, maximum_reconnection_backoff: Duration, - reconnection_backoff: Duration, + initial_reconnection_backoff: Duration, } impl<'a> Client<'a> { @@ -41,7 +41,7 @@ impl<'a> Client<'a> { initial_endpoint, ConnectionManager::new( initial_endpoint, - config.reconnection_backoff, + config.initial_reconnection_backoff, config.maximum_reconnection_backoff, ) .await, @@ -50,8 +50,8 @@ impl<'a> Client<'a> { Client { connections_managers, - reconnection_backoff: config.maximum_reconnection_backoff, - maximum_reconnection_backoff: config.reconnection_backoff, + initial_reconnection_backoff: config.maximum_reconnection_backoff, + maximum_reconnection_backoff: config.initial_reconnection_backoff, } } @@ -66,7 +66,7 @@ impl<'a> Client<'a> { // so that other connections could progress let new_manager = ConnectionManager::new( address, - self.reconnection_backoff, + self.initial_reconnection_backoff, self.maximum_reconnection_backoff, ) .await; diff --git a/mixnode/src/node/packet_forwarding.rs b/mixnode/src/node/packet_forwarding.rs index 15707a7b43..1378c543af 100644 --- a/mixnode/src/node/packet_forwarding.rs +++ b/mixnode/src/node/packet_forwarding.rs @@ -14,12 +14,12 @@ pub(crate) struct PacketForwarder<'a> { impl<'a: 'static> PacketForwarder<'a> { pub(crate) async fn new( initial_endpoints: Vec, - reconnection_backoff: Duration, + initial_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, ) -> PacketForwarder<'a> { let tcp_client_config = multi_tcp_client::Config::new( initial_endpoints, - reconnection_backoff, + initial_reconnection_backoff, maximum_reconnection_backoff, ); From 3756de9e866876db0ed5312606b387a2786325b2 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 14:22:50 +0000 Subject: [PATCH 227/273] processing data -> packet processor --- mixnode/src/node/listener.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mixnode/src/node/listener.rs b/mixnode/src/node/listener.rs index d12e9f4b6d..7146318ad0 100644 --- a/mixnode/src/node/listener.rs +++ b/mixnode/src/node/listener.rs @@ -9,11 +9,11 @@ use tokio::task::JoinHandle; async fn process_received_packet( packet_data: [u8; sphinx::PACKET_SIZE], - processing_data: PacketProcessor, + packet_processor: PacketProcessor, forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec)>, ) { // all processing incl. delay was done, the only thing left is to forward it - match processing_data.process_sphinx_packet(packet_data).await { + match packet_processor.process_sphinx_packet(packet_data).await { Err(e) => debug!("We failed to process received sphinx packet - {:?}", e), Ok(res) => match res { MixProcessingResult::ForwardHop(hop_address, hop_data) => { @@ -25,7 +25,7 @@ async fn process_received_packet( forwarding_channel .unbounded_send((hop_address, hop_data)) .unwrap(); - processing_data.report_sent(hop_address); + packet_processor.report_sent(hop_address); } MixProcessingResult::LoopMessage => { warn!("Somehow processed a loop cover message that we haven't implemented yet!") From 0c90afeaa23ac773419da5d01f5a21c6c5fdff8c Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 14:37:01 +0000 Subject: [PATCH 228/273] Adjusted tcp client tests to work concurrently + without "wait" --- .travis.yml | 2 - common/clients/multi-tcp-client/src/lib.rs | 73 ++++++++++------------ 2 files changed, 32 insertions(+), 43 deletions(-) diff --git a/.travis.yml b/.travis.yml index 06857f4fb7..7041ae8d65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,6 @@ jobs: allow_failures: - rust: nightly fast_finish: true -env: - - RUST_TEST_THREADS=1 before_script: - rustup component add rustfmt script: diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index c5965f2372..ea9d1010a5 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -89,19 +89,22 @@ impl<'a> Client<'a> { mod tests { use super::*; use std::str; - use std::{env, time}; + use std::time; use tokio::prelude::*; - const CLOSE_MESSAGE: [u8; 3] = [0, 0, 0]; + const SERVER_MSG_LEN: usize = 16; + const CLOSE_MESSAGE: [u8; SERVER_MSG_LEN] = [0; SERVER_MSG_LEN]; struct DummyServer { received_buf: Vec>, + listener: tokio::net::TcpListener, } impl DummyServer { - fn new() -> Self { + async fn new(address: SocketAddr) -> Self { DummyServer { received_buf: Vec::new(), + listener: tokio::net::TcpListener::bind(address).await.unwrap(), } } @@ -111,10 +114,9 @@ mod tests { // this is only used in tests so slightly higher logging levels are fine async fn listen_until(mut self, addr: SocketAddr, close_message: &[u8]) -> Self { - let mut listener = tokio::net::TcpListener::bind(addr).await.unwrap(); - let (mut socket, _) = listener.accept().await.unwrap(); + let (mut socket, _) = self.listener.accept().await.unwrap(); loop { - let mut buf = [0u8; 1024]; + let mut buf = [0u8; SERVER_MSG_LEN]; match socket.read(&mut buf).await { Ok(n) if n == 0 => { info!("Remote connection closed"); @@ -141,38 +143,26 @@ mod tests { #[test] fn client_reconnects_to_server_after_it_went_down() { - let num_test_threads = env::var("RUST_TEST_THREADS").expect("Number of test threads must be set to 1 to prevent tests interacting with themselves! (RUST_TEST_THREADS=1)"); - assert_eq!(num_test_threads, "1", "Number of test threads must be set to 1 to prevent tests interacting with themselves! (RUST_TEST_THREADS=1)"); - let mut rt = tokio::runtime::Runtime::new().unwrap(); - let addr = "127.0.0.1:5000".parse().unwrap(); + let addr = "127.0.0.1:6000".parse().unwrap(); let reconnection_backoff = Duration::from_secs(1); let client_config = Config::new(vec![addr], reconnection_backoff, 10 * reconnection_backoff); - let messages_to_send = vec![b"foomp1", b"foomp2"]; + let messages_to_send = vec![[1u8; SERVER_MSG_LEN], [2; SERVER_MSG_LEN]]; + + let dummy_server = rt.block_on(DummyServer::new(addr)); let finished_dummy_server_future = - rt.spawn(DummyServer::new().listen_until(addr, CLOSE_MESSAGE.as_ref())); + rt.spawn(dummy_server.listen_until(addr, &CLOSE_MESSAGE)); let mut c = rt.block_on(Client::new(client_config)); for msg in &messages_to_send { - rt.block_on(c.send(addr, *msg)).unwrap(); - rt.block_on( - async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }, - ); + rt.block_on(c.send(addr, msg)).unwrap(); } // kill server - rt.block_on(c.send(addr, CLOSE_MESSAGE.as_ref())).unwrap(); - rt.block_on(async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }); - - // try to send - go into reconnection - let post_kill_message = b"new foomp"; - - // we are trying to send to killed server - assert!(rt.block_on(c.send(addr, post_kill_message)).is_err()); - // the server future should have already been resolved + rt.block_on(c.send(addr, &CLOSE_MESSAGE)).unwrap(); let received_messages = rt .block_on(finished_dummy_server_future) .unwrap() @@ -180,51 +170,52 @@ mod tests { assert_eq!(received_messages, messages_to_send); - let new_server_future = - rt.spawn(DummyServer::new().listen_until(addr, CLOSE_MESSAGE.as_ref())); + // try to send - go into reconnection + let post_kill_message = [3u8; SERVER_MSG_LEN]; + + // we are trying to send to killed server + assert!(rt.block_on(c.send(addr, &post_kill_message)).is_err()); + + let new_dummy_server = rt.block_on(DummyServer::new(addr)); + let new_server_future = rt.spawn(new_dummy_server.listen_until(addr, &CLOSE_MESSAGE)); // keep sending after we leave reconnection backoff and reconnect loop { - if rt.block_on(c.send(addr, post_kill_message)).is_ok() { + if rt.block_on(c.send(addr, &post_kill_message)).is_ok() { break; } rt.block_on( async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }, ); } - rt.block_on(async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }); // kill the server to ensure it actually got the message - rt.block_on(c.send(addr, CLOSE_MESSAGE.as_ref())).unwrap(); + rt.block_on(c.send(addr, &CLOSE_MESSAGE)).unwrap(); let new_received_messages = rt.block_on(new_server_future).unwrap().get_received(); assert_eq!(post_kill_message.to_vec(), new_received_messages[0]); } #[test] fn server_receives_all_sent_messages_when_up() { - let num_test_threads = env::var("RUST_TEST_THREADS").expect("Number of test threads must be set to 1 to prevent tests interacting with themselves! (RUST_TEST_THREADS=1)"); - assert_eq!(num_test_threads, "1", "Number of test threads must be set to 1 to prevent tests interacting with themselves! (RUST_TEST_THREADS=1)"); - let mut rt = tokio::runtime::Runtime::new().unwrap(); - let addr = "127.0.0.1:5000".parse().unwrap(); + let addr = "127.0.0.1:6001".parse().unwrap(); let reconnection_backoff = Duration::from_secs(2); let client_config = Config::new(vec![addr], reconnection_backoff, 10 * reconnection_backoff); - let messages_to_send = vec![b"foomp1", b"foomp2"]; + let messages_to_send = vec![[1u8; SERVER_MSG_LEN], [2; SERVER_MSG_LEN]]; + + let dummy_server = rt.block_on(DummyServer::new(addr)); let finished_dummy_server_future = - rt.spawn(DummyServer::new().listen_until(addr, CLOSE_MESSAGE.as_ref())); + rt.spawn(dummy_server.listen_until(addr, &CLOSE_MESSAGE)); let mut c = rt.block_on(Client::new(client_config)); for msg in &messages_to_send { - rt.block_on(c.send(addr, *msg)).unwrap(); - rt.block_on( - async move { tokio::time::delay_for(time::Duration::from_millis(50)).await }, - ); + rt.block_on(c.send(addr, msg)).unwrap(); } - rt.block_on(c.send(addr, CLOSE_MESSAGE.as_ref())).unwrap(); + rt.block_on(c.send(addr, &CLOSE_MESSAGE)).unwrap(); // the server future should have already been resolved let received_messages = rt From b922c869f018bb7dd4a61921c5eb57466246341f Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 14:42:46 +0000 Subject: [PATCH 229/273] Got rid of compiler warnings --- mixnode/src/main.rs | 1 - mixnode/src/mix_peer.rs | 45 --------------------------- mixnode/src/node/packet_forwarding.rs | 4 +-- mixnode/src/node/packet_processing.rs | 2 +- 4 files changed, 3 insertions(+), 49 deletions(-) delete mode 100644 mixnode/src/mix_peer.rs diff --git a/mixnode/src/main.rs b/mixnode/src/main.rs index d073b1385c..994f74f23d 100644 --- a/mixnode/src/main.rs +++ b/mixnode/src/main.rs @@ -3,7 +3,6 @@ use clap::{App, ArgMatches}; pub mod built_info; mod commands; mod config; -mod mix_peer; mod node; fn main() { diff --git a/mixnode/src/mix_peer.rs b/mixnode/src/mix_peer.rs deleted file mode 100644 index 82be36c475..0000000000 --- a/mixnode/src/mix_peer.rs +++ /dev/null @@ -1,45 +0,0 @@ -use addressing; -use addressing::AddressTypeError; -use sphinx::route::NodeAddressBytes; -use std::error::Error; -use std::net::SocketAddr; -use tokio::prelude::*; - -#[derive(Debug)] -pub struct MixPeer { - connection: SocketAddr, -} - -#[derive(Debug)] -pub enum MixPeerError { - InvalidAddressError, -} - -impl From for MixPeerError { - fn from(_: AddressTypeError) -> Self { - use MixPeerError::*; - - InvalidAddressError - } -} - -impl MixPeer { - // note that very soon `next_hop_address` will be changed to `next_hop_metadata` - pub fn new(next_hop_address: NodeAddressBytes) -> Result { - let next_hop_socket_address = - addressing::socket_address_from_encoded_bytes(next_hop_address.to_bytes())?; - Ok(MixPeer { - connection: next_hop_socket_address, - }) - } - - pub async fn send(&self, bytes: Vec) -> Result<(), Box> { - let mut stream = tokio::net::TcpStream::connect(self.connection).await?; - stream.write_all(&bytes).await?; - Ok(()) - } - - pub fn stringify(&self) -> String { - self.connection.to_string() - } -} diff --git a/mixnode/src/node/packet_forwarding.rs b/mixnode/src/node/packet_forwarding.rs index 1378c543af..8335322081 100644 --- a/mixnode/src/node/packet_forwarding.rs +++ b/mixnode/src/node/packet_forwarding.rs @@ -11,12 +11,12 @@ pub(crate) struct PacketForwarder<'a> { conn_rx: mpsc::UnboundedReceiver<(SocketAddr, Vec)>, } -impl<'a: 'static> PacketForwarder<'a> { +impl PacketForwarder<'static> { pub(crate) async fn new( initial_endpoints: Vec, initial_reconnection_backoff: Duration, maximum_reconnection_backoff: Duration, - ) -> PacketForwarder<'a> { + ) -> PacketForwarder<'static> { let tcp_client_config = multi_tcp_client::Config::new( initial_endpoints, initial_reconnection_backoff, diff --git a/mixnode/src/node/packet_processing.rs b/mixnode/src/node/packet_processing.rs index ad9eb66730..ec6187e9aa 100644 --- a/mixnode/src/node/packet_processing.rs +++ b/mixnode/src/node/packet_processing.rs @@ -8,7 +8,6 @@ use sphinx::{ProcessedPacket, SphinxPacket}; use std::net::SocketAddr; use std::ops::Deref; use std::sync::Arc; -use std::time::Duration; #[derive(Debug)] pub enum MixProcessingError { @@ -20,6 +19,7 @@ pub enum MixProcessingError { pub enum MixProcessingResult { ForwardHop(SocketAddr, Vec), + #[allow(dead_code)] LoopMessage, } From 88eef54cc54d2b10d120bfea3998c93c52b987de Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 27 Feb 2020 14:44:14 +0000 Subject: [PATCH 230/273] dead code --- common/clients/multi-tcp-client/src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/common/clients/multi-tcp-client/src/lib.rs b/common/clients/multi-tcp-client/src/lib.rs index ea9d1010a5..34b64e77a5 100644 --- a/common/clients/multi-tcp-client/src/lib.rs +++ b/common/clients/multi-tcp-client/src/lib.rs @@ -113,7 +113,7 @@ mod tests { } // this is only used in tests so slightly higher logging levels are fine - async fn listen_until(mut self, addr: SocketAddr, close_message: &[u8]) -> Self { + async fn listen_until(mut self, close_message: &[u8]) -> Self { let (mut socket, _) = self.listener.accept().await.unwrap(); loop { let mut buf = [0u8; SERVER_MSG_LEN]; @@ -152,8 +152,7 @@ mod tests { let messages_to_send = vec![[1u8; SERVER_MSG_LEN], [2; SERVER_MSG_LEN]]; let dummy_server = rt.block_on(DummyServer::new(addr)); - let finished_dummy_server_future = - rt.spawn(dummy_server.listen_until(addr, &CLOSE_MESSAGE)); + let finished_dummy_server_future = rt.spawn(dummy_server.listen_until(&CLOSE_MESSAGE)); let mut c = rt.block_on(Client::new(client_config)); @@ -177,7 +176,7 @@ mod tests { assert!(rt.block_on(c.send(addr, &post_kill_message)).is_err()); let new_dummy_server = rt.block_on(DummyServer::new(addr)); - let new_server_future = rt.spawn(new_dummy_server.listen_until(addr, &CLOSE_MESSAGE)); + let new_server_future = rt.spawn(new_dummy_server.listen_until(&CLOSE_MESSAGE)); // keep sending after we leave reconnection backoff and reconnect loop { @@ -206,8 +205,7 @@ mod tests { let messages_to_send = vec![[1u8; SERVER_MSG_LEN], [2; SERVER_MSG_LEN]]; let dummy_server = rt.block_on(DummyServer::new(addr)); - let finished_dummy_server_future = - rt.spawn(dummy_server.listen_until(addr, &CLOSE_MESSAGE)); + let finished_dummy_server_future = rt.spawn(dummy_server.listen_until(&CLOSE_MESSAGE)); let mut c = rt.block_on(Client::new(client_config)); From e600dc7c4fa9e2dd53e1f4206b52b4d63c59d5aa Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Thu, 27 Feb 2020 17:14:52 +0000 Subject: [PATCH 231/273] all: fixing mis-spelling --- mixnode/src/commands/init.rs | 2 +- mixnode/src/config/mod.rs | 2 +- mixnode/src/config/{persistance => persistence}/mod.rs | 0 mixnode/src/config/{persistance => persistence}/pathfinder.rs | 0 mixnode/src/node/mod.rs | 2 +- nym-client/src/client/mod.rs | 2 +- nym-client/src/commands/init.rs | 2 +- nym-client/src/config/mod.rs | 2 +- nym-client/src/config/{persistance => persistence}/mod.rs | 0 .../src/config/{persistance => persistence}/pathfinder.rs | 0 sfw-provider/src/commands/init.rs | 2 +- sfw-provider/src/config/mod.rs | 2 +- sfw-provider/src/config/{persistance => persistence}/mod.rs | 0 .../src/config/{persistance => persistence}/pathfinder.rs | 0 sfw-provider/src/provider/mod.rs | 2 +- 15 files changed, 9 insertions(+), 9 deletions(-) rename mixnode/src/config/{persistance => persistence}/mod.rs (100%) rename mixnode/src/config/{persistance => persistence}/pathfinder.rs (100%) rename nym-client/src/config/{persistance => persistence}/mod.rs (100%) rename nym-client/src/config/{persistance => persistence}/pathfinder.rs (100%) rename sfw-provider/src/config/{persistance => persistence}/mod.rs (100%) rename sfw-provider/src/config/{persistance => persistence}/pathfinder.rs (100%) diff --git a/mixnode/src/commands/init.rs b/mixnode/src/commands/init.rs index b7a6c521c7..4c8a1243a5 100644 --- a/mixnode/src/commands/init.rs +++ b/mixnode/src/commands/init.rs @@ -1,5 +1,5 @@ use crate::commands::override_config; -use crate::config::persistance::pathfinder::MixNodePathfinder; +use crate::config::persistence::pathfinder::MixNodePathfinder; use clap::{App, Arg, ArgMatches}; use config::NymConfig; use crypto::encryption; diff --git a/mixnode/src/config/mod.rs b/mixnode/src/config/mod.rs index 55690e6c23..c53b917210 100644 --- a/mixnode/src/config/mod.rs +++ b/mixnode/src/config/mod.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::time; -pub mod persistance; +pub mod persistence; mod template; // 'MIXNODE' diff --git a/mixnode/src/config/persistance/mod.rs b/mixnode/src/config/persistence/mod.rs similarity index 100% rename from mixnode/src/config/persistance/mod.rs rename to mixnode/src/config/persistence/mod.rs diff --git a/mixnode/src/config/persistance/pathfinder.rs b/mixnode/src/config/persistence/pathfinder.rs similarity index 100% rename from mixnode/src/config/persistance/pathfinder.rs rename to mixnode/src/config/persistence/pathfinder.rs diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 35167905df..628c02372b 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -1,4 +1,4 @@ -use crate::config::persistance::pathfinder::MixNodePathfinder; +use crate::config::persistence::pathfinder::MixNodePathfinder; use crate::config::Config; use crate::node::packet_processing::PacketProcessor; use crypto::encryption; diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index e950d806ff..19874f5a3e 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -1,7 +1,7 @@ use crate::client::mix_traffic::MixTrafficController; use crate::client::received_buffer::ReceivedMessagesBuffer; use crate::client::topology_control::TopologyInnerRef; -use crate::config::persistance::pathfinder::ClientPathfinder; +use crate::config::persistence::pathfinder::ClientPathfinder; use crate::config::{Config, SocketType}; use crate::sockets::tcp; use crate::sockets::ws; diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index c9f47790c4..292aacf655 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -1,6 +1,6 @@ use crate::built_info; use crate::commands::override_config; -use crate::config::persistance::pathfinder::ClientPathfinder; +use crate::config::persistence::pathfinder::ClientPathfinder; use clap::{App, Arg, ArgMatches}; use config::NymConfig; use crypto::identity::MixIdentityKeyPair; diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index b1d6a8b7b2..8b23dfa224 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Deserializer, Serialize}; use std::path::PathBuf; use std::time; -pub mod persistance; +pub mod persistence; mod template; // 'CLIENT' diff --git a/nym-client/src/config/persistance/mod.rs b/nym-client/src/config/persistence/mod.rs similarity index 100% rename from nym-client/src/config/persistance/mod.rs rename to nym-client/src/config/persistence/mod.rs diff --git a/nym-client/src/config/persistance/pathfinder.rs b/nym-client/src/config/persistence/pathfinder.rs similarity index 100% rename from nym-client/src/config/persistance/pathfinder.rs rename to nym-client/src/config/persistence/pathfinder.rs diff --git a/sfw-provider/src/commands/init.rs b/sfw-provider/src/commands/init.rs index a488cd691f..f87e189c51 100644 --- a/sfw-provider/src/commands/init.rs +++ b/sfw-provider/src/commands/init.rs @@ -1,5 +1,5 @@ use crate::commands::override_config; -use crate::config::persistance::pathfinder::ProviderPathfinder; +use crate::config::persistence::pathfinder::ProviderPathfinder; use clap::{App, Arg, ArgMatches}; use config::NymConfig; use crypto::encryption; diff --git a/sfw-provider/src/config/mod.rs b/sfw-provider/src/config/mod.rs index b578ef92b7..7da7ed4a45 100644 --- a/sfw-provider/src/config/mod.rs +++ b/sfw-provider/src/config/mod.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use std::str::FromStr; use std::time; -pub mod persistance; +pub mod persistence; mod template; // 'PROVIDER' diff --git a/sfw-provider/src/config/persistance/mod.rs b/sfw-provider/src/config/persistence/mod.rs similarity index 100% rename from sfw-provider/src/config/persistance/mod.rs rename to sfw-provider/src/config/persistence/mod.rs diff --git a/sfw-provider/src/config/persistance/pathfinder.rs b/sfw-provider/src/config/persistence/pathfinder.rs similarity index 100% rename from sfw-provider/src/config/persistance/pathfinder.rs rename to sfw-provider/src/config/persistence/pathfinder.rs diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index fa6518e118..be34b8e7d2 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -1,4 +1,4 @@ -use crate::config::persistance::pathfinder::ProviderPathfinder; +use crate::config::persistence::pathfinder::ProviderPathfinder; use crate::config::Config; use crate::provider::client_handling::{ClientProcessingData, ClientRequestProcessor}; use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData}; From 3cb813d056147591f7ab823c11040a375a410f31 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 28 Feb 2020 11:48:08 +0000 Subject: [PATCH 232/273] Updated Storage to work with async io --- sfw-provider/src/provider/storage/mod.rs | 139 ++++++++++++++--------- 1 file changed, 86 insertions(+), 53 deletions(-) diff --git a/sfw-provider/src/provider/storage/mod.rs b/sfw-provider/src/provider/storage/mod.rs index 6123d5c686..f669567f44 100644 --- a/sfw-provider/src/provider/storage/mod.rs +++ b/sfw-provider/src/provider/storage/mod.rs @@ -1,22 +1,30 @@ +use futures::lock::Mutex; +use futures::StreamExt; use log::*; use rand::Rng; use sfw_provider_requests::DUMMY_MESSAGE_CONTENT; use sphinx::route::{DestinationAddressBytes, SURBIdentifier}; -use std::fs::File; use std::io; -use std::io::Write; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; +use std::sync::Arc; +use tokio::fs; +use tokio::fs::File; +use tokio::prelude::*; -pub enum StoreError { - ClientDoesntExistError, - FileIOFailure, +const DUMMY_MESSAGE: ClientFile = ClientFile { + content: DUMMY_MESSAGE_CONTENT.to_vec(), + path: Default::default(), +}; + +#[derive(Clone)] +pub struct ClientFile { + content: Vec, + path: PathBuf, } -impl From for StoreError { - fn from(_: std::io::Error) -> Self { - use StoreError::*; - - FileIOFailure +impl ClientFile { + fn new(content: Vec, path: PathBuf) -> Self { + ClientFile { content, path } } } @@ -25,7 +33,6 @@ pub struct StoreData { #[allow(dead_code)] client_surb_id: SURBIdentifier, message: Vec, - filename_length: u16, } impl StoreData { @@ -33,21 +40,29 @@ impl StoreData { client_address: DestinationAddressBytes, client_surb_id: SURBIdentifier, message: Vec, - filename_length: u16, ) -> Self { StoreData { client_address, client_surb_id, message, - filename_length, } } } -// TODO: replace with database -pub struct ClientStorage(()); +// TODO: replace with proper database... +pub struct ClientStorage { + inner: Arc>, +} -// TODO: change it to some generic implementation to inject fs +// even though the data inside is extremely cheap to copy, we have to have a single mutex, +// so might as well store the data behind it +pub struct ClientStorageInner { + message_retrieval_limit: usize, + filename_length: u16, + main_store_path_dir: PathBuf, +} + +// TODO: change it to some generic implementation to inject fs (or even better - proper database) impl ClientStorage { pub(crate) fn generate_random_file_name(length: usize) -> String { rand::thread_rng() @@ -56,16 +71,14 @@ impl ClientStorage { .collect::() } - fn dummy_message() -> Vec { - // TODO: should it be padded to constant length? - DUMMY_MESSAGE_CONTENT.to_vec() - } + pub(crate) async fn store_processed_data(&self, store_data: StoreData) -> io::Result<()> { + let inner_data = self.inner.lock().await; - pub fn store_processed_data(store_data: StoreData, store_dir: &Path) -> io::Result<()> { + // TODO: to replace with store_data.to_b58_string() once PR on sphinx is merged let client_dir_name = bs58::encode(store_data.client_address).into_string(); - let full_store_dir = store_dir.join(client_dir_name); - let full_store_path = full_store_dir.join(ClientStorage::generate_random_file_name( - store_data.filename_length as usize, + let full_store_dir = inner_data.main_store_path_dir.join(client_dir_name); + let full_store_path = full_store_dir.join(Self::generate_random_file_name( + inner_data.filename_length as usize, )); debug!( "going to store: {:?} in file: {:?}", @@ -74,44 +87,61 @@ impl ClientStorage { // TODO: what to do with surbIDs?? - // we can use normal io here, no need for tokio as it's all happening in one thread per connection - let mut file = File::create(full_store_path)?; - file.write_all(store_data.message.as_ref())?; - - Ok(()) + let mut file = File::create(full_store_path).await?; + file.write_all(store_data.message.as_ref()).await } - pub fn retrieve_client_files( + pub(crate) async fn retrieve_client_files( + &self, client_address: DestinationAddressBytes, - store_dir: &Path, - message_retrieval_limit: u16, - ) -> Result>, StoreError> { + ) -> io::Result> { + let inner_data = self.inner.lock().await; + + // TODO: to replace with store_data.to_b58_string() once PR on sphinx is merged let client_dir_name = bs58::encode(client_address).into_string(); - let full_store_dir = store_dir.join(client_dir_name); + let full_store_dir = inner_data.main_store_path_dir.join(client_dir_name); trace!("going to lookup: {:?}!", full_store_dir); if !full_store_dir.exists() { - return Err(StoreError::ClientDoesntExistError); + return Err(io::Error::new( + io::ErrorKind::NotFound, + "Target client does not exist", + )); } - let msgs: Vec<_> = std::fs::read_dir(full_store_dir)? - .filter_map(|entry| entry.ok()) - .filter(|entry| ClientStorage::is_valid_file(entry)) - .map(|entry| { - // Not yet sure how to exactly get rid of those unwraps - let content = std::fs::read(entry.path()).unwrap(); - ClientStorage::delete_file(entry.path()).unwrap(); - content - }) // TODO: THIS MAP IS UNSAFE (BOTH FOR READING AND DELETING)!! - in the case where there are multiple requests from the same client being processed in parallel - .chain(std::iter::repeat(ClientStorage::dummy_message())) - .take(message_retrieval_limit as usize) - .collect(); + let mut msgs = Vec::new(); + let read_dir = fs::read_dir(full_store_dir).await?; + + while let Some(dir_entry) = read_dir.next().await { + if let Ok(dir_entry) = dir_entry { + if !Self::is_valid_file(&dir_entry).await { + continue; + } + // Do not delete the file itself here! + // Only do it after client has received it + let client_file = + ClientFile::new(fs::read(dir_entry.path()).await?, dir_entry.path()); + msgs.push(client_file) + } + if msgs.len() == inner_data.message_retrieval_limit { + break; + } + } + + // make sure we always return as many messages as we need + if msgs.len() != inner_data.message_retrieval_limit as usize { + msgs = msgs + .into_iter() + .chain(std::iter::repeat(DUMMY_MESSAGE)) + .take(inner_data.message_retrieval_limit) + .collect(); + } Ok(msgs) } - fn is_valid_file(entry: &std::fs::DirEntry) -> bool { - let metadata = match entry.metadata() { + async fn is_valid_file(entry: &fs::DirEntry) -> bool { + let metadata = match entry.metadata().await { Ok(meta) => meta, Err(e) => { error!( @@ -134,10 +164,13 @@ impl ClientStorage { is_file } - // TODO: THIS NEEDS A LOCKING MECHANISM!!! (or a db layer on top - basically 'ClientStorage' on steroids) - // TODO 2: This should only be called AFTER we sent the reply. Because if client's connection failed after sending request + fn delete_files(&self, files: Vec) -> io::Result<()> { + unimplemented!() + } + + // TODO: This should only be called AFTER we sent the reply. Because if client's connection failed after sending request // the messages would be deleted but he wouldn't have received them - fn delete_file(path: PathBuf) -> io::Result<()> { + fn delete_file(&self, path: PathBuf) -> io::Result<()> { trace!("Here {:?} will be deleted!", path); std::fs::remove_file(path) // another argument for db layer -> remove_file is NOT guaranteed to immediately get rid of the file } From 03aac5bd355579320902aea5b4c84cabd8f2d8a9 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 28 Feb 2020 12:32:47 +0000 Subject: [PATCH 233/273] Updated sphinx --- Cargo.lock | 371 ++++++++++-------- common/clients/mix-client/Cargo.toml | 2 +- common/clients/provider-client/Cargo.toml | 2 +- common/crypto/Cargo.toml | 2 +- common/healthcheck/Cargo.toml | 2 +- common/topology/Cargo.toml | 2 +- mixnode/Cargo.toml | 2 +- sfw-provider/Cargo.toml | 2 +- sfw-provider/sfw-provider-requests/Cargo.toml | 2 +- sfw-provider/src/commands/run.rs | 3 +- sfw-provider/src/provider/storage/mod.rs | 6 +- 11 files changed, 228 insertions(+), 168 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d5206d34b..0e804a8610 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ dependencies = [ "byteorder", "bytes 0.4.12", "env_logger 0.7.1", - "futures 0.3.1", + "futures 0.3.4", "integer-encoding", "log", "protobuf", @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.6" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +checksum = "d5e63fd144e18ba274ae7095c0197a870a7b9468abc801dd62f190d80817d2ec" dependencies = [ "memchr", ] @@ -103,14 +103,26 @@ checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" [[package]] name = "assert-json-diff" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9881d306dee755eccf052d652b774a6b2861e86b4772f555262130e58e4f81d2" +checksum = "9c356497fd3417158bcb318266ac83c391219ca3a5fa659049f42e0041ab57d6" dependencies = [ + "extend", "serde", "serde_json", ] +[[package]] +name = "async-trait" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "750b1c38a1dfadd108da0f01c08f4cdc7ff1bb39b325f9c82cc972361780a6e1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atty" version = "0.2.14" @@ -136,9 +148,9 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.42" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4b1549d804b6c73f4817df2ba073709e96e426f12987127c48e6745568c350b" +checksum = "e4036b9bf40f3cf16aba72a3d65e8a520fc4bafcdc7079aea8f848c58c5b5536" dependencies = [ "backtrace-sys", "cfg-if", @@ -256,9 +268,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "bytes" @@ -357,9 +369,9 @@ dependencies = [ [[package]] name = "colored" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8815e2ab78f3a59928fc32e141fbeece88320a240e43f47b2fd64ea3a88a5b3d" +checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" dependencies = [ "atty", "lazy_static", @@ -436,54 +448,47 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ "crossbeam-epoch", - "crossbeam-utils 0.7.0", + "crossbeam-utils", + "maybe-uninit", ] [[package]] name = "crossbeam-epoch" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg 0.1.7", + "autocfg 1.0.0", "cfg-if", - "crossbeam-utils 0.7.0", + "crossbeam-utils", "lazy_static", + "maybe-uninit", "memoffset", "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.1.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -dependencies = [ - "crossbeam-utils 0.6.6", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" dependencies = [ "cfg-if", - "lazy_static", + "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 0.1.7", + "autocfg 1.0.0", "cfg-if", "lazy_static", ] @@ -591,9 +596,9 @@ checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" [[package]] name = "dtoa" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" +checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" [[package]] name = "either" @@ -638,11 +643,23 @@ dependencies = [ [[package]] name = "error-chain" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" +checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" dependencies = [ - "version_check 0.1.5", + "version_check", +] + +[[package]] +name = "extend" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9db393664b0e6c6230a14115e7e798f80b70f54038dc21165db24c6b7f28fc" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -736,9 +753,9 @@ checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" [[package]] name = "futures" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987" +checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" dependencies = [ "futures-channel", "futures-core", @@ -751,9 +768,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" +checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" dependencies = [ "futures-core", "futures-sink", @@ -761,9 +778,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" +checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" [[package]] name = "futures-cpupool" @@ -777,9 +794,9 @@ dependencies = [ [[package]] name = "futures-executor" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" +checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" dependencies = [ "futures-core", "futures-task", @@ -788,15 +805,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" +checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" [[package]] name = "futures-macro" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" +checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -806,21 +823,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16" +checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" [[package]] name = "futures-task" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" +checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" [[package]] name = "futures-util" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" +checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" dependencies = [ "futures-channel", "futures-core", @@ -907,7 +924,7 @@ dependencies = [ "addressing", "crypto", "directory-client", - "futures 0.3.1", + "futures 0.3.4", "itertools", "log", "mix-client", @@ -919,15 +936,15 @@ dependencies = [ "serde_derive", "sfw-provider-requests", "sphinx", - "tokio 0.2.10", + "tokio 0.2.12", "topology", ] [[package]] name = "hermit-abi" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772" +checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" dependencies = [ "libc", ] @@ -1068,9 +1085,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b54058f0a6ff80b6803da8faf8997cde53872b38f4023728f6830b06cd3c0dc" +checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" dependencies = [ "autocfg 1.0.0", ] @@ -1086,9 +1103,12 @@ dependencies = [ [[package]] name = "integer-encoding" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aec89c15e2cfa0f0eae8ca60e03cb10b30d25ea2c0ad7d6be60a95e32729994" +checksum = "553a280155f89ff1c798520ccf877d1bc9571314164e998f8a7b5fe2d84cb45c" +dependencies = [ + "async-trait", +] [[package]] name = "iovec" @@ -1116,9 +1136,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "jobserver" -version = "0.1.19" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b06c1b455f1cf4269a8cfc320ab930a810e2375a42af5075eb8a8b36405ce0" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" dependencies = [ "libc", ] @@ -1147,9 +1167,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" [[package]] name = "libgit2-sys" @@ -1225,9 +1245,9 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" -version = "2.3.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memoffset" @@ -1256,9 +1276,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" +checksum = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" dependencies = [ "adler32", ] @@ -1337,7 +1357,7 @@ dependencies = [ "rand 0.7.3", "rand_distr", "sphinx", - "tokio 0.2.10", + "tokio 0.2.12", "topology", ] @@ -1363,9 +1383,9 @@ dependencies = [ name = "multi-tcp-client" version = "0.1.0" dependencies = [ - "futures 0.3.1", + "futures 0.3.4", "log", - "tokio 0.2.10", + "tokio 0.2.12", ] [[package]] @@ -1440,7 +1460,7 @@ dependencies = [ "directory-client", "dirs", "dotenv", - "futures 0.3.1", + "futures 0.3.4", "healthcheck", "log", "mix-client", @@ -1454,7 +1474,7 @@ dependencies = [ "sfw-provider-requests", "sphinx", "tempfile", - "tokio 0.2.10", + "tokio 0.2.12", "tokio-tungstenite", "topology", ] @@ -1473,7 +1493,7 @@ dependencies = [ "directory-client", "dirs", "dotenv", - "futures 0.3.1", + "futures 0.3.4", "log", "multi-tcp-client", "pemstore", @@ -1481,7 +1501,7 @@ dependencies = [ "serde", "sphinx", "tempfile", - "tokio 0.2.10", + "tokio 0.2.12", ] [[package]] @@ -1497,7 +1517,7 @@ dependencies = [ "directory-client", "dirs", "dotenv", - "futures 0.3.1", + "futures 0.3.4", "hmac", "log", "pemstore", @@ -1509,7 +1529,7 @@ dependencies = [ "sha2", "sphinx", "tempfile", - "tokio 0.2.10", + "tokio 0.2.12", ] [[package]] @@ -1525,13 +1545,13 @@ dependencies = [ "directory-client", "dirs", "dotenv", - "futures 0.3.1", + "futures 0.3.4", "healthcheck", "log", "pretty_env_logger", "serde", "tempfile", - "tokio 0.2.10", + "tokio 0.2.12", "topology", ] @@ -1543,9 +1563,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.26" +version = "0.10.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3cc5799d98e1088141b8e01ff760112bbd9f19d850c124500566ca6901a585" +checksum = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" dependencies = [ "bitflags", "cfg-if", @@ -1563,11 +1583,11 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.53" +version = "0.9.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465d16ae7fc0e313318f7de5cecf57b2fbe7511fd213978b457e1c96ff46736f" +checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" dependencies = [ - "autocfg 0.1.7", + "autocfg 1.0.0", "cc", "libc", "pkg-config", @@ -1635,9 +1655,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ "ucd-trie", ] @@ -1654,9 +1674,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" +checksum = "27e5277315f6b4f27e0e6744feb5d5ba1891e7164871033d3c8344c6783b349a" dependencies = [ "pest", "pest_meta", @@ -1667,9 +1687,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" dependencies = [ "maplit", "pest", @@ -1678,18 +1698,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fca1c4ff21f60ca2d37b80d72b63dab823a9d19d3cda3a81d18bc03f0ba8c5" +checksum = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6544cd4e4ecace61075a6ec78074beeef98d58aa9a3d07d053d993b2946a90d6" +checksum = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" dependencies = [ "proc-macro2", "quote", @@ -1731,6 +1751,32 @@ dependencies = [ "log", ] +[[package]] +name = "proc-macro-error" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052b3c9af39c7e5e94245f820530487d19eb285faedcb40e0c3275132293f242" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d175bef481c7902e63e3165627123fff3502f06ac043d3ef42d08c1246da9253" +dependencies = [ + "proc-macro2", + "quote", + "rustversion", + "syn", + "syn-mid", +] + [[package]] name = "proc-macro-hack" version = "0.5.11" @@ -1750,9 +1796,9 @@ checksum = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" [[package]] name = "proc-macro2" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acb317c6ff86a4e579dfa00fc5e6cca91ecbb4e7eb2df0468805b674eb88548" +checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" dependencies = [ "unicode-xid", ] @@ -1786,12 +1832,12 @@ dependencies = [ name = "provider-client" version = "0.1.0" dependencies = [ - "futures 0.3.1", + "futures 0.3.4", "log", "pretty_env_logger", "sfw-provider-requests", "sphinx", - "tokio 0.2.10", + "tokio 0.2.12", ] [[package]] @@ -2006,9 +2052,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5508c1941e4e7cb19965abef075d35a9a8b5cdf0846f30b4050e9b55dc55e87" +checksum = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" dependencies = [ "aho-corasick", "memchr", @@ -2018,9 +2064,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e734e891f5b408a29efbf8309e656876276f49ab6a6ac208600b4419bd893d90" +checksum = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" [[package]] name = "remove_dir_all" @@ -2074,7 +2120,7 @@ dependencies = [ "base64 0.11.0", "blake2b_simd", "constant_time_eq", - "crossbeam-utils 0.7.0", + "crossbeam-utils", ] [[package]] @@ -2092,6 +2138,17 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ryu" version = "1.0.2" @@ -2100,9 +2157,9 @@ checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" [[package]] name = "schannel" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" +checksum = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" dependencies = [ "lazy_static", "winapi 0.3.8", @@ -2110,9 +2167,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" @@ -2172,9 +2229,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.45" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab8f15f15d6c41a154c1b128a22f2dfabe350ef53c40953d84e36155c91192b" +checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" dependencies = [ "itoa", "ryu", @@ -2252,9 +2309,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" +checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" [[package]] name = "socket2" @@ -2271,7 +2328,7 @@ dependencies = [ [[package]] name = "sphinx" version = "0.1.0" -source = "git+https://github.com/nymtech/sphinx?rev=4e51ed38a5de6fcb454c8903f1fe5e6b52720c77#4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" +source = "git+https://github.com/nymtech/sphinx?rev=fde6f0523e5bd8e02e5bba0936be266271efb7cc#fde6f0523e5bd8e02e5bba0936be266271efb7cc" dependencies = [ "aes-ctr", "arrayref", @@ -2328,15 +2385,26 @@ checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" [[package]] name = "syn" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6f3550d8dff9ef7dc34d384ac6f107e5d31c8f57d9f28e0081503f547ac8f5" +checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" dependencies = [ "proc-macro2", "quote", "unicode-xid", ] +[[package]] +name = "syn-mid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "synstructure" version = "0.12.3" @@ -2423,9 +2491,9 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fc73332507b971a5010664991a441b5ee0de92017f5a0e8b00fd684573045b" +checksum = "b34bee1facdc352fba10c9c58b654e6ecb6a2250167772bf86071f7c5f2f5061" dependencies = [ "bytes 0.5.4", "fnv", @@ -2458,9 +2526,9 @@ dependencies = [ [[package]] name = "tokio-codec" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" +checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b" dependencies = [ "bytes 0.4.12", "futures 0.1.29", @@ -2469,9 +2537,9 @@ dependencies = [ [[package]] name = "tokio-current-thread" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16217cad7f1b840c5a97dfb3c43b0c871fef423a6e8d2118c604e843662a443" +checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e" dependencies = [ "futures 0.1.29", "tokio-executor", @@ -2479,19 +2547,19 @@ dependencies = [ [[package]] name = "tokio-executor" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6df436c42b0c3330a82d855d2ef017cd793090ad550a6bc2184f4b933532ab" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ - "crossbeam-utils 0.6.6", + "crossbeam-utils", "futures 0.1.29", ] [[package]] name = "tokio-io" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" +checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes 0.4.12", "futures 0.1.29", @@ -2500,21 +2568,22 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50a61f268a3db2acee8dcab514efc813dc6dbe8a00e86076f935f94304b59a7a" +checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ + "proc-macro2", "quote", "syn", ] [[package]] name = "tokio-reactor" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146" +checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351" dependencies = [ - "crossbeam-utils 0.6.6", + "crossbeam-utils", "futures 0.1.29", "lazy_static", "log", @@ -2529,9 +2598,9 @@ dependencies = [ [[package]] name = "tokio-sync" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76" +checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee" dependencies = [ "fnv", "futures 0.1.29", @@ -2539,9 +2608,9 @@ dependencies = [ [[package]] name = "tokio-tcp" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d14b10654be682ac43efee27401d792507e30fd8d26389e1da3b185de2e4119" +checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72" dependencies = [ "bytes 0.4.12", "futures 0.1.29", @@ -2553,13 +2622,13 @@ dependencies = [ [[package]] name = "tokio-threadpool" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c32ffea4827978e9aa392d2f743d973c1dfa3730a2ed3f22ce1e6984da848c" +checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ "crossbeam-deque", "crossbeam-queue", - "crossbeam-utils 0.6.6", + "crossbeam-utils", "futures 0.1.29", "lazy_static", "log", @@ -2570,11 +2639,11 @@ dependencies = [ [[package]] name = "tokio-timer" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1739638e364e558128461fc1ad84d997702c8e31c2e6b18fb99842268199e827" +checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils 0.6.6", + "crossbeam-utils", "futures 0.1.29", "slab", "tokio-executor", @@ -2586,10 +2655,10 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b8fe88007ebc363512449868d7da4389c9400072a3f666f212c7280082882a" dependencies = [ - "futures 0.3.1", + "futures 0.3.4", "log", "pin-project", - "tokio 0.2.10", + "tokio 0.2.12", "tungstenite", ] @@ -2669,7 +2738,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check 0.9.1", + "version_check", ] [[package]] @@ -2687,7 +2756,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "smallvec 1.1.0", + "smallvec 1.2.0", ] [[package]] @@ -2766,12 +2835,6 @@ dependencies = [ "semver", ] -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.1" diff --git a/common/clients/mix-client/Cargo.toml b/common/clients/mix-client/Cargo.toml index b269af2d02..bcdd0e2666 100644 --- a/common/clients/mix-client/Cargo.toml +++ b/common/clients/mix-client/Cargo.toml @@ -18,6 +18,6 @@ addressing = {path = "../../addressing"} topology = {path = "../../topology"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } # sphinx = { path = "../../../../sphinx"} diff --git a/common/clients/provider-client/Cargo.toml b/common/clients/provider-client/Cargo.toml index 7897a94c7e..ff96bc16b0 100644 --- a/common/clients/provider-client/Cargo.toml +++ b/common/clients/provider-client/Cargo.toml @@ -16,4 +16,4 @@ tokio = { version = "0.2", features = ["full"] } sfw-provider-requests = { path = "../../../sfw-provider/sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index 7044e14cc9..9966355263 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -15,4 +15,4 @@ rand = "0.7.2" rand_os = "0.1" ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } \ No newline at end of file +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } \ No newline at end of file diff --git a/common/healthcheck/Cargo.toml b/common/healthcheck/Cargo.toml index 0eb6b3c147..81da13d03d 100644 --- a/common/healthcheck/Cargo.toml +++ b/common/healthcheck/Cargo.toml @@ -27,7 +27,7 @@ sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" } topology = {path = "../topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } # sphinx = { path = "../../../sphinx"} [dev-dependencies] diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index c3ee8e72e1..d64ce55319 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -19,5 +19,5 @@ addressing = {path = "../addressing"} version-checker = {path = "../version-checker" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } # sphinx = { path = "../../../sphinx"} diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index 277138b2ad..0a4541e2ee 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -28,7 +28,7 @@ multi-tcp-client = { path = "../common/clients/multi-tcp-client" } pemstore = {path = "../common/pemstore"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } [build-dependencies] built = "0.3.2" diff --git a/sfw-provider/Cargo.toml b/sfw-provider/Cargo.toml index 62b3f15bef..c082614b97 100644 --- a/sfw-provider/Cargo.toml +++ b/sfw-provider/Cargo.toml @@ -31,7 +31,7 @@ pemstore = {path = "../common/pemstore"} sfw-provider-requests = { path = "./sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } [build-dependencies] built = "0.3.2" diff --git a/sfw-provider/sfw-provider-requests/Cargo.toml b/sfw-provider/sfw-provider-requests/Cargo.toml index 9d0cb7e241..1db2954da4 100644 --- a/sfw-provider/sfw-provider-requests/Cargo.toml +++ b/sfw-provider/sfw-provider-requests/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] bs58 = "0.3.0" -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } diff --git a/sfw-provider/src/commands/run.rs b/sfw-provider/src/commands/run.rs index cabb7bcfd1..123398e6db 100644 --- a/sfw-provider/src/commands/run.rs +++ b/sfw-provider/src/commands/run.rs @@ -157,6 +157,5 @@ pub fn execute(matches: &ArgMatches) { config.get_clients_ledger_path() ); - let provider = ServiceProvider::new(config); - provider.start().unwrap() + ServiceProvider::new(config).run(); } diff --git a/sfw-provider/src/provider/storage/mod.rs b/sfw-provider/src/provider/storage/mod.rs index f669567f44..6b2725a25d 100644 --- a/sfw-provider/src/provider/storage/mod.rs +++ b/sfw-provider/src/provider/storage/mod.rs @@ -74,8 +74,7 @@ impl ClientStorage { pub(crate) async fn store_processed_data(&self, store_data: StoreData) -> io::Result<()> { let inner_data = self.inner.lock().await; - // TODO: to replace with store_data.to_b58_string() once PR on sphinx is merged - let client_dir_name = bs58::encode(store_data.client_address).into_string(); + let client_dir_name = client_address.to_base58_string(); let full_store_dir = inner_data.main_store_path_dir.join(client_dir_name); let full_store_path = full_store_dir.join(Self::generate_random_file_name( inner_data.filename_length as usize, @@ -97,8 +96,7 @@ impl ClientStorage { ) -> io::Result> { let inner_data = self.inner.lock().await; - // TODO: to replace with store_data.to_b58_string() once PR on sphinx is merged - let client_dir_name = bs58::encode(client_address).into_string(); + let client_dir_name = client_address.to_base58_string(); let full_store_dir = inner_data.main_store_path_dir.join(client_dir_name); trace!("going to lookup: {:?}!", full_store_dir); From b6e2c25c09fa94d7d750de01b3135b9714d3390b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 28 Feb 2020 12:33:09 +0000 Subject: [PATCH 234/273] Made relevant mix methods private --- mixnode/src/node/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mixnode/src/node/mod.rs b/mixnode/src/node/mod.rs index 35167905df..100ac7d166 100644 --- a/mixnode/src/node/mod.rs +++ b/mixnode/src/node/mod.rs @@ -43,7 +43,7 @@ impl MixNode { } } - pub fn start_presence_notifier(&self) { + fn start_presence_notifier(&self) { info!("Starting presence notifier..."); let notifier_config = presence::NotifierConfig::new( self.config.get_presence_directory_server(), @@ -55,7 +55,7 @@ impl MixNode { presence::Notifier::new(notifier_config).start(self.runtime.handle()); } - pub fn start_metrics_reporter(&self) -> metrics::MetricsReporter { + fn start_metrics_reporter(&self) -> metrics::MetricsReporter { info!("Starting metrics reporter..."); metrics::MetricsController::new( self.config.get_metrics_directory_server(), @@ -65,7 +65,7 @@ impl MixNode { .start(self.runtime.handle()) } - pub fn start_socket_listener( + fn start_socket_listener( &self, metrics_reporter: metrics::MetricsReporter, forwarding_channel: mpsc::UnboundedSender<(SocketAddr, Vec)>, @@ -84,7 +84,7 @@ impl MixNode { ); } - pub fn start_packet_forwarder(&mut self) -> mpsc::UnboundedSender<(SocketAddr, Vec)> { + fn start_packet_forwarder(&mut self) -> mpsc::UnboundedSender<(SocketAddr, Vec)> { info!("Starting packet forwarder..."); // this can later be replaced with topology information From da9593cee2a4d821462c2126be07851beb79bcc7 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 28 Feb 2020 12:33:21 +0000 Subject: [PATCH 235/273] Missed updated sphinx cargo.toml file --- nym-client/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 4d32bdba48..8d471ddb2e 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -40,7 +40,7 @@ sfw-provider-requests = { path = "../sfw-provider/sfw-provider-requests" } topology = {path = "../common/topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="4e51ed38a5de6fcb454c8903f1fe5e6b52720c77" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } # sphinx = { path = "../../sphinx"} [build-dependencies] From 1d36f73e891c849a54f6ce0ef7e220d58969aff1 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 28 Feb 2020 13:02:52 +0000 Subject: [PATCH 236/273] Handling of received sphinx packet (incl. store) --- .../src/provider/mix_handling/listener.rs | 78 ++++++++++++++ sfw-provider/src/provider/mix_handling/mod.rs | 101 +----------------- .../mix_handling/packet_processing.rs | 89 +++++++++++++++ 3 files changed, 169 insertions(+), 99 deletions(-) create mode 100644 sfw-provider/src/provider/mix_handling/listener.rs create mode 100644 sfw-provider/src/provider/mix_handling/packet_processing.rs diff --git a/sfw-provider/src/provider/mix_handling/listener.rs b/sfw-provider/src/provider/mix_handling/listener.rs new file mode 100644 index 0000000000..00a6b97062 --- /dev/null +++ b/sfw-provider/src/provider/mix_handling/listener.rs @@ -0,0 +1,78 @@ +use crate::provider::mix_handling::packet_processing::{MixProcessingResult, PacketProcessor}; +use log::*; +use std::io; +use std::net::SocketAddr; +use tokio::prelude::*; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; + +async fn process_received_packet( + packet_data: [u8; sphinx::PACKET_SIZE], + packet_processor: PacketProcessor, +) { + match packet_processor.process_sphinx_packet(packet_data).await { + Err(e) => debug!("We failed to process received sphinx packet - {:?}", e), + Ok(res) => match res { + MixProcessingResult::ForwardHop => { + error!("Somehow processed a forward hop message - those are not implemented for providers!") + } + MixProcessingResult::FinalHop => { + trace!("successfully processed [and stored] a final hop packet") + } + }, + } +} + +async fn process_socket_connection( + mut socket: tokio::net::TcpStream, + packet_processor: PacketProcessor, +) { + let mut buf = [0u8; sphinx::PACKET_SIZE]; + loop { + match socket.read(&mut buf).await { + // socket closed + Ok(n) if n == 0 => { + trace!("Remote connection closed."); + return; + } + Ok(n) => { + if n != sphinx::PACKET_SIZE { + warn!("read data of different length than expected sphinx packet size - {} (expected {})", n, sphinx::PACKET_SIZE); + continue; + } + + // we must be able to handle multiple packets from same connection independently + tokio::spawn(process_received_packet( + buf.clone(), + packet_processor.clone(), + )) + } + Err(e) => { + warn!( + "failed to read from socket. Closing the connection; err = {:?}", + e + ); + return; + } + }; + } +} + +pub(crate) fn run_mix_socket_listener( + handle: &Handle, + addr: SocketAddr, + packet_processor: PacketProcessor, +) -> JoinHandle> { + let handle_clone = handle.clone(); + handle.spawn(async move { + let mut listener = tokio::net::TcpListener::bind(addr).await?; + loop { + let (socket, _) = listener.accept().await?; + + let thread_packet_processor = packet_processor.clone(); + handle_clone.spawn(async move { + process_socket_connection(socket, thread_packet_processor).await; + }); + } + }) +} diff --git a/sfw-provider/src/provider/mix_handling/mod.rs b/sfw-provider/src/provider/mix_handling/mod.rs index b50303331c..ad8693dddb 100644 --- a/sfw-provider/src/provider/mix_handling/mod.rs +++ b/sfw-provider/src/provider/mix_handling/mod.rs @@ -5,102 +5,5 @@ use sphinx::{ProcessedPacket, SphinxPacket}; use std::path::PathBuf; use std::sync::{Arc, RwLock}; -// TODO: this will probably need to be moved elsewhere I imagine -// DUPLICATE WITH MIXNODE CODE!!! -#[derive(Debug)] -pub enum MixProcessingError { - FileIOFailure, - InvalidPayload, - NonMatchingRecipient, - ReceivedForwardHopError, - SphinxRecoveryError, - SphinxProcessingError, -} - -impl From for MixProcessingError { - // for time being just have a single error instance for all possible results of sphinx::ProcessingError - fn from(_: sphinx::ProcessingError) -> Self { - use MixProcessingError::*; - - SphinxRecoveryError - } -} - -impl From for MixProcessingError { - fn from(_: std::io::Error) -> Self { - use MixProcessingError::*; - - FileIOFailure - } -} - -// ProcessingData defines all data required to correctly unwrap sphinx packets -#[derive(Debug, Clone)] -pub(crate) struct MixProcessingData { - secret_key: encryption::PrivateKey, - pub(crate) store_dir: PathBuf, - new_messages_filename_length: u16, -} - -impl MixProcessingData { - pub(crate) fn new( - secret_key: encryption::PrivateKey, - store_dir: PathBuf, - new_messages_filename_length: u16, - ) -> Self { - MixProcessingData { - secret_key, - store_dir, - new_messages_filename_length, - } - } - - pub(crate) fn add_arc_rwlock(self) -> Arc> { - Arc::new(RwLock::new(self)) - } -} - -pub(crate) struct MixPacketProcessor(()); - -impl MixPacketProcessor { - pub fn process_sphinx_data_packet( - packet_data: &[u8], - processing_data: &RwLock, - ) -> Result { - let packet = SphinxPacket::from_bytes(packet_data)?; - let read_processing_data = match processing_data.read() { - Ok(guard) => guard, - Err(e) => { - error!("processing data lock was poisoned! - {:?}", e); - std::process::exit(1) - } - }; - let (client_address, client_surb_id, payload) = - match packet.process(read_processing_data.secret_key.inner()) { - Ok(ProcessedPacket::ProcessedPacketFinalHop(client_address, surb_id, payload)) => { - (client_address, surb_id, payload) - } - Ok(_) => return Err(MixProcessingError::ReceivedForwardHopError), - Err(e) => { - warn!("Error unwrapping Sphinx packet: {:?}", e); - return Err(MixProcessingError::SphinxProcessingError); - } - }; - - // TODO: should provider try to be recovering plaintext? this would potentially make client retrieve messages of non-constant length, - // perhaps provider should be re-padding them on retrieval or storing full data? - let (payload_destination, message) = payload - .try_recover_destination_and_plaintext() - .ok_or_else(|| MixProcessingError::InvalidPayload)?; - if client_address != payload_destination { - return Err(MixProcessingError::NonMatchingRecipient); - } - - Ok(StoreData::new( - client_address, - client_surb_id, - message, - read_processing_data.new_messages_filename_length, - )) - } -} +pub(crate) mod listener; +pub(crate) mod packet_processing; diff --git a/sfw-provider/src/provider/mix_handling/packet_processing.rs b/sfw-provider/src/provider/mix_handling/packet_processing.rs new file mode 100644 index 0000000000..f7c2d6098e --- /dev/null +++ b/sfw-provider/src/provider/mix_handling/packet_processing.rs @@ -0,0 +1,89 @@ +use crate::provider::storage::{ClientStorage, StoreData}; +use crypto::encryption; +use sphinx::route::{DestinationAddressBytes, SURBIdentifier}; +use sphinx::{ProcessedPacket, SphinxPacket}; +use std::ops::Deref; +use std::sync::Arc; + +#[derive(Debug)] +pub enum MixProcessingError { + ReceivedForwardHopError, + NonMatchingRecipient, + InvalidPayload, + SphinxProcessingError, + InvalidHopAddress, +} + +pub enum MixProcessingResult { + #[allow(dead_code)] + ForwardHop, + FinalHop, +} + +impl From for MixProcessingError { + // for time being just have a single error instance for all possible results of sphinx::ProcessingError + fn from(_: sphinx::ProcessingError) -> Self { + use MixProcessingError::*; + + SphinxProcessingError + } +} + +// PacketProcessor contains all data required to correctly unwrap and store sphinx packets +#[derive(Clone)] +pub struct PacketProcessor { + secret_key: Arc, + client_store: ClientStorage, +} + +impl PacketProcessor { + pub(crate) fn new(secret_key: encryption::PrivateKey, client_store: ClientStorage) -> Self { + PacketProcessor { + secret_key: Arc::new(secret_key), + client_store, + } + } + + async fn process_final_hop( + &self, + client_address: DestinationAddressBytes, + surb_id: SURBIdentifier, + payload: Payload, + ) -> Result { + // TODO: should provider try to be recovering plaintext? this would potentially make client retrieve messages of non-constant length, + // perhaps provider should be re-padding them on retrieval or storing full data? + let (payload_destination, message) = payload + .try_recover_destination_and_plaintext() + .ok_or_else(|| MixProcessingError::InvalidPayload)?; + if client_address != payload_destination { + return Err(MixProcessingError::NonMatchingRecipient); + } + + let store_data = StoreData::new(client_address, surb_id, message); + self.client_store.store_processed_data(store_data).await?; + + Ok(MixProcessingResult::FinalHop) + } + + pub(crate) async fn process_sphinx_packet( + &self, + raw_packet_data: [u8; sphinx::PACKET_SIZE], + ) -> Result { + let packet = SphinxPacket::from_bytes(&raw_packet_data)?; + + match packet.process(self.secret_key.deref().inner()) { + Ok(ProcessedPacket::ProcessedPacketForwardHop(_, _, _)) => { + warn!("Received a forward hop message - those are not implemented for providers"); + Err(MixProcessingError::ReceivedForwardHopError) + } + Ok(ProcessedPacket::ProcessedPacketFinalHop(client_address, surb_id, payload)) => { + self.process_final_hop(client_address, surb_id, payload) + .await + } + Err(e) => { + warn!("Failed to unwrap Sphinx packet: {:?}", e); + Err(MixProcessingError::SphinxProcessingError) + } + } + } +} From ec93fdbbdea328816cbc64aa8fd687840be6cc39 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 28 Feb 2020 13:03:29 +0000 Subject: [PATCH 237/273] Extra comments + derived traits --- mixnode/src/node/metrics.rs | 2 ++ sfw-provider/src/provider/storage/mod.rs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/mixnode/src/node/metrics.rs b/mixnode/src/node/metrics.rs index 3bb5b97c82..cf8e52e942 100644 --- a/mixnode/src/node/metrics.rs +++ b/mixnode/src/node/metrics.rs @@ -17,6 +17,8 @@ pub(crate) enum MetricEvent { } #[derive(Debug, Clone)] +// Note: you should NEVER create more than a single instance of this using 'new()'. +// You should always use .clone() to create additional instances struct MixMetrics { inner: Arc>, } diff --git a/sfw-provider/src/provider/storage/mod.rs b/sfw-provider/src/provider/storage/mod.rs index 6b2725a25d..458e4e1061 100644 --- a/sfw-provider/src/provider/storage/mod.rs +++ b/sfw-provider/src/provider/storage/mod.rs @@ -50,6 +50,9 @@ impl StoreData { } // TODO: replace with proper database... +// Note: you should NEVER create more than a single instance of this using 'new()'. +// You should always use .clone() to create additional instances +#[derive(Clone, Debug)] pub struct ClientStorage { inner: Arc>, } From a4c9bc1df6bf85f4aea6444d34e20bd06acc9e53 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 28 Feb 2020 13:03:43 +0000 Subject: [PATCH 238/273] Adjusted clients ledger --- .../src/provider/client_handling/ledger.rs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 sfw-provider/src/provider/client_handling/ledger.rs diff --git a/sfw-provider/src/provider/client_handling/ledger.rs b/sfw-provider/src/provider/client_handling/ledger.rs new file mode 100644 index 0000000000..8f3115ce16 --- /dev/null +++ b/sfw-provider/src/provider/client_handling/ledger.rs @@ -0,0 +1,60 @@ +use directory_client::presence::providers::MixProviderClient; +use futures::lock::Mutex; +use sfw_provider_requests::AuthToken; +use sphinx::route::DestinationAddressBytes; +use std::collections::HashMap; +use std::io; +use std::path::PathBuf; +use std::sync::Arc; + +#[derive(Debug, Clone)] +// Note: you should NEVER create more than a single instance of this using 'new()'. +// You should always use .clone() to create additional instances +pub struct ClientLedger { + inner: Arc>, +} + +impl ClientLedger { + pub(crate) fn new() -> Self { + ClientLedger { + inner: Arc::new(Mutex::new(ClientLedgerInner(HashMap::new()))), + } + } + + async fn has_token(&self, auth_token: &AuthToken) -> bool { + self.inner.lock().await.0.contains_key(auth_token) + } + + pub(crate) async fn insert_token( + &mut self, + auth_token: AuthToken, + client_address: DestinationAddressBytes, + ) -> Option { + self.inner.lock().await.0.insert(auth_token, client_address) + } + + pub(crate) async fn current_clients(&self) -> Vec { + self.inner + .lock() + .await + .0 + .iter() + .map(|(_, v)| bs58::encode(v).into_string()) + .map(|pub_key| MixProviderClient { pub_key }) + .collect() + } + + #[allow(dead_code)] + pub(crate) fn load(_file: PathBuf) -> Self { + // TODO: actual loading, + // temporarily just create a new one + Self::new() + } + + #[allow(dead_code)] + pub(crate) async fn save(&self, _file: PathBuf) -> io::Result<()> { + unimplemented!() + } +} + +struct ClientLedgerInner(HashMap); From 73ea1e29e078194a1621bbfc0e646127c18175ff Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 28 Feb 2020 13:03:55 +0000 Subject: [PATCH 239/273] Pending work --- .../src/provider/client_handling/listener.rs | 161 ++++++++ .../src/provider/client_handling/mod.rs | 176 ++++----- .../client_handling/request_processing.rs | 16 + sfw-provider/src/provider/mod.rs | 360 ++++-------------- sfw-provider/src/provider/presence.rs | 26 +- 5 files changed, 358 insertions(+), 381 deletions(-) create mode 100644 sfw-provider/src/provider/client_handling/listener.rs create mode 100644 sfw-provider/src/provider/client_handling/request_processing.rs diff --git a/sfw-provider/src/provider/client_handling/listener.rs b/sfw-provider/src/provider/client_handling/listener.rs new file mode 100644 index 0000000000..f81707f622 --- /dev/null +++ b/sfw-provider/src/provider/client_handling/listener.rs @@ -0,0 +1,161 @@ +use crate::provider::client_handling::request_processing::RequestProcessor; +use log::*; +use std::io; +use std::net::SocketAddr; +use tokio::prelude::*; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; + +async fn process_socket_connection( + mut socket: tokio::net::TcpStream, + request_processor: RequestProcessor, +) { + let mut buf = [0u8; sphinx::PACKET_SIZE]; + loop { + match socket.read(&mut buf).await { + // socket closed + Ok(n) if n == 0 => { + trace!("Remote connection closed."); + return; + } + Ok(n) => { + if n != sphinx::PACKET_SIZE { + warn!("read data of different length than expected sphinx packet size - {} (expected {})", n, sphinx::PACKET_SIZE); + continue; + } + + tokio::spawn(async {}); + + // // we must be able to handle multiple packets from same connection independently + // tokio::spawn(process_received_packet( + // buf.clone(), + // // TODO: + // // TODO: + // // note: processing_data is relatively cheap (and safe) to clone - + // // it contains arc to private key and metrics reporter (which is just + // // a single mpsc unbounded sender) + // request_processor.clone(), + // )) + } + Err(e) => { + warn!( + "failed to read from socket. Closing the connection; err = {:?}", + e + ); + return; + } + }; + } +} + +// // TODO: FIGURE OUT HOW TO SET READ_DEADLINES IN TOKIO +// async fn process_client_socket_connection( +// mut socket: tokio::net::TcpStream, +// processing_data: Arc, +// ) { +// let mut buf = [0; 1024]; +// +// // TODO: restore the for loop once we go back to persistent tcp socket connection +// let response = match socket.read(&mut buf).await { +// // socket closed +// Ok(n) if n == 0 => { +// trace!("Remote connection closed."); +// Err(()) +// } +// Ok(n) => { +// match ClientRequestProcessor::process_client_request( +// buf[..n].as_ref(), +// processing_data, +// ) +// .await +// { +// Err(e) => { +// warn!("failed to process client request; err = {:?}", e); +// Err(()) +// } +// Ok(res) => Ok(res), +// } +// } +// Err(e) => { +// warn!("failed to read from socket; err = {:?}", e); +// Err(()) +// } +// }; +// +// if let Err(e) = socket.shutdown(Shutdown::Read) { +// warn!("failed to close read part of the socket; err = {:?}", e) +// } +// +// match response { +// Ok(res) => { +// ServiceProvider::send_response(socket, &res).await; +// } +// _ => { +// ServiceProvider::send_response(socket, b"bad foomp").await; +// } +// } +// } + +// async fn send_response(mut socket: tokio::net::TcpStream, data: &[u8]) { +// if let Err(e) = socket.write_all(data).await { +// warn!("failed to write reply to socket; err = {:?}", e) +// } +// if let Err(e) = socket.shutdown(Shutdown::Write) { +// warn!("failed to close write part of the socket; err = {:?}", e) +// } +// } + +// async fn start_client_listening( +// address: SocketAddr, +// store_dir: PathBuf, +// client_ledger: Arc>, +// secret_key: encryption::PrivateKey, +// message_retrieval_limit: u16, +// ) -> Result<(), ProviderError> { +// let mut listener = tokio::net::TcpListener::bind(address).await?; +// let processing_data = ClientProcessingData::new( +// store_dir, +// client_ledger, +// secret_key, +// message_retrieval_limit, +// ) +// .add_arc(); +// +// loop { +// let (socket, _) = listener.accept().await?; +// // do note that the underlying data is NOT copied here; arc is incremented and lock is shared +// // (if I understand it all correctly) +// let thread_processing_data = processing_data.clone(); +// tokio::spawn(async move { +// ServiceProvider::process_client_socket_connection(socket, thread_processing_data) +// .await +// }); +// } +// } + +// let client_future = rt.spawn(ServiceProvider::start_client_listening( +// self.config.get_clients_listening_address(), +// self.config.get_clients_inboxes_dir(), +// thread_shareable_ledger, +// self.sphinx_keypair.private_key().clone(), // CLONE IS DONE TEMPORARILY UNTIL PROVIDER IS REFACTORED THE MIXNODE STYLE +// self.config.get_message_retrieval_limit(), +// )); + +pub(crate) fn run_client_socket_listener( + handle: &Handle, + addr: SocketAddr, + request_processor: RequestProcessor, +) -> JoinHandle> { + let handle_clone = handle.clone(); + handle.spawn(async move { + let mut listener = tokio::net::TcpListener::bind(addr).await?; + loop { + let (socket, _) = listener.accept().await?; + + let thread_request_processor = request_processor.clone(); + handle_clone.spawn(async move { + process_socket_connection(socket, thread_request_processor).await; + }); + } + }) +} diff --git a/sfw-provider/src/provider/client_handling/mod.rs b/sfw-provider/src/provider/client_handling/mod.rs index 3890316548..5f20b253f6 100644 --- a/sfw-provider/src/provider/client_handling/mod.rs +++ b/sfw-provider/src/provider/client_handling/mod.rs @@ -1,5 +1,5 @@ +use crate::provider::client_handling::ledger::ClientLedger; use crate::provider::storage::{ClientStorage, StoreError}; -use crate::provider::ClientLedger; use crypto::encryption; use futures::lock::Mutex as FMutex; use hmac::{Hmac, Mac}; @@ -16,6 +16,10 @@ use std::sync::Arc; type HmacSha256 = Hmac; +pub(crate) mod ledger; +pub(crate) mod listener; +pub(crate) mod request_processing; + #[derive(Debug)] pub enum ClientProcessingError { ClientDoesntExistError, @@ -81,91 +85,91 @@ impl ClientProcessingData { pub(crate) struct ClientRequestProcessor; impl ClientRequestProcessor { - pub(crate) async fn process_client_request( - data: &[u8], - processing_data: Arc, - ) -> Result, ClientProcessingError> { - let client_request = ProviderRequests::from_bytes(&data)?; - trace!("Received the following request: {:?}", client_request); - match client_request { - ProviderRequests::Register(req) => Ok(ClientRequestProcessor::register_new_client( - req, - processing_data, - ) - .await? - .to_bytes()), - ProviderRequests::PullMessages(req) => Ok( - ClientRequestProcessor::process_pull_messages_request(req, processing_data) - .await? - .to_bytes(), - ), - } - } - - async fn process_pull_messages_request( - req: PullRequest, - processing_data: Arc, - ) -> Result { - // TODO: this lock is completely unnecessary as we're only reading the data. - // Wait for https://github.com/nymtech/nym-sfw-provider/issues/19 to resolve. - let unlocked_ledger = processing_data.registered_clients_ledger.lock().await; - - if unlocked_ledger.has_token(&req.auth_token) { - // drop the mutex so that we could do IO without blocking others wanting to get the lock - drop(unlocked_ledger); - let retrieved_messages = ClientStorage::retrieve_client_files( - req.destination_address, - processing_data.store_dir.as_path(), - processing_data.message_retrieval_limit, - )?; - Ok(PullResponse::new(retrieved_messages)) - } else { - Err(ClientProcessingError::WrongToken) - } - } - - async fn register_new_client( - req: RegisterRequest, - processing_data: Arc, - ) -> Result { - debug!( - "Processing register new client request: {:?}", - req.destination_address - ); - let mut unlocked_ledger = processing_data.registered_clients_ledger.lock().await; - - let auth_token = ClientRequestProcessor::generate_new_auth_token( - req.destination_address.to_vec(), - &processing_data.secret_key, - ); - if !unlocked_ledger.has_token(&auth_token) { - unlocked_ledger.insert_token(auth_token.clone(), req.destination_address); - ClientRequestProcessor::create_storage_dir( - req.destination_address, - processing_data.store_dir.as_path(), - )?; - } - Ok(RegisterResponse::new(auth_token)) - } - - fn create_storage_dir( - client_address: sphinx::route::DestinationAddressBytes, - store_dir: &Path, - ) -> io::Result<()> { - let client_dir_name = bs58::encode(client_address).into_string(); - let full_store_dir = store_dir.join(client_dir_name); - std::fs::create_dir_all(full_store_dir) - } - - fn generate_new_auth_token(data: Vec, key: &encryption::PrivateKey) -> AuthToken { - // also note that `new_varkey` doesn't even have an execution branch returning an error - let mut auth_token_raw = HmacSha256::new_varkey(&key.to_bytes()) - .expect("HMAC should be able take key of any size"); - auth_token_raw.input(&data); - let mut auth_token = [0u8; 32]; - auth_token.copy_from_slice(&auth_token_raw.result().code().to_vec()); - AuthToken(auth_token) - } + // pub(crate) async fn process_client_request( + // data: &[u8], + // processing_data: Arc, + // ) -> Result, ClientProcessingError> { + // let client_request = ProviderRequests::from_bytes(&data)?; + // trace!("Received the following request: {:?}", client_request); + // match client_request { + // ProviderRequests::Register(req) => Ok(ClientRequestProcessor::register_new_client( + // req, + // processing_data, + // ) + // .await? + // .to_bytes()), + // ProviderRequests::PullMessages(req) => Ok( + // ClientRequestProcessor::process_pull_messages_request(req, processing_data) + // .await? + // .to_bytes(), + // ), + // } + // } + // + // async fn process_pull_messages_request( + // req: PullRequest, + // processing_data: Arc, + // ) -> Result { + // // TODO: this lock is completely unnecessary as we're only reading the data. + // // Wait for https://github.com/nymtech/nym-sfw-provider/issues/19 to resolve. + // let unlocked_ledger = processing_data.registered_clients_ledger.lock().await; + // + // if unlocked_ledger.has_token(&req.auth_token) { + // // drop the mutex so that we could do IO without blocking others wanting to get the lock + // drop(unlocked_ledger); + // let retrieved_messages = ClientStorage::retrieve_client_files( + // req.destination_address, + // processing_data.store_dir.as_path(), + // processing_data.message_retrieval_limit, + // )?; + // Ok(PullResponse::new(retrieved_messages)) + // } else { + // Err(ClientProcessingError::WrongToken) + // } + // } + // + // async fn register_new_client( + // req: RegisterRequest, + // processing_data: Arc, + // ) -> Result { + // debug!( + // "Processing register new client request: {:?}", + // req.destination_address + // ); + // let mut unlocked_ledger = processing_data.registered_clients_ledger.lock().await; + // + // let auth_token = ClientRequestProcessor::generate_new_auth_token( + // req.destination_address.to_vec(), + // &processing_data.secret_key, + // ); + // if !unlocked_ledger.has_token(&auth_token) { + // unlocked_ledger.insert_token(auth_token.clone(), req.destination_address); + // ClientRequestProcessor::create_storage_dir( + // req.destination_address, + // processing_data.store_dir.as_path(), + // )?; + // } + // Ok(RegisterResponse::new(auth_token)) + // } + // + // fn create_storage_dir( + // client_address: sphinx::route::DestinationAddressBytes, + // store_dir: &Path, + // ) -> io::Result<()> { + // let client_dir_name = bs58::encode(client_address).into_string(); + // let full_store_dir = store_dir.join(client_dir_name); + // std::fs::create_dir_all(full_store_dir) + // } + // + // fn generate_new_auth_token(data: Vec, key: &encryption::PrivateKey) -> AuthToken { + // // also note that `new_varkey` doesn't even have an execution branch returning an error + // let mut auth_token_raw = HmacSha256::new_varkey(&key.to_bytes()) + // .expect("HMAC should be able take key of any size"); + // auth_token_raw.input(&data); + // let mut auth_token = [0u8; 32]; + // auth_token.copy_from_slice(&auth_token_raw.result().code().to_vec()); + // AuthToken(auth_token) + // } } #[cfg(test)] diff --git a/sfw-provider/src/provider/client_handling/request_processing.rs b/sfw-provider/src/provider/client_handling/request_processing.rs new file mode 100644 index 0000000000..fe8faba5d5 --- /dev/null +++ b/sfw-provider/src/provider/client_handling/request_processing.rs @@ -0,0 +1,16 @@ +use crypto::encryption; +use std::sync::Arc; + +// PacketProcessor contains all data required to correctly process client requests +#[derive(Clone)] +pub struct RequestProcessor { + secret_key: Arc, +} + +impl RequestProcessor { + pub(crate) fn new(secret_key: encryption::PrivateKey) -> Self { + RequestProcessor { + secret_key: Arc::new(secret_key), + } + } +} diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index fa6518e118..02b656723b 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -1,22 +1,9 @@ use crate::config::persistance::pathfinder::ProviderPathfinder; use crate::config::Config; -use crate::provider::client_handling::{ClientProcessingData, ClientRequestProcessor}; -use crate::provider::mix_handling::{MixPacketProcessor, MixProcessingData}; -use crate::provider::storage::ClientStorage; +use crate::provider::client_handling::ledger::ClientLedger; use crypto::encryption; -use directory_client::presence::providers::MixProviderClient; -use futures::io::Error; -use futures::lock::Mutex as FMutex; use log::*; use pemstore::pemstore::PemStore; -use sfw_provider_requests::AuthToken; -use sphinx::route::DestinationAddressBytes; -use std::collections::HashMap; -use std::net::{Shutdown, SocketAddr}; -use std::path::PathBuf; -use std::sync::Arc; -use std::sync::RwLock; -use tokio::prelude::*; use tokio::runtime::Runtime; mod client_handling; @@ -24,88 +11,40 @@ mod mix_handling; pub mod presence; mod storage; -#[derive(Debug)] -pub enum ProviderError { - TcpListenerBindingError, - TcpListenerConnectionError, - TcpListenerUnexpectedEof, - - TcpListenerUnknownError, -} - -impl From for ProviderError { - fn from(err: Error) -> Self { - use ProviderError::*; - match err.kind() { - io::ErrorKind::ConnectionRefused => TcpListenerConnectionError, - io::ErrorKind::ConnectionReset => TcpListenerConnectionError, - io::ErrorKind::ConnectionAborted => TcpListenerConnectionError, - io::ErrorKind::NotConnected => TcpListenerConnectionError, - - io::ErrorKind::AddrInUse => TcpListenerBindingError, - io::ErrorKind::AddrNotAvailable => TcpListenerBindingError, - io::ErrorKind::UnexpectedEof => TcpListenerUnexpectedEof, - _ => TcpListenerUnknownError, - } - } -} - -#[derive(Debug)] -pub struct ClientLedger(HashMap); - -impl ClientLedger { - fn new() -> Self { - ClientLedger(HashMap::new()) - } - - fn add_arc_futures_mutex(self) -> Arc> { - Arc::new(FMutex::new(self)) - } - - fn has_token(&self, auth_token: &AuthToken) -> bool { - self.0.contains_key(auth_token) - } - - fn insert_token( - &mut self, - auth_token: AuthToken, - client_address: DestinationAddressBytes, - ) -> Option { - self.0.insert(auth_token, client_address) - } - - fn current_clients(&self) -> Vec { - self.0 - .iter() - .map(|(_, v)| bs58::encode(v).into_string()) - .map(|pub_key| MixProviderClient { pub_key }) - .collect() - } - - #[allow(dead_code)] - fn load(_file: PathBuf) -> Self { - unimplemented!() - } -} +// #[derive(Debug)] +// pub enum ProviderError { +// TcpListenerBindingError, +// TcpListenerConnectionError, +// TcpListenerUnexpectedEof, +// +// TcpListenerUnknownError, +// } +// +// impl From for ProviderError { +// fn from(err: Error) -> Self { +// use ProviderError::*; +// match err.kind() { +// io::ErrorKind::ConnectionRefused => TcpListenerConnectionError, +// io::ErrorKind::ConnectionReset => TcpListenerConnectionError, +// io::ErrorKind::ConnectionAborted => TcpListenerConnectionError, +// io::ErrorKind::NotConnected => TcpListenerConnectionError, +// +// io::ErrorKind::AddrInUse => TcpListenerBindingError, +// io::ErrorKind::AddrNotAvailable => TcpListenerBindingError, +// io::ErrorKind::UnexpectedEof => TcpListenerUnexpectedEof, +// _ => TcpListenerUnknownError, +// } +// } +// } pub struct ServiceProvider { + runtime: Runtime, config: Config, sphinx_keypair: encryption::KeyPair, registered_clients_ledger: ClientLedger, } impl ServiceProvider { - pub fn new(config: Config) -> Self { - let sphinx_keypair = Self::load_sphinx_keys(&config); - - ServiceProvider { - config, - sphinx_keypair, - // TODO: load initial ledger from file - registered_clients_ledger: ClientLedger::new(), - } - } - fn load_sphinx_keys(config_file: &Config) -> encryption::KeyPair { let sphinx_keypair = PemStore::new(ProviderPathfinder::new_from_config(&config_file)) .read_encryption() @@ -117,177 +56,19 @@ impl ServiceProvider { sphinx_keypair } - async fn process_mixnet_socket_connection( - mut socket: tokio::net::TcpStream, - processing_data: Arc>, - ) { - let mut buf = [0u8; sphinx::PACKET_SIZE]; - - // In a loop, read data from the socket and write the data back. - loop { - match socket.read(&mut buf).await { - // socket closed - Ok(n) if n == 0 => { - trace!("Remote connection closed."); - return; - } - Ok(_) => { - let store_data = match MixPacketProcessor::process_sphinx_data_packet( - buf.as_ref(), - processing_data.as_ref(), - ) { - Ok(sd) => sd, - Err(e) => { - warn!("failed to process sphinx packet; err = {:?}", e); - return; - } - }; - let processing_data_lock = match processing_data.read() { - Ok(guard) => guard, - Err(e) => { - error!("processing data lock was poisoned! - {:?}", e); - std::process::exit(1) - } - }; - ClientStorage::store_processed_data( - store_data, - processing_data_lock.store_dir.as_path(), - ) - .unwrap_or_else(|e| { - error!("failed to store processed sphinx message; err = {:?}", e); - }); - } - Err(e) => { - warn!("failed to read from socket; err = {:?}", e); - return; - } - }; - - // Write the some data back - if let Err(e) = socket.write_all(b"foomp").await { - warn!("failed to write reply to socket; err = {:?}", e); - return; - } + pub fn new(config: Config) -> Self { + let sphinx_keypair = Self::load_sphinx_keys(&config); + let registered_clients_ledger = ClientLedger::load(config.get_clients_ledger_path()); + ServiceProvider { + runtime: Runtime::new().unwrap(), + config, + sphinx_keypair, + registered_clients_ledger, } } - async fn send_response(mut socket: tokio::net::TcpStream, data: &[u8]) { - if let Err(e) = socket.write_all(data).await { - warn!("failed to write reply to socket; err = {:?}", e) - } - if let Err(e) = socket.shutdown(Shutdown::Write) { - warn!("failed to close write part of the socket; err = {:?}", e) - } - } - - // TODO: FIGURE OUT HOW TO SET READ_DEADLINES IN TOKIO - async fn process_client_socket_connection( - mut socket: tokio::net::TcpStream, - processing_data: Arc, - ) { - let mut buf = [0; 1024]; - - // TODO: restore the for loop once we go back to persistent tcp socket connection - let response = match socket.read(&mut buf).await { - // socket closed - Ok(n) if n == 0 => { - trace!("Remote connection closed."); - Err(()) - } - Ok(n) => { - match ClientRequestProcessor::process_client_request( - buf[..n].as_ref(), - processing_data, - ) - .await - { - Err(e) => { - warn!("failed to process client request; err = {:?}", e); - Err(()) - } - Ok(res) => Ok(res), - } - } - Err(e) => { - warn!("failed to read from socket; err = {:?}", e); - Err(()) - } - }; - - if let Err(e) = socket.shutdown(Shutdown::Read) { - warn!("failed to close read part of the socket; err = {:?}", e) - } - - match response { - Ok(res) => { - ServiceProvider::send_response(socket, &res).await; - } - _ => { - ServiceProvider::send_response(socket, b"bad foomp").await; - } - } - } - - async fn start_mixnet_listening( - address: SocketAddr, - secret_key: encryption::PrivateKey, - store_dir: PathBuf, - new_messages_filename_length: u16, - ) -> Result<(), ProviderError> { - let mut listener = tokio::net::TcpListener::bind(address).await?; - let processing_data = - MixProcessingData::new(secret_key, store_dir, new_messages_filename_length) - .add_arc_rwlock(); - - loop { - let (socket, _) = listener.accept().await?; - // do note that the underlying data is NOT copied here; arc is incremented and lock is shared - // (if I understand it all correctly) - let thread_processing_data = processing_data.clone(); - tokio::spawn(async move { - ServiceProvider::process_mixnet_socket_connection(socket, thread_processing_data) - .await - }); - } - } - - async fn start_client_listening( - address: SocketAddr, - store_dir: PathBuf, - client_ledger: Arc>, - secret_key: encryption::PrivateKey, - message_retrieval_limit: u16, - ) -> Result<(), ProviderError> { - let mut listener = tokio::net::TcpListener::bind(address).await?; - let processing_data = ClientProcessingData::new( - store_dir, - client_ledger, - secret_key, - message_retrieval_limit, - ) - .add_arc(); - - loop { - let (socket, _) = listener.accept().await?; - // do note that the underlying data is NOT copied here; arc is incremented and lock is shared - // (if I understand it all correctly) - let thread_processing_data = processing_data.clone(); - tokio::spawn(async move { - ServiceProvider::process_client_socket_connection(socket, thread_processing_data) - .await - }); - } - } - - pub fn start(self) -> Result<(), Box> { - // Create the runtime, probably later move it to Provider struct itself? - // TODO: figure out the difference between Runtime and Handle - let mut rt = Runtime::new()?; - // let mut h = rt.handle(); - - let initial_client_ledger = self.registered_clients_ledger; - let thread_shareable_ledger = initial_client_ledger.add_arc_futures_mutex(); - + fn start_presence_notifier(&self) { + info!("Starting presence notifier..."); let notifier_config = presence::NotifierConfig::new( self.config.get_presence_directory_server(), self.config.get_mix_announce_address(), @@ -295,35 +76,50 @@ impl ServiceProvider { self.sphinx_keypair.public_key().to_base58_string(), self.config.get_presence_sending_delay(), ); + presence::Notifier::new(notifier_config, self.registered_clients_ledger.clone()) + .start(self.runtime.handle()); + } - let presence_future = rt.spawn({ - let presence_notifier = - presence::Notifier::new(notifier_config, thread_shareable_ledger.clone()); - presence_notifier.run() - }); + fn start_mix_socket_listener(&self) { + info!("Starting client socket listener..."); + let packet_processor = mix_handling::packet_processing::PacketProcessor::new( + self.sphinx_keypair.private_key().clone(), + ); - let mix_future = rt.spawn(ServiceProvider::start_mixnet_listening( + mix_handling::listener::run_mix_socket_listener( + self.runtime.handle(), self.config.get_mix_listening_address(), - self.sphinx_keypair.private_key().clone(), // CLONE IS DONE TEMPORARILY UNTIL PROVIDER IS REFACTORED THE MIXNODE STYLE - self.config.get_clients_inboxes_dir(), - self.config.get_stored_messages_filename_length(), - )); - let client_future = rt.spawn(ServiceProvider::start_client_listening( - self.config.get_clients_listening_address(), - self.config.get_clients_inboxes_dir(), - thread_shareable_ledger, - self.sphinx_keypair.private_key().clone(), // CLONE IS DONE TEMPORARILY UNTIL PROVIDER IS REFACTORED THE MIXNODE STYLE - self.config.get_message_retrieval_limit(), - )); - // Spawn the root task - rt.block_on(async { - let future_results = - futures::future::join3(mix_future, client_future, presence_future).await; - assert!(future_results.0.is_ok() && future_results.1.is_ok()); - }); + packet_processor, + ); + } - // this line in theory should never be reached as the runtime should be permanently blocked on listeners - error!("The server went kaput..."); - Ok(()) + fn start_client_socket_listener(&self) { + info!("Starting mix socket listener..."); + let packet_processor = client_handling::request_processing::RequestProcessor::new( + self.sphinx_keypair.private_key().clone(), + ); + + client_handling::listener::run_client_socket_listener( + self.runtime.handle(), + self.config.get_mix_listening_address(), + packet_processor, + ); + } + + pub fn run(&mut self) { + self.start_presence_notifier(); + self.start_mix_socket_listener(); + self.start_client_socket_listener(); + + if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) { + error!( + "There was an error while capturing SIGINT - {:?}. We will terminate regardless", + e + ); + } + + println!( + "Received SIGINT - the provider will terminate now (threads are not YET nicely stopped)" + ); } } diff --git a/sfw-provider/src/provider/presence.rs b/sfw-provider/src/provider/presence.rs index 67df2bba08..babdeacca6 100644 --- a/sfw-provider/src/provider/presence.rs +++ b/sfw-provider/src/provider/presence.rs @@ -3,10 +3,10 @@ use crate::provider::ClientLedger; use directory_client::presence::providers::MixProviderPresence; use directory_client::requests::presence_providers_post::PresenceMixProviderPoster; use directory_client::DirectoryClient; -use futures::lock::Mutex as FMutex; use log::{debug, error}; -use std::sync::Arc; use std::time::Duration; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; pub struct NotifierConfig { directory_server: String, @@ -36,7 +36,7 @@ impl NotifierConfig { pub struct Notifier { net_client: directory_client::Client, - client_ledger: Arc>, + client_ledger: ClientLedger, sending_delay: Duration, client_listener: String, mixnet_listener: String, @@ -44,7 +44,7 @@ pub struct Notifier { } impl Notifier { - pub fn new(config: NotifierConfig, client_ledger: Arc>) -> Notifier { + pub fn new(config: NotifierConfig, client_ledger: ClientLedger) -> Notifier { let directory_client_cfg = directory_client::Config { base_url: config.directory_server, }; @@ -61,13 +61,11 @@ impl Notifier { } async fn make_presence(&self) -> MixProviderPresence { - let unlocked_ledger = self.client_ledger.lock().await; - MixProviderPresence { client_listener: self.client_listener.clone(), mixnet_listener: self.mixnet_listener.clone(), pub_key: self.pub_key_string.clone(), - registered_clients: unlocked_ledger.current_clients(), + registered_clients: self.client_ledger.current_clients().await, last_seen: 0, version: built_info::PKG_VERSION.to_string(), } @@ -80,11 +78,13 @@ impl Notifier { } } - pub async fn run(self) { - loop { - let presence = self.make_presence().await; - self.notify(presence); - tokio::time::delay_for(self.sending_delay).await; - } + pub fn start(self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + loop { + let presence = self.make_presence().await; + self.notify(presence); + tokio::time::delay_for(self.sending_delay).await; + } + }) } } From 3851e9e350e847d4d62460732ce8ff17504ad25b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 09:52:51 +0000 Subject: [PATCH 240/273] Constructor for client storage --- sfw-provider/src/provider/storage/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sfw-provider/src/provider/storage/mod.rs b/sfw-provider/src/provider/storage/mod.rs index 458e4e1061..c0eeef3481 100644 --- a/sfw-provider/src/provider/storage/mod.rs +++ b/sfw-provider/src/provider/storage/mod.rs @@ -67,6 +67,16 @@ pub struct ClientStorageInner { // TODO: change it to some generic implementation to inject fs (or even better - proper database) impl ClientStorage { + pub(crate) fn new(message_limit: usize, filename_len: u16, main_store_dir: PathBuf) -> Self { + ClientStorage { + inner: Arc::new(Mutex::new(ClientStorageInner { + message_retrieval_limit: message_limit, + filename_length: filename_len, + main_store_path_dir: main_store_dir, + })), + } + } + pub(crate) fn generate_random_file_name(length: usize) -> String { rand::thread_rng() .sample_iter(&rand::distributions::Alphanumeric) From b8f0fdefee17f83353fb9f436365e62ab53b3174 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 11:12:07 +0000 Subject: [PATCH 241/273] made has token method on client ledger public --- sfw-provider/src/provider/client_handling/ledger.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sfw-provider/src/provider/client_handling/ledger.rs b/sfw-provider/src/provider/client_handling/ledger.rs index 8f3115ce16..e079782610 100644 --- a/sfw-provider/src/provider/client_handling/ledger.rs +++ b/sfw-provider/src/provider/client_handling/ledger.rs @@ -21,7 +21,7 @@ impl ClientLedger { } } - async fn has_token(&self, auth_token: &AuthToken) -> bool { + pub(crate) async fn has_token(&self, auth_token: &AuthToken) -> bool { self.inner.lock().await.0.contains_key(auth_token) } From b85bc53002d8acb85367702335122610ea26f30e Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 12:13:31 +0000 Subject: [PATCH 242/273] Fix to provider requests due to previous sphinx changes --- .../sfw-provider-requests/src/requests.rs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/sfw-provider/sfw-provider-requests/src/requests.rs b/sfw-provider/sfw-provider-requests/src/requests.rs index c8a15f44dc..06ab0a8741 100644 --- a/sfw-provider/sfw-provider-requests/src/requests.rs +++ b/sfw-provider/sfw-provider-requests/src/requests.rs @@ -79,7 +79,7 @@ impl ProviderRequest for PullRequest { Self::get_prefix() .to_vec() .into_iter() - .chain(self.destination_address.iter().cloned()) + .chain(self.destination_address.to_bytes().iter().cloned()) .chain(self.auth_token.0.iter().cloned()) .collect() } @@ -102,8 +102,8 @@ impl ProviderRequest for PullRequest { auth_token.copy_from_slice(&bytes[34..]); Ok(PullRequest { - auth_token: AuthToken(auth_token), - destination_address, + auth_token: AuthToken::from_bytes(auth_token), + destination_address: DestinationAddressBytes::from_bytes(destination_address), }) } } @@ -130,7 +130,7 @@ impl ProviderRequest for RegisterRequest { Self::get_prefix() .to_vec() .into_iter() - .chain(self.destination_address.iter().cloned()) + .chain(self.destination_address.to_bytes().iter().cloned()) .collect() } @@ -149,7 +149,7 @@ impl ProviderRequest for RegisterRequest { destination_address.copy_from_slice(&bytes[2..]); Ok(RegisterRequest { - destination_address, + destination_address: DestinationAddressBytes::from_bytes(destination_address), }) } } @@ -160,12 +160,12 @@ mod creating_pull_request { #[test] fn it_is_possible_to_recover_it_from_bytes() { - let address = [ + let address = DestinationAddressBytes::from_bytes([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, - ]; + ]); let auth_token = [1u8; 32]; - let pull_request = PullRequest::new(address, AuthToken(auth_token)); + let pull_request = PullRequest::new(address.clone(), AuthToken(auth_token)); let bytes = pull_request.to_bytes(); let recovered = PullRequest::from_bytes(&bytes).unwrap(); @@ -175,12 +175,12 @@ mod creating_pull_request { #[test] fn it_is_possible_to_recover_it_from_bytes_with_enum_wrapper() { - let address = [ + let address = DestinationAddressBytes::from_bytes([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, - ]; + ]); let auth_token = [1u8; 32]; - let pull_request = PullRequest::new(address, AuthToken(auth_token)); + let pull_request = PullRequest::new(address.clone(), AuthToken(auth_token)); let bytes = pull_request.to_bytes(); let recovered = ProviderRequests::from_bytes(&bytes).unwrap(); @@ -200,11 +200,11 @@ mod creating_register_request { #[test] fn it_is_possible_to_recover_it_from_bytes() { - let address = [ + let address = DestinationAddressBytes::from_bytes([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, - ]; - let register_request = RegisterRequest::new(address); + ]); + let register_request = RegisterRequest::new(address.clone()); let bytes = register_request.to_bytes(); let recovered = RegisterRequest::from_bytes(&bytes).unwrap(); @@ -213,11 +213,11 @@ mod creating_register_request { #[test] fn it_is_possible_to_recover_it_from_bytes_with_enum_wrapper() { - let address = [ + let address = DestinationAddressBytes::from_bytes([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, - ]; - let register_request = RegisterRequest::new(address); + ]); + let register_request = RegisterRequest::new(address.clone()); let bytes = register_request.to_bytes(); let recovered = ProviderRequests::from_bytes(&bytes).unwrap(); From 575bcaa43b396f9f76142e6b81f9c382c0fe5d02 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 12:13:53 +0000 Subject: [PATCH 243/273] Adjusted field access to AuthToken --- sfw-provider/sfw-provider-requests/src/lib.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sfw-provider/sfw-provider-requests/src/lib.rs b/sfw-provider/sfw-provider-requests/src/lib.rs index 6d307aaf42..7a4656e417 100644 --- a/sfw-provider/sfw-provider-requests/src/lib.rs +++ b/sfw-provider/sfw-provider-requests/src/lib.rs @@ -6,7 +6,7 @@ pub const DUMMY_MESSAGE_CONTENT: &[u8] = // To be renamed to 'AuthToken' once it is safe to replace it #[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct AuthToken(pub [u8; 32]); +pub struct AuthToken([u8; 32]); #[derive(Debug)] pub enum AuthTokenConversionError { @@ -15,6 +15,18 @@ pub enum AuthTokenConversionError { } impl AuthToken { + pub fn from_bytes(bytes: [u8; 32]) -> Self { + AuthToken(bytes) + } + + pub fn to_bytes(&self) -> [u8; 32] { + self.0 + } + + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + pub fn try_from_base58_string>( val: S, ) -> Result { From 1f69f09072dda899b335fc5d2098a4f23803a921 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 12:19:38 +0000 Subject: [PATCH 244/273] Fixed address derivation --- common/crypto/src/identity/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index 8f627e14aa..192b4911df 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -68,7 +68,7 @@ impl MixIdentityPublicKey { let public_key_bytes = self.to_bytes(); temporary_address.copy_from_slice(&public_key_bytes[..]); - temporary_address + DestinationAddressBytes::from_bytes(temporary_address) } pub fn to_bytes(&self) -> Vec { From 63dfbc1465d9f22a1ceab3e5ff817b981180b835 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 12:21:24 +0000 Subject: [PATCH 245/273] Using native to_base58_string in client ledger --- sfw-provider/src/provider/client_handling/ledger.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sfw-provider/src/provider/client_handling/ledger.rs b/sfw-provider/src/provider/client_handling/ledger.rs index e079782610..cb615d240f 100644 --- a/sfw-provider/src/provider/client_handling/ledger.rs +++ b/sfw-provider/src/provider/client_handling/ledger.rs @@ -39,7 +39,7 @@ impl ClientLedger { .await .0 .iter() - .map(|(_, v)| bs58::encode(v).into_string()) + .map(|(_, v)| v.to_base58_string()) .map(|pub_key| MixProviderClient { pub_key }) .collect() } From 3a2ad5ae106f5c748960d81d47b5d75d3652961d Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 15:40:12 +0000 Subject: [PATCH 246/273] sphinx version upgrade --- Cargo.lock | 2 +- common/clients/mix-client/Cargo.toml | 2 +- common/clients/provider-client/Cargo.toml | 2 +- common/crypto/Cargo.toml | 2 +- common/healthcheck/Cargo.toml | 2 +- common/topology/Cargo.toml | 2 +- mixnode/Cargo.toml | 2 +- nym-client/Cargo.toml | 2 +- sfw-provider/Cargo.toml | 2 +- sfw-provider/sfw-provider-requests/Cargo.toml | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e804a8610..2970ad85d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2328,7 +2328,7 @@ dependencies = [ [[package]] name = "sphinx" version = "0.1.0" -source = "git+https://github.com/nymtech/sphinx?rev=fde6f0523e5bd8e02e5bba0936be266271efb7cc#fde6f0523e5bd8e02e5bba0936be266271efb7cc" +source = "git+https://github.com/nymtech/sphinx?rev=23f9c89b257ee0936e70afd682e9ed6a62e89eee#23f9c89b257ee0936e70afd682e9ed6a62e89eee" dependencies = [ "aes-ctr", "arrayref", diff --git a/common/clients/mix-client/Cargo.toml b/common/clients/mix-client/Cargo.toml index bcdd0e2666..40ddb9306d 100644 --- a/common/clients/mix-client/Cargo.toml +++ b/common/clients/mix-client/Cargo.toml @@ -18,6 +18,6 @@ addressing = {path = "../../addressing"} topology = {path = "../../topology"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } # sphinx = { path = "../../../../sphinx"} diff --git a/common/clients/provider-client/Cargo.toml b/common/clients/provider-client/Cargo.toml index ff96bc16b0..f5ecafe9cc 100644 --- a/common/clients/provider-client/Cargo.toml +++ b/common/clients/provider-client/Cargo.toml @@ -16,4 +16,4 @@ tokio = { version = "0.2", features = ["full"] } sfw-provider-requests = { path = "../../../sfw-provider/sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } diff --git a/common/crypto/Cargo.toml b/common/crypto/Cargo.toml index 9966355263..f2a29d0ec2 100644 --- a/common/crypto/Cargo.toml +++ b/common/crypto/Cargo.toml @@ -15,4 +15,4 @@ rand = "0.7.2" rand_os = "0.1" ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } \ No newline at end of file +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } \ No newline at end of file diff --git a/common/healthcheck/Cargo.toml b/common/healthcheck/Cargo.toml index 81da13d03d..b8f7212792 100644 --- a/common/healthcheck/Cargo.toml +++ b/common/healthcheck/Cargo.toml @@ -27,7 +27,7 @@ sfw-provider-requests = { path = "../../sfw-provider/sfw-provider-requests" } topology = {path = "../topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } # sphinx = { path = "../../../sphinx"} [dev-dependencies] diff --git a/common/topology/Cargo.toml b/common/topology/Cargo.toml index d64ce55319..b414460c82 100644 --- a/common/topology/Cargo.toml +++ b/common/topology/Cargo.toml @@ -19,5 +19,5 @@ addressing = {path = "../addressing"} version-checker = {path = "../version-checker" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } # sphinx = { path = "../../../sphinx"} diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index 0a4541e2ee..e7ec30adba 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -28,7 +28,7 @@ multi-tcp-client = { path = "../common/clients/multi-tcp-client" } pemstore = {path = "../common/pemstore"} ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } [build-dependencies] built = "0.3.2" diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 8d471ddb2e..87ba8de733 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -40,7 +40,7 @@ sfw-provider-requests = { path = "../sfw-provider/sfw-provider-requests" } topology = {path = "../common/topology" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } # sphinx = { path = "../../sphinx"} [build-dependencies] diff --git a/sfw-provider/Cargo.toml b/sfw-provider/Cargo.toml index c082614b97..f32a530a81 100644 --- a/sfw-provider/Cargo.toml +++ b/sfw-provider/Cargo.toml @@ -31,7 +31,7 @@ pemstore = {path = "../common/pemstore"} sfw-provider-requests = { path = "./sfw-provider-requests" } ## will be moved to proper dependencies once released -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } [build-dependencies] built = "0.3.2" diff --git a/sfw-provider/sfw-provider-requests/Cargo.toml b/sfw-provider/sfw-provider-requests/Cargo.toml index 1db2954da4..a34bae654e 100644 --- a/sfw-provider/sfw-provider-requests/Cargo.toml +++ b/sfw-provider/sfw-provider-requests/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] bs58 = "0.3.0" -sphinx = { git = "https://github.com/nymtech/sphinx", rev="fde6f0523e5bd8e02e5bba0936be266271efb7cc" } +sphinx = { git = "https://github.com/nymtech/sphinx", rev="23f9c89b257ee0936e70afd682e9ed6a62e89eee" } From 30611d0a68a2fe4bb7a5fc2bf1c2dff90c93c3c9 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 15:43:52 +0000 Subject: [PATCH 247/273] Reversed Client ledger mapping --- .../src/provider/client_handling/ledger.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sfw-provider/src/provider/client_handling/ledger.rs b/sfw-provider/src/provider/client_handling/ledger.rs index cb615d240f..b8deea12d0 100644 --- a/sfw-provider/src/provider/client_handling/ledger.rs +++ b/sfw-provider/src/provider/client_handling/ledger.rs @@ -21,16 +21,16 @@ impl ClientLedger { } } - pub(crate) async fn has_token(&self, auth_token: &AuthToken) -> bool { - self.inner.lock().await.0.contains_key(auth_token) + pub(crate) async fn has_client(&self, client_address: &DestinationAddressBytes) -> bool { + self.inner.lock().await.0.contains_key(client_address) } pub(crate) async fn insert_token( &mut self, auth_token: AuthToken, client_address: DestinationAddressBytes, - ) -> Option { - self.inner.lock().await.0.insert(auth_token, client_address) + ) -> Option { + self.inner.lock().await.0.insert(client_address, auth_token) } pub(crate) async fn current_clients(&self) -> Vec { @@ -38,8 +38,8 @@ impl ClientLedger { .lock() .await .0 - .iter() - .map(|(_, v)| v.to_base58_string()) + .keys() + .map(|client_address| client_address.to_base58_string()) .map(|pub_key| MixProviderClient { pub_key }) .collect() } @@ -57,4 +57,4 @@ impl ClientLedger { } } -struct ClientLedgerInner(HashMap); +struct ClientLedgerInner(HashMap); From 0f5167b57f3bc249fc59c54c86517054bef5a4aa Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 15:44:29 +0000 Subject: [PATCH 248/273] Added ledger method to remove existing token --- sfw-provider/src/provider/client_handling/ledger.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sfw-provider/src/provider/client_handling/ledger.rs b/sfw-provider/src/provider/client_handling/ledger.rs index b8deea12d0..e1abadf3f4 100644 --- a/sfw-provider/src/provider/client_handling/ledger.rs +++ b/sfw-provider/src/provider/client_handling/ledger.rs @@ -33,6 +33,13 @@ impl ClientLedger { self.inner.lock().await.0.insert(client_address, auth_token) } + pub(crate) async fn remove_token( + &mut self, + client_address: &DestinationAddressBytes, + ) -> Option { + self.inner.lock().await.0.remove(client_address) + } + pub(crate) async fn current_clients(&self) -> Vec { self.inner .lock() From 8ed53847d3857bf960cdb7d83fca5b6406e57b7f Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 15:55:52 +0000 Subject: [PATCH 249/273] Method to actually retrieve stored auth token to compare it against one sent --- sfw-provider/src/provider/client_handling/ledger.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sfw-provider/src/provider/client_handling/ledger.rs b/sfw-provider/src/provider/client_handling/ledger.rs index e1abadf3f4..18e06a3639 100644 --- a/sfw-provider/src/provider/client_handling/ledger.rs +++ b/sfw-provider/src/provider/client_handling/ledger.rs @@ -25,6 +25,13 @@ impl ClientLedger { self.inner.lock().await.0.contains_key(client_address) } + pub(crate) async fn retrieve_token( + &self, + client_address: &DestinationAddressBytes, + ) -> Option<&AuthToken> { + self.inner.lock().await.0.get(client_address) + } + pub(crate) async fn insert_token( &mut self, auth_token: AuthToken, From b6727ac7b42f10a7d4323e5bf0934aac6209349f Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Mon, 2 Mar 2020 17:04:58 +0000 Subject: [PATCH 250/273] Replaced retrieve token method with verify token to bypass lifetime issues --- sfw-provider/src/provider/client_handling/ledger.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sfw-provider/src/provider/client_handling/ledger.rs b/sfw-provider/src/provider/client_handling/ledger.rs index 18e06a3639..3844358331 100644 --- a/sfw-provider/src/provider/client_handling/ledger.rs +++ b/sfw-provider/src/provider/client_handling/ledger.rs @@ -25,11 +25,15 @@ impl ClientLedger { self.inner.lock().await.0.contains_key(client_address) } - pub(crate) async fn retrieve_token( + pub(crate) async fn verify_token( &self, + auth_token: &AuthToken, client_address: &DestinationAddressBytes, - ) -> Option<&AuthToken> { - self.inner.lock().await.0.get(client_address) + ) -> bool { + match self.inner.lock().await.0.get(client_address) { + None => false, + Some(expected_token) => expected_token == auth_token, + } } pub(crate) async fn insert_token( From 20f2f1e04f43649c94fae9dbe0ec68061e40acef Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 3 Mar 2020 10:21:58 +0000 Subject: [PATCH 251/273] Updated mockito to fix build issues --- Cargo.lock | 6 +++--- common/clients/directory-client/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2970ad85d0..15f3faaa2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1363,9 +1363,9 @@ dependencies = [ [[package]] name = "mockito" -version = "0.22.0" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e524e85ea7c80559354217a6470c14abc2411802a9996aed1821559b9e28e3c" +checksum = "ae82e6bad452dd42b0f4437414eae3c8c27b958a55dc6c198e351042c4e3024e" dependencies = [ "assert-json-diff", "colored", @@ -1373,7 +1373,7 @@ dependencies = [ "httparse", "lazy_static", "log", - "percent-encoding 1.0.1", + "percent-encoding 2.1.0", "rand 0.7.3", "regex", "serde_json", diff --git a/common/clients/directory-client/Cargo.toml b/common/clients/directory-client/Cargo.toml index a9e30636e0..a38459dcd2 100644 --- a/common/clients/directory-client/Cargo.toml +++ b/common/clients/directory-client/Cargo.toml @@ -16,4 +16,4 @@ serde = { version = "1.0.104", features = ["derive"] } topology = {path = "../../topology"} [dev-dependencies] -mockito = "0.22.0" +mockito = "0.23.0" From a411dec54093a7417ef0e27a9f94ac56bf5f4249 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 3 Mar 2020 10:23:55 +0000 Subject: [PATCH 252/273] Client changes due to updates in sphinx --- common/clients/provider-client/src/lib.rs | 4 ++-- common/healthcheck/src/path_check.rs | 3 ++- nym-client/src/client/cover_traffic_stream.rs | 2 +- nym-client/src/client/mod.rs | 8 ++++---- nym-client/src/client/real_traffic_stream.rs | 2 +- nym-client/src/commands/init.rs | 7 +++++-- nym-client/src/sockets/tcp.rs | 8 ++++---- nym-client/src/sockets/ws.rs | 13 +++++++++---- 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/common/clients/provider-client/src/lib.rs b/common/clients/provider-client/src/lib.rs index 72bca9c682..7191941a93 100644 --- a/common/clients/provider-client/src/lib.rs +++ b/common/clients/provider-client/src/lib.rs @@ -89,7 +89,7 @@ impl ProviderClient { } }; - let pull_request = PullRequest::new(self.our_address, auth_token); + let pull_request = PullRequest::new(self.our_address.clone(), auth_token); let bytes = pull_request.to_bytes(); let response = self.send_request(bytes).await?; @@ -103,7 +103,7 @@ impl ProviderClient { return Err(ProviderClientError::ClientAlreadyRegisteredError); } - let register_request = RegisterRequest::new(self.our_address); + let register_request = RegisterRequest::new(self.our_address.clone()); let bytes = register_request.to_bytes(); let response = self.send_request(bytes).await?; diff --git a/common/healthcheck/src/path_check.rs b/common/healthcheck/src/path_check.rs index 3a64043706..3833dd5c8e 100644 --- a/common/healthcheck/src/path_check.rs +++ b/common/healthcheck/src/path_check.rs @@ -37,7 +37,8 @@ impl PathChecker { let address = identity_keys.public_key().derive_address(); for provider in providers { - let mut provider_client = ProviderClient::new(provider.client_listener, address, None); + let mut provider_client = + ProviderClient::new(provider.client_listener, address.clone(), None); let insertion_result = match provider_client.register().await { Ok(token) => { debug!("registered at provider {}", provider.pub_key); diff --git a/nym-client/src/client/cover_traffic_stream.rs b/nym-client/src/client/cover_traffic_stream.rs index ac012a9a07..d23ca59747 100644 --- a/nym-client/src/client/cover_traffic_stream.rs +++ b/nym-client/src/client/cover_traffic_stream.rs @@ -29,7 +29,7 @@ pub(crate) async fn start_loop_cover_traffic_stream( }; let cover_message = match mix_client::packet::loop_cover_message( - our_info.address, + our_info.address.clone(), our_info.identifier, topology, average_packet_delay_duration, diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index e950d806ff..e8783f44b4 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -119,7 +119,7 @@ impl NymClient { let mut provider_poller = provider_poller::ProviderPoller::new( poller_input_tx, provider_client_listener_address, - self_address, + self_address.clone(), self.config .get_provider_auth_token() .map(|str_token| AuthToken::try_from_base58_string(str_token).ok()) @@ -149,7 +149,7 @@ impl NymClient { let loop_cover_traffic_future = rt.spawn(cover_traffic_stream::start_loop_cover_traffic_stream( mix_tx.clone(), - Destination::new(self_address, Default::default()), + Destination::new(self_address.clone(), Default::default()), topology_controller.get_inner_ref(), self.config.get_loop_cover_traffic_average_delay(), self.config.get_average_packet_delay(), @@ -160,7 +160,7 @@ impl NymClient { let topology_ref = topology_controller.get_inner_ref(); let average_packet_delay = self.config.get_average_packet_delay(); let message_sending_average_delay = self.config.get_message_sending_average_delay(); - + let self_address_clone = self_address.clone(); // future constantly pumping traffic at some specified average rate // if a real message is available on 'input_rx' that might have been received from say // the websocket, the real message is used, otherwise a loop cover message is generated @@ -169,7 +169,7 @@ impl NymClient { real_traffic_stream::OutQueueControl::new( mix_tx, input_rx, - Destination::new(self_address, Default::default()), + Destination::new(self_address_clone, Default::default()), topology_ref, average_packet_delay, message_sending_average_delay, diff --git a/nym-client/src/client/real_traffic_stream.rs b/nym-client/src/client/real_traffic_stream.rs index 5da904a7fe..0fb9a198fe 100644 --- a/nym-client/src/client/real_traffic_stream.rs +++ b/nym-client/src/client/real_traffic_stream.rs @@ -102,7 +102,7 @@ impl OutQueueControl { let next_packet = match next_message { StreamMessage::Cover => mix_client::packet::loop_cover_message( - self.our_info.address, + self.our_info.address.clone(), self.our_info.identifier, topology, self.average_packet_delay, diff --git a/nym-client/src/commands/init.rs b/nym-client/src/commands/init.rs index c9f47790c4..586b63df00 100644 --- a/nym-client/src/commands/init.rs +++ b/nym-client/src/commands/init.rs @@ -49,8 +49,11 @@ async fn try_provider_registrations( ) -> Option<(String, AuthToken)> { // since the order of providers is non-deterministic we can just try to get a first 'working' provider for provider in providers { - let provider_client = - provider_client::ProviderClient::new(provider.client_listener, our_address, None); + let provider_client = provider_client::ProviderClient::new( + provider.client_listener, + our_address.clone(), + None, + ); let auth_token = provider_client.register().await; if let Ok(token) = auth_token { return Some((provider.pub_key, token)); diff --git a/nym-client/src/sockets/tcp.rs b/nym-client/src/sockets/tcp.rs index ad6d7ec463..c8b65c0df4 100644 --- a/nym-client/src/sockets/tcp.rs +++ b/nym-client/src/sockets/tcp.rs @@ -84,7 +84,7 @@ fn parse_send_request(data: &[u8]) -> Result { Ok(ClientRequest::Send { message, - recipient_address, + recipient_address: DestinationAddressBytes::from_bytes(recipient_address), }) } @@ -153,7 +153,7 @@ impl ClientRequest { async fn handle_own_details(self_address_bytes: DestinationAddressBytes) -> ServerResponse { ServerResponse::OwnDetails { - address: self_address_bytes.to_vec(), + address: self_address_bytes.to_bytes().to_vec(), } } } @@ -274,7 +274,7 @@ async fn accept_connection( topology: topology.clone(), msg_input: msg_input.clone(), msg_query: msg_query.clone(), - self_address, + self_address: self_address.clone(), }; match handle_connection(&buf[..n], request_handling_data).await { Ok(res) => res, @@ -313,7 +313,7 @@ pub async fn start_tcpsocket( stream, message_tx.clone(), received_messages_query_tx.clone(), - self_address, + self_address.clone(), topology.clone(), )); } diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index c9ff1ab2be..845c4971c9 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -50,7 +50,9 @@ impl Connection { } ClientRequest::Fetch => ClientRequest::handle_fetch(self.msg_query.clone()).await, ClientRequest::GetClients => ClientRequest::handle_get_clients(&self.topology).await, - ClientRequest::OwnDetails => ClientRequest::handle_own_details(self.self_address).await, + ClientRequest::OwnDetails => { + ClientRequest::handle_own_details(self.self_address.clone()).await + } } } @@ -218,7 +220,10 @@ impl ClientRequest { let dummy_surb = [0; 16]; - let input_msg = InputMessage(Destination::new(address, dummy_surb), message_bytes); + let input_msg = InputMessage( + Destination::new(DestinationAddressBytes::from_bytes(address), dummy_surb), + message_bytes, + ); input_tx.send(input_msg).await.unwrap(); ServerResponse::Send @@ -279,7 +284,7 @@ impl ClientRequest { } async fn handle_own_details(self_address_bytes: DestinationAddressBytes) -> ServerResponse { - let self_address = bs58::encode(&self_address_bytes).into_string(); + let self_address = self_address_bytes.to_base58_string(); ServerResponse::OwnDetails { address: self_address, } @@ -404,7 +409,7 @@ pub async fn start_websocket( stream, message_tx.clone(), received_messages_query_tx.clone(), - self_address, + self_address.clone(), topology.clone(), )); } From 59dcf77bf5b579780b163ebb69e2a5f58102ac06 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 3 Mar 2020 12:43:28 +0000 Subject: [PATCH 253/273] Defined ErrorResponse type --- .../sfw-provider-requests/src/responses.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sfw-provider/sfw-provider-requests/src/responses.rs b/sfw-provider/sfw-provider-requests/src/responses.rs index 4135ed06e4..47163ef022 100644 --- a/sfw-provider/sfw-provider-requests/src/responses.rs +++ b/sfw-provider/sfw-provider-requests/src/responses.rs @@ -26,6 +26,10 @@ pub struct RegisterResponse { pub auth_token: AuthToken, } +pub struct ErrorResponse { + pub message: String, +} + impl PullResponse { pub fn new(messages: Vec>) -> Self { PullResponse { messages } @@ -38,6 +42,14 @@ impl RegisterResponse { } } +impl ErrorResponse { + pub fn new>(message: S) -> Self { + ErrorResponse { + message: message.into(), + } + } +} + // TODO: This should go into some kind of utils module/crate fn read_be_u16(input: &mut &[u8]) -> u16 { let (int_bytes, rest) = input.split_at(std::mem::size_of::()); @@ -126,6 +138,19 @@ impl ProviderResponse for RegisterResponse { } } +impl ProviderResponse for ErrorResponse { + fn to_bytes(&self) -> Vec { + self.message.clone().into_bytes() + } + + fn from_bytes(bytes: &[u8]) -> Result { + match String::from_utf8(bytes.to_vec()) { + Err(_) => Err(ProviderResponseError::UnmarshalError), + Ok(message) => Ok(ErrorResponse { message }), + } + } +} + #[cfg(test)] mod creating_pull_response { use super::*; From 937e15e631ff756af86272b3d4d33415fc45ea27 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 3 Mar 2020 12:43:50 +0000 Subject: [PATCH 254/273] Removed unused 'has_client' method from the ledger --- sfw-provider/src/provider/client_handling/ledger.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sfw-provider/src/provider/client_handling/ledger.rs b/sfw-provider/src/provider/client_handling/ledger.rs index 3844358331..a5dda44f36 100644 --- a/sfw-provider/src/provider/client_handling/ledger.rs +++ b/sfw-provider/src/provider/client_handling/ledger.rs @@ -21,10 +21,6 @@ impl ClientLedger { } } - pub(crate) async fn has_client(&self, client_address: &DestinationAddressBytes) -> bool { - self.inner.lock().await.0.contains_key(client_address) - } - pub(crate) async fn verify_token( &self, auth_token: &AuthToken, From c167aca3e30bce8bb5e7d77b6261889a6aa3184c Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 3 Mar 2020 12:44:18 +0000 Subject: [PATCH 255/273] Theoretically thread safe client storage --- sfw-provider/src/provider/storage/mod.rs | 59 +++++++++++++++++------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/sfw-provider/src/provider/storage/mod.rs b/sfw-provider/src/provider/storage/mod.rs index c0eeef3481..9df6ff440a 100644 --- a/sfw-provider/src/provider/storage/mod.rs +++ b/sfw-provider/src/provider/storage/mod.rs @@ -11,12 +11,14 @@ use tokio::fs; use tokio::fs::File; use tokio::prelude::*; -const DUMMY_MESSAGE: ClientFile = ClientFile { - content: DUMMY_MESSAGE_CONTENT.to_vec(), - path: Default::default(), -}; +fn dummy_message() -> ClientFile { + ClientFile { + content: DUMMY_MESSAGE_CONTENT.to_vec(), + path: Default::default(), + } +} -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ClientFile { content: Vec, path: PathBuf, @@ -26,6 +28,10 @@ impl ClientFile { fn new(content: Vec, path: PathBuf) -> Self { ClientFile { content, path } } + + pub(crate) fn into_tuple(self) -> (Vec, PathBuf) { + (self.content, self.path) + } } pub struct StoreData { @@ -77,6 +83,21 @@ impl ClientStorage { } } + // TODO: does this method really require locking? + // The worst that can happen is client sending 2 requests: to pull messages and register + // if register does not lock, then under specific timing pull messages will fail, + // but can simply be retried with no issues + pub(crate) async fn create_storage_dir( + &self, + client_address: DestinationAddressBytes, + ) -> io::Result<()> { + let inner_data = self.inner.lock().await; + + let client_dir_name = client_address.to_base58_string(); + let full_store_dir = inner_data.main_store_path_dir.join(client_dir_name); + fs::create_dir_all(full_store_dir).await + } + pub(crate) fn generate_random_file_name(length: usize) -> String { rand::thread_rng() .sample_iter(&rand::distributions::Alphanumeric) @@ -87,7 +108,7 @@ impl ClientStorage { pub(crate) async fn store_processed_data(&self, store_data: StoreData) -> io::Result<()> { let inner_data = self.inner.lock().await; - let client_dir_name = client_address.to_base58_string(); + let client_dir_name = store_data.client_address.to_base58_string(); let full_store_dir = inner_data.main_store_path_dir.join(client_dir_name); let full_store_path = full_store_dir.join(Self::generate_random_file_name( inner_data.filename_length as usize, @@ -121,7 +142,7 @@ impl ClientStorage { } let mut msgs = Vec::new(); - let read_dir = fs::read_dir(full_store_dir).await?; + let mut read_dir = fs::read_dir(full_store_dir).await?; while let Some(dir_entry) = read_dir.next().await { if let Ok(dir_entry) = dir_entry { @@ -139,11 +160,13 @@ impl ClientStorage { } } + let dummy_message = dummy_message(); + // make sure we always return as many messages as we need if msgs.len() != inner_data.message_retrieval_limit as usize { msgs = msgs .into_iter() - .chain(std::iter::repeat(DUMMY_MESSAGE)) + .chain(std::iter::repeat(dummy_message)) .take(inner_data.message_retrieval_limit) .collect(); } @@ -175,14 +198,18 @@ impl ClientStorage { is_file } - fn delete_files(&self, files: Vec) -> io::Result<()> { - unimplemented!() - } + pub(crate) async fn delete_files(&self, file_paths: Vec) -> io::Result<()> { + let dummy_message = dummy_message(); + let _guard = self.inner.lock().await; - // TODO: This should only be called AFTER we sent the reply. Because if client's connection failed after sending request - // the messages would be deleted but he wouldn't have received them - fn delete_file(&self, path: PathBuf) -> io::Result<()> { - trace!("Here {:?} will be deleted!", path); - std::fs::remove_file(path) // another argument for db layer -> remove_file is NOT guaranteed to immediately get rid of the file + for file_path in file_paths { + if file_path == dummy_message.path { + continue; + } + if let Err(e) = fs::remove_file(file_path).await { + error!("Failed to delete client message! - {:?}", e) + } + } + Ok(()) } } From ff4284a3b92573d45baf437cb3004fdffc662596 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 3 Mar 2020 12:44:37 +0000 Subject: [PATCH 256/273] Redundant code in mix handling --- sfw-provider/src/provider/mix_handling/mod.rs | 7 ------- .../src/provider/mix_handling/packet_processing.rs | 13 ++++++++++++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sfw-provider/src/provider/mix_handling/mod.rs b/sfw-provider/src/provider/mix_handling/mod.rs index ad8693dddb..236fd73481 100644 --- a/sfw-provider/src/provider/mix_handling/mod.rs +++ b/sfw-provider/src/provider/mix_handling/mod.rs @@ -1,9 +1,2 @@ -use crate::provider::storage::StoreData; -use crypto::encryption; -use log::{error, warn}; -use sphinx::{ProcessedPacket, SphinxPacket}; -use std::path::PathBuf; -use std::sync::{Arc, RwLock}; - pub(crate) mod listener; pub(crate) mod packet_processing; diff --git a/sfw-provider/src/provider/mix_handling/packet_processing.rs b/sfw-provider/src/provider/mix_handling/packet_processing.rs index f7c2d6098e..12d50c322c 100644 --- a/sfw-provider/src/provider/mix_handling/packet_processing.rs +++ b/sfw-provider/src/provider/mix_handling/packet_processing.rs @@ -1,7 +1,10 @@ use crate::provider::storage::{ClientStorage, StoreData}; use crypto::encryption; +use log::*; +use sphinx::payload::Payload; use sphinx::route::{DestinationAddressBytes, SURBIdentifier}; use sphinx::{ProcessedPacket, SphinxPacket}; +use std::io; use std::ops::Deref; use std::sync::Arc; @@ -11,7 +14,7 @@ pub enum MixProcessingError { NonMatchingRecipient, InvalidPayload, SphinxProcessingError, - InvalidHopAddress, + IOError(String), } pub enum MixProcessingResult { @@ -29,6 +32,14 @@ impl From for MixProcessingError { } } +impl From for MixProcessingError { + fn from(e: io::Error) -> Self { + use MixProcessingError::*; + + IOError(e.to_string()) + } +} + // PacketProcessor contains all data required to correctly unwrap and store sphinx packets #[derive(Clone)] pub struct PacketProcessor { From 3852c99f9ee46caabcbbd8a239209e3ddd1b8c34 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 3 Mar 2020 12:44:50 +0000 Subject: [PATCH 257/273] Removed commented code --- sfw-provider/src/provider/mod.rs | 55 +++++++++++++------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/sfw-provider/src/provider/mod.rs b/sfw-provider/src/provider/mod.rs index 02b656723b..6b40f3ed83 100644 --- a/sfw-provider/src/provider/mod.rs +++ b/sfw-provider/src/provider/mod.rs @@ -1,6 +1,7 @@ use crate::config::persistance::pathfinder::ProviderPathfinder; use crate::config::Config; use crate::provider::client_handling::ledger::ClientLedger; +use crate::provider::storage::ClientStorage; use crypto::encryption; use log::*; use pemstore::pemstore::PemStore; @@ -11,32 +12,6 @@ mod mix_handling; pub mod presence; mod storage; -// #[derive(Debug)] -// pub enum ProviderError { -// TcpListenerBindingError, -// TcpListenerConnectionError, -// TcpListenerUnexpectedEof, -// -// TcpListenerUnknownError, -// } -// -// impl From for ProviderError { -// fn from(err: Error) -> Self { -// use ProviderError::*; -// match err.kind() { -// io::ErrorKind::ConnectionRefused => TcpListenerConnectionError, -// io::ErrorKind::ConnectionReset => TcpListenerConnectionError, -// io::ErrorKind::ConnectionAborted => TcpListenerConnectionError, -// io::ErrorKind::NotConnected => TcpListenerConnectionError, -// -// io::ErrorKind::AddrInUse => TcpListenerBindingError, -// io::ErrorKind::AddrNotAvailable => TcpListenerBindingError, -// io::ErrorKind::UnexpectedEof => TcpListenerUnexpectedEof, -// _ => TcpListenerUnknownError, -// } -// } -// } - pub struct ServiceProvider { runtime: Runtime, config: Config, @@ -80,10 +55,11 @@ impl ServiceProvider { .start(self.runtime.handle()); } - fn start_mix_socket_listener(&self) { - info!("Starting client socket listener..."); + fn start_mix_socket_listener(&self, client_storage: ClientStorage) { + info!("Starting mix socket listener..."); let packet_processor = mix_handling::packet_processing::PacketProcessor::new( self.sphinx_keypair.private_key().clone(), + client_storage, ); mix_handling::listener::run_mix_socket_listener( @@ -93,23 +69,36 @@ impl ServiceProvider { ); } - fn start_client_socket_listener(&self) { - info!("Starting mix socket listener..."); + fn start_client_socket_listener(&self, client_storage: ClientStorage) { + info!("Starting client socket listener..."); let packet_processor = client_handling::request_processing::RequestProcessor::new( self.sphinx_keypair.private_key().clone(), + client_storage, + self.registered_clients_ledger.clone(), ); client_handling::listener::run_client_socket_listener( self.runtime.handle(), - self.config.get_mix_listening_address(), + self.config.get_clients_listening_address(), packet_processor, ); } pub fn run(&mut self) { + // A possible future optimisation, depending on bottlenecks and resource usage: + // considering, presumably, there will be more mix packets received than client requests: + // create 2 separate runtimes - one with bigger threadpool dedicated solely for + // the mix handling and the other one for the rest of tasks + + let client_storage = ClientStorage::new( + self.config.get_message_retrieval_limit() as usize, + self.config.get_stored_messages_filename_length(), + self.config.get_clients_inboxes_dir(), + ); + self.start_presence_notifier(); - self.start_mix_socket_listener(); - self.start_client_socket_listener(); + self.start_mix_socket_listener(client_storage.clone()); + self.start_client_socket_listener(client_storage); if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) { error!( From 49924989420089ae1c835362566a081c7371ecd7 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Tue, 3 Mar 2020 12:45:04 +0000 Subject: [PATCH 258/273] Refactored client handling --- .../src/provider/client_handling/listener.rs | 164 +++------- .../src/provider/client_handling/mod.rs | 279 ------------------ .../client_handling/request_processing.rs | 189 +++++++++++- 3 files changed, 237 insertions(+), 395 deletions(-) diff --git a/sfw-provider/src/provider/client_handling/listener.rs b/sfw-provider/src/provider/client_handling/listener.rs index f81707f622..f3cee1bf5e 100644 --- a/sfw-provider/src/provider/client_handling/listener.rs +++ b/sfw-provider/src/provider/client_handling/listener.rs @@ -1,16 +1,58 @@ -use crate::provider::client_handling::request_processing::RequestProcessor; +use crate::provider::client_handling::request_processing::{ + ClientProcessingResult, RequestProcessor, +}; use log::*; +use sfw_provider_requests::responses::{ + ErrorResponse, ProviderResponse, PullResponse, RegisterResponse, +}; use std::io; use std::net::SocketAddr; use tokio::prelude::*; use tokio::runtime::Handle; use tokio::task::JoinHandle; +async fn process_request( + socket: &mut tokio::net::TcpStream, + packet_data: &[u8], + request_processor: &mut RequestProcessor, +) { + match request_processor.process_client_request(packet_data).await { + Err(e) => { + warn!("We failed to process client request - {:?}", e); + let response_bytes = ErrorResponse::new(format!("{:?}", e)).to_bytes(); + if let Err(e) = socket.write_all(&response_bytes).await { + debug!("Failed to write response to the client - {:?}", e); + } + } + Ok(res) => match res { + ClientProcessingResult::RegisterResponse(auth_token) => { + let response_bytes = RegisterResponse::new(auth_token).to_bytes(); + if let Err(e) = socket.write_all(&response_bytes).await { + debug!("Failed to write response to the client - {:?}", e); + } + } + ClientProcessingResult::PullResponse(retrieved_messages) => { + let (messages, paths): (Vec<_>, Vec<_>) = retrieved_messages + .into_iter() + .map(|c| c.into_tuple()) + .unzip(); + let response_bytes = PullResponse::new(messages).to_bytes(); + if let Ok(_) = socket.write_all(&response_bytes).await { + // only delete stored messages if we managed to actually send the response + if let Err(e) = request_processor.delete_sent_messages(paths).await { + error!("Somehow failed to delete stored messages! - {:?}", e); + } + } + } + }, + } +} + async fn process_socket_connection( mut socket: tokio::net::TcpStream, - request_processor: RequestProcessor, + mut request_processor: RequestProcessor, ) { - let mut buf = [0u8; sphinx::PACKET_SIZE]; + let mut buf = [0u8; 1024]; loop { match socket.read(&mut buf).await { // socket closed @@ -18,25 +60,10 @@ async fn process_socket_connection( trace!("Remote connection closed."); return; } - Ok(n) => { - if n != sphinx::PACKET_SIZE { - warn!("read data of different length than expected sphinx packet size - {} (expected {})", n, sphinx::PACKET_SIZE); - continue; - } - - tokio::spawn(async {}); - - // // we must be able to handle multiple packets from same connection independently - // tokio::spawn(process_received_packet( - // buf.clone(), - // // TODO: - // // TODO: - // // note: processing_data is relatively cheap (and safe) to clone - - // // it contains arc to private key and metrics reporter (which is just - // // a single mpsc unbounded sender) - // request_processor.clone(), - // )) - } + // in here we do not really want to process multiple requests from the same + // client concurrently as then we might end up with really weird race conditions + // plus realistically it wouldn't really introduce any speed up + Ok(n) => process_request(&mut socket, &buf[0..n], &mut request_processor).await, Err(e) => { warn!( "failed to read from socket. Closing the connection; err = {:?}", @@ -48,99 +75,6 @@ async fn process_socket_connection( } } -// // TODO: FIGURE OUT HOW TO SET READ_DEADLINES IN TOKIO -// async fn process_client_socket_connection( -// mut socket: tokio::net::TcpStream, -// processing_data: Arc, -// ) { -// let mut buf = [0; 1024]; -// -// // TODO: restore the for loop once we go back to persistent tcp socket connection -// let response = match socket.read(&mut buf).await { -// // socket closed -// Ok(n) if n == 0 => { -// trace!("Remote connection closed."); -// Err(()) -// } -// Ok(n) => { -// match ClientRequestProcessor::process_client_request( -// buf[..n].as_ref(), -// processing_data, -// ) -// .await -// { -// Err(e) => { -// warn!("failed to process client request; err = {:?}", e); -// Err(()) -// } -// Ok(res) => Ok(res), -// } -// } -// Err(e) => { -// warn!("failed to read from socket; err = {:?}", e); -// Err(()) -// } -// }; -// -// if let Err(e) = socket.shutdown(Shutdown::Read) { -// warn!("failed to close read part of the socket; err = {:?}", e) -// } -// -// match response { -// Ok(res) => { -// ServiceProvider::send_response(socket, &res).await; -// } -// _ => { -// ServiceProvider::send_response(socket, b"bad foomp").await; -// } -// } -// } - -// async fn send_response(mut socket: tokio::net::TcpStream, data: &[u8]) { -// if let Err(e) = socket.write_all(data).await { -// warn!("failed to write reply to socket; err = {:?}", e) -// } -// if let Err(e) = socket.shutdown(Shutdown::Write) { -// warn!("failed to close write part of the socket; err = {:?}", e) -// } -// } - -// async fn start_client_listening( -// address: SocketAddr, -// store_dir: PathBuf, -// client_ledger: Arc>, -// secret_key: encryption::PrivateKey, -// message_retrieval_limit: u16, -// ) -> Result<(), ProviderError> { -// let mut listener = tokio::net::TcpListener::bind(address).await?; -// let processing_data = ClientProcessingData::new( -// store_dir, -// client_ledger, -// secret_key, -// message_retrieval_limit, -// ) -// .add_arc(); -// -// loop { -// let (socket, _) = listener.accept().await?; -// // do note that the underlying data is NOT copied here; arc is incremented and lock is shared -// // (if I understand it all correctly) -// let thread_processing_data = processing_data.clone(); -// tokio::spawn(async move { -// ServiceProvider::process_client_socket_connection(socket, thread_processing_data) -// .await -// }); -// } -// } - -// let client_future = rt.spawn(ServiceProvider::start_client_listening( -// self.config.get_clients_listening_address(), -// self.config.get_clients_inboxes_dir(), -// thread_shareable_ledger, -// self.sphinx_keypair.private_key().clone(), // CLONE IS DONE TEMPORARILY UNTIL PROVIDER IS REFACTORED THE MIXNODE STYLE -// self.config.get_message_retrieval_limit(), -// )); - pub(crate) fn run_client_socket_listener( handle: &Handle, addr: SocketAddr, diff --git a/sfw-provider/src/provider/client_handling/mod.rs b/sfw-provider/src/provider/client_handling/mod.rs index 5f20b253f6..c7fdd30b23 100644 --- a/sfw-provider/src/provider/client_handling/mod.rs +++ b/sfw-provider/src/provider/client_handling/mod.rs @@ -1,282 +1,3 @@ -use crate::provider::client_handling::ledger::ClientLedger; -use crate::provider::storage::{ClientStorage, StoreError}; -use crypto::encryption; -use futures::lock::Mutex as FMutex; -use hmac::{Hmac, Mac}; -use log::*; -use sfw_provider_requests::requests::{ - ProviderRequestError, ProviderRequests, PullRequest, RegisterRequest, -}; -use sfw_provider_requests::responses::{ProviderResponse, PullResponse, RegisterResponse}; -use sfw_provider_requests::AuthToken; -use sha2::Sha256; -use std::io; -use std::path::{Path, PathBuf}; -use std::sync::Arc; - -type HmacSha256 = Hmac; - pub(crate) mod ledger; pub(crate) mod listener; pub(crate) mod request_processing; - -#[derive(Debug)] -pub enum ClientProcessingError { - ClientDoesntExistError, - StoreError, - InvalidRequest, - WrongToken, - IOError, -} - -impl From for ClientProcessingError { - fn from(_: ProviderRequestError) -> Self { - use ClientProcessingError::*; - - InvalidRequest - } -} - -impl From for ClientProcessingError { - fn from(e: StoreError) -> Self { - match e { - StoreError::ClientDoesntExistError => ClientProcessingError::ClientDoesntExistError, - _ => ClientProcessingError::StoreError, - } - } -} - -impl From for ClientProcessingError { - fn from(_: io::Error) -> Self { - use ClientProcessingError::*; - - IOError - } -} - -#[derive(Debug)] -pub(crate) struct ClientProcessingData { - store_dir: PathBuf, - registered_clients_ledger: Arc>, - secret_key: encryption::PrivateKey, - message_retrieval_limit: u16, -} - -impl ClientProcessingData { - pub(crate) fn new( - store_dir: PathBuf, - registered_clients_ledger: Arc>, - secret_key: encryption::PrivateKey, - message_retrieval_limit: u16, - ) -> Self { - ClientProcessingData { - store_dir, - registered_clients_ledger, - secret_key, - message_retrieval_limit, - } - } - - pub(crate) fn add_arc(self) -> Arc { - Arc::new(self) - } -} - -pub(crate) struct ClientRequestProcessor; - -impl ClientRequestProcessor { - // pub(crate) async fn process_client_request( - // data: &[u8], - // processing_data: Arc, - // ) -> Result, ClientProcessingError> { - // let client_request = ProviderRequests::from_bytes(&data)?; - // trace!("Received the following request: {:?}", client_request); - // match client_request { - // ProviderRequests::Register(req) => Ok(ClientRequestProcessor::register_new_client( - // req, - // processing_data, - // ) - // .await? - // .to_bytes()), - // ProviderRequests::PullMessages(req) => Ok( - // ClientRequestProcessor::process_pull_messages_request(req, processing_data) - // .await? - // .to_bytes(), - // ), - // } - // } - // - // async fn process_pull_messages_request( - // req: PullRequest, - // processing_data: Arc, - // ) -> Result { - // // TODO: this lock is completely unnecessary as we're only reading the data. - // // Wait for https://github.com/nymtech/nym-sfw-provider/issues/19 to resolve. - // let unlocked_ledger = processing_data.registered_clients_ledger.lock().await; - // - // if unlocked_ledger.has_token(&req.auth_token) { - // // drop the mutex so that we could do IO without blocking others wanting to get the lock - // drop(unlocked_ledger); - // let retrieved_messages = ClientStorage::retrieve_client_files( - // req.destination_address, - // processing_data.store_dir.as_path(), - // processing_data.message_retrieval_limit, - // )?; - // Ok(PullResponse::new(retrieved_messages)) - // } else { - // Err(ClientProcessingError::WrongToken) - // } - // } - // - // async fn register_new_client( - // req: RegisterRequest, - // processing_data: Arc, - // ) -> Result { - // debug!( - // "Processing register new client request: {:?}", - // req.destination_address - // ); - // let mut unlocked_ledger = processing_data.registered_clients_ledger.lock().await; - // - // let auth_token = ClientRequestProcessor::generate_new_auth_token( - // req.destination_address.to_vec(), - // &processing_data.secret_key, - // ); - // if !unlocked_ledger.has_token(&auth_token) { - // unlocked_ledger.insert_token(auth_token.clone(), req.destination_address); - // ClientRequestProcessor::create_storage_dir( - // req.destination_address, - // processing_data.store_dir.as_path(), - // )?; - // } - // Ok(RegisterResponse::new(auth_token)) - // } - // - // fn create_storage_dir( - // client_address: sphinx::route::DestinationAddressBytes, - // store_dir: &Path, - // ) -> io::Result<()> { - // let client_dir_name = bs58::encode(client_address).into_string(); - // let full_store_dir = store_dir.join(client_dir_name); - // std::fs::create_dir_all(full_store_dir) - // } - // - // fn generate_new_auth_token(data: Vec, key: &encryption::PrivateKey) -> AuthToken { - // // also note that `new_varkey` doesn't even have an execution branch returning an error - // let mut auth_token_raw = HmacSha256::new_varkey(&key.to_bytes()) - // .expect("HMAC should be able take key of any size"); - // auth_token_raw.input(&data); - // let mut auth_token = [0u8; 32]; - // auth_token.copy_from_slice(&auth_token_raw.result().code().to_vec()); - // AuthToken(auth_token) - // } -} - -#[cfg(test)] -mod register_new_client { - // use super::*; - - // TODO: those tests require being called in async context. we need to research how to test this stuff... - // #[test] - // fn registers_new_auth_token_for_each_new_client() { - // let req1 = RegisterRequest { - // destination_address: [1u8; 32], - // }; - // let registered_client_ledger = ClientLedger::new(); - // let store_dir = PathBuf::from("./foo/"); - // let key = Scalar::from_bytes_mod_order([1u8; 32]); - // let client_processing_data = ClientProcessingData::new(store_dir, registered_client_ledger, key).add_arc_futures_mutex(); - // - // - // // need to do async.... - // client_processing_data.lock().await; - // assert_eq!(0, registered_client_ledger.0.len()); - // ClientRequestProcessor::register_new_client( - // req1, - // client_processing_data.clone(), - // ); - // - // assert_eq!(1, registered_client_ledger.0.len()); - // - // let req2 = RegisterRequest { - // destination_address: [2u8; 32], - // }; - // ClientRequestProcessor::register_new_client( - // req2, - // client_processing_data, - // ); - // assert_eq!(2, registered_client_ledger.0.len()); - // } - // - // #[test] - // fn registers_given_token_only_once() { - // let req1 = RegisterRequest { - // destination_address: [1u8; 32], - // }; - // let registered_client_ledger = ClientLedger::new(); - // let store_dir = PathBuf::from("./foo/"); - // let key = Scalar::from_bytes_mod_order([1u8; 32]); - // let client_processing_data = ClientProcessingData::new(store_dir, registered_client_ledger, key).add_arc_futures_mutex(); - // - // ClientRequestProcessor::register_new_client( - // req1, - // client_processing_data.clone(), - // ); - // let req2 = RegisterRequest { - // destination_address: [1u8; 32], - // }; - // ClientRequestProcessor::register_new_client( - // req2, - // client_processing_data.clone(), - // ); - // - // client_processing_data.lock().await; - // - // assert_eq!(1, registered_client_ledger.0.len()) - // } -} - -#[cfg(test)] -mod create_storage_dir { - use super::*; - use sphinx::route::DestinationAddressBytes; - - #[test] - fn it_creates_a_correct_storage_directory() { - let client_address: DestinationAddressBytes = [1u8; 32]; - let store_dir = Path::new("/tmp/"); - ClientRequestProcessor::create_storage_dir(client_address, store_dir).unwrap(); - } -} - -#[cfg(test)] -mod generating_new_auth_token { - use super::*; - - #[test] - fn for_the_same_input_generates_the_same_auth_token() { - let data1 = vec![1u8; 55]; - let data2 = vec![1u8; 55]; - let key = encryption::PrivateKey::from_bytes(&[1u8; 32]); - let token1 = ClientRequestProcessor::generate_new_auth_token(data1, &key); - let token2 = ClientRequestProcessor::generate_new_auth_token(data2, &key); - assert_eq!(token1, token2); - } - - #[test] - fn for_different_inputs_generates_different_auth_tokens() { - let data1 = vec![1u8; 55]; - let data2 = vec![2u8; 55]; - let key = encryption::PrivateKey::from_bytes(&[1u8; 32]); - let token1 = ClientRequestProcessor::generate_new_auth_token(data1, &key); - let token2 = ClientRequestProcessor::generate_new_auth_token(data2, &key); - assert_ne!(token1, token2); - - let data1 = vec![1u8; 50]; - let data2 = vec![2u8; 55]; - let key = encryption::PrivateKey::from_bytes(&[1u8; 32]); - let token1 = ClientRequestProcessor::generate_new_auth_token(data1, &key); - let token2 = ClientRequestProcessor::generate_new_auth_token(data2, &key); - assert_ne!(token1, token2); - } -} diff --git a/sfw-provider/src/provider/client_handling/request_processing.rs b/sfw-provider/src/provider/client_handling/request_processing.rs index fe8faba5d5..6e7a36bb08 100644 --- a/sfw-provider/src/provider/client_handling/request_processing.rs +++ b/sfw-provider/src/provider/client_handling/request_processing.rs @@ -1,16 +1,203 @@ +use crate::provider::client_handling::ledger::ClientLedger; +use crate::provider::storage::{ClientFile, ClientStorage}; use crypto::encryption; +use hmac::{Hmac, Mac}; +use log::*; +use sfw_provider_requests::requests::{ + ProviderRequestError, ProviderRequests, PullRequest, RegisterRequest, +}; +use sfw_provider_requests::AuthToken; +use sha2::Sha256; +use sphinx::route::DestinationAddressBytes; +use std::io; +use std::path::PathBuf; use std::sync::Arc; +#[derive(Debug)] +pub enum ClientProcessingResult { + PullResponse(Vec), + RegisterResponse(AuthToken), +} + +#[derive(Debug)] +pub enum ClientProcessingError { + InvalidRequest, + InvalidToken, + IOError(String), +} + +impl From for ClientProcessingError { + fn from(_: ProviderRequestError) -> Self { + use ClientProcessingError::*; + + InvalidRequest + } +} + +impl From for ClientProcessingError { + fn from(e: io::Error) -> Self { + use ClientProcessingError::*; + + IOError(e.to_string()) + } +} + // PacketProcessor contains all data required to correctly process client requests #[derive(Clone)] pub struct RequestProcessor { secret_key: Arc, + client_storage: ClientStorage, + client_ledger: ClientLedger, } impl RequestProcessor { - pub(crate) fn new(secret_key: encryption::PrivateKey) -> Self { + pub(crate) fn new( + secret_key: encryption::PrivateKey, + client_storage: ClientStorage, + client_ledger: ClientLedger, + ) -> Self { RequestProcessor { secret_key: Arc::new(secret_key), + client_storage, + client_ledger, } } + + pub(crate) async fn process_client_request( + &mut self, + request_bytes: &[u8], + ) -> Result { + let client_request = ProviderRequests::from_bytes(request_bytes)?; + debug!("Received the following request: {:?}", client_request); + match client_request { + ProviderRequests::Register(req) => self.process_register_request(req).await, + ProviderRequests::PullMessages(req) => self.process_pull_request(req).await, + } + } + + pub(crate) async fn process_register_request( + &mut self, + req: RegisterRequest, + ) -> Result { + debug!( + "Processing register new client request: {:?}", + req.destination_address.to_base58_string() + ); + + let auth_token = self.generate_new_auth_token(req.destination_address.clone()); + if let Some(_) = self + .client_ledger + .insert_token(auth_token.clone(), req.destination_address.clone()) + .await + { + info!( + "Client {:?} was already registered before!", + req.destination_address.to_base58_string() + ) + } else { + if let Err(e) = self + .client_storage + .create_storage_dir(req.destination_address.clone()) + .await + { + error!("We failed to create inbox directory for the client -{:?}\nReverting issued token...", e); + // we must revert our changes if this operation failed + self.client_ledger + .remove_token(&req.destination_address) + .await; + } + } + + Ok(ClientProcessingResult::RegisterResponse(auth_token)) + } + + fn generate_new_auth_token(&self, client_address: DestinationAddressBytes) -> AuthToken { + type HmacSha256 = Hmac; + + // note that `new_varkey` doesn't even have an execution branch returning an error + // (true as of hmac 0.7.1) + let mut auth_token_raw = HmacSha256::new_varkey(&self.secret_key.to_bytes()).unwrap(); + auth_token_raw.input(client_address.as_bytes()); + let mut auth_token = [0u8; 32]; + auth_token.copy_from_slice(auth_token_raw.result().code().as_slice()); + AuthToken::from_bytes(auth_token) + } + + pub(crate) async fn process_pull_request( + &self, + req: PullRequest, + ) -> Result { + if self + .client_ledger + .verify_token(&req.auth_token, &req.destination_address) + .await + { + let retrieved_messages = self + .client_storage + .retrieve_client_files(req.destination_address) + .await?; + return Ok(ClientProcessingResult::PullResponse(retrieved_messages)); + } + + Err(ClientProcessingError::InvalidToken) + } + + pub(crate) async fn delete_sent_messages(&self, file_paths: Vec) -> io::Result<()> { + self.client_storage.delete_files(file_paths).await + } +} + +#[cfg(test)] +mod generating_new_auth_token { + use super::*; + + #[test] + fn for_the_same_input_generates_the_same_auth_token() { + let client_address1 = DestinationAddressBytes::from_bytes([1; 32]); + let client_address2 = DestinationAddressBytes::from_bytes([1; 32]); + let key = encryption::PrivateKey::from_bytes(&[2u8; 32]); + + let request_processor = RequestProcessor { + secret_key: Arc::new(key), + client_storage: ClientStorage::new(3, 16, Default::default()), + client_ledger: ClientLedger::new(), + }; + + let token1 = request_processor.generate_new_auth_token(client_address1); + let token2 = request_processor.generate_new_auth_token(client_address2); + assert_eq!(token1, token2); + } + + #[test] + fn for_different_inputs_generates_different_auth_tokens() { + let client_address1 = DestinationAddressBytes::from_bytes([1; 32]); + let client_address2 = DestinationAddressBytes::from_bytes([2; 32]); + let key1 = encryption::PrivateKey::from_bytes(&[3u8; 32]); + let key2 = encryption::PrivateKey::from_bytes(&[4u8; 32]); + + let request_processor1 = RequestProcessor { + secret_key: Arc::new(key1), + client_storage: ClientStorage::new(3, 16, Default::default()), + client_ledger: ClientLedger::new(), + }; + + let request_processor2 = RequestProcessor { + secret_key: Arc::new(key2), + client_storage: ClientStorage::new(3, 16, Default::default()), + client_ledger: ClientLedger::new(), + }; + + let token1 = request_processor1.generate_new_auth_token(client_address1.clone()); + let token2 = request_processor1.generate_new_auth_token(client_address2.clone()); + + let token3 = request_processor2.generate_new_auth_token(client_address1); + let token4 = request_processor2.generate_new_auth_token(client_address2); + + assert_ne!(token1, token2); + assert_ne!(token1, token3); + assert_ne!(token1, token4); + assert_ne!(token2, token3); + assert_ne!(token2, token4); + assert_ne!(token3, token4); + } } From 7ea3c688208c126a9ae864759cec6b45cff1a133 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 4 Mar 2020 14:37:27 +0000 Subject: [PATCH 259/273] Refactored received buffer controller --- nym-client/src/client/received_buffer.rs | 177 ++++++++++++++--------- 1 file changed, 106 insertions(+), 71 deletions(-) diff --git a/nym-client/src/client/received_buffer.rs b/nym-client/src/client/received_buffer.rs index a6df580c9f..9350099ed8 100644 --- a/nym-client/src/client/received_buffer.rs +++ b/nym-client/src/client/received_buffer.rs @@ -1,89 +1,124 @@ +use crate::client::provider_poller::PolledMessagesReceiver; use futures::channel::{mpsc, oneshot}; -use futures::lock::Mutex as FMutex; +use futures::lock::Mutex; use futures::StreamExt; -use log::{error, info, trace}; +use log::*; use std::sync::Arc; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; -pub type BufferResponse = oneshot::Sender>>; +pub(crate) type ReceivedBufferResponse = oneshot::Sender>>; +pub(crate) type ReceivedBufferRequestSender = mpsc::UnboundedSender; +pub(crate) type ReceivedBufferRequestReceiver = mpsc::UnboundedReceiver; -pub(crate) struct ReceivedMessagesBuffer { - inner: Arc>, -} - -impl ReceivedMessagesBuffer { - pub(crate) fn new() -> Self { - ReceivedMessagesBuffer { - inner: Arc::new(FMutex::new(Inner::new())), - } - } - - pub(crate) async fn start_controllers( - self, - poller_rx: mpsc::UnboundedReceiver>>, // to receive new messages - query_receiver: mpsc::UnboundedReceiver, // to receive requests to acquire all stored messages - ) { - let input_controller_future = tokio::spawn(Self::run_poller_input_controller( - self.inner.clone(), - poller_rx, - )); - let output_controller_future = tokio::spawn(Self::run_query_output_controller( - self.inner, - query_receiver, - )); - - futures::future::select(input_controller_future, output_controller_future).await; - panic!("One of the received buffer controllers failed!") - } - - pub(crate) async fn run_poller_input_controller( - buf: Arc>, - mut poller_rx: mpsc::UnboundedReceiver>>, - ) { - info!("Started Received Messages Buffer Input Controller"); - - while let Some(new_messages) = poller_rx.next().await { - Inner::add_new_messages(&*buf, new_messages).await; - } - } - - pub(crate) async fn run_query_output_controller( - buf: Arc>, - mut query_receiver: mpsc::UnboundedReceiver, - ) { - info!("Started Received Messages Buffer Output Controller"); - - while let Some(request) = query_receiver.next().await { - let messages = Inner::acquire_and_empty(&*buf).await; - if let Err(failed_messages) = request.send(messages) { - error!( - "Failed to send the messages to the requester. Adding them back to the buffer" - ); - Inner::add_new_messages(&*buf, failed_messages).await; - } - } - } -} - -pub(crate) struct Inner { +struct ReceivedMessagesBufferInner { messages: Vec>, } -impl Inner { +#[derive(Debug, Clone)] +// Note: you should NEVER create more than a single instance of this using 'new()'. +// You should always use .clone() to create additional instances +struct ReceivedMessagesBuffer { + inner: Arc>, +} + +impl ReceivedMessagesBuffer { fn new() -> Self { - Inner { - messages: Vec::new(), + ReceivedMessagesBuffer { + inner: Arc::new(Mutex::new(ReceivedMessagesBufferInner { + messages: Vec::new(), + })), } } - async fn add_new_messages(buf: &FMutex, msgs: Vec>) { + async fn add_new_messages(&mut self, msgs: Vec>) { trace!("Adding new messages to the buffer! {:?}", msgs); - let mut unlocked = buf.lock().await; - unlocked.messages.extend(msgs); + self.inner.lock().await.messages.extend(msgs) } - async fn acquire_and_empty(buf: &FMutex) -> Vec> { + async fn acquire_and_empty(&mut self) -> Vec> { trace!("Emptying the buffer and returning all messages"); - let mut unlocked = buf.lock().await; - std::mem::replace(&mut unlocked.messages, Vec::new()) + let mutex_guard = self.inner.lock().await; + std::mem::replace(&mut mutex_guard.messages, Vec::new()) + } +} + +struct RequestReceiver { + received_buffer: ReceivedMessagesBuffer, + query_receiver: ReceivedBufferRequestReceiver, +} + +impl RequestReceiver { + fn new( + received_buffer: ReceivedMessagesBuffer, + query_receiver: ReceivedBufferRequestReceiver, + ) -> Self { + RequestReceiver { + received_buffer, + query_receiver, + } + } + + fn start(mut self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + while let Some(request) = self.query_receiver.next().await { + let messages = self.received_buffer.acquire_and_empty().await; + if let Err(failed_messages) = request.send(messages) { + error!( + "Failed to send the messages to the requester. Adding them back to the buffer" + ); + self.received_buffer.add_new_messages(failed_messages).await; + } + } + }) + } +} + +struct MessageReceiver { + received_buffer: ReceivedMessagesBuffer, + poller_receiver: PolledMessagesReceiver, +} + +impl MessageReceiver { + fn new( + received_buffer: ReceivedMessagesBuffer, + poller_receiver: PolledMessagesReceiver, + ) -> Self { + MessageReceiver { + received_buffer, + poller_receiver, + } + } + fn start(mut self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + while let Some(new_messages) = self.poller_receiver.next().await { + self.received_buffer.add_new_messages(new_messages).await; + } + }) + } +} + +pub(crate) struct ReceivedMessagesBufferController { + messsage_receiver: MessageReceiver, + request_receiver: RequestReceiver, +} + +impl ReceivedMessagesBufferController { + fn new( + query_receiver: ReceivedBufferRequestReceiver, + poller_receiver: PolledMessagesReceiver, + ) -> Self { + let received_buffer = ReceivedMessagesBuffer::new(); + + ReceivedMessagesBufferController { + messsage_receiver: MessageReceiver::new(received_buffer.clone(), poller_receiver), + request_receiver: RequestReceiver::new(received_buffer, query_receiver), + } + } + + fn start(mut self, handle: &Handle) { + // TODO: should we do anything with JoinHandle(s) returned by start methods? + self.messsage_receiver.start(handle); + self.request_receiver.start(handle); } } From 8cde30459ba16784c0e01b882fecc7046e3f9e86 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 4 Mar 2020 14:39:16 +0000 Subject: [PATCH 260/273] Made new and start method on buffer controller public to the crate --- nym-client/src/client/received_buffer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nym-client/src/client/received_buffer.rs b/nym-client/src/client/received_buffer.rs index 9350099ed8..d776088151 100644 --- a/nym-client/src/client/received_buffer.rs +++ b/nym-client/src/client/received_buffer.rs @@ -104,7 +104,7 @@ pub(crate) struct ReceivedMessagesBufferController { } impl ReceivedMessagesBufferController { - fn new( + pub(crate) fn new( query_receiver: ReceivedBufferRequestReceiver, poller_receiver: PolledMessagesReceiver, ) -> Self { @@ -116,7 +116,7 @@ impl ReceivedMessagesBufferController { } } - fn start(mut self, handle: &Handle) { + pub(crate) fn start(mut self, handle: &Handle) { // TODO: should we do anything with JoinHandle(s) returned by start methods? self.messsage_receiver.start(handle); self.request_receiver.start(handle); From 9f416b6570b2f0e11981ecbe5d8a4af90ebe9b5c Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 4 Mar 2020 15:33:32 +0000 Subject: [PATCH 261/273] Converted cover traffic stream into an actual stream --- nym-client/src/client/cover_traffic_stream.rs | 118 ++++++++++++++---- 1 file changed, 94 insertions(+), 24 deletions(-) diff --git a/nym-client/src/client/cover_traffic_stream.rs b/nym-client/src/client/cover_traffic_stream.rs index d23ca59747..0eddaee926 100644 --- a/nym-client/src/client/cover_traffic_stream.rs +++ b/nym-client/src/client/cover_traffic_stream.rs @@ -1,38 +1,90 @@ -use crate::client::mix_traffic::MixMessage; -use crate::client::topology_control::TopologyInnerRef; -use futures::channel::mpsc; -use log::{error, info, trace, warn}; +use crate::client::mix_traffic::{MixMessage, MixMessageSender}; +use crate::client::topology_control::TopologyAccessor; +use futures::task::{Context, Poll}; +use futures::{Stream, StreamExt}; +use log::*; use sphinx::route::Destination; -use std::time; +use std::pin::Pin; +use std::time::Duration; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; +use tokio::time; use topology::NymTopology; -pub(crate) async fn start_loop_cover_traffic_stream( - tx: mpsc::UnboundedSender, +pub(crate) struct LoopCoverTrafficStream { + average_packet_delay: Duration, + average_cover_message_sending_delay: Duration, + next_delay: time::Delay, + mix_tx: MixMessageSender, our_info: Destination, - topology_ctrl_ref: TopologyInnerRef, - average_cover_message_delay_duration: time::Duration, - average_packet_delay_duration: time::Duration, -) { - info!("Starting loop cover traffic stream"); - loop { - trace!("next cover message!"); - let delay_duration = mix_client::poisson::sample(average_cover_message_delay_duration); - tokio::time::delay_for(delay_duration).await; + topology_access: TopologyAccessor, +} - let read_lock = topology_ctrl_ref.read().await; - let topology = match read_lock.topology.as_ref() { +impl Stream for LoopCoverTrafficStream { + // Item is only used to indicate we should create a new message rather than actual cover message + // reason being to not introduce unnecessary complexity by having to keep state of topology + // mutex when trying to acquire it. So right now the Stream trait serves as a glorified timer. + // Perhaps this should be changed in the future. + type Item = (); + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + // it is not yet time to return a message + if Pin::new(&mut self.next_delay).poll(cx).is_pending() { + return Poll::Pending; + }; + + // we know it's time to send a message, so let's prepare delay for the next one + // Get the `now` by looking at the current `delay` deadline + let now = self.next_delay.deadline(); + let next_poisson_delay = mix_client::poisson::sample(self.average_message_sending_delay); + + // The next interval value is `next_poisson_delay` after the one that just + // yielded. + let next = now + next_poisson_delay; + self.next_delay.reset(next); + + Poll::Ready(Some(())) + } +} + +impl LoopCoverTrafficStream { + fn new( + mix_tx: MixMessageSender, + our_info: Destination, + mut topology_access: TopologyAccessor, + average_cover_message_sending_delay: time::Duration, + average_packet_delay: time::Duration, + ) -> Self { + LoopCoverTrafficStream { + average_packet_delay, + average_cover_message_sending_delay, + next_delay: time::delay_for(Default::default()), + mix_tx, + our_info, + topology_access, + } + } + + async fn on_new_message(&mut self) { + trace!("next cover message!"); + let topology = match self + .topology_access + .get_exclusive_topology_reference() + .await + .as_ref() + { None => { warn!("No valid topology detected - won't send any loop cover message this time"); - continue; + return; } Some(topology) => topology, }; let cover_message = match mix_client::packet::loop_cover_message( - our_info.address.clone(), - our_info.identifier, + self.our_info.address.clone(), + self.our_info.identifier, topology, - average_packet_delay_duration, + self.average_packet_delay, ) { Ok(message) => message, Err(err) => { @@ -40,7 +92,7 @@ pub(crate) async fn start_loop_cover_traffic_stream( "Somehow we managed to create an invalid cover message - {:?}", err ); - continue; + return; } }; @@ -48,7 +100,25 @@ pub(crate) async fn start_loop_cover_traffic_stream( // - we run out of memory // - the receiver channel is closed // in either case there's no recovery and we can only panic - tx.unbounded_send(MixMessage::new(cover_message.0, cover_message.1)) + self.mix_tx + .unbounded_send(MixMessage::new(cover_message.0, cover_message.1)) .unwrap(); } + + async fn run(&mut self) { + // we should set initial delay only when we actually start the stream + self.next_delay = time::delay_for(mix_client::poisson::sample( + self.average_cover_message_sending_delay, + )); + + while let Some(_) = self.next().await { + self.on_new_message().await; + } + } + + pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + self.run().await; + }) + } } From 76cf73b70bbb2daa2093d1af2e0e3dea0fe22b50 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Wed, 4 Mar 2020 15:34:57 +0000 Subject: [PATCH 262/273] Exposed new method --- nym-client/src/client/cover_traffic_stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-client/src/client/cover_traffic_stream.rs b/nym-client/src/client/cover_traffic_stream.rs index 0eddaee926..91faf16851 100644 --- a/nym-client/src/client/cover_traffic_stream.rs +++ b/nym-client/src/client/cover_traffic_stream.rs @@ -48,7 +48,7 @@ impl Stream for LoopCoverTrafficStream { } impl LoopCoverTrafficStream { - fn new( + pub(crate) fn new( mix_tx: MixMessageSender, our_info: Destination, mut topology_access: TopologyAccessor, From b3434108404a766719bc5afa879355a83a9a253a Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 5 Mar 2020 10:04:33 +0000 Subject: [PATCH 263/273] Updated client config with tcp client related fields --- nym-client/src/config/mod.rs | 22 ++++++++++++++++++++++ nym-client/src/config/template.rs | 11 ++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/nym-client/src/config/mod.rs b/nym-client/src/config/mod.rs index 8b23dfa224..207f5b222a 100644 --- a/nym-client/src/config/mod.rs +++ b/nym-client/src/config/mod.rs @@ -18,6 +18,8 @@ const DEFAULT_AVERAGE_PACKET_DELAY: u64 = 200; const DEFAULT_FETCH_MESSAGES_DELAY: u64 = 1000; const DEFAULT_TOPOLOGY_REFRESH_RATE: u64 = 10_000; const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: u64 = 5_000; +const DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF: u64 = 10_000; // 10s +const DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF: u64 = 300_000; // 5min const DEFAULT_NUMBER_OF_HEALTHCHECK_TEST_PACKETS: u64 = 2; const DEFAULT_NODE_SCORE_THRESHOLD: f64 = 0.0; @@ -203,6 +205,14 @@ impl Config { pub fn get_node_score_threshold(&self) -> f64 { self.debug.node_score_threshold } + + pub fn get_packet_forwarding_initial_backoff(&self) -> time::Duration { + time::Duration::from_millis(self.debug.packet_forwarding_initial_backoff) + } + + pub fn get_packet_forwarding_maximum_backoff(&self) -> time::Duration { + time::Duration::from_millis(self.debug.packet_forwarding_maximum_backoff) + } } fn de_option_string<'de, D>(deserializer: D) -> Result, D::Error> @@ -356,6 +366,16 @@ pub struct Debug { /// node received during healthcheck. Node's score must be above that value to be /// considered healthy. node_score_threshold: f64, + + /// Initial value of an exponential backoff to reconnect to dropped TCP connection when + /// forwarding sphinx packets. + /// The provided value is interpreted as milliseconds. + packet_forwarding_initial_backoff: u64, + + /// Maximum value of an exponential backoff to reconnect to dropped TCP connection when + /// forwarding sphinx packets. + /// The provided value is interpreted as milliseconds. + packet_forwarding_maximum_backoff: u64, } impl Default for Debug { @@ -370,6 +390,8 @@ impl Default for Debug { topology_resolution_timeout: DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT, number_of_healthcheck_test_packets: DEFAULT_NUMBER_OF_HEALTHCHECK_TEST_PACKETS, node_score_threshold: DEFAULT_NODE_SCORE_THRESHOLD, + packet_forwarding_initial_backoff: DEFAULT_PACKET_FORWARDING_INITIAL_BACKOFF, + packet_forwarding_maximum_backoff: DEFAULT_PACKET_FORWARDING_MAXIMUM_BACKOFF, } } } diff --git a/nym-client/src/config/template.rs b/nym-client/src/config/template.rs index 561ae802ad..c18730ed4b 100644 --- a/nym-client/src/config/template.rs +++ b/nym-client/src/config/template.rs @@ -109,6 +109,15 @@ number_of_healthcheck_test_packets = {{ debug.number_of_healthcheck_test_packets # node received during healthcheck. Node's score must be above that value to be # considered healthy. node_score_threshold = {{ debug.node_score_threshold }} - + +# Initial value of an exponential backoff to reconnect to dropped TCP connection when +# forwarding sphinx packets. +# The provided value is interpreted as milliseconds. +packet_forwarding_initial_backoff = {{ debug.packet_forwarding_initial_backoff }} + +# Maximum value of an exponential backoff to reconnect to dropped TCP connection when +# forwarding sphinx packets. +# The provided value is interpreted as milliseconds. +packet_forwarding_maximum_backoff = {{ debug.packet_forwarding_maximum_backoff }} "# } From 374da49bcd24ec10dd3e124cce6ebcf5979268fd Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 5 Mar 2020 12:54:17 +0000 Subject: [PATCH 264/273] Initial version of nym-client using persistent tcp connections Not yet fully refactored --- Cargo.lock | 1 + common/topology/src/lib.rs | 4 +- nym-client/Cargo.toml | 1 + nym-client/src/client/cover_traffic_stream.rs | 20 +- nym-client/src/client/mix_traffic.rs | 76 +++- nym-client/src/client/mod.rs | 388 +++++++++++------- nym-client/src/client/provider_poller.rs | 19 +- nym-client/src/client/real_traffic_stream.rs | 109 ++--- nym-client/src/client/received_buffer.rs | 4 +- nym-client/src/client/topology_control.rs | 160 ++++---- nym-client/src/commands/run.rs | 4 +- nym-client/src/sockets/tcp.rs | 64 ++- nym-client/src/sockets/ws.rs | 66 ++- 13 files changed, 552 insertions(+), 364 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 15f3faaa2e..2a6b9e79c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1464,6 +1464,7 @@ dependencies = [ "healthcheck", "log", "mix-client", + "multi-tcp-client", "pem", "pemstore", "pretty_env_logger", diff --git a/common/topology/src/lib.rs b/common/topology/src/lib.rs index 5edba93d7d..eb81cdfd42 100644 --- a/common/topology/src/lib.rs +++ b/common/topology/src/lib.rs @@ -10,7 +10,9 @@ mod filter; pub mod mix; pub mod provider; -pub trait NymTopology: Sized + std::fmt::Debug + Send + Sync { +// TODO: Figure out why 'Clone' was required to have 'TopologyAccessor' working +// even though it only contains an Arc +pub trait NymTopology: Sized + std::fmt::Debug + Send + Sync + Clone { fn new(directory_server: String) -> Self; fn new_from_nodes( mix_nodes: Vec, diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index 87ba8de733..ac4bde4fc8 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -34,6 +34,7 @@ crypto = {path = "../common/crypto"} directory-client = { path = "../common/clients/directory-client" } healthcheck = { path = "../common/healthcheck" } mix-client = { path = "../common/clients/mix-client" } +multi-tcp-client = { path = "../common/clients/multi-tcp-client" } pemstore = {path = "../common/pemstore"} provider-client = { path = "../common/clients/provider-client" } sfw-provider-requests = { path = "../sfw-provider/sfw-provider-requests" } diff --git a/nym-client/src/client/cover_traffic_stream.rs b/nym-client/src/client/cover_traffic_stream.rs index 91faf16851..443f71e4c2 100644 --- a/nym-client/src/client/cover_traffic_stream.rs +++ b/nym-client/src/client/cover_traffic_stream.rs @@ -1,7 +1,7 @@ use crate::client::mix_traffic::{MixMessage, MixMessageSender}; use crate::client::topology_control::TopologyAccessor; use futures::task::{Context, Poll}; -use futures::{Stream, StreamExt}; +use futures::{Future, Stream, StreamExt}; use log::*; use sphinx::route::Destination; use std::pin::Pin; @@ -27,7 +27,7 @@ impl Stream for LoopCoverTrafficStream { // Perhaps this should be changed in the future. type Item = (); - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // it is not yet time to return a message if Pin::new(&mut self.next_delay).poll(cx).is_pending() { return Poll::Pending; @@ -36,7 +36,8 @@ impl Stream for LoopCoverTrafficStream { // we know it's time to send a message, so let's prepare delay for the next one // Get the `now` by looking at the current `delay` deadline let now = self.next_delay.deadline(); - let next_poisson_delay = mix_client::poisson::sample(self.average_message_sending_delay); + let next_poisson_delay = + mix_client::poisson::sample(self.average_cover_message_sending_delay); // The next interval value is `next_poisson_delay` after the one that just // yielded. @@ -47,11 +48,11 @@ impl Stream for LoopCoverTrafficStream { } } -impl LoopCoverTrafficStream { +impl LoopCoverTrafficStream { pub(crate) fn new( mix_tx: MixMessageSender, our_info: Destination, - mut topology_access: TopologyAccessor, + topology_access: TopologyAccessor, average_cover_message_sending_delay: time::Duration, average_packet_delay: time::Duration, ) -> Self { @@ -67,12 +68,7 @@ impl LoopCoverTrafficStream { async fn on_new_message(&mut self) { trace!("next cover message!"); - let topology = match self - .topology_access - .get_exclusive_topology_reference() - .await - .as_ref() - { + let topology = match self.topology_access.get_current_topology().await { None => { warn!("No valid topology detected - won't send any loop cover message this time"); return; @@ -83,7 +79,7 @@ impl LoopCoverTrafficStream { let cover_message = match mix_client::packet::loop_cover_message( self.our_info.address.clone(), self.our_info.identifier, - topology, + &topology, self.average_packet_delay, ) { Ok(message) => message, diff --git a/nym-client/src/client/mix_traffic.rs b/nym-client/src/client/mix_traffic.rs index bfcd3ddfc6..c71a7c3be6 100644 --- a/nym-client/src/client/mix_traffic.rs +++ b/nym-client/src/client/mix_traffic.rs @@ -1,10 +1,15 @@ use futures::channel::mpsc; use futures::StreamExt; -use log::{debug, error, info, trace}; +use log::*; use sphinx::SphinxPacket; use std::net::SocketAddr; +use std::time::Duration; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; pub(crate) struct MixMessage(SocketAddr, SphinxPacket); +pub(crate) type MixMessageSender = mpsc::UnboundedSender; +pub(crate) type MixMessageReceiver = mpsc::UnboundedReceiver; impl MixMessage { pub(crate) fn new(address: SocketAddr, packet: SphinxPacket) -> Self { @@ -12,26 +17,57 @@ impl MixMessage { } } -pub(crate) struct MixTrafficController; +// TODO: put our TCP client here +pub(crate) struct MixTrafficController<'a> { + tcp_client: multi_tcp_client::Client<'a>, + mix_rx: MixMessageReceiver, +} -impl MixTrafficController { - pub(crate) async fn run(mut rx: mpsc::UnboundedReceiver) { - info!("Mix Traffic Controller started!"); - let mix_client = mix_client::MixClient::new(); - while let Some(mix_message) = rx.next().await { - debug!("Got a mix_message for {:?}", mix_message.0); - let send_res = mix_client.send(mix_message.1, mix_message.0).await; - match send_res { - Ok(_) => { - trace!("sent a mix message"); - } - // TODO: should there be some kind of threshold of failed messages - // that if reached, the application blows? - Err(e) => error!( - "We failed to send the message to {} :( - {:?}", - mix_message.0, e - ), - }; +impl MixTrafficController<'static> { + pub(crate) async fn new( + initial_endpoints: Vec, + initial_reconnection_backoff: Duration, + maximum_reconnection_backoff: Duration, + mix_rx: MixMessageReceiver, + ) -> Self { + let tcp_client_config = multi_tcp_client::Config::new( + initial_endpoints, + initial_reconnection_backoff, + maximum_reconnection_backoff, + ); + + MixTrafficController { + tcp_client: multi_tcp_client::Client::new(tcp_client_config).await, + mix_rx, } } + + async fn on_message(&mut self, mix_message: MixMessage) { + debug!("Got a mix_message for {:?}", mix_message.0); + match self + .tcp_client + .send(mix_message.0, &mix_message.1.to_bytes()) + .await + { + Ok(_) => trace!("sent a mix message"), + // TODO: should there be some kind of threshold of failed messages + // that if reached, the application blows? + Err(e) => error!( + "We failed to send the packet to {} - {:?}", + mix_message.0, e + ), + }; + } + + pub(crate) async fn run(&mut self) { + while let Some(mix_message) = self.mix_rx.next().await { + self.on_message(mix_message).await; + } + } + + pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + self.run().await; + }) + } } diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index f2cc8b35d0..fcef9d8b64 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -1,14 +1,19 @@ -use crate::client::mix_traffic::MixTrafficController; -use crate::client::received_buffer::ReceivedMessagesBuffer; -use crate::client::topology_control::TopologyInnerRef; +use crate::client::cover_traffic_stream::LoopCoverTrafficStream; +use crate::client::mix_traffic::{MixMessageReceiver, MixMessageSender, MixTrafficController}; +use crate::client::provider_poller::{PolledMessagesReceiver, PolledMessagesSender}; +use crate::client::received_buffer::{ + ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController, +}; +use crate::client::topology_control::{ + TopologyAccessor, TopologyRefresher, TopologyRefresherConfig, +}; use crate::config::persistence::pathfinder::ClientPathfinder; use crate::config::{Config, SocketType}; use crate::sockets::tcp; use crate::sockets::ws; use crypto::identity::MixIdentityKeyPair; -use directory_client::presence::Topology; +use directory_client::presence; use futures::channel::mpsc; -use futures::join; use log::*; use pemstore::pemstore::PemStore; use sfw_provider_requests::AuthToken; @@ -24,14 +29,16 @@ mod real_traffic_stream; pub mod received_buffer; pub mod topology_control; +pub type InputMessageSender = mpsc::UnboundedSender; +pub type InputMessageReceiver = mpsc::UnboundedReceiver; + pub struct NymClient { + runtime: Runtime, config: Config, identity_keypair: MixIdentityKeyPair, // to be used by "send" function or socket, etc - pub input_tx: mpsc::UnboundedSender, - // the other end of the above channel - input_rx: mpsc::UnboundedReceiver, + pub input_tx: Option, } #[derive(Debug)] @@ -51,32 +58,220 @@ impl NymClient { pub fn new(config: Config) -> Self { let identity_keypair = Self::load_identity_keys(&config); - let (input_tx, input_rx) = mpsc::unbounded::(); NymClient { + runtime: Runtime::new().unwrap(), config, identity_keypair, - input_tx, - input_rx, + input_tx: None, } } + pub fn as_mix_destination(&self) -> Destination { + Destination::new( + self.identity_keypair.public_key().derive_address(), + // TODO: what with SURBs? + Default::default(), + ) + } + async fn get_provider_socket_address( - &self, - topology_ctrl_ref: TopologyInnerRef, + provider_id: String, + mut topology_accessor: TopologyAccessor, ) -> SocketAddr { - // this is temporary and assumes there exists only a single provider. - topology_ctrl_ref.read().await.topology.as_ref().unwrap() + topology_accessor.get_current_topology().await.as_ref().expect("The current network topoloy is empty - are you using correct directory server?") .providers() - .first() - .expect("Could not get a compatible provider from the initial network topology, are you using the right directory server?") + .iter() + .find(|provider| provider.pub_key == provider_id) + .expect(format!("Could not find provider with id {:?} - are you sure it is still online? Perhaps try to run `nym-client init` again to obtain a new provider", provider_id).as_ref()) .client_listener } - pub fn start(self) -> Result<(), Box> { - info!("Starting nym client"); - let mut rt = Runtime::new()?; + // future constantly pumping loop cover traffic at some specified average rate + // the pumped traffic goes to the MixTrafficController + fn start_cover_traffic_stream( + &self, + topology_accessor: TopologyAccessor, + mix_tx: MixMessageSender, + ) { + info!("Starting loop cover traffic stream..."); + // we need to explicitly enter runtime due to "next_delay: time::delay_for(Default::default())" + // set in the constructor which HAS TO be called within context of a tokio runtime + self.runtime + .enter(|| { + LoopCoverTrafficStream::new( + mix_tx, + self.as_mix_destination(), + topology_accessor, + self.config.get_loop_cover_traffic_average_delay(), + self.config.get_average_packet_delay(), + ) + }) + .start(self.runtime.handle()); + } + fn start_real_traffic_stream( + &self, + topology_accessor: TopologyAccessor, + mix_tx: MixMessageSender, + input_rx: InputMessageReceiver, + ) { + info!("Starting real traffic stream..."); + // we need to explicitly enter runtime due to "next_delay: time::delay_for(Default::default())" + // set in the constructor which HAS TO be called within context of a tokio runtime + self.runtime + .enter(|| { + real_traffic_stream::OutQueueControl::new( + mix_tx, + input_rx, + self.as_mix_destination(), + topology_accessor, + self.config.get_average_packet_delay(), + self.config.get_message_sending_average_delay(), + ) + }) + .start(self.runtime.handle()); + } + + // buffer controlling all messages fetched from provider + // required so that other components would be able to use them (say the websocket) + fn start_received_messages_buffer_controller( + &self, + query_receiver: ReceivedBufferRequestReceiver, + poller_receiver: PolledMessagesReceiver, + ) { + info!("Starting 'received messages buffer controller'..."); + ReceivedMessagesBufferController::new(query_receiver, poller_receiver) + .start(self.runtime.handle()) + } + + // future constantly trying to fetch any received messages from the provider + // the received messages are sent to ReceivedMessagesBuffer to be available to rest of the system + fn start_provider_poller( + &mut self, + topology_accessor: TopologyAccessor, + poller_input_tx: PolledMessagesSender, + ) { + info!("Starting provider poller..."); + // we already have our provider written in the config + let provider_id = self.config.get_provider_id(); + + let provider_client_listener_address = self.runtime.block_on( + Self::get_provider_socket_address(provider_id, topology_accessor), + ); + + let mut provider_poller = provider_poller::ProviderPoller::new( + poller_input_tx, + provider_client_listener_address, + self.identity_keypair.public_key().derive_address(), + self.config + .get_provider_auth_token() + .map(|str_token| AuthToken::try_from_base58_string(str_token).ok()) + .unwrap_or(None), + self.config.get_fetch_message_delay(), + ); + + if !provider_poller.is_registered() { + info!("Trying to perform initial provider registration..."); + self.runtime + .block_on(provider_poller.perform_initial_registration()) + .expect("Failed to perform initial provider registration"); + } + provider_poller.start(self.runtime.handle()); + } + + // future responsible for periodically polling directory server and updating + // the current global view of topology + fn start_topology_refresher( + &mut self, + topology_accessor: TopologyAccessor, + ) { + let healthcheck_keys = MixIdentityKeyPair::new(); + + let topology_refresher_config = TopologyRefresherConfig::new( + self.config.get_directory_server(), + self.config.get_topology_refresh_rate(), + healthcheck_keys, + self.config.get_topology_resolution_timeout(), + self.config.get_number_of_healthcheck_test_packets() as usize, + self.config.get_node_score_threshold(), + ); + let mut topology_refresher = + 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!("Obtaining initial network topology..."); + self.runtime.block_on(topology_refresher.refresh()); + info!("Starting topology refresher..."); + topology_refresher.start(self.runtime.handle()); + } + + // controller for sending sphinx packets to mixnet (either real traffic or cover traffic) + fn start_mix_traffic_controller(&mut self, mix_rx: MixMessageReceiver) { + info!("Starting mix trafic controller..."); + // TODO: possible optimisation: set the initial endpoints to all known mixes from layer 1 + let initial_mix_endpoints = Vec::new(); + self.runtime + .block_on(MixTrafficController::new( + initial_mix_endpoints, + self.config.get_packet_forwarding_initial_backoff(), + self.config.get_packet_forwarding_maximum_backoff(), + mix_rx, + )) + .start(self.runtime.handle()); + } + + fn start_socket_listener( + &self, + topology_accessor: TopologyAccessor, + received_messages_buffer_output_tx: ReceivedBufferRequestSender, + input_tx: InputMessageSender, + ) { + match self.config.get_socket_type() { + SocketType::WebSocket => { + ws::start_websocket( + self.runtime.handle(), + self.config.get_listening_port(), + input_tx, + received_messages_buffer_output_tx, + self.identity_keypair.public_key().derive_address(), + topology_accessor, + ); + } + SocketType::TCP => { + tcp::start_tcpsocket( + self.runtime.handle(), + self.config.get_listening_port(), + input_tx, + received_messages_buffer_output_tx, + self.identity_keypair.public_key().derive_address(), + topology_accessor, + ); + } + SocketType::None => (), + } + } + + /// EXPERIMENTAL DIRECT RUST API + pub fn send_message(&self) {} + + /// blocking version of `start` method. Will run forever (or until SIGINT is sent) + pub fn run_forever(&mut self) { + self.start(); + if let Err(e) = self.runtime.block_on(tokio::signal::ctrl_c()) { + error!( + "There was an error while capturing SIGINT - {:?}. We will terminate regardless", + e + ); + } + + println!( + "Received SIGINT - the mixnode will terminate now (threads are not YET nicely stopped)" + ); + } + + pub fn start(&mut self) { + info!("Starting nym client"); // channels for inter-component communication // mix_tx is the transmitter for any component generating sphinx packets that are to be sent to the mixnet @@ -94,142 +289,27 @@ impl NymClient { let (received_messages_buffer_output_tx, received_messages_buffer_output_rx) = mpsc::unbounded(); - let self_address = self.identity_keypair.public_key().derive_address(); + // channels responsible for controlling real messages + let (input_tx, input_rx) = mpsc::unbounded::(); - // generate same type of keys we have as our identity - let healthcheck_keys = MixIdentityKeyPair::new(); - - info!("Trying to obtain initial compatible network topology before starting up rest of modules"); - // TODO: when we switch to our graph topology, we need to remember to change 'Topology' type - let topology_controller_config = topology_control::TopologyControlConfig::::new( - self.config.get_directory_server(), - self.config.get_topology_refresh_rate(), - healthcheck_keys, - self.config.get_topology_resolution_timeout(), - self.config.get_number_of_healthcheck_test_packets() as usize, + // TODO: when we switch to our graph topology, we need to remember to change 'presence::Topology' type + let shared_topology_accessor = TopologyAccessor::::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()); + self.start_received_messages_buffer_controller( + received_messages_buffer_output_rx, + poller_input_rx, ); - - let topology_controller = rt.block_on(topology_control::TopologyControl::::new( - topology_controller_config, - )); - - let provider_client_listener_address = - rt.block_on(self.get_provider_socket_address(topology_controller.get_inner_ref())); - - let mut provider_poller = provider_poller::ProviderPoller::new( - poller_input_tx, - provider_client_listener_address, - self_address.clone(), - self.config - .get_provider_auth_token() - .map(|str_token| AuthToken::try_from_base58_string(str_token).ok()) - .unwrap_or(None), - self.config.get_fetch_message_delay(), + self.start_provider_poller(shared_topology_accessor.clone(), poller_input_tx); + self.start_mix_traffic_controller(mix_rx); + self.start_cover_traffic_stream(shared_topology_accessor.clone(), mix_tx.clone()); + self.start_real_traffic_stream(shared_topology_accessor.clone(), mix_tx, input_rx); + self.start_socket_listener( + shared_topology_accessor, + received_messages_buffer_output_tx, + input_tx.clone(), ); - - // registration - if let Err(err) = rt.block_on(provider_poller.perform_initial_registration()) { - panic!("Failed to perform initial provider registration: {:?}", err); - }; - - // setup all of futures for the components running on the client - - // buffer controlling all messages fetched from provider - // required so that other components would be able to use them (say the websocket) - let received_messages_buffer_controllers_future = rt.spawn( - ReceivedMessagesBuffer::new() - .start_controllers(poller_input_rx, received_messages_buffer_output_rx), - ); - - // controller for sending sphinx packets to mixnet (either real traffic or cover traffic) - let mix_traffic_future = rt.spawn(MixTrafficController::run(mix_rx)); - - // future constantly pumping loop cover traffic at some specified average rate - // the pumped traffic goes to the MixTrafficController - let loop_cover_traffic_future = - rt.spawn(cover_traffic_stream::start_loop_cover_traffic_stream( - mix_tx.clone(), - Destination::new(self_address.clone(), Default::default()), - topology_controller.get_inner_ref(), - self.config.get_loop_cover_traffic_average_delay(), - self.config.get_average_packet_delay(), - )); - - // cloning arguments required by OutQueueControl; required due to move - let input_rx = self.input_rx; - let topology_ref = topology_controller.get_inner_ref(); - let average_packet_delay = self.config.get_average_packet_delay(); - let message_sending_average_delay = self.config.get_message_sending_average_delay(); - let self_address_clone = self_address.clone(); - // future constantly pumping traffic at some specified average rate - // if a real message is available on 'input_rx' that might have been received from say - // the websocket, the real message is used, otherwise a loop cover message is generated - // the pumped traffic goes to the MixTrafficController - let out_queue_control_future = rt.spawn(async move { - real_traffic_stream::OutQueueControl::new( - mix_tx, - input_rx, - Destination::new(self_address_clone, Default::default()), - topology_ref, - average_packet_delay, - message_sending_average_delay, - ) - .run_out_queue_control() - .await - }); - - // future constantly trying to fetch any received messages from the provider - // the received messages are sent to ReceivedMessagesBuffer to be available to rest of the system - let provider_polling_future = rt.spawn(provider_poller.start_provider_polling()); - - match self.config.get_socket_type() { - SocketType::WebSocket => { - rt.spawn(ws::start_websocket( - self.config.get_listening_port(), - self.input_tx, - received_messages_buffer_output_tx, - self_address, - topology_controller.get_inner_ref(), - )); - } - SocketType::TCP => { - rt.spawn(tcp::start_tcpsocket( - self.config.get_listening_port(), - self.input_tx, - received_messages_buffer_output_tx, - self_address, - topology_controller.get_inner_ref(), - )); - } - SocketType::None => (), - } - - // future responsible for periodically polling directory server and updating - // the current global view of topology - let topology_refresher_future = rt.spawn(topology_controller.run_refresher()); - - rt.block_on(async { - let future_results = join!( - received_messages_buffer_controllers_future, - mix_traffic_future, - loop_cover_traffic_future, - out_queue_control_future, - provider_polling_future, - topology_refresher_future, - ); - - assert!( - future_results.0.is_ok() - && future_results.1.is_ok() - && future_results.2.is_ok() - && future_results.3.is_ok() - && future_results.4.is_ok() - && future_results.5.is_ok() - ); - }); - - // this line in theory should never be reached as the runtime should be permanently blocked on traffic senders - error!("The client went kaput..."); - Ok(()) + self.input_tx = Some(input_tx); } } diff --git a/nym-client/src/client/provider_poller.rs b/nym-client/src/client/provider_poller.rs index 526acb9e4b..24d329c9ae 100644 --- a/nym-client/src/client/provider_poller.rs +++ b/nym-client/src/client/provider_poller.rs @@ -1,10 +1,15 @@ use futures::channel::mpsc; -use log::{debug, error, info, trace, warn}; +use log::*; use provider_client::ProviderClientError; use sfw_provider_requests::AuthToken; use sphinx::route::DestinationAddressBytes; use std::net::SocketAddr; use std::time; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; + +pub(crate) type PolledMessagesSender = mpsc::UnboundedSender>>; +pub(crate) type PolledMessagesReceiver = mpsc::UnboundedReceiver>>; pub(crate) struct ProviderPoller { polling_rate: time::Duration, @@ -31,11 +36,15 @@ impl ProviderPoller { } } + pub(crate) fn is_registered(&self) -> bool { + self.provider_client.is_registered() + } + // This method is only temporary until registration is moved to `client init` pub(crate) async fn perform_initial_registration(&mut self) -> Result<(), ProviderClientError> { debug!("performing initial provider registration"); - if !self.provider_client.is_registered() { + if !self.is_registered() { let auth_token = match self.provider_client.register().await { // in this particular case we can ignore this error Err(ProviderClientError::ClientAlreadyRegisteredError) => return Ok(()), @@ -52,8 +61,6 @@ impl ProviderPoller { } pub(crate) async fn start_provider_polling(self) { - info!("Starting provider poller"); - let loop_message = &mix_client::packet::LOOP_COVER_MESSAGE_PAYLOAD.to_vec(); let dummy_message = &sfw_provider_requests::DUMMY_MESSAGE_CONTENT.to_vec(); @@ -86,4 +93,8 @@ impl ProviderPoller { tokio::time::delay_for(self.polling_rate).await; } } + + pub(crate) fn start(self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { self.start_provider_polling().await }) + } } diff --git a/nym-client/src/client/real_traffic_stream.rs b/nym-client/src/client/real_traffic_stream.rs index 0fb9a198fe..43af644874 100644 --- a/nym-client/src/client/real_traffic_stream.rs +++ b/nym-client/src/client/real_traffic_stream.rs @@ -1,5 +1,5 @@ use crate::client::mix_traffic::MixMessage; -use crate::client::topology_control::TopologyInnerRef; +use crate::client::topology_control::TopologyAccessor; use crate::client::InputMessage; use futures::channel::mpsc; use futures::task::{Context, Poll}; @@ -8,6 +8,8 @@ use log::{error, info, trace, warn}; use sphinx::route::Destination; use std::pin::Pin; use std::time::Duration; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; use tokio::time; use topology::NymTopology; @@ -18,7 +20,7 @@ pub(crate) struct OutQueueControl { mix_tx: mpsc::UnboundedSender, input_rx: mpsc::UnboundedReceiver, our_info: Destination, - topology_ctrl_ref: TopologyInnerRef, + topology_access: TopologyAccessor, } pub(crate) enum StreamMessage { @@ -60,12 +62,12 @@ impl Stream for OutQueueControl { } } -impl OutQueueControl { +impl OutQueueControl { pub(crate) fn new( mix_tx: mpsc::UnboundedSender, input_rx: mpsc::UnboundedReceiver, our_info: Destination, - topology: TopologyInnerRef, + topology_access: TopologyAccessor, average_packet_delay: Duration, average_message_sending_delay: Duration, ) -> Self { @@ -76,10 +78,57 @@ impl OutQueueControl { mix_tx, input_rx, our_info, - topology_ctrl_ref: topology, + topology_access, } } + async fn on_message(&mut self, next_message: StreamMessage) { + trace!("created new message"); + let topology = match self.topology_access.get_current_topology().await { + None => { + warn!("No valid topology detected - won't send any real or loop message this time"); + // TODO: this creates a potential problem: we can lose real messages if we were + // unable to get topology, perhaps we should store them in some buffer? + return; + } + Some(topology) => topology, + }; + + let next_packet = match next_message { + StreamMessage::Cover => mix_client::packet::loop_cover_message( + self.our_info.address.clone(), + self.our_info.identifier, + &topology, + self.average_packet_delay, + ), + StreamMessage::Real(real_message) => mix_client::packet::encapsulate_message( + real_message.0, + real_message.1, + &topology, + self.average_packet_delay, + ), + }; + + let next_packet = match next_packet { + Ok(message) => message, + Err(err) => { + error!( + "Somehow we managed to create an invalid traffic message - {:?}", + err + ); + return; + } + }; + + // if this one fails, there's no retrying because it means that either: + // - we run out of memory + // - the receiver channel is closed + // in either case there's no recovery and we can only panic + self.mix_tx + .unbounded_send(MixMessage::new(next_packet.0, next_packet.1)) + .unwrap(); + } + pub(crate) async fn run_out_queue_control(mut self) { // we should set initial delay only when we actually start the stream self.next_delay = time::delay_for(mix_client::poisson::sample( @@ -88,51 +137,11 @@ impl OutQueueControl { info!("starting out queue controller"); while let Some(next_message) = self.next().await { - trace!("created new message"); - let read_lock = self.topology_ctrl_ref.read().await; - let topology = match read_lock.topology.as_ref() { - None => { - warn!( - "No valid topology detected - won't send any loop cover message this time" - ); - continue; - } - Some(topology) => topology, - }; - - let next_packet = match next_message { - StreamMessage::Cover => mix_client::packet::loop_cover_message( - self.our_info.address.clone(), - self.our_info.identifier, - topology, - self.average_packet_delay, - ), - StreamMessage::Real(real_message) => mix_client::packet::encapsulate_message( - real_message.0, - real_message.1, - topology, - self.average_packet_delay, - ), - }; - - let next_packet = match next_packet { - Ok(message) => message, - Err(err) => { - error!( - "Somehow we managed to create an invalid traffic message - {:?}", - err - ); - continue; - } - }; - - // if this one fails, there's no retrying because it means that either: - // - we run out of memory - // - the receiver channel is closed - // in either case there's no recovery and we can only panic - self.mix_tx - .unbounded_send(MixMessage::new(next_packet.0, next_packet.1)) - .unwrap(); + self.on_message(next_message).await; } } + + pub(crate) fn start(self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { self.run_out_queue_control().await }) + } } diff --git a/nym-client/src/client/received_buffer.rs b/nym-client/src/client/received_buffer.rs index d776088151..123fc92882 100644 --- a/nym-client/src/client/received_buffer.rs +++ b/nym-client/src/client/received_buffer.rs @@ -38,7 +38,7 @@ impl ReceivedMessagesBuffer { async fn acquire_and_empty(&mut self) -> Vec> { trace!("Emptying the buffer and returning all messages"); - let mutex_guard = self.inner.lock().await; + let mut mutex_guard = self.inner.lock().await; std::mem::replace(&mut mutex_guard.messages, Vec::new()) } } @@ -116,7 +116,7 @@ impl ReceivedMessagesBufferController { } } - pub(crate) fn start(mut self, handle: &Handle) { + pub(crate) fn start(self, handle: &Handle) { // TODO: should we do anything with JoinHandle(s) returned by start methods? self.messsage_receiver.start(handle); self.request_receiver.start(handle); diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index c7403503c3..5bedcaddc8 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -1,23 +1,51 @@ use crate::built_info; use crypto::identity::MixIdentityKeyPair; +use futures::lock::Mutex; use healthcheck::HealthChecker; -use log::{error, info, trace, warn}; -use std::marker::PhantomData; +use log::*; use std::sync::Arc; use std::time; -use tokio::sync::RwLock as FRwLock; +use std::time::Duration; +use tokio::runtime::Handle; +// use tokio::sync::RwLock; +use tokio::task::JoinHandle; use topology::NymTopology; -const NODE_HEALTH_THRESHOLD: f64 = 0.0; +struct TopologyAccessorInner(Option); -// auxiliary type for ease of use -pub type TopologyInnerRef = Arc>>; +impl TopologyAccessorInner { + fn new() -> Self { + TopologyAccessorInner(None) + } -pub(crate) struct TopologyControl { - directory_server: String, - inner: Arc>>, - health_checker: HealthChecker, - refresh_rate: time::Duration, + fn update(&mut self, new: Option) { + self.0 = new; + } +} + +#[derive(Clone, Debug)] +pub(crate) struct TopologyAccessor { + // TODO: this requires some actual benchmarking to determine if obtaining mutex is not going + // to cause some bottlenecking and whether perhaps RwLock would be better + inner: Arc>>, +} + +impl TopologyAccessor { + pub(crate) fn new() -> Self { + TopologyAccessor { + inner: Arc::new(Mutex::new(TopologyAccessorInner::new())), + } + } + + async fn update_global_topology(&mut self, new_topology: Option) { + self.inner.lock().await.update(new_topology); + } + + pub(crate) async fn get_current_topology(&mut self) -> Option { + // TODO: considering topology is gotten quite frequently, the clone call might be rather + // expensive in the grand scheme of things... + self.inner.lock().await.0.clone() + } } #[derive(Debug)] @@ -26,40 +54,48 @@ enum TopologyError { NoValidPathsError, } -pub(crate) struct TopologyControlConfig { +pub(crate) struct TopologyRefresherConfig { directory_server: String, refresh_rate: time::Duration, identity_keypair: MixIdentityKeyPair, resolution_timeout: time::Duration, number_test_packets: usize, - - // the only reason I put phantom data here is so that we would we able to infer type - // of TopologyControl directly from the provided config rather than having to - // specify it during TopologyControl::::new() call - _topology_type_phantom: PhantomData<*const T>, + node_score_threshold: f64, } -impl TopologyControlConfig { +impl TopologyRefresherConfig { pub(crate) fn new( directory_server: String, refresh_rate: time::Duration, identity_keypair: MixIdentityKeyPair, resolution_timeout: time::Duration, number_test_packets: usize, + node_score_threshold: f64, ) -> Self { - TopologyControlConfig { + TopologyRefresherConfig { directory_server, refresh_rate, identity_keypair, resolution_timeout, number_test_packets, - _topology_type_phantom: PhantomData, + node_score_threshold, } } } -impl TopologyControl { - pub(crate) async fn new(cfg: TopologyControlConfig) -> Self { +pub(crate) struct TopologyRefresher { + directory_server: String, + topology_accessor: TopologyAccessor, + health_checker: HealthChecker, + refresh_rate: Duration, + node_score_threshold: f64, +} + +impl TopologyRefresher { + pub(crate) fn new( + cfg: TopologyRefresherConfig, + topology_accessor: TopologyAccessor, + ) -> Self { // this is a temporary solution as the healthcheck will eventually be moved to validators let health_checker = healthcheck::HealthChecker::new( cfg.resolution_timeout, @@ -67,26 +103,13 @@ impl TopologyControl { cfg.identity_keypair, ); - let mut topology_control = TopologyControl { + TopologyRefresher { directory_server: cfg.directory_server, - refresh_rate: cfg.refresh_rate, - inner: Arc::new(FRwLock::new(Inner::new(None))), + topology_accessor, health_checker, - }; - - // best effort approach to try to get a valid topology after call to 'new' - let initial_topology = match topology_control.get_current_compatible_topology().await { - Ok(topology) => Some(topology), - Err(err) => { - error!("Initial topology is invalid - {:?}. Right now it will be impossible to send any packets through the mixnet!", err); - None - } - }; - topology_control - .update_global_topology(initial_topology) - .await; - - topology_control + refresh_rate: cfg.refresh_rate, + node_score_threshold: cfg.node_score_threshold, + } } async fn get_current_compatible_topology(&self) -> Result { @@ -110,7 +133,7 @@ impl TopologyControl { }; let healthy_topology = healthcheck_scores - .filter_topology_by_score(&version_filtered_topology, NODE_HEALTH_THRESHOLD); + .filter_topology_by_score(&version_filtered_topology, self.node_score_threshold); // make sure you can still send a packet through the network: if !healthy_topology.can_construct_path_through() { @@ -120,42 +143,27 @@ impl TopologyControl { Ok(healthy_topology) } - pub(crate) fn get_inner_ref(&self) -> Arc>> { - self.inner.clone() + pub(crate) async fn refresh(&mut self) { + trace!("Refreshing the topology"); + let new_topology = match self.get_current_compatible_topology().await { + Ok(topology) => Some(topology), + Err(err) => { + warn!("the obtained topology seems to be invalid - {:?}, it will be impossible to send packets through", err); + None + } + }; + + self.topology_accessor + .update_global_topology(new_topology) + .await; } - async fn update_global_topology(&mut self, new_topology: Option) { - // acquire write lock - let mut write_lock = self.inner.write().await; - write_lock.topology = new_topology; - } - - pub(crate) async fn run_refresher(mut self) { - info!("Starting topology refresher"); - loop { - trace!("Refreshing the topology"); - let new_topology_res = self.get_current_compatible_topology().await; - - let new_topology = match new_topology_res { - Ok(topology) => Some(topology), - Err(err) => { - warn!("the obtained topology seems to be invalid - {:?}, it will be impossible to send packets through", err); - None - } - }; - - self.update_global_topology(new_topology).await; - tokio::time::delay_for(self.refresh_rate).await; - } - } -} - -pub struct Inner { - pub topology: Option, -} - -impl Inner { - fn new(topology: Option) -> Self { - Inner { topology } + pub(crate) fn start(mut self, handle: &Handle) -> JoinHandle<()> { + handle.spawn(async move { + loop { + self.refresh().await; + tokio::time::delay_for(self.refresh_rate).await; + } + }) } } diff --git a/nym-client/src/commands/run.rs b/nym-client/src/commands/run.rs index 9d2918a204..9f59192bf5 100644 --- a/nym-client/src/commands/run.rs +++ b/nym-client/src/commands/run.rs @@ -50,7 +50,5 @@ pub fn execute(matches: &ArgMatches) { .expect("Failed to load config file"); config = override_config(config, matches); - - let client = NymClient::new(config); - client.start().unwrap(); + NymClient::new(config).run_forever(); } diff --git a/nym-client/src/sockets/tcp.rs b/nym-client/src/sockets/tcp.rs index c8b65c0df4..54ae1281a9 100644 --- a/nym-client/src/sockets/tcp.rs +++ b/nym-client/src/sockets/tcp.rs @@ -1,5 +1,5 @@ -use crate::client::received_buffer::BufferResponse; -use crate::client::topology_control::TopologyInnerRef; +use crate::client::received_buffer::ReceivedBufferResponse; +use crate::client::topology_control::TopologyAccessor; use crate::client::InputMessage; use futures::channel::{mpsc, oneshot}; use futures::future::FutureExt; @@ -11,6 +11,8 @@ use std::convert::TryFrom; use std::io; use std::net::SocketAddr; use tokio::prelude::*; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; use topology::NymTopology; const SEND_REQUEST_PREFIX: u8 = 1; @@ -110,7 +112,9 @@ impl ClientRequest { ServerResponse::Send } - async fn handle_fetch(mut msg_query: mpsc::UnboundedSender) -> ServerResponse { + async fn handle_fetch( + mut msg_query: mpsc::UnboundedSender, + ) -> ServerResponse { trace!("handle_fetch called"); let (res_tx, res_rx) = oneshot::channel(); if msg_query.send(res_tx).await.is_err() { @@ -133,9 +137,10 @@ impl ClientRequest { ServerResponse::Fetch { messages } } - async fn handle_get_clients(topology: &TopologyInnerRef) -> ServerResponse { - let topology_data = &topology.read().await.topology; - match topology_data { + async fn handle_get_clients( + mut topology_accessor: TopologyAccessor, + ) -> ServerResponse { + match topology_accessor.get_current_topology().await { Some(topology) => { let clients = topology .providers() @@ -228,7 +233,7 @@ async fn handle_connection( } ClientRequest::Fetch => ClientRequest::handle_fetch(request_handling_data.msg_query).await, ClientRequest::GetClients => { - ClientRequest::handle_get_clients(&request_handling_data.topology).await + ClientRequest::handle_get_clients(request_handling_data.topology_accessor).await } ClientRequest::OwnDetails => { ClientRequest::handle_own_details(request_handling_data.self_address).await @@ -240,17 +245,17 @@ async fn handle_connection( struct RequestHandlingData { msg_input: mpsc::UnboundedSender, - msg_query: mpsc::UnboundedSender, + msg_query: mpsc::UnboundedSender, self_address: DestinationAddressBytes, - topology: TopologyInnerRef, + topology_accessor: TopologyAccessor, } async fn accept_connection( mut socket: tokio::net::TcpStream, msg_input: mpsc::UnboundedSender, - msg_query: mpsc::UnboundedSender, + msg_query: mpsc::UnboundedSender, self_address: DestinationAddressBytes, - topology: TopologyInnerRef, + topology_accessor: TopologyAccessor, ) { let address = socket .peer_addr() @@ -271,7 +276,7 @@ async fn accept_connection( } Ok(n) => { let request_handling_data = RequestHandlingData { - topology: topology.clone(), + topology_accessor: topology_accessor.clone(), msg_input: msg_input.clone(), msg_query: msg_query.clone(), self_address: self_address.clone(), @@ -295,16 +300,16 @@ async fn accept_connection( } } -pub async fn start_tcpsocket( +pub(crate) async fn run_tcpsocket( listening_port: u16, message_tx: mpsc::UnboundedSender, - received_messages_query_tx: mpsc::UnboundedSender, + received_messages_query_tx: mpsc::UnboundedSender, self_address: DestinationAddressBytes, - topology: TopologyInnerRef, -) -> Result<(), TCPSocketError> { + topology_accessor: TopologyAccessor, +) { let address = SocketAddr::new("127.0.0.1".parse().unwrap(), listening_port); info!("Starting tcp socket listener at {:?}", address); - let mut listener = tokio::net::TcpListener::bind(address).await?; + let mut listener = tokio::net::TcpListener::bind(address).await.unwrap(); while let Ok((stream, _)) = listener.accept().await { // it's fine to be cloning the channel on all new connection, because in principle @@ -314,10 +319,27 @@ pub async fn start_tcpsocket( message_tx.clone(), received_messages_query_tx.clone(), self_address.clone(), - topology.clone(), + topology_accessor.clone(), )); } - - error!("The tcpsocket went kaput..."); - Ok(()) +} + +pub(crate) fn start_tcpsocket( + handle: &Handle, + listening_port: u16, + message_tx: mpsc::UnboundedSender, + received_messages_query_tx: mpsc::UnboundedSender, + self_address: DestinationAddressBytes, + topology_accessor: TopologyAccessor, +) -> JoinHandle<()> { + handle.spawn(async move { + run_tcpsocket( + listening_port, + message_tx, + received_messages_query_tx, + self_address, + topology_accessor, + ) + .await; + }) } diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index 845c4971c9..07a27acac7 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -1,5 +1,5 @@ -use crate::client::received_buffer::BufferResponse; -use crate::client::topology_control::TopologyInnerRef; +use crate::client::received_buffer::ReceivedBufferResponse; +use crate::client::topology_control::TopologyAccessor; use crate::client::InputMessage; use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; use futures::channel::{mpsc, oneshot}; @@ -12,6 +12,8 @@ use sphinx::route::{Destination, DestinationAddressBytes}; use std::convert::TryFrom; use std::io; use std::net::SocketAddr; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; use tokio_tungstenite::tungstenite::protocol::frame::coding::CloseCode; use tokio_tungstenite::tungstenite::protocol::{CloseFrame, Message}; use topology::NymTopology; @@ -19,10 +21,10 @@ use topology::NymTopology; struct Connection { address: SocketAddr, msg_input: mpsc::UnboundedSender, - msg_query: mpsc::UnboundedSender, + msg_query: mpsc::UnboundedSender, rx: UnboundedReceiver, self_address: DestinationAddressBytes, - topology: TopologyInnerRef, + topology_accessor: TopologyAccessor, tx: UnboundedSender, } @@ -49,7 +51,9 @@ impl Connection { ClientRequest::handle_send(message, recipient_address, self.msg_input.clone()).await } ClientRequest::Fetch => ClientRequest::handle_fetch(self.msg_query.clone()).await, - ClientRequest::GetClients => ClientRequest::handle_get_clients(&self.topology).await, + ClientRequest::GetClients => { + ClientRequest::handle_get_clients(self.topology_accessor.clone()).await + } ClientRequest::OwnDetails => { ClientRequest::handle_own_details(self.self_address.clone()).await } @@ -229,7 +233,9 @@ impl ClientRequest { ServerResponse::Send } - async fn handle_fetch(mut msg_query: mpsc::UnboundedSender) -> ServerResponse { + async fn handle_fetch( + mut msg_query: mpsc::UnboundedSender, + ) -> ServerResponse { let (res_tx, res_rx) = oneshot::channel(); if msg_query.send(res_tx).await.is_err() { warn!("Failed to handle_fetch. msg_query.send() is an error."); @@ -265,9 +271,10 @@ impl ClientRequest { ServerResponse::Fetch { messages } } - async fn handle_get_clients(topology: &TopologyInnerRef) -> ServerResponse { - let topology_data = &topology.read().await.topology; - match topology_data { + async fn handle_get_clients( + mut topology_accessor: TopologyAccessor, + ) -> ServerResponse { + match topology_accessor.get_current_topology().await { Some(topology) => { let clients = topology .providers() @@ -313,9 +320,9 @@ impl Into for ServerResponse { async fn accept_connection( stream: tokio::net::TcpStream, msg_input: mpsc::UnboundedSender, - msg_query: mpsc::UnboundedSender, + msg_query: mpsc::UnboundedSender, self_address: DestinationAddressBytes, - topology: TopologyInnerRef, + topology_accessor: TopologyAccessor, ) { let address = stream .peer_addr() @@ -339,7 +346,7 @@ async fn accept_connection( address, rx: msg_rx, tx: response_tx, - topology, + topology_accessor, msg_input, msg_query, self_address, @@ -391,16 +398,16 @@ async fn accept_connection( } } -pub async fn start_websocket( +pub(crate) async fn run_websocket( listening_port: u16, message_tx: mpsc::UnboundedSender, - received_messages_query_tx: mpsc::UnboundedSender, + received_messages_query_tx: mpsc::UnboundedSender, self_address: DestinationAddressBytes, - topology: TopologyInnerRef, -) -> Result<(), WebSocketError> { + topology_accessor: TopologyAccessor, +) { let address = SocketAddr::new("127.0.0.1".parse().unwrap(), listening_port); info!("Starting websocket listener at {:?}", address); - let mut listener = tokio::net::TcpListener::bind(address).await?; + let mut listener = tokio::net::TcpListener::bind(address).await.unwrap(); while let Ok((stream, _)) = listener.accept().await { // it's fine to be cloning the channel on all new connection, because in principle @@ -410,10 +417,27 @@ pub async fn start_websocket( message_tx.clone(), received_messages_query_tx.clone(), self_address.clone(), - topology.clone(), + topology_accessor.clone(), )); } - - error!("The websocket went kaput..."); - Ok(()) +} + +pub(crate) fn start_websocket( + handle: &Handle, + listening_port: u16, + message_tx: mpsc::UnboundedSender, + received_messages_query_tx: mpsc::UnboundedSender, + self_address: DestinationAddressBytes, + topology_accessor: TopologyAccessor, +) -> JoinHandle<()> { + handle.spawn(async move { + run_websocket( + listening_port, + message_tx, + received_messages_query_tx, + self_address, + topology_accessor, + ) + .await; + }) } From 9eae7a23ce758913117db1a138d31b6ced3b65b9 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 5 Mar 2020 14:59:55 +0000 Subject: [PATCH 265/273] Significantly reduced number of calls to clone topology --- common/clients/mix-client/src/packet.rs | 35 +++++++++++++++++++ nym-client/src/client/cover_traffic_stream.rs | 8 ++--- nym-client/src/client/mod.rs | 2 +- nym-client/src/client/real_traffic_stream.rs | 12 +++---- nym-client/src/client/topology_control.rs | 19 +++++++++- nym-client/src/sockets/tcp.rs | 2 +- nym-client/src/sockets/ws.rs | 2 +- 7 files changed, 66 insertions(+), 14 deletions(-) diff --git a/common/clients/mix-client/src/packet.rs b/common/clients/mix-client/src/packet.rs index 5132d4cb92..8e0172bd3b 100644 --- a/common/clients/mix-client/src/packet.rs +++ b/common/clients/mix-client/src/packet.rs @@ -39,6 +39,7 @@ impl From for SphinxPacketEncapsulationError { } } +#[deprecated(note = "please use loop_cover_message_route instead")] pub fn loop_cover_message( our_address: DestinationAddressBytes, surb_id: SURBIdentifier, @@ -55,6 +56,23 @@ pub fn loop_cover_message( ) } +pub fn loop_cover_message_route( + our_address: DestinationAddressBytes, + surb_id: SURBIdentifier, + route: Vec, + average_delay: time::Duration, +) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> { + let destination = Destination::new(our_address, surb_id); + + encapsulate_message_route( + destination, + LOOP_COVER_MESSAGE_PAYLOAD.to_vec(), + route, + average_delay, + ) +} + +#[deprecated(note = "please use encapsulate_message_route instead")] pub fn encapsulate_message( recipient: Destination, message: Vec, @@ -81,3 +99,20 @@ pub fn encapsulate_message( Ok((first_node_address, packet)) } + +pub fn encapsulate_message_route( + recipient: Destination, + message: Vec, + route: Vec, + average_delay: time::Duration, +) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> { + let delays = sphinx::header::delays::generate_from_average_duration(route.len(), average_delay); + + // build the packet + let packet = sphinx::SphinxPacket::new(message, &route[..], &recipient, &delays)?; + + let first_node_address = + addressing::socket_address_from_encoded_bytes(route.first().unwrap().address.to_bytes())?; + + Ok((first_node_address, packet)) +} diff --git a/nym-client/src/client/cover_traffic_stream.rs b/nym-client/src/client/cover_traffic_stream.rs index 443f71e4c2..c0e4e8dc57 100644 --- a/nym-client/src/client/cover_traffic_stream.rs +++ b/nym-client/src/client/cover_traffic_stream.rs @@ -68,18 +68,18 @@ impl LoopCoverTrafficStream { async fn on_new_message(&mut self) { trace!("next cover message!"); - let topology = match self.topology_access.get_current_topology().await { + let route = match self.topology_access.random_route().await { None => { warn!("No valid topology detected - won't send any loop cover message this time"); return; } - Some(topology) => topology, + Some(route) => route, }; - let cover_message = match mix_client::packet::loop_cover_message( + let cover_message = match mix_client::packet::loop_cover_message_route( self.our_info.address.clone(), self.our_info.identifier, - &topology, + route, self.average_packet_delay, ) { Ok(message) => message, diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index fcef9d8b64..437d32e700 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -79,7 +79,7 @@ impl NymClient { provider_id: String, mut topology_accessor: TopologyAccessor, ) -> SocketAddr { - topology_accessor.get_current_topology().await.as_ref().expect("The current network topoloy is empty - are you using correct directory server?") + topology_accessor.get_current_topology_clone().await.as_ref().expect("The current network topoloy is empty - are you using correct directory server?") .providers() .iter() .find(|provider| provider.pub_key == provider_id) diff --git a/nym-client/src/client/real_traffic_stream.rs b/nym-client/src/client/real_traffic_stream.rs index 43af644874..2e83196610 100644 --- a/nym-client/src/client/real_traffic_stream.rs +++ b/nym-client/src/client/real_traffic_stream.rs @@ -84,27 +84,27 @@ impl OutQueueControl { async fn on_message(&mut self, next_message: StreamMessage) { trace!("created new message"); - let topology = match self.topology_access.get_current_topology().await { + let route = match self.topology_access.random_route().await { None => { warn!("No valid topology detected - won't send any real or loop message this time"); // TODO: this creates a potential problem: we can lose real messages if we were // unable to get topology, perhaps we should store them in some buffer? return; } - Some(topology) => topology, + Some(route) => route, }; let next_packet = match next_message { - StreamMessage::Cover => mix_client::packet::loop_cover_message( + StreamMessage::Cover => mix_client::packet::loop_cover_message_route( self.our_info.address.clone(), self.our_info.identifier, - &topology, + route, self.average_packet_delay, ), - StreamMessage::Real(real_message) => mix_client::packet::encapsulate_message( + StreamMessage::Real(real_message) => mix_client::packet::encapsulate_message_route( real_message.0, real_message.1, - &topology, + route, self.average_packet_delay, ), }; diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index 5bedcaddc8..7365abc87c 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -41,11 +41,28 @@ impl TopologyAccessor { self.inner.lock().await.update(new_topology); } - pub(crate) async fn get_current_topology(&mut self) -> Option { + pub(crate) async fn get_current_topology_clone(&mut self) -> Option { // TODO: considering topology is gotten quite frequently, the clone call might be rather // expensive in the grand scheme of things... self.inner.lock().await.0.clone() } + + // this is a rather temporary solution as each client will have an associated provider + // currently that is not implemented yet and there only exists one provider in the network + pub(crate) async fn random_route(&mut self) -> Option> { + match &self.inner.lock().await.0 { + None => None, + Some(ref topology) => { + let mut providers = topology.providers(); + if providers.is_empty() { + return None; + } + // unwrap is fine here as we asserted there is at least single provider + let provider = providers.pop().unwrap().into(); + topology.route_to(provider).ok() + } + } + } } #[derive(Debug)] diff --git a/nym-client/src/sockets/tcp.rs b/nym-client/src/sockets/tcp.rs index 54ae1281a9..abfe06b3c1 100644 --- a/nym-client/src/sockets/tcp.rs +++ b/nym-client/src/sockets/tcp.rs @@ -140,7 +140,7 @@ impl ClientRequest { async fn handle_get_clients( mut topology_accessor: TopologyAccessor, ) -> ServerResponse { - match topology_accessor.get_current_topology().await { + match topology_accessor.get_current_topology_clone().await { Some(topology) => { let clients = topology .providers() diff --git a/nym-client/src/sockets/ws.rs b/nym-client/src/sockets/ws.rs index 07a27acac7..01bd6a111a 100644 --- a/nym-client/src/sockets/ws.rs +++ b/nym-client/src/sockets/ws.rs @@ -274,7 +274,7 @@ impl ClientRequest { async fn handle_get_clients( mut topology_accessor: TopologyAccessor, ) -> ServerResponse { - match topology_accessor.get_current_topology().await { + match topology_accessor.get_current_topology_clone().await { Some(topology) => { let clients = topology .providers() From 8b90953ae07f85a879b88249db258b6904cc2a4f Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 5 Mar 2020 15:07:33 +0000 Subject: [PATCH 266/273] Allowed used of deprecated function inside a deprecated function --- common/clients/mix-client/src/packet.rs | 1 + nym-client/src/client/topology_control.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/clients/mix-client/src/packet.rs b/common/clients/mix-client/src/packet.rs index 8e0172bd3b..6b25afd6b1 100644 --- a/common/clients/mix-client/src/packet.rs +++ b/common/clients/mix-client/src/packet.rs @@ -48,6 +48,7 @@ pub fn loop_cover_message( ) -> Result<(SocketAddr, SphinxPacket), SphinxPacketEncapsulationError> { let destination = Destination::new(our_address, surb_id); + #[allow(deprecated)] encapsulate_message( destination, LOOP_COVER_MESSAGE_PAYLOAD.to_vec(), diff --git a/nym-client/src/client/topology_control.rs b/nym-client/src/client/topology_control.rs index 7365abc87c..1b11641bb9 100644 --- a/nym-client/src/client/topology_control.rs +++ b/nym-client/src/client/topology_control.rs @@ -41,9 +41,8 @@ impl TopologyAccessor { self.inner.lock().await.update(new_topology); } + // Unless you absolutely need the entire topology, use `random_route` instead pub(crate) async fn get_current_topology_clone(&mut self) -> Option { - // TODO: considering topology is gotten quite frequently, the clone call might be rather - // expensive in the grand scheme of things... self.inner.lock().await.0.clone() } From f73d6371d22c2ae7273f5cf4e6340e89072cc686 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 5 Mar 2020 15:18:07 +0000 Subject: [PATCH 267/273] Made clippy happier --- .../src/connection_manager/mod.rs | 2 +- common/crypto/src/identity/mod.rs | 4 +-- mixnode/src/node/listener.rs | 2 +- nym-client/src/client/mod.rs | 2 +- .../src/provider/client_handling/listener.rs | 2 +- .../client_handling/request_processing.rs | 25 +++++++++---------- .../src/provider/mix_handling/listener.rs | 5 +--- 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/common/clients/multi-tcp-client/src/connection_manager/mod.rs b/common/clients/multi-tcp-client/src/connection_manager/mod.rs index a695ca05c9..bbd0f8bcd7 100644 --- a/common/clients/multi-tcp-client/src/connection_manager/mod.rs +++ b/common/clients/multi-tcp-client/src/connection_manager/mod.rs @@ -77,7 +77,7 @@ impl<'a> ConnectionManager<'a> { return match conn_writer.write_all(msg).await { // if we failed to write to connection we should reconnect // TODO: is this true? can we fail to write to a connection while it still remains open and valid? - Ok(res) => Ok(res), + Ok(_) => Ok(()), Err(e) => { trace!("Creating connection reconnector!"); self.state = ConnectionState::Reconnecting(ConnectionReconnector::new( diff --git a/common/crypto/src/identity/mod.rs b/common/crypto/src/identity/mod.rs index 192b4911df..9b7a396671 100644 --- a/common/crypto/src/identity/mod.rs +++ b/common/crypto/src/identity/mod.rs @@ -121,8 +121,8 @@ impl MixIdentityPrivateKey { // TODO: this will be implemented differently by using the proper trait impl MixIdentityPrivateKey { - pub fn as_scalar(self) -> Scalar { - let encryption_key = self.0; + pub fn as_scalar(&self) -> Scalar { + let encryption_key = &self.0; encryption_key.0 } } diff --git a/mixnode/src/node/listener.rs b/mixnode/src/node/listener.rs index 7146318ad0..0409a1754d 100644 --- a/mixnode/src/node/listener.rs +++ b/mixnode/src/node/listener.rs @@ -55,7 +55,7 @@ async fn process_socket_connection( // we must be able to handle multiple packets from same connection independently tokio::spawn(process_received_packet( - buf.clone(), + buf, // note: processing_data is relatively cheap (and safe) to clone - // it contains arc to private key and metrics reporter (which is just // a single mpsc unbounded sender) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 437d32e700..dbb975c7bc 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -83,7 +83,7 @@ impl NymClient { .providers() .iter() .find(|provider| provider.pub_key == provider_id) - .expect(format!("Could not find provider with id {:?} - are you sure it is still online? Perhaps try to run `nym-client init` again to obtain a new provider", provider_id).as_ref()) + .unwrap_or_else( || panic!("Could not find provider with id {:?} - are you sure it is still online? Perhaps try to run `nym-client init` again to obtain a new provider", provider_id)) .client_listener } diff --git a/sfw-provider/src/provider/client_handling/listener.rs b/sfw-provider/src/provider/client_handling/listener.rs index f3cee1bf5e..260c947b07 100644 --- a/sfw-provider/src/provider/client_handling/listener.rs +++ b/sfw-provider/src/provider/client_handling/listener.rs @@ -37,7 +37,7 @@ async fn process_request( .map(|c| c.into_tuple()) .unzip(); let response_bytes = PullResponse::new(messages).to_bytes(); - if let Ok(_) = socket.write_all(&response_bytes).await { + if socket.write_all(&response_bytes).await.is_ok() { // only delete stored messages if we managed to actually send the response if let Err(e) = request_processor.delete_sent_messages(paths).await { error!("Somehow failed to delete stored messages! - {:?}", e); diff --git a/sfw-provider/src/provider/client_handling/request_processing.rs b/sfw-provider/src/provider/client_handling/request_processing.rs index 6e7a36bb08..57707c1b0d 100644 --- a/sfw-provider/src/provider/client_handling/request_processing.rs +++ b/sfw-provider/src/provider/client_handling/request_processing.rs @@ -85,27 +85,26 @@ impl RequestProcessor { ); let auth_token = self.generate_new_auth_token(req.destination_address.clone()); - if let Some(_) = self + if self .client_ledger .insert_token(auth_token.clone(), req.destination_address.clone()) .await + .is_some() { info!( "Client {:?} was already registered before!", req.destination_address.to_base58_string() ) - } else { - if let Err(e) = self - .client_storage - .create_storage_dir(req.destination_address.clone()) - .await - { - error!("We failed to create inbox directory for the client -{:?}\nReverting issued token...", e); - // we must revert our changes if this operation failed - self.client_ledger - .remove_token(&req.destination_address) - .await; - } + } else if let Err(e) = self + .client_storage + .create_storage_dir(req.destination_address.clone()) + .await + { + error!("We failed to create inbox directory for the client -{:?}\nReverting issued token...", e); + // we must revert our changes if this operation failed + self.client_ledger + .remove_token(&req.destination_address) + .await; } Ok(ClientProcessingResult::RegisterResponse(auth_token)) diff --git a/sfw-provider/src/provider/mix_handling/listener.rs b/sfw-provider/src/provider/mix_handling/listener.rs index 00a6b97062..70a37c7841 100644 --- a/sfw-provider/src/provider/mix_handling/listener.rs +++ b/sfw-provider/src/provider/mix_handling/listener.rs @@ -42,10 +42,7 @@ async fn process_socket_connection( } // we must be able to handle multiple packets from same connection independently - tokio::spawn(process_received_packet( - buf.clone(), - packet_processor.clone(), - )) + tokio::spawn(process_received_packet(buf, packet_processor.clone())) } Err(e) => { warn!( From e19419447862ef9d9580024c536605f3bb0a21af Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 5 Mar 2020 15:24:19 +0000 Subject: [PATCH 268/273] experimental native rust api --- nym-client/src/client/mod.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index dbb975c7bc..7e7f7c42b9 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -26,11 +26,11 @@ mod cover_traffic_stream; mod mix_traffic; mod provider_poller; mod real_traffic_stream; -pub mod received_buffer; -pub mod topology_control; +pub(crate) mod received_buffer; +pub(crate) mod topology_control; -pub type InputMessageSender = mpsc::UnboundedSender; -pub type InputMessageReceiver = mpsc::UnboundedReceiver; +pub(crate) type InputMessageSender = mpsc::UnboundedSender; +pub(crate) type InputMessageReceiver = mpsc::UnboundedReceiver; pub struct NymClient { runtime: Runtime, @@ -38,11 +38,12 @@ pub struct NymClient { identity_keypair: MixIdentityKeyPair, // to be used by "send" function or socket, etc - pub input_tx: Option, + input_tx: Option, } #[derive(Debug)] -pub struct InputMessage(pub Destination, pub Vec); +// TODO: make fields private +pub(crate) struct InputMessage(pub Destination, pub Vec); impl NymClient { fn load_identity_keys(config_file: &Config) -> MixIdentityKeyPair { @@ -253,7 +254,14 @@ impl NymClient { } /// EXPERIMENTAL DIRECT RUST API - pub fn send_message(&self) {} + /// It's entirely untested and there are absolutely no guarantees about it + pub fn send_message(&self, destination: Destination, message: Vec) { + self.input_tx + .as_ref() + .unwrap() + .unbounded_send(InputMessage(destination, message)) + .unwrap() + } /// blocking version of `start` method. Will run forever (or until SIGINT is sent) pub fn run_forever(&mut self) { From d3594b620c353bffb437539204cf75fc66651718 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Thu, 5 Mar 2020 15:29:25 +0000 Subject: [PATCH 269/273] Replaced cryptic unwrap with an expect --- nym-client/src/client/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nym-client/src/client/mod.rs b/nym-client/src/client/mod.rs index 7e7f7c42b9..f4d5e5e347 100644 --- a/nym-client/src/client/mod.rs +++ b/nym-client/src/client/mod.rs @@ -258,7 +258,7 @@ impl NymClient { pub fn send_message(&self, destination: Destination, message: Vec) { self.input_tx .as_ref() - .unwrap() + .expect("start method was not called before!") .unbounded_send(InputMessage(destination, message)) .unwrap() } From 6c730c217fc9b4f4aec2e79dddf122fdc2276523 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 6 Mar 2020 11:39:47 +0000 Subject: [PATCH 270/273] Trying to run travis test in single threaded mode --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7041ae8d65..bf149680d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,5 +11,5 @@ before_script: - rustup component add rustfmt script: - cargo build - - cargo test + - cargo test -- --test-threads=1 - cargo fmt -- --check \ No newline at end of file From 2b1781b06d3887b612e8d8c58d1239e60a7927ef Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 6 Mar 2020 11:47:52 +0000 Subject: [PATCH 271/273] Updated binary versions --- Cargo.lock | 8 ++++---- mixnode/Cargo.toml | 2 +- nym-client/Cargo.toml | 2 +- sfw-provider/Cargo.toml | 2 +- validator/Cargo.toml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a6b9e79c6..994a105603 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1448,7 +1448,7 @@ dependencies = [ [[package]] name = "nym-client" -version = "0.4.1" +version = "0.5.0-rc.1" dependencies = [ "addressing", "bs58", @@ -1482,7 +1482,7 @@ dependencies = [ [[package]] name = "nym-mixnode" -version = "0.4.1" +version = "0.5.0-rc.1" dependencies = [ "addressing", "bs58", @@ -1507,7 +1507,7 @@ dependencies = [ [[package]] name = "nym-sfw-provider" -version = "0.4.1" +version = "0.5.0-rc.1" dependencies = [ "bs58", "built", @@ -1535,7 +1535,7 @@ dependencies = [ [[package]] name = "nym-validator" -version = "0.4.1" +version = "0.5.0-rc.1" dependencies = [ "abci", "built", diff --git a/mixnode/Cargo.toml b/mixnode/Cargo.toml index e7ec30adba..ff1ec5b08e 100644 --- a/mixnode/Cargo.toml +++ b/mixnode/Cargo.toml @@ -1,7 +1,7 @@ [package] build = "build.rs" name = "nym-mixnode" -version = "0.4.1" +version = "0.5.0-rc.1" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] edition = "2018" diff --git a/nym-client/Cargo.toml b/nym-client/Cargo.toml index ac4bde4fc8..0b5dd51e4a 100644 --- a/nym-client/Cargo.toml +++ b/nym-client/Cargo.toml @@ -1,7 +1,7 @@ [package] build = "build.rs" name = "nym-client" -version = "0.4.1" +version = "0.5.0-rc.1" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] edition = "2018" diff --git a/sfw-provider/Cargo.toml b/sfw-provider/Cargo.toml index f32a530a81..debf9bbde4 100644 --- a/sfw-provider/Cargo.toml +++ b/sfw-provider/Cargo.toml @@ -1,7 +1,7 @@ [package] build = "build.rs" name = "nym-sfw-provider" -version = "0.4.1" +version = "0.5.0-rc.1" authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] edition = "2018" diff --git a/validator/Cargo.toml b/validator/Cargo.toml index ebe0c12ee0..5398c6a719 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -1,7 +1,7 @@ [package] build = "build.rs" name = "nym-validator" -version = "0.4.1" +version = "0.5.0-rc.1" authors = ["Jedrzej Stuczynski "] edition = "2018" From 2053eff0364228729289ec6b4f4abb7b78d47c8b Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 6 Mar 2020 12:04:40 +0000 Subject: [PATCH 272/273] Updated changelog --- CHANGELOG.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 983dcc4625..8a65d4c2ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,39 @@ # Changelog +## [Unreleased](https://github.com/nymtech/nym/tree/HEAD) + +[Full Changelog](https://github.com/nymtech/nym/compare/v0.4.1...HEAD) + +**Closed issues:** + +- COMPILE: Could not compile project using Cargo [\#118](https://github.com/nymtech/nym/issues/118) +- Wherever unbounded mpsc channel is used, prefer unbounded\_send\(\) over send\(\).await [\#90](https://github.com/nymtech/nym/issues/90) +- Add a `Send` method in nym-client [\#81](https://github.com/nymtech/nym/issues/81) +- Start on Tendermint integration [\#79](https://github.com/nymtech/nym/issues/79) +- Ditch DummyKeyPair [\#75](https://github.com/nymtech/nym/issues/75) +- Replace args with proper config files [\#69](https://github.com/nymtech/nym/issues/69) +- Fix incorrectly used Arcs [\#47](https://github.com/nymtech/nym/issues/47) +- nym-mixnode mandatory host option [\#26](https://github.com/nymtech/nym/issues/26) +- Create config struct for mixnode \(possibly also for client\) [\#21](https://github.com/nymtech/nym/issues/21) +- Reuse TCP socket connection between client and mixnodes [\#20](https://github.com/nymtech/nym/issues/20) +- Once implementation is available, wherever appropriate, replace `futures::lock::Mutex` with `futures::lock::RwLock` [\#9](https://github.com/nymtech/nym/issues/9) +- Check if RwLock on MixProcessingData is still needed [\#8](https://github.com/nymtech/nym/issues/8) +- Reuse TCP socket connection between mixnodes and providers [\#3](https://github.com/nymtech/nym/issues/3) +- Persistent socket connection with other mixes [\#2](https://github.com/nymtech/nym/issues/2) + +**Merged pull requests:** + +- Feature/client refactoring [\#128](https://github.com/nymtech/nym/pull/128) ([jstuczyn](https://github.com/jstuczyn)) +- Feature/provider refactoring [\#125](https://github.com/nymtech/nym/pull/125) ([jstuczyn](https://github.com/jstuczyn)) +- all: fixing mis-spelling [\#123](https://github.com/nymtech/nym/pull/123) ([futurechimp](https://github.com/futurechimp)) +- Feature/further clippy fixes [\#121](https://github.com/nymtech/nym/pull/121) ([jstuczyn](https://github.com/jstuczyn)) +- Feature/tokio tungstenite dependency fix [\#120](https://github.com/nymtech/nym/pull/120) ([jstuczyn](https://github.com/jstuczyn)) +- Feature/config files cleanup [\#119](https://github.com/nymtech/nym/pull/119) ([jstuczyn](https://github.com/jstuczyn)) +- Feature/config files [\#117](https://github.com/nymtech/nym/pull/117) ([jstuczyn](https://github.com/jstuczyn)) +- Feature/un genericize keys [\#111](https://github.com/nymtech/nym/pull/111) ([futurechimp](https://github.com/futurechimp)) +- Feature/abci [\#110](https://github.com/nymtech/nym/pull/110) ([futurechimp](https://github.com/futurechimp)) +- Simplified the use of generics on identity keypair by using output types [\#109](https://github.com/nymtech/nym/pull/109) ([jstuczyn](https://github.com/jstuczyn)) + ## [v0.4.1](https://github.com/nymtech/nym/tree/v0.4.1) (2020-01-29) [Full Changelog](https://github.com/nymtech/nym/compare/v0.4.0...v0.4.1) @@ -94,6 +128,10 @@ This release also integrates a health-checker and network topology refresh into [Full Changelog](https://github.com/nymtech/nym/compare/v0.3.1...v0.3.2) +**Merged pull requests:** + +- Feature/separate presence address [\#59](https://github.com/nymtech/nym/pull/59) ([jstuczyn](https://github.com/jstuczyn)) + ## [v0.3.1](https://github.com/nymtech/nym/tree/v0.3.1) (2020-01-16) [Full Changelog](https://github.com/nymtech/nym/compare/v0.3.0...v0.3.1) @@ -120,7 +158,6 @@ This release also integrates a health-checker and network topology refresh into **Merged pull requests:** -- Feature/separate presence address [\#59](https://github.com/nymtech/nym/pull/59) ([jstuczyn](https://github.com/jstuczyn)) - Feature/client topology filtering [\#54](https://github.com/nymtech/nym/pull/54) ([jstuczyn](https://github.com/jstuczyn)) - print public key for nym client tools [\#53](https://github.com/nymtech/nym/pull/53) ([mileschet](https://github.com/mileschet)) - Showing binding warning on binding to localhost, 0.0.0.0 or 127.0.0.1 [\#52](https://github.com/nymtech/nym/pull/52) ([jstuczyn](https://github.com/jstuczyn)) From 4abb4d0b454ef26c94022f2a4e17c0b998b37147 Mon Sep 17 00:00:00 2001 From: Jedrzej Stuczynski Date: Fri, 6 Mar 2020 12:51:24 +0000 Subject: [PATCH 273/273] Updated run_local_network script to work with 0.5.0 --- scripts/run_local_network.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scripts/run_local_network.sh b/scripts/run_local_network.sh index fe2f949408..a890f2bc36 100755 --- a/scripts/run_local_network.sh +++ b/scripts/run_local_network.sh @@ -21,24 +21,32 @@ killall nym-mixnode killall nym-sfw-provider echo "Press CTRL-C to stop." +echo "Make sure you have started nym-directory !" -cargo build +cargo build --release --manifest-path nym-client/Cargo.toml --features=local +cargo build --release --manifest-path mixnode/Cargo.toml --features=local +cargo build --release --manifest-path sfw-provider/Cargo.toml --features=local MAX_LAYERS=3 NUMMIXES=${1:-3} # Set $NUMMIXES to default of 3, but allow the user to set other values if desired + +$PWD/target/release/nym-sfw-provider init --id provider-local --clients-host 127.0.0.1 --mix-host 127.0.0.1 --mix-port 4000 --mix-announce-port 4000 +$PWD/target/release/nym-sfw-provider run --id provider-local & + +sleep 1 + for (( j=0; j<$NUMMIXES; j++ )) # Note: to disable logging (or direct it to another output) modify the constant on top of mixnode or provider; # Will make it later either configurable by flags or config file. do let layer=j%MAX_LAYERS+1 - $PWD/target/debug/nym-mixnode run --port $((9980+$j)) --host "localhost" --layer $layer --directory http://localhost:8080 & + $PWD/target/release/nym-mixnode init --id mix-local$j --host 127.0.0.1 --port $((9980+$j)) --layer $layer --announce-host 127.0.0.1:$((9980+$j)) + $PWD/target/release/nym-mixnode run --id mix-local$j & sleep 1 done -sleep 1 -$PWD/target/debug/nym-sfw-provider run --clientHost "localhost" --mixHost "localhost" --mixPort 9997 --clientPort 9998 --directory http://localhost:8080 # trap call ctrl_c() trap ctrl_c SIGINT SIGTERM SIGTSTP