From 4622882fdafa517ede7325138ec22a0db45cdfa6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 9 Jan 2016 18:20:31 +0100 Subject: [PATCH] Separate out Ethereum-network-specific resources. Fix State to use secure trie variants. --- res/null_morden.json | 34 +++++++++++ src/block.rs | 14 ++++- src/blockchain.rs | 3 +- src/engine.rs | 4 +- src/ethereum/ethash.rs | 13 +++- src/ethereum/mod.rs | 61 +++++++++++++++++++ {res => src/ethereum/res}/frontier.json | 0 {res => src/ethereum/res}/frontier_test.json | 0 {res => src/ethereum/res}/homestead_test.json | 0 {res => src/ethereum/res}/morden.json | 0 {res => src/ethereum/res}/olympic.json | 0 src/spec.rs | 31 +++------- src/state.rs | 16 ++--- 13 files changed, 136 insertions(+), 40 deletions(-) create mode 100644 res/null_morden.json rename {res => src/ethereum/res}/frontier.json (100%) rename {res => src/ethereum/res}/frontier_test.json (100%) rename {res => src/ethereum/res}/homestead_test.json (100%) rename {res => src/ethereum/res}/morden.json (100%) rename {res => src/ethereum/res}/olympic.json (100%) diff --git a/res/null_morden.json b/res/null_morden.json new file mode 100644 index 000000000..7f069cb4e --- /dev/null +++ b/res/null_morden.json @@ -0,0 +1,34 @@ +{ + "engineName": "NullEngine", + "params": { + "accountStartNonce": "0x0100000", + "frontierCompatibilityModeLimit": "0xfffa2990", + "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" + }, + "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 } } }, + "102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376", "nonce": "1048576" } + } +} \ No newline at end of file diff --git a/src/block.rs b/src/block.rs index ae05cc94d..3c42e4e27 100644 --- a/src/block.rs +++ b/src/block.rs @@ -1,7 +1,6 @@ use util::*; use transaction::*; use receipt::*; -use blockchain::*; use engine::*; use header::*; use env_info::*; @@ -125,7 +124,7 @@ impl<'engine> OpenBlock<'engine> { } /// Turn this into a `ClosedBlock`. A BlockChain must be provided in order to figure ou the uncles. - pub fn close(self, _bc: &BlockChain) -> ClosedBlock { unimplemented!(); } + pub fn close(self, _uncles: Vec
) -> ClosedBlock<'engine> { unimplemented!(); } } impl<'engine> IsBlock for OpenBlock<'engine> { @@ -153,3 +152,14 @@ impl SealedBlock { impl IsBlock for SealedBlock { fn block(&self) -> &Block { &self.block } } + +#[test] +fn open_block() { + use super::*; + use spec::*; + let engine = Spec::new_test().to_engine().unwrap(); + let genesis_header = engine.spec().genesis_header(); + let mut db = OverlayDB::new_temp(); + engine.spec().ensure_db_good(&mut db); + let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]); +} \ No newline at end of file diff --git a/src/blockchain.rs b/src/blockchain.rs index 957764392..048159b08 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -77,11 +77,12 @@ impl BlockChain { /// use std::str::FromStr; /// use ethcore::spec::*; /// use ethcore::blockchain::*; + /// use ethcore::ethereum; /// use util::hash::*; /// use util::uint::*; /// /// fn main() { - /// let spec = Spec::new_frontier(); + /// let spec = ethereum::new_frontier(); /// /// let mut dir = env::temp_dir(); /// dir.push(H32::random().hex()); diff --git a/src/engine.rs b/src/engine.rs index 318c5a6b4..7fee085e9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -25,8 +25,8 @@ pub trait Engine { fn evm_schedule(&self, env_info: &EnvInfo) -> EvmSchedule; /// Some intrinsic operation parameters; by default they take their value from the `spec()`'s `engine_params`. - fn maximum_extra_data_size(&self, _env_info: &EnvInfo) -> usize { decode(&self.spec().engine_params.get("maximum_extra_data_size").unwrap()) } - fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("account_start_nonce").unwrap()) } + fn maximum_extra_data_size(&self, _env_info: &EnvInfo) -> usize { decode(&self.spec().engine_params.get("maximumExtraDataSize").unwrap()) } + fn account_start_nonce(&self) -> U256 { decode(&self.spec().engine_params.get("accountStartNonce").unwrap()) } /// Block transformation functions, before and after the transactions. fn on_new_block(&self, _block: &mut Block) {} diff --git a/src/ethereum/ethash.rs b/src/ethereum/ethash.rs index 802ef4d64..13b1f6a46 100644 --- a/src/ethereum/ethash.rs +++ b/src/ethereum/ethash.rs @@ -30,10 +30,17 @@ impl Engine for Ethash { // TODO: test for on_close_block. #[test] fn playpen() { - use util::overlaydb::*; - let engine = Spec::new_morden().to_engine().unwrap(); + use super::*; + use state::*; + let engine = new_morden().to_engine().unwrap(); let genesis_header = engine.spec().genesis_header(); let mut db = OverlayDB::new_temp(); engine.spec().ensure_db_good(&mut db); -// let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]); + assert!(SecTrieDB::new(&db, &genesis_header.state_root).contains(&address_from_hex("102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c"))); + { + let s = State::from_existing(db.clone(), genesis_header.state_root.clone(), engine.account_start_nonce()); + assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64)); + } + let b = OpenBlock::new(engine.deref(), db, &genesis_header, vec![genesis_header.hash()]); +// let c = b.close(); } \ No newline at end of file diff --git a/src/ethereum/mod.rs b/src/ethereum/mod.rs index 31cbcc496..e743c6d09 100644 --- a/src/ethereum/mod.rs +++ b/src/ethereum/mod.rs @@ -9,3 +9,64 @@ pub mod denominations; pub use self::ethash::*; pub use self::denominations::*; +use super::spec::*; + +/// Create a new Olympic chain spec. +pub fn new_olympic() -> Spec { Spec::from_json_utf8(include_bytes!("res/olympic.json")) } + +/// Create a new Frontier mainnet chain spec. +pub fn new_frontier() -> Spec { Spec::from_json_utf8(include_bytes!("res/frontier.json")) } + +/// Create a new Frontier chain spec as though it never changes to Homestead. +pub fn new_frontier_test() -> Spec { Spec::from_json_utf8(include_bytes!("res/frontier_test.json")) } + +/// Create a new Homestead chain spec as though it never changed from Frontier. +pub fn new_homestead_test() -> Spec { Spec::from_json_utf8(include_bytes!("res/homestead_test.json")) } + +/// Create a new Morden chain spec. +pub fn new_morden() -> Spec { Spec::from_json_utf8(include_bytes!("res/morden.json")) } + +#[cfg(test)] +mod tests { + use common::*; + use state::*; + use engine::*; + use super::*; + + #[test] + fn ensure_db_good() { + let engine = new_morden().to_engine().unwrap(); + let genesis_header = engine.spec().genesis_header(); + let mut db = OverlayDB::new_temp(); + engine.spec().ensure_db_good(&mut db); + let s = State::from_existing(db.clone(), genesis_header.state_root.clone(), engine.account_start_nonce()); + assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64)); + assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64)); + assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64)); + assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000004")), U256::from(1u64)); + assert_eq!(s.balance(&address_from_hex("102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c")), U256::from(1u64) << 200); + assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000000")), U256::from(0u64)); + } + + #[test] + fn morden() { + let morden = new_morden(); + + assert_eq!(*morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); + let genesis = morden.genesis_block(); + assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()); + + morden.to_engine(); + } + + #[test] + fn frontier() { + let frontier = new_frontier(); + + assert_eq!(*frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap()); + let genesis = frontier.genesis_block(); + assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap()); + + frontier.to_engine(); + } +} \ No newline at end of file diff --git a/res/frontier.json b/src/ethereum/res/frontier.json similarity index 100% rename from res/frontier.json rename to src/ethereum/res/frontier.json diff --git a/res/frontier_test.json b/src/ethereum/res/frontier_test.json similarity index 100% rename from res/frontier_test.json rename to src/ethereum/res/frontier_test.json diff --git a/res/homestead_test.json b/src/ethereum/res/homestead_test.json similarity index 100% rename from res/homestead_test.json rename to src/ethereum/res/homestead_test.json diff --git a/res/morden.json b/src/ethereum/res/morden.json similarity index 100% rename from res/morden.json rename to src/ethereum/res/morden.json diff --git a/res/olympic.json b/src/ethereum/res/olympic.json similarity index 100% rename from res/olympic.json rename to src/ethereum/res/olympic.json diff --git a/src/spec.rs b/src/spec.rs index 2a6463075..2af075b1b 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -212,14 +212,8 @@ impl Spec { Self::from_json(json) } - /// Create a new Olympic chain spec. - pub fn new_olympic() -> Spec { Self::from_json_utf8(include_bytes!("../res/olympic.json")) } - - /// Create a new Frontier chain spec. - pub fn new_frontier() -> Spec { Self::from_json_utf8(include_bytes!("../res/frontier.json")) } - - /// Create a new Morden chain spec. - pub fn new_morden() -> Spec { Self::from_json_utf8(include_bytes!("../res/morden.json")) } + /// 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")) } } #[cfg(test)] @@ -231,24 +225,13 @@ mod tests { use super::*; #[test] - fn morden() { - let morden = Spec::new_morden(); + fn test_chain() { + let test_spec = Spec::new_test(); - assert_eq!(*morden.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); - let genesis = morden.genesis_block(); + assert_eq!(*test_spec.state_root(), H256::from_str("f3f4696bbf3b3b07775128eb7a3763279a394e382130f27c21e70233e04946a9").unwrap()); + let genesis = test_spec.genesis_block(); assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303").unwrap()); - morden.to_engine(); - } - - #[test] - fn frontier() { - let frontier = Spec::new_frontier(); - - assert_eq!(*frontier.state_root(), H256::from_str("d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544").unwrap()); - let genesis = frontier.genesis_block(); - assert_eq!(BlockView::new(&genesis).header_view().sha3(), H256::from_str("d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3").unwrap()); - - frontier.to_engine(); + let _ = test_spec.to_engine(); } } \ No newline at end of file diff --git a/src/state.rs b/src/state.rs index 63cf4f347..f0e10fa2d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -27,7 +27,7 @@ impl State { let mut root = H256::new(); { // init trie and reset root too null - let _ = TrieDBMut::new(&mut db, &mut root); + let _ = SecTrieDBMut::new(&mut db, &mut root); } State { @@ -39,10 +39,10 @@ impl State { } /// Creates new state with existing state root - pub fn from_existing(mut db: OverlayDB, mut root: H256, account_start_nonce: U256) -> State { + pub fn from_existing(db: OverlayDB, root: H256, account_start_nonce: U256) -> State { { // trie should panic! if root does not exist - let _ = TrieDB::new(&mut db, &mut root); + let _ = SecTrieDB::new(&db, &root); } State { @@ -142,7 +142,7 @@ impl State { unimplemented!(); } - /// Commit accounts to TrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. + /// Commit accounts to SecTrieDBMut. This is similar to cpp-ethereum's dev::eth::commit. /// `accounts` is mutable because we may need to commit the code or storage and record that. pub fn commit_into(db: &mut HashDB, mut root: H256, accounts: &mut HashMap>) -> H256 { // first, commit the sub trees. @@ -158,7 +158,7 @@ impl State { } { - let mut trie = TrieDBMut::from_existing(db, &mut root); + let mut trie = SecTrieDBMut::from_existing(db, &mut root); for (address, ref a) in accounts.iter() { match a { &&Some(ref account) => trie.insert(address, &account.rlp()), @@ -184,7 +184,7 @@ impl State { /// `require_code` requires that the code be cached, too. fn get(&self, a: &Address, require_code: bool) -> Ref> { self.cache.borrow_mut().entry(a.clone()).or_insert_with(|| - TrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); + SecTrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); if require_code { if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() { account.cache_code(&self.db); @@ -202,7 +202,7 @@ impl State { /// If it doesn't exist, make account equal the evaluation of `default`. fn require_or_from Account, G: FnOnce(&mut Account)>(&self, a: &Address, require_code: bool, default: F, not_default: G) -> RefMut { self.cache.borrow_mut().entry(a.clone()).or_insert_with(|| - TrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); + SecTrieDB::new(&self.db, &self.root).get(&a).map(|rlp| Account::from_rlp(rlp))); let preexists = self.cache.borrow().get(a).unwrap().is_none(); if preexists { self.cache.borrow_mut().insert(a.clone(), Some(default())); @@ -333,7 +333,7 @@ fn ensure_cached() { let a = Address::from_str("0000000000000000000000000000000000000000").unwrap(); s.require(&a, false); s.commit(); - assert_eq!(s.root().hex(), "ec68b85fa2e0526dc0e821a5b33135459114f19173ce0479f5c09b21cc25b9a4"); + assert_eq!(s.root().hex(), "0ce23f3c809de377b008a4a3ee94a0834aac8bec1f86e28ffe4fdb5a15b0c785"); } #[test]