From 4f53db60ed1b163db6d4a0e1490002d235d277f5 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 14 Jan 2016 21:24:03 +0100 Subject: [PATCH] New JSON conversion traits. --- Cargo.toml | 1 + src/bytes.rs | 2 +- src/common.rs | 1 + src/hash.rs | 34 +++++++++---- src/json_aid.rs | 101 ++++++++++++++++++++++++++++++++------ src/lib.rs | 10 ++-- src/sha3.rs | 8 +-- src/standard.rs | 4 +- src/uint.rs | 128 ++++++++++++++++++++++++++++++++---------------- 9 files changed, 213 insertions(+), 76 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9da1b73c9..6e32edcab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ eth-secp256k1 = { git = "https://github.com/arkpar/rust-secp256k1.git" } rust-crypto = "0.2.34" elastic-array = "0.4" heapsize = "0.2" +itertools = "0.4" [dev-dependencies] json-tests = { path = "json-tests" } diff --git a/src/bytes.rs b/src/bytes.rs index e925d4509..479a91df0 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -40,7 +40,7 @@ use std::slice; use std::cmp::Ordering; use std::error::Error as StdError; use std::ops::{Deref, DerefMut}; -use uint::{U128, U256}; +use uint::{Uint, U128, U256}; use hash::FixedHash; pub struct PrettySlice<'a> (&'a [u8]); diff --git a/src/common.rs b/src/common.rs index c57357af8..78f9fbf64 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,4 +1,5 @@ pub use standard::*; +pub use from_json::*; pub use error::*; pub use hash::*; pub use uint::*; diff --git a/src/hash.rs b/src/hash.rs index f67833072..8c1772b2c 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -1,23 +1,18 @@ //! General hash types, a fixed-size raw-data type used as the output of hash functions. -use std::str::FromStr; -use std::fmt; -use std::ops; -use std::hash::{Hash, Hasher}; -use std::ops::{Index, IndexMut, Deref, DerefMut, BitOr, BitOrAssign, BitAnd, BitXor}; -use std::cmp::{PartialOrd, Ordering}; -use rustc_serialize::hex::*; +use standard::*; +use math::log2; use error::UtilError; use rand::Rng; use rand::os::OsRng; use bytes::{BytesConvertable,Populatable}; -use math::log2; -use uint::U256; +use from_json::*; +use uint::{Uint, U256}; /// Trait for a fixed-size byte array to be used as the output of hash functions. /// /// Note: types implementing `FixedHash` must be also `BytesConvertable`. -pub trait FixedHash: Sized + BytesConvertable + Populatable { +pub trait FixedHash: Sized + BytesConvertable + Populatable + FromStr + Default { fn new() -> Self; /// Synonym for `new()`. Prefer to new as it's more readable. fn zero() -> Self; @@ -196,6 +191,20 @@ macro_rules! impl_hash { } } + impl FromJson for $from { + fn from_json(json: &Json) -> Self { + match json { + &Json::String(ref s) => { + match s.len() % 2 { + 0 => FromStr::from_str(clean_0x(s)).unwrap(), + _ => FromStr::from_str(&("0".to_string() + &(clean_0x(s).to_string()))[..]).unwrap() + } + }, + _ => Default::default(), + } + } + } + impl fmt::Debug for $from { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for i in self.0.iter() { @@ -381,6 +390,7 @@ macro_rules! impl_hash { &self ^ &rhs } } + impl $from { pub fn hex(&self) -> String { format!("{:?}", self) @@ -389,6 +399,10 @@ macro_rules! impl_hash { pub fn from_bloomed(b: &T) -> Self where T: FixedHash { b.bloom_part($size) } } + impl Default for $from { + fn default() -> Self { $from::new() } + } + impl From for $from { fn from(mut value: u64) -> $from { let mut ret = $from::new(); diff --git a/src/json_aid.rs b/src/json_aid.rs index d065352af..570f466bc 100644 --- a/src/json_aid.rs +++ b/src/json_aid.rs @@ -8,6 +8,14 @@ pub fn clean(s: &str) -> &str { } } +pub fn u256_from_str(s: &str) -> U256 { + if s.len() >= 2 && &s[0..2] == "0x" { + U256::from_str(&s[2..]).unwrap_or(U256::from(0)) + } else { + U256::from_dec_str(s).unwrap_or(U256::from(0)) + } +} + pub fn bytes_from_json(json: &Json) -> Bytes { let s = json.as_string().unwrap_or(""); if s.len() % 2 == 1 { @@ -34,34 +42,97 @@ pub fn vec_h256_from_json(json: &Json) -> Vec { json.as_array().unwrap().iter().map(&h256_from_json).collect() } -pub fn u256_from_str(s: &str) -> U256 { - if s.len() >= 2 && &s[0..2] == "0x" { - U256::from_str(&s[2..]).unwrap_or(U256::from(0)) - } else { - U256::from_dec_str(s).unwrap_or(U256::from(0)) - } -} - -pub fn u256_from_json(json: &Json) -> U256 { - u256_from_str(json.as_string().unwrap_or("")) +pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap { + json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut m, (key, value)| { + m.insert(H256::from(&u256_from_str(key)), H256::from(&U256::from_json(value))); + m + }) } pub fn usize_from_json(json: &Json) -> usize { - u256_from_json(json).low_u64() as usize + U256::from_json(json).low_u64() as usize } pub fn u64_from_json(json: &Json) -> u64 { - u256_from_json(json).low_u64() + U256::from_json(json).low_u64() } pub fn u32_from_json(json: &Json) -> u32 { - u256_from_json(json).low_u32() + U256::from_json(json).low_u32() } pub fn u16_from_json(json: &Json) -> u16 { - u256_from_json(json).low_u32() as u16 + U256::from_json(json).low_u32() as u16 } pub fn u8_from_json(json: &Json) -> u8 { - u256_from_json(json).low_u32() as u8 + U256::from_json(json).low_u32() as u8 +} + +impl FromJson for Vec where T: FromJson { + fn from_json(json: &Json) -> Self { + json.as_array().unwrap().iter().map(|x|T::from_json(x)).collect() + } +} + +impl FromJson for u64 { + fn from_json(json: &Json) -> Self { + U256::from_json(json).low_u64() + } +} + +impl FromJson for u32 { + fn from_json(json: &Json) -> Self { + U256::from_json(json).low_u64() as u32 + } +} + +impl FromJson for u16 { + fn from_json(json: &Json) -> Self { + U256::from_json(json).low_u64() as u16 + } +} + +impl FromJson for u8 { + fn from_json(json: &Json) -> Self { + U256::from_json(json).low_u64() as u8 + } +} + +#[test] +fn u256_from_json() { + let j = Json::from_str("{ \"dec\": \"10\", \"hex\": \"0x0a\", \"int\": 10 }").unwrap(); + + let v: U256 = xjson!(&j["dec"]); + assert_eq!(U256::from(10), v); + let v: U256 = xjson!(&j["hex"]); + assert_eq!(U256::from(10), v); + let v: U256 = xjson!(&j["int"]); + assert_eq!(U256::from(10), v); +} + +#[test] +fn h256_from_json_() { + let j = Json::from_str("{ \"with\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\", \"without\": \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\" }").unwrap(); + + let v: H256 = xjson!(&j["with"]); + assert_eq!(H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(), v); + let v: H256 = xjson!(&j["without"]); + assert_eq!(H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(), v); +} + +#[test] +fn vec_u256_from_json() { + let j = Json::from_str("{ \"array\": [ \"10\", \"0x0a\", 10] }").unwrap(); + + let v: Vec = xjson!(&j["array"]); + assert_eq!(vec![U256::from(10); 3], v); +} + +#[test] +fn vec_h256_from_json_() { + let j = Json::from_str("{ \"array\": [ \"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\", \"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\"] }").unwrap(); + + let v: Vec = xjson!(&j["array"]); + assert_eq!(vec![H256::from_str("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef").unwrap(); 2], v); } diff --git a/src/lib.rs b/src/lib.rs index 7bfe147f9..533ae86bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,16 +34,15 @@ extern crate mio; extern crate rand; extern crate rocksdb; extern crate tiny_keccak; - #[macro_use] extern crate heapsize; #[macro_use] extern crate log; #[macro_use] extern crate lazy_static; - +#[macro_use] +extern crate itertools; extern crate env_logger; - extern crate time; extern crate crypto as rcrypto; extern crate secp256k1; @@ -51,12 +50,16 @@ extern crate arrayvec; extern crate elastic_array; pub mod standard; +#[macro_use] +pub mod from_json; +#[macro_use] pub mod common; pub mod error; pub mod hash; pub mod uint; pub mod bytes; pub mod rlp; +pub mod misc; pub mod json_aid; pub mod vector; pub mod sha3; @@ -75,6 +78,7 @@ pub mod semantic_version; pub mod network; pub use common::*; +pub use misc::*; pub use json_aid::*; pub use rlp::*; pub use hashdb::*; diff --git a/src/sha3.rs b/src/sha3.rs index 5b0a0c6a4..c23dfc7a9 100644 --- a/src/sha3.rs +++ b/src/sha3.rs @@ -5,6 +5,8 @@ use tiny_keccak::Keccak; use bytes::{BytesConvertable,Populatable}; use hash::{H256, FixedHash}; +pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] ); + /// Types implementing this trait are sha3able. /// /// ``` @@ -43,12 +45,10 @@ impl Hashable for T where T: BytesConvertable { #[test] fn sha3_empty() { - use std::str::FromStr; - assert_eq!([0u8; 0].sha3(), H256::from_str("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").unwrap()); + assert_eq!([0u8; 0].sha3(), SHA3_EMPTY); } #[test] fn sha3_as() { - use std::str::FromStr; - assert_eq!([0x41u8; 32].sha3(), H256::from_str("59cad5948673622c1d64e2322488bf01619f7ff45789741b15a9f782ce9290a8").unwrap()); + assert_eq!([0x41u8; 32].sha3(), From::from("59cad5948673622c1d64e2322488bf01619f7ff45789741b15a9f782ce9290a8")); } diff --git a/src/standard.rs b/src/standard.rs index 7298dc232..19a084a2c 100644 --- a/src/standard.rs +++ b/src/standard.rs @@ -7,6 +7,7 @@ pub use std::ptr; pub use std::result; pub use std::option; pub use std::mem; +pub use std::ops; pub use std::path::Path; pub use std::str::{FromStr}; @@ -22,6 +23,7 @@ pub use std::collections::*; pub use rustc_serialize::json::Json; pub use rustc_serialize::base64::FromBase64; -pub use rustc_serialize::hex::FromHex; +pub use rustc_serialize::hex::{FromHex, FromHexError}; pub use heapsize::HeapSizeOf; +pub use itertools::Itertools; diff --git a/src/uint.rs b/src/uint.rs index b5607390b..78e8d7cd0 100644 --- a/src/uint.rs +++ b/src/uint.rs @@ -21,17 +21,8 @@ ///! The functions here are designed to be fast. ///! -use std::fmt; -use std::cmp::*; -use std::ops::*; -use std::str::FromStr; -use std::hash::{Hash, Hasher}; -use rustc_serialize::hex::{FromHex, FromHexError}; - -pub trait FromDecStr: Sized { - type Err; - fn from_dec_str(value: &str) -> Result; -} +use standard::*; +use from_json::*; macro_rules! impl_map_from { ($thing:ident, $from:ty, $to:ty) => { @@ -43,32 +34,77 @@ macro_rules! impl_map_from { } } +pub trait Uint: Sized + Default + FromStr + From + FromJson + fmt::Debug + fmt::Display + PartialOrd + Ord + PartialEq + Eq + Hash { + + /// Size of this type. + const SIZE: usize; + + fn zero() -> Self; + fn one() -> Self; + + type FromDecStrErr; + fn from_dec_str(value: &str) -> Result; + + /// Conversion to u32 + fn low_u32(&self) -> u32; + + /// Conversion to u64 + fn low_u64(&self) -> u64; + + /// Conversion to u32 with overflow checking + fn as_u32(&self) -> u32; + + /// Conversion to u64 with overflow checking + fn as_u64(&self) -> u64; + + /// Return the least number of bits needed to represent the number + fn bits(&self) -> usize; + fn bit(&self, index: usize) -> bool; + fn byte(&self, index: usize) -> u8; + fn to_bytes(&self, bytes: &mut[u8]); + + fn exp10(n: usize) -> Self; +} + macro_rules! construct_uint { ($name:ident, $n_words:expr) => ( /// Little-endian large integer type #[derive(Copy, Clone, Eq, PartialEq)] pub struct $name(pub [u64; $n_words]); - impl $name { - pub const SIZE: usize = $n_words * 8; + impl Uint for $name { + const SIZE: usize = $n_words * 8; + + type FromDecStrErr = FromHexError; + + /// TODO: optimize, throw appropriate err + fn from_dec_str(value: &str) -> Result { + Ok(value.bytes() + .map(|b| b - 48) + .fold($name::from(0u64), | acc, c | + // fast multiplication by 10 + // (acc << 3) + (acc << 1) => acc * 10 + (acc << 3) + (acc << 1) + $name::from(c) + )) + } /// Conversion to u32 #[inline] - pub fn low_u32(&self) -> u32 { + fn low_u32(&self) -> u32 { let &$name(ref arr) = self; arr[0] as u32 } /// Conversion to u64 #[inline] - pub fn low_u64(&self) -> u64 { + fn low_u64(&self) -> u64 { let &$name(ref arr) = self; arr[0] } /// Conversion to u32 with overflow checking #[inline] - pub fn as_u32(&self) -> u32 { + fn as_u32(&self) -> u32 { let &$name(ref arr) = self; if (arr[0] & (0xffffffffu64 << 32)) != 0 { panic!("Integer overflow when casting U256") @@ -78,7 +114,7 @@ macro_rules! construct_uint { /// Conversion to u64 with overflow checking #[inline] - pub fn as_u64(&self) -> u64 { + fn as_u64(&self) -> u64 { let &$name(ref arr) = self; for i in 1..$n_words { if arr[i] != 0 { @@ -89,7 +125,7 @@ macro_rules! construct_uint { } /// Return the least number of bits needed to represent the number #[inline] - pub fn bits(&self) -> usize { + fn bits(&self) -> usize { let &$name(ref arr) = self; for i in 1..$n_words { if arr[$n_words - i] > 0 { return (0x40 * ($n_words - i + 1)) - arr[$n_words - i].leading_zeros() as usize; } @@ -98,18 +134,18 @@ macro_rules! construct_uint { } #[inline] - pub fn bit(&self, index: usize) -> bool { + fn bit(&self, index: usize) -> bool { let &$name(ref arr) = self; arr[index / 64] & (1 << (index % 64)) != 0 } #[inline] - pub fn byte(&self, index: usize) -> u8 { + fn byte(&self, index: usize) -> u8 { let &$name(ref arr) = self; (arr[index / 8] >> ((index % 8)) * 8) as u8 } - pub fn to_bytes(&self, bytes: &mut[u8]) { + fn to_bytes(&self, bytes: &mut[u8]) { assert!($n_words * 8 == bytes.len()); let &$name(ref arr) = self; for i in 0..bytes.len() { @@ -120,7 +156,7 @@ macro_rules! construct_uint { } #[inline] - pub fn exp10(n: usize) -> $name { + fn exp10(n: usize) -> $name { match n { 0 => $name::from(1u64), _ => $name::exp10(n - 1) * $name::from(10u64) @@ -128,15 +164,17 @@ macro_rules! construct_uint { } #[inline] - pub fn zero() -> $name { + fn zero() -> $name { From::from(0u64) } #[inline] - pub fn one() -> $name { + fn one() -> $name { From::from(1u64) } + } + impl $name { /// Multiplication by u32 fn mul_u32(self, other: u32) -> $name { let $name(ref arr) = self; @@ -154,6 +192,12 @@ macro_rules! construct_uint { } } + impl Default for $name { + fn default() -> Self { + $name::zero() + } + } + impl From for $name { fn from(value: u64) -> $name { let mut ret = [0; $n_words]; @@ -162,6 +206,23 @@ macro_rules! construct_uint { } } + impl FromJson for $name { + fn from_json(json: &Json) -> Self { + match json { + &Json::String(ref s) => { + if s.len() >= 2 && &s[0..2] == "0x" { + FromStr::from_str(&s[2..]).unwrap_or(Default::default()) + } else { + Uint::from_dec_str(s).unwrap_or(Default::default()) + } + }, + &Json::U64(u) => From::from(u), + &Json::I64(i) => From::from(i as u64), + _ => Uint::zero(), + } + } + } + impl_map_from!($name, u8, u64); impl_map_from!($name, u16, u64); impl_map_from!($name, u32, u64); @@ -443,22 +504,6 @@ macro_rules! construct_uint { state.finish(); } } - - impl FromDecStr for $name { - type Err = FromHexError; - - /// TODO: optimize, throw appropriate err - fn from_dec_str(value: &str) -> Result { - Ok(value.bytes() - .map(|b| b - 48) - .fold($name::from(0u64), | acc, c | - // fast multiplication by 10 - // (acc << 3) + (acc << 1) => acc * 10 - (acc << 3) + (acc << 1) + $name::from(c) - )) - } - } - ); } @@ -557,8 +602,7 @@ pub const BAD_U256: U256 = U256([0xffffffffffffffffu64; 4]); #[cfg(test)] mod tests { - use uint::U256; - use uint::FromDecStr; + use uint::{Uint, U256}; use std::str::FromStr; #[test]