diff --git a/Cargo.lock b/Cargo.lock
index 4f80ede3961f86585e0c01c6b96e6b14a95fa9fe..7462c888c69db1d76567ebebf2713874e07cf297 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -457,7 +457,6 @@ dependencies = [
  "pest_derive 2.1.0 (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.86 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -548,22 +547,23 @@ dependencies = [
 name = "durs-ws2p-v1-legacy"
 version = "0.1.0-a0.1"
 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)",
  "dubp-documents 0.12.0",
  "duniter-conf 0.1.0-a0.1",
  "duniter-module 0.1.0-a0.1",
  "duniter-network 0.1.0-a0.1",
  "dup-crypto 0.6.0",
+ "durs-common-tools 0.1.0",
  "durs-message 0.1.0-a0.1",
  "durs-network-documents 0.3.1",
  "durs-wot 0.8.0-a0.9",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
- "sqlite 0.23.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unwrap 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ws 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1145,6 +1145,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 name = "serde"
 version = "1.0.86"
 source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde_derive 1.0.86 (registry+https://github.com/rust-lang/crates.io-index)",
+]
 
 [[package]]
 name = "serde_derive"
@@ -1210,33 +1213,6 @@ name = "snowflake"
 version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
-[[package]]
-name = "sqlite"
-version = "0.23.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
- "sqlite3-sys 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "sqlite3-src"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
- "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "sqlite3-sys"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
- "sqlite3-src 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
 [[package]]
 name = "strsim"
 version = "0.7.0"
@@ -1377,6 +1353,11 @@ dependencies = [
  "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "unwrap"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "url"
 version = "1.7.2"
@@ -1571,9 +1552,6 @@ dependencies = [
 "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
 "checksum smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "88aea073965ab29f6edb5493faf96ad662fb18aa9eeb186a3b7057951605ed15"
 "checksum snowflake 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1"
-"checksum sqlite 0.23.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d7b10278336e7fd9dc259399a0f9ff419616738b6a841b136c362e16db626"
-"checksum sqlite3-src 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "62cd5d67c1eb94a2a019b8049f625f65ae7faee8f3c54f0197e5dbc68d5f698c"
-"checksum sqlite3-sys 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71fec807a1534bd13eeaaec396175d67c79bdc68df55e18a452726ec62a8fb08"
 "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
 "checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3"
 "checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04"
@@ -1592,6 +1570,7 @@ dependencies = [
 "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
 "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
 "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
+"checksum unwrap 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e33648dd74328e622c7be51f3b40a303c63f93e6fa5f08778b6203a4c25c20f"
 "checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
 "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d"
 "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
diff --git a/lib/modules/ws2p-v1-legacy/Cargo.toml b/lib/modules/ws2p-v1-legacy/Cargo.toml
index 30909adcbd8fd8a1b68575c73b674dc764638240..6b2f9788e9322cb7e04df870d65c7b29277a61ed 100644
--- a/lib/modules/ws2p-v1-legacy/Cargo.toml
+++ b/lib/modules/ws2p-v1-legacy/Cargo.toml
@@ -7,26 +7,27 @@ license = "AGPL-3.0"
 edition = "2018"
 
 [lib]
-path = "lib.rs"
+path = "src/lib.rs"
 
 [dependencies]
+bincode = "1.0.1"
 byteorder = "1.2.3"
+dubp-documents= { path = "../../tools/documents" }
 duniter-conf = { path = "../../core/conf" }
+duniter-module = { path = "../../core/module" }
+duniter-network = { path = "../../core/network" }
 dup-crypto = { path = "../../tools/crypto" }
-dubp-documents= { path = "../../tools/documents" }
 durs-network-documents = { path = "../../tools/network-documents" }
 durs-message =  { path = "../../core/message" }
-duniter-module = { path = "../../core/module" }
-duniter-network = { path = "../../core/network" }
+durs-common-tools = { path = "../../tools/common-tools" }
 durs-wot = { path = "../../tools/wot" }
 log = "0.4.*"
 rand = "0.4.*"
-sqlite = "0.23.*"
-serde = "1.0.*"
-serde_derive = "1.0.*"
+serde = { version = "1.0.*", features = ["derive"] }
 serde_json = "1.0.*"
 structopt= "0.2.*"
-ws = { version = "0.7.*", features = ["permessage-deflate"] }
+unwrap = "1.2.1"
+ws = "0.7.*"
 
 [features]
 ssl = ["ws/ssl"]
\ No newline at end of file
diff --git a/lib/modules/ws2p-v1-legacy/clippy.toml b/lib/modules/ws2p-v1-legacy/clippy.toml
index 05bd01e10385174463601aedba2ae3ab0fbe921b..fbfe7d8acc90ca3b7b85b00e5f8111ce7f9ab078 100644
--- a/lib/modules/ws2p-v1-legacy/clippy.toml
+++ b/lib/modules/ws2p-v1-legacy/clippy.toml
@@ -1 +1 @@
-cyclomatic-complexity-threshold = 77
\ No newline at end of file
+cyclomatic-complexity-threshold = 51
\ No newline at end of file
diff --git a/lib/modules/ws2p-v1-legacy/constants.rs b/lib/modules/ws2p-v1-legacy/constants.rs
deleted file mode 100644
index aaada76d3146c71a060bf8edea98979815bcad7d..0000000000000000000000000000000000000000
--- a/lib/modules/ws2p-v1-legacy/constants.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-pub static WS2P_OUTCOMING_INTERVAL_AT_STARTUP: &'static u64 = &75;
-pub static WS2P_OUTCOMING_INTERVAL: &'static u64 = &300;
-pub static WS2P_DEFAULT_OUTCOMING_QUOTA: &'static usize = &10;
-pub static WS2P_NEGOTIATION_TIMEOUT: &'static u64 = &15;
-//pub static WS2P_REQUEST_TIMEOUT : &'static u64 = &30;
-pub static WS2P_EXPIRE_TIMEOUT: &'static u64 = &120;
-pub static WS2P_SPAM_INTERVAL_IN_MILLI_SECS: &'static u64 = &80;
-pub static WS2P_SPAM_LIMIT: &'static usize = &6;
-pub static WS2P_SPAM_SLEEP_TIME_IN_SEC: &'static u64 = &100;
-pub static DURATION_BEFORE_RECORDING_ENDPOINT: &'static u64 = &180;
-pub static BLOCKS_REQUEST_INTERVAL: &'static u64 = &60;
-pub static PENDING_IDENTITIES_REQUEST_INTERVAL: &'static u64 = &40;
diff --git a/lib/modules/ws2p-v1-legacy/datas.rs b/lib/modules/ws2p-v1-legacy/datas.rs
deleted file mode 100644
index 89968a51f3c43f48a1a1350b08881efa54a3481d..0000000000000000000000000000000000000000
--- a/lib/modules/ws2p-v1-legacy/datas.rs
+++ /dev/null
@@ -1,491 +0,0 @@
-//  Copyright (C) 2018  The Duniter Project Developers.
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-use crate::*;
-use dup_crypto::keys::*;
-use durs_message::requests::BlockchainRequest;
-use durs_message::*;
-use durs_network_documents::network_endpoint::*;
-use durs_network_documents::network_head::*;
-use durs_network_documents::*;
-use std::collections::HashSet;
-use std::sync::mpsc;
-
-#[derive(Debug)]
-pub struct WS2PModuleDatas {
-    pub router_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
-    pub currency: Option<String>,
-    pub key_pair: Option<KeyPairEnum>,
-    pub conf: WS2PConf,
-    pub ssl: bool,
-    pub node_id: NodeId,
-    pub main_thread_channel: (
-        mpsc::Sender<WS2PThreadSignal>,
-        mpsc::Receiver<WS2PThreadSignal>,
-    ),
-    pub ws2p_endpoints: HashMap<NodeFullId, (EndpointV1, WS2PConnectionState)>,
-    pub websockets: HashMap<NodeFullId, WsSender>,
-    pub requests_awaiting_response:
-        HashMap<ModuleReqId, (OldNetworkRequest, NodeFullId, SystemTime)>,
-    pub heads_cache: HashMap<NodeFullId, NetworkHead>,
-    pub my_head: Option<NetworkHead>,
-    pub uids_cache: HashMap<PubKey, String>,
-    pub count_dal_requests: u32,
-}
-
-#[inline]
-#[cfg(not(feature = "ssl"))]
-fn ssl() -> bool {
-    false
-}
-#[cfg(feature = "ssl")]
-fn ssl() -> bool {
-    true
-}
-
-impl WS2PModuleDatas {
-    pub fn new(
-        router_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
-        conf: WS2PConf,
-        node_id: NodeId,
-    ) -> WS2PModuleDatas {
-        WS2PModuleDatas {
-            router_sender,
-            key_pair: None,
-            currency: None,
-            conf,
-            ssl: ssl(),
-            node_id,
-            main_thread_channel: mpsc::channel(),
-            ws2p_endpoints: HashMap::new(),
-            websockets: HashMap::new(),
-            requests_awaiting_response: HashMap::new(),
-            heads_cache: HashMap::new(),
-            my_head: None,
-            uids_cache: HashMap::new(),
-            count_dal_requests: 0,
-        }
-    }
-    pub fn open_db(db_path: &PathBuf) -> Result<sqlite::Connection, sqlite::Error> {
-        let conn: sqlite::Connection;
-        if !db_path.as_path().exists() {
-            conn = sqlite::open(db_path.as_path())?;
-            conn.execute(
-                "CREATE TABLE endpoints (hash_full_id TEXT, status INTEGER, node_id INTEGER, pubkey TEXT,
-                api INTEGER, version INTEGER, endpoint TEXT, last_check INTEGER);",
-            )?;
-        } else {
-            conn = sqlite::open(db_path.as_path())?;
-        }
-        Ok(conn)
-    }
-    pub fn send_dal_request(&mut self, req: &BlockchainRequest) {
-        self.count_dal_requests += 1;
-        if self.count_dal_requests == std::u32::MAX {
-            self.count_dal_requests = 0;
-        }
-        self.router_sender
-            .send(RouterThreadMessage::ModuleMessage(DursMsg::Request {
-                req_from: WS2PModule::name(),
-                req_to: ModuleRole::BlockchainDatas,
-                req_id: ModuleReqId(self.count_dal_requests),
-                req_content: DursReqContent::BlockchainRequest(req.clone()),
-            }))
-            .expect("Fail to send message to router !");
-    }
-    pub fn send_network_req_response(
-        &self,
-        requester: ModuleStaticName,
-        req_id: ModuleReqId,
-        response: NetworkResponse,
-    ) {
-        self.router_sender
-            .send(RouterThreadMessage::ModuleMessage(DursMsg::Response {
-                res_from: WS2PModule::name(),
-                res_to: requester,
-                req_id,
-                res_content: DursResContent::NetworkResponse(response),
-            }))
-            .expect("Fail to send message to router !");
-    }
-    pub fn send_network_event(&self, event: &NetworkEvent) {
-        let module_event = match event {
-            NetworkEvent::ConnectionStateChange(_, _, _, _) => {
-                ModuleEvent::ConnectionsChangeNodeNetwork
-            }
-            NetworkEvent::ReceiveBlocks(_) => ModuleEvent::NewBlockFromNetwork,
-            NetworkEvent::ReceiveDocuments(network_docs) => {
-                if !network_docs.is_empty() {
-                    match network_docs[0] {
-                        BlockchainDocument::Block(_) => ModuleEvent::NewBlockFromNetwork,
-                        BlockchainDocument::Transaction(_) => ModuleEvent::NewTxFromNetwork,
-                        _ => ModuleEvent::NewWotDocFromNetwork,
-                    }
-                } else {
-                    return;
-                }
-            }
-            NetworkEvent::ReceiveHeads(_) => ModuleEvent::NewValidHeadFromNetwork,
-            NetworkEvent::ReceivePeers(_) => ModuleEvent::NewValidPeerFromNodeNetwork,
-            NetworkEvent::NewSelfPeer(_) => ModuleEvent::NewSelfPeer,
-        };
-        self.router_sender
-            .send(RouterThreadMessage::ModuleMessage(DursMsg::Event {
-                event_type: module_event,
-                event_content: DursEvent::NetworkEvent(event.clone()),
-            }))
-            .expect("Fail to send network event to router !");
-    }
-    fn count_established_connections(&self) -> usize {
-        let mut count_established_connections = 0;
-        for (_ws2p_full_id, (_ep, state)) in self.ws2p_endpoints.clone() {
-            if let WS2PConnectionState::Established = state {
-                count_established_connections += 1;
-            }
-        }
-        count_established_connections
-    }
-    pub fn connect_to_know_endpoints(&mut self) {
-        info!("WS2P: connect to know endpoints...");
-        let mut count_established_connections = 0;
-        let mut pubkeys = HashSet::new();
-        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.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.issuer);
-                        reachable_endpoints.push(ep);
-                    }
-                    _ => {
-                        pubkeys.insert(ep.issuer);
-                        unreachable_endpoints.push(ep);
-                    }
-                }
-            }
-        }
-        let mut free_outcoming_rooms =
-            self.conf.clone().outcoming_quota - count_established_connections;
-        while free_outcoming_rooms > 0 {
-            let ep = if !reachable_endpoints.is_empty() {
-                reachable_endpoints
-                    .pop()
-                    .expect("WS2P: Fail to pop() reachable_endpoints !")
-            } else if !unreachable_endpoints.is_empty() {
-                unreachable_endpoints
-                    .pop()
-                    .expect("WS2P: Fail to pop() unreachable_endpoints !")
-            } else {
-                break;
-            };
-            if !self.ssl && ep.port == 443 {
-                continue;
-            }
-            self.connect_to_without_checking_quotas(&ep);
-            free_outcoming_rooms -= 1;
-        }
-    }
-    pub fn connect_to(&mut self, endpoint: &EndpointV1) {
-        // Add endpoint to endpoints list (if there isn't already)
-        match self.ws2p_endpoints.get(
-            &endpoint
-                .node_full_id()
-                .expect("WS2P: Fail to get ep.node_full_id() !"),
-        ) {
-            Some(_) => {
-                self.ws2p_endpoints
-                    .get_mut(
-                        &endpoint
-                            .node_full_id()
-                            .expect("WS2P: Fail to get ep.node_full_id() !"),
-                    )
-                    .expect("WS2P: Fail to get_mut() a ws2p_endpoint !")
-                    .1 = WS2PConnectionState::NeverTry;
-            }
-            None => {
-                self.ws2p_endpoints.insert(
-                    endpoint
-                        .node_full_id()
-                        .expect("WS2P: Fail to get ep.node_full_id() !"),
-                    (endpoint.clone(), WS2PConnectionState::NeverTry),
-                );
-            }
-        };
-        if self.conf.clone().outcoming_quota > self.count_established_connections() {
-            self.connect_to_without_checking_quotas(&endpoint);
-        }
-    }
-    pub fn close_connection(
-        &mut self,
-        ws2p_full_id: &NodeFullId,
-        reason: WS2PCloseConnectionReason,
-    ) {
-        match reason {
-            WS2PCloseConnectionReason::NegociationTimeout => {}
-            WS2PCloseConnectionReason::AuthMessInvalidSig
-            | WS2PCloseConnectionReason::Timeout
-            | WS2PCloseConnectionReason::WsError
-            | WS2PCloseConnectionReason::Unknow => {
-                self.ws2p_endpoints
-                    .get_mut(ws2p_full_id)
-                    .expect("Failure : attempt to delete a non-existent connection !")
-                    .1 = WS2PConnectionState::Close
-            }
-        }
-        if let Some(websocket) = self.websockets.get(&ws2p_full_id) {
-            let _result = websocket.0.close(ws::CloseCode::Normal);
-        }
-        let _result = self.websockets.remove(ws2p_full_id);
-    }
-    pub fn ws2p_conn_message_pretreatment(&mut self, message: WS2PConnectionMessage) -> WS2PSignal {
-        let ws2p_full_id = message.0;
-        match message.1 {
-            WS2PConnectionMessagePayload::WrongUrl
-            | WS2PConnectionMessagePayload::FailOpenWS
-            | WS2PConnectionMessagePayload::FailToSplitWS => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = WS2PConnectionState::WSError;
-                return WS2PSignal::WSError(ws2p_full_id);
-            }
-            WS2PConnectionMessagePayload::TryToSendConnectMess => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = WS2PConnectionState::TryToSendConnectMess;
-            }
-            WS2PConnectionMessagePayload::FailSendConnectMess => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to mut ep !")
-                    .1 = WS2PConnectionState::Unreachable;
-            }
-            WS2PConnectionMessagePayload::WebsocketOk(sender) => {
-                self.websockets.insert(ws2p_full_id, sender);
-            }
-            WS2PConnectionMessagePayload::ValidConnectMessage(response, new_con_state) => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = new_con_state;
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("Endpoint don't exist !")
-                    .1 = WS2PConnectionState::ConnectMessOk;
-                debug!("Send: {:#?}", response);
-                if let Some(websocket) = self.websockets.get_mut(&ws2p_full_id) {
-                    if websocket.0.send(Message::text(response)).is_err() {
-                        return WS2PSignal::WSError(ws2p_full_id);
-                    }
-                } else {
-                    // Connection closed by remote peer
-                    self.ws2p_endpoints
-                        .get_mut(&ws2p_full_id)
-                        .expect("Endpoint don't exist !")
-                        .1 = WS2PConnectionState::Close;
-                }
-            }
-            WS2PConnectionMessagePayload::ValidAckMessage(response, new_con_state) => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = new_con_state;
-                if let WS2PConnectionState::AckMessOk = self.ws2p_endpoints[&ws2p_full_id].1 {
-                    debug!("Send: {:#?}", response);
-                    if let Some(websocket) = self.websockets.get_mut(&ws2p_full_id) {
-                        if websocket.0.send(Message::text(response)).is_err() {
-                            return WS2PSignal::WSError(ws2p_full_id);
-                        }
-                    } else {
-                        panic!("Fatal error : no websocket for {} !", ws2p_full_id);
-                    }
-                }
-            }
-            WS2PConnectionMessagePayload::ValidOk(new_con_state) => {
-                self.ws2p_endpoints
-                    .get_mut(&ws2p_full_id)
-                    .expect("WS2P: Fail to get mut ep !")
-                    .1 = new_con_state;
-                match self.ws2p_endpoints[&ws2p_full_id].1 {
-                    WS2PConnectionState::OkMessOkWaitingAckMess => {}
-                    WS2PConnectionState::Established => {
-                        return WS2PSignal::ConnectionEstablished(ws2p_full_id);
-                    }
-                    _ => {
-                        self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::Unknow);
-                        return WS2PSignal::Empty;
-                    }
-                }
-            }
-            WS2PConnectionMessagePayload::DalRequest(req_id, req_body) => {
-                return WS2PSignal::DalRequest(ws2p_full_id, req_id, req_body);
-            }
-            WS2PConnectionMessagePayload::PeerCard(body, ws2p_endpoints) => {
-                return WS2PSignal::PeerCard(ws2p_full_id, body, ws2p_endpoints);
-            }
-            WS2PConnectionMessagePayload::Heads(heads) => {
-                let mut applied_heads = Vec::with_capacity(heads.len());
-                for head in heads {
-                    if let Ok(head) = NetworkHead::from_json_value(&head) {
-                        if head.verify()
-                            && (self.my_head.is_none()
-                                || head.node_full_id()
-                                    != self
-                                        .my_head
-                                        .clone()
-                                        .expect("WS2P: Fail to clone my_head")
-                                        .node_full_id())
-                            && head.apply(&mut self.heads_cache)
-                        {
-                            applied_heads.push(head);
-                        }
-                    }
-                }
-                return WS2PSignal::Heads(ws2p_full_id, applied_heads);
-            }
-            WS2PConnectionMessagePayload::Document(network_doc) => {
-                return WS2PSignal::Document(ws2p_full_id, network_doc);
-            }
-            WS2PConnectionMessagePayload::ReqResponse(req_id, response) => {
-                if self.requests_awaiting_response.len() > req_id.0 as usize {
-                    if let Some((ref ws2p_request, ref recipient_fulld_id, ref _timestamp)) =
-                        self.requests_awaiting_response.remove(&req_id)
-                    {
-                        return WS2PSignal::ReqResponse(
-                            req_id,
-                            *ws2p_request,
-                            *recipient_fulld_id,
-                            response,
-                        );
-                    }
-                }
-            }
-            WS2PConnectionMessagePayload::NegociationTimeout => {
-                match self.ws2p_endpoints[&ws2p_full_id].1 {
-                    WS2PConnectionState::AckMessOk | WS2PConnectionState::ConnectMessOk => {
-                        self.ws2p_endpoints
-                            .get_mut(&ws2p_full_id)
-                            .expect("WS2P: Fail to get mut ep !")
-                            .1 = WS2PConnectionState::Denial
-                    }
-                    WS2PConnectionState::WaitingConnectMess => {
-                        self.ws2p_endpoints
-                            .get_mut(&ws2p_full_id)
-                            .expect("WS2P: Fail to get mut ep !")
-                            .1 = WS2PConnectionState::NoResponse
-                    }
-                    _ => {
-                        self.ws2p_endpoints
-                            .get_mut(&ws2p_full_id)
-                            .expect("WS2P: Fail to get mut ep !")
-                            .1 = WS2PConnectionState::Unreachable
-                    }
-                }
-                self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::NegociationTimeout);
-                return WS2PSignal::NegociationTimeout(ws2p_full_id);
-            }
-            WS2PConnectionMessagePayload::Timeout => {
-                self.close_connection(&ws2p_full_id, WS2PCloseConnectionReason::Timeout);
-                return WS2PSignal::Timeout(ws2p_full_id);
-            }
-            WS2PConnectionMessagePayload::UnknowMessage => {
-                warn!("WS2P : Receive Unknow Message from {}.", &ws2p_full_id.1)
-            }
-            WS2PConnectionMessagePayload::WrongFormatMessage => warn!(
-                "WS2P : Receive Wrong Format Message from {}.",
-                &ws2p_full_id.1
-            ),
-            WS2PConnectionMessagePayload::InvalidMessage => return WS2PSignal::Empty,
-            WS2PConnectionMessagePayload::Close => {
-                if self.websockets.contains_key(&ws2p_full_id) {
-                    self.close_connection(
-                        &ws2p_full_id,
-                        WS2PCloseConnectionReason::AuthMessInvalidSig,
-                    )
-                }
-            }
-        }
-        let connections_count = self.websockets.len();
-        if connections_count == 0 {
-            return WS2PSignal::NoConnection;
-        }
-        // Detect timeout requests
-        let mut requests_timeout = Vec::new();
-        for &(ref req, ref ws2p_full_id, ref timestamp) in
-            self.requests_awaiting_response.clone().values()
-        {
-            if SystemTime::now().duration_since(*timestamp).unwrap() > Duration::new(20, 0) {
-                requests_timeout.push(req.get_req_full_id());
-                warn!("request timeout : {:?} (sent to {:?})", req, ws2p_full_id);
-            }
-        }
-        // Delete (and resend) timeout requests
-        for req_id in requests_timeout {
-            //let ws2p_endpoints = self.ws2p_endpoints.clone();
-            let _request_option = self.requests_awaiting_response.remove(&req_id.1);
-            /*if let Some((request, _, _)) = request_option {
-                let _request_result = self.send_request_to_specific_node(
-                    &get_random_connection(&ws2p_endpoints),
-                    &request,
-                );
-            }*/
-        }
-        WS2PSignal::Empty
-    }
-
-    pub fn send_request_to_specific_node(
-        &mut self,
-        receiver_ws2p_full_id: &NodeFullId,
-        ws2p_request: &OldNetworkRequest,
-    ) -> ws::Result<()> {
-        self.websockets
-            .get_mut(receiver_ws2p_full_id)
-            .expect("WS2P: Fail to get mut websocket !")
-            .0
-            .send(Message::text(
-                network_request_to_json(ws2p_request).to_string(),
-            ))?;
-        self.requests_awaiting_response.insert(
-            ws2p_request.get_req_id(),
-            (*ws2p_request, *receiver_ws2p_full_id, SystemTime::now()),
-        );
-        debug!(
-            "send request {} to {}",
-            network_request_to_json(ws2p_request).to_string(),
-            receiver_ws2p_full_id
-        );
-        Ok(())
-    }
-
-    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();
-        let key_pair_copy = self.key_pair;
-        thread::spawn(move || {
-            let _result = connect_to_ws2p_endpoint(
-                &endpoint_copy,
-                &conductor_sender_copy,
-                &currency_copy.expect("WS2PError : No currency !"),
-                key_pair_copy.expect("WS2PError : No key_pair !"),
-            );
-        });
-    }
-}
diff --git a/lib/modules/ws2p-v1-legacy/lib.rs b/lib/modules/ws2p-v1-legacy/lib.rs
deleted file mode 100644
index a3e7663e068d2b5ee8ab454e75133c49390b155f..0000000000000000000000000000000000000000
--- a/lib/modules/ws2p-v1-legacy/lib.rs
+++ /dev/null
@@ -1,1147 +0,0 @@
-//  Copyright (C) 2018  The Duniter Project Developers.
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-//! WebSocketToPeer API for the Duniter project.
-
-#![deny(
-    missing_debug_implementations,
-    missing_copy_implementations,
-    trivial_casts,
-    unsafe_code,
-    unstable_features,
-    unused_import_braces,
-    unused_qualifications
-)]
-#![recursion_limit = "256"]
-
-#[macro_use]
-extern crate log;
-#[macro_use]
-extern crate serde_derive;
-#[macro_use]
-extern crate serde_json;
-#[macro_use]
-extern crate structopt;
-
-mod ack_message;
-mod connect_message;
-pub mod constants;
-mod datas;
-mod heads;
-mod ok_message;
-pub mod parsers;
-pub mod serializer;
-pub mod ws2p_connection;
-pub mod ws2p_db;
-pub mod ws2p_requests;
-
-use crate::ack_message::WS2PAckMessageV1;
-use crate::connect_message::WS2PConnectMessageV1;
-use crate::constants::*;
-use crate::datas::*;
-use crate::ok_message::WS2POkMessageV1;
-use crate::parsers::blocks::parse_json_block;
-use crate::ws2p_connection::*;
-use crate::ws2p_requests::network_request_to_json;
-use dubp_documents::{Blockstamp, Document};
-use duniter_conf::DuRsConf;
-use duniter_module::*;
-use duniter_network::cli::sync::SyncOpt;
-use duniter_network::documents::*;
-use duniter_network::events::*;
-use duniter_network::requests::*;
-use duniter_network::*;
-use dup_crypto::keys::*;
-use durs_message::events::*;
-use durs_message::requests::*;
-use durs_message::responses::*;
-use durs_message::*;
-use durs_network_documents::network_endpoint::*;
-use durs_network_documents::network_head::*;
-use durs_network_documents::*;
-use std::collections::HashMap;
-use std::ops::Deref;
-use std::path::PathBuf;
-use std::sync::mpsc;
-use std::thread;
-use std::time::{Duration, SystemTime, UNIX_EPOCH};
-use ws::Message;
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
-/// WS2P Configuration
-pub struct WS2PConf {
-    /// Limit of outcoming connections
-    pub outcoming_quota: usize,
-    /// Default WS2P endpoints provides by configuration file
-    pub sync_endpoints: Vec<EndpointV1>,
-}
-
-impl Default for WS2PConf {
-    fn default() -> Self {
-        WS2PConf {
-            outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA,
-            sync_endpoints: vec![
-                EndpointV1::parse_from_raw(
-                    "WS2P c1c39a0a ts.g1.librelois.fr 443 /ws2p",
-                    PubKey::Ed25519(
-                        ed25519::PublicKey::from_base58(
-                            "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx",
-                        )
-                        .unwrap(),
-                    ),
-                    0,
-                    0,
-                )
-                .unwrap(),
-                EndpointV1::parse_from_raw(
-                    "WS2P fb17fcd4 g1.duniter.fr 443 /ws2p",
-                    PubKey::Ed25519(
-                        ed25519::PublicKey::from_base58(
-                            "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE",
-                        )
-                        .unwrap(),
-                    ),
-                    0,
-                    0,
-                )
-                .unwrap(),
-                EndpointV1::parse_from_raw(
-                    "WS2P 7b33becd g1.nordstrom.duniter.org 443 /ws2p",
-                    PubKey::Ed25519(
-                        ed25519::PublicKey::from_base58(
-                            "DWoSCRLQyQ48dLxUGr1MDKg4NFcbPbC56LN2hJjCCPpZ",
-                        )
-                        .unwrap(),
-                    ),
-                    0,
-                    0,
-                )
-                .unwrap(),
-                EndpointV1::parse_from_raw(
-                    "WS2P dff60418 duniter.normandie-libre.fr 443 /ws2p",
-                    PubKey::Ed25519(
-                        ed25519::PublicKey::from_base58(
-                            "8t6Di3pLxxoTEfjXHjF49pNpjSTXuGEQ6BpkT75CkNb2",
-                        )
-                        .unwrap(),
-                    ),
-                    0,
-                    0,
-                )
-                .unwrap(),
-            ],
-        }
-    }
-}
-
-#[derive(Debug)]
-/// Store a Signal receive from network (after message treatment)
-pub enum WS2PSignal {
-    /// Receive a websocket error from a connextion. `NodeFullId` store the identifier of connection.
-    WSError(NodeFullId),
-    /// A new connection is successfully established with `NodeFullId`.
-    ConnectionEstablished(NodeFullId),
-    NegociationTimeout(NodeFullId),
-    Timeout(NodeFullId),
-    DalRequest(NodeFullId, ModuleReqId, serde_json::Value),
-    PeerCard(NodeFullId, serde_json::Value, Vec<EndpointV1>),
-    Heads(NodeFullId, Vec<NetworkHead>),
-    Document(NodeFullId, BlockchainDocument),
-    ReqResponse(
-        ModuleReqId,
-        OldNetworkRequest,
-        NodeFullId,
-        serde_json::Value,
-    ),
-    Empty,
-    NoConnection,
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub enum NetworkConsensusError {
-    InsufficientData(usize),
-    Fork,
-}
-
-#[derive(Debug)]
-pub enum SendRequestError {
-    RequestTypeMustNotBeTransmitted(),
-    WSError(usize, Vec<ws::Error>),
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct WS2PModule {}
-
-#[derive(Debug)]
-pub enum WS2PThreadSignal {
-    DursMsg(Box<DursMsg>),
-    WS2PConnectionMessage(WS2PConnectionMessage),
-}
-
-pub trait WS2PMessage: Sized {
-    fn parse(v: &serde_json::Value, currency: String) -> Option<Self>;
-    fn to_raw(&self) -> String;
-    fn sign(&self, key_pair: KeyPairEnum) -> Sig {
-        key_pair.sign(self.to_raw().as_bytes())
-    }
-    fn verify(&self) -> bool;
-    //fn parse_and_verify(v: serde_json::Value, currency: String) -> bool;
-}
-
-impl Default for WS2PModule {
-    fn default() -> WS2PModule {
-        WS2PModule {}
-    }
-}
-
-#[derive(Debug)]
-/// WS2PFeaturesParseError
-pub enum WS2PFeaturesParseError {
-    /// UnknowApiFeature
-    UnknowApiFeature(String),
-}
-
-impl ApiModule<DuRsConf, DursMsg> for WS2PModule {
-    type ParseErr = WS2PFeaturesParseError;
-    /// Parse raw api features
-    fn parse_raw_api_features(str_features: &str) -> Result<ApiFeatures, Self::ParseErr> {
-        let str_features: Vec<&str> = str_features.split(' ').collect();
-        let mut api_features = Vec::with_capacity(0);
-        for str_feature in str_features {
-            match str_feature {
-                "DEF" => api_features[0] += 1u8,
-                "LOW" => api_features[0] += 2u8,
-                "ABF" => api_features[0] += 4u8,
-                _ => {
-                    return Err(WS2PFeaturesParseError::UnknowApiFeature(String::from(
-                        str_feature,
-                    )));
-                }
-            }
-        }
-        Ok(ApiFeatures(api_features))
-    }
-}
-
-impl NetworkModule<DuRsConf, DursMsg> for WS2PModule {
-    fn sync(
-        _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
-        _keys: RequiredKeysContent,
-        _conf: WS2PConf,
-        _main_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
-        _sync_params: SyncOpt,
-    ) -> Result<(), ModuleInitError> {
-        println!("Downlaod blockchain from network...");
-        println!("Error : not yet implemented !");
-        Ok(())
-    }
-}
-
-#[derive(StructOpt, Debug, Copy, Clone)]
-#[structopt(
-    name = "ws2p",
-    raw(setting = "structopt::clap::AppSettings::ColoredHelp")
-)]
-/// WS2Pv1 subcommand options
-pub struct WS2POpt {}
-
-impl DursModule<DuRsConf, DursMsg> for WS2PModule {
-    type ModuleConf = WS2PConf;
-    type ModuleOpt = WS2POpt;
-
-    fn name() -> ModuleStaticName {
-        ModuleStaticName("ws2p")
-    }
-    fn priority() -> ModulePriority {
-        ModulePriority::Essential()
-    }
-    fn ask_required_keys() -> RequiredKeys {
-        RequiredKeys::NetworkKeyPair()
-    }
-    fn have_subcommand() -> bool {
-        true
-    }
-    fn exec_subcommand(
-        _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
-        _keys: RequiredKeysContent,
-        _module_conf: Self::ModuleConf,
-        _subcommand_args: WS2POpt,
-    ) {
-        println!("Succesfully exec ws2p subcommand !")
-    }
-    fn start(
-        soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
-        keys: RequiredKeysContent,
-        conf: WS2PConf,
-        router_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
-        load_conf_only: bool,
-    ) -> Result<(), ModuleInitError> {
-        // Get start time
-        let start_time = SystemTime::now();
-
-        // Define WS2PModuleDatas
-        let mut ws2p_module = WS2PModuleDatas::new(
-            router_sender.clone(),
-            conf,
-            NodeId(soft_meta_datas.conf.my_node_id()),
-        );
-
-        // load conf
-        let key_pair = match keys {
-            RequiredKeysContent::NetworkKeyPair(key_pair) => key_pair,
-            _ => panic!("WS2PModule fatal error at load_conf() : keys != NetworkKeyPair"),
-        };
-        let mut ws2p_endpoints = HashMap::new();
-        for ep in &ws2p_module.conf.sync_endpoints {
-            ws2p_endpoints.insert(
-                ep.node_full_id()
-                    .expect("Fail to get endpoint node_full_id"),
-                (ep.clone(), WS2PConnectionState::Close),
-            );
-            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());
-        ws2p_module.ws2p_endpoints = ws2p_endpoints;
-
-        // Create ws2p main thread channel
-        let ws2p_sender_clone = ws2p_module.main_thread_channel.0.clone();
-
-        // Create proxy channel
-        let (proxy_sender, proxy_receiver): (mpsc::Sender<DursMsg>, mpsc::Receiver<DursMsg>) =
-            mpsc::channel();
-        let proxy_sender_clone = proxy_sender.clone();
-
-        // Launch a proxy thread that transform DursMsg to WS2PThreadSignal(DursMsg)
-        thread::spawn(move || {
-            // Send proxy sender to main
-            router_sender
-                .send(RouterThreadMessage::ModuleRegistration(
-                    WS2PModule::name(),
-                    proxy_sender_clone,
-                    vec![ModuleRole::InterNodesNetwork],
-                    vec![
-                        ModuleEvent::NewValidBlock,
-                        ModuleEvent::NewWotDocInPool,
-                        ModuleEvent::NewTxinPool,
-                    ],
-                    vec![],
-                    vec![],
-                ))
-                .expect("Fatal error : ws2p module fail to send is sender channel !");
-            debug!("Send ws2p sender to main thread.");
-            loop {
-                match proxy_receiver.recv() {
-                    Ok(message) => {
-                        let stop = if let DursMsg::Stop = message {
-                            true
-                        } else {
-                            false
-                        };
-                        ws2p_sender_clone
-                            .send(WS2PThreadSignal::DursMsg(Box::new(message)))
-                            .expect(
-                                "Fatal error : fail to relay DursMsgContent to ws2p main thread !",
-                            );
-                        if stop {
-                            break;
-                        };
-                    }
-                    Err(e) => panic!(format!("{}", e)),
-                }
-            }
-        });
-
-        // open ws2p bdd
-        let mut db_path =
-            duniter_conf::datas_path(&soft_meta_datas.profile, &soft_meta_datas.conf.currency());
-        db_path.push("ws2p.db");
-        let db = WS2PModuleDatas::open_db(&db_path).expect("Fatal error : fail to open WS2P DB !");
-
-        // Get ws2p endpoints in BDD
-        let mut count = 0;
-        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)
-            {
-                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)),
-                );
-            }
-        }
-        info!("Load {} endpoints from bdd !", count);
-
-        // Stop here in load_conf_only mode
-        if load_conf_only {
-            return Ok(());
-        }
-
-        // Initialize variables
-        let mut last_ws2p_connecting_wave = SystemTime::now();
-        let mut last_ws2p_connections_print = SystemTime::now();
-        let mut endpoints_to_update_status: HashMap<NodeFullId, SystemTime> = HashMap::new();
-        let mut last_identities_request = UNIX_EPOCH;
-        let mut current_blockstamp = Blockstamp::default();
-        let mut next_receiver = 0;
-
-        // Request current blockstamp
-        ws2p_module.send_dal_request(&BlockchainRequest::CurrentBlockstamp());
-
-        // Start
-        ws2p_module.connect_to_know_endpoints();
-        loop {
-            match ws2p_module
-                .main_thread_channel
-                .1
-                .recv_timeout(Duration::from_millis(200))
-            {
-                Ok(message) => match message {
-                    WS2PThreadSignal::DursMsg(ref durs_mesage) => {
-                        match *durs_mesage.deref() {
-                            DursMsg::Stop => break,
-                            DursMsg::Request {
-                                ref req_content, ..
-                            } => {
-                                if let DursReqContent::OldNetworkRequest(ref old_net_request) =
-                                    *req_content
-                                {
-                                    match *old_net_request {
-                                        OldNetworkRequest::GetBlocks(
-                                            ref req_id,
-                                            ref count,
-                                            ref from,
-                                        ) => {
-                                            let mut receiver_index = 0;
-                                            let mut real_receiver = None;
-                                            for (ws2p_full_id, (_ep, state)) in
-                                                ws2p_module.ws2p_endpoints.clone()
-                                            {
-                                                if let WS2PConnectionState::Established = state {
-                                                    if receiver_index == next_receiver {
-                                                        real_receiver = Some(ws2p_full_id);
-                                                        break;
-                                                    }
-                                                    receiver_index += 1;
-                                                }
-                                            }
-                                            if real_receiver.is_none() {
-                                                next_receiver = 0;
-                                                for (ws2p_full_id, (_ep, state)) in
-                                                    &ws2p_module.ws2p_endpoints
-                                                {
-                                                    if let WS2PConnectionState::Established = *state
-                                                    {
-                                                        real_receiver = Some(*ws2p_full_id);
-                                                        break;
-                                                    }
-                                                }
-                                            } else {
-                                                next_receiver += 1;
-                                            }
-                                            if let Some(real_receiver) = real_receiver {
-                                                debug!("WS2P: send req to: ({:?})", real_receiver);
-                                                let _blocks_request_result = ws2p_module
-                                                    .send_request_to_specific_node(
-                                                        &real_receiver,
-                                                        &OldNetworkRequest::GetBlocks(
-                                                            *req_id, *count, *from,
-                                                        ),
-                                                    );
-                                            } else {
-                                                warn!("WS2P: not found peer to send request !");
-                                            }
-                                        }
-                                        OldNetworkRequest::GetEndpoints(ref _request) => {}
-                                        _ => {}
-                                    }
-                                }
-                            }
-                            DursMsg::Event {
-                                ref event_content, ..
-                            } => {
-                                if let DursEvent::BlockchainEvent(ref bc_event) = *event_content {
-                                    match *bc_event.deref() {
-                                        BlockchainEvent::StackUpValidBlock(ref block) => {
-                                            current_blockstamp = block.deref().blockstamp();
-                                            debug!(
-                                                "WS2PModule : current_blockstamp = {}",
-                                                current_blockstamp
-                                            );
-                                            ws2p_module.my_head = Some(heads::generate_my_head(
-                                                &key_pair,
-                                                NodeId(soft_meta_datas.conf.my_node_id()),
-                                                soft_meta_datas.soft_name,
-                                                soft_meta_datas.soft_version,
-                                                &current_blockstamp,
-                                                None,
-                                            ));
-                                            ws2p_module.send_network_event(
-                                                &NetworkEvent::ReceiveHeads(vec![ws2p_module
-                                                    .my_head
-                                                    .clone()
-                                                    .unwrap()]),
-                                            );
-                                            // Send my head to all connections
-                                            let my_json_head = serializer::serialize_head(
-                                                ws2p_module.my_head.clone().unwrap(),
-                                            );
-                                            trace!("Send my HEAD: {:#?}", my_json_head);
-                                            let _results: Result<(), ws::Error> = ws2p_module
-                                                .websockets
-                                                .iter_mut()
-                                                .map(|ws| {
-                                                    (ws.1).0.send(Message::text(
-                                                        json!({
-                                                            "name": "HEAD",
-                                                            "body": {
-                                                                "heads": [my_json_head]
-                                                            }
-                                                        })
-                                                        .to_string(),
-                                                    ))
-                                                })
-                                                .collect();
-                                        }
-                                        BlockchainEvent::RevertBlocks(ref _blocks) => {}
-                                        _ => {}
-                                    }
-                                }
-                            }
-                            DursMsg::Response {
-                                ref res_content, ..
-                            } => {
-                                if let DursResContent::BlockchainResponse(ref bc_res) = *res_content
-                                {
-                                    match *bc_res.deref() {
-                                        BlockchainResponse::CurrentBlockstamp(
-                                            ref _requester_id,
-                                            ref current_blockstamp_,
-                                        ) => {
-                                            debug!(
-                                                "WS2PModule : receive DALResBc::CurrentBlockstamp({})",
-                                                current_blockstamp
-                                            );
-                                            current_blockstamp = *current_blockstamp_;
-                                            if ws2p_module.my_head.is_none() {
-                                                ws2p_module.my_head =
-                                                    Some(heads::generate_my_head(
-                                                        &key_pair,
-                                                        NodeId(soft_meta_datas.conf.my_node_id()),
-                                                        soft_meta_datas.soft_name,
-                                                        soft_meta_datas.soft_version,
-                                                        &current_blockstamp,
-                                                        None,
-                                                    ));
-                                            }
-                                            ws2p_module.send_network_event(
-                                                &NetworkEvent::ReceiveHeads(vec![ws2p_module
-                                                    .my_head
-                                                    .clone()
-                                                    .unwrap()]),
-                                            );
-                                        }
-                                        BlockchainResponse::UIDs(ref _req_id, ref uids) => {
-                                            // Add uids to heads
-                                            for head in ws2p_module.heads_cache.values_mut() {
-                                                if let Some(uid_option) = uids.get(&head.pubkey()) {
-                                                    if let Some(ref uid) = *uid_option {
-                                                        head.set_uid(uid);
-                                                        ws2p_module
-                                                            .uids_cache
-                                                            .insert(head.pubkey(), uid.to_string());
-                                                    } else {
-                                                        ws2p_module
-                                                            .uids_cache
-                                                            .remove(&head.pubkey());
-                                                    }
-                                                }
-                                            }
-                                            // Resent heads to other modules
-                                            ws2p_module.send_network_event(
-                                                &NetworkEvent::ReceiveHeads(
-                                                    ws2p_module
-                                                        .heads_cache
-                                                        .values()
-                                                        .cloned()
-                                                        .collect(),
-                                                ),
-                                            );
-                                            // Resent to other modules connections that match receive uids
-                                            for (node_full_id, (ep, conn_state)) in
-                                                &ws2p_module.ws2p_endpoints
-                                            {
-                                                if let Some(uid_option) = uids.get(&node_full_id.1)
-                                                {
-                                                    ws2p_module.send_network_event(
-                                                        &NetworkEvent::ConnectionStateChange(
-                                                            *node_full_id,
-                                                            *conn_state as u32,
-                                                            uid_option.clone(),
-                                                            ep.get_url(false, false)
-                                                                .expect("Endpoint unreachable !"),
-                                                        ),
-                                                    );
-                                                }
-                                            }
-                                        }
-                                        _ => {} // Others BlockchainResponse variants
-                                    }
-                                }
-                            }
-                            _ => {} // Others DursMsg variants
-                        }
-                    }
-                    WS2PThreadSignal::WS2PConnectionMessage(ws2p_conn_message) => match ws2p_module
-                        .ws2p_conn_message_pretreatment(ws2p_conn_message)
-                    {
-                        WS2PSignal::NoConnection => {
-                            warn!("WS2PSignal::NoConnection");
-                        }
-                        WS2PSignal::ConnectionEstablished(ws2p_full_id) => {
-                            let req_id =
-                                ModuleReqId(ws2p_module.requests_awaiting_response.len() as u32);
-                            let module_id = WS2PModule::name();
-                            debug!("WS2P: send req to: ({:?})", ws2p_full_id);
-                            let _current_request_result = ws2p_module
-                                .send_request_to_specific_node(
-                                    &ws2p_full_id,
-                                    &OldNetworkRequest::GetCurrent(ModuleReqFullId(
-                                        module_id, req_id,
-                                    )),
-                                );
-                            if ws2p_module.uids_cache.get(&ws2p_full_id.1).is_none() {
-                                ws2p_module.send_dal_request(&BlockchainRequest::UIDs(vec![
-                                    ws2p_full_id.1,
-                                ]));
-                            }
-                            ws2p_module.send_network_event(&NetworkEvent::ConnectionStateChange(
-                                ws2p_full_id,
-                                WS2PConnectionState::Established as u32,
-                                ws2p_module.uids_cache.get(&ws2p_full_id.1).cloned(),
-                                ws2p_module.ws2p_endpoints[&ws2p_full_id]
-                                    .0
-                                    .get_url(false, false)
-                                    .expect("Endpoint unreachable !"),
-                            ));
-                        }
-                        WS2PSignal::WSError(ws2p_full_id) => {
-                            endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());
-                            ws2p_module.close_connection(
-                                &ws2p_full_id,
-                                WS2PCloseConnectionReason::WsError,
-                            );
-                            ws2p_module.send_network_event(&NetworkEvent::ConnectionStateChange(
-                                ws2p_full_id,
-                                WS2PConnectionState::WSError as u32,
-                                ws2p_module.uids_cache.get(&ws2p_full_id.1).cloned(),
-                                ws2p_module.ws2p_endpoints[&ws2p_full_id]
-                                    .0
-                                    .get_url(false, false)
-                                    .expect("Endpoint unreachable !"),
-                            ));
-                        }
-                        WS2PSignal::NegociationTimeout(ws2p_full_id) => {
-                            endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());
-                            ws2p_module.send_network_event(&NetworkEvent::ConnectionStateChange(
-                                ws2p_full_id,
-                                WS2PConnectionState::Denial as u32,
-                                ws2p_module.uids_cache.get(&ws2p_full_id.1).cloned(),
-                                ws2p_module.ws2p_endpoints[&ws2p_full_id]
-                                    .0
-                                    .get_url(false, false)
-                                    .expect("Endpoint unreachable !"),
-                            ));
-                        }
-                        WS2PSignal::Timeout(ws2p_full_id) => {
-                            endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());
-                            ws2p_module.send_network_event(&NetworkEvent::ConnectionStateChange(
-                                ws2p_full_id,
-                                WS2PConnectionState::Close as u32,
-                                ws2p_module.uids_cache.get(&ws2p_full_id.1).cloned(),
-                                ws2p_module.ws2p_endpoints[&ws2p_full_id]
-                                    .0
-                                    .get_url(false, false)
-                                    .expect("Endpoint unreachable !"),
-                            ));
-                        }
-                        WS2PSignal::PeerCard(_ws2p_full_id, _peer_card, ws2p_endpoints) => {
-                            //trace!("WS2PSignal::PeerCard({})", ws2p_full_id);
-                            //ws2p_module.send_network_event(NetworkEvent::ReceivePeers(_));
-                            for ep in ws2p_endpoints {
-                                match ws2p_module.ws2p_endpoints.get(
-                                    &ep.node_full_id()
-                                        .expect("WS2P: Fail to get ep.node_full_id() !"),
-                                ) {
-                                    Some(_) => {}
-                                    None => {
-                                        if let Some(_api) =
-                                            ws2p_db::string_to_api(&ep.api.0.clone())
-                                        {
-                                            endpoints_to_update_status.insert(
-                                                ep.node_full_id().expect(
-                                                    "WS2P: Fail to get ep.node_full_id() !",
-                                                ),
-                                                SystemTime::now(),
-                                            );
-                                        }
-                                        if cfg!(feature = "ssl") || ep.port != 443 {
-                                            ws2p_module.connect_to(&ep);
-                                        }
-                                    }
-                                };
-                            }
-                        }
-                        WS2PSignal::Heads(ws2p_full_id, heads) => {
-                            trace!("WS2PSignal::Heads({}, {:?})", ws2p_full_id, heads.len());
-                            ws2p_module.send_dal_request(&BlockchainRequest::UIDs(
-                                heads.iter().map(NetworkHead::pubkey).collect(),
-                            ));
-                            ws2p_module.send_network_event(&NetworkEvent::ReceiveHeads(
-                                heads
-                                    .iter()
-                                    .map(|head| {
-                                        let mut new_head = head.clone();
-                                        if let Some(uid) =
-                                            ws2p_module.uids_cache.get(&head.pubkey())
-                                        {
-                                            new_head.set_uid(uid);
-                                        }
-                                        new_head
-                                    })
-                                    .collect(),
-                            ));
-                        }
-                        WS2PSignal::Document(ws2p_full_id, network_doc) => {
-                            trace!("WS2PSignal::Document({})", ws2p_full_id);
-                            ws2p_module.send_network_event(&NetworkEvent::ReceiveDocuments(vec![
-                                network_doc,
-                            ]));
-                        }
-                        WS2PSignal::ReqResponse(req_id, req, recipient_full_id, response) => {
-                            match req {
-                                OldNetworkRequest::GetCurrent(ref _req_id) => {
-                                    info!("WS2PSignal::ReceiveCurrent({}, {:?})", req_id.0, req);
-                                    if let Some(block) = parse_json_block(&response) {
-                                        ws2p_module.send_network_req_response(
-                                            req.get_req_full_id().0,
-                                            req.get_req_full_id().1,
-                                            NetworkResponse::CurrentBlock(
-                                                ModuleReqFullId(WS2PModule::name(), req_id),
-                                                recipient_full_id,
-                                                Box::new(block),
-                                            ),
-                                        );
-                                    }
-                                }
-                                OldNetworkRequest::GetBlocks(ref _req_id, count, from) => {
-                                    info!(
-                                        "WS2PSignal::ReceiveChunk({}, {} blocks from {})",
-                                        req_id.0, count, from
-                                    );
-                                    if response.is_array() {
-                                        let mut chunk = Vec::new();
-                                        for json_block in response.as_array().unwrap() {
-                                            if let Some(block) = parse_json_block(json_block) {
-                                                chunk.push(block);
-                                            } else {
-                                                warn!("WS2PModule: Error : fail to parse one json block !");
-                                            }
-                                        }
-                                        debug!("Send chunk to followers : {}", from);
-                                        ws2p_module.send_network_event(
-                                            &NetworkEvent::ReceiveBlocks(chunk),
-                                        );
-                                    }
-                                }
-                                OldNetworkRequest::GetRequirementsPending(_req_id, min_cert) => {
-                                    info!(
-                                        "WS2PSignal::ReceiveRequirementsPending({}, {})",
-                                        req_id.0, min_cert
-                                    );
-                                    debug!("----------------------------------------");
-                                    debug!("-      BEGIN IDENTITIES PENDING        -");
-                                    debug!("----------------------------------------");
-                                    debug!("{:#?}", response);
-                                    debug!("----------------------------------------");
-                                    debug!("-       END IDENTITIES PENDING         -");
-                                    debug!("----------------------------------------");
-                                }
-                                _ => {}
-                            }
-                        }
-                        WS2PSignal::Empty => {}
-                        _ => {}
-                    },
-                },
-                Err(e) => match e {
-                    mpsc::RecvTimeoutError::Disconnected => {
-                        panic!("Disconnected ws2p module !");
-                    }
-                    mpsc::RecvTimeoutError::Timeout => {}
-                },
-            }
-            if SystemTime::now()
-                .duration_since(last_ws2p_connections_print)
-                .unwrap()
-                > Duration::new(5, 0)
-            {
-                last_ws2p_connections_print = SystemTime::now();
-                let mut connected_nodes = Vec::new();
-                for (k, (_ep, state)) in ws2p_module.ws2p_endpoints.clone() {
-                    if let WS2PConnectionState::Established = state {
-                        connected_nodes.push(k);
-                    }
-                }
-                // Print current_blockstamp
-                info!(
-                    "WS2PModule : current_blockstamp() = {:?}",
-                    current_blockstamp
-                );
-                // New WS2P connection wave
-                if connected_nodes.len() < ws2p_module.conf.clone().outcoming_quota
-                    && (SystemTime::now()
-                        .duration_since(last_ws2p_connecting_wave)
-                        .unwrap()
-                        > Duration::new(*WS2P_OUTCOMING_INTERVAL, 0)
-                        || (SystemTime::now()
-                            .duration_since(last_ws2p_connecting_wave)
-                            .unwrap()
-                            > Duration::new(*WS2P_OUTCOMING_INTERVAL_AT_STARTUP, 0)
-                            && SystemTime::now().duration_since(start_time).unwrap()
-                                < Duration::new(*WS2P_OUTCOMING_INTERVAL, 0)))
-                {
-                    last_ws2p_connecting_wave = SystemTime::now();
-                    info!("Connected to know endpoints...");
-                    ws2p_module.connect_to_know_endpoints();
-                }
-                // Request pending_identities from network
-                if SystemTime::now()
-                    .duration_since(last_identities_request)
-                    .unwrap()
-                    > Duration::new(*PENDING_IDENTITIES_REQUEST_INTERVAL, 0)
-                    && SystemTime::now().duration_since(start_time).unwrap() > Duration::new(10, 0)
-                {
-                    /*info!("get pending_identities from all connections...");
-                    let _blocks_request_result = ws2p_module.send_request_to_all_connections(
-                        &OldNetworkRequest::GetRequirementsPending(ModuleReqId(0 as u32), 5),
-                    );*/
-                    last_identities_request = SystemTime::now();
-                }
-                // Write pending endpoints
-                for (ep_full_id, received_time) in endpoints_to_update_status.clone() {
-                    if SystemTime::now().duration_since(received_time).unwrap()
-                        > Duration::new(*DURATION_BEFORE_RECORDING_ENDPOINT, 0)
-                    {
-                        if let Some(&(ref ep, ref state)) =
-                            ws2p_module.ws2p_endpoints.get(&ep_full_id)
-                        {
-                            ws2p_db::write_endpoint(
-                                &db,
-                                &ep,
-                                state.to_u32(),
-                                SystemTime::now()
-                                    .duration_since(UNIX_EPOCH)
-                                    .unwrap()
-                                    .as_secs(),
-                            );
-                        }
-                        endpoints_to_update_status.remove(&ep_full_id);
-                    } else {
-                        info!(
-                            "Write {} endpoint in {} secs.",
-                            ep_full_id,
-                            *DURATION_BEFORE_RECORDING_ENDPOINT
-                                - SystemTime::now()
-                                    .duration_since(received_time)
-                                    .unwrap()
-                                    .as_secs()
-                        );
-                    }
-                }
-                // ..
-                // Request current blockstamp
-                ws2p_module.send_dal_request(&BlockchainRequest::CurrentBlockstamp());
-            }
-        }
-        Ok(())
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::parsers::blocks::parse_json_block;
-    use super::*;
-    use dubp_documents::documents::block::BlockDocument;
-    use duniter_module::DursModule;
-    use dup_crypto::keys::PublicKey;
-    use durs_network_documents::network_endpoint::NetworkEndpointApi;
-    use std::fs;
-    use std::path::PathBuf;
-    use std::time::{SystemTime, UNIX_EPOCH};
-
-    #[test]
-    fn test_parse_json_block() {
-        let json_block = json!({
-            "fork": false,
-            "version": 10,
-            "nonce": 10500000059239 as u64,
-            "number": 109966,
-            "powMin": 88,
-            "time": 1523300656,
-            "medianTime": 1523295259,
-            "membersCount": 933,
-            "monetaryMass": 146881563,
-            "unitbase": 0,
-            "issuersCount": 44,
-            "issuersFrame": 221,
-            "issuersFrameVar": 0,
-            "currency": "g1",
-            "issuer": "GRBPV3Y7PQnB9LaZhSGuS3BqBJbSHyibzYq65kTh1nQ4",
-            "signature": "GCg2Lti3TdxWlhA8JF8pRI+dRQ0XZVtcC4BqO/COTpjTQFdWG6qmUNVvdeYCtR/lu1JQe3N/IhrbyV6L/6I+Cg==",
-            "hash": "000000EF5B2AA849F4C3AF3D35E1284EA1F34A9F617EA806CE8371619023DC74",
-            "parameters": "",
-            "previousHash": "000004C00602F8A27AE078DE6351C0DDA1EA0974A78D2BEFA7DFBE7B7C3146FD",
-            "previousIssuer": "5SwfQubSat5SunNafCsunEGTY93nVM4kLSsuprNqQb6S",
-            "inner_hash": "61F02B1A6AE2E4B9A1FD66CE673258B4B21C0076795571EE3C9DC440DD06C46C",
-            "dividend": null,
-            "identities": [],
-            "joiners": [],
-            "actives": [],
-            "leavers": [],
-            "revoked": [],
-            "excluded": [],
-            "certifications": [
-                "Hm5qjaNuHogNRdGZ4vgnLA9DMZVUu5YWzVup5mubuxCc:8AmdBsimcLziXaCS4AcVUfPx7rkjeic7482dLbBkuZw6:109964:yHKBGMeuxyIqFb295gVNK6neRC+U0tmsX1Zed3TLjS3ZZHYYycE1piLcYsTKll4ifNVp6rm+hd/CLdHYB+29CA==",
-                "BncjgJeFpGsMCCsUfzNLEexjsbuX3V2mg9P67ov2LkwK:DyBUBNpzpfvjtwYYSaVMM6ST6t2DNg3NCE9CU9bRQFhF:105864:cJEGW9WxJwlMA2+4LNAK4YieyseUy1WIkFh1YLYD+JJtJEoCSnIQRXzhiAoRpGaj0bRz8sTpwI6PRkuVoDJJDQ=="
-            ],
-            "transactions": [
-                {
-                "version": 10,
-                "currency": "g1",
-                "locktime": 0,
-                "hash": "80FE1E83DC4D0B722CA5F8363EFC6A3E29071032EBB71C1E0DF8D4FEA589C698",
-                "blockstamp": "109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B",
-                "blockstampTime": 0,
-                "issuers": [
-                    "6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT"
-                ],
-                "inputs": [
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98284",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98519",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98779",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99054",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99326",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99599",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99884",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100174",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100469",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100746",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:101036",
-                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:101327"
-                ],
-                "outputs": [
-                    "12000:0:SIG(HmH5beJqKGMeotcQUrSW7Wo5tKvAksHmfYXfiSQ9EbWz)",
-                    "12:0:SIG(6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT)"
-                ],
-                "unlocks": [
-                    "0:SIG(0)",
-                    "1:SIG(0)",
-                    "2:SIG(0)",
-                    "3:SIG(0)",
-                    "4:SIG(0)",
-                    "5:SIG(0)",
-                    "6:SIG(0)",
-                    "7:SIG(0)",
-                    "8:SIG(0)",
-                    "9:SIG(0)",
-                    "10:SIG(0)",
-                    "11:SIG(0)"
-                ],
-                "signatures": [
-                    "MZxoKxYgwufh/s5mwLCsYEZXtIsP1hEKCyAzLipJsvCbR9xj7wXUw0C/ahwvZfBtR7+QVPIfLmwYEol1JcHjDw=="
-                ],
-                "comment": "Adhesion 2018"
-                },
-                {
-                "version": 10,
-                "currency": "g1",
-                "locktime": 0,
-                "hash": "B80507412B35BD5EB437AE0D3EB97E60E3A4974F5CDEA1AF7E2127C0E943481F",
-                "blockstamp": "109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B",
-                "blockstampTime": 0,
-                "issuers": [
-                    "8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU"
-                ],
-                "inputs": [
-                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:91560",
-                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:91850",
-                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92111",
-                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92385",
-                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92635"
-                ],
-                "outputs": [
-                    "5000:0:SIG(BzHnbec1Gov7dLSt1EzJS7vikoQCECeuvZs4wamZAcT1)",
-                    "5:0:SIG(8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU)"
-                ],
-                "unlocks": [
-                    "0:SIG(0)",
-                    "1:SIG(0)",
-                    "2:SIG(0)",
-                    "3:SIG(0)",
-                    "4:SIG(0)"
-                ],
-                "signatures": [
-                    "A+ukwRvLWs1gZQ0KAqAnknEgmRQHdrnOvNuBx/WZqje17BAPrVxSxKpqwU6MiajU+ppigsYp6Bu0FdPf/tGnCQ=="
-                ],
-                "comment": ""
-                },
-                {
-                "version": 10,
-                "currency": "g1",
-                "locktime": 0,
-                "hash": "D8970E6629C0381A78534EEDD86803E9215A7EC4C494BAEA79EB19425F9B4D31",
-                "blockstamp": "109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B",
-                "blockstampTime": 0,
-                "issuers": [
-                    "FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg"
-                ],
-                "inputs": [
-                    "1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:36597",
-                    "1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:36880",
-                    "1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:37082"
-                ],
-                "outputs": [
-                    "3000:0:SIG(BBC8Rnh4CWN1wBrPLevK7GRFFVDVw7Lu24YNMUmhqoHU)"
-                ],
-                "unlocks": [
-                    "0:SIG(0)",
-                    "1:SIG(0)",
-                    "2:SIG(0)"
-                ],
-                "signatures": [
-                    "OpiF/oQfIigOeAtsteukU0w9FPSELE+BVTxhmsQ8bEeYGlwovG2VF8ZFiJkLLPi6vFuKgwzULJfjNGd97twZCw=="
-                ],
-                "comment": "1 billet pour une seance.pour un chouette film"
-                }
-            ],
-        });
-        let mut block: BlockDocument =
-            parse_json_block(&json_block).expect("Fail to parse test json block !");
-        assert_eq!(
-            block
-                .inner_hash
-                .expect("Try to get inner_hash of an uncompleted or reduce block !")
-                .to_hex(),
-            "61F02B1A6AE2E4B9A1FD66CE673258B4B21C0076795571EE3C9DC440DD06C46C"
-        );
-        block.compute_hash();
-        assert_eq!(
-            block
-                .hash
-                .expect("Try to get hash of an uncompleted or reduce block !")
-                .0
-                .to_hex(),
-            "000000EF5B2AA849F4C3AF3D35E1284EA1F34A9F617EA806CE8371619023DC74"
-        );
-    }
-
-    #[test]
-    fn endpoint_db_tests() {
-        let test_db_path = PathBuf::from("test.db");
-        if test_db_path.as_path().exists() {
-            fs::remove_file(&test_db_path).unwrap();
-        }
-        let db = WS2PModuleDatas::open_db(&test_db_path).unwrap();
-
-        let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
-
-        let mut endpoint = EndpointV1::parse_from_raw(
-            "WS2P cb06a19b g1.imirhil.fr 53012 /",
-            PubKey::Ed25519(
-                ed25519::PublicKey::from_base58("5gJYnQp8v7bWwk7EWRoL8vCLof1r3y9c6VDdnGSM1GLv")
-                    .unwrap(),
-            ),
-            1,
-            current_time.as_secs(),
-        )
-        .expect("Failt to parse test endpoint !");
-
-        ws2p_db::write_endpoint(&db, &endpoint, 1, current_time.as_secs());
-        let mut written_endpoints =
-            ws2p_db::get_endpoints_for_api(&db, &NetworkEndpointApi(String::from("WS2P")));
-        assert_eq!(endpoint, written_endpoints.pop().unwrap());
-
-        // Test status update
-        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")));
-        assert_eq!(endpoint, written_endpoints.pop().unwrap());
-    }
-
-    #[test]
-    fn ws2p_requests() {
-        let module_id = WS2PModule::name();
-        let request =
-            OldNetworkRequest::GetBlocks(ModuleReqFullId(module_id, ModuleReqId(58)), 50, 0);
-        assert_eq!(
-            network_request_to_json(&request),
-            json!({
-                "reqId": format!("{:x}", 58),
-                "body": {
-                    "name": "BLOCKS_CHUNK",
-                    "params": {
-                        "count": 50,
-                        "fromNumber": 0
-                    }
-                }
-            })
-        );
-        assert_eq!(
-            network_request_to_json(&request).to_string(),
-            "{\"body\":{\"name\":\"BLOCKS_CHUNK\",\"params\":{\"count\":50,\"fromNumber\":0}},\"reqId\":\"3a\"}"
-        );
-    }
-
-    #[test]
-    fn ws2p_parse_head() {
-        let head = json!({
-            "message": "WS2POTMIC:HEAD:1:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3",
-            "sig": "trtK9GXvTdfND995ohWEderpO3NkIqi1X6mBeVvMcaHckq+lIGqjWvJ9t9Vccz5t+VGaSmGUihDl4q6eldIYBw==",
-            "messageV2": "WS2POTMIC:HEAD:2:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3:25:22",
-            "sigV2": "x6ehPMuYjGY+z7wEGnJGyMBxMKUdu01RWaF0b0XCtoVjg67cCvT4H0V/Qcxn4bAGqzy5ux2fA7NiI+81bBnqDw==",
-            "step": 0
-        });
-        let mut heads_count = 0;
-        if let Ok(head) = NetworkHead::from_json_value(&head) {
-            if let NetworkHead::V2(ref head_v2) = head {
-                heads_count += 1;
-                assert_eq!(
-                    head_v2.message.to_string(),
-                    String::from("WS2POTMIC:HEAD:1:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3")
-                );
-            }
-            assert_eq!(head.verify(), true);
-        } else {
-            panic!("Fail to parse head !")
-        }
-        assert_eq!(heads_count, 1);
-    }
-}
diff --git a/lib/modules/ws2p-v1-legacy/ack_message.rs b/lib/modules/ws2p-v1-legacy/src/ack_message.rs
similarity index 67%
rename from lib/modules/ws2p-v1-legacy/ack_message.rs
rename to lib/modules/ws2p-v1-legacy/src/ack_message.rs
index e1fcdeed37934ec8ed568e98d33bad0ea166e74e..f18bf55e7e389f748ddbf0bf6c286c78daea6712 100644
--- a/lib/modules/ws2p-v1-legacy/ack_message.rs
+++ b/lib/modules/ws2p-v1-legacy/src/ack_message.rs
@@ -1,4 +1,4 @@
-use super::WS2PMessage;
+use crate::*;
 use dup_crypto::keys::*;
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 
@@ -11,20 +11,19 @@ pub struct WS2PAckMessageV1 {
 }
 
 impl WS2PMessage for WS2PAckMessageV1 {
-    fn parse(v: &serde_json::Value, currency: String) -> Option<Self> {
+    fn parse(v: &serde_json::Value, currency: String) -> Result<Self, WS2PMsgParseErr> {
         let pubkey = match v.get("pub") {
-            Some(pubkey) => pubkey.as_str().unwrap().to_string(),
-            None => return None,
+            Some(pubkey) => pubkey.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),
+            None => return Err(WS2PMsgParseErr {}),
         };
         let signature = match v.get("sig") {
-            Some(signature) => signature.as_str().unwrap().to_string(),
-            None => return None,
+            Some(signature) => signature.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),
+            None => return Err(WS2PMsgParseErr {}),
         };
-        let pubkey = PubKey::Ed25519(ed25519::PublicKey::from_base58(&pubkey).unwrap());
-        let signature: Option<Sig> = Some(Sig::Ed25519(
-            ed25519::Signature::from_base64(&signature).unwrap(),
-        ));
-        Some(WS2PAckMessageV1 {
+        let pubkey = PubKey::Ed25519(ed25519::PublicKey::from_base58(&pubkey)?);
+        let signature: Option<Sig> =
+            Some(Sig::Ed25519(ed25519::Signature::from_base64(&signature)?));
+        Ok(WS2PAckMessageV1 {
             currency,
             pubkey,
             challenge: "".to_string(),
@@ -38,8 +37,11 @@ impl WS2PMessage for WS2PAckMessageV1 {
         )
     }
     fn verify(&self) -> bool {
-        self.pubkey
-            .verify(self.to_raw().as_bytes(), &self.signature.unwrap())
+        if let Some(sig) = self.signature {
+            self.pubkey.verify(self.to_raw().as_bytes(), &sig)
+        } else {
+            false
+        }
     }
 }
 
diff --git a/lib/modules/ws2p-v1-legacy/connect_message.rs b/lib/modules/ws2p-v1-legacy/src/connect_message.rs
similarity index 66%
rename from lib/modules/ws2p-v1-legacy/connect_message.rs
rename to lib/modules/ws2p-v1-legacy/src/connect_message.rs
index 1f917aded01c469b82019f7af394407ecf343d67..e229cc1def5b603c1ee14e61ec092e0079366620 100644
--- a/lib/modules/ws2p-v1-legacy/connect_message.rs
+++ b/lib/modules/ws2p-v1-legacy/src/connect_message.rs
@@ -1,4 +1,4 @@
-use super::WS2PMessage;
+use crate::*;
 use dup_crypto::keys::*;
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 
@@ -11,24 +11,22 @@ pub struct WS2PConnectMessageV1 {
 }
 
 impl WS2PMessage for WS2PConnectMessageV1 {
-    fn parse(v: &serde_json::Value, currency: String) -> Option<Self> {
+    fn parse(v: &serde_json::Value, currency: String) -> Result<Self, WS2PMsgParseErr> {
         let pubkey = match v.get("pub") {
-            Some(pubkey) => pubkey.as_str().unwrap().to_string(),
-            None => return None,
+            Some(pubkey) => pubkey.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),
+            None => return Err(WS2PMsgParseErr {}),
         };
         let challenge = match v.get("challenge") {
-            Some(challenge) => challenge.as_str().unwrap().to_string(),
-            None => return None,
+            Some(challenge) => challenge.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),
+            None => return Err(WS2PMsgParseErr {}),
         };
         let signature = match v.get("sig") {
-            Some(signature) => signature.as_str().unwrap().to_string(),
-            None => return None,
+            Some(signature) => signature.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),
+            None => return Err(WS2PMsgParseErr {}),
         };
-        let pubkey = PubKey::Ed25519(ed25519::PublicKey::from_base58(&pubkey).unwrap());
-        let signature = Some(Sig::Ed25519(
-            ed25519::Signature::from_base64(&signature).unwrap(),
-        ));
-        Some(WS2PConnectMessageV1 {
+        let pubkey = PubKey::Ed25519(ed25519::PublicKey::from_base58(&pubkey)?);
+        let signature = Some(Sig::Ed25519(ed25519::Signature::from_base64(&signature)?));
+        Ok(WS2PConnectMessageV1 {
             currency,
             pubkey,
             challenge,
@@ -42,8 +40,11 @@ impl WS2PMessage for WS2PConnectMessageV1 {
         )
     }
     fn verify(&self) -> bool {
-        self.pubkey
-            .verify(self.to_raw().as_bytes(), &self.signature.unwrap())
+        if let Some(sig) = self.signature {
+            self.pubkey.verify(self.to_raw().as_bytes(), &sig)
+        } else {
+            false
+        }
     }
 }
 
diff --git a/lib/modules/ws2p-v1-legacy/src/constants.rs b/lib/modules/ws2p-v1-legacy/src/constants.rs
new file mode 100644
index 0000000000000000000000000000000000000000..cdc14af73e1013607c3c924fc838e602d1cdb1a6
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/constants.rs
@@ -0,0 +1,55 @@
+//  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/>.
+
+//! WS2Pv1 constants
+
+/// API Name
+pub static WS2P_API: &'static str = "WS2P";
+
+/// Interval between to sequence of general actions
+pub static WS2P_GENERAL_STATE_INTERVAL: &'static u64 = &15;
+
+/// Connection wave interval at startup
+pub static WS2P_OUTCOMING_INTERVAL_AT_STARTUP: &'static u64 = &75;
+
+/// Interval of connection waves after the start-up phase
+pub static WS2P_OUTCOMING_INTERVAL: &'static u64 = &300;
+
+/// Default outgoing connection quota
+pub static WS2P_DEFAULT_OUTCOMING_QUOTA: &'static usize = &10;
+
+/// Maximum duration of a connection negotiation
+pub static WS2P_NEGOTIATION_TIMEOUT: &'static u64 = &15;
+
+/// Maximum waiting time for a response to a request
+//pub static WS2P_REQUEST_TIMEOUT : &'static u64 = &30;
+
+/// Maximum duration of inactivity of a connection (the connection will be closed after this delay)
+pub static WS2P_EXPIRE_TIMEOUT: &'static u64 = &120;
+
+/// Interval between 2 messages from which it''s perhaps a spam (in milliseconds)
+pub static WS2P_SPAM_INTERVAL_IN_MILLI_SECS: &'static u64 = &80;
+
+/// Number of consecutive closed messages from which messages will be considered as spam.
+pub static WS2P_SPAM_LIMIT: &'static usize = &6;
+
+/// Rest time in a situation of proven spam
+pub static WS2P_SPAM_SLEEP_TIME_IN_SEC: &'static u64 = &100;
+
+/// Duration between 2 endpoints saving
+pub static DURATION_BETWEEN_2_ENDPOINTS_SAVING: &'static u64 = &180;
+
+/// Duration between 2 requests from the pool of the wot data
+pub static PENDING_IDENTITIES_REQUEST_INTERVAL: &'static u64 = &40;
diff --git a/lib/modules/ws2p-v1-legacy/src/events/mod.rs b/lib/modules/ws2p-v1-legacy/src/events/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0e3731259de36e027add6106ac4b7d949cc746f4
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/events/mod.rs
@@ -0,0 +1,19 @@
+//  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/>.
+
+//! Sub-module managing the events received and sent.
+
+pub mod received;
+pub mod sent;
diff --git a/lib/modules/ws2p-v1-legacy/src/events/received.rs b/lib/modules/ws2p-v1-legacy/src/events/received.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a8bb4f2785f185b944d75cf63ffce24243bd8d20
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/events/received.rs
@@ -0,0 +1,72 @@
+//  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/>.
+
+//! Sub-module managing events received from other durs modules
+
+use crate::*;
+use dubp_documents::Document;
+use duniter_module::*;
+use durs_message::events::DursEvent;
+use std::ops::Deref;
+
+pub fn receive_event(
+    ws2p_module: &mut WS2PModule,
+    _event_type: ModuleEvent,
+    event_content: &DursEvent,
+) {
+    if let DursEvent::BlockchainEvent(ref bc_event) = *event_content {
+        match *bc_event.deref() {
+            BlockchainEvent::StackUpValidBlock(ref block) => {
+                ws2p_module.current_blockstamp = block.deref().blockstamp();
+                debug!(
+                    "WS2PModule : current_blockstamp = {}",
+                    ws2p_module.current_blockstamp
+                );
+                ws2p_module.my_head = Some(heads::generate_my_head(
+                    &ws2p_module.key_pair,
+                    ws2p_module.node_id,
+                    ws2p_module.soft_name,
+                    ws2p_module.soft_version,
+                    &ws2p_module.current_blockstamp,
+                    None,
+                ));
+                super::sent::send_network_event(
+                    ws2p_module,
+                    NetworkEvent::ReceiveHeads(vec![unwrap!(ws2p_module.my_head.clone())]),
+                );
+                // Send my head to all connections
+                let my_json_head = serializer::serialize_head(unwrap!(ws2p_module.my_head.clone()));
+                trace!("Send my HEAD: {:#?}", my_json_head);
+                let _results: Result<(), ws::Error> = ws2p_module
+                    .websockets
+                    .iter_mut()
+                    .map(|ws| {
+                        (ws.1).0.send(Message::text(
+                            json!({
+                                "name": "HEAD",
+                                "body": {
+                                    "heads": [my_json_head]
+                                }
+                            })
+                            .to_string(),
+                        ))
+                    })
+                    .collect();
+            }
+            BlockchainEvent::RevertBlocks(ref _blocks) => {}
+            _ => {}
+        }
+    }
+}
diff --git a/lib/modules/ws2p-v1-legacy/src/events/sent.rs b/lib/modules/ws2p-v1-legacy/src/events/sent.rs
new file mode 100644
index 0000000000000000000000000000000000000000..bb5a49b6b86efbd31a3df4fdca1cd3f14e7dfa80
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/events/sent.rs
@@ -0,0 +1,59 @@
+//  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/>.
+
+//! Sub-module managing the events emitted by the blockchain module.
+
+use crate::WS2PModule;
+use duniter_module::{ModuleEvent, RouterThreadMessage};
+use duniter_network::documents::BlockchainDocument;
+use duniter_network::events::NetworkEvent;
+use durs_message::events::DursEvent;
+use durs_message::*;
+
+pub fn send_network_events(ws2p_module: &mut WS2PModule, events: Vec<NetworkEvent>) {
+    for event in events {
+        send_network_event(ws2p_module, event);
+    }
+}
+
+pub fn send_network_event(ws2p_module: &mut WS2PModule, event: NetworkEvent) {
+    let module_event = match event {
+        NetworkEvent::ConnectionStateChange(_, _, _, _) => {
+            ModuleEvent::ConnectionsChangeNodeNetwork
+        }
+        NetworkEvent::ReceiveBlocks(_) => ModuleEvent::NewBlockFromNetwork,
+        NetworkEvent::ReceiveDocuments(ref network_docs) => {
+            if !network_docs.is_empty() {
+                match network_docs[0] {
+                    BlockchainDocument::Block(_) => ModuleEvent::NewBlockFromNetwork,
+                    BlockchainDocument::Transaction(_) => ModuleEvent::NewTxFromNetwork,
+                    _ => ModuleEvent::NewWotDocFromNetwork,
+                }
+            } else {
+                return;
+            }
+        }
+        NetworkEvent::ReceiveHeads(_) => ModuleEvent::NewValidHeadFromNetwork,
+        NetworkEvent::ReceivePeers(_) => ModuleEvent::NewValidPeerFromNodeNetwork,
+        NetworkEvent::NewSelfPeer(_) => ModuleEvent::NewSelfPeer,
+    };
+    ws2p_module
+        .router_sender
+        .send(RouterThreadMessage::ModuleMessage(DursMsg::Event {
+            event_type: module_event,
+            event_content: DursEvent::NetworkEvent(event),
+        }))
+        .expect("Fail to send network event to router !");
+}
diff --git a/lib/modules/ws2p-v1-legacy/heads.rs b/lib/modules/ws2p-v1-legacy/src/heads.rs
similarity index 100%
rename from lib/modules/ws2p-v1-legacy/heads.rs
rename to lib/modules/ws2p-v1-legacy/src/heads.rs
diff --git a/lib/modules/ws2p-v1-legacy/src/lib.rs b/lib/modules/ws2p-v1-legacy/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c19854b3f2a4dc6cd1960ff93bcd693f1bc69565
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/lib.rs
@@ -0,0 +1,1075 @@
+//  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/>.
+
+//! WebSocketToPeer API for the Durs project.
+
+#![deny(
+    missing_debug_implementations,
+    missing_copy_implementations,
+    trivial_casts,
+    unsafe_code,
+    unstable_features,
+    unused_import_braces,
+    unused_qualifications
+)]
+#![recursion_limit = "256"]
+
+#[macro_use]
+extern crate log;
+#[macro_use]
+extern crate serde_json;
+#[macro_use]
+extern crate structopt;
+
+mod ack_message;
+mod connect_message;
+pub mod constants;
+mod events;
+mod heads;
+mod ok_message;
+pub mod parsers;
+mod requests;
+mod responses;
+pub mod serializer;
+pub mod ws2p_db;
+pub mod ws_connections;
+
+use crate::ack_message::WS2PAckMessageV1;
+use crate::connect_message::WS2PConnectMessageV1;
+use crate::constants::*;
+use crate::ok_message::WS2POkMessageV1;
+use crate::parsers::blocks::parse_json_block;
+use crate::requests::sent::send_dal_request;
+use crate::ws2p_db::DbEndpoint;
+use crate::ws_connections::messages::WS2PConnectionMessage;
+use crate::ws_connections::states::WS2PConnectionState;
+use crate::ws_connections::*;
+use dubp_documents::Blockstamp;
+use duniter_conf::DuRsConf;
+use duniter_module::*;
+use duniter_network::cli::sync::SyncOpt;
+use duniter_network::documents::*;
+use duniter_network::events::*;
+use duniter_network::requests::*;
+use duniter_network::*;
+use dup_crypto::keys::*;
+use durs_common_tools::fatal_error;
+use durs_message::events::*;
+use durs_message::requests::*;
+use durs_message::responses::*;
+use durs_message::*;
+use durs_network_documents::network_endpoint::*;
+use durs_network_documents::network_head::*;
+use durs_network_documents::*;
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+use std::fs;
+use std::ops::Deref;
+use std::path::PathBuf;
+use std::sync::mpsc;
+use std::thread;
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+use unwrap::unwrap;
+use ws::Message;
+
+#[inline]
+#[cfg(not(feature = "ssl"))]
+pub fn ssl() -> bool {
+    false
+}
+#[inline]
+#[cfg(feature = "ssl")]
+pub fn ssl() -> bool {
+    true
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
+/// WS2P Configuration
+pub struct WS2PConf {
+    /// Limit of outcoming connections
+    pub outcoming_quota: usize,
+    /// Default WS2P endpoints provides by configuration file
+    pub sync_endpoints: Vec<EndpointV1>,
+}
+
+impl Default for WS2PConf {
+    fn default() -> Self {
+        WS2PConf {
+            outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA,
+            sync_endpoints: vec![
+                unwrap!(EndpointV1::parse_from_raw(
+                    "WS2P c1c39a0a ts.g1.librelois.fr 443 /ws2p",
+                    PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                        "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx",
+                    )),),
+                    0,
+                    0,
+                )),
+                unwrap!(EndpointV1::parse_from_raw(
+                    "WS2P fb17fcd4 g1.duniter.fr 443 /ws2p",
+                    PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                        "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE",
+                    ))),
+                    0,
+                    0,
+                )),
+                unwrap!(EndpointV1::parse_from_raw(
+                    "WS2P 7b33becd g1.nordstrom.duniter.org 443 /ws2p",
+                    PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                        "DWoSCRLQyQ48dLxUGr1MDKg4NFcbPbC56LN2hJjCCPpZ",
+                    ))),
+                    0,
+                    0,
+                )),
+                unwrap!(EndpointV1::parse_from_raw(
+                    "WS2P dff60418 duniter.normandie-libre.fr 443 /ws2p",
+                    PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                        "8t6Di3pLxxoTEfjXHjF49pNpjSTXuGEQ6BpkT75CkNb2",
+                    ))),
+                    0,
+                    0,
+                )),
+            ],
+        }
+    }
+}
+
+#[derive(Debug)]
+/// Store a Signal receive from network (after message treatment)
+pub enum WS2PSignal {
+    /// Receive a websocket error from a connextion. `NodeFullId` store the identifier of connection.
+    WSError(NodeFullId),
+    /// A new connection is successfully established with `NodeFullId`.
+    ConnectionEstablished(NodeFullId),
+    NegociationTimeout(NodeFullId),
+    Timeout(NodeFullId),
+    DalRequest(NodeFullId, ModuleReqId, serde_json::Value),
+    PeerCard(NodeFullId, serde_json::Value, Vec<EndpointV1>),
+    Heads(NodeFullId, Vec<NetworkHead>),
+    Document(NodeFullId, BlockchainDocument),
+    ReqResponse(
+        ModuleReqId,
+        OldNetworkRequest,
+        NodeFullId,
+        serde_json::Value,
+    ),
+    Empty,
+    NoConnection,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum NetworkConsensusError {
+    InsufficientData(usize),
+    Fork,
+}
+
+#[derive(Debug)]
+pub enum SendRequestError {
+    RequestTypeMustNotBeTransmitted(),
+    WSError(usize, Vec<ws::Error>),
+}
+
+#[derive(Debug)]
+pub struct WS2PModule {
+    pub conf: WS2PConf,
+    pub count_dal_requests: u32,
+    pub currency: Option<String>,
+    pub current_blockstamp: Blockstamp,
+    pub ep_file_path: PathBuf,
+    pub heads_cache: HashMap<NodeFullId, NetworkHead>,
+    pub key_pair: KeyPairEnum,
+    pub main_thread_channel: (
+        mpsc::Sender<WS2PThreadSignal>,
+        mpsc::Receiver<WS2PThreadSignal>,
+    ),
+    pub my_head: Option<NetworkHead>,
+    pub next_receiver: usize,
+    pub node_id: NodeId,
+    pub requests_awaiting_response:
+        HashMap<ModuleReqId, (OldNetworkRequest, NodeFullId, SystemTime)>,
+    pub router_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
+    pub soft_name: &'static str,
+    pub soft_version: &'static str,
+    pub ssl: bool,
+    pub websockets: HashMap<NodeFullId, WsSender>,
+    pub ws2p_endpoints: HashMap<NodeFullId, DbEndpoint>,
+    pub uids_cache: HashMap<PubKey, String>,
+}
+
+impl WS2PModule {
+    pub fn new(
+        soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
+        conf: WS2PConf,
+        ep_file_path: PathBuf,
+        key_pair: KeyPairEnum,
+        router_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
+    ) -> WS2PModule {
+        WS2PModule {
+            router_sender,
+            key_pair,
+            currency: None,
+            current_blockstamp: Blockstamp::default(),
+            conf,
+            ep_file_path,
+            soft_name: soft_meta_datas.soft_name,
+            soft_version: soft_meta_datas.soft_version,
+            ssl: ssl(),
+            node_id: NodeId(soft_meta_datas.conf.my_node_id()),
+            main_thread_channel: mpsc::channel(),
+            next_receiver: 0,
+            ws2p_endpoints: HashMap::new(),
+            websockets: HashMap::new(),
+            requests_awaiting_response: HashMap::new(),
+            heads_cache: HashMap::new(),
+            my_head: None,
+            uids_cache: HashMap::new(),
+            count_dal_requests: 0,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub enum WS2PThreadSignal {
+    DursMsg(Box<DursMsg>),
+    WS2PConnectionMessage(WS2PConnectionMessage),
+}
+
+#[derive(Copy, Clone, Debug)]
+/// Error when parsing WS2P message
+pub struct WS2PMsgParseErr {}
+
+impl From<dup_crypto::bases::BaseConvertionError> for WS2PMsgParseErr {
+    fn from(_: dup_crypto::bases::BaseConvertionError) -> Self {
+        WS2PMsgParseErr {}
+    }
+}
+
+pub trait WS2PMessage: Sized {
+    fn parse(v: &serde_json::Value, currency: String) -> Result<Self, WS2PMsgParseErr>;
+    fn to_raw(&self) -> String;
+    fn sign(&self, key_pair: KeyPairEnum) -> Sig {
+        key_pair.sign(self.to_raw().as_bytes())
+    }
+    fn verify(&self) -> bool;
+    //fn parse_and_verify(v: serde_json::Value, currency: String) -> bool;
+}
+
+#[derive(Debug)]
+/// WS2PFeaturesParseError
+pub enum WS2PFeaturesParseError {
+    /// UnknowApiFeature
+    UnknowApiFeature(String),
+}
+
+impl ApiModule<DuRsConf, DursMsg> for WS2PModule {
+    type ParseErr = WS2PFeaturesParseError;
+    /// Parse raw api features
+    fn parse_raw_api_features(str_features: &str) -> Result<ApiFeatures, Self::ParseErr> {
+        let str_features: Vec<&str> = str_features.split(' ').collect();
+        let mut api_features = Vec::with_capacity(0);
+        for str_feature in str_features {
+            match str_feature {
+                "DEF" => api_features[0] += 1u8,
+                "LOW" => api_features[0] += 2u8,
+                "ABF" => api_features[0] += 4u8,
+                _ => {
+                    return Err(WS2PFeaturesParseError::UnknowApiFeature(String::from(
+                        str_feature,
+                    )));
+                }
+            }
+        }
+        Ok(ApiFeatures(api_features))
+    }
+}
+
+impl NetworkModule<DuRsConf, DursMsg> for WS2PModule {
+    fn sync(
+        _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
+        _keys: RequiredKeysContent,
+        _conf: WS2PConf,
+        _main_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
+        _sync_params: SyncOpt,
+    ) -> Result<(), ModuleInitError> {
+        println!("Downlaod blockchain from network...");
+        println!("Error : not yet implemented !");
+        Ok(())
+    }
+}
+
+#[derive(StructOpt, Debug, Copy, Clone)]
+#[structopt(
+    name = "ws2p",
+    raw(setting = "structopt::clap::AppSettings::ColoredHelp")
+)]
+/// WS2Pv1 subcommand options
+pub struct WS2POpt {}
+
+impl DursModule<DuRsConf, DursMsg> for WS2PModule {
+    type ModuleConf = WS2PConf;
+    type ModuleOpt = WS2POpt;
+
+    fn name() -> ModuleStaticName {
+        ModuleStaticName("ws2p")
+    }
+    fn priority() -> ModulePriority {
+        ModulePriority::Essential()
+    }
+    fn ask_required_keys() -> RequiredKeys {
+        RequiredKeys::NetworkKeyPair()
+    }
+    fn have_subcommand() -> bool {
+        true
+    }
+    fn exec_subcommand(
+        _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
+        _keys: RequiredKeysContent,
+        _module_conf: Self::ModuleConf,
+        _subcommand_args: WS2POpt,
+    ) {
+        println!("Succesfully exec ws2p subcommand !")
+    }
+    fn start(
+        soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
+        keys: RequiredKeysContent,
+        conf: WS2PConf,
+        router_sender: mpsc::Sender<RouterThreadMessage<DursMsg>>,
+        load_conf_only: bool,
+    ) -> Result<(), ModuleInitError> {
+        // Get start time
+        let start_time = SystemTime::now();
+
+        // Get key_pair
+        let key_pair = if let RequiredKeysContent::NetworkKeyPair(key_pair) = keys {
+            key_pair
+        } else {
+            return Err(ModuleInitError::FailToLoadConf(
+                "WS2PModule fatal error at load_conf() : keys != NetworkKeyPair",
+            ));
+        };
+
+        // load conf
+        let mut ws2p_endpoints = HashMap::new();
+        for ep in &conf.sync_endpoints {
+            info!("Load sync endpoint {}", ep.raw_endpoint);
+            let node_full_id = ep
+                .node_full_id()
+                .expect("Fail to get endpoint node_full_id");
+            ws2p_endpoints.insert(
+                node_full_id,
+                DbEndpoint {
+                    ep: ep.clone(),
+                    state: WS2PConnectionState::Close,
+                    last_check: 0,
+                },
+            );
+        }
+
+        // Get endpoints file path
+        let mut ep_file_path =
+            duniter_conf::datas_path(&soft_meta_datas.profile, &soft_meta_datas.conf.currency());
+        ep_file_path.push("ws2pv1");
+        if !ep_file_path.exists() {
+            fs::create_dir(ep_file_path.as_path()).expect("Impossible to create ws2pv1 dir !");
+        }
+        ep_file_path.push("endpoints.bin");
+
+        // Define WS2PModule
+        let mut ws2p_module = WS2PModule::new(
+            soft_meta_datas,
+            conf,
+            ep_file_path.clone(),
+            key_pair,
+            router_sender.clone(),
+        );
+        ws2p_module.currency = Some(soft_meta_datas.conf.currency().to_string());
+        ws2p_module.ws2p_endpoints = ws2p_endpoints;
+
+        // Create ws2p main thread channel
+        let ws2p_sender_clone = ws2p_module.main_thread_channel.0.clone();
+
+        // Get ws2p endpoints in file
+        info!("TMP: WS2P SSL={}", ssl());
+        let count;
+        match ws2p_db::get_endpoints(&ep_file_path) {
+            Ok(ws2p_enpoints) => {
+                let ws2p_enpoints = ws2p_enpoints
+                    .into_iter()
+                    .filter(|(_, dal_ep)| cfg!(feature = "ssl") || dal_ep.ep.port != 443)
+                    .collect::<Vec<(NodeFullId, DbEndpoint)>>();
+                count = ws2p_enpoints.len();
+                ws2p_module.ws2p_endpoints.extend(ws2p_enpoints);
+            }
+            Err(err) => fatal_error!("WS2Pv1: fail to load endpoints from DB: {:?}", err),
+        }
+        info!("Load {} endpoints from DB !", count);
+
+        // Stop here in load_conf_only mode
+        if load_conf_only {
+            return Ok(());
+        }
+
+        // Create proxy channel
+        let (proxy_sender, proxy_receiver): (mpsc::Sender<DursMsg>, mpsc::Receiver<DursMsg>) =
+            mpsc::channel();
+        let proxy_sender_clone = proxy_sender.clone();
+
+        // Launch a proxy thread that transform DursMsg to WS2PThreadSignal(DursMsg)
+        thread::spawn(move || {
+            // Send proxy sender to main
+            router_sender
+                .send(RouterThreadMessage::ModuleRegistration(
+                    WS2PModule::name(),
+                    proxy_sender_clone,
+                    vec![ModuleRole::InterNodesNetwork],
+                    vec![
+                        ModuleEvent::NewValidBlock,
+                        ModuleEvent::NewWotDocInPool,
+                        ModuleEvent::NewTxinPool,
+                    ],
+                    vec![],
+                    vec![],
+                ))
+                .expect("Fatal error : ws2p module fail to send is sender channel !");
+            debug!("Send ws2p sender to main thread.");
+            loop {
+                match proxy_receiver.recv() {
+                    Ok(message) => {
+                        let stop = if let DursMsg::Stop = message {
+                            true
+                        } else {
+                            false
+                        };
+                        ws2p_sender_clone
+                            .send(WS2PThreadSignal::DursMsg(Box::new(message)))
+                            .expect(
+                                "Fatal error : fail to relay DursMsgContent to ws2p main thread !",
+                            );
+                        if stop {
+                            break;
+                        };
+                    }
+                    Err(e) => panic!(format!("{}", e)),
+                }
+            }
+        });
+
+        // Request current blockstamp
+        send_dal_request(&mut ws2p_module, &BlockchainRequest::CurrentBlockstamp());
+
+        // Start
+        connect_to_know_endpoints(&mut ws2p_module);
+        ws2p_module.main_loop(start_time, soft_meta_datas);
+
+        Ok(())
+    }
+}
+
+impl WS2PModule {
+    fn main_loop(mut self, start_time: SystemTime, soft_meta_datas: &SoftwareMetaDatas<DuRsConf>) {
+        // Initialize variables
+        let key_pair = self.key_pair;
+        let mut last_ws2p_connecting_wave = SystemTime::now();
+        let mut last_ws2p_state_print = SystemTime::now();
+        let mut last_ws2p_endpoints_write = SystemTime::now();
+        let mut endpoints_to_update_status: HashMap<NodeFullId, SystemTime> = HashMap::new();
+        let mut last_identities_request = UNIX_EPOCH;
+
+        loop {
+            match self
+                .main_thread_channel
+                .1
+                .recv_timeout(Duration::from_millis(200))
+            {
+                Ok(message) => match message {
+                    WS2PThreadSignal::DursMsg(ref durs_mesage) => {
+                        match *durs_mesage.deref() {
+                            DursMsg::Stop => break,
+                            DursMsg::Request {
+                                ref req_content, ..
+                            } => requests::received::receive_req(&mut self, req_content),
+                            DursMsg::Event {
+                                ref event_type,
+                                ref event_content,
+                                ..
+                            } => events::received::receive_event(
+                                &mut self,
+                                *event_type,
+                                event_content,
+                            ),
+                            DursMsg::Response {
+                                ref res_content, ..
+                            } => {
+                                if let DursResContent::BlockchainResponse(ref bc_res) = *res_content
+                                {
+                                    match *bc_res.deref() {
+                                        BlockchainResponse::CurrentBlockstamp(
+                                            ref _requester_id,
+                                            ref current_blockstamp_,
+                                        ) => {
+                                            debug!(
+                                                "WS2PModule : receive DALResBc::CurrentBlockstamp({})",
+                                                self.current_blockstamp
+                                            );
+                                            self.current_blockstamp = *current_blockstamp_;
+                                            if self.my_head.is_none() {
+                                                self.my_head = Some(heads::generate_my_head(
+                                                    &key_pair,
+                                                    NodeId(soft_meta_datas.conf.my_node_id()),
+                                                    soft_meta_datas.soft_name,
+                                                    soft_meta_datas.soft_version,
+                                                    &self.current_blockstamp,
+                                                    None,
+                                                ));
+                                            }
+                                            let event =
+                                                NetworkEvent::ReceiveHeads(vec![unwrap!(self
+                                                    .my_head
+                                                    .clone())]);
+                                            events::sent::send_network_event(&mut self, event);
+                                        }
+                                        BlockchainResponse::UIDs(ref _req_id, ref uids) => {
+                                            // Add uids to heads
+                                            for head in self.heads_cache.values_mut() {
+                                                if let Some(uid_option) = uids.get(&head.pubkey()) {
+                                                    if let Some(ref uid) = *uid_option {
+                                                        head.set_uid(uid);
+                                                        self.uids_cache
+                                                            .insert(head.pubkey(), uid.to_string());
+                                                    } else {
+                                                        self.uids_cache.remove(&head.pubkey());
+                                                    }
+                                                }
+                                            }
+                                            // Resent heads to other modules
+                                            let event = NetworkEvent::ReceiveHeads(
+                                                self.heads_cache.values().cloned().collect(),
+                                            );
+                                            events::sent::send_network_event(&mut self, event);
+                                            // Resent to other modules connections that match receive uids
+                                            let events = self.ws2p_endpoints
+                                                .iter()
+                                                .filter_map(|(node_full_id, DbEndpoint { ep, state, .. })| {
+                                                    if let Some(uid_option) = uids.get(&node_full_id.1) {
+                                                        Some(NetworkEvent::ConnectionStateChange(
+                                                            *node_full_id,
+                                                            *state as u32,
+                                                            uid_option.clone(),
+                                                            ep.get_url(false, false)
+                                                                .expect("Endpoint unreachable !"),
+                                                        ))
+                                                    } else {
+                                                        None
+                                                    }
+                                                })
+                                                .collect();
+                                            events::sent::send_network_events(&mut self, events);
+                                        }
+                                        _ => {} // Others BlockchainResponse variants
+                                    }
+                                }
+                            }
+                            _ => {} // Others DursMsg variants
+                        }
+                    }
+                    WS2PThreadSignal::WS2PConnectionMessage(ws2p_conn_message) => {
+                        match crate::ws_connections::messages::ws2p_conn_message_pretreatment(
+                            &mut self,
+                            ws2p_conn_message,
+                        ) {
+                            WS2PSignal::NoConnection => {
+                                warn!("WS2PSignal::NoConnection");
+                            }
+                            WS2PSignal::ConnectionEstablished(ws2p_full_id) => {
+                                let req_id =
+                                    ModuleReqId(self.requests_awaiting_response.len() as u32);
+                                let module_id = WS2PModule::name();
+                                debug!("WS2P: send req to: ({:?})", ws2p_full_id);
+                                let _current_request_result =
+                                    ws_connections::requests::sent::send_request_to_specific_node(
+                                        &mut self,
+                                        &ws2p_full_id,
+                                        &OldNetworkRequest::GetCurrent(ModuleReqFullId(
+                                            module_id, req_id,
+                                        )),
+                                    );
+                                if self.uids_cache.get(&ws2p_full_id.1).is_none() {
+                                    send_dal_request(
+                                        &mut self,
+                                        &BlockchainRequest::UIDs(vec![ws2p_full_id.1]),
+                                    );
+                                }
+                                let event = NetworkEvent::ConnectionStateChange(
+                                    ws2p_full_id,
+                                    WS2PConnectionState::Established as u32,
+                                    self.uids_cache.get(&ws2p_full_id.1).cloned(),
+                                    self.ws2p_endpoints[&ws2p_full_id]
+                                        .ep
+                                        .get_url(false, false)
+                                        .expect("Endpoint unreachable !"),
+                                );
+                                events::sent::send_network_event(&mut self, event);
+                            }
+                            WS2PSignal::WSError(ws2p_full_id) => {
+                                endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());
+                                close_connection(
+                                    &mut self,
+                                    &ws2p_full_id,
+                                    WS2PCloseConnectionReason::WsError,
+                                );
+                                let event = NetworkEvent::ConnectionStateChange(
+                                    ws2p_full_id,
+                                    WS2PConnectionState::WSError as u32,
+                                    self.uids_cache.get(&ws2p_full_id.1).cloned(),
+                                    self.ws2p_endpoints[&ws2p_full_id]
+                                        .ep
+                                        .get_url(false, false)
+                                        .expect("Endpoint unreachable !"),
+                                );
+                                events::sent::send_network_event(&mut self, event);
+                            }
+                            WS2PSignal::NegociationTimeout(ws2p_full_id) => {
+                                endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());
+                                let event = NetworkEvent::ConnectionStateChange(
+                                    ws2p_full_id,
+                                    WS2PConnectionState::Denial as u32,
+                                    self.uids_cache.get(&ws2p_full_id.1).cloned(),
+                                    self.ws2p_endpoints[&ws2p_full_id]
+                                        .ep
+                                        .get_url(false, false)
+                                        .expect("Endpoint unreachable !"),
+                                );
+                                events::sent::send_network_event(&mut self, event);
+                            }
+                            WS2PSignal::Timeout(ws2p_full_id) => {
+                                endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());
+                                let event = NetworkEvent::ConnectionStateChange(
+                                    ws2p_full_id,
+                                    WS2PConnectionState::Close as u32,
+                                    self.uids_cache.get(&ws2p_full_id.1).cloned(),
+                                    self.ws2p_endpoints[&ws2p_full_id]
+                                        .ep
+                                        .get_url(false, false)
+                                        .expect("Endpoint unreachable !"),
+                                );
+                                events::sent::send_network_event(&mut self, event);
+                            }
+                            WS2PSignal::PeerCard(_ws2p_full_id, _peer_card, ws2p_endpoints) => {
+                                //trace!("WS2PSignal::PeerCard({})", ws2p_full_id);
+                                //self.send_network_event(NetworkEvent::ReceivePeers(_));
+                                for ep in ws2p_endpoints {
+                                    match self.ws2p_endpoints.get(
+                                        &ep.node_full_id()
+                                            .expect("WS2P: Fail to get ep.node_full_id() !"),
+                                    ) {
+                                        Some(_) => {}
+                                        None => {
+                                            if let Some(_api) =
+                                                ws2p_db::string_to_api(&ep.api.0.clone())
+                                            {
+                                                endpoints_to_update_status.insert(
+                                                    ep.node_full_id().expect(
+                                                        "WS2P: Fail to get ep.node_full_id() !",
+                                                    ),
+                                                    SystemTime::now(),
+                                                );
+                                            }
+                                            if cfg!(feature = "ssl") || ep.port != 443 {
+                                                connect_to(&mut self, &ep);
+                                            }
+                                        }
+                                    };
+                                }
+                            }
+                            WS2PSignal::Heads(ws2p_full_id, heads) => {
+                                trace!("WS2PSignal::Heads({}, {:?})", ws2p_full_id, heads.len());
+                                send_dal_request(
+                                    &mut self,
+                                    &BlockchainRequest::UIDs(
+                                        heads.iter().map(NetworkHead::pubkey).collect(),
+                                    ),
+                                );
+                                let event = NetworkEvent::ReceiveHeads(
+                                    heads
+                                        .iter()
+                                        .map(|head| {
+                                            let mut new_head = head.clone();
+                                            if let Some(uid) = self.uids_cache.get(&head.pubkey()) {
+                                                new_head.set_uid(uid);
+                                            }
+                                            new_head
+                                        })
+                                        .collect(),
+                                );
+                                events::sent::send_network_event(&mut self, event);
+                            }
+                            WS2PSignal::Document(ws2p_full_id, network_doc) => {
+                                trace!("WS2PSignal::Document({})", ws2p_full_id);
+                                events::sent::send_network_event(
+                                    &mut self,
+                                    NetworkEvent::ReceiveDocuments(vec![network_doc]),
+                                );
+                            }
+                            WS2PSignal::ReqResponse(req_id, req, recipient_full_id, response) => {
+                                match req {
+                                    OldNetworkRequest::GetCurrent(ref _req_id) => {
+                                        info!(
+                                            "WS2PSignal::ReceiveCurrent({}, {:?})",
+                                            req_id.0, req
+                                        );
+                                        if let Some(block) = parse_json_block(&response) {
+                                            crate::responses::sent::send_network_req_response(
+                                                &self,
+                                                req.get_req_full_id().0,
+                                                req.get_req_full_id().1,
+                                                NetworkResponse::CurrentBlock(
+                                                    ModuleReqFullId(WS2PModule::name(), req_id),
+                                                    recipient_full_id,
+                                                    Box::new(block),
+                                                ),
+                                            );
+                                        }
+                                    }
+                                    OldNetworkRequest::GetBlocks(ref _req_id, count, from) => {
+                                        info!(
+                                            "WS2PSignal::ReceiveChunk({}, {} blocks from {})",
+                                            req_id.0, count, from
+                                        );
+                                        if response.is_array() {
+                                            let mut chunk = Vec::new();
+                                            for json_block in unwrap!(response.as_array()) {
+                                                if let Some(block) = parse_json_block(json_block) {
+                                                    chunk.push(block);
+                                                } else {
+                                                    warn!("WS2PModule: Error : fail to parse one json block !");
+                                                }
+                                            }
+                                            debug!("Send chunk to followers : {}", from);
+                                            events::sent::send_network_event(
+                                                &mut self,
+                                                NetworkEvent::ReceiveBlocks(chunk),
+                                            );
+                                        }
+                                    }
+                                    OldNetworkRequest::GetRequirementsPending(
+                                        _req_id,
+                                        min_cert,
+                                    ) => {
+                                        info!(
+                                            "WS2PSignal::ReceiveRequirementsPending({}, {})",
+                                            req_id.0, min_cert
+                                        );
+                                        debug!("----------------------------------------");
+                                        debug!("-      BEGIN IDENTITIES PENDING        -");
+                                        debug!("----------------------------------------");
+                                        debug!("{:#?}", response);
+                                        debug!("----------------------------------------");
+                                        debug!("-       END IDENTITIES PENDING         -");
+                                        debug!("----------------------------------------");
+                                    }
+                                    _ => {}
+                                }
+                            }
+                            WS2PSignal::Empty => {}
+                            _ => {}
+                        }
+                    }
+                },
+                Err(e) => match e {
+                    mpsc::RecvTimeoutError::Disconnected => {
+                        panic!("Disconnected ws2p module !");
+                    }
+                    mpsc::RecvTimeoutError::Timeout => {}
+                },
+            }
+            if unwrap!(SystemTime::now().duration_since(last_ws2p_endpoints_write))
+                > Duration::new(*DURATION_BETWEEN_2_ENDPOINTS_SAVING, 0)
+            {
+                last_ws2p_endpoints_write = SystemTime::now();
+                if let Err(err) = ws2p_db::write_endpoints(&self.ep_file_path, &self.ws2p_endpoints)
+                {
+                    fatal_error!("WS2P1: Fail to write endpoints in DB : {:?}", err);
+                }
+            }
+            if unwrap!(SystemTime::now().duration_since(last_ws2p_state_print))
+                > Duration::new(*WS2P_GENERAL_STATE_INTERVAL, 0)
+            {
+                last_ws2p_state_print = SystemTime::now();
+                let mut connected_nodes = Vec::new();
+                for (k, DbEndpoint { state, .. }) in self.ws2p_endpoints.clone() {
+                    if let WS2PConnectionState::Established = state {
+                        connected_nodes.push(k);
+                    }
+                }
+                // Print current_blockstamp
+                info!(
+                    "WS2PModule : current_blockstamp() = {:?}",
+                    self.current_blockstamp
+                );
+                // New WS2P connection wave
+                if connected_nodes.len() < self.conf.clone().outcoming_quota
+                    && (unwrap!(SystemTime::now().duration_since(last_ws2p_connecting_wave))
+                        > Duration::new(*WS2P_OUTCOMING_INTERVAL, 0)
+                        || (unwrap!(SystemTime::now().duration_since(last_ws2p_connecting_wave))
+                            > Duration::new(*WS2P_OUTCOMING_INTERVAL_AT_STARTUP, 0)
+                            && unwrap!(SystemTime::now().duration_since(start_time))
+                                < Duration::new(*WS2P_OUTCOMING_INTERVAL, 0)))
+                {
+                    last_ws2p_connecting_wave = SystemTime::now();
+                    info!("Connected to know endpoints...");
+                    connect_to_know_endpoints(&mut self);
+                }
+                // Request pending_identities from network
+                if unwrap!(SystemTime::now().duration_since(last_identities_request))
+                    > Duration::new(*PENDING_IDENTITIES_REQUEST_INTERVAL, 0)
+                    && unwrap!(SystemTime::now().duration_since(start_time)) > Duration::new(10, 0)
+                {
+                    /*info!("get pending_identities from all connections...");
+                    let _blocks_request_result = self.send_request_to_all_connections(
+                        &OldNetworkRequest::GetRequirementsPending(ModuleReqId(0 as u32), 5),
+                    );*/
+                    last_identities_request = SystemTime::now();
+                }
+                // ..
+                // Request current blockstamp
+                send_dal_request(&mut self, &BlockchainRequest::CurrentBlockstamp());
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::parsers::blocks::parse_json_block;
+    use super::*;
+    use crate::ws_connections::requests::sent::network_request_to_json;
+    use dubp_documents::documents::block::BlockDocument;
+    use duniter_module::DursModule;
+
+    #[test]
+    fn test_parse_json_block() {
+        let json_block = json!({
+            "fork": false,
+            "version": 10,
+            "nonce": 10500000059239 as u64,
+            "number": 109966,
+            "powMin": 88,
+            "time": 1523300656,
+            "medianTime": 1523295259,
+            "membersCount": 933,
+            "monetaryMass": 146881563,
+            "unitbase": 0,
+            "issuersCount": 44,
+            "issuersFrame": 221,
+            "issuersFrameVar": 0,
+            "currency": "g1",
+            "issuer": "GRBPV3Y7PQnB9LaZhSGuS3BqBJbSHyibzYq65kTh1nQ4",
+            "signature": "GCg2Lti3TdxWlhA8JF8pRI+dRQ0XZVtcC4BqO/COTpjTQFdWG6qmUNVvdeYCtR/lu1JQe3N/IhrbyV6L/6I+Cg==",
+            "hash": "000000EF5B2AA849F4C3AF3D35E1284EA1F34A9F617EA806CE8371619023DC74",
+            "parameters": "",
+            "previousHash": "000004C00602F8A27AE078DE6351C0DDA1EA0974A78D2BEFA7DFBE7B7C3146FD",
+            "previousIssuer": "5SwfQubSat5SunNafCsunEGTY93nVM4kLSsuprNqQb6S",
+            "inner_hash": "61F02B1A6AE2E4B9A1FD66CE673258B4B21C0076795571EE3C9DC440DD06C46C",
+            "dividend": null,
+            "identities": [],
+            "joiners": [],
+            "actives": [],
+            "leavers": [],
+            "revoked": [],
+            "excluded": [],
+            "certifications": [
+                "Hm5qjaNuHogNRdGZ4vgnLA9DMZVUu5YWzVup5mubuxCc:8AmdBsimcLziXaCS4AcVUfPx7rkjeic7482dLbBkuZw6:109964:yHKBGMeuxyIqFb295gVNK6neRC+U0tmsX1Zed3TLjS3ZZHYYycE1piLcYsTKll4ifNVp6rm+hd/CLdHYB+29CA==",
+                "BncjgJeFpGsMCCsUfzNLEexjsbuX3V2mg9P67ov2LkwK:DyBUBNpzpfvjtwYYSaVMM6ST6t2DNg3NCE9CU9bRQFhF:105864:cJEGW9WxJwlMA2+4LNAK4YieyseUy1WIkFh1YLYD+JJtJEoCSnIQRXzhiAoRpGaj0bRz8sTpwI6PRkuVoDJJDQ=="
+            ],
+            "transactions": [
+                {
+                "version": 10,
+                "currency": "g1",
+                "locktime": 0,
+                "hash": "80FE1E83DC4D0B722CA5F8363EFC6A3E29071032EBB71C1E0DF8D4FEA589C698",
+                "blockstamp": "109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B",
+                "blockstampTime": 0,
+                "issuers": [
+                    "6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT"
+                ],
+                "inputs": [
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98284",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98519",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98779",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99054",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99326",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99599",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99884",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100174",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100469",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100746",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:101036",
+                    "1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:101327"
+                ],
+                "outputs": [
+                    "12000:0:SIG(HmH5beJqKGMeotcQUrSW7Wo5tKvAksHmfYXfiSQ9EbWz)",
+                    "12:0:SIG(6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT)"
+                ],
+                "unlocks": [
+                    "0:SIG(0)",
+                    "1:SIG(0)",
+                    "2:SIG(0)",
+                    "3:SIG(0)",
+                    "4:SIG(0)",
+                    "5:SIG(0)",
+                    "6:SIG(0)",
+                    "7:SIG(0)",
+                    "8:SIG(0)",
+                    "9:SIG(0)",
+                    "10:SIG(0)",
+                    "11:SIG(0)"
+                ],
+                "signatures": [
+                    "MZxoKxYgwufh/s5mwLCsYEZXtIsP1hEKCyAzLipJsvCbR9xj7wXUw0C/ahwvZfBtR7+QVPIfLmwYEol1JcHjDw=="
+                ],
+                "comment": "Adhesion 2018"
+                },
+                {
+                "version": 10,
+                "currency": "g1",
+                "locktime": 0,
+                "hash": "B80507412B35BD5EB437AE0D3EB97E60E3A4974F5CDEA1AF7E2127C0E943481F",
+                "blockstamp": "109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B",
+                "blockstampTime": 0,
+                "issuers": [
+                    "8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU"
+                ],
+                "inputs": [
+                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:91560",
+                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:91850",
+                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92111",
+                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92385",
+                    "1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92635"
+                ],
+                "outputs": [
+                    "5000:0:SIG(BzHnbec1Gov7dLSt1EzJS7vikoQCECeuvZs4wamZAcT1)",
+                    "5:0:SIG(8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU)"
+                ],
+                "unlocks": [
+                    "0:SIG(0)",
+                    "1:SIG(0)",
+                    "2:SIG(0)",
+                    "3:SIG(0)",
+                    "4:SIG(0)"
+                ],
+                "signatures": [
+                    "A+ukwRvLWs1gZQ0KAqAnknEgmRQHdrnOvNuBx/WZqje17BAPrVxSxKpqwU6MiajU+ppigsYp6Bu0FdPf/tGnCQ=="
+                ],
+                "comment": ""
+                },
+                {
+                "version": 10,
+                "currency": "g1",
+                "locktime": 0,
+                "hash": "D8970E6629C0381A78534EEDD86803E9215A7EC4C494BAEA79EB19425F9B4D31",
+                "blockstamp": "109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B",
+                "blockstampTime": 0,
+                "issuers": [
+                    "FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg"
+                ],
+                "inputs": [
+                    "1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:36597",
+                    "1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:36880",
+                    "1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:37082"
+                ],
+                "outputs": [
+                    "3000:0:SIG(BBC8Rnh4CWN1wBrPLevK7GRFFVDVw7Lu24YNMUmhqoHU)"
+                ],
+                "unlocks": [
+                    "0:SIG(0)",
+                    "1:SIG(0)",
+                    "2:SIG(0)"
+                ],
+                "signatures": [
+                    "OpiF/oQfIigOeAtsteukU0w9FPSELE+BVTxhmsQ8bEeYGlwovG2VF8ZFiJkLLPi6vFuKgwzULJfjNGd97twZCw=="
+                ],
+                "comment": "1 billet pour une seance.pour un chouette film"
+                }
+            ],
+        });
+        let mut block: BlockDocument =
+            parse_json_block(&json_block).expect("Fail to parse test json block !");
+        assert_eq!(
+            block
+                .inner_hash
+                .expect("Try to get inner_hash of an uncompleted or reduce block !")
+                .to_hex(),
+            "61F02B1A6AE2E4B9A1FD66CE673258B4B21C0076795571EE3C9DC440DD06C46C"
+        );
+        block.compute_hash();
+        assert_eq!(
+            block
+                .hash
+                .expect("Try to get hash of an uncompleted or reduce block !")
+                .0
+                .to_hex(),
+            "000000EF5B2AA849F4C3AF3D35E1284EA1F34A9F617EA806CE8371619023DC74"
+        );
+    }
+
+    #[test]
+    fn ws2p_requests() {
+        let module_id = WS2PModule::name();
+        let request =
+            OldNetworkRequest::GetBlocks(ModuleReqFullId(module_id, ModuleReqId(58)), 50, 0);
+        assert_eq!(
+            network_request_to_json(&request),
+            json!({
+                "reqId": format!("{:x}", 58),
+                "body": {
+                    "name": "BLOCKS_CHUNK",
+                    "params": {
+                        "count": 50,
+                        "fromNumber": 0
+                    }
+                }
+            })
+        );
+        assert_eq!(
+            network_request_to_json(&request).to_string(),
+            "{\"body\":{\"name\":\"BLOCKS_CHUNK\",\"params\":{\"count\":50,\"fromNumber\":0}},\"reqId\":\"3a\"}"
+        );
+    }
+
+    #[test]
+    fn ws2p_parse_head() {
+        let head = json!({
+            "message": "WS2POTMIC:HEAD:1:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3",
+            "sig": "trtK9GXvTdfND995ohWEderpO3NkIqi1X6mBeVvMcaHckq+lIGqjWvJ9t9Vccz5t+VGaSmGUihDl4q6eldIYBw==",
+            "messageV2": "WS2POTMIC:HEAD:2:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3:25:22",
+            "sigV2": "x6ehPMuYjGY+z7wEGnJGyMBxMKUdu01RWaF0b0XCtoVjg67cCvT4H0V/Qcxn4bAGqzy5ux2fA7NiI+81bBnqDw==",
+            "step": 0
+        });
+        let mut heads_count = 0;
+        if let Ok(head) = NetworkHead::from_json_value(&head) {
+            if let NetworkHead::V2(ref head_v2) = head {
+                heads_count += 1;
+                assert_eq!(
+                    head_v2.message.to_string(),
+                    String::from("WS2POTMIC:HEAD:1:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3")
+                );
+            }
+            assert_eq!(head.verify(), true);
+        } else {
+            panic!("Fail to parse head !")
+        }
+        assert_eq!(heads_count, 1);
+    }
+}
diff --git a/lib/modules/ws2p-v1-legacy/ok_message.rs b/lib/modules/ws2p-v1-legacy/src/ok_message.rs
similarity index 59%
rename from lib/modules/ws2p-v1-legacy/ok_message.rs
rename to lib/modules/ws2p-v1-legacy/src/ok_message.rs
index 95a63c9dbae39afd26c44fb2919ebe1dbe12e67a..5d5660343ce6ac847fbf90d64f2fe75aa989ab5e 100644
--- a/lib/modules/ws2p-v1-legacy/ok_message.rs
+++ b/lib/modules/ws2p-v1-legacy/src/ok_message.rs
@@ -1,4 +1,4 @@
-use super::WS2PMessage;
+use crate::*;
 use dup_crypto::keys::*;
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 
@@ -11,23 +11,18 @@ pub struct WS2POkMessageV1 {
 }
 
 impl WS2PMessage for WS2POkMessageV1 {
-    fn parse(v: &serde_json::Value, currency: String) -> Option<Self> {
+    fn parse(v: &serde_json::Value, currency: String) -> Result<Self, WS2PMsgParseErr> {
         let signature = match v.get("sig") {
-            Some(signature) => signature
-                .as_str()
-                .expect("Parsing of OK message : fail to convert sig to str")
-                .to_string(),
-            None => return None,
+            Some(signature) => signature.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),
+            None => return Err(WS2PMsgParseErr {}),
         };
-        let pubkey: PubKey = PubKey::Ed25519(
-            ed25519::PublicKey::from_base58("969qRJs8KhsnkyzqarpL4RKZGMdVKNbZgu8fhsigM7Lj")
-                .expect("fail to create default pubkey !"),
-        );
-        let signature: Option<Sig> = Some(Sig::Ed25519(
-            dup_crypto::keys::Signature::from_base64(&signature)
-                .expect("fail to parse signature of OK message !"),
-        ));
-        Some(WS2POkMessageV1 {
+        let pubkey: PubKey = PubKey::Ed25519(ed25519::PublicKey::from_base58(
+            "969qRJs8KhsnkyzqarpL4RKZGMdVKNbZgu8fhsigM7Lj",
+        )?);
+        let signature: Option<Sig> = Some(Sig::Ed25519(dup_crypto::keys::Signature::from_base64(
+            &signature,
+        )?));
+        Ok(WS2POkMessageV1 {
             currency,
             pubkey,
             challenge: "".to_string(),
@@ -41,8 +36,11 @@ impl WS2PMessage for WS2POkMessageV1 {
         )
     }
     fn verify(&self) -> bool {
-        self.pubkey
-            .verify(self.to_raw().as_bytes(), &self.signature.unwrap())
+        if let Some(sig) = self.signature {
+            self.pubkey.verify(self.to_raw().as_bytes(), &sig)
+        } else {
+            false
+        }
     }
 }
 
diff --git a/lib/modules/ws2p-v1-legacy/parsers/blocks.rs b/lib/modules/ws2p-v1-legacy/src/parsers/blocks.rs
similarity index 100%
rename from lib/modules/ws2p-v1-legacy/parsers/blocks.rs
rename to lib/modules/ws2p-v1-legacy/src/parsers/blocks.rs
diff --git a/lib/modules/ws2p-v1-legacy/parsers/excluded.rs b/lib/modules/ws2p-v1-legacy/src/parsers/excluded.rs
similarity index 54%
rename from lib/modules/ws2p-v1-legacy/parsers/excluded.rs
rename to lib/modules/ws2p-v1-legacy/src/parsers/excluded.rs
index 8e7dd84b6676ca6dae072635bdabd6a4a6e9cb18..aff225c31fa79fe7ef72c658b496973006f3d37d 100644
--- a/lib/modules/ws2p-v1-legacy/parsers/excluded.rs
+++ b/lib/modules/ws2p-v1-legacy/src/parsers/excluded.rs
@@ -1,13 +1,14 @@
 use dup_crypto::keys::ed25519;
 use dup_crypto::keys::*;
+use unwrap::unwrap;
 
 pub fn parse_exclusions(json_datas: &str) -> Option<Vec<PubKey>> {
-    let raw_exclusions: serde_json::Value = serde_json::from_str(json_datas).unwrap();
+    let raw_exclusions: serde_json::Value = unwrap!(serde_json::from_str(json_datas));
 
     if raw_exclusions.is_array() {
-        Some(parse_exclusions_from_json_value(
-            raw_exclusions.as_array().unwrap(),
-        ))
+        Some(parse_exclusions_from_json_value(unwrap!(
+            raw_exclusions.as_array()
+        )))
     } else {
         None
     }
@@ -16,9 +17,9 @@ pub fn parse_exclusions(json_datas: &str) -> Option<Vec<PubKey>> {
 pub fn parse_exclusions_from_json_value(array_exclusions: &[serde_json::Value]) -> Vec<PubKey> {
     let mut exclusions: Vec<PubKey> = Vec::new();
     for exclusion in array_exclusions.iter() {
-        exclusions.push(PubKey::Ed25519(
-            ed25519::PublicKey::from_base58(exclusion.as_str().unwrap()).unwrap(),
-        ));
+        exclusions.push(PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+            unwrap!(exclusion.as_str())
+        ))));
     }
     exclusions
 }
diff --git a/lib/modules/ws2p-v1-legacy/parsers/identities.rs b/lib/modules/ws2p-v1-legacy/src/parsers/identities.rs
similarity index 78%
rename from lib/modules/ws2p-v1-legacy/parsers/identities.rs
rename to lib/modules/ws2p-v1-legacy/src/parsers/identities.rs
index 02aab9a9f82b9c130b5453e69fe3677ec5c3c7b4..254214a3fa58208c267ad0a57f527bbe11564045 100644
--- a/lib/modules/ws2p-v1-legacy/parsers/identities.rs
+++ b/lib/modules/ws2p-v1-legacy/src/parsers/identities.rs
@@ -2,6 +2,7 @@ use dubp_documents::documents::identity::*;
 use dubp_documents::Blockstamp;
 use dubp_documents::DocumentBuilder;
 use dup_crypto::keys::*;
+use unwrap::unwrap;
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum IdentityParseError {
@@ -9,10 +10,10 @@ pub enum IdentityParseError {
 }
 
 pub fn parse_identities(currency: &str, json_datas: &str) -> Option<Vec<IdentityDocument>> {
-    let raw_idties: serde_json::Value = serde_json::from_str(json_datas).unwrap();
+    let raw_idties: serde_json::Value = unwrap!(serde_json::from_str(json_datas));
     if raw_idties.is_array() {
         return Some(
-            parse_identities_from_json_value(currency, raw_idties.as_array().unwrap())
+            parse_identities_from_json_value(currency, unwrap!(raw_idties.as_array()))
                 .iter()
                 .map(|i| {
                     i.clone()
@@ -31,18 +32,18 @@ pub fn parse_identities_from_json_value(
     array_identities
         .iter()
         .map(|idty| {
-            let idty_datas: Vec<&str> = idty.as_str().unwrap().split(':').collect();
+            let idty_datas: Vec<&str> = unwrap!(idty.as_str()).split(':').collect();
             if idty_datas.len() == 4 {
                 let idty_doc_builder = IdentityDocumentBuilder {
                     currency,
-                    issuer: &PubKey::Ed25519(
-                        ed25519::PublicKey::from_base58(idty_datas[0]).unwrap(),
-                    ),
-                    blockstamp: &Blockstamp::from_string(idty_datas[2]).unwrap(),
+                    issuer: &PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                        idty_datas[0]
+                    ))),
+                    blockstamp: &unwrap!(Blockstamp::from_string(idty_datas[2])),
                     username: idty_datas[3],
                 };
                 let idty_sig =
-                    Sig::Ed25519(ed25519::Signature::from_base64(idty_datas[1]).unwrap());
+                    Sig::Ed25519(unwrap!(ed25519::Signature::from_base64(idty_datas[1])));
                 //memberships.push(membership_doc_builder.build_with_signature(vec![membership_sig]));
                 Ok(idty_doc_builder.build_with_signature(vec![idty_sig]))
             } else {
@@ -57,7 +58,7 @@ pub fn parse_compact_identity(
     source: &serde_json::Value,
 ) -> Option<IdentityDocument> {
     if source.is_string() {
-        let idty_elements: Vec<&str> = source.as_str().unwrap().split(':').collect();
+        let idty_elements: Vec<&str> = unwrap!(source.as_str()).split(':').collect();
         let issuer = match ed25519::PublicKey::from_base58(idty_elements[0]) {
             Ok(pubkey) => PubKey::Ed25519(pubkey),
             Err(_) => return None,
diff --git a/lib/modules/ws2p-v1-legacy/parsers/memberships.rs b/lib/modules/ws2p-v1-legacy/src/parsers/memberships.rs
similarity index 68%
rename from lib/modules/ws2p-v1-legacy/parsers/memberships.rs
rename to lib/modules/ws2p-v1-legacy/src/parsers/memberships.rs
index a04d2d99b24ae6b7951322c8609fbc458b4d21b1..3fb2cf2a9b6e7d9724ec71588ffe6cd5305ea8c1 100644
--- a/lib/modules/ws2p-v1-legacy/parsers/memberships.rs
+++ b/lib/modules/ws2p-v1-legacy/src/parsers/memberships.rs
@@ -2,6 +2,7 @@ use dubp_documents::documents::membership::*;
 use dubp_documents::Blockstamp;
 use dubp_documents::DocumentBuilder;
 use dup_crypto::keys::*;
+use unwrap::unwrap;
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum MembershipParseError {
@@ -13,13 +14,13 @@ pub fn parse_memberships(
     membership_type: MembershipType,
     json_datas: &str,
 ) -> Option<Vec<MembershipDocument>> {
-    let raw_memberships: serde_json::Value = serde_json::from_str(json_datas).unwrap();
+    let raw_memberships: serde_json::Value = unwrap!(serde_json::from_str(json_datas));
     if raw_memberships.is_array() {
         return Some(
             parse_memberships_from_json_value(
                 currency,
                 membership_type,
-                raw_memberships.as_array().unwrap(),
+                unwrap!(raw_memberships.as_array()),
             )
             .iter()
             .map(|m| {
@@ -41,20 +42,21 @@ pub fn parse_memberships_from_json_value(
     array_memberships
         .iter()
         .map(|membership| {
-            let membership_datas: Vec<&str> = membership.as_str().unwrap().split(':').collect();
+            let membership_datas: Vec<&str> = unwrap!(membership.as_str()).split(':').collect();
             if membership_datas.len() == 5 {
                 let membership_doc_builder = MembershipDocumentBuilder {
                     currency,
-                    issuer: &PubKey::Ed25519(
-                        ed25519::PublicKey::from_base58(membership_datas[0]).unwrap(),
-                    ),
-                    blockstamp: &Blockstamp::from_string(membership_datas[2]).unwrap(),
+                    issuer: &PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                        membership_datas[0]
+                    ))),
+                    blockstamp: &unwrap!(Blockstamp::from_string(membership_datas[2])),
                     membership: membership_type,
                     identity_username: membership_datas[4],
-                    identity_blockstamp: &Blockstamp::from_string(membership_datas[3]).unwrap(),
+                    identity_blockstamp: &unwrap!(Blockstamp::from_string(membership_datas[3])),
                 };
-                let membership_sig =
-                    Sig::Ed25519(ed25519::Signature::from_base64(membership_datas[1]).unwrap());
+                let membership_sig = Sig::Ed25519(unwrap!(ed25519::Signature::from_base64(
+                    membership_datas[1]
+                )));
                 Ok(membership_doc_builder.build_with_signature(vec![membership_sig]))
             } else {
                 Err(MembershipParseError::WrongFormat())
diff --git a/lib/modules/ws2p-v1-legacy/parsers/mod.rs b/lib/modules/ws2p-v1-legacy/src/parsers/mod.rs
similarity index 74%
rename from lib/modules/ws2p-v1-legacy/parsers/mod.rs
rename to lib/modules/ws2p-v1-legacy/src/parsers/mod.rs
index ca9a42a80f33ac97d570c0caeb1eeb1d33b14022..7c9c1edc3c5ea907901cef92df3c89ba288a5628 100644
--- a/lib/modules/ws2p-v1-legacy/parsers/mod.rs
+++ b/lib/modules/ws2p-v1-legacy/src/parsers/mod.rs
@@ -27,6 +27,7 @@ mod tests {
     use dubp_documents::DocumentBuilder;
     use dup_crypto::keys::*;
     use std::str::FromStr;
+    use unwrap::unwrap;
 
     #[test]
     fn parse_json_tx() {
@@ -57,28 +58,24 @@ mod tests {
 
         let tx_builder = TransactionDocumentBuilder {
             currency: "g1",
-            blockstamp: &Blockstamp::from_string(
+            blockstamp: &unwrap!(Blockstamp::from_string(
                 "112533-000002150F2E805E604D9B31212D079570AAD8D3A4D8BB75F2C15A94A345B6B1",
-            )
-            .unwrap(),
+            )),
             locktime: &0,
-            issuers: &vec![PubKey::Ed25519(
-                ed25519::PublicKey::from_base58("51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2")
-                    .unwrap(),
-            )],
-            inputs: &vec![TransactionInput::from_str(
+            issuers: &vec![PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                "51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2"
+            )))],
+            inputs: &vec![unwrap!(TransactionInput::from_str(
                 "1000:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:46496",
-            )
-            .unwrap()],
-            outputs: &vec![TransactionOutput::from_str(
+            ))],
+            outputs: &vec![unwrap!(TransactionOutput::from_str(
                 "1000:0:SIG(2yN8BRSkARcqE8NCxKMBiHfTpx1EvwULFn56Myf6qRmy)",
-            )
-            .unwrap()],
-            unlocks: &vec![TransactionInputUnlocks::from_str("0:SIG(0)").unwrap()],
+            ))],
+            unlocks: &vec![unwrap!(TransactionInputUnlocks::from_str("0:SIG(0)"))],
             comment: "Merci pour la calligraphie ;) de Liam",
             hash: None,
         };
-        let mut tx_doc = tx_builder.build_with_signature(vec![Sig::Ed25519(ed25519::Signature::from_base64("5olrjFylTCsVq8I5Yr7FpXeviynICyvIwe1yG5N0RJF+VZb+bCFBnLAMpmMCU2qzUvK7z41UXOrMRybXiLa2Dw==").unwrap())]);
+        let mut tx_doc = tx_builder.build_with_signature(vec![Sig::Ed25519(unwrap!(ed25519::Signature::from_base64("5olrjFylTCsVq8I5Yr7FpXeviynICyvIwe1yG5N0RJF+VZb+bCFBnLAMpmMCU2qzUvK7z41UXOrMRybXiLa2Dw==")))]);
         tx_doc.compute_hash();
         assert_eq!(
             parse_transaction("g1", &tx_json).expect("Fail to parse transaction !"),
@@ -116,32 +113,29 @@ mod tests {
 
         let tx_builder = TransactionDocumentBuilder {
             currency: "g1",
-            blockstamp: &Blockstamp::from_string(
+            blockstamp: &unwrap!(Blockstamp::from_string(
                 "58-00005B9167EBA1E32C6EAD42AE7F72D8F14B765D3C9E47D233B553D47C5AEE0C",
-            )
-            .unwrap(),
+            )),
             locktime: &0,
-            issuers: &vec![PubKey::Ed25519(
-                ed25519::PublicKey::from_base58("FVUFRrk1K5TQGsY7PRLwqHgdHRoHrwb1hcucp4C2N5tD")
-                    .unwrap(),
-            )],
-            inputs: &vec![TransactionInput::from_str(
+            issuers: &vec![PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                "FVUFRrk1K5TQGsY7PRLwqHgdHRoHrwb1hcucp4C2N5tD"
+            )))],
+            inputs: &vec![unwrap!(TransactionInput::from_str(
                 "1000:0:D:FVUFRrk1K5TQGsY7PRLwqHgdHRoHrwb1hcucp4C2N5tD:1",
-            )
-            .unwrap()],
+            ))],
             outputs: &vec![
-                TransactionOutput::from_str("3:0:SIG(7vU9BMDhN6fBuRa2iK3JRbC6pqQKb4qDMGsFcQuT5cz)")
-                    .unwrap(),
-                TransactionOutput::from_str(
+                unwrap!(TransactionOutput::from_str(
+                    "3:0:SIG(7vU9BMDhN6fBuRa2iK3JRbC6pqQKb4qDMGsFcQuT5cz)"
+                )),
+                unwrap!(TransactionOutput::from_str(
                     "997:0:SIG(FVUFRrk1K5TQGsY7PRLwqHgdHRoHrwb1hcucp4C2N5tD)",
-                )
-                .unwrap(),
+                )),
             ],
-            unlocks: &vec![TransactionInputUnlocks::from_str("0:SIG(0)").unwrap()],
+            unlocks: &vec![unwrap!(TransactionInputUnlocks::from_str("0:SIG(0)"))],
             comment: "Un petit cafe ;-)",
             hash: None,
         };
-        let mut tx_doc = tx_builder.build_with_signature(vec![Sig::Ed25519(ed25519::Signature::from_base64("VWbvsiybM4L2X5+o+6lIiuKNw5KrD1yGZqmV+lHtA28XoRUFzochSIgfoUqBsTAaYEHY45vSX917LDXudTEzBg==").unwrap())]);
+        let mut tx_doc = tx_builder.build_with_signature(vec![Sig::Ed25519(unwrap!(ed25519::Signature::from_base64("VWbvsiybM4L2X5+o+6lIiuKNw5KrD1yGZqmV+lHtA28XoRUFzochSIgfoUqBsTAaYEHY45vSX917LDXudTEzBg==")))]);
         tx_doc.compute_hash();
         assert_eq!(
             parse_transaction("g1", &tx_json).expect("Fail to parse transaction !"),
diff --git a/lib/modules/ws2p-v1-legacy/parsers/transactions.rs b/lib/modules/ws2p-v1-legacy/src/parsers/transactions.rs
similarity index 88%
rename from lib/modules/ws2p-v1-legacy/parsers/transactions.rs
rename to lib/modules/ws2p-v1-legacy/src/parsers/transactions.rs
index f979aa73d19353710d94f0dd884a3aba18edb951..5beacc276a4c13908d207a57cd5c480ef3b045a4 100644
--- a/lib/modules/ws2p-v1-legacy/parsers/transactions.rs
+++ b/lib/modules/ws2p-v1-legacy/src/parsers/transactions.rs
@@ -96,6 +96,7 @@ pub fn parse_transaction(
 #[cfg(test)]
 mod tests {
     use super::*;
+    use unwrap::unwrap;
 
     #[test]
     fn parse_compact_tx() {
@@ -110,24 +111,20 @@ Merci pour la calligraphie ;) de Liam$\
 
         let _tx_builder = TransactionDocumentBuilder {
             currency: "g1",
-            blockstamp: &Blockstamp::from_string(
+            blockstamp: &unwrap!(Blockstamp::from_string(
                 "112533-000002150F2E805E604D9B31212D079570AAD8D3A4D8BB75F2C15A94A345B6B1",
-            )
-            .unwrap(),
+            )),
             locktime: &0,
-            issuers: &vec![PubKey::Ed25519(
-                ed25519::PublicKey::from_base58("51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2")
-                    .unwrap(),
-            )],
-            inputs: &vec![TransactionInput::from_str(
+            issuers: &vec![PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(
+                "51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2"
+            )))],
+            inputs: &vec![unwrap!(TransactionInput::from_str(
                 "1000:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:46496",
-            )
-            .unwrap()],
-            outputs: &vec![TransactionOutput::from_str(
+            ))],
+            outputs: &vec![unwrap!(TransactionOutput::from_str(
                 "1000:0:SIG(2yN8BRSkARcqE8NCxKMBiHfTpx1EvwULFn56Myf6qRmy)",
-            )
-            .unwrap()],
-            unlocks: &vec![TransactionInputUnlocks::from_str("0:SIG(0)").unwrap()],
+            ))],
+            unlocks: &vec![unwrap!(TransactionInputUnlocks::from_str("0:SIG(0)"))],
             comment: "Merci pour la calligraphie ;) de Liam",
             hash: None,
         };
diff --git a/lib/modules/ws2p-v1-legacy/src/requests/mod.rs b/lib/modules/ws2p-v1-legacy/src/requests/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f245617818e5144e6abe8e56576027bdb39bf901
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/requests/mod.rs
@@ -0,0 +1,19 @@
+//  Copyright (C) 2018  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Sub-module managing the inter-modules requests sent and received.
+
+pub mod received;
+pub mod sent;
diff --git a/lib/modules/ws2p-v1-legacy/src/requests/received.rs b/lib/modules/ws2p-v1-legacy/src/requests/received.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ab9c2d0edc5c53e7a071b1a3b706078a9e832435
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/requests/received.rs
@@ -0,0 +1,66 @@
+//  Copyright (C) 2018  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Sub-module managing the inter-modules requests received.
+
+use crate::ws2p_db::DbEndpoint;
+use crate::ws_connections::states::WS2PConnectionState;
+use crate::WS2PModule;
+use duniter_network::requests::OldNetworkRequest;
+use durs_message::requests::DursReqContent;
+
+pub fn receive_req(ws2p_module: &mut WS2PModule, req_content: &DursReqContent) {
+    if let DursReqContent::OldNetworkRequest(ref old_net_request) = *req_content {
+        match *old_net_request {
+            OldNetworkRequest::GetBlocks(ref req_id, ref count, ref from) => {
+                let mut receiver_index = 0;
+                let mut real_receiver = None;
+                for (ws2p_full_id, DbEndpoint { state, .. }) in ws2p_module.ws2p_endpoints.clone() {
+                    if let WS2PConnectionState::Established = state {
+                        if receiver_index == ws2p_module.next_receiver {
+                            real_receiver = Some(ws2p_full_id);
+                            break;
+                        }
+                        receiver_index += 1;
+                    }
+                }
+                if real_receiver.is_none() {
+                    ws2p_module.next_receiver = 0;
+                    for (ws2p_full_id, DbEndpoint { state, .. }) in &ws2p_module.ws2p_endpoints {
+                        if let WS2PConnectionState::Established = *state {
+                            real_receiver = Some(*ws2p_full_id);
+                            break;
+                        }
+                    }
+                } else {
+                    ws2p_module.next_receiver += 1;
+                }
+                if let Some(real_receiver) = real_receiver {
+                    debug!("WS2P: send req to: ({:?})", real_receiver);
+                    let _blocks_request_result =
+                        crate::ws_connections::requests::sent::send_request_to_specific_node(
+                            ws2p_module,
+                            &real_receiver,
+                            &OldNetworkRequest::GetBlocks(*req_id, *count, *from),
+                        );
+                } else {
+                    warn!("WS2P: not found peer to send request !");
+                }
+            }
+            OldNetworkRequest::GetEndpoints(ref _request) => {}
+            _ => {}
+        }
+    }
+}
diff --git a/lib/modules/ws2p-v1-legacy/src/requests/sent.rs b/lib/modules/ws2p-v1-legacy/src/requests/sent.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e07093a98f866de4c6a000d15d6f033bd99c1c08
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/requests/sent.rs
@@ -0,0 +1,37 @@
+//  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/>.
+
+//! Sub-module managing the inter-modules requests sent.
+
+use crate::WS2PModule;
+use duniter_module::{DursModule, ModuleReqId, ModuleRole, RouterThreadMessage};
+use durs_message::requests::{BlockchainRequest, DursReqContent};
+use durs_message::*;
+
+pub fn send_dal_request(ws2p_module: &mut WS2PModule, req: &BlockchainRequest) {
+    ws2p_module.count_dal_requests += 1;
+    if ws2p_module.count_dal_requests == std::u32::MAX {
+        ws2p_module.count_dal_requests = 0;
+    }
+    ws2p_module
+        .router_sender
+        .send(RouterThreadMessage::ModuleMessage(DursMsg::Request {
+            req_from: WS2PModule::name(),
+            req_to: ModuleRole::BlockchainDatas,
+            req_id: ModuleReqId(ws2p_module.count_dal_requests),
+            req_content: DursReqContent::BlockchainRequest(req.clone()),
+        }))
+        .expect("Fail to send message to router !");
+}
diff --git a/lib/modules/ws2p-v1-legacy/src/responses/mod.rs b/lib/modules/ws2p-v1-legacy/src/responses/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..516f7af7e549f4127da05b1b019695a6c127e704
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/responses/mod.rs
@@ -0,0 +1,19 @@
+//  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/>.
+
+//! Sub-module managing the inter-modules responses sent and received.
+
+pub mod received;
+pub mod sent;
diff --git a/lib/modules/ws2p-v1-legacy/src/responses/received.rs b/lib/modules/ws2p-v1-legacy/src/responses/received.rs
new file mode 100644
index 0000000000000000000000000000000000000000..64e0f4874ad6423479942df4288f3653b6ee8227
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/responses/received.rs
@@ -0,0 +1,16 @@
+//  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/>.
+
+//! Sub-module managing the inter-modules responses received.
diff --git a/lib/modules/ws2p-v1-legacy/src/responses/sent.rs b/lib/modules/ws2p-v1-legacy/src/responses/sent.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f102489cbd94ef75a7846a9c122f1e56cfa98e51
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/responses/sent.rs
@@ -0,0 +1,36 @@
+//  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/>.
+
+//! Sub-module managing the inter-modules responses sent.
+
+use crate::*;
+use durs_message::*;
+
+pub fn send_network_req_response(
+    ws2p_module: &WS2PModule,
+    requester: ModuleStaticName,
+    req_id: ModuleReqId,
+    response: NetworkResponse,
+) {
+    ws2p_module
+        .router_sender
+        .send(RouterThreadMessage::ModuleMessage(DursMsg::Response {
+            res_from: WS2PModule::name(),
+            res_to: requester,
+            req_id,
+            res_content: DursResContent::NetworkResponse(response),
+        }))
+        .expect("Fail to send message to router !");
+}
diff --git a/lib/modules/ws2p-v1-legacy/serializer.rs b/lib/modules/ws2p-v1-legacy/src/serializer.rs
similarity index 100%
rename from lib/modules/ws2p-v1-legacy/serializer.rs
rename to lib/modules/ws2p-v1-legacy/src/serializer.rs
diff --git a/lib/modules/ws2p-v1-legacy/src/ws2p_db.rs b/lib/modules/ws2p-v1-legacy/src/ws2p_db.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0b09fb1f1c79e29d91d712f66b9c5ea20d47062c
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/ws2p_db.rs
@@ -0,0 +1,97 @@
+//  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/>.
+
+//! Manage WS2Pv1 storage.
+
+use crate::ws_connections::states::WS2PConnectionState;
+use durs_network_documents::network_endpoint::EndpointV1;
+use durs_network_documents::NodeFullId;
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+use std::fs::File;
+use std::path::Path;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum EndpointApi {
+    WS2P,
+    //WS2PS,
+    //WS2PTOR,
+    //DASA,
+    //BMA,
+    //BMAS,
+}
+
+pub fn string_to_api(api: &str) -> Option<EndpointApi> {
+    match api {
+        "WS2P" => Some(EndpointApi::WS2P),
+        //"WS2PS" => Some(EndpointApi::WS2PS),
+        //"WS2PTOR" => Some(EndpointApi::WS2PTOR),
+        //"DASA" => Some(EndpointApi::DASA),
+        //"BASIC_MERKLED_API" => Some(EndpointApi::BMA),
+        //"BMAS" => Some(EndpointApi::BMAS),
+        &_ => None,
+    }
+}
+
+#[derive(Debug)]
+pub enum Ws2pPeersDbError {
+    IoErr(std::io::Error),
+    SerdeErr(bincode::Error),
+}
+
+impl From<std::io::Error> for Ws2pPeersDbError {
+    fn from(e: std::io::Error) -> Self {
+        Ws2pPeersDbError::IoErr(e)
+    }
+}
+
+impl From<bincode::Error> for Ws2pPeersDbError {
+    fn from(e: bincode::Error) -> Self {
+        Ws2pPeersDbError::SerdeErr(e)
+    }
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct DbEndpoint {
+    pub ep: EndpointV1,
+    pub state: WS2PConnectionState,
+    pub last_check: u64,
+}
+
+pub fn get_endpoints(
+    file_path: &Path,
+) -> Result<HashMap<NodeFullId, DbEndpoint>, Ws2pPeersDbError> {
+    if file_path.exists() {
+        let bin_endpoints = durs_common_tools::read_bin_file(file_path)?;
+        if bin_endpoints.is_empty() {
+            Ok(HashMap::new())
+        } else {
+            Ok(bincode::deserialize(&bin_endpoints[..])?)
+        }
+    } else {
+        File::create(file_path)?;
+        Ok(HashMap::new())
+    }
+}
+
+pub fn write_endpoints<S: std::hash::BuildHasher>(
+    file_path: &Path,
+    endpoints: &HashMap<NodeFullId, DbEndpoint, S>,
+) -> Result<(), Ws2pPeersDbError> {
+    let bin_endpoints = bincode::serialize(&endpoints)?;
+    durs_common_tools::write_bin_file(file_path, &bin_endpoints)?;
+
+    Ok(())
+}
diff --git a/lib/modules/ws2p-v1-legacy/src/ws_connections/handler.rs b/lib/modules/ws2p-v1-legacy/src/ws_connections/handler.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d53b59eab09adab0ed3fc7be570f24c68b429875
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/ws_connections/handler.rs
@@ -0,0 +1,241 @@
+//  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/>.
+
+//! WS2P connections handler.
+
+use super::messages::*;
+use super::meta_datas::WS2PConnectionMetaDatas;
+use super::states::WS2PConnectionState;
+use crate::constants::*;
+use crate::*;
+use dup_crypto::keys::*;
+use std::sync::mpsc;
+#[allow(deprecated)]
+use ws::util::{Timeout, Token};
+use ws::{CloseCode, Frame, Handler, Handshake, Message, Sender};
+
+const CONNECT: Token = Token(1);
+const EXPIRE: Token = Token(2);
+
+// Our Handler struct.
+// Here we explicity indicate that the Client needs a Sender,
+// whereas a closure captures the Sender for us automatically.
+#[allow(deprecated)]
+#[derive(Debug)]
+pub struct Client {
+    ws: Sender,
+    conductor_sender: mpsc::Sender<WS2PThreadSignal>,
+    currency: String,
+    key_pair: KeyPairEnum,
+    connect_message: Message,
+    conn_meta_datas: WS2PConnectionMetaDatas,
+    last_mess_time: SystemTime,
+    spam_interval: bool,
+    spam_counter: usize,
+    timeout: Option<Timeout>,
+}
+
+pub fn connect_to_ws2p_endpoint(
+    endpoint: &EndpointV1,
+    conductor_sender: &mpsc::Sender<WS2PThreadSignal>,
+    currency: &str,
+    key_pair: KeyPairEnum,
+) -> ws::Result<()> {
+    // Get endpoint url
+    let ws_url = endpoint.get_url(true, false).expect("Endpoint unreachable");
+
+    // Create WS2PConnectionMetaDatass
+    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.issuer);
+    conn_meta_datas.remote_uuid = Some(
+        endpoint
+            .node_id
+            .expect("WS2P: Fail to get ep.node_uuid() !"),
+    );
+
+    // Generate connect message
+    let connect_message =
+        generate_connect_message(currency, key_pair, conn_meta_datas.challenge.clone());
+
+    // Log
+    info!("WS2P: Try connection to {} ...", ws_url);
+
+    // Connect to websocket
+    ws::connect(ws_url, |ws| Client {
+        ws,
+        conductor_sender: conductor_sender.clone(),
+        currency: String::from(currency),
+        key_pair,
+        connect_message: connect_message.clone(),
+        conn_meta_datas: conn_meta_datas.clone(),
+        last_mess_time: SystemTime::now(),
+        spam_interval: false,
+        spam_counter: 0,
+        timeout: None,
+    })
+}
+
+// We implement the Handler trait for Client so that we can get more
+// fine-grained control of the connection.
+impl Handler for Client {
+    // `on_open` will be called only after the WebSocket handshake is successful
+    // so at this point we know that the connection is ready to send/receive messages.
+    // We ignore the `Handshake` for now, but you could also use this method to setup
+    // Handler state or reject the connection based on the details of the Request
+    // or Response, such as by checking cookies or Auth headers.
+    fn on_open(&mut self, _: Handshake) -> ws::Result<()> {
+        // Define timeouts
+        self.ws.timeout(WS2P_NEGOTIATION_TIMEOUT * 1_000, CONNECT)?;
+        self.ws.timeout(WS2P_EXPIRE_TIMEOUT * 1_000, EXPIRE)?;
+        // Send ws::Sender to WS2PConductor
+        let result = self
+            .conductor_sender
+            .send(WS2PThreadSignal::WS2PConnectionMessage(
+                WS2PConnectionMessage(
+                    self.conn_meta_datas.node_full_id(),
+                    WS2PConnectionMessagePayload::WebsocketOk(WsSender(self.ws.clone())),
+                ),
+            ));
+        // If WS2PConductor is unrechable, close connection.
+        if result.is_err() {
+            debug!("Close ws2p connection because ws2p main thread is unrechable !");
+            self.ws.close(CloseCode::Normal)
+        } else {
+            // Send CONNECT Message
+            self.ws.send(self.connect_message.clone())
+        }
+    }
+
+    // `on_message` is roughly equivalent to the Handler closure. It takes a `Message`
+    // and returns a `Result<()>`.
+    fn on_message(&mut self, msg: Message) -> ws::Result<()> {
+        // Spam ?
+        if unwrap!(SystemTime::now().duration_since(self.last_mess_time))
+            > Duration::new(*WS2P_SPAM_INTERVAL_IN_MILLI_SECS, 0)
+        {
+            if self.spam_interval {
+                self.spam_counter += 1;
+            } else {
+                self.spam_interval = true;
+                self.spam_counter = 2;
+            }
+        } else {
+            self.spam_interval = false;
+            self.spam_counter = 0;
+        }
+        // Spam ?
+        if self.spam_counter >= *WS2P_SPAM_LIMIT {
+            thread::sleep(Duration::from_millis(*WS2P_SPAM_SLEEP_TIME_IN_SEC));
+            self.last_mess_time = SystemTime::now();
+            return Ok(());
+        }
+        self.last_mess_time = SystemTime::now();
+
+        // Parse and check incoming message
+        if msg.is_text() {
+            let s: String = msg
+                .into_text()
+                .expect("WS2P: Fail to convert message payload to String !");
+            trace!("WS2P: receive mess: {}", s);
+            let json_message: serde_json::Value = serde_json::from_str(&s)
+                .expect("WS2P: Fail to convert string message ton json value !");
+            let result = self
+                .conductor_sender
+                .send(WS2PThreadSignal::WS2PConnectionMessage(
+                    WS2PConnectionMessage(
+                        self.conn_meta_datas.node_full_id(),
+                        self.conn_meta_datas.parse_and_check_incoming_message(
+                            &self.currency,
+                            self.key_pair,
+                            &json_message,
+                        ),
+                    ),
+                ));
+            if result.is_err() {
+                info!("Close ws2p connection because ws2p main thread is unrechable !");
+                self.ws.close(CloseCode::Normal)?;
+            }
+        }
+        Ok(())
+    }
+    fn on_timeout(&mut self, event: Token) -> ws::Result<()> {
+        match event {
+            CONNECT => {
+                if self.conn_meta_datas.state != WS2PConnectionState::Established {
+                    let _result =
+                        self.conductor_sender
+                            .send(WS2PThreadSignal::WS2PConnectionMessage(
+                                WS2PConnectionMessage(
+                                    self.conn_meta_datas.node_full_id(),
+                                    WS2PConnectionMessagePayload::NegociationTimeout,
+                                ),
+                            ));
+                    self.ws.close(CloseCode::Away)
+                } else {
+                    Ok(())
+                }
+            }
+            EXPIRE => {
+                let _result = self
+                    .conductor_sender
+                    .send(WS2PThreadSignal::WS2PConnectionMessage(
+                        WS2PConnectionMessage(
+                            self.conn_meta_datas.node_full_id(),
+                            WS2PConnectionMessagePayload::Timeout,
+                        ),
+                    ));
+                self.ws.close(CloseCode::Away)
+            }
+            _ => Ok(()),
+        }
+    }
+    #[allow(deprecated)]
+    fn on_new_timeout(&mut self, event: Token, timeout: Timeout) -> ws::Result<()> {
+        if event == EXPIRE {
+            if let Some(t) = self.timeout.take() {
+                self.ws.cancel(t)?;
+            }
+            self.timeout = Some(timeout)
+        }
+        Ok(())
+    }
+    fn on_frame(&mut self, frame: Frame) -> ws::Result<Option<Frame>> {
+        // some activity has occurred, let's reset the expiration timeout
+        self.ws.timeout(WS2P_EXPIRE_TIMEOUT * 1_000, EXPIRE)?;
+        Ok(Some(frame))
+    }
+    fn on_close(&mut self, code: CloseCode, reason: &str) {
+        // The WebSocket protocol allows for a utf8 reason for the closing state after the
+        // close code. WS-RS will attempt to interpret this data as a utf8 description of the
+        // reason for closing the connection. I many cases, `reason` will be an empty string.
+        // So, you may not normally want to display `reason` to the user,
+        // but let's assume that we know that `reason` is human-readable.
+        match code {
+            CloseCode::Normal => info!("The remote server close the connection."),
+            CloseCode::Away => info!("The remote server is leaving."),
+            _ => warn!("The remote server encountered an error: {}", reason),
+        }
+        let _result = self
+            .conductor_sender
+            .send(WS2PThreadSignal::WS2PConnectionMessage(
+                WS2PConnectionMessage(
+                    self.conn_meta_datas.node_full_id(),
+                    WS2PConnectionMessagePayload::Close,
+                ),
+            ));
+    }
+}
diff --git a/lib/modules/ws2p-v1-legacy/src/ws_connections/messages.rs b/lib/modules/ws2p-v1-legacy/src/ws_connections/messages.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d64651843cad411c6746336c732ad829621e31a1
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/ws_connections/messages.rs
@@ -0,0 +1,291 @@
+//  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/>.
+
+//! Define ws2p connections messages.
+
+use super::*;
+use durs_network_documents::NodeFullId;
+use ws::Message;
+
+#[derive(Debug)]
+/// WS2Pv1 connection Message
+pub struct WS2PConnectionMessage(pub NodeFullId, pub WS2PConnectionMessagePayload);
+
+#[derive(Debug)]
+/// WS2Pv1 connection Message payload
+pub enum WS2PConnectionMessagePayload {
+    FailOpenWS,
+    WrongUrl,
+    FailToSplitWS,
+    TryToSendConnectMess,
+    FailSendConnectMess,
+    WebsocketOk(WsSender),
+    NegociationTimeout,
+    ValidConnectMessage(String, WS2PConnectionState),
+    ValidAckMessage(String, WS2PConnectionState),
+    ValidOk(WS2PConnectionState),
+    DalRequest(ModuleReqId, serde_json::Value),
+    PeerCard(serde_json::Value, Vec<EndpointV1>),
+    Heads(Vec<serde_json::Value>),
+    Document(BlockchainDocument),
+    ReqResponse(ModuleReqId, serde_json::Value),
+    InvalidMessage,
+    WrongFormatMessage,
+    UnknowMessage,
+    Timeout,
+    Close,
+}
+
+pub fn generate_connect_message(
+    currency: &str,
+    key_pair: KeyPairEnum,
+    challenge: String,
+) -> Message {
+    // Create CONNECT Message
+    let mut connect_message = WS2PConnectMessageV1 {
+        currency: String::from(currency),
+        pubkey: key_pair.public_key(),
+        challenge,
+        signature: None,
+    };
+    connect_message.signature = Some(connect_message.sign(key_pair));
+    Message::text(
+        serde_json::to_string(&connect_message).expect("Fail to serialize CONNECT message !"),
+    )
+}
+
+pub fn ws2p_conn_message_pretreatment(
+    ws2p_module: &mut WS2PModule,
+    message: WS2PConnectionMessage,
+) -> WS2PSignal {
+    let ws2p_full_id = message.0;
+    match message.1 {
+        WS2PConnectionMessagePayload::WrongUrl
+        | WS2PConnectionMessagePayload::FailOpenWS
+        | WS2PConnectionMessagePayload::FailToSplitWS => {
+            let dal_ep = ws2p_module
+                .ws2p_endpoints
+                .get_mut(&ws2p_full_id)
+                .expect("WS2P: Fail to get mut ep !");
+            dal_ep.state = WS2PConnectionState::WSError;
+            dal_ep.last_check = durs_common_tools::current_timestamp();
+            return WS2PSignal::WSError(ws2p_full_id);
+        }
+        WS2PConnectionMessagePayload::TryToSendConnectMess => {
+            ws2p_module
+                .ws2p_endpoints
+                .get_mut(&ws2p_full_id)
+                .expect("WS2P: Fail to get mut ep !")
+                .state = WS2PConnectionState::TryToSendConnectMess;
+        }
+        WS2PConnectionMessagePayload::FailSendConnectMess => {
+            let dal_ep = ws2p_module
+                .ws2p_endpoints
+                .get_mut(&ws2p_full_id)
+                .expect("WS2P: Fail to get mut ep !");
+            dal_ep.state = WS2PConnectionState::Unreachable;
+            dal_ep.last_check = durs_common_tools::current_timestamp();
+        }
+        WS2PConnectionMessagePayload::WebsocketOk(sender) => {
+            ws2p_module.websockets.insert(ws2p_full_id, sender);
+        }
+        WS2PConnectionMessagePayload::ValidConnectMessage(response, new_con_state) => {
+            ws2p_module
+                .ws2p_endpoints
+                .get_mut(&ws2p_full_id)
+                .expect("WS2P: Fail to get mut ep !")
+                .state = new_con_state;
+            debug!("Send: {:#?}", response);
+            if let Some(websocket) = ws2p_module.websockets.get_mut(&ws2p_full_id) {
+                if websocket.0.send(Message::text(response)).is_err() {
+                    return WS2PSignal::WSError(ws2p_full_id);
+                }
+            } else {
+                // Connection closed by remote peer
+                let dal_ep = ws2p_module
+                    .ws2p_endpoints
+                    .get_mut(&ws2p_full_id)
+                    .expect("WS2P: Fail to get mut ep !");
+                dal_ep.state = WS2PConnectionState::Close;
+                dal_ep.last_check = durs_common_tools::current_timestamp();
+            }
+        }
+        WS2PConnectionMessagePayload::ValidAckMessage(response, new_con_state) => {
+            ws2p_module
+                .ws2p_endpoints
+                .get_mut(&ws2p_full_id)
+                .expect("WS2P: Fail to get mut ep !")
+                .state = new_con_state;
+            if let WS2PConnectionState::AckMessOk = ws2p_module.ws2p_endpoints[&ws2p_full_id].state
+            {
+                debug!("Send: {:#?}", response);
+                if let Some(websocket) = ws2p_module.websockets.get_mut(&ws2p_full_id) {
+                    if websocket.0.send(Message::text(response)).is_err() {
+                        return WS2PSignal::WSError(ws2p_full_id);
+                    }
+                } else {
+                    fatal_error!("Fatal error : no websocket for {} !", ws2p_full_id);
+                }
+            }
+        }
+        WS2PConnectionMessagePayload::ValidOk(new_con_state) => {
+            ws2p_module
+                .ws2p_endpoints
+                .get_mut(&ws2p_full_id)
+                .expect("WS2P: Fail to get mut ep !")
+                .state = new_con_state;
+            let mut close_conn = false;
+            let signal = match ws2p_module.ws2p_endpoints[&ws2p_full_id].state {
+                WS2PConnectionState::OkMessOkWaitingAckMess => WS2PSignal::Empty,
+                WS2PConnectionState::Established => WS2PSignal::ConnectionEstablished(ws2p_full_id),
+                _ => {
+                    close_conn = true;
+                    WS2PSignal::Empty
+                }
+            };
+            if close_conn {
+                close_connection(
+                    ws2p_module,
+                    &ws2p_full_id,
+                    WS2PCloseConnectionReason::Unknow,
+                );
+            }
+
+            return signal;
+        }
+        WS2PConnectionMessagePayload::DalRequest(req_id, req_body) => {
+            return WS2PSignal::DalRequest(ws2p_full_id, req_id, req_body);
+        }
+        WS2PConnectionMessagePayload::PeerCard(body, ws2p_endpoints) => {
+            return WS2PSignal::PeerCard(ws2p_full_id, body, ws2p_endpoints);
+        }
+        WS2PConnectionMessagePayload::Heads(heads) => {
+            let mut applied_heads = Vec::with_capacity(heads.len());
+            for head in heads {
+                if let Ok(head) = NetworkHead::from_json_value(&head) {
+                    if head.verify()
+                        && (ws2p_module.my_head.is_none()
+                            || head.node_full_id()
+                                != ws2p_module
+                                    .my_head
+                                    .clone()
+                                    .expect("WS2P: Fail to clone my_head")
+                                    .node_full_id())
+                        && head.apply(&mut ws2p_module.heads_cache)
+                    {
+                        applied_heads.push(head);
+                    }
+                }
+            }
+            return WS2PSignal::Heads(ws2p_full_id, applied_heads);
+        }
+        WS2PConnectionMessagePayload::Document(network_doc) => {
+            return WS2PSignal::Document(ws2p_full_id, network_doc);
+        }
+        WS2PConnectionMessagePayload::ReqResponse(req_id, response) => {
+            if ws2p_module.requests_awaiting_response.len() > req_id.0 as usize {
+                if let Some((ref ws2p_request, ref recipient_fulld_id, ref _timestamp)) =
+                    ws2p_module.requests_awaiting_response.remove(&req_id)
+                {
+                    return WS2PSignal::ReqResponse(
+                        req_id,
+                        *ws2p_request,
+                        *recipient_fulld_id,
+                        response,
+                    );
+                }
+            }
+        }
+        WS2PConnectionMessagePayload::NegociationTimeout => {
+            match ws2p_module.ws2p_endpoints[&ws2p_full_id].state {
+                WS2PConnectionState::AckMessOk | WS2PConnectionState::ConnectMessOk => {
+                    ws2p_module
+                        .ws2p_endpoints
+                        .get_mut(&ws2p_full_id)
+                        .expect("WS2P: Fail to get mut ep !")
+                        .state = WS2PConnectionState::Denial
+                }
+                WS2PConnectionState::WaitingConnectMess => {
+                    ws2p_module
+                        .ws2p_endpoints
+                        .get_mut(&ws2p_full_id)
+                        .expect("WS2P: Fail to get mut ep !")
+                        .state = WS2PConnectionState::NoResponse
+                }
+                _ => {
+                    let dal_ep = ws2p_module
+                        .ws2p_endpoints
+                        .get_mut(&ws2p_full_id)
+                        .expect("WS2P: Fail to get mut ep !");
+                    dal_ep.state = WS2PConnectionState::Unreachable;
+                    dal_ep.last_check = durs_common_tools::current_timestamp();
+                }
+            }
+            close_connection(
+                ws2p_module,
+                &ws2p_full_id,
+                WS2PCloseConnectionReason::NegociationTimeout,
+            );
+            return WS2PSignal::NegociationTimeout(ws2p_full_id);
+        }
+        WS2PConnectionMessagePayload::Timeout => {
+            close_connection(
+                ws2p_module,
+                &ws2p_full_id,
+                WS2PCloseConnectionReason::Timeout,
+            );
+            return WS2PSignal::Timeout(ws2p_full_id);
+        }
+        WS2PConnectionMessagePayload::UnknowMessage => {
+            warn!("WS2P : Receive Unknow Message from {}.", &ws2p_full_id.1)
+        }
+        WS2PConnectionMessagePayload::WrongFormatMessage => warn!(
+            "WS2P : Receive Wrong Format Message from {}.",
+            &ws2p_full_id.1
+        ),
+        WS2PConnectionMessagePayload::InvalidMessage => return WS2PSignal::Empty,
+        WS2PConnectionMessagePayload::Close => close_connection(
+            ws2p_module,
+            &ws2p_full_id,
+            WS2PCloseConnectionReason::AuthMessInvalidSig,
+        ),
+    }
+    let connections_count = ws2p_module.websockets.len();
+    if connections_count == 0 {
+        return WS2PSignal::NoConnection;
+    }
+    // Detect timeout requests
+    let mut requests_timeout = Vec::new();
+    for &(ref req, ref ws2p_full_id, ref timestamp) in
+        ws2p_module.requests_awaiting_response.clone().values()
+    {
+        if unwrap!(SystemTime::now().duration_since(*timestamp)) > Duration::new(20, 0) {
+            requests_timeout.push(req.get_req_full_id());
+            warn!("request timeout : {:?} (sent to {:?})", req, ws2p_full_id);
+        }
+    }
+    // Delete (and resend) timeout requests
+    for req_id in requests_timeout {
+        //let ws2p_endpoints = ws2p_module.ws2p_endpoints.clone();
+        let _request_option = ws2p_module.requests_awaiting_response.remove(&req_id.1);
+        /*if let Some((request, _, _)) = request_option {
+            let _request_result = ws2p_module.send_request_to_specific_node(
+                &get_random_connection(&ws2p_endpoints),
+                &request,
+            );
+        }*/
+    }
+    WS2PSignal::Empty
+}
diff --git a/lib/modules/ws2p-v1-legacy/ws2p_connection.rs b/lib/modules/ws2p-v1-legacy/src/ws_connections/meta_datas.rs
similarity index 52%
rename from lib/modules/ws2p-v1-legacy/ws2p_connection.rs
rename to lib/modules/ws2p-v1-legacy/src/ws_connections/meta_datas.rs
index c25f3f608ccad66b3667567d2b6ff115b0288f11..e5b8950d343bae47adee7be7527c19f66540693d 100644
--- a/lib/modules/ws2p-v1-legacy/ws2p_connection.rs
+++ b/lib/modules/ws2p-v1-legacy/src/ws_connections/meta_datas.rs
@@ -1,4 +1,22 @@
-use crate::constants::*;
+//  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/>.
+
+//! WS2P connections meta datas.
+
+use super::messages::WS2PConnectionMessagePayload;
+use super::states::WS2PConnectionState;
 use crate::parsers::blocks::parse_json_block;
 use crate::*;
 use duniter_module::ModuleReqId;
@@ -6,294 +24,7 @@ use duniter_network::documents::BlockchainDocument;
 use dup_crypto::keys::*;
 use durs_network_documents::network_endpoint::{EndpointV1, NetworkEndpointApi};
 use durs_network_documents::NodeId;
-use rand::Rng;
-use std::sync::mpsc;
-use ws::deflate::DeflateBuilder;
-#[allow(deprecated)]
-use ws::util::{Timeout, Token};
-use ws::{connect, CloseCode, Frame, Handler, Handshake, Message, Sender};
-
-const CONNECT: Token = Token(1);
-const EXPIRE: Token = Token(2);
-
-/// Store a websocket sender
-pub struct WsSender(pub Sender);
-
-impl ::std::fmt::Debug for WsSender {
-    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
-        write!(f, "WsSender {{ }}")
-    }
-}
-
-// Our Handler struct.
-// Here we explicity indicate that the Client needs a Sender,
-// whereas a closure captures the Sender for us automatically.
 #[allow(deprecated)]
-struct Client {
-    ws: Sender,
-    conductor_sender: mpsc::Sender<WS2PThreadSignal>,
-    currency: String,
-    key_pair: KeyPairEnum,
-    connect_message: Message,
-    conn_meta_datas: WS2PConnectionMetaDatas,
-    last_mess_time: SystemTime,
-    spam_interval: bool,
-    spam_counter: usize,
-    timeout: Option<Timeout>,
-}
-
-// We implement the Handler trait for Client so that we can get more
-// fine-grained control of the connection.
-impl Handler for Client {
-    // `on_open` will be called only after the WebSocket handshake is successful
-    // so at this point we know that the connection is ready to send/receive messages.
-    // We ignore the `Handshake` for now, but you could also use this method to setup
-    // Handler state or reject the connection based on the details of the Request
-    // or Response, such as by checking cookies or Auth headers.
-    fn on_open(&mut self, _: Handshake) -> ws::Result<()> {
-        // Define timeouts
-        self.ws.timeout(WS2P_NEGOTIATION_TIMEOUT * 1_000, CONNECT)?;
-        self.ws.timeout(WS2P_EXPIRE_TIMEOUT * 1_000, EXPIRE)?;
-        // Send ws::Sender to WS2PConductor
-        let result = self
-            .conductor_sender
-            .send(WS2PThreadSignal::WS2PConnectionMessage(
-                WS2PConnectionMessage(
-                    self.conn_meta_datas.node_full_id(),
-                    WS2PConnectionMessagePayload::WebsocketOk(WsSender(self.ws.clone())),
-                ),
-            ));
-        // If WS2PConductor is unrechable, close connection.
-        if result.is_err() {
-            debug!("Close ws2p connection because ws2p main thread is unrechable !");
-            self.ws.close(CloseCode::Normal)
-        } else {
-            // Send CONNECT Message
-            self.ws.send(self.connect_message.clone())
-        }
-    }
-
-    // `on_message` is roughly equivalent to the Handler closure. It takes a `Message`
-    // and returns a `Result<()>`.
-    fn on_message(&mut self, msg: Message) -> ws::Result<()> {
-        // Spam ?
-        if SystemTime::now()
-            .duration_since(self.last_mess_time)
-            .unwrap()
-            > Duration::new(*WS2P_SPAM_INTERVAL_IN_MILLI_SECS, 0)
-        {
-            if self.spam_interval {
-                self.spam_counter += 1;
-            } else {
-                self.spam_interval = true;
-                self.spam_counter = 2;
-            }
-        } else {
-            self.spam_interval = false;
-            self.spam_counter = 0;
-        }
-        // Spam ?
-        if self.spam_counter >= *WS2P_SPAM_LIMIT {
-            thread::sleep(Duration::from_millis(*WS2P_SPAM_SLEEP_TIME_IN_SEC));
-            self.last_mess_time = SystemTime::now();
-            return Ok(());
-        }
-        self.last_mess_time = SystemTime::now();
-
-        // Parse and check incoming message
-        if msg.is_text() {
-            let s: String = msg
-                .into_text()
-                .expect("WS2P: Fail to convert message payload to String !");
-            trace!("WS2P: receive mess: {}", s);
-            let json_message: serde_json::Value = serde_json::from_str(&s)
-                .expect("WS2P: Fail to convert string message ton json value !");
-            let result = self
-                .conductor_sender
-                .send(WS2PThreadSignal::WS2PConnectionMessage(
-                    WS2PConnectionMessage(
-                        self.conn_meta_datas.node_full_id(),
-                        self.conn_meta_datas.parse_and_check_incoming_message(
-                            &self.currency,
-                            self.key_pair,
-                            &json_message,
-                        ),
-                    ),
-                ));
-            if result.is_err() {
-                info!("Close ws2p connection because ws2p main thread is unrechable !");
-                self.ws.close(CloseCode::Normal)?;
-            }
-        }
-        Ok(())
-    }
-    fn on_timeout(&mut self, event: Token) -> ws::Result<()> {
-        match event {
-            CONNECT => {
-                if self.conn_meta_datas.state != WS2PConnectionState::Established {
-                    let _result =
-                        self.conductor_sender
-                            .send(WS2PThreadSignal::WS2PConnectionMessage(
-                                WS2PConnectionMessage(
-                                    self.conn_meta_datas.node_full_id(),
-                                    WS2PConnectionMessagePayload::NegociationTimeout,
-                                ),
-                            ));
-                    self.ws.close(CloseCode::Away)
-                } else {
-                    Ok(())
-                }
-            }
-            EXPIRE => {
-                let _result = self
-                    .conductor_sender
-                    .send(WS2PThreadSignal::WS2PConnectionMessage(
-                        WS2PConnectionMessage(
-                            self.conn_meta_datas.node_full_id(),
-                            WS2PConnectionMessagePayload::Timeout,
-                        ),
-                    ));
-                self.ws.close(CloseCode::Away)
-            }
-            _ => Ok(()),
-        }
-    }
-    #[allow(deprecated)]
-    fn on_new_timeout(&mut self, event: Token, timeout: Timeout) -> ws::Result<()> {
-        if event == EXPIRE {
-            if let Some(t) = self.timeout.take() {
-                self.ws.cancel(t)?;
-            }
-            self.timeout = Some(timeout)
-        }
-        Ok(())
-    }
-    fn on_frame(&mut self, frame: Frame) -> ws::Result<Option<Frame>> {
-        // some activity has occurred, let's reset the expiration timeout
-        self.ws.timeout(WS2P_EXPIRE_TIMEOUT * 1_000, EXPIRE)?;
-        Ok(Some(frame))
-    }
-    fn on_close(&mut self, code: CloseCode, reason: &str) {
-        // The WebSocket protocol allows for a utf8 reason for the closing state after the
-        // close code. WS-RS will attempt to interpret this data as a utf8 description of the
-        // reason for closing the connection. I many cases, `reason` will be an empty string.
-        // So, you may not normally want to display `reason` to the user,
-        // but let's assume that we know that `reason` is human-readable.
-        match code {
-            CloseCode::Normal => info!("The remote server close the connection."),
-            CloseCode::Away => info!("The remote server is leaving."),
-            _ => warn!("The remote server encountered an error: {}", reason),
-        }
-        let _result = self
-            .conductor_sender
-            .send(WS2PThreadSignal::WS2PConnectionMessage(
-                WS2PConnectionMessage(
-                    self.conn_meta_datas.node_full_id(),
-                    WS2PConnectionMessagePayload::Close,
-                ),
-            ));
-    }
-}
-
-#[derive(Debug, Copy, Clone)]
-pub enum WS2POrderForListeningThread {
-    Close,
-}
-
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum WS2PConnectionState {
-    NeverTry = 0,
-    TryToOpenWS = 1,
-    WSError = 2,
-    TryToSendConnectMess = 3,
-    Unreachable = 4,
-    WaitingConnectMess = 5,
-    NoResponse = 6,
-    ConnectMessOk = 7,
-    OkMessOkWaitingAckMess = 8,
-    AckMessOk = 9,
-    Denial = 10,
-    Close = 11,
-    Established = 12,
-}
-
-impl From<u32> for WS2PConnectionState {
-    fn from(integer: u32) -> Self {
-        match integer {
-            1 | 2 => WS2PConnectionState::WSError,
-            3 | 4 => WS2PConnectionState::Unreachable,
-            5 | 6 => WS2PConnectionState::NoResponse,
-            7 | 8 | 9 | 10 => WS2PConnectionState::Denial,
-            11 | 12 => WS2PConnectionState::Close,
-            _ => WS2PConnectionState::NeverTry,
-        }
-    }
-}
-
-impl WS2PConnectionState {
-    pub fn from_u32(integer: u32, from_db: bool) -> Self {
-        if from_db {
-            WS2PConnectionState::from(integer)
-        } else {
-            match integer {
-                1 => WS2PConnectionState::TryToOpenWS,
-                2 => WS2PConnectionState::WSError,
-                3 | 4 => WS2PConnectionState::Unreachable,
-                5 | 6 => WS2PConnectionState::NoResponse,
-                7 => WS2PConnectionState::ConnectMessOk,
-                8 => WS2PConnectionState::OkMessOkWaitingAckMess,
-                9 => WS2PConnectionState::AckMessOk,
-                10 => WS2PConnectionState::Denial,
-                11 => WS2PConnectionState::Close,
-                12 => WS2PConnectionState::Established,
-                _ => WS2PConnectionState::NeverTry,
-            }
-        }
-    }
-    pub fn to_u32(self) -> u32 {
-        match self {
-            WS2PConnectionState::NeverTry => 0,
-            _ => 1,
-        }
-    }
-}
-
-#[derive(Debug)]
-pub enum WS2PConnectionMessagePayload {
-    FailOpenWS,
-    WrongUrl,
-    FailToSplitWS,
-    TryToSendConnectMess,
-    FailSendConnectMess,
-    WebsocketOk(WsSender),
-    NegociationTimeout,
-    ValidConnectMessage(String, WS2PConnectionState),
-    ValidAckMessage(String, WS2PConnectionState),
-    ValidOk(WS2PConnectionState),
-    DalRequest(ModuleReqId, serde_json::Value),
-    PeerCard(serde_json::Value, Vec<EndpointV1>),
-    Heads(Vec<serde_json::Value>),
-    Document(BlockchainDocument),
-    ReqResponse(ModuleReqId, serde_json::Value),
-    InvalidMessage,
-    WrongFormatMessage,
-    UnknowMessage,
-    Timeout,
-    Close,
-}
-
-#[derive(Debug)]
-pub struct WS2PConnectionMessage(pub NodeFullId, pub WS2PConnectionMessagePayload);
-
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum WS2PCloseConnectionReason {
-    AuthMessInvalidSig,
-    NegociationTimeout,
-    Timeout,
-    WsError,
-    Unknow,
-}
-
 #[derive(Debug, Clone)]
 pub struct WS2PConnectionMetaDatas {
     pub state: WS2PConnectionState,
@@ -304,13 +35,6 @@ pub struct WS2PConnectionMetaDatas {
     pub current_blockstamp: Option<(u32, String)>,
 }
 
-#[derive(Debug, Clone)]
-pub struct WS2PDatasForListeningThread {
-    pub conn_meta_datas: WS2PConnectionMetaDatas,
-    pub currency: String,
-    pub key_pair: KeyPairEnum,
-}
-
 impl WS2PConnectionMetaDatas {
     pub fn new(challenge: String) -> Self {
         WS2PConnectionMetaDatas {
@@ -340,11 +64,11 @@ impl WS2PConnectionMetaDatas {
     ) -> WS2PConnectionMessagePayload {
         if let Some(s) = m.get("auth") {
             if s.is_string() {
-                match s.as_str().unwrap() {
+                match s.as_str().unwrap_or("") {
                     "CONNECT" => {
                         let message = WS2PConnectMessageV1::parse(m, currency.to_string())
                             .expect("Failed to parsing CONNECT Message !");
-                        if message.verify() && message.pubkey == self.remote_pubkey.unwrap() {
+                        if message.verify() && message.pubkey == unwrap!(self.remote_pubkey) {
                             match self.state {
                                 WS2PConnectionState::WaitingConnectMess => {
                                     debug!("CONNECT sig is valid.");
@@ -358,7 +82,7 @@ impl WS2PConnectionMetaDatas {
                                     };
                                     response.signature = Some(response.sign(key_pair));
                                     return WS2PConnectionMessagePayload::ValidConnectMessage(
-                                        serde_json::to_string(&response).unwrap(),
+                                        unwrap!(serde_json::to_string(&response)),
                                         self.state,
                                     );
                                 }
@@ -391,7 +115,7 @@ impl WS2PConnectionMetaDatas {
                             };
                             response.signature = Some(response.sign(key_pair));
                             return WS2PConnectionMessagePayload::ValidAckMessage(
-                                serde_json::to_string(&response).unwrap(),
+                                unwrap!(serde_json::to_string(&response)),
                                 self.state,
                             );
                         } else {
@@ -483,7 +207,7 @@ impl WS2PConnectionMetaDatas {
             match body.get("name") {
                 Some(s) => {
                     if s.is_string() {
-                        match s.as_str().unwrap() {
+                        match s.as_str().unwrap_or("") {
                             "BLOCK" => match body.get("block") {
                                 Some(block) => {
                                     if let Some(block_doc) = parse_json_block(&block) {
@@ -584,85 +308,3 @@ impl WS2PConnectionMetaDatas {
         }
     }
 }
-
-pub fn get_random_connection<S: ::std::hash::BuildHasher>(
-    connections: &HashMap<NodeFullId, (EndpointV1, WS2PConnectionState), S>,
-) -> NodeFullId {
-    let mut rng = rand::thread_rng();
-    let mut loop_count = 0;
-    loop {
-        for (ws2p_full_id, (_ep, state)) in &(*connections) {
-            if loop_count > 10 {
-                return *ws2p_full_id;
-            }
-            if let WS2PConnectionState::Established = state {
-                if rng.gen::<bool>() {
-                    return *ws2p_full_id;
-                }
-            }
-        }
-        loop_count += 1;
-    }
-}
-
-pub fn connect_to_ws2p_endpoint(
-    endpoint: &EndpointV1,
-    conductor_sender: &mpsc::Sender<WS2PThreadSignal>,
-    currency: &str,
-    key_pair: KeyPairEnum,
-) -> ws::Result<()> {
-    // Get endpoint url
-    let ws_url = endpoint.get_url(true, false).expect("Endpoint unreachable");
-
-    // Create WS2PConnectionMetaDatass
-    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.issuer);
-    conn_meta_datas.remote_uuid = Some(
-        endpoint
-            .node_id
-            .expect("WS2P: Fail to get ep.node_uuid() !"),
-    );
-
-    // Generate connect message
-    let connect_message =
-        generate_connect_message(currency, key_pair, conn_meta_datas.challenge.clone());
-
-    // Log
-    info!("WS2P: Try connection to {} ...", ws_url);
-
-    // Connect to websocket
-    connect(ws_url, |ws| {
-        DeflateBuilder::new().build(Client {
-            ws,
-            conductor_sender: conductor_sender.clone(),
-            currency: String::from(currency),
-            key_pair,
-            connect_message: connect_message.clone(),
-            conn_meta_datas: conn_meta_datas.clone(),
-            last_mess_time: SystemTime::now(),
-            spam_interval: false,
-            spam_counter: 0,
-            timeout: None,
-        })
-    })
-}
-
-pub fn generate_connect_message(
-    currency: &str,
-    key_pair: KeyPairEnum,
-    challenge: String,
-) -> Message {
-    // Create CONNECT Message
-    let mut connect_message = WS2PConnectMessageV1 {
-        currency: String::from(currency),
-        pubkey: key_pair.public_key(),
-        challenge,
-        signature: None,
-    };
-    connect_message.signature = Some(connect_message.sign(key_pair));
-    Message::text(
-        serde_json::to_string(&connect_message).expect("Fail to serialize CONNECT message !"),
-    )
-}
diff --git a/lib/modules/ws2p-v1-legacy/src/ws_connections/mod.rs b/lib/modules/ws2p-v1-legacy/src/ws_connections/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f18a33b95847845d26644a60bf35c7e7de98393a
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/ws_connections/mod.rs
@@ -0,0 +1,184 @@
+//  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/>.
+
+//! Manage websockets connections.
+
+pub mod handler;
+pub mod messages;
+mod meta_datas;
+pub mod requests;
+pub mod states;
+
+use crate::*;
+use duniter_module::ModuleReqId;
+use duniter_network::documents::BlockchainDocument;
+use dup_crypto::keys::*;
+use durs_network_documents::network_endpoint::EndpointV1;
+use rand::Rng;
+use states::WS2PConnectionState;
+use std::collections::HashSet;
+#[allow(deprecated)]
+use ws::Sender;
+
+/// Store a websocket sender
+pub struct WsSender(pub Sender);
+
+impl ::std::fmt::Debug for WsSender {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+        write!(f, "WsSender {{ }}")
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum WS2PCloseConnectionReason {
+    AuthMessInvalidSig,
+    NegociationTimeout,
+    Timeout,
+    WsError,
+    Unknow,
+}
+
+pub fn connect_to_know_endpoints(ws2p_module: &mut WS2PModule) {
+    info!("WS2P: connect to know endpoints...");
+    let mut count_established_connections = 0;
+    let mut pubkeys = HashSet::new();
+    let mut reachable_endpoints = Vec::new();
+    let mut unreachable_endpoints = Vec::new();
+    for (_ws2p_full_id, DbEndpoint { ep, state, .. }) in ws2p_module.ws2p_endpoints.clone() {
+        if ep.issuer == ws2p_module.key_pair.public_key() || !pubkeys.contains(&ep.issuer) {
+            match state {
+                WS2PConnectionState::Established => count_established_connections += 1,
+                WS2PConnectionState::NeverTry
+                | WS2PConnectionState::Close
+                | WS2PConnectionState::Denial => {
+                    pubkeys.insert(ep.issuer);
+                    reachable_endpoints.push(ep);
+                }
+                _ => {
+                    pubkeys.insert(ep.issuer);
+                    unreachable_endpoints.push(ep);
+                }
+            }
+        }
+    }
+    let mut free_outcoming_rooms =
+        ws2p_module.conf.clone().outcoming_quota - count_established_connections;
+    while free_outcoming_rooms > 0 {
+        let ep = if !reachable_endpoints.is_empty() {
+            reachable_endpoints
+                .pop()
+                .expect("WS2P: Fail to pop() reachable_endpoints !")
+        } else if !unreachable_endpoints.is_empty() {
+            unreachable_endpoints
+                .pop()
+                .expect("WS2P: Fail to pop() unreachable_endpoints !")
+        } else {
+            break;
+        };
+        if !ws2p_module.ssl && ep.port == 443 {
+            continue;
+        }
+        connect_to_without_checking_quotas(ws2p_module, unwrap!(ep.node_full_id()));
+        free_outcoming_rooms -= 1;
+    }
+}
+
+pub fn connect_to(ws2p_module: &mut WS2PModule, ep: &EndpointV1) {
+    // Add endpoint to endpoints list (if there isn't already)
+    let node_full_id = ep
+        .node_full_id()
+        .expect("WS2P: Fail to get ep.node_full_id() !");
+    ws2p_module
+        .ws2p_endpoints
+        .entry(node_full_id)
+        .or_insert(DbEndpoint {
+            ep: ep.clone(),
+            state: WS2PConnectionState::NeverTry,
+            last_check: 0,
+        });
+    let count_established_connections = count_established_connections(&ws2p_module);
+    if ws2p_module.conf.outcoming_quota > count_established_connections {
+        connect_to_without_checking_quotas(ws2p_module, node_full_id);
+    }
+}
+
+pub fn connect_to_without_checking_quotas(ws2p_module: &mut WS2PModule, node_full_id: NodeFullId) {
+    let endpoint = unwrap!(ws2p_module.ws2p_endpoints.get(&node_full_id));
+    let endpoint_copy = endpoint.ep.clone();
+    let conductor_sender_copy = ws2p_module.main_thread_channel.0.clone();
+    let currency_copy = ws2p_module.currency.clone();
+    let key_pair_copy = ws2p_module.key_pair;
+    thread::spawn(move || {
+        let _result = crate::ws_connections::handler::connect_to_ws2p_endpoint(
+            &endpoint_copy,
+            &conductor_sender_copy,
+            &currency_copy.expect("WS2PError : No currency !"),
+            key_pair_copy,
+        );
+    });
+}
+
+pub fn close_connection(
+    ws2p_module: &mut WS2PModule,
+    ws2p_full_id: &NodeFullId,
+    reason: WS2PCloseConnectionReason,
+) {
+    match reason {
+        WS2PCloseConnectionReason::NegociationTimeout => {}
+        WS2PCloseConnectionReason::AuthMessInvalidSig
+        | WS2PCloseConnectionReason::Timeout
+        | WS2PCloseConnectionReason::WsError
+        | WS2PCloseConnectionReason::Unknow => {
+            if let Some(dal_ep) = ws2p_module.ws2p_endpoints.get_mut(ws2p_full_id) {
+                dal_ep.state = WS2PConnectionState::Close;
+                dal_ep.last_check = durs_common_tools::current_timestamp();
+            }
+        }
+    }
+    if let Some(websocket) = ws2p_module.websockets.get(&ws2p_full_id) {
+        let _result = websocket.0.close(ws::CloseCode::Normal);
+    }
+    let _result = ws2p_module.websockets.remove(ws2p_full_id);
+}
+
+pub fn get_random_connection<S: ::std::hash::BuildHasher>(
+    connections: &HashMap<NodeFullId, (EndpointV1, WS2PConnectionState), S>,
+) -> NodeFullId {
+    let mut rng = rand::thread_rng();
+    let mut loop_count = 0;
+    loop {
+        for (ws2p_full_id, (_ep, state)) in &(*connections) {
+            if loop_count > 10 {
+                return *ws2p_full_id;
+            }
+            if let WS2PConnectionState::Established = state {
+                if rng.gen::<bool>() {
+                    return *ws2p_full_id;
+                }
+            }
+        }
+        loop_count += 1;
+    }
+}
+
+pub fn count_established_connections(ws2p_module: &WS2PModule) -> usize {
+    let mut count_established_connections = 0;
+    for DbEndpoint { state, .. } in ws2p_module.ws2p_endpoints.values() {
+        if let WS2PConnectionState::Established = state {
+            count_established_connections += 1;
+        }
+    }
+    count_established_connections
+}
diff --git a/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/mod.rs b/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3dbfe5e950138bd0e1f68c118381e10e9ec1f52f
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/mod.rs
@@ -0,0 +1,19 @@
+//  Copyright (C) 2018  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Sub-module managing the WS2Pv1 requests sent and received.
+
+pub mod received;
+pub mod sent;
diff --git a/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/received.rs b/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/received.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/received.rs
@@ -0,0 +1 @@
+
diff --git a/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/sent.rs b/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/sent.rs
new file mode 100644
index 0000000000000000000000000000000000000000..6a3b539bfbca2a3daeefe177237817eaab4b3c16
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/ws_connections/requests/sent.rs
@@ -0,0 +1,77 @@
+//  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/>.
+
+//! Sub-module managing the WS2Pv1 requests sent.
+
+use crate::WS2PModule;
+use duniter_network::requests::OldNetworkRequest;
+use durs_network_documents::NodeFullId;
+use std::time::SystemTime;
+use ws::Message;
+
+pub fn send_request_to_specific_node(
+    ws2p_module: &mut WS2PModule,
+    ws2p_full_id: &NodeFullId,
+    ws2p_request: &OldNetworkRequest,
+) -> ws::Result<()> {
+    if let Some(ws) = ws2p_module.websockets.get_mut(ws2p_full_id) {
+        let json_req = network_request_to_json(ws2p_request).to_string();
+        debug!("send request {} to {}", json_req, ws2p_full_id);
+        ws.0.send(Message::text(json_req))?;
+        ws2p_module.requests_awaiting_response.insert(
+            ws2p_request.get_req_id(),
+            (*ws2p_request, *ws2p_full_id, SystemTime::now()),
+        );
+    } else {
+        warn!("WS2P: Fail to get mut websocket !");
+    }
+    Ok(())
+}
+
+pub fn network_request_to_json(request: &OldNetworkRequest) -> serde_json::Value {
+    let (request_id, request_type, request_params) = match *request {
+        OldNetworkRequest::GetCurrent(ref req_full_id) => (req_full_id.1, "CURRENT", json!({})),
+        OldNetworkRequest::GetBlocks(ref req_full_id, count, from_mumber) => (
+            req_full_id.1,
+            "BLOCKS_CHUNK",
+            json!({
+                "count": count,
+                "fromNumber": from_mumber
+            }),
+        ),
+        OldNetworkRequest::GetRequirementsPending(ref req_full_id, min_cert) => (
+            req_full_id.1,
+            "WOT_REQUIREMENTS_OF_PENDING",
+            json!({ "minCert": min_cert }),
+        ),
+        OldNetworkRequest::GetConsensus(_) => {
+            panic!("GetConsensus() request must be not convert to json !");
+        }
+        OldNetworkRequest::GetHeadsCache(_) => {
+            panic!("GetHeadsCache() request must be not convert to json !");
+        }
+        OldNetworkRequest::GetEndpoints(_) => {
+            panic!("GetEndpoints() request must be not convert to json !");
+        }
+    };
+
+    json!({
+        "reqId": request_id,
+        "body" : {
+            "name": request_type,
+            "params": request_params
+        }
+    })
+}
diff --git a/lib/modules/ws2p-v1-legacy/src/ws_connections/states.rs b/lib/modules/ws2p-v1-legacy/src/ws_connections/states.rs
new file mode 100644
index 0000000000000000000000000000000000000000..48e9142536735327af3acb08784ccfa3d91b40bb
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/ws_connections/states.rs
@@ -0,0 +1,76 @@
+//  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/>.
+
+//! Define ws2p connections states.
+
+use serde::{Deserialize, Serialize};
+
+#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
+pub enum WS2PConnectionState {
+    NeverTry = 0,
+    TryToOpenWS = 1,
+    WSError = 2,
+    TryToSendConnectMess = 3,
+    Unreachable = 4,
+    WaitingConnectMess = 5,
+    NoResponse = 6,
+    ConnectMessOk = 7,
+    OkMessOkWaitingAckMess = 8,
+    AckMessOk = 9,
+    Denial = 10,
+    Close = 11,
+    Established = 12,
+}
+
+impl From<u32> for WS2PConnectionState {
+    fn from(integer: u32) -> Self {
+        match integer {
+            1 | 2 => WS2PConnectionState::WSError,
+            3 | 4 => WS2PConnectionState::Unreachable,
+            5 | 6 => WS2PConnectionState::NoResponse,
+            7 | 8 | 9 | 10 => WS2PConnectionState::Denial,
+            11 | 12 => WS2PConnectionState::Close,
+            _ => WS2PConnectionState::NeverTry,
+        }
+    }
+}
+
+impl WS2PConnectionState {
+    pub fn from_u32(integer: u32, from_db: bool) -> Self {
+        if from_db {
+            WS2PConnectionState::from(integer)
+        } else {
+            match integer {
+                1 => WS2PConnectionState::TryToOpenWS,
+                2 => WS2PConnectionState::WSError,
+                3 | 4 => WS2PConnectionState::Unreachable,
+                5 | 6 => WS2PConnectionState::NoResponse,
+                7 => WS2PConnectionState::ConnectMessOk,
+                8 => WS2PConnectionState::OkMessOkWaitingAckMess,
+                9 => WS2PConnectionState::AckMessOk,
+                10 => WS2PConnectionState::Denial,
+                11 => WS2PConnectionState::Close,
+                12 => WS2PConnectionState::Established,
+                _ => WS2PConnectionState::NeverTry,
+            }
+        }
+    }
+    pub fn to_u32(self) -> u32 {
+        match self {
+            WS2PConnectionState::NeverTry => 0,
+            _ => 1,
+        }
+    }
+}
diff --git a/lib/modules/ws2p-v1-legacy/ws2p_db.rs b/lib/modules/ws2p-v1-legacy/ws2p_db.rs
deleted file mode 100644
index c23229dced15baccab7ec2c2358005e90ee9404f..0000000000000000000000000000000000000000
--- a/lib/modules/ws2p-v1-legacy/ws2p_db.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use dup_crypto::keys::*;
-use durs_network_documents::network_endpoint::{EndpointV1, NetworkEndpointApi};
-use sqlite::*;
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum EndpointApi {
-    WS2P,
-    //WS2PS,
-    //WS2PTOR,
-    //DASA,
-    //BMA,
-    //BMAS,
-}
-
-impl From<u32> for EndpointApi {
-    fn from(integer: u32) -> Self {
-        match integer {
-            _ => EndpointApi::WS2P,
-        }
-    }
-}
-
-pub fn string_to_api(api: &str) -> Option<EndpointApi> {
-    match api {
-        "WS2P" => Some(EndpointApi::WS2P),
-        //"WS2PS" => Some(EndpointApi::WS2PS),
-        //"WS2PTOR" => Some(EndpointApi::WS2PTOR),
-        //"DASA" => Some(EndpointApi::DASA),
-        //"BASIC_MERKLED_API" => Some(EndpointApi::BMA),
-        //"BMAS" => Some(EndpointApi::BMAS),
-        &_ => None,
-    }
-}
-
-pub fn api_to_integer(api: &NetworkEndpointApi) -> i64 {
-    match api.0.as_str() {
-        "WS2P" => 1,
-        //EndpointApi::WS2PS => 2,
-        //EndpointApi::WS2PTOR => 3,
-        //EndpointApi::DASA => 4,
-        //EndpointApi::BMA => 5,
-        //EndpointApi::BMAS => 6,
-        _ => 0,
-    }
-}
-
-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 !")
-        .cursor();
-
-    cursor
-        .bind(&[Value::Integer(api_to_integer(&api))])
-        .expect("get_endpoints_for_api() : Error in cursor binding !");
-    let mut endpoints = Vec::new();
-    while let Some(row) = cursor
-        .next()
-        .expect("get_endpoints_for_api() : Error in cursor.next()")
-    {
-        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 EndpointV1::parse_from_raw(
-            &raw_ep,
-            ep_issuer,
-            row[1].as_integer().unwrap() as u32,
-            row[7].as_integer().unwrap() as u64,
-        ) {
-            Ok(ep) => ep,
-            Err(e) => panic!(format!(
-                "Fail to parse endpoint : {} (Error: {:?})",
-                raw_ep, e
-            )),
-        };
-        ep.status = row[1].as_integer().unwrap() as u32;
-        ep.last_check = row[7].as_integer().unwrap() as u64;
-
-        endpoints.push(ep);
-    }
-    endpoints
-}
-
-pub fn write_endpoint(
-    db: &Connection,
-    endpoint: &EndpointV1,
-    new_status: u32,
-    new_last_check: u64,
-) {
-    let hash_full_id = endpoint
-        .node_full_id()
-        .expect("Fail to write endpoint : node_full_id() return None !")
-        .sha256();
-    // Check if endpoint it's already written
-    let mut cursor: Cursor = db
-        .prepare("SELECT status FROM endpoints WHERE hash_full_id=? ORDER BY status DESC;")
-        .expect("write_endpoint() : Error in SQL request !")
-        .cursor();
-    cursor
-        .bind(&[Value::String(hash_full_id.to_string())])
-        .expect("write_endpoint() : Error in cursor binding !");
-
-    // If endpoint it's already written, update status
-    if let Some(row) = cursor
-        .next()
-        .expect("write_endpoint() : Error in cursor.next()")
-    {
-        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
-            ))
-            .expect("Fail to parse SQL request update endpoint  status !");
-        }
-    } 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),
-                            1, ep_v10.raw_endpoint, new_last_check
-                        )
-                    )
-                    .expect("Fail to parse SQL request INSERT endpoint !");
-    }
-}
diff --git a/lib/modules/ws2p-v1-legacy/ws2p_requests.rs b/lib/modules/ws2p-v1-legacy/ws2p_requests.rs
deleted file mode 100644
index 7ba0058b69cc05270e25207f0c8283e0d2fd94ec..0000000000000000000000000000000000000000
--- a/lib/modules/ws2p-v1-legacy/ws2p_requests.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-use duniter_network::requests::OldNetworkRequest;
-
-pub fn network_request_to_json(request: &OldNetworkRequest) -> serde_json::Value {
-    let (request_id, request_type, request_params) = match *request {
-        OldNetworkRequest::GetCurrent(ref req_full_id) => (req_full_id.1, "CURRENT", json!({})),
-        OldNetworkRequest::GetBlocks(ref req_full_id, count, from_mumber) => (
-            req_full_id.1,
-            "BLOCKS_CHUNK",
-            json!({
-                "count": count,
-                "fromNumber": from_mumber
-            }),
-        ),
-        OldNetworkRequest::GetRequirementsPending(ref req_full_id, min_cert) => (
-            req_full_id.1,
-            "WOT_REQUIREMENTS_OF_PENDING",
-            json!({ "minCert": min_cert }),
-        ),
-        OldNetworkRequest::GetConsensus(_) => {
-            panic!("GetConsensus() request must be not convert to json !");
-        }
-        OldNetworkRequest::GetHeadsCache(_) => {
-            panic!("GetHeadsCache() request must be not convert to json !");
-        }
-        OldNetworkRequest::GetEndpoints(_) => {
-            panic!("GetEndpoints() request must be not convert to json !");
-        }
-    };
-
-    json!({
-        "reqId": request_id,
-        "body" : {
-            "name": request_type,
-            "params": request_params
-        }
-    })
-}
diff --git a/lib/tools/common-tools/src/lib.rs b/lib/tools/common-tools/src/lib.rs
index f726482e81abc33714cdd6568f1ed7c61dcef69a..be0eee5c79ed280a92f9d2804510bb618aa28e8f 100644
--- a/lib/tools/common-tools/src/lib.rs
+++ b/lib/tools/common-tools/src/lib.rs
@@ -26,6 +26,13 @@
     unused_import_braces
 )]
 
+use std::fs::File;
+use std::io::Read;
+use std::io::Write;
+use std::path::Path;
+use std::time::SystemTime;
+use std::time::UNIX_EPOCH;
+
 /// Interrupts the program and log error message
 /// WARNING: this macro must not be called before the logger is initialized !
 #[macro_export]
@@ -44,14 +51,35 @@ macro_rules! fatal_error {
     });
 }
 
-/*macro_rules! error {
-    (target: $target:expr, $($arg:tt)+) => (
-        log!(target: $target, $crate::Level::Error, $($arg)+);
-    );
-    ($($arg:tt)+) => (
-        log!($crate::Level::Error, $($arg)+);
-    )
-}*/
+#[inline]
+/// Get current timestamp in seconds
+pub fn current_timestamp() -> u64 {
+    SystemTime::now()
+        .duration_since(UNIX_EPOCH)
+        .expect("SystemTime::duration_since failed")
+        .as_secs()
+}
+
+/// Read bin file
+pub fn read_bin_file(file_path: &Path) -> Result<Vec<u8>, std::io::Error> {
+    let mut file = File::open(file_path)?;
+    if file.metadata()?.len() == 0 {
+        Ok(vec![])
+    } else {
+        let mut bin_datas = Vec::new();
+        file.read_to_end(&mut bin_datas)?;
+
+        Ok(bin_datas)
+    }
+}
+
+/// Write bin file
+pub fn write_bin_file(file_path: &Path, datas: &[u8]) -> Result<(), std::io::Error> {
+    let mut file = File::create(file_path)?;
+    file.write_all(&datas[..])?;
+
+    Ok(())
+}
 
 /// Unescape backslash
 pub fn unescape_str(source: &str) -> String {
diff --git a/lib/tools/network-documents/Cargo.toml b/lib/tools/network-documents/Cargo.toml
index d53595f8fac4df3824c306b366cda94e3f5c14d8..f218c13302f30109c8c6db85e3d541bb759b1f47 100644
--- a/lib/tools/network-documents/Cargo.toml
+++ b/lib/tools/network-documents/Cargo.toml
@@ -18,8 +18,7 @@ dubp-documents= { path = "../documents" }
 hex = "0.3.*"
 pest = "2.1.0"
 pest_derive = "2.1.0"
-serde = "1.0.*"
-serde_derive = "1.0.*"
+serde = { version = "1.0.*", features = ["derive"] }
 serde_json = "1.0.*"
 
 [dev-dependencies]
diff --git a/lib/tools/network-documents/src/lib.rs b/lib/tools/network-documents/src/lib.rs
index dd95a0f3487a9171c1764b98251a262003f00b6c..a5eeec12b3471e759b21ba937929b3fbf28f0d4d 100644
--- a/lib/tools/network-documents/src/lib.rs
+++ b/lib/tools/network-documents/src/lib.rs
@@ -30,8 +30,6 @@ extern crate pest_derive;
 #[cfg(test)]
 #[macro_use]
 extern crate pretty_assertions;
-#[macro_use]
-extern crate serde_derive;
 
 pub mod network_endpoint;
 pub mod network_head;
@@ -48,6 +46,7 @@ use dup_crypto::hashs::*;
 use dup_crypto::keys::*;
 use pest::iterators::Pair;
 use pest::Parser;
+use serde::{Deserialize, Serialize};
 use std::fmt::{Display, Error, Formatter};
 
 #[derive(Parser)]
@@ -116,7 +115,7 @@ impl<'a> From<&'a str> for NodeId {
     }
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 /// Complete identifier of a duniter node.
 pub struct NodeFullId(pub NodeId, pub PubKey);
 
diff --git a/lib/tools/network-documents/src/network_head.rs b/lib/tools/network-documents/src/network_head.rs
index b3d50090775474171623cb3fdc09d3dab5d3eead..fddce933d645e1a1c5b5c7421af59aeef17893fd 100644
--- a/lib/tools/network-documents/src/network_head.rs
+++ b/lib/tools/network-documents/src/network_head.rs
@@ -21,6 +21,7 @@ use crate::{NodeFullId, NodeId};
 use dubp_documents::blockstamp::*;
 use dup_crypto::bases::BaseConvertionError;
 use dup_crypto::keys::*;
+use serde::{Deserialize, Serialize};
 use serde_json;
 use std::collections::HashMap;
 use std::num::ParseIntError;
diff --git a/lib/tools/network-documents/src/network_head_v2.rs b/lib/tools/network-documents/src/network_head_v2.rs
index f8e8b596f360b932042c026cb132f1c046425d21..cd113c9fc076c5da0e26804f24694a79b56e2131 100644
--- a/lib/tools/network-documents/src/network_head_v2.rs
+++ b/lib/tools/network-documents/src/network_head_v2.rs
@@ -19,6 +19,7 @@ use crate::NodeId;
 use dubp_documents::blockstamp::*;
 use dup_crypto::bases::BaseConvertionError;
 use dup_crypto::keys::*;
+use serde::{Deserialize, Serialize};
 use std::cmp::Ordering;
 use std::num::ParseIntError;
 use std::ops::Deref;