Reduce number of unnecessary censor users for efficiency
This commit is contained in:
parent
d03f86d4f6
commit
642827e228
|
@ -10,6 +10,7 @@ anyhow = "1.0"
|
|||
array-bytes = "6.2.0"
|
||||
bincode = "1"
|
||||
clap = { version = "4.4.14", features = ["derive"] }
|
||||
curve25519-dalek = { version = "4", default-features = false }
|
||||
hyper = { version = "0.14.28", features = ["full"] }
|
||||
lox_cli = { git = "https://git-crysp.uwaterloo.ca/vvecna/lox_cli.git", version = "0.1" }
|
||||
lox-library = { git = "https://gitlab.torproject.org/vecna/lox.git", version = "0.1.0" }
|
||||
|
|
|
@ -86,4 +86,14 @@ impl Bridge {
|
|||
self.real_connections = 0;
|
||||
self.total_connections = 0;
|
||||
}
|
||||
|
||||
// Has this bridge been distributed to a non-censor user?
|
||||
pub fn has_been_distributed(&self) -> bool {
|
||||
self.first_real_user > 0
|
||||
}
|
||||
|
||||
// Does Troll Patrol think this bridge is blocked?
|
||||
pub fn troll_patrol_blocked(&self) -> bool {
|
||||
self.first_detected_blocked > 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{bridge::Bridge, config::Config};
|
||||
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use lox_cli::{get_lox_pub, networking::Networking};
|
||||
use lox_library::{cred::Lox, scalar_u32};
|
||||
use rand::Rng;
|
||||
|
@ -24,6 +25,12 @@ pub struct Censor {
|
|||
// credentials we have for this bridge).
|
||||
pub lox_credentials: HashMap<[u8; 20], (Lox, u32)>,
|
||||
|
||||
// Map of buckets to count of censor users with that bucket. Note
|
||||
// that this is the count of users that have *ever* had that bucket,
|
||||
// so this variable should NOT be used to count the overall number
|
||||
// of censor agents.
|
||||
pub agents: HashMap<Scalar, u32>,
|
||||
|
||||
// If censor implements random blocking, this is the date when it
|
||||
// will start blocking all the bridges it knows.
|
||||
pub delay_date: u32,
|
||||
|
@ -52,6 +59,7 @@ impl Censor {
|
|||
start_date,
|
||||
known_bridges: HashSet::<[u8; 20]>::new(),
|
||||
lox_credentials: HashMap::<[u8; 20], (Lox, u32)>::new(),
|
||||
agents: HashMap::<Scalar, u32>::new(),
|
||||
delay_date: delay_date,
|
||||
partial_blocking_percent: partial_blocking_percent,
|
||||
}
|
||||
|
@ -116,6 +124,25 @@ impl Censor {
|
|||
}
|
||||
}
|
||||
|
||||
// Get the number of agents the censor has with a given bucket
|
||||
pub fn num_agents(&self, bucket: &Scalar) -> u32 {
|
||||
match self.agents.get(bucket) {
|
||||
Some(v) => *v,
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Add to the number of agents a censor has for a given bucket
|
||||
pub fn add_agent(&mut self, bucket: &Scalar) {
|
||||
self.agents.insert(
|
||||
*bucket,
|
||||
match self.agents.get(bucket) {
|
||||
Some(v) => *v + 1,
|
||||
None => 1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Censor sends a positive report for the given bridge. Returns true
|
||||
// if successful, false otherwise.
|
||||
pub async fn send_positive_report(&self, config: &Config, fingerprint: &[u8; 20]) -> bool {
|
||||
|
@ -158,11 +185,8 @@ impl Censor {
|
|||
{
|
||||
let bridge = bridges.get_mut(fingerprint).unwrap();
|
||||
|
||||
// A large number
|
||||
let num_connections = 30000;
|
||||
|
||||
// Make a bunch of connections to the bridge
|
||||
bridge.censor_flood(num_connections);
|
||||
bridge.censor_flood(config.censor_max_connections);
|
||||
|
||||
// If we have a lv3+ credential, submit a bunch of
|
||||
// positive reports
|
||||
|
@ -176,11 +200,9 @@ impl Censor {
|
|||
// detection algorithm. In practice, the censor
|
||||
// should submit as many reports as possible.
|
||||
let num_prs = if config.one_positive_report_per_cred {
|
||||
// *cred_count
|
||||
min(*cred_count, 25)
|
||||
min(*cred_count, config.censor_max_pr)
|
||||
} else {
|
||||
//30000
|
||||
25
|
||||
config.censor_max_pr
|
||||
};
|
||||
for _ in 0..num_prs {
|
||||
self.send_positive_report(config, &bridge.fingerprint).await;
|
||||
|
|
|
@ -10,6 +10,11 @@ pub struct Config {
|
|||
pub bootstrapping_period_duration: u32,
|
||||
// Define censor behavior
|
||||
pub censor_secrecy: censor::Secrecy,
|
||||
// The maximum number of connections for the censor to make to each
|
||||
// bridge
|
||||
pub censor_max_connections: u32,
|
||||
// The maximum number of positive reports for the censor to submit
|
||||
pub censor_max_pr: u32,
|
||||
pub censor_speed: censor::Speed,
|
||||
pub censor_event_duration: u32,
|
||||
pub censor_totality: censor::Totality,
|
||||
|
|
29
src/main.rs
29
src/main.rs
|
@ -43,6 +43,8 @@ pub struct Config {
|
|||
pub tp_test_port: u16,
|
||||
pub bootstrapping_period_duration: u32,
|
||||
pub censor_secrecy: censor::Secrecy,
|
||||
pub censor_max_connections: u32,
|
||||
pub censor_max_pr: u32,
|
||||
pub censor_speed: censor::Speed,
|
||||
pub censor_event_duration: u32,
|
||||
pub censor_totality: censor::Totality,
|
||||
|
@ -95,6 +97,8 @@ pub async fn main() {
|
|||
tp_net,
|
||||
bootstrapping_period_duration: config.bootstrapping_period_duration,
|
||||
censor_secrecy: config.censor_secrecy,
|
||||
censor_max_connections: config.censor_max_connections,
|
||||
censor_max_pr: config.censor_max_pr,
|
||||
censor_speed: config.censor_speed,
|
||||
censor_event_duration: config.censor_event_duration,
|
||||
censor_totality: config.censor_totality,
|
||||
|
@ -207,13 +211,23 @@ pub async fn main() {
|
|||
users.shuffle(&mut rng);
|
||||
|
||||
// Users do daily actions
|
||||
for user in &mut users {
|
||||
let invited_friends = user
|
||||
let mut i = 0;
|
||||
while i < users.len() {
|
||||
let user = users.get_mut(i).unwrap();
|
||||
if let Ok((mut invited_friends, remove_user)) = user
|
||||
.daily_tasks(&sconfig, &mut bridges, &mut censor, &mut invites)
|
||||
.await;
|
||||
.await
|
||||
{
|
||||
// We remove censor users once they stop serving a purpose
|
||||
if remove_user {
|
||||
// This removes the user and replaces them with the
|
||||
// last element of the vector, for efficiency. This
|
||||
// is fine to do because the users act in a random
|
||||
// order anyway.
|
||||
users.swap_remove(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if invited_friends.is_ok() {
|
||||
let mut invited_friends = invited_friends.unwrap();
|
||||
if invited_friends.len() > 0 {
|
||||
new_users.append(&mut invited_friends);
|
||||
}
|
||||
|
@ -228,6 +242,11 @@ pub async fn main() {
|
|||
count_users_cannot_connect += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate loop (note that we do not reach this if we remove
|
||||
// a user, so we'll get the replacement user at that same
|
||||
// index)
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Also count number of new users with/without connections
|
||||
|
|
267
src/user.rs
267
src/user.rs
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue