Browse Source

Update to the latest cuckoo simulation code

Ian Goldberg 1 year ago
parent
commit
64ebbe5d68

+ 0 - 99
cuckoo_simulation/cuckoo_vars.py

@@ -1,99 +0,0 @@
-import math
-
-def get_b_0(k, epsilon, ell, u):
-    #    plot([(1+(1+u)*epsilon*x)/((1-ell)*x+(1+u)*epsilon*x+1), 0.25], (x, 0, 1))
-    return ((1+(1+u)*epsilon*k)/((1-ell)*k+(1+u)*epsilon*k+1))
-
-def get_delta_e(phi_e, gamma, K, n, epsilon):
-    return math.sqrt(abs(math.log(phi_e)*32*epsilon/(gamma*K**2*math.log(n))))
-
-def get_phi_e(delta_e, gamma, K, n, epsilon):
-    return math.exp(-1*gamma*((delta_e*K)**2)*math.log(n)/(32*epsilon))
-
-def get_delta_age_ell(phi_age_ell, c, K, n):
-    C=c*math.log(n)
-    return math.sqrt((2*C*K/n) - (2*math.log(phi_age_ell)/C))
-
-def get_phi_age_ell(delta_age_ell, C, K, n):
-    return math.exp(((C**2)*K/n) - ((delta_age_ell**2)*C/2))
-
-def get_delta_age_u(u, c, K, n):
-    C=c*math.log(n)
-    #delta_age_u**2/(1+delta_age_u)=A
-    A = ((2*C*K/n) - (2*math.log(u)/C))
-    #quadratic of the form x^2 - A*x - A = 0
-    #only one positive root as A is always positive.
-    root = 0.5 * (A + math.sqrt(A**2+4*A))
-    return root
-
-def get_phi_age_u(delta_age_u, C, K, n):
-    return math.exp(((C**2)*K/n) - ((delta_age_u**2)*C/(2*(1+delta_age_u))))
-
-def get_delta_n(gamma, epsilon, K, n):
-    return gamma*K*(math.log(n))**3/(epsilon*n)
-
-def get_delta_ell(delta_e, delta_age_ell, delta_n):
-    return delta_e + delta_age_ell - (delta_e * delta_age_ell) - ((1 - delta_e) * delta_n)
-
-def get_delta_u(delta_e, delta_age_u, delta_n):
-    return delta_e + delta_age_u + delta_e * delta_age_u + ((1 + delta_e) * delta_n)
-
-def get_p_i(phi_e, epsilon, phi_age_ell, phi_age_u, C):
-    phi_e_honest = phi_e**C
-    print(phi_e_honest)
-    phi_e_byz = (phi_e**(epsilon**2))**C
-    print(phi_e_byz)
-    p1 = phi_e_honest + 2*phi_age_ell + phi_e_byz
-    p2 = phi_e_honest + 2*phi_age_u + phi_e_byz
-    p3 = phi_e_honest + phi_age_ell
-    p4 = phi_e_byz + phi_age_u
-    return [p1, p2, p3, p4]
-
-n=10**8
-K=20
-c=40
-C=c*math.log(n)
-gamma=1
-epsilon=0.2
-
-delta_e = 0.1
-delta_age_ell = 0.12
-delta_age_u = 0.11
-
-#phi_age_ell_1=0.02
-#phi_age_u_1=0.02
-#phi_e=5*(10**-30)
-
-#delta_age_u_1 = get_delta_age_u(phi_age_u_1, c, K, n)
-#print("delta_age_u is: ", delta_age_u_1)
-
-#delta_age_ell_1 = get_delta_age_ell(phi_age_ell_1, c, K, n)
-#print("delta_age_l is: ", delta_age_ell_1)
-
-delta_e_2 = get_delta_e(5*(10**-30), gamma, K, n, epsilon)
-print("delta_e_2 is: ", delta_e_2)
-
-delta_n = get_delta_n(gamma, epsilon, K, n)
-print("delta_n is: ", delta_n)
-
-delta_ell = get_delta_ell(delta_e, delta_age_ell, delta_n)
-print("delta_ell is: ", delta_ell)
-
-delta_u = get_delta_u(delta_e, delta_age_ell, delta_n)
-print("delta_u is: ", delta_u)
-
-b_0 = get_b_0(K, epsilon, delta_ell, delta_u)
-print("b_0 is: ", b_0)
-
-phi_age_ell = get_phi_age_ell(delta_age_ell, C, K, n)
-print("phi_age_ell is: ", phi_age_ell)
-
-phi_age_u = get_phi_age_u(delta_age_u, C, K, n)
-print("phi_age_u is: ", phi_age_u)
-
-phi_e = get_phi_e(delta_e, gamma, K, n, epsilon)
-print("phi_e is: ", phi_e)
-print("phi_e^(epsilon^2) is: ", phi_e**(epsilon**2))
-
-[p1, p2, p3, p4] = get_p_i(phi_e, epsilon, phi_age_ell, phi_age_u, C)
-print(p1, p2, p3, p4)

