Some basic thresholds

This commit is contained in:
Vecna 2024-07-03 13:35:20 -04:00
parent ed37bf1874
commit 264e3824d6
2 changed files with 119 additions and 83 deletions

View File

@ -1,12 +1,6 @@
use crate::analysis::Analyzer; use crate::analysis::Analyzer;
use lox_library::{ use lox_library::proto::{level_up::LEVEL_INTERVAL, trust_promotion::UNTRUSTED_INTERVAL};
bridge_table::MAX_BRIDGES_PER_BUCKET, use std::cmp::min;
proto::{
level_up::{LEVEL_INTERVAL, LEVEL_INVITATIONS},
trust_promotion::UNTRUSTED_INTERVAL,
},
OPENINV_K,
};
// Get max value from slice, or 0 if empty // Get max value from slice, or 0 if empty
fn max(nums: &[u32]) -> u32 { fn max(nums: &[u32]) -> u32 {
@ -19,73 +13,20 @@ fn max(nums: &[u32]) -> u32 {
max_num max_num
} }
// Max users expected on a Lox bridge based on how long it's been around // Safe u32 subtraction: x - y or 0 if y > x
fn max_users(days: u32) -> u32 { fn ssub(x: u32, y: u32) -> u32 {
if days <= UNTRUSTED_INTERVAL { if y > x {
// 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 {
1
} 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
0 0
} else if max_users <= 40 {
// expect 30, possibly as few as 21
0
} else if max_users <= 96 {
// expect 90
8
} else { } else {
16 x - y
}; }
// If we see positive reports from trusted users, halve 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 /// Analyzer for Lox bridges based on how long they've been active
pub struct LoxAnalyzer {} pub struct LoxAnalyzer {
// simple variable threshold from 0-4 or something
harshness: u32,
}
impl Analyzer for LoxAnalyzer { impl Analyzer for LoxAnalyzer {
// At this stage, we expect 0-10 users, so 0, 8, or 16 bridge-ips // At this stage, we expect 0-10 users, so 0, 8, or 16 bridge-ips
@ -101,13 +42,13 @@ impl Analyzer for LoxAnalyzer {
// Get max bridge_ips we've seen // Get max bridge_ips we've seen
let max_bridge_ips = max(bridge_ips); let max_bridge_ips = max(bridge_ips);
if too_few_bridge_ips(age, max_bridge_ips, bridge_ips_today, 0) { if self.too_few_bridge_ips(age, max_bridge_ips, bridge_ips_today, 0) {
return true; return true;
} }
// If we have more negative reports than expected, consider the // If we have too many negative reports, consider the bridge
// bridge blocked // blocked
negative_reports_today > max_negative_reports(age) self.too_many_negative_reports(negative_reports_today)
} }
fn stage_two( fn stage_two(
@ -122,13 +63,13 @@ impl Analyzer for LoxAnalyzer {
// Get max bridge_ips we've seen // Get max bridge_ips we've seen
let max_bridge_ips = max(bridge_ips); let max_bridge_ips = max(bridge_ips);
if too_few_bridge_ips(age, max_bridge_ips, bridge_ips_today, 0) { if self.too_few_bridge_ips(age, max_bridge_ips, bridge_ips_today, 0) {
return true; return true;
} }
// If we have more negative reports than expected, consider the // If we have too many negative reports, consider the bridge
// bridge blocked // blocked
negative_reports_today > max_negative_reports(age) self.too_many_negative_reports(negative_reports_today)
} }
fn stage_three( fn stage_three(
@ -145,7 +86,7 @@ impl Analyzer for LoxAnalyzer {
// Get max bridge_ips we've seen // Get max bridge_ips we've seen
let max_bridge_ips = max(bridge_ips); let max_bridge_ips = max(bridge_ips);
if too_few_bridge_ips( if self.too_few_bridge_ips(
age, age,
max_bridge_ips, max_bridge_ips,
bridge_ips_today, bridge_ips_today,
@ -154,8 +95,102 @@ impl Analyzer for LoxAnalyzer {
return true; return true;
} }
// If we have more negative reports than expected, consider the // If we have too many negative reports, consider the bridge
// bridge blocked // blocked
negative_reports_today > max_negative_reports(age) self.too_many_negative_reports(negative_reports_today)
}
}
impl LoxAnalyzer {
pub fn new(harshness: u32) -> Self {
Self { harshness }
}
// Maximum number of negative reports without considering the bridge blocked
fn too_many_negative_reports(&self, negative_reports_today: u32) -> bool {
// If we have more negative reports than 4 - harshness
negative_reports_today > 4 - self.harshness
// harshness: 4, blocked if 1 negative report
// harshness: 3, blocked if 2 negative reports
// harshness: 2, blocked if 3 negative reports
// harshness: 1, blocked if 4 negative reports
// harshness: 0, blocked if 5 negative reports
}
// Based on the age of the bridge and the max bridge-ips observed, do we
// have too few today?
fn too_few_bridge_ips(
&self,
days: u32,
max_bridge_ips: u32,
bridge_ips_today: u32,
positive_reports_today: u32,
) -> bool {
if days <= UNTRUSTED_INTERVAL {
// We expect 0-10 users
if self.harshness == 4 {
// In the most extreme case, mark any bridge with 0 connections
bridge_ips_today == 0
} else if self.harshness >= 2 {
// With medium harshness, mark any bridge that has had
// 9+ connections and now sees 0
max_bridge_ips > 8 && bridge_ips_today == 0
} else {
// With low harshness, we cannot make a judgement from
// bridge stats
false
}
} else if days <= UNTRUSTED_INTERVAL + LEVEL_INTERVAL[1] {
// We expect 1-30 users
let threshold = min(max_bridge_ips, 32);
// If we're at least 4 - self.harshness bins below the
// maximum value we've seen or 32 if we've seen a lot of
// connections, consider the bridge blocked.
bridge_ips_today < ssub(threshold, (4 - self.harshness) * 8)
// Examples:
// max 64 connections, harshness = 4
// Return true if today's count is less than 32
// max 32 connections, harshness = 4
// Return true if today's count is less than 32
// max 32 connections, harshness = 3
// Return true if today's count is less than 24
// max 32 connections, harshness = 2
// Return true if today's count is less than 16
// max 8 connections, harshness = 4
// Return true if today's count is less than 8
// max 8 connections, harshness = 3
// Return false
} else {
// Similar, but consider positive reports as well
let threshold = min(max_bridge_ips, 32);
// We allow positive reports to increase the threshold for
// considering the bridge blocked. 8 positive reports
// (rounded up) remove 1 level reduction from the threshold
let threshold = ssub(
threshold,
(4 - self.harshness + (positive_reports_today + 7) / 8) * 8,
);
// For example, suppose we've seen 32+ connections.
//
// If we have harshness 4, we mark the bridge blocked if it
// has fewer than 32 connections.
//
// If we have harshness 4 but have seen 1-7 positive
// reports, we mark the bridge blocked only if it has fewer
// than 24 connections.
//
// 25 positive reports reduce all thresholds to 0, meaning
// bridges will never be marked blocked from bridge stats
// if the adversary can submit 25 positive reports.
// Consider the bridge blocked if we have too few connections
bridge_ips_today < threshold
}
} }
} }

View File

@ -103,7 +103,8 @@ async fn update_daily_info(
update_positive_reports(db, distributors).await; update_positive_reports(db, distributors).await;
let new_blockages = guess_blockages( let new_blockages = guess_blockages(
db, db,
&lox_analysis::LoxAnalyzer {}, // Using max_threshold for convenience
&lox_analysis::LoxAnalyzer::new(max_threshold),
confidence, confidence,
min_historical_days, min_historical_days,
max_historical_days, max_historical_days,