Zero-alloc trie lookups (#3998)

* triedb cleanup

* factor out common portion of trie query

* allocate far fewer times in node decoding

* fix bench compilation

* introduce OwnedNode variant to make iter fast again

* generalize recorder trait to Query

* decode trie outputs cost-free in state

* test for passing closure as query
This commit is contained in:
Robert Habermeier
2017-01-06 16:18:45 +01:00
committed by Gav Wood
parent e983339edd
commit 9c00eb4e8a
13 changed files with 395 additions and 382 deletions

View File

@@ -178,8 +178,8 @@ impl Account {
SecTrieDBMut would not set it to an invalid state root. Therefore the root is valid and DB creation \
using it will not fail.");
let item: U256 = match db.get(key){
Ok(x) => x.map_or_else(U256::zero, |v| decode(&*v)),
let item: U256 = match db.get_with(key, ::rlp::decode) {
Ok(x) => x.unwrap_or_else(U256::zero),
Err(e) => panic!("Encountered potential DB corruption: {}", e),
};
let value: H256 = item.into();
@@ -453,12 +453,12 @@ impl Account {
/// omitted.
pub fn prove_storage(&self, db: &HashDB, storage_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> {
use util::trie::{Trie, TrieDB};
use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder};
use util::trie::recorder::Recorder;
let mut recorder = TrieRecorder::with_depth(from_level);
let mut recorder = Recorder::with_depth(from_level);
let trie = TrieDB::new(db, &self.storage_root)?;
let _ = trie.get_recorded(&storage_key, &mut recorder)?;
let _ = trie.get_with(&storage_key, &mut recorder)?;
Ok(recorder.drain().into_iter().map(|r| r.data).collect())
}

View File

@@ -32,7 +32,7 @@ use state_db::StateDB;
use util::*;
use util::trie::recorder::{Recorder, BasicRecorder as TrieRecorder};
use util::trie::recorder::Recorder;
mod account;
mod substate;
@@ -425,8 +425,8 @@ impl State {
// account is not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let maybe_acc = match db.get(address) {
Ok(acc) => acc.map(|v| Account::from_rlp(&v)),
let maybe_acc = match db.get_with(address, Account::from_rlp) {
Ok(acc) => acc,
Err(e) => panic!("Potential DB corruption encountered: {}", e),
};
let r = maybe_acc.as_ref().map_or(H256::new(), |a| {
@@ -690,8 +690,8 @@ impl State {
// not found in the global cache, get from the DB and insert into local
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
let mut maybe_acc = match db.get(a) {
Ok(acc) => acc.map(|v| Account::from_rlp(&v)),
let mut maybe_acc = match db.get_with(a, Account::from_rlp) {
Ok(acc) => acc,
Err(e) => panic!("Potential DB corruption encountered: {}", e),
};
if let Some(ref mut account) = maybe_acc.as_mut() {
@@ -722,9 +722,8 @@ impl State {
None => {
let maybe_acc = if self.db.check_non_null_bloom(a) {
let db = self.factories.trie.readonly(self.db.as_hashdb(), &self.root).expect(SEC_TRIE_DB_UNWRAP_STR);
match db.get(a) {
Ok(Some(acc)) => AccountEntry::new_clean(Some(Account::from_rlp(&acc))),
Ok(None) => AccountEntry::new_clean(None),
match db.get_with(a, Account::from_rlp) {
Ok(acc) => AccountEntry::new_clean(acc),
Err(e) => panic!("Potential DB corruption encountered: {}", e),
}
} else {
@@ -770,9 +769,9 @@ impl State {
/// Requires a secure trie to be used for accurate results.
/// `account_key` == sha3(address)
pub fn prove_account(&self, account_key: H256, from_level: u32) -> Result<Vec<Bytes>, Box<TrieError>> {
let mut recorder = TrieRecorder::with_depth(from_level);
let mut recorder = Recorder::with_depth(from_level);
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
let _ = trie.get_recorded(&account_key, &mut recorder)?;
trie.get_with(&account_key, &mut recorder)?;
Ok(recorder.drain().into_iter().map(|r| r.data).collect())
}
@@ -786,8 +785,8 @@ impl State {
// TODO: probably could look into cache somehow but it's keyed by
// address, not sha3(address).
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
let acc = match trie.get(&account_key)? {
Some(rlp) => Account::from_rlp(&rlp),
let acc = match trie.get_with(&account_key, Account::from_rlp)? {
Some(acc) => acc,
None => return Ok(Vec::new()),
};
@@ -799,8 +798,8 @@ impl State {
/// Only works when backed by a secure trie.
pub fn code_by_address_hash(&self, account_key: H256) -> Result<Option<Bytes>, Box<TrieError>> {
let trie = TrieDB::new(self.db.as_hashdb(), &self.root)?;
let mut acc = match trie.get(&account_key)? {
Some(rlp) => Account::from_rlp(&rlp),
let mut acc = match trie.get_with(&account_key, Account::from_rlp)? {
Some(acc) => acc,
None => return Ok(None),
};