diff --git a/ethcore/pod/src/state.rs b/ethcore/pod/src/state.rs
index bfee964b8..f83ecc07c 100644
--- a/ethcore/pod/src/state.rs
+++ b/ethcore/pod/src/state.rs
@@ -31,7 +31,9 @@ pub struct PodState(BTreeMap
);
impl PodState {
/// Get the underlying map.
- pub fn get(&self) -> &BTreeMap { &self.0 }
+ pub fn get(&self) -> &BTreeMap {
+ &self.0
+ }
/// Get the root hash of the trie of the RLP of this.
pub fn root(&self) -> H256 {
@@ -39,7 +41,9 @@ impl PodState {
}
/// Drain object to get the underlying map.
- pub fn drain(self) -> BTreeMap { self.0 }
+ pub fn drain(self) -> BTreeMap {
+ self.0
+ }
}
impl From for PodState {
diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs
index 4488d0f32..73cc809a4 100644
--- a/ethcore/src/json_tests/chain.rs
+++ b/ethcore/src/json_tests/chain.rs
@@ -77,7 +77,6 @@ pub fn json_chain_test(json_data: &[u8], start_stop_ho
let state = From::from(blockchain.pre_state.clone());
spec.set_genesis_state(state).expect("Failed to overwrite genesis state");
spec.overwrite_genesis_params(genesis);
- assert!(spec.is_state_root_valid());
spec
};
diff --git a/ethcore/src/spec/spec.rs b/ethcore/src/spec/spec.rs
index 39971e7b9..1d732f8f0 100644
--- a/ethcore/src/spec/spec.rs
+++ b/ethcore/src/spec/spec.rs
@@ -16,18 +16,19 @@
//! Parameters for a block chain.
-use std::collections::BTreeMap;
-use std::io::Read;
-use std::path::Path;
-use std::sync::Arc;
+use std::{
+ collections::BTreeMap,
+ fmt,
+ io::Read,
+ path::Path,
+ sync::Arc,
+};
use bytes::Bytes;
use ethereum_types::{H256, Bloom, U256, Address};
use ethjson;
use hash::{KECCAK_NULL_RLP, keccak};
-use parking_lot::RwLock;
use rlp::{Rlp, RlpStream};
-use rustc_hex::{FromHex, ToHex};
use types::{
BlockNumber,
header::Header,
@@ -96,6 +97,93 @@ impl<'a, T: AsRef> From<&'a T> for SpecParams<'a> {
}
}
+/// given a pre-constructor state, run all the given constructors and produce a new state and
+/// state root.
+fn run_constructors(
+ genesis_state: &PodState,
+ constructors: &[(Address, Bytes)],
+ engine: &Engine,
+ author: Address,
+ timestamp: u64,
+ difficulty: U256,
+ factories: &Factories,
+ mut db: T
+) -> Result<(H256, T), Error> {
+ let mut root = KECCAK_NULL_RLP;
+
+ // basic accounts in spec.
+ {
+ let mut t = factories.trie.create(db.as_hash_db_mut(), &mut root);
+
+ for (address, account) in genesis_state.get().iter() {
+ t.insert(address.as_bytes(), &account.rlp())?;
+ }
+ }
+
+ for (address, account) in genesis_state.get().iter() {
+ db.note_non_null_account(address);
+ account.insert_additional(
+ &mut *factories.accountdb.create(
+ db.as_hash_db_mut(),
+ keccak(address),
+ ),
+ &factories.trie,
+ );
+ }
+
+ let start_nonce = engine.account_start_nonce(0);
+
+ let mut state = State::from_existing(db, root, start_nonce, factories.clone())?;
+
+ // Execute contract constructors.
+ let env_info = EnvInfo {
+ number: 0,
+ author,
+ timestamp,
+ difficulty,
+ last_hashes: Default::default(),
+ gas_used: U256::zero(),
+ gas_limit: U256::max_value(),
+ };
+
+ let from = Address::zero();
+ for &(ref address, ref constructor) in constructors.iter() {
+ trace!(target: "spec", "run_constructors: Creating a contract at {}.", address);
+ trace!(target: "spec", " .. root before = {}", state.root());
+ let params = ActionParams {
+ code_address: address.clone(),
+ code_hash: Some(keccak(constructor)),
+ code_version: U256::zero(),
+ address: address.clone(),
+ sender: from.clone(),
+ origin: from.clone(),
+ gas: U256::max_value(),
+ gas_price: Default::default(),
+ value: ActionValue::Transfer(Default::default()),
+ code: Some(Arc::new(constructor.clone())),
+ data: None,
+ call_type: CallType::None,
+ params_type: ParamsType::Embedded,
+ };
+
+ let mut substate = Substate::new();
+
+ {
+ let machine = engine.machine();
+ let schedule = machine.schedule(env_info.number);
+ let mut exec = Executive::new(&mut state, &env_info, &machine, &schedule);
+ // failing create is not a bug
+ if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) {
+ warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e);
+ }
+ }
+
+ let _ = state.commit()?;
+ }
+
+ Ok(state.drop())
+}
+
/// Parameters for a block chain; includes both those intrinsic to the design of the
/// chain and those to be interpreted by the active chain engine.
pub struct Spec {
@@ -137,7 +225,7 @@ pub struct Spec {
constructors: Vec<(Address, Bytes)>,
/// May be prepopulated if we know this in advance.
- state_root_memo: RwLock,
+ state_root_memo: H256,
/// Genesis state as plain old data.
genesis_state: PodState,
@@ -163,13 +251,14 @@ impl Clone for Spec {
seal_rlp: self.seal_rlp.clone(),
hardcoded_sync: self.hardcoded_sync.clone(),
constructors: self.constructors.clone(),
- state_root_memo: RwLock::new(*self.state_root_memo.read()),
+ state_root_memo: self.state_root_memo,
genesis_state: self.genesis_state.clone(),
}
}
}
/// Part of `Spec`. Describes the hardcoded synchronization parameters.
+#[derive(Debug, Clone)]
pub struct SpecHardcodedSync {
/// Header of the block to jump to for hardcoded sync, and total difficulty.
pub header: encoded::Header,
@@ -180,34 +269,26 @@ pub struct SpecHardcodedSync {
pub chts: Vec,
}
-impl SpecHardcodedSync {
- /// Turns this specifications back into JSON. Useful for pretty printing.
- pub fn to_json(self) -> ethjson::spec::HardcodedSync {
- self.into()
- }
-}
-
-#[cfg(test)]
-impl Clone for SpecHardcodedSync {
- fn clone(&self) -> SpecHardcodedSync {
+impl From for SpecHardcodedSync {
+ fn from(sync: ethjson::spec::HardcodedSync) -> Self {
SpecHardcodedSync {
- header: self.header.clone(),
- total_difficulty: self.total_difficulty.clone(),
- chts: self.chts.clone(),
- }
- }
-}
-
-impl From for ethjson::spec::HardcodedSync {
- fn from(sync: SpecHardcodedSync) -> ethjson::spec::HardcodedSync {
- ethjson::spec::HardcodedSync {
- header: sync.header.into_inner().to_hex(),
- total_difficulty: ethjson::uint::Uint(sync.total_difficulty),
+ header: encoded::Header::new(sync.header.into()),
+ total_difficulty: sync.total_difficulty.into(),
chts: sync.chts.into_iter().map(Into::into).collect(),
}
}
}
+impl fmt::Display for SpecHardcodedSync {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "{{")?;
+ writeln!(f, r#"header": "{:?},"#, self.header)?;
+ writeln!(f, r#"total_difficulty": "{:?},"#, self.total_difficulty)?;
+ writeln!(f, r#"chts": {:#?}"#, self.chts.iter().map(|x| format!(r#"{}"#, x)).collect::>())?;
+ writeln!(f, "}}")
+ }
+}
+
fn load_machine_from(s: ethjson::spec::Spec) -> Machine {
let builtins = s.accounts.builtins().into_iter().map(|p| (p.0.into(), From::from(p.1))).collect();
let params = CommonParams::from(s.params);
@@ -226,59 +307,51 @@ fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result = s.accounts
+ .constructors()
+ .into_iter()
+ .map(|(a, c)| (a.into(), c.into()))
+ .collect();
+ let genesis_state: PodState = s.accounts.into();
+
+ let (state_root_memo, _) = run_constructors(
+ &genesis_state,
+ &constructors,
+ &*engine,
+ author,
+ timestamp,
+ difficulty,
+ &Default::default(),
+ BasicBackend(journaldb::new_memory_db()),
+ )?;
+
+ let s = Spec {
+ engine,
name: s.name.clone().into(),
- engine: Spec::engine(spec_params, s.engine, params, builtins),
data_dir: s.data_dir.unwrap_or(s.name).into(),
nodes: s.nodes.unwrap_or_else(Vec::new),
parent_hash: g.parent_hash,
transactions_root: g.transactions_root,
receipts_root: g.receipts_root,
- author: g.author,
- difficulty: g.difficulty,
+ author,
+ difficulty,
gas_limit: g.gas_limit,
gas_used: g.gas_used,
- timestamp: g.timestamp,
+ timestamp,
extra_data: g.extra_data,
seal_rlp,
hardcoded_sync,
- constructors: s.accounts
- .constructors()
- .into_iter()
- .map(|(a, c)| (a.into(), c.into()))
- .collect(),
- state_root_memo: RwLock::new(Default::default()), // will be overwritten right after.
- genesis_state: s.accounts.into(),
+ constructors,
+ genesis_state,
+ state_root_memo,
};
- // use memoized state root if provided.
- match g.state_root {
- Some(root) => *s.state_root_memo.get_mut() = root,
- None => {
- let _ = s.run_constructors(
- &Default::default(),
- BasicBackend(journaldb::new_memory_db()),
- )?;
- }
- }
-
Ok(s)
}
@@ -337,95 +410,9 @@ impl Spec {
}
}
- // given a pre-constructor state, run all the given constructors and produce a new state and
- // state root.
- fn run_constructors(&self, factories: &Factories, mut db: T) -> Result {
- let mut root = KECCAK_NULL_RLP;
-
- // basic accounts in spec.
- {
- let mut t = factories.trie.create(db.as_hash_db_mut(), &mut root);
-
- for (address, account) in self.genesis_state.get().iter() {
- t.insert(address.as_bytes(), &account.rlp())?;
- }
- }
-
- for (address, account) in self.genesis_state.get().iter() {
- db.note_non_null_account(address);
- account.insert_additional(
- &mut *factories.accountdb.create(
- db.as_hash_db_mut(),
- keccak(address),
- ),
- &factories.trie,
- );
- }
-
- let start_nonce = self.engine.account_start_nonce(0);
-
- let (root, db) = {
- let mut state = State::from_existing(db, root, start_nonce, factories.clone())?;
-
- // Execute contract constructors.
- let env_info = EnvInfo {
- number: 0,
- author: self.author,
- timestamp: self.timestamp,
- difficulty: self.difficulty,
- last_hashes: Default::default(),
- gas_used: U256::zero(),
- gas_limit: U256::max_value(),
- };
-
- let from = Address::zero();
- for &(ref address, ref constructor) in self.constructors.iter() {
- trace!(target: "spec", "run_constructors: Creating a contract at {}.", address);
- trace!(target: "spec", " .. root before = {}", state.root());
- let params = ActionParams {
- code_address: address.clone(),
- code_hash: Some(keccak(constructor)),
- code_version: U256::zero(),
- address: address.clone(),
- sender: from.clone(),
- origin: from.clone(),
- gas: U256::max_value(),
- gas_price: Default::default(),
- value: ActionValue::Transfer(Default::default()),
- code: Some(Arc::new(constructor.clone())),
- data: None,
- call_type: CallType::None,
- params_type: ParamsType::Embedded,
- };
-
- let mut substate = Substate::new();
-
- {
- let machine = self.engine.machine();
- let schedule = machine.schedule(env_info.number);
- let mut exec = Executive::new(&mut state, &env_info, &machine, &schedule);
- if let Err(e) = exec.create(params, &mut substate, &mut NoopTracer, &mut NoopVMTracer) {
- warn!(target: "spec", "Genesis constructor execution at {} failed: {}.", address, e);
- }
- }
-
- if let Err(e) = state.commit() {
- warn!(target: "spec", "Genesis constructor trie commit at {} failed: {}.", address, e);
- }
-
- trace!(target: "spec", " .. root after = {}", state.root());
- }
-
- state.drop()
- };
-
- *self.state_root_memo.write() = root;
- Ok(db)
- }
-
/// Return the state root for the genesis state, memoising accordingly.
pub fn state_root(&self) -> H256 {
- self.state_root_memo.read().clone()
+ self.state_root_memo
}
/// Get common blockchain parameters.
@@ -511,11 +498,18 @@ impl Spec {
/// Alter the value of the genesis state.
pub fn set_genesis_state(&mut self, s: PodState) -> Result<(), Error> {
self.genesis_state = s;
- let _ = self.run_constructors(
+ let (root, _) = run_constructors(
+ &self.genesis_state,
+ &self.constructors,
+ &*self.engine,
+ self.author,
+ self.timestamp,
+ self.difficulty,
&Default::default(),
BasicBackend(journaldb::new_memory_db()),
)?;
+ self.state_root_memo = root;
Ok(())
}
@@ -524,14 +518,6 @@ impl Spec {
&self.genesis_state
}
- /// Returns `false` if the memoized state root is invalid. `true` otherwise.
- pub fn is_state_root_valid(&self) -> bool {
- // TODO: get rid of this function and ensure state root always is valid.
- // we're mostly there, but `self.genesis_state.root()` doesn't encompass
- // post-constructor state.
- *self.state_root_memo.read() == self.genesis_state.root()
- }
-
/// Ensure that the given state DB has the trie nodes in for the genesis state.
pub fn ensure_db_good(&self, db: T, factories: &Factories) -> Result {
if db.as_hash_db().contains(&self.state_root(), hash_db::EMPTY_PREFIX) {
@@ -540,7 +526,18 @@ impl Spec {
// TODO: could optimize so we don't re-run, but `ensure_db_good` is barely ever
// called anyway.
- let db = self.run_constructors(factories, db)?;
+ let (root, db) = run_constructors(
+ &self.genesis_state,
+ &self.constructors,
+ &*self.engine,
+ self.author,
+ self.timestamp,
+ self.difficulty,
+ factories,
+ db
+ )?;
+
+ assert_eq!(root, self.state_root(), "Spec's state root has not been precomputed correctly.");
Ok(db)
}
diff --git a/json/src/bytes.rs b/json/src/bytes.rs
index 3fbdee238..5956cd428 100644
--- a/json/src/bytes.rs
+++ b/json/src/bytes.rs
@@ -34,9 +34,15 @@ impl Bytes {
}
}
-impl Into> for Bytes {
- fn into(self) -> Vec {
- self.0
+impl From for Vec {
+ fn from(bytes: Bytes) -> Self {
+ bytes.0
+ }
+}
+
+impl From> for Bytes {
+ fn from(bytes: Vec) -> Self {
+ Bytes(bytes)
}
}
diff --git a/json/src/spec/hardcoded_sync.rs b/json/src/spec/hardcoded_sync.rs
index 262d54312..eed3dac65 100644
--- a/json/src/spec/hardcoded_sync.rs
+++ b/json/src/spec/hardcoded_sync.rs
@@ -18,14 +18,15 @@
use hash::H256;
use uint::Uint;
+use bytes::Bytes;
/// Spec hardcoded sync.
-#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[derive(Debug, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct HardcodedSync {
/// Hexadecimal of the RLP encoding of the header of the block to start synchronization from.
- pub header: String,
+ pub header: Bytes,
/// Total difficulty including the block of `header`.
pub total_difficulty: Uint,
/// Ordered trie roots of blocks before and including `header`.
@@ -54,7 +55,7 @@ mod tests {
}"#;
let deserialized: HardcodedSync = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, HardcodedSync {
- header: String::from("f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23"),
+ header: "f901f9a0d405da4e66f1445d455195229624e133f5baafe72b5cf7b3c36c12c8146e98b7a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a05fb2b4bfdef7b314451cb138a534d225c922fc0e5fbe25e451142732c3e25c25a088d2ec6b9860aae1a2c3b299f72b6a5d70d7f7ba4722c78f2c49ba96273c2158a007c6fdfa8eea7e86b81f5b0fc0f78f90cc19f4aa60d323151e0cac660199e9a1b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefba82524d84568e932a80a0a0349d8c3df71f1a48a9df7d03fd5f14aeee7d91332c009ecaff0a71ead405bd88ab4e252a7e8c2a23".parse().unwrap(),
total_difficulty: Uint(U256::from(0x400000000u64)),
chts: vec![
H256(Eth256::from_str("11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa").unwrap()),
diff --git a/parity/export_hardcoded_sync.rs b/parity/export_hardcoded_sync.rs
index 0e527b341..adcdf5035 100644
--- a/parity/export_hardcoded_sync.rs
+++ b/parity/export_hardcoded_sync.rs
@@ -96,7 +96,7 @@ pub fn execute(cmd: ExportHsyncCmd) -> Result {
let hs = service.client().read_hardcoded_sync()
.map_err(|e| format!("Error reading hardcoded sync: {}", e))?;
if let Some(hs) = hs {
- Ok(::serde_json::to_string_pretty(&hs.to_json()).expect("generated JSON is always valid"))
+ Ok(format!("{}", hs))
} else {
Err("Error: cannot generate hardcoded sync because the database is empty.".into())
}
diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs
index ea9f5b722..94ef264c2 100644
--- a/rpc/src/v1/tests/eth.rs
+++ b/rpc/src/v1/tests/eth.rs
@@ -68,7 +68,6 @@ fn make_spec(chain: &BlockChain) -> Spec {
let state = chain.pre_state.clone().into();
spec.set_genesis_state(state).expect("unable to set genesis state");
spec.overwrite_genesis_params(genesis);
- assert!(spec.is_state_root_valid());
spec
}