spec loading cleanup (#858)

* spec loading cleanup in progress

* changed engine field in json spec

* refactored engine params

* polishing spec loading refactor

* fixed compiling json tests

* fixed compiling parity

* removed warnings

* removed commented out code

* fixed failing test

* bringing back removed TODO in spec.
This commit is contained in:
Marek Kotewicz
2016-04-09 19:20:35 +02:00
committed by Gav Wood
parent d823fd7685
commit 373284ca0a
46 changed files with 957 additions and 704 deletions

View File

@@ -21,7 +21,7 @@ use hash::H256;
use blockchain::state::State;
use blockchain::header::Header;
use blockchain::block::Block;
use spec::Genesis;
use spec::{Genesis, Seal, Ethereum};
/// Blockchain deserialization.
#[derive(Debug, PartialEq, Deserialize)]
@@ -54,10 +54,10 @@ impl BlockChain {
/// Returns spec compatible genesis struct.
pub fn genesis(&self) -> Genesis {
Genesis {
nonce: Some(self.genesis_block.nonce.clone()),
mix_hash: Some(self.genesis_block.mix_hash.clone()),
seal_fields: None,
seal_rlp: None,
seal: Seal::Ethereum(Ethereum {
nonce: self.genesis_block.nonce.clone(),
mix_hash: self.genesis_block.mix_hash.clone(),
}),
difficulty: self.genesis_block.difficulty,
author: self.genesis_block.author.clone(),
timestamp: self.genesis_block.timestamp,

View File

@@ -22,7 +22,7 @@ use blockchain::account::Account;
/// Blockchain test state deserializer.
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct State(pub BTreeMap<Address, Account>);
pub struct State(BTreeMap<Address, Account>);
impl IntoIterator for State {
type Item = <BTreeMap<Address, Account> as IntoIterator>::Item;

View File

@@ -22,9 +22,19 @@ use spec::builtin::Builtin;
/// Spec account.
#[derive(Debug, PartialEq, Deserialize)]
pub struct Account {
builtin: Option<Builtin>,
balance: Option<Uint>,
nonce: Option<Uint>,
/// Builtin contract.
pub builtin: Option<Builtin>,
/// Balance.
pub balance: Option<Uint>,
/// Nonce.
pub nonce: Option<Uint>,
}
impl Account {
/// Returns true if account does not have nonce and balance.
pub fn is_empty(&self) -> bool {
self.balance.is_none() && self.nonce.is_none()
}
}
#[cfg(test)]

View File

@@ -17,14 +17,16 @@
//! Spec builtin deserialization.
/// Linear pricing.
#[derive(Debug, PartialEq, Deserialize)]
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Linear {
base: u64,
word: u64,
/// Base price.
pub base: usize,
/// Price for word.
pub word: usize,
}
/// Pricing variants.
#[derive(Debug, PartialEq, Deserialize)]
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub enum Pricing {
/// Linear pricing.
#[serde(rename="linear")]
@@ -32,10 +34,12 @@ pub enum Pricing {
}
/// Spec builtin.
#[derive(Debug, PartialEq, Deserialize)]
#[derive(Debug, PartialEq, Deserialize, Clone)]
pub struct Builtin {
name: String,
pricing: Pricing,
/// Builtin name.
pub name: String,
/// Builtin pricing.
pub pricing: Pricing,
}
#[cfg(test)]

63
json/src/spec/engine.rs Normal file
View File

@@ -0,0 +1,63 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity 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.
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
//! Engine deserialization.
use serde::{Deserializer, Error};
use serde::de::Visitor;
use spec::Ethash;
/// Engine deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub enum Engine {
/// Null engine.
Null,
/// Ethash engine.
Ethash(Ethash),
}
#[cfg(test)]
mod tests {
use serde_json;
use spec::Engine;
#[test]
fn engine_deserialization() {
let s = r#"{
"Null": null
}"#;
let deserialized: Engine = serde_json::from_str(s).unwrap();
assert_eq!(Engine::Null, deserialized);
let s = r#"{
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
}
}
}"#;
let _deserialized: Engine = serde_json::from_str(s).unwrap();
}
}

75
json/src/spec/ethash.rs Normal file
View File

@@ -0,0 +1,75 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity 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.
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
//! Ethash params deserialization.
use uint::Uint;
use hash::Address;
/// Ethash params deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub struct EthashParams {
/// Tie breaking gas.
#[serde(rename="tieBreakingGas")]
pub tie_breaking_gas: bool,
/// Gas limit divisor.
#[serde(rename="gasLimitBoundDivisor")]
pub gas_limit_bound_divisor: Uint,
/// Minimum difficulty.
#[serde(rename="minimumDifficulty")]
pub minimum_difficulty: Uint,
/// Difficulty bound divisor.
#[serde(rename="difficultyBoundDivisor")]
pub difficulty_bound_divisor: Uint,
/// Block duration.
#[serde(rename="durationLimit")]
pub duration_limit: Uint,
/// Block reward.
#[serde(rename="blockReward")]
pub block_reward: Uint,
/// Namereg contract address.
pub registrar: Address,
}
/// Ethash engine deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub struct Ethash {
/// Ethash params.
pub params: EthashParams,
}
#[cfg(test)]
mod tests {
use serde_json;
use spec::ethash::Ethash;
#[test]
fn ethash_deserialization() {
let s = r#"{
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
}
}"#;
let _deserialized: Ethash = serde_json::from_str(s).unwrap();
}
}

