diff --git a/Cargo.lock b/Cargo.lock index 73d671bfc9e556e3726f32884b3d78b403cdc2c9..99c9b4adab1844cd673c8641d2a1bf0133a75da5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,6 +166,11 @@ dependencies = [ "cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "dirs" version = "1.0.2" @@ -281,6 +286,7 @@ version = "0.8.0-a0.1" dependencies = [ "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "duniter-crypto 0.2.0-a0.1", "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -324,6 +330,7 @@ dependencies = [ "duniter-documents 0.8.0-a0.1", "duniter-module 0.1.0-a0.1", "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", @@ -662,6 +669,15 @@ name = "pkg-config" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pretty_assertions" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.4.6" @@ -1147,6 +1163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" +"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum dirs 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37a76dd8b997af7107d0bb69d43903cf37153a18266f8b3fdb9911f28efb5444" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" @@ -1184,6 +1201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pbr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "deb73390ab68d81992bd994d145f697451bb0b54fd39738e72eef32458ad6907" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" +"checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6" "checksum proc-macro2 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "effdb53b25cdad54f8f48843d67398f7ef2e14f12c1b4cb4effc549a6462a4d6" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e44651a0dc4cdd99f71c83b561e221f714912d11af1a4dff0631f923d53af035" diff --git a/crypto/keys/ed25519.rs b/crypto/keys/ed25519.rs index ef762c258616e224de2b7fcce3d6d85f5fa59dad..edc0f5f20900847830a604775f7b30500ad4202e 100644 --- a/crypto/keys/ed25519.rs +++ b/crypto/keys/ed25519.rs @@ -134,6 +134,10 @@ impl super::Signature for Signature { } } + fn to_bytes_vector(&self) -> Vec<u8> { + self.0.to_vec() + } + fn to_base64(&self) -> String { base64::encode(&self.0[..]) // need to take a slice for required trait `AsRef<[u8]>` } @@ -215,6 +219,10 @@ impl super::PublicKey for PublicKey { } } + fn to_bytes_vector(&self) -> Vec<u8> { + self.0.to_vec() + } + fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool { crypto::ed25519::verify(message, &self.0, &signature.0) } diff --git a/crypto/keys/mod.rs b/crypto/keys/mod.rs index 1bd615e17ff431f1fb7ab0fd6963f7ea5459a0d8..632781eab3144afc08617a3d415f0cd99c7d151e 100644 --- a/crypto/keys/mod.rs +++ b/crypto/keys/mod.rs @@ -103,6 +103,9 @@ pub trait Signature: Clone + Display + Debug + PartialEq + Eq + Hash { /// [`BaseConvertionError`]: enum.BaseConvertionError.html fn from_base64(base64_string: &str) -> Result<Self, BaseConvertionError>; + /// Convert Signature into butes vector + fn to_bytes_vector(&self) -> Vec<u8>; + /// Encode the signature into Base64 string format. fn to_base64(&self) -> String; } @@ -135,6 +138,12 @@ impl Signature for Sig { fn from_base64(_base64_string: &str) -> Result<Self, BaseConvertionError> { unimplemented!() } + fn to_bytes_vector(&self) -> Vec<u8> { + match *self { + Sig::Ed25519(ed25519_sig) => ed25519_sig.to_bytes_vector(), + Sig::Schnorr() => panic!("Schnorr algo not yet supported !"), + } + } fn to_base64(&self) -> String { match *self { Sig::Ed25519(ed25519_sig) => ed25519_sig.to_base64(), @@ -165,6 +174,9 @@ pub trait PublicKey: Clone + Display + Debug + PartialEq + Eq + Hash + ToBase58 /// [`BaseConvertionError`]: enum.BaseConvertionError.html fn from_base58(base58_string: &str) -> Result<Self, BaseConvertionError>; + /// Convert into bytes vector + fn to_bytes_vector(&self) -> Vec<u8>; + /// Verify a signature with this public key. fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool; } @@ -208,6 +220,12 @@ impl PublicKey for PubKey { fn from_base58(_base58_string: &str) -> Result<Self, BaseConvertionError> { unimplemented!() } + fn to_bytes_vector(&self) -> Vec<u8> { + match *self { + PubKey::Ed25519(ed25519_pubkey) => ed25519_pubkey.to_bytes_vector(), + PubKey::Schnorr() => panic!("Schnorr algo not yet supported !"), + } + } fn verify(&self, message: &[u8], signature: &Self::Signature) -> bool { match *self { PubKey::Ed25519(ed25519_pubkey) => if let Sig::Ed25519(ed25519_sig) = signature { diff --git a/documents/Cargo.toml b/documents/Cargo.toml index 4c541dcb0bbaacf5f805952a63a81cd377e02389..0f6fd6ff01c1ff0d37fab7097e9a8d19ec22358e 100644 --- a/documents/Cargo.toml +++ b/documents/Cargo.toml @@ -14,6 +14,7 @@ path = "lib.rs" [dependencies] base58 = "0.1.*" base64 = "0.9.*" +byteorder = "1.2.3" duniter-crypto = { path = "../crypto" } lazy_static = "1.0.*" linked-hash-map = "0.5.*" diff --git a/documents/lib.rs b/documents/lib.rs index 1ea9914223164135b15c3b245f0ad71d2135ff89..aaf595cb3847a1436826e1cf1cb3cbe6e921d156 100644 --- a/documents/lib.rs +++ b/documents/lib.rs @@ -30,15 +30,19 @@ extern crate serde_derive; extern crate base58; extern crate base64; +extern crate byteorder; extern crate crypto; extern crate duniter_crypto; extern crate linked_hash_map; extern crate regex; extern crate serde; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use duniter_crypto::keys::BaseConvertionError; use std::cmp::Ordering; use std::fmt::{Debug, Display, Error, Formatter}; +use std::io::Cursor; +use std::mem; pub mod blockchain; @@ -78,6 +82,11 @@ impl Default for Hash { } impl Hash { + /// Convert Hash into bytes vector + pub fn to_bytes_vector(&self) -> Vec<u8> { + self.0.to_vec() + } + /// Convert a `Hash` to an hex string. pub fn to_hex(&self) -> String { let strings: Vec<String> = self.0.iter().map(|b| format!("{:02X}", b)).collect(); @@ -210,7 +219,57 @@ impl Ord for Blockstamp { } } +#[derive(Debug)] +/// Error when converting a byte vector to Blockstamp +pub enum ReadBytesBlockstampError { + /// Bytes vector is too short + TooShort(), + /// Bytes vector is too long + TooLong(), + /// IoError + IoError(::std::io::Error), +} + +impl From<::std::io::Error> for ReadBytesBlockstampError { + fn from(e: ::std::io::Error) -> Self { + ReadBytesBlockstampError::IoError(e) + } +} + impl Blockstamp { + /// Convert Blockstamp into bytes vector + pub fn to_bytes_vector(&self) -> Vec<u8> { + let mut bytes = Vec::with_capacity(36); + // BlockId + let mut buffer = [0u8; mem::size_of::<u32>()]; + buffer + .as_mut() + .write_u32::<BigEndian>(self.id.0) + .expect("Unable to write"); + bytes.extend_from_slice(&buffer); + // BlockHash + bytes.extend(self.hash.0.to_bytes_vector()); + bytes + } + /// Create Blockstamp from bytes slice + pub fn from_bytes_slice(bytes: &[u8]) -> Result<Blockstamp, ReadBytesBlockstampError> { + if bytes.len() > 36 { + Err(ReadBytesBlockstampError::TooLong()) + } else if bytes.len() < 36 { + Err(ReadBytesBlockstampError::TooShort()) + } else { + // read id + let mut id_bytes = Cursor::new(bytes[0..4].to_vec()); + let id = BlockId(id_bytes.read_u32::<BigEndian>()?); + // read hash + let mut hash_datas: [u8; 32] = [0u8; 32]; + hash_datas.copy_from_slice(&bytes[4..36]); + let hash = BlockHash(Hash(hash_datas)); + // return Blockstamp + Ok(Blockstamp { id, hash }) + } + } + /// Create a `BlockUId` from a text. pub fn from_string(src: &str) -> Result<Blockstamp, BlockUIdParseError> { let mut split = src.split('-'); diff --git a/network/Cargo.toml b/network/Cargo.toml index f9e6c5df9d36b1a7cf1fcfef00fa9d8e2bd0a484..6313e20e8d5c8f85dfea8053fd01cc8b0ff93512 100644 --- a/network/Cargo.toml +++ b/network/Cargo.toml @@ -20,6 +20,9 @@ serde = "1.0.*" serde_derive = "1.0.*" serde_json = "1.0.*" +[dev-dependencies] +pretty_assertions = "0.5.1" + [features] # Treat warnings as a build error. strict = [] \ No newline at end of file diff --git a/network/lib.rs b/network/lib.rs index 4df1a8168d0ca02df5611f09e6f2b95cdc57e9c6..c6d1db69c60592121c9eecae58d3d14363f1b813 100644 --- a/network/lib.rs +++ b/network/lib.rs @@ -24,6 +24,9 @@ #[macro_use] extern crate lazy_static; +//#[cfg(test)] +//#[macro_use] +//extern crate pretty_assertions; #[macro_use] extern crate serde_derive; @@ -50,7 +53,7 @@ use duniter_documents::blockchain::Document; use duniter_documents::{BlockHash, BlockId, Blockstamp, Hash}; use duniter_module::*; use network_head::NetworkHead; -use network_peer::NetworkPeer; +use network_peer::PeerCard; use std::fmt::{Debug, Display, Error, Formatter}; use std::ops::Deref; use std::sync::mpsc; @@ -82,34 +85,34 @@ pub struct SyncEndpoint { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] /// Random identifier with which several Duniter nodes with the same network keypair can be differentiated -pub struct NodeUUID(pub u32); +pub struct NodeId(pub u32); -impl Default for NodeUUID { - fn default() -> NodeUUID { - NodeUUID(0) +impl Default for NodeId { + fn default() -> NodeId { + NodeId(0) } } -impl Display for NodeUUID { +impl Display for NodeId { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { write!(f, "{:x}", self.0) } } -impl<'a> From<&'a str> for NodeUUID { - fn from(source: &'a str) -> NodeUUID { - NodeUUID(u32::from_str_radix(source, 16).expect("Fail to parse NodeUUID")) +impl<'a> From<&'a str> for NodeId { + fn from(source: &'a str) -> NodeId { + NodeId(u32::from_str_radix(source, 16).expect("Fail to parse NodeId")) } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] /// Complete identifier of a duniter node. -pub struct NodeFullId(pub NodeUUID, pub PubKey); +pub struct NodeFullId(pub NodeId, pub PubKey); impl Default for NodeFullId { fn default() -> NodeFullId { NodeFullId( - NodeUUID::default(), + NodeId::default(), PubKey::Ed25519( ed25519::PublicKey::from_base58("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") .unwrap(), @@ -318,7 +321,7 @@ pub enum NetworkEvent { /// Receiving Pending Documents ReceiveDocuments(Vec<NetworkDocument>), /// Receipt of peer cards - ReceivePeers(Vec<NetworkPeer>), + ReceivePeers(Vec<PeerCard>), /// Receiving heads ReceiveHeads(Vec<NetworkHead>), } @@ -335,7 +338,7 @@ mod tests { ed25519::PublicKey::from_base58("D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx") .unwrap(), ); - let node_id = NodeUUID(u32::from_str_radix("c1c39a0a", 16).unwrap()); + let node_id = NodeId(u32::from_str_radix("c1c39a0a", 16).unwrap()); let full_id = NodeFullId(node_id, issuer); assert_eq!( NetworkEndpoint::parse_from_raw("WS2P c1c39a0a i3.ifee.fr 80 /ws2p", issuer, 0, 0, 1), @@ -361,7 +364,7 @@ mod tests { ed25519::PublicKey::from_base58("5gJYnQp8v7bWwk7EWRoL8vCLof1r3y9c6VDdnGSM1GLv") .unwrap(), ); - let node_id = NodeUUID(u32::from_str_radix("cb06a19b", 16).unwrap()); + let node_id = NodeId(u32::from_str_radix("cb06a19b", 16).unwrap()); let full_id = NodeFullId(node_id, issuer); assert_eq!( NetworkEndpoint::parse_from_raw("WS2P cb06a19b g1.imirhil.fr 53012 /", issuer, 0, 0, 1), diff --git a/network/network_endpoint.rs b/network/network_endpoint.rs index 85180159f25b470db77da34585602b6298e14331..b584d5f7ffa5bae3867390143fdfdc570c18594e 100644 --- a/network/network_endpoint.rs +++ b/network/network_endpoint.rs @@ -23,7 +23,7 @@ extern crate regex; extern crate serde; use self::regex::Regex; -use super::{NodeFullId, NodeUUID}; +use super::{NodeFullId, NodeId}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use duniter_crypto::keys::PubKey; use duniter_documents::Hash; @@ -128,7 +128,7 @@ pub struct NetworkEndpointV10 { /// API Name pub api: NetworkEndpointApi, /// Node unique identifier - pub node_id: Option<NodeUUID>, + pub node_id: Option<NodeId>, /// Public key of the node declaring this endpoint pub issuer: PubKey, /// NodeFullID hash @@ -230,7 +230,7 @@ impl EndpointV11Api { } } /// Convert api name into bytes vector - pub fn into_bytes(&self) -> Vec<u8> { + pub fn to_bytes_vector(&self) -> Vec<u8> { match *self { EndpointV11Api::Bin(api_bin_name) => vec![api_bin_name.into_u8()], EndpointV11Api::Str(ref api_name) => api_name.as_bytes().to_vec(), @@ -291,7 +291,7 @@ impl EndpointV11NetworkFeatures { self.0.len() as u8 } /// Convert Self into bytes - pub fn into_bytes(&self) -> &[u8] { + pub fn to_bytes_slice(&self) -> &[u8] { &self.0 } /// network feature ip_v4 is enable ? @@ -434,13 +434,13 @@ impl EndpointV11 { } } /// Convert endpoint into bytes vector - pub fn into_bytes(self) -> Vec<u8> { + pub fn into_bytes_vector(self) -> Vec<u8> { let endpoint_size = self.compute_endpoint_size(); let mut binary_endpoint = Vec::with_capacity(endpoint_size.total_size()); binary_endpoint.push(endpoint_size.api_size); binary_endpoint.push(endpoint_size.host_size); binary_endpoint.push(endpoint_size.path_size); - binary_endpoint.append(&mut self.api.into_bytes()); + binary_endpoint.append(&mut self.api.to_bytes_vector()); // api_version let mut buffer = [0u8; mem::size_of::<u16>()]; buffer @@ -451,7 +451,7 @@ impl EndpointV11 { // nf_size binary_endpoint.push(endpoint_size.nf_size); // network_features - binary_endpoint.extend_from_slice(&self.network_features.into_bytes()); + binary_endpoint.extend_from_slice(&self.network_features.to_bytes_slice()); binary_endpoint.push(endpoint_size.af_size); binary_endpoint.append(&mut self.api_features.clone()); if let Some(ip_v4) = self.ip_v4 { @@ -624,10 +624,14 @@ impl EndpointV11 { "All api features must be declared !", ))); } - for i in (4 + network_features_count) - ..(4 + network_features_count + api_features_count) + /*for i in (4 + network_features_count) + ..(4 + network_features_count + api_features_count)*/ + for str_feature in raw_ep_elements + .iter() + .take(4 + network_features_count + api_features_count) + .skip(4 + network_features_count) { - if let Ok(feature) = raw_ep_elements[i].parse::<usize>() { + if let Ok(feature) = str_feature.parse::<usize>() { if feature > *MAX_API_FEATURES_COUNT { return Err(ParseEndpointError::TooHighApiFeature()); } @@ -636,24 +640,24 @@ impl EndpointV11 { api_features[byte_index] += feature.pow(2); } else if let EndpointV11Api::Bin(know_api) = api { if let ApiKnownByDuniter::WS2P() = know_api { - match raw_ep_elements[i] { + match *str_feature { "DEF" => api_features[0] += 1u8, "LOW" => api_features[0] += 2u8, "ABF" => api_features[0] += 4u8, _ => { return Err(ParseEndpointError::UnknowApiFeature( - String::from(raw_ep_elements[i]), + String::from(*str_feature), )) } } } else { return Err(ParseEndpointError::UnknowApiFeature(String::from( - raw_ep_elements[i], + *str_feature, ))); } } else { return Err(ParseEndpointError::UnknowApiFeature(String::from( - raw_ep_elements[i], + *str_feature, ))); } } @@ -758,7 +762,7 @@ impl NetworkEndpoint { } } /// Accessors providing node unique identifier - pub fn node_uuid(&self) -> Option<NodeUUID> { + pub fn node_uuid(&self) -> Option<NodeId> { match *self { NetworkEndpoint::V10(ref ep) => ep.node_id, _ => panic!("Endpoint version is not supported !"), @@ -855,7 +859,7 @@ impl NetworkEndpoint { let node_id = match caps.name("uuid") { Some(caps_node_id) => { match u32::from_str_radix(caps_node_id.as_str(), 16) { - Ok(node_id) => Some(NodeUUID(node_id)), + Ok(node_id) => Some(NodeId(node_id)), Err(_) => None, } } @@ -914,7 +918,7 @@ mod tests { EndpointV11::parse_from_raw(str_endpoint, 0, 0), Ok(NetworkEndpoint::V11(endpoint.clone())), ); - let binary_endpoint = endpoint.clone().into_bytes(); + let binary_endpoint = endpoint.clone().into_bytes_vector(); assert_eq!( EndpointV11::from_bytes(&binary_endpoint) .expect("Fail to convert byte vector into endpoint !"), @@ -938,7 +942,7 @@ mod tests { last_check: 0, }; assert_eq!( - endpoint_v11.into_bytes(), + endpoint_v11.into_bytes_vector(), vec![ 0, 15, 4, 1, 0, 2, 1, 4, 1, 7, 103, 49, 46, 100, 117, 114, 115, 46, 105, 102, 101, 101, 46, 102, 114, 1, 187, 119, 115, 50, 112, @@ -966,7 +970,7 @@ mod tests { EndpointV11::parse_from_raw(str_endpoint, 0, 0), Ok(NetworkEndpoint::V11(endpoint.clone())), ); - let binary_endpoint = endpoint.clone().into_bytes(); + let binary_endpoint = endpoint.clone().into_bytes_vector(); assert_eq!( EndpointV11::from_bytes(&binary_endpoint) .expect("Fail to convert byte vector into endpoint !"), @@ -994,7 +998,7 @@ mod tests { EndpointV11::parse_from_raw(str_endpoint, 0, 0), Ok(NetworkEndpoint::V11(endpoint.clone())), ); - let binary_endpoint = endpoint.clone().into_bytes(); + let binary_endpoint = endpoint.clone().into_bytes_vector(); assert_eq!( EndpointV11::from_bytes(&binary_endpoint) .expect("Fail to convert byte vector into endpoint !"), @@ -1023,7 +1027,7 @@ mod tests { EndpointV11::parse_from_raw(str_endpoint, 0, 0), Ok(NetworkEndpoint::V11(endpoint.clone())), ); - let binary_endpoint = endpoint.clone().into_bytes(); + let binary_endpoint = endpoint.clone().into_bytes_vector(); assert_eq!( EndpointV11::from_bytes(&binary_endpoint) .expect("Fail to convert byte vector into endpoint !"), diff --git a/network/network_head.rs b/network/network_head.rs index 91bf2843e11cbcffa64cd1232f6b00c40576271a..cd32676d83ff7a9849eb62f8e811e581425f9a0f 100644 --- a/network/network_head.rs +++ b/network/network_head.rs @@ -19,7 +19,7 @@ extern crate duniter_crypto; extern crate duniter_documents; extern crate serde_json; -use super::{NodeFullId, NodeUUID}; +use super::{NodeFullId, NodeId}; use duniter_crypto::keys::*; use duniter_documents::Blockstamp; use std::cmp::Ordering; @@ -38,7 +38,7 @@ pub struct NetworkHeadMessageV2 { /// Head blockstamp pub blockstamp: Blockstamp, /// Head node id - pub node_uuid: NodeUUID, + pub node_uuid: NodeId, /// Issuer node software pub software: String, /// Issuer node soft version @@ -157,7 +157,7 @@ impl NetworkHeadMessage { version: source_array[2].parse().unwrap(), pubkey: PubKey::Ed25519(pubkey), blockstamp: Blockstamp::from_string(source_array[4]).unwrap(), - node_uuid: NodeUUID(u32::from_str_radix(source_array[5], 16).unwrap()), + node_uuid: NodeId(u32::from_str_radix(source_array[5], 16).unwrap()), software: source_array[6].to_string(), soft_version: source_array[7].to_string(), prefix: source_array[8].parse().unwrap(), @@ -184,7 +184,7 @@ impl NetworkHeadMessage { } } /// Get head node id - fn node_uuid(&self) -> NodeUUID { + fn node_uuid(&self) -> NodeId { match *self { NetworkHeadMessage::V2(ref head_message_v2) => head_message_v2.node_uuid, _ => panic!("This HEAD version is not supported !"), @@ -371,7 +371,7 @@ impl NetworkHead { } } /// Returns issuer node id - pub fn node_uuid(&self) -> NodeUUID { + pub fn node_uuid(&self) -> NodeId { match *self { NetworkHead::V2(ref head_v2) => head_v2.message_v2.node_uuid(), _ => panic!("This HEAD version is not supported !"), diff --git a/network/network_peer.rs b/network/network_peer.rs index b539af01dcaaff77a0b3f9e4557f0065aa7b5649..eae07036d9d8fee8ae748f6fc76cf95ef5e1aac9 100644 --- a/network/network_peer.rs +++ b/network/network_peer.rs @@ -21,13 +21,59 @@ extern crate duniter_documents; extern crate duniter_module; extern crate serde; -use super::network_endpoint::NetworkEndpoint; +use super::network_endpoint::*; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use duniter_crypto::keys::*; -use duniter_documents::Blockstamp; +use duniter_documents::{Blockstamp, ReadBytesBlockstampError}; +use std::io::Cursor; +use std::mem; +use NodeId; + +/// Total size of all fixed size fields of an PeerCardV11 +pub static PEER_CARDV11_FIXED_SIZE: &'static usize = &140; + +#[derive(Debug)] +/// Error when converting a byte vector to peerCard +pub enum PeerCardReadBytesError { + /// Bytes vector is too short + TooShort(String), + /// Bytes vector is too long + TooLong(), + /// IoError + IoError(::std::io::Error), + /// FromUtf8Error + FromUtf8Error(::std::string::FromUtf8Error), + /// ReadBytesBlockstampError + ReadBytesBlockstampError(ReadBytesBlockstampError), + /// EndpointReadBytesError + EndpointReadBytesError(EndpointReadBytesError), + /// too early version (don't support binary format) + TooEarlyVersion(), + /// Version not yet supported + VersionNotYetSupported(), +} + +impl From<::std::io::Error> for PeerCardReadBytesError { + fn from(e: ::std::io::Error) -> Self { + PeerCardReadBytesError::IoError(e) + } +} + +impl From<ReadBytesBlockstampError> for PeerCardReadBytesError { + fn from(e: ReadBytesBlockstampError) -> Self { + PeerCardReadBytesError::ReadBytesBlockstampError(e) + } +} + +impl From<EndpointReadBytesError> for PeerCardReadBytesError { + fn from(e: EndpointReadBytesError) -> Self { + PeerCardReadBytesError::EndpointReadBytesError(e) + } +} #[derive(Debug, Clone, PartialEq, Eq, Hash)] /// Peer card V10 -pub struct NetworkPeerV10 { +pub struct PeerCardV10 { /// Peer card Blockstamp pub blockstamp: Blockstamp, /// Peer card issuer @@ -36,34 +82,196 @@ pub struct NetworkPeerV10 { pub endpoints: Vec<NetworkEndpoint>, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +/// Peer card V11 +pub struct PeerCardV11 { + /// Currency code + pub currency_code: u16, + /// Peer card issuer + pub issuer: PubKey, + /// Issuer node id + pub node_id: NodeId, + /// Peer card Blockstamp + pub blockstamp: Blockstamp, + /// Peer card endpoints list + pub endpoints: Vec<EndpointV11>, + /// Signature + pub sig: Option<Sig>, +} + +impl PeerCardV11 { + /// Convert peer card into bytes vector + pub fn into_bytes_vector(self) -> Vec<u8> { + let endpoints_count = self.endpoints.len() as u8; + let mut binary_endpoints = vec![]; + for ep in self.endpoints { + binary_endpoints.push(ep.into_bytes_vector()); + } + let endpoints_size: usize = binary_endpoints.iter().map(|bin_ep| bin_ep.len() + 2).sum(); + let peer_card_size = *PEER_CARDV11_FIXED_SIZE + endpoints_size; + let mut binary_peer_card = Vec::with_capacity(peer_card_size + 2); + // peer_card_size + let mut buffer = [0u8; mem::size_of::<u16>()]; + buffer + .as_mut() + .write_u16::<BigEndian>(peer_card_size as u16) + .expect("Unable to write"); + binary_peer_card.extend_from_slice(&buffer); + // version + binary_peer_card.push(11u8); + // endpoints_count + binary_peer_card.push(endpoints_count); + // currency_code + let mut buffer = [0u8; mem::size_of::<u16>()]; + buffer + .as_mut() + .write_u16::<BigEndian>(self.currency_code) + .expect("Unable to write"); + binary_peer_card.extend_from_slice(&buffer); + // node_id + let mut buffer = [0u8; mem::size_of::<u32>()]; + buffer + .as_mut() + .write_u32::<BigEndian>(self.node_id.0) + .expect("Unable to write"); + binary_peer_card.extend_from_slice(&buffer); + // issuer_public_key + binary_peer_card.extend(self.issuer.to_bytes_vector()); + // blockstamp + binary_peer_card.extend(self.blockstamp.to_bytes_vector()); + // endpoints_datas + for bin_ep in binary_endpoints { + let mut buffer = [0u8; mem::size_of::<u16>()]; + buffer + .as_mut() + .write_u16::<BigEndian>(bin_ep.len() as u16) + .expect("Unable to write"); + binary_peer_card.extend_from_slice(&buffer); + binary_peer_card.extend(bin_ep); + } + // sig + if let Some(sig) = self.sig { + binary_peer_card.extend(sig.to_bytes_vector()); + } + binary_peer_card + } + /// Create peer card from bytes vector (without peer_card_size field) + pub fn from_bytes(binary_peer_card: &[u8]) -> Result<PeerCardV11, PeerCardReadBytesError> { + let mut index = 0; + // read version + let version = binary_peer_card[index]; + index += 1; + // read endpoints_count + let endpoints_count = binary_peer_card[index]; + index += 1; + // read currency_code + let mut currency_code_bytes = Cursor::new(binary_peer_card[index..index + 2].to_vec()); + index += 2; + let currency_code = currency_code_bytes.read_u16::<BigEndian>()?; + // read node_id + let mut node_id_bytes = Cursor::new(binary_peer_card[index..index + 4].to_vec()); + index += 4; + let node_id = NodeId(node_id_bytes.read_u32::<BigEndian>()?); + // read issuer_public_key + let issuer = if binary_peer_card.len() > index + 32 { + index += 32; + let mut issuer_datas: [u8; 32] = [0u8; 32]; + issuer_datas.copy_from_slice(&binary_peer_card[index - 32..index]); + PubKey::Ed25519(ed25519::PublicKey(issuer_datas)) + } else { + return Err(PeerCardReadBytesError::TooShort(String::from("issuer"))); + }; + // read blockstamp + let blockstamp = if binary_peer_card.len() > index + 36 { + index += 36; + Blockstamp::from_bytes_slice(&binary_peer_card[index - 36..index])? + } else { + return Err(PeerCardReadBytesError::TooShort(String::from("blockstamp"))); + }; + // read endpoints_datas + let mut endpoints = Vec::with_capacity(endpoints_count as usize); + for _ in 0..endpoints_count { + // read endpoint_size + if binary_peer_card.len() < index + 2 { + return Err(PeerCardReadBytesError::TooShort(String::from( + "endpoint_size", + ))); + } + + let mut endpoint_size_bytes = Cursor::new(binary_peer_card[index..index + 2].to_vec()); + index += 2; + let endpoint_size = endpoint_size_bytes.read_u16::<BigEndian>()?; + // read endpoint_datas + if binary_peer_card.len() < index + endpoint_size as usize { + return Err(PeerCardReadBytesError::TooShort(format!( + "endpoint_datas (expected >= {} found {})", + index + endpoint_size as usize, + binary_peer_card.len() + ))); + } + endpoints.push(EndpointV11::from_bytes( + &binary_peer_card[index..index + endpoint_size as usize], + )?); + index += endpoint_size as usize; + } + // read signature + let sig = if binary_peer_card.len() > index + 64 { + return Err(PeerCardReadBytesError::TooLong()); + } else if binary_peer_card.len() == index + 64 { + let mut sig_datas: [u8; 64] = [0u8; 64]; + sig_datas.copy_from_slice(&binary_peer_card[index..index + 64]); + Some(Sig::Ed25519(ed25519::Signature(sig_datas))) + } else if binary_peer_card.len() > index { + return Err(PeerCardReadBytesError::TooLong()); + } else if binary_peer_card.len() == index { + None + } else { + return Err(PeerCardReadBytesError::TooShort(String::from("end"))); + }; + + match version { + tmp if tmp > 11 => Err(PeerCardReadBytesError::VersionNotYetSupported()), + 11 => Ok(PeerCardV11 { + currency_code, + issuer, + node_id, + blockstamp, + endpoints, + sig, + }), + _ => Err(PeerCardReadBytesError::TooEarlyVersion()), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] /// Peer card -pub enum NetworkPeer { +pub enum PeerCard { /// Peer card V10 - V10(NetworkPeerV10), + V10(PeerCardV10), /// Peer card V11 - V11(), + V11(PeerCardV11), } -impl NetworkPeer { +impl PeerCard { /// Get peer card version pub fn version(&self) -> u32 { match *self { - NetworkPeer::V10(ref _peer_v10) => 10, - _ => panic!("Peer version is not supported !"), + PeerCard::V10(ref _peer_v10) => 10, + PeerCard::V11(ref _peer_v11) => 11, } } /// Get peer card blockstamp pub fn blockstamp(&self) -> Blockstamp { match *self { - NetworkPeer::V10(ref peer_v10) => peer_v10.blockstamp, + PeerCard::V10(ref peer_v10) => peer_v10.blockstamp, _ => panic!("Peer version is not supported !"), } } /// Get peer card issuer pub fn issuer(&self) -> PubKey { match *self { - NetworkPeer::V10(ref peer_v10) => peer_v10.issuer, + PeerCard::V10(ref peer_v10) => peer_v10.issuer, _ => panic!("Peer version is not supported !"), } } @@ -76,3 +284,76 @@ impl NetworkPeer { Vec::with_capacity(0) } } + +#[cfg(test)] +mod tests { + use super::*; + use std::net::Ipv4Addr; + use std::str::FromStr; + + fn create_endpoint_v11() -> EndpointV11 { + EndpointV11 { + api: EndpointV11Api::Bin(ApiKnownByDuniter::WS2P()), + api_version: 2, + network_features: EndpointV11NetworkFeatures(vec![4u8]), + api_features: vec![7u8], + ip_v4: None, + ip_v6: None, + host: Some(String::from("g1.durs.ifee.fr")), + port: 443u16, + path: Some(String::from("ws2p")), + status: 0, + last_check: 0, + } + } + fn create_second_endpoint_v11() -> EndpointV11 { + EndpointV11 { + api: EndpointV11Api::Bin(ApiKnownByDuniter::WS2P()), + api_version: 2, + network_features: EndpointV11NetworkFeatures(vec![5u8]), + api_features: vec![7u8], + ip_v4: Some(Ipv4Addr::from_str("84.16.72.210").unwrap()), + ip_v6: None, + host: None, + port: 443u16, + path: Some(String::from("ws2p")), + status: 0, + last_check: 0, + } + } + + #[test] + fn test_convert_peer_card_v11_into_bytes_vector() { + let peer_card_v11 = PeerCardV11 { + currency_code: 1u16, + issuer: PubKey::Ed25519( + ed25519::PublicKey::from_base58("D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx") + .unwrap(), + ), + node_id: NodeId(0), + blockstamp: Blockstamp::from_string( + "50-000005B1CEB4EC5245EF7E33101A330A1C9A358EC45A25FC13F78BB58C9E7370", + ).unwrap(), + endpoints: vec![create_endpoint_v11(), create_second_endpoint_v11()], + sig: None, + }; + let peer_card_v11_bytes = peer_card_v11.clone().into_bytes_vector(); + assert_eq!( + peer_card_v11_bytes, + vec![ + 0, 195, 11, 2, 0, 1, 0, 0, 0, 0, 180, 102, 127, 51, 114, 94, 79, 133, 126, 196, + 144, 254, 52, 196, 225, 10, 252, 31, 249, 217, 220, 10, 64, 221, 246, 95, 190, 249, + 34, 159, 139, 243, 0, 0, 0, 50, 0, 0, 5, 177, 206, 180, 236, 82, 69, 239, 126, 51, + 16, 26, 51, 10, 28, 154, 53, 142, 196, 90, 37, 252, 19, 247, 139, 181, 140, 158, + 115, 112, 0, 31, 0, 15, 4, 1, 0, 2, 1, 4, 1, 7, 103, 49, 46, 100, 117, 114, 115, + 46, 105, 102, 101, 101, 46, 102, 114, 1, 187, 119, 115, 50, 112, 0, 20, 0, 0, 4, 1, + 0, 2, 1, 5, 1, 7, 84, 16, 72, 210, 1, 187, 119, 115, 50, 112, + ] + ); + assert_eq!( + peer_card_v11, + PeerCardV11::from_bytes(&peer_card_v11_bytes[2..]) + .expect("Fail to parse peer card bytes vector"), + ) + } +} diff --git a/ws2p/datas.rs b/ws2p/datas.rs index 0177cf120786960addd972c5046ac558de09ce39..1a81444e0b245d128393563936d3a5c0ae704991 100644 --- a/ws2p/datas.rs +++ b/ws2p/datas.rs @@ -31,7 +31,7 @@ pub struct WS2PModuleDatas { pub currency: Option<String>, pub key_pair: Option<KeyPairEnum>, pub conf: WS2PConf, - pub node_id: NodeUUID, + pub node_id: NodeId, pub main_thread_channel: ( mpsc::Sender<WS2PThreadSignal>, mpsc::Receiver<WS2PThreadSignal>, diff --git a/ws2p/heads.rs b/ws2p/heads.rs index 5528e630d4b596cd2eb6f003cc0c853fb49db2bd..26d1fd72d4d97880903e126154d48f5cb1638c8b 100644 --- a/ws2p/heads.rs +++ b/ws2p/heads.rs @@ -19,7 +19,7 @@ use *; pub fn generate_my_head( network_keypair: &KeyPairEnum, - node_id: NodeUUID, + node_id: NodeId, soft_name: &str, soft_version: &str, my_current_blockstamp: &Blockstamp, diff --git a/ws2p/lib.rs b/ws2p/lib.rs index ec3fe6bd6d3a22776a5685a53ba0840880da13f6..d5d7758f0bae788246e04c2ba1c155a918210f26 100644 --- a/ws2p/lib.rs +++ b/ws2p/lib.rs @@ -219,7 +219,7 @@ impl DuniterModule<DuRsConf, DuniterMessage> for WS2PModule { key_pair: None, currency: None, conf, - node_id: NodeUUID(soft_meta_datas.conf.my_node_id()), + node_id: NodeId(soft_meta_datas.conf.my_node_id()), main_thread_channel: mpsc::channel(), ws2p_endpoints: HashMap::new(), websockets: HashMap::new(), @@ -349,7 +349,7 @@ impl DuniterModule<DuRsConf, DuniterMessage> for WS2PModule { if ws2p_module.my_head.is_none() { ws2p_module.my_head = Some(heads::generate_my_head( &key_pair, - NodeUUID(soft_meta_datas.conf.my_node_id()), + NodeId(soft_meta_datas.conf.my_node_id()), soft_meta_datas.soft_name, soft_meta_datas.soft_version, ¤t_blockstamp, @@ -435,7 +435,7 @@ impl DuniterModule<DuRsConf, DuniterMessage> for WS2PModule { ); ws2p_module.my_head = Some(heads::generate_my_head( &key_pair, - NodeUUID(soft_meta_datas.conf.my_node_id()), + NodeId(soft_meta_datas.conf.my_node_id()), soft_meta_datas.soft_name, soft_meta_datas.soft_version, ¤t_blockstamp, @@ -488,7 +488,7 @@ impl DuniterModule<DuRsConf, DuniterMessage> for WS2PModule { ws2p_module.my_head = Some(heads::generate_my_head( &key_pair, - NodeUUID(soft_meta_datas.conf.my_node_id()), + NodeId(soft_meta_datas.conf.my_node_id()), soft_meta_datas.soft_name, soft_meta_datas.soft_version, ¤t_blockstamp, diff --git a/ws2p/ws2p_connection.rs b/ws2p/ws2p_connection.rs index d4a50aa45f860900f6624aa0be667084ca0acebb..09ea0134763157e5819b4a31651b324cb4c6db94 100644 --- a/ws2p/ws2p_connection.rs +++ b/ws2p/ws2p_connection.rs @@ -2,7 +2,7 @@ use constants::*; use duniter_crypto::keys::*; use duniter_module::ModuleReqId; use duniter_network::network_endpoint::{NetworkEndpoint, NetworkEndpointApi}; -use duniter_network::{NetworkDocument, NodeUUID}; +use duniter_network::{NetworkDocument, NodeId}; use parsers::blocks::parse_json_block; use rand::Rng; use std::sync::mpsc; @@ -292,7 +292,7 @@ pub enum WS2PCloseConnectionReason { #[derive(Debug, Clone)] pub struct WS2PConnectionMetaDatas { pub state: WS2PConnectionState, - pub remote_uuid: Option<NodeUUID>, + pub remote_uuid: Option<NodeId>, pub remote_pubkey: Option<PubKey>, pub challenge: String, pub remote_challenge: String, diff --git a/ws2p/ws2p_db.rs b/ws2p/ws2p_db.rs index 740ef46af0b32fa1820a1c7f176f17949e141d0e..2dbc60b14e8dbef7fbf415bc6b615474d24263cc 100644 --- a/ws2p/ws2p_db.rs +++ b/ws2p/ws2p_db.rs @@ -79,7 +79,10 @@ pub fn get_endpoints_for_api( 1u16, ) { Ok(ep) => ep, - Err(_) => panic!(format!("Fail to parse endpoint : {}", raw_ep)), + Err(e) => panic!(format!( + "Fail to parse endpoint : {} (Error: {:?})", + raw_ep, e + )), }; ep.set_status(row[1].as_integer().unwrap() as u32); ep.set_last_check(row[7].as_integer().unwrap() as u64);