Compare commits
No commits in common. "12882938e3b554ff3c61b8d015bdb6e8d6783a20" and "3469fa67a4e991e1f465115c2326197677f40baa" have entirely different histories.
12882938e3
...
3469fa67a4
|
@ -57,12 +57,10 @@ pub struct Config {
|
||||||
// We start with this many level 4 users
|
// We start with this many level 4 users
|
||||||
pub num_initial_trusted_users: u32,
|
pub num_initial_trusted_users: u32,
|
||||||
pub one_positive_report_per_cred: bool,
|
pub one_positive_report_per_cred: bool,
|
||||||
pub prob_censor_gets_invite: f64,
|
|
||||||
pub prob_connection_fails: f64,
|
pub prob_connection_fails: f64,
|
||||||
pub prob_user_invites_friend: f64,
|
pub prob_user_invites_friend: f64,
|
||||||
pub prob_user_is_censor: f64,
|
pub prob_user_is_censor: f64,
|
||||||
pub prob_user_submits_reports: f64,
|
pub prob_user_submits_reports: f64,
|
||||||
pub prob_user_treats_throttling_as_blocking: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -103,12 +101,10 @@ pub async fn main() {
|
||||||
censor_partial_blocking_percent: config.censor_partial_blocking_percent,
|
censor_partial_blocking_percent: config.censor_partial_blocking_percent,
|
||||||
country: config.country,
|
country: config.country,
|
||||||
one_positive_report_per_cred: config.one_positive_report_per_cred,
|
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_connection_fails: config.prob_connection_fails,
|
||||||
prob_user_invites_friend: config.prob_user_invites_friend,
|
prob_user_invites_friend: config.prob_user_invites_friend,
|
||||||
prob_user_is_censor: config.prob_user_is_censor,
|
prob_user_is_censor: config.prob_user_is_censor,
|
||||||
prob_user_submits_reports: config.prob_user_submits_reports,
|
prob_user_submits_reports: config.prob_user_submits_reports,
|
||||||
prob_user_treats_throttling_as_blocking: config.prob_user_treats_throttling_as_blocking,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
@ -201,6 +197,10 @@ pub async fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Have Troll Patrol run its update process so we have a negative
|
||||||
|
// report key for tomorrow
|
||||||
|
tp_net_test.request("/update".to_string(), vec![]).await;
|
||||||
|
|
||||||
// Advance LA's time to tomorrow
|
// Advance LA's time to tomorrow
|
||||||
la_net_test
|
la_net_test
|
||||||
.request(
|
.request(
|
||||||
|
@ -232,12 +232,12 @@ pub async fn main() {
|
||||||
for day in 1..=config.num_days {
|
for day in 1..=config.num_days {
|
||||||
println!("Starting day {} of the simulation", day);
|
println!("Starting day {} of the simulation", day);
|
||||||
println!(
|
println!(
|
||||||
" We have {} users and {} bridges",
|
"We have {} users and {} bridges",
|
||||||
users.len(),
|
users.len(),
|
||||||
bridges.len()
|
bridges.len()
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
" The censor has learned {} bridges",
|
"The censor has learned {} bridges",
|
||||||
censor.known_bridges.len()
|
censor.known_bridges.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -258,30 +258,6 @@ pub async fn main() {
|
||||||
let mut num_users_requesting_invites: u32 =
|
let mut num_users_requesting_invites: u32 =
|
||||||
rng.gen_range(config.min_new_users_per_day..=config.max_new_users_per_day);
|
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();
|
let mut new_users = Vec::<User>::new();
|
||||||
|
|
||||||
// Shuffle users so they act in a random order
|
// Shuffle users so they act in a random order
|
||||||
|
@ -293,7 +269,6 @@ pub async fn main() {
|
||||||
.daily_tasks(
|
.daily_tasks(
|
||||||
&sconfig,
|
&sconfig,
|
||||||
num_users_requesting_invites,
|
num_users_requesting_invites,
|
||||||
num_censor_invitations,
|
|
||||||
&mut bridges,
|
&mut bridges,
|
||||||
&mut censor,
|
&mut censor,
|
||||||
)
|
)
|
||||||
|
@ -303,17 +278,10 @@ pub async fn main() {
|
||||||
let mut invited_friends = invited_friends.unwrap();
|
let mut invited_friends = invited_friends.unwrap();
|
||||||
if invited_friends.len() > 0 {
|
if invited_friends.len() > 0 {
|
||||||
if !user.is_censor {
|
if !user.is_censor {
|
||||||
// Censors always invite as many censor friends
|
// Users should never invite more friends than
|
||||||
// as possible. Honest users may invite honest
|
// need invitations, so this should never become
|
||||||
// friends, or they may accidentally invite
|
// negative
|
||||||
// censor friends.
|
num_users_requesting_invites -= invited_friends.len() as u32;
|
||||||
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
|
// If this user invited any friends, add them to the
|
||||||
// list of users
|
// list of users
|
||||||
|
@ -328,7 +296,7 @@ pub async fn main() {
|
||||||
// If any users couldn't get invites, they join with open-entry
|
// If any users couldn't get invites, they join with open-entry
|
||||||
// invitations
|
// invitations
|
||||||
for _ in 0..num_users_requesting_invites {
|
for _ in 0..num_users_requesting_invites {
|
||||||
let user = User::new(&sconfig, false).await;
|
let user = User::new(&sconfig).await;
|
||||||
if user.is_ok() {
|
if user.is_ok() {
|
||||||
users.push(user.unwrap());
|
users.push(user.unwrap());
|
||||||
} else {
|
} else {
|
||||||
|
@ -336,17 +304,6 @@ 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 TASKS
|
||||||
censor.end_of_day_tasks(&sconfig, &mut bridges).await;
|
censor.end_of_day_tasks(&sconfig, &mut bridges).await;
|
||||||
|
|
||||||
|
|
|
@ -33,14 +33,7 @@ pub async fn handle(db: &Db, req: Request<Body>) -> Result<Response<Body>, Infal
|
||||||
return Ok(prepare_header(val));
|
return Ok(prepare_header(val));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Get the current key or generate a new one. Note that
|
let pubkey = get_negative_report_public_key(&db, date);
|
||||||
// this code is only called in simulation. In
|
|
||||||
// production, users should not be able to induce Troll
|
|
||||||
// Patrol to generate new keys.
|
|
||||||
let pubkey = match get_negative_report_public_key(&db, date) {
|
|
||||||
Some(k) => Some(k),
|
|
||||||
None => new_negative_report_key(&db, date),
|
|
||||||
};
|
|
||||||
prepare_header(serde_json::to_string(&pubkey).unwrap())
|
prepare_header(serde_json::to_string(&pubkey).unwrap())
|
||||||
}),
|
}),
|
||||||
(&Method::POST, "/negativereport") => Ok::<_, Infallible>({
|
(&Method::POST, "/negativereport") => Ok::<_, Infallible>({
|
||||||
|
|
|
@ -94,31 +94,6 @@ impl Censor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
|
||||||
// If we don't have an appropriate Lox credential, we can't send
|
|
||||||
// a report. Return false.
|
|
||||||
if !self.has_lox_cred(fingerprint) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (cred, _) = &self.lox_credentials.get(fingerprint).unwrap();
|
|
||||||
let pr = PositiveReport::from_lox_credential(
|
|
||||||
*fingerprint,
|
|
||||||
None,
|
|
||||||
cred,
|
|
||||||
get_lox_pub(&config.la_pubkeys),
|
|
||||||
config.country.clone(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
config
|
|
||||||
.tp_net
|
|
||||||
.request("/positivereport".to_string(), pr.to_json().into_bytes())
|
|
||||||
.await;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a bunch of connections and submit positive reports if possible
|
// Make a bunch of connections and submit positive reports if possible
|
||||||
async fn flood(&self, config: &Config, bridges: &mut HashMap<[u8; 20], Bridge>) {
|
async fn flood(&self, config: &Config, bridges: &mut HashMap<[u8; 20], Bridge>) {
|
||||||
// Only do this if Flooding censor
|
// Only do this if Flooding censor
|
||||||
|
@ -140,7 +115,8 @@ impl Censor {
|
||||||
// If we have a lv3+ credential, submit a bunch of
|
// If we have a lv3+ credential, submit a bunch of
|
||||||
// positive reports
|
// positive reports
|
||||||
if self.has_lox_cred(fingerprint) {
|
if self.has_lox_cred(fingerprint) {
|
||||||
let (_cred, cred_count) =
|
let lox_pub = get_lox_pub(&config.la_pubkeys);
|
||||||
|
let (cred, cred_count) =
|
||||||
&self.lox_credentials.get(&bridge.fingerprint).unwrap();
|
&self.lox_credentials.get(&bridge.fingerprint).unwrap();
|
||||||
let num_prs = if config.one_positive_report_per_cred {
|
let num_prs = if config.one_positive_report_per_cred {
|
||||||
*cred_count
|
*cred_count
|
||||||
|
@ -148,7 +124,18 @@ impl Censor {
|
||||||
rng.gen_range(1000..30000)
|
rng.gen_range(1000..30000)
|
||||||
};
|
};
|
||||||
for _ in 0..num_prs {
|
for _ in 0..num_prs {
|
||||||
self.send_positive_report(config, &bridge.fingerprint).await;
|
let pr = PositiveReport::from_lox_credential(
|
||||||
|
bridge.fingerprint,
|
||||||
|
None,
|
||||||
|
cred,
|
||||||
|
lox_pub,
|
||||||
|
config.country.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
config
|
||||||
|
.tp_net
|
||||||
|
.request("/positivereport".to_string(), pr.to_json().into_bytes())
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,10 @@ pub struct Config {
|
||||||
// share information with each other.
|
// share information with each other.
|
||||||
pub country: String,
|
pub country: String,
|
||||||
pub one_positive_report_per_cred: bool,
|
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
|
// Probability that a connection randomly fails, even though censor
|
||||||
// does not block the bridge
|
// does not block the bridge
|
||||||
pub prob_connection_fails: f64,
|
pub prob_connection_fails: f64,
|
||||||
pub prob_user_invites_friend: f64,
|
pub prob_user_invites_friend: f64,
|
||||||
pub prob_user_is_censor: f64,
|
pub prob_user_is_censor: f64,
|
||||||
pub prob_user_submits_reports: f64,
|
pub prob_user_submits_reports: f64,
|
||||||
pub prob_user_treats_throttling_as_blocking: f64,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub struct User {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub async fn new(config: &Config, is_censor: bool) -> Result<Self, Error> {
|
pub async fn new(config: &Config) -> Result<Self, Error> {
|
||||||
let cred = get_lox_credential(
|
let cred = get_lox_credential(
|
||||||
&config.la_net,
|
&config.la_net,
|
||||||
&get_open_invitation(&config.la_net).await?,
|
&get_open_invitation(&config.la_net).await?,
|
||||||
|
@ -54,6 +54,9 @@ impl User {
|
||||||
.await?
|
.await?
|
||||||
.0;
|
.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
|
// Probabilistically decide whether this user submits reports
|
||||||
let submits_reports = if is_censor {
|
let submits_reports = if is_censor {
|
||||||
false
|
false
|
||||||
|
@ -93,12 +96,7 @@ impl User {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This should probably return an actual error type
|
// TODO: This should probably return an actual error type
|
||||||
pub async fn invite(
|
pub async fn invite(&mut self, config: &Config, censor: &mut Censor) -> Result<Self, Error> {
|
||||||
&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 etable = get_reachability_credential(&config.la_net).await?;
|
||||||
let (new_cred, invite) = issue_invite(
|
let (new_cred, invite) = issue_invite(
|
||||||
&config.la_net,
|
&config.la_net,
|
||||||
|
@ -129,8 +127,13 @@ impl User {
|
||||||
.await?
|
.await?
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
// Calling function decides if the invited user is a censor
|
// If the inviting user is a censor, the invitee will also be a
|
||||||
let is_censor = invited_user_is_censor;
|
// censor. If not, probabilistically decide.
|
||||||
|
let is_censor = if self.is_censor {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
event_happens(config.prob_user_is_censor)
|
||||||
|
};
|
||||||
|
|
||||||
// Probabilistically decide whether this user submits reports
|
// Probabilistically decide whether this user submits reports
|
||||||
let submits_reports = if is_censor {
|
let submits_reports = if is_censor {
|
||||||
|
@ -153,45 +156,23 @@ impl User {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to "connect" to the bridge, returns true if successful.
|
// Attempt to "connect" to the bridge, returns true if successful
|
||||||
// Note that this does not involve making a real connection to a
|
pub fn connect(&self, config: &Config, bridge: &mut Bridge, censor: &Censor) -> bool {
|
||||||
// real bridge. The function is async because the *censor* might
|
|
||||||
// submit a positive report during this function.
|
|
||||||
pub async fn connect(&self, config: &Config, bridge: &mut Bridge, censor: &Censor) -> bool {
|
|
||||||
if censor.blocks_bridge(config, &bridge.fingerprint) {
|
if censor.blocks_bridge(config, &bridge.fingerprint) {
|
||||||
if config.censor_totality == Full
|
if config.censor_totality == Full
|
||||||
|| config.censor_totality == Partial
|
|| config.censor_totality == Partial
|
||||||
&& event_happens(censor.partial_blocking_percent)
|
&& event_happens(censor.partial_blocking_percent)
|
||||||
|
|| config.censor_totality == Throttling
|
||||||
{
|
{
|
||||||
// If censor tries to hide its censorship, record a
|
// If censor tries to hide its censorship or
|
||||||
|
// throttles rather than actually blocking, record a
|
||||||
// false connection
|
// false connection
|
||||||
if config.censor_hides == Hiding {
|
if config.censor_hides == Hiding || config.censor_totality == Throttling {
|
||||||
bridge.connect_total();
|
bridge.connect_total();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return false because the connection failed
|
// Return false because the connection failed
|
||||||
return false;
|
return false;
|
||||||
} else if config.censor_totality == Throttling {
|
|
||||||
// With some probability, the user connects but gives up
|
|
||||||
// because there is too much interference. In this case,
|
|
||||||
// a real connection occurs, but we treat it like a
|
|
||||||
// false connection from the censor.
|
|
||||||
if event_happens(config.prob_user_treats_throttling_as_blocking) {
|
|
||||||
bridge.connect_total();
|
|
||||||
|
|
||||||
// A Hiding censor does not make an additional
|
|
||||||
// connection here, but it will make a false
|
|
||||||
// positive report if possible.
|
|
||||||
if config.censor_hides == Hiding && censor.has_lox_cred(&bridge.fingerprint) {
|
|
||||||
censor
|
|
||||||
.send_positive_report(config, &bridge.fingerprint)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return false because there was interference
|
|
||||||
// detected in the connection
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,21 +232,14 @@ impl User {
|
||||||
&mut self,
|
&mut self,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
num_users_requesting_invites: u32,
|
num_users_requesting_invites: u32,
|
||||||
num_censor_invites: u32,
|
|
||||||
bridges: &mut HashMap<[u8; 20], Bridge>,
|
bridges: &mut HashMap<[u8; 20], Bridge>,
|
||||||
censor: &mut Censor,
|
censor: &mut Censor,
|
||||||
) -> Result<Vec<User>, Error> {
|
) -> Result<Vec<User>, Error> {
|
||||||
if self.is_censor {
|
if self.is_censor {
|
||||||
self.daily_tasks_censor(config, bridges, censor).await
|
self.daily_tasks_censor(config, bridges, censor).await
|
||||||
} else {
|
} else {
|
||||||
self.daily_tasks_non_censor(
|
self.daily_tasks_non_censor(config, num_users_requesting_invites, bridges, censor)
|
||||||
config,
|
.await
|
||||||
num_users_requesting_invites,
|
|
||||||
num_censor_invites,
|
|
||||||
bridges,
|
|
||||||
censor,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +251,6 @@ impl User {
|
||||||
&mut self,
|
&mut self,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
num_users_requesting_invites: u32,
|
num_users_requesting_invites: u32,
|
||||||
num_censor_invites: u32,
|
|
||||||
bridges: &mut HashMap<[u8; 20], Bridge>,
|
bridges: &mut HashMap<[u8; 20], Bridge>,
|
||||||
censor: &mut Censor,
|
censor: &mut Censor,
|
||||||
) -> Result<Vec<User>, Error> {
|
) -> Result<Vec<User>, Error> {
|
||||||
|
@ -318,16 +291,13 @@ impl User {
|
||||||
for i in 0..bucket.len() {
|
for i in 0..bucket.len() {
|
||||||
// At level 0, we only have 1 bridge
|
// At level 0, we only have 1 bridge
|
||||||
if bucket[i] != BridgeLine::default() {
|
if bucket[i] != BridgeLine::default() {
|
||||||
if self
|
if self.connect(
|
||||||
.connect(
|
&config,
|
||||||
&config,
|
bridges
|
||||||
bridges
|
.get_mut(&bucket[i].get_hashed_fingerprint())
|
||||||
.get_mut(&bucket[i].get_hashed_fingerprint())
|
.unwrap(),
|
||||||
.unwrap(),
|
&censor,
|
||||||
&censor,
|
) {
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
succeeded.push(bucket[i]);
|
succeeded.push(bucket[i]);
|
||||||
} else {
|
} else {
|
||||||
failed.push(bucket[i]);
|
failed.push(bucket[i]);
|
||||||
|
@ -368,16 +338,13 @@ impl User {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Attempt to connect to second cred's bridge
|
// Attempt to connect to second cred's bridge
|
||||||
if self
|
if self.connect(
|
||||||
.connect(
|
&config,
|
||||||
&config,
|
bridges
|
||||||
bridges
|
.get_mut(&bridgeline.get_hashed_fingerprint())
|
||||||
.get_mut(&bridgeline.get_hashed_fingerprint())
|
.unwrap(),
|
||||||
.unwrap(),
|
censor,
|
||||||
censor,
|
) {
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
succeeded.push(bridgeline);
|
succeeded.push(bridgeline);
|
||||||
if second_reachcred.is_some()
|
if second_reachcred.is_some()
|
||||||
&& eligible_for_trust_promotion(&config.la_net, &second_cred).await
|
&& eligible_for_trust_promotion(&config.la_net, &second_cred).await
|
||||||
|
@ -517,8 +484,7 @@ impl User {
|
||||||
let mut new_friends = Vec::<User>::new();
|
let mut new_friends = Vec::<User>::new();
|
||||||
for _i in 0..min(invitations, num_users_requesting_invites) {
|
for _i in 0..min(invitations, num_users_requesting_invites) {
|
||||||
if event_happens(config.prob_user_invites_friend) {
|
if event_happens(config.prob_user_invites_friend) {
|
||||||
// Invite non-censor friend
|
match self.invite(&config, censor).await {
|
||||||
match self.invite(&config, censor, false).await {
|
|
||||||
Ok(friend) => {
|
Ok(friend) => {
|
||||||
// You really shouldn't push your friends,
|
// You really shouldn't push your friends,
|
||||||
// especially new ones whose boundaries you
|
// especially new ones whose boundaries you
|
||||||
|
@ -532,22 +498,6 @@ 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)
|
Ok(new_friends)
|
||||||
} else {
|
} else {
|
||||||
Ok(Vec::<User>::new())
|
Ok(Vec::<User>::new())
|
||||||
|
@ -665,7 +615,7 @@ impl User {
|
||||||
let invitations = scalar_u32(&self.primary_cred.invites_remaining).unwrap();
|
let invitations = scalar_u32(&self.primary_cred.invites_remaining).unwrap();
|
||||||
let mut new_friends = Vec::<User>::new();
|
let mut new_friends = Vec::<User>::new();
|
||||||
for _ in 0..invitations {
|
for _ in 0..invitations {
|
||||||
match self.invite(&config, censor, true).await {
|
match self.invite(&config, censor).await {
|
||||||
Ok(friend) => {
|
Ok(friend) => {
|
||||||
new_friends.push(friend);
|
new_friends.push(friend);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue