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:
parent
33e0a234f2
commit
190e4db266
@ -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<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?
|
||||
/// 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<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)
|
||||
}
|
||||
}
|
@ -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<LastHashes>,
|
||||
}
|
||||
|
||||
@ -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<JournalDB>,
|
||||
parent: &Header,
|
||||
@ -240,11 +238,10 @@ impl<'x> OpenBlock<'x> {
|
||||
gas_range_target: (U256, U256),
|
||||
extra_data: Bytes,
|
||||
) -> 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 {
|
||||
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<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
{
|
||||
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<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
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<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
) -> Result<LockedBlock, Error> {
|
||||
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<JournalDB>,
|
||||
parent: &Header,
|
||||
last_hashes: Arc<LastHashes>,
|
||||
vm_factory: &EvmFactory,
|
||||
trie_factory: TrieFactory,
|
||||
factories: Factories,
|
||||
) -> Result<SealedBlock, Error> {
|
||||
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);
|
||||
|
@ -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<PanicHandler>,
|
||||
verifier: Box<Verifier>,
|
||||
vm_factory: Arc<EvmFactory>,
|
||||
trie_factory: TrieFactory,
|
||||
miner: Arc<Miner>,
|
||||
sleep_state: Mutex<SleepState>,
|
||||
liveness: AtomicBool,
|
||||
@ -139,6 +138,7 @@ pub struct Client {
|
||||
notify: RwLock<Vec<Weak<ChainNotify>>>,
|
||||
queue_transactions: AtomicUsize,
|
||||
last_hashes: RwLock<VecDeque<H256>>,
|
||||
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 {
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
30
ethcore/src/factory.rs
Normal file
30
ethcore/src/factory.rs
Normal 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,
|
||||
}
|
@ -64,8 +64,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
|
||||
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);
|
||||
|
@ -139,6 +139,7 @@ mod externalities;
|
||||
mod verification;
|
||||
mod blockchain;
|
||||
mod types;
|
||||
mod factory;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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::<f32>() < ACCOUNT_CHURN)
|
||||
.map(|(k, v)| (H256::from_slice(&k), v.to_owned()))
|
||||
.collect()
|
||||
.collect();
|
||||
|
||||
temp
|
||||
};
|
||||
|
||||
// sweep once to alter storage tries.
|
||||
|
@ -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),
|
||||
|
@ -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<HashMap<Address, Option<Account>>>,
|
||||
snapshots: RefCell<Vec<HashMap<Address, Option<Option<Account>>>>>,
|
||||
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<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();
|
||||
{
|
||||
// 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<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) {
|
||||
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<Address,
|
||||
Option<Account>>
|
||||
accounts: &mut HashMap<Address, Option<Account>>
|
||||
) -> 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<Account>) -> 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,
|
||||
|
@ -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 = 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<F>(get_test_spec: F, block_numbe
|
||||
// forge block.
|
||||
let mut b = OpenBlock::new(
|
||||
test_engine,
|
||||
&vm_factory,
|
||||
Default::default(),
|
||||
false,
|
||||
db,
|
||||
|
@ -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<Iterator<Item = TrieItem> + '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<Option<&'a [u8]>>
|
||||
where 'a: 'key
|
||||
fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result<Option<&'a [u8]>>
|
||||
where 'a: 'b, R: Recorder
|
||||
{
|
||||
self.raw.get(&key.sha3())
|
||||
self.raw.get_recorded(&key.sha3(), rec)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<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.
|
||||
fn iter<'a>(&'a self) -> Box<Iterator<Item = TrieItem> + '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<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))]
|
||||
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<Box<Trie + 'db>> {
|
||||
pub fn readonly<'db>(&self, db: &'db HashDB, root: &'db H256) -> Result<TrieKinds<'db>> {
|
||||
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)))),
|
||||
}
|
||||
}
|
||||
|
||||
|
236
util/src/trie/recorder.rs
Normal file
236
util/src/trie/recorder.rs
Normal 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
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
@ -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<Option<&'a [u8]>>
|
||||
where 'a: 'key
|
||||
fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result<Option<&'a [u8]>>
|
||||
where 'a: 'b, R: Recorder
|
||||
{
|
||||
self.raw.get(&key.sha3())
|
||||
self.raw.get_recorded(&key.sha3(), rec)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Vec<H256>> {
|
||||
let mut ret: Vec<H256> = 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<Node> {
|
||||
self.root_data().map(Node::decoded)
|
||||
fn root_node<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result<Node> {
|
||||
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<Node> {
|
||||
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<Node> {
|
||||
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<Option<&'db [u8]>>
|
||||
where 'db: 'key
|
||||
fn do_lookup<'key, R: 'key>(&'db self, key: &NibbleSlice<'key>, r: &'key mut R) -> super::Result<Option<&'db [u8]>>
|
||||
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<Option<&'db [u8]>>
|
||||
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<Option<&'db [u8]>> 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<R: Recorder>(&'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::<H256>();
|
||||
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<Iterator<Item = TrieItem> + '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<Option<&'a [u8]>>
|
||||
where 'a: 'key
|
||||
fn get_recorded<'a, 'b, R: 'b>(&'a self, key: &'b [u8], rec: &'b mut R) -> super::Result<Option<&'a [u8]>>
|
||||
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::<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<_>>());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user