diff --git a/.gitignore b/.gitignore
index ac6d158ede7acc17cb7cb1c4a9562faebf3fe145..131d75fddfc02a6abe821fdc762e12cde28e8a5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
 
 **/*.rs.bk
 *.wot
+*.bin
diff --git a/Cargo.lock b/Cargo.lock
index e7db55386932a1efe17fd46f07f735466144f23b..ea4d9ed9642507e4dc0f26d63276c0891cc91009 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6,6 +6,14 @@ dependencies = [
  "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "arrayvec"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "base58"
 version = "0.1.0"
@@ -39,6 +47,42 @@ name = "byteorder"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "cfg-if"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-epoch 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "duniter-crypto"
 version = "0.1.0"
@@ -63,14 +107,20 @@ dependencies = [
 
 [[package]]
 name = "duniter-wotb"
-version = "0.6.0"
+version = "0.6.1"
 dependencies = [
  "bincode 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "either"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "fuchsia-zircon"
 version = "0.3.3"
@@ -90,6 +140,11 @@ name = "gcc"
 version = "0.3.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "lazy_static"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "lazy_static"
 version = "1.0.0"
@@ -113,6 +168,24 @@ dependencies = [
  "libc 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "memoffset"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "nodrop"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num_cpus"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "quote"
 version = "0.3.15"
@@ -127,6 +200,27 @@ dependencies = [
  "libc 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "rayon"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "redox_syscall"
 version = "0.1.37"
@@ -171,6 +265,11 @@ name = "safemem"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "scopeguard"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "serde"
 version = "1.0.27"
@@ -276,26 +375,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [metadata]
 "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
+"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
 "checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
 "checksum base64 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c4a342b450b268e1be8036311e2c613d7f8a7ed31214dff1cc3b60852a3168d"
 "checksum bincode 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9a6301db0b49fb63551bc15b5ae348147101cdf323242b93ec7546d5002ff1af"
 "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
 "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
+"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
+"checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3"
+"checksum crossbeam-epoch 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "59796cc6cbbdc6bb319161349db0c3250ec73ec7fcb763a51065ec4e2e158552"
+"checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9"
+"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
 "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
 "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
 "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb"
+"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
 "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
 "checksum libc 0.2.35 (registry+https://github.com/rust-lang/crates.io-index)" = "96264e9b293e95d25bfcbbf8a88ffd1aedc85b754eba8b7d78012f638ba220eb"
 "checksum linked-hash-map 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2aab0478615bb586559b0114d94dd8eca4fdbb73b443adcb0d00b61692b4bf"
 "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
+"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3"
+"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
+"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
 "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
 "checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1"
+"checksum rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "485541959c8ecc49865526fe6c4de9653dd6e60d829d6edf0be228167b60372d"
+"checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
 "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
 "checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa"
 "checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e"
 "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
 "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
 "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
+"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
 "checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526"
 "checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0"
 "checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5"
diff --git a/wotb/Cargo.toml b/wotb/Cargo.toml
index fe318989e46c84174bf933ded504670604082f9a..dc81a5a31760a3ec732ca17ce0cca25e620cfa35 100644
--- a/wotb/Cargo.toml
+++ b/wotb/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "duniter-wotb"
-version = "0.6.0"
+version = "0.6.1"
 authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@duniter.org>"]
 description = "Makes Web of Trust computations for the Duniter project."
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
@@ -15,4 +15,5 @@ path = "lib.rs"
 serde = "1.0.24"
 serde_derive = "1.0.24"
 bincode = "0.9.2"
-byteorder = "1.2.1"
\ No newline at end of file
+byteorder = "1.2.1"
+rayon = "1.0.0"
diff --git a/wotb/lib.rs b/wotb/lib.rs
index 3cc916f5f2f6b81495a4cdf02b5acb63b3dc0080..bcc07a8a034f8842060756f540132dd862b99164 100644
--- a/wotb/lib.rs
+++ b/wotb/lib.rs
@@ -32,11 +32,14 @@
 
 extern crate bincode;
 extern crate byteorder;
+extern crate rayon;
 extern crate serde;
 #[macro_use]
 extern crate serde_derive;
 
 pub mod legacy;
+pub mod rusty;
+
 pub use legacy::LegacyWebOfTrust;
 
 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
@@ -167,7 +170,7 @@ pub trait WebOfTrust {
     fn add_node(&mut self) -> NodeId;
 
     /// Remove the last node.
-    /// Returns `None` if the WoT was empty.
+    /// Returns `None` if the WoT was empty, otherwise new top node id.
     fn rem_node(&mut self) -> Option<NodeId>;
 
     /// Get the size of the WoT.
@@ -177,7 +180,7 @@ pub trait WebOfTrust {
     /// Returns `None` if this node doesn't exist.
     fn is_enabled(&self, id: NodeId) -> Option<bool>;
 
-    /// Set if given node is enabled.
+    /// Set the enabled state of given node.
     /// Returns `Null` if this node doesn't exist, `enabled` otherwise.
     fn set_enabled(&mut self, id: NodeId, enabled: bool) -> Option<bool>;
 
@@ -282,7 +285,6 @@ pub trait WebOfTrust {
         let mut buffer_3b: Vec<u8> = Vec::with_capacity(3);
         let mut count_bytes = 0;
         let mut remaining_links: u8 = 0;
-        let mut source: u32 = 0;
         let mut target: u32 = 0;
         for byte in file_pointing_to_links {
             if remaining_links == 0 {
@@ -293,7 +295,7 @@ pub trait WebOfTrust {
                 buffer_3b.push(byte);
                 if count_bytes % 3 == 2 {
                     let mut buf = &buffer_3b.clone()[..];
-                    source = buf.read_u24::<BigEndian>().expect("fail to parse source");
+                    let source = buf.read_u24::<BigEndian>().expect("fail to parse source");
                     self.add_link(NodeId(source as usize), NodeId((target - 1) as usize));
                     remaining_links -= 1;
                     buffer_3b.clear();
@@ -351,20 +353,17 @@ pub trait WebOfTrust {
         }
         // Write links
         for n in 0..nodes_count {
-            match self.get_links_source(NodeId(n as usize)) {
-                Some(sources) => {
-                    // Write sources_counts
-                    let mut bytes = Vec::with_capacity(1);
-                    bytes.write_u8(sources.len() as u8).unwrap();
+            if let Some(sources) = self.get_links_source(NodeId(n as usize)) {
+                // Write sources_counts
+                let mut bytes = Vec::with_capacity(1);
+                bytes.write_u8(sources.len() as u8).unwrap();
+                buffer.append(&mut bytes);
+                for source in sources {
+                    // Write source
+                    let mut bytes: Vec<u8> = Vec::with_capacity(3);
+                    bytes.write_u24::<BigEndian>(source.0 as u32).unwrap();
                     buffer.append(&mut bytes);
-                    for source in sources.iter() {
-                        // Write source
-                        let mut bytes: Vec<u8> = Vec::with_capacity(3);
-                        bytes.write_u24::<BigEndian>(source.0 as u32).unwrap();
-                        buffer.append(&mut bytes);
-                    }
                 }
-                None => {}
             };
         }
         // Create or open file
@@ -629,7 +628,12 @@ mod tests {
         assert_eq!(wot.get_non_sentries(3).len(), 12); // 12 - 0
         assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 1).len(), 0); // KO
         assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2).len(), 1); // It exists 3 -> 2 -> 0
-        assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2)[0].len(), 3); // It exists 3 -> 2 -> 0
+        assert!(wot.get_paths(NodeId(3), NodeId(0), 2).contains(&vec![
+            NodeId(3),
+            NodeId(2),
+            NodeId(0),
+        ]));
+
         assert_eq!(
             wot.is_outdistanced(WotDistanceParameters {
                 node: NodeId(0),
@@ -684,7 +688,12 @@ mod tests {
         assert_eq!(wot.get_non_sentries(3).len(), 12); // 12 - 0
         assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 1).len(), 0); // KO
         assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2).len(), 1); // It exists 3 -> 2 -> 0
-        assert_eq!(wot.get_paths(NodeId(3), NodeId(0), 2)[0].len(), 3); // It exists 3 -> 2 -> 0
+        assert!(wot.get_paths(NodeId(3), NodeId(0), 2).contains(&vec![
+            NodeId(3),
+            NodeId(2),
+            NodeId(0),
+        ]));
+
         assert_eq!(
             wot.is_outdistanced(WotDistanceParameters {
                 node: NodeId(0),
diff --git a/wotb/rusty.rs b/wotb/rusty.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4e58cea57398974714d2c9e0a8b75b5d5dedaf45
--- /dev/null
+++ b/wotb/rusty.rs
@@ -0,0 +1,323 @@
+//  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 <https://www.gnu.org/licenses/>.
+
+//! Experimental implementation of the Web of Trust in a more "rusty" style.
+
+use std::collections::HashSet;
+use rayon::prelude::*;
+
+use WebOfTrust;
+use WotDistance;
+use WotDistanceParameters;
+use HasLinkResult;
+use RemLinkResult;
+use NewLinkResult;
+
+use NodeId;
+
+/// A node in the *WoT* graph.
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct Node {
+    /// Is this node enabled ?
+    enabled: bool,
+
+    /// Set of links this node is the target.
+    links_source: HashSet<NodeId>,
+
+    /// 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)]
+pub struct RustyWebOfTrust {
+    /// List of nodes in the WoT.
+    nodes: Vec<Node>,
+    /// Maximum number of links a node can issue.
+    max_links: usize,
+}
+
+impl RustyWebOfTrust {
+    /// Create a new Web of Trust with the maximum of links a node can issue.
+    pub fn new(max_links: usize) -> RustyWebOfTrust {
+        RustyWebOfTrust {
+            nodes: vec![],
+            max_links,
+        }
+    }
+
+    /// Test if a node is a sentry.
+    pub fn is_sentry(&self, node: NodeId, sentry_requirement: usize) -> Option<bool> {
+        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,
+        )
+    }
+}
+
+impl WebOfTrust for RustyWebOfTrust {
+    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<NodeId> {
+        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<bool> {
+        self.nodes.get(id.0).map(|n| n.enabled)
+    }
+
+    fn set_enabled(&mut self, id: NodeId, enabled: bool) -> Option<bool> {
+        self.nodes
+            .get_mut(id.0)
+            .map(|n| n.enabled = enabled)
+            .map(|_| enabled)
+    }
+
+    fn get_enabled(&self) -> Vec<NodeId> {
+        self.nodes
+            .par_iter()
+            .enumerate()
+            .filter(|&(_, n)| n.enabled)
+            .map(|(i, _)| NodeId(i))
+            .collect()
+    }
+
+    fn get_disabled(&self) -> Vec<NodeId> {
+        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<Vec<NodeId>> {
+        self.nodes
+            .get(target.0)
+            .map(|n| n.links_source.iter().cloned().collect())
+    }
+
+    fn issued_count(&self, id: NodeId) -> Option<usize> {
+        self.nodes.get(id.0).map(|n| n.issued_count)
+    }
+
+    fn get_sentries(&self, sentry_requirement: usize) -> Vec<NodeId> {
+        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<NodeId> {
+        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_paths(&self, from: NodeId, to: NodeId, k_max: u32) -> Vec<Vec<NodeId>> {
+        if from == to {
+            vec![vec![to]]
+        } else if k_max > 0 {
+            self.nodes[to.0]
+                .links_source
+                .par_iter()
+                .map(|&source| self.get_paths(from, source, k_max - 1))
+                .map(|paths| {
+                    paths
+                        .iter()
+                        .map(|path| {
+                            let mut path = path.clone();
+                            path.push(to);
+                            path
+                        })
+                        .collect::<Vec<Vec<NodeId>>>()
+                })
+                .reduce(
+                    || vec![],
+                    |mut acc, mut paths| {
+                        acc.append(&mut paths);
+                        acc
+                    },
+                )
+        } else {
+            vec![]
+        }
+    }
+
+    fn compute_distance(&self, params: WotDistanceParameters) -> Option<WotDistance> {
+        let WotDistanceParameters {
+            node,
+            sentry_requirement,
+            step_max,
+            x_percent,
+        } = params;
+
+        if node.0 >= self.size() {
+            return None;
+        }
+
+        let mut area = HashSet::new();
+        area.insert(node);
+        let mut border = HashSet::new();
+        border.insert(node);
+
+        for _ in 0..step_max {
+            border = border
+                .par_iter()
+                .map(|&id| {
+                    self.nodes[id.0]
+                        .links_source
+                        .iter()
+                        .filter(|source| !area.contains(source))
+                        .cloned()
+                        .collect::<HashSet<_>>()
+                })
+                .reduce(HashSet::new, |mut acc, sources| {
+                    for source in sources {
+                        acc.insert(source);
+                    }
+                    acc
+                });
+            area.extend(border.iter());
+        }
+
+        let sentries: Vec<_> = self.get_sentries(sentry_requirement as usize);
+
+        let success = area.iter().filter(|n| sentries.contains(n)).count() as u32;
+        let sentries = sentries.len() as u32
+            - if self.is_sentry(node, sentry_requirement as usize).unwrap() {
+                1
+            } else {
+                0
+            };
+
+        Some(WotDistance {
+            sentries,
+            reached: (area.len() - 1) as u32,
+            success,
+            outdistanced: f64::from(success) < x_percent * f64::from(sentries),
+        })
+    }
+
+    fn is_outdistanced(&self, params: WotDistanceParameters) -> Option<bool> {
+        self.compute_distance(params)
+            .map(|result| result.outdistanced)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use tests::generic_wot_test;
+
+    #[test]
+    fn wot_tests() {
+        let mut wot1 = RustyWebOfTrust::new(3);
+        let mut wot2 = RustyWebOfTrust::new(3);
+        generic_wot_test(&mut wot1, &mut wot2);
+    }
+}