add default serde serialization to ethereum-types

This commit is contained in:
debris 2018-01-23 14:25:15 +01:00
parent 87912d868f
commit d053c5b6bf
10 changed files with 224 additions and 6 deletions

View File

@ -1,7 +1,7 @@
[package]
name = "ethbloom"
version = "0.3.0"
authors = ["debris <marek.kotewicz@gmail.com>"]
authors = ["Parity Technologies <admin@parity.io>"]
description = "Ethereum bloom filter"
license = "MIT"
documentation = "https://docs.rs/ethbloom"
@ -12,12 +12,15 @@ repository = "https://github.com/debris/ethbloom"
tiny-keccak = "1.4"
crunchy = { version = "0.1.6", features = ["limit_256"] }
fixed-hash = { version = "0.1.1", path = "../fixed-hash" }
ethereum-types-serialize = { version = "0.1", path = "../serialize", optional = true }
serde = { version = "1.0", optional = true }
[dev-dependencies]
rand = "0.4"
rustc-hex = "1.0"
[features]
default = ["std", "heapsizeof"]
default = ["std", "heapsizeof", "serialize"]
std = ["fixed-hash/std"]
heapsizeof = ["fixed-hash/heapsizeof"]
serialize = ["std", "ethereum-types-serialize", "serde"]

View File

@ -40,6 +40,15 @@ extern crate crunchy;
#[macro_use]
extern crate fixed_hash;
#[cfg(feature="serialize")]
extern crate ethereum_types_serialize;
#[cfg(feature="serialize")]
extern crate serde;
#[cfg(feature="serialize")]
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use core::{ops, mem, str};
use tiny_keccak::keccak256;
@ -224,6 +233,19 @@ impl<'a> From<&'a Bloom> for BloomRef<'a> {
}
}
impl Serialize for Bloom {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ethereum_types_serialize::serialize(&self.0, serializer)
}
}
impl<'de> Deserialize<'de> for Bloom {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
ethereum_types_serialize::deserialize_check_len(deserializer, ethereum_types_serialize::ExpectedLen::Exact(256))
.map(|x| (&*x).into())
}
}
#[cfg(test)]
mod tests {
extern crate rustc_hex;

View File

@ -1,7 +1,7 @@
[package]
name = "ethereum-types"
version = "0.1.4"
authors = ["debris <marek.kotewicz@gmail.com>"]
authors = ["Parity Technologies <admin@parity.io>"]
license = "MIT"
homepage = "https://github.com/paritytech/primitives"
description = "Ethereum types"
@ -14,8 +14,11 @@ uint = { path = "../uint", version = "0.1" }
fixed-hash = { path = "../fixed-hash", version = "0.1.1" }
ethbloom = { path = "../ethbloom", version = "0.3" }
crunchy = "0.1.5"
ethereum-types-serialize = { version = "0.1", path = "../serialize", optional = true }
serde = { version = "1.0", optional = true }
[features]
default = ["std", "heapsizeof"]
default = ["std", "heapsizeof", "serialize"]
std = ["uint/std", "fixed-hash/std", "ethbloom/std"]
heapsizeof = ["uint/heapsizeof", "fixed-hash/heapsizeof", "ethbloom/heapsizeof"]
serialize = ["std", "ethereum-types-serialize", "serde", "ethbloom/serialize"]

View File

@ -1,5 +1,11 @@
use U256;
#[cfg(feature="serialize")]
use serde::{Serialize, Serializer, Deserialize, Deserializer};
#[cfg(feature="serialize")]
use ethereum_types_serialize;
construct_hash!(H32, 4);
construct_hash!(H64, 8);
construct_hash!(H128, 16);
@ -70,3 +76,31 @@ impl<'a> From<&'a H160> for H256 {
}
}
macro_rules! impl_serde {
($name: ident, $len: expr) => {
#[cfg(feature="serialize")]
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
ethereum_types_serialize::serialize(&self.0, serializer)
}
}
#[cfg(feature="serialize")]
impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
ethereum_types_serialize::deserialize_check_len(deserializer, ethereum_types_serialize::ExpectedLen::Exact($len))
.map(|x| (&*x).into())
}
}
}
}
impl_serde!(H32, 4);
impl_serde!(H64, 8);
impl_serde!(H128, 16);
impl_serde!(H160, 20);
impl_serde!(H256, 32);
impl_serde!(H264, 33);
impl_serde!(H512, 64);
impl_serde!(H520, 65);
impl_serde!(H1024, 128);

View File

@ -12,6 +12,11 @@ extern crate uint as uint_crate;
extern crate fixed_hash;
extern crate ethbloom;
#[cfg(feature="serialize")]
extern crate ethereum_types_serialize;
#[cfg(feature="serialize")]
extern crate serde;
mod hash;
mod uint;

View File

@ -1,3 +1,9 @@
#[cfg(feature="serialize")]
use serde::{Serialize, Serializer, Deserialize, Deserializer};
#[cfg(feature="serialize")]
use ethereum_types_serialize;
construct_uint!(U128, 2);
construct_uint!(U256, 4);
construct_uint!(U512, 8);
@ -322,3 +328,28 @@ impl From<U512> for [u8; 64] {
arr
}
}
macro_rules! impl_serde {
($name: ident, $len: expr) => {
#[cfg(feature="serialize")]
impl Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
let mut bytes = [0u8; $len * 8];
self.to_big_endian(&mut bytes);
ethereum_types_serialize::serialize_uint(&bytes, serializer)
}
}
#[cfg(feature="serialize")]
impl<'de> Deserialize<'de> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
ethereum_types_serialize::deserialize_check_len(deserializer, ethereum_types_serialize::ExpectedLen::Between(0, $len * 8))
.map(|x| (&*x).into())
}
}
}
}
impl_serde!(U128, 2);
impl_serde!(U256, 4);
impl_serde!(U512, 8);

View File

@ -1,7 +1,7 @@
[package]
name = "fixed-hash"
version = "0.1.2"
authors = ["debris <marek.kotewicz@gmail.com>"]
authors = ["Parity Technologies <admin@parity.io>"]
license = "MIT"
homepage = "https://github.com/paritytech/primitives"
description = "Fixed-size hashes"

8
serialize/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "ethereum-types-serialize"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
serde = "1.0"
rustc-hex = "1.0"

112
serialize/src/lib.rs Normal file
View File

@ -0,0 +1,112 @@
extern crate serde;
extern crate rustc_hex;
use std::fmt;
use serde::{de, Serializer, Deserializer};
use rustc_hex::{ToHex, FromHex};
/// Serializes a slice of bytes.
pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> where
S: Serializer,
{
let 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<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error> 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 = 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<Vec<u8>, 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<Vec<u8>, D::Error> where
D: Deserializer<'de>,
{
struct Visitor {
len: ExpectedLen,
}
impl<'a> de::Visitor<'a> for Visitor {
type Value = Vec<u8>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a 0x-prefixed hex string with {}", self.len)
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
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 => {
FromHex::from_hex(&*format!("0{}", &v[2..]))
},
_ => FromHex::from_hex(&v[2..])
};
bytes.map_err(|e| E::custom(&format!("invalid hex value: {:?}", e)))
}
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
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 })
}

View File

@ -1,7 +1,7 @@
[package]
name = "tests"
version = "0.1.0"
authors = ["debris <marek.kotewicz@gmail.com>"]
authors = ["Parity Technologies <admin@parity.io>"]
[build-dependencies]
rustc_version = "0.2"