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..ab69d36e70 --- /dev/null +++ b/substrate/primitives/src/bytes.rs @@ -0,0 +1,124 @@ +// 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. +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)) +} + +/// Serialize a slice of bytes as uint. +/// +/// The representation will have all leading zeros trimmed. +pub fn serialize_uint(bytes: &[u8], serializer: S) -> Result where + S: Serializer, +{ + let non_zero = bytes.iter().take_while(|b| **b == 0).count(); + let bytes = &bytes[non_zero..]; + if bytes.is_empty() { + return serializer.serialize_str("0x0"); + } + + let hex = ::rustc_hex::ToHex::to_hex(bytes); + let has_leading_zero = !hex.is_empty() && &hex[0..1] == "0"; + serializer.serialize_str( + &format!("0x{}", if has_leading_zero { &hex[1..] } else { &hex }) + ) +} + +/// Expected length of bytes vector. +#[derive(Debug, PartialEq, Eq)] +pub enum ExpectedLen { + /// Any length in bytes. + Any, + /// Exact length in bytes. + Exact(usize), + /// A bytes length between (min; max]. + Between(usize, usize), +} + +impl fmt::Display for ExpectedLen { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + ExpectedLen::Any => write!(fmt, "even length"), + ExpectedLen::Exact(v) => write!(fmt, "length of {}", v * 2), + ExpectedLen::Between(min, max) => write!(fmt, "length between ({}; {}]", min * 2, max * 2), + } + } +} + +/// Deserialize into vector of bytes. +pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where + D: Deserializer<'de>, +{ + deserialize_check_len(deserializer, ExpectedLen::Any) +} + +/// Deserialize into vector of bytes with additional size check. +pub fn deserialize_check_len<'de, D>(deserializer: D, len: ExpectedLen) -> Result, D::Error> where + D: Deserializer<'de>, +{ + struct Visitor { + len: ExpectedLen, + } + + impl<'a> de::Visitor<'a> for Visitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a 0x-prefixed hex string with {}", self.len) + } + + fn visit_str(self, v: &str) -> Result { + if v.len() < 2 || &v[0..2] != "0x" { + return Err(E::custom("prefix is missing")) + } + + let is_len_valid = match self.len { + // just make sure that we have all nibbles + ExpectedLen::Any => v.len() % 2 == 0, + ExpectedLen::Exact(len) => v.len() == 2 * len + 2, + ExpectedLen::Between(min, max) => v.len() <= 2 * max + 2 && v.len() > 2 * min + 2, + }; + + if !is_len_valid { + return Err(E::invalid_length(v.len() - 2, &self)) + } + + let bytes = match self.len { + ExpectedLen::Between(..) if v.len() % 2 != 0 => { + ::rustc_hex::FromHex::from_hex(&*format!("0{}", &v[2..])) + }, + _ => ::rustc_hex::FromHex::from_hex(&v[2..]) + }; + + bytes.map_err(|e| E::custom(&format!("invalid hex value: {:?}", e))) + } + + 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 { len }) +} diff --git a/substrate/primitives/src/hash.rs b/substrate/primitives/src/hash.rs index 788a395f0e..10b32f3741 100644 --- a/substrate/primitives/src/hash.rs +++ b/substrate/primitives/src/hash.rs @@ -16,52 +16,22 @@ //! 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) - } - - 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) + bytes::deserialize_check_len(deserializer, bytes::ExpectedLen::Exact($len)) + .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..cecf69dd2a 100644 --- a/substrate/primitives/src/uint.rs +++ b/substrate/primitives/src/uint.rs @@ -16,57 +16,24 @@ //! 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_uint(&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") - } - - 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) + bytes::deserialize_check_len(deserializer, bytes::ExpectedLen::Between(0, $len * 8)) + .map(|x| (&*x).into()) } } }