diff --git a/Cargo.lock b/Cargo.lock index 8ef60cbc6..aa4f0a902 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "rpassword 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -216,6 +217,7 @@ dependencies = [ "ethash 1.1.0", "ethcore-devtools 1.1.0", "ethcore-util 1.1.0", + "ethjson 0.1.0", "heapsize 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index ac097a05f..62a3c31c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ ethminer = { path = "miner" } ethcore-devtools = { path = "devtools" } ethcore-rpc = { path = "rpc", optional = true } ethjson = { path = "json" } +serde_json = "0.7.0" [features] default = ["rpc"] @@ -40,6 +41,10 @@ travis-nightly = ["ethcore/json-tests", "dev"] path = "parity/main.rs" name = "parity" +[[bin]] +path = "parity/rpctest.rs" +name = "rpctest" + [profile.release] debug = false lto = false diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 3683222b1..5b613fdb2 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -21,6 +21,7 @@ clippy = { version = "0.0.54", optional = true } crossbeam = "0.1.5" lazy_static = "0.1" ethcore-devtools = { path = "../devtools" } +ethjson = { path = "../json" } [features] jit = ["evmjit"] diff --git a/ethcore/res/ethereum/frontier.json b/ethcore/res/ethereum/frontier.json index cd66ad3f1..8f9209179 100644 --- a/ethcore/res/ethereum/frontier.json +++ b/ethcore/res/ethereum/frontier.json @@ -33,10 +33,10 @@ "enode://248f12bc8b18d5289358085520ac78cd8076485211e6d96ab0bc93d6cd25442db0ce3a937dc404f64f207b0b9aed50e25e98ce32af5ac7cb321ff285b97de485@parity-node-zero.ethcore.io:30303" ], "accounts": { - "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, - "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, - "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "3282791d6fd713f1e94f4bfd565eaa78b3a0599d": { "balance": "1337000000000000000000" }, diff --git a/ethcore/res/ethereum/frontier_like_test.json b/ethcore/res/ethereum/frontier_like_test.json index 3e4108566..376b369c4 100644 --- a/ethcore/res/ethereum/frontier_like_test.json +++ b/ethcore/res/ethereum/frontier_like_test.json @@ -1,5 +1,5 @@ { - "engineName": "Frontier (Test)", + "name": "Frontier (Test)", "engineName": "Ethash", "params": { "accountStartNonce": "0x00", @@ -26,9 +26,9 @@ "gasLimit": "0x1388" }, "accounts": { - "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, - "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, - "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "linear": { "base": 15, "word": 3 } } } + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } } } diff --git a/ethcore/res/ethereum/frontier_test.json b/ethcore/res/ethereum/frontier_test.json index 8ee1cafd9..92e8f5877 100644 --- a/ethcore/res/ethereum/frontier_test.json +++ b/ethcore/res/ethereum/frontier_test.json @@ -1,5 +1,5 @@ { - "engineName": "Frontier (Test)", + "name": "Frontier (Test)", "engineName": "Ethash", "params": { "accountStartNonce": "0x00", @@ -26,9 +26,9 @@ "gasLimit": "0x1388" }, "accounts": { - "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, - "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, - "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, - "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "linear": { "base": 15, "word": 3 } } } + "0000000000000000000000000000000000000001": { "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } } } diff --git a/ethcore/res/ethereum/homestead_test.json b/ethcore/res/ethereum/homestead_test.json index 1fb5dff80..0f0e630dd 100644 --- a/ethcore/res/ethereum/homestead_test.json +++ b/ethcore/res/ethereum/homestead_test.json @@ -26,9 +26,9 @@ "gasLimit": "0x1388" }, "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "linear": { "base": 15, "word": 3 } } } + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } } } } diff --git a/ethcore/res/ethereum/morden.json b/ethcore/res/ethereum/morden.json index e9f8e0e99..2cf4785db 100644 --- a/ethcore/res/ethereum/morden.json +++ b/ethcore/res/ethereum/morden.json @@ -29,10 +29,10 @@ "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" ], "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } } } diff --git a/ethcore/res/ethereum/olympic.json b/ethcore/res/ethereum/olympic.json index b3dfc1ed8..0cc2e6a57 100644 --- a/ethcore/res/ethereum/olympic.json +++ b/ethcore/res/ethereum/olympic.json @@ -26,10 +26,10 @@ "gasLimit": "0x2fefd8" }, "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, "e6716f9544a56c530d868e4bfbacb172315bdead": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, "b9c015918bdaba24b4ff057a92a3873d6eb201be": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }, diff --git a/ethcore/res/null_morden.json b/ethcore/res/null_morden.json index 46507ff95..70b48fbdb 100644 --- a/ethcore/res/null_morden.json +++ b/ethcore/res/null_morden.json @@ -26,10 +26,10 @@ "gasLimit": "0x2fefd8" }, "accounts": { - "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, - "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, - "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, - "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "linear": { "base": 15, "word": 3 } } }, + "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } } } diff --git a/ethcore/src/builtin.rs b/ethcore/src/builtin.rs index 5589a2525..2ee8e24b1 100644 --- a/ethcore/src/builtin.rs +++ b/ethcore/src/builtin.rs @@ -19,7 +19,7 @@ use crypto::sha2::Sha256; use crypto::ripemd160::Ripemd160; use crypto::digest::Digest; -/// Definition of a contract whose implementation is built-in. +/// Definition of a contract whose implementation is built-in. pub struct Builtin { /// The gas cost of running this built-in for the given size of input data. pub cost: Box U256>, // TODO: U256 should be bignum. @@ -63,14 +63,16 @@ impl Builtin { /// Create a builtin from JSON. /// - /// JSON must be of the form `{ "name": "identity", "linear": {"base": 10, "word": 20} }`. + /// JSON must be of the form `{ "name": "identity", "pricing": {"base": 10, "word": 20} }`. pub fn from_json(json: &Json) -> Option { // NICE: figure out a more convenient means of handing errors here. if let Json::String(ref name) = json["name"] { - if let Json::Object(ref o) = json["linear"] { - if let Json::U64(ref word) = o["word"] { - if let Json::U64(ref base) = o["base"] { - return Self::from_named_linear(&name[..], *base as usize, *word as usize); + if let Json::Object(ref o) = json["pricing"] { + if let Json::Object(ref o) = o["linear"] { + if let Json::U64(ref word) = o["word"] { + if let Json::U64(ref base) = o["base"] { + return Self::from_named_linear(&name[..], *base as usize, *word as usize); + } } } } @@ -274,7 +276,7 @@ fn from_named_linear() { #[test] fn from_json() { - let text = "{ \"name\": \"identity\", \"linear\": {\"base\": 10, \"word\": 20} }"; + let text = r#"{"name": "identity", "pricing": {"linear": {"base": 10, "word": 20}}}"#; let json = Json::from_str(text).unwrap(); let b = Builtin::from_json(&json).unwrap(); assert_eq!((*b.cost)(0), U256::from(10)); diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 572cda2fa..dfa321639 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -83,6 +83,7 @@ extern crate time; extern crate env_logger; extern crate num_cpus; extern crate crossbeam; +extern crate ethjson; #[cfg(test)] extern crate ethcore_devtools as devtools; #[cfg(feature = "jit" )] extern crate evmjit; @@ -100,13 +101,13 @@ pub mod spec; pub mod transaction; pub mod views; pub mod receipt; +pub mod pod_state; mod common; mod basic_types; #[macro_use] mod evm; mod env_info; mod pod_account; -mod pod_state; mod account_diff; mod state_diff; mod engine; diff --git a/ethcore/src/pod_account.rs b/ethcore/src/pod_account.rs index d2690051c..8f38326ca 100644 --- a/ethcore/src/pod_account.rs +++ b/ethcore/src/pod_account.rs @@ -17,6 +17,7 @@ use util::*; use account::*; use account_db::*; +use ethjson; #[derive(Debug,Clone,PartialEq,Eq)] /// An account, expressed as Plain-Old-Data (hence the name). @@ -73,6 +74,21 @@ impl PodAccount { } } +impl From for PodAccount { + fn from(a: ethjson::blockchain::Account) -> Self { + PodAccount { + balance: a.balance.into(), + nonce: a.nonce.into(), + code: a.code.into(), + storage: a.storage.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| { + let key: U256 = key.into(); + acc.insert(H256::from(key), value.into()); + acc + }) + } + } +} + impl fmt::Display for PodAccount { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "(bal={}; nonce={}; code={} bytes, #{}; storage={} items)", self.balance, self.nonce, self.code.len(), self.code.sha3(), self.storage.len()) diff --git a/ethcore/src/pod_state.rs b/ethcore/src/pod_state.rs index b873249ac..5782803ef 100644 --- a/ethcore/src/pod_state.rs +++ b/ethcore/src/pod_state.rs @@ -14,11 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! State of all accounts in the system expressed in Plain Old Data. + use util::*; use pod_account::*; +use ethjson; -#[derive(Debug,Clone,PartialEq,Eq,Default)] /// State of all accounts in the system expressed in Plain Old Data. +#[derive(Debug,Clone,PartialEq,Eq,Default)] pub struct PodState (BTreeMap); impl PodState { @@ -64,6 +67,15 @@ impl FromJson for PodState { } } +impl From for PodState { + fn from(s: ethjson::blockchain::State) -> PodState { + PodState(s.0.into_iter().fold(BTreeMap::new(), |mut acc, (key, value)| { + acc.insert(key.into(), PodAccount::from(value)); + acc + })) + } +} + impl fmt::Display for PodState { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for (add, acc) in &self.0 { diff --git a/ethcore/src/spec/genesis.rs b/ethcore/src/spec/genesis.rs new file mode 100644 index 000000000..686e8f6d1 --- /dev/null +++ b/ethcore/src/spec/genesis.rs @@ -0,0 +1,91 @@ +// 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 . + +use util::rlp::*; +use util::numbers::{Uint, U256}; +use util::hash::{H64, Address, H256}; +use ethjson; + +/// Genesis seal type. +pub enum Seal { + /// Classic ethereum seal. + Ethereum { + /// Seal nonce. + nonce: H64, + /// Seal mix hash. + mix_hash: H256, + }, + /// Generic seal. + Generic { + /// Number of seal fields. + fields: usize, + /// Seal rlp. + rlp: Vec, + }, +} + +/// Genesis components. +pub struct Genesis { + /// Seal. + pub seal: Seal, + /// Difficulty. + pub difficulty: U256, + /// Author. + pub author: Address, + /// Timestamp. + pub timestamp: u64, + /// Parent hash. + pub parent_hash: H256, + /// Gas limit. + pub gas_limit: U256, + /// Transactions root. + pub transactions_root: H256, + /// Receipts root. + pub receipts_root: H256, + /// State root. + pub state_root: Option, + /// Gas used. + pub gas_used: U256, + /// Extra data. + pub extra_data: Vec, +} + +impl From for Genesis { + fn from(g: ethjson::spec::Genesis) -> Self { + Genesis { + seal: match (g.nonce, g.mix_hash) { + (Some(nonce), Some(mix_hash)) => Seal::Ethereum { + nonce: nonce.into(), + mix_hash: mix_hash.into(), + }, + _ => Seal::Generic { + fields: g.seal_fields.unwrap(), + rlp: g.seal_rlp.unwrap().into(), + } + }, + difficulty: g.difficulty.into(), + author: g.author.into(), + timestamp: g.timestamp.into(), + parent_hash: g.parent_hash.into(), + gas_limit: g.gas_limit.into(), + transactions_root: g.transactions_root.map_or_else(|| SHA3_NULL_RLP.clone(), Into::into), + receipts_root: g.receipts_root.map_or_else(|| SHA3_NULL_RLP.clone(), Into::into), + state_root: g.state_root.map(Into::into), + gas_used: g.gas_used.map_or_else(U256::zero, Into::into), + extra_data: g.extra_data.map_or_else(Vec::new, Into::into), + } + } +} diff --git a/ethcore/src/spec/mod.rs b/ethcore/src/spec/mod.rs new file mode 100644 index 000000000..b85165d89 --- /dev/null +++ b/ethcore/src/spec/mod.rs @@ -0,0 +1,23 @@ +// 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 . + +//! Blockchain params. + +mod genesis; +pub mod spec; + +pub use self::spec::*; +pub use self::genesis::Genesis; diff --git a/ethcore/src/spec.rs b/ethcore/src/spec/spec.rs similarity index 92% rename from ethcore/src/spec.rs rename to ethcore/src/spec/spec.rs index 2208350cc..c7e2e4e9f 100644 --- a/ethcore/src/spec.rs +++ b/ethcore/src/spec/spec.rs @@ -21,6 +21,8 @@ use engine::*; use pod_state::*; use null_engine::*; use account_db::*; +use ethereum; +use super::genesis::{Seal as GenesisSeal, Genesis}; /// Convert JSON value to equivalent RLP representation. // TODO: handle container types. @@ -106,7 +108,7 @@ impl Spec { pub fn to_engine(self) -> Result, Error> { match self.engine_name.as_ref() { "NullEngine" => Ok(NullEngine::new_boxed(self)), - "Ethash" => Ok(super::ethereum::Ethash::new_boxed(self)), + "Ethash" => Ok(ethereum::Ethash::new_boxed(self)), _ => Err(Error::UnknownEngineName(self.engine_name.clone())) } } @@ -197,6 +199,32 @@ impl Spec { self.state_root_memo = RwLock::new(genesis.find("stateRoot").and_then(|_| Some(H256::from_json(&genesis["stateRoot"])))); } + /// Overwrite the genesis components. + pub fn overwrite_genesis_params(&mut self, g: Genesis) { + let (seal_fields, seal_rlp) = match g.seal { + GenesisSeal::Generic { fields, rlp } => (fields, rlp), + GenesisSeal::Ethereum { nonce, mix_hash } => { + let mut s = RlpStream::new(); + s.append(&mix_hash); + s.append(&nonce); + (2, s.out()) + } + }; + + self.parent_hash = g.parent_hash; + self.transactions_root = g.transactions_root; + self.receipts_root = g.receipts_root; + self.author = g.author; + self.difficulty = g.difficulty; + self.gas_limit = g.gas_limit; + self.gas_used = g.gas_used; + self.timestamp = g.timestamp; + self.extra_data = g.extra_data; + self.seal_fields = seal_fields; + self.seal_rlp = seal_rlp; + self.state_root_memo = RwLock::new(g.state_root); + } + /// Alter the value of the genesis state. pub fn set_genesis_state(&mut self, s: PodState) { self.genesis_state = s; @@ -304,7 +332,7 @@ impl Spec { } /// Create a new Spec which conforms to the Morden chain except that it's a NullEngine consensus. - pub fn new_test() -> Spec { Self::from_json_utf8(include_bytes!("../res/null_morden.json")) } + pub fn new_test() -> Spec { Self::from_json_utf8(include_bytes!("../../res/null_morden.json")) } } #[cfg(test)] diff --git a/json/src/blockchain/account.rs b/json/src/blockchain/account.rs index eb6f0a1dd..990c024c7 100644 --- a/json/src/blockchain/account.rs +++ b/json/src/blockchain/account.rs @@ -19,14 +19,19 @@ use std::collections::BTreeMap; use uint::Uint; use bytes::Bytes; +use hash::H256; /// Blockchain test account deserializer. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, PartialEq, Deserialize, Clone)] pub struct Account { - balance: Uint, - code: Bytes, - nonce: Uint, - storage: BTreeMap, + /// Balance. + pub balance: Uint, + /// Code. + pub code: Bytes, + /// Nonce. + pub nonce: Uint, + /// Storage. + pub storage: BTreeMap, } #[cfg(test)] @@ -35,7 +40,7 @@ mod tests { use blockchain::account::Account; #[test] - fn header_deserialization() { + fn account_deserialization() { let s = r#"{ "balance" : "0x09184e72a078", "code" : "0x600140600155", diff --git a/json/src/blockchain/block.rs b/json/src/blockchain/block.rs index ed297077c..190102ae5 100644 --- a/json/src/blockchain/block.rs +++ b/json/src/blockchain/block.rs @@ -31,6 +31,13 @@ pub struct Block { uncles: Vec
, } +impl Block { + /// Returns block rlp. + pub fn rlp(&self) -> Vec { + self.rlp.clone().into() + } +} + #[cfg(test)] mod tests { use serde_json; diff --git a/json/src/blockchain/blockchain.rs b/json/src/blockchain/blockchain.rs index f6b1259ca..4783819e5 100644 --- a/json/src/blockchain/blockchain.rs +++ b/json/src/blockchain/blockchain.rs @@ -20,19 +20,57 @@ use bytes::Bytes; use blockchain::state::State; use blockchain::header::Header; use blockchain::block::Block; +use spec::Genesis; /// Blockchain deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct BlockChain { + /// Genesis block header. #[serde(rename="genesisBlockHeader")] - genesis_block: Header, + pub genesis_block: Header, + /// Genesis block rlp. #[serde(rename="genesisRLP")] - genesis_rlp: Bytes, - blocks: Vec, + pub genesis_rlp: Bytes, + /// Blocks. + pub blocks: Vec, + /// Post state. #[serde(rename="postState")] - post_state: State, + pub post_state: State, + /// Pre state. #[serde(rename="pre")] - pre_state: State, + pub pre_state: State, +} + +impl BlockChain { + /// Returns genesis block rlp. + pub fn genesis_rlp(&self) -> Vec { + self.genesis_rlp.clone().into() + } + + /// Returns blocks rlp. + pub fn blocks_rlp(&self) -> Vec> { + self.blocks.iter().map(|block| block.rlp()).collect() + } + + /// 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, + difficulty: self.genesis_block.difficulty, + author: self.genesis_block.author.clone(), + timestamp: self.genesis_block.timestamp, + parent_hash: self.genesis_block.parent_hash.clone(), + gas_limit: self.genesis_block.gas_limit, + transactions_root: Some(self.genesis_block.transactions_root.clone()), + receipts_root: Some(self.genesis_block.receipts_root.clone()), + state_root: Some(self.genesis_block.state_root.clone()), + gas_used: Some(self.genesis_block.gas_used), + extra_data: Some(self.genesis_block.extra_data.clone()), + } + } } #[cfg(test)] diff --git a/json/src/blockchain/header.rs b/json/src/blockchain/header.rs index e97039213..ece6d6359 100644 --- a/json/src/blockchain/header.rs +++ b/json/src/blockchain/header.rs @@ -23,31 +23,48 @@ use bytes::Bytes; /// Blockchain test header deserializer. #[derive(Debug, PartialEq, Deserialize)] pub struct Header { - bloom: Bloom, - coinbase: Address, - difficulty: Uint, + /// Blocks bloom. + pub bloom: Bloom, + /// Blocks author. + #[serde(rename="coinbase")] + pub author: Address, + /// Difficulty. + pub difficulty: Uint, #[serde(rename="extraData")] - extra_data: Bytes, + /// Extra data. + pub extra_data: Bytes, + /// Gas limit. #[serde(rename="gasLimit")] - gas_limit: Uint, + pub gas_limit: Uint, + /// Gas used. #[serde(rename="gasUsed")] - gas_used: Uint, - hash: H256, + pub gas_used: Uint, + /// Hash. + pub hash: H256, #[serde(rename="mixHash")] - mix_hash: H256, - nonce: H64, - number: Uint, + /// Mix hash. + pub mix_hash: H256, + /// Seal nonce. + pub nonce: H64, + /// Block number. + pub number: Uint, + /// Parent hash. #[serde(rename="parentHash")] - parent_hash: H256, + pub parent_hash: H256, + /// Receipt root. #[serde(rename="receiptTrie")] - receipt_trie: H256, + pub receipts_root: H256, + /// State root. #[serde(rename="stateRoot")] - state_root: H256, - timestamp: Uint, + pub state_root: H256, + /// Timestamp. + pub timestamp: Uint, + /// Transactions root. #[serde(rename="transactionsTrie")] - transactions_trie: H256, + pub transactions_root: H256, + /// Uncles hash. #[serde(rename="uncleHash")] - uncle_hash: H256, + pub uncles_hash: H256, } #[cfg(test)] diff --git a/json/src/blockchain/mod.rs b/json/src/blockchain/mod.rs index 046b2e534..727469ea2 100644 --- a/json/src/blockchain/mod.rs +++ b/json/src/blockchain/mod.rs @@ -23,3 +23,11 @@ pub mod header; pub mod state; pub mod transaction; pub mod test; + +pub use self::account::Account; +pub use self::block::Block; +pub use self::blockchain::BlockChain; +pub use self::header::Header; +pub use self::state::State; +pub use self::test::Test; +pub use self::transaction::Transaction; diff --git a/json/src/blockchain/state.rs b/json/src/blockchain/state.rs index 2af0dbf21..51e77e95e 100644 --- a/json/src/blockchain/state.rs +++ b/json/src/blockchain/state.rs @@ -22,8 +22,8 @@ use hash::Address; use blockchain::account::Account; /// Blockchain test state deserializer. -#[derive(Debug, PartialEq, Deserialize)] -pub struct State(BTreeMap); +#[derive(Debug, PartialEq, Deserialize, Clone)] +pub struct State(pub BTreeMap); impl Deref for State { type Target = BTreeMap; diff --git a/json/src/blockchain/test.rs b/json/src/blockchain/test.rs index 6097a60e6..6f48a8bc6 100644 --- a/json/src/blockchain/test.rs +++ b/json/src/blockchain/test.rs @@ -21,6 +21,7 @@ use std::ops::Deref; use blockchain::blockchain::BlockChain; /// Blockchain test deserializer. +#[derive(Debug, PartialEq, Deserialize)] pub struct Test(BTreeMap); impl Deref for Test { diff --git a/json/src/bytes.rs b/json/src/bytes.rs index 28b7636d4..061795b40 100644 --- a/json/src/bytes.rs +++ b/json/src/bytes.rs @@ -21,7 +21,7 @@ use serde::{Deserialize, Deserializer, Error}; use serde::de::Visitor; /// Lenient bytes json deserialization for test json files. -#[derive(Default, Debug, PartialEq)] +#[derive(Default, Debug, PartialEq, Clone)] pub struct Bytes(Vec); impl Into> for Bytes { diff --git a/json/src/hash.rs b/json/src/hash.rs index 098f2e7a8..c555b6266 100644 --- a/json/src/hash.rs +++ b/json/src/hash.rs @@ -25,7 +25,7 @@ use util::hash::{H64 as Hash64, Address as Hash160, H256 as Hash256, H2048 as Ha macro_rules! impl_hash { ($name: ident, $inner: ident) => { /// Lenient hash json deserialization for test json files. - #[derive(Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] + #[derive(Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] pub struct $name($inner); impl Into<$inner> for $name { @@ -46,6 +46,10 @@ macro_rules! impl_hash { fn visit_str(&mut self, value: &str) -> Result where E: Error { let value = match value.len() { 0 => $inner::from(0), + 2 if value == "0x" => $inner::from(0), + _ if value.starts_with("0x") => try!($inner::from_str(&value[2..]).map_err(|_| { + Error::custom(format!("Invalid hex value {}.", value).as_ref()) + })), _ => try!($inner::from_str(value).map_err(|_| { Error::custom(format!("Invalid hex value {}.", value).as_ref()) })) diff --git a/json/src/lib.rs.in b/json/src/lib.rs.in index 6d3437811..0d85ce569 100644 --- a/json/src/lib.rs.in +++ b/json/src/lib.rs.in @@ -23,3 +23,4 @@ pub mod hash; pub mod uint; pub mod bytes; pub mod blockchain; +pub mod spec; diff --git a/json/src/spec/account.rs b/json/src/spec/account.rs new file mode 100644 index 000000000..1440b1bdc --- /dev/null +++ b/json/src/spec/account.rs @@ -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 . + +//! Spec account deserialization. + +use uint::Uint; +use spec::builtin::Builtin; + +/// Spec account. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Account { + builtin: Option, + balance: Option, + nonce: Option, +} + +#[cfg(test)] +mod tests { + use serde_json; + use spec::account::Account; + + #[test] + fn account_deserialization() { + let s = r#"{ + "balance": "1", + "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } + }"#; + let _deserialized: Account = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } +} diff --git a/json/src/spec/builtin.rs b/json/src/spec/builtin.rs new file mode 100644 index 000000000..21f8a2ac1 --- /dev/null +++ b/json/src/spec/builtin.rs @@ -0,0 +1,55 @@ +// 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 . + +//! Spec builtin deserialization. + +/// Linear pricing. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Linear { + base: u64, + word: u64, +} + +/// Pricing variants. +#[derive(Debug, PartialEq, Deserialize)] +pub enum Pricing { + /// Linear pricing. + #[serde(rename="linear")] + Linear(Linear), +} + +/// Spec builtin. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Builtin { + name: String, + pricing: Pricing, +} + +#[cfg(test)] +mod tests { + use serde_json; + use spec::builtin::Builtin; + + #[test] + fn builtin_deserialization() { + let s = r#"{ + "name": "ecrecover", + "pricing": { "linear": { "base": 3000, "word": 0 } } + }"#; + let _deserialized: Builtin = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } +} diff --git a/json/src/spec/genesis.rs b/json/src/spec/genesis.rs new file mode 100644 index 000000000..a2b484397 --- /dev/null +++ b/json/src/spec/genesis.rs @@ -0,0 +1,91 @@ +// 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 . + +//! Spec genesis deserialization. + +use uint::Uint; +use hash::{H64, Address, H256}; +use bytes::Bytes; + +/// Spec genesis. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Genesis { + // old seal + /// Seal nonce. + pub nonce: Option, + #[serde(rename="mixHash")] + /// Seal mix hash. + pub mix_hash: Option, + + // new seal // TODO: consider moving it to a separate seal structure + #[serde(rename="sealFields")] + /// Number of seal fields. + pub seal_fields: Option, + #[serde(rename="sealRlp")] + /// Seal rlp. + pub seal_rlp: Option, + + /// Difficulty. + pub difficulty: Uint, + /// Block author. + pub author: Address, + /// Block timestamp. + pub timestamp: Uint, + /// Parent hash. + #[serde(rename="parentHash")] + pub parent_hash: H256, + /// Gas limit. + #[serde(rename="gasLimit")] + pub gas_limit: Uint, + /// Transactions root. + #[serde(rename="transactionsRoot")] + pub transactions_root: Option, + /// Receipts root. + #[serde(rename="receiptsRoot")] + pub receipts_root: Option, + /// State root. + #[serde(rename="stateRoot")] + pub state_root: Option, + /// Gas used. + #[serde(rename="gasUsed")] + pub gas_used: Option, + /// Extra data. + #[serde(rename="extraData")] + pub extra_data: Option, +} + +#[cfg(test)] +mod tests { + use serde_json; + use spec::genesis::Genesis; + + #[test] + fn genesis_deserialization() { + let s = r#"{ + "nonce": "0x0000000000000042", + "difficulty": "0x400000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1388", + "stateRoot": "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544" + }"#; + let _deserialized: Genesis = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } +} diff --git a/json/src/spec/mod.rs b/json/src/spec/mod.rs new file mode 100644 index 000000000..8783563d1 --- /dev/null +++ b/json/src/spec/mod.rs @@ -0,0 +1,29 @@ +// 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 . + +//! Spec deserialization. + +pub mod account; +pub mod builtin; +pub mod genesis; +pub mod params; +pub mod spec; + +pub use self::account::Account; +pub use self::builtin::Builtin; +pub use self::genesis::Genesis; +pub use self::params::Params; +pub use self::spec::Spec; diff --git a/json/src/spec/params.rs b/json/src/spec/params.rs new file mode 100644 index 000000000..e55f7fc48 --- /dev/null +++ b/json/src/spec/params.rs @@ -0,0 +1,74 @@ +// 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 . + +//! Spec params deserialization. + +use uint::Uint; +use hash::Address; + +/// Spec params. +#[derive(Debug, PartialEq, Deserialize)] +pub struct Params { + #[serde(rename="accountStartNonce")] + account_start_nonce: Uint, + #[serde(rename="frontierCompatibilityModeLimit")] + frontier_compatibility_mode_limit: Uint, + #[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, + #[serde(rename="networkID")] + network_id: Uint, +} + +#[cfg(test)] +mod tests { + use serde_json; + use spec::params::Params; + + #[test] + fn params_deserialization() { + let s = r#"{ + "accountStartNonce": "0x00", + "frontierCompatibilityModeLimit": "0x118c30", + "maximumExtraDataSize": "0x20", + "tieBreakingGas": false, + "minGasLimit": "0x1388", + "gasLimitBoundDivisor": "0x0400", + "minimumDifficulty": "0x020000", + "difficultyBoundDivisor": "0x0800", + "durationLimit": "0x0d", + "blockReward": "0x4563918244F40000", + "registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b", + "networkID" : "0x1" + }"#; + let _deserialized: Params = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } +} diff --git a/json/src/spec/spec.rs b/json/src/spec/spec.rs new file mode 100644 index 000000000..2dd4ac486 --- /dev/null +++ b/json/src/spec/spec.rs @@ -0,0 +1,84 @@ +// 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 . + +//! Spec deserialization. + +use std::collections::BTreeMap; +use hash::Address; +use spec::account::Account; +use spec::params::Params; +use spec::genesis::Genesis; + +/// 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, +} + +#[cfg(test)] +mod tests { + use serde_json; + use spec::spec::Spec; + + #[test] + fn spec_deserialization() { + let s = r#"{ + "name": "Morden", + "engineName": "Ethash", + "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", + "difficulty": "0x20000", + "mixHash": "0x00000000000000000000000000000000000000647572616c65787365646c6578", + "author": "0x0000000000000000000000000000000000000000", + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x", + "gasLimit": "0x2fefd8" + }, + "nodes": [ + "enode://b1217cbaa440e35ed471157123fe468e19e8b5ad5bedb4b1fdbcbdab6fb2f5ed3e95dd9c24a22a79fdb2352204cea207df27d92bfd21bfd41545e8b16f637499@104.44.138.37:30303" + ], + "accounts": { + "0000000000000000000000000000000000000001": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } }, + "0000000000000000000000000000000000000002": { "balance": "1", "nonce": "1048576", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } }, + "0000000000000000000000000000000000000003": { "balance": "1", "nonce": "1048576", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } }, + "0000000000000000000000000000000000000004": { "balance": "1", "nonce": "1048576", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + } + }"#; + let _deserialized: Spec = serde_json::from_str(s).unwrap(); + // TODO: validate all fields + } +} diff --git a/json/src/uint.rs b/json/src/uint.rs index 41e717277..f6eae80e4 100644 --- a/json/src/uint.rs +++ b/json/src/uint.rs @@ -22,7 +22,7 @@ use serde::de::Visitor; use util::numbers::{U256, Uint as U}; /// Lenient uint json deserialization for test json files. -#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] pub struct Uint(U256); impl Into for Uint { @@ -31,6 +31,12 @@ impl Into for Uint { } } +impl Into for Uint { + fn into(self) -> u64 { + u64::from(self.0) + } +} + impl Deserialize for Uint { fn deserialize(deserializer: &mut D) -> Result where D: Deserializer { diff --git a/parity/rpctest.rs b/parity/rpctest.rs new file mode 100644 index 000000000..4fd31b134 --- /dev/null +++ b/parity/rpctest.rs @@ -0,0 +1,143 @@ +// 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 . + +extern crate ctrlc; +extern crate docopt; +extern crate rustc_serialize; +extern crate serde_json; +extern crate ethjson; +extern crate ethcore_util as util; +extern crate ethcore; +extern crate ethcore_devtools as devtools; +extern crate ethcore_rpc as rpc; + +use std::collections::HashMap; +use std::sync::{Arc, Mutex, Condvar}; +use std::process; +use std::fs::File; +use std::path::Path; +use docopt::Docopt; +use ctrlc::CtrlC; +use ethcore::spec::Genesis; +use ethcore::pod_state::PodState; +use ethcore::ethereum; +use ethcore::client::{BlockChainClient, Client, ClientConfig}; +use devtools::RandomTempPath; +use util::IoChannel; +use rpc::v1::tests::helpers::{TestSyncProvider, Config as SyncConfig, TestMinerService, TestAccountProvider}; +use rpc::v1::{Eth, EthClient}; +use util::panics::MayPanic; + +const USAGE: &'static str = r#" +Parity rpctest client. + By Wood/Paronyan/Kotewicz/Drwięga/Volf. + Copyright 2015, 2016 Ethcore (UK) Limited + +Usage: + rpctest --json --name [options] + rpctest --help + +Options: + --jsonrpc-addr HOST Specify the hostname portion of the JSONRPC API + server [default: 127.0.0.1]. + --jsonrpc-port PORT Specify the port portion of the JSONRPC API server + [default: 8545]. +"#; + +#[derive(Debug, RustcDecodable)] +struct Args { + arg_test_file: String, + arg_test_name: String, + flag_jsonrpc_addr: String, + flag_jsonrpc_port: u16, +} + +struct Configuration { + args: Args, +} + +impl Configuration { + fn parse() -> Self { + Configuration { + args: Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()) + } + } + + fn execute(&self) { + println!("file path: {:?}", self.args.arg_test_file); + println!("test name: {:?}", self.args.arg_test_name); + + let path = Path::new(&self.args.arg_test_file); + let file = File::open(path).unwrap_or_else(|_| { + println!("Cannot open file."); + process::exit(1); + }); + + let tests: ethjson::blockchain::Test = serde_json::from_reader(file).unwrap_or_else(|_| { + println!("Invalid json file."); + process::exit(2); + }); + + let blockchain = tests.get(&self.args.arg_test_name).unwrap_or_else(|| { + println!("Invalid test name."); + process::exit(3); + }); + + let genesis = Genesis::from(blockchain.genesis()); + let state = PodState::from(blockchain.pre_state.clone()); + let mut spec = ethereum::new_frontier_test(); + spec.set_genesis_state(state); + spec.overwrite_genesis_params(genesis); + assert!(spec.is_state_root_valid()); + + let temp = RandomTempPath::new(); + { + let client: Arc = Client::new(ClientConfig::default(), spec, temp.as_path(), IoChannel::disconnected()).unwrap(); + for b in &blockchain.blocks_rlp() { + let _ = client.import_block(b.clone()); + client.flush_queue(); + client.import_verified_blocks(&IoChannel::disconnected()); + } + let sync = Arc::new(TestSyncProvider::new(SyncConfig { + protocol_version: 65, + num_peers: 120 + })); + + let miner = Arc::new(TestMinerService::default()); + let accounts = Arc::new(TestAccountProvider::new(HashMap::new())); + let server = rpc::RpcServer::new(); + server.add_delegate(EthClient::new(&client, &sync, &accounts, &miner).to_delegate()); + + let url = format!("{}:{}", self.args.flag_jsonrpc_addr, self.args.flag_jsonrpc_port); + let panic_handler = server.start_http(url.as_ref(), "*", 1); + let exit = Arc::new(Condvar::new()); + + let e = exit.clone(); + CtrlC::set_handler(move || { e.notify_all(); }); + + let e = exit.clone(); + panic_handler.on_panic(move |_reason| { e.notify_all(); }); + + let mutex = Mutex::new(()); + let _ = exit.wait(mutex.lock().unwrap()).unwrap(); + } + + } +} + +fn main() { + Configuration::parse().execute(); +} diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index b82a20e89..c81f58156 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -23,8 +23,7 @@ mod impls; mod types; mod helpers; -#[cfg(test)] -mod tests; +pub mod tests; pub use self::traits::{Web3, Eth, EthFilter, Personal, Net}; pub use self::impls::*; diff --git a/rpc/src/v1/tests/helpers/account_provider.rs b/rpc/src/v1/tests/helpers/account_provider.rs index ce5b76b44..614aba346 100644 --- a/rpc/src/v1/tests/helpers/account_provider.rs +++ b/rpc/src/v1/tests/helpers/account_provider.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Test implementation of account provider. + use std::sync::RwLock; use std::collections::HashMap; use std::io; @@ -31,6 +33,7 @@ pub struct TestAccount { } impl TestAccount { + /// Creates new test account. pub fn new(password: &str) -> Self { TestAccount { unlocked: false, @@ -42,6 +45,7 @@ impl TestAccount { /// Test account provider. pub struct TestAccountProvider { accounts: RwLock>, + /// Added accounts passwords. pub adds: RwLock>, } diff --git a/rpc/src/v1/tests/helpers/external_miner.rs b/rpc/src/v1/tests/helpers/external_miner.rs index a5111b302..1799c36c5 100644 --- a/rpc/src/v1/tests/helpers/external_miner.rs +++ b/rpc/src/v1/tests/helpers/external_miner.rs @@ -22,10 +22,12 @@ use v1::helpers::ExternalMinerService; /// Test ExternalMinerService; pub struct TestExternalMiner { + /// External miners hashrates. pub hashrates: Arc>> } impl TestExternalMiner { + /// Creates new external miner. pub fn new(hashrates: Arc>>) -> Self { TestExternalMiner { hashrates: hashrates, diff --git a/rpc/src/v1/tests/helpers/miner_service.rs b/rpc/src/v1/tests/helpers/miner_service.rs index ad9dcaedd..22e210392 100644 --- a/rpc/src/v1/tests/helpers/miner_service.rs +++ b/rpc/src/v1/tests/helpers/miner_service.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Test implementation of miner service. + use util::{Address, H256, Bytes}; use util::standard::*; use ethcore::error::Error; @@ -22,8 +24,11 @@ use ethcore::block::ClosedBlock; use ethcore::transaction::SignedTransaction; use ethminer::{MinerService, MinerStatus, AccountDetails}; +/// Test miner service. pub struct TestMinerService { + /// Imported transactions. pub imported_transactions: RwLock>, + /// Latest closed block. pub latest_closed_block: Mutex>, } diff --git a/rpc/src/v1/tests/helpers/mod.rs b/rpc/src/v1/tests/helpers/mod.rs index fc652e7d6..c9db61f6d 100644 --- a/rpc/src/v1/tests/helpers/mod.rs +++ b/rpc/src/v1/tests/helpers/mod.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Test rpc services. + mod account_provider; mod sync_provider; mod miner_service; diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 5b4b5b099..48b4f55a9 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -14,19 +14,27 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +//! Test implementation of SyncProvider. + use ethsync::{SyncProvider, SyncStatus, SyncState}; use std::sync::{RwLock}; +/// TestSyncProvider config. pub struct Config { + /// Protocol version. pub protocol_version: u8, + /// Number of peers. pub num_peers: usize, } +/// Test sync provider. pub struct TestSyncProvider { + /// Sync status. pub status: RwLock, } impl TestSyncProvider { + /// Creates new sync provider. pub fn new(config: Config) -> Self { TestSyncProvider { status: RwLock::new(SyncStatus { diff --git a/rpc/src/v1/tests/mod.rs b/rpc/src/v1/tests/mod.rs index 21085a0fd..7a6340ce1 100644 --- a/rpc/src/v1/tests/mod.rs +++ b/rpc/src/v1/tests/mod.rs @@ -16,8 +16,12 @@ //!TODO: load custom blockchain state and test +pub mod helpers; +#[cfg(test)] mod eth; +#[cfg(test)] mod net; +#[cfg(test)] mod web3; -mod helpers; +#[cfg(test)] mod personal;