|
@@ -46,6 +46,64 @@ pub fn give_bucket_to_censor(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// Check if bucket is blocked, according to the LA (regardless of
|
|
|
+// whether the censor actually blocks the bridges)
|
|
|
+pub fn bucket_blocked_lox(bucket: &[BridgeLine], bridges: &HashMap<[u8; 20], Bridge>) -> bool {
|
|
|
+ // Count number of non-default bridges (either 1 or 3)
|
|
|
+ let mut num_real_bridges = 0;
|
|
|
+ let mut num_blocked_bridges = 0;
|
|
|
+
|
|
|
+ for bridge in bucket {
|
|
|
+ if *bridge != BridgeLine::default() {
|
|
|
+ match bridges.get(&bridge.get_hashed_fingerprint()) {
|
|
|
+ Some(b) => {
|
|
|
+ num_real_bridges += 1;
|
|
|
+ if b.troll_patrol_blocked() {
|
|
|
+ num_blocked_bridges += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ None => {
|
|
|
+ // Something went wrong, I guess
|
|
|
+ println!(
|
|
|
+ "Tried to check if bridge was blocked before it was added to simulation"
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Return true if open-entry bucket with a blocked bridge or
|
|
|
+ // invite-only bucket with 2+ blocked bridges
|
|
|
+ num_real_bridges == 1 && num_blocked_bridges == 1
|
|
|
+ || num_real_bridges == 3 && num_blocked_bridges >= 2
|
|
|
+}
|
|
|
+
|
|
|
+// Check if bucket contains a bridge that has ever been distributed to a
|
|
|
+// real user
|
|
|
+pub fn bucket_has_been_distributed(
|
|
|
+ bucket: &[BridgeLine],
|
|
|
+ bridges: &HashMap<[u8; 20], Bridge>,
|
|
|
+) -> bool {
|
|
|
+ for bridge in bucket {
|
|
|
+ if *bridge != BridgeLine::default() {
|
|
|
+ match bridges.get(&bridge.get_hashed_fingerprint()) {
|
|
|
+ Some(b) => {
|
|
|
+ if b.has_been_distributed() {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ None => {
|
|
|
+ // Something went wrong, I guess
|
|
|
+ println!(
|
|
|
+ "Tried to check if bridge had been distributed before it was added to simulation"
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ false
|
|
|
+}
|
|
|
+
|
|
|
pub struct User {
|
|
|
// Does this user cooperate with a censor?
|
|
|
pub is_censor: bool,
|
|
@@ -123,6 +181,30 @@ impl User {
|
|
|
if is_censor {
|
|
|
// Give bridges to censor
|
|
|
give_bucket_to_censor(&bucket, bridges, censor);
|
|
|
+
|
|
|
+ // Various conditions to avoid creating censor users if we don't need to
|
|
|
+
|
|
|
+ // If the bucket is already marked blocked, don't create this user
|
|
|
+ if bucket_blocked_lox(&bucket, bridges) {
|
|
|
+ return Err(anyhow!("Bucket blocked, don't create new censor user"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // If the bucket has never been distributed to a real user, don't create this user
|
|
|
+ if !bucket_has_been_distributed(&bucket, bridges) {
|
|
|
+ return Err(anyhow!(
|
|
|
+ "Bucket never distributed to a real user, don't create new censor user"
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we already have enough agents with this bucket, don't create this user
|
|
|
+ if censor.num_agents(&result.primary_cred.bucket) > config.censor_max_pr {
|
|
|
+ return Err(anyhow!(
|
|
|
+ "We already have enough censor users with this bucket"
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we haven't returned yet, add this user to censor's list
|
|
|
+ censor.add_agent(&result.primary_cred.bucket);
|
|
|
} else {
|
|
|
// Test bridges to see if they work
|
|
|
let (s, f) = result.test_bridges(&bucket, config, bridges, censor);
|
|
@@ -229,6 +311,23 @@ impl User {
|
|
|
|
|
|
if is_censor {
|
|
|
give_bucket_to_censor(&bucket, bridges, censor);
|
|
|
+
|
|
|
+ // Various conditions to avoid creating censor users unnecessarily
|
|
|
+
|
|
|
+ // If the bucket is already marked blocked, don't create this user
|
|
|
+ if bucket_blocked_lox(&bucket, bridges) {
|
|
|
+ return Err(anyhow!("Bucket blocked, don't create new censor user"));
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we already have enough agents with this bucket, don't create this user
|
|
|
+ if censor.num_agents(&result.primary_cred.bucket) > config.censor_max_pr {
|
|
|
+ return Err(anyhow!(
|
|
|
+ "We already have enough censor users with this bucket"
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we haven't returned yet, add this user to censor's list
|
|
|
+ censor.add_agent(&result.primary_cred.bucket);
|
|
|
} else {
|
|
|
let (s, f) = result.test_bridges(&bucket, config, bridges, censor);
|
|
|
|
|
@@ -411,12 +510,17 @@ impl User {
|
|
|
bridges: &mut HashMap<[u8; 20], Bridge>,
|
|
|
censor: &mut Censor,
|
|
|
invites: &mut Vec<Invitation>,
|
|
|
- ) -> Result<Vec<User>> {
|
|
|
+ ) -> Result<(Vec<User>, bool)> {
|
|
|
if self.is_censor {
|
|
|
self.daily_tasks_censor(config, bridges, censor).await
|
|
|
} else {
|
|
|
- self.daily_tasks_non_censor(config, bridges, censor, invites)
|
|
|
+ match self
|
|
|
+ .daily_tasks_non_censor(config, bridges, censor, invites)
|
|
|
.await
|
|
|
+ {
|
|
|
+ Ok(users) => Ok((users, false)),
|
|
|
+ Err(e) => Err(e),
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -756,13 +860,15 @@ impl User {
|
|
|
}
|
|
|
|
|
|
// User cooperates with censor and performs daily tasks to try to
|
|
|
- // learn more bridges.
|
|
|
+ // learn more bridges. Returns a vector of newly invited users
|
|
|
+ // and a boolean indicating whether this censor user should be
|
|
|
+ // removed from the global user set (for efficiency).
|
|
|
pub async fn daily_tasks_censor(
|
|
|
&mut self,
|
|
|
config: &Config,
|
|
|
bridges: &mut HashMap<[u8; 20], Bridge>,
|
|
|
censor: &mut Censor,
|
|
|
- ) -> Result<Vec<User>> {
|
|
|
+ ) -> Result<(Vec<User>, bool)> {
|
|
|
// Download bucket to see if bridge is still reachable and if we
|
|
|
// have any new bridges
|
|
|
let (bucket, reachcred) = get_bucket(&config.la_net, &self.primary_cred).await?;
|
|
@@ -770,14 +876,52 @@ impl User {
|
|
|
|
|
|
// Make sure each bridge is in global bridges set and known by
|
|
|
// censor
|
|
|
- for bridgeline in bucket {
|
|
|
- if bridgeline != BridgeLine::default() {
|
|
|
- let fingerprint = bridgeline.get_hashed_fingerprint();
|
|
|
- if !bridges.contains_key(&fingerprint) {
|
|
|
- let bridge = Bridge::from_bridge_line(&bridgeline);
|
|
|
- bridges.insert(fingerprint, bridge);
|
|
|
+ give_bucket_to_censor(&bucket, bridges, censor);
|
|
|
+
|
|
|
+ // If Lox has marked the bridge blocked, migrate if possible
|
|
|
+ if bucket_blocked_lox(&bucket, bridges) {
|
|
|
+ // If we can migrate, migrate
|
|
|
+ if level >= MIN_TRUST_LEVEL {
|
|
|
+ if let Ok(migcred) = check_blockage(
|
|
|
+ &config.la_net,
|
|
|
+ &self.primary_cred,
|
|
|
+ get_lox_pub(&config.la_pubkeys),
|
|
|
+ )
|
|
|
+ .await
|
|
|
+ {
|
|
|
+ if let Ok(cred) = blockage_migration(
|
|
|
+ &config.la_net,
|
|
|
+ &self.primary_cred,
|
|
|
+ &migcred,
|
|
|
+ get_lox_pub(&config.la_pubkeys),
|
|
|
+ get_migration_pub(&config.la_pubkeys),
|
|
|
+ )
|
|
|
+ .await
|
|
|
+ {
|
|
|
+ // Successfully migrated!
|
|
|
+
|
|
|
+ // Update credential
|
|
|
+ self.primary_cred = cred;
|
|
|
+
|
|
|
+ // You can't migrate to level 3 or 4, so the
|
|
|
+ // censor doesn't want this new credential
|
|
|
+
|
|
|
+ // Download bucket to see if bridge is still
|
|
|
+ // reachable and if we have any new bridges
|
|
|
+ let (bucket, _reachcred) =
|
|
|
+ get_bucket(&config.la_net, &self.primary_cred).await?;
|
|
|
+
|
|
|
+ // Give new bucket to censor
|
|
|
+ give_bucket_to_censor(&bucket, bridges, censor);
|
|
|
+
|
|
|
+ // Add this user to censor's list
|
|
|
+ censor.add_agent(&self.primary_cred.bucket);
|
|
|
+ }
|
|
|
}
|
|
|
- censor.learn_bridge(&fingerprint);
|
|
|
+ } else {
|
|
|
+ // We can't migrate, and we can't level up or anything.
|
|
|
+ // Mark this user for deletion.
|
|
|
+ return Ok((Vec::<User>::new(), true));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -788,7 +932,7 @@ impl User {
|
|
|
&& eligible_for_level_up(&config.la_net, &self.primary_cred).await
|
|
|
{
|
|
|
let new_cred = if level == 0 {
|
|
|
- trust_migration(
|
|
|
+ let nc = trust_migration(
|
|
|
&config.la_net,
|
|
|
&self.primary_cred,
|
|
|
&trust_promotion(
|
|
@@ -800,7 +944,13 @@ impl User {
|
|
|
get_lox_pub(&config.la_pubkeys),
|
|
|
get_migration_pub(&config.la_pubkeys),
|
|
|
)
|
|
|
- .await?
|
|
|
+ .await?;
|
|
|
+
|
|
|
+ // We now have a new bucket value, so add the user to censor's list
|
|
|
+ censor.add_agent(&nc.bucket);
|
|
|
+
|
|
|
+ // New credential as new_cred
|
|
|
+ nc
|
|
|
} else {
|
|
|
level_up(
|
|
|
&config.la_net,
|
|
@@ -834,97 +984,8 @@ impl User {
|
|
|
censor.give_lox_cred(&fingerprint, &self.primary_cred, false);
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
- // LA has identified this bucket as blocked. This change
|
|
|
- // will not be reverted, so either migrate or replace the
|
|
|
- // primary credential with a new level 0 credential and work
|
|
|
- // on gaining trust for that one.
|
|
|
-
|
|
|
- // Migrate if able
|
|
|
- if level >= MIN_TRUST_LEVEL {
|
|
|
- if let Ok(migcred) = check_blockage(
|
|
|
- &config.la_net,
|
|
|
- &self.primary_cred,
|
|
|
- get_lox_pub(&config.la_pubkeys),
|
|
|
- )
|
|
|
- .await
|
|
|
- {
|
|
|
- if let Ok(cred) = blockage_migration(
|
|
|
- &config.la_net,
|
|
|
- &self.primary_cred,
|
|
|
- &migcred,
|
|
|
- get_lox_pub(&config.la_pubkeys),
|
|
|
- get_migration_pub(&config.la_pubkeys),
|
|
|
- )
|
|
|
- .await
|
|
|
- {
|
|
|
- self.primary_cred = cred;
|
|
|
-
|
|
|
- // You can't migrate to level 3 or 4, so the
|
|
|
- // censor doesn't want this new credential
|
|
|
-
|
|
|
- // Download bucket to see if bridge is still
|
|
|
- // reachable and if we have any new bridges
|
|
|
- let (bucket, _reachcred) =
|
|
|
- get_bucket(&config.la_net, &self.primary_cred).await?;
|
|
|
-
|
|
|
- // Make sure each bridge is in global bridges
|
|
|
- // set and known by censor
|
|
|
- for bridgeline in bucket {
|
|
|
- if bridgeline != BridgeLine::default() {
|
|
|
- let fingerprint = bridgeline.get_hashed_fingerprint();
|
|
|
- if !bridges.contains_key(&fingerprint) {
|
|
|
- let bridge = Bridge::from_bridge_line(&bridgeline);
|
|
|
- bridges.insert(fingerprint, bridge);
|
|
|
- }
|
|
|
- censor.learn_bridge(&fingerprint);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Removing this case for efficiency. If the censor is in
|
|
|
- // play, we just assume it wins the open-entry game and stop
|
|
|
- // distributing open-entry invites altogether.
|
|
|
- /*
|
|
|
- } else {
|
|
|
- // If unable to migrate, try to get a new open-entry
|
|
|
- // credential and start over
|
|
|
- let res = Self::get_new_credential(&config).await;
|
|
|
- if res.is_ok() {
|
|
|
- let (new_cred, bl) = res.unwrap();
|
|
|
- let fingerprint = bl.get_hashed_fingerprint();
|
|
|
- if !bridges.contains_key(&fingerprint) {
|
|
|
- let bridge = Bridge::from_bridge_line(&bl);
|
|
|
- bridges.insert(fingerprint, bridge);
|
|
|
- }
|
|
|
- censor.learn_bridge(&fingerprint);
|
|
|
- // Censor doesn't want new_cred yet
|
|
|
- self.primary_cred = new_cred;
|
|
|
- }
|
|
|
- */
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
- // Also removing this case for efficiency.
|
|
|
- /*
|
|
|
- // Separately from primary credential, censor user requests a
|
|
|
- // new secondary credential each day just to block the
|
|
|
- // open-entry bridges. This is stored but not reused.
|
|
|
- let res = Self::get_new_credential(&config).await;
|
|
|
- if res.is_ok() {
|
|
|
- let (_new_cred, bl) = res.unwrap();
|
|
|
- let fingerprint = bl.get_hashed_fingerprint();
|
|
|
- if !bridges.contains_key(&fingerprint) {
|
|
|
- let bridge = Bridge::from_bridge_line(&bl);
|
|
|
- bridges.insert(fingerprint, bridge);
|
|
|
- }
|
|
|
- censor.learn_bridge(&fingerprint);
|
|
|
- // Censor doesn't want new_cred. User doesn't actually use
|
|
|
- // secondary_cred, so don't store it.
|
|
|
- }
|
|
|
- */
|
|
|
-
|
|
|
// 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();
|
|
@@ -945,6 +1006,6 @@ impl User {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- Ok(new_friends)
|
|
|
+ Ok((new_friends, false))
|
|
|
}
|
|
|
}
|