View File

@@ -17,27 +17,15 @@
//! Spec genesis deserialization.
use uint::Uint;
use hash::{H64, Address, H256};
use hash::{Address, H256};
use bytes::Bytes;
use spec::Seal;
/// Spec genesis.
#[derive(Debug, PartialEq, Deserialize)]
pub struct Genesis {
// old seal
/// Seal nonce.
pub nonce: Option<H64>,
#[serde(rename="mixHash")]
/// Seal mix hash.
pub mix_hash: Option<H256>,
// new seal // TODO: consider moving it to a separate seal structure
#[serde(rename="sealFields")]
/// Number of seal fields.
pub seal_fields: Option<usize>,
#[serde(rename="sealRlp")]
/// Seal rlp.
pub seal_rlp: Option<Bytes>,
/// Seal.
pub seal: Seal,
/// Difficulty.
pub difficulty: Uint,
/// Block author.
@@ -77,7 +65,12 @@ mod tests {
let s = r#"{
"nonce": "0x0000000000000042",
"difficulty": "0x400000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"seal": {
"ethereum": {
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x00006d6f7264656e"
}
},
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

View File

@@ -21,9 +21,17 @@ pub mod builtin;
pub mod genesis;
pub mod params;
pub mod spec;
pub mod seal;
pub mod engine;
pub mod state;
pub mod ethash;
pub use self::account::Account;
pub use self::builtin::Builtin;
pub use self::builtin::{Builtin, Pricing, Linear};
pub use self::genesis::Genesis;
pub use self::params::Params;
pub use self::spec::Spec;
pub use self::seal::{Seal, Ethereum, Generic};
pub use self::engine::Engine;
pub use self::state::State;
pub use self::ethash::{Ethash, EthashParams};

View File

@@ -17,34 +17,25 @@
//! Spec params deserialization.
use uint::Uint;
use hash::Address;
/// Spec params.
#[derive(Debug, PartialEq, Deserialize)]
pub struct Params {
/// Account start nonce.
#[serde(rename="accountStartNonce")]
account_start_nonce: Uint,
pub account_start_nonce: Uint,
/// Homestead transition block number.
#[serde(rename="frontierCompatibilityModeLimit")]
frontier_compatibility_mode_limit: Uint,
pub frontier_compatibility_mode_limit: Uint,
/// Maximum size of extra data.
#[serde(rename="maximumExtraDataSize")]
maximum_extra_data_size: Uint,
#[serde(rename="tieBreakingGas")]
tie_breaking_gas: bool,
#[serde(rename="minGasLimit")]
min_gas_limit: Uint,
#[serde(rename="gasLimitBoundDivisor")]
gas_limit_bound_divisor: Uint,
#[serde(rename="minimumDifficulty")]
minimum_difficulty: Uint,
#[serde(rename="difficultyBoundDivisor")]
difficulty_bound_divisor: Uint,
#[serde(rename="durationLimit")]
duration_limit: Uint,
#[serde(rename="blockReward")]
block_reward: Uint,
registrar: Address,
pub maximum_extra_data_size: Uint,
/// Network id.
#[serde(rename="networkID")]
network_id: Uint,
pub network_id: Uint,
/// Minimum gas limit.
#[serde(rename="minGasLimit")]
pub min_gas_limit: Uint,
}
#[cfg(test)]
@@ -55,19 +46,13 @@ mod tests {
#[test]
fn params_deserialization() {
let s = r#"{
"accountStartNonce": "0x00",
"frontierCompatibilityModeLimit": "0x118c30",
"maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"networkID" : "0x1",
"minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"networkID" : "0x1"
"accountStartNonce": "0x00"
}"#;
let _deserialized: Params = serde_json::from_str(s).unwrap();
// TODO: validate all fields
}

73
json/src/spec/seal.rs Normal file
View File

@@ -0,0 +1,73 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity 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.
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
//! Spec seal deserialization.
use hash::{H64, H256};
use bytes::Bytes;
/// Ethereum seal.
#[derive(Debug, PartialEq, Deserialize)]
pub struct Ethereum {
/// Seal nonce.
pub nonce: H64,
/// Seal mix hash.
#[serde(rename="mixHash")]
pub mix_hash: H256,
}
/// Generic seal.
#[derive(Debug, PartialEq, Deserialize)]
pub struct Generic {
/// Number of fields.
pub fields: usize,
/// Their rlp.
pub rlp: Bytes,
}
/// Seal variants.
#[derive(Debug, PartialEq, Deserialize)]
pub enum Seal {
/// Ethereum seal.
#[serde(rename="ethereum")]
Ethereum(Ethereum),
/// Generic seal.
#[serde(rename="generic")]
Generic(Generic),
}
#[cfg(test)]
mod tests {
use serde_json;
use spec::Seal;
#[test]
fn builtin_deserialization() {
let s = r#"[{
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},{
"generic": {
"fields": 1,
"rlp": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
}
}]"#;
let _deserialized: Vec<Seal> = serde_json::from_str(s).unwrap();
// TODO: validate all fields
}
}

