Trie query recording and AccountDB factory for no mangling (#1944)

* optionally use no mangling for accountdb

* add the recorder module

* get_recorded for tries, no virtual dispatch on readonly tries

* add recording test
This commit is contained in:
Robert Habermeier 2016-08-24 16:53:36 +02:00 committed by Gav Wood
parent 33e0a234f2
commit 190e4db266
24 changed files with 590 additions and 182 deletions

View File

@ -35,6 +35,38 @@ fn combine_key<'a>(address_hash: &'a H256, key: &'a H256) -> H256 {
dst 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<HashDB + 'db> {
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<HashDB + 'db> {
match *self {
Factory::Mangled => Box::new(AccountDBMut::from_hash(db, address_hash)),
Factory::Plain => Box::new(WrappingMut(db)),
}
}
}
// TODO: introduce HashDBMut? // TODO: introduce HashDBMut?
/// DB backend wrapper for Account trie /// DB backend wrapper for Account trie
/// Transforms trie node keys for the database /// 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<H256, i32> {
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<H256, i32> {
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)
}
}

View File

@ -21,7 +21,7 @@ use engines::Engine;
use state::*; use state::*;
use verification::PreverifiedBlock; use verification::PreverifiedBlock;
use trace::FlatTrace; use trace::FlatTrace;
use evm::Factory as EvmFactory; use factory::Factories;
/// A block, encoded as it is on the block chain. /// A block, encoded as it is on the block chain.
#[derive(Default, Debug, Clone, PartialEq)] #[derive(Default, Debug, Clone, PartialEq)]
@ -192,7 +192,6 @@ impl IsBlock for ExecutedBlock {
pub struct OpenBlock<'x> { pub struct OpenBlock<'x> {
block: ExecutedBlock, block: ExecutedBlock,
engine: &'x Engine, engine: &'x Engine,
vm_factory: &'x EvmFactory,
last_hashes: Arc<LastHashes>, last_hashes: Arc<LastHashes>,
} }
@ -230,8 +229,7 @@ impl<'x> OpenBlock<'x> {
/// Create a new `OpenBlock` ready for transaction pushing. /// Create a new `OpenBlock` ready for transaction pushing.
pub fn new( pub fn new(
engine: &'x Engine, engine: &'x Engine,
vm_factory: &'x EvmFactory, factories: Factories,
trie_factory: TrieFactory,
tracing: bool, tracing: bool,
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
@ -240,11 +238,10 @@ impl<'x> OpenBlock<'x> {
gas_range_target: (U256, U256), gas_range_target: (U256, U256),
extra_data: Bytes, extra_data: Bytes,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
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 { let mut r = OpenBlock {
block: ExecutedBlock::new(state, tracing), block: ExecutedBlock::new(state, tracing),
engine: engine, engine: engine,
vm_factory: vm_factory,
last_hashes: last_hashes, last_hashes: last_hashes,
}; };
@ -332,7 +329,7 @@ impl<'x> OpenBlock<'x> {
let env_info = self.env_info(); let env_info = self.env_info();
// info!("env_info says gas_used={}", env_info.gas_used); // 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) => { Ok(outcome) => {
self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); self.block.transactions_set.insert(h.unwrap_or_else(||t.hash()));
self.block.base.transactions.push(t); self.block.base.transactions.push(t);
@ -421,14 +418,13 @@ impl ClosedBlock {
} }
/// Given an engine reference, reopen the `ClosedBlock` into an `OpenBlock`. /// 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). // revert rewards (i.e. set state back at last transaction's state).
let mut block = self.block; let mut block = self.block;
block.state = self.unclosed_state; block.state = self.unclosed_state;
OpenBlock { OpenBlock {
block: block, block: block,
engine: engine, engine: engine,
vm_factory: vm_factory,
last_hashes: self.last_hashes, last_hashes: self.last_hashes,
} }
} }
@ -499,17 +495,16 @@ pub fn enact(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: Arc<LastHashes>, last_hashes: Arc<LastHashes>,
vm_factory: &EvmFactory, factories: Factories,
trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
{ {
if ::log::max_log_level() >= ::log::LogLevel::Trace { 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())); 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_difficulty(*header.difficulty());
b.set_gas_limit(*header.gas_limit()); b.set_gas_limit(*header.gas_limit());
b.set_timestamp(header.timestamp()); b.set_timestamp(header.timestamp());
@ -532,12 +527,11 @@ pub fn enact_bytes(
db: Box<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: Arc<LastHashes>, last_hashes: Arc<LastHashes>,
vm_factory: &EvmFactory, factories: Factories,
trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
let block = BlockView::new(block_bytes); let block = BlockView::new(block_bytes);
let header = block.header(); 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 /// 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<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: Arc<LastHashes>, last_hashes: Arc<LastHashes>,
vm_factory: &EvmFactory, factories: Factories,
trie_factory: TrieFactory,
) -> Result<LockedBlock, Error> { ) -> Result<LockedBlock, Error> {
let view = BlockView::new(&block.bytes); 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 /// 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<JournalDB>, db: Box<JournalDB>,
parent: &Header, parent: &Header,
last_hashes: Arc<LastHashes>, last_hashes: Arc<LastHashes>,
vm_factory: &EvmFactory, factories: Factories,
trie_factory: TrieFactory,
) -> Result<SealedBlock, Error> { ) -> Result<SealedBlock, Error> {
let header = BlockView::new(block_bytes).header_view(); 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)] #[cfg(test)]
@ -587,8 +579,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]); let last_hashes = Arc::new(vec![genesis_header.hash()]);
let vm_factory = Default::default(); let b = OpenBlock::new(&*spec.engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
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 = b.close_and_lock(); let b = b.close_and_lock();
let _ = b.seal(&*spec.engine, vec![]); let _ = b.seal(&*spec.engine, vec![]);
} }
@ -603,9 +594,8 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let vm_factory = Default::default();
let last_hashes = Arc::new(vec![genesis_header.hash()]); 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(); .close_and_lock().seal(engine, vec![]).unwrap();
let orig_bytes = b.rlp_bytes(); let orig_bytes = b.rlp_bytes();
let orig_db = b.drain(); let orig_db = b.drain();
@ -613,7 +603,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); 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); assert_eq!(e.rlp_bytes(), orig_bytes);
@ -632,9 +622,8 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let vm_factory = Default::default();
let last_hashes = Arc::new(vec![genesis_header.hash()]); 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(); let mut uncle1_header = Header::new();
uncle1_header.extra_data = b"uncle1".to_vec(); uncle1_header.extra_data = b"uncle1".to_vec();
let mut uncle2_header = Header::new(); let mut uncle2_header = Header::new();
@ -649,7 +638,7 @@ mod tests {
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); 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(); let bytes = e.rlp_bytes();
assert_eq!(bytes, orig_bytes); assert_eq!(bytes, orig_bytes);

