Browse Source

Make censors less likely to get invites from trusted users

Vecna 10 months ago
parent
commit
12882938e3
3 changed files with 91 additions and 21 deletions
  1. 50 5
      src/bin/simulation.rs
  2. 3 0
      src/simulation/config.rs
  3. 38 16
      src/simulation/user.rs

+ 50 - 5
src/bin/simulation.rs

@@ -57,6 +57,7 @@ pub struct Config {
     // We start with this many level 4 users
     pub num_initial_trusted_users: u32,
     pub one_positive_report_per_cred: bool,
+    pub prob_censor_gets_invite: f64,
     pub prob_connection_fails: f64,
     pub prob_user_invites_friend: f64,
     pub prob_user_is_censor: f64,
@@ -102,6 +103,7 @@ pub async fn main() {
         censor_partial_blocking_percent: config.censor_partial_blocking_percent,
         country: config.country,
         one_positive_report_per_cred: config.one_positive_report_per_cred,
+        prob_censor_gets_invite: config.prob_censor_gets_invite,
         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,
@@ -256,6 +258,30 @@ pub async fn main() {
         let mut 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
@@ -267,6 +293,7 @@ pub async fn main() {
                 .daily_tasks(
                     &sconfig,
                     num_users_requesting_invites,
+                    num_censor_invitations,
                     &mut bridges,
                     &mut censor,
                 )
@@ -276,10 +303,17 @@ pub async fn main() {
                 let mut invited_friends = invited_friends.unwrap();
                 if invited_friends.len() > 0 {
                     if !user.is_censor {
-                        // Users should never invite more friends than
-                        // need invitations, so this should never become
-                        // negative
-                        num_users_requesting_invites -= invited_friends.len() as u32;
+                        // 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
@@ -294,7 +328,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).await;
+            let user = User::new(&sconfig, false).await;
             if user.is_ok() {
                 users.push(user.unwrap());
             } else {
@@ -302,6 +336,17 @@ 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
         censor.end_of_day_tasks(&sconfig, &mut bridges).await;
 

+ 3 - 0
src/simulation/config.rs

@@ -17,6 +17,9 @@ pub struct Config {
     // share information with each other.
     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.
+    pub prob_censor_gets_invite: f64,
     // Probability that a connection randomly fails, even though censor
     // does not block the bridge
     pub prob_connection_fails: f64,

+ 38 - 16
src/simulation/user.rs

@@ -45,7 +45,7 @@ pub struct User {
 }
 
 impl User {
-    pub async fn new(config: &Config) -> Result<Self, Error> {
+    pub async fn new(config: &Config, is_censor: bool) -> Result<Self, Error> {
         let cred = get_lox_credential(
             &config.la_net,
             &get_open_invitation(&config.la_net).await?,
@@ -54,9 +54,6 @@ impl User {
         .await?
         .0;
 
-        // Probabilistically decide whether this user cooperates with a censor
-        let is_censor = event_happens(config.prob_user_is_censor);
-
         // Probabilistically decide whether this user submits reports
         let submits_reports = if is_censor {
             false
@@ -96,7 +93,12 @@ impl User {
     }
 
     // TODO: This should probably return an actual error type
-    pub async fn invite(&mut self, config: &Config, censor: &mut Censor) -> Result<Self, Error> {
+    pub async fn invite(
+        &mut self,
+        config: &Config,
+        censor: &mut Censor,
+        invited_user_is_censor: bool,
+    ) -> Result<Self, Error> {
         let etable = get_reachability_credential(&config.la_net).await?;
         let (new_cred, invite) = issue_invite(
             &config.la_net,
@@ -127,13 +129,8 @@ impl User {
         .await?
         .0;
 
-        // If the inviting user is a censor, the invitee will also be a
-        // censor. If not, probabilistically decide.
-        let is_censor = if self.is_censor {
-            true
-        } else {
-            event_happens(config.prob_user_is_censor)
-        };
+        // 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 {
@@ -254,14 +251,21 @@ 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>, Error> {
         if self.is_censor {
             self.daily_tasks_censor(config, bridges, censor).await
         } else {
-            self.daily_tasks_non_censor(config, num_users_requesting_invites, bridges, censor)
-                .await
+            self.daily_tasks_non_censor(
+                config,
+                num_users_requesting_invites,
+                num_censor_invites,
+                bridges,
+                censor,
+            )
+            .await
         }
     }
 
@@ -273,6 +277,7 @@ 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>, Error> {
@@ -512,7 +517,8 @@ impl User {
             let mut new_friends = Vec::<User>::new();
             for _i in 0..min(invitations, num_users_requesting_invites) {
                 if event_happens(config.prob_user_invites_friend) {
-                    match self.invite(&config, censor).await {
+                    // Invite non-censor friend
+                    match self.invite(&config, censor, false).await {
                         Ok(friend) => {
                             // You really shouldn't push your friends,
                             // especially new ones whose boundaries you
@@ -526,6 +532,22 @@ 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())
@@ -643,7 +665,7 @@ impl User {
         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).await {
+            match self.invite(&config, censor, true).await {
                 Ok(friend) => {
                     new_friends.push(friend);
                 }