// Before running this, run: // 1. rdsys // 2. lox-distributor // 3. troll-patrol with the feature "simulation" use troll_patrol::{ extra_info::ExtraInfo, get_date, increment_simulated_date, set_simulated_date, simulation::{ bridge::Bridge, censor::{self, Censor}, config::Config as SConfig, extra_infos_server, user::User, }, }; use clap::Parser; use lox_cli::{networking::*, *}; use lox_library::proto::{level_up::LEVEL_INTERVAL, trust_promotion::UNTRUSTED_INTERVAL}; use rand::Rng; use serde::Deserialize; use std::{ collections::{HashMap, HashSet}, fs::File, io::BufReader, path::PathBuf, time::Duration, }; use tokio::{spawn, time::sleep}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { /// Name/path of the configuration file #[arg(short, long, default_value = "simulation_config.json")] config: PathBuf, } #[derive(Debug, Deserialize)] pub struct Config { pub la_port: u16, pub la_test_port: u16, pub tp_port: u16, pub tp_test_port: u16, pub censor_hides: censor::Hides, pub censor_speed: censor::Speed, pub censor_event_duration: u32, pub censor_totality: censor::Totality, pub censor_partial_blocking_percent: f64, pub country: String, pub min_new_users_per_day: u32, pub max_new_users_per_day: u32, // How many days to simulate pub num_days: u32, // We start with this many level 4 users pub num_initial_trusted_users: u32, pub prob_connection_fails: f64, pub prob_user_invites_friend: f64, pub prob_user_is_censor: f64, pub prob_user_submits_reports: f64, } #[tokio::main] pub async fn main() { let args: Args = Args::parse(); let config: Config = serde_json::from_reader(BufReader::new( File::open(&args.config).expect("Could not read config file"), )) .expect("Reading config file from JSON failed"); let la_net = HyperNet { hostname: format!("http://localhost:{}", config.la_port), }; let la_net_test = HyperNet { hostname: format!("http://localhost:{}", config.la_test_port), }; let tp_net = HyperNet { hostname: format!("http://localhost:{}", config.tp_port), }; let tp_net_test = HyperNet { hostname: format!("http://localhost:{}", config.tp_test_port), }; let extra_infos_net = HyperNet { hostname: "http://localhost:8004".to_string(), }; let la_pubkeys = get_lox_auth_keys(&la_net).await; let sconfig = SConfig { la_pubkeys, la_net, tp_net, censor_hides: config.censor_hides, censor_speed: config.censor_speed, censor_event_duration: config.censor_event_duration, censor_totality: config.censor_totality, censor_partial_blocking_percent: config.censor_partial_blocking_percent, country: config.country, prob_connection_fails: config.prob_connection_fails, 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, }; let mut rng = rand::thread_rng(); // Set up censor let mut censor = Censor::new(&sconfig); // Set up bridges (no bridges yet) let mut bridges = HashMap::<[u8; 20], Bridge>::new(); // Set up users let mut users = Vec::::new(); if config.num_initial_trusted_users > 0 { // Add some number of trusted users initially for _ in 0..config.num_initial_trusted_users { users.push(User::trusted_user(&sconfig).await); } // Level trusted users up to level 4 // Advance LA's time la_net_test .request( "/advancedays".to_string(), serde_json::to_string(&(UNTRUSTED_INTERVAL as u16)) .unwrap() .into(), ) .await; // Advance simulated time set_simulated_date(get_date() + UNTRUSTED_INTERVAL); for user in &mut users { user.primary_cred = trust_migration( &sconfig.la_net, &user.primary_cred, &trust_promotion( &sconfig.la_net, &user.primary_cred, get_lox_pub(&sconfig.la_pubkeys), ) .await, get_lox_pub(&sconfig.la_pubkeys), get_migration_pub(&sconfig.la_pubkeys), ) .await; } for i in 1..LEVEL_INTERVAL.len() - 2 { // Advance LA's time to tomorrow la_net_test .request( "/advancedays".to_string(), serde_json::to_string(&(LEVEL_INTERVAL[i] as u16)) .unwrap() .into(), ) .await; // Advance simulated time to tomorrow set_simulated_date(get_date() + LEVEL_INTERVAL[i]); for user in &mut users { let reachcred = get_bucket(&sconfig.la_net, &user.primary_cred).await.1; user.primary_cred = level_up( &sconfig.la_net, &user.primary_cred, &reachcred.unwrap(), get_lox_pub(&sconfig.la_pubkeys), get_reachability_pub(&sconfig.la_pubkeys), ) .await; } } } // Set up extra-infos server 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 day in 1..=config.num_days { println!("Starting day {} of the simulation", day); // USER TASKS // Add some new users let num_new_users: u32 = rng.gen_range(config.min_new_users_per_day..=config.max_new_users_per_day); for _ in 0..num_new_users { users.push(User::new(&sconfig).await); } let mut new_users = Vec::::new(); // Users do daily actions for user in &mut users { let mut invited_friends = user.daily_tasks(&sconfig, &mut bridges, &mut censor).await; // If this user invited any friends, add them to the list of users new_users.append(&mut invited_friends); } // Add new users users.append(&mut new_users); // CENSOR TASKS censor.end_of_day_tasks(&sconfig, &mut bridges).await; // BRIDGE TASKS let mut new_extra_infos = HashSet::::new(); for (_, bridge) in bridges.iter_mut() { // Bridge reports its connections for the day new_extra_infos.insert(bridge.gen_extra_info(&sconfig.country)); // Bridge resets for tomorrow bridge.reset_for_tomorrow(); } // Publish all the bridges' extra-infos for today extra_infos_net .request( "/add".to_string(), serde_json::to_string(&new_extra_infos).unwrap().into(), ) .await; // TROLL PATROL TASKS let new_blockages_resp = tp_net_test.request("/update".to_string(), vec![]).await; let new_blockages: HashMap> = serde_json::from_slice(&new_blockages_resp).unwrap(); // TODO: Track more stats about new blockages for (bridge, ccs) in new_blockages { let fingerprint = array_bytes::hex2array(bridge).unwrap(); for cc in ccs { if cc == sconfig.country { if censor.knows_bridge(&fingerprint) { tp += 1; } else { fp += 1; } } } } // LOX AUTHORITY TASKS // Advance LA's time to tomorrow la_net_test .request( "/advancedays".to_string(), serde_json::to_string(&(1 as u16)).unwrap().into(), ) .await; // SIMULATION TASKS // Advance simulated time to tomorrow increment_simulated_date(); } println!("True Positives: {}", tp); println!("False Positives: {}", fp); }