From 25d1fb96a626166422038085a8456d91a86899f1 Mon Sep 17 00:00:00 2001 From: Vecna Date: Tue, 18 Jun 2024 14:31:37 -0400 Subject: [PATCH] Track whether bridge has actually been distributed to users --- src/bridge.rs | 4 ++ src/main.rs | 89 +++++++++++++++++++++++++++++++--- src/user.rs | 129 ++++++++++++++++++++++++++++++++------------------ 3 files changed, 170 insertions(+), 52 deletions(-) diff --git a/src/bridge.rs b/src/bridge.rs index 93c4818..515dd3c 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -15,6 +15,9 @@ pub struct Bridge { // we created this Bridge object pub first_distributed: u32, + // Date the bridge was first distributed to a non-censor user + pub first_real_user: u32, + // First date a censor blocked this bridge pub first_blocked: u32, @@ -35,6 +38,7 @@ impl Bridge { Self { fingerprint: *fingerprint, first_distributed: get_date(), + first_real_user: 0, // set this afterwards if user is non-censor first_blocked: 0, first_detected_blocked: 0, first_positive_report: 0, diff --git a/src/main.rs b/src/main.rs index a49b5f7..e13c907 100644 --- a/src/main.rs +++ b/src/main.rs @@ -126,11 +126,21 @@ pub async fn main() { }); sleep(Duration::from_millis(1)).await; + // Only consider bridges that have been distributed to users let mut false_neg = 0; let mut false_pos = 0; let mut true_neg = 0; let mut true_pos = 0; + // All bridges, including those only known to the censor + let mut total_fn = 0; + let mut total_fp = 0; + let mut total_tn = 0; + let mut total_tp = 0; + + // Track daily percentage of users who have at least one working bridge + let mut percent_users_can_connect = Vec::::new(); + // Track memory use during simulation let mut max_physical_mem = 0; let mut max_virtual_mem = 0; @@ -140,6 +150,10 @@ pub async fn main() { // Save some function calls by storing this let date = get_date(); + // Count of users who could use at least one bridge today + let mut count_users_can_connect = 0; + let mut count_users_cannot_connect = 0; + println!("Starting day {} of the simulation", day); println!( " We have {} users and {} bridges", @@ -195,8 +209,37 @@ pub async fn main() { new_users.append(&mut invited_friends); } } + + // Count the number of non-censor users who are able to + // connect to at least one bridge + if !user.is_censor { + if user.able_to_connect { + count_users_can_connect += 1; + } else { + count_users_cannot_connect += 1; + } + } } + // Also count number of new users with/without connections + for user in &new_users { + // Count the number of non-censor users who are able to + // connect to at least one bridge + if !user.is_censor { + if user.able_to_connect { + count_users_can_connect += 1; + } else { + count_users_cannot_connect += 1; + } + } + } + + // Add percent of users who can connect to vector + percent_users_can_connect.push( + count_users_can_connect as f64 + / (count_users_can_connect + count_users_cannot_connect) as f64, + ); + // Add new users users.append(&mut new_users); @@ -285,14 +328,30 @@ pub async fn main() { if really_blocked && bridge.first_blocked == 0 { bridge.first_blocked = date; } + + // Increase appropriate count. Only increase main count if + // this is a bridge that has actually been distributed to a + // non-censor user. Increase the total count regardless. if detected_blocked && really_blocked { - true_pos += 1; + if bridge.first_real_user > 0 { + true_pos += 1; + } + total_tp += 1; } else if detected_blocked { - false_pos += 1; + if bridge.first_real_user > 0 { + false_pos += 1; + } + total_fp += 1; } else if really_blocked { - false_neg += 1; + if bridge.first_real_user > 0 { + false_neg += 1; + } + total_fn += 1; } else { - true_neg += 1; + if bridge.first_real_user > 0 { + true_neg += 1; + } + total_tn += 1; } } @@ -334,6 +393,13 @@ pub async fn main() { max_virtual_mem ); + println!("\nThese total values include bridges never distributed to real users..."); + println!("Total true positives: {}", total_tp); + println!("Total true negatives: {}", total_tn); + println!("Total false positives: {}", total_fp); + println!("Total false negatives: {}", total_fn); + + println!("\nThese values only include bridges actually distributed to users..."); println!("True Positives: {}", true_pos); println!("True Negatives: {}", true_neg); println!("False Positives: {}", false_pos); @@ -342,16 +408,27 @@ pub async fn main() { println!("\nFull stats per bridge:"); println!( - "Fingerprint,first_distributed,first_blocked,first_detected_blocked,first_positive_report" + "Fingerprint,first_distributed,first_real_user,first_blocked,first_detected_blocked,first_positive_report" ); for (fingerprint, bridge) in bridges { println!( - "{},{},{},{},{}", + "{},{},{},{},{},{}", array_bytes::bytes2hex("", fingerprint), bridge.first_distributed, + bridge.first_real_user, bridge.first_blocked, bridge.first_detected_blocked, bridge.first_positive_report ); } + println!("End full stats per bridge\n"); + + println!("\nWhich users can connect:"); + println!("join_date,able_to_connect"); + for user in users { + if !user.is_censor { + println!("{},{}", user.join_date, user.able_to_connect); + } + } + println!("End which users can connect"); } diff --git a/src/user.rs b/src/user.rs index f39ca10..8f29adc 100644 --- a/src/user.rs +++ b/src/user.rs @@ -90,19 +90,27 @@ impl User { .unwrap(); if is_censor { censor.learn_bridge(&bridgeline.get_hashed_fingerprint()); - } else if Self::connect(in_censorship_range, config, bridge, censor) { - able_to_connect = true; - } else if submits_reports { - // New user only has one bridge, so no need - // to collect the negative reports before - // sending. Just send one now. - let mut negative_reports = Vec::::new(); - negative_reports.push(NegativeReport::from_bridgeline( - bridgeline, - config.country.to_string(), - BridgeDistributor::Lox, - )); - Self::send_negative_reports(&config, negative_reports).await?; + } else { + // If this is the first time the bridge has been + // distributed to a real user, store that info + if bridge.first_real_user == 0 { + bridge.first_real_user = get_date(); + } + + if Self::connect(in_censorship_range, config, bridge, censor) { + able_to_connect = true; + } else if submits_reports { + // New user only has one bridge, so no need + // to collect the negative reports before + // sending. Just send one now. + let mut negative_reports = Vec::::new(); + negative_reports.push(NegativeReport::from_bridgeline( + bridgeline, + config.country.to_string(), + BridgeDistributor::Lox, + )); + Self::send_negative_reports(&config, negative_reports).await?; + } } } } @@ -138,16 +146,28 @@ impl User { ) .await?; self.primary_cred = new_cred; - if self.is_censor { - // Make sure censor has access to each bridge and each - // credential - let (bucket, _reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?; - for bl in bucket { + // Make sure bridge is in list of bridges and that censor has + // access if applicable + let (bucket, _reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?; + for bl in bucket { + if bl != BridgeLine::default() { let fingerprint = bl.get_hashed_fingerprint(); - censor.learn_bridge(&fingerprint); - censor.give_lox_cred(&fingerprint, &self.primary_cred); + if !bridges.contains_key(&fingerprint) { + let bridge = Bridge::from_bridge_line(&bl); + bridges.insert(fingerprint, bridge); + } + let bridge = bridges.get_mut(&fingerprint).unwrap(); + if self.is_censor { + censor.learn_bridge(&fingerprint); + censor.give_lox_cred(&fingerprint, &self.primary_cred); + // If this is the first time the bridge has been + // distributed to a real user, store that info + } else if bridge.first_real_user == 0 { + bridge.first_real_user = get_date(); + } } } + let friend_cred = redeem_invite( &config.la_net, &invite, @@ -184,24 +204,31 @@ impl User { let mut negative_reports = Vec::::new(); let (bucket, _reachcred) = get_bucket(&config.la_net, &friend_cred).await?; for bridgeline in bucket { + let fingerprint = bridgeline.get_hashed_fingerprint(); if bridgeline != BridgeLine::default() { - if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { + if !bridges.contains_key(&fingerprint) { let bridge = Bridge::from_bridge_line(&bridgeline); - bridges.insert(bridgeline.get_hashed_fingerprint(), bridge); + bridges.insert(fingerprint, bridge); } - let bridge = bridges - .get_mut(&bridgeline.get_hashed_fingerprint()) - .unwrap(); + let bridge = bridges.get_mut(&fingerprint).unwrap(); if is_censor { - censor.learn_bridge(&bridgeline.get_hashed_fingerprint()); - } else if Self::connect(in_censorship_range, config, bridge, censor) { - able_to_connect = true; - } else if submits_reports { - negative_reports.push(NegativeReport::from_bridgeline( - bridgeline, - config.country.to_string(), - BridgeDistributor::Lox, - )); + censor.learn_bridge(&fingerprint); + } else { + // If this is the first time the bridge has been + // distributed to a real user, store that info + if bridge.first_real_user == 0 { + bridge.first_real_user = get_date(); + } + + if Self::connect(in_censorship_range, config, bridge, censor) { + able_to_connect = true; + } else if submits_reports { + negative_reports.push(NegativeReport::from_bridgeline( + bridgeline, + config.country.to_string(), + BridgeDistributor::Lox, + )); + } } } } @@ -370,9 +397,17 @@ impl User { // Make sure each bridge in bucket is in the global bridges set for bridgeline in bucket { if bridgeline != BridgeLine::default() { - if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { + let fingerprint = bridgeline.get_hashed_fingerprint(); + if !bridges.contains_key(&fingerprint) { let bridge = Bridge::from_bridge_line(&bridgeline); - bridges.insert(bridgeline.get_hashed_fingerprint(), bridge); + bridges.insert(fingerprint, bridge); + } + + // If this is the first time the bridge has been + // distributed to a real user, store that info + let bridge = bridges.get_mut(&fingerprint).unwrap(); + if bridge.first_real_user == 0 { + bridge.first_real_user = get_date(); } } } @@ -437,21 +472,23 @@ impl User { get_bucket(&config.la_net, &second_cred).await?; for bridgeline in second_bucket { if bridgeline != BridgeLine::default() { - if !bridges.contains_key(&bridgeline.get_hashed_fingerprint()) { + let fingerprint = bridgeline.get_hashed_fingerprint(); + if !bridges.contains_key(&fingerprint) { bridges.insert( - bridgeline.get_hashed_fingerprint(), + bridgeline.fingerprint, Bridge::from_bridge_line(&bridgeline), ); } + + // If this is the first time the bridge has been + // distributed to a real user, store that info + let bridge = bridges.get_mut(&fingerprint).unwrap(); + if bridge.first_real_user == 0 { + bridge.first_real_user = get_date(); + } + // Attempt to connect to second cred's bridge - if Self::connect( - self.in_censorship_range, - &config, - bridges - .get_mut(&bridgeline.get_hashed_fingerprint()) - .unwrap(), - censor, - ) { + if Self::connect(self.in_censorship_range, &config, bridge, censor) { succeeded.push(bridgeline); if second_reachcred.is_some() && eligible_for_trust_promotion(&config.la_net, &second_cred).await