diff --git a/Cargo.toml b/Cargo.toml index 59b22722cc..9f3a584389 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,5 +34,6 @@ serde = { version = "1", features = [ "derive" ], optional = true } serde_json = "<1.0.45" serde_test = "1" secp256k1 = { version = "0.20.0", features = [ "recovery", "rand-std" ] } +bincode = "1.3.1" # We need to pin ryu (transitive dep from serde_json) to stay compatible with Rust 1.22.0 ryu = "<1.0.5" diff --git a/src/lib.rs b/src/lib.rs index 7007cf9ebe..e7387de444 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ pub extern crate bech32; #[cfg(feature = "serde")] #[macro_use] extern crate serde; #[cfg(all(test, feature = "serde"))] extern crate serde_json; #[cfg(all(test, feature = "serde"))] extern crate serde_test; +#[cfg(all(test, feature = "serde"))] extern crate bincode; #[cfg(all(test, feature = "unstable"))] extern crate test; #[cfg(target_pointer_width = "16")] diff --git a/src/util/endian.rs b/src/util/endian.rs index d4ae5ec4d6..e7c620f5ba 100644 --- a/src/util/endian.rs +++ b/src/util/endian.rs @@ -54,6 +54,7 @@ macro_rules! define_le_to_array { define_slice_to_be!(slice_to_u32_be, u32); define_slice_to_be!(slice_to_u64_be, u64); define_be_to_array!(u32_to_array_be, u32, 4); +define_be_to_array!(u64_to_array_be, u64, 8); define_slice_to_le!(slice_to_u16_le, u16); define_slice_to_le!(slice_to_u32_le, u32); define_slice_to_le!(slice_to_u64_le, u64); diff --git a/src/util/uint.rs b/src/util/uint.rs index 314ac6b014..064ce1212c 100644 --- a/src/util/uint.rs +++ b/src/util/uint.rs @@ -90,9 +90,23 @@ macro_rules! construct_uint { } } - /// Creates big integer value from a byte slice array using + /// Creates big integer value from a byte array using /// big-endian encoding pub fn from_be_bytes(bytes: [u8; $n_words * 8]) -> $name { + Self::_from_be_slice(&bytes) + } + + /// Creates big integer value from a byte slice using + /// big-endian encoding + pub fn from_be_slice(bytes: &[u8]) -> Result<$name, ParseLengthError> { + if bytes.len() != $n_words * 8 { + Err(ParseLengthError { actual: bytes.len(), expected: $n_words*8 }) + } else { + Ok(Self::_from_be_slice(bytes)) + } + } + + fn _from_be_slice(bytes: &[u8]) -> $name { use super::endian::slice_to_u64_be; let mut slice = [0u64; $n_words]; slice.iter_mut() @@ -102,6 +116,17 @@ macro_rules! construct_uint { $name(slice) } + /// Convert a big integer into a byte array using big-endian encoding + pub fn to_be_bytes(&self) -> [u8; $n_words * 8] { + use super::endian::u64_to_array_be; + let mut res = [0; $n_words * 8]; + for i in 0..$n_words { + let start = i * 8; + res[start..start+8].copy_from_slice(&u64_to_array_be(self.0[$n_words - (i+1)])); + } + res + } + // divmod like operation, returns (quotient, remainder) #[inline] fn div_rem(self, other: Self) -> (Self, Self) { @@ -403,12 +428,89 @@ macro_rules! construct_uint { Ok($name(ret)) } } + + #[cfg(feature = "serde")] + impl $crate::serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: $crate::serde::Serializer, + { + use $crate::hashes::hex::ToHex; + let bytes = self.to_be_bytes(); + if serializer.is_human_readable() { + serializer.serialize_str(&bytes.to_hex()) + } else { + serializer.serialize_bytes(&bytes) + } + } + } + + #[cfg(feature = "serde")] + impl<'de> $crate::serde::Deserialize<'de> for $name { + fn deserialize>( + deserializer: D, + ) -> Result { + use ::std::fmt; + use $crate::hashes::hex::FromHex; + use $crate::serde::de; + struct Visitor; + impl<'de> de::Visitor<'de> for Visitor { + type Value = $name; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} bytes or a hex string with {} characters", $n_words * 8, $n_words * 8 * 2) + } + + fn visit_str(self, s: &str) -> Result + where + E: de::Error, + { + let bytes = Vec::from_hex(s) + .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(s), &self))?; + $name::from_be_slice(&bytes) + .map_err(|_| de::Error::invalid_length(bytes.len() * 2, &self)) + } + + fn visit_bytes(self, bytes: &[u8]) -> Result + where + E: de::Error, + { + $name::from_be_slice(bytes) + .map_err(|_| de::Error::invalid_length(bytes.len(), &self)) + } + } + + if deserializer.is_human_readable() { + deserializer.deserialize_str(Visitor) + } else { + deserializer.deserialize_bytes(Visitor) + } + } + } ); } construct_uint!(Uint256, 4); construct_uint!(Uint128, 2); +/// Invalid slice length +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +/// Invalid slice length +pub struct ParseLengthError { + /// The length of the slice de-facto + pub actual: usize, + /// The required length of the slice + pub expected: usize, +} + +impl ::std::fmt::Display for ParseLengthError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Invalid length: got {}, expected {}", self.actual, self.expected) + } +} + +impl ::std::error::Error for ParseLengthError {} + impl Uint256 { /// Increment by 1 #[inline] @@ -505,6 +607,16 @@ mod tests { Uint256([0x11fed2bad1c0ffe0, 0xbaadf00ddefaceda, 0xdeafbabe2bedfeed, 0x1badcafedeadbeef])); } + #[test] + pub fn uint_to_be_bytes() { + assert_eq!(Uint128([0xdeafbabe2bedfeed, 0x1badcafedeadbeef]).to_be_bytes(), + [0x1b, 0xad, 0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xaf, 0xba, 0xbe, 0x2b, 0xed, 0xfe, 0xed]); + + assert_eq!(Uint256([0x11fed2bad1c0ffe0, 0xbaadf00ddefaceda, 0xdeafbabe2bedfeed, 0x1badcafedeadbeef]).to_be_bytes(), + [0x1b, 0xad, 0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xaf, 0xba, 0xbe, 0x2b, 0xed, 0xfe, 0xed, + 0xba, 0xad, 0xf0, 0x0d, 0xde, 0xfa, 0xce, 0xda, 0x11, 0xfe, 0xd2, 0xba, 0xd1, 0xc0, 0xff, 0xe0]); + } + #[test] pub fn uint256_arithmetic_test() { let init = Uint256::from_u64(0xDEADBEEFDEADBEEF).unwrap(); @@ -612,4 +724,43 @@ mod tests { assert_eq!(end1.ok(), Some(start1)); assert_eq!(end2.ok(), Some(start2)); } + + #[cfg(feature = "serde")] + #[test] + pub fn uint256_serde_test() { + let check = |uint, hex| { + let json = format!("\"{}\"", hex); + assert_eq!(::serde_json::to_string(&uint).unwrap(), json); + assert_eq!(::serde_json::from_str::(&json).unwrap(), uint); + + let bin_encoded = ::bincode::serialize(&uint).unwrap(); + let bin_decoded: Uint256 = ::bincode::deserialize(&bin_encoded).unwrap(); + assert_eq!(bin_decoded, uint); + }; + + check( + Uint256::from_u64(0).unwrap(), + "0000000000000000000000000000000000000000000000000000000000000000", + ); + check( + Uint256::from_u64(0xDEADBEEF).unwrap(), + "00000000000000000000000000000000000000000000000000000000deadbeef", + ); + check( + Uint256([0xaa11, 0xbb22, 0xcc33, 0xdd44]), + "000000000000dd44000000000000cc33000000000000bb22000000000000aa11", + ); + check( + Uint256([u64::max_value(), u64::max_value(), u64::max_value(), u64::max_value()]), + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + check( + Uint256([ 0xA69B4555DEADBEEF, 0xA69B455CD41BB662, 0xD41BB662A69B4550, 0xDEADBEEAA69B455C ]), + "deadbeeaa69b455cd41bb662a69b4550a69b455cd41bb662a69b4555deadbeef", + ); + + assert!(::serde_json::from_str::("\"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffg\"").is_err()); // invalid char + assert!(::serde_json::from_str::("\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"").is_err()); // invalid length + assert!(::serde_json::from_str::("\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"").is_err()); // invalid length + } }