+ 35 - 0
cuckoo_simulation/find_m

@@ -0,0 +1,35 @@
+#!/bin/bash
+
+if [ $# != 3 ]; then
+	echo "Usage: $0 g lgr lgc" >&2
+	exit 1
+fi
+
+g=$1
+lgr=$2
+lgc=$3
+n=$((g<<lgr))
+gdir="g${g}_lgr${lgr}_lgc${lgc}_n${n}"
+mlow=0
+mhigh=$((n/5))
+
+runm() {
+	m=$1
+	h=$((n-m))
+	mdir="m${m}_h${h}"
+	./run_m $g $lgr $lgc $m
+	# Find the lowest b0 reported
+	grep FINAL ${gdir}/${mdir}/k*.out | cut -d' ' -f2 | cut -d= -f2 | sort -n | head -1
+}
+
+while [ $((mhigh > $((mlow+1)) && (mhigh-mlow)*100 > mhigh)) = 1 ]; do
+	mmid=$(((mhigh + mlow)/2))
+	b0mid=$(runm $mmid)
+	echo $mmid $b0mid
+	if [ $b0mid \< 0.25 ]; then
+		mlow=$mmid
+	else
+		mhigh=$mmid
+	fi
+done
+echo $mlow > ${gdir}/m.out

+ 22 - 0
cuckoo_simulation/run_m

@@ -0,0 +1,22 @@
+#!/bin/bash
+
+if [ $# != 4 ]; then
+	echo "Usage: $0 g lgr lgc m" >&2
+	exit 1
+fi
+
+g=$1
+lgr=$2
+lgc=$3
+m=$4
+n=$((g<<lgr))
+h=$((n-m))
+gdir="g${g}_lgr${lgr}_lgc${lgc}_n${n}"
+mdir="m${m}_h${h}"
+mkdir -p ${gdir}/${mdir}
+for k in {01..40}; do
+	now=`date`
+	echo "${now}: $h $m $lgr $k $g $lgc 100000 10" >> run.log
+	stdbuf -o 0 ./target/release/cuckoo_simulation $h $m $lgr $k $g $lgc 100000 10 > ${gdir}/${mdir}/k${k}.out 2>&1 &
+done
+wait

+ 104 - 47
cuckoo_simulation/src/main.rs

@@ -3,69 +3,68 @@ mod stats;
 mod types;
 
 use std::{env, process};
-use crate::types::{Quorum, Region};
+use crate::types::{Quorum, Region, NodeCount};
 use crate::stats::{CurrentStats, CumulativeStats};
 use crate::sim::Simulation;
 use rand_pcg::Pcg64;
 use rand::SeedableRng;
+// use log::{info, error, set_logger};
+// use simplelog::*;
+// use std::fs::File;
 
 fn usage(cmd: &String) {
-    eprintln!("Usage: {} h m r k g iters seed", cmd);
+    eprintln!("Usage: {} h m lg_r k g lg_c T S", 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!("k:           k - 1 no. of secondary joins in CCR before new primary joins are accepted");
+    eprintln!("k:           k - 1 no. of secondary joins in CCR before new primary joins are accepted, k*g/g' nodes cuckoo-ed out with each primary join");
     eprintln!("g:           desired number of nodes in a *region*");
+    eprintln!("lg_c:        log_2 of the number of regions per quorum");
     eprintln!("T:           number of iterations after initialization");
-    eprintln!("seed:        random seed");
+    eprintln!("S:        number of seeds");
 }
-
-fn main() {
-    let args: Vec<String> = env::args().collect();
-
-    if args.len() != 9 {
-        usage(&args[0]);
-        process::exit(1);
+fn run_print_errors(sim: &mut Simulation, is_init: bool, h: usize, m: usize, ctr: usize, max: usize) -> bool
+{
+    let (prefix, iter, iters, inserted, inserted_below_bmax, rejoined_below_bmax, number_rejoined) = if is_init
+    {
+        let (init_iter, a, b, c, d) = sim.init(h, m);
+        ("FAILED INIT ".to_string(), init_iter, m, a, b, c, d)
     }
-
-    let valid_args: Vec<usize> = args.iter().enumerate()
-        .filter(|(i, _)| *i != 0)
-        .map(|(_, v)| v.parse::<usize>())
-        .filter(|x| x.is_ok())
-        .map(|x| x.unwrap())
-        .collect();
-
-    if valid_args.len() != 8 {
-        usage(&args[0]);
-        process::exit(1);
+    else
+    {
+        let (a, b, c, d) = sim.move_malicious();
+        ("FAILED MOVE MALICIOUS ".to_string(), ctr, max, a, b, c, d)
+    };
+    if !inserted {
+        let reason = "due to failed malicious node cuckoo insert";
+        println!("{} iteration {} / {} {}", prefix, iter, iters, reason);
     }
-
-    let h = valid_args[0];
-    let m = valid_args[1];
-    let lg_r = valid_args[2];
-    let lg_c = valid_args[3];
-    let k = valid_args[4];
-    let g = valid_args[5];
-    let iters = valid_args[6];
-    let seed = valid_args[7];
-
-    if (lg_c > lg_r) | (k > g) {
-        usage(&args[0]);
-        process::exit(1);
+    if !inserted_below_bmax {
+        let reason = "as b_max was broken by moving malicious node";
+        println!("{} iteration {} / {} {}", prefix, iter, iters, reason);
+    }
+    if !rejoined_below_bmax {
+        let reason = "as b_max was broken by re-joining node".to_string();
+        println!("{} iteration {} / {} {} {}", prefix, iter, iters, reason, number_rejoined + 1);
     }
+    inserted && inserted_below_bmax && rejoined_below_bmax
+}
 
+fn run_sim_for_seed(h: NodeCount, m: NodeCount, lg_r: NodeCount, lg_c: NodeCount,
+                    k: NodeCount, g: NodeCount, iters: usize, seed: usize) ->
+                    (f64, NodeCount, NodeCount)
+{
     let blankregion = Region {
         num_honest: 0,
         num_malicious: 0,
-        last_join: 0
+        last_join: 0,
+        num_nodes_since_last_primary_join: 0,
     };
 
     let blankquorum = Quorum {
         tot_honest: 0,
         tot_malicious: 0,
         tot_last_join: 0,
-        num_nodes_since_last_primary_join: 0,
     };
 
     let mut sim = Simulation {
@@ -85,18 +84,76 @@ fn main() {
     sim.regions.resize(1<<lg_r, blankregion);
     sim.quorums.resize(1<<(lg_r-lg_c), blankquorum);
 
-    eprintln!("Starting simulation h={} m={} 2^r={} regions 2^c={} regions/quorum k={} g={} desired quorum size T={} seed={}",
-              h, m, 1 << lg_r, 1 << lg_c, k, g, iters, seed);
+    eprintln!("Starting simulation n={}, h={}, m={}, 2^r={} regions, CCR: k={}, desired quorum size: g={}, regions/quorum: 2^c={},  T={}, seed={}",
+              h+m, h, m, 1 << lg_r, k, g, 1 << lg_c, iters, seed);
 
-    sim.init(h, m);
-    for iter in 0..iters {
-        sim.move_malicious();
-        if iter % 100000 == 0 {
-            eprintln!("iter {}", iter);
+    let successful_init = run_print_errors(&mut sim, true, h, m, 0, 0);
+    let mut successful_move_malicious = false;
+    if successful_init {
+        for iter in 0..iters {
+            successful_move_malicious = run_print_errors(&mut sim, false, 0, 0, iter, iters);
+            if !successful_move_malicious
+                {break;}
         }
     }
 
-    println!("h={} m={} 2^r={} 2^c={} k={} g={} T={} seed={}",
-             h, m, 1 << lg_r, 1<< lg_c, k, g, iters, seed);
+    println!("Results for seed={} h={} m={} 2^r={} k={} g={} 2^c={} T={}:",
+             seed, h, m, 1 << lg_r, k, g, 1<< lg_c, iters);
     sim.cum_stats.print();
+
+    if successful_init && successful_move_malicious
+    {
+        (sim.cum_stats.max_b_0, sim.cum_stats.min_tot_nodes, sim.cum_stats.max_tot_nodes)
+    } else {
+        (999.0, 1000000, 0)
+    }
+}
+
+fn main() {
+    let args: Vec<String> = env::args().collect();
+
+    if args.len() != 9 {
+        usage(&args[0]);
+        process::exit(1);
+    }
+
+    let valid_args: Vec<usize> = args.iter().enumerate()
+        .filter(|(i, _)| *i != 0)
+        .map(|(_, v)| v.parse::<usize>())
+        .filter(|x| x.is_ok())
+        .map(|x| x.unwrap())
+        .collect();
+
+    if valid_args.len() != 8 {
+        usage(&args[0]);
+        process::exit(1);
+    }
+
+    let h = valid_args[0];
+    let m = valid_args[1];
+    let lg_r = valid_args[2];
+    let k = valid_args[3];
+    let g = valid_args[4];
+    let lg_c = valid_args[5];
+    let iters = valid_args[6];
+    let no_of_seeds = valid_args[7];
+
+    if (lg_c > lg_r) | (k > g) {
+        usage(&args[0]);
+        process::exit(1);
+    }
+
+    let mut b_0_array : Vec<f64> = vec![999.0; no_of_seeds];
+    let mut mins_array : Vec<NodeCount> = vec![1000; no_of_seeds];
+    let mut maxs_array : Vec<NodeCount> = vec![0; no_of_seeds];
+    for seed_ctr in 1..no_of_seeds + 1 {
+        let (b0, mins, maxs) = run_sim_for_seed(h, m, lg_r, lg_c, k, g, iters, seed_ctr);
+        b_0_array[seed_ctr - 1] = b0;
+        mins_array[seed_ctr - 1] = mins;
+        maxs_array[seed_ctr - 1] = maxs;
+    }
+    let max_b_0 = b_0_array.iter().cloned().fold(-1./0. /* -inf */, f64::max);
+    let min_mins = mins_array.iter().cloned().fold(1000, NodeCount::min);
+    let max_maxs = maxs_array.iter().cloned().fold(0, NodeCount::max);
+    println!("FINAL Max_b_0={} min_s={} max_s={}", max_b_0, min_mins, max_maxs);
 }

+ 129 - 59
cuckoo_simulation/src/sim.rs

@@ -4,6 +4,7 @@ use crate::stats::{CumulativeStats, CurrentStats};
 use reservoir_sampling::unweighted::core::l;
 use rand_pcg::Pcg64;
 use rand::RngCore;
+use rand::seq::SliceRandom;
 
 pub struct Simulation {
     pub done_init: bool,
@@ -24,6 +25,12 @@ impl Simulation {
         (self.rand.next_u64() as RegionCount) & self.num_region_mask
     }
 
+    pub fn random_shuffle_regions(&mut self, n:usize) -> Vec<u32> {
+        let mut vec: Vec<u32> = (0..n as u32).collect();
+        vec.shuffle(&mut self.rand);
+        vec
+    }
+
     // Reservoir-sampling to pick which nodes to kick out for CCR
     fn pick_honest_malicious_nodes_to_kick_out(&mut self, m: NodeCount, n: NodeCount, n_bad: NodeCount) -> NodeCount {
         let mut selected_indices: Vec<usize> = vec![0usize; m as usize];
@@ -35,32 +42,67 @@ impl Simulation {
     }
 
     // Kick-out nodes as per CCR.
-    fn kick_out_nodes(&mut self, region: RegionCount) {
+    fn kick_out_nodes(&mut self, region: RegionCount) -> (bool, usize) {
+        // This func will always be called after at least 1 malicious node is inserted into the quorum.
+        // TODO Check: We only want to chuck out k*g'/g nodes out of the *existing* nodes in the quorum.
+        // So effectively, the no. of malicious nodes is decremented by 1 here.
+        let num_malicious = self.regions[region].num_malicious - 1;
+        let current_region_size = num_malicious + self.regions[region].num_honest;
+        let number_to_kick_out : usize = (self.k as f64 * current_region_size as f64/ self.g as f64).round() as usize;
+        // println!(
+        //     "KICKING OUT: ***Region {}*** had {} honest {} malicious nodes = total of {} nodes.",
+        //     region,
+        //     self.regions[region].num_honest,
+        //     num_malicious,
+        //     current_region_size
+        // );
         let quorum = region >> self.lg_regions_per_quorum;
-        let current_region_size = self.regions[region].num_malicious + self.regions[region].num_honest;
-        let number_to_kick_out : usize = ((self.k * current_region_size / self.g ) as f64).round() as usize;
-        let num_malicious_to_kick_out = self.pick_honest_malicious_nodes_to_kick_out(number_to_kick_out, current_region_size, self.regions[region].num_malicious);
-        let num_honest_to_kick_out = number_to_kick_out - num_malicious_to_kick_out;
-
-        self.regions[region].num_malicious -= num_malicious_to_kick_out;
-        self.regions[region].num_honest -= num_honest_to_kick_out;
-        self.quorums[quorum].tot_malicious -= num_malicious_to_kick_out;
-        self.quorums[quorum].tot_honest -= num_honest_to_kick_out;
-
-        // Re-insert each node that was kicked out earlier, into new quorums, while maintaining
-        // honest/malicious status.
-        for _ in 0..num_honest_to_kick_out {
-            let secondary_join_region = self.rand_region();
-            self.insert(false, secondary_join_region, false);
-        }
-        for _ in 0..num_malicious_to_kick_out {
-            let secondary_join_region = self.rand_region();
-            self.insert(true, secondary_join_region, false);
+
+        if number_to_kick_out > 0
+        {
+            let num_malicious_to_kick_out = self.pick_honest_malicious_nodes_to_kick_out(number_to_kick_out, current_region_size, num_malicious);
+            let num_honest_to_kick_out = number_to_kick_out - num_malicious_to_kick_out;
+            // println!("KICKING OUT: We choose to kick out {} honest {} malicious nodes = total of {} nodes.",
+            //          num_honest_to_kick_out,
+            //          num_malicious_to_kick_out,
+            //          number_to_kick_out
+            // );
+
+            self.regions[region].num_malicious -= num_malicious_to_kick_out;
+            self.regions[region].num_honest -= num_honest_to_kick_out;
+            self.quorums[quorum].tot_malicious -= num_malicious_to_kick_out;
+            self.quorums[quorum].tot_honest -= num_honest_to_kick_out;
+
+            // Re-insert each node that was kicked out earlier, into new quorums, while maintaining
+            // honest/malicious status.
+            for _ctr in 0..num_honest_to_kick_out {
+                let secondary_join_region = self.rand_region();
+                // Don't need to check the return value as it'll continue to be true (only honest nodes were inserted)
+                self.insert(false, secondary_join_region, false);
+                // println!(
+                //     "KICKING OUT: honest node {} to region {}",
+                //     ctr, secondary_join_region
+                // );
+            }
+            for ctr in 0..num_malicious_to_kick_out {
+                let secondary_join_region = self.rand_region();
+                let below_bmax = self.insert(true, secondary_join_region, false);
+                if !below_bmax
+                {
+                    return (below_bmax, ctr + 1);
+                }
+                // println!(
+                //     "KICKING OUT: malicious node {} to region {}",
+                //     ctr, secondary_join_region
+                // );
+            }
+            return (true, num_malicious_to_kick_out)
         }
+        (true, 0)
     }
 
     // Insert a new node into a given region in the DHT.
-    fn insert(&mut self, is_malicious: bool, region: RegionCount, is_primary_join: bool) {
+    fn insert(&mut self, is_malicious: bool, region: RegionCount, is_primary_join: bool) -> bool {
         let quorum = region >> self.lg_regions_per_quorum;
 
         // Insert the new node into that region.
@@ -73,92 +115,120 @@ impl Simulation {
             self.quorums[quorum].tot_honest += 1;
         }
         if is_primary_join {
-            self.quorums[quorum].num_nodes_since_last_primary_join = 0
+            self.regions[region].num_nodes_since_last_primary_join = 0
         } else {
-            self.quorums[quorum].num_nodes_since_last_primary_join += 1
+            self.regions[region].num_nodes_since_last_primary_join += 1
         }
         if self.done_init {
-            self.cur_stats.update(quorum, &self.quorums[quorum], false);
+            return self.cur_stats.update(quorum, &self.quorums[quorum], false);
+            // if broke_b_max {
+            //     print!("FAILED INVARIANT b_0 < 1/3 while re-inserting cuckoo-ed out nodes");
+            // }
         }
+        true
     }
 
-    pub fn init(&mut self, num_honest: NodeCount, num_malicious: NodeCount) {
+    pub fn init(&mut self, num_honest: NodeCount, num_malicious: NodeCount) -> (usize, bool, bool, bool, usize) {
         for _ in 0..num_honest {
             // The original honest nodes are simply "mapped" to random locations -
             let target_region = self.rand_region();
+            // Don't need to check the return value as it'll always be true (b_0 max won't be broken as only honest nodes were inserted)
             self.insert(false, target_region, false);
         }
-        for _ in 0..num_malicious {
-            self.cuckoo_insert();
+        for ctr  in 0..num_malicious {
+            let (inserted, inserted_below_bmax, rejoined_below_bmax, number_rejoined) = self.cuckoo_insert();
+            if !inserted || !inserted_below_bmax || !rejoined_below_bmax {
+                return (ctr, inserted, inserted_below_bmax, rejoined_below_bmax, number_rejoined );
+            }
         }
         self.done_init = true;
         self.cur_stats.dirty = true;
         self.collect_stats();
         self.update_cumstats(true);
+        (num_malicious, true, true, true, 0)
     }
 
     // Insert a new node into a random region in the DHT, then
     // evict a fraction of nodes in that region into random
     // regions in the DHT (but don't evict nodes from the regions
     // _those_ nodes land in).
-    pub fn cuckoo_insert(&mut self) {
-        loop {
-            // Pick a random region to put the new node into. Also get the quorum for that region.
-            let region = self.rand_region();
-            let quorum = region >> self.lg_regions_per_quorum;
+    pub fn cuckoo_insert(&mut self) -> (bool, bool, bool, usize) {
+        let regions_ordering = self.random_shuffle_regions(self.regions.len());
+        // println!("Random permutation of target regions: {:?}", regions_ordering);
 
-            if self.quorums[quorum].num_nodes_since_last_primary_join >= self.k - 1 {
-                self.kick_out_nodes(region);
-                self.insert(true, region, true); // True for primary join
+        for ctr in 0..regions_ordering.len() {
+            // Pick a random region to put the new node into. Also get the quorum for that region.
+            let region = regions_ordering[ctr] as usize;
+            // println!(
+            //     "TRYING CUCKOO INSERT: target region {} quorum {} no. of nodes since last 1ary join {} k {}",
+            //     region, quorum, self.regions[region].num_nodes_since_last_primary_join, self.k
+            // );
+            if self.regions[region].num_nodes_since_last_primary_join >= self.k - 1 {
+                // println!("------ SUCCESSFUL CUCKOO INSERT: target region {}", region);
+                let below_bmax_insert_own = self.insert(true, region, true); // True for primary join
+                let (below_bmax_insert_kicked_out, no_of_malicious_kicked_out) = self.kick_out_nodes(region);
 
                 // Update the age of the region that was emptied out.
                 // 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);
+                    // as it's being updated in the collect_stats call essentially.
+                    // self.cur_stats.update(quorum, &self.quorums[quorum], false);
                     self.collect_stats();
                     self.update_cumstats(false);
                 }
-                break;
+                return (true, below_bmax_insert_own, below_bmax_insert_kicked_out, no_of_malicious_kicked_out)
             }
         }
+        (false, true, true, 0)
     }
 
     // Remove an existing malicious node from the quorum that has the lowest fraction of faulty nodes
-    pub fn move_malicious(&mut self) {
-        let find_min_b_0_quorum = || {
-            let compute_b_0 = |q: Quorum| {
-                let b_0: 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
-                };
-                b_0
+    pub fn move_malicious(&mut self) -> (bool, bool, bool, usize) {
+        let find_min_b_0_region = || {
+            let compute_b_0 = |r: Region| {
+                (r.num_malicious as f64) / (r.num_honest as f64 + r.num_malicious as f64)
             };
 
-            let b_0_array: Vec<f64> = self.quorums.iter().map(|&q| compute_b_0(q)).collect();
-            let (min_b_0_quorum, _) = b_0_array.iter().enumerate().fold((0, 0.0), |(min_index, min_val), (index, &val)| if val < min_val {
+            let b_0_array: Vec<f64> = self.regions.iter().map(|&q| compute_b_0(q)).collect();
+            // eprintln!("b_0 array for");
+            // let mut ctr: i8 = 0;
+            // for &b_0 in &b_0_array {
+            //     print!("{} {},", ctr, b_0);
+            //     ctr = ctr + 1;
+            // }
+            // println!("\n");
+            let (min_b_0_region, _) = b_0_array.iter().enumerate().fold((0, 1.0), |(min_index, min_val), (index, &val)| if val < min_val && val > 0.0 {
+                // eprintln!(
+                //     "Replacing {} index {} with current val {} index {}",
+                //     min_val, min_index, val, index
+                // );
                 (index, val)
             } else {
                 (min_index, min_val)
             });
 
-            min_b_0_quorum
+            min_b_0_region
         };
 
         // Pick quorum with least fraction of byz nodes
-        let min_b_0_quorum = find_min_b_0_quorum();
-        if self.quorums[min_b_0_quorum].tot_malicious > 0 {
-            self.quorums[min_b_0_quorum].tot_malicious -= 1;
-            self.cur_stats.update(min_b_0_quorum, &self.quorums[min_b_0_quorum], false);
+        let min_b_0_region = find_min_b_0_region();
+        let min_b_0_quorum = min_b_0_region >> self.lg_regions_per_quorum;
+        // eprintln!(
+        //     "MOVE MALICIOUS out of region {} which has {} honest {} malicious nodes",
+        //     min_b_0_region,
+        //     self.regions[min_b_0_region].num_honest,
+        //     self.regions[min_b_0_region].num_malicious
+        // );
+        self.regions[min_b_0_region].num_malicious -= 1;
+        self.quorums[min_b_0_quorum].tot_malicious -= 1;
+        // Don't need to check return value as b_0 for min_b_0_quorum decreases.
+        self.cur_stats
+            .update(min_b_0_quorum, &self.quorums[min_b_0_quorum], false);
 
-            // Insert it back into the DHT
-            self.cuckoo_insert();
-        }
+        // Insert it back into the DHT
+        return self.cuckoo_insert();
     }
 
     pub fn collect_stats(&mut self) {

+ 17 - 19
cuckoo_simulation/src/stats.rs

@@ -22,11 +22,12 @@ pub struct CurrentStats {
     pub min_b_0: f64,
     pub min_b_0_quorum: RegionCount,
     pub max_b_0: f64,
-    pub max_epsilon_quorum: RegionCount,
+    pub max_b_0_quorum: RegionCount,
 }
 
 impl CurrentStats {
-    pub fn update(&mut self, i: RegionCount, q: &Quorum, force: bool) {
+    pub fn update(&mut self, i: RegionCount, q: &Quorum, force: bool) -> bool {
+        let mut below_bmax: bool = true;
         if self.dirty == false && (
             self.min_tot_nodes_quorum == i ||
                 self.max_tot_nodes_quorum == i ||
@@ -37,7 +38,7 @@ impl CurrentStats {
                 self.min_tot_last_join_quorum == i ||
                 self.max_tot_last_join_quorum == i ||
                 self.min_b_0_quorum == i ||
-                self.max_epsilon_quorum == i) {
+                self.max_b_0_quorum == i) {
             self.dirty = true;
         }
         let nodes = q.tot_honest + q.tot_malicious;
@@ -73,22 +74,19 @@ impl CurrentStats {
             self.max_tot_last_join = q.tot_last_join;
             self.max_tot_last_join_quorum = i;
         }
-        let b_0: 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 || b_0 < self.min_b_0 {
+        let b_0 : f64 = (q.tot_malicious as f64) / (q.tot_honest as f64 + q.tot_malicious as f64);
+        if force || (b_0 < self.min_b_0 && q.tot_malicious != 0) {
             self.min_b_0 = b_0;
             self.min_b_0_quorum = i;
         }
         if force || b_0 > self.max_b_0 {
             self.max_b_0 = b_0;
-            self.max_epsilon_quorum = i;
+            self.max_b_0_quorum = i;
+            if b_0 > 0.25 {
+                below_bmax = false;
+            }
         }
+        below_bmax
     }
 
     #[allow(dead_code)]
@@ -97,7 +95,7 @@ impl CurrentStats {
         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!("b_0 {} ({}) {} ({})", self.min_b_0, self.min_b_0_quorum, self.max_b_0, self.max_epsilon_quorum);
+        println!("b_0 {} ({}) {} ({})", self.min_b_0, self.min_b_0_quorum, self.max_b_0, self.max_b_0_quorum);
     }
 }
 
@@ -118,10 +116,10 @@ pub struct CumulativeStats {
 impl CumulativeStats {
     #[allow(dead_code)]
     pub 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!("b_0 {} {}", self.min_b_0, self.max_b_0);
+        println!("Total nodes: min {} max {}, honest: min {} max {}, malicious: min {} max {}, b_0: min {} max {}",
+            self.min_tot_nodes, self.max_tot_nodes,
+            self.min_tot_honest, self.max_tot_honest,
+            self.min_tot_malicious, self.max_tot_malicious,
+            self.min_b_0, self.max_b_0);
     }
 }

+ 0 - 8
cuckoo_simulation/src/test.rs

@@ -1,8 +0,0 @@
-use reservoir_sampling::unweighted::l;
-
-pub(crate) fn hehehehe () {
-    let mut sampled_arr = vec![0usize; 10];
-    println!("Orig array: {:?}", sampled_arr);
-    l(0usize..100, sampled_arr.as_mut_slice());
-    println!("Sampled array: {:?}", sampled_arr);
-}

+ 1 - 2
cuckoo_simulation/src/types.rs

@@ -1,5 +1,4 @@
 pub type NodeCount = usize;
-pub type QuorumCount = usize;
 pub type TimeCount = usize;
 pub type RegionCount = usize;
 
@@ -8,7 +7,6 @@ pub struct Quorum {
     pub tot_honest: NodeCount,
     pub tot_malicious: NodeCount,
     pub tot_last_join: TimeCount,
-    pub num_nodes_since_last_primary_join: NodeCount,
 }
 
 #[derive(Debug, Clone, Copy)]
@@ -16,4 +14,5 @@ pub struct Region {
     pub(crate) num_honest: NodeCount,
     pub(crate) num_malicious: NodeCount,
     pub(crate) last_join: TimeCount,
+    pub num_nodes_since_last_primary_join: NodeCount,
 }