duniter_rs_wotb/
lib.rs

1//! `duniter-rs-wotb` is a crate making **Web of Trust** computations for
2//! the [Duniter](https://github.com/duniter) project.
3//!
4//! It defines a structure storing a Web of Trust (WoT).
5//!
6//! WoT test is translated from [duniter/wotb Javascript test]
7//! (https://github.com/duniter/wotb/blob/master/wotcpp/webOfTrust.cpp).
8//!
9//! Larger WoT test is missing and will only be made in the Javascript wrapper of this
10//! module to be sure it's fully compatible with the actual
11//! [duniter-ts](https://github.com/duniter/duniter) software.
12
13#![deny(missing_docs,
14        missing_debug_implementations, missing_copy_implementations,
15        trivial_casts, trivial_numeric_casts,
16        unsafe_code,
17        unstable_features,
18        unused_import_braces, unused_qualifications)]
19
20extern crate serde;
21#[macro_use]
22extern crate serde_derive;
23extern crate bincode;
24
25use std::ops::Deref;
26use std::collections::HashSet;
27use std::collections::hash_set::Iter;
28use std::rc::Rc;
29use std::fs::File;
30use std::io::prelude::*;
31
32use bincode::{serialize, deserialize, Infinite};
33
34/// Wrapper for a node id.
35#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
36pub struct NodeId(pub usize);
37
38impl Deref for NodeId {
39    type Target = usize;
40
41    fn deref(&self) -> &Self::Target {
42        &self.0
43    }
44}
45
46/// Results of a certification, with the current certification count
47/// of the destination as parameter.
48#[derive(Debug, Copy, Clone, PartialEq, Eq)]
49pub enum NewLinkResult {
50    /// Certification worked.
51    Ok(usize),
52    /// This certification already exist.
53    AlreadyCertified(usize),
54    /// All available certifications has been used.
55    AllCertificationsUsed(usize),
56    /// Unknown source.
57    UnknownSource(),
58    /// Unknown target.
59    UnknownTarget(),
60    /// Self linking is forbidden.
61    SelfLinkingForbidden(),
62}
63
64/// Results of a certification removal, with the current certification count
65/// of the destination as parameter.
66#[derive(Debug, Copy, Clone, PartialEq, Eq)]
67pub enum RemovedLinkResult {
68    /// Certification has been removed.
69    Removed(usize),
70    /// Requested certification doesn't exist.
71    UnknownCert(usize),
72    /// Unknown source.
73    UnknownSource(),
74    /// Unknown target.
75    UnknownTarget(),
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79struct Node {
80    id: NodeId,
81    /// Is the node enabled ?
82    pub enabled: bool,
83    certs: HashSet<NodeId>,
84    issued_count: usize,
85}
86
87impl Node {
88    /// Create a new node.
89    pub fn new(id: usize) -> Node {
90        Node {
91            id: NodeId(id),
92            enabled: true,
93            certs: HashSet::new(),
94            issued_count: 0,
95        }
96    }
97
98    /// Getter of node id.
99    pub fn id(&self) -> NodeId {
100        self.id
101    }
102
103    /// Add a certification from this node to the given node.
104    ///
105    /// Certification will fail if this node already used all its certs.
106    pub fn link_to(&mut self, to: &mut Node, max_certs: usize) -> NewLinkResult {
107        if self.issued_count >= max_certs {
108            NewLinkResult::AllCertificationsUsed(to.certs.len())
109        } else if to.certs.contains(&self.id()) {
110            NewLinkResult::AlreadyCertified(to.certs.len())
111        } else {
112            to.certs.insert(self.id());
113            self.issued_count += 1;
114            NewLinkResult::Ok(to.certs.len())
115        }
116    }
117
118    /// Remove a certification (if it exist) from this node to the given node.
119    pub fn unlink_to(&mut self, to: &mut Node) -> RemovedLinkResult {
120        if to.certs.contains(&self.id()) {
121            to.certs.remove(&self.id());
122            self.issued_count -= 1;
123            RemovedLinkResult::Removed(to.certs.len())
124        } else {
125            RemovedLinkResult::UnknownCert(to.certs.len())
126        }
127    }
128
129    /// Tells if this node has a link from the given node.
130    pub fn has_link_from(&self, from: &Node) -> bool {
131        self.certs.contains(&from.id())
132    }
133
134    /// Tells if this node has a link to the given node.
135    pub fn has_link_to(&self, to: &Node) -> bool {
136        to.has_link_from(&self)
137    }
138
139    /// Give an iterator of node certs.
140    pub fn links_iter(&self) -> Iter<NodeId> {
141        self.certs.iter()
142    }
143
144    /// Getter of the issued count.
145    pub fn issued_count(&self) -> usize {
146        self.issued_count
147    }
148}
149
150/// Results of WebOfTrust::compute_distance.
151#[derive(Debug, Copy, Clone, PartialEq, Eq)]
152pub struct WotDistance {
153    /// Sentries count
154    pub sentries: u32,
155    /// Success count
156    pub success: u32,
157    /// Reached count
158    pub reached: u32,
159    /// Is the node outdistanced ?
160    pub outdistanced: bool,
161}
162
163#[derive(Debug)]
164struct WotStep {
165    pub previous: Option<Rc<Box<WotStep>>>,
166    pub node: NodeId,
167    pub distance: u32,
168}
169
170/// Store a Web of Trust.
171///
172/// Allow to create/remove nodes and links between them.
173///
174/// It provides methods to find sentries nodes, find all paths
175/// between 2 nodes and to compute distances in the web.
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct WebOfTrust {
178    nodes: Vec<Node>,
179    /// Maxiumum amout of certifications a node can provide.
180    ///
181    /// It can be changed afterward, and will be accounted for every future calculations.
182    ///
183    /// In practice it should not change after initialization.
184    pub max_cert: usize,
185}
186
187impl WebOfTrust {
188    /// Create a new Web of Trust with the maxium certificications count.
189    pub fn new(max_cert: usize) -> WebOfTrust {
190        WebOfTrust {
191            nodes: vec![],
192            max_cert,
193        }
194    }
195
196    /// Read WoT from file.
197    pub fn from_file(path: &str) -> Option<WebOfTrust> {
198        let mut file = match File::open(path) {
199            Ok(file) => file,
200            Err(_) => return None,
201        };
202
203        let mut content: Vec<u8> = vec![];
204        if let Err(_) = file.read_to_end(&mut content) {
205            return None;
206        }
207
208        match deserialize::<WebOfTrust>(&content[..]) {
209            Ok(wot) => Some(wot),
210            Err(_) => None,
211        }
212    }
213
214    /// Write WoT to file.
215    pub fn to_file(&self, path: &str) -> bool {
216        let encoded: Vec<u8> = serialize(self, Infinite).unwrap();
217
218        match File::create(path) {
219            Ok(mut buffer) => buffer.write_all(&encoded).is_ok(),
220            Err(_) => false,
221        }
222    }
223
224    /// Add a new node.
225    pub fn add_node(&mut self) -> NodeId {
226        let node_id = self.nodes.len();
227        self.nodes.push(Node::new(node_id));
228
229        NodeId(node_id)
230    }
231
232    /// Remove given node if it exits.
233    pub fn remove_node(&mut self) -> Option<NodeId> {
234        self.nodes.pop();
235
236        if self.nodes.len() > 0 {
237            Some(NodeId(self.nodes.iter().len() - 1))
238        } else {
239            None
240        }
241    }
242
243    fn check_matches(&self, node: NodeId, d: u32, d_max: u32, mut checked: Vec<bool>) -> Vec<bool> {
244        let mut linked_nodes = Vec::new();
245
246        for linked_node in self.nodes[*node].links_iter() {
247            checked[**linked_node] = true;
248            linked_nodes.push(*linked_node);
249        }
250
251        if d < d_max {
252            for linked_node in linked_nodes.iter() {
253                checked = self.check_matches(*linked_node, d + 1, d_max, checked);
254            }
255        }
256
257        checked
258    }
259
260    /// Compute distance between a node and the network.
261    pub fn compute_distance(
262        &self,
263        member: NodeId,
264        d_min: u32,
265        k_max: u32,
266        x_percent: f64,
267    ) -> WotDistance {
268        let d_min = d_min as usize;
269
270        let mut result = WotDistance {
271            sentries: 0,
272            success: 0,
273            reached: 0,
274            outdistanced: false,
275        };
276
277        let mut sentries: Vec<bool> = self.nodes
278            .iter()
279            .map(|x| {
280                x.enabled && x.issued_count() >= d_min && x.links_iter().count() >= d_min
281            })
282            .collect();
283        sentries[*member] = false;
284
285        let mut checked: Vec<bool> = self.nodes.iter().map(|_| false).collect();
286
287        if k_max >= 1 {
288            checked = self.check_matches(member, 1, k_max, checked);
289        }
290
291        for (&sentry, &check) in sentries.iter().zip(checked.iter()) {
292            if sentry {
293                result.sentries += 1;
294                if check {
295                    result.success += 1;
296                    result.reached += 1;
297                }
298            } else {
299                if check {
300                    result.reached += 1;
301                }
302            }
303        }
304
305        result.outdistanced = (result.success as f64) < x_percent * (result.sentries as f64);
306        result
307    }
308
309    /// Get sentries array.
310    pub fn get_sentries(&self, d_min: usize) -> Vec<NodeId> {
311        self.nodes
312            .iter()
313            .filter(|x| {
314                x.enabled && x.issued_count() >= d_min && x.links_iter().count() >= d_min
315            })
316            .map(|x| x.id())
317            .collect()
318    }
319
320    /// Get non sentries array.
321    pub fn get_non_sentries(&self, d_min: usize) -> Vec<NodeId> {
322        self.nodes
323            .iter()
324            .filter(|x| {
325                x.enabled && (x.issued_count < d_min || x.links_iter().count() < d_min)
326            })
327            .map(|x| x.id())
328            .collect()
329    }
330
331    /// Get disabled array.
332    pub fn get_disabled(&self) -> Vec<NodeId> {
333        self.nodes
334            .iter()
335            .filter(|x| !x.enabled)
336            .map(|x| x.id())
337            .collect()
338    }
339
340    fn lookup(
341        &self,
342        source: NodeId,
343        target: NodeId,
344        distance: u32,
345        distance_max: u32,
346        previous: Rc<Box<WotStep>>,
347        paths: &mut Vec<Rc<Box<WotStep>>>,
348        matching_paths: &mut Vec<Rc<Box<WotStep>>>,
349        distances: &mut Vec<u32>,
350    ) {
351        if source != target && distance <= distance_max {
352            let mut local_paths: Vec<Rc<Box<WotStep>>> = vec![];
353
354            for &by in self.nodes[*target].links_iter() {
355                if distance < distances[*by] {
356                    distances[*by] = distance;
357                    let step = Rc::new(Box::new(WotStep {
358                        previous: Some(previous.clone()),
359                        node: by,
360                        distance,
361                    }));
362
363                    paths.push(step.clone());
364                    local_paths.push(step.clone());
365                    if by == source {
366                        matching_paths.push(step.clone());
367                    }
368                }
369            }
370
371            if distance <= distance_max {
372                for path in local_paths.iter() {
373                    self.lookup(
374                        source,
375                        path.node,
376                        distance + 1,
377                        distance_max,
378                        path.clone(),
379                        paths,
380                        matching_paths,
381                        distances,
382                    );
383                }
384            }
385        }
386    }
387
388    /// Get paths from one node to the other.
389    pub fn get_paths(&self, from: NodeId, to: NodeId, k_max: u32) -> Vec<Vec<NodeId>> {
390        let mut paths: Vec<Rc<Box<WotStep>>> = vec![];
391        let mut matching_paths: Vec<Rc<Box<WotStep>>> = vec![];
392        let mut distances: Vec<u32> = self.nodes.iter().map(|_| k_max + 1).collect();
393        distances[*to] = 0;
394
395        let root = Rc::new(Box::new(WotStep {
396            previous: None,
397            node: to,
398            distance: 0,
399        }));
400
401        paths.push(root.clone());
402
403        self.lookup(
404            from,
405            to,
406            1,
407            k_max,
408            root,
409            &mut paths,
410            &mut matching_paths,
411            &mut distances,
412        );
413
414        let mut result: Vec<Vec<NodeId>> = Vec::with_capacity(matching_paths.len());
415
416        for step in matching_paths.iter() {
417            let mut vecpath = vec![];
418            let mut step = step.clone();
419
420            loop {
421                vecpath.push(step.node);
422                if step.previous.is_none() {
423                    break;
424                }
425                step = step.previous.clone().unwrap();
426            }
427
428            result.push(vecpath);
429        }
430
431        result
432    }
433
434    /// Number of nodes in the WoT.
435    pub fn size(&self) -> usize {
436        self.nodes.iter().count()
437    }
438
439    /// Tells if requested node is enabled (None if doesn't exist).
440    pub fn is_enabled(&self, node: NodeId) -> Option<bool> {
441        if *node >= self.size() {
442            None
443        } else {
444            Some(self.nodes[*node].enabled)
445        }
446    }
447
448    /// Set if a node is enabled.
449    pub fn set_enabled(&mut self, node: NodeId, state: bool) -> Option<bool> {
450        if *node >= self.size() {
451            None
452        } else {
453            self.nodes[*node].enabled = state;
454            Some(state)
455        }
456    }
457
458    /// Add link from a node to another.
459    pub fn add_link(&mut self, from: NodeId, to: NodeId) -> NewLinkResult {
460        if *from == *to {
461            NewLinkResult::SelfLinkingForbidden()
462        } else if *from >= self.size() {
463            NewLinkResult::UnknownSource()
464        } else if *to >= self.size() {
465            NewLinkResult::UnknownTarget()
466        } else {
467            if *from < *to {
468                // split `nodes` in two part to allow borrowing 2 nodes at the same time
469                let (start, end) = self.nodes.split_at_mut(*to);
470                start[*from].link_to(&mut end[0], self.max_cert)
471            } else {
472                // split `nodes` in two part to allow borrowing 2 nodes at the same time
473                let (start, end) = self.nodes.split_at_mut(*from);
474                end[0].link_to(&mut start[*to], self.max_cert)
475            }
476        }
477    }
478
479    /// Remove a link from a node to another.
480    pub fn remove_link(&mut self, from: NodeId, to: NodeId) -> RemovedLinkResult {
481        if *from >= self.size() {
482            RemovedLinkResult::UnknownSource()
483        } else if *to >= self.size() {
484            RemovedLinkResult::UnknownTarget()
485        } else {
486            if *from < *to {
487                // split `nodes` in two part to allow borrowing 2 nodes at the same time
488                let (start, end) = self.nodes.split_at_mut(*to);
489                start[*from].unlink_to(&mut end[0])
490            } else {
491                // split `nodes` in two part to allow borrowing 2 nodes at the same time
492                let (start, end) = self.nodes.split_at_mut(*from);
493                end[0].unlink_to(&mut start[*to])
494            }
495        }
496    }
497
498    /// Test if a link exist from a node to another.
499    pub fn exists_link(&self, from: NodeId, to: NodeId) -> bool {
500        if *from >= self.size() || *to >= self.size() {
501            false
502        } else {
503            self.nodes[*from].has_link_to(&self.nodes[*to])
504        }
505    }
506
507    /// Test if a node is outdistanced in the network.
508    pub fn is_outdistanced(
509        &self,
510        node: NodeId,
511        d_min: u32,
512        d_max: u32,
513        x_percent: f64,
514    ) -> Option<bool> {
515        if *node >= self.size() {
516            None
517        } else {
518            Some(
519                self.compute_distance(node, d_min, d_max, x_percent)
520                    .outdistanced,
521            )
522        }
523    }
524}
525
526#[cfg(test)]
527mod tests {
528    use super::*;
529
530    #[test]
531    fn node_tests() {
532        // Correct node id
533        assert_eq!(*Node::new(1).id(), 1);
534
535        // Create 2 nodes
536        let mut node1 = Node::new(1);
537        let mut node2 = Node::new(2);
538
539        // Default value
540        assert_eq!(node1.issued_count(), 0);
541        assert_eq!(node2.links_iter().count(), 0);
542        assert!(!node1.has_link_to(&node2));
543        assert!(!node2.has_link_to(&node2));
544        assert!(!node1.has_link_from(&node1));
545        assert!(!node2.has_link_from(&node1));
546
547        // New link 1 -> 2
548        match node1.link_to(&mut node2, 10) {
549            NewLinkResult::Ok(1) => (),
550            _ => panic!(),
551        };
552
553        assert_eq!(node1.issued_count(), 1);
554        assert_eq!(node2.links_iter().count(), 1);
555        assert!(node1.has_link_to(&node2));
556        assert!(!node2.has_link_to(&node2));
557        assert!(!node1.has_link_from(&node1));
558        assert!(node2.has_link_from(&node1));
559
560        // Existing link 1 -> 2
561        match node1.link_to(&mut node2, 10) {
562            NewLinkResult::AlreadyCertified(1) => (),
563            _ => panic!(),
564        };
565
566        assert_eq!(node1.issued_count(), 1);
567        assert_eq!(node2.links_iter().count(), 1);
568        assert!(node1.has_link_to(&node2));
569        assert!(!node2.has_link_to(&node2));
570        assert!(!node1.has_link_from(&node1));
571        assert!(node2.has_link_from(&node1));
572
573        // Max certification count
574        let mut node3 = Node::new(3);
575        match node1.link_to(&mut node3, 1) {
576            NewLinkResult::AllCertificationsUsed(0) => (),
577            _ => panic!(),
578        };
579
580        assert_eq!(node1.issued_count(), 1);
581        assert_eq!(node2.links_iter().count(), 1);
582        assert_eq!(node3.links_iter().count(), 0);
583        assert!(node1.has_link_to(&node2));
584        assert!(!node2.has_link_to(&node2));
585        assert!(!node1.has_link_from(&node1));
586        assert!(node2.has_link_from(&node1));
587    }
588
589    /// This test is a translation of https://github.com/duniter/wotb/blob/master/tests/test.js
590    #[test]
591    fn wot_tests() {
592        let mut wot = WebOfTrust::new(3);
593
594        // should have an initial size of 0
595        assert_eq!(wot.size(), 0);
596
597        // should return `None()` if testing `is_enabled()` with out-of-bounds node
598        assert_eq!(wot.is_enabled(NodeId(0)), None);
599        assert_eq!(wot.is_enabled(NodeId(23)), None);
600
601        // should give nomber 0 if we add a node
602        // - add a node
603        assert_eq!(wot.add_node(), NodeId(0));
604        assert_eq!(wot.size(), 1);
605        assert_eq!(wot.get_disabled().len(), 0);
606
607        // - add another
608        assert_eq!(wot.add_node(), NodeId(1));
609        assert_eq!(wot.size(), 2);
610        assert_eq!(wot.get_disabled().len(), 0);
611
612        // - add 10 nodes
613        for i in 0..10 {
614            assert_eq!(wot.add_node(), NodeId(i + 2));
615        }
616
617        assert_eq!(wot.size(), 12);
618
619        // shouldn't be able to self cert
620        assert_eq!(
621            wot.add_link(NodeId(0), NodeId(0)),
622            NewLinkResult::SelfLinkingForbidden()
623        );
624
625        // should add certs only in the boundaries of max_cert
626        assert_eq!(wot.add_link(NodeId(0), NodeId(1)), NewLinkResult::Ok(1));
627        assert_eq!(wot.add_link(NodeId(0), NodeId(2)), NewLinkResult::Ok(1));
628        assert_eq!(wot.add_link(NodeId(0), NodeId(3)), NewLinkResult::Ok(1));
629        assert_eq!(
630            wot.add_link(NodeId(0), NodeId(4)),
631            NewLinkResult::AllCertificationsUsed(0)
632        );
633
634        assert_eq!(wot.max_cert, 3);
635        assert_eq!(wot.exists_link(NodeId(0), NodeId(1)), true);
636        assert_eq!(wot.exists_link(NodeId(0), NodeId(2)), true);
637        assert_eq!(wot.exists_link(NodeId(0), NodeId(3)), true);
638        assert_eq!(wot.exists_link(NodeId(0), NodeId(4)), false);
639
640        wot.max_cert = 4;
641        assert_eq!(wot.max_cert, 4);
642        assert_eq!(wot.exists_link(NodeId(0), NodeId(4)), false);
643        wot.add_link(NodeId(0), NodeId(4));
644        assert_eq!(wot.exists_link(NodeId(0), NodeId(4)), true);
645        wot.remove_link(NodeId(0), NodeId(1));
646        wot.remove_link(NodeId(0), NodeId(2));
647        wot.remove_link(NodeId(0), NodeId(3));
648        wot.remove_link(NodeId(0), NodeId(4));
649
650        // false when not linked or out of bounds
651        assert_eq!(wot.exists_link(NodeId(0), NodeId(6)), false);
652        assert_eq!(wot.exists_link(NodeId(23), NodeId(0)), false);
653        assert_eq!(wot.exists_link(NodeId(2), NodeId(53)), false);
654
655        // created nodes should be enabled
656        assert_eq!(wot.is_enabled(NodeId(0)), Some(true));
657        assert_eq!(wot.is_enabled(NodeId(1)), Some(true));
658        assert_eq!(wot.is_enabled(NodeId(2)), Some(true));
659        assert_eq!(wot.is_enabled(NodeId(3)), Some(true));
660        assert_eq!(wot.is_enabled(NodeId(11)), Some(true));
661
662        // should be able to disable some nodes
663        assert_eq!(wot.set_enabled(NodeId(0), false), Some(false));
664        assert_eq!(wot.set_enabled(NodeId(1), false), Some(false));
665        assert_eq!(wot.set_enabled(NodeId(2), false), Some(false));
666        assert_eq!(wot.get_disabled().len(), 3);
667        assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));
668
669        // node 0 and 2 should be disabled
670        assert_eq!(wot.is_enabled(NodeId(0)), Some(false));
671        assert_eq!(wot.is_enabled(NodeId(1)), Some(true));
672        assert_eq!(wot.is_enabled(NodeId(2)), Some(false));
673        assert_eq!(wot.is_enabled(NodeId(3)), Some(true));
674        // - set enabled again
675        assert_eq!(wot.set_enabled(NodeId(0), true), Some(true));
676        assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));
677        assert_eq!(wot.set_enabled(NodeId(2), true), Some(true));
678        assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));
679        assert_eq!(wot.get_disabled().len(), 0);
680
681        // should not exist a link from 2 to 0
682        assert_eq!(wot.exists_link(NodeId(2), NodeId(0)), false);
683
684        // should be able to add some links, cert count is returned
685        assert_eq!(wot.add_link(NodeId(2), NodeId(0)), NewLinkResult::Ok(1));
686        assert_eq!(wot.add_link(NodeId(4), NodeId(0)), NewLinkResult::Ok(2));
687        assert_eq!(
688            wot.add_link(NodeId(4), NodeId(0)),
689            NewLinkResult::AlreadyCertified(2)
690        );
691        assert_eq!(
692            wot.add_link(NodeId(4), NodeId(0)),
693            NewLinkResult::AlreadyCertified(2)
694        );
695        assert_eq!(wot.add_link(NodeId(5), NodeId(0)), NewLinkResult::Ok(3));
696
697        // should exist new links
698        /* WoT is:
699         *
700         * 2 --> 0
701         * 4 --> 0
702         * 5 --> 0
703         */
704
705        assert_eq!(wot.exists_link(NodeId(2), NodeId(0)), true);
706        assert_eq!(wot.exists_link(NodeId(4), NodeId(0)), true);
707        assert_eq!(wot.exists_link(NodeId(5), NodeId(0)), true);
708        assert_eq!(wot.exists_link(NodeId(2), NodeId(1)), false);
709
710        // should be able to remove some links
711        assert_eq!(
712            wot.remove_link(NodeId(4), NodeId(0)),
713            RemovedLinkResult::Removed(2)
714        );
715        /*
716         * WoT is now:
717         *
718         * 2 --> 0
719         * 5 --> 0
720         */
721
722        // should exist less links
723        assert_eq!(wot.exists_link(NodeId(2), NodeId(0)), true);
724        assert_eq!(wot.exists_link(NodeId(4), NodeId(0)), false);
725        assert_eq!(wot.exists_link(NodeId(5), NodeId(0)), true);
726        assert_eq!(wot.exists_link(NodeId(2), NodeId(1)), false);
727
728        // should successfully use distance rule
729        assert_eq!(wot.is_outdistanced(NodeId(0), 1, 1, 1.0), Some(false));
730        // => no because 2,4,5 have certified him
731        assert_eq!(wot.is_outdistanced(NodeId(0), 2, 1, 1.0), Some(false));
732        // => no because only member 2 has 2 certs, and has certified him
733        assert_eq!(wot.is_outdistanced(NodeId(0), 3, 1, 1.0), Some(false));
734        // => no because no member has issued 3 certifications
735
736        // - we add links from member 3
737        assert_eq!(wot.add_link(NodeId(3), NodeId(1)), NewLinkResult::Ok(1));
738        assert_eq!(wot.add_link(NodeId(3), NodeId(2)), NewLinkResult::Ok(1));
739        /*
740         * WoT is now:
741         *
742         * 2 --> 0
743         * 5 --> 0
744         * 3 --> 1
745         * 3 --> 2
746         */
747        assert_eq!(wot.size(), 12);
748        assert_eq!(wot.get_sentries(1).len(), 1);
749        assert_eq!(wot.get_sentries(1)[0], NodeId(2));
750        assert_eq!(wot.get_sentries(2).len(), 0);
751        assert_eq!(wot.get_sentries(3).len(), 0);
752        assert_eq!(wot.get_non_sentries(1).len(), 11); // 12 - 1
753        assert_eq!(wot.get_non_sentries(2).len(), 12); // 12 - 0
754        assert_eq!(wot.get_non_sentries(3).len(), 12); // 12 - 0
755        assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 1).len(), 0); // KO
756        assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2).len(), 1); // It exists 3 -> 2 -> 0
757        assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2)[0].len(), 3); // It exists 3 -> 2 -> 0
758        assert_eq!(wot.is_outdistanced(NodeId(0), 1, 1, 1.0), Some(false)); // OK : 2 -> 0
759        assert_eq!(wot.is_outdistanced(NodeId(0), 2, 1, 1.0), Some(false)); // OK : 2 -> 0
760        assert_eq!(wot.is_outdistanced(NodeId(0), 3, 1, 1.0), Some(false)); // OK : no stry \w 3 lnk
761        assert_eq!(wot.is_outdistanced(NodeId(0), 2, 2, 1.0), Some(false)); // OK : 2 -> 0
762
763        wot.add_link(NodeId(1), NodeId(3));
764        wot.add_link(NodeId(2), NodeId(3));
765
766        assert_eq!(wot.size(), 12);
767        assert_eq!(wot.get_sentries(1).len(), 3);
768        assert_eq!(wot.get_sentries(1)[0], NodeId(1));
769        assert_eq!(wot.get_sentries(1)[1], NodeId(2));
770        assert_eq!(wot.get_sentries(1)[2], NodeId(3));
771
772        assert_eq!(wot.get_sentries(2).len(), 1);
773        assert_eq!(wot.get_sentries(2)[0], NodeId(3));
774        assert_eq!(wot.get_sentries(3).len(), 0);
775        assert_eq!(wot.get_non_sentries(1).len(), 9); // 12 - 3
776        assert_eq!(wot.get_non_sentries(2).len(), 11); // 12 - 1
777        assert_eq!(wot.get_non_sentries(3).len(), 12); // 12 - 0
778        assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 1).len(), 0); // KO
779        assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2).len(), 1); // It exists 3 -> 2 -> 0
780        assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2)[0].len(), 3); // It exists 3 -> 2 -> 0
781        assert_eq!(wot.is_outdistanced(NodeId(0), 1, 1, 1.0), Some(true)); // KO : No path 3 -> 0
782        assert_eq!(wot.is_outdistanced(NodeId(0), 2, 1, 1.0), Some(true)); // KO : No path 3 -> 0
783        assert_eq!(wot.is_outdistanced(NodeId(0), 3, 1, 1.0), Some(false)); // OK : no stry \w 3 lnk
784        assert_eq!(wot.is_outdistanced(NodeId(0), 2, 2, 1.0), Some(false)); // OK : 3 -> 2 -> 0
785
786        // should have 12 nodes
787        assert_eq!(wot.size(), 12);
788
789        // delete top node (return new top node id)
790        assert_eq!(wot.remove_node(), Some(NodeId(10)));
791
792        // should have 11 nodes
793        assert_eq!(wot.size(), 11);
794
795        // should work with member 3 disabled
796        // - with member 3 disabled (non-member)
797        assert_eq!(wot.set_enabled(NodeId(3), false), Some(false));
798        assert_eq!(wot.get_disabled().len(), 1);
799        assert_eq!(wot.is_outdistanced(NodeId(0), 2, 1, 1.0), Some(false)); // OK : Disabled
800
801        // should be able to make a mem copy
802        {
803            let wot2 = wot.clone();
804
805            assert_eq!(wot.size(), wot2.size());
806            assert_eq!(
807                wot.get_non_sentries(1).len(),
808                wot2.get_non_sentries(1).len()
809            );
810        }
811
812        // serialization
813        assert_eq!(wot.to_file("test.wot"), true);
814
815        // deserialization
816        {
817            let wot2 = WebOfTrust::from_file("test.wot").unwrap();
818
819            assert_eq!(wot.size(), wot2.size());
820            assert_eq!(
821                wot.get_non_sentries(1).len(),
822                wot2.get_non_sentries(1).len()
823            );
824        }
825
826    }
827}