diff --git a/.gitignore b/.gitignore index fdc4d09925..8efcd46083 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ v6-topology.json /explorer/public/downloads/mixmining.json /explorer/public/downloads/topology.json /clients/validator/examples/nym-driver-example/current-contract.txt +validator-api/v4.json +validator-api/v6.json \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c333bb8925..78ae03f8e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61" + [[package]] name = "arrayref" version = "0.3.6" @@ -670,13 +676,33 @@ dependencies = [ "syn", ] +[[package]] +name = "devise" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411cf45ac38f00df3679689616649dc12607b846db171780bb790b514a042832" +dependencies = [ + "devise_codegen 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "devise_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "devise" version = "0.3.0" source = "git+https://github.com/SergioBenitez/Devise.git?rev=df00b5#df00b5162edd53e8d496e7935774e69b5f7f6bdf" dependencies = [ - "devise_codegen", - "devise_core", + "devise_codegen 0.3.0 (git+https://github.com/SergioBenitez/Devise.git?rev=df00b5)", + "devise_core 0.3.0 (git+https://github.com/SergioBenitez/Devise.git?rev=df00b5)", +] + +[[package]] +name = "devise_codegen" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cf7081f06822f1787e29359354426132cf832cc977d7a8ff747848631462ad1" +dependencies = [ + "devise_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "quote", ] [[package]] @@ -684,10 +710,23 @@ name = "devise_codegen" version = "0.3.0" source = "git+https://github.com/SergioBenitez/Devise.git?rev=df00b5#df00b5162edd53e8d496e7935774e69b5f7f6bdf" dependencies = [ - "devise_core", + "devise_core 0.3.0 (git+https://github.com/SergioBenitez/Devise.git?rev=df00b5)", "quote", ] +[[package]] +name = "devise_core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80c23631758736875d7ce08f847f296b4001b72cf90878e85b47df7ac5442147" +dependencies = [ + "bitflags", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + [[package]] name = "devise_core" version = "0.3.0" @@ -1086,6 +1125,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "generator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1d9279ca822891c1a4dae06d185612cf8fc6acfe5dff37781b41297811b12ee" +dependencies = [ + "cc", + "libc", + "log", + "rustversion", + "winapi", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -1497,7 +1549,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0e8460f2f2121162705187214720353c517b97bdfb3494c0b1e33d83ebe4bed" dependencies = [ "cfg-if 0.1.10", - "generator", + "generator 0.6.25", + "scoped-tls", + "serde", + "serde_json", +] + +[[package]] +name = "loom" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa5348dc45fa5f2419b6dd4ea20345e6b01b1fcc9d176a322eada1ac3f382ba" +dependencies = [ + "cfg-if 1.0.0", + "generator 0.7.0", "scoped-tls", "serde", "serde_json", @@ -1628,6 +1693,27 @@ dependencies = [ "version_check", ] +[[package]] +name = "multer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdd568fea4758b30d6423f013f7171e193c34aa97828d1bd9f924fb3af30a8c" +dependencies = [ + "bytes", + "derive_more", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "mime", + "spin", + "tokio", + "tokio-util", + "twoway", + "version_check", +] + [[package]] name = "native-tls" version = "0.2.7" @@ -1786,7 +1872,7 @@ dependencies = [ "pemstore", "pretty_env_logger", "rand 0.7.3", - "rocket", + "rocket 0.5.0-dev", "rocket_contrib", "serde", "serial_test", @@ -1853,6 +1939,7 @@ dependencies = [ name = "nym-validator-api" version = "0.10.1" dependencies = [ + "anyhow", "clap", "crypto", "dotenv", @@ -1866,6 +1953,8 @@ dependencies = [ "pretty_env_logger", "rand 0.7.3", "reqwest", + "rocket 0.5.0-rc.1", + "rocket_cors", "serde", "serde_json", "tokio", @@ -2530,16 +2619,16 @@ dependencies = [ "indexmap", "log", "memchr", - "multer", + "multer 1.2.2", "num_cpus", "parking_lot", "pin-project-lite", "rand 0.8.4", "ref-cast", - "rocket_codegen", - "rocket_http", + "rocket_codegen 0.5.0-dev", + "rocket_http 0.5.0-dev", "serde", - "state", + "state 0.4.2", "tempfile", "time", "tokio", @@ -2549,16 +2638,71 @@ dependencies = [ "yansi", ] +[[package]] +name = "rocket" +version = "0.5.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a71c18c42a0eb15bf3816831caf0dad11e7966f2a41aaf486a701979c4dd1f2" +dependencies = [ + "async-stream 0.3.2", + "async-trait", + "atomic", + "atty", + "binascii", + "bytes", + "either", + "figment", + "futures", + "indexmap", + "log", + "memchr", + "multer 2.0.0", + "num_cpus", + "parking_lot", + "pin-project-lite", + "rand 0.8.4", + "ref-cast", + "rocket_codegen 0.5.0-rc.1", + "rocket_http 0.5.0-rc.1", + "serde", + "serde_json", + "state 0.5.2", + "tempfile", + "time", + "tokio", + "tokio-stream", + "tokio-util", + "ubyte", + "version_check", + "yansi", +] + [[package]] name = "rocket_codegen" version = "0.5.0-dev" source = "git+https://github.com/SergioBenitez/Rocket?rev=f442ad93cb2788bfd08aca5d94e509db20888101#f442ad93cb2788bfd08aca5d94e509db20888101" dependencies = [ - "devise", + "devise 0.3.0 (git+https://github.com/SergioBenitez/Devise.git?rev=df00b5)", "glob", "indexmap", "quote", - "rocket_http", + "rocket_http 0.5.0-dev", + "unicode-xid", +] + +[[package]] +name = "rocket_codegen" +version = "0.5.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66f5fa462f7eb958bba8710c17c5d774bbbd59809fa76fb1957af7e545aea8bb" +dependencies = [ + "devise 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glob", + "indexmap", + "proc-macro2", + "quote", + "rocket_http 0.5.0-rc.1", + "syn", "unicode-xid", ] @@ -2568,12 +2712,27 @@ version = "0.5.0-dev" source = "git+https://github.com/SergioBenitez/Rocket?rev=f442ad93cb2788bfd08aca5d94e509db20888101#f442ad93cb2788bfd08aca5d94e509db20888101" dependencies = [ "log", - "rocket", + "rocket 0.5.0-dev", "serde", "serde_json", "tokio", ] +[[package]] +name = "rocket_cors" +version = "0.5.2" +source = "git+https://github.com/lawliet89/rocket_cors?branch=master#dfd3662c49e2f6fc37df35091cb94d82f7fb5915" +dependencies = [ + "log", + "regex", + "rocket 0.5.0-rc.1", + "serde", + "serde_derive", + "unicase", + "unicase_serde", + "url", +] + [[package]] name = "rocket_http" version = "0.5.0-dev" @@ -2595,7 +2754,35 @@ dependencies = [ "serde", "smallvec", "stable-pattern", - "state", + "state 0.4.2", + "time", + "tokio", + "uncased", +] + +[[package]] +name = "rocket_http" +version = "0.5.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c8b7d512d2fcac2316ebe590cde67573844b99e6cc9ee0f53375fa16e25ebd" +dependencies = [ + "cookie", + "either", + "http", + "hyper", + "indexmap", + "log", + "memchr", + "mime", + "parking_lot", + "pear", + "percent-encoding", + "pin-project-lite", + "ref-cast", + "serde", + "smallvec", + "stable-pattern", + "state 0.5.2", "time", "tokio", "uncased", @@ -2972,6 +3159,12 @@ dependencies = [ "sha2 0.8.2", ] +[[package]] +name = "spin" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fdd7196b4ae35a111c6dc97f9cc152ca3ea8ad744f7cb46a9f27b3ef8f2f54" + [[package]] name = "stable-pattern" version = "0.1.0" @@ -2995,7 +3188,16 @@ name = "state" version = "0.4.2" source = "git+https://github.com/SergioBenitez/state.git?rev=8f94dc#8f94dce673b7d4b0e7b96c808a84f5e2a4be4a60" dependencies = [ - "loom", + "loom 0.3.6", +] + +[[package]] +name = "state" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cf4f5369e6d3044b5e365c9690f451516ac8f0954084622b49ea3fde2f6de5" +dependencies = [ + "loom 0.5.0", ] [[package]] @@ -3434,6 +3636,25 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eeba86d422ce181a719445e51872fa30f1f7413b62becb52e95ec91aa262d85c" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicase_serde" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef53697679d874d69f3160af80bc28de12730a985d57bdf2b47456ccb8b11f1" +dependencies = [ + "serde", + "unicase", +] + [[package]] name = "unicode-bidi" version = "0.3.5" diff --git a/common/client-libs/validator-client/src/lib.rs b/common/client-libs/validator-client/src/lib.rs index 4325edb4b4..ed0cd60851 100644 --- a/common/client-libs/validator-client/src/lib.rs +++ b/common/client-libs/validator-client/src/lib.rs @@ -104,6 +104,11 @@ impl Client { ) } + // async fn latest_block(&self) -> Block { + // let path = format!("{}/block", self.available_validators_rest_urls[0]); + // let response = self.reqwest_client.get(path).send().await?.json().await?; + // } + async fn query_validators(&mut self, query: String) -> Result where for<'a> T: Deserialize<'a>, diff --git a/validator-api/Cargo.toml b/validator-api/Cargo.toml index ef7b52c305..95e11106d8 100644 --- a/validator-api/Cargo.toml +++ b/validator-api/Cargo.toml @@ -4,7 +4,10 @@ [package] name = "nym-validator-api" version = "0.10.1" -authors = ["Dave Hrycyszyn ", "Jędrzej Stuczyński "] +authors = [ + "Dave Hrycyszyn ", + "Jędrzej Stuczyński ", +] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -17,19 +20,23 @@ log = "0.4" pin-project = "1.0" pretty_env_logger = "0.4" rand = "0.7" -reqwest = { version = "0.11", features = ["json"] } +reqwest = { version="0.11", features=["json"] } +rocket = { version="0.5.0-rc.1", features=["json"] } serde = "1.0" serde_json = "1.0" -tokio = { version = "1.4", features = ["rt-multi-thread", "macros", "signal", "time"] } +tokio = { version="1.4", features=["rt-multi-thread", "macros", "signal", "time"] } +rocket_cors = { git="https://github.com/lawliet89/rocket_cors", branch="master" } +anyhow = "1" + ## internal -crypto = { path = "../common/crypto" } -gateway-client = { path = "../common/client-libs/gateway-client" } -gateway-requests = { path = "../gateway/gateway-requests" } -mixnet-contract = { path = "../common/mixnet-contract" } -nymsphinx = { path = "../common/nymsphinx" } -topology = { path = "../common/topology" } -validator-client = { path = "../common/client-libs/validator-client" } -version-checker = { path = "../common/version-checker" } +crypto = { path="../common/crypto" } +gateway-client = { path="../common/client-libs/gateway-client" } +gateway-requests = { path="../gateway/gateway-requests" } +mixnet-contract = { path="../common/mixnet-contract" } +nymsphinx = { path="../common/nymsphinx" } +topology = { path="../common/topology" } +validator-client = { path="../common/client-libs/validator-client" } +version-checker = { path="../common/version-checker" } [dev-dependencies] diff --git a/validator-api/Rocket.toml b/validator-api/Rocket.toml new file mode 100644 index 0000000000..a094d2157a --- /dev/null +++ b/validator-api/Rocket.toml @@ -0,0 +1,7 @@ +[default] +limits = { forms = "64 kB", json = "1 MiB" } +port = 8080 +address = "127.0.0.1" + +[release] +address = "0.0.0.0" \ No newline at end of file diff --git a/validator-api/src/cache.rs b/validator-api/src/cache.rs new file mode 100644 index 0000000000..05587f2f5c --- /dev/null +++ b/validator-api/src/cache.rs @@ -0,0 +1,41 @@ +use anyhow::Result; +use mixnet_contract::MixNodeBond; +use std::time::Instant; +use validator_client::Client; + +pub struct MixNodeCache { + value: Vec, + #[allow(dead_code)] + as_at: Instant, + validator_client: Client, +} +impl MixNodeCache { + pub fn init( + value: Vec, + validators_rest_uris: Vec, + mixnet_contract: String, + ) -> Self { + let config = validator_client::Config::new(validators_rest_uris, mixnet_contract); + let validator_client = validator_client::Client::new(config); + MixNodeCache { + value, + as_at: Instant::now(), + validator_client, + } + } + + fn set_value(&mut self, value: Vec) { + self.value = value; + self.as_at = Instant::now() + } + + pub fn value(&self) -> Vec { + self.value.clone() + } + + pub async fn cache(&mut self) -> Result<()> { + let mixnodes = self.validator_client.get_mix_nodes().await?; + self.set_value(mixnodes); + Ok(()) + } +} diff --git a/validator-api/src/gateways_reader.rs b/validator-api/src/gateways_reader.rs index e4288caec4..1938431cae 100644 --- a/validator-api/src/gateways_reader.rs +++ b/validator-api/src/gateways_reader.rs @@ -5,7 +5,6 @@ use crypto::asymmetric::identity; use futures::stream::Stream; use futures::task::Context; use gateway_client::{AcknowledgementReceiver, MixnetMessageReceiver}; -use log::*; use std::pin::Pin; use std::task::{Poll, Waker}; diff --git a/validator-api/src/main.rs b/validator-api/src/main.rs index 1813ebecfa..84acd3526b 100644 --- a/validator-api/src/main.rs +++ b/validator-api/src/main.rs @@ -1,6 +1,14 @@ // Copyright 2020 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 +#[macro_use] +extern crate rocket; + +use rocket::http::Method; +use rocket::serde::json::Json; +use rocket::State; +use rocket_cors::{AllowedHeaders, AllowedOrigins}; + use crate::monitor::preparer::PacketPreparer; use crate::monitor::processor::{ ReceivedProcessor, ReceivedProcessorReceiver, ReceivedProcessorSender, @@ -12,15 +20,21 @@ use crate::monitor::sender::PacketSender; use crate::monitor::summary_producer::SummaryProducer; use crate::tested_network::good_topology::parse_topology_file; use crate::tested_network::TestedNetwork; +use anyhow::Result; +use cache::MixNodeCache; use clap::{App, Arg, ArgMatches}; use crypto::asymmetric::{encryption, identity}; use futures::channel::mpsc; -use log::*; +use log::info; +use mixnet_contract::MixNodeBond; use nymsphinx::addressing::clients::Recipient; use std::sync::Arc; use std::time::Duration; +use tokio::sync::RwLock; +use tokio::time; use topology::NymTopology; +mod cache; mod chunker; pub(crate) mod gateways_reader; mod monitor; @@ -35,6 +49,7 @@ const NODE_STATUS_API_ARG: &str = "node-status-api"; const DETAILED_REPORT_ARG: &str = "detailed-report"; const GATEWAY_SENDING_RATE_ARG: &str = "gateway-rate"; const MIXNET_CONTRACT_ARG: &str = "mixnet-contract"; +const CACHE_INTERVAL_ARG: &str = "cache-interval"; const DEFAULT_VALIDATORS: &[&str] = &[ // "http://testnet-finney-validator.nymtech.net:1317", @@ -45,6 +60,7 @@ const DEFAULT_VALIDATORS: &[&str] = &[ const DEFAULT_NODE_STATUS_API: &str = "http://localhost:8081"; const DEFAULT_GATEWAY_SENDING_RATE: usize = 500; const DEFAULT_MIXNET_CONTRACT: &str = "hal1k0jntykt7e4g3y88ltc60czgjuqdy4c9c6gv94"; +const DEFAULT_CACHE_INTERVAL_ARG: u64 = 60; pub(crate) const TIME_CHUNK_SIZE: Duration = Duration::from_millis(50); pub(crate) const PENALISE_OUTDATED: bool = false; @@ -99,120 +115,13 @@ fn parse_args<'a>() -> ArgMatches<'a> { .long(GATEWAY_SENDING_RATE_ARG) .short("r") ) + .arg(Arg::with_name(CACHE_INTERVAL_ARG) + .help("Specified rate at which cache will be refreshed, global for all cache") + .takes_value(true) + .long(CACHE_INTERVAL_ARG)) .get_matches() } -#[tokio::main] -async fn main() { - println!("Network monitor starting..."); - let matches = parse_args(); - let v4_topology_path = matches.value_of(V4_TOPOLOGY_ARG).unwrap(); - let v6_topology_path = matches.value_of(V6_TOPOLOGY_ARG).unwrap(); - - let v4_topology = parse_topology_file(v4_topology_path); - let v6_topology = parse_topology_file(v6_topology_path); - - let validators_rest_uris_borrowed = matches - .values_of(VALIDATORS_ARG) - .map(|args| args.collect::>()) - .unwrap_or_else(|| DEFAULT_VALIDATORS.to_vec()); - - let validators_rest_uris = validators_rest_uris_borrowed - .into_iter() - .map(|uri| uri.to_string()) - .collect::>(); - - let node_status_api_uri = matches - .value_of(NODE_STATUS_API_ARG) - .unwrap_or(DEFAULT_NODE_STATUS_API); - - let mixnet_contract = matches - .value_of(MIXNET_CONTRACT_ARG) - .unwrap_or(DEFAULT_MIXNET_CONTRACT); - - let detailed_report = matches.is_present(DETAILED_REPORT_ARG); - let sending_rate = matches - .value_of(GATEWAY_SENDING_RATE_ARG) - .map(|v| v.parse().unwrap()) - .unwrap_or_else(|| DEFAULT_GATEWAY_SENDING_RATE); - - check_if_up_to_date(&v4_topology, &v6_topology); - setup_logging(); - - println!("* validator servers: {:?}", validators_rest_uris); - println!("* node status api server: {}", node_status_api_uri); - println!("* mixnet contract: {}", mixnet_contract); - println!("* detailed report printing: {}", detailed_report); - println!("* gateway sending rate: {} packets/s", sending_rate); - - // TODO: in the future I guess this should somehow change to distribute the load - let tested_mix_gateway = v4_topology.gateways()[0].clone(); - println!( - "* gateway for testing mixnodes: {}", - tested_mix_gateway.identity_key.to_base58_string() - ); - - // TODO: those keys change constant throughout the whole execution of the monitor. - // and on top of that, they are used with ALL the gateways -> presumably this should change - // in the future - let mut rng = rand::rngs::OsRng; - - let identity_keypair = Arc::new(identity::KeyPair::new(&mut rng)); - let encryption_keypair = Arc::new(encryption::KeyPair::new(&mut rng)); - - let test_mixnode_sender = Recipient::new( - *identity_keypair.public_key(), - *encryption_keypair.public_key(), - tested_mix_gateway.identity_key, - ); - - let tested_network = TestedNetwork::new_good(v4_topology, v6_topology); - let validator_client = new_validator_client(validators_rest_uris, mixnet_contract); - let node_status_api_client = new_node_status_api_client(node_status_api_uri); - - let (gateway_status_update_sender, gateway_status_update_receiver) = mpsc::unbounded(); - let (received_processor_sender_channel, received_processor_receiver_channel) = - mpsc::unbounded(); - - let packet_preparer = new_packet_preparer( - validator_client, - tested_network.clone(), - test_mixnode_sender, - *identity_keypair.public_key(), - *encryption_keypair.public_key(), - ); - - let packet_sender = new_packet_sender( - gateway_status_update_sender, - Arc::clone(&identity_keypair), - sending_rate, - ); - let received_processor = new_received_processor( - received_processor_receiver_channel, - Arc::clone(&encryption_keypair), - ); - let summary_producer = new_summary_producer(detailed_report); - let mut packet_receiver = new_packet_receiver( - gateway_status_update_receiver, - received_processor_sender_channel, - ); - - let mut monitor = monitor::Monitor::new( - packet_preparer, - packet_sender, - received_processor, - summary_producer, - node_status_api_client, - tested_network, - ); - - tokio::spawn(async move { packet_receiver.run().await }); - - tokio::spawn(async move { monitor.run().await }); - - wait_for_interrupt().await -} - async fn wait_for_interrupt() { if let Err(e) = tokio::signal::ctrl_c().await { error!( @@ -354,3 +263,174 @@ fn check_if_up_to_date(v4_topology: &NymTopology, v6_topology: &NymTopology) { } } } + +#[get("/mixnodes")] +async fn get_mixnodes(mixnode_cache: &State>>) -> Json> { + let mixnodes = mixnode_cache.read().await; + Json(mixnodes.value()) +} + +#[tokio::main] +async fn main() -> Result<()> { + info!("Network monitor starting..."); + let matches = parse_args(); + let v4_topology_path = matches.value_of(V4_TOPOLOGY_ARG).unwrap(); + let v6_topology_path = matches.value_of(V6_TOPOLOGY_ARG).unwrap(); + + let v4_topology = parse_topology_file(v4_topology_path); + let v6_topology = parse_topology_file(v6_topology_path); + + let validators_rest_uris_borrowed = matches + .values_of(VALIDATORS_ARG) + .map(|args| args.collect::>()) + .unwrap_or_else(|| DEFAULT_VALIDATORS.to_vec()); + + let validators_rest_uris = validators_rest_uris_borrowed + .into_iter() + .map(|uri| uri.to_string()) + .collect::>(); + + let node_status_api_uri = matches + .value_of(NODE_STATUS_API_ARG) + .unwrap_or(DEFAULT_NODE_STATUS_API); + + let mixnet_contract = matches + .value_of(MIXNET_CONTRACT_ARG) + .unwrap_or(DEFAULT_MIXNET_CONTRACT) + .to_string(); + + let detailed_report = matches.is_present(DETAILED_REPORT_ARG); + let sending_rate = matches + .value_of(GATEWAY_SENDING_RATE_ARG) + .map(|v| v.parse().unwrap()) + .unwrap_or_else(|| DEFAULT_GATEWAY_SENDING_RATE); + + let cache_interval_arg = matches + .value_of(CACHE_INTERVAL_ARG) + .map(|v| v.parse().unwrap()) + .unwrap_or_else(|| DEFAULT_CACHE_INTERVAL_ARG); + + check_if_up_to_date(&v4_topology, &v6_topology); + setup_logging(); + + info!("* validator servers: {:?}", validators_rest_uris); + info!("* node status api server: {}", node_status_api_uri); + info!("* mixnet contract: {}", mixnet_contract); + info!("* detailed report printing: {}", detailed_report); + info!("* gateway sending rate: {} packets/s", sending_rate); + + // TODO: in the future I guess this should somehow change to distribute the load + let tested_mix_gateway = v4_topology.gateways()[0].clone(); + info!( + "* gateway for testing mixnodes: {}", + tested_mix_gateway.identity_key.to_base58_string() + ); + + // TODO: those keys change constant throughout the whole execution of the monitor. + // and on top of that, they are used with ALL the gateways -> presumably this should change + // in the future + let mut rng = rand::rngs::OsRng; + + let identity_keypair = Arc::new(identity::KeyPair::new(&mut rng)); + let encryption_keypair = Arc::new(encryption::KeyPair::new(&mut rng)); + + let test_mixnode_sender = Recipient::new( + *identity_keypair.public_key(), + *encryption_keypair.public_key(), + tested_mix_gateway.identity_key, + ); + + let tested_network = TestedNetwork::new_good(v4_topology, v6_topology); + let validator_client = new_validator_client(validators_rest_uris.clone(), &mixnet_contract); + let node_status_api_client = new_node_status_api_client(node_status_api_uri); + + let (gateway_status_update_sender, gateway_status_update_receiver) = mpsc::unbounded(); + let (received_processor_sender_channel, received_processor_receiver_channel) = + mpsc::unbounded(); + + let packet_preparer = new_packet_preparer( + validator_client, + tested_network.clone(), + test_mixnode_sender, + *identity_keypair.public_key(), + *encryption_keypair.public_key(), + ); + + let packet_sender = new_packet_sender( + gateway_status_update_sender, + Arc::clone(&identity_keypair), + sending_rate, + ); + let received_processor = new_received_processor( + received_processor_receiver_channel, + Arc::clone(&encryption_keypair), + ); + let summary_producer = new_summary_producer(detailed_report); + let mut packet_receiver = new_packet_receiver( + gateway_status_update_receiver, + received_processor_sender_channel, + ); + + let mut monitor = monitor::Monitor::new( + packet_preparer, + packet_sender, + received_processor, + summary_producer, + node_status_api_client, + tested_network, + ); + + let mixnode_cache = Arc::new(RwLock::new(MixNodeCache::init( + vec![], + validators_rest_uris, + mixnet_contract, + ))); + + let write_mixnode_cache = Arc::clone(&mixnode_cache); + + tokio::spawn(async move { + let mut interval = time::interval(Duration::from_secs(cache_interval_arg)); + loop { + interval.tick().await; + { + match write_mixnode_cache.try_write() { + Ok(mut w) => w.cache().await.unwrap(), + // If we don't get the write lock skip a tick + Err(e) => error!("Could not aquire write lock on cache: {}", e), + } + } + } + }); + + tokio::spawn(async move { packet_receiver.run().await }); + + tokio::spawn(async move { monitor.run().await }); + + let allowed_origins = AllowedOrigins::all(); + + // You can also deserialize this + let cors = rocket_cors::CorsOptions { + allowed_origins, + allowed_methods: vec![Method::Post, Method::Get] + .into_iter() + .map(From::from) + .collect(), + allowed_headers: AllowedHeaders::all(), + allow_credentials: true, + ..Default::default() + } + .to_cors()?; + + rocket::build() + .attach(cors) + .mount("/", routes![get_mixnodes]) + .manage(mixnode_cache) + .ignite() + .await? + .launch() + .await?; + + wait_for_interrupt().await; + + Ok(()) +} diff --git a/validator-api/src/monitor/mod.rs b/validator-api/src/monitor/mod.rs index a3c1a6ca96..1e7030f84e 100644 --- a/validator-api/src/monitor/mod.rs +++ b/validator-api/src/monitor/mod.rs @@ -9,7 +9,7 @@ use crate::node_status_api; use crate::node_status_api::models::{BatchGatewayStatus, BatchMixStatus}; use crate::test_packet::NodeType; use crate::tested_network::TestedNetwork; -use log::*; +use log::{debug, info}; use tokio::time::{sleep, Duration, Instant}; pub(crate) mod preparer; @@ -139,7 +139,7 @@ impl Monitor { async fn test_run(&mut self) { info!(target: "Monitor", "Starting test run no. {}", self.nonce); - debug!(target: "Monitor", "preparing mix packets to all nodes..."); + debug!(target: "Monitor", "Preparing mix packets to all nodes..."); let prepared_packets = match self.packet_preparer.prepare_test_packets(self.nonce).await { Ok(packets) => packets, Err(err) => { @@ -151,12 +151,16 @@ impl Monitor { self.received_processor.set_new_expected(self.nonce).await; - info!(target: "Monitor", "starting to send all the packets..."); + info!(target: "Monitor", "Starting to send all the packets..."); self.packet_sender .send_packets(prepared_packets.packets) .await; - info!(target: "Monitor", "sending is over, waiting for {:?} before checking what we received", PACKET_DELIVERY_TIMEOUT); + info!( + target: "Monitor", + "Sending is over, waiting for {:?} before checking what we received", + PACKET_DELIVERY_TIMEOUT + ); // give the packets some time to traverse the network sleep(PACKET_DELIVERY_TIMEOUT).await; diff --git a/validator-api/src/monitor/preparer.rs b/validator-api/src/monitor/preparer.rs index a1aba7b924..9b1d6bdb19 100644 --- a/validator-api/src/monitor/preparer.rs +++ b/validator-api/src/monitor/preparer.rs @@ -6,7 +6,7 @@ use crate::monitor::sender::GatewayPackets; use crate::test_packet::{NodeType, TestPacket}; use crate::tested_network::TestedNetwork; use crypto::asymmetric::{encryption, identity}; -use log::*; +use log::{info, warn}; use mixnet_contract::{GatewayBond, MixNodeBond}; use nymsphinx::addressing::clients::Recipient; use nymsphinx::forwarding::packet::MixPacket; @@ -248,8 +248,9 @@ impl PacketPreparer { PreparedNode::TestedMix(mix, [v4_packet, v6_packet]) } Err(err) => { - warn!(target: "bad node", - "mix {} is malformed - {}", + warn!( + target: "Bad node", + "Mix {} is malformed - {}", mixnode_bond.mix_node().identity_key, err ); @@ -286,8 +287,9 @@ impl PacketPreparer { PreparedNode::TestedGateway(gateway, [v4_packet, v6_packet]) } Err(err) => { - warn!(target: "bad node", - "gateway {} is malformed - {:?}", + warn!( + target: "Bad node", + "gateway {} is malformed - {:?}", gateway_bond.gateway().identity_key, err ); diff --git a/validator-api/src/monitor/processor.rs b/validator-api/src/monitor/processor.rs index f2e9da15ee..fda77b1149 100644 --- a/validator-api/src/monitor/processor.rs +++ b/validator-api/src/monitor/processor.rs @@ -7,7 +7,7 @@ use crypto::asymmetric::encryption; use futures::channel::mpsc; use futures::lock::{Mutex, MutexGuard}; use futures::{SinkExt, StreamExt}; -use log::*; +use log::warn; use nymsphinx::receiver::MessageReceiver; use std::fmt::{self, Display, Formatter}; use std::mem; diff --git a/validator-api/src/monitor/sender.rs b/validator-api/src/monitor/sender.rs index 8c97b3a993..7d4fdebf41 100644 --- a/validator-api/src/monitor/sender.rs +++ b/validator-api/src/monitor/sender.rs @@ -10,7 +10,7 @@ use futures::task::Context; use futures::{Future, Stream}; use gateway_client::error::GatewayClientError; use gateway_client::{AcknowledgementReceiver, GatewayClient, MixnetMessageReceiver}; -use log::*; +use log::{debug, info, warn}; use nymsphinx::forwarding::packet::MixPacket; use pin_project::pin_project; use std::collections::HashMap; @@ -129,10 +129,15 @@ impl PacketSender { max_sending_rate: usize, ) -> Result<(), GatewayClientError> { let gateway_id = client.gateway_identity().to_base58_string(); - info!(target: "MessageSender", "Got {} packets to send to gateway {}", mix_packets.len(), gateway_id); + info!( + target: "MessageSender", + "Got {} packets to send to gateway {}", + mix_packets.len(), + gateway_id + ); if mix_packets.len() <= max_sending_rate { - debug!(target: "MessageSender", "Everything is going to get sent as one."); + debug!(target: "MessageSender","Everything is going to get sent as one."); client.batch_send_mix_packets(mix_packets).await?; } else { let packets_per_time_chunk = @@ -140,9 +145,10 @@ impl PacketSender { let total_expected_time = Duration::from_secs_f64(mix_packets.len() as f64 / max_sending_rate as f64); - info!(target: "MessageSender", - "With our rate of {} packets/s it should take around {:?} to send it all to {} ...", - max_sending_rate, total_expected_time, gateway_id + info!( + target: "MessageSender", + "With our rate of {} packets/s it should take around {:?} to send it all to {} ...", + max_sending_rate, total_expected_time, gateway_id ); fn split_off_vec(vec: &mut Vec, at: usize) -> Option> { @@ -160,7 +166,7 @@ impl PacketSender { // this way we won't have to do reallocations in here as they're unavoidable when // splitting a vector into multiple vectors while let Some(retained) = split_off_vec(&mut mix_packets, packets_per_time_chunk) { - debug!(target: "MessageSender", "Sending {} packets...", mix_packets.len()); + debug!(target: "MessageSender","Sending {} packets...", mix_packets.len()); if mix_packets.len() == 1 { client.send_mix_packet(mix_packets.pop().unwrap()).await?; diff --git a/validator-api/src/monitor/summary_producer.rs b/validator-api/src/monitor/summary_producer.rs index ca4a4c575e..e22b70d75d 100644 --- a/validator-api/src/monitor/summary_producer.rs +++ b/validator-api/src/monitor/summary_producer.rs @@ -7,7 +7,6 @@ use crate::node_status_api::models::{ }; use crate::test_packet::{NodeType, TestPacket}; use crate::PENALISE_OUTDATED; -use log::*; use std::collections::HashMap; #[derive(Default)] @@ -74,56 +73,74 @@ pub(crate) struct TestReport { impl TestReport { fn print(&self, detailed: bool) { - info!(target: "Test Report", "Sent total of {} packets", self.total_sent); - info!(target: "Test Report", "Received total of {} packets", self.total_received); - info!(target: "Test Report", "{} nodes are invalid", self.malformed.len()); + info!("Sent total of {} packets", self.total_sent); + info!("Received total of {} packets", self.total_received); + info!("{} nodes are invalid", self.malformed.len()); - info!(target: "Test Report", "{} mixnodes speak ONLY IPv4 (NO IPv6 connectivity)", self.only_ipv4_compatible_mixes.len()); - info!(target: "Test Report", "{} mixnodes speak ONLY IPv6 (NO IPv4 connectivity)", self.only_ipv6_compatible_mixes.len()); - info!(target: "Test Report", "{} mixnodes are totally unroutable!", self.completely_unroutable_mixes.len()); - info!(target: "Test Report", "{} mixnodes work fine!", self.fully_working_mixes.len()); + info!( + "{} mixnodes speak ONLY IPv4 (NO IPv6 connectivity)", + self.only_ipv4_compatible_mixes.len() + ); + info!( + "{} mixnodes speak ONLY IPv6 (NO IPv4 connectivity)", + self.only_ipv6_compatible_mixes.len() + ); + info!( + "{} mixnodes are totally unroutable!", + self.completely_unroutable_mixes.len() + ); + info!("{} mixnodes work fine!", self.fully_working_mixes.len()); - info!(target: "Test Report", "{} gateways speak ONLY IPv4 (NO IPv6 connectivity)", self.only_ipv4_compatible_gateways.len()); - info!(target: "Test Report", "{} gateways speak ONLY IPv6 (NO IPv4 connectivity)", self.only_ipv6_compatible_gateways.len()); - info!(target: "Test Report", "{} gateways are totally unroutable!", self.completely_unroutable_gateways.len()); - info!(target: "Test Report", "{} gateways work fine!", self.fully_working_gateways.len()); + info!( + "{} gateways speak ONLY IPv4 (NO IPv6 connectivity)", + self.only_ipv4_compatible_gateways.len() + ); + info!( + "{} gateways speak ONLY IPv6 (NO IPv4 connectivity)", + self.only_ipv6_compatible_gateways.len() + ); + info!( + "{} gateways are totally unroutable!", + self.completely_unroutable_gateways.len() + ); + info!("{} gateways work fine!", self.fully_working_gateways.len()); if detailed { - info!(target: "Detailed report", "full summary:"); + info!("full summary:"); for malformed in self.malformed.iter() { - info!(target: "Invalid node", "{}", malformed) + info!("{}", malformed) } for v4_node in self.only_ipv4_compatible_mixes.iter() { - info!(target: "IPv4-only mixnode", "{}", v4_node) + info!("{}", v4_node) } for v6_node in self.only_ipv6_compatible_mixes.iter() { - info!(target: "IPv6-only mixnode", "{}", v6_node) + info!("{}", v6_node) } for unroutable in self.completely_unroutable_mixes.iter() { - info!(target: "Unroutable mixnode", "{}", unroutable) + info!("{}", unroutable) } for working in self.fully_working_mixes.iter() { - info!(target: "Fully working mixnode", "{}", working) + info!("{}", working) } for v4_node in self.only_ipv4_compatible_gateways.iter() { - info!(target: "IPv4-only gateway", "{}", v4_node) + info!("{}", v4_node) } for v6_node in self.only_ipv6_compatible_gateways.iter() { - info!(target: "IPv6-only gateway", "{}", v6_node) + info!("{}", v6_node) } for unroutable in self.completely_unroutable_gateways.iter() { - info!(target: "Unroutable gateway", "{}", unroutable) + info!("{}", unroutable) } for working in self.fully_working_gateways.iter() { - info!(target: "Fully working gateway", "{}", working) + info!("{}", working) } } } diff --git a/validator-api/src/tested_network/mod.rs b/validator-api/src/tested_network/mod.rs index abcf9026ba..f3f41c056d 100644 --- a/validator-api/src/tested_network/mod.rs +++ b/validator-api/src/tested_network/mod.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::test_packet::IpVersion; -use log::*; use topology::{gateway, mix, NymTopology}; pub(crate) mod good_topology; diff --git a/validator-api/v4.json b/validator-api/v4.json deleted file mode 100644 index 4a67eecacd..0000000000 --- a/validator-api/v4.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "mixnodes": [ - { - "amount": [ - { - "denom": "uhal", - "amount": "100000000" - } - ], - "owner": "hal1c8t5cj5sgjt474y3wr04m6h3648yg7se0dkff2", - "mix_node": { - "host": "178.79.174.233", - "mix_port": 1789, - "verloc_port": 1790, - "http_api_port": 8000, - "clients_port": 9000, - "layer": 1, - "location": "London, UK", - "sphinx_key": "73V9Hdo79YxAUwUgAN6qgkhzcDJGymcGnGF6tFseaWFT", - "identity_key": "824WyExLUWvLE2mpSHBatN4AoByuLzfnHFeHWiBYzg4z", - "version": "0.10.0" - } - }, - { - "amount": [ - { - "denom": "uhal", - "amount": "100000000" - } - ], - "owner": "hal1yz3ac9382la00l4srczdtma0vrg5p7a72kky6t", - "mix_node": { - "host": "178.79.172.191", - "mix_port": 1789, - "verloc_port": 1790, - "http_api_port": 8000, - "clients_port": 9000, - "layer": 2, - "location": "London, UK", - "sphinx_key": "7eAfV7CgzGzbKU9QKYyUFKmvsCgPC1NMUF2U16Guezsn", - "identity_key": "7NBLS5M7QxFqUZkBZn2RWe3kkM8uH3MT2mtK4UPP2q7d", - "version": "0.10.0" - } - }, - { - "amount": [ - { - "denom": "uhal", - "amount": "100000000" - } - ], - "owner": "hal1rz7sqd90pjj3a6r540vyjsvw5ac3l26ddgzn5p", - "mix_node": { - "host": "139.162.211.224", - "mix_port": 1789, - "verloc_port": 1790, - "http_api_port": 8000, - "clients_port": 9000, - "layer": 3, - "location": "London, UK", - "sphinx_key": "EvYDvouNZQTmYbY2hgmdY55AxNmZCv8R8x3Kjrg7F8q7", - "identity_key": "J1xMf1BYEpT2W7HGk8HUCAXhhDubC6U61AzkU9hGDjHc", - "version": "0.10.0" - } - } - ], - "gateways": [ - { - "amount": [ - { - "denom": "uhal", - "amount": "100000000" - } - ], - "owner": "hal1r2w0y5v93sawac3yfhx0fkls86cp3mxn44jucp", - "gateway": { - "host": "176.58.120.67", - "mix_port": 1789, - "clients_port": 9000, - "location": "London, UK", - "sphinx_key": "4zdhKkDQ2ZuyqCA2Mm5e2ZxwbrnFTCfdMP1FmDKhB9Mp", - "identity_key": "AmoRv85ak8UrYkqd43NZpQJFQjn8rtgMfViBgAFaPDRh", - "version": "0.10.0" - } - } - ] -} diff --git a/validator-api/v6.json b/validator-api/v6.json deleted file mode 100644 index 3c28fed12c..0000000000 --- a/validator-api/v6.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "mixnodes": [ - { - "amount": [ - { - "denom": "uhal", - "amount": "100000000" - } - ], - "owner": "hal1c8t5cj5sgjt474y3wr04m6h3648yg7se0dkff2", - "mix_node": { - "host": "2a01:7e00::f03c:92ff:fe2f:5eee", - "mix_port": 1789, - "verloc_port": 1790, - "http_api_port": 8000, - "clients_port": 9000, - "layer": 1, - "location": "London, UK", - "sphinx_key": "73V9Hdo79YxAUwUgAN6qgkhzcDJGymcGnGF6tFseaWFT", - "identity_key": "824WyExLUWvLE2mpSHBatN4AoByuLzfnHFeHWiBYzg4z", - "version": "0.10.0" - } - }, - { - "amount": [ - { - "denom": "uhal", - "amount": "100000000" - } - ], - "owner": "hal1yz3ac9382la00l4srczdtma0vrg5p7a72kky6t", - "mix_node": { - "host": "2a01:7e00::f03c:92ff:fe2f:d749", - "mix_port": 1789, - "verloc_port": 1790, - "http_api_port": 8000, - "clients_port": 9000, - "layer": 2, - "location": "London, UK", - "sphinx_key": "7eAfV7CgzGzbKU9QKYyUFKmvsCgPC1NMUF2U16Guezsn", - "identity_key": "7NBLS5M7QxFqUZkBZn2RWe3kkM8uH3MT2mtK4UPP2q7d", - "version": "0.10.0" - } - }, - { - "amount": [ - { - "denom": "uhal", - "amount": "100000000" - } - ], - "owner": "hal1rz7sqd90pjj3a6r540vyjsvw5ac3l26ddgzn5p", - "mix_node": { - "host": "2a01:7e00::f03c:92ff:fe2f:6b03", - "layer": 3, - "mix_port": 1789, - "verloc_port": 1790, - "http_api_port": 8000, - "clients_port": 9000, - "location": "London, UK", - "sphinx_key": "EvYDvouNZQTmYbY2hgmdY55AxNmZCv8R8x3Kjrg7F8q7", - "identity_key": "J1xMf1BYEpT2W7HGk8HUCAXhhDubC6U61AzkU9hGDjHc", - "version": "0.10.0" - } - } - ], - "gateways": [ - { - "amount": [ - { - "denom": "uhal", - "amount": "100000000" - } - ], - "owner": "hal1r2w0y5v93sawac3yfhx0fkls86cp3mxn44jucp", - "gateway": { - "host": "2a01:7e00::f03c:92ff:fe2f:6b08", - "mix_port": 1789, - "clients_port": 9000, - "location": "London, UK", - "sphinx_key": "4zdhKkDQ2ZuyqCA2Mm5e2ZxwbrnFTCfdMP1FmDKhB9Mp", - "identity_key": "AmoRv85ak8UrYkqd43NZpQJFQjn8rtgMfViBgAFaPDRh", - "version": "0.10.0" - } - } - ] -}