diff --git a/Cargo.lock b/Cargo.lock index 976ea54..e89142c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "http" diff --git a/Cargo.toml b/Cargo.toml index 2fd2cc2..4eef62d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ nom = "6.1.2" blake2b_simd = "0.5" chrono = { version = "0.4.23", "features" = ["serde"] } log = { version = "0.4.19", "features" = ["std"] } -hex = "0.4.2" +hex = { version = "0.4.2", "features" = ["serde"] } reqwest = { version = "0.11.9", features = ["json", "rustls-tls"], default-features = false } base64 = "0.21.2" url = { version = "2.2.2", features = ["serde"] } diff --git a/src/transport/endpoints.rs b/src/transport/endpoints.rs index 343b6ef..469355e 100644 --- a/src/transport/endpoints.rs +++ b/src/transport/endpoints.rs @@ -165,7 +165,7 @@ impl SiaApiRequest for ConsensusUpdatesRequest { // Create the path_params HashMap to substitute {height} and {hash} in the path schema let mut path_params = HashMap::new(); path_params.insert("height".to_owned(), self.height.to_string()); - path_params.insert("hash".to_owned(), format!("{:02x}", self.block_hash.0)); + path_params.insert("hash".to_owned(), format!("{}", self.block_hash.0)); let mut query_params = HashMap::new(); if let Some(limit) = self.limit { diff --git a/src/types.rs b/src/types.rs index 01d1212..b04a8ea 100644 --- a/src/types.rs +++ b/src/types.rs @@ -11,7 +11,7 @@ use std::fmt; use std::str::FromStr; mod hash; -pub use hash::{Hash256, ParseHashError}; +pub use hash::{Hash256, Hash256Error}; mod signature; pub use signature::{Signature, SignatureError}; @@ -184,7 +184,7 @@ impl<'de> Deserialize<'de> for BlockID { E: serde::de::Error, { if let Some(hex_str) = value.strip_prefix("bid:") { - Hash256::from_str_no_prefix(hex_str) + Hash256::from_str(hex_str) .map(BlockID) .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) } else { @@ -207,7 +207,7 @@ impl Serialize for BlockID { } impl fmt::Display for BlockID { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "bid:{:02x}", self.0) } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] diff --git a/src/types/hash.rs b/src/types/hash.rs index 250f70c..03f992c 100644 --- a/src/types/hash.rs +++ b/src/types/hash.rs @@ -1,110 +1,45 @@ -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use hex; +use serde::{Deserialize, Serialize}; use std::convert::TryFrom; -use std::fmt; +use std::fmt::{self, Display}; use std::str::FromStr; use thiserror::Error; #[derive(Debug, Error)] -pub enum ParseHashError { - #[error("Hash256 invalid prefix: expected 32 byte hex string prefixed with 'h:', found {0}")] - InvalidPrefix(String), - #[error("Hash256 invalid hex: expected 32 byte hex string prefixed with 'h:', found {0}")] +pub enum Hash256Error { + #[error("Hash256::from_str invalid hex: expected 32 byte hex string, found {0}")] InvalidHex(String), - #[error("Hash256 invalid length: expected 32 byte hex string prefixed with 'h:', found {0}")] + #[error("Hash256::from_str invalid length: expected 32 byte hex string, found {0}")] InvalidLength(String), - #[error("Hash256 invalid slice length: expected 32 byte slice, found {0} byte slice")] - InvalidSliceLength(usize), + #[error("Hash256::TryFrom<&[u8]> invalid slice length: expected 32 byte slice, found {0:?}")] + InvalidSliceLength(Vec), } -#[derive(Clone, Eq, PartialEq)] -pub struct Hash256(pub [u8; 32]); +#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[serde(transparent)] +pub struct Hash256(#[serde(with = "hex::serde")] pub [u8; 32]); -impl Hash256 { - const fn const_default() -> Hash256 { Hash256([0; 32]) } +impl FromStr for Hash256 { + type Err = Hash256Error; - // Method for parsing a hex string without the "h:" prefix - pub fn from_str_no_prefix(hex_str: &str) -> Result { + fn from_str(hex_str: &str) -> Result { if hex_str.len() != 64 { - return Err(ParseHashError::InvalidLength(hex_str.to_string())); + return Err(Hash256Error::InvalidLength(hex_str.to_string())); } let mut bytes = [0u8; 32]; match hex::decode_to_slice(hex_str, &mut bytes) { Ok(_) => Ok(Hash256(bytes)), - Err(_) => Err(ParseHashError::InvalidHex(hex_str.to_string())), - } - } -} - -impl Default for Hash256 { - fn default() -> Self { Hash256::const_default() } -} - -impl fmt::Display for Hash256 { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "h:{:02x}", self) } -} - -impl fmt::Debug for Hash256 { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) } -} - -impl fmt::LowerHex for Hash256 { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - for byte in &self.0 { - write!(f, "{:02x}", byte)?; + Err(_) => Err(Hash256Error::InvalidHex(hex_str.to_string())), } - Ok(()) } } -impl Serialize for Hash256 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl<'de> Deserialize<'de> for Hash256 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct H256Visitor; - - impl<'de> serde::de::Visitor<'de> for H256Visitor { - type Value = Hash256; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string prefixed with 'h:' and followed by a 32 byte hex string") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - Hash256::from_str(value).map_err(|_| E::invalid_value(serde::de::Unexpected::Str(value), &self)) - } - } - - deserializer.deserialize_str(H256Visitor) - } -} - -impl FromStr for Hash256 { - type Err = ParseHashError; - - fn from_str(value: &str) -> Result { - if let Some(hex_str) = value.strip_prefix("h:") { - Hash256::from_str_no_prefix(hex_str) - } else { - Err(ParseHashError::InvalidPrefix(value.to_string())) - } - } +impl Display for Hash256 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", hex::encode(&self.0)) } } impl TryFrom<&[u8]> for Hash256 { - type Error = ParseHashError; + type Error = Hash256Error; fn try_from(slice: &[u8]) -> Result { let slice_len = slice.len(); @@ -113,7 +48,7 @@ impl TryFrom<&[u8]> for Hash256 { array.copy_from_slice(slice); Ok(Hash256(array)) } else { - Err(ParseHashError::InvalidSliceLength(slice_len)) + Err(Hash256Error::InvalidSliceLength(slice.to_owned())) } } } @@ -125,115 +60,69 @@ mod tests { cross_target_tests! { fn test_default() { - let hash = Hash256::from_str("h:0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let hash = Hash256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); assert_eq!(hash, Hash256::default()); } fn test_valid() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); - assert_eq!(hash.to_string(), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + let hash = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + assert_eq!(hash.to_string(), "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); } fn test_display() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); - assert_eq!(hash.to_string(), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); - } - - fn test_debug() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); - assert_eq!(format!("{:?}", hash), "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); + let hash = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + assert_eq!(hash.to_string(), "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); } fn test_serialize() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let hash = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); let serialized = serde_json::to_string(&hash).unwrap(); - assert_eq!(&serialized, r#""h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#); + assert_eq!(&serialized, r#""c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#); } fn test_deserialize() { - let hash = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); - let deserialized: Hash256 = serde_json::from_str(r#""h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).unwrap(); + let hash = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let deserialized: Hash256 = serde_json::from_str(r#""c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).unwrap(); assert_eq!(deserialized, hash); } - fn test_deserialize_missing_prefix() { - let err = serde_json::from_str::(r#""c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee""#).expect_err("no prefix"); - assert!(format!("{:?}", err).contains("expected a string prefixed with 'h:' and followed by a 32 byte hex string")); - } - - fn test_missing_prefix() { - let test_case = "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::from_str(test_case).expect_err("no prefix"); - match err { - ParseHashError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_corrupt_prefix() { - let test_case = ":c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::from_str(test_case).expect_err("no prefix"); - match err { - ParseHashError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_wrong_prefix() { - let test_case = "i:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - let err = Hash256::from_str(test_case).expect_err("wrong prefix"); - match err { - ParseHashError::InvalidPrefix(ref e) if test_case == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - fn test_invalid_hex() { - let err = Hash256::from_str("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg").expect_err("no prefix"); + let err = Hash256::from_str("c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg").expect_err("no prefix"); let expected = "c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeg"; match err { - ParseHashError::InvalidHex(ref e) if expected == e => (), + Hash256Error::InvalidHex(ref e) if expected == e => (), _ => panic!("unexpected error: {:?}", err), } } fn test_invalid_length() { - let err = Hash256::from_str("h:badc0de").expect_err("invalid length"); + let err = Hash256::from_str("badc0de").expect_err("invalid length"); let expected = "badc0de"; match err { - ParseHashError::InvalidLength(ref e) if expected == e => (), + Hash256Error::InvalidLength(ref e) if expected == e => (), _ => panic!("unexpected error: {:?}", err), } } - fn test_from_str_no_prefix_valid() { - let hash = Hash256::from_str_no_prefix("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); + fn test_from_str_valid() { + let hash = Hash256::from_str("0000000000000000000000000000000000000000000000000000000000000000").unwrap(); assert_eq!(hash, Hash256::default()) } - fn test_from_str_no_prefix_invalid_length() { - let err = Hash256::from_str_no_prefix("badc0de").expect_err("invalid length"); + fn test_from_str_invalid_length() { + let err = Hash256::from_str("badc0de").expect_err("invalid length"); let expected = "badc0de"; match err { - ParseHashError::InvalidLength(ref e) if expected == e => (), + Hash256Error::InvalidLength(ref e) if expected == e => (), _ => panic!("unexpected error: {:?}", err), } } - fn test_from_str_no_prefix_invalid_hex() { - let err = Hash256::from_str_no_prefix("g00000000000000000000000000000000000000000000000000000000000000e").expect_err("invalid hex"); + fn test_from_str_invalid_hex() { + let err = Hash256::from_str("g00000000000000000000000000000000000000000000000000000000000000e").expect_err("invalid hex"); let expected = "g00000000000000000000000000000000000000000000000000000000000000e"; match err { - ParseHashError::InvalidHex(ref e) if expected == e => (), - _ => panic!("unexpected error: {:?}", err), - } - } - - fn test_from_str_no_prefix_invalid_has_prefix() { - let err = Hash256::from_str_no_prefix("h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").expect_err("invalid hex"); - let expected = "h:c0ffeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; - match err { - ParseHashError::InvalidLength(ref e) if expected == e => (), + Hash256Error::InvalidHex(ref e) if expected == e => (), _ => panic!("unexpected error: {:?}", err), } }