// Copyright 2015-2017 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 .
use std::fmt;
use hashdb::*;
use nibbleslice::*;
use rlp::*;
use super::node::{Node, OwnedNode};
use super::lookup::Lookup;
use super::{Trie, TrieItem, TrieError, TrieIterator, Query};
use bigint::hash::H256;
use {ToPretty, Bytes};
/// 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.
/// Use `get` and `contains` to query values associated with keys in the trie.
///
/// # Example
/// ```
/// extern crate ethcore_util as util;
/// extern crate ethcore_bigint as bigint;
///
/// use util::trie::*;
/// use util::hashdb::*;
/// use util::memorydb::*;
/// use bigint::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(), DBValue::from_slice(b"bar"));
/// }
/// ```
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
}
/// Get the data of the root node.
fn root_data(&self) -> super::Result {
self.db.get(self.root).ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root)))
}
/// Indentation helper for `format_all`.
fn fmt_indent(&self, f: &mut fmt::Formatter, size: usize) -> fmt::Result {
for _ in 0..size {
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) => writeln!(f, "'{:?}: {:?}.", slice, value.pretty())?,
Node::Extension(ref slice, ref item) => {
write!(f, "'{:?} ", slice)?;
if let Ok(node) = self.get_raw_or_lookup(&*item) {
self.fmt_all(Node::decoded(&node), f, deepness)?;
}
},
Node::Branch(ref nodes, ref value) => {
writeln!(f, "")?;
if let Some(ref v) = *value {
self.fmt_indent(f, deepness + 1)?;
writeln!(f, "=: {:?}", v.pretty())?
}
for i in 0..16 {
let node = self.get_raw_or_lookup(&*nodes[i]);
match node.as_ref().map(|n| Node::decoded(&*n)) {
Ok(Node::Empty) => {},
Ok(n) => {
self.fmt_indent(f, deepness + 1)?;
write!(f, "'{:x} ", i)?;
self.fmt_all(n, f, deepness + 1)?;
}
Err(e) => {
write!(f, "ERROR: {}", e)?;
}
}
}
},
// empty
Node::Empty => {
writeln!(f, "")?;
}
};
Ok(())
}
/// 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
/// may require a database lookup.
fn get_raw_or_lookup(&'db self, node: &'db [u8]) -> super::Result {
// check if its keccak + len
let r = Rlp::new(node);
match r.is_data() && r.size() == 32 {
true => {
let key = r.as_val::();
self.db.get(&key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(key)))
}
false => Ok(DBValue::from_slice(node))
}
}
}
impl<'db> Trie for TrieDB<'db> {
fn iter<'a>(&'a self) -> super::Result + 'a>> {
TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>)
}
fn root(&self) -> &H256 { self.root }
fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> super::Result