View File

@@ -16,21 +16,33 @@
//! Spec deserialization.
use std::collections::BTreeMap;
use hash::Address;
use spec::account::Account;
use spec::params::Params;
use spec::genesis::Genesis;
use std::io::Read;
use serde_json;
use serde_json::Error;
use spec::{Params, Genesis, Engine, State};
/// Spec deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub struct Spec {
name: String,
#[serde(rename="engineName")]
engine_name: String, // TODO: consider making it an enum
params: Params,
genesis: Genesis,
accounts: BTreeMap<Address, Account>,
/// Spec name.
pub name: String,
/// Engine.
pub engine: Engine,
/// Spec params.
pub params: Params,
/// Genesis header.
pub genesis: Genesis,
/// Genesis state.
pub accounts: State,
/// Boot nodes.
pub nodes: Option<Vec<String>>,
}
impl Spec {
/// Loads test from json.
pub fn load<R>(reader: R) -> Result<Self, Error> where R: Read {
serde_json::from_reader(reader)
}
}
#[cfg(test)]
@@ -42,25 +54,34 @@ mod tests {
fn spec_deserialization() {
let s = r#"{
"name": "Morden",
"engineName": "Ethash",
"engine": {
"Ethash": {
"params": {
"tieBreakingGas": false,
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
}
}
},
"params": {
"accountStartNonce": "0x0100000",
"frontierCompatibilityModeLimit": "0x789b0",
"maximumExtraDataSize": "0x20",
"tieBreakingGas": false,
"minGasLimit": "0x1388",
"gasLimitBoundDivisor": "0x0400",
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x4563918244F40000",
"registrar": "",
"networkID" : "0x2"
},
"genesis": {
"nonce": "0x00006d6f7264656e",
"seal": {
"ethereum": {
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x00006d6f7264656e"
}
},
"difficulty": "0x20000",
"mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",

44
json/src/spec/state.rs Normal file
View File

@@ -0,0 +1,44 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity 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.
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
//! Blockchain test state deserializer.
use std::collections::BTreeMap;
use hash::Address;
use spec::{Account, Builtin};
/// Blockchain test state deserializer.
#[derive(Debug, PartialEq, Deserialize)]
pub struct State(BTreeMap<Address, Account>);
impl State {
/// Returns all builtins.
pub fn builtins(&self) -> BTreeMap<Address, Builtin> {
self.0
.iter()
.filter_map(|ref pair| pair.1.builtin.clone().map(|b| (pair.0.clone(), b.clone())))
.collect()
}
}
impl IntoIterator for State {
type Item = <BTreeMap<Address, Account> as IntoIterator>::Item;
type IntoIter = <BTreeMap<Address, Account> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}

View File

@@ -23,7 +23,7 @@ use util::numbers::{U256, Uint as U};
/// Lenient uint json deserialization for test json files.
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub struct Uint(U256);
pub struct Uint(pub U256);
impl Into<U256> for Uint {
fn into(self) -> U256 {
@@ -37,9 +37,15 @@ impl Into<u64> for Uint {
}
}
impl Into<usize> for Uint {
fn into(self) -> usize {
// TODO: clean it after util conversions refactored.
u64::from(self.0) as usize
}
}
impl Into<u8> for Uint {
fn into(self) -> u8 {
<Uint as Into<u64>>::into(self) as u8
u64::from(self.0) as u8
}
}
@@ -55,6 +61,10 @@ struct UintVisitor;
impl Visitor for UintVisitor {
type Value = Uint;
fn visit_u64<E>(&mut self, value: u64) -> Result<Self::Value, E> where E: Error {
Ok(Uint(U256::from(value)))
}
fn visit_str<E>(&mut self, value: &str) -> Result<Self::Value, E> where E: Error {
let value = match value.len() {
0 => U256::from(0),
@@ -83,12 +93,13 @@ mod test {
#[test]
fn uint_deserialization() {
let s = r#"["0xa", "10", "", "0x"]"#;
let s = r#"["0xa", "10", "", "0x", 0]"#;
let deserialized: Vec<Uint> = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, vec![
Uint(U256::from(10)),
Uint(U256::from(10)),
Uint(U256::from(0)),
Uint(U256::from(0)),
Uint(U256::from(0))
]);
}