old-main.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. use std::env;
  2. use std::process;
  3. use rand_core::SeedableRng;
  4. use rand_core::RngCore;
  5. use rand_xoshiro::SplitMix64;
  6. type NodeCount = usize;
  7. type RegionCount = usize;
  8. type TimeCount = usize;
  9. #[derive(Debug, Clone, Copy)]
  10. struct Region {
  11. num_honest: NodeCount,
  12. num_malicious: NodeCount,
  13. last_join: TimeCount,
  14. }
  15. #[derive(Debug, Clone, Copy)]
  16. struct Quorum {
  17. tot_honest: NodeCount,
  18. tot_malicious: NodeCount,
  19. tot_last_join: TimeCount,
  20. }
  21. #[derive(Debug, Clone, Copy, Default)]
  22. struct CurrentStats {
  23. dirty: bool,
  24. min_tot_nodes: NodeCount,
  25. min_tot_nodes_quorum: RegionCount,
  26. max_tot_nodes: NodeCount,
  27. max_tot_nodes_quorum: RegionCount,
  28. min_tot_honest: NodeCount,
  29. min_tot_honest_quorum: RegionCount,
  30. max_tot_honest: NodeCount,
  31. max_tot_honest_quorum: RegionCount,
  32. min_tot_malicious: NodeCount,
  33. min_tot_malicious_quorum: RegionCount,
  34. max_tot_malicious: NodeCount,
  35. max_tot_malicious_quorum: RegionCount,
  36. min_tot_last_join: TimeCount,
  37. min_tot_last_join_quorum: RegionCount,
  38. max_tot_last_join: TimeCount,
  39. max_tot_last_join_quorum: RegionCount,
  40. min_epsilon: f64,
  41. min_epsilon_quorum: RegionCount,
  42. max_epsilon: f64,
  43. max_epsilon_quorum: RegionCount,
  44. }
  45. impl CurrentStats {
  46. fn update(&mut self, i: RegionCount, q: &Quorum, force: bool) {
  47. if self.dirty == false && (
  48. self.min_tot_nodes_quorum == i ||
  49. self.max_tot_nodes_quorum == i ||
  50. self.min_tot_honest_quorum == i ||
  51. self.max_tot_honest_quorum == i ||
  52. self.min_tot_malicious_quorum == i ||
  53. self.max_tot_malicious_quorum == i ||
  54. self.min_tot_last_join_quorum == i ||
  55. self.max_tot_last_join_quorum == i ||
  56. self.min_epsilon_quorum == i ||
  57. self.max_epsilon_quorum == i) {
  58. self.dirty = true;
  59. }
  60. let nodes = q.tot_honest + q.tot_malicious;
  61. if force || nodes < self.min_tot_nodes {
  62. self.min_tot_nodes = nodes;
  63. self.min_tot_nodes_quorum = i;
  64. }
  65. if force || nodes > self.max_tot_nodes {
  66. self.max_tot_nodes = nodes;
  67. self.max_tot_nodes_quorum = i;
  68. }
  69. if force || q.tot_honest < self.min_tot_honest {
  70. self.min_tot_honest = q.tot_honest;
  71. self.min_tot_honest_quorum = i;
  72. }
  73. if force || q.tot_honest > self.max_tot_honest {
  74. self.max_tot_honest = q.tot_honest;
  75. self.max_tot_honest_quorum = i;
  76. }
  77. if force || q.tot_malicious < self.min_tot_malicious {
  78. self.min_tot_malicious = q.tot_malicious;
  79. self.min_tot_malicious_quorum = i;
  80. }
  81. if force || q.tot_malicious > self.max_tot_malicious {
  82. self.max_tot_malicious = q.tot_malicious;
  83. self.max_tot_malicious_quorum = i;
  84. }
  85. if force || q.tot_last_join < self.min_tot_last_join {
  86. self.min_tot_last_join = q.tot_last_join;
  87. self.min_tot_last_join_quorum = i;
  88. }
  89. if force || q.tot_last_join > self.max_tot_last_join {
  90. self.max_tot_last_join = q.tot_last_join;
  91. self.max_tot_last_join_quorum = i;
  92. }
  93. let epsilon : f64 = if q.tot_honest > 0 {
  94. (q.tot_malicious as f64) /
  95. (q.tot_honest as f64)
  96. } else if q.tot_malicious > 0 {
  97. 1000000.0
  98. } else {
  99. 0.0
  100. };
  101. if force || epsilon < self.min_epsilon {
  102. self.min_epsilon = epsilon;
  103. self.min_epsilon_quorum = i;
  104. }
  105. if force || epsilon > self.max_epsilon {
  106. self.max_epsilon = epsilon;
  107. self.max_epsilon_quorum = i;
  108. }
  109. }
  110. #[allow(dead_code)]
  111. fn print(&self) {
  112. print!("nodes {} ({}) {} ({}) ", self.min_tot_nodes, self.min_tot_nodes_quorum, self.max_tot_nodes, self.max_tot_nodes_quorum);
  113. print!("honest {} ({}) {} ({}) ", self.min_tot_honest, self.min_tot_honest_quorum, self.max_tot_honest, self.max_tot_honest_quorum);
  114. print!("malicious {} ({}) {} ({}) ", self.min_tot_malicious, self.min_tot_malicious_quorum, self.max_tot_malicious, self.max_tot_malicious_quorum);
  115. print!("lastjoin {} ({}) {} ({}) ", self.min_tot_last_join, self.min_tot_last_join_quorum, self.max_tot_last_join, self.max_tot_last_join_quorum);
  116. println!("epsilon {} ({}) {} ({})", self.min_epsilon, self.min_epsilon_quorum, self.max_epsilon, self.max_epsilon_quorum);
  117. }
  118. }
  119. #[derive(Debug, Clone, Copy, Default)]
  120. struct CumulativeStats {
  121. min_tot_honest: NodeCount,
  122. max_tot_honest: NodeCount,
  123. min_tot_malicious: NodeCount,
  124. max_tot_malicious: NodeCount,
  125. min_tot_nodes: NodeCount,
  126. max_tot_nodes: NodeCount,
  127. min_age: TimeCount,
  128. max_age: TimeCount,
  129. min_epsilon: f64,
  130. max_epsilon: f64,
  131. }
  132. impl CumulativeStats {
  133. #[allow(dead_code)]
  134. fn print(&self) {
  135. print!("nodes {} {} ", self.min_tot_nodes, self.max_tot_nodes);
  136. print!("honest {} {} ", self.min_tot_honest, self.max_tot_honest);
  137. print!("malicious {} {} ", self.min_tot_malicious, self.max_tot_malicious);
  138. print!("age {} {} ", self.min_age, self.max_age);
  139. println!("epsilon {} {}", self.min_epsilon, self.max_epsilon);
  140. }
  141. }
  142. struct Simulation {
  143. done_init: bool,
  144. now: TimeCount,
  145. rand: SplitMix64,
  146. lg_regions_per_quorum: u8,
  147. num_region_mask: RegionCount,
  148. regions: Vec<Region>,
  149. quorums: Vec<Quorum>,
  150. cur_stats: CurrentStats,
  151. cum_stats: CumulativeStats,
  152. }
  153. impl Simulation {
  154. fn rand_region(&mut self) -> RegionCount {
  155. (self.rand.next_u64() as RegionCount) & self.num_region_mask
  156. }
  157. // Insert a new node into a random region in the DHT.
  158. fn insert(&mut self, is_malicious: bool) {
  159. let insregion = self.rand_region();
  160. let quorum = insregion >> self.lg_regions_per_quorum;
  161. if is_malicious {
  162. self.regions[insregion].num_malicious += 1;
  163. self.quorums[quorum].tot_malicious += 1;
  164. } else {
  165. self.regions[insregion].num_honest += 1;
  166. self.quorums[quorum].tot_honest += 1;
  167. }
  168. if self.done_init {
  169. self.cur_stats.update(quorum, &self.quorums[quorum], false);
  170. }
  171. }
  172. // Insert a new node into a random region in the DHT, then
  173. // evict all the other nodes in that region into random
  174. // regions in the DHT (but don't evict nodes from the regions
  175. // _those_ nodes land in).
  176. fn cuckoo_insert(&mut self, is_malicious: bool) {
  177. // Pick a random region to put the new node into. Also get the quorum for that region.
  178. let insregion = self.rand_region();
  179. let quorum = insregion >> self.lg_regions_per_quorum;
  180. // Kick out all nodes in that region
  181. // (subtract honest, malicious counts for that region from the quorum totals).
  182. let num_malicious = self.regions[insregion].num_malicious;
  183. let num_honest = self.regions[insregion].num_honest;
  184. self.quorums[quorum].tot_malicious -= num_malicious;
  185. self.quorums[quorum].tot_honest -= num_honest;
  186. // Insert the new node into that region.
  187. // Also update honest/malicious counts for region + quorum.
  188. if is_malicious {
  189. self.regions[insregion].num_malicious = 1;
  190. self.quorums[quorum].tot_malicious += 1;
  191. self.regions[insregion].num_honest = 0;
  192. } else {
  193. self.regions[insregion].num_malicious = 0;
  194. self.regions[insregion].num_honest = 1;
  195. self.quorums[quorum].tot_honest += 1;
  196. }
  197. // Re-insert each node that was kicked out earlier, into new k-regions, while maintaining
  198. // honest/malicious status.
  199. for _ in 0..num_honest {
  200. self.insert(false);
  201. }
  202. for _ in 0..num_malicious {
  203. self.insert(true);
  204. }
  205. // Update the age of the region that was emptied out.
  206. let last_join = self.regions[insregion].last_join;
  207. self.regions[insregion].last_join = self.now;
  208. self.quorums[quorum].tot_last_join += self.now - last_join;
  209. self.now += 1;
  210. // Cuckoo-ing after initialization: update stats for the quorum with the cuckood-out region
  211. if self.done_init {
  212. self.cur_stats.update(quorum, &self.quorums[quorum], false);
  213. self.collect_stats();
  214. self.update_cumstats(false);
  215. }
  216. }
  217. fn init(&mut self, num_honest : NodeCount, num_malicious : NodeCount) {
  218. for _ in 0..num_honest {
  219. self.cuckoo_insert(false);
  220. }
  221. for _ in 0..num_malicious {
  222. self.cuckoo_insert(true);
  223. }
  224. self.done_init = true;
  225. self.cur_stats.dirty = true;
  226. self.collect_stats();
  227. self.update_cumstats(true);
  228. }
  229. fn move_malicious(&mut self) {
  230. // Remove an existing malicious node
  231. // For now, just randomly
  232. loop {
  233. let region = self.rand_region();
  234. let quorum = region >> self.lg_regions_per_quorum;
  235. if self.regions[region].num_malicious > 0 {
  236. self.regions[region].num_malicious -= 1;
  237. self.quorums[quorum].tot_malicious -= 1;
  238. self.cur_stats.update(quorum, &self.quorums[quorum], false);
  239. break;
  240. }
  241. }
  242. // Insert it back into the DHT
  243. self.cuckoo_insert(true);
  244. }
  245. #[allow(dead_code)]
  246. fn count_nodes(&mut self) {
  247. let mut num_honest : NodeCount = 0;
  248. let mut num_malicious : NodeCount = 0;
  249. let mut last_join : TimeCount = 0;
  250. for r in self.regions.iter() {
  251. num_honest += r.num_honest;
  252. num_malicious += r.num_malicious;
  253. last_join += r.last_join;
  254. }
  255. print!("regions h={} m={} l={} ", num_honest, num_malicious, last_join);
  256. let mut tot_honest : NodeCount = 0;
  257. let mut tot_malicious : NodeCount = 0;
  258. let mut tot_last_join : TimeCount = 0;
  259. for q in self.quorums.iter() {
  260. tot_honest += q.tot_honest;
  261. tot_malicious += q.tot_malicious;
  262. tot_last_join += q.tot_last_join;
  263. }
  264. println!("quorums h={} m={} l={}", tot_honest, tot_malicious, tot_last_join);
  265. }
  266. fn collect_stats(&mut self) {
  267. if self.cur_stats.dirty {
  268. for (i, q) in self.quorums.iter().enumerate() {
  269. self.cur_stats.update(i, q, i==0);
  270. }
  271. self.cur_stats.dirty = false;
  272. }
  273. }
  274. fn update_cumstats(&mut self, force: bool) {
  275. let stat = &self.cur_stats;
  276. if force || stat.min_tot_nodes < self.cum_stats.min_tot_nodes {
  277. self.cum_stats.min_tot_nodes = stat.min_tot_nodes;
  278. }
  279. if force || stat.max_tot_nodes > self.cum_stats.max_tot_nodes {
  280. self.cum_stats.max_tot_nodes = stat.max_tot_nodes;
  281. }
  282. if force || stat.min_tot_honest < self.cum_stats.min_tot_honest {
  283. self.cum_stats.min_tot_honest = stat.min_tot_honest;
  284. }
  285. if force || stat.max_tot_honest > self.cum_stats.max_tot_honest {
  286. self.cum_stats.max_tot_honest = stat.max_tot_honest;
  287. }
  288. if force || stat.min_tot_malicious < self.cum_stats.min_tot_malicious {
  289. self.cum_stats.min_tot_malicious = stat.min_tot_malicious;
  290. }
  291. if force || stat.max_tot_malicious > self.cum_stats.max_tot_malicious {
  292. self.cum_stats.max_tot_malicious = stat.max_tot_malicious;
  293. }
  294. let min_age = (self.now<<self.lg_regions_per_quorum) - stat.max_tot_last_join;
  295. let max_age = (self.now<<self.lg_regions_per_quorum) - stat.min_tot_last_join;
  296. if force || min_age < self.cum_stats.min_age {
  297. self.cum_stats.min_age = min_age;
  298. }
  299. if force || max_age > self.cum_stats.max_age {
  300. self.cum_stats.max_age = max_age;
  301. }
  302. if force || stat.min_epsilon < self.cum_stats.min_epsilon {
  303. self.cum_stats.min_epsilon = stat.min_epsilon;
  304. }
  305. if force || stat.max_epsilon > self.cum_stats.max_epsilon {
  306. self.cum_stats.max_epsilon = stat.max_epsilon;
  307. }
  308. }
  309. }
  310. fn usage(cmd: &String) {
  311. eprintln!("Usage: {} h m lg_r lg_c iters seed", cmd);
  312. eprintln!("h: number of honest nodes");
  313. eprintln!("m: number of malicious nodes");
  314. eprintln!("lg_r: log_2 of the number of regions");
  315. eprintln!("lg_c: log_2 of the number of regions per quorum");
  316. eprintln!("iters: number of iterations after initialization");
  317. eprintln!("seed: random seed");
  318. }
  319. fn main() {
  320. let args: Vec<String> = env::args().collect();
  321. if args.len() != 7 {
  322. usage(&args[0]);
  323. process::exit(1);
  324. }
  325. let arg_h = args[1].parse::<NodeCount>();
  326. let arg_m = args[2].parse::<NodeCount>();
  327. let arg_lg_r = args[3].parse::<u8>();
  328. let arg_lg_c = args[4].parse::<u8>();
  329. let arg_iters = args[5].parse::<TimeCount>();
  330. let arg_seed = args[6].parse::<u64>();
  331. if arg_h.is_err() || arg_m.is_err() || arg_lg_r.is_err() ||
  332. arg_lg_c.is_err() || arg_iters.is_err() || arg_seed.is_err() {
  333. usage(&args[0]);
  334. process::exit(1);
  335. }
  336. // Number of honest nodes
  337. let h : NodeCount = arg_h.unwrap();
  338. // Number of malicious nodes
  339. let m : NodeCount = arg_m.unwrap();
  340. // TODO: In the cuckoo-rule, we don't care about each region.
  341. // We only care about a k-region at a time.
  342. // In commensal-cuckooing, we only care about the quorum that is being joined to.
  343. // log_2 of number of regions
  344. let lg_r : u8 = arg_lg_r.unwrap();
  345. // log_2 of number of regions per quorum (must be smaller than r)
  346. let lg_c : u8 = arg_lg_c.unwrap();
  347. // Number of iterations after initialization
  348. let iters : TimeCount = arg_iters.unwrap();
  349. // 64-bit random seed
  350. let seed : u64 = arg_seed.unwrap();
  351. if lg_c > lg_r {
  352. usage(&args[0]);
  353. process::exit(1);
  354. }
  355. let blankregion = Region {
  356. num_honest: 0,
  357. num_malicious: 0,
  358. last_join: 0
  359. };
  360. let blankquorum = Quorum {
  361. tot_honest: 0,
  362. tot_malicious: 0,
  363. tot_last_join: 0,
  364. };
  365. let mut sim = Simulation {
  366. done_init: false,
  367. now: 0,
  368. rand: SplitMix64::seed_from_u64(seed),
  369. lg_regions_per_quorum: lg_c,
  370. num_region_mask: (1<<lg_r)-1,
  371. regions: Vec::new(),
  372. quorums: Vec::new(),
  373. cur_stats: CurrentStats::default(),
  374. cum_stats: CumulativeStats::default(),
  375. };
  376. sim.regions.resize(1<<lg_r, blankregion);
  377. sim.quorums.resize(1<<(lg_r-lg_c), blankquorum);
  378. eprintln!("Starting simulation h={} m={} r={} C={} seed={}",
  379. h, m, 1<<lg_r, 1<<lg_c, seed);
  380. sim.init(h, m);
  381. for iter in 0..iters {
  382. sim.move_malicious();
  383. if iter % 100000 == 0 {
  384. eprintln!("iter {}", iter);
  385. }
  386. }
  387. print!("{} {} {} {} {} {} ", h, m, lg_r, lg_c, iters, seed);
  388. sim.cum_stats.print();
  389. }