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
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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());
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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
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.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);
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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),
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
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 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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<_>>());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user