Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Merge pull request #353 from ethcore/state
Browse files Browse the repository at this point in the history
Performance optimizations
  • Loading branch information
Gav Wood committed Feb 5, 2016
2 parents 9dbdd33 + bfb7d37 commit 85e2ef1
Show file tree
Hide file tree
Showing 17 changed files with 505 additions and 129 deletions.
2 changes: 1 addition & 1 deletion ethcore/res/ethereum/tests
21 changes: 14 additions & 7 deletions ethcore/src/account.rs
Expand Up @@ -2,6 +2,7 @@

use util::*;
use pod_account::*;
use account_db::*;

/// Single account in the system.
#[derive(Clone)]
Expand Down Expand Up @@ -99,7 +100,7 @@ impl Account {
}

/// Get (and cache) the contents of the trie's storage at `key`.
pub fn storage_at(&self, db: &HashDB, key: &H256) -> H256 {
pub fn storage_at(&self, db: &AccountDB, key: &H256) -> H256 {
self.storage_overlay.borrow_mut().entry(key.clone()).or_insert_with(||{
(Filth::Clean, H256::from(SecTrieDB::new(db, &self.storage_root).get(key.bytes()).map_or(U256::zero(), |v| -> U256 {decode(v)})))
}).1.clone()
Expand Down Expand Up @@ -147,7 +148,7 @@ impl Account {
}

/// Provide a database to lookup `code_hash`. Should not be called if it is a contract without code.
pub fn cache_code(&mut self, db: &HashDB) -> bool {
pub fn cache_code(&mut self, db: &AccountDB) -> bool {
// TODO: fill out self.code_cache;
trace!("Account::cache_code: ic={}; self.code_hash={:?}, self.code_cache={}", self.is_cached(), self.code_hash, self.code_cache.pretty());
self.is_cached() ||
Expand Down Expand Up @@ -184,7 +185,7 @@ impl Account {
pub fn sub_balance(&mut self, x: &U256) { self.balance = self.balance - *x; }

/// Commit the `storage_overlay` to the backing DB and update `storage_root`.
pub fn commit_storage(&mut self, db: &mut HashDB) {
pub fn commit_storage(&mut self, db: &mut AccountDBMut) {
let mut t = SecTrieDBMut::from_existing(db, &mut self.storage_root);
for (k, &mut (ref mut f, ref mut v)) in self.storage_overlay.borrow_mut().iter_mut() {
if f == &Filth::Dirty {
Expand All @@ -200,7 +201,7 @@ impl Account {
}

/// Commit any unsaved code. `code_hash` will always return the hash of the `code_cache` after this.
pub fn commit_code(&mut self, db: &mut HashDB) {
pub fn commit_code(&mut self, db: &mut AccountDBMut) {
trace!("Commiting code of {:?} - {:?}, {:?}", self, self.code_hash.is_none(), self.code_cache.is_empty());
match (self.code_hash.is_none(), self.code_cache.is_empty()) {
(true, true) => self.code_hash = Some(SHA3_EMPTY),
Expand Down Expand Up @@ -233,10 +234,12 @@ mod tests {

use util::*;
use super::*;
use account_db::*;

#[test]
fn storage_at() {
let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new());
let rlp = {
let mut a = Account::new_contract(U256::from(69u8));
a.set_storage(H256::from(&U256::from(0x00u64)), H256::from(&U256::from(0x1234u64)));
Expand All @@ -248,13 +251,14 @@ mod tests {

let a = Account::from_rlp(&rlp);
assert_eq!(a.storage_root().unwrap().hex(), "c57e1afb758b07f8d2c8f13a3b6e44fa5ff94ab266facc5a4fd3f062426e50b2");
assert_eq!(a.storage_at(&db, &H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64)));
assert_eq!(a.storage_at(&db, &H256::from(&U256::from(0x01u64))), H256::new());
assert_eq!(a.storage_at(&db.immutable(), &H256::from(&U256::from(0x00u64))), H256::from(&U256::from(0x1234u64)));
assert_eq!(a.storage_at(&db.immutable(), &H256::from(&U256::from(0x01u64))), H256::new());
}

#[test]
fn note_code() {
let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new());

let rlp = {
let mut a = Account::new_contract(U256::from(69u8));
Expand All @@ -264,7 +268,7 @@ mod tests {
};

let mut a = Account::from_rlp(&rlp);
assert!(a.cache_code(&db));
assert!(a.cache_code(&db.immutable()));

let mut a = Account::from_rlp(&rlp);
assert_eq!(a.note_code(vec![0x55, 0x44, 0xffu8]), Ok(()));
Expand All @@ -274,6 +278,7 @@ mod tests {
fn commit_storage() {
let mut a = Account::new_contract(U256::from(69u8));
let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new());
a.set_storage(x!(0), x!(0x1234));
assert_eq!(a.storage_root(), None);
a.commit_storage(&mut db);
Expand All @@ -284,6 +289,7 @@ mod tests {
fn commit_remove_commit_storage() {
let mut a = Account::new_contract(U256::from(69u8));
let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new());
a.set_storage(x!(0), x!(0x1234));
a.commit_storage(&mut db);
a.set_storage(x!(1), x!(0x1234));
Expand All @@ -297,6 +303,7 @@ mod tests {
fn commit_code() {
let mut a = Account::new_contract(U256::from(69u8));
let mut db = MemoryDB::new();
let mut db = AccountDBMut::new(&mut db, &Address::new());
a.init_code(vec![0x55, 0x44, 0xffu8]);
assert_eq!(a.code_hash(), SHA3_EMPTY);
a.commit_code(&mut db);
Expand Down
120 changes: 120 additions & 0 deletions ethcore/src/account_db.rs
@@ -0,0 +1,120 @@
//! DB backend wrapper for Account trie
use util::*;

static NULL_RLP_STATIC: [u8; 1] = [0x80; 1];

// TODO: introduce HashDBMut?
/// DB backend wrapper for Account trie
/// Transforms trie node keys for the database
pub struct AccountDB<'db> {
db: &'db HashDB,
address: H256,
}

#[inline]
fn combine_key<'a>(address: &'a H256, key: &'a H256) -> H256 {
let mut addr_hash = address.sha3();
// preserve 96 bits of original key for db lookup
addr_hash[0..12].clone_from_slice(&[0u8; 12]);
&addr_hash ^ key
}

impl<'db> AccountDB<'db> {
pub fn new(db: &'db HashDB, address: &Address) -> AccountDB<'db> {
AccountDB {
db: db,
address: x!(address.clone()),
}
}
}

impl<'db> HashDB for AccountDB<'db>{
fn keys(&self) -> HashMap<H256, i32> {
unimplemented!()
}

fn lookup(&self, key: &H256) -> Option<&[u8]> {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
}
self.db.lookup(&combine_key(&self.address, key))
}

fn exists(&self, key: &H256) -> bool {
if key == &SHA3_NULL_RLP {
return true;
}
self.db.exists(&combine_key(&self.address, key))
}

fn insert(&mut self, _value: &[u8]) -> H256 {
unimplemented!()
}

fn emplace(&mut self, _key: H256, _value: Bytes) {
unimplemented!()
}

fn kill(&mut self, _key: &H256) {
unimplemented!()
}
}

/// DB backend wrapper for Account trie
pub struct AccountDBMut<'db> {
db: &'db mut HashDB,
address: H256,
}

impl<'db> AccountDBMut<'db> {
pub fn new(db: &'db mut HashDB, address: &Address) -> AccountDBMut<'db> {
AccountDBMut {
db: db,
address: x!(address.clone()),
}
}

#[allow(dead_code)]
pub fn immutable(&'db self) -> AccountDB<'db> {
AccountDB { db: self.db, address: self.address.clone() }
}
}

impl<'db> HashDB for AccountDBMut<'db>{
fn keys(&self) -> HashMap<H256, i32> {
unimplemented!()
}

fn lookup(&self, key: &H256) -> Option<&[u8]> {
if key == &SHA3_NULL_RLP {
return Some(&NULL_RLP_STATIC);
}
self.db.lookup(&combine_key(&self.address, key))
}

fn exists(&self, key: &H256) -> bool {
if key == &SHA3_NULL_RLP {
return true;
}
self.db.exists(&combine_key(&self.address, key))
}

fn insert(&mut self, value: &[u8]) -> H256 {
let k = value.sha3();
let ak = combine_key(&self.address, &k);
self.db.emplace(ak, value.to_vec());
k
}

fn emplace(&mut self, key: H256, value: Bytes) {
let key = combine_key(&self.address, &key);
self.db.emplace(key, value.to_vec())
}

fn kill(&mut self, key: &H256) {
let key = combine_key(&self.address, key);
self.db.kill(&key)
}
}


26 changes: 14 additions & 12 deletions ethcore/src/block.rs
Expand Up @@ -62,7 +62,7 @@ impl Decodable for Block {
/// Internal type for a block's common elements.
// TODO: rename to ExecutedBlock
// TODO: use BareBlock
#[derive(Debug, Clone)]
#[derive(Debug)]
pub struct ExecutedBlock {
base: Block,

Expand Down Expand Up @@ -318,8 +318,10 @@ impl IsBlock for SealedBlock {
/// Enact the block given by block header, transactions and uncles
pub fn enact<'x, 'y>(header: &Header, transactions: &[Transaction], uncles: &[Header], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
{
let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce());
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
if ::log::max_log_level() >= ::log::LogLevel::Trace {
let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce());
trace!("enact(): root={}, author={}, author_balance={}\n", s.root(), header.author(), s.balance(&header.author()));
}
}

let mut b = OpenBlock::new(engine, db, parent, last_hashes, header.author().clone(), header.extra_data().clone());
Expand Down Expand Up @@ -363,10 +365,10 @@ mod tests {
let engine = Spec::new_test().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let mut db = db_result.take();
engine.spec().ensure_db_good(&mut db);
let last_hashes = vec![genesis_header.hash()];
let b = OpenBlock::new(engine.deref(), db.clone(), &genesis_header, &last_hashes, Address::zero(), vec![]);
let b = OpenBlock::new(engine.deref(), db, &genesis_header, &last_hashes, Address::zero(), vec![]);
let b = b.close();
let _ = b.seal(vec![]);
}
Expand All @@ -378,16 +380,16 @@ mod tests {
let genesis_header = engine.spec().genesis_header();

let mut db_result = get_temp_journal_db();
let db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let b = OpenBlock::new(engine.deref(), db.clone(), &genesis_header, &vec![genesis_header.hash()], Address::zero(), vec![]).close().seal(vec![]).unwrap();
let mut db = db_result.take();
engine.spec().ensure_db_good(&mut db);
let b = OpenBlock::new(engine.deref(), db, &genesis_header, &vec![genesis_header.hash()], Address::zero(), vec![]).close().seal(vec![]).unwrap();
let orig_bytes = b.rlp_bytes();
let orig_db = b.drain();

let mut db_result = get_temp_journal_db();
let db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let e = enact_and_seal(&orig_bytes, engine.deref(), db.clone(), &genesis_header, &vec![genesis_header.hash()]).unwrap();
let mut db = db_result.take();
engine.spec().ensure_db_good(&mut db);
let e = enact_and_seal(&orig_bytes, engine.deref(), db, &genesis_header, &vec![genesis_header.hash()]).unwrap();

assert_eq!(e.rlp_bytes(), orig_bytes);

Expand Down
19 changes: 7 additions & 12 deletions ethcore/src/client.rs
Expand Up @@ -139,10 +139,10 @@ impl ClientReport {
pub struct Client {
chain: Arc<RwLock<BlockChain>>,
engine: Arc<Box<Engine>>,
state_db: JournalDB,
state_db: Arc<DB>,
state_journal: Mutex<JournalDB>,
block_queue: RwLock<BlockQueue>,
report: RwLock<ClientReport>,
uncommited_states: RwLock<HashMap<H256, JournalDB>>,
import_lock: Mutex<()>
}

Expand Down Expand Up @@ -180,16 +180,16 @@ impl Client {

let engine = Arc::new(try!(spec.to_engine()));
let mut state_db = JournalDB::new_with_arc(db.clone());
if engine.spec().ensure_db_good(&mut state_db) {
if state_db.is_empty() && engine.spec().ensure_db_good(&mut state_db) {
state_db.commit(0, &engine.spec().genesis_header().hash(), None).expect("Error commiting genesis state to state DB");
}
Ok(Arc::new(Client {
chain: chain,
engine: engine.clone(),
state_db: state_db,
state_db: db.clone(),
state_journal: Mutex::new(JournalDB::new_with_arc(db)),
block_queue: RwLock::new(BlockQueue::new(engine, message_channel)),
report: RwLock::new(Default::default()),
uncommited_states: RwLock::new(HashMap::new()),
import_lock: Mutex::new(()),
}))
}
Expand Down Expand Up @@ -242,7 +242,7 @@ impl Client {
}
}

let db = self.state_db.clone();
let db = self.state_journal.lock().unwrap().clone();
let result = match enact_verified(&block, self.engine.deref().deref(), db, &parent, &last_hashes) {
Ok(b) => b,
Err(e) => {
Expand Down Expand Up @@ -277,14 +277,9 @@ impl Client {
ret
}

/// Clear cached state overlay
pub fn clear_state(&self, hash: &H256) {
self.uncommited_states.write().unwrap().remove(hash);
}

/// Get a copy of the best block's state.
pub fn state(&self) -> State {
State::from_existing(self.state_db.clone(), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce())
State::from_existing(JournalDB::new_with_arc(self.state_db.clone()), HeaderView::new(&self.best_block_header()).state_root(), self.engine.account_start_nonce())
}

/// Get info on the cache.
Expand Down
12 changes: 6 additions & 6 deletions ethcore/src/ethereum/ethash.rs
Expand Up @@ -232,10 +232,10 @@ fn on_close_block() {
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let mut db = db_result.take();
engine.spec().ensure_db_good(&mut db);
let last_hashes = vec![genesis_header.hash()];
let b = OpenBlock::new(engine.deref(), db.clone(), &genesis_header, &last_hashes, Address::zero(), vec![]);
let b = OpenBlock::new(engine.deref(), db, &genesis_header, &last_hashes, Address::zero(), vec![]);
let b = b.close();
assert_eq!(b.state().balance(&Address::zero()), U256::from_str("4563918244f40000").unwrap());
}
Expand All @@ -246,10 +246,10 @@ fn on_close_block_with_uncle() {
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let mut db = db_result.take();
engine.spec().ensure_db_good(&mut db);
let last_hashes = vec![genesis_header.hash()];
let mut b = OpenBlock::new(engine.deref(), db.clone(), &genesis_header, &last_hashes, Address::zero(), vec![]);
let mut b = OpenBlock::new(engine.deref(), db, &genesis_header, &last_hashes, Address::zero(), vec![]);
let mut uncle = Header::new();
let uncle_author = address_from_hex("ef2d6d194084c2de36e0dabfce45d046b37d1106");
uncle.author = uncle_author.clone();
Expand Down
6 changes: 3 additions & 3 deletions ethcore/src/ethereum/mod.rs
Expand Up @@ -44,9 +44,9 @@ mod tests {
let engine = new_morden().to_engine().unwrap();
let genesis_header = engine.spec().genesis_header();
let mut db_result = get_temp_journal_db();
let mut db = db_result.reference_mut();
engine.spec().ensure_db_good(db);
let s = State::from_existing(db.clone(), genesis_header.state_root.clone(), engine.account_start_nonce());
let mut db = db_result.take();
engine.spec().ensure_db_good(&mut db);
let s = State::from_existing(db, genesis_header.state_root.clone(), engine.account_start_nonce());
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000001")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000002")), U256::from(1u64));
assert_eq!(s.balance(&address_from_hex("0000000000000000000000000000000000000003")), U256::from(1u64));
Expand Down

0 comments on commit 85e2ef1

Please sign in to comment.