// Copyright (C) 2017-2018 The Duniter Project Developers. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . //! Experimental implementation of the Web of Trust in a more "rusty" style. use super::{HasLinkResult, NewLinkResult, RemLinkResult}; use rayon::prelude::*; use std::collections::HashSet; use NodeId; use WebOfTrust; /// A node in the `WoT` graph. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] struct Node { /// Is this node enabled ? enabled: bool, /// Set of links this node is the target. links_source: HashSet, /// Number of links the node issued. issued_count: usize, } impl Node { /// Create a new node. pub fn new() -> Node { Node { enabled: true, links_source: HashSet::new(), issued_count: 0, } } } /// A more idiomatic implementation of a Web of Trust. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct RustyWebOfTrust { /// List of nodes in the WoT. nodes: Vec, /// Maximum number of links a node can issue. max_links: usize, } impl Default for RustyWebOfTrust { fn default() -> RustyWebOfTrust { RustyWebOfTrust { nodes: Vec::new(), max_links: 4_000_000_000, } } } impl WebOfTrust for RustyWebOfTrust { fn new(max_links: usize) -> RustyWebOfTrust { RustyWebOfTrust { nodes: vec![], max_links, } } fn get_max_link(&self) -> usize { self.max_links } fn set_max_link(&mut self, max_links: usize) { self.max_links = max_links; } fn add_node(&mut self) -> NodeId { self.nodes.push(Node::new()); NodeId(self.nodes.len() - 1) } fn rem_node(&mut self) -> Option { self.nodes.pop(); if !self.nodes.is_empty() { Some(NodeId(self.nodes.len() - 1)) } else { None } } fn size(&self) -> usize { self.nodes.len() } fn is_enabled(&self, id: NodeId) -> Option { self.nodes.get(id.0).map(|n| n.enabled) } fn set_enabled(&mut self, id: NodeId, enabled: bool) -> Option { self.nodes .get_mut(id.0) .map(|n| n.enabled = enabled) .map(|_| enabled) } fn get_enabled(&self) -> Vec { self.nodes .par_iter() .enumerate() .filter(|&(_, n)| n.enabled) .map(|(i, _)| NodeId(i)) .collect() } fn get_disabled(&self) -> Vec { self.nodes .par_iter() .enumerate() .filter(|&(_, n)| !n.enabled) .map(|(i, _)| NodeId(i)) .collect() } fn add_link(&mut self, source: NodeId, target: NodeId) -> NewLinkResult { if source == target { NewLinkResult::SelfLinkingForbidden() } else if source.0 >= self.size() { NewLinkResult::UnknownSource() } else if target.0 >= self.size() { NewLinkResult::UnknownTarget() } else if self.nodes[source.0].issued_count >= self.max_links { NewLinkResult::AllCertificationsUsed(self.nodes[target.0].links_source.len()) } else if self.nodes[target.0].links_source.contains(&source) { NewLinkResult::AlreadyCertified(self.nodes[target.0].links_source.len()) } else { self.nodes[source.0].issued_count += 1; self.nodes[target.0].links_source.insert(source); NewLinkResult::Ok(self.nodes[target.0].links_source.len()) } } fn rem_link(&mut self, source: NodeId, target: NodeId) -> RemLinkResult { if source.0 >= self.size() { RemLinkResult::UnknownSource() } else if target.0 >= self.size() { RemLinkResult::UnknownTarget() } else if !self.nodes[target.0].links_source.contains(&source) { RemLinkResult::UnknownCert(self.nodes[target.0].links_source.len()) } else { self.nodes[source.0].issued_count -= 1; self.nodes[target.0].links_source.remove(&source); RemLinkResult::Removed(self.nodes[target.0].links_source.len()) } } fn has_link(&self, source: NodeId, target: NodeId) -> HasLinkResult { if source.0 >= self.size() { HasLinkResult::UnknownSource() } else if target.0 >= self.size() { HasLinkResult::UnknownTarget() } else { HasLinkResult::Link(self.nodes[target.0].links_source.contains(&source)) } } fn get_links_source(&self, target: NodeId) -> Option> { self.nodes .get(target.0) .map(|n| n.links_source.iter().cloned().collect()) } fn issued_count(&self, id: NodeId) -> Option { self.nodes.get(id.0).map(|n| n.issued_count) } fn is_sentry(&self, node: NodeId, sentry_requirement: usize) -> Option { if node.0 >= self.size() { return None; } let node = &self.nodes[node.0]; Some( node.enabled && node.issued_count >= sentry_requirement && node.links_source.len() >= sentry_requirement, ) } fn get_sentries(&self, sentry_requirement: usize) -> Vec { self.nodes .par_iter() .enumerate() .filter(|&(_, n)| { n.enabled && n.issued_count >= sentry_requirement && n.links_source.len() >= sentry_requirement }) .map(|(i, _)| NodeId(i)) .collect() } fn get_non_sentries(&self, sentry_requirement: usize) -> Vec { self.nodes .par_iter() .enumerate() .filter(|&(_, n)| { n.enabled && (n.issued_count < sentry_requirement || n.links_source.len() < sentry_requirement) }) .map(|(i, _)| NodeId(i)) .collect() } } #[cfg(test)] mod tests { use super::*; use tests::generic_wot_test; #[test] fn wot_tests() { generic_wot_test::(); } }