// 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 .
use common::*;
use hashdb::*;
use nibbleslice::*;
use rlp::*;
use super::node::Node;
use super::recorder::{Recorder, NoOp};
use super::{Trie, TrieItem, TrieError};
/// A `Trie` implementation using a generic `HashDB` backing database.
///
/// Use it as a `Trie` trait object. You can use `db()` to get the backing database object, `keys`
/// to get the keys belonging to the trie in the backing database, and `db_items_remaining()` to get
/// which items in the backing database do not belong to this trie. If this is the only trie in the
/// backing database, then `db_items_remaining()` should be empty.
///
/// # Example
/// ```
/// extern crate ethcore_util as util;
///
/// use util::trie::*;
/// use util::hashdb::*;
/// use util::memorydb::*;
/// use util::hash::*;
///
/// fn main() {
/// let mut memdb = MemoryDB::new();
/// let mut root = H256::new();
/// TrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap();
/// let t = TrieDB::new(&memdb, &root).unwrap();
/// assert!(t.contains(b"foo").unwrap());
/// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar");
/// assert!(t.db_items_remaining().unwrap().is_empty());
/// }
/// ```
pub struct TrieDB<'db> {
db: &'db HashDB,
root: &'db H256,
/// The number of hashes performed so far in operations on this trie.
pub hash_count: usize,
}
#[cfg_attr(feature="dev", allow(wrong_self_convention))]
impl<'db> TrieDB<'db> {
/// Create a new trie with the backing database `db` and `root`
/// Returns an error if `root` does not exist
pub fn new(db: &'db HashDB, root: &'db H256) -> super::Result {
if !db.contains(root) {
Err(Box::new(TrieError::InvalidStateRoot(*root)))
} else {
Ok(TrieDB {
db: db,
root: root,
hash_count: 0
})
}
}
/// Get the backing database.
pub fn db(&'db self) -> &'db HashDB {
self.db
}
/// Determine all the keys in the backing database that belong to the trie.
pub fn keys(&self) -> super::Result> {
let mut ret: Vec = Vec::new();
ret.push(self.root.clone());
try!(self.accumulate_keys(try!(self.root_node(&mut NoOp)), &mut ret));
Ok(ret)
}
/// Convert a vector of hashes to a hashmap of hash to occurrences.
pub fn to_map(hashes: Vec) -> HashMap {
let mut r: HashMap = HashMap::new();
for h in hashes.into_iter() {
*r.entry(h).or_insert(0) += 1;
}
r
}
/// Determine occurrences of items in the backing database which are not related to this
/// trie.
pub fn db_items_remaining(&self) -> super::Result> {
let mut ret = self.db.keys();
for (k, v) in Self::to_map(try!(self.keys())).into_iter() {
let keycount = *ret.get(&k).unwrap_or(&0);
match keycount <= v as i32 {
true => ret.remove(&k),
_ => ret.insert(k, keycount - v as i32),
};
}
Ok(ret)
}
/// Recursion helper for `keys`.
fn accumulate_keys(&self, node: Node, acc: &mut Vec) -> super::Result<()> {
let mut handle_payload = |payload| {
let p = Rlp::new(payload);
if p.is_data() && p.size() == 32 {
acc.push(p.as_val());
}
self.accumulate_keys(try!(self.get_node(payload, &mut NoOp, 0)), acc)
};
match node {
Node::Extension(_, payload) => try!(handle_payload(payload)),
Node::Branch(payloads, _) => for payload in &payloads { try!(handle_payload(payload)) },
_ => {},
}
Ok(())
}
/// Get the root node's RLP.
fn root_node(&self, r: &mut R) -> super::Result {
self.root_data(r).map(Node::decoded)
}
/// Get the data of the root node.
fn root_data<'a, R: 'a + Recorder>(&self, r: &'a mut R) -> super::Result<&[u8]> {
self.db.get(self.root).ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root)))
.map(|node| { r.record(self.root, node, 0); node })
}
/// Get the root node as a `Node`.
fn get_node<'a, R: 'a + Recorder>(&'db self, node: &'db [u8], r: &'a mut R, depth: u32) -> super::Result {
self.get_raw_or_lookup(node, r, depth).map(Node::decoded)
}
/// Indentation helper for `formal_all`.
fn fmt_indent(&self, f: &mut fmt::Formatter, size: usize) -> fmt::Result {
for _ in 0..size {
try!(write!(f, " "));
}
Ok(())
}
/// Recursion helper for implementation of formatting trait.
fn fmt_all(&self, node: Node, f: &mut fmt::Formatter, deepness: usize) -> fmt::Result {
match node {
Node::Leaf(slice, value) => try!(writeln!(f, "'{:?}: {:?}.", slice, value.pretty())),
Node::Extension(ref slice, item) => {
try!(write!(f, "'{:?} ", slice));
if let Ok(node) = self.get_node(item, &mut NoOp, 0) {
try!(self.fmt_all(node, f, deepness));
}
},
Node::Branch(ref nodes, ref value) => {
try!(writeln!(f, ""));
if let Some(v) = *value {
try!(self.fmt_indent(f, deepness + 1));
try!(writeln!(f, "=: {:?}", v.pretty()))
}
for i in 0..16 {
match self.get_node(nodes[i], &mut NoOp, 0) {
Ok(Node::Empty) => {},
Ok(n) => {
try!(self.fmt_indent(f, deepness + 1));
try!(write!(f, "'{:x} ", i));
try!(self.fmt_all(n, f, deepness + 1));
}
Err(e) => {
try!(write!(f, "ERROR: {}", e));
}
}
}
},
// empty
Node::Empty => {
try!(writeln!(f, ""));
}
};
Ok(())
}
/// Return optional data for a key given as a `NibbleSlice`. Returns `None` if no data exists.
fn do_lookup<'key, R: 'key>(&'db self, key: &NibbleSlice<'key>, r: &'key mut R) -> super::Result