|
@@ -0,0 +1,172 @@
|
|
|
+use crate::{analysis::Analyzer, BridgeCountryInfo, BridgeInfoType};
|
|
|
+use lox_library::{
|
|
|
+ bridge_table::MAX_BRIDGES_PER_BUCKET,
|
|
|
+ proto::{
|
|
|
+ level_up::{LEVEL_INTERVAL, LEVEL_INVITATIONS, MAX_LEVEL},
|
|
|
+ trust_promotion::UNTRUSTED_INTERVAL,
|
|
|
+ },
|
|
|
+ OPENINV_K,
|
|
|
+};
|
|
|
+
|
|
|
+// Get max value from slice, or 0 if empty
|
|
|
+fn max(nums: &[u32]) -> u32 {
|
|
|
+ let mut max_num = 0;
|
|
|
+ for i in nums {
|
|
|
+ if *i > max_num {
|
|
|
+ max_num = *i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ max_num
|
|
|
+}
|
|
|
+
|
|
|
+// Max users expected on a Lox bridge based on how long it's been around
|
|
|
+fn max_users(days: u32) -> u32 {
|
|
|
+ if days <= UNTRUSTED_INTERVAL {
|
|
|
+ // k users for open-entry bucket
|
|
|
+ OPENINV_K
|
|
|
+ } else if days <= UNTRUSTED_INTERVAL + LEVEL_INTERVAL[1] {
|
|
|
+ // k users per bridge x 3 bridges in invite-only bucket
|
|
|
+ OPENINV_K * MAX_BRIDGES_PER_BUCKET as u32
|
|
|
+ } else if days <= UNTRUSTED_INTERVAL + LEVEL_INTERVAL[2] {
|
|
|
+ // suppose users have used all their invitations
|
|
|
+ OPENINV_K * MAX_BRIDGES_PER_BUCKET as u32 * (1 + LEVEL_INVITATIONS[1])
|
|
|
+ } else {
|
|
|
+ // stop counting here, just say it's a lot
|
|
|
+ 100
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// Maximum number of negative reports without considering the bridge blocked
|
|
|
+fn max_negative_reports(days: u32) -> u32 {
|
|
|
+ // How many users do we expect to use this bridge?
|
|
|
+ let max_users = max_users(days);
|
|
|
+
|
|
|
+ // Based on that, allow this many negative reports
|
|
|
+ if max_users <= 10 {
|
|
|
+ 0
|
|
|
+ } else if max_users <= 30 {
|
|
|
+ 5
|
|
|
+ } else {
|
|
|
+ 10
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+fn too_few_bridge_ips(
|
|
|
+ days: u32,
|
|
|
+ max_bridge_ips: u32,
|
|
|
+ bridge_ips_today: u32,
|
|
|
+ positive_reports_today: u32,
|
|
|
+) -> bool {
|
|
|
+ // How many users do we expect to use this bridge?
|
|
|
+ let max_users = max_users(days);
|
|
|
+
|
|
|
+ // Based on that, expect this many bridge ips
|
|
|
+ let min_bip = if max_users <= 16 {
|
|
|
+ // expect 10
|
|
|
+ 8
|
|
|
+ } else if max_users <= 40 {
|
|
|
+ // expect 30
|
|
|
+ 24
|
|
|
+ } else if max_users <= 96 {
|
|
|
+ // expect 90
|
|
|
+ 32
|
|
|
+ } else {
|
|
|
+ // also 32, but it could be something else
|
|
|
+ 32
|
|
|
+ };
|
|
|
+
|
|
|
+ // If we normally see low usage, halve the minimum
|
|
|
+ let min_bip = if max_bridge_ips <= 8 {
|
|
|
+ // If we have 1-8 users, allow the number to be 0
|
|
|
+ 0
|
|
|
+ } else if max_bridge_ips <= max_users / 2 {
|
|
|
+ min_bip / 2
|
|
|
+ } else {
|
|
|
+ min_bip
|
|
|
+ };
|
|
|
+
|
|
|
+ // If we see positive reports from trusted users, double the minimum
|
|
|
+ let min_bip = if positive_reports_today > 0 {
|
|
|
+ min_bip * 2
|
|
|
+ } else {
|
|
|
+ min_bip
|
|
|
+ };
|
|
|
+
|
|
|
+ bridge_ips_today < min_bip
|
|
|
+}
|
|
|
+
|
|
|
+/// Analyzer for Lox bridges based on how long they've been active
|
|
|
+pub struct LoxAnalyzer {}
|
|
|
+
|
|
|
+impl Analyzer for LoxAnalyzer {
|
|
|
+ // At this stage, we expect 0-10 users, so 0, 8, or 16 bridge-ips
|
|
|
+ fn stage_one(
|
|
|
+ &self,
|
|
|
+ age: u32,
|
|
|
+ _confidence: f64,
|
|
|
+ bridge_ips: &[u32],
|
|
|
+ bridge_ips_today: u32,
|
|
|
+ _negative_reports: &[u32],
|
|
|
+ negative_reports_today: u32,
|
|
|
+ ) -> bool {
|
|
|
+ // Get max bridge_ips we've seen
|
|
|
+ let max_bridge_ips = max(bridge_ips);
|
|
|
+
|
|
|
+ if too_few_bridge_ips(age, max_bridge_ips, bridge_ips_today, 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we have more negative reports than expected, consider the
|
|
|
+ // bridge blocked
|
|
|
+ negative_reports_today > max_negative_reports(age)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn stage_two(
|
|
|
+ &self,
|
|
|
+ age: u32,
|
|
|
+ _confidence: f64,
|
|
|
+ bridge_ips: &[u32],
|
|
|
+ bridge_ips_today: u32,
|
|
|
+ _negative_reports: &[u32],
|
|
|
+ negative_reports_today: u32,
|
|
|
+ ) -> bool {
|
|
|
+ // Get max bridge_ips we've seen
|
|
|
+ let max_bridge_ips = max(bridge_ips);
|
|
|
+
|
|
|
+ if too_few_bridge_ips(age, max_bridge_ips, bridge_ips_today, 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we have more negative reports than expected, consider the
|
|
|
+ // bridge blocked
|
|
|
+ negative_reports_today > max_negative_reports(age)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn stage_three(
|
|
|
+ &self,
|
|
|
+ age: u32,
|
|
|
+ _confidence: f64,
|
|
|
+ bridge_ips: &[u32],
|
|
|
+ bridge_ips_today: u32,
|
|
|
+ _negative_reports: &[u32],
|
|
|
+ negative_reports_today: u32,
|
|
|
+ _positive_reports: &[u32],
|
|
|
+ positive_reports_today: u32,
|
|
|
+ ) -> bool {
|
|
|
+ // Get max bridge_ips we've seen
|
|
|
+ let max_bridge_ips = max(bridge_ips);
|
|
|
+
|
|
|
+ if too_few_bridge_ips(
|
|
|
+ age,
|
|
|
+ max_bridge_ips,
|
|
|
+ bridge_ips_today,
|
|
|
+ positive_reports_today,
|
|
|
+ ) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If we have more negative reports than expected, consider the
|
|
|
+ // bridge blocked
|
|
|
+ negative_reports_today > max_negative_reports(age)
|
|
|
+ }
|
|
|
+}
|