diff --git a/Cargo.lock b/Cargo.lock
index 3760ea3076ed8933035009417ead039d331c50e8..801e0864f7d4280911b83fc09235c1b93e77305a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -55,6 +55,14 @@ name = "base58"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "base64"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "base64"
 version = "0.9.2"
@@ -230,13 +238,13 @@ dependencies = [
  "duniter-message 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "durs-network-documents 0.1.0-a0.1",
  "durs-wot 0.8.0-a0.9",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "pbr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustbreak 2.0.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustbreak 2.0.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "sqlite 0.23.9 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -250,7 +258,7 @@ dependencies = [
  "dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "duniter-documents 0.9.0-b1",
  "duniter-module 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -267,7 +275,8 @@ dependencies = [
  "duniter-message 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
+ "durs-network-documents 0.1.0-a0.1",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -285,11 +294,10 @@ dependencies = [
  "duniter-documents 0.9.0-b1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "durs-wot 0.8.0-a0.9",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustbreak 2.0.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rustbreak 2.0.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -302,12 +310,13 @@ dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -318,7 +327,8 @@ dependencies = [
  "duniter-documents 0.9.0-b1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
+ "durs-network-documents 0.1.0-a0.1",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -329,7 +339,8 @@ name = "duniter-module"
 version = "0.1.0-a0.1"
 dependencies = [
  "duniter-documents 0.9.0-b1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
+ "durs-network-documents 0.1.0-a0.1",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -342,7 +353,7 @@ version = "0.1.0-a0.1"
 dependencies = [
  "duniter-documents 0.9.0-b1",
  "duniter-module 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "durs-network-documents 0.1.0-a0.1",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -359,7 +370,7 @@ dependencies = [
  "duniter-message 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "durs-network-documents 0.1.0-a0.1",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -371,13 +382,13 @@ dependencies = [
 
 [[package]]
 name = "dup-crypto"
-version = "0.3.0-b2"
+version = "0.3.0-b3"
 dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rust-crypto-wasm 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -400,7 +411,8 @@ dependencies = [
  "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "duniter-documents 0.9.0-b1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
+ "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest_derive 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -424,11 +436,13 @@ dependencies = [
 name = "durs-ws2p"
 version = "0.1.0-a0.1"
 dependencies = [
+ "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "duniter-conf 0.1.0-a0.1",
+ "duniter-documents 0.9.0-b1",
  "duniter-message 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "durs-network-documents 0.1.0-a0.1",
  "durs-ws2p-messages 0.1.0-a0.1",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -444,11 +458,10 @@ dependencies = [
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "duniter-documents 0.9.0-b1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "durs-network-documents 0.1.0-a0.1",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -465,12 +478,11 @@ dependencies = [
  "duniter-message 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
- "dup-crypto 0.3.0-b2",
+ "dup-crypto 0.3.0-b3",
  "durs-network-documents 0.1.0-a0.1",
  "durs-wot 0.8.0-a0.9",
  "log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_derive 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -486,21 +498,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "failure"
-version = "0.1.1"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "backtrace 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "failure_derive"
-version = "0.1.1"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -548,6 +561,16 @@ dependencies = [
  "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "hex"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "hex"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "httparse"
 version = "1.3.0"
@@ -772,7 +795,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "pest 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -803,23 +826,18 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "0.4.6"
+version = "0.4.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "quote"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "quote"
 version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -902,25 +920,26 @@ dependencies = [
 ]
 
 [[package]]
-name = "rust-crypto"
-version = "0.2.36"
+name = "rust-crypto-wasm"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
+ "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rustbreak"
-version = "2.0.0-rc2"
+version = "2.0.0-rc3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.78 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -929,11 +948,6 @@ name = "rustc-demangle"
 version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "rustc-serialize"
-version = "0.3.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "safemem"
 version = "0.2.0"
@@ -954,7 +968,7 @@ name = "serde_derive"
 version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1051,46 +1065,40 @@ name = "structopt-derive"
 version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
-[[package]]
-name = "syn"
-version = "0.11.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "syn"
 version = "0.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
-name = "synom"
-version = "0.11.3"
+name = "syn"
+version = "0.15.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "synstructure"
-version = "0.6.1"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1166,11 +1174,6 @@ name = "unicode-width"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "unicode-xid"
-version = "0.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
 [[package]]
 name = "unicode-xid"
 version = "0.1.0"
@@ -1262,6 +1265,7 @@ dependencies = [
 "checksum backtrace-sys 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "bff67d0c06556c0b8e6b5f090f0eac52d950d9dfd1d35ba04e4ca3543eaf6a7e"
 "checksum base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83"
 "checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2"
+"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
 "checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9"
 "checksum bincode 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9f2fb9e29e72fd6bc12071533d5dc7664cb01480c59406f656d7ac25c7bd8ff7"
 "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
@@ -1283,8 +1287,8 @@ dependencies = [
 "checksum dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37a76dd8b997af7107d0bb69d43903cf37153a18266f8b3fdb9911f28efb5444"
 "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
 "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
-"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82"
-"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b"
+"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7"
+"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596"
 "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
 "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
@@ -1292,6 +1296,8 @@ dependencies = [
 "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 generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d"
+"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa"
+"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
 "checksum httparse 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "422d5a93b40dbfbd8c8c24d1b4b5ef455973ef3afffc172487429fecd05bde42"
 "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
 "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08"
@@ -1323,8 +1329,7 @@ dependencies = [
 "checksum pest_meta 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f6d5f6f0e6082578c86af197d780dc38328e3f768cec06aac9bc46d714e8221"
 "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f"
 "checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6"
-"checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6"
-"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
+"checksum proc-macro2 0.4.21 (registry+https://github.com/rust-lang/crates.io-index)" = "ab2fc21ba78ac73e4ff6b3818ece00be4e175ffbef4d0a717d978b48b24150c4"
 "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035"
 "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
 "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5"
@@ -1335,10 +1340,9 @@ dependencies = [
 "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8"
 "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
 "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
-"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
-"checksum rustbreak 2.0.0-rc2 (registry+https://github.com/rust-lang/crates.io-index)" = "676cb04876f4391d5d2f2c9029d10cfe5fe28ad625ad8460531bed1191477083"
+"checksum rust-crypto-wasm 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9dcf11edbc9a0effb4a99ddbe909dd26fb2e71459064879218c27b0add1cb6ec"
+"checksum rustbreak 2.0.0-rc3 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c185a2ede13fcb28feb6864ee9412a20f57bd83b4be18dc81fde4d6e786982"
 "checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649"
-"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.78 (registry+https://github.com/rust-lang/crates.io-index)" = "92ec94e2754699adddbbc4f555791bd3acc2a2f5574cba16c93a4a9cf4a04415"
@@ -1355,10 +1359,9 @@ dependencies = [
 "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
 "checksum structopt 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e9ad6a11096cbecdcca0cc6aa403fdfdbaeda2fb3323a39c98e6a166a1e45a"
 "checksum structopt-derive 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4cbce8ccdc62166bd594c14396a3242bf94c337a51dbfa9be1076dd74b3db2af"
-"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad"
 "checksum syn 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c67da57e61ebc7b7b6fff56bb34440ca3a83db037320b0507af4c10368deda7d"
-"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6"
-"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd"
+"checksum syn 0.15.18 (registry+https://github.com/rust-lang/crates.io-index)" = "90c39a061e2f412a9f869540471ab679e85e50c6b05604daf28bc3060f75c430"
+"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
 "checksum term 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6b677dd1e8214ea1ef4297f85dbcbed8e8cdddb561040cc998ca2551c37561"
 "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
 "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
@@ -1369,7 +1372,6 @@ dependencies = [
 "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
 "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25"
 "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
-"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
 "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
 "checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7"
 "checksum vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cbe533e138811704c0e3cbde65a818b35d3240409b4346256c5ede403e082474"
diff --git a/blockchain/Cargo.toml b/blockchain/Cargo.toml
index 218ff6b2665bc19edc73a2ff9c98bcb8c136f601..ea020681ce68abb27c2362846dbdefc0e34179d3 100644
--- a/blockchain/Cargo.toml
+++ b/blockchain/Cargo.toml
@@ -22,7 +22,7 @@ durs-wot = { path = "../wot" }
 log = "0.4.*"
 num_cpus = "1.8.*"
 pbr = "1.0.*"
-rustbreak = {version = "2.0.0-rc2", features = ["bin_enc"]}
+rustbreak = {version = "2.0.0-rc3", features = ["bin_enc"]}
 serde = "1.0.*"
 serde_json = "1.0.*"
 sqlite = "0.23.*"
diff --git a/blockchain/lib.rs b/blockchain/lib.rs
index a01dd2df2418c20b6195031e3b977a55e07e3148..67d300e88539ea12eee48f7202e26ee5651999a3 100644
--- a/blockchain/lib.rs
+++ b/blockchain/lib.rs
@@ -444,7 +444,7 @@ impl BlockchainModule {
         wot: &BinDB<W>,
     ) -> Blockstamp {
         debug!("BlockchainModule : receive_blocks()");
-        let blocks: Vec<&NetworkBlock> = blocks_in_box.into_iter().map(|b| b.deref()).collect();
+        let blocks: Vec<&NetworkBlock> = blocks_in_box.iter().map(|b| b.deref()).collect();
         let mut current_blockstamp = *current_blockstamp;
         let mut save_blocks_dbs = false;
         let mut save_wots_dbs = false;
diff --git a/core/Cargo.toml b/core/Cargo.toml
index fe81eec107eab6d1e93c58fece27862c20642a4b..37d43e959dcb3c3620116efae31c34d6ff6dd48c 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -16,6 +16,7 @@ dup-crypto = { path = "../crypto" }
 duniter-message =  { path = "../message" }
 duniter-module = { path = "../module" }
 duniter-network = { path = "../network" }
+durs-network-documents = { path = "../network-documents" }
 log = "0.4.*"
 log-panics = "2.0.*"
 serde = "1.0.*"
diff --git a/core/lib.rs b/core/lib.rs
index bca1a77c35b1b383dbb3e1491d6d2f2926b69fa4..ba7b2c047f2d96223a3ed69433800bfbf49aaeaa 100644
--- a/core/lib.rs
+++ b/core/lib.rs
@@ -40,6 +40,7 @@ extern crate duniter_message;
 extern crate duniter_module;
 extern crate duniter_network;
 extern crate dup_crypto;
+extern crate durs_network_documents;
 extern crate log_panics;
 extern crate serde_json;
 extern crate simplelog;
@@ -206,7 +207,7 @@ impl<'a, 'b: 'a> DuniterCore<'b, 'a, DuRsConf> {
             keypairs: None,
             run_duration_in_secs,
             rooter_sender: None,
-            modules_count: 0,
+            modules_count: 1, // Count blockchain module
             network_modules_count: 0,
             thread_pool: ThreadPool::new(*THREAD_POOL_SIZE),
         }
@@ -441,15 +442,22 @@ impl<'a, 'b: 'a> DuniterCore<'b, 'a, DuRsConf> {
                 panic!("Try to start core without rooter_sender !");
             };
 
-            // Send blockchain sender to rooter thread
+            // Send expected modules count to rooter thread
             rooter_sender
-                .send(RooterThreadMessage::ModuleSender(
+                .send(RooterThreadMessage::ModulesCount(self.modules_count))
+                .expect("Fatal error: fail to send expected modules count to rooter thread !");
+
+            // Send blockchain module registration to rooter thread
+            rooter_sender
+                .send(RooterThreadMessage::ModuleRegistration(
                     BlockchainModule::name(),
                     blockchain_sender,
                     vec![ModuleRole::BlockchainDatas, ModuleRole::BlockValidation],
                     vec![ModuleEvent::NewBlockFromNetwork],
+                    vec![],
+                    vec![],
                 ))
-                .expect("Fatal error: fail to send blockchain sender to rooter thread !");
+                .expect("Fatal error: fail to send blockchain registration to rooter thread !");
 
             // Instantiate blockchain module and load is conf
             let mut blockchain_module = BlockchainModule::load_blockchain_conf(
diff --git a/core/rooter.rs b/core/rooter.rs
index e12801b739b11706aece03d78815908840d3e9ea..1dce12f5578f4450a9617700c031d7c51839bb9b 100644
--- a/core/rooter.rs
+++ b/core/rooter.rs
@@ -19,6 +19,7 @@ use duniter_conf;
 use duniter_conf::DuRsConf;
 use duniter_message::*;
 use duniter_module::*;
+use durs_network_documents::network_endpoint::EndpointEnum;
 use std::collections::HashMap;
 use std::sync::mpsc;
 use std::sync::mpsc::RecvTimeoutError;
@@ -26,6 +27,8 @@ use std::thread;
 use std::time::Duration;
 use std::time::SystemTime;
 
+static MAX_REGISTRATION_DELAY: &'static u64 = &20;
+
 /// Start broadcasting thread
 fn start_broadcasting_thread(
     start_time: SystemTime,
@@ -38,17 +41,27 @@ fn start_broadcasting_thread(
     let mut pool_msgs: HashMap<DursMsgReceiver, Vec<DursMsgContent>> = HashMap::new();
     let mut events_subscriptions: HashMap<ModuleEvent, Vec<ModuleStaticName>> = HashMap::new();
     let mut roles: HashMap<ModuleRole, Vec<ModuleStaticName>> = HashMap::new();
+    let mut registrations_count = 0;
+    let mut expected_registrations_count = None;
+    let mut local_node_endpoints: Vec<EndpointEnum> = Vec::new();
+    let mut reserved_apis_name: HashMap<ModuleStaticName, Vec<String>> = HashMap::new();
 
     loop {
         match receiver.recv_timeout(Duration::from_secs(1)) {
             Ok(mess) => {
                 match mess {
-                    RooterThreadMessage::ModuleSender(
+                    RooterThreadMessage::ModulesCount(modules_count) => {
+                        expected_registrations_count = Some(modules_count)
+                    }
+                    RooterThreadMessage::ModuleRegistration(
                         module_static_name,
                         module_sender,
                         sender_roles,
                         events_subscription,
+                        module_reserved_apis_name,
+                        mut module_endpoints,
                     ) => {
+                        registrations_count += 1;
                         // For all events
                         for event in events_subscription {
                             // Send pending message of this event
@@ -93,6 +106,41 @@ fn start_broadcasting_thread(
                                 .or_insert_with(Vec::new)
                                 .push(module_static_name);
                         }
+                        // For all endpoints
+                        for ep in &module_endpoints {
+                            let ep_api = ep.api();
+                            if !module_reserved_apis_name.contains(&ep_api.0) {
+                                panic!("Fatal error : Module {} try to declare endpoint with undeclared api name: {} !", module_static_name.0, ep_api.0);
+                            }
+                            for other_module_ep in &local_node_endpoints {
+                                if ep_api == other_module_ep.api() {
+                                    panic!("Fatal error : two modules try to declare endpoint of same api : {} !", ep_api.0);
+                                }
+                            }
+                        }
+                        // Store reserved APIs name
+                        reserved_apis_name.insert(module_static_name, module_reserved_apis_name);
+                        // Add module endpoints to local node endpoints
+                        local_node_endpoints.append(&mut module_endpoints);
+                        // Send endpoints to network module
+                        if expected_registrations_count.is_some()
+                            && registrations_count == expected_registrations_count.unwrap()
+                        {
+                            // Get list of InterNodesNetwork modules
+                            let receivers = roles
+                                .get(&ModuleRole::InterNodesNetwork)
+                                .expect("Fatal error : no module with role InterNodesNetwork !")
+                                .to_vec();
+                            // Send endpoints to receivers
+                            send_msg_to_several_receivers(
+                                DursMsg(
+                                    DursMsgReceiver::Role(ModuleRole::InterNodesNetwork),
+                                    DursMsgContent::Endpoints(local_node_endpoints.clone()),
+                                ),
+                                &receivers,
+                                &modules_senders,
+                            );
+                        }
                         // Add this sender to modules_senders
                         modules_senders.insert(module_static_name, module_sender);
                     }
@@ -175,6 +223,20 @@ fn start_broadcasting_thread(
                 }
             },
         }
+        if (expected_registrations_count.is_none()
+            || registrations_count < expected_registrations_count.unwrap())
+            && SystemTime::now()
+                .duration_since(start_time)
+                .expect("Duration error !")
+                .as_secs()
+                > *MAX_REGISTRATION_DELAY
+        {
+            panic!(
+                "{} modules have registered, but expected {} !",
+                registrations_count,
+                expected_registrations_count.unwrap_or(0)
+            );
+        }
     }
 }
 
@@ -303,11 +365,23 @@ pub fn start_rooter(
             match rooter_receiver.recv_timeout(Duration::from_secs(1)) {
                 Ok(mess) => {
                     match mess {
-                        RooterThreadMessage::ModuleSender(
+                        RooterThreadMessage::ModulesCount(expected_registrations_count) => {
+                            // Relay to broadcasting thread
+                            broadcasting_sender
+                                .send(RooterThreadMessage::ModulesCount(
+                                    expected_registrations_count,
+                                ))
+                                .expect(
+                                    "Fail to relay ModulesCount message to broadcasting thread !",
+                                );
+                        }
+                        RooterThreadMessage::ModuleRegistration(
                             module_static_name,
                             module_sender,
                             events_subscription,
                             sender_roles,
+                            _module_reserved_apis_name,
+                            _module_endpoints,
                         ) => {
                             // Send pending messages destined specifically to this module
                             if let Some(msgs) = pool_msgs.remove(&module_static_name) {
@@ -329,13 +403,17 @@ pub fn start_rooter(
                             modules_senders.insert(module_static_name, module_sender.clone());
                             // Relay to broadcasting thread
                             broadcasting_sender
-                                .send(RooterThreadMessage::ModuleSender(
+                                .send(RooterThreadMessage::ModuleRegistration(
                                     module_static_name,
                                     module_sender,
                                     events_subscription,
                                     sender_roles,
+                                    vec![],
+                                    vec![],
                                 ))
-                                .expect("Fail to relay message to broadcasting thread !");
+                                .expect(
+                                    "Fail to relay module registration to broadcasting thread !",
+                                );
                             // Log the number of modules_senders received
                             info!(
                                 "Rooter thread receive {} module senders",
@@ -369,13 +447,13 @@ pub fn start_rooter(
                                         broadcasting_sender
                                             .send(RooterThreadMessage::ModuleMessage(msg))
                                             .expect(
-                                                "Fail to relay message to broadcasting thread !",
+                                                "Fail to relay specific role message to broadcasting thread !",
                                             );
                                     }
                                 }
                                 DursMsgReceiver::Event(_module_event) => broadcasting_sender
                                     .send(RooterThreadMessage::ModuleMessage(msg))
-                                    .expect("Fail to relay message to broadcasting thread !"),
+                                    .expect("Fail to relay specific event message to broadcasting thread !"),
                                 DursMsgReceiver::One(module_static_name) => {
                                     if let Some(module_sender) =
                                         modules_senders.get(&module_static_name)
diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml
index 9927beb5b62f9b4a692388dcd04c4260ac8cd379..def563d3cbe91ae1b8fb04b4f59e19b0df294d15 100644
--- a/crypto/Cargo.toml
+++ b/crypto/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "dup-crypto"
-version = "0.3.0-b2"
+version = "0.3.0-b3"
 authors = ["nanocryk <nanocryk@duniter.org>", "elois <elois@duniter.org>"]
 description = "Manage cryptographic operations for the DUP (DUniter Protocol)."
 repository = "https://git.duniter.org/nodes/rust/duniter-rs"
@@ -16,10 +16,12 @@ base58 = "0.1.*"
 base64 = "0.10.*"
 bincode = "1.0.*"
 rand = "0.5.*"
-rust-crypto = "0.2.*"
+rust-crypto-wasm = "0.3.1"
 serde = "1.0.*"
 serde_derive = "1.0.*"
 
+
+
 [features]
 # Treat warnings as a build error.
 strict = []
\ No newline at end of file
diff --git a/crypto/src/hashs/mod.rs b/crypto/src/hashs/mod.rs
index da51bdcea51e87d1ec59c3224e0c55a10c0bc813..8b2c580a100d7b1aee8a12fce2d5f7d1fbb8cf84 100644
--- a/crypto/src/hashs/mod.rs
+++ b/crypto/src/hashs/mod.rs
@@ -70,6 +70,13 @@ impl Hash {
         Hash(hash_buffer)
     }
 
+    /// Compute hash of a string
+    pub fn compute_str(str_datas: &str) -> Hash {
+        let mut sha256 = Sha256::new();
+        sha256.input_str(&str_datas);
+        Hash::from_hex(&sha256.result_str()).expect("Sha256 result must be an hexa string !")
+    }
+
     /// Convert Hash into bytes vector
     pub fn to_bytes_vector(&self) -> Vec<u8> {
         self.0.to_vec()
diff --git a/crypto/src/keys/mod.rs b/crypto/src/keys/mod.rs
index 46a7ff9dbc5b07ee4ff35413e11fc611b78a8c18..051dd974d03e870089242685c11851210259eb70 100644
--- a/crypto/src/keys/mod.rs
+++ b/crypto/src/keys/mod.rs
@@ -58,6 +58,7 @@ use std::hash::Hash;
 
 pub mod bin_signable;
 pub mod ed25519;
+pub mod text_signable;
 
 /// Cryptographic keys algorithms list
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
diff --git a/crypto/src/keys/text_signable.rs b/crypto/src/keys/text_signable.rs
new file mode 100644
index 0000000000000000000000000000000000000000..43c02f062a63a9c65c10ad028dafd0cd597aa17e
--- /dev/null
+++ b/crypto/src/keys/text_signable.rs
@@ -0,0 +1,69 @@
+//  Copyright (C) 2018  The Durs 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/>.
+
+//! Generic code for signing data in text format
+
+use super::*;
+
+/// Signatureable in text format
+pub trait TextSignable: Debug + Clone {
+    /// Return signable text
+    fn as_signable_text(&self) -> String;
+    /// Return message issuer pubkey
+    fn issuer_pubkey(&self) -> PubKey;
+    /// Return message signature
+    fn signature(&self) -> Option<Sig>;
+    /// Change signature
+    fn set_signature(&mut self, _signature: Sig);
+    /// Sign text message
+    fn sign(&mut self, priv_key: PrivKey) -> Result<String, SignError> {
+        if self.signature().is_some() {
+            return Err(SignError::AlreadySign());
+        }
+        let text = self.as_signable_text();
+        match self.issuer_pubkey() {
+            PubKey::Ed25519(_) => match priv_key {
+                PrivKey::Ed25519(priv_key) => {
+                    let sig = priv_key.sign(&text.as_bytes());
+                    self.set_signature(Sig::Ed25519(sig));
+                    let str_sig = sig.to_base64();
+                    Ok(format!("{}{}", text, str_sig))
+                }
+                _ => Err(SignError::WrongAlgo()),
+            },
+            _ => Err(SignError::WrongAlgo()),
+        }
+    }
+    /// Check signature of text message
+    fn verify(&self) -> Result<(), SigError> {
+        if let Some(signature) = self.signature() {
+            match self.issuer_pubkey() {
+                PubKey::Ed25519(pubkey) => match signature {
+                    Sig::Ed25519(sig) => {
+                        if pubkey.verify(&self.as_signable_text().as_bytes(), &sig) {
+                            Ok(())
+                        } else {
+                            Err(SigError::InvalidSig())
+                        }
+                    }
+                    _ => Err(SigError::NotSameAlgo()),
+                },
+                _ => Err(SigError::NotSameAlgo()),
+            }
+        } else {
+            Err(SigError::NotSig())
+        }
+    }
+}
diff --git a/dal/Cargo.toml b/dal/Cargo.toml
index dd16c4e68660cdc2f131ee8101145657bc4a0912..ea6bbd619378f99a30a764a7025113db4d47a8cb 100644
--- a/dal/Cargo.toml
+++ b/dal/Cargo.toml
@@ -15,8 +15,7 @@ duniter-module = { path = "../module" }
 duniter-network = { path = "../network" }
 durs-wot = { path = "../wot" }
 log = "0.4.*"
-rustbreak = {version = "2.0.0-rc2", features = ["bin_enc"]}
-rust-crypto = "0.2.*"
+rustbreak = {version = "2.0.0-rc3", features = ["bin_enc"]}
 serde = "1.0.*"
 serde_derive = "1.0.*"
 serde_json = "1.0.*"
diff --git a/documents/Cargo.toml b/documents/Cargo.toml
index 8b887f77d5e098b0b485df066480aeee63315120..042065c0a3fa54f60e1dbe7dada6e6ea8e4aaa3e 100644
--- a/documents/Cargo.toml
+++ b/documents/Cargo.toml
@@ -18,9 +18,12 @@ byteorder = "1.2.3"
 dup-crypto = { path = "../crypto" }
 pest = "2.0"
 pest_derive = "2.0"
-rust-crypto = "0.2.*"
 serde = "1.0.*"
 serde_derive = "1.0.*"
+serde_json = "1.0.*"
+
+[dev-dependencies]
+pretty_assertions = "0.5.1"
 
 [features]
 # Treat warnings as a build error.
diff --git a/documents/src/documents_grammar.pest b/documents/src/documents_grammar.pest
index aba4de05ba21ae2a0b0a1a1352b74f79a101280f..695f6e262c9b875cf694333a59e786338a2debe3 100644
--- a/documents/src/documents_grammar.pest
+++ b/documents/src/documents_grammar.pest
@@ -6,15 +6,17 @@ nl = _{ "\n" } // Only one way to break a line is accepted
 hexa_upper = { ASCII_DIGIT | 'A'..'F' }
 base58 = { !("O" | "I" | "l") ~ ASCII_ALPHANUMERIC }
 base64 = { ASCII_ALPHANUMERIC | "+" | "/" }
-integer = @{ "0" | ('1'..'9' ~ ASCII_DIGIT*) }
+no_zero_u_int = @{ '1'..'9' ~ ASCII_DIGIT* }
+u_int = @{ "0" | no_zero_u_int }
 
 hash = @{ hexa_upper{64} }
 currency = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "-" | "_")* }
 pubkey = @{ base58{43,44} }
-uid = @{ ASCII_ALPHA+ }
-block_id = @{ integer }
+uid = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "-" | "_")* }
+block_id = @{ u_int }
 blockstamp =  ${ block_id ~ "-" ~ hash }
-ed25519_sig = @{ base64{86,88} ~ "="{0,2} }
+ed25519_sig = @{ base64{88} | (base64{87} ~ "=") | (base64{86} ~ "==") }
+//^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$
 
 v10 = _{ "Version: 10" }
 
@@ -87,16 +89,16 @@ revoc = ${
     (&(SOI ~ v10 ~ nl) ~ revoc_v10)
 }
 
-tx_locktime = @{ integer }
-tx_amount =  @{ integer }
-tx_amount_base =  @{ integer }
-du_block_id = @{ integer }
-tx_index =  @{ integer }
-input_index = @{ integer }
-issuer_index = @{ integer }
+tx_locktime = @{ u_int }
+tx_amount =  @{ u_int }
+tx_amount_base =  @{ u_int }
+du_block_id = @{ u_int }
+tx_index =  @{ u_int }
+input_index = @{ u_int }
+issuer_index = @{ u_int }
 xhx_secret = @ { ASCII_ALPHANUMERIC+ }
-csv_duration = @{ integer }
-cltv_timestamp = @{ integer }
+csv_duration = @{ u_int }
+cltv_timestamp = @{ u_int }
 
 tx_input = ${ tx_input_du | tx_input_tx }
 tx_input_du = ${ tx_amount ~ ":" ~ tx_amount_base ~ ":D:" ~ pubkey ~ ":" ~ du_block_id }
diff --git a/documents/src/lib.rs b/documents/src/lib.rs
index 5202225d080cd4ae825ed655e872d1b0d0a396dc..586f5d41f7889685eae9de7f8846cfe15f5150e6 100644
--- a/documents/src/lib.rs
+++ b/documents/src/lib.rs
@@ -29,14 +29,17 @@
 extern crate base58;
 extern crate base64;
 extern crate byteorder;
-extern crate crypto;
 extern crate dup_crypto;
 extern crate pest;
 #[macro_use]
 extern crate pest_derive;
+#[cfg(test)]
+#[macro_use]
+extern crate pretty_assertions;
 extern crate serde;
 #[macro_use]
 extern crate serde_derive;
+extern crate serde_json;
 
 pub mod blockstamp;
 mod currencies_codes;
@@ -48,6 +51,7 @@ use dup_crypto::hashs::Hash;
 use dup_crypto::keys::*;
 use pest::iterators::Pair;
 use pest::Parser;
+use serde::Serialize;
 use std::cmp::Ordering;
 use std::fmt::{Debug, Display, Error, Formatter};
 use std::io::Cursor;
@@ -313,3 +317,19 @@ pub trait DocumentParser<S, D, E> {
     /// Parse a source and return a document or an error.
     fn parse(source: S) -> Result<D, E>;
 }
+
+/// Jsonify a document
+pub trait ToJsonObject {
+    type JsonObject: Serialize;
+    /// Transforms an object into a json object
+    fn to_json_object(&self) -> Self::JsonObject;
+
+    /// Convert to JSON String
+    fn to_json_string(&self) -> Result<String, serde_json::Error> {
+        Ok(serde_json::to_string(&self.to_json_object())?)
+    }
+    /// Convert to JSON String pretty
+    fn to_json_string_pretty(&self) -> Result<String, serde_json::Error> {
+        Ok(serde_json::to_string_pretty(&self.to_json_object())?)
+    }
+}
diff --git a/documents/src/v10/block.rs b/documents/src/v10/block.rs
index f9b417d23e89b585ba02eddcf208dc0ec0f59052..2a53d4ea79bcb3f7a22d4927d24af1fbbf81a32a 100644
--- a/documents/src/v10/block.rs
+++ b/documents/src/v10/block.rs
@@ -15,8 +15,6 @@
 
 //! Wrappers around Block document.
 
-use crypto::digest::Digest;
-use crypto::sha2::Sha256;
 use dup_crypto::hashs::Hash;
 use dup_crypto::keys::*;
 use std::ops::Deref;
@@ -277,10 +275,7 @@ impl BlockDocument {
     }
     /// Compute inner hash
     pub fn compute_inner_hash(&mut self) {
-        let mut sha256 = Sha256::new();
-        let inner_text = self.generate_compact_inner_text();
-        sha256.input_str(&inner_text);
-        self.inner_hash = Some(Hash::from_hex(&sha256.result_str()).unwrap());
+        self.inner_hash = Some(Hash::compute_str(&self.generate_compact_inner_text()));
     }
     /// Fill inner_hash_and_nonce_str
     pub fn fill_inner_hash_and_nonce_str(&mut self, new_nonce: Option<u64>) {
@@ -302,16 +297,14 @@ impl BlockDocument {
     }
     /// Compute hash
     pub fn compute_hash(&mut self) {
-        let mut sha256 = Sha256::new();
-        sha256.input_str(&format!(
+        self.hash = Some(BlockHash(Hash::compute_str(&format!(
             "InnerHash: {}\nNonce: {}\n{}\n",
             self.inner_hash
                 .expect("Try to get inner_hash of an uncompleted or reduce block !")
                 .to_hex(),
             self.nonce,
             self.signatures[0]
-        ));
-        self.hash = Some(BlockHash(Hash::from_hex(&sha256.result_str()).unwrap()));
+        ))));
     }
     /// Lightens the block (for example to store it while minimizing the space required)
     pub fn reduce(&mut self) {
@@ -718,4 +711,144 @@ Nonce: "
             "000004F8B84A3590243BA562E5F2BA379F55A0B387C5D6FAC1022DFF7FFE6014"
         );
     }
+
+    #[test]
+    fn generate_and_verify_block_2() {
+        let ms1 = MembershipDocumentParser::parse(
+            "Version: 10
+Type: Membership
+Currency: g1
+Issuer: 4VZkro3N7VonygybESHngKUABA6gSrbW77Ktb94zE969
+Block: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2
+Membership: IN
+UserID: piaaf31
+CertTS: 74077-0000022816648B2F7801E059F67CCD0C023FF0ED84459D52C70494D74DDCC6F6
+gvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8FKpABA==",
+        )
+        .expect("Fail to parse ms1");
+
+        let tx1 = TransactionDocumentParser::parse(
+            "Version: 10
+Type: Transaction
+Currency: g1
+Blockstamp: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2
+Locktime: 0
+Issuers:
+51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2
+Inputs:
+1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:163766
+1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164040
+1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164320
+1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164584
+1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164849
+1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165118
+1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165389
+Unlocks:
+0:SIG(0)
+1:SIG(0)
+2:SIG(0)
+3:SIG(0)
+4:SIG(0)
+5:SIG(0)
+6:SIG(0)
+Outputs:
+7000:0:SIG(98wxzS683Tc1WWm1YxpL5WpxS7wBa1mZBccKSsYpaant)
+28:0:SIG(51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2)
+Comment: Panier mixte plus 40 pommes merci
+7o/yIh0BNSAv5pNmHz04uUBl8TuP2s4HRFMtKeGFQfXNYJPUyJTP/dj6hdrgKtJkm5dCfbxT4KRy6wJf+dj1Cw==",
+        )
+        .expect("Fail to parse tx1");
+
+        let tx2 = TransactionDocumentParser::parse(
+            "Version: 10
+Type: Transaction
+Currency: g1
+Blockstamp: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2
+Locktime: 0
+Issuers:
+3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX
+Inputs:
+1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:148827
+1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149100
+1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149370
+1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149664
+1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149943
+1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:150222
+Unlocks:
+0:SIG(0)
+1:SIG(0)
+2:SIG(0)
+3:SIG(0)
+4:SIG(0)
+5:SIG(0)
+Outputs:
+6000:0:SIG(AopwTfXhj8VqZReFJYGGWnoWnXNj3RgaqFcGGywXpZrD)
+12:0:SIG(3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX)
+Comment: En reglement de tes bons bocaux de fruits et legumes
+nxr4exGrt16jteN9ZX3XZPP9l+X0OUbZ1o/QjE1hbWQNtVU3HhH9SJoEvNj2iVl3gCRr9u2OA9uj9vCyUDyjAg==
+",
+        )
+        .expect("Fail to parse tx2");
+
+        let mut block = BlockDocument {
+            nonce: 0,
+            number: BlockId(165_647),
+            pow_min: 90,
+            time: 1_540_633_175,
+            median_time: 1_540_627_811,
+            members_count: 1402,
+            monetary_mass: 386_008_811,
+            unit_base: 0,
+            issuers_count: 37,
+            issuers_frame: 186,
+            issuers_frame_var: 0,
+            currency: CurrencyName(String::from("g1")),
+            issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58("A4pc9Uuk4NXkWG8CibicjjPpEPdiup1mhjMoRWUZsonq").unwrap())],
+            signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64("2Z/+9ADdZvHXs19YR8+qDzgfl8WJlBG5PcbFvBG9TOuUJbjAdxhcgxrFrSRIABGWcCrIgLkB805fZVLP8jOjBA==").unwrap())],
+            hash: None,
+            parameters: None,
+            previous_hash: Hash::from_hex("000003E78FA4133F2C13B416F330C8DFB5A41EB87E37190615DB334F2C914A51").expect("fail to parse previous_hash"),
+            previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58("8NmGZmGjL1LUgJQRg282yQF7KTdQuRNAg8QfSa2qvd65").unwrap())),
+            inner_hash: None,//Some(Hash::from_hex("3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846").expect("fail to parse inner_hash")),
+            dividend: None,
+            identities: vec![],
+            joiners: vec![],
+            actives: vec![ms1],
+            leavers: vec![],
+            revoked: vec![],
+            excluded: vec![],
+            certifications: vec![],
+            transactions: vec![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))],
+            inner_hash_and_nonce_str: String::new(),
+        };
+        // test inner_hash computation
+        block.compute_inner_hash();
+        println!("{}", block.generate_compact_text());
+        assert_eq!(
+            block
+                .inner_hash
+                .expect("Try to get inner_hash of an uncompleted or reduce block !")
+                .to_hex(),
+            "3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846"
+        );
+        // test generate_compact_text()
+        let block_compact_text = block.generate_compact_text();
+        assert_eq!(
+            block_compact_text,
+            "Version: 10\nType: Block\nCurrency: g1\nNumber: 165647\nPoWMin: 90\nTime: 1540633175\nMedianTime: 1540627811\nUnitBase: 0\nIssuer: A4pc9Uuk4NXkWG8CibicjjPpEPdiup1mhjMoRWUZsonq\nIssuersFrame: 186\nIssuersFrameVar: 0\nDifferentIssuersCount: 37\nPreviousHash: 000003E78FA4133F2C13B416F330C8DFB5A41EB87E37190615DB334F2C914A51\nPreviousIssuer: 8NmGZmGjL1LUgJQRg282yQF7KTdQuRNAg8QfSa2qvd65\nMembersCount: 1402\nIdentities:\nJoiners:\nActives:\n4VZkro3N7VonygybESHngKUABA6gSrbW77Ktb94zE969:gvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8FKpABA==:165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2:74077-0000022816648B2F7801E059F67CCD0C023FF0ED84459D52C70494D74DDCC6F6:piaaf31\nLeavers:\nRevoked:\nExcluded:\nCertifications:\nTransactions:\nTX:10:1:7:7:2:1:0\n165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\n51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:163766\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164040\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164320\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164584\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164849\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165118\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165389\n0:SIG(0)\n1:SIG(0)\n2:SIG(0)\n3:SIG(0)\n4:SIG(0)\n5:SIG(0)\n6:SIG(0)\n7000:0:SIG(98wxzS683Tc1WWm1YxpL5WpxS7wBa1mZBccKSsYpaant)\n28:0:SIG(51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2)\nPanier mixte plus 40 pommes merci\n7o/yIh0BNSAv5pNmHz04uUBl8TuP2s4HRFMtKeGFQfXNYJPUyJTP/dj6hdrgKtJkm5dCfbxT4KRy6wJf+dj1Cw==\nTX:10:1:6:6:2:1:0\n165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\n3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:148827\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149100\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149370\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149664\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149943\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:150222\n0:SIG(0)\n1:SIG(0)\n2:SIG(0)\n3:SIG(0)\n4:SIG(0)\n5:SIG(0)\n6000:0:SIG(AopwTfXhj8VqZReFJYGGWnoWnXNj3RgaqFcGGywXpZrD)\n12:0:SIG(3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX)\nEn reglement de tes bons bocaux de fruits et legumes\nnxr4exGrt16jteN9ZX3XZPP9l+X0OUbZ1o/QjE1hbWQNtVU3HhH9SJoEvNj2iVl3gCRr9u2OA9uj9vCyUDyjAg==\nInnerHash: 3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846\nNonce: "
+        );
+        // Test signature validity
+        block.fill_inner_hash_and_nonce_str(Some(10_300_000_090_296));
+        assert_eq!(block.verify_signatures(), VerificationResult::Valid());
+        // Test hash computation
+        block.compute_hash();
+        assert_eq!(
+            block
+                .hash
+                .expect("Try to get hash of an uncompleted or reduce block !")
+                .0
+                .to_hex(),
+            "000002026E32A3D649B34968AAF9D03C4F19A5954229C54A801BBB1CD216B230"
+        );
+    }
 }
diff --git a/documents/src/v10/mod.rs b/documents/src/v10/mod.rs
index 6c6653fa7457ccc4829fdbe79ebe9adabb06928d..bcad4ff71c566f938edf84cb9a83e5c574a66701 100644
--- a/documents/src/v10/mod.rs
+++ b/documents/src/v10/mod.rs
@@ -22,7 +22,6 @@ pub mod membership;
 pub mod revocation;
 pub mod transaction;
 
-use crypto::digest::Digest;
 use dup_crypto::keys::PrivateKey;
 use pest::Parser;
 
@@ -149,12 +148,6 @@ pub trait TextDocument: Document<PublicKey = PubKey, CurrencyType = str> {
         &text[0..(text.len() - sigs_str_len)]
     }
 
-    /// Return sha256 hash of text document
-    fn hash<H: Digest>(&self, digest: &mut H) -> String {
-        digest.input_str(self.as_text());
-        digest.result_str()
-    }
-
     /*/// Return document as text with leading signatures.
     fn as_text_with_signatures(&self) -> String {
         let mut text = self.as_text().to_string();
diff --git a/documents/src/v10/transaction.rs b/documents/src/v10/transaction.rs
index c0729efc4dbb638155fd653d52ff0129bd9f254c..a3878a290579e4214dce47c417e4daa384ac8f45 100644
--- a/documents/src/v10/transaction.rs
+++ b/documents/src/v10/transaction.rs
@@ -15,8 +15,7 @@
 
 //! Wrappers around Transaction documents.
 
-use crypto::digest::Digest;
-use crypto::sha2::Sha256;
+use dup_crypto::hashs::*;
 use pest::iterators::Pair;
 use pest::iterators::Pairs;
 use pest::Parser;
@@ -513,7 +512,6 @@ pub struct TransactionDocument {
 impl TransactionDocument {
     /// Compute transaction hash
     pub fn compute_hash(&mut self) -> Hash {
-        let mut sha256 = Sha256::new();
         let mut hashing_text = if let Some(ref text) = self.text {
             text.clone()
         } else {
@@ -522,8 +520,7 @@ impl TransactionDocument {
         hashing_text.push_str(&self.signatures[0].to_string());
         hashing_text.push_str("\n");
         //println!("tx_text_hasing={}", hashing_text);
-        sha256.input_str(&hashing_text);
-        self.hash = Some(Hash::from_hex(&sha256.result_str()).unwrap());
+        self.hash = Some(Hash::compute_str(&hashing_text));
         self.hash.expect("Try to get hash of a reduce tx !")
     }
     /// get transaction hash option
diff --git a/message/Cargo.toml b/message/Cargo.toml
index c6ec17a1bec33c514a1806ed3486c34924e9b00e..79c4e074f2d503a1dc6b2abb02e18deb25929e47 100644
--- a/message/Cargo.toml
+++ b/message/Cargo.toml
@@ -14,6 +14,7 @@ duniter-dal = { path = "../dal" }
 duniter-documents = { path = "../documents" }
 duniter-module = { path = "../module" }
 duniter-network = { path = "../network" }
+durs-network-documents = { path = "../network-documents" }
 serde = "1.0.*"
 serde_derive = "1.0.*"
 serde_json = "1.0.*"
diff --git a/message/lib.rs b/message/lib.rs
index 2e0f2ade4212c08965a140c57d6f6a8173bdfe1a..d2d1e2e59ae18d2462ac849229dd801009b67d46 100644
--- a/message/lib.rs
+++ b/message/lib.rs
@@ -34,6 +34,7 @@ extern crate duniter_documents;
 extern crate duniter_module;
 extern crate duniter_network;
 extern crate dup_crypto;
+extern crate durs_network_documents;
 extern crate serde;
 extern crate serde_json;
 
@@ -45,6 +46,7 @@ use duniter_module::*;
 use duniter_network::{NetworkEvent, NetworkResponse, OldNetworkRequest};
 use dup_crypto::hashs::Hash;
 use dup_crypto::keys::Sig;
+use durs_network_documents::network_endpoint::EndpointEnum;
 
 #[derive(Debug, Clone)]
 /// Message exchanged between Durs modules
@@ -76,6 +78,8 @@ pub enum DursMsgContent {
     Binary(Vec<u8>),
     /// New configuration of a module to save
     SaveNewModuleConf(ModuleStaticName, serde_json::Value),
+    /// List of local node endpoints
+    Endpoints(Vec<EndpointEnum>),
     /// Response of DALRequest
     DALResponse(Box<DALResponse>),
     /// Blockchain event
diff --git a/module/Cargo.toml b/module/Cargo.toml
index 97a06dd068e5afcb41f5b081be086f7d35efc519..a49bd6d9b50d2ebbddd23493e80a75cea58c1185 100644
--- a/module/Cargo.toml
+++ b/module/Cargo.toml
@@ -11,6 +11,7 @@ path = "lib.rs"
 [dependencies]
 dup-crypto = { path = "../crypto" }
 duniter-documents = { path = "../documents" }
+durs-network-documents = { path = "../network-documents" }
 serde = "1.0.*"
 serde_derive = "1.0.*"
 serde_json = "1.0.*"
diff --git a/module/lib.rs b/module/lib.rs
index 5f0f39ef3047dbff6e6499f6d67bbc6b3dae4dbf..a2834f43a01f781cd34bac7336e80216be4e4a74 100644
--- a/module/lib.rs
+++ b/module/lib.rs
@@ -34,12 +34,14 @@ extern crate serde_derive;
 
 extern crate duniter_documents;
 extern crate dup_crypto;
+extern crate durs_network_documents;
 extern crate serde;
 extern crate serde_json;
 extern crate structopt;
 
 use duniter_documents::CurrencyName;
 use dup_crypto::keys::{KeyPair, KeyPairEnum};
+use durs_network_documents::network_endpoint::EndpointEnum;
 use serde::de::DeserializeOwned;
 use serde::ser::{Serialize, Serializer};
 use std::collections::HashSet;
@@ -213,12 +215,16 @@ pub enum ModuleInitError {
 #[derive(Debug, Clone)]
 /// Type sent by each module to the rooter during initialization
 pub enum RooterThreadMessage<M: ModuleMessage> {
-    /// Channel on which the module listens
-    ModuleSender(
+    /// Number of expected modules
+    ModulesCount(usize),
+    /// Registration of the module at the rooter
+    ModuleRegistration(
         ModuleStaticName,
         mpsc::Sender<M>,
         Vec<ModuleRole>,
         Vec<ModuleEvent>,
+        Vec<String>,
+        Vec<EndpointEnum>,
     ),
     /// Module message
     ModuleMessage(M),
diff --git a/network-documents/Cargo.toml b/network-documents/Cargo.toml
index be4f452368f614bfe47734ded628fd8ac2476cd3..9785dbd526e7856d24b6ce57fe35e941a62be669 100644
--- a/network-documents/Cargo.toml
+++ b/network-documents/Cargo.toml
@@ -12,6 +12,7 @@ path = "src/lib.rs"
 base58 = "0.1.*"
 dup-crypto = { path = "../crypto" }
 duniter-documents = { path = "../documents" }
+hex = "0.3.*"
 pest = "2.0"
 pest_derive = "2.0"
 serde = "1.0.*"
diff --git a/network-documents/src/lib.rs b/network-documents/src/lib.rs
index 784fac492a0db57fe60cb27519136493d97a5065..e2fc967a0009ea9fc11454ade98f78a058dd28ef 100644
--- a/network-documents/src/lib.rs
+++ b/network-documents/src/lib.rs
@@ -29,6 +29,7 @@
 extern crate base58;
 extern crate duniter_documents;
 extern crate dup_crypto;
+extern crate hex;
 extern crate pest;
 #[macro_use]
 extern crate pest_derive;
@@ -55,6 +56,12 @@ use std::fmt::{Display, Error, Formatter};
 /// Parser for network documents
 struct NetworkDocsParser;
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+/// ParseError
+pub enum ParseError {
+    /// Pest grammar error
+    PestError(String),
+}
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Random identifier with which several Duniter nodes with the same network keypair can be differentiated
 pub struct NodeId(pub u32);
@@ -134,9 +141,8 @@ mod tests {
         let node_id = NodeId(u32::from_str_radix("c1c39a0a", 16).unwrap());
         let full_id = NodeFullId(node_id, issuer);
         assert_eq!(
-            EndpointEnum::parse_from_raw("WS2P c1c39a0a i3.ifee.fr 80 /ws2p", issuer, 0, 0, 1),
-            Ok(EndpointEnum::V1(EndpointEnumV1 {
-                version: 1,
+            EndpointV1::parse_from_raw("WS2P c1c39a0a i3.ifee.fr 80 /ws2p", issuer, 0, 0),
+            Ok(EndpointV1 {
                 issuer,
                 api: NetworkEndpointApi(String::from("WS2P")),
                 node_id: Some(node_id),
@@ -147,7 +153,7 @@ mod tests {
                 raw_endpoint: String::from("WS2P c1c39a0a i3.ifee.fr 80 /ws2p"),
                 last_check: 0,
                 status: 0,
-            },))
+            })
         );
     }
 
@@ -160,9 +166,8 @@ mod tests {
         let node_id = NodeId(u32::from_str_radix("cb06a19b", 16).unwrap());
         let full_id = NodeFullId(node_id, issuer);
         assert_eq!(
-            EndpointEnum::parse_from_raw("WS2P cb06a19b g1.imirhil.fr 53012 /", issuer, 0, 0, 1),
-            Ok(EndpointEnum::V1(EndpointEnumV1 {
-                version: 1,
+            EndpointV1::parse_from_raw("WS2P cb06a19b g1.imirhil.fr 53012", issuer, 0, 0),
+            Ok(EndpointV1 {
                 issuer,
                 api: NetworkEndpointApi(String::from("WS2P")),
                 node_id: Some(node_id),
@@ -170,10 +175,10 @@ mod tests {
                 host: String::from("g1.imirhil.fr"),
                 port: 53012,
                 path: None,
-                raw_endpoint: String::from("WS2P cb06a19b g1.imirhil.fr 53012 /"),
+                raw_endpoint: String::from("WS2P cb06a19b g1.imirhil.fr 53012"),
                 last_check: 0,
                 status: 0,
-            },))
+            })
         );
     }
 }
diff --git a/network-documents/src/network_documents.pest b/network-documents/src/network_documents.pest
index 44b3b35d42af28a4b4cb46c1d7f10e7cf77994d2..5461807299b8949616d2d5c7312cd65de0d80761 100644
--- a/network-documents/src/network_documents.pest
+++ b/network-documents/src/network_documents.pest
@@ -1,16 +1,99 @@
 //r"^(?P<api>[A-Z0-9_]+) (?P<version>[1-9][0-9]*)? ?(?P<uuid>[a-f0-9]{6,8})? ?(?P<host>[a-z_][a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+) (?P<port>[0-9]+)(?: /?(?P<path>.+)?)? *$"
+WHITESPACE = _{ "" }
 
+// Single character rules
+nl = _{ "\n" }
+no_zero_hexa_lower = @{ '1'..'9' | 'a'..'f' }
+hexa_lower = @{ ASCII_DIGIT | 'a'..'f' }
+hexa_upper = @{ ASCII_DIGIT | 'A'..'F' }
+base58 = { !("O" | "I" | "l") ~ ASCII_ALPHANUMERIC }
+base64 = { ASCII_ALPHANUMERIC | "+" | "/" }
+
+// Numbers rules
+tens = @{ '1'..'9' ~ ASCII_DIGIT }
+u8_hundreds = @{ ("2" ~ ('0'..'4' ~ ASCII_DIGIT | ('0'..'5'){2})) | ("1" ~ ASCII_DIGIT{2}) }
+u8 = @{ u8_hundreds | tens | ASCII_DIGIT }
 no_zero_u_int = @{ '1'..'9' ~ ASCII_DIGIT* }
 u_int = @{ "0" | no_zero_u_int }
 
-api_name = @{ (ASCII_ALPHA_UPPER | ASCII_DIGIT)+ }
-version = @{ u_int }
-node_id = @{ ASCII_HEX_DIGIT{6,8} }
+// Usefull types rules
+currency = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "-" | "_"){,255} }
+block_id = @{ u_int }
+hash = @{ hexa_upper{64} }
+pubkey = @{ base58{43,44} }
+blockstamp =  ${ block_id ~ "-" ~ hash }
+ed25519_sig = @{ base64{88} | (base64{87} ~ "=") | (base64{86} ~ "==") }
+
+// Endpoint v1 rules
+api_name = @{ (ASCII_ALPHA_UPPER | ASCII_DIGIT | "_")+ }
+node_id = @{ hexa_lower{1,8} }
 host = @{ ASCII_ALPHA_LOWER ~ (ASCII_ALPHA_LOWER | ASCII_DIGIT | "-" | "_" | ".")* }
 port = @{ u_int }
 path_inner = @{ (ASCII_ALPHANUMERIC | "-" | "_" | ".")+ }
-path = _{ "/"? ~ path_inner }
 
-endpoint_v1 = { api_name ~ version? ~ node_id? ~ host ~ port ~ path? }
+endpoint_v1 = ${ api_name ~ (" " ~ node_id)? ~ " " ~ host ~ " " ~ port ~ (" " ~ "/"? ~ path_inner)? ~ " "? }
+
+// IP v6 rules 
+ip6_seg  = _{ hexa_lower{1,4} }
+ip6_full = _{ (ip6_seg ~ ":"){7} ~ ip6_seg }
+ip6_no_full = @{
+    ip6_seg? ~
+    (":" ~ ip6_seg){0,6} ~
+    "::" ~
+    (ip6_seg ~ ":"){0,6} ~
+    ip6_seg?
+    //       (IPV6SEG:){7,7}IPV6SEG|                # 1:2:3:4:5:6:7:8
+    //       (IPV6SEG:){1,7}:|                      # 1::                                 1:2:3:4:5:6:7::
+    //       (IPV6SEG:){1,6}:IPV6SEG|               # 1::8               1:2:3:4:5:6::8   1:2:3:4:5:6::8
+    //       (IPV6SEG:){1,5}(:IPV6SEG){1,2}|        # 1::7:8             1:2:3:4:5::7:8   1:2:3:4:5::8
+    //       (IPV6SEG:){1,4}(:IPV6SEG){1,3}|        # 1::6:7:8           1:2:3:4::6:7:8   1:2:3:4::8
+    //       (IPV6SEG:){1,3}(:IPV6SEG){1,4}|        # 1::5:6:7:8         1:2:3::5:6:7:8   1:2:3::8
+    //       (IPV6SEG:){1,2}(:IPV6SEG){1,5}|        # 1::4:5:6:7:8       1:2::4:5:6:7:8   1:2::8
+    //       IPV6SEG:((:IPV6SEG){1,6})|             # 1::3:4:5:6:7:8     1::3:4:5:6:7:8   1::8
+    //       :((:IPV6SEG){1,7}|:)|                  # ::2:3:4:5:6:7:8    ::2:3:4:5:6:7:8  ::8       ::       
+
+}
+ip6_inner = @{ ip6_full | ip6_no_full }
+ip6 = _{ "[" ~ ip6_inner ~ "] " }
+
+// Endpoint v2 rules
+api_version_inner = @{ no_zero_u_int }
+api_version = _{ "V" ~ api_version_inner ~ " " }
+tls = @{ "S " }
+tor = @{ "TOR " }
+network_features = _{ tls? ~ tor? }
+api_features_inner = @{ (hexa_lower{2})+ | no_zero_hexa_lower }
+api_features = _{ api_features_inner ~ " " }
+host_v2_inner = @{ ASCII_ALPHA_LOWER ~ (ASCII_ALPHA_LOWER | ASCII_DIGIT | "-" | "_" | ".")* }
+host_v2 = _{ host_v2_inner ~ " " }
+ip4_inner = { u8 ~ "." ~ u8 ~ "." ~ u8 ~ "." ~ u8 }
+ip4 = _{ ip4_inner ~ " " }
+path = _{ " " ~ path_inner }
+endpoint_v2 = ${ api_name ~ " " ~ (api_version)? ~ (network_features)? ~ (api_features)? ~ host_v2? ~ ip4? ~ ip6? ~ port ~ path? }
+
+// Peer v11 rules
+peer_v11 = ${ "11:" ~ currency ~ ":" ~ node_id ~ ":" ~ pubkey ~ ":" ~ blockstamp ~ nl ~ (endpoint_v2 ~ nl)+ ~ ed25519_sig? }
+
+// HEADv3 rules
+api_outgoing_conf = @{ u8 }
+api_incoming_conf = @{ u8 }
+free_member_rooms = @{ u8 }
+free_mirror_rooms = @{ u8 }
+software = @{ ASCII_ALPHA ~ ASCII_ALPHANUMERIC* }
+soft_version = @{ ASCII_DIGIT ~ "." ~ ASCII_DIGIT ~ "." ~ ASCII_DIGIT ~ ("-" ~ ("a" | "b" | "rc") ~ ASCII_DIGIT ~ ("." ~ ASCII_DIGIT){0,2})? }
+step = @{ u_int }
 
-WHITESPACE = _{ " " }
\ No newline at end of file
+head_v3 = ${
+    "3:" ~ currency ~ ":" ~
+    api_outgoing_conf ~ ":" ~
+    api_incoming_conf ~ ":" ~
+    free_member_rooms ~ ":" ~
+    free_mirror_rooms ~ ":" ~
+    node_id ~ ":" ~
+    pubkey ~ ":" ~
+    blockstamp ~ ":" ~
+    software ~ ":" ~
+    soft_version ~ nl ~
+    ed25519_sig? ~
+    (nl ~ step)?
+}
diff --git a/network-documents/src/network_endpoint.rs b/network-documents/src/network_endpoint.rs
index 62c7feaf40fda5337efe76e60dcb9cd930a9eeb7..479c8c3e09a0722b9ad3d572448996bbf2f0573e 100644
--- a/network-documents/src/network_endpoint.rs
+++ b/network-documents/src/network_endpoint.rs
@@ -21,9 +21,10 @@ extern crate serde;
 
 use dup_crypto::hashs::Hash;
 use dup_crypto::keys::PubKey;
+use hex;
+use pest::iterators::Pair;
 use pest::Parser;
-use std::net::{AddrParseError, Ipv4Addr, Ipv6Addr};
-use std::num::ParseIntError;
+use std::net::{Ipv4Addr, Ipv6Addr};
 use std::str::FromStr;
 use *;
 
@@ -39,100 +40,26 @@ pub static MAX_API_FEATURES_COUNT: &'static usize = &2040;
 pub struct ApiFeatures(pub Vec<u8>);
 
 impl ApiFeatures {
-    fn to_string_for_api(&self, api: &NetworkEndpointApi) -> String {
-        match api.0.as_str() {
-            "WS2P" => {
-                let mut af_count = 0;
-                let def = if self.0[0] | 0b_1111_1110 == 255u8 {
-                    af_count += 1;
-                    " DEF"
-                } else {
-                    ""
-                };
-                let low = if self.0[0] | 0b_1111_1101 == 255u8 {
-                    af_count += 1;
-                    " LOW"
-                } else {
-                    ""
-                };
-                let abf = if self.0[0] | 0b_1111_1011 == 255u8 {
-                    af_count += 1;
-                    " ABF"
-                } else {
-                    ""
-                };
-                format!("{}{}{}{}", af_count, def, low, abf)
+    fn is_empty(&self) -> bool {
+        for byte in &self.0 {
+            if *byte > 0u8 {
+                return false;
             }
-            _ => String::from(""),
         }
+        true
     }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-/// ParseEndpointError
-pub enum ParseEndpointError {
-    /// VersionNotSupported
-    VersionNotSupported(),
-    /// WrongV1Format
-    WrongV1Format(String),
-    /// WrongV2Format (human-readable explanation)
-    WrongV2Format(&'static str),
-    /// ApiNameTooLong
-    ApiNameTooLong(),
-    /// ParseIntError
-    ParseIntError(ParseIntError),
-    /// UnknowNetworkFeature (feature name)
-    UnknowNetworkFeature(String),
-    /// Maximum number of network features exceeded
-    MaxNetworkFeatures(),
-    /// Maximum number of api features exceeded
-    MaxApiFeatures(),
-    /// UnknowApiFeature (feature name)
-    UnknowApiFeature(String),
-    /// TooHighApiFeature
-    TooHighApiFeature(),
-    /// IP Parse error
-    AddrParseError(AddrParseError),
-}
 
-impl From<ParseIntError> for ParseEndpointError {
-    fn from(e: ParseIntError) -> Self {
-        ParseEndpointError::ParseIntError(e)
-    }
-}
-
-impl From<AddrParseError> for ParseEndpointError {
-    fn from(e: AddrParseError) -> Self {
-        ParseEndpointError::AddrParseError(e)
-    }
-}
-
-#[derive(Debug)]
-/// Error when converting a byte vector to Endpoint
-pub enum EndpointReadBytesError {
-    /// Bytes vector is too short
-    TooShort(),
-    /// Bytes vector is too long
-    TooLong(),
-    /// Wrong api datas Length
-    WrongApiDatasLen(),
-    /// Unknow api name
-    UnknowApiName(),
-    /// IoError
-    IoError(::std::io::Error),
-    /// FromUtf8Error
-    FromUtf8Error(::std::string::FromUtf8Error),
-}
-
-impl From<::std::io::Error> for EndpointReadBytesError {
-    fn from(e: ::std::io::Error) -> Self {
-        EndpointReadBytesError::IoError(e)
-    }
-}
-
-impl From<::std::string::FromUtf8Error> for EndpointReadBytesError {
-    fn from(e: ::std::string::FromUtf8Error) -> Self {
-        EndpointReadBytesError::FromUtf8Error(e)
+    fn to_string(&self) -> String {
+        if self.is_empty() {
+            String::from("")
+        } else {
+            let hex_str = hex::encode(self.0.clone());
+            if hex_str.len() == 2 {
+                format!("{} ", &hex_str[1..])
+            } else {
+                format!("{} ", hex_str)
+            }
+        }
     }
 }
 
@@ -142,9 +69,7 @@ pub struct NetworkEndpointApi(pub String);
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Endpoint v1
-pub struct EndpointEnumV1 {
-    /// API version
-    pub version: usize,
+pub struct EndpointV1 {
     /// API Name
     pub api: NetworkEndpointApi,
     /// Node unique identifier
@@ -167,42 +92,129 @@ pub struct EndpointEnumV1 {
     pub last_check: u64,
 }
 
+impl EndpointV1 {
+    /// Accessors providing node full identifier
+    pub fn node_full_id(&self) -> Option<NodeFullId> {
+        match self.node_id {
+            Some(node_id) => Some(NodeFullId(node_id, self.issuer)),
+            None => None,
+        }
+    }
+    /// Generate endpoint url
+    pub fn get_url(&self, get_protocol: bool, _supported_ip_v6: bool) -> Option<String> {
+        let protocol = match &self.api.0[..] {
+            "WS2P" | "WS2PTOR" => "ws",
+            _ => "http",
+        };
+        let tls = match self.port {
+            443 => "s",
+            _ => "",
+        };
+        let path = match self.path {
+            Some(ref path_string) => path_string.clone(),
+            None => String::new(),
+        };
+        if get_protocol {
+            Some(format!(
+                "{}{}://{}:{}/{}",
+                protocol, tls, self.host, self.port, path
+            ))
+        } else {
+            Some(format!("{}:{}/{}", self.host, self.port, path))
+        }
+    }
+    /// Generate from pest pair
+    fn from_pest_pair(
+        pair: Pair<Rule>,
+        issuer: PubKey,
+        status: u32,
+        last_check: u64,
+    ) -> EndpointV1 {
+        let raw_endpoint = String::from(pair.as_str());
+        let mut api_name = "";
+        let mut node_id = None;
+        let mut hash_full_id = None;
+        let mut host_str = "";
+        let mut port = 0;
+        let mut path = None;
+
+        for ep_pair in pair.into_inner() {
+            match ep_pair.as_rule() {
+                Rule::api_name => api_name = ep_pair.as_str(),
+                Rule::node_id => {
+                    node_id = Some(NodeId(u32::from_str_radix(ep_pair.as_str(), 16).unwrap()));
+                    hash_full_id = match node_id {
+                        Some(node_id_) => Some(NodeFullId(node_id_, issuer).sha256()),
+                        None => None,
+                    };
+                }
+                Rule::host => host_str = ep_pair.as_str(),
+                Rule::port => port = ep_pair.as_str().parse().unwrap(),
+                Rule::path_inner => path = Some(String::from(ep_pair.as_str())),
+                _ => panic!("unexpected rule: {:?}", ep_pair.as_rule()), // Grammar ensures that we never reach this line
+            }
+        }
+        EndpointV1 {
+            issuer,
+            api: NetworkEndpointApi(String::from(api_name)),
+            node_id,
+            hash_full_id,
+            host: String::from(host_str),
+            port,
+            path,
+            raw_endpoint,
+            status,
+            last_check,
+        }
+    }
+
+    /// parse from ut8 format
+    pub fn parse_from_raw(
+        raw_endpoint: &str,
+        issuer: PubKey,
+        status: u32,
+        last_check: u64,
+    ) -> Result<EndpointV1, ParseError> {
+        match NetworkDocsParser::parse(Rule::endpoint_v1, raw_endpoint) {
+            Ok(mut ep_v1_pairs) => Ok(EndpointV1::from_pest_pair(
+                ep_v1_pairs.next().unwrap(),
+                issuer,
+                status,
+                last_check,
+            )),
+            Err(pest_error) => Err(ParseError::PestError(format!("{}", pest_error))),
+        }
+    }
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Network features
 pub struct EndpointV2NetworkFeatures(pub Vec<u8>);
 
 impl ToString for EndpointV2NetworkFeatures {
     fn to_string(&self) -> String {
+        if self.is_empty() {
+            return "".to_owned();
+        }
+        let mut features_str = Vec::with_capacity(2);
         if self.tls() {
-            String::from("1 TLS")
-        } else if self.tor() {
-            String::from("1 TOR")
-        } else {
-            String::from("")
+            features_str.push("S");
+        }
+        if self.tor() {
+            features_str.push("TOR");
         }
+        format!("{} ", features_str.join(" "))
     }
 }
 
 impl EndpointV2NetworkFeatures {
-    /// Parse network features from utf8 string's array
-    pub fn from_str_array(
-        str_array: &[&str],
-    ) -> Result<EndpointV2NetworkFeatures, ParseEndpointError> {
-        let mut network_features = 0u8;
-        for nf_str in str_array {
-            match *nf_str {
-                "IP4" => network_features += 1u8,
-                "IP6" => network_features += 2u8,
-                "TLS" => network_features += 4u8,
-                "TOR" => network_features += 8u8,
-                &_ => {
-                    return Err(ParseEndpointError::UnknowNetworkFeature(String::from(
-                        *nf_str,
-                    )))
-                }
+    fn is_empty(&self) -> bool {
+        for byte in &self.0 {
+            if *byte > 0u8 {
+                return false;
             }
         }
-        Ok(EndpointV2NetworkFeatures(vec![network_features]))
+        true
     }
     /// Network features size
     pub fn size(&self) -> u8 {
@@ -212,21 +224,13 @@ impl EndpointV2NetworkFeatures {
     pub fn to_bytes_slice(&self) -> &[u8] {
         &self.0
     }
-    /// network feature ip_v4 is enable ?
-    pub fn ip_v4(&self) -> bool {
-        self.0[0] & 0b0000_0001 == 1u8
-    }
-    /// network feature ip_v6 is enable ?
-    pub fn ip_v6(&self) -> bool {
-        self.0[0] & 0b0000_0010 == 2u8
-    }
     /// TLS feature is enable ?
     pub fn tls(&self) -> bool {
-        self.0[0] & 0b0000_0100 == 4u8
+        self.0[0] & 0b0000_0001 == 1u8
     }
     /// TOR feature is enable ?
     pub fn tor(&self) -> bool {
-        self.0[0] & 0b0000_1000 == 8u8
+        self.0[0] & 0b0000_0010 == 2u8
     }
 }
 
@@ -281,17 +285,21 @@ impl ToString for EndpointV2 {
         } else {
             String::from("")
         };
-        let path: &str = if let Some(ref path) = self.path {
-            path
+        let path = if let Some(ref path) = self.path {
+            format!(" {}", path)
         } else {
-            ""
+            "".to_owned()
         };
         format!(
-            "{api} {version} {nf} {af} {port} {host}{ip4}{ip6}{path}",
+            "{api} {version}{nf}{af}{host}{ip4}{ip6}{port}{path}",
             api = self.api.0,
-            version = self.api_version,
+            version = if self.api_version > 0 {
+                format!("V{} ", self.api_version)
+            } else {
+                "".to_owned()
+            },
             nf = self.network_features.to_string(),
-            af = self.api_features.to_string_for_api(&self.api),
+            af = self.api_features.to_string(),
             port = self.port,
             host = host,
             ip4 = ip4,
@@ -301,47 +309,6 @@ impl ToString for EndpointV2 {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-/// Size informations of Endpoint v2
-pub struct EndpointV2Size {
-    /// Api nalme size
-    pub api_size: u8,
-    /// Hostname size
-    pub host_size: u8,
-    /// Optional path size
-    pub path_size: u8,
-    /// Network features size
-    pub nf_size: u8,
-    /// Network feature ip_v4
-    pub ip_v4: bool,
-    /// Network feature ip_v6
-    pub ip_v6: bool,
-    /// Api features size
-    pub af_size: u8,
-}
-
-impl EndpointV2Size {
-    /// Compute total size of endpoint in binary format
-    pub fn total_size(self) -> usize {
-        let mut total_size = self.api_size as usize
-            + self.host_size as usize
-            + self.path_size as usize
-            + self.nf_size as usize
-            + self.af_size as usize
-            + ENDPOINTV2_FIXED_SIZE;
-        if self.api_size == 0u8 {
-            total_size += 1;
-        }
-        if self.ip_v4 {
-            total_size += 4;
-        }
-        if self.ip_v6 {
-            total_size += 16;
-        }
-        total_size
-    }
-}
-
 impl EndpointV2 {
     /// Generate endpoint url
     pub fn get_url(&self, get_protocol: bool, supported_ip_v6: bool) -> Option<String> {
@@ -375,232 +342,60 @@ impl EndpointV2 {
             Some(format!("{}:{}/{}", host, self.port, path))
         }
     }
-    /// parse from ut8 format
-    pub fn parse_from_raw(
-        raw_endpoint: &str,
-        _status: u32,
-        _last_check: u64,
-    ) -> Result<EndpointEnum, ParseEndpointError> {
-        let raw_ep_elements: Vec<&str> = raw_endpoint.split(' ').collect();
-        if raw_ep_elements.len() >= 6 {
-            let api = NetworkEndpointApi(String::from(raw_ep_elements[0]));
-            let api_version: u16 = raw_ep_elements[1].parse()?;
-            let network_features_count: usize = raw_ep_elements[2].parse()?;
-            if network_features_count > *MAX_NETWORK_FEATURES_COUNT {
-                Err(ParseEndpointError::MaxNetworkFeatures())
-            } else if raw_ep_elements.len() >= 6 + network_features_count {
-                let network_features = EndpointV2NetworkFeatures::from_str_array(
-                    &raw_ep_elements[3..(3 + network_features_count)],
-                )?;
-                let api_features_count: usize =
-                    raw_ep_elements[3 + network_features_count].parse()?;
-                if network_features_count > *MAX_API_FEATURES_COUNT {
-                    Err(ParseEndpointError::MaxApiFeatures())
-                } else {
-                    let mut af_bytes_count = network_features_count / 8;
-                    if network_features_count % 8 != 0 {
-                        af_bytes_count += 1;
-                    }
-                    let mut api_features = vec![0u8; af_bytes_count];
-                    if raw_ep_elements.len() < 4 + network_features_count + api_features_count {
-                        return Err(ParseEndpointError::WrongV2Format(
-                            "All api features must be declared !",
-                        ));
-                    }
-                    for str_feature in raw_ep_elements
-                        .iter()
-                        .take(4 + network_features_count + api_features_count)
-                        .skip(4 + network_features_count)
-                    {
-                        if let Ok(feature) = str_feature.parse::<usize>() {
-                            if feature > *MAX_API_FEATURES_COUNT {
-                                return Err(ParseEndpointError::TooHighApiFeature());
-                            }
-                            let byte_index = feature / 8;
-                            let feature = (feature % 8) as u8;
-                            api_features[byte_index] += feature.pow(2);
-                        } else if &api.0 == "WS2P" {
-                            match *str_feature {
-                                "DEF" => api_features[0] += 1u8,
-                                "LOW" => api_features[0] += 2u8,
-                                "ABF" => api_features[0] += 4u8,
-                                _ => {
-                                    return Err(ParseEndpointError::UnknowApiFeature(String::from(
-                                        *str_feature,
-                                    )))
-                                }
-                            }
-                        } else {
-                            return Err(ParseEndpointError::UnknowApiFeature(String::from(
-                                *str_feature,
-                            )));
-                        }
-                    }
-                    let mut index = 4 + network_features_count + api_features_count;
-                    let port = if let Ok(port) = raw_ep_elements[index].parse::<u16>() {
-                        index += 1;
-                        port
-                    } else {
-                        return Err(ParseEndpointError::WrongV2Format(
-                            "Missing port or is not integer !",
-                        ));
-                    };
-                    // HOST IP4 [IP6] PATH
-                    let (host, ip_v4, ip_v6, path) = if raw_ep_elements.len() == index + 4 {
-                        // HOST IP4 [IP6] PATH
-                        let len2 = raw_ep_elements[index + 2].len();
-                        (
-                            Some(String::from(raw_ep_elements[index])),
-                            Some(Ipv4Addr::from_str(raw_ep_elements[index + 1])?),
-                            Some(Ipv6Addr::from_str(
-                                &raw_ep_elements[index + 2][1..len2 - 1],
-                            )?),
-                            Some(String::from(raw_ep_elements[index + 3])),
-                        )
-                    } else if raw_ep_elements.len() == index + 3 {
-                        // IP4 [IP6] PATH
-                        if let Ok(ip_v4) = Ipv4Addr::from_str(raw_ep_elements[index]) {
-                            let len1 = raw_ep_elements[index + 1].len();
-                            (
-                                None,
-                                Some(ip_v4),
-                                Some(Ipv6Addr::from_str(
-                                    &raw_ep_elements[index + 1][1..len1 - 1],
-                                )?),
-                                Some(String::from(raw_ep_elements[index + 2])),
-                            )
-                        } else {
-                            let len1 = raw_ep_elements[index + 1].len();
-                            let len2 = raw_ep_elements[index + 2].len();
-                            if let Some('[') = raw_ep_elements[index + 1].chars().next() {
-                                // HOST [IP6] PATH
-                                (
-                                    Some(String::from(raw_ep_elements[index])),
-                                    None,
-                                    Some(Ipv6Addr::from_str(
-                                        &raw_ep_elements[index + 1][1..len1 - 1],
-                                    )?),
-                                    Some(String::from(raw_ep_elements[index + 2])),
-                                )
-                            } else if let Some('[') = raw_ep_elements[index + 2].chars().next() {
-                                // HOST IP4 [IP6]
-                                (
-                                    Some(String::from(raw_ep_elements[index])),
-                                    Some(Ipv4Addr::from_str(raw_ep_elements[index + 1])?),
-                                    Some(Ipv6Addr::from_str(
-                                        &raw_ep_elements[index + 2][1..len2 - 1],
-                                    )?),
-                                    None,
-                                )
-                            } else {
-                                // HOST IP4 PATH
-                                (
-                                    Some(String::from(raw_ep_elements[index])),
-                                    Some(Ipv4Addr::from_str(raw_ep_elements[index + 1])?),
-                                    None,
-                                    Some(String::from(raw_ep_elements[index + 2])),
-                                )
-                            }
-                        }
-                    } else if raw_ep_elements.len() == index + 2 {
-                        let len0 = raw_ep_elements[index].len();
-                        let len1 = raw_ep_elements[index + 1].len();
-                        if let Ok(ip_v4) = Ipv4Addr::from_str(raw_ep_elements[index]) {
-                            if let Some('[') = raw_ep_elements[index + 1].chars().next() {
-                                // IP4 [IP6]
-                                (
-                                    None,
-                                    Some(ip_v4),
-                                    Some(Ipv6Addr::from_str(
-                                        &raw_ep_elements[index + 1][1..len1 - 1],
-                                    )?),
-                                    None,
-                                )
-                            } else {
-                                // IP4 PATH
-                                (
-                                    None,
-                                    Some(ip_v4),
-                                    None,
-                                    Some(String::from(raw_ep_elements[index + 1])),
-                                )
-                            }
-                        } else if let Some('[') = raw_ep_elements[index].chars().next() {
-                            // [IP6] PATH
-                            (
-                                None,
-                                None,
-                                Some(Ipv6Addr::from_str(&raw_ep_elements[index][1..len0 - 1])?),
-                                Some(String::from(raw_ep_elements[index + 1])),
-                            )
-                        } else if let Ok(ip_v4) = Ipv4Addr::from_str(raw_ep_elements[index + 1]) {
-                            // HOST IP4
-                            (
-                                Some(String::from(raw_ep_elements[index])),
-                                Some(ip_v4),
-                                None,
-                                None,
-                            )
-                        } else if let Some('[') = raw_ep_elements[index + 1].chars().next() {
-                            // HOST [IP6]
-                            (
-                                Some(String::from(raw_ep_elements[index])),
-                                None,
-                                Some(Ipv6Addr::from_str(
-                                    &raw_ep_elements[index + 1][1..len1 - 1],
-                                )?),
-                                None,
-                            )
-                        } else {
-                            // HOST PATH
-                            (
-                                Some(String::from(raw_ep_elements[index])),
-                                None,
-                                None,
-                                Some(String::from(raw_ep_elements[index + 1])),
-                            )
-                        }
-                    } else if raw_ep_elements.len() == index + 1 {
-                        let len0 = raw_ep_elements[index].len();
-                        if let Some('[') = raw_ep_elements[index].chars().next() {
-                            // IP6
-                            (
-                                None,
-                                None,
-                                Some(Ipv6Addr::from_str(&raw_ep_elements[index][1..len0])?),
-                                None,
-                            )
-                        } else if let Ok(ip_v4) = Ipv4Addr::from_str(raw_ep_elements[index]) {
-                            // IP4
-                            (None, Some(ip_v4), None, None)
-                        } else {
-                            // HOST
-                            (Some(String::from(raw_ep_elements[index])), None, None, None)
-                        }
+    /// Generate from pest pair
+    pub fn from_pest_pair(pair: Pair<Rule>) -> EndpointV2 {
+        let mut api_str = "";
+        let mut api_version = 0;
+        let mut network_features = EndpointV2NetworkFeatures(vec![0u8]);
+        let mut api_features = ApiFeatures(vec![]);
+        let mut ip_v4 = None;
+        let mut ip_v6 = None;
+        let mut host = None;
+        let mut port = 0;
+        let mut path = None;
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::api_name => api_str = field.as_str(),
+                Rule::api_version_inner => api_version = field.as_str().parse().unwrap(),
+                Rule::tls => network_features.0[0] |= 0b_0000_0001,
+                Rule::tor => network_features.0[0] |= 0b_0000_0010,
+                Rule::api_features_inner => {
+                    api_features = if field.as_str().len() == 1 {
+                        ApiFeatures(hex::decode(&format!("0{}", field.as_str())).unwrap())
                     } else {
-                        return Err(ParseEndpointError::WrongV2Format("Invalid fields count !"));
+                        ApiFeatures(hex::decode(field.as_str()).unwrap())
                     };
-                    Ok(EndpointEnum::V2(EndpointV2 {
-                        api,
-                        api_version,
-                        network_features,
-                        api_features: ApiFeatures(api_features.to_vec()),
-                        ip_v4,
-                        ip_v6,
-                        host,
-                        port,
-                        path,
-                    }))
                 }
-            } else {
-                Err(ParseEndpointError::WrongV2Format(
-                    "All network features must be declared !",
-                ))
+                Rule::port => port = field.as_str().parse().unwrap(),
+                Rule::host_v2_inner => host = Some(String::from(field.as_str())),
+                Rule::ip4_inner => ip_v4 = Some(Ipv4Addr::from_str(field.as_str()).unwrap()),
+                Rule::ip6_inner => ip_v6 = Some(Ipv6Addr::from_str(field.as_str()).unwrap()),
+                Rule::path_inner => path = Some(String::from(field.as_str())),
+                _ => panic!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
             }
-        } else {
-            Err(ParseEndpointError::WrongV2Format(
-                "An endpoint must contain at least 6 elements",
-            ))
+        }
+        if network_features.is_empty() {
+            network_features = EndpointV2NetworkFeatures(vec![]);
+        }
+        EndpointV2 {
+            api: NetworkEndpointApi(String::from(api_str)),
+            api_version,
+            network_features,
+            api_features,
+            ip_v4,
+            ip_v6,
+            host,
+            port,
+            path,
+        }
+    }
+    /// parse from raw ascii format
+    pub fn parse_from_raw(raw_endpoint: &str) -> Result<EndpointEnum, ParseError> {
+        match NetworkDocsParser::parse(Rule::endpoint_v2, raw_endpoint) {
+            Ok(mut ep_v2_pairs) => Ok(EndpointEnum::V2(EndpointV2::from_pest_pair(
+                ep_v2_pairs.next().unwrap(),
+            ))),
+            Err(pest_error) => Err(ParseError::PestError(format!("{}", pest_error))),
         }
     }
 }
@@ -609,7 +404,7 @@ impl EndpointV2 {
 /// Endpoint
 pub enum EndpointEnum {
     /// Endpoint v1
-    V1(EndpointEnumV1),
+    V1(EndpointV1),
     /// Endpoint v2
     V2(EndpointV2),
 }
@@ -645,13 +440,6 @@ impl EndpointEnum {
             EndpointEnum::V2(ref _ep) => unreachable!(),
         }
     }
-    /// Accessors providing node full identifier
-    pub fn node_full_id(&self) -> Option<NodeFullId> {
-        match self.node_uuid() {
-            Some(node_id) => Some(NodeFullId(node_id, self.pubkey())),
-            None => None,
-        }
-    }
     /// Accessors providing port number
     pub fn port(&self) -> usize {
         match *self {
@@ -715,103 +503,6 @@ impl EndpointEnum {
             EndpointEnum::V2(ref ep_v2) => ep_v2.get_url(get_protocol, supported_ip_v6),
         }
     }
-    /// Parse Endpoint from raw format
-    pub fn parse_from_raw(
-        raw_endpoint: &str,
-        issuer: PubKey,
-        status: u32,
-        last_check: u64,
-        endpoint_version: u16,
-    ) -> Result<EndpointEnum, ParseEndpointError> {
-        match endpoint_version {
-            1 => match NetworkDocsParser::parse(Rule::endpoint_v1, raw_endpoint) {
-                Ok(mut ep_ast) => {
-                    let ep_pairs = ep_ast.next().unwrap().into_inner(); // get and unwrap the `envpoint_v1` rule; never fails
-
-                    let mut api_name = "";
-                    let mut node_id = None;
-                    let mut hash_full_id = None;
-                    let mut host_str = "";
-                    let mut port = 0;
-                    let mut path = None;
-
-                    for ep_pair in ep_pairs {
-                        match ep_pair.as_rule() {
-                            Rule::api_name => api_name = ep_pair.as_str(),
-                            Rule::node_id => {
-                                node_id = match u32::from_str_radix(ep_pair.as_str(), 16) {
-                                    Ok(node_id) => Some(NodeId(node_id)),
-                                    Err(_) => {
-                                        return Err(ParseEndpointError::WrongV1Format(
-                                            "NodeId must be an hexadecimal string !".to_owned(),
-                                        ))
-                                    }
-                                };
-                                hash_full_id = match node_id {
-                                    Some(node_id_) => Some(NodeFullId(node_id_, issuer).sha256()),
-                                    None => None,
-                                };
-                            }
-                            Rule::host => host_str = ep_pair.as_str(),
-                            Rule::port => port = ep_pair.as_str().parse().unwrap(),
-                            Rule::path_inner => path = Some(String::from(ep_pair.as_str())),
-                            _ => panic!("unexpected rule: {:?}", ep_pair.as_rule()), // Grammar ensures that we never reach this line
-                        }
-                    }
-                    Ok(EndpointEnum::V1(EndpointEnumV1 {
-                        version: 1,
-                        issuer,
-                        api: NetworkEndpointApi(String::from(api_name)),
-                        node_id,
-                        hash_full_id,
-                        host: String::from(host_str),
-                        port,
-                        path,
-                        raw_endpoint: String::from(raw_endpoint),
-                        status,
-                        last_check,
-                    }))
-                }
-                Err(e) => Err(ParseEndpointError::WrongV1Format(format!("{}", e))),
-            },
-            /*match ENDPOINT_V1_REGEX.captures(raw_endpoint) {
-                Some(caps) => {
-                    let node_id = match caps.name("uuid") {
-                        Some(caps_node_id) => {
-                            match u32::from_str_radix(caps_node_id.as_str(), 16) {
-                                Ok(node_id) => Some(NodeId(node_id)),
-                                Err(_) => None,
-                            }
-                        }
-                        None => None,
-                    };
-                    let hash_full_id = match node_id {
-                        Some(node_id_) => Some(NodeFullId(node_id_, issuer).sha256()),
-                        None => None,
-                    };
-                    Ok(EndpointEnum::V1(EndpointEnumV1 {
-                        version: 1,
-                        issuer,
-                        api: NetworkEndpointApi(String::from(&caps["api"])),
-                        node_id,
-                        hash_full_id,
-                        host: String::from(&caps["host"]),
-                        port: caps["port"].parse().unwrap_or(80),
-                        path: match caps.name("path") {
-                            Some(m) => Some(m.as_str().to_string()),
-                            None => None,
-                        },
-                        raw_endpoint: String::from(raw_endpoint),
-                        status,
-                        last_check,
-                    }))
-                }
-                None => Err(ParseEndpointError::WrongV1Format()),
-            },*/
-            2 => EndpointV2::parse_from_raw(raw_endpoint, status, last_check),
-            _ => Err(ParseEndpointError::VersionNotSupported()),
-        }
-    }
 }
 
 #[cfg(test)]
@@ -819,9 +510,31 @@ mod tests {
     use super::*;
     use tests::bincode::{deserialize, serialize};
 
+    #[test]
+    fn test_network_features() {
+        assert_eq!(EndpointV2NetworkFeatures(vec![1u8]).tls(), true);
+        assert_eq!(EndpointV2NetworkFeatures(vec![1u8]).tor(), false);
+        assert_eq!(EndpointV2NetworkFeatures(vec![2u8]).tls(), false);
+        assert_eq!(EndpointV2NetworkFeatures(vec![2u8]).tor(), true);
+        assert_eq!(EndpointV2NetworkFeatures(vec![3u8]).tls(), true);
+        assert_eq!(EndpointV2NetworkFeatures(vec![3u8]).tor(), true);
+
+        assert_eq!(
+            EndpointV2NetworkFeatures(vec![1u8]).to_string().as_str(),
+            "S "
+        );
+        assert_eq!(
+            EndpointV2NetworkFeatures(vec![2u8]).to_string().as_str(),
+            "TOR "
+        );
+        assert_eq!(
+            EndpointV2NetworkFeatures(vec![3u8]).to_string().as_str(),
+            "S TOR "
+        );
+    }
     fn test_parse_and_read_endpoint(str_endpoint: &str, endpoint: EndpointV2) {
         assert_eq!(
-            EndpointV2::parse_from_raw(str_endpoint, 0, 0),
+            EndpointV2::parse_from_raw(str_endpoint),
             Ok(EndpointEnum::V2(endpoint.clone())),
         );
         let binary_endpoint = serialize(&endpoint).expect("Fail to serialize endpoint !");
@@ -831,13 +544,47 @@ mod tests {
         assert_eq!(str_endpoint, endpoint.to_string());
     }
 
+    #[test]
+    fn test_parse_and_read_minimal_endpoint() {
+        let str_endpoint = "UNKNOWN_API 8080";
+        let endpoint = EndpointV2 {
+            api: NetworkEndpointApi(String::from("UNKNOWN_API")),
+            api_version: 0,
+            network_features: EndpointV2NetworkFeatures(vec![]),
+            api_features: ApiFeatures(vec![]),
+            ip_v4: None,
+            ip_v6: None,
+            host: None,
+            port: 8080u16,
+            path: None,
+        };
+        test_parse_and_read_endpoint(str_endpoint, endpoint);
+    }
+
+    #[test]
+    fn test_parse_and_read_classic_v1_endpoint() {
+        let str_endpoint = "ES_CORE_API g1.data.duniter.fr 443";
+        let endpoint = EndpointV2 {
+            api: NetworkEndpointApi(String::from("ES_CORE_API")),
+            api_version: 0,
+            network_features: EndpointV2NetworkFeatures(vec![]),
+            api_features: ApiFeatures(vec![]),
+            ip_v4: None,
+            ip_v6: None,
+            host: Some(String::from("g1.data.duniter.fr")),
+            port: 443u16,
+            path: None,
+        };
+        test_parse_and_read_endpoint(str_endpoint, endpoint);
+    }
+
     #[test]
     fn test_parse_and_read_endpoint_with_host() {
-        let str_endpoint = "WS2P 2 1 TLS 3 DEF LOW ABF 443 g1.durs.ifee.fr ws2p";
+        let str_endpoint = "WS2P V2 S 7 g1.durs.ifee.fr 443 ws2p";
         let endpoint = EndpointV2 {
             api: NetworkEndpointApi(String::from("WS2P")),
             api_version: 2,
-            network_features: EndpointV2NetworkFeatures(vec![4u8]),
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: None,
             ip_v6: None,
@@ -850,11 +597,11 @@ mod tests {
 
     #[test]
     fn test_parse_and_read_endpoint_with_ipv4() {
-        let str_endpoint = "WS2P 2 1 TLS 3 DEF LOW ABF 443 84.16.72.210 ws2p";
+        let str_endpoint = "WS2P V2 S 7 84.16.72.210 443 ws2p";
         let endpoint = EndpointV2 {
             api: NetworkEndpointApi(String::from("WS2P")),
             api_version: 2,
-            network_features: EndpointV2NetworkFeatures(vec![4u8]),
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()),
             ip_v6: None,
@@ -867,11 +614,11 @@ mod tests {
 
     #[test]
     fn test_parse_and_read_endpoint_with_ipv6() {
-        let str_endpoint = "WS2P 2 1 TLS 3 DEF LOW ABF 443 [2001:41d0:8:c5aa::1] ws2p";
+        let str_endpoint = "WS2P V2 S 7 [2001:41d0:8:c5aa::1] 443 ws2p";
         let endpoint = EndpointV2 {
             api: NetworkEndpointApi(String::from("WS2P")),
             api_version: 2,
-            network_features: EndpointV2NetworkFeatures(vec![4u8]),
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: None,
             ip_v6: Some(Ipv6Addr::from_str("2001:41d0:8:c5aa::1").unwrap()),
@@ -884,12 +631,11 @@ mod tests {
 
     #[test]
     fn test_parse_and_read_endpoint_with_ipv4_and_ip_v6() {
-        let str_endpoint =
-            "WS2P 2 1 TLS 3 DEF LOW ABF 443 5.135.188.170 [2001:41d0:8:c5aa::1] ws2p";
+        let str_endpoint = "WS2P V2 S 7 5.135.188.170 [2001:41d0:8:c5aa::1] 443 ws2p";
         let endpoint = EndpointV2 {
             api: NetworkEndpointApi(String::from("WS2P")),
             api_version: 2,
-            network_features: EndpointV2NetworkFeatures(vec![4u8]),
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("5.135.188.170").unwrap()),
             ip_v6: Some(Ipv6Addr::from_str("2001:41d0:8:c5aa::1").unwrap()),
@@ -899,4 +645,21 @@ mod tests {
         };
         test_parse_and_read_endpoint(str_endpoint, endpoint);
     }
+
+    #[test]
+    fn test_parse_and_read_endpoint_with_all_fields() {
+        let str_endpoint = "WS2P V2 S 7 g1.durs.info 5.135.188.170 [2001:41d0:8:c5aa::1] 443 ws2p";
+        let endpoint = EndpointV2 {
+            api: NetworkEndpointApi(String::from("WS2P")),
+            api_version: 2,
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
+            api_features: ApiFeatures(vec![7u8]),
+            ip_v4: Some(Ipv4Addr::from_str("5.135.188.170").unwrap()),
+            ip_v6: Some(Ipv6Addr::from_str("2001:41d0:8:c5aa::1").unwrap()),
+            host: Some(String::from("g1.durs.info")),
+            port: 443u16,
+            path: Some(String::from("ws2p")),
+        };
+        test_parse_and_read_endpoint(str_endpoint, endpoint);
+    }
 }
diff --git a/network-documents/src/network_head.rs b/network-documents/src/network_head.rs
index 5f6d2979299109836ba086004bad7243efd54622..396e977ce73795786dafd2920957ef0d5ce06ce0 100644
--- a/network-documents/src/network_head.rs
+++ b/network-documents/src/network_head.rs
@@ -32,7 +32,7 @@ pub enum NetworkHead {
     /// Head V2
     V2(Box<NetworkHeadV2>),
     /// head V3
-    V3(Box<NetworkHeadV3Container>),
+    V3(Box<NetworkHeadV3>),
 }
 
 impl ToString for NetworkHead {
diff --git a/network-documents/src/network_head_v3.rs b/network-documents/src/network_head_v3.rs
index 25251ff5334093f41bcf449b13db563c6f62c27e..e9998fe2db72f1a68f5e5ac0ed7828dc92b7c15d 100644
--- a/network-documents/src/network_head_v3.rs
+++ b/network-documents/src/network_head_v3.rs
@@ -17,68 +17,27 @@
 
 use base58::ToBase58;
 use duniter_documents::blockstamp::Blockstamp;
-use dup_crypto::keys::bin_signable::BinSignable;
+use duniter_documents::{BlockHash, BlockId, CurrencyName, ToJsonObject};
+use dup_crypto::keys::text_signable::TextSignable;
 use dup_crypto::keys::*;
-use serde_json;
+use pest::iterators::Pair;
+use pest::Parser;
 use std::cmp::Ordering;
-use NodeId;
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-/// Head V3
-pub struct NetworkHeadV3Container {
-    /// Head step
-    pub step: u8,
-    /// head body
-    pub body: NetworkHeadV3,
-}
-
-impl PartialOrd for NetworkHeadV3Container {
-    fn partial_cmp(&self, other: &NetworkHeadV3Container) -> Option<Ordering> {
-        Some(self.cmp(other))
-    }
-}
-
-impl Ord for NetworkHeadV3Container {
-    fn cmp(&self, other: &NetworkHeadV3Container) -> Ordering {
-        self.body.cmp(&other.body)
-    }
-}
-
-impl NetworkHeadV3Container {
-    /// Convert to JSON String
-    pub fn to_json_head(&self) -> Result<String, serde_json::Error> {
-        Ok(serde_json::to_string_pretty(&JsonHeadV3 {
-            api_outgoing_conf: self.body.api_outgoing_conf,
-            api_incoming_conf: self.body.api_incoming_conf,
-            free_mirror_rooms: self.body.free_mirror_rooms,
-            low_priority_rooms: self.body.low_priority_rooms,
-            node_id: self.body.node_id,
-            algorithm: self.body.pubkey.algo(),
-            pubkey: self.body.pubkey.to_base58(),
-            blockstamp: self.body.blockstamp.to_string(),
-            software: &self.body.software,
-            soft_version: &self.body.soft_version,
-            signature: if let Some(sig) = self.body.signature {
-                Some(sig.to_base64())
-            } else {
-                None
-            },
-            step: self.step,
-        })?)
-    }
-}
+use *;
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Head V3
 pub struct NetworkHeadV3 {
+    /// Currency name
+    pub currency_name: CurrencyName,
     /// WS2P Private configuration
     pub api_outgoing_conf: u8,
     /// WS2P Public configuration
     pub api_incoming_conf: u8,
+    /// Issuer node free member rooms
+    pub free_member_rooms: u8,
     /// Issuer node free mirror rooms
     pub free_mirror_rooms: u8,
-    /// Issuer node free "low priority" rooms
-    pub low_priority_rooms: u8,
     /// Issuer node id
     pub node_id: NodeId,
     /// Issuer pubkey
@@ -91,6 +50,8 @@ pub struct NetworkHeadV3 {
     pub soft_version: String,
     /// Issuer signature
     pub signature: Option<Sig>,
+    /// Head step
+    pub step: u8,
 }
 
 impl PartialOrd for NetworkHeadV3 {
@@ -105,7 +66,7 @@ impl Ord for NetworkHeadV3 {
     }
 }
 
-impl<'de> BinSignable<'de> for NetworkHeadV3 {
+/*impl<'de> BinSignable<'de> for NetworkHeadV3 {
     fn issuer_pubkey(&self) -> PubKey {
         self.pubkey
     }
@@ -115,19 +76,122 @@ impl<'de> BinSignable<'de> for NetworkHeadV3 {
     fn set_signature(&mut self, signature: Sig) {
         self.signature = Some(signature);
     }
+}*/
+
+impl TextSignable for NetworkHeadV3 {
+    fn as_signable_text(&self) -> String {
+        format!(
+"3:{currency}:{api_outgoing_conf}:{api_incoming_conf}:{free_member_rooms}:{free_mirror_rooms}:{node_id}:{pubkey}:{blockstamp}:{software}:{soft_version}\n",
+            currency = self.currency_name,
+            api_outgoing_conf = self.api_outgoing_conf,
+            api_incoming_conf = self.api_incoming_conf,
+            free_member_rooms = self.free_member_rooms,
+            free_mirror_rooms = self.free_mirror_rooms,
+            node_id = format!("{}", self.node_id),
+            pubkey = self.pubkey.to_base58(),
+            blockstamp = self.blockstamp.to_string(),
+            software = self.software,
+            soft_version = self.soft_version,
+        )
+    }
+    fn issuer_pubkey(&self) -> PubKey {
+        self.pubkey
+    }
+    fn signature(&self) -> Option<Sig> {
+        self.signature
+    }
+    fn set_signature(&mut self, signature: Sig) {
+        self.signature = Some(signature);
+    }
+}
+
+impl NetworkHeadV3 {
+    /// parse from raw ascii format
+    pub fn parse_from_raw(raw_peer: &str) -> Result<NetworkHeadV3, ParseError> {
+        match NetworkDocsParser::parse(Rule::head_v3, raw_peer) {
+            Ok(mut head_v3_pairs) => {
+                Ok(NetworkHeadV3::from_pest_pair(head_v3_pairs.next().unwrap()))
+            }
+            Err(pest_error) => Err(ParseError::PestError(format!("{}", pest_error))),
+        }
+    }
+    /// Generate from pest pair
+    fn from_pest_pair(pair: Pair<Rule>) -> NetworkHeadV3 {
+        let mut currency_str = "";
+        let mut api_outgoing_conf = 0;
+        let mut api_incoming_conf = 0;
+        let mut free_member_rooms = 0;
+        let mut free_mirror_rooms = 0;
+        let mut node_id = NodeId(0);
+        let mut pubkey = None;
+        let mut blockstamp = None;
+        let mut software = "";
+        let mut soft_version = "";
+        let mut signature = None;
+        let mut step = 0;
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::currency => currency_str = field.as_str(),
+                Rule::api_outgoing_conf => api_outgoing_conf = field.as_str().parse().unwrap(),
+                Rule::api_incoming_conf => api_incoming_conf = field.as_str().parse().unwrap(),
+                Rule::free_member_rooms => free_member_rooms = field.as_str().parse().unwrap(),
+                Rule::free_mirror_rooms => free_mirror_rooms = field.as_str().parse().unwrap(),
+                Rule::node_id => node_id = NodeId(field.as_str().parse().unwrap()),
+                Rule::pubkey => {
+                    pubkey = Some(PubKey::Ed25519(
+                        ed25519::PublicKey::from_base58(field.as_str()).unwrap(),
+                    ))
+                }
+                Rule::blockstamp => {
+                    let mut inner_rules = field.into_inner(); // { block_id ~ "-" ~ hash }
+
+                    let block_id: &str = inner_rules.next().unwrap().as_str();
+                    let block_hash: &str = inner_rules.next().unwrap().as_str();
+                    blockstamp = Some(Blockstamp {
+                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
+                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
+                    });
+                }
+                Rule::software => software = field.as_str(),
+                Rule::soft_version => soft_version = field.as_str(),
+                Rule::ed25519_sig => {
+                    signature = Some(Sig::Ed25519(
+                        ed25519::Signature::from_base64(field.as_str()).unwrap(),
+                    ))
+                }
+                Rule::step => step = field.as_str().parse().unwrap(),
+                _ => panic!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
+            }
+        }
+        NetworkHeadV3 {
+            currency_name: CurrencyName(currency_str.to_owned()),
+            api_outgoing_conf,
+            api_incoming_conf,
+            free_member_rooms,
+            free_mirror_rooms,
+            node_id,
+            pubkey: pubkey.expect("Grammar must ensure that head v3 have valid issuer pubkey !"),
+            blockstamp: blockstamp
+                .expect("Grammar must ensure that head v3 have valid blockstamp!"),
+            software: software.to_owned(),
+            soft_version: soft_version.to_owned(),
+            signature,
+            step,
+        }
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Head V3 for json serializer
-pub struct JsonHeadV3<'a> {
+pub struct JsonHeadV3 {
     /// WS2P Private configuration
     pub api_outgoing_conf: u8,
     /// WS2P Public configuration
     pub api_incoming_conf: u8,
+    /// Issuer node free member rooms
+    pub free_member_rooms: u8,
     /// Issuer node free mirror rooms
     pub free_mirror_rooms: u8,
-    /// Issuer node free "low priority" rooms
-    pub low_priority_rooms: u8,
     /// Issuer node id
     pub node_id: NodeId,
     /// Issuer key algorithm
@@ -137,57 +201,77 @@ pub struct JsonHeadV3<'a> {
     /// Head blockstamp
     pub blockstamp: String,
     /// Issuer node software
-    pub software: &'a str,
+    pub software: String,
     /// Issuer node soft version
-    pub soft_version: &'a str,
+    pub soft_version: String,
     /// Issuer signature
     pub signature: Option<String>,
     /// Head step
     pub step: u8,
 }
 
+impl<'a> ToJsonObject for NetworkHeadV3 {
+    type JsonObject = JsonHeadV3;
+
+    fn to_json_object(&self) -> Self::JsonObject {
+        JsonHeadV3 {
+            api_outgoing_conf: self.api_outgoing_conf,
+            api_incoming_conf: self.api_incoming_conf,
+            free_member_rooms: self.free_member_rooms,
+            free_mirror_rooms: self.free_mirror_rooms,
+            node_id: self.node_id,
+            algorithm: self.pubkey.algo(),
+            pubkey: self.pubkey.to_base58(),
+            blockstamp: self.blockstamp.to_string(),
+            software: self.software.clone(),
+            soft_version: self.soft_version.clone(),
+            signature: if let Some(sig) = self.signature {
+                Some(sig.to_base64())
+            } else {
+                None
+            },
+            step: self.step,
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
-    use dup_crypto::keys::bin_signable::BinSignable;
-    use tests::bincode::deserialize;
     use tests::keypair1;
 
     #[test]
     fn head_v3_sign_and_verify() {
-        let mut head_v3 = NetworkHeadV3Container {
+        let keypair = keypair1();
+        let mut head_v3 = NetworkHeadV3 {
+            currency_name: CurrencyName("g1".to_owned()),
+            api_outgoing_conf: 0u8,
+            api_incoming_conf: 0u8,
+            free_mirror_rooms: 0u8,
+            free_member_rooms: 0u8,
+            node_id: NodeId(0),
+            pubkey: PubKey::Ed25519(keypair.public_key()),
+            blockstamp: Blockstamp::from_string(
+                "50-000005B1CEB4EC5245EF7E33101A330A1C9A358EC45A25FC13F78BB58C9E7370",
+            )
+            .unwrap(),
+            software: String::from("durs"),
+            soft_version: String::from("0.1.0-a0.1"),
+            signature: None,
             step: 0,
-            body: NetworkHeadV3 {
-                api_outgoing_conf: 0u8,
-                api_incoming_conf: 0u8,
-                free_mirror_rooms: 0u8,
-                low_priority_rooms: 0u8,
-                node_id: NodeId(0),
-                pubkey: PubKey::Ed25519(keypair1().public_key()),
-                blockstamp: Blockstamp::from_string(
-                    "50-000005B1CEB4EC5245EF7E33101A330A1C9A358EC45A25FC13F78BB58C9E7370",
-                )
-                .unwrap(),
-                software: String::from("durs"),
-                soft_version: String::from("0.1.0-a0.1"),
-                signature: None,
-            },
         };
         // Sign
-        let sign_result = head_v3
-            .body
-            .sign(PrivKey::Ed25519(keypair1().private_key()));
-        if let Ok(head_v3_body_bytes) = sign_result {
-            let deser_head_v3_body: NetworkHeadV3 =
-                deserialize(&head_v3_body_bytes).expect("Fail to deserialize PeerCardV11 !");
-            assert_eq!(head_v3.body, deser_head_v3_body,)
+        let sign_result = head_v3.sign(PrivKey::Ed25519(keypair.private_key()));
+        if let Ok(head_v3_raw) = sign_result {
+            println!("{}", head_v3_raw);
+            assert_eq!(
+                head_v3,
+                NetworkHeadV3::parse_from_raw(&head_v3_raw).expect("Fail to parse head v3 !")
+            )
         } else {
-            panic!("failt to sign head v3 : {:?}", sign_result.err().unwrap())
+            panic!("fail to sign head v3 : {:?}", sign_result.err().unwrap())
         }
         // Verify signature
-        head_v3.body.verify().expect("HEADv3 : Invalid signature !");
-        //let json_head_v3 = head_v3.to_json_head().expect("Fail to serialize HEAD v3 !");
-        //println!("{}", json_head_v3);
-        //panic!();
+        head_v3.verify().expect("HEADv3 : Invalid signature !");
     }
 }
diff --git a/network-documents/src/network_peer.rs b/network-documents/src/network_peer.rs
index bcd71b161328212b0b05d31fd7d567d8a9393ce0..c80a23e4d241d5e95656443d825aaaecf0b123c7 100644
--- a/network-documents/src/network_peer.rs
+++ b/network-documents/src/network_peer.rs
@@ -21,9 +21,12 @@ extern crate serde;
 
 use base58::ToBase58;
 use duniter_documents::{blockstamp::Blockstamp, CurrencyName};
-use dup_crypto::keys::bin_signable::BinSignable;
+use duniter_documents::{BlockHash, BlockId};
+use dup_crypto::keys::text_signable::TextSignable;
 use dup_crypto::keys::*;
 use network_endpoint::*;
+use pest::iterators::Pair;
+use pest::Parser;
 use *;
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -48,34 +51,105 @@ pub struct PeerCardV11 {
     pub node_id: NodeId,
     /// Peer card Blockstamp
     pub blockstamp: Blockstamp,
-    /// Peer card endpoints list
-    pub endpoints: Vec<EndpointEnum>,
+    /// Peer card binary endpoints
+    pub endpoints: Vec<EndpointV2>,
+    /// Peer card string endpoints
+    pub endpoints_str: Vec<String>,
     /// Signature
     pub sig: Option<Sig>,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-/// Peer card V11 for JSON Serializer
-pub struct JsonPeerCardV11<'a> {
-    /// PeerCard version
-    pub version: usize,
-    /// Currency Name
-    pub currency_name: &'a str,
-    /// Issuer node id
-    pub node_id: NodeId,
-    /// Issuer pubkey alogirithm
-    pub algorithm: KeysAlgo,
-    /// Issuer pubkey
-    pub pubkey: String,
-    /// Peer card creation blockstamp
-    pub blockstamp: String,
-    /// Endpoints
-    pub endpoints: Vec<String>,
-    /// Signature
-    pub signature: Option<String>,
+impl TextSignable for PeerCardV11 {
+    fn as_signable_text(&self) -> String {
+        format!(
+            "11:{currency}:{node_id}:{pubkey}:{blockstamp}\n{endpoinds}\n{endpoints_str}{nl}",
+            currency = self.currency_name.0,
+            node_id = format!("{}", self.node_id),
+            pubkey = self.issuer.to_base58(),
+            blockstamp = self.blockstamp.to_string(),
+            endpoinds = self
+                .endpoints
+                .iter()
+                .map(EndpointV2::to_string)
+                .collect::<Vec<String>>()
+                .join("\n"),
+            endpoints_str = self.endpoints_str.join("\n"),
+            nl = if self.endpoints_str.is_empty() {
+                ""
+            } else {
+                "\n"
+            },
+        )
+    }
+    fn issuer_pubkey(&self) -> PubKey {
+        self.issuer
+    }
+    fn signature(&self) -> Option<Sig> {
+        self.sig
+    }
+    fn set_signature(&mut self, signature: Sig) {
+        self.sig = Some(signature);
+    }
 }
 
 impl PeerCardV11 {
+    /// parse from raw ascii format
+    pub fn parse_from_raw(raw_peer: &str) -> Result<PeerCardV11, ParseError> {
+        match NetworkDocsParser::parse(Rule::peer_v11, raw_peer) {
+            Ok(mut peer_v11_pairs) => {
+                Ok(PeerCardV11::from_pest_pair(peer_v11_pairs.next().unwrap()))
+            }
+            Err(pest_error) => Err(ParseError::PestError(format!("{}", pest_error))),
+        }
+    }
+    /// Generate from pest pair
+    fn from_pest_pair(pair: Pair<Rule>) -> PeerCardV11 {
+        let mut currency_str = "";
+        let mut node_id = NodeId(0);
+        let mut issuer = None;
+        let mut blockstamp = None;
+        let mut endpoints = Vec::new();
+        let mut sig = None;
+        for field in pair.into_inner() {
+            match field.as_rule() {
+                Rule::currency => currency_str = field.as_str(),
+                Rule::node_id => node_id = NodeId(field.as_str().parse().unwrap()),
+                Rule::pubkey => {
+                    issuer = Some(PubKey::Ed25519(
+                        ed25519::PublicKey::from_base58(field.as_str()).unwrap(),
+                    ))
+                }
+                Rule::blockstamp => {
+                    let mut inner_rules = field.into_inner(); // { block_id ~ "-" ~ hash }
+
+                    let block_id: &str = inner_rules.next().unwrap().as_str();
+                    let block_hash: &str = inner_rules.next().unwrap().as_str();
+                    blockstamp = Some(Blockstamp {
+                        id: BlockId(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.
+                        hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.
+                    });
+                }
+                Rule::endpoint_v2 => endpoints.push(EndpointV2::from_pest_pair(field)),
+                Rule::ed25519_sig => {
+                    sig = Some(Sig::Ed25519(
+                        ed25519::Signature::from_base64(field.as_str()).unwrap(),
+                    ))
+                }
+                _ => panic!("unexpected rule: {:?}", field.as_rule()), // Grammar ensures that we never reach this line
+            }
+        }
+        let endpoints_len = endpoints.len();
+        PeerCardV11 {
+            currency_name: CurrencyName(currency_str.to_owned()),
+            issuer: issuer.expect("Grammar must ensure that peer v11 have valid issuer pubkey !"),
+            node_id,
+            blockstamp: blockstamp
+                .expect("Grammar must ensure that peer v11 have valid blockstamp!"),
+            endpoints,
+            endpoints_str: Vec::with_capacity(endpoints_len),
+            sig,
+        }
+    }
     /// Convert to JSON String
     pub fn to_json_peer(&self) -> Result<String, serde_json::Error> {
         Ok(serde_json::to_string_pretty(&JsonPeerCardV11 {
@@ -95,16 +169,25 @@ impl PeerCardV11 {
     }
 }
 
-impl<'de> BinSignable<'de> for PeerCardV11 {
-    fn issuer_pubkey(&self) -> PubKey {
-        self.issuer
-    }
-    fn signature(&self) -> Option<Sig> {
-        self.sig
-    }
-    fn set_signature(&mut self, signature: Sig) {
-        self.sig = Some(signature)
-    }
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
+/// Peer card V11 for JSON Serializer
+pub struct JsonPeerCardV11<'a> {
+    /// PeerCard version
+    pub version: usize,
+    /// Currency Name
+    pub currency_name: &'a str,
+    /// Issuer node id
+    pub node_id: NodeId,
+    /// Issuer pubkey alogirithm
+    pub algorithm: KeysAlgo,
+    /// Issuer pubkey
+    pub pubkey: String,
+    /// Peer card creation blockstamp
+    pub blockstamp: String,
+    /// Endpoints
+    pub endpoints: Vec<String>,
+    /// Signature
+    pub signature: Option<String>,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -128,24 +211,18 @@ impl PeerCard {
     pub fn blockstamp(&self) -> Blockstamp {
         match *self {
             PeerCard::V10(ref peer_v10) => peer_v10.blockstamp,
-            _ => panic!("Peer version is not supported !"),
+            PeerCard::V11(ref peer_v11) => peer_v11.blockstamp,
+            //_ => panic!("Peer version is not supported !"),
         }
     }
     /// Get peer card issuer
     pub fn issuer(&self) -> PubKey {
         match *self {
             PeerCard::V10(ref peer_v10) => peer_v10.issuer,
-            _ => panic!("Peer version is not supported !"),
+            PeerCard::V11(ref peer_v11) => peer_v11.issuer,
+            //_ => panic!("Peer version is not supported !"),
         }
     }
-    /// Verify validity of peer card signature
-    pub fn verify(&self) -> bool {
-        false
-    }
-    /// Get peer card endpoint
-    pub fn get_endpoints(&self) -> Vec<EndpointEnum> {
-        Vec::with_capacity(0)
-    }
 }
 
 #[cfg(test)]
@@ -153,14 +230,13 @@ mod tests {
     use super::*;
     use std::net::Ipv4Addr;
     use std::str::FromStr;
-    use tests::bincode::deserialize;
     use tests::keypair1;
 
     fn create_endpoint_v2() -> EndpointV2 {
         EndpointV2 {
             api: NetworkEndpointApi(String::from("WS2P")),
             api_version: 2,
-            network_features: EndpointV2NetworkFeatures(vec![4u8]),
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: None,
             ip_v6: None,
@@ -173,7 +249,7 @@ mod tests {
         EndpointV2 {
             api: NetworkEndpointApi(String::from("WS2P")),
             api_version: 2,
-            network_features: EndpointV2NetworkFeatures(vec![5u8]),
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()),
             ip_v6: None,
@@ -194,20 +270,21 @@ mod tests {
                 "50-000005B1CEB4EC5245EF7E33101A330A1C9A358EC45A25FC13F78BB58C9E7370",
             )
             .unwrap(),
-            endpoints: vec![
-                EndpointEnum::V2(create_endpoint_v2()),
-                EndpointEnum::V2(create_second_endpoint_v2()),
-            ],
+            endpoints: vec![create_endpoint_v2(), create_second_endpoint_v2()],
+            endpoints_str: vec![],
             sig: None,
         };
         // Sign
         let sign_result = peer_card_v11.sign(PrivKey::Ed25519(keypair1.private_key()));
-        if let Ok(peer_card_v11_bytes) = sign_result {
-            let deser_peer_card_v11: PeerCardV11 =
-                deserialize(&peer_card_v11_bytes).expect("Fail to deserialize PeerCardV11 !");
-            assert_eq!(peer_card_v11, deser_peer_card_v11,)
+        if let Ok(peer_card_v11_raw) = sign_result {
+            println!("{}", peer_card_v11_raw);
+            assert_eq!(
+                peer_card_v11,
+                PeerCardV11::parse_from_raw(&peer_card_v11_raw)
+                    .expect("Fail to parse peer card v11 !")
+            )
         } else {
-            panic!("failt to sign peer card : {:?}", sign_result.err().unwrap())
+            panic!("fail to sign peer card : {:?}", sign_result.err().unwrap())
         }
         // Verify signature
         peer_card_v11
diff --git a/tui/lib.rs b/tui/lib.rs
index e31369b328cd71f564e393933af6d60b9f05f184..eda016905c0084fcdb4f7cfc721ff52b4788d4a7 100644
--- a/tui/lib.rs
+++ b/tui/lib.rs
@@ -467,7 +467,7 @@ impl DuniterModule<DuRsConf, DursMsg> for TuiModule {
         thread::spawn(move || {
             // Send proxy sender to main
             main_sender
-                .send(RooterThreadMessage::ModuleSender(
+                .send(RooterThreadMessage::ModuleRegistration(
                     TuiModule::name(),
                     proxy_sender,
                     vec![ModuleRole::UserInterface],
@@ -477,6 +477,8 @@ impl DuniterModule<DuRsConf, DursMsg> for TuiModule {
                         ModuleEvent::NewValidHeadFromNetwork,
                         ModuleEvent::NewValidPeerFromNodeNetwork,
                     ],
+                    vec![],
+                    vec![],
                 ))
                 .expect("Fatal error : tui module fail to send is sender channel !");
             debug!("Send tui sender to main thread.");
diff --git a/ws2p-messages/Cargo.toml b/ws2p-messages/Cargo.toml
index fa0e7d6a3b23e82603a94eecd5ca620991670047..ea73afd5e54633c93344cb2f088da9e75bac7bb9 100644
--- a/ws2p-messages/Cargo.toml
+++ b/ws2p-messages/Cargo.toml
@@ -15,7 +15,6 @@ dup-crypto = { path = "../crypto" }
 duniter-documents = { path = "../documents" }
 durs-network-documents = { path = "../network-documents" }
 log = "0.4.*"
-rust-crypto = "0.2.*"
 serde = "1.0.*"
 serde_derive = "1.0.*"
 serde_json = "1.0.*"
diff --git a/ws2p-messages/lib.rs b/ws2p-messages/lib.rs
index 17746468d0b2013b80f42006ccc8056c724b42f3..63e50c1c282cbd1adb65c50ec7bc8e0aecc5edaf 100644
--- a/ws2p-messages/lib.rs
+++ b/ws2p-messages/lib.rs
@@ -75,31 +75,31 @@ mod tests {
         )
     }
 
-    pub fn create_endpoint_v11() -> EndpointEnum {
-        EndpointEnum::V2(EndpointV2 {
+    pub fn create_endpoint_v11() -> EndpointV2 {
+        EndpointV2 {
             api: NetworkEndpointApi(String::from("WS2P")),
             api_version: 2,
-            network_features: EndpointV2NetworkFeatures(vec![4u8]),
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: None,
             ip_v6: None,
             host: Some(String::from("g1.durs.ifee.fr")),
             port: 443u16,
             path: Some(String::from("ws2p")),
-        })
+        }
     }
-    pub fn create_second_endpoint_v11() -> EndpointEnum {
-        EndpointEnum::V2(EndpointV2 {
+    pub fn create_second_endpoint_v11() -> EndpointV2 {
+        EndpointV2 {
             api: NetworkEndpointApi(String::from("WS2P")),
             api_version: 2,
-            network_features: EndpointV2NetworkFeatures(vec![5u8]),
+            network_features: EndpointV2NetworkFeatures(vec![1u8]),
             api_features: ApiFeatures(vec![7u8]),
             ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()),
             ip_v6: None,
             host: None,
             port: 443u16,
             path: Some(String::from("ws2p")),
-        })
+        }
     }
 
     pub fn create_peer_card_v11() -> PeerCardV11 {
@@ -112,6 +112,7 @@ mod tests {
             )
             .unwrap(),
             endpoints: vec![create_endpoint_v11(), create_second_endpoint_v11()],
+            endpoints_str: vec![],
             sig: None,
         }
     }
diff --git a/ws2p-messages/v2/connect.rs b/ws2p-messages/v2/connect.rs
index 19426fb017ef69d4c25975b4be488f1428026d03..37d70705089424fbd0fcdf2988524f770d1af3d2 100644
--- a/ws2p-messages/v2/connect.rs
+++ b/ws2p-messages/v2/connect.rs
@@ -87,6 +87,7 @@ mod tests {
     use super::super::*;
     use super::*;
     use duniter_documents::Blockstamp;
+    use dup_crypto::keys::text_signable::TextSignable;
     use tests::*;
 
     #[test]
diff --git a/ws2p-messages/v2/mod.rs b/ws2p-messages/v2/mod.rs
index b0c4aabd9d1a1c1372f4a77aadcd952d96996006..af7d32e9768b722f238d8afc3501db30d54bc665 100644
--- a/ws2p-messages/v2/mod.rs
+++ b/ws2p-messages/v2/mod.rs
@@ -84,6 +84,7 @@ impl<'de> BinSignable<'de> for WS2Pv0Message {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use dup_crypto::keys::text_signable::TextSignable;
     use tests::*;
 
     #[test]
diff --git a/ws2p-messages/v2/payload_container.rs b/ws2p-messages/v2/payload_container.rs
index af62ee4d93eb5a9158bbbffe6d03e80d8ca11cf9..8bf5cfd89778dfb5a8a5a9477edb8d6686ab80f8 100644
--- a/ws2p-messages/v2/payload_container.rs
+++ b/ws2p-messages/v2/payload_container.rs
@@ -26,7 +26,7 @@ use duniter_documents::v10::revocation::RevocationDocument;
 use duniter_documents::v10::transaction::TransactionDocument;
 use dup_crypto::hashs::Hash;
 use durs_network_documents::network_head_v2::NetworkHeadV2;
-use durs_network_documents::network_head_v3::NetworkHeadV3Container;
+use durs_network_documents::network_head_v3::NetworkHeadV3;
 use durs_network_documents::network_peer::PeerCardV11;
 
 /// WS2P v2 message payload metadata size
@@ -54,7 +54,7 @@ pub enum WS2Pv0MessagePayload {
     /// HEADS_V2 Message
     Headsv2(Vec<NetworkHeadV2>),
     /// HEADS_V3 Message
-    Heads3(Vec<NetworkHeadV3Container>),
+    Heads3(Vec<NetworkHeadV3>),
     /// BLOCKS Message
     Blocks(Vec<BlockDocument>),
     /// PENDING_IDENTITIES Message
diff --git a/ws2p-v1-legacy/Cargo.toml b/ws2p-v1-legacy/Cargo.toml
index 0350c5e73c4327cabd96d92c743535f2e86e18ac..0d80b8d94ed2c251b2b7cd0740be7e8b039c7247 100644
--- a/ws2p-v1-legacy/Cargo.toml
+++ b/ws2p-v1-legacy/Cargo.toml
@@ -21,7 +21,6 @@ duniter-network = { path = "../network" }
 durs-wot = { path = "../wot" }
 log = "0.4.*"
 rand = "0.4.*"
-rust-crypto = "0.2.*"
 sqlite = "0.23.*"
 serde = "1.0.*"
 serde_derive = "1.0.*"
diff --git a/ws2p-v1-legacy/datas.rs b/ws2p-v1-legacy/datas.rs
index 7e2ea17e9b06af9fbc89ec53f53b9564dfcfdf76..a73937d4a33df9749d4396abf478d86ef5a8039a 100644
--- a/ws2p-v1-legacy/datas.rs
+++ b/ws2p-v1-legacy/datas.rs
@@ -37,7 +37,7 @@ pub struct WS2PModuleDatas {
         mpsc::Sender<WS2PThreadSignal>,
         mpsc::Receiver<WS2PThreadSignal>,
     ),
-    pub ws2p_endpoints: HashMap<NodeFullId, (EndpointEnum, WS2PConnectionState)>,
+    pub ws2p_endpoints: HashMap<NodeFullId, (EndpointV1, WS2PConnectionState)>,
     pub websockets: HashMap<NodeFullId, WsSender>,
     pub requests_awaiting_response:
         HashMap<ModuleReqId, (OldNetworkRequest, NodeFullId, SystemTime)>,
@@ -169,18 +169,17 @@ impl WS2PModuleDatas {
         let mut reachable_endpoints = Vec::new();
         let mut unreachable_endpoints = Vec::new();
         for (_ws2p_full_id, (ep, state)) in self.ws2p_endpoints.clone() {
-            if ep.pubkey() == self.key_pair.unwrap().public_key() || !pubkeys.contains(&ep.pubkey())
-            {
+            if ep.issuer == self.key_pair.unwrap().public_key() || !pubkeys.contains(&ep.issuer) {
                 match state {
                     WS2PConnectionState::Established => count_established_connections += 1,
                     WS2PConnectionState::NeverTry
                     | WS2PConnectionState::Close
                     | WS2PConnectionState::Denial => {
-                        pubkeys.insert(ep.pubkey());
+                        pubkeys.insert(ep.issuer);
                         reachable_endpoints.push(ep);
                     }
                     _ => {
-                        pubkeys.insert(ep.pubkey());
+                        pubkeys.insert(ep.issuer);
                         unreachable_endpoints.push(ep);
                     }
                 }
@@ -200,13 +199,13 @@ impl WS2PModuleDatas {
             } else {
                 break;
             };
-            if cfg!(feature = "ssl") || ep.port() != 443 {
+            if cfg!(feature = "ssl") || ep.port != 443 {
                 self.connect_to_without_checking_quotas(&ep);
                 free_outcoming_rooms -= 1;
             }
         }
     }
-    pub fn connect_to(&mut self, endpoint: &EndpointEnum) {
+    pub fn connect_to(&mut self, endpoint: &EndpointV1) {
         // Add endpoint to endpoints list (if there isn't already)
         match self.ws2p_endpoints.get(
             &endpoint
@@ -529,7 +528,7 @@ impl WS2PModuleDatas {
         Ok(())
     }
 
-    fn connect_to_without_checking_quotas(&mut self, endpoint: &EndpointEnum) {
+    fn connect_to_without_checking_quotas(&mut self, endpoint: &EndpointV1) {
         let endpoint_copy = endpoint.clone();
         let conductor_sender_copy = self.main_thread_channel.0.clone();
         let currency_copy = self.currency.clone();
diff --git a/ws2p-v1-legacy/lib.rs b/ws2p-v1-legacy/lib.rs
index ad5732f0972755715680100791a71ddbe7fa9088..520ef3559cae83ce37bb801b26eefb50dda34a9a 100644
--- a/ws2p-v1-legacy/lib.rs
+++ b/ws2p-v1-legacy/lib.rs
@@ -94,7 +94,7 @@ pub struct WS2PConf {
     /// Limit of outcoming connections
     pub outcoming_quota: usize,
     /// Default WS2P endpoints provides by configuration file
-    pub sync_endpoints: Vec<EndpointEnum>,
+    pub sync_endpoints: Vec<EndpointV1>,
 }
 
 impl Default for WS2PConf {
@@ -102,7 +102,7 @@ impl Default for WS2PConf {
         WS2PConf {
             outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA,
             sync_endpoints: vec![
-                EndpointEnum::parse_from_raw(
+                EndpointV1::parse_from_raw(
                     "WS2P c1c39a0a g1-monit.librelois.fr 443 /ws2p",
                     PubKey::Ed25519(
                         ed25519::PublicKey::from_base58(
@@ -112,10 +112,9 @@ impl Default for WS2PConf {
                     ),
                     0,
                     0,
-                    1u16,
                 )
                 .unwrap(),
-                EndpointEnum::parse_from_raw(
+                EndpointV1::parse_from_raw(
                     "WS2P b48824f0 g1.monnaielibreoccitanie.org 443 /ws2p",
                     PubKey::Ed25519(
                         ed25519::PublicKey::from_base58(
@@ -125,7 +124,6 @@ impl Default for WS2PConf {
                     ),
                     0,
                     0,
-                    1u16,
                 )
                 .unwrap(),
             ],
@@ -143,7 +141,7 @@ pub enum WS2PSignal {
     NegociationTimeout(NodeFullId),
     Timeout(NodeFullId),
     DalRequest(NodeFullId, ModuleReqId, serde_json::Value),
-    PeerCard(NodeFullId, serde_json::Value, Vec<EndpointEnum>),
+    PeerCard(NodeFullId, serde_json::Value, Vec<EndpointV1>),
     Heads(NodeFullId, Vec<NetworkHead>),
     Document(NodeFullId, BlockchainDocument),
     ReqResponse(
@@ -307,7 +305,7 @@ impl DuniterModule<DuRsConf, DursMsg> for WS2PModule {
                     .expect("Fail to get endpoint node_full_id"),
                 (ep.clone(), WS2PConnectionState::Close),
             );
-            info!("Load sync endpoint {}", ep.raw());
+            info!("Load sync endpoint {}", ep.raw_endpoint);
         }
         ws2p_module.key_pair = Some(key_pair);
         ws2p_module.currency = Some(soft_meta_datas.conf.currency().to_string());
@@ -327,7 +325,7 @@ impl DuniterModule<DuRsConf, DursMsg> for WS2PModule {
         thread::spawn(move || {
             // Send proxy sender to main
             rooter_sender
-                .send(RooterThreadMessage::ModuleSender(
+                .send(RooterThreadMessage::ModuleRegistration(
                     WS2PModule::name(),
                     proxy_sender_clone,
                     vec![ModuleRole::InterNodesNetwork],
@@ -336,6 +334,8 @@ impl DuniterModule<DuRsConf, DursMsg> for WS2PModule {
                         ModuleEvent::NewWotDocInPool,
                         ModuleEvent::NewTxinPool,
                     ],
+                    vec![],
+                    vec![],
                 ))
                 .expect("Fatal error : ws2p module fail to send is sender channel !");
             debug!("Send ws2p sender to main thread.");
@@ -372,14 +372,14 @@ impl DuniterModule<DuRsConf, DursMsg> for WS2PModule {
         let dal_enpoints =
             ws2p_db::get_endpoints_for_api(&db, &NetworkEndpointApi(String::from("WS2P")));
         for ep in dal_enpoints {
-            if ep.api() == NetworkEndpointApi(String::from("WS2P"))
-                && (cfg!(feature = "ssl") || ep.port() != 443)
+            if ep.api == NetworkEndpointApi(String::from("WS2P"))
+                && (cfg!(feature = "ssl") || ep.port != 443)
             {
                 count += 1;
                 ws2p_module.ws2p_endpoints.insert(
                     ep.node_full_id()
                         .expect("WS2P: Fail to get ep.node_full_id() !"),
-                    (ep.clone(), WS2PConnectionState::from(ep.status())),
+                    (ep.clone(), WS2PConnectionState::from(ep.status)),
                 );
             }
         }
@@ -675,7 +675,7 @@ impl DuniterModule<DuRsConf, DursMsg> for WS2PModule {
                                     Some(_) => {}
                                     None => {
                                         if let Some(_api) =
-                                            ws2p_db::string_to_api(&ep.api().0.clone())
+                                            ws2p_db::string_to_api(&ep.api.0.clone())
                                         {
                                             endpoints_to_update_status.insert(
                                                 ep.node_full_id().expect(
@@ -684,7 +684,7 @@ impl DuniterModule<DuRsConf, DursMsg> for WS2PModule {
                                                 SystemTime::now(),
                                             );
                                         }
-                                        if cfg!(feature = "ssl") || ep.port() != 443 {
+                                        if cfg!(feature = "ssl") || ep.port != 443 {
                                             ws2p_module.connect_to(&ep);
                                         }
                                     }
@@ -1117,7 +1117,7 @@ mod tests {
 
         let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
 
-        let mut endpoint = EndpointEnum::parse_from_raw(
+        let mut endpoint = EndpointV1::parse_from_raw(
             "WS2P cb06a19b g1.imirhil.fr 53012 /",
             PubKey::Ed25519(
                 ed25519::PublicKey::from_base58("5gJYnQp8v7bWwk7EWRoL8vCLof1r3y9c6VDdnGSM1GLv")
@@ -1125,7 +1125,6 @@ mod tests {
             ),
             1,
             current_time.as_secs(),
-            1,
         )
         .expect("Failt to parse test endpoint !");
 
@@ -1135,7 +1134,7 @@ mod tests {
         assert_eq!(endpoint, written_endpoints.pop().unwrap());
 
         // Test status update
-        endpoint.set_status(3);
+        endpoint.status = 3;
         ws2p_db::write_endpoint(&db, &endpoint, 3, current_time.as_secs());
         let mut written_endpoints =
             ws2p_db::get_endpoints_for_api(&db, &NetworkEndpointApi(String::from("WS2P")));
diff --git a/ws2p-v1-legacy/ws2p_connection.rs b/ws2p-v1-legacy/ws2p_connection.rs
index 1b66ebb0459bbbaf47d5dc44fdc7c900f310a4c0..721b9f02a758f40a5e42dc1c14bd80ac7535c194 100644
--- a/ws2p-v1-legacy/ws2p_connection.rs
+++ b/ws2p-v1-legacy/ws2p_connection.rs
@@ -2,7 +2,7 @@ use constants::*;
 use duniter_module::ModuleReqId;
 use duniter_network::BlockchainDocument;
 use dup_crypto::keys::*;
-use durs_network_documents::network_endpoint::{EndpointEnum, NetworkEndpointApi};
+use durs_network_documents::network_endpoint::{EndpointV1, NetworkEndpointApi};
 use durs_network_documents::NodeId;
 use parsers::blocks::parse_json_block;
 use rand::Rng;
@@ -271,7 +271,7 @@ pub enum WS2PConnectionMessagePayload {
     ValidAckMessage(String, WS2PConnectionState),
     ValidOk(WS2PConnectionState),
     DalRequest(ModuleReqId, serde_json::Value),
-    PeerCard(serde_json::Value, Vec<EndpointEnum>),
+    PeerCard(serde_json::Value, Vec<EndpointV1>),
     Heads(Vec<serde_json::Value>),
     Document(BlockchainDocument),
     ReqResponse(ModuleReqId, serde_json::Value),
@@ -544,19 +544,18 @@ impl WS2PConnectionMetaDatas {
                 Some(raw_pubkey) => {
                     match ed25519::PublicKey::from_base58(raw_pubkey.as_str().unwrap_or("")) {
                         Ok(pubkey) => {
-                            let mut ws2p_endpoints: Vec<EndpointEnum> = Vec::new();
+                            let mut ws2p_endpoints: Vec<EndpointV1> = Vec::new();
                             match peer.get("endpoints") {
                                 Some(endpoints) => match endpoints.as_array() {
                                     Some(array_endpoints) => {
                                         for endpoint in array_endpoints {
-                                            if let Ok(ep) = EndpointEnum::parse_from_raw(
+                                            if let Ok(ep) = EndpointV1::parse_from_raw(
                                                 endpoint.as_str().unwrap_or(""),
                                                 PubKey::Ed25519(pubkey),
                                                 0,
                                                 0,
-                                                1u16,
                                             ) {
-                                                if ep.api()
+                                                if ep.api
                                                     == NetworkEndpointApi(String::from("WS2P"))
                                                 {
                                                     ws2p_endpoints.push(ep);
@@ -584,7 +583,7 @@ impl WS2PConnectionMetaDatas {
 }
 
 pub fn get_random_connection<S: ::std::hash::BuildHasher>(
-    connections: &HashMap<NodeFullId, (EndpointEnum, WS2PConnectionState), S>,
+    connections: &HashMap<NodeFullId, (EndpointV1, WS2PConnectionState), S>,
 ) -> NodeFullId {
     let mut rng = rand::thread_rng();
     let mut loop_count = 0;
@@ -604,7 +603,7 @@ pub fn get_random_connection<S: ::std::hash::BuildHasher>(
 }
 
 pub fn connect_to_ws2p_endpoint(
-    endpoint: &EndpointEnum,
+    endpoint: &EndpointV1,
     conductor_sender: &mpsc::Sender<WS2PThreadSignal>,
     currency: &str,
     key_pair: KeyPairEnum,
@@ -616,10 +615,10 @@ pub fn connect_to_ws2p_endpoint(
     let mut conn_meta_datas = WS2PConnectionMetaDatas::new(
         "b60a14fd-0826-4ae0-83eb-1a92cd59fd5308535fd3-78f2-4678-9315-cd6e3b7871b1".to_string(),
     );
-    conn_meta_datas.remote_pubkey = Some(endpoint.pubkey());
+    conn_meta_datas.remote_pubkey = Some(endpoint.issuer);
     conn_meta_datas.remote_uuid = Some(
         endpoint
-            .node_uuid()
+            .node_id
             .expect("WS2P: Fail to get ep.node_uuid() !"),
     );
 
diff --git a/ws2p-v1-legacy/ws2p_db.rs b/ws2p-v1-legacy/ws2p_db.rs
index 6166fc4abca713608b406aa3bf8928b3e7d13f55..c23229dced15baccab7ec2c2358005e90ee9404f 100644
--- a/ws2p-v1-legacy/ws2p_db.rs
+++ b/ws2p-v1-legacy/ws2p_db.rs
@@ -1,5 +1,5 @@
 use dup_crypto::keys::*;
-use durs_network_documents::network_endpoint::{EndpointEnum, NetworkEndpointApi};
+use durs_network_documents::network_endpoint::{EndpointV1, NetworkEndpointApi};
 use sqlite::*;
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -44,7 +44,7 @@ pub fn api_to_integer(api: &NetworkEndpointApi) -> i64 {
     }
 }
 
-pub fn get_endpoints_for_api(db: &Connection, api: &NetworkEndpointApi) -> Vec<EndpointEnum> {
+pub fn get_endpoints_for_api(db: &Connection, api: &NetworkEndpointApi) -> Vec<EndpointV1> {
     let mut cursor: Cursor = db
         .prepare("SELECT hash_full_id, status, node_id, pubkey, api, version, endpoint, last_check FROM endpoints WHERE api=? ORDER BY status DESC;")
         .expect("get_endpoints_for_api() : Error in SQL request !")
@@ -61,12 +61,11 @@ pub fn get_endpoints_for_api(db: &Connection, api: &NetworkEndpointApi) -> Vec<E
         let raw_ep = row[6].as_string().unwrap().to_string();
         let ep_issuer =
             PubKey::Ed25519(ed25519::PublicKey::from_base58(row[3].as_string().unwrap()).unwrap());
-        let mut ep = match EndpointEnum::parse_from_raw(
+        let mut ep = match EndpointV1::parse_from_raw(
             &raw_ep,
             ep_issuer,
             row[1].as_integer().unwrap() as u32,
             row[7].as_integer().unwrap() as u64,
-            1u16,
         ) {
             Ok(ep) => ep,
             Err(e) => panic!(format!(
@@ -74,8 +73,8 @@ pub fn get_endpoints_for_api(db: &Connection, api: &NetworkEndpointApi) -> Vec<E
                 raw_ep, e
             )),
         };
-        ep.set_status(row[1].as_integer().unwrap() as u32);
-        ep.set_last_check(row[7].as_integer().unwrap() as u64);
+        ep.status = row[1].as_integer().unwrap() as u32;
+        ep.last_check = row[7].as_integer().unwrap() as u64;
 
         endpoints.push(ep);
     }
@@ -84,7 +83,7 @@ pub fn get_endpoints_for_api(db: &Connection, api: &NetworkEndpointApi) -> Vec<E
 
 pub fn write_endpoint(
     db: &Connection,
-    endpoint: &EndpointEnum,
+    endpoint: &EndpointV1,
     new_status: u32,
     new_last_check: u64,
 ) {
@@ -106,26 +105,24 @@ pub fn write_endpoint(
         .next()
         .expect("write_endpoint() : Error in cursor.next()")
     {
-        if row[0].as_integer().expect("fail to read ep status !") as u32 != endpoint.status() {
+        if row[0].as_integer().expect("fail to read ep status !") as u32 != endpoint.status {
             db.execute(format!(
                 "UPDATE endpoints SET status={} WHERE hash_full_id='{}'",
-                endpoint.status(),
-                hash_full_id
+                endpoint.status, hash_full_id
             ))
             .expect("Fail to parse SQL request update endpoint  status !");
         }
-    } else if let EndpointEnum::V1(ref ep_v10) = *endpoint {
+    } else {
+        let ep_v10 = endpoint;
         db
                     .execute(
                         format!(
                             "INSERT INTO endpoints (hash_full_id, status, node_id, pubkey, api, version, endpoint, last_check) VALUES ('{}', {}, {}, '{}', {}, {}, '{}', {});",
                             ep_v10.hash_full_id.expect("ep_v10.hash_full_id = None"), new_status, ep_v10.node_id.expect("ep_v10.node_id = None").0,
                             ep_v10.issuer.to_string(), api_to_integer(&ep_v10.api),
-                            ep_v10.version, ep_v10.raw_endpoint, new_last_check
+                            1, ep_v10.raw_endpoint, new_last_check
                         )
                     )
                     .expect("Fail to parse SQL request INSERT endpoint !");
-    } else {
-        panic!("write_endpoint() : Endpoint version is not supported !")
     }
 }
diff --git a/ws2p/Cargo.toml b/ws2p/Cargo.toml
index dbd5d5b9ec1b1e5bd01bdbfa9f514c88a96b61ac..e9d05a0301ab14a3a257163541b7c27e2d1e0f1d 100644
--- a/ws2p/Cargo.toml
+++ b/ws2p/Cargo.toml
@@ -9,8 +9,10 @@ license = "AGPL-3.0"
 path = "lib.rs"
 
 [dependencies]
-duniter-conf = { path = "../conf" }
+bincode = "1.0.*"
+duniter-conf= { path = "../conf" }
 dup-crypto = { path = "../crypto" }
+duniter-documents = { path = "../documents" }
 durs-network-documents = { path = "../network-documents" }
 durs-ws2p-messages = { path = "../ws2p-messages" }
 duniter-message= { path = "../message" }
diff --git a/ws2p/generate_peer.rs b/ws2p/generate_peer.rs
new file mode 100644
index 0000000000000000000000000000000000000000..65340f644086e6f2d43600a66b50b0c5826bfa90
--- /dev/null
+++ b/ws2p/generate_peer.rs
@@ -0,0 +1,124 @@
+//  Copyright (C) 2018  The Durs 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/>.
+
+//! Generate self peer card
+
+use bincode;
+use duniter_documents::{Blockstamp, CurrencyName};
+use dup_crypto::keys::PubKey;
+use durs_network_documents::network_endpoint::*;
+use durs_network_documents::network_peer::*;
+use durs_network_documents::*;
+
+pub fn _self_peer_update_endpoints(
+    self_peer: PeerCardV11,
+    blockstamp: Blockstamp,
+    new_endpoints: Vec<EndpointEnum>,
+) -> PeerCardV11 {
+    let max_eps = self_peer.endpoints.len() + self_peer.endpoints_str.len() + new_endpoints.len();
+    let apis: Vec<NetworkEndpointApi> = new_endpoints
+        .iter()
+        .filter(|ep| {
+            if let EndpointEnum::V2(_) = ep {
+                true
+            } else {
+                false
+            }
+        })
+        .map(|ep| ep.api())
+        .collect();
+    let mut new_endpoints_bin = Vec::with_capacity(max_eps);
+    let mut new_endpoints_str = Vec::with_capacity(max_eps);
+    for ep in self_peer.endpoints {
+        if !apis.contains(&ep.api) {
+            new_endpoints_bin.push(ep);
+        }
+    }
+    for ep in self_peer.endpoints_str {
+        let ep_clone = ep.clone();
+        let ep_fields: Vec<&str> = ep_clone.split(' ').collect();
+        if !apis.contains(&NetworkEndpointApi(ep_fields[0].to_owned())) {
+            new_endpoints_str.push(ep);
+        }
+    }
+    for ep in new_endpoints {
+        if let EndpointEnum::V2(ep_v2) = ep {
+            let bin_len = bincode::serialize(&ep_v2)
+                .unwrap_or_else(|_| {
+                    panic!(
+                        "Fail to update self peer : invalid endpoint : {:?} !",
+                        ep_v2
+                    )
+                })
+                .len();
+            let str_ep = ep_v2.to_string();
+            if str_ep.len() < bin_len {
+                new_endpoints_str.push(str_ep);
+            } else {
+                new_endpoints_bin.push(ep_v2);
+            }
+        }
+    }
+
+    PeerCardV11 {
+        currency_name: self_peer.currency_name,
+        issuer: self_peer.issuer,
+        node_id: self_peer.node_id,
+        blockstamp,
+        endpoints: new_endpoints_bin,
+        endpoints_str: new_endpoints_str,
+        sig: None,
+    }
+}
+
+pub fn _generate_self_peer(
+    currency_name: CurrencyName,
+    issuer: PubKey,
+    node_id: NodeId,
+    blockstamp: Blockstamp,
+    endpoints: Vec<EndpointEnum>,
+) -> PeerCardV11 {
+    let mut endpoints_bin = Vec::with_capacity(endpoints.len());
+    let mut endpoints_str = Vec::with_capacity(endpoints.len());
+
+    for ep in endpoints {
+        if let EndpointEnum::V2(ep_v2) = ep {
+            let bin_len = bincode::serialize(&ep_v2)
+                .unwrap_or_else(|_| {
+                    panic!(
+                        "Fail to generate self peer : invalid endpoint : {:?} !",
+                        ep_v2
+                    )
+                })
+                .len();
+            let str_ep = ep_v2.to_string();
+            if str_ep.len() < bin_len {
+                endpoints_str.push(str_ep);
+            } else {
+                endpoints_bin.push(ep_v2);
+            }
+        }
+    }
+
+    PeerCardV11 {
+        currency_name,
+        issuer,
+        node_id,
+        blockstamp,
+        endpoints: endpoints_bin,
+        endpoints_str,
+        sig: None,
+    }
+}
diff --git a/ws2p/lib.rs b/ws2p/lib.rs
index c6c5aadbf08da3b670a8089c015ccadf795ceeaf..b3c808309bdb7835635569748fa5c9ea680607fa 100644
--- a/ws2p/lib.rs
+++ b/ws2p/lib.rs
@@ -34,7 +34,9 @@ extern crate serde_derive;
 #[macro_use]
 extern crate structopt;
 
+extern crate bincode;
 extern crate duniter_conf;
+extern crate duniter_documents;
 extern crate duniter_message;
 extern crate duniter_module;
 extern crate duniter_network;
@@ -43,13 +45,13 @@ extern crate durs_network_documents;
 extern crate durs_ws2p_messages;
 
 mod constants;
+mod generate_peer;
 
 use constants::*;
 use duniter_conf::DuRsConf;
 use duniter_message::DursMsg;
 use duniter_module::*;
 use duniter_network::*;
-use dup_crypto::keys::*;
 use durs_network_documents::network_endpoint::*;
 use std::sync::mpsc;
 
@@ -67,32 +69,8 @@ impl Default for WS2PConf {
         WS2PConf {
             outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA,
             sync_endpoints: vec![
-                EndpointEnum::parse_from_raw(
-                    "WS2P c1c39a0a g1-monit.librelois.fr 443 /ws2p",
-                    PubKey::Ed25519(
-                        ed25519::PublicKey::from_base58(
-                            "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx",
-                        )
-                        .unwrap(),
-                    ),
-                    0,
-                    0,
-                    1u16,
-                )
-                .unwrap(),
-                EndpointEnum::parse_from_raw(
-                    "WS2P b48824f0 g1.monnaielibreoccitanie.org 443 /ws2p",
-                    PubKey::Ed25519(
-                        ed25519::PublicKey::from_base58(
-                            "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef",
-                        )
-                        .unwrap(),
-                    ),
-                    0,
-                    0,
-                    1u16,
-                )
-                .unwrap(),
+                EndpointV2::parse_from_raw("WS2P g1-monit.librelois.fr 443 ws2p").unwrap(),
+                EndpointV2::parse_from_raw("WS2P g1.monnaielibreoccitanie.org 443 ws2p").unwrap(),
             ],
         }
     }