main.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. cmp::Ordering,
  20. collections::{HashMap, HashSet},
  21. };
  22. use darkfi::util::time::Timestamp;
  23. use darkfi_serial::Encodable;
  24. use num_bigint::BigUint;
  25. use rand::prelude::SliceRandom;
  26. /// Number of random samples in each query
  27. const K: usize = 20;
  28. /// Minimum number of votes to count as a successful query
  29. const ALPHA: usize = 14;
  30. /// Consecutive successful queries required for consensus
  31. const BETA: usize = 20;
  32. // Security and network dynamics related constants
  33. /// Amount of nodes in the created network
  34. const NETWORK_SIZE: usize = 100;
  35. // A node can produce a max of 10 messages per cycle
  36. //const MAX_MESSAGE_RATE: usize = 10;
  37. /// A node that produces >5 malformed messages is considered malicious
  38. const MALICIOUS_THRESHOLD: usize = 5;
  39. /// A node only gossips to 10 random peers
  40. const GOSSIP_SIZE: usize = 10;
  41. /// 2% probability that a node goes offline
  42. const NODE_OFFLINE_PROB: f64 = 0.02;
  43. /// 5% probability that a node comes back online
  44. const NODE_ONLINE_PROB: f64 = 0.05;
  45. /// 5% probability that a node becomes malicious
  46. const NODE_MALICIOUS_PROB: f64 = 0.05;
  47. /// Maximum storage capacity for each node in terms of number of messages
  48. const MAX_STORAGE_CAPACITY: usize = 500;
  49. struct Metrics {
  50. offline_nodes: Vec<usize>,
  51. malicious_nodes: Vec<usize>,
  52. malformed_messages: usize,
  53. messages_stored: HashMap<usize, usize>,
  54. }
  55. impl Metrics {
  56. fn new() -> Self {
  57. Metrics {
  58. offline_nodes: vec![],
  59. malicious_nodes: vec![],
  60. malformed_messages: 0,
  61. messages_stored: HashMap::new(),
  62. }
  63. }
  64. // Utility functions to update metrics
  65. fn increment_malformed(&mut self) {
  66. self.malformed_messages += 1;
  67. }
  68. fn update_stored_messages(&mut self, node_id: usize, count: usize) {
  69. self.messages_stored.insert(node_id, count);
  70. }
  71. }
  72. #[derive(Hash, Clone, Eq, PartialEq, Debug)]
  73. struct Message {
  74. timestamp: Timestamp,
  75. content: String,
  76. // The IDs of previous messages
  77. references: Vec<blake3::Hash>,
  78. }
  79. impl Message {
  80. fn id(&self) -> blake3::Hash {
  81. let mut hasher = blake3::Hasher::new();
  82. self.timestamp.encode(&mut hasher).unwrap();
  83. self.content.encode(&mut hasher).unwrap();
  84. for reference in &self.references {
  85. reference.as_bytes().encode(&mut hasher).unwrap();
  86. }
  87. hasher.finalize()
  88. }
  89. }
  90. #[derive(Clone, Eq, PartialEq)]
  91. struct SnowballNode {
  92. id: usize,
  93. malicious_counter: usize,
  94. online: bool,
  95. malicious: bool,
  96. preference: Option<Message>,
  97. message_votes: HashMap<Message, usize>,
  98. counts: HashMap<Message, usize>,
  99. dag: HashMap<blake3::Hash, Message>,
  100. orphan_pool: Vec<Message>,
  101. finalized_preference: Option<Message>,
  102. }
  103. impl std::hash::Hash for SnowballNode {
  104. fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
  105. self.id.hash(state);
  106. }
  107. }
  108. impl SnowballNode {
  109. fn new(id: usize) -> Self {
  110. SnowballNode {
  111. id,
  112. malicious_counter: 0,
  113. online: true,
  114. malicious: false,
  115. preference: None,
  116. message_votes: HashMap::new(),
  117. counts: HashMap::new(),
  118. dag: HashMap::new(),
  119. orphan_pool: vec![],
  120. finalized_preference: None,
  121. }
  122. }
  123. fn is_malicious(&self) -> bool {
  124. self.malicious || self.malicious_counter > MALICIOUS_THRESHOLD
  125. }
  126. fn query(&self, network: &HashMap<usize, SnowballNode>) -> Option<Message> {
  127. let mut sample_votes = HashMap::new();
  128. for _ in 0..K {
  129. // Get a random node
  130. let node = &network[&(rand::random::<usize>() % NETWORK_SIZE)];
  131. if let Some(pref) = &node.preference {
  132. *sample_votes.entry(pref.clone()).or_insert(0) += 1;
  133. }
  134. }
  135. sample_votes.into_iter().max_by_key(|&(_, count)| count).map(|(message, _)| message)
  136. }
  137. fn receive_vote(&mut self, from_node: &SnowballNode, message: &Message) {
  138. // Request any missing parent messages
  139. let missing_parents = self.request_missing_references(from_node, message);
  140. for parent_msg in missing_parents {
  141. if self.validate_message(&parent_msg) {
  142. self.add_to_dag(&parent_msg);
  143. } else {
  144. // The parent message is invalid.
  145. self.malicious_counter += 1;
  146. return
  147. }
  148. }
  149. *self.message_votes.entry(message.clone()).or_insert(0) += 1;
  150. if message.references.iter().all(|ref_id| self.dag.contains_key(ref_id)) {
  151. self.add_to_dag(message);
  152. } else {
  153. self.orphan_pool.push(message.clone());
  154. }
  155. }
  156. fn request_missing_references(
  157. &self,
  158. from_node: &SnowballNode,
  159. message: &Message,
  160. ) -> Vec<Message> {
  161. let mut missing_refs = Vec::new();
  162. for ref_id in &message.references {
  163. if !self.dag.contains_key(ref_id) {
  164. if let Some(parent_msg) = from_node.dag.get(ref_id) {
  165. missing_refs.push(parent_msg.clone());
  166. }
  167. }
  168. }
  169. missing_refs
  170. }
  171. fn update_preference(&mut self) {
  172. let mut max_message = None;
  173. let mut max_count: usize = 0;
  174. let mut max_timestamp = Timestamp::current_time();
  175. let mut max_target = BigUint::from_bytes_be(&[0xff; 32]);
  176. for (message, &vote_count) in self.message_votes.iter() {
  177. let is_better = match vote_count.cmp(&max_count) {
  178. Ordering::Greater => true,
  179. Ordering::Equal => match message.timestamp.0.cmp(&max_timestamp.0) {
  180. Ordering::Less => true,
  181. Ordering::Equal => {
  182. let message_target = BigUint::from_bytes_be(message.id().as_bytes());
  183. message_target < max_target
  184. }
  185. Ordering::Greater => false,
  186. },
  187. Ordering::Less => false,
  188. };
  189. if is_better {
  190. max_count = vote_count;
  191. max_message = Some(message.clone());
  192. max_timestamp = message.timestamp;
  193. max_target = BigUint::from_bytes_be(message.id().as_bytes());
  194. }
  195. }
  196. if let Some(max_message) = max_message {
  197. if max_count >= ALPHA {
  198. *self.counts.entry(max_message.clone()).or_insert(0) += 1;
  199. if self.counts[&max_message] >= BETA {
  200. // Setting the finalized preference if not already set
  201. if self.finalized_preference.is_none() {
  202. self.finalized_preference = Some(max_message.clone());
  203. //println!(
  204. // "Node {} finalized preference to message {}",
  205. // self.id, max_message.content
  206. //);
  207. }
  208. self.preference = Some(max_message);
  209. }
  210. } else {
  211. self.counts.insert(max_message, 0);
  212. }
  213. }
  214. }
  215. fn add_to_dag(&mut self, msg: &Message) {
  216. self.dag.insert(msg.id(), msg.clone());
  217. self.check_orphan_pool();
  218. }
  219. fn check_orphan_pool(&mut self) {
  220. let mut i = 0;
  221. while i < self.orphan_pool.len() {
  222. if self.orphan_pool[i].references.iter().all(|ref_id| self.dag.contains_key(ref_id)) {
  223. let msg = self.orphan_pool.remove(i);
  224. self.add_to_dag(&msg);
  225. } else {
  226. i += 1;
  227. }
  228. }
  229. }
  230. fn random_references(&self) -> Vec<blake3::Hash> {
  231. let mut references = vec![];
  232. let keys: Vec<blake3::Hash> = self.dag.keys().cloned().collect();
  233. if !keys.is_empty() {
  234. // Up to 2 references
  235. for _ in 0..rand::random::<usize>() % 3 {
  236. let random_ref = keys[rand::random::<usize>() % keys.len()];
  237. if !references.contains(&random_ref) {
  238. references.push(random_ref);
  239. }
  240. }
  241. }
  242. references
  243. }
  244. fn act_malicious(&mut self, network: &HashMap<usize, SnowballNode>) -> Option<Message> {
  245. if rand::random::<f64>() < 0.7 {
  246. // 70% chance to send a malformed message
  247. let references = self.random_references();
  248. let malformed_msg = Message {
  249. timestamp: Timestamp::current_time(),
  250. content: format!("Malformed {}", rand::random::<usize>() % 1000),
  251. references,
  252. };
  253. return Some(malformed_msg)
  254. } else {
  255. // 30% chance to change preference rapidly
  256. if let Some(vote) = self.query(network) {
  257. self.preference = Some(vote);
  258. }
  259. }
  260. None
  261. }
  262. fn validate_message(&self, message: &Message) -> bool {
  263. // In our example, simply checking if the content starts with "Malformed"
  264. !message.content.starts_with("Malformed")
  265. }
  266. fn prune_old_messages(&mut self) {
  267. if self.dag.len() > MAX_STORAGE_CAPACITY {
  268. // Here we're just removing random messages, but in a real-world application,
  269. // more sophisticated policies would be needed.
  270. let random_key =
  271. *self.dag.keys().nth(rand::random::<usize>() % self.dag.len()).unwrap();
  272. self.dag.remove(&random_key);
  273. }
  274. }
  275. }
  276. fn main() {
  277. let mut network: HashMap<usize, SnowballNode> = HashMap::new();
  278. let mut offline_nodes: HashSet<usize> = HashSet::new();
  279. let mut metrics = Metrics::new();
  280. // Genesis message
  281. let genesis = Message {
  282. timestamp: Timestamp::current_time(),
  283. content: String::from("Genesis"),
  284. references: vec![],
  285. };
  286. // Initialize nodes and add the genesis message to each node's DAG
  287. for i in 0..NETWORK_SIZE {
  288. let mut node = SnowballNode::new(i);
  289. node.add_to_dag(&genesis);
  290. node.online = rand::random::<f64>() < NODE_ONLINE_PROB;
  291. node.malicious = rand::random::<f64>() < NODE_MALICIOUS_PROB;
  292. network.insert(i, node);
  293. }
  294. for _ in 0..1000 {
  295. // Simulate network dynamics
  296. for idx in 0..NETWORK_SIZE {
  297. if rand::random::<f64>() < NODE_OFFLINE_PROB && !offline_nodes.contains(&idx) {
  298. offline_nodes.insert(idx);
  299. network.get_mut(&idx).unwrap().online = false;
  300. //println!("Node {} went offline", idx);
  301. } else if rand::random::<f64>() < NODE_ONLINE_PROB && offline_nodes.contains(&idx) {
  302. offline_nodes.remove(&idx);
  303. network.get_mut(&idx).unwrap().online = true;
  304. //println!("Node {} came online", idx);
  305. }
  306. }
  307. metrics.offline_nodes.push(offline_nodes.len());
  308. metrics
  309. .malicious_nodes
  310. .push(network.iter().filter(|(_, node)| node.is_malicious()).count());
  311. // This simulates concurrent conflicting messages being sent
  312. // Up to 5 nodes may produce messages concurrently:
  313. let number_of_messages = rand::random::<usize>() % 5;
  314. for _ in 0..number_of_messages {
  315. let random_node_index = rand::random::<usize>() % NETWORK_SIZE;
  316. if let Some(node) = network.get_mut(&random_node_index) {
  317. if !node.is_malicious() && node.online {
  318. //println!("Node {} created a message", random_node_index);
  319. let references = node.random_references();
  320. let msg = Message {
  321. timestamp: Timestamp::current_time(),
  322. content: format!("Message {}", rand::random::<usize>() % 1000),
  323. references,
  324. };
  325. node.add_to_dag(&msg);
  326. node.preference = Some(msg.clone());
  327. }
  328. }
  329. }
  330. // Nodes may act maliciously
  331. for node in network.clone().values_mut() {
  332. if node.is_malicious() && node.online {
  333. if let Some(malformed_msg) = node.act_malicious(&network) {
  334. // Disseminate the malformed message
  335. let mut node_indices: Vec<usize> = network.keys().cloned().collect();
  336. node_indices.shuffle(&mut rand::thread_rng());
  337. for &idx in node_indices.iter().take(GOSSIP_SIZE) {
  338. if let Some(other_node) = network.get_mut(&idx) {
  339. if !other_node.is_malicious() && other_node.online {
  340. other_node.receive_vote(node, &malformed_msg);
  341. }
  342. }
  343. }
  344. metrics.increment_malformed();
  345. }
  346. }
  347. }
  348. for node in network.clone().values() {
  349. if node.online {
  350. if let Some(vote) = node.query(&network) {
  351. // Add random delay before disseminating
  352. //std::thread::sleep(std::time::Duration::from_millis(rand::random::<u64>() % 100));
  353. let mut node_indices: Vec<usize> = network.keys().cloned().collect();
  354. node_indices.shuffle(&mut rand::thread_rng());
  355. // Implementing gossip protocol
  356. for &idx in node_indices.iter().take(GOSSIP_SIZE) {
  357. if let Some(other_node) = network.get_mut(&idx) {
  358. if other_node.validate_message(&vote) {
  359. if !other_node.is_malicious() && other_node.online {
  360. other_node.receive_vote(node, &vote);
  361. }
  362. } else {
  363. // Increase malicious counter if a malformed message is received
  364. other_node.malicious_counter += 1;
  365. }
  366. }
  367. }
  368. }
  369. }
  370. }
  371. for node in network.values_mut() {
  372. if node.online {
  373. node.update_preference();
  374. }
  375. node.prune_old_messages();
  376. metrics.update_stored_messages(node.id, node.dag.len());
  377. }
  378. }
  379. // Check the state of the network
  380. let consensus_count = network.iter().filter(|(_, node)| node.preference.is_some()).count();
  381. println!("Number of nodes that reached consensus: {}", consensus_count);
  382. let finalized_count =
  383. network.iter().filter(|(_, node)| node.finalized_preference.is_some()).count();
  384. println!("Number of nodes that reached explicit finality: {}", finalized_count);
  385. //println!("Total malformed messages detected: {}", metrics.malformed_messages);
  386. //println!("Malicious nodes per cycle: {:?}", metrics.malicious_nodes);
  387. //println!("Offline nodes per cycle: {:?}", metrics.offline_nodes);
  388. //println!("Messages stored by node per cycle: {:?}", metrics.messages_stored);
  389. }