Make HashDB generic (#8739)

The `patricia_trie` crate is generic over the hasher (by way of HashDB) and node encoding scheme. Adds a new `patricia_trie_ethereum` crate with concrete impls for Keccak/RLP.
This commit is contained in:
David 2018-07-02 18:50:05 +02:00 committed by GitHub
parent f0628f5450
commit 8805eb968d
3 changed files with 162 additions and 58 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "memorydb"
version = "0.1.1"
version = "0.2.0"
authors = ["Parity Technologies <admin@parity.io>"]
description = "in-memory implementation of hashdb"
license = "GPL-3.0"
@ -8,8 +8,12 @@ license = "GPL-3.0"
[dependencies]
elastic-array = "0.10"
heapsize = "0.4"
ethereum-types = "0.3"
keccak-hash = { version = "0.1.0", path = "../hash" }
hashdb = { version = "0.1.1", path = "../hashdb" }
hashdb = { version = "0.2.0", path = "../hashdb" }
plain_hasher = { path = "../plain_hasher" }
rlp = { version = "0.2.1", path = "../rlp" }
[dev-dependencies]
tiny-keccak = "1.4.2"
ethereum-types = "0.3"
keccak-hasher = { path = "../keccak-hasher" }
keccak-hash = { path = "../hash" }

79
benches/memdb.rs Normal file
View File

@ -0,0 +1,79 @@
// Copyright 2015-2018 Parity Technologies (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/>.
#![feature(test)]
extern crate hashdb;
extern crate memorydb;
extern crate keccak_hasher;
extern crate keccak_hash;
extern crate rlp;
extern crate test;
use memorydb::MemoryDB;
use keccak_hasher::KeccakHasher;
use hashdb::{HashDB, Hasher};
use keccak_hash::KECCAK_NULL_RLP;
use rlp::NULL_RLP;
use test::{Bencher, black_box};
#[bench]
fn instantiation(b: &mut Bencher) {
b.iter(|| {
MemoryDB::<KeccakHasher>::new();
})
}
#[bench]
fn compare_to_null_embedded_in_struct(b: &mut Bencher) {
struct X {a_hash: <KeccakHasher as Hasher>::Out};
let x = X {a_hash: KeccakHasher::hash(&NULL_RLP)};
let key = KeccakHasher::hash(b"abc");
b.iter(|| {
black_box(key == x.a_hash);
})
}
#[bench]
fn compare_to_null_in_const(b: &mut Bencher) {
let key = KeccakHasher::hash(b"abc");
b.iter(|| {
black_box(key == KECCAK_NULL_RLP);
})
}
#[bench]
fn contains_with_non_null_key(b: &mut Bencher) {
let mut m = MemoryDB::<KeccakHasher>::new();
let key = KeccakHasher::hash(b"abc");
m.insert(b"abcefghijklmnopqrstuvxyz");
b.iter(|| {
m.contains(&key);
})
}
#[bench]
fn contains_with_null_key(b: &mut Bencher) {
let mut m = MemoryDB::<KeccakHasher>::new();
let null_key = KeccakHasher::hash(&NULL_RLP);
m.insert(b"abcefghijklmnopqrstuvxyz");
b.iter(|| {
m.contains(&null_key);
})
}

View File

@ -15,23 +15,25 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Reference-counted memory-based `HashDB` implementation.
extern crate heapsize;
extern crate ethereum_types;
extern crate hashdb;
extern crate keccak_hash as keccak;
extern crate plain_hasher;
extern crate rlp;
extern crate elastic_array;
extern crate hashdb;
extern crate heapsize;
extern crate rlp;
#[cfg(test)] extern crate keccak_hasher;
#[cfg(test)] extern crate tiny_keccak;
#[cfg(test)] extern crate ethereum_types;
use std::mem;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use hashdb::{HashDB, Hasher as KeyHasher, DBValue, AsHashDB};
use heapsize::HeapSizeOf;
use ethereum_types::H256;
use hashdb::{HashDB, DBValue};
use keccak::{KECCAK_NULL_RLP, keccak};
use plain_hasher::H256FastMap;
use rlp::NULL_RLP;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash;
use std::mem;
// Backing `HashMap` parametrized with a `Hasher` for the keys `Hasher::Out` and the `Hasher::StdHasher` as hash map builder.
type FastMap<H, T> = HashMap<<H as KeyHasher>::Out, T, hash::BuildHasherDefault<<H as KeyHasher>::StdHasher>>;
/// Reference-counted memory-based `HashDB` implementation.
///
/// Use `new()` to create a new database. Insert items with `insert()`, remove items
@ -42,11 +44,14 @@ use rlp::NULL_RLP;
/// # Example
/// ```rust
/// extern crate hashdb;
/// extern crate keccak_hasher;
/// extern crate memorydb;
///
/// use hashdb::*;
/// use keccak_hasher::KeccakHasher;
/// use memorydb::*;
/// fn main() {
/// let mut m = MemoryDB::new();
/// let mut m = MemoryDB::<KeccakHasher>::new();
/// let d = "Hello world!".as_bytes();
///
/// let k = m.insert(d);
@ -77,15 +82,17 @@ use rlp::NULL_RLP;
/// }
/// ```
#[derive(Default, Clone, PartialEq)]
pub struct MemoryDB {
data: H256FastMap<(DBValue, i32)>,
pub struct MemoryDB<H: KeyHasher> {
data: FastMap<H, (DBValue, i32)>,
hashed_null_node: H::Out,
}
impl MemoryDB {
impl<H: KeyHasher> MemoryDB<H> {
/// Create a new instance of the memory DB.
pub fn new() -> MemoryDB {
pub fn new() -> MemoryDB<H> {
MemoryDB {
data: H256FastMap::default(),
data: FastMap::<H,_>::default(),
hashed_null_node: H::hash(&NULL_RLP)
}
}
@ -94,11 +101,15 @@ impl MemoryDB {
/// # Examples
/// ```rust
/// extern crate hashdb;
/// extern crate keccak_hasher;
/// extern crate memorydb;
///
/// use hashdb::*;
/// use keccak_hasher::KeccakHasher;
/// use memorydb::*;
///
/// fn main() {
/// let mut m = MemoryDB::new();
/// let mut m = MemoryDB::<KeccakHasher>::new();
/// let hello_bytes = "Hello world!".as_bytes();
/// let hash = m.insert(hello_bytes);
/// assert!(m.contains(&hash));
@ -116,8 +127,8 @@ impl MemoryDB {
}
/// Return the internal map of hashes to data, clearing the current state.
pub fn drain(&mut self) -> H256FastMap<(DBValue, i32)> {
mem::replace(&mut self.data, H256FastMap::default())
pub fn drain(&mut self) -> FastMap<H, (DBValue, i32)> {
mem::replace(&mut self.data, FastMap::<H,_>::default())
}
/// Grab the raw information associated with a key. Returns None if the key
@ -125,8 +136,8 @@ impl MemoryDB {
///
/// Even when Some is returned, the data is only guaranteed to be useful
/// when the refs > 0.
pub fn raw(&self, key: &H256) -> Option<(DBValue, i32)> {
if key == &KECCAK_NULL_RLP {
pub fn raw(&self, key: &<H as KeyHasher>::Out) -> Option<(DBValue, i32)> {
if key == &self.hashed_null_node {
return Some((DBValue::from_slice(&NULL_RLP), 1));
}
self.data.get(key).cloned()
@ -139,8 +150,8 @@ impl MemoryDB {
/// Remove an element and delete it from storage if reference count reaches zero.
/// If the value was purged, return the old value.
pub fn remove_and_purge(&mut self, key: &H256) -> Option<DBValue> {
if key == &KECCAK_NULL_RLP {
pub fn remove_and_purge(&mut self, key: &<H as KeyHasher>::Out) -> Option<DBValue> {
if key == &self.hashed_null_node {
return None;
}
match self.data.entry(key.clone()) {
@ -177,19 +188,9 @@ impl MemoryDB {
}
}
impl HashDB for MemoryDB {
fn get(&self, key: &H256) -> Option<DBValue> {
if key == &KECCAK_NULL_RLP {
return Some(DBValue::from_slice(&NULL_RLP));
}
impl<H: KeyHasher> HashDB<H> for MemoryDB<H> {
match self.data.get(key) {
Some(&(ref d, rc)) if rc > 0 => Some(d.clone()),
_ => None
}
}
fn keys(&self) -> HashMap<H256, i32> {
fn keys(&self) -> HashMap<H::Out, i32> {
self.data.iter()
.filter_map(|(k, v)| if v.1 != 0 {
Some((*k, v.1))
@ -199,8 +200,19 @@ impl HashDB for MemoryDB {
.collect()
}
fn contains(&self, key: &H256) -> bool {
if key == &KECCAK_NULL_RLP {
fn get(&self, key: &H::Out) -> Option<DBValue> {
if key == &self.hashed_null_node {
return Some(DBValue::from_slice(&NULL_RLP));
}
match self.data.get(key) {
Some(&(ref d, rc)) if rc > 0 => Some(d.clone()),
_ => None
}
}
fn contains(&self, key: &H::Out) -> bool {
if key == &self.hashed_null_node {
return true;
}
@ -210,15 +222,15 @@ impl HashDB for MemoryDB {
}
}
fn insert(&mut self, value: &[u8]) -> H256 {
fn insert(&mut self, value: &[u8]) -> H::Out {
if value == &NULL_RLP {
return KECCAK_NULL_RLP.clone();
return self.hashed_null_node.clone();
}
let key = keccak(value);
let key = H::hash(value);
match self.data.entry(key) {
Entry::Occupied(mut entry) => {
let &mut (ref mut old_value, ref mut rc) = entry.get_mut();
if *rc >= -0x80000000i32 && *rc <= 0 {
if *rc <= 0 {
*old_value = DBValue::from_slice(value);
}
*rc += 1;
@ -230,7 +242,7 @@ impl HashDB for MemoryDB {
key
}
fn emplace(&mut self, key: H256, value: DBValue) {
fn emplace(&mut self, key:H::Out, value: DBValue) {
if &*value == &NULL_RLP {
return;
}
@ -238,7 +250,7 @@ impl HashDB for MemoryDB {
match self.data.entry(key) {
Entry::Occupied(mut entry) => {
let &mut (ref mut old_value, ref mut rc) = entry.get_mut();
if *rc >= -0x80000000i32 && *rc <= 0 {
if *rc <= 0 {
*old_value = value;
}
*rc += 1;
@ -249,8 +261,8 @@ impl HashDB for MemoryDB {
}
}
fn remove(&mut self, key: &H256) {
if key == &KECCAK_NULL_RLP {
fn remove(&mut self, key: &H::Out) {
if key == &self.hashed_null_node {
return;
}
@ -266,17 +278,26 @@ impl HashDB for MemoryDB {
}
}
impl<H: KeyHasher> AsHashDB<H> for MemoryDB<H> {
fn as_hashdb(&self) -> &HashDB<H> { self }
fn as_hashdb_mut(&mut self) -> &mut HashDB<H> { self }
}
#[cfg(test)]
mod tests {
use keccak::keccak;
use super::*;
use tiny_keccak::Keccak;
use ethereum_types::H256;
use keccak_hasher::KeccakHasher;
#[test]
fn memorydb_remove_and_purge() {
let hello_bytes = b"Hello world!";
let hello_key = keccak(hello_bytes);
let mut hello_key = [0;32];
Keccak::keccak256(hello_bytes, &mut hello_key);
let hello_key = H256(hello_key);
let mut m = MemoryDB::new();
let mut m = MemoryDB::<KeccakHasher>::new();
m.remove(&hello_key);
assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.purge();
@ -286,7 +307,7 @@ mod tests {
m.purge();
assert_eq!(m.raw(&hello_key), None);
let mut m = MemoryDB::new();
let mut m = MemoryDB::<KeccakHasher>::new();
assert!(m.remove_and_purge(&hello_key).is_none());
assert_eq!(m.raw(&hello_key).unwrap().1, -1);
m.insert(hello_bytes);
@ -299,8 +320,8 @@ mod tests {
#[test]
fn consolidate() {
let mut main = MemoryDB::new();
let mut other = MemoryDB::new();
let mut main = MemoryDB::<KeccakHasher>::new();
let mut other = MemoryDB::<KeccakHasher>::new();
let remove_key = other.insert(b"doggo");
main.remove(&remove_key);