123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- use std::env;
- use std::process;
- use rand_core::SeedableRng;
- use rand_core::RngCore;
- use rand_xoshiro::SplitMix64;
- type NodeCount = usize;
- type RegionCount = usize;
- type TimeCount = usize;
- #[derive(Debug, Clone, Copy)]
- struct Region {
- num_honest: NodeCount,
- num_malicious: NodeCount,
- last_join: TimeCount,
- }
- #[derive(Debug, Clone, Copy)]
- struct Quorum {
- tot_honest: NodeCount,
- tot_malicious: NodeCount,
- tot_last_join: TimeCount,
- }
- #[derive(Debug, Clone, Copy, Default)]
- struct CurrentStats {
- dirty: bool,
- min_tot_nodes: NodeCount,
- min_tot_nodes_quorum: RegionCount,
- max_tot_nodes: NodeCount,
- max_tot_nodes_quorum: RegionCount,
- min_tot_honest: NodeCount,
- min_tot_honest_quorum: RegionCount,
- max_tot_honest: NodeCount,
- max_tot_honest_quorum: RegionCount,
- min_tot_malicious: NodeCount,
- min_tot_malicious_quorum: RegionCount,
- max_tot_malicious: NodeCount,
- max_tot_malicious_quorum: RegionCount,
- min_tot_last_join: TimeCount,
- min_tot_last_join_quorum: RegionCount,
- max_tot_last_join: TimeCount,
- max_tot_last_join_quorum: RegionCount,
- min_epsilon: f64,
- min_epsilon_quorum: RegionCount,
- max_epsilon: f64,
- max_epsilon_quorum: RegionCount,
- }
- impl CurrentStats {
- fn update(&mut self, i: RegionCount, q: &Quorum, force: bool) {
- if self.dirty == false && (
- self.min_tot_nodes_quorum == i ||
- self.max_tot_nodes_quorum == i ||
- self.min_tot_honest_quorum == i ||
- self.max_tot_honest_quorum == i ||
- self.min_tot_malicious_quorum == i ||
- self.max_tot_malicious_quorum == i ||
- self.min_tot_last_join_quorum == i ||
- self.max_tot_last_join_quorum == i ||
- self.min_epsilon_quorum == i ||
- self.max_epsilon_quorum == i) {
- self.dirty = true;
- }
- let nodes = q.tot_honest + q.tot_malicious;
- if force || nodes < self.min_tot_nodes {
- self.min_tot_nodes = nodes;
- self.min_tot_nodes_quorum = i;
- }
- if force || nodes > self.max_tot_nodes {
- self.max_tot_nodes = nodes;
- self.max_tot_nodes_quorum = i;
- }
- if force || q.tot_honest < self.min_tot_honest {
- self.min_tot_honest = q.tot_honest;
- self.min_tot_honest_quorum = i;
- }
- if force || q.tot_honest > self.max_tot_honest {
- self.max_tot_honest = q.tot_honest;
- self.max_tot_honest_quorum = i;
- }
- if force || q.tot_malicious < self.min_tot_malicious {
- self.min_tot_malicious = q.tot_malicious;
- self.min_tot_malicious_quorum = i;
- }
- if force || q.tot_malicious > self.max_tot_malicious {
- self.max_tot_malicious = q.tot_malicious;
- self.max_tot_malicious_quorum = i;
- }
- if force || q.tot_last_join < self.min_tot_last_join {
- self.min_tot_last_join = q.tot_last_join;
- self.min_tot_last_join_quorum = i;
- }
- if force || q.tot_last_join > self.max_tot_last_join {
- self.max_tot_last_join = q.tot_last_join;
- self.max_tot_last_join_quorum = i;
- }
- let epsilon : f64 = if q.tot_honest > 0 {
- (q.tot_malicious as f64) /
- (q.tot_honest as f64)
- } else if q.tot_malicious > 0 {
- 1000000.0
- } else {
- 0.0
- };
- if force || epsilon < self.min_epsilon {
- self.min_epsilon = epsilon;
- self.min_epsilon_quorum = i;
- }
- if force || epsilon > self.max_epsilon {
- self.max_epsilon = epsilon;
- self.max_epsilon_quorum = i;
- }
- }
- #[allow(dead_code)]
- fn print(&self) {
- print!("nodes {} ({}) {} ({}) ", self.min_tot_nodes, self.min_tot_nodes_quorum, self.max_tot_nodes, self.max_tot_nodes_quorum);
- print!("honest {} ({}) {} ({}) ", self.min_tot_honest, self.min_tot_honest_quorum, self.max_tot_honest, self.max_tot_honest_quorum);
- print!("malicious {} ({}) {} ({}) ", self.min_tot_malicious, self.min_tot_malicious_quorum, self.max_tot_malicious, self.max_tot_malicious_quorum);
- print!("lastjoin {} ({}) {} ({}) ", self.min_tot_last_join, self.min_tot_last_join_quorum, self.max_tot_last_join, self.max_tot_last_join_quorum);
- println!("epsilon {} ({}) {} ({})", self.min_epsilon, self.min_epsilon_quorum, self.max_epsilon, self.max_epsilon_quorum);
- }
- }
- #[derive(Debug, Clone, Copy, Default)]
- struct CumulativeStats {
- min_tot_honest: NodeCount,
- max_tot_honest: NodeCount,
- min_tot_malicious: NodeCount,
- max_tot_malicious: NodeCount,
- min_tot_nodes: NodeCount,
- max_tot_nodes: NodeCount,
- min_age: TimeCount,
- max_age: TimeCount,
- min_epsilon: f64,
- max_epsilon: f64,
- }
- impl CumulativeStats {
- #[allow(dead_code)]
- fn print(&self) {
- print!("nodes {} {} ", self.min_tot_nodes, self.max_tot_nodes);
- print!("honest {} {} ", self.min_tot_honest, self.max_tot_honest);
- print!("malicious {} {} ", self.min_tot_malicious, self.max_tot_malicious);
- print!("age {} {} ", self.min_age, self.max_age);
- println!("epsilon {} {}", self.min_epsilon, self.max_epsilon);
- }
- }
- struct Simulation {
- done_init: bool,
- now: TimeCount,
- rand: SplitMix64,
- lg_regions_per_quorum: u8,
- num_region_mask: RegionCount,
- regions: Vec<Region>,
- quorums: Vec<Quorum>,
- cur_stats: CurrentStats,
- cum_stats: CumulativeStats,
- }
- impl Simulation {
- fn rand_region(&mut self) -> RegionCount {
- (self.rand.next_u64() as RegionCount) & self.num_region_mask
- }
- // Insert a new node into a random region in the DHT.
- fn insert(&mut self, is_malicious: bool) {
- let insregion = self.rand_region();
- let quorum = insregion >> self.lg_regions_per_quorum;
- if is_malicious {
- self.regions[insregion].num_malicious += 1;
- self.quorums[quorum].tot_malicious += 1;
- } else {
- self.regions[insregion].num_honest += 1;
- self.quorums[quorum].tot_honest += 1;
- }
- if self.done_init {
- self.cur_stats.update(quorum, &self.quorums[quorum], false);
- }
- }
- // Insert a new node into a random region in the DHT, then
- // evict all the other nodes in that region into random
- // regions in the DHT (but don't evict nodes from the regions
- // _those_ nodes land in).
- fn cuckoo_insert(&mut self, is_malicious: bool) {
- // Pick a random region to put the new node into. Also get the quorum for that region.
- let insregion = self.rand_region();
- let quorum = insregion >> self.lg_regions_per_quorum;
- // Kick out all nodes in that region
- // (subtract honest, malicious counts for that region from the quorum totals).
- let num_malicious = self.regions[insregion].num_malicious;
- let num_honest = self.regions[insregion].num_honest;
- self.quorums[quorum].tot_malicious -= num_malicious;
- self.quorums[quorum].tot_honest -= num_honest;
- // Insert the new node into that region.
- // Also update honest/malicious counts for region + quorum.
- if is_malicious {
- self.regions[insregion].num_malicious = 1;
- self.quorums[quorum].tot_malicious += 1;
- self.regions[insregion].num_honest = 0;
- } else {
- self.regions[insregion].num_malicious = 0;
- self.regions[insregion].num_honest = 1;
- self.quorums[quorum].tot_honest += 1;
- }
- // Re-insert each node that was kicked out earlier, into new k-regions, while maintaining
- // honest/malicious status.
- for _ in 0..num_honest {
- self.insert(false);
- }
- for _ in 0..num_malicious {
- self.insert(true);
- }
- // Update the age of the region that was emptied out.
- let last_join = self.regions[insregion].last_join;
- self.regions[insregion].last_join = self.now;
- self.quorums[quorum].tot_last_join += self.now - last_join;
- self.now += 1;
- // Cuckoo-ing after initialization: update stats for the quorum with the cuckood-out region
- if self.done_init {
- self.cur_stats.update(quorum, &self.quorums[quorum], false);
- self.collect_stats();
- self.update_cumstats(false);
- }
- }
- fn init(&mut self, num_honest : NodeCount, num_malicious : NodeCount) {
- for _ in 0..num_honest {
- self.cuckoo_insert(false);
- }
- for _ in 0..num_malicious {
- self.cuckoo_insert(true);
- }
- self.done_init = true;
- self.cur_stats.dirty = true;
- self.collect_stats();
- self.update_cumstats(true);
- }
- fn move_malicious(&mut self) {
- // Remove an existing malicious node
- // For now, just randomly
- loop {
- let region = self.rand_region();
- let quorum = region >> self.lg_regions_per_quorum;
- if self.regions[region].num_malicious > 0 {
- self.regions[region].num_malicious -= 1;
- self.quorums[quorum].tot_malicious -= 1;
- self.cur_stats.update(quorum, &self.quorums[quorum], false);
- break;
- }
- }
- // Insert it back into the DHT
- self.cuckoo_insert(true);
- }
- #[allow(dead_code)]
- fn count_nodes(&mut self) {
- let mut num_honest : NodeCount = 0;
- let mut num_malicious : NodeCount = 0;
- let mut last_join : TimeCount = 0;
- for r in self.regions.iter() {
- num_honest += r.num_honest;
- num_malicious += r.num_malicious;
- last_join += r.last_join;
- }
- print!("regions h={} m={} l={} ", num_honest, num_malicious, last_join);
- let mut tot_honest : NodeCount = 0;
- let mut tot_malicious : NodeCount = 0;
- let mut tot_last_join : TimeCount = 0;
- for q in self.quorums.iter() {
- tot_honest += q.tot_honest;
- tot_malicious += q.tot_malicious;
- tot_last_join += q.tot_last_join;
- }
- println!("quorums h={} m={} l={}", tot_honest, tot_malicious, tot_last_join);
- }
- fn collect_stats(&mut self) {
- if self.cur_stats.dirty {
- for (i, q) in self.quorums.iter().enumerate() {
- self.cur_stats.update(i, q, i==0);
- }
- self.cur_stats.dirty = false;
- }
- }
- fn update_cumstats(&mut self, force: bool) {
- let stat = &self.cur_stats;
- if force || stat.min_tot_nodes < self.cum_stats.min_tot_nodes {
- self.cum_stats.min_tot_nodes = stat.min_tot_nodes;
- }
- if force || stat.max_tot_nodes > self.cum_stats.max_tot_nodes {
- self.cum_stats.max_tot_nodes = stat.max_tot_nodes;
- }
- if force || stat.min_tot_honest < self.cum_stats.min_tot_honest {
- self.cum_stats.min_tot_honest = stat.min_tot_honest;
- }
- if force || stat.max_tot_honest > self.cum_stats.max_tot_honest {
- self.cum_stats.max_tot_honest = stat.max_tot_honest;
- }
- if force || stat.min_tot_malicious < self.cum_stats.min_tot_malicious {
- self.cum_stats.min_tot_malicious = stat.min_tot_malicious;
- }
- if force || stat.max_tot_malicious > self.cum_stats.max_tot_malicious {
- self.cum_stats.max_tot_malicious = stat.max_tot_malicious;
- }
- let min_age = (self.now<<self.lg_regions_per_quorum) - stat.max_tot_last_join;
- let max_age = (self.now<<self.lg_regions_per_quorum) - stat.min_tot_last_join;
- if force || min_age < self.cum_stats.min_age {
- self.cum_stats.min_age = min_age;
- }
- if force || max_age > self.cum_stats.max_age {
- self.cum_stats.max_age = max_age;
- }
- if force || stat.min_epsilon < self.cum_stats.min_epsilon {
- self.cum_stats.min_epsilon = stat.min_epsilon;
- }
- if force || stat.max_epsilon > self.cum_stats.max_epsilon {
- self.cum_stats.max_epsilon = stat.max_epsilon;
- }
- }
- }
- fn usage(cmd: &String) {
- eprintln!("Usage: {} h m lg_r lg_c iters seed", cmd);
- eprintln!("h: number of honest nodes");
- eprintln!("m: number of malicious nodes");
- eprintln!("lg_r: log_2 of the number of regions");
- eprintln!("lg_c: log_2 of the number of regions per quorum");
- eprintln!("iters: number of iterations after initialization");
- eprintln!("seed: random seed");
- }
- fn main() {
- let args: Vec<String> = env::args().collect();
- if args.len() != 7 {
- usage(&args[0]);
- process::exit(1);
- }
- let arg_h = args[1].parse::<NodeCount>();
- let arg_m = args[2].parse::<NodeCount>();
- let arg_lg_r = args[3].parse::<u8>();
- let arg_lg_c = args[4].parse::<u8>();
- let arg_iters = args[5].parse::<TimeCount>();
- let arg_seed = args[6].parse::<u64>();
- if arg_h.is_err() || arg_m.is_err() || arg_lg_r.is_err() ||
- arg_lg_c.is_err() || arg_iters.is_err() || arg_seed.is_err() {
- usage(&args[0]);
- process::exit(1);
- }
- // Number of honest nodes
- let h : NodeCount = arg_h.unwrap();
- // Number of malicious nodes
- let m : NodeCount = arg_m.unwrap();
- // TODO: In the cuckoo-rule, we don't care about each region.
- // We only care about a k-region at a time.
- // In commensal-cuckooing, we only care about the quorum that is being joined to.
- // log_2 of number of regions
- let lg_r : u8 = arg_lg_r.unwrap();
- // log_2 of number of regions per quorum (must be smaller than r)
- let lg_c : u8 = arg_lg_c.unwrap();
- // Number of iterations after initialization
- let iters : TimeCount = arg_iters.unwrap();
- // 64-bit random seed
- let seed : u64 = arg_seed.unwrap();
- if lg_c > lg_r {
- usage(&args[0]);
- process::exit(1);
- }
- let blankregion = Region {
- num_honest: 0,
- num_malicious: 0,
- last_join: 0
- };
- let blankquorum = Quorum {
- tot_honest: 0,
- tot_malicious: 0,
- tot_last_join: 0,
- };
- let mut sim = Simulation {
- done_init: false,
- now: 0,
- rand: SplitMix64::seed_from_u64(seed),
- lg_regions_per_quorum: lg_c,
- num_region_mask: (1<<lg_r)-1,
- regions: Vec::new(),
- quorums: Vec::new(),
- cur_stats: CurrentStats::default(),
- cum_stats: CumulativeStats::default(),
- };
- sim.regions.resize(1<<lg_r, blankregion);
- sim.quorums.resize(1<<(lg_r-lg_c), blankquorum);
- eprintln!("Starting simulation h={} m={} r={} C={} seed={}",
- h, m, 1<<lg_r, 1<<lg_c, seed);
- sim.init(h, m);
- for iter in 0..iters {
- sim.move_malicious();
- if iter % 100000 == 0 {
- eprintln!("iter {}", iter);
- }
- }
- print!("{} {} {} {} {} {} ", h, m, lg_r, lg_c, iters, seed);
- sim.cum_stats.print();
- }
|