From 7aa08917ea37fd68c052753b9d27c1ccce3933ad Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 20 Aug 2018 12:04:22 +0200 Subject: [PATCH] Simplify the implementation of peer_info (#327) * Simplify the implementation of peer_info * Add Serde-Support to MultiAddr --- multiaddr/Cargo.toml | 3 ++ multiaddr/src/lib.rs | 33 +++++++++++++++++ multiaddr/tests/lib.rs | 23 ++++++++++++ peerstore/Cargo.toml | 4 +-- peerstore/src/peer_info.rs | 72 +++----------------------------------- 5 files changed, 66 insertions(+), 69 deletions(-) diff --git a/multiaddr/Cargo.toml b/multiaddr/Cargo.toml index da75c138..fbe679c8 100644 --- a/multiaddr/Cargo.toml +++ b/multiaddr/Cargo.toml @@ -13,6 +13,9 @@ bs58 = "0.2.0" byteorder = "~0.4" multihash = { path = "../multihash" } integer-encoding = "~1.0.3" +serde = "1.0.70" [dev-dependencies] data-encoding = "~1.1.2" +serde_json = "1.0" +bincode = "1.0.1" \ No newline at end of file diff --git a/multiaddr/src/lib.rs b/multiaddr/src/lib.rs index 0b164a5c..4d3ebe07 100644 --- a/multiaddr/src/lib.rs +++ b/multiaddr/src/lib.rs @@ -6,6 +6,7 @@ extern crate bs58; extern crate byteorder; extern crate integer_encoding; +extern crate serde; pub extern crate multihash; mod protocol; @@ -14,7 +15,10 @@ mod errors; pub use errors::{Result, Error}; pub use protocol::{Protocol, ProtocolArgSize, AddrComponent}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as DeserializerError}; + use std::fmt; +use std::result::Result as StdResult; use std::iter::FromIterator; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6, IpAddr, Ipv4Addr, Ipv6Addr}; use std::str::FromStr; @@ -25,6 +29,35 @@ pub struct Multiaddr { bytes: Vec, } +impl Serialize for Multiaddr { + fn serialize(&self, serializer: S) -> StdResult + where + S: Serializer, + { + if serializer.is_human_readable() { + // Serialize to a human-readable string "2015-05-15T17:01:00Z". + self.to_string().serialize(serializer) + } else { + self.to_bytes().serialize(serializer) + } + } +} + +impl<'de> Deserialize<'de> for Multiaddr { + fn deserialize(deserializer: D) -> StdResult + where + D: Deserializer<'de>, + { + if deserializer.is_human_readable() { + let addr: String = Deserialize::deserialize(deserializer)?; + addr.parse::().map_err(|err| DeserializerError::custom(err)) + } else { + let addr: Vec = Deserialize::deserialize(deserializer)?; + Multiaddr::from_bytes(addr).map_err(|err| DeserializerError::custom(err)) + } + } +} + impl fmt::Debug for Multiaddr { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/multiaddr/tests/lib.rs b/multiaddr/tests/lib.rs index 0f28bb03..17085363 100644 --- a/multiaddr/tests/lib.rs +++ b/multiaddr/tests/lib.rs @@ -1,5 +1,7 @@ extern crate multiaddr; extern crate data_encoding; +extern crate serde_json; +extern crate bincode; use data_encoding::hex; use multiaddr::*; @@ -173,3 +175,24 @@ fn from_bytes_fail() { let bytes = vec![1, 2, 3, 4]; assert!(Multiaddr::from_bytes(bytes).is_err()); } + + +#[test] +fn ser_and_deser_json() { + let addr : Multiaddr = "/ip4/0.0.0.0/tcp/0".parse::().unwrap(); + let serialized = serde_json::to_string(&addr).unwrap(); + assert_eq!(serialized, "\"/ip4/0.0.0.0/tcp/0\""); + let deserialized: Multiaddr = serde_json::from_str(&serialized).unwrap(); + assert_eq!(addr, deserialized); +} + + +#[test] +fn ser_and_deser_bincode() { + let addr : Multiaddr = "/ip4/0.0.0.0/tcp/0".parse::().unwrap(); + let serialized = bincode::serialize(&addr).unwrap(); + // compact addressing + assert_eq!(serialized, vec![8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 6, 0, 0]); + let deserialized: Multiaddr = bincode::deserialize(&serialized).unwrap(); + assert_eq!(addr, deserialized); +} \ No newline at end of file diff --git a/peerstore/Cargo.toml b/peerstore/Cargo.toml index 9a287edb..8e36ce86 100644 --- a/peerstore/Cargo.toml +++ b/peerstore/Cargo.toml @@ -11,8 +11,8 @@ futures = "0.1.0" owning_ref = "0.3.3" libp2p-core = { path = "../core" } multiaddr = { path = "../multiaddr" } -serde = "1.0" -serde_derive = "1.0" +serde = "1.0.70" +serde_derive = "1.0.70" [dev-dependencies] tempfile = "2.2" diff --git a/peerstore/src/peer_info.rs b/peerstore/src/peer_info.rs index 76d1c4c6..7641c71f 100644 --- a/peerstore/src/peer_info.rs +++ b/peerstore/src/peer_info.rs @@ -27,15 +27,12 @@ //! more thoughts about this. use multiaddr::Multiaddr; -use serde::de::Error as DeserializerError; -use serde::ser::SerializeStruct; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cmp::Ordering; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::SystemTime; use TTL; /// Information about a peer. -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] pub struct PeerInfo { // Adresses, and the time at which they will be considered expired. addrs: Vec<(Multiaddr, SystemTime)>, @@ -108,67 +105,6 @@ pub enum AddAddrBehaviour { IgnoreTtlIfInferior, } -impl Serialize for PeerInfo { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut s = serializer.serialize_struct("PeerInfo", 2)?; - s.serialize_field( - "addrs", - &self.addrs - .iter() - .map(|&(ref addr, ref expires)| { - let addr = addr.to_string(); - let from_epoch = expires.duration_since(UNIX_EPOCH) - // This `unwrap_or` case happens if the user has their system time set to - // before EPOCH. Times-to-live will be be longer than expected, but it's a very - // improbable corner case and is not attackable in any way, so we don't really - // care. - .unwrap_or(Duration::new(0, 0)); - let secs = from_epoch - .as_secs() - .saturating_mul(1_000) - .saturating_add(from_epoch.subsec_nanos() as u64 / 1_000_000); - (addr, secs) - }) - .collect::>(), - )?; - s.end() - } -} - -impl<'de> Deserialize<'de> for PeerInfo { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - // We deserialize to an intermdiate struct first, then turn that struct into a `PeerInfo`. - let interm = { - #[derive(Deserialize)] - struct Interm { - addrs: Vec<(String, u64)>, - } - Interm::deserialize(deserializer)? - }; - - let addrs = { - let mut out = Vec::with_capacity(interm.addrs.len()); - for (addr, since_epoch) in interm.addrs { - let addr = match addr.parse::() { - Ok(a) => a, - Err(err) => return Err(DeserializerError::custom(err)), - }; - let expires = UNIX_EPOCH + Duration::from_millis(since_epoch); - out.push((addr, expires)); - } - out - }; - - Ok(PeerInfo { addrs: addrs }) - } -} - // The reason why we need to implement the PartialOrd trait is that the datastore library (a // key-value storage) which we use allows performing queries where the results can be ordered. // @@ -181,10 +117,12 @@ impl PartialOrd for PeerInfo { } } + #[cfg(test)] mod tests { extern crate serde_json; use super::*; + use std::time::UNIX_EPOCH; #[test] fn ser_and_deser() { @@ -198,4 +136,4 @@ mod tests { let deserialized: PeerInfo = serde_json::from_str(&serialized).unwrap(); assert_eq!(peer_info, deserialized); } -} +} \ No newline at end of file