bip32: Implement hex serialization for Fingerprint and ChainCode

This commit is contained in:
Steven Roose 2019-07-15 19:41:59 +02:00
parent 4530e403e0
commit 2c2d55d90a
No known key found for this signature in database
GPG Key ID: 2F2A88D7F8D68E87
2 changed files with 154 additions and 58 deletions

View File

@ -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<D>(deserializer: D) -> Result<Self, D::Error>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: $crate::serde::Serializer,
{
let &$thing(ref dat) = self;
(&dat[..]).serialize(serializer)
}
}
}
}
macro_rules! impl_array_newtype_show { macro_rules! impl_array_newtype_show {
($thing:ident) => { ($thing:ident) => {
impl ::std::fmt::Debug for $thing { 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<I>(iter: I) -> Result<Self, bitcoin_hashes::hex::Error>
where I: Iterator<Item=Result<u8, bitcoin_hashes::hex::Error>> +
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<Self, Self::Err> {
hex::FromHex::from_hex(s)
}
}
#[cfg(feature="serde")]
impl ::serde::Serialize for $t {
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
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: ::serde::Deserializer<'de>>(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<E>(self, v: &[u8]) -> Result<Self::Value, E>
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<E>(self, v: &str) -> Result<Self::Value, E>
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
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 { macro_rules! user_enum {
( (
$(#[$attr:meta])* $(#[$attr:meta])*

View File

@ -23,7 +23,7 @@ use std::str::FromStr;
#[cfg(feature = "serde")] use serde; #[cfg(feature = "serde")] use serde;
use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; 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 secp256k1::{self, Secp256k1};
use network::constants::Network; use network::constants::Network;
@ -34,13 +34,13 @@ use util::key::{PublicKey, PrivateKey};
pub struct ChainCode([u8; 32]); pub struct ChainCode([u8; 32]);
impl_array_newtype!(ChainCode, u8, 32); impl_array_newtype!(ChainCode, u8, 32);
impl_array_newtype_show!(ChainCode); impl_array_newtype_show!(ChainCode);
impl_array_newtype_encodable!(ChainCode, u8, 32); impl_bytes_newtype!(ChainCode, 32);
/// A fingerprint /// A fingerprint
pub struct Fingerprint([u8; 4]); pub struct Fingerprint([u8; 4]);
impl_array_newtype!(Fingerprint, u8, 4); impl_array_newtype!(Fingerprint, u8, 4);
impl_array_newtype_show!(Fingerprint); impl_array_newtype_show!(Fingerprint);
impl_array_newtype_encodable!(Fingerprint, u8, 4); impl_bytes_newtype!(Fingerprint, 4);
impl Default for Fingerprint { impl Default for Fingerprint {
fn default() -> Fingerprint { Fingerprint([0; 4]) } fn default() -> Fingerprint { Fingerprint([0; 4]) }
@ -690,6 +690,9 @@ impl FromStr for ExtendedPubKey {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use super::ChildNumber::{Hardened, Normal};
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; use std::string::ToString;
@ -698,10 +701,6 @@ mod tests {
use network::constants::Network::{self, Bitcoin}; use network::constants::Network::{self, Bitcoin};
use super::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey};
use super::ChildNumber::{Hardened, Normal};
use super::Error;
#[test] #[test]
fn test_parse_derivation_path() { fn test_parse_derivation_path() {
assert_eq!(DerivationPath::from_str("42"), Err(Error::InvalidDerivationPathFormat)); 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).unwrap());
serde_round_trip!(ChildNumber::from_hardened_idx((1 << 31) - 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()
);
}
} }