diff --git a/ethcore/src/account_db.rs b/ethcore/src/account_db.rs index cee2b4d48..15042403d 100644 --- a/ethcore/src/account_db.rs +++ b/ethcore/src/account_db.rs @@ -35,6 +35,38 @@ fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 { dst } +/// A factory for different kinds of account dbs. +#[derive(Debug, Clone)] +pub enum Factory { + /// Mangle hashes based on address. + Mangled, + /// Don't mangle hashes. + Plain, +} + +impl Default for Factory { + fn default() -> Self { Factory::Mangled } +} + +impl Factory { + /// Create a read-only accountdb. + /// This will panic when write operations are called. + pub fn readonly<'db>(&self, db: &'db HashDB, address_hash: H256) -> Box { + match *self { + Factory::Mangled => Box::new(AccountDB::from_hash(db, address_hash)), + Factory::Plain => Box::new(Wrapping(db)), + } + } + + /// Create a new mutable hashdb. + pub fn create<'db>(&self, db: &'db mut HashDB, address_hash: H256) -> Box { + match *self { + Factory::Mangled => Box::new(AccountDBMut::from_hash(db, address_hash)), + Factory::Plain => Box::new(WrappingMut(db)), + } + } +} + // TODO: introduce HashDBMut? /// DB backend wrapper for Account trie /// Transforms trie node keys for the database @@ -162,4 +194,79 @@ impl<'db> HashDB for AccountDBMut<'db>{ } } +struct Wrapping<'db>(&'db HashDB); +impl<'db> HashDB for Wrapping<'db> { + fn keys(&self) -> HashMap { + unimplemented!() + } + + fn get(&self, key: &H256) -> Option<&[u8]> { + if key == &SHA3_NULL_RLP { + return Some(&NULL_RLP_STATIC); + } + self.0.get(key) + } + + fn contains(&self, key: &H256) -> bool { + if key == &SHA3_NULL_RLP { + return true; + } + self.0.contains(key) + } + + fn insert(&mut self, _value: &[u8]) -> H256 { + unimplemented!() + } + + fn emplace(&mut self, _key: H256, _value: Bytes) { + unimplemented!() + } + + fn remove(&mut self, _key: &H256) { + unimplemented!() + } +} + +struct WrappingMut<'db>(&'db mut HashDB); + +impl<'db> HashDB for WrappingMut<'db>{ + fn keys(&self) -> HashMap { + unimplemented!() + } + + fn get(&self, key: &H256) -> Option<&[u8]> { + if key == &SHA3_NULL_RLP { + return Some(&NULL_RLP_STATIC); + } + self.0.get(key) + } + + fn contains(&self, key: &H256) -> bool { + if key == &SHA3_NULL_RLP { + return true; + } + self.0.contains(key) + } + + fn insert(&mut self, value: &[u8]) -> H256 { + if value == &NULL_RLP { + return SHA3_NULL_RLP.clone(); + } + self.0.insert(value) + } + + fn emplace(&mut self, key: H256, value: Bytes) { + if key == SHA3_NULL_RLP { + return; + } + self.0.emplace(key, value) + } + + fn remove(&mut self, key: &H256) { + if key == &SHA3_NULL_RLP { + return; + } + self.0.remove(key) + } +} \ No newline at end of file diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 3591fee05..cd02b9a1b 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -21,7 +21,7 @@ use engines::Engine; use state::*; use verification::PreverifiedBlock; use trace::FlatTrace; -use evm::Factory as EvmFactory; +use factory::Factories; /// A block, encoded as it is on the block chain. #[derive(Default, Debug, Clone, PartialEq)] @@ -192,7 +192,6 @@ impl IsBlock for ExecutedBlock { pub struct OpenBlock<'x> { block: ExecutedBlock, engine: &'x Engine, - vm_factory: &'x EvmFactory, last_hashes: Arc, } @@ -230,8 +229,7 @@ impl<'x> OpenBlock<'x> { /// Create a new `OpenBlock` ready for transaction pushing. pub fn new( engine: &'x Engine, - vm_factory: &'x EvmFactory, - trie_factory: TrieFactory, + factories: Factories, tracing: bool, db: Box, parent: &Header, @@ -240,11 +238,10 @@ impl<'x> OpenBlock<'x> { gas_range_target: (U256, U256), extra_data: Bytes, ) -> Result { - let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), trie_factory)); + let state = try!(State::from_existing(db, parent.state_root().clone(), engine.account_start_nonce(), factories)); let mut r = OpenBlock { block: ExecutedBlock::new(state, tracing), engine: engine, - vm_factory: vm_factory, last_hashes: last_hashes, }; @@ -332,7 +329,7 @@ impl<'x> OpenBlock<'x> { let env_info = self.env_info(); // info!("env_info says gas_used={}", env_info.gas_used); - match self.block.state.apply(&env_info, self.engine, self.vm_factory, &t, self.block.traces.is_some()) { + match self.block.state.apply(&env_info, self.engine, &t, self.block.traces.is_some()) { Ok(outcome) => { self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); self.block.base.transactions.push(t); @@ -421,14 +418,13 @@ impl ClosedBlock { } /// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`. - pub fn reopen<'a>(self, engine: &'a Engine, vm_factory: &'a EvmFactory) -> OpenBlock<'a> { + pub fn reopen<'a>(self, engine: &'a Engine) -> OpenBlock<'a> { // revert rewards (i.e. set state back at last transaction's state). let mut block = self.block; block.state = self.unclosed_state; OpenBlock { block: block, engine: engine, - vm_factory: vm_factory, last_hashes: self.last_hashes, } } @@ -499,17 +495,16 @@ pub fn enact( db: Box, parent: &Header, last_hashes: Arc, - vm_factory: &EvmFactory, - trie_factory: TrieFactory, + factories: Factories, ) -> Result { { if ::log::max_log_level() >= ::log::LogLevel::Trace { - let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), trie_factory.clone())); + let s = try!(State::from_existing(db.boxed_clone(), parent.state_root().clone(), engine.account_start_nonce(), factories.clone())); trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author())); } } - let mut b = try!(OpenBlock::new(engine, vm_factory, trie_factory, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![])); + let mut b = try!(OpenBlock::new(engine, factories, tracing, db, parent, last_hashes, Address::new(), (3141562.into(), 31415620.into()), vec![])); b.set_difficulty(*header.difficulty()); b.set_gas_limit(*header.gas_limit()); b.set_timestamp(header.timestamp()); @@ -532,12 +527,11 @@ pub fn enact_bytes( db: Box, parent: &Header, last_hashes: Arc, - vm_factory: &EvmFactory, - trie_factory: TrieFactory, + factories: Factories, ) -> Result { let block = BlockView::new(block_bytes); let header = block.header(); - enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory) + enact(&header, &block.transactions(), &block.uncles(), engine, tracing, db, parent, last_hashes, factories) } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header @@ -549,11 +543,10 @@ pub fn enact_verified( db: Box, parent: &Header, last_hashes: Arc, - vm_factory: &EvmFactory, - trie_factory: TrieFactory, + factories: Factories, ) -> Result { let view = BlockView::new(&block.bytes); - enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, vm_factory, trie_factory) + enact(&block.header, &block.transactions, &view.uncles(), engine, tracing, db, parent, last_hashes, factories) } /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards @@ -565,11 +558,10 @@ pub fn enact_and_seal( db: Box, parent: &Header, last_hashes: Arc, - vm_factory: &EvmFactory, - trie_factory: TrieFactory, + factories: Factories, ) -> Result { let header = BlockView::new(block_bytes).header_view(); - Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, vm_factory, trie_factory)).seal(engine, header.seal()))) + Ok(try!(try!(enact_bytes(block_bytes, engine, tracing, db, parent, last_hashes, factories)).seal(engine, header.seal()))) } #[cfg(test)] @@ -587,8 +579,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let vm_factory = Default::default(); - let b = OpenBlock::new(&*spec.engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); let _ = b.seal(&*spec.engine, vec![]); } @@ -603,9 +594,8 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); - let vm_factory = Default::default(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap() .close_and_lock().seal(engine, vec![]).unwrap(); let orig_bytes = b.rlp_bytes(); let orig_db = b.drain(); @@ -613,7 +603,7 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); - let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap(); + let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap(); assert_eq!(e.rlp_bytes(), orig_bytes); @@ -632,9 +622,8 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); - let vm_factory = Default::default(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let mut open_block = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let mut open_block = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes.clone(), Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut uncle1_header = Header::new(); uncle1_header.extra_data = b"uncle1".to_vec(); let mut uncle2_header = Header::new(); @@ -649,7 +638,7 @@ mod tests { let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); - let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, &Default::default(), Default::default()).unwrap(); + let e = enact_and_seal(&orig_bytes, engine, false, db, &genesis_header, last_hashes, Default::default()).unwrap(); let bytes = e.rlp_bytes(); assert_eq!(bytes, orig_bytes); diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index aced57e4c..5e0a4b9f8 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -65,6 +65,7 @@ use evm::Factory as EvmFactory; use miner::{Miner, MinerService}; use util::TrieFactory; use snapshot::{self, io as snapshot_io}; +use factory::Factories; // re-export pub use types::blockchain_info::BlockChainInfo; @@ -130,8 +131,6 @@ pub struct Client { import_lock: Mutex<()>, panic_handler: Arc, verifier: Box, - vm_factory: Arc, - trie_factory: TrieFactory, miner: Arc, sleep_state: Mutex, liveness: AtomicBool, @@ -139,6 +138,7 @@ pub struct Client { notify: RwLock>>, queue_transactions: AtomicUsize, last_hashes: RwLock>, + factories: Factories, } const HISTORY: u64 = 1200; @@ -188,6 +188,13 @@ impl Client { panic_handler.forward_from(&block_queue); let awake = match config.mode { Mode::Dark(..) => false, _ => true }; + + let factories = Factories { + vm: EvmFactory::new(config.vm_type), + trie: TrieFactory::new(config.trie_spec), + accountdb: Default::default(), + }; + let client = Client { sleep_state: Mutex::new(SleepState::new(awake)), liveness: AtomicBool::new(awake), @@ -202,13 +209,12 @@ impl Client { import_lock: Mutex::new(()), panic_handler: panic_handler, verifier: verification::new(config.verifier_type), - vm_factory: Arc::new(EvmFactory::new(config.vm_type)), - trie_factory: TrieFactory::new(config.trie_spec), miner: miner, io_channel: message_channel, notify: RwLock::new(Vec::new()), queue_transactions: AtomicUsize::new(0), last_hashes: RwLock::new(VecDeque::new()), + factories: factories, }; Ok(Arc::new(client)) } @@ -289,7 +295,7 @@ impl Client { let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let db = self.state_db.lock().boxed_clone(); - let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, &self.vm_factory, self.trie_factory.clone()); + let enact_result = enact_verified(block, engine, self.tracedb.tracing_enabled(), db, &parent, last_hashes, self.factories.clone()); if let Err(e) = enact_result { warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); return Err(()); @@ -493,7 +499,7 @@ impl Client { let root = HeaderView::new(&header).state_root(); - State::from_existing(db, root, self.engine.account_start_nonce(), self.trie_factory.clone()).ok() + State::from_existing(db, root, self.engine.account_start_nonce(), self.factories.clone()).ok() }) } @@ -518,7 +524,7 @@ impl Client { self.state_db.lock().boxed_clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce(), - self.trie_factory.clone()) + self.factories.clone()) .expect("State root of best block header always valid.") } @@ -688,7 +694,7 @@ impl BlockChainClient for Client { state.add_balance(&sender, &(needed_balance - balance)); } let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; - let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, options)); + let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options)); // TODO gav move this into Executive. ret.state_diff = original_state.map(|original| state.diff_from(original)); @@ -720,7 +726,7 @@ impl BlockChainClient for Client { gas_limit: view.gas_limit(), }; for t in txs.iter().take(address.index) { - match Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, Default::default()) { + match Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, Default::default()) { Ok(x) => { env_info.gas_used = env_info.gas_used + x.gas_used; } Err(ee) => { return Err(CallError::Execution(ee)) } } @@ -728,7 +734,7 @@ impl BlockChainClient for Client { let t = &txs[address.index]; let original_state = if analytics.state_diffing { Some(state.clone()) } else { None }; - let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.vm_factory).transact(t, options)); + let mut ret = try!(Executive::new(&mut state, &env_info, &*self.engine, &self.factories.vm).transact(t, options)); ret.state_diff = original_state.map(|original| state.diff_from(original)); Ok(ret) @@ -1029,8 +1035,7 @@ impl MiningBlockChainClient for Client { let mut open_block = OpenBlock::new( engine, - &self.vm_factory, - self.trie_factory.clone(), + self.factories.clone(), false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. self.state_db.lock().boxed_clone(), &self.chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"), @@ -1054,7 +1059,7 @@ impl MiningBlockChainClient for Client { } fn vm_factory(&self) -> &EvmFactory { - &self.vm_factory + &self.factories.vm } fn import_sealed_block(&self, block: SealedBlock) -> ImportResult { diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index be1e9da25..410a90347 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -276,7 +276,6 @@ impl MiningBlockChainClient for TestBlockChainClient { let last_hashes = vec![genesis_header.hash()]; let mut open_block = OpenBlock::new( engine, - self.vm_factory(), Default::default(), false, db, diff --git a/ethcore/src/engines/basic_authority.rs b/ethcore/src/engines/basic_authority.rs index 9a5a97337..7ec5a66a4 100644 --- a/ethcore/src/engines/basic_authority.rs +++ b/ethcore/src/engines/basic_authority.rs @@ -252,8 +252,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let vm_factory = Default::default(); - let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); assert!(b.try_seal(engine, seal).is_ok()); diff --git a/ethcore/src/engines/instant_seal.rs b/ethcore/src/engines/instant_seal.rs index ae1aa20cd..6a3d3c700 100644 --- a/ethcore/src/engines/instant_seal.rs +++ b/ethcore/src/engines/instant_seal.rs @@ -86,8 +86,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let vm_factory = Default::default(); - let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close_and_lock(); // Seal with empty AccountProvider. let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); diff --git a/ethcore/src/ethereum/ethash.rs b/ethcore/src/ethereum/ethash.rs index d1b2082bf..c658432a2 100644 --- a/ethcore/src/ethereum/ethash.rs +++ b/ethcore/src/ethereum/ethash.rs @@ -357,8 +357,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let vm_factory = Default::default(); - let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let b = b.close(); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); } @@ -372,8 +371,7 @@ mod tests { let mut db = db_result.take(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); let last_hashes = Arc::new(vec![genesis_header.hash()]); - let vm_factory = Default::default(); - let mut b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); + let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap(); let mut uncle = Header::new(); let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); uncle.author = uncle_author.clone(); diff --git a/ethcore/src/evm/factory.rs b/ethcore/src/evm/factory.rs index 50c384a99..94800c7de 100644 --- a/ethcore/src/evm/factory.rs +++ b/ethcore/src/evm/factory.rs @@ -80,6 +80,7 @@ impl VMType { } /// Evm factory. Creates appropriate Evm. +#[derive(Clone)] pub struct Factory { evm: VMType } @@ -128,7 +129,7 @@ impl Factory { impl Default for Factory { /// Returns jitvm factory - #[cfg(feature = "jit")] + #[cfg(all(feature = "jit", not(test)))] fn default() -> Factory { Factory { evm: VMType::Jit @@ -136,7 +137,7 @@ impl Default for Factory { } /// Returns native rust evm factory - #[cfg(not(feature = "jit"))] + #[cfg(any(not(feature = "jit"), test))] fn default() -> Factory { Factory { evm: VMType::Interpreter diff --git a/ethcore/src/factory.rs b/ethcore/src/factory.rs new file mode 100644 index 000000000..dec341820 --- /dev/null +++ b/ethcore/src/factory.rs @@ -0,0 +1,30 @@ +// 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::trie::TrieFactory; +use evm::Factory as EvmFactory; +use account_db::Factory as AccountFactory; + +/// Collection of factories. +#[derive(Default, Clone)] +pub struct Factories { + /// factory for evm. + pub vm: EvmFactory, + /// factory for tries. + pub trie: TrieFactory, + /// factory for account databases. + pub accountdb: AccountFactory, +} \ No newline at end of file diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index 97f9f70f0..28aaa62ec 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -64,8 +64,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec { state.populate_from(pre); state.commit() .expect(&format!("State test {} failed due to internal error.", name)); - let vm_factory = Default::default(); - let res = state.apply(&env, &*engine, &vm_factory, &transaction, false); + let res = state.apply(&env, &*engine, &transaction, false); if fail_unless(state.root() == &post_state_root) { println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root); diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index c21720640..2da6abe3d 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -139,6 +139,7 @@ mod externalities; mod verification; mod blockchain; mod types; +mod factory; #[cfg(test)] mod tests; diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index 06879ca81..3ca3f0d74 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -270,7 +270,7 @@ impl Miner { Some(old_block) => { trace!(target: "miner", "Already have previous work; updating and returning"); // add transactions to old_block - old_block.reopen(&*self.engine, chain.vm_factory()) + old_block.reopen(&*self.engine) } None => { // block not found - create it. diff --git a/ethcore/src/snapshot/account.rs b/ethcore/src/snapshot/account.rs index c1c4ac251..3c31bab0d 100644 --- a/ethcore/src/snapshot/account.rs +++ b/ethcore/src/snapshot/account.rs @@ -17,8 +17,9 @@ //! Account state encoding and decoding use account_db::{AccountDB, AccountDBMut}; -use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY, TrieDB}; +use util::{U256, FixedHash, H256, Bytes, HashDB, SHA3_EMPTY}; use util::rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View}; +use util::trie::{TrieDB, Trie}; use snapshot::Error; // An alternate account structure from ::account::Account. diff --git a/ethcore/src/snapshot/mod.rs b/ethcore/src/snapshot/mod.rs index 118b0988a..4e33c9ebc 100644 --- a/ethcore/src/snapshot/mod.rs +++ b/ethcore/src/snapshot/mod.rs @@ -26,13 +26,14 @@ use engines::Engine; use ids::BlockID; use views::BlockView; -use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut}; +use util::{Bytes, Hashable, HashDB, snappy}; use util::Mutex; use util::hash::{FixedHash, H256}; use util::journaldb::{self, Algorithm, JournalDB}; use util::kvdb::Database; use util::rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View, Compressible, RlpType}; use util::rlp::SHA3_NULL_RLP; +use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut}; use self::account::Account; use self::block::AbridgedBlock; diff --git a/ethcore/src/snapshot/tests/helpers.rs b/ethcore/src/snapshot/tests/helpers.rs index e6a92642e..aa055e8fb 100644 --- a/ethcore/src/snapshot/tests/helpers.rs +++ b/ethcore/src/snapshot/tests/helpers.rs @@ -24,7 +24,7 @@ use snapshot::account::Account; use util::hash::{FixedHash, H256}; use util::hashdb::HashDB; use util::trie::{Alphabet, StandardMap, SecTrieDBMut, TrieMut, ValueMode}; -use util::trie::{TrieDB, TrieDBMut}; +use util::trie::{TrieDB, TrieDBMut, Trie}; use util::rlp::SHA3_NULL_RLP; // the proportion of accounts we will alter each tick. @@ -51,10 +51,12 @@ impl StateProducer { // modify existing accounts. let mut accounts_to_modify: Vec<_> = { let trie = TrieDB::new(&*db, &self.state_root).unwrap(); - trie.iter() + let temp = trie.iter() // binding required due to complicated lifetime stuff .filter(|_| rng.gen::() < ACCOUNT_CHURN) .map(|(k, v)| (H256::from_slice(&k), v.to_owned())) - .collect() + .collect(); + + temp }; // sweep once to alter storage tries. diff --git a/ethcore/src/state/account.rs b/ethcore/src/state/account.rs index 829198910..72e2bb0ed 100644 --- a/ethcore/src/state/account.rs +++ b/ethcore/src/state/account.rs @@ -19,7 +19,6 @@ use std::collections::hash_map::Entry; use util::*; use pod_account::*; -use account_db::*; use std::cell::{Ref, RefCell, Cell}; @@ -148,7 +147,7 @@ impl Account { } /// Get (and cache) the contents of the trie's storage at `key`. - pub fn storage_at(&self, db: &AccountDB, key: &H256) -> H256 { + pub fn storage_at(&self, db: &HashDB, key: &H256) -> H256 { self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{ let db = SecTrieDB::new(db, &self.storage_root) .expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \ @@ -225,7 +224,7 @@ impl Account { } /// Provide a database to get `code_hash`. Should not be called if it is a contract without code. - pub fn cache_code(&mut self, db: &AccountDB) -> bool { + pub fn cache_code(&mut self, db: &HashDB) -> bool { // TODO: fill out self.code_cache; trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty()); self.is_cached() || @@ -277,7 +276,7 @@ impl Account { } /// Commit the `storage_overlay` to the backing DB and update `storage_root`. - pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut AccountDBMut) { + pub fn commit_storage(&mut self, trie_factory: &TrieFactory, db: &mut HashDB) { let mut t = trie_factory.from_existing(db, &mut self.storage_root) .expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \ SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \ @@ -300,7 +299,7 @@ impl Account { } /// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this. - pub fn commit_code(&mut self, db: &mut AccountDBMut) { + pub fn commit_code(&mut self, db: &mut HashDB) { trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty()); match (self.code_hash.is_none(), self.code_cache.is_empty()) { (true, true) => self.code_hash = Some(SHA3_EMPTY), diff --git a/ethcore/src/state/mod.rs b/ethcore/src/state/mod.rs index c62a77d25..46e77cd34 100644 --- a/ethcore/src/state/mod.rs +++ b/ethcore/src/state/mod.rs @@ -18,8 +18,7 @@ use std::cell::{RefCell, RefMut}; use common::*; use engines::Engine; use executive::{Executive, TransactOptions}; -use evm::Factory as EvmFactory; -use account_db::*; +use factory::Factories; use trace::FlatTrace; use pod_account::*; use pod_state::{self, PodState}; @@ -49,7 +48,7 @@ pub struct State { cache: RefCell>>, snapshots: RefCell>>>>, account_start_nonce: U256, - trie_factory: TrieFactory, + factories: Factories, } const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with valid root. Creating a SecTrieDB with a valid root will not fail. \ @@ -58,11 +57,11 @@ const SEC_TRIE_DB_UNWRAP_STR: &'static str = "A state can only be created with v impl State { /// Creates new state with empty state root #[cfg(test)] - pub fn new(mut db: Box, account_start_nonce: U256, trie_factory: TrieFactory) -> State { + pub fn new(mut db: Box, account_start_nonce: U256, factories: Factories) -> State { let mut root = H256::new(); { // init trie and reset root too null - let _ = trie_factory.create(db.as_hashdb_mut(), &mut root); + let _ = factories.trie.create(db.as_hashdb_mut(), &mut root); } State { @@ -71,12 +70,12 @@ impl State { cache: RefCell::new(HashMap::new()), snapshots: RefCell::new(Vec::new()), account_start_nonce: account_start_nonce, - trie_factory: trie_factory, + factories: factories, } } /// Creates new state with existing state root - pub fn from_existing(db: Box, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result { + pub fn from_existing(db: Box, root: H256, account_start_nonce: U256, factories: Factories) -> Result { if !db.as_hashdb().contains(&root) { return Err(TrieError::InvalidStateRoot(root)); } @@ -87,7 +86,7 @@ impl State { cache: RefCell::new(HashMap::new()), snapshots: RefCell::new(Vec::new()), account_start_nonce: account_start_nonce, - trie_factory: trie_factory, + factories: factories }; Ok(state) @@ -185,8 +184,11 @@ impl State { /// Mutate storage of account `address` so that it is `value` for `key`. pub fn storage_at(&self, address: &Address, key: &H256) -> H256 { - self.ensure_cached(address, false, - |a| a.as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key))) + self.ensure_cached(address, false, |a| a.as_ref().map_or(H256::new(), |a| { + let addr_hash = a.address_hash(address); + let db = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash); + a.storage_at(db.as_hashdb(), key) + })) } /// Mutate storage of account `a` so that it is `value` for `key`. @@ -236,11 +238,12 @@ impl State { /// Execute a given transaction. /// This will change the state accordingly. - pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, vm_factory: &EvmFactory, t: &SignedTransaction, tracing: bool) -> ApplyResult { + pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction, tracing: bool) -> ApplyResult { // let old = self.to_pod(); let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true }; - let e = try!(Executive::new(self, env_info, engine, vm_factory).transact(t, options)); + let vm_factory = self.factories.vm.clone(); + let e = try!(Executive::new(self, env_info, engine, &vm_factory).transact(t, options)); // TODO uncomment once to_pod() works correctly. // trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); @@ -254,27 +257,27 @@ impl State { /// `accounts` is mutable because we may need to commit the code or storage and record that. #[cfg_attr(feature="dev", allow(match_ref_pats))] pub fn commit_into( - trie_factory: &TrieFactory, + factories: &Factories, db: &mut HashDB, root: &mut H256, - accounts: &mut HashMap> + accounts: &mut HashMap> ) -> Result<(), Error> { // first, commit the sub trees. // TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? for (address, ref mut a) in accounts.iter_mut() { match a { &mut&mut Some(ref mut account) if account.is_dirty() => { - let mut account_db = AccountDBMut::from_hash(db, account.address_hash(address)); - account.commit_storage(trie_factory, &mut account_db); - account.commit_code(&mut account_db); + let addr_hash = account.address_hash(address); + let mut account_db = factories.accountdb.create(db, addr_hash); + account.commit_storage(&factories.trie, account_db.as_hashdb_mut()); + account.commit_code(account_db.as_hashdb_mut()); } _ => {} } } { - let mut trie = trie_factory.from_existing(db, root).unwrap(); + let mut trie = factories.trie.from_existing(db, root).unwrap(); for (address, ref mut a) in accounts.iter_mut() { match **a { Some(ref mut account) if account.is_dirty() => { @@ -293,7 +296,7 @@ impl State { /// Commits our cached account changes into the trie. pub fn commit(&mut self) -> Result<(), Error> { assert!(self.snapshots.borrow().is_empty()); - Self::commit_into(&self.trie_factory, self.db.as_hashdb_mut(), &mut self.root, &mut *self.cache.borrow_mut()) + Self::commit_into(&self.factories, self.db.as_hashdb_mut(), &mut self.root, &mut *self.cache.borrow_mut()) } /// Clear state cache @@ -351,7 +354,7 @@ impl State { where F: FnOnce(&Option) -> U { let have_key = self.cache.borrow().contains_key(a); if !have_key { - let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let maybe_acc = match db.get(a) { Ok(acc) => acc.map(Account::from_rlp), Err(e) => panic!("Potential DB corruption encountered: {}", e), @@ -361,7 +364,8 @@ impl State { if require_code { if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() { let addr_hash = account.address_hash(a); - account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash)); + let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash); + account.cache_code(accountdb.as_hashdb()); } } @@ -380,7 +384,7 @@ impl State { { let contains_key = self.cache.borrow().contains_key(a); if !contains_key { - let db = self.trie_factory.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); + let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR); let maybe_acc = match db.get(a) { Ok(acc) => acc.map(Account::from_rlp), Err(e) => panic!("Potential DB corruption encountered: {}", e), @@ -400,7 +404,8 @@ impl State { let account = c.get_mut(a).unwrap().as_mut().unwrap(); if require_code { let addr_hash = account.address_hash(a); - account.cache_code(&AccountDB::from_hash(self.db.as_hashdb(), addr_hash)); + let accountdb = self.factories.accountdb.readonly(self.db.as_hashdb(), addr_hash); + account.cache_code(accountdb.as_hashdb()); } account }) @@ -421,7 +426,7 @@ impl Clone for State { cache: RefCell::new(self.cache.borrow().clone()), snapshots: RefCell::new(self.snapshots.borrow().clone()), account_start_nonce: self.account_start_nonce.clone(), - trie_factory: self.trie_factory.clone(), + factories: self.factories.clone(), } } } @@ -464,8 +469,7 @@ fn should_apply_create_transaction() { }.sign(&"".sha3()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 0, @@ -525,8 +529,7 @@ fn should_trace_failed_create_transaction() { }.sign(&"".sha3()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), action: trace::Action::Create(trace::Create { @@ -564,8 +567,7 @@ fn should_trace_call_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), action: trace::Action::Call(trace::Call { @@ -607,8 +609,7 @@ fn should_trace_basic_call_transaction() { }.sign(&"".sha3()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), action: trace::Action::Call(trace::Call { @@ -649,8 +650,7 @@ fn should_trace_call_transaction_to_builtin() { data: vec![], }.sign(&"".sha3()); - let vm_factory = Default::default(); - let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -693,8 +693,7 @@ fn should_not_trace_subcall_transaction_to_builtin() { }.sign(&"".sha3()); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); - let vm_factory = Default::default(); - let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -738,8 +737,7 @@ fn should_not_trace_callcode() { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - let vm_factory = Default::default(); - let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -801,8 +799,7 @@ fn should_not_trace_delegatecall() { state.init_code(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); - let vm_factory = Default::default(); - let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -861,8 +858,7 @@ fn should_trace_failed_call_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), action: trace::Action::Call(trace::Call { @@ -903,8 +899,7 @@ fn should_trace_call_with_subcall_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -963,8 +958,7 @@ fn should_trace_call_with_basic_subcall_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 1, @@ -1019,8 +1013,7 @@ fn should_not_trace_call_with_invalid_basic_subcall_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 0, @@ -1064,8 +1057,7 @@ fn should_trace_failed_subcall_transaction() { state.init_code(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 1, @@ -1122,8 +1114,7 @@ fn should_trace_call_with_subcall_with_subcall_transaction() { state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 1, @@ -1198,8 +1189,7 @@ fn should_trace_failed_subcall_with_subcall_transaction() { state.init_code(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), @@ -1271,8 +1261,7 @@ fn should_trace_suicide() { state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); state.add_balance(&0xa.into(), &50.into()); state.add_balance(t.sender().as_ref().unwrap(), &100.into()); - let vm_factory = Default::default(); - let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap(); + let result = state.apply(&info, &engine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { trace_address: Default::default(), subtraces: 1, diff --git a/ethcore/src/tests/helpers.rs b/ethcore/src/tests/helpers.rs index bc671b717..ff35e7023 100644 --- a/ethcore/src/tests/helpers.rs +++ b/ethcore/src/tests/helpers.rs @@ -139,7 +139,6 @@ pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_numbe let mut db_result = get_temp_journal_db(); let mut db = db_result.take(); test_spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); - let vm_factory = Default::default(); let genesis_header = test_spec.genesis_header(); let mut rolling_timestamp = 40; @@ -156,7 +155,6 @@ pub fn generate_dummy_client_with_spec_and_data(get_test_spec: F, block_numbe // forge block. let mut b = OpenBlock::new( test_engine, - &vm_factory, Default::default(), false, db, diff --git a/util/src/hashdb.rs b/util/src/hashdb.rs index 52b126ac7..55cc2a89e 100644 --- a/util/src/hashdb.rs +++ b/util/src/hashdb.rs @@ -136,4 +136,4 @@ impl AsHashDB for T { fn as_hashdb_mut(&mut self) -> &mut HashDB { self } -} +} \ No newline at end of file diff --git a/util/src/trie/fatdb.rs b/util/src/trie/fatdb.rs index 244662670..bb35bd467 100644 --- a/util/src/trie/fatdb.rs +++ b/util/src/trie/fatdb.rs @@ -17,7 +17,7 @@ use hash::H256; use sha3::Hashable; use hashdb::HashDB; -use super::{TrieDB, Trie, TrieDBIterator, TrieItem}; +use super::{TrieDB, Trie, TrieDBIterator, TrieItem, Recorder}; /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// Additionaly it stores inserted hash-key mappings for later retrieval. @@ -43,16 +43,11 @@ impl<'db> FatDB<'db> { pub fn db(&self) -> &HashDB { self.raw.db() } - - /// Iterator over all key / vlaues in the trie. - pub fn iter(&self) -> FatDBIterator { - FatDBIterator::new(&self.raw) - } } impl<'db> Trie for FatDB<'db> { fn iter<'a>(&'a self) -> Box + 'a> { - Box::new(FatDB::iter(self)) + Box::new(FatDBIterator::new(&self.raw)) } fn root(&self) -> &H256 { @@ -63,10 +58,10 @@ impl<'db> Trie for FatDB<'db> { self.raw.contains(&key.sha3()) } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result> - where 'a: 'key + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> + where 'a: 'b, R: Recorder { - self.raw.get(&key.sha3()) + self.raw.get_recorded(&key.sha3(), rec) } } diff --git a/util/src/trie/mod.rs b/util/src/trie/mod.rs index 6500059b9..886ccd724 100644 --- a/util/src/trie/mod.rs +++ b/util/src/trie/mod.rs @@ -34,6 +34,9 @@ pub mod triedbmut; pub mod sectriedb; /// Export the sectriedbmut module. pub mod sectriedbmut; +/// Trie query recording. +pub mod recorder; + mod fatdb; mod fatdbmut; @@ -45,6 +48,7 @@ pub use self::sectriedbmut::SecTrieDBMut; pub use self::sectriedb::SecTrieDB; pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; +pub use self::recorder::Recorder; /// Trie Errors. /// @@ -88,7 +92,14 @@ pub trait Trie { } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result> where 'a: 'key; + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result> where 'a: 'key { + self.get_recorded(key, &mut recorder::NoOp) + } + + /// Query the value of the given key in this trie while recording visited nodes + /// to the given recorder. If the query fails, the nodes passed to the recorder are unspecified. + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> Result> + where 'a: 'b, R: Recorder; /// Returns an iterator over elements of trie. fn iter<'a>(&'a self) -> Box + 'a>; @@ -119,7 +130,6 @@ pub trait TrieMut { fn remove(&mut self, key: &[u8]) -> Result<()>; } - /// Trie types #[derive(Debug, PartialEq, Clone)] pub enum TrieSpec { @@ -143,6 +153,51 @@ pub struct TrieFactory { spec: TrieSpec, } +/// All different kinds of tries. +/// This is used to prevent a heap allocation for every created trie. +pub enum TrieKinds<'db> { + /// A generic trie db. + Generic(TrieDB<'db>), + /// A secure trie db. + Secure(SecTrieDB<'db>), + /// A fat trie db. + Fat(FatDB<'db>), +} + +// wrapper macro for making the match easier to deal with. +macro_rules! wrapper { + ($me: ident, $f_name: ident, $($param: ident),*) => { + match *$me { + TrieKinds::Generic(ref t) => t.$f_name($($param),*), + TrieKinds::Secure(ref t) => t.$f_name($($param),*), + TrieKinds::Fat(ref t) => t.$f_name($($param),*), + } + } +} + +impl<'db> Trie for TrieKinds<'db> { + fn root(&self) -> &H256 { + wrapper!(self, root,) + } + + fn is_empty(&self) -> bool { + wrapper!(self, is_empty,) + } + + fn contains(&self, key: &[u8]) -> Result { + wrapper!(self, contains, key) + } + + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], r: &'b mut R) -> Result> + where 'a: 'b, R: Recorder { + wrapper!(self, get_recorded, key, r) + } + + fn iter<'a>(&'a self) -> Box + 'a> { + wrapper!(self, iter,) + } +} + #[cfg_attr(feature="dev", allow(wrong_self_convention))] impl TrieFactory { /// Creates new factory. @@ -153,11 +208,11 @@ impl TrieFactory { } /// Create new immutable instance of Trie. - pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result> { + pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result> { match self.spec { - TrieSpec::Generic => Ok(Box::new(try!(TrieDB::new(db, root)))), - TrieSpec::Secure => Ok(Box::new(try!(SecTrieDB::new(db, root)))), - TrieSpec::Fat => Ok(Box::new(try!(FatDB::new(db, root)))), + TrieSpec::Generic => Ok(TrieKinds::Generic(try!(TrieDB::new(db, root)))), + TrieSpec::Secure => Ok(TrieKinds::Secure(try!(SecTrieDB::new(db, root)))), + TrieSpec::Fat => Ok(TrieKinds::Fat(try!(FatDB::new(db, root)))), } } diff --git a/util/src/trie/recorder.rs b/util/src/trie/recorder.rs new file mode 100644 index 000000000..a48f277b4 --- /dev/null +++ b/util/src/trie/recorder.rs @@ -0,0 +1,236 @@ +// 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 sha3::Hashable; +use {Bytes, H256}; + +/// A record of a visited node. +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Record { + /// The depth of this node. + pub depth: u32, + + /// The raw data of the node. + pub data: Bytes, + + /// The hash of the data. + pub hash: H256, +} + +/// Trie node recorder. +/// +/// These are used to record which nodes are visited during a trie query. +/// Inline nodes are not to be recorded, as they are contained within their parent. +pub trait Recorder { + + /// Record that the given node has been visited. + /// + /// The depth parameter is the depth of the visited node, with the root node having depth 0. + fn record(&mut self, hash: &H256, data: &[u8], depth: u32); + + /// Drain all accepted records from the recorder in ascending order by depth. + fn drain(&mut self) -> Vec where Self: Sized; +} + +/// A no-op trie recorder. This ignores everything which is thrown at it. +pub struct NoOp; + +impl Recorder for NoOp { + #[inline] + fn record(&mut self, _hash: &H256, _data: &[u8], _depth: u32) {} + + #[inline] + fn drain(&mut self) -> Vec { Vec::new() } +} + +/// A simple recorder. Does nothing fancy but fulfills the `Recorder` interface +/// properly. +pub struct BasicRecorder { + nodes: Vec, + min_depth: u32, +} + +impl BasicRecorder { + /// Create a new `BasicRecorder` which records all given nodes. + #[inline] + pub fn new() -> Self { + BasicRecorder::with_depth(0) + } + + /// Create a `BasicRecorder` which only records nodes beyond a given depth. + pub fn with_depth(depth: u32) -> Self { + BasicRecorder { + nodes: Vec::new(), + min_depth: depth, + } + } +} + +impl Recorder for BasicRecorder { + fn record(&mut self, hash: &H256, data: &[u8], depth: u32) { + debug_assert_eq!(data.sha3(), *hash); + + if depth >= self.min_depth { + self.nodes.push(Record { + depth: depth, + data: data.into(), + hash: *hash, + }) + } + } + + fn drain(&mut self) -> Vec { + ::std::mem::replace(&mut self.nodes, Vec::new()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use sha3::Hashable; + use ::H256; + + #[test] + fn no_op_does_nothing() { + let mut no_op = NoOp; + let (node1, node2) = (&[1], &[2]); + let (hash1, hash2) = (node1.sha3(), node2.sha3()); + no_op.record(&hash1, node1, 1); + no_op.record(&hash2, node2, 2); + + assert_eq!(no_op.drain(), Vec::new()); + } + + #[test] + fn basic_recorder() { + let mut basic = BasicRecorder::new(); + + let node1 = vec![1, 2, 3, 4]; + let node2 = vec![4, 5, 6, 7, 8, 9, 10]; + + let (hash1, hash2) = (node1.sha3(), node2.sha3()); + basic.record(&hash1, &node1, 0); + basic.record(&hash2, &node2, 456); + + let record1 = Record { + data: node1, + hash: hash1, + depth: 0, + }; + + let record2 = Record { + data: node2, + hash: hash2, + depth: 456 + }; + + assert_eq!(basic.drain(), vec![record1, record2]); + } + + #[test] + fn basic_recorder_min_depth() { + let mut basic = BasicRecorder::with_depth(400); + + let node1 = vec![1, 2, 3, 4]; + let node2 = vec![4, 5, 6, 7, 8, 9, 10]; + + let hash1 = node1.sha3(); + let hash2 = node2.sha3(); + basic.record(&hash1, &node1, 0); + basic.record(&hash2, &node2, 456); + + let records = basic.drain(); + + assert_eq!(records.len(), 1); + + assert_eq!(records[0].clone(), Record { + data: node2, + hash: hash2, + depth: 456, + }); + } + + #[test] + fn trie_record() { + use trie::{TrieDB, TrieDBMut, Trie, TrieMut}; + use memorydb::MemoryDB; + + let mut db = MemoryDB::new(); + + let mut root = H256::default(); + + { + let mut x = TrieDBMut::new(&mut db, &mut root); + + x.insert(b"dog", b"cat").unwrap(); + x.insert(b"lunch", b"time").unwrap(); + x.insert(b"notdog", b"notcat").unwrap(); + x.insert(b"hotdog", b"hotcat").unwrap(); + x.insert(b"letter", b"confusion").unwrap(); + x.insert(b"insert", b"remove").unwrap(); + x.insert(b"pirate", b"aargh!").unwrap(); + x.insert(b"yo ho ho", b"and a bottle of rum").unwrap(); + } + + let trie = TrieDB::new(&db, &root).unwrap(); + let mut recorder = BasicRecorder::new(); + + trie.get_recorded(b"pirate", &mut recorder).unwrap().unwrap(); + + let nodes: Vec<_> = recorder.drain().into_iter().map(|r| r.data).collect(); + assert_eq!(nodes, vec![ + vec![ + 248, 81, 128, 128, 128, 128, 128, 128, 160, 50, 19, 71, 57, 213, 63, 125, 149, + 92, 119, 88, 96, 80, 126, 59, 11, 160, 142, 98, 229, 237, 200, 231, 224, 79, 118, + 215, 93, 144, 246, 179, 176, 160, 118, 211, 171, 199, 172, 136, 136, 240, 221, 59, + 110, 82, 86, 54, 23, 95, 48, 108, 71, 125, 59, 51, 253, 210, 18, 116, 79, 0, 236, + 102, 142, 48, 128, 128, 128, 128, 128, 128, 128, 128, 128 + ], + vec![ + 248, 60, 206, 134, 32, 105, 114, 97, 116, 101, 134, 97, 97, 114, 103, 104, 33, + 128, 128, 128, 128, 128, 128, 128, 128, 221, 136, 32, 111, 32, 104, 111, 32, 104, + 111, 147, 97, 110, 100, 32, 97, 32, 98, 111, 116, 116, 108, 101, 32, 111, 102, + 32, 114, 117, 109, 128, 128, 128, 128, 128, 128, 128 + ] + ]); + + trie.get_recorded(b"letter", &mut recorder).unwrap().unwrap(); + + let nodes: Vec<_> = recorder.drain().into_iter().map(|r| r.data).collect(); + assert_eq!(nodes, vec![ + vec![ + 248, 81, 128, 128, 128, 128, 128, 128, 160, 50, 19, 71, 57, 213, 63, 125, 149, + 92, 119, 88, 96, 80, 126, 59, 11, 160, 142, 98, 229, 237, 200, 231, 224, 79, 118, + 215, 93, 144, 246, 179, 176, 160, 118, 211, 171, 199, 172, 136, 136, 240, 221, + 59, 110, 82, 86, 54, 23, 95, 48, 108, 71, 125, 59, 51, 253, 210, 18, 116, 79, + 0, 236, 102, 142, 48, 128, 128, 128, 128, 128, 128, 128, 128, 128 + ], + vec![ + 248, 99, 128, 128, 128, 128, 200, 131, 32, 111, 103, 131, 99, 97, 116, 128, 128, + 128, 206, 134, 32, 111, 116, 100, 111, 103, 134, 104, 111, 116, 99, 97, 116, 206, + 134, 32, 110, 115, 101, 114, 116, 134, 114, 101, 109, 111, 118, 101, 128, 128, + 160, 202, 250, 252, 153, 229, 63, 255, 13, 100, 197, 80, 120, 190, 186, 92, 5, + 255, 135, 245, 205, 180, 213, 161, 8, 47, 107, 13, 105, 218, 1, 9, 5, 128, + 206, 134, 32, 111, 116, 100, 111, 103, 134, 110, 111, 116, 99, 97, 116, 128, 128 + ], + vec![ + 235, 128, 128, 128, 128, 128, 128, 208, 133, 53, 116, 116, 101, 114, 137, 99, + 111, 110, 102, 117, 115, 105, 111, 110, 202, 132, 53, 110, 99, 104, 132, 116, + 105, 109, 101, 128, 128, 128, 128, 128, 128, 128, 128, 128 + ] + ]); + } +} \ No newline at end of file diff --git a/util/src/trie/sectriedb.rs b/util/src/trie/sectriedb.rs index 7869439a7..9e807884c 100644 --- a/util/src/trie/sectriedb.rs +++ b/util/src/trie/sectriedb.rs @@ -18,7 +18,7 @@ use hash::H256; use sha3::Hashable; use hashdb::HashDB; use super::triedb::TrieDB; -use super::{Trie, TrieItem}; +use super::{Trie, TrieItem, Recorder}; /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// @@ -59,10 +59,10 @@ impl<'db> Trie for SecTrieDB<'db> { self.raw.contains(&key.sha3()) } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result> - where 'a: 'key + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> + where 'a: 'b, R: Recorder { - self.raw.get(&key.sha3()) + self.raw.get_recorded(&key.sha3(), rec) } } diff --git a/util/src/trie/triedb.rs b/util/src/trie/triedb.rs index 33f2cc4fc..8e6cb98fa 100644 --- a/util/src/trie/triedb.rs +++ b/util/src/trie/triedb.rs @@ -19,6 +19,7 @@ use hashdb::*; use nibbleslice::*; use rlp::*; use super::node::Node; +use super::recorder::{Recorder, NoOp}; use super::{Trie, TrieItem, TrieError}; /// A `Trie` implementation using a generic `HashDB` backing database. @@ -79,7 +80,7 @@ impl<'db> TrieDB<'db> { pub fn keys(&self) -> super::Result> { let mut ret: Vec = Vec::new(); ret.push(self.root.clone()); - try!(self.accumulate_keys(try!(self.root_node()), &mut ret)); + try!(self.accumulate_keys(try!(self.root_node(&mut NoOp)), &mut ret)); Ok(ret) } @@ -114,7 +115,7 @@ impl<'db> TrieDB<'db> { acc.push(p.as_val()); } - self.accumulate_keys(try!(self.get_node(payload)), acc) + self.accumulate_keys(try!(self.get_node(payload, &mut NoOp, 0)), acc) }; match node { @@ -127,18 +128,19 @@ impl<'db> TrieDB<'db> { } /// Get the root node's RLP. - fn root_node(&self) -> super::Result { - self.root_data().map(Node::decoded) + fn root_node<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result { + self.root_data(r).map(Node::decoded) } /// Get the data of the root node. - fn root_data(&self) -> super::Result<&[u8]> { + fn root_data<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result<&[u8]> { self.db.get(self.root).ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) + .map(|node| { r.record(self.root, node, 0); node }) } /// Get the root node as a `Node`. - fn get_node(&'db self, node: &'db [u8]) -> super::Result { - self.get_raw_or_lookup(node).map(Node::decoded) + fn get_node<'a, R: 'a + Recorder>(&'db self, node: &'db [u8], r: &'a mut R, depth: u32) -> super::Result { + self.get_raw_or_lookup(node, r, depth).map(Node::decoded) } /// Indentation helper for `formal_all`. @@ -155,7 +157,7 @@ impl<'db> TrieDB<'db> { Node::Leaf(slice, value) => try!(writeln!(f, "'{:?}: {:?}.", slice, value.pretty())), Node::Extension(ref slice, item) => { try!(write!(f, "'{:?} ", slice)); - if let Ok(node) = self.get_node(item) { + if let Ok(node) = self.get_node(item, &mut NoOp, 0) { try!(self.fmt_all(node, f, deepness)); } }, @@ -166,7 +168,7 @@ impl<'db> TrieDB<'db> { try!(writeln!(f, "=: {:?}", v.pretty())) } for i in 0..16 { - match self.get_node(nodes[i]) { + match self.get_node(nodes[i], &mut NoOp, 0) { Ok(Node::Empty) => {}, Ok(n) => { try!(self.fmt_indent(f, deepness + 1)); @@ -188,29 +190,36 @@ impl<'db> TrieDB<'db> { } /// Return optional data for a key given as a `NibbleSlice`. Returns `None` if no data exists. - fn do_lookup<'key>(&'db self, key: &NibbleSlice<'key>) -> super::Result> - where 'db: 'key + fn do_lookup<'key, R: 'key>(&'db self, key: &NibbleSlice<'key>, r: &'key mut R) -> super::Result> + where 'db: 'key, R: Recorder { - let root_rlp = try!(self.root_data()); - self.get_from_node(root_rlp, key) + let root_rlp = try!(self.root_data(r)); + self.get_from_node(root_rlp, key, r, 1) } /// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no /// value exists for the key. /// /// Note: Not a public API; use Trie trait functions. - fn get_from_node<'key>(&'db self, node: &'db [u8], key: &NibbleSlice<'key>) -> super::Result> - where 'db: 'key - { + fn get_from_node<'key, R: 'key>( + &'db self, + node: &'db [u8], + key: &NibbleSlice<'key>, + r: &'key mut R, + d: u32 + ) -> super::Result> where 'db: 'key, R: Recorder { match Node::decoded(node) { Node::Leaf(ref slice, value) if key == slice => Ok(Some(value)), Node::Extension(ref slice, item) if key.starts_with(slice) => { - let data = try!(self.get_raw_or_lookup(item)); - self.get_from_node(data, &key.mid(slice.len())) + let data = try!(self.get_raw_or_lookup(item, r, d)); + self.get_from_node(data, &key.mid(slice.len()), r, d + 1) }, Node::Branch(ref nodes, value) => match key.is_empty() { true => Ok(value), - false => self.get_from_node(try!(self.get_raw_or_lookup(nodes[key.at(0) as usize])), &key.mid(1)) + false => { + let node = try!(self.get_raw_or_lookup(nodes[key.at(0) as usize], r, d)); + self.get_from_node(node, &key.mid(1), r, d + 1) + } }, _ => Ok(None) } @@ -219,13 +228,14 @@ impl<'db> TrieDB<'db> { /// Given some node-describing data `node`, return the actual node RLP. /// This could be a simple identity operation in the case that the node is sufficiently small, but /// may require a database lookup. - fn get_raw_or_lookup(&'db self, node: &'db [u8]) -> super::Result<&'db [u8]> { + fn get_raw_or_lookup(&'db self, node: &'db [u8], rec: &mut R, d: u32) -> super::Result<&'db [u8]> { // check if its sha3 + len let r = Rlp::new(node); match r.is_data() && r.size() == 32 { true => { let key = r.as_val::(); self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key))) + .map(|raw| { rec.record(&key, raw, d); raw }) } false => Ok(node) } @@ -275,7 +285,7 @@ impl<'a> TrieDBIterator<'a> { trail: vec![], key_nibbles: Vec::new(), }; - r.descend(db.root_data().unwrap()); + r.descend(db.root_data(&mut NoOp).unwrap()); r } @@ -283,7 +293,7 @@ impl<'a> TrieDBIterator<'a> { fn descend(&mut self, d: &'a [u8]) { self.trail.push(Crumb { status: Status::Entering, - node: self.db.get_node(d).unwrap(), + node: self.db.get_node(d, &mut NoOp, 0).unwrap(), }); match self.trail.last().unwrap().node { Node::Leaf(n, _) | Node::Extension(n, _) => { self.key_nibbles.extend(n.iter()); }, @@ -341,24 +351,17 @@ impl<'a> Iterator for TrieDBIterator<'a> { } } -impl<'db> TrieDB<'db> { - /// Get all keys/values stored in the trie. - pub fn iter(&self) -> TrieDBIterator { - TrieDBIterator::new(self) - } -} - impl<'db> Trie for TrieDB<'db> { fn iter<'a>(&'a self) -> Box + 'a> { - Box::new(TrieDB::iter(self)) + Box::new(TrieDBIterator::new(self)) } fn root(&self) -> &H256 { self.root } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result> - where 'a: 'key + fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result> + where 'a: 'b, R: Recorder { - self.do_lookup(&NibbleSlice::new(key)) + self.do_lookup(&NibbleSlice::new(key), rec) } } @@ -387,6 +390,8 @@ fn iterator() { t.insert(x, x).unwrap(); } } - assert_eq!(d.iter().map(|i|i.to_vec()).collect::>(), TrieDB::new(&memdb, &root).unwrap().iter().map(|x|x.0).collect::>()); - assert_eq!(d, TrieDB::new(&memdb, &root).unwrap().iter().map(|x|x.1).collect::>()); + + let t = TrieDB::new(&memdb, &root).unwrap(); + assert_eq!(d.iter().map(|i|i.to_vec()).collect::>(), t.iter().map(|x|x.0).collect::>()); + assert_eq!(d, t.iter().map(|x|x.1).collect::>()); }