From f864fd9a7366ba4339a6d8dcd50a8bf305330c9b Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Wed, 24 Apr 2019 20:47:46 +0200 Subject: [PATCH 1/5] [ref] ws2pv1: move crate code in folder src --- lib/modules/ws2p-v1-legacy/Cargo.toml | 2 +- lib/modules/ws2p-v1-legacy/{ => src}/ack_message.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/connect_message.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/constants.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/datas.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/heads.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/lib.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/ok_message.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/parsers/blocks.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/parsers/excluded.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/parsers/identities.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/parsers/memberships.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/parsers/mod.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/parsers/transactions.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/serializer.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/ws2p_connection.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/ws2p_db.rs | 0 lib/modules/ws2p-v1-legacy/{ => src}/ws2p_requests.rs | 0 18 files changed, 1 insertion(+), 1 deletion(-) rename lib/modules/ws2p-v1-legacy/{ => src}/ack_message.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/connect_message.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/constants.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/datas.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/heads.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/lib.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/ok_message.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/parsers/blocks.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/parsers/excluded.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/parsers/identities.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/parsers/memberships.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/parsers/mod.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/parsers/transactions.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/serializer.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/ws2p_connection.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/ws2p_db.rs (100%) rename lib/modules/ws2p-v1-legacy/{ => src}/ws2p_requests.rs (100%) diff --git a/lib/modules/ws2p-v1-legacy/Cargo.toml b/lib/modules/ws2p-v1-legacy/Cargo.toml index 30909adc..e4aba182 100644 --- a/lib/modules/ws2p-v1-legacy/Cargo.toml +++ b/lib/modules/ws2p-v1-legacy/Cargo.toml @@ -7,7 +7,7 @@ license = "AGPL-3.0" edition = "2018" [lib] -path = "lib.rs" +path = "src/lib.rs" [dependencies] byteorder = "1.2.3" diff --git a/lib/modules/ws2p-v1-legacy/ack_message.rs b/lib/modules/ws2p-v1-legacy/src/ack_message.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/ack_message.rs rename to lib/modules/ws2p-v1-legacy/src/ack_message.rs diff --git a/lib/modules/ws2p-v1-legacy/connect_message.rs b/lib/modules/ws2p-v1-legacy/src/connect_message.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/connect_message.rs rename to lib/modules/ws2p-v1-legacy/src/connect_message.rs diff --git a/lib/modules/ws2p-v1-legacy/constants.rs b/lib/modules/ws2p-v1-legacy/src/constants.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/constants.rs rename to lib/modules/ws2p-v1-legacy/src/constants.rs diff --git a/lib/modules/ws2p-v1-legacy/datas.rs b/lib/modules/ws2p-v1-legacy/src/datas.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/datas.rs rename to lib/modules/ws2p-v1-legacy/src/datas.rs 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/lib.rs b/lib/modules/ws2p-v1-legacy/src/lib.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/lib.rs rename to lib/modules/ws2p-v1-legacy/src/lib.rs diff --git a/lib/modules/ws2p-v1-legacy/ok_message.rs b/lib/modules/ws2p-v1-legacy/src/ok_message.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/ok_message.rs rename to lib/modules/ws2p-v1-legacy/src/ok_message.rs 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 100% rename from lib/modules/ws2p-v1-legacy/parsers/excluded.rs rename to lib/modules/ws2p-v1-legacy/src/parsers/excluded.rs diff --git a/lib/modules/ws2p-v1-legacy/parsers/identities.rs b/lib/modules/ws2p-v1-legacy/src/parsers/identities.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/parsers/identities.rs rename to lib/modules/ws2p-v1-legacy/src/parsers/identities.rs diff --git a/lib/modules/ws2p-v1-legacy/parsers/memberships.rs b/lib/modules/ws2p-v1-legacy/src/parsers/memberships.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/parsers/memberships.rs rename to lib/modules/ws2p-v1-legacy/src/parsers/memberships.rs diff --git a/lib/modules/ws2p-v1-legacy/parsers/mod.rs b/lib/modules/ws2p-v1-legacy/src/parsers/mod.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/parsers/mod.rs rename to lib/modules/ws2p-v1-legacy/src/parsers/mod.rs diff --git a/lib/modules/ws2p-v1-legacy/parsers/transactions.rs b/lib/modules/ws2p-v1-legacy/src/parsers/transactions.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/parsers/transactions.rs rename to lib/modules/ws2p-v1-legacy/src/parsers/transactions.rs 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/ws2p_connection.rs b/lib/modules/ws2p-v1-legacy/src/ws2p_connection.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/ws2p_connection.rs rename to lib/modules/ws2p-v1-legacy/src/ws2p_connection.rs diff --git a/lib/modules/ws2p-v1-legacy/ws2p_db.rs b/lib/modules/ws2p-v1-legacy/src/ws2p_db.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/ws2p_db.rs rename to lib/modules/ws2p-v1-legacy/src/ws2p_db.rs diff --git a/lib/modules/ws2p-v1-legacy/ws2p_requests.rs b/lib/modules/ws2p-v1-legacy/src/ws2p_requests.rs similarity index 100% rename from lib/modules/ws2p-v1-legacy/ws2p_requests.rs rename to lib/modules/ws2p-v1-legacy/src/ws2p_requests.rs -- GitLab From cea0216b5701a0418ae10850bde37980a8041588 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Thu, 25 Apr 2019 00:53:11 +0200 Subject: [PATCH 2/5] [feat] common-tools: add fn read & write binary file --- lib/tools/common-tools/src/lib.rs | 44 +++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/lib/tools/common-tools/src/lib.rs b/lib/tools/common-tools/src/lib.rs index f726482e..be0eee5c 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 { -- GitLab From c0ad94390555672cb038bdccce17b113f319daef Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Thu, 25 Apr 2019 00:54:34 +0200 Subject: [PATCH 3/5] [ref] network-documents: use new serde derive way --- lib/tools/network-documents/Cargo.toml | 3 +-- lib/tools/network-documents/src/lib.rs | 5 ++--- lib/tools/network-documents/src/network_head.rs | 1 + lib/tools/network-documents/src/network_head_v2.rs | 1 + 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/tools/network-documents/Cargo.toml b/lib/tools/network-documents/Cargo.toml index d53595f8..f218c133 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 dd95a0f3..a5eeec12 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 b3d50090..fddce933 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 f8e8b596..cd113c9f 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; -- GitLab From c544ba13f02e9e408def849a789bbc7f1aea22ed Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Thu, 25 Apr 2019 00:55:46 +0200 Subject: [PATCH 4/5] [ref+feat] ws2p1: deep ref whole crate + migrate sqlite -> bin file --- lib/modules/ws2p-v1-legacy/Cargo.toml | 15 +- lib/modules/ws2p-v1-legacy/clippy.toml | 2 +- lib/modules/ws2p-v1-legacy/src/ack_message.rs | 28 +- .../ws2p-v1-legacy/src/connect_message.rs | 31 +- lib/modules/ws2p-v1-legacy/src/constants.rs | 47 +- lib/modules/ws2p-v1-legacy/src/datas.rs | 491 --------- lib/modules/ws2p-v1-legacy/src/events/mod.rs | 19 + .../ws2p-v1-legacy/src/events/received.rs | 72 ++ lib/modules/ws2p-v1-legacy/src/events/sent.rs | 59 ++ lib/modules/ws2p-v1-legacy/src/lib.rs | 956 ++++++++---------- lib/modules/ws2p-v1-legacy/src/ok_message.rs | 34 +- .../ws2p-v1-legacy/src/parsers/excluded.rs | 15 +- .../ws2p-v1-legacy/src/parsers/identities.rs | 19 +- .../ws2p-v1-legacy/src/parsers/memberships.rs | 22 +- lib/modules/ws2p-v1-legacy/src/parsers/mod.rs | 58 +- .../src/parsers/transactions.rs | 25 +- .../ws2p-v1-legacy/src/requests/mod.rs | 19 + .../ws2p-v1-legacy/src/requests/received.rs | 66 ++ .../ws2p-v1-legacy/src/requests/sent.rs | 37 + .../ws2p-v1-legacy/src/responses/mod.rs | 19 + .../ws2p-v1-legacy/src/responses/received.rs | 16 + .../ws2p-v1-legacy/src/responses/sent.rs | 36 + lib/modules/ws2p-v1-legacy/src/ws2p_db.rs | 157 ++- .../ws2p-v1-legacy/src/ws2p_requests.rs | 37 - .../src/ws_connections/handler.rs | 241 +++++ .../src/ws_connections/messages.rs | 291 ++++++ .../meta_datas.rs} | 406 +------- .../ws2p-v1-legacy/src/ws_connections/mod.rs | 184 ++++ .../src/ws_connections/requests/mod.rs | 19 + .../src/ws_connections/requests/received.rs | 1 + .../src/ws_connections/requests/sent.rs | 77 ++ .../src/ws_connections/states.rs | 76 ++ 32 files changed, 1929 insertions(+), 1646 deletions(-) delete mode 100644 lib/modules/ws2p-v1-legacy/src/datas.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/events/mod.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/events/received.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/events/sent.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/requests/mod.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/requests/received.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/requests/sent.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/responses/mod.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/responses/received.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/responses/sent.rs delete mode 100644 lib/modules/ws2p-v1-legacy/src/ws2p_requests.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/ws_connections/handler.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/ws_connections/messages.rs rename lib/modules/ws2p-v1-legacy/src/{ws2p_connection.rs => ws_connections/meta_datas.rs} (52%) create mode 100644 lib/modules/ws2p-v1-legacy/src/ws_connections/mod.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/ws_connections/requests/mod.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/ws_connections/requests/received.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/ws_connections/requests/sent.rs create mode 100644 lib/modules/ws2p-v1-legacy/src/ws_connections/states.rs diff --git a/lib/modules/ws2p-v1-legacy/Cargo.toml b/lib/modules/ws2p-v1-legacy/Cargo.toml index e4aba182..6b2f9788 100644 --- a/lib/modules/ws2p-v1-legacy/Cargo.toml +++ b/lib/modules/ws2p-v1-legacy/Cargo.toml @@ -10,23 +10,24 @@ edition = "2018" 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 05bd01e1..fbfe7d8a 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/src/ack_message.rs b/lib/modules/ws2p-v1-legacy/src/ack_message.rs index e1fcdeed..f18bf55e 100644 --- a/lib/modules/ws2p-v1-legacy/src/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/src/connect_message.rs b/lib/modules/ws2p-v1-legacy/src/connect_message.rs index 1f917ade..e229cc1d 100644 --- a/lib/modules/ws2p-v1-legacy/src/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 index aaada76d..cdc14af7 100644 --- a/lib/modules/ws2p-v1-legacy/src/constants.rs +++ b/lib/modules/ws2p-v1-legacy/src/constants.rs @@ -1,12 +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; -pub static DURATION_BEFORE_RECORDING_ENDPOINT: &'static u64 = &180; -pub static BLOCKS_REQUEST_INTERVAL: &'static u64 = &60; + +/// 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/datas.rs b/lib/modules/ws2p-v1-legacy/src/datas.rs deleted file mode 100644 index 89968a51..00000000 --- a/lib/modules/ws2p-v1-legacy/src/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, - ¤cy_copy.expect("WS2PError : No currency !"), - key_pair_copy.expect("WS2PError : No key_pair !"), - ); - }); - } -} 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 00000000..0e373125 --- /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 00000000..a8bb4f27 --- /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 00000000..bb5a49b6 --- /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/src/lib.rs b/lib/modules/ws2p-v1-legacy/src/lib.rs index a3e7663e..c19854b3 100644 --- a/lib/modules/ws2p-v1-legacy/src/lib.rs +++ b/lib/modules/ws2p-v1-legacy/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2018 The Duniter Project Developers. +// 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 @@ -13,7 +13,7 @@ // 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. +//! WebSocketToPeer API for the Durs project. #![deny( missing_debug_implementations, @@ -29,8 +29,6 @@ #[macro_use] extern crate log; #[macro_use] -extern crate serde_derive; -#[macro_use] extern crate serde_json; #[macro_use] extern crate structopt; @@ -38,24 +36,27 @@ extern crate structopt; mod ack_message; mod connect_message; pub mod constants; -mod datas; +mod events; mod heads; mod ok_message; pub mod parsers; +mod requests; +mod responses; pub mod serializer; -pub mod ws2p_connection; pub mod ws2p_db; -pub mod ws2p_requests; +pub mod ws_connections; 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 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; @@ -64,6 +65,7 @@ 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::*; @@ -71,14 +73,28 @@ 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 { @@ -93,54 +109,38 @@ impl Default for WS2PConf { WS2PConf { outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA, sync_endpoints: vec![ - EndpointV1::parse_from_raw( + unwrap!(EndpointV1::parse_from_raw( "WS2P c1c39a0a ts.g1.librelois.fr 443 /ws2p", - PubKey::Ed25519( - ed25519::PublicKey::from_base58( - "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx", - ) - .unwrap(), - ), + PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58( + "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx", + )),), 0, 0, - ) - .unwrap(), - EndpointV1::parse_from_raw( + )), + unwrap!(EndpointV1::parse_from_raw( "WS2P fb17fcd4 g1.duniter.fr 443 /ws2p", - PubKey::Ed25519( - ed25519::PublicKey::from_base58( - "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE", - ) - .unwrap(), - ), + PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58( + "38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE", + ))), 0, 0, - ) - .unwrap(), - EndpointV1::parse_from_raw( + )), + unwrap!(EndpointV1::parse_from_raw( "WS2P 7b33becd g1.nordstrom.duniter.org 443 /ws2p", - PubKey::Ed25519( - ed25519::PublicKey::from_base58( - "DWoSCRLQyQ48dLxUGr1MDKg4NFcbPbC56LN2hJjCCPpZ", - ) - .unwrap(), - ), + PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58( + "DWoSCRLQyQ48dLxUGr1MDKg4NFcbPbC56LN2hJjCCPpZ", + ))), 0, 0, - ) - .unwrap(), - EndpointV1::parse_from_raw( + )), + unwrap!(EndpointV1::parse_from_raw( "WS2P dff60418 duniter.normandie-libre.fr 443 /ws2p", - PubKey::Ed25519( - ed25519::PublicKey::from_base58( - "8t6Di3pLxxoTEfjXHjF49pNpjSTXuGEQ6BpkT75CkNb2", - ) - .unwrap(), - ), + PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58( + "8t6Di3pLxxoTEfjXHjF49pNpjSTXuGEQ6BpkT75CkNb2", + ))), 0, 0, - ) - .unwrap(), + )), ], } } @@ -181,8 +181,64 @@ pub enum SendRequestError { WSError(usize, Vec<ws::Error>), } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct WS2PModule {} +#[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 { @@ -190,8 +246,18 @@ pub enum WS2PThreadSignal { 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) -> Option<Self>; + 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()) @@ -200,12 +266,6 @@ pub trait WS2PMessage: Sized { //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 { @@ -291,34 +351,76 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule { // 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()), - ); + // 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 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 { + 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( - ep.node_full_id() - .expect("Fail to get endpoint node_full_id"), - (ep.clone(), WS2PConnectionState::Close), + node_full_id, + DbEndpoint { + ep: ep.clone(), + state: WS2PConnectionState::Close, + last_check: 0, + }, ); - info!("Load sync endpoint {}", ep.raw_endpoint); } - ws2p_module.key_pair = Some(key_pair); + + // 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(); @@ -364,50 +466,29 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule { } }); - // 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 !"); + // Request current blockstamp + send_dal_request(&mut ws2p_module, &BlockchainRequest::CurrentBlockstamp()); - // 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); + // Start + connect_to_know_endpoints(&mut ws2p_module); + ws2p_module.main_loop(start_time, soft_meta_datas); - // Stop here in load_conf_only mode - if load_conf_only { - return Ok(()); - } + 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_connections_print = 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; - 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 + match self .main_thread_channel .1 .recv_timeout(Duration::from_millis(200)) @@ -418,112 +499,16 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule { 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) => {} - _ => {} - } - } - } + } => requests::received::receive_req(&mut self, req_content), 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, - ¤t_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) => {} - _ => {} - } - } - } + ref event_type, + ref event_content, + .. + } => events::received::receive_event( + &mut self, + *event_type, + event_content, + ), DursMsg::Response { ref res_content, .. } => { @@ -536,70 +521,61 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule { ) => { debug!( "WS2PModule : receive DALResBc::CurrentBlockstamp({})", - current_blockstamp + self.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, - ¤t_blockstamp, - None, - )); + 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, + )); } - ws2p_module.send_network_event( - &NetworkEvent::ReceiveHeads(vec![ws2p_module + let event = + NetworkEvent::ReceiveHeads(vec![unwrap!(self .my_head - .clone() - .unwrap()]), - ); + .clone())]); + events::sent::send_network_event(&mut self, event); } BlockchainResponse::UIDs(ref _req_id, ref uids) => { // Add uids to heads - for head in ws2p_module.heads_cache.values_mut() { + 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); - ws2p_module - .uids_cache + self.uids_cache .insert(head.pubkey(), uid.to_string()); } else { - ws2p_module - .uids_cache - .remove(&head.pubkey()); + self.uids_cache.remove(&head.pubkey()); } } } // Resent heads to other modules - ws2p_module.send_network_event( - &NetworkEvent::ReceiveHeads( - ws2p_module - .heads_cache - .values() - .cloned() - .collect(), - ), + 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 - 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( + 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, - *conn_state as u32, + *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 } @@ -608,187 +584,208 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule { _ => {} // 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( + 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, - &OldNetworkRequest::GetCurrent(ModuleReqFullId( - module_id, req_id, - )), + WS2PCloseConnectionReason::WsError, ); - if ws2p_module.uids_cache.get(&ws2p_full_id.1).is_none() { - ws2p_module.send_dal_request(&BlockchainRequest::UIDs(vec![ - ws2p_full_id.1, - ])); + 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); } - 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() !", + 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), ), - 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 ); - } - } - 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 !"); + 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), + ); } - 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!("----------------------------------------"); } + _ => {} } - 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 => {} + _ => {} } - WS2PSignal::Empty => {} - _ => {} - }, + } }, Err(e) => match e { mpsc::RecvTimeoutError::Disconnected => { @@ -797,14 +794,21 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule { mpsc::RecvTimeoutError::Timeout => {} }, } - if SystemTime::now() - .duration_since(last_ws2p_connections_print) - .unwrap() - > Duration::new(5, 0) + 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_connections_print = SystemTime::now(); + last_ws2p_state_print = SystemTime::now(); let mut connected_nodes = Vec::new(); - for (k, (_ep, state)) in ws2p_module.ws2p_endpoints.clone() { + for (k, DbEndpoint { state, .. }) in self.ws2p_endpoints.clone() { if let WS2PConnectionState::Established = state { connected_nodes.push(k); } @@ -812,75 +816,37 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule { // Print current_blockstamp info!( "WS2PModule : current_blockstamp() = {:?}", - current_blockstamp + self.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() + if connected_nodes.len() < self.conf.clone().outcoming_quota + && (unwrap!(SystemTime::now().duration_since(last_ws2p_connecting_wave)) > Duration::new(*WS2P_OUTCOMING_INTERVAL, 0) - || (SystemTime::now() - .duration_since(last_ws2p_connecting_wave) - .unwrap() + || (unwrap!(SystemTime::now().duration_since(last_ws2p_connecting_wave)) > Duration::new(*WS2P_OUTCOMING_INTERVAL_AT_STARTUP, 0) - && SystemTime::now().duration_since(start_time).unwrap() + && unwrap!(SystemTime::now().duration_since(start_time)) < Duration::new(*WS2P_OUTCOMING_INTERVAL, 0))) { last_ws2p_connecting_wave = SystemTime::now(); info!("Connected to know endpoints..."); - ws2p_module.connect_to_know_endpoints(); + connect_to_know_endpoints(&mut self); } // Request pending_identities from network - if SystemTime::now() - .duration_since(last_identities_request) - .unwrap() + if unwrap!(SystemTime::now().duration_since(last_identities_request)) > Duration::new(*PENDING_IDENTITIES_REQUEST_INTERVAL, 0) - && SystemTime::now().duration_since(start_time).unwrap() > Duration::new(10, 0) + && unwrap!(SystemTime::now().duration_since(start_time)) > Duration::new(10, 0) { /*info!("get pending_identities from all connections..."); - let _blocks_request_result = ws2p_module.send_request_to_all_connections( + let _blocks_request_result = self.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()); + send_dal_request(&mut self, &BlockchainRequest::CurrentBlockstamp()); } } - Ok(()) } } @@ -888,13 +854,9 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule { 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; - 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() { @@ -1062,40 +1024,6 @@ mod tests { ); } - #[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(); diff --git a/lib/modules/ws2p-v1-legacy/src/ok_message.rs b/lib/modules/ws2p-v1-legacy/src/ok_message.rs index 95a63c9d..5d566034 100644 --- a/lib/modules/ws2p-v1-legacy/src/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/src/parsers/excluded.rs b/lib/modules/ws2p-v1-legacy/src/parsers/excluded.rs index 8e7dd84b..aff225c3 100644 --- a/lib/modules/ws2p-v1-legacy/src/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/src/parsers/identities.rs b/lib/modules/ws2p-v1-legacy/src/parsers/identities.rs index 02aab9a9..254214a3 100644 --- a/lib/modules/ws2p-v1-legacy/src/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/src/parsers/memberships.rs b/lib/modules/ws2p-v1-legacy/src/parsers/memberships.rs index a04d2d99..3fb2cf2a 100644 --- a/lib/modules/ws2p-v1-legacy/src/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/src/parsers/mod.rs b/lib/modules/ws2p-v1-legacy/src/parsers/mod.rs index ca9a42a8..7c9c1edc 100644 --- a/lib/modules/ws2p-v1-legacy/src/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/src/parsers/transactions.rs b/lib/modules/ws2p-v1-legacy/src/parsers/transactions.rs index f979aa73..5beacc27 100644 --- a/lib/modules/ws2p-v1-legacy/src/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 00000000..f2456178 --- /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 00000000..ab9c2d0e --- /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 00000000..e07093a9 --- /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 00000000..516f7af7 --- /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 00000000..64e0f487 --- /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 00000000..f102489c --- /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/src/ws2p_db.rs b/lib/modules/ws2p-v1-legacy/src/ws2p_db.rs index c23229dc..0b09fb1f 100644 --- a/lib/modules/ws2p-v1-legacy/src/ws2p_db.rs +++ b/lib/modules/ws2p-v1-legacy/src/ws2p_db.rs @@ -1,6 +1,27 @@ -use dup_crypto::keys::*; -use durs_network_documents::network_endpoint::{EndpointV1, NetworkEndpointApi}; -use sqlite::*; +// 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 { @@ -12,14 +33,6 @@ pub enum EndpointApi { //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), @@ -32,97 +45,53 @@ pub fn string_to_api(api: &str) -> Option<EndpointApi> { } } -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, - } +#[derive(Debug)] +pub enum Ws2pPeersDbError { + IoErr(std::io::Error), + SerdeErr(bincode::Error), } -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; +impl From<std::io::Error> for Ws2pPeersDbError { + fn from(e: std::io::Error) -> Self { + Ws2pPeersDbError::IoErr(e) + } +} - endpoints.push(ep); +impl From<bincode::Error> for Ws2pPeersDbError { + fn from(e: bincode::Error) -> Self { + Ws2pPeersDbError::SerdeErr(e) } - 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 !"); +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct DbEndpoint { + pub ep: EndpointV1, + pub state: WS2PConnectionState, + pub last_check: u64, +} - // 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 !"); +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 { - 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 !"); + 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/ws2p_requests.rs b/lib/modules/ws2p-v1-legacy/src/ws2p_requests.rs deleted file mode 100644 index 7ba0058b..00000000 --- a/lib/modules/ws2p-v1-legacy/src/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/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 00000000..d53b59ea --- /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 00000000..d6465184 --- /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/src/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/src/ws2p_connection.rs rename to lib/modules/ws2p-v1-legacy/src/ws_connections/meta_datas.rs index c25f3f60..e5b8950d 100644 --- a/lib/modules/ws2p-v1-legacy/src/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 00000000..f18a33b9 --- /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, + ¤cy_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 00000000..3dbfe5e9 --- /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 00000000..8b137891 --- /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 00000000..6a3b539b --- /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 00000000..48e91425 --- /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, + } + } +} -- GitLab From c5cfa20ca07f20a8d1f5a51812b3d1d66c40e155 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Thu, 25 Apr 2019 00:56:20 +0200 Subject: [PATCH 5/5] [build] Update Cargo.lock --- Cargo.lock | 45 ++++++++++++--------------------------------- 1 file changed, 12 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f80ede3..7462c888 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" -- GitLab