diff --git a/substrate/primitives/src/block.rs b/substrate/primitives/src/block.rs index dd297535c5..8e36366f2c 100644 --- a/substrate/primitives/src/block.rs +++ b/substrate/primitives/src/block.rs @@ -16,6 +16,7 @@ //! Block and header type definitions. +use bytes; use hash::H256; /// Hash used to refer to a block hash. @@ -37,7 +38,7 @@ impl From for ParachainId { /// A wrapper type for parachain block header raw bytes. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct ParachainBlockHeader(pub Vec); +pub struct ParachainBlockHeader(#[serde(with="bytes")] pub Vec); /// A parachain block proposal. #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] @@ -108,12 +109,7 @@ mod tests { "para_blocks": [ { "parachain": 5, - "header": [ - 1, - 2, - 3, - 4 - ], + "header": "0x01020304", "proof_hash": "0x0000000000000000000000000000000000000000000000000000000000000005" } ] diff --git a/substrate/primitives/src/bytes.rs b/substrate/primitives/src/bytes.rs new file mode 100644 index 0000000000..efe21da706 --- /dev/null +++ b/substrate/primitives/src/bytes.rs @@ -0,0 +1,99 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see . + +use std::fmt; + +use serde::{de, Serializer, Deserializer}; + +/// Serializes a slice of bytes into a most compact form. +pub fn serialize(bytes: &[u8], serializer: S) -> Result where + S: Serializer, +{ + let hex = ::rustc_hex::ToHex::to_hex(bytes); + serializer.serialize_str(&format!("0x{}", hex)) +} + +pub fn serialize_compact(bytes: &[u8], serializer: S) -> Result where + S: Serializer, +{ + let mut non_zero = bytes.len(); + for (i, b) in bytes.iter().enumerate() { + if *b != 0 { + non_zero = i; + break; + } + } + + if non_zero == bytes.len() { + return serializer.serialize_str("0x0"); + } + + let hex = ::rustc_hex::ToHex::to_hex(&bytes[non_zero..]); + let has_leading_zero = !hex.is_empty() && &hex[0..1] == "0"; + return serializer.serialize_str(&format!("0x{}", if has_leading_zero { &hex[1..] } else { &hex })); +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where + D: Deserializer<'de>, +{ + deserialize_with_check(deserializer, |_| Ok(())) +} + +pub fn deserialize_with_check<'de, D, F>(deserializer: D, check: F) -> Result, D::Error> where + D: Deserializer<'de>, + F: Fn(&str) -> Result<(), ErrorKind>, +{ + struct Visitor { + check: F, + } + + impl<'a, F> de::Visitor<'a> for Visitor where + F: Fn(&str) -> Result<(), ErrorKind>, + { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a 0x-prefixed hex string") + } + + fn visit_str(self, v: &str) -> Result { + if v.len() < 2 || &v[0..2] != "0x" { + return Err(E::custom("prefix is missing")) + } + + (self.check)(v).map_err(|err| match err { + ErrorKind::InvalidLength(len) => E::invalid_length(len, &self), + })?; + + let v = if v.len() % 2 == 0 { v[2..].to_owned() } else { format!("0{}", &v[2..]) }; + let bytes = ::rustc_hex::FromHex::from_hex(v.as_str()) + .map_err(|e| E::custom(&format!("invalid hex value: {:?}", e)))?; + Ok(bytes) + } + + fn visit_string(self, v: String) -> Result { + self.visit_str(&v) + } + } + // TODO [ToDr] Use raw bytes if we switch to RLP / binencoding + // (visit_bytes, visit_bytes_buf) + deserializer.deserialize_str(Visitor { check }) +} + +pub enum ErrorKind { + InvalidLength(usize), +} + diff --git a/substrate/primitives/src/hash.rs b/substrate/primitives/src/hash.rs index 788a395f0e..a93324276e 100644 --- a/substrate/primitives/src/hash.rs +++ b/substrate/primitives/src/hash.rs @@ -16,52 +16,28 @@ //! A fixed hash type. -use std::fmt; -use serde::{de, Serialize, Serializer, Deserialize, Deserializer}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + +use bytes; macro_rules! impl_serde { ($name: ident, $len: expr) => { impl Serialize for $name { fn serialize(&self, serializer: S) -> Result where S: Serializer { - // TODO [ToDr] Use raw bytes if we switch to RLP / binencoding - // (serialize_bytes) - serializer.serialize_str(&format!("0x{:?}", self)) - + bytes::serialize(&self.0, serializer) } } impl<'de> Deserialize<'de> for $name { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { - struct Visitor; - impl<'a> de::Visitor<'a> for Visitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed hex string of {} bytes", $len) + bytes::deserialize_with_check(deserializer, |v: &str| { + // 0x + len + if v.len() != 2 + $len * 2 { + Err(bytes::ErrorKind::InvalidLength(v.len() - 2)) + } else { + Ok(()) } - - fn visit_str(self, v: &str) -> Result { - if v.len() < 2 || &v[0..2] != "0x" { - return Err(E::custom("prefix is missing")) - } - - // 0x + len - if v.len() != 2 + $len * 2 { - return Err(E::invalid_length(v.len() - 2, &self)); - } - - let bytes = ::rustc_hex::FromHex::from_hex(&v[2..]) - .map_err(|e| E::custom(&format!("invalid hex value: {:?}", e)))?; - Ok((&*bytes).into()) - } - - fn visit_string(self, v: String) -> Result { - self.visit_str(&v) - } - } - // TODO [ToDr] Use raw bytes if we switch to RLP / binencoding - // (visit_bytes, visit_bytes_buf) - deserializer.deserialize_str(Visitor) + }).map(|x| (&*x).into()) } } } diff --git a/substrate/primitives/src/lib.rs b/substrate/primitives/src/lib.rs index a4a5337744..596c13f4ac 100644 --- a/substrate/primitives/src/lib.rs +++ b/substrate/primitives/src/lib.rs @@ -38,6 +38,7 @@ extern crate polkadot_serializer; #[macro_use] extern crate pretty_assertions; +mod bytes; pub mod block; pub mod hash; pub mod uint; diff --git a/substrate/primitives/src/uint.rs b/substrate/primitives/src/uint.rs index 305523fe71..8f1c07bde1 100644 --- a/substrate/primitives/src/uint.rs +++ b/substrate/primitives/src/uint.rs @@ -16,57 +16,30 @@ //! An unsigned fixed-size integer. -use std::fmt; -use serde::{de, Serialize, Serializer, Deserialize, Deserializer}; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; + +use bytes; macro_rules! impl_serde { ($name: ident, $len: expr) => { impl Serialize for $name { fn serialize(&self, serializer: S) -> Result where S: Serializer { - // TODO [ToDr] Use raw bytes if we switch to RLP / binencoding - // (serialize_bytes) - if self.is_zero() { - // TODO [ToDr] LowerHex of 0 is broken - serializer.serialize_str("0x0") - } else { - serializer.serialize_str(&format!("{:#x}", self)) - } + let mut bytes = [0u8; $len * 8]; + self.to_big_endian(&mut bytes); + bytes::serialize_compact(&bytes, serializer) } } impl<'de> Deserialize<'de> for $name { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { - struct Visitor; - impl<'a> de::Visitor<'a> for Visitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a 0x-prefixed hex string") + bytes::deserialize_with_check(deserializer, |v: &str| { + // 0x + len + if v.len() > 2 + $len * 16 || v.len() == 2 { + Err(bytes::ErrorKind::InvalidLength(v.len() - 2)) + } else { + Ok(()) } - - fn visit_str(self, v: &str) -> Result { - if v.len() < 3 || &v[0..2] != "0x" { - return Err(E::custom("prefix is missing")) - } - - // 0x + len - if v.len() > 2 + $len * 16 { - return Err(E::invalid_length(v.len() - 2, &self)); - } - - let v = if v.len() % 2 == 0 { v[2..].to_owned() } else { format!("0{}", &v[2..]) }; - let bytes = ::rustc_hex::FromHex::from_hex(v.as_str()) - .map_err(|e| E::custom(&format!("invalid hex value: {:?}", e)))?; - Ok((&*bytes).into()) - } - - fn visit_string(self, v: String) -> Result { - self.visit_str(&v) - } - } - // TODO [ToDr] Use raw bytes if we switch to RLP / binencoding - // (visit_bytes, visit_bytes_buf) - deserializer.deserialize_str(Visitor) + }).map(|bytes| (&*bytes).into()) } } }