Explorar o código

Change how censor-cooperating users join the simulation

Vecna hai 1 ano
pai
achega
ebc508b64f
Modificáronse 4 ficheiros con 95 adicións e 125 borrados
  1. 7 1
      src/censor.rs
  2. 3 1
      src/config.rs
  3. 13 55
      src/main.rs
  4. 72 68
      src/user.rs

+ 7 - 1
src/censor.rs

@@ -57,6 +57,10 @@ impl Censor {
         }
     }
 
+    pub fn is_active(&self) -> bool {
+        get_date() >= self.start_date
+    }
+
     pub fn knows_bridge(&self, fingerprint: &[u8; 20]) -> bool {
         self.known_bridges.contains(fingerprint)
     }
@@ -218,7 +222,9 @@ impl Censor {
         config: &Config,
         bridges: &mut HashMap<[u8; 20], Bridge>,
     ) {
-        if get_date() >= self.start_date {
+        // This check is redundant because the simulation driver only
+        // calls this function if the censor is active.
+        if self.is_active() {
             if config.censor_secrecy == Secrecy::Flooding
                 && !(config.censor_speed == Speed::Random && self.delay_date <= get_date())
             {

+ 3 - 1
src/config.rs

@@ -19,7 +19,9 @@ pub struct Config {
     pub country: String,
     pub one_positive_report_per_cred: bool,
     // Probability that a censor-cooperating user can convince an honest
-    // user to give them an invite.
+    // user to give them an invite. This number is the baseline, for
+    // soliciting an invitation from a level 1 user. We assume it is
+    // harder for the censor to get invitations from higher-level users.
     pub prob_censor_gets_invite: f64,
     // Probability that a connection randomly fails, even though censor
     // does not block the bridge

+ 13 - 55
src/main.rs

@@ -137,6 +137,9 @@ pub async fn main() {
 
     // Main loop
     for day in 1..=config.num_days {
+        // Save some function calls by storing this
+        let date = get_date();
+
         println!("Starting day {} of the simulation", day);
         println!(
             "    We have {} users and {} bridges",
@@ -167,33 +170,9 @@ pub async fn main() {
         // USER TASKS
 
         // Number of users who want to join today
-        let mut num_users_requesting_invites: u32 =
+        let num_users_requesting_invites: u32 =
             rng.gen_range(config.min_new_users_per_day..=config.max_new_users_per_day);
 
-        // How many of the new users are censors?
-        let mut num_new_censor_users = 0;
-        for _ in 0..num_users_requesting_invites {
-            let num: f64 = rng.gen_range(0.0..1.0);
-            if num < config.prob_user_is_censor {
-                num_new_censor_users += 1;
-                num_users_requesting_invites -= 1;
-            }
-        }
-
-        // Determine whether each new censor user can get an invite from
-        // an existing trusted user or needs to join via open-entry
-        // invite. Note: We still favor honest users by giving them
-        // invites *first*. This means if only a small number of invites
-        // are available, the censor may still not get invited.
-        let mut num_censor_invitations = 0;
-        for _ in 0..num_new_censor_users {
-            let num: f64 = rng.gen_range(0.0..1.0);
-            if num < config.prob_censor_gets_invite {
-                num_censor_invitations += 1;
-                num_new_censor_users -= 1;
-            }
-        }
-
         let mut new_users = Vec::<User>::new();
 
         // Shuffle users so they act in a random order
@@ -205,7 +184,6 @@ pub async fn main() {
                 .daily_tasks(
                     &sconfig,
                     num_users_requesting_invites,
-                    num_censor_invitations,
                     &mut bridges,
                     &mut censor,
                 )
@@ -214,21 +192,6 @@ pub async fn main() {
             if invited_friends.is_ok() {
                 let mut invited_friends = invited_friends.unwrap();
                 if invited_friends.len() > 0 {
-                    if !user.is_censor {
-                        // Censors always invite as many censor friends
-                        // as possible. Honest users may invite honest
-                        // friends, or they may accidentally invite
-                        // censor friends.
-                        for inv_friend in &invited_friends {
-                            if inv_friend.is_censor {
-                                num_censor_invitations -= 1;
-                            } else {
-                                num_users_requesting_invites -= 1;
-                            }
-                        }
-                    }
-                    // If this user invited any friends, add them to the
-                    // list of users
                     new_users.append(&mut invited_friends);
                 }
             }
@@ -240,7 +203,7 @@ pub async fn main() {
         // If any users couldn't get invites, they join with open-entry
         // invitations
         for _ in 0..num_users_requesting_invites {
-            let user = User::new(&sconfig, false).await;
+            let user = User::new(&sconfig, false, &mut bridges, &mut censor).await;
             if user.is_ok() {
                 users.push(user.unwrap());
             } else {
@@ -248,20 +211,15 @@ pub async fn main() {
             }
         }
 
-        // If any censor users couldn't get invites, they also join with
-        // open-entry invitations
-        for _ in 0..(num_new_censor_users + num_censor_invitations) {
-            let user = User::new(&sconfig, true).await;
-            if user.is_ok() {
-                users.push(user.unwrap());
-            } else {
-                eprintln!("Failed to create new censor user.");
+        // CENSOR TASKS
+        if censor.is_active() {
+            // Censor gets as many open-entry invites as possible
+            while let Ok(new_user) = User::new(&sconfig, true, &mut bridges, &mut censor).await {
+                users.push(new_user);
             }
+            censor.end_of_day_tasks(&sconfig, &mut bridges).await;
         }
 
-        // CENSOR TASKS
-        censor.end_of_day_tasks(&sconfig, &mut bridges).await;
-
         // BRIDGE TASKS
         let mut new_extra_infos = HashSet::<ExtraInfo>::new();
         for (_, bridge) in bridges.iter_mut() {
@@ -319,13 +277,13 @@ pub async fn main() {
             // If this is the first day Troll Patrol has determined this
             // bridge is blocked, note that for stats
             if detected_blocked && bridge.first_detected_blocked == 0 {
-                bridge.first_detected_blocked = get_date();
+                bridge.first_detected_blocked = date;
             }
 
             // Check if censor actually blocks this bridge
             let really_blocked = censor.blocks_bridge(&sconfig, fingerprint);
             if really_blocked && bridge.first_blocked == 0 {
-                bridge.first_blocked = get_date();
+                bridge.first_blocked = date;
             }
             if detected_blocked && really_blocked {
                 true_pos += 1;

+ 72 - 68
src/user.rs

@@ -43,14 +43,13 @@ pub struct User {
 }
 
 impl User {
-    pub async fn new(config: &Config, is_censor: bool) -> Result<Self> {
-        let cred = get_lox_credential(
-            &config.la_net,
-            &get_open_invitation(&config.la_net).await?,
-            get_lox_pub(&config.la_pubkeys),
-        )
-        .await?
-        .0;
+    pub async fn new(
+        config: &Config,
+        is_censor: bool,
+        bridges: &mut HashMap<[u8; 20], Bridge>,
+        censor: &mut Censor,
+    ) -> Result<Self> {
+        let cred = Self::get_new_credential(&config).await?.0;
 
         // Probabilistically decide whether this user submits reports
         let submits_reports = if is_censor {
@@ -64,6 +63,21 @@ impl User {
         let mut rng = rand::thread_rng();
         let prob_use_bridges = rng.gen_range(0.0..=1.0);
 
+        // If the user cooperates with the censor, immediately tell the
+        // censor about all the bridges
+        if is_censor {
+            let (bucket, _reachcred) = get_bucket(&config.la_net, &cred).await?;
+            for bridgeline in bucket {
+                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);
+                    }
+                    censor.learn_bridge(&bridgeline.get_hashed_fingerprint());
+                }
+            }
+        }
+
         Ok(Self {
             is_censor,
             primary_cred: cred,
@@ -73,27 +87,11 @@ impl User {
         })
     }
 
-    pub async fn trusted_user(config: &Config) -> Result<Self> {
-        let cred = get_lox_credential(
-            &config.la_net,
-            &get_open_invitation(&config.la_net).await?,
-            get_lox_pub(&config.la_pubkeys),
-        )
-        .await?
-        .0;
-        Ok(Self {
-            is_censor: false,
-            primary_cred: cred,
-            secondary_cred: None,
-            submits_reports: true,
-            prob_use_bridges: 1.0,
-        })
-    }
-
     // TODO: This should probably return an actual error type
     pub async fn invite(
         &mut self,
         config: &Config,
+        bridges: &mut HashMap<[u8; 20], Bridge>,
         censor: &mut Censor,
         invited_user_is_censor: bool,
     ) -> Result<Self> {
@@ -130,17 +128,31 @@ impl User {
         // Calling function decides if the invited user is a censor
         let is_censor = invited_user_is_censor;
 
-        // Probabilistically decide whether this user submits reports
-        let submits_reports = if is_censor {
-            false
+        // Decide how likely this user is to use bridges on a given day
+        // and whether they submit reports
+        let (prob_use_bridges, submits_reports) = if is_censor {
+            (0.0, false)
         } else {
-            event_happens(config.prob_user_submits_reports)
+            let mut rng = rand::thread_rng();
+            let prob_use_bridges = rng.gen_range(0.0..=1.0);
+            let submits_reports = event_happens(config.prob_user_submits_reports);
+            (prob_use_bridges, submits_reports)
         };
 
-        // Randomly determine how likely this user is to use bridges on
-        // a given day
-        let mut rng = rand::thread_rng();
-        let prob_use_bridges = rng.gen_range(0.0..=1.0);
+        // If the user cooperates with the censor, immediately tell the
+        // censor about all the bridges
+        if is_censor {
+            let (bucket, _reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?;
+            for bridgeline in bucket {
+                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);
+                    }
+                    censor.learn_bridge(&bridgeline.get_hashed_fingerprint());
+                }
+            }
+        }
 
         Ok(Self {
             is_censor,
@@ -153,8 +165,7 @@ impl User {
 
     // Attempt to "connect" to the bridge, returns true if successful.
     // Note that this does not involve making a real connection to a
-    // real bridge. The function is async because the *censor* might
-    // submit a positive report during this function.
+    // real bridge.
     pub fn connect(&self, config: &Config, bridge: &mut Bridge, censor: &Censor) -> bool {
         if censor.blocks_bridge(config, &bridge.fingerprint) {
             if config.censor_totality == Full
@@ -254,21 +265,14 @@ impl User {
         &mut self,
         config: &Config,
         num_users_requesting_invites: u32,
-        num_censor_invites: u32,
         bridges: &mut HashMap<[u8; 20], Bridge>,
         censor: &mut Censor,
     ) -> Result<Vec<User>> {
         if self.is_censor {
             self.daily_tasks_censor(config, bridges, censor).await
         } else {
-            self.daily_tasks_non_censor(
-                config,
-                num_users_requesting_invites,
-                num_censor_invites,
-                bridges,
-                censor,
-            )
-            .await
+            self.daily_tasks_non_censor(config, num_users_requesting_invites, bridges, censor)
+                .await
         }
     }
 
@@ -280,7 +284,6 @@ impl User {
         &mut self,
         config: &Config,
         num_users_requesting_invites: u32,
-        num_censor_invites: u32,
         bridges: &mut HashMap<[u8; 20], Bridge>,
         censor: &mut Censor,
     ) -> Result<Vec<User>> {
@@ -518,10 +521,31 @@ impl User {
                 None => 0, // This is probably an error case that should not happen
             };
             let mut new_friends = Vec::<User>::new();
+
+            // Scale the probability of inviting a censor, based on the
+            // user's own trust level. We assume that users with
+            // more-trusted credentials are less likely to invite
+            // censors because they have more to lose.
+            let level_scale = match scalar_u32(&self.primary_cred.trust_level) {
+                // These numbers are fairly arbitrary.
+                Some(4) => 0.01,
+                Some(3) => 0.1,
+                Some(2) => 0.5,
+                _ => 1.0,
+            };
             for _i in 0..min(invitations, num_users_requesting_invites) {
                 if event_happens(config.prob_user_invites_friend) {
-                    // Invite non-censor friend
-                    match self.invite(&config, censor, false).await {
+                    // With some probability, the user is convinced to
+                    // invite a censor. We assume users with higher
+                    // trust levels will be more cautious with
+                    // invitations because they have more to lose.
+                    let invited_friend_is_censor = censor.is_active()
+                        && event_happens(config.prob_censor_gets_invite * level_scale);
+                    // Invite friend (who might be a censor)
+                    match self
+                        .invite(&config, bridges, censor, invited_friend_is_censor)
+                        .await
+                    {
                         Ok(friend) => {
                             // You really shouldn't push your friends,
                             // especially new ones whose boundaries you
@@ -535,22 +559,6 @@ impl User {
                 }
             }
 
-            // Invite censor users if applicable
-            let invitations = invitations - new_friends.len() as u32;
-            for _i in 0..min(invitations, num_censor_invites) {
-                if event_happens(config.prob_user_invites_friend) {
-                    // Invite non-censor friend
-                    match self.invite(&config, censor, true).await {
-                        Ok(friend) => {
-                            new_friends.push(friend);
-                        }
-                        Err(e) => {
-                            println!("{}", e);
-                        }
-                    }
-                }
-            }
-
             Ok(new_friends)
         } else {
             Ok(Vec::<User>::new())
@@ -641,8 +649,6 @@ impl User {
                 censor.learn_bridge(&fingerprint);
                 // Censor doesn't want new_cred yet
                 self.primary_cred = new_cred;
-            } else {
-                eprintln!("Censor failed to get new credential");
             }
         }
 
@@ -660,15 +666,13 @@ impl User {
             censor.learn_bridge(&fingerprint);
             // Censor doesn't want new_cred. User doesn't actually use
             // secondary_cred, so don't store it.
-        } else {
-            eprintln!("Censor failed to get new credential");
         }
 
         // Censor user invites as many censor friends as possible
         let invitations = scalar_u32(&self.primary_cred.invites_remaining).unwrap();
         let mut new_friends = Vec::<User>::new();
         for _ in 0..invitations {
-            match self.invite(&config, censor, true).await {
+            match self.invite(&config, bridges, censor, true).await {
                 Ok(friend) => {
                     new_friends.push(friend);
                 }