From 2c2d55d90a5b483c4fe4da3acdb79f530fa583d0 Mon Sep 17 00:00:00 2001 From: Steven Roose Date: Mon, 15 Jul 2019 19:41:59 +0200 Subject: [PATCH] bip32: Implement hex serialization for Fingerprint and ChainCode --- src/internal_macros.rs | 175 +++++++++++++++++++++++++++++------------ src/util/bip32.rs | 37 +++++++-- 2 files changed, 154 insertions(+), 58 deletions(-) diff --git a/src/internal_macros.rs b/src/internal_macros.rs index 473d5c2c..d944c00d 100644 --- a/src/internal_macros.rs +++ b/src/internal_macros.rs @@ -162,57 +162,6 @@ macro_rules! impl_array_newtype { } } -macro_rules! impl_array_newtype_encodable { - ($thing:ident, $ty:ty, $len:expr) => { - #[cfg(feature = "serde")] - impl<'de> $crate::serde::Deserialize<'de> for $thing { - fn deserialize(deserializer: D) -> Result - where - D: $crate::serde::Deserializer<'de>, - { - use $crate::std::fmt::{self, Formatter}; - - struct Visitor; - impl<'de> $crate::serde::de::Visitor<'de> for Visitor { - type Value = $thing; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("a fixed size array") - } - - #[inline] - fn visit_seq(self, mut seq: A) -> Result - where - A: $crate::serde::de::SeqAccess<'de>, - { - let mut ret: [$ty; $len] = [0; $len]; - for item in ret.iter_mut() { - *item = match seq.next_element()? { - Some(c) => c, - None => return Err($crate::serde::de::Error::custom("end of stream")) - }; - } - Ok($thing(ret)) - } - } - - deserializer.deserialize_seq(Visitor) - } - } - - #[cfg(feature = "serde")] - impl $crate::serde::Serialize for $thing { - fn serialize(&self, serializer: S) -> Result - where - S: $crate::serde::Serializer, - { - let &$thing(ref dat) = self; - (&dat[..]).serialize(serializer) - } - } - } -} - macro_rules! impl_array_newtype_show { ($thing:ident) => { impl ::std::fmt::Debug for $thing { @@ -621,6 +570,130 @@ macro_rules! serde_struct_human_string_impl { ) } +/// Implements several traits for byte-based newtypes. +/// Implements: +/// - std::fmt::LowerHex (implies bitcoin_hashes::hex::ToHex) +/// - std::fmt::Display +/// - std::str::FromStr +/// - bitcoin_hashes::hex::FromHex +macro_rules! impl_bytes_newtype { + ($t:ident, $len:expr) => ( + + impl ::std::fmt::LowerHex for $t { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for &ch in self.0.iter() { + write!(f, "{:02x}", ch)?; + } + Ok(()) + } + } + + impl ::std::fmt::Display for $t { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } + } + + impl ::bitcoin_hashes::hex::FromHex for $t { + fn from_byte_iter(iter: I) -> Result + where I: Iterator> + + ExactSizeIterator + + DoubleEndedIterator, + { + if iter.len() == $len { + let mut ret = [0; $len]; + for (n, byte) in iter.enumerate() { + ret[n] = byte?; + } + Ok($t(ret)) + } else { + Err(::bitcoin_hashes::hex::Error::InvalidLength(2 * $len, 2 * iter.len())) + } + } + } + + impl ::std::str::FromStr for $t { + type Err = bitcoin_hashes::hex::Error; + fn from_str(s: &str) -> Result { + hex::FromHex::from_hex(s) + } + } + + #[cfg(feature="serde")] + impl ::serde::Serialize for $t { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + s.serialize_str(&::bitcoin_hashes::hex::ToHex::to_hex(self)) + } else { + s.serialize_bytes(&self[..]) + } + } + } + + #[cfg(feature="serde")] + impl<'de> ::serde::Deserialize<'de> for $t { + fn deserialize>(d: D) -> Result<$t, D::Error> { + if d.is_human_readable() { + struct HexVisitor; + + impl<'de> ::serde::de::Visitor<'de> for HexVisitor { + type Value = $t; + + fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + formatter.write_str("an ASCII hex string") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + if let Ok(hex) = ::std::str::from_utf8(v) { + ::bitcoin_hashes::hex::FromHex::from_hex(hex).map_err(E::custom) + } else { + return Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)); + } + } + + fn visit_str(self, v: &str) -> Result + where + E: ::serde::de::Error, + { + ::bitcoin_hashes::hex::FromHex::from_hex(v).map_err(E::custom) + } + } + + d.deserialize_str(HexVisitor) + } else { + struct BytesVisitor; + + impl<'de> ::serde::de::Visitor<'de> for BytesVisitor { + type Value = $t; + + fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + formatter.write_str("a bytestring") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + if v.len() != $len { + Err(E::invalid_length(v.len(), &stringify!($len))) + } else { + let mut ret = [0; $len]; + ret.copy_from_slice(v); + Ok($t(ret)) + } + } + } + + d.deserialize_bytes(BytesVisitor) + } + } + } + ) +} + macro_rules! user_enum { ( $(#[$attr:meta])* diff --git a/src/util/bip32.rs b/src/util/bip32.rs index ff1b9614..62653899 100644 --- a/src/util/bip32.rs +++ b/src/util/bip32.rs @@ -23,7 +23,7 @@ use std::str::FromStr; #[cfg(feature = "serde")] use serde; use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; -use bitcoin_hashes::{hash160, sha512, Hash, HashEngine, Hmac, HmacEngine}; +use bitcoin_hashes::{self, hex, hash160, sha512, Hash, HashEngine, Hmac, HmacEngine}; use secp256k1::{self, Secp256k1}; use network::constants::Network; @@ -34,13 +34,13 @@ use util::key::{PublicKey, PrivateKey}; pub struct ChainCode([u8; 32]); impl_array_newtype!(ChainCode, u8, 32); impl_array_newtype_show!(ChainCode); -impl_array_newtype_encodable!(ChainCode, u8, 32); +impl_bytes_newtype!(ChainCode, 32); /// A fingerprint pub struct Fingerprint([u8; 4]); impl_array_newtype!(Fingerprint, u8, 4); impl_array_newtype_show!(Fingerprint); -impl_array_newtype_encodable!(Fingerprint, u8, 4); +impl_bytes_newtype!(Fingerprint, 4); impl Default for Fingerprint { fn default() -> Fingerprint { Fingerprint([0; 4]) } @@ -690,6 +690,9 @@ impl FromStr for ExtendedPubKey { #[cfg(test)] mod tests { + use super::*; + use super::ChildNumber::{Hardened, Normal}; + use std::str::FromStr; use std::string::ToString; @@ -698,10 +701,6 @@ mod tests { use network::constants::Network::{self, Bitcoin}; - use super::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey}; - use super::ChildNumber::{Hardened, Normal}; - use super::Error; - #[test] fn test_parse_derivation_path() { assert_eq!(DerivationPath::from_str("42"), Err(Error::InvalidDerivationPathFormat)); @@ -962,5 +961,29 @@ mod tests { serde_round_trip!(ChildNumber::from_hardened_idx(1).unwrap()); serde_round_trip!(ChildNumber::from_hardened_idx((1 << 31) - 1).unwrap()); } + + #[test] + #[cfg(feature = "serde")] + pub fn encode_fingerprint_chaincode() { + use serde_json; + let fp = Fingerprint::from(&[1u8,2,3,42][..]); + let cc = ChainCode::from( + &[1u8,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2][..] + ); + + serde_round_trip!(fp); + serde_round_trip!(cc); + + assert_eq!("\"0102032a\"", serde_json::to_string(&fp).unwrap()); + assert_eq!( + "\"0102030405060708090001020304050607080900010203040506070809000102\"", + serde_json::to_string(&cc).unwrap() + ); + assert_eq!("0102032a", fp.to_string()); + assert_eq!( + "0102030405060708090001020304050607080900010203040506070809000102", + cc.to_string() + ); + } }