From 799d03254fb6d4408b811e9be3aeee6c878951cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 10 Nov 2017 21:31:48 +0100 Subject: [PATCH] Serialization with serde. --- substrate/Cargo.lock | 127 ++++++++++++++++++++++++++++-- substrate/Cargo.toml | 5 +- substrate/primitives/Cargo.toml | 9 ++- substrate/primitives/src/block.rs | 62 +++++++++++++-- substrate/primitives/src/hash.rs | 95 ++++++++++++++++++++++ substrate/primitives/src/lib.rs | 22 +++--- substrate/primitives/src/uint.rs | 93 ++++++++++++++++++++++ substrate/serializer/Cargo.toml | 8 ++ substrate/serializer/src/lib.rs | 45 +++++++++++ 9 files changed, 444 insertions(+), 22 deletions(-) create mode 100644 substrate/serializer/Cargo.toml create mode 100644 substrate/serializer/src/lib.rs diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 0940a8a3d1..3af622b751 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -1,10 +1,9 @@ [root] -name = "polkadot-primitives" +name = "polkadot-serializer" version = "0.1.0" dependencies = [ - "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fixed-hash 0.1.0 (git+https://github.com/paritytech/primitives.git)", - "uint 0.1.0 (git+https://github.com/paritytech/primitives.git)", + "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -66,6 +65,16 @@ name = "crunchy" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "difference" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dtoa" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "env_logger" version = "0.4.3" @@ -101,6 +110,11 @@ dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itoa" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -133,12 +147,16 @@ dependencies = [ "libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-traits" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "polkadot" version = "0.1.0" dependencies = [ "polkadot-cli 0.1.0", - "polkadot-primitives 0.1.0", ] [[package]] @@ -150,6 +168,34 @@ dependencies = [ "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "polkadot-primitives" +version = "0.1.0" +dependencies = [ + "crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "fixed-hash 0.1.0 (git+https://github.com/paritytech/primitives.git)", + "polkadot-serializer 0.1.0", + "pretty_assertions 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.1.0 (git+https://github.com/paritytech/primitives.git)", +] + +[[package]] +name = "pretty_assertions" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rand" version = "0.3.18" @@ -215,11 +261,64 @@ name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "serde" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive_internals" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "strsim" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "termion" version = "1.5.1" @@ -262,6 +361,11 @@ name = "unicode-width" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unreachable" version = "1.0.0" @@ -309,15 +413,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" +"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" +"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" "checksum fixed-hash 0.1.0 (git+https://github.com/paritytech/primitives.git)" = "" "checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159" "checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82" +"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9e5e58fa1a4c3b915a561a78a22ee0cac6ab97dca2504428bc1cb074375f8d5" "checksum libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "5ba3df4dcb460b9dfbd070d41c94c19209620c191b0340b929ce748a2bcd42d2" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" +"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0" +"checksum pretty_assertions 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94b6bbc8a323d89a019c4cdde21850522fb8405e97add70827177fc2f86c1495" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd" "checksum redox_syscall 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde11f18c108289bef24469638a04dce49da56084f2d50618b226e47eb04509" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" @@ -327,12 +437,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69" "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "0c9cab69e16835717c9b8bd13c29f92b6aa34fe32ce2866b1ab481cf2da8442a" +"checksum serde_derive 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3bdafe3e71710131a919735916caa5b18c2754ad0d33d8ae5d586ccc804a403e" +"checksum serde_derive_internals 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32f1926285523b2db55df263d2aa4eb69ddcfa7a7eade6430323637866b513ab" +"checksum serde_json 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e4586746d1974a030c48919731ecffd0ed28d0c40749d0d18d43b3a7d6c9b20e" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" "checksum uint 0.1.0 (git+https://github.com/paritytech/primitives.git)" = "" "checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index a58294fc86..d506a38ffa 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -5,6 +5,9 @@ authors = ["Parity Team "] [dependencies] polkadot-cli = { path = "cli" } -polkadot-primitives = { path = "primitives" } [workspace] +members = [ + "primitives", + "serializer" +] diff --git a/substrate/primitives/Cargo.toml b/substrate/primitives/Cargo.toml index 29d84fa9c0..36352ad157 100644 --- a/substrate/primitives/Cargo.toml +++ b/substrate/primitives/Cargo.toml @@ -5,8 +5,15 @@ authors = ["Parity Team "] [dependencies] crunchy = "0.1.5" -uint = { git = "https://github.com/paritytech/primitives.git" } fixed-hash = { git = "https://github.com/paritytech/primitives.git" } +rustc-hex = "1.0" +serde = "1.0" +serde_derive = "1.0" +uint = { git = "https://github.com/paritytech/primitives.git" } + +[dev-dependencies] +polkadot-serializer = { path = "../serializer", version = "0.1" } +pretty_assertions = "0.4" [features] default = ["std"] diff --git a/substrate/primitives/src/block.rs b/substrate/primitives/src/block.rs index 9b3c7beb0d..dd297535c5 100644 --- a/substrate/primitives/src/block.rs +++ b/substrate/primitives/src/block.rs @@ -24,7 +24,7 @@ pub type HeaderHash = H256; pub type ProofHash = H256; /// Unique identifier of a parachain. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Serialize, Deserialize)] pub struct ParachainId(u64); impl From for u64 { @@ -35,19 +35,23 @@ impl From for ParachainId { fn from(x: u64) -> Self { ParachainId(x) } } +/// A wrapper type for parachain block header raw bytes. +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct ParachainBlockHeader(pub Vec); + /// A parachain block proposal. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ParachainProposal { /// The ID of the parachain this is a proposal for. pub parachain: ParachainId, /// Parachain block header bytes. - pub header: Vec, + pub header: ParachainBlockHeader, /// Hash of data necessary to prove validity of the header. pub proof_hash: ProofHash, } /// A relay chain block header. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Header { /// Block parent's hash. pub parent_hash: HeaderHash, @@ -63,8 +67,56 @@ pub struct Header { /// /// Included candidates should be sorted by parachain ID, and without duplicate /// IDs. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Body { /// Parachain proposal blocks. pub para_blocks: Vec, } + + +#[cfg(test)] +mod tests { + use super::*; + use polkadot_serializer as ser; + + #[test] + fn test_header_serialization() { + assert_eq!(ser::to_string_pretty(&Header { + parent_hash: 5.into(), + state_root: 3.into(), + timestamp: 10, + number: 67, + }), r#"{ + "parent_hash": "0x0000000000000000000000000000000000000000000000000000000000000005", + "state_root": "0x0000000000000000000000000000000000000000000000000000000000000003", + "timestamp": 10, + "number": 67 +}"#); + } + + #[test] + fn test_body_serialization() { + assert_eq!(ser::to_string_pretty(&Body { + para_blocks: vec![ + ParachainProposal { + parachain: 5.into(), + header: ParachainBlockHeader(vec![1, 2, 3, 4]), + proof_hash: 5.into(), + } + ], + }), r#"{ + "para_blocks": [ + { + "parachain": 5, + "header": [ + 1, + 2, + 3, + 4 + ], + "proof_hash": "0x0000000000000000000000000000000000000000000000000000000000000005" + } + ] +}"#); + } +} diff --git a/substrate/primitives/src/hash.rs b/substrate/primitives/src/hash.rs index 5d62f86ca2..144655f0af 100644 --- a/substrate/primitives/src/hash.rs +++ b/substrate/primitives/src/hash.rs @@ -16,5 +16,100 @@ //! A fixed hash type. +use std::fmt; +use serde::{de, Serialize, Serializer, Deserialize, Deserializer}; + +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)) + + } + } + + 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) + } + } + } +} + impl_hash!(H160, 20); +impl_serde!(H160, 20); impl_hash!(H256, 32); +impl_serde!(H256, 32); + +#[cfg(test)] +mod tests { + use super::*; + use polkadot_serializer as ser; + + #[test] + fn test_h160() { + let tests = vec![ + (H160::from(0), "0x0000000000000000000000000000000000000000"), + (H160::from(2), "0x0000000000000000000000000000000000000002"), + (H160::from(15), "0x000000000000000000000000000000000000000f"), + (H160::from(16), "0x0000000000000000000000000000000000000010"), + (H160::from(1_000), "0x00000000000000000000000000000000000003e8"), + (H160::from(100_000), "0x00000000000000000000000000000000000186a0"), + (H160::from(u64::max_value()), "0x000000000000000000000000ffffffffffffffff"), + ]; + + for (number, expected) in tests { + assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); + assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + } + } + + #[test] + fn test_h256() { + let tests = vec![ + (H256::from(0), "0x0000000000000000000000000000000000000000000000000000000000000000"), + (H256::from(2), "0x0000000000000000000000000000000000000000000000000000000000000002"), + (H256::from(15), "0x000000000000000000000000000000000000000000000000000000000000000f"), + (H256::from(16), "0x0000000000000000000000000000000000000000000000000000000000000010"), + (H256::from(1_000), "0x00000000000000000000000000000000000000000000000000000000000003e8"), + (H256::from(100_000), "0x00000000000000000000000000000000000000000000000000000000000186a0"), + (H256::from(u64::max_value()), "0x000000000000000000000000000000000000000000000000ffffffffffffffff"), + ]; + + for (number, expected) in tests { + assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); + assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + } + } +} diff --git a/substrate/primitives/src/lib.rs b/substrate/primitives/src/lib.rs index d16ba72467..a4a5337744 100644 --- a/substrate/primitives/src/lib.rs +++ b/substrate/primitives/src/lib.rs @@ -18,27 +18,29 @@ #![warn(missing_docs)] -#[cfg(feature="std")] -extern crate core; +extern crate serde; +extern crate rustc_hex; #[macro_use] extern crate crunchy; #[macro_use] extern crate fixed_hash; #[macro_use] +extern crate serde_derive; +#[macro_use] extern crate uint as uint_crate; +#[cfg(feature="std")] +extern crate core; +#[cfg(test)] +extern crate polkadot_serializer; +#[cfg(test)] +#[macro_use] +extern crate pretty_assertions; + pub mod block; pub mod hash; pub mod uint; /// Alias to 160-bit hash when used in the context of an account address. pub type Address = hash::H160; - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/substrate/primitives/src/uint.rs b/substrate/primitives/src/uint.rs index 4d9c1f46ab..95714b74d4 100644 --- a/substrate/primitives/src/uint.rs +++ b/substrate/primitives/src/uint.rs @@ -16,5 +16,98 @@ //! An unsigned fixed-size integer. +use std::fmt; +use serde::{de, Serialize, Serializer, Deserialize, Deserializer}; + +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)) + } + } + } + + 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() < 2 || &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) + } + } + } +} + construct_uint!(U256, 4); +impl_serde!(U256, 4); construct_uint!(U512, 8); +impl_serde!(U512, 8); + +#[cfg(test)] +mod tests { + use super::*; + use polkadot_serializer as ser; + + macro_rules! test { + ($name: ident, $test_name: ident) => { + #[test] + fn $test_name() { + let tests = vec![ + ($name::from(0), "0x0"), + ($name::from(1), "0x1"), + ($name::from(2), "0x2"), + ($name::from(10), "0xa"), + ($name::from(15), "0xf"), + ($name::from(15), "0xf"), + ($name::from(16), "0x10"), + ($name::from(1_000), "0x3e8"), + ($name::from(100_000), "0x186a0"), + ($name::from(u64::max_value()), "0xffffffffffffffff"), + ($name::from(u64::max_value()) + 1.into(), "0x10000000000000000"), + ]; + + for (number, expected) in tests { + assert_eq!(format!("{:?}", expected), ser::to_string_pretty(&number)); + assert_eq!(number, ser::from_str(&format!("{:?}", expected)).unwrap()); + } + } + } + } + + test!(U256, test_u256); + test!(U512, test_u512); +} diff --git a/substrate/serializer/Cargo.toml b/substrate/serializer/Cargo.toml new file mode 100644 index 0000000000..34db3dfb76 --- /dev/null +++ b/substrate/serializer/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "polkadot-serializer" +version = "0.1.0" +authors = ["Parity Team "] + +[dependencies] +serde = "1.0" +serde_json = "1.0" diff --git a/substrate/serializer/src/lib.rs b/substrate/serializer/src/lib.rs new file mode 100644 index 0000000000..56b9e71257 --- /dev/null +++ b/substrate/serializer/src/lib.rs @@ -0,0 +1,45 @@ +// 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 . + +//! Polkadot customizable serde serializer. +//! +//! The idea is that we can later change the implementation +//! to something more compact, but for now we're using JSON. + +#![warn(missing_docs)] + +extern crate serde; +extern crate serde_json; + +pub use serde_json::{from_str, from_slice, from_reader, Result}; + +const PROOF: &str = "Serializers are infallible; qed"; + +/// Serialize the given data structure as a pretty-printed String of JSON. +pub fn to_string_pretty(value: &T) -> String { + serde_json::to_string_pretty(value).expect(PROOF) +} + +/// Serialize the given data structure as a JSON byte vector. +pub fn to_vec(value: &T) -> Vec { + serde_json::to_vec(value).expect(PROOF) +} + +/// Serialize the given data structure as JSON into the IO stream. +pub fn to_writer(writer: W, value: &T) -> Result<()> { + serde_json::to_writer(writer, value) +} +