Browse Source

Track whether bridge has actually been distributed to users

Vecna 1 year ago
parent
commit
25d1fb96a6
3 changed files with 170 additions and 52 deletions
  1. 4 0
      src/bridge.rs
  2. 83 6
      src/main.rs
  3. 83 46
      src/user.rs

+ 4 - 0
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,

+ 83 - 6
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::<f64>::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");
 }

+ 83 - 46
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::<NegativeReport>::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::<NegativeReport>::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::<NegativeReport>::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