View File

@ -65,6 +65,7 @@ use evm::Factory as EvmFactory;
use miner::{Miner, MinerService}; use miner::{Miner, MinerService};
use util::TrieFactory; use util::TrieFactory;
use snapshot::{self, io as snapshot_io}; use snapshot::{self, io as snapshot_io};
use factory::Factories;
// re-export // re-export
pub use types::blockchain_info::BlockChainInfo; pub use types::blockchain_info::BlockChainInfo;
@ -130,8 +131,6 @@ pub struct Client {
import_lock: Mutex<()>, import_lock: Mutex<()>,
panic_handler: Arc<PanicHandler>, panic_handler: Arc<PanicHandler>,
verifier: Box<Verifier>, verifier: Box<Verifier>,
vm_factory: Arc<EvmFactory>,
trie_factory: TrieFactory,
miner: Arc<Miner>, miner: Arc<Miner>,
sleep_state: Mutex<SleepState>, sleep_state: Mutex<SleepState>,
liveness: AtomicBool, liveness: AtomicBool,
@ -139,6 +138,7 @@ pub struct Client {
notify: RwLock<Vec<Weak<ChainNotify>>>, notify: RwLock<Vec<Weak<ChainNotify>>>,
queue_transactions: AtomicUsize, queue_transactions: AtomicUsize,
last_hashes: RwLock<VecDeque<H256>>, last_hashes: RwLock<VecDeque<H256>>,
factories: Factories,
} }
const HISTORY: u64 = 1200; const HISTORY: u64 = 1200;
@ -188,6 +188,13 @@ impl Client {
panic_handler.forward_from(&block_queue); panic_handler.forward_from(&block_queue);
let awake = match config.mode { Mode::Dark(..) => false, _ => true }; 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 { let client = Client {
sleep_state: Mutex::new(SleepState::new(awake)), sleep_state: Mutex::new(SleepState::new(awake)),
liveness: AtomicBool::new(awake), liveness: AtomicBool::new(awake),
@ -202,13 +209,12 @@ impl Client {
import_lock: Mutex::new(()), import_lock: Mutex::new(()),
panic_handler: panic_handler, panic_handler: panic_handler,
verifier: verification::new(config.verifier_type), verifier: verification::new(config.verifier_type),
vm_factory: Arc::new(EvmFactory::new(config.vm_type)),
trie_factory: TrieFactory::new(config.trie_spec),
miner: miner, miner: miner,
io_channel: message_channel, io_channel: message_channel,
notify: RwLock::new(Vec::new()), notify: RwLock::new(Vec::new()),
queue_transactions: AtomicUsize::new(0), queue_transactions: AtomicUsize::new(0),
last_hashes: RwLock::new(VecDeque::new()), last_hashes: RwLock::new(VecDeque::new()),
factories: factories,
}; };
Ok(Arc::new(client)) Ok(Arc::new(client))
} }
@ -289,7 +295,7 @@ impl Client {
let last_hashes = self.build_last_hashes(header.parent_hash.clone()); let last_hashes = self.build_last_hashes(header.parent_hash.clone());
let db = self.state_db.lock().boxed_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 { if let Err(e) = enact_result {
warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e); warn!(target: "client", "Block import failed for #{} ({})\nError: {:?}", header.number(), header.hash(), e);
return Err(()); return Err(());
@ -493,7 +499,7 @@ impl Client {
let root = HeaderView::new(&header).state_root(); 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(), self.state_db.lock().boxed_clone(),
HeaderView::new(&self.best_block_header()).state_root(), HeaderView::new(&self.best_block_header()).state_root(),
self.engine.account_start_nonce(), self.engine.account_start_nonce(),
self.trie_factory.clone()) self.factories.clone())
.expect("State root of best block header always valid.") .expect("State root of best block header always valid.")
} }
@ -688,7 +694,7 @@ impl BlockChainClient for Client {
state.add_balance(&sender, &(needed_balance - balance)); state.add_balance(&sender, &(needed_balance - balance));
} }
let options = TransactOptions { tracing: analytics.transaction_tracing, vm_tracing: analytics.vm_tracing, check_nonce: false }; 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. // TODO gav move this into Executive.
ret.state_diff = original_state.map(|original| state.diff_from(original)); ret.state_diff = original_state.map(|original| state.diff_from(original));
@ -720,7 +726,7 @@ impl BlockChainClient for Client {
gas_limit: view.gas_limit(), gas_limit: view.gas_limit(),
}; };
for t in txs.iter().take(address.index) { 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; } Ok(x) => { env_info.gas_used = env_info.gas_used + x.gas_used; }
Err(ee) => { return Err(CallError::Execution(ee)) } Err(ee) => { return Err(CallError::Execution(ee)) }
} }
@ -728,7 +734,7 @@ impl BlockChainClient for Client {
let t = &txs[address.index]; let t = &txs[address.index];
let original_state = if analytics.state_diffing { Some(state.clone()) } else { None }; 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)); ret.state_diff = original_state.map(|original| state.diff_from(original));
Ok(ret) Ok(ret)
@ -1029,8 +1035,7 @@ impl MiningBlockChainClient for Client {
let mut open_block = OpenBlock::new( let mut open_block = OpenBlock::new(
engine, engine,
&self.vm_factory, self.factories.clone(),
self.trie_factory.clone(),
false, // TODO: this will need to be parameterised once we want to do immediate mining insertion. false, // TODO: this will need to be parameterised once we want to do immediate mining insertion.
self.state_db.lock().boxed_clone(), self.state_db.lock().boxed_clone(),
&self.chain.block_header(&h).expect("h is best block hash: so its header must exist: qed"), &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 { fn vm_factory(&self) -> &EvmFactory {
&self.vm_factory &self.factories.vm
} }
fn import_sealed_block(&self, block: SealedBlock) -> ImportResult { fn import_sealed_block(&self, block: SealedBlock) -> ImportResult {

View File

@ -276,7 +276,6 @@ impl MiningBlockChainClient for TestBlockChainClient {
let last_hashes = vec![genesis_header.hash()]; let last_hashes = vec![genesis_header.hash()];
let mut open_block = OpenBlock::new( let mut open_block = OpenBlock::new(
engine, engine,
self.vm_factory(),
Default::default(), Default::default(),
false, false,
db, db,

View File

@ -252,8 +252,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]); let last_hashes = Arc::new(vec![genesis_header.hash()]);
let vm_factory = Default::default(); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock(); let b = b.close_and_lock();
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();
assert!(b.try_seal(engine, seal).is_ok()); assert!(b.try_seal(engine, seal).is_ok());

View File

@ -86,8 +86,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]); let last_hashes = Arc::new(vec![genesis_header.hash()]);
let vm_factory = Default::default(); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = OpenBlock::new(engine, &vm_factory, Default::default(), false, db, &genesis_header, last_hashes, addr, (3141562.into(), 31415620.into()), vec![]).unwrap();
let b = b.close_and_lock(); let b = b.close_and_lock();
// Seal with empty AccountProvider. // Seal with empty AccountProvider.
let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap(); let seal = engine.generate_seal(b.block(), Some(&tap)).unwrap();

View File

@ -357,8 +357,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]); let last_hashes = Arc::new(vec![genesis_header.hash()]);
let vm_factory = Default::default(); let b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
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 = b.close(); let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap()); assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
} }
@ -372,8 +371,7 @@ mod tests {
let mut db = db_result.take(); let mut db = db_result.take();
spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let last_hashes = Arc::new(vec![genesis_header.hash()]); let last_hashes = Arc::new(vec![genesis_header.hash()]);
let vm_factory = Default::default(); let mut b = OpenBlock::new(engine, Default::default(), false, db, &genesis_header, last_hashes, Address::zero(), (3141562.into(), 31415620.into()), vec![]).unwrap();
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 uncle = Header::new(); let mut uncle = Header::new();
let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into(); let uncle_author: Address = "ef2d6d194084c2de36e0dabfce45d046b37d1106".into();
uncle.author = uncle_author.clone(); uncle.author = uncle_author.clone();

View File

@ -80,6 +80,7 @@ impl VMType {
} }
/// Evm factory. Creates appropriate Evm. /// Evm factory. Creates appropriate Evm.
#[derive(Clone)]
pub struct Factory { pub struct Factory {
evm: VMType evm: VMType
} }
@ -128,7 +129,7 @@ impl Factory {
impl Default for Factory { impl Default for Factory {
/// Returns jitvm factory /// Returns jitvm factory
#[cfg(feature = "jit")] #[cfg(all(feature = "jit", not(test)))]
fn default() -> Factory { fn default() -> Factory {
Factory { Factory {
evm: VMType::Jit evm: VMType::Jit
@ -136,7 +137,7 @@ impl Default for Factory {
} }
/// Returns native rust evm factory /// Returns native rust evm factory
#[cfg(not(feature = "jit"))] #[cfg(any(not(feature = "jit"), test))]
fn default() -> Factory { fn default() -> Factory {
Factory { Factory {
evm: VMType::Interpreter evm: VMType::Interpreter

30
ethcore/src/factory.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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,
}

View File

@ -64,8 +64,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
state.populate_from(pre); state.populate_from(pre);
state.commit() state.commit()
.expect(&format!("State test {} failed due to internal error.", name)); .expect(&format!("State test {} failed due to internal error.", name));
let vm_factory = Default::default(); let res = state.apply(&env, &*engine, &transaction, false);
let res = state.apply(&env, &*engine, &vm_factory, &transaction, false);
if fail_unless(state.root() == &post_state_root) { if fail_unless(state.root() == &post_state_root) {
println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root); println!("!!! {}: State mismatch (got: {}, expect: {}):", name, state.root(), post_state_root);

View File

@ -139,6 +139,7 @@ mod externalities;
mod verification; mod verification;
mod blockchain; mod blockchain;
mod types; mod types;
mod factory;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

View File

@ -270,7 +270,7 @@ impl Miner {
Some(old_block) => { Some(old_block) => {
trace!(target: "miner", "Already have previous work; updating and returning"); trace!(target: "miner", "Already have previous work; updating and returning");
// add transactions to old_block // add transactions to old_block
old_block.reopen(&*self.engine, chain.vm_factory()) old_block.reopen(&*self.engine)
} }
None => { None => {
// block not found - create it. // block not found - create it.

View File

@ -17,8 +17,9 @@
//! Account state encoding and decoding //! Account state encoding and decoding
use account_db::{AccountDB, AccountDBMut}; 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::rlp::{Rlp, RlpStream, Stream, UntrustedRlp, View};
use util::trie::{TrieDB, Trie};
use snapshot::Error; use snapshot::Error;
// An alternate account structure from ::account::Account. // An alternate account structure from ::account::Account.

View File

@ -26,13 +26,14 @@ use engines::Engine;
use ids::BlockID; use ids::BlockID;
use views::BlockView; use views::BlockView;
use util::{Bytes, Hashable, HashDB, snappy, TrieDB, TrieDBMut, TrieMut}; use util::{Bytes, Hashable, HashDB, snappy};
use util::Mutex; use util::Mutex;
use util::hash::{FixedHash, H256}; use util::hash::{FixedHash, H256};
use util::journaldb::{self, Algorithm, JournalDB}; use util::journaldb::{self, Algorithm, JournalDB};
use util::kvdb::Database; use util::kvdb::Database;
use util::rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View, Compressible, RlpType}; use util::rlp::{DecoderError, RlpStream, Stream, UntrustedRlp, View, Compressible, RlpType};
use util::rlp::SHA3_NULL_RLP; use util::rlp::SHA3_NULL_RLP;
use util::trie::{TrieDB, TrieDBMut, Trie, TrieMut};
use self::account::Account; use self::account::Account;
use self::block::AbridgedBlock; use self::block::AbridgedBlock;

View File

@ -24,7 +24,7 @@ use snapshot::account::Account;
use util::hash::{FixedHash, H256}; use util::hash::{FixedHash, H256};
use util::hashdb::HashDB; use util::hashdb::HashDB;
use util::trie::{Alphabet, StandardMap, SecTrieDBMut, TrieMut, ValueMode}; 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; use util::rlp::SHA3_NULL_RLP;
// the proportion of accounts we will alter each tick. // the proportion of accounts we will alter each tick.
@ -51,10 +51,12 @@ impl StateProducer {
// modify existing accounts. // modify existing accounts.
let mut accounts_to_modify: Vec<_> = { let mut accounts_to_modify: Vec<_> = {
let trie = TrieDB::new(&*db, &self.state_root).unwrap(); 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::<f32>() < ACCOUNT_CHURN) .filter(|_| rng.gen::<f32>() < ACCOUNT_CHURN)
.map(|(k, v)| (H256::from_slice(&k), v.to_owned())) .map(|(k, v)| (H256::from_slice(&k), v.to_owned()))
.collect() .collect();
temp
}; };
// sweep once to alter storage tries. // sweep once to alter storage tries.

View File

@ -19,7 +19,6 @@
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use util::*; use util::*;
use pod_account::*; use pod_account::*;
use account_db::*;
use std::cell::{Ref, RefCell, Cell}; use std::cell::{Ref, RefCell, Cell};
@ -148,7 +147,7 @@ impl Account {
} }
/// Get (and cache) the contents of the trie's storage at `key`. /// 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(||{ self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{
let db = SecTrieDB::new(db, &self.storage_root) let db = SecTrieDB::new(db, &self.storage_root)
.expect("Account storage_root initially set to zero (valid) and only altered by SecTrieDBMut. \ .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. /// 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; // 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()); trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
self.is_cached() || self.is_cached() ||
@ -277,7 +276,7 @@ impl Account {
} }
/// Commit the `storage_overlay` to the backing DB and update `storage_root`. /// 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) 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. \ .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 \ 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. /// 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()); 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()) { match (self.code_hash.is_none(), self.code_cache.is_empty()) {
(true, true) => self.code_hash = Some(SHA3_EMPTY), (true, true) => self.code_hash = Some(SHA3_EMPTY),

View File

@ -18,8 +18,7 @@ use std::cell::{RefCell, RefMut};
use common::*; use common::*;
use engines::Engine; use engines::Engine;
use executive::{Executive, TransactOptions}; use executive::{Executive, TransactOptions};
use evm::Factory as EvmFactory; use factory::Factories;
use account_db::*;
use trace::FlatTrace; use trace::FlatTrace;
use pod_account::*; use pod_account::*;
use pod_state::{self, PodState}; use pod_state::{self, PodState};
@ -49,7 +48,7 @@ pub struct State {
cache: RefCell<HashMap<Address, Option<Account>>>, cache: RefCell<HashMap<Address, Option<Account>>>,
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>, snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
account_start_nonce: U256, 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. \ 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 { impl State {
/// Creates new state with empty state root /// Creates new state with empty state root
#[cfg(test)] #[cfg(test)]
pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256, trie_factory: TrieFactory) -> State { pub fn new(mut db: Box<JournalDB>, account_start_nonce: U256, factories: Factories) -> State {
let mut root = H256::new(); let mut root = H256::new();
{ {
// init trie and reset root too null // 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 { State {
@ -71,12 +70,12 @@ impl State {
cache: RefCell::new(HashMap::new()), cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()), snapshots: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce, account_start_nonce: account_start_nonce,
trie_factory: trie_factory, factories: factories,
} }
} }
/// Creates new state with existing state root /// Creates new state with existing state root
pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256, trie_factory: TrieFactory) -> Result<State, TrieError> { pub fn from_existing(db: Box<JournalDB>, root: H256, account_start_nonce: U256, factories: Factories) -> Result<State, TrieError> {
if !db.as_hashdb().contains(&root) { if !db.as_hashdb().contains(&root) {
return Err(TrieError::InvalidStateRoot(root)); return Err(TrieError::InvalidStateRoot(root));
} }
@ -87,7 +86,7 @@ impl State {
cache: RefCell::new(HashMap::new()), cache: RefCell::new(HashMap::new()),
snapshots: RefCell::new(Vec::new()), snapshots: RefCell::new(Vec::new()),
account_start_nonce: account_start_nonce, account_start_nonce: account_start_nonce,
trie_factory: trie_factory, factories: factories
}; };
Ok(state) Ok(state)
@ -185,8 +184,11 @@ impl State {
/// Mutate storage of account `address` so that it is `value` for `key`. /// Mutate storage of account `address` so that it is `value` for `key`.
pub fn storage_at(&self, address: &Address, key: &H256) -> H256 { pub fn storage_at(&self, address: &Address, key: &H256) -> H256 {
self.ensure_cached(address, false, self.ensure_cached(address, false, |a| a.as_ref().map_or(H256::new(), |a| {
|a| a.as_ref().map_or(H256::new(), |a|a.storage_at(&AccountDB::from_hash(self.db.as_hashdb(), a.address_hash(address)), key))) 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`. /// Mutate storage of account `a` so that it is `value` for `key`.
@ -236,11 +238,12 @@ impl State {
/// Execute a given transaction. /// Execute a given transaction.
/// This will change the state accordingly. /// 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 old = self.to_pod();
let options = TransactOptions { tracing: tracing, vm_tracing: false, check_nonce: true }; 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. // TODO uncomment once to_pod() works correctly.
// trace!("Applied transaction. Diff:\n{}\n", state_diff::diff_pod(&old, &self.to_pod())); // 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. /// `accounts` is mutable because we may need to commit the code or storage and record that.
#[cfg_attr(feature="dev", allow(match_ref_pats))] #[cfg_attr(feature="dev", allow(match_ref_pats))]
pub fn commit_into( pub fn commit_into(
trie_factory: &TrieFactory, factories: &Factories,
db: &mut HashDB, db: &mut HashDB,
root: &mut H256, root: &mut H256,
accounts: &mut HashMap<Address, accounts: &mut HashMap<Address, Option<Account>>
Option<Account>>
) -> Result<(), Error> { ) -> Result<(), Error> {
// first, commit the sub trees. // first, commit the sub trees.
// TODO: is this necessary or can we dispense with the `ref mut a` for just `a`? // 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() { for (address, ref mut a) in accounts.iter_mut() {
match a { match a {
&mut&mut Some(ref mut account) if account.is_dirty() => { &mut&mut Some(ref mut account) if account.is_dirty() => {
let mut account_db = AccountDBMut::from_hash(db, account.address_hash(address)); let addr_hash = account.address_hash(address);
account.commit_storage(trie_factory, &mut account_db); let mut account_db = factories.accountdb.create(db, addr_hash);
account.commit_code(&mut account_db); 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() { for (address, ref mut a) in accounts.iter_mut() {
match **a { match **a {
Some(ref mut account) if account.is_dirty() => { Some(ref mut account) if account.is_dirty() => {
@ -293,7 +296,7 @@ impl State {
/// Commits our cached account changes into the trie. /// Commits our cached account changes into the trie.
pub fn commit(&mut self) -> Result<(), Error> { pub fn commit(&mut self) -> Result<(), Error> {
assert!(self.snapshots.borrow().is_empty()); 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 /// Clear state cache
@ -351,7 +354,7 @@ impl State {
where F: FnOnce(&Option<Account>) -> U { where F: FnOnce(&Option<Account>) -> U {
let have_key = self.cache.borrow().contains_key(a); let have_key = self.cache.borrow().contains_key(a);
if !have_key { 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) { let maybe_acc = match db.get(a) {
Ok(acc) => acc.map(Account::from_rlp), Ok(acc) => acc.map(Account::from_rlp),
Err(e) => panic!("Potential DB corruption encountered: {}", e), Err(e) => panic!("Potential DB corruption encountered: {}", e),
@ -361,7 +364,8 @@ impl State {
if require_code { if require_code {
if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() { if let Some(ref mut account) = self.cache.borrow_mut().get_mut(a).unwrap().as_mut() {
let addr_hash = account.address_hash(a); 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); let contains_key = self.cache.borrow().contains_key(a);
if !contains_key { 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) { let maybe_acc = match db.get(a) {
Ok(acc) => acc.map(Account::from_rlp), Ok(acc) => acc.map(Account::from_rlp),
Err(e) => panic!("Potential DB corruption encountered: {}", e), Err(e) => panic!("Potential DB corruption encountered: {}", e),
@ -400,7 +404,8 @@ impl State {
let account = c.get_mut(a).unwrap().as_mut().unwrap(); let account = c.get_mut(a).unwrap().as_mut().unwrap();
if require_code { if require_code {
let addr_hash = account.address_hash(a); 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 account
}) })
@ -421,7 +426,7 @@ impl Clone for State {
cache: RefCell::new(self.cache.borrow().clone()), cache: RefCell::new(self.cache.borrow().clone()),
snapshots: RefCell::new(self.snapshots.borrow().clone()), snapshots: RefCell::new(self.snapshots.borrow().clone()),
account_start_nonce: self.account_start_nonce.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()); }.sign(&"".sha3());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
subtraces: 0, subtraces: 0,
@ -525,8 +529,7 @@ fn should_trace_failed_create_transaction() {
}.sign(&"".sha3()); }.sign(&"".sha3());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
action: trace::Action::Create(trace::Create { 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.init_code(&0xa.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
@ -607,8 +609,7 @@ fn should_trace_basic_call_transaction() {
}.sign(&"".sha3()); }.sign(&"".sha3());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
action: trace::Action::Call(trace::Call { action: trace::Action::Call(trace::Call {
@ -649,8 +650,7 @@ fn should_trace_call_transaction_to_builtin() {
data: vec![], data: vec![],
}.sign(&"".sha3()); }.sign(&"".sha3());
let vm_factory = Default::default(); let result = state.apply(&info, engine, &t, true).unwrap();
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -693,8 +693,7 @@ fn should_not_trace_subcall_transaction_to_builtin() {
}.sign(&"".sha3()); }.sign(&"".sha3());
state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("600060006000600060006001610be0f1").unwrap());
let vm_factory = Default::default(); let result = state.apply(&info, engine, &t, true).unwrap();
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), 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(&0xa.into(), FromHex::from_hex("60006000600060006000600b611000f2").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
let vm_factory = Default::default(); let result = state.apply(&info, engine, &t, true).unwrap();
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), 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(&0xa.into(), FromHex::from_hex("6000600060006000600b618000f4").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
let vm_factory = Default::default(); let result = state.apply(&info, engine, &t, true).unwrap();
let result = state.apply(&info, engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), 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.init_code(&0xa.into(), FromHex::from_hex("5b600056").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
action: trace::Action::Call(trace::Call { 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(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), 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.init_code(&0xa.into(), FromHex::from_hex("60006000600060006045600b6000f1").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
subtraces: 1, 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.init_code(&0xa.into(), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds.
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
subtraces: 0, 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(&0xa.into(), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap());
state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap()); state.init_code(&0xb.into(), FromHex::from_hex("5b600056").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
subtraces: 1, 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(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap());
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
subtraces: 1, 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(&0xb.into(), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap());
state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap()); state.init_code(&0xc.into(), FromHex::from_hex("6000").unwrap());
state.add_balance(t.sender().as_ref().unwrap(), &(100.into())); state.add_balance(t.sender().as_ref().unwrap(), &(100.into()));
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
@ -1271,8 +1261,7 @@ fn should_trace_suicide() {
state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()); state.init_code(&0xa.into(), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap());
state.add_balance(&0xa.into(), &50.into()); state.add_balance(&0xa.into(), &50.into());
state.add_balance(t.sender().as_ref().unwrap(), &100.into()); state.add_balance(t.sender().as_ref().unwrap(), &100.into());
let vm_factory = Default::default(); let result = state.apply(&info, &engine, &t, true).unwrap();
let result = state.apply(&info, &engine, &vm_factory, &t, true).unwrap();
let expected_trace = vec![FlatTrace { let expected_trace = vec![FlatTrace {
trace_address: Default::default(), trace_address: Default::default(),
subtraces: 1, subtraces: 1,

View File

@ -139,7 +139,6 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
let mut db_result = get_temp_journal_db(); let mut db_result = get_temp_journal_db();
let mut db = db_result.take(); let mut db = db_result.take();
test_spec.ensure_db_good(db.as_hashdb_mut()).unwrap(); test_spec.ensure_db_good(db.as_hashdb_mut()).unwrap();
let vm_factory = Default::default();
let genesis_header = test_spec.genesis_header(); let genesis_header = test_spec.genesis_header();
let mut rolling_timestamp = 40; let mut rolling_timestamp = 40;
@ -156,7 +155,6 @@ pub fn generate_dummy_client_with_spec_and_data<F>(get_test_spec: F, block_numbe
// forge block. // forge block.
let mut b = OpenBlock::new( let mut b = OpenBlock::new(
test_engine, test_engine,
&vm_factory,
Default::default(), Default::default(),
false, false,
db, db,

View File

@ -17,7 +17,7 @@
use hash::H256; use hash::H256;
use sha3::Hashable; use sha3::Hashable;
use hashdb::HashDB; 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. /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database.
/// Additionaly it stores inserted hash-key mappings for later retrieval. /// Additionaly it stores inserted hash-key mappings for later retrieval.
@ -43,16 +43,11 @@ impl<'db> FatDB<'db> {
pub fn db(&self) -> &HashDB { pub fn db(&self) -> &HashDB {
self.raw.db() 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> { impl<'db> Trie for FatDB<'db> {
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> { fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> {
Box::new(FatDB::iter(self)) Box::new(FatDBIterator::new(&self.raw))
} }
fn root(&self) -> &H256 { fn root(&self) -> &H256 {
@ -63,10 +58,10 @@ impl<'db> Trie for FatDB<'db> {
self.raw.contains(&key.sha3()) self.raw.contains(&key.sha3())
} }
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<&'a [u8]>> fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result<Option<&'a [u8]>>
where 'a: 'key where 'a: 'b, R: Recorder
{ {
self.raw.get(&key.sha3()) self.raw.get_recorded(&key.sha3(), rec)
} }
} }

View File

@ -34,6 +34,9 @@ pub mod triedbmut;
pub mod sectriedb; pub mod sectriedb;
/// Export the sectriedbmut module. /// Export the sectriedbmut module.
pub mod sectriedbmut; pub mod sectriedbmut;
/// Trie query recording.
pub mod recorder;
mod fatdb; mod fatdb;
mod fatdbmut; mod fatdbmut;
@ -45,6 +48,7 @@ pub use self::sectriedbmut::SecTrieDBMut;
pub use self::sectriedb::SecTrieDB; pub use self::sectriedb::SecTrieDB;
pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdb::{FatDB, FatDBIterator};
pub use self::fatdbmut::FatDBMut; pub use self::fatdbmut::FatDBMut;
pub use self::recorder::Recorder;
/// Trie Errors. /// Trie Errors.
/// ///
@ -88,7 +92,14 @@ pub trait Trie {
} }
/// What is the value of the given key in this trie? /// What is the value of the given key in this trie?
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<&'a [u8]>> where 'a: 'key; fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result<Option<&'a [u8]>> 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<Option<&'a [u8]>>
where 'a: 'b, R: Recorder;
/// Returns an iterator over elements of trie. /// Returns an iterator over elements of trie.
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a>; fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a>;
@ -119,7 +130,6 @@ pub trait TrieMut {
fn remove(&mut self, key: &[u8]) -> Result<()>; fn remove(&mut self, key: &[u8]) -> Result<()>;
} }
/// Trie types /// Trie types
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum TrieSpec { pub enum TrieSpec {
@ -143,6 +153,51 @@ pub struct TrieFactory {
spec: TrieSpec, 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<bool> {
wrapper!(self, contains, key)
}
fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], r: &'b mut R) -> Result<Option<&'a [u8]>>
where 'a: 'b, R: Recorder {
wrapper!(self, get_recorded, key, r)
}
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> {
wrapper!(self, iter,)
}
}
#[cfg_attr(feature="dev", allow(wrong_self_convention))] #[cfg_attr(feature="dev", allow(wrong_self_convention))]
impl TrieFactory { impl TrieFactory {
/// Creates new factory. /// Creates new factory.
@ -153,11 +208,11 @@ impl TrieFactory {
} }
/// Create new immutable instance of Trie. /// Create new immutable instance of Trie.
pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result<Box<Trie + 'db>> { pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result<TrieKinds<'db>> {
match self.spec { match self.spec {
TrieSpec::Generic => Ok(Box::new(try!(TrieDB::new(db, root)))), TrieSpec::Generic => Ok(TrieKinds::Generic(try!(TrieDB::new(db, root)))),
TrieSpec::Secure => Ok(Box::new(try!(SecTrieDB::new(db, root)))), TrieSpec::Secure => Ok(TrieKinds::Secure(try!(SecTrieDB::new(db, root)))),
TrieSpec::Fat => Ok(Box::new(try!(FatDB::new(db, root)))), TrieSpec::Fat => Ok(TrieKinds::Fat(try!(FatDB::new(db, root)))),
} }
} }

236
util/src/trie/recorder.rs Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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<Record> 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<Record> { Vec::new() }
}
/// A simple recorder. Does nothing fancy but fulfills the `Recorder` interface
/// properly.
pub struct BasicRecorder {
nodes: Vec<Record>,
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<Record> {
::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
]
]);
}
}

View File

@ -18,7 +18,7 @@ use hash::H256;
use sha3::Hashable; use sha3::Hashable;
use hashdb::HashDB; use hashdb::HashDB;
use super::triedb::TrieDB; 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. /// 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()) self.raw.contains(&key.sha3())
} }
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<&'a [u8]>> fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result<Option<&'a [u8]>>
where 'a: 'key where 'a: 'b, R: Recorder
{ {
self.raw.get(&key.sha3()) self.raw.get_recorded(&key.sha3(), rec)
} }
} }

View File

@ -19,6 +19,7 @@ use hashdb::*;
use nibbleslice::*; use nibbleslice::*;
use rlp::*; use rlp::*;
use super::node::Node; use super::node::Node;
use super::recorder::{Recorder, NoOp};
use super::{Trie, TrieItem, TrieError}; use super::{Trie, TrieItem, TrieError};
/// A `Trie` implementation using a generic `HashDB` backing database. /// A `Trie` implementation using a generic `HashDB` backing database.
@ -79,7 +80,7 @@ impl<'db> TrieDB<'db> {
pub fn keys(&self) -> super::Result<Vec<H256>> { pub fn keys(&self) -> super::Result<Vec<H256>> {
let mut ret: Vec<H256> = Vec::new(); let mut ret: Vec<H256> = Vec::new();
ret.push(self.root.clone()); 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) Ok(ret)
} }
@ -114,7 +115,7 @@ impl<'db> TrieDB<'db> {
acc.push(p.as_val()); 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 { match node {
@ -127,18 +128,19 @@ impl<'db> TrieDB<'db> {
} }
/// Get the root node's RLP. /// Get the root node's RLP.
fn root_node(&self) -> super::Result<Node> { fn root_node<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result<Node> {
self.root_data().map(Node::decoded) self.root_data(r).map(Node::decoded)
} }
/// Get the data of the root node. /// 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))) 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`. /// Get the root node as a `Node`.
fn get_node(&'db self, node: &'db [u8]) -> super::Result<Node> { fn get_node<'a, R: 'a + Recorder>(&'db self, node: &'db [u8], r: &'a mut R, depth: u32) -> super::Result<Node> {
self.get_raw_or_lookup(node).map(Node::decoded) self.get_raw_or_lookup(node, r, depth).map(Node::decoded)
} }
/// Indentation helper for `formal_all`. /// Indentation helper for `formal_all`.
@ -155,7 +157,7 @@ impl<'db> TrieDB<'db> {
Node::Leaf(slice, value) => try!(writeln!(f, "'{:?}: {:?}.", slice, value.pretty())), Node::Leaf(slice, value) => try!(writeln!(f, "'{:?}: {:?}.", slice, value.pretty())),
Node::Extension(ref slice, item) => { Node::Extension(ref slice, item) => {
try!(write!(f, "'{:?} ", slice)); 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)); try!(self.fmt_all(node, f, deepness));
} }
}, },
@ -166,7 +168,7 @@ impl<'db> TrieDB<'db> {
try!(writeln!(f, "=: {:?}", v.pretty())) try!(writeln!(f, "=: {:?}", v.pretty()))
} }
for i in 0..16 { for i in 0..16 {
match self.get_node(nodes[i]) { match self.get_node(nodes[i], &mut NoOp, 0) {
Ok(Node::Empty) => {}, Ok(Node::Empty) => {},
Ok(n) => { Ok(n) => {
try!(self.fmt_indent(f, deepness + 1)); 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. /// 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<Option<&'db [u8]>> fn do_lookup<'key, R: 'key>(&'db self, key: &NibbleSlice<'key>, r: &'key mut R) -> super::Result<Option<&'db [u8]>>
where 'db: 'key where 'db: 'key, R: Recorder
{ {
let root_rlp = try!(self.root_data()); let root_rlp = try!(self.root_data(r));
self.get_from_node(root_rlp, key) 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 /// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no
/// value exists for the key. /// value exists for the key.
/// ///
/// Note: Not a public API; use Trie trait functions. /// Note: Not a public API; use Trie trait functions.
fn get_from_node<'key>(&'db self, node: &'db [u8], key: &NibbleSlice<'key>) -> super::Result<Option<&'db [u8]>> fn get_from_node<'key, R: 'key>(
where 'db: 'key &'db self,
{ node: &'db [u8],
key: &NibbleSlice<'key>,
r: &'key mut R,
d: u32
) -> super::Result<Option<&'db [u8]>> where 'db: 'key, R: Recorder {
match Node::decoded(node) { match Node::decoded(node) {
Node::Leaf(ref slice, value) if key == slice => Ok(Some(value)), Node::Leaf(ref slice, value) if key == slice => Ok(Some(value)),
Node::Extension(ref slice, item) if key.starts_with(slice) => { Node::Extension(ref slice, item) if key.starts_with(slice) => {
let data = try!(self.get_raw_or_lookup(item)); let data = try!(self.get_raw_or_lookup(item, r, d));
self.get_from_node(data, &key.mid(slice.len())) self.get_from_node(data, &key.mid(slice.len()), r, d + 1)
}, },
Node::Branch(ref nodes, value) => match key.is_empty() { Node::Branch(ref nodes, value) => match key.is_empty() {
true => Ok(value), 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) _ => Ok(None)
} }
@ -219,13 +228,14 @@ impl<'db> TrieDB<'db> {
/// Given some node-describing data `node`, return the actual node RLP. /// 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 /// This could be a simple identity operation in the case that the node is sufficiently small, but
/// may require a database lookup. /// may require a database lookup.
fn get_raw_or_lookup(&'db self, node: &'db [u8]) -> super::Result<&'db [u8]> { fn get_raw_or_lookup<R: Recorder>(&'db self, node: &'db [u8], rec: &mut R, d: u32) -> super::Result<&'db [u8]> {
// check if its sha3 + len // check if its sha3 + len
let r = Rlp::new(node); let r = Rlp::new(node);
match r.is_data() && r.size() == 32 { match r.is_data() && r.size() == 32 {
true => { true => {
let key = r.as_val::<H256>(); let key = r.as_val::<H256>();
self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key))) self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key)))
.map(|raw| { rec.record(&key, raw, d); raw })
} }
false => Ok(node) false => Ok(node)
} }
@ -275,7 +285,7 @@ impl<'a> TrieDBIterator<'a> {
trail: vec![], trail: vec![],
key_nibbles: Vec::new(), key_nibbles: Vec::new(),
}; };
r.descend(db.root_data().unwrap()); r.descend(db.root_data(&mut NoOp).unwrap());
r r
} }
@ -283,7 +293,7 @@ impl<'a> TrieDBIterator<'a> {
fn descend(&mut self, d: &'a [u8]) { fn descend(&mut self, d: &'a [u8]) {
self.trail.push(Crumb { self.trail.push(Crumb {
status: Status::Entering, 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 { match self.trail.last().unwrap().node {
Node::Leaf(n, _) | Node::Extension(n, _) => { self.key_nibbles.extend(n.iter()); }, 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> { impl<'db> Trie for TrieDB<'db> {
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> { fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + 'a> {
Box::new(TrieDB::iter(self)) Box::new(TrieDBIterator::new(self))
} }
fn root(&self) -> &H256 { self.root } fn root(&self) -> &H256 { self.root }
fn get<'a, 'key>(&'a self, key: &'key [u8]) -> super::Result<Option<&'a [u8]>> fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result<Option<&'a [u8]>>
where 'a: 'key 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(); t.insert(x, x).unwrap();
} }
} }
assert_eq!(d.iter().map(|i|i.to_vec()).collect::<Vec<_>>(), TrieDB::new(&memdb, &root).unwrap().iter().map(|x|x.0).collect::<Vec<_>>());
assert_eq!(d, TrieDB::new(&memdb, &root).unwrap().iter().map(|x|x.1).collect::<Vec<_>>()); let t = TrieDB::new(&memdb, &root).unwrap();
assert_eq!(d.iter().map(|i|i.to_vec()).collect::<Vec<_>>(), t.iter().map(|x|x.0).collect::<Vec<_>>());
assert_eq!(d, t.iter().map(|x|x.1).collect::<Vec<_>>());
} }