diff --git a/src/bin/simulation.rs b/src/bin/simulation.rs index e44b5d2..673cfce 100644 --- a/src/bin/simulation.rs +++ b/src/bin/simulation.rs @@ -5,8 +5,14 @@ use troll_patrol::{ extra_info::ExtraInfo, - increment_simulated_date, - simulation::{bridge::Bridge, censor::Censor, extra_infos_server, state::State, user::User}, + get_date, increment_simulated_date, + simulation::{ + bridge::Bridge, + censor::{Censor, Hides::*, Speed::*, Totality::*}, + extra_infos_server, + state::State, + user::User, + }, }; use clap::Parser; @@ -18,8 +24,9 @@ use std::{ fs::File, io::BufReader, path::PathBuf, + time::Duration, }; -use tokio::spawn; +use tokio::{spawn, time::sleep}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -70,7 +77,7 @@ pub async fn main() { hostname: format!("http://localhost:{}", config.tp_test_port), }; let extra_infos_net = HyperNet { - hostname: "http://localhost:8003".to_string(), + hostname: "http://localhost:8004".to_string(), }; let la_pubkeys = get_lox_auth_keys(&la_net).await; @@ -84,7 +91,7 @@ pub async fn main() { prob_user_invites_friend: config.prob_user_invites_friend, prob_user_is_censor: config.prob_user_is_censor, prob_user_submits_reports: config.prob_user_submits_reports, - probs_user_in_country: config.probs_user_in_country, + probs_user_in_country: config.probs_user_in_country.clone(), sharing: config.sharing, }; @@ -92,6 +99,10 @@ pub async fn main() { // Set up censors let mut censors = HashMap::::new(); + for i in 0..config.probs_user_in_country.len() { + let cc = config.probs_user_in_country[i].0.clone(); + censors.insert(cc.clone(), Censor::new(cc, Fast, Overt, Full)); + } // Set up bridges (no bridges yet) let mut bridges = HashMap::<[u8; 20], Bridge>::new(); @@ -103,9 +114,17 @@ pub async fn main() { spawn(async move { extra_infos_server::server().await; }); + sleep(Duration::from_millis(1)).await; + + let mut fp = 0; + let mut tp = 0; // Main loop for _ in 0..config.num_days { + let la_net = HyperNet { + hostname: format!("http://localhost:{}", config.la_port), + }; + // USER TASKS // Add some new users @@ -120,8 +139,7 @@ pub async fn main() { // Users do daily actions for user in &mut users { // TODO: Refactor out connections from return - let (mut invited_friends, _connections) = - user.daily_tasks(&state, &mut bridges, &mut censors).await; + let mut invited_friends = user.daily_tasks(&state, &mut bridges, &mut censors).await; // If this user invited any friends, add them to the list of users new_users.append(&mut invited_friends); @@ -158,7 +176,18 @@ pub async fn main() { let new_blockages: HashMap> = serde_json::from_slice(&new_blockages_resp).unwrap(); - // TODO: Track stats about new blockages + // TODO: Track more stats about new blockages + for (bridge, ccs) in new_blockages { + let fingerprint = array_bytes::hex2array(bridge).unwrap(); + for cc in ccs { + let censor = censors.get(&cc).unwrap(); + if censor.knows_bridge(&fingerprint) { + tp += 1; + } else { + fp += 1; + } + } + } // LOX AUTHORITY TASKS @@ -175,4 +204,7 @@ pub async fn main() { // Advance simulated time to tomorrow increment_simulated_date(); } + + println!("True Positives: {}", tp); + println!("False Positives: {}", fp); } diff --git a/src/lib.rs b/src/lib.rs index 64e7272..3ad3944 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -871,20 +871,24 @@ pub async fn report_blockages( let mut blockages_str = HashMap::>::new(); for (fingerprint, countries) in blockages { let fpr_string = array_bytes::bytes2hex("", fingerprint); - blockages_str.insert(fpr_string, countries); + if countries.len() > 0 { + blockages_str.insert(fpr_string, countries); + } } - // Report blocked bridges to bridge distributor - let client = Client::new(); - let req = Request::builder() - .method(Method::POST) - .uri(uri) - .body(Body::from(serde_json::to_string(&blockages_str).unwrap())) - .unwrap(); - let resp = client.request(req).await.unwrap(); - let buf = hyper::body::to_bytes(resp).await.unwrap(); - let resp_str: String = serde_json::from_slice(&buf).unwrap(); - assert_eq!("OK", resp_str); + if blockages_str.len() > 0 { + // Report blocked bridges to bridge distributor + let client = Client::new(); + let req = Request::builder() + .method(Method::POST) + .uri(uri) + .body(Body::from(serde_json::to_string(&blockages_str).unwrap())) + .unwrap(); + let resp = client.request(req).await.unwrap(); + let buf = hyper::body::to_bytes(resp).await.unwrap(); + let resp_str: String = serde_json::from_slice(&buf).unwrap(); + assert_eq!("OK", resp_str); + } } // Unit tests diff --git a/src/main.rs b/src/main.rs index 9083443..937ce20 100644 --- a/src/main.rs +++ b/src/main.rs @@ -155,6 +155,9 @@ async fn context_manager( ) { let db: Db = sled::open(&db_config.db_path).unwrap(); + // Create negative report key for today if we don't have one + new_negative_report_key(&db, get_date()); + while let Some(cmd) = context_rx.recv().await { use Command::*; match cmd { diff --git a/src/simulation/bridge.rs b/src/simulation/bridge.rs index 0862a12..9d55589 100644 --- a/src/simulation/bridge.rs +++ b/src/simulation/bridge.rs @@ -19,19 +19,15 @@ impl Bridge { } pub fn from_bridge_line(bridgeline: &BridgeLine) -> Self { - Self::new(&bridgeline.fingerprint) + Self::new(&bridgeline.get_hashed_fingerprint()) } pub fn connect_real(&mut self, country: &str) { if self.real_connections.contains_key(country) { let prev = self.real_connections.get(country).unwrap(); - self.real_connections - .insert(country.to_string(), prev + 1) - .unwrap(); + self.real_connections.insert(country.to_string(), prev + 1); } else { - self.real_connections - .insert(country.to_string(), 1) - .unwrap(); + self.real_connections.insert(country.to_string(), 1); } self.connect_total(country); } @@ -39,13 +35,9 @@ impl Bridge { pub fn connect_total(&mut self, country: &str) { if self.total_connections.contains_key(country) { let prev = self.total_connections.get(country).unwrap(); - self.total_connections - .insert(country.to_string(), prev + 1) - .unwrap(); + self.total_connections.insert(country.to_string(), prev + 1); } else { - self.total_connections - .insert(country.to_string(), 1) - .unwrap(); + self.total_connections.insert(country.to_string(), 1); } } @@ -54,19 +46,17 @@ impl Bridge { if self.total_connections.contains_key(country) { let prev = self.total_connections.get(country).unwrap(); self.total_connections - .insert(country.to_string(), prev + num_connections) - .unwrap(); + .insert(country.to_string(), prev + num_connections); } else { self.total_connections - .insert(country.to_string(), num_connections) - .unwrap(); + .insert(country.to_string(), num_connections); } } // Generate an extra-info report for today pub fn gen_extra_info(&self) -> ExtraInfo { ExtraInfo { - nickname: String::default(), + nickname: String::from("simulation-bridge"), fingerprint: self.fingerprint, date: get_date(), bridge_ips: self.total_connections.clone(), diff --git a/src/simulation/extra_infos_server.rs b/src/simulation/extra_infos_server.rs index a585f35..827b5d3 100644 --- a/src/simulation/extra_infos_server.rs +++ b/src/simulation/extra_infos_server.rs @@ -130,7 +130,9 @@ fn add_extra_infos(extra_infos_pages: &mut Vec, request: Bytes) -> Respo for extra_info in extra_infos { extra_infos_file.push_str(extra_info.to_string().as_str()); } - extra_infos_pages.push(extra_infos_file); + if extra_infos_file.len() > 0 { + extra_infos_pages.push(extra_infos_file); + } prepare_header("OK".to_string()) } diff --git a/src/simulation/user.rs b/src/simulation/user.rs index 3fc7c98..a27d5c5 100644 --- a/src/simulation/user.rs +++ b/src/simulation/user.rs @@ -9,7 +9,7 @@ use crate::{ censor::{Censor, Hides::*, Speed::*, Totality::*}, state::State, }, - BridgeDistributor, + BridgeDistributor, COUNTRY_CODES, }; use lox_cli::{networking::*, *}; use lox_library::{ @@ -69,7 +69,7 @@ impl User { let mut cc = String::default(); for (country, prob) in &state.probs_user_in_country { let prob = *prob; - if prob < num { + if num < prob { cc = country.to_string(); break; } else { @@ -78,6 +78,7 @@ impl User { } cc }; + assert!(COUNTRY_CODES.contains(cc.as_str())); // Randomly determine how likely this user is to use bridges on // a given day @@ -135,7 +136,7 @@ impl User { let mut cc = String::default(); for (country, prob) in &state.probs_user_in_country { let prob = *prob; - if prob < num { + if num < prob { cc = country.to_string(); break; } else { @@ -237,7 +238,7 @@ impl User { state: &State, bridges: &mut HashMap<[u8; 20], Bridge>, censors: &mut HashMap, - ) -> (Vec, Vec<[u8; 20]>) { + ) -> Vec { // Probabilistically decide if the user should use bridges today if event_happens(self.prob_use_bridges) { // Download bucket to see if bridge is still reachable. (We @@ -248,23 +249,25 @@ impl User { // Make sure each bridge in bucket is in the global bridges set for bridgeline in bucket { - if !bridges.contains_key(&bridgeline.fingerprint) { - let bridge = Bridge::from_bridge_line(&bridgeline); - bridges.insert(bridgeline.fingerprint, bridge).unwrap(); - } - // Also, if this user cooperates with censors, make sure - // each applicable censor knows about their bridges. - if self.censor { - if state.sharing { - for c in censors.values_mut() { - if !c.knows_bridge(&bridgeline.fingerprint) { - c.learn_bridge(&bridgeline.fingerprint); + if bridgeline != BridgeLine::default() { + if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { + let bridge = Bridge::from_bridge_line(&bridgeline); + bridges.insert(bridgeline.get_hashed_fingerprint(), bridge); + } + // Also, if this user cooperates with censors, make sure + // each applicable censor knows about their bridges. + if self.censor { + if state.sharing { + for c in censors.values_mut() { + if !c.knows_bridge(&bridgeline.get_hashed_fingerprint()) { + c.learn_bridge(&bridgeline.get_hashed_fingerprint()); + } + } + } else { + let censor = censors.get_mut(&self.country).unwrap(); + if !censor.knows_bridge(&bridgeline.get_hashed_fingerprint()) { + censor.learn_bridge(&bridgeline.get_hashed_fingerprint()); } - } - } else { - let censor = censors.get_mut(&self.country).unwrap(); - if !censor.knows_bridge(&bridgeline.fingerprint) { - censor.learn_bridge(&bridgeline.fingerprint); } } } @@ -282,14 +285,17 @@ impl User { // Can we level up the secondary credential? let mut second_level_up = false; + // Attempt to connect to each bridge let mut failed = Vec::::new(); let mut succeeded = Vec::::new(); for i in 0..bucket.len() { // At level 0, we only have 1 bridge - if level > 0 || i == 0 { + if bucket[i] != BridgeLine::default() { if self.connect( &state, - bridges.get_mut(&bucket[i].fingerprint).unwrap(), + bridges + .get_mut(&bucket[i].get_hashed_fingerprint()) + .unwrap(), &censors.get(&self.country).unwrap(), ) { succeeded.push(bucket[i]); @@ -321,45 +327,49 @@ impl User { let second_cred = second_cred.as_ref().unwrap(); let (second_bucket, second_reachcred) = get_bucket(&state.la_net, &second_cred).await; - if !bridges.contains_key(&second_bucket[0].fingerprint) { - bridges - .insert( - second_bucket[0].fingerprint, - Bridge::from_bridge_line(&second_bucket[0]), - ) - .unwrap(); - } - if self.connect( - &state, - bridges.get_mut(&second_bucket[0].fingerprint).unwrap(), - &censors.get(&self.country).unwrap(), - ) { - succeeded.push(second_bucket[0]); - if second_reachcred.is_some() - && eligible_for_trust_promotion(&state.la_net, &second_cred).await - { - second_level_up = true; + for bridgeline in second_bucket { + if bridgeline != BridgeLine::default() { + if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { + bridges.insert( + bridgeline.get_hashed_fingerprint(), + Bridge::from_bridge_line(&bridgeline), + ); + } + if self.connect( + &state, + bridges + .get_mut(&bridgeline.get_hashed_fingerprint()) + .unwrap(), + &censors.get(&self.country).unwrap(), + ) { + succeeded.push(bridgeline); + if second_reachcred.is_some() + && eligible_for_trust_promotion(&state.la_net, &second_cred).await + { + second_level_up = true; + } + } else { + failed.push(bridgeline); + } } - } else { - failed.push(second_bucket[0]); } } let mut negative_reports = Vec::::new(); let mut positive_reports = Vec::::new(); if self.submits_reports { - for bridge in &failed { + for bridgeline in &failed { negative_reports.push(NegativeReport::from_bridgeline( - *bridge, + *bridgeline, self.country.to_string(), BridgeDistributor::Lox, )); } if level >= 3 { - for bridge in &succeeded { + for bridgeline in &succeeded { positive_reports.push( PositiveReport::from_lox_credential( - bridge.fingerprint, + bridgeline.get_hashed_fingerprint(), None, &self.primary_cred, get_lox_pub(&state.la_pubkeys), @@ -392,8 +402,8 @@ impl User { let censor = censors.get_mut(&self.country).unwrap(); let (bucket, reachcred) = get_bucket(&state.la_net, &self.primary_cred).await; for bl in bucket { - censor.learn_bridge(&bl.fingerprint); - censor.give_lox_cred(&bl.fingerprint, &self.primary_cred); + censor.learn_bridge(&bl.get_hashed_fingerprint()); + censor.give_lox_cred(&bl.get_hashed_fingerprint(), &self.primary_cred); } } } @@ -461,16 +471,9 @@ impl User { } } - // List of fingerprints we contacted. This should not - // actually be more than one. - let mut connections = Vec::<[u8; 20]>::new(); - for bridge in succeeded { - connections.push(bridge.get_hashed_fingerprint()); - } - - (new_friends, connections) + new_friends } else { - (Vec::::new(), Vec::<[u8; 20]>::new()) + Vec::::new() } } }