From 517c705ab5bd88da4269de4bc1a03c5dcd3651f9 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Thu, 14 Jul 2016 18:06:46 +0200 Subject: [PATCH] In-memory trie operations (#1408) * small cleanups in trie * Memory trie skeleton * decode nodes from RLP * memorytrie -> memorytriedb * implement Trie for MemoryTrie * partially implement insert * implement trie insertion * don't load whole trie into memory, lookup across memory and db * re-implement insertion and lazily load necessary nodes from DB * node removal w/o fixing * reduce churn in node storage * finish remove implementation * committing the in-memory trie * reload root node after commit * generate elastic arrays when converting nodes to rlp * port triedbmut tests over to memorytriedb, fix a few bugs. * hash count and dirty flag * initial attempt for node inspection * back to skeleton * inspection framework * implement insertion * no panic paths in insert * implement deletion without fixing * node fixing * commit nodes to db * tracing targets and bugfix * get tests to pass with a lot of tracing * set playpen iterations to 10 * remove some tracing statements * make TrieMut::root take &mut self * replace triedbmut with memorytriedb * treat empty insert value as removal * add test for null insert * fix some style concerns * trie: use nodehandle for root_node, minor cleanup --- util/src/trie/fatdbmut.rs | 6 +- util/src/trie/mod.rs | 1 - util/src/trie/node.rs | 6 +- util/src/trie/sectriedbmut.rs | 8 +- util/src/trie/triedb.rs | 5 +- util/src/trie/triedbmut.rs | 1518 ++++++++++++++++++--------------- util/src/trie/trietraits.rs | 6 +- 7 files changed, 833 insertions(+), 717 deletions(-) diff --git a/util/src/trie/fatdbmut.rs b/util/src/trie/fatdbmut.rs index e07ee0ed1..0dcc8b406 100644 --- a/util/src/trie/fatdbmut.rs +++ b/util/src/trie/fatdbmut.rs @@ -54,10 +54,14 @@ impl<'db> FatDBMut<'db> { } impl<'db> TrieMut for FatDBMut<'db> { - fn root(&self) -> &H256 { + fn root(&mut self) -> &H256 { self.raw.root() } + fn is_empty(&self) -> bool { + self.raw.is_empty() + } + fn contains(&self, key: &[u8]) -> bool { self.raw.contains(&key.sha3()) } diff --git a/util/src/trie/mod.rs b/util/src/trie/mod.rs index 2843932a7..d608863cd 100644 --- a/util/src/trie/mod.rs +++ b/util/src/trie/mod.rs @@ -38,7 +38,6 @@ pub mod sectriedb; pub mod sectriedbmut; mod fatdb; - mod fatdbmut; pub use self::trietraits::{Trie, TrieMut}; diff --git a/util/src/trie/node.rs b/util/src/trie/node.rs index 8f9351d05..b07410a80 100644 --- a/util/src/trie/node.rs +++ b/util/src/trie/node.rs @@ -25,11 +25,11 @@ pub enum Node<'a> { /// Null trie node; could be an empty root or an empty branch entry. Empty, /// Leaf node; has key slice and value. Value may not be empty. - Leaf(NibbleSlice<'a>, &'a[u8]), + Leaf(NibbleSlice<'a>, &'a [u8]), /// Extension node; has key slice and node data. Data may not be null. - Extension(NibbleSlice<'a>, &'a[u8]), + Extension(NibbleSlice<'a>, &'a [u8]), /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. - Branch([&'a[u8]; 16], Option<&'a [u8]>) + Branch([&'a [u8]; 16], Option<&'a [u8]>) } impl<'a> Node<'a> { diff --git a/util/src/trie/sectriedbmut.rs b/util/src/trie/sectriedbmut.rs index 243e5dec8..e8eb24c1f 100644 --- a/util/src/trie/sectriedbmut.rs +++ b/util/src/trie/sectriedbmut.rs @@ -51,7 +51,13 @@ impl<'db> SecTrieDBMut<'db> { } impl<'db> TrieMut for SecTrieDBMut<'db> { - fn root(&self) -> &H256 { self.raw.root() } + fn root(&mut self) -> &H256 { + self.raw.root() + } + + fn is_empty(&self) -> bool { + self.raw.is_empty() + } fn contains(&self, key: &[u8]) -> bool { self.raw.contains(&key.sha3()) diff --git a/util/src/trie/triedb.rs b/util/src/trie/triedb.rs index e938fcc64..57e5834a0 100644 --- a/util/src/trie/triedb.rs +++ b/util/src/trie/triedb.rs @@ -88,8 +88,7 @@ impl<'db> TrieDB<'db> { pub fn to_map(hashes: Vec) -> HashMap { let mut r: HashMap = HashMap::new(); for h in hashes.into_iter() { - let c = *r.get(&h).unwrap_or(&0); - r.insert(h, c + 1); + *r.entry(h).or_insert(0) += 1; } r } @@ -184,7 +183,7 @@ impl<'db> TrieDB<'db> { /// Return optional data for a key given as a `NibbleSlice`. Returns `None` if no data exists. fn do_lookup<'a, 'key>(&'a self, key: &NibbleSlice<'key>) -> Option<&'a [u8]> where 'a: 'key { - let root_rlp = self.db.get(&self.root).expect("Trie root not found!"); + let root_rlp = self.root_data(); self.get_from_node(&root_rlp, key) } diff --git a/util/src/trie/triedbmut.rs b/util/src/trie/triedbmut.rs index 859bc52e1..8a3d1b9ba 100644 --- a/util/src/trie/triedbmut.rs +++ b/util/src/trie/triedbmut.rs @@ -14,21 +14,249 @@ // 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::journal::Journal; -use super::trietraits::TrieMut; -use super::TrieError; +//! In-memory trie representation. + +use super::{TrieError, TrieMut}; +use super::node::Node as RlpNode; + +use ::{Bytes, HashDB, H256, SHA3_NULL_RLP}; +use ::bytes::ToPretty; +use ::nibbleslice::NibbleSlice; +use ::rlp::{Rlp, RlpStream, View, Stream}; + +use elastic_array::ElasticArray1024; + +use std::collections::{HashSet, VecDeque}; +use std::mem; +use std::ops::Index; + +// For lookups into the Node storage buffer. +// This is deliberately non-copyable. +#[derive(Debug)] +struct StorageHandle(usize); + +// Handles to nodes in the trie. +#[derive(Debug)] +enum NodeHandle { + /// Loaded into memory. + InMemory(StorageHandle), + /// Either a hash or an inline node + Hash(H256), +} + +impl From for NodeHandle { + fn from(handle: StorageHandle) -> Self { + NodeHandle::InMemory(handle) + } +} + +impl From for NodeHandle { + fn from(hash: H256) -> Self { + NodeHandle::Hash(hash) + } +} + +fn empty_children() -> [Option; 16] { + [ + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + ] +} + +/// Node types in the Trie. +#[derive(Debug)] +enum Node { + /// Empty node. + Empty, + /// A leaf node contains the end of a key and a value. + /// This key is encoded from a `NibbleSlice`, meaning it contains + /// a flag indicating it is a leaf. + Leaf(Bytes, Bytes), + /// An extension contains a shared portion of a key and a child node. + /// The shared portion is encoded from a `NibbleSlice` meaning it contains + /// a flag indicating it is an extension. + /// The child node is always a branch. + Extension(Bytes, NodeHandle), + /// A branch has up to 16 children and an optional value. + Branch([Option; 16], Option) +} + +impl Node { + // load an inline node into memory or get the hash to do the lookup later. + fn inline_or_hash(node: &[u8], db: &HashDB, storage: &mut NodeStorage) -> NodeHandle { + let r = Rlp::new(node); + if r.is_data() && r.size() == 32 { + NodeHandle::Hash(r.as_val::()) + } else { + let child = Node::from_rlp(node, db, storage); + NodeHandle::InMemory(storage.alloc(Stored::New(child))) + } + } + + // decode a node from rlp without getting its children. + fn from_rlp(rlp: &[u8], db: &HashDB, storage: &mut NodeStorage) -> Self { + match RlpNode::decoded(rlp) { + RlpNode::Empty => Node::Empty, + RlpNode::Leaf(k, v) => Node::Leaf(k.encoded(true), v.to_owned()), + RlpNode::Extension(partial, cb) => { + let key = partial.encoded(false); + + Node::Extension(key, Self::inline_or_hash(cb, db, storage)) + } + RlpNode::Branch(children_rlp, v) => { + let val = v.map(|x| x.to_owned()); + let mut children = empty_children(); + + for i in 0..16 { + let raw = children_rlp[i]; + let child_rlp = Rlp::new(raw); + if !child_rlp.is_empty() { + children[i] = Some(Self::inline_or_hash(raw, db, storage)); + } + } + + Node::Branch(children, val) + } + } + } + + // encode a node to RLP + // TODO: parallelize + fn to_rlp(self, mut child_cb: F) -> ElasticArray1024 + where F: FnMut(NodeHandle, &mut RlpStream) + { + match self { + Node::Empty => { + let mut stream = RlpStream::new(); + stream.append_empty_data(); + stream.drain() + } + Node::Leaf(partial, value) => { + let mut stream = RlpStream::new_list(2); + stream.append(&partial); + stream.append(&value); + stream.drain() + } + Node::Extension(partial, child) => { + let mut stream = RlpStream::new_list(2); + stream.append(&partial); + child_cb(child, &mut stream); + stream.drain() + } + Node::Branch(mut children, value) => { + let mut stream = RlpStream::new_list(17); + for child in children.iter_mut().map(Option::take) { + if let Some(handle) = child { + child_cb(handle, &mut stream); + } else { + stream.append_empty_data(); + } + } + if let Some(value) = value { + stream.append(&value); + } else { + stream.append_empty_data(); + } + + stream.drain() + } + } + } +} + +// post-inspect action. +enum Action { + // Replace a node with a new one. + Replace(Node), + // Restore the original node. This trusts that the node is actually the original. + Restore(Node), + // if it is a new node, just clears the storage. + Delete, +} + +// post-insert action. Same as action without delete +enum InsertAction { + // Replace a node with a new one. + Replace(Node), + // Restore the original node. + Restore(Node), +} + +impl InsertAction { + fn as_action(self) -> Action { + match self { + InsertAction::Replace(n) => Action::Replace(n), + InsertAction::Restore(n) => Action::Restore(n), + } + } + + // unwrap the node, disregarding replace or restore state. + fn unwrap_node(self) -> Node { + match self { + InsertAction::Replace(n) | InsertAction::Restore(n) => n, + } + } +} + +// What kind of node is stored here. +enum Stored { + // A new node. + New(Node), + // A cached node, loaded from the DB. + Cached(Node, H256), +} + +/// Compact and cache-friendly storage for Trie nodes. +struct NodeStorage { + nodes: Vec, + free_indices: VecDeque, +} + +impl NodeStorage { + /// Create a new storage. + fn empty() -> Self { + NodeStorage { + nodes: Vec::new(), + free_indices: VecDeque::new(), + } + } + + /// Allocate a new node in the storage. + fn alloc(&mut self, stored: Stored) -> StorageHandle { + if let Some(idx) = self.free_indices.pop_front() { + self.nodes[idx] = stored; + StorageHandle(idx) + } else { + self.nodes.push(stored); + StorageHandle(self.nodes.len() - 1) + } + } + + /// Remove a node from the storage, consuming the handle and returning the node. + fn destroy(&mut self, handle: StorageHandle) -> Stored { + let idx = handle.0; + + self.free_indices.push_back(idx); + mem::replace(&mut self.nodes[idx], Stored::New(Node::Empty)) + } +} + +impl<'a> Index<&'a StorageHandle> for NodeStorage { + type Output = Node; + + fn index(&self, handle: &'a StorageHandle) -> &Node { + match self.nodes[handle.0] { + Stored::New(ref node) => node, + Stored::Cached(ref node, _) => node, + } + } +} /// 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. +/// Use it as a `TrieMut` trait object. You can use `db()` to get the backing database object. +/// Note that changes are not committed to the database until `commit` is called. +/// Querying the root or dropping the trie will commit automatically. /// /// # Example /// ``` @@ -48,200 +276,147 @@ use super::TrieError; /// t.insert(b"foo", b"bar"); /// assert!(t.contains(b"foo")); /// assert_eq!(t.get(b"foo").unwrap(), b"bar"); -/// assert!(t.db_items_remaining().is_empty()); /// t.remove(b"foo"); /// assert!(!t.contains(b"foo")); -/// assert!(t.db_items_remaining().is_empty()); /// } /// ``` -pub struct TrieDBMut<'db> { - db: &'db mut HashDB, - root: &'db mut H256, - /// The number of hashes performed so far in operations on this trie. +pub struct TrieDBMut<'a> { + storage: NodeStorage, + db: &'a mut HashDB, + root: &'a mut H256, + root_handle: NodeHandle, + death_row: HashSet, + /// The number of hash operations this trie has performed. + /// Note that none are performed until changes are committed. pub hash_count: usize, } -/// Option-like type allowing either a Node object passthrough or Bytes in the case of data alteration. -enum MaybeChanged<'a> { - Same(Node<'a>), - Changed(Bytes), -} +impl<'a> TrieDBMut<'a> { + /// Create a new trie with backing database `db` and empty `root`. + pub fn new(db: &'a mut HashDB, root: &'a mut H256) -> Self { + *root = SHA3_NULL_RLP; + let root_handle = NodeHandle::Hash(SHA3_NULL_RLP); -#[cfg_attr(feature="dev", allow(wrong_self_convention))] -impl<'db> TrieDBMut<'db> { - /// Create a new trie with the backing database `db` and empty `root` - /// Initialise to the state entailed by the genesis block. - /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut HashDB, root: &'db mut H256) -> Self { - let mut r = TrieDBMut{ + TrieDBMut { + storage: NodeStorage::empty(), db: db, root: root, - hash_count: 0 - }; - - // set root rlp - *r.root = SHA3_NULL_RLP.clone(); - r - } - - /// Create a new trie with the backing database `db` and `root`. - /// Returns an error if `root` does not exist. - pub fn from_existing(db: &'db mut HashDB, root: &'db mut H256) -> Result { - if !db.contains(root) { - Err(TrieError::InvalidStateRoot) - } else { - Ok(TrieDBMut { - db: db, - root: root, - hash_count: 0 - }) + root_handle: root_handle, + death_row: HashSet::new(), + hash_count: 0, } } + /// Create a new trie with the backing database `db` and `root. + /// Returns an error if `root` does not exist. + pub fn from_existing(db: &'a mut HashDB, root: &'a mut H256) -> Result { + if !db.contains(root) { + return Err(TrieError::InvalidStateRoot); + } + + let root_handle = NodeHandle::Hash(*root); + Ok(TrieDBMut { + storage: NodeStorage::empty(), + db: db, + root: root, + root_handle: root_handle, + death_row: HashSet::new(), + hash_count: 0, + }) + } /// Get the backing database. pub fn db(&self) -> &HashDB { self.db } - /// Get the backing database. + /// Get the backing database mutably. pub fn db_mut(&mut self) -> &mut HashDB { self.db } - /// Determine all the keys in the backing database that belong to the trie. - pub fn keys(&self) -> Vec { - let mut ret: Vec = Vec::new(); - ret.push(self.root.clone()); - self.accumulate_keys(self.root_node(), &mut ret); - ret + // cache a node by hash + fn cache(&mut self, hash: H256) -> StorageHandle { + let node_rlp = self.db.get(&hash).expect("Not found!"); + let node = Node::from_rlp(node_rlp, &*self.db, &mut self.storage); + self.storage.alloc(Stored::Cached(node, hash)) } - /// Convert a vector of hashes to a hashmap of hash to occurances. - pub fn to_map(hashes: Vec) -> HashMap { - let mut r: HashMap = HashMap::new(); - for h in hashes.into_iter() { - let c = *r.get(&h).unwrap_or(&0); - r.insert(h, c + 1); - } - r - } - - /// Determine occurances of items in the backing database which are not related to this - /// trie. - pub fn db_items_remaining(&self) -> HashMap { - let mut ret = self.db.keys(); - for (k, v) in Self::to_map(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), - }; - } - ret - } - - /// Set the trie to a new root node's RLP, inserting the new RLP into the backing database - /// and removing the old. - fn set_root_rlp(&mut self, root_data: &[u8]) { - self.db.remove(&self.root); - *self.root = self.db.insert(root_data); - self.hash_count += 1; - trace!("set_root_rlp {:?} {:?}", root_data.pretty(), self.root); - } - - /// Apply the items in `journal` into the backing database. - fn apply(&mut self, journal: Journal) { - self.hash_count += journal.apply(self.db).inserts; - } - - /// Recursion helper for `keys`. - fn accumulate_keys(&self, node: Node, acc: &mut Vec) { - 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(self.get_node(payload), acc); - }; - - match node { - Node::Extension(_, payload) => handle_payload(payload), - Node::Branch(payloads, _) => for payload in &payloads { handle_payload(payload) }, - _ => {}, - } - } - - /// Get the root node's RLP. - fn root_node(&self) -> Node { - Node::decoded(self.db.get(&self.root).expect("Trie root not found!")) - } - - /// Get the root node as a `Node`. - fn get_node<'a>(&'a self, node: &'a [u8]) -> Node { - Node::decoded(self.get_raw_or_lookup(node)) - } - - /// 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, ref item) => { - try!(write!(f, "'{:?} ", slice)); - try!(self.fmt_all(self.get_node(item), f, deepness)); + // inspect a node, choosing either to replace, restore, or delete it. + // if restored or replaced, returns the new node along with a flag of whether it was changed. + fn inspect(&mut self, stored: Stored, inspector: F) -> Option<(Stored, bool)> + where F: FnOnce(&mut Self, Node) -> Action { + match stored { + Stored::New(node) => match inspector(self, node) { + Action::Restore(node) => Some((Stored::New(node), false)), + Action::Replace(node) => Some((Stored::New(node), true)), + Action::Delete => None, }, - 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())) + Stored::Cached(node, hash) => match inspector(self, node) { + Action::Restore(node) => Some((Stored::Cached(node, hash), false)), + Action::Replace(node) => { + self.death_row.insert(hash); + Some((Stored::New(node), true)) } - for i in 0..16 { - match self.get_node(nodes[i]) { - Node::Empty => {}, - n => { - try!(self.fmt_indent(f, deepness + 1)); - try!(write!(f, "'{:x} ", i)); - try!(self.fmt_all(n, f, deepness + 1)); - } + Action::Delete => { + self.death_row.insert(hash); + None + } + }, + } + } + + // walk the trie, attempting to find the key's node. + fn lookup<'x, 'key>(&'x self, partial: NibbleSlice<'key>, handle: &NodeHandle) -> Option<&'x [u8]> + where 'x: 'key { + match *handle { + NodeHandle::Hash(ref hash) => self.do_db_lookup(hash, partial), + NodeHandle::InMemory(ref handle) => match self.storage[handle] { + Node::Empty => None, + Node::Leaf(ref key, ref value) => { + if NibbleSlice::from_encoded(key).0 == partial { + Some(value) + } else { + None + } + } + Node::Extension(ref slice, ref child) => { + let slice = NibbleSlice::from_encoded(slice).0; + if partial.starts_with(&slice) { + self.lookup(partial.mid(slice.len()), child) + } else { + None + } + } + Node::Branch(ref children, ref value) => { + if partial.is_empty() { + value.as_ref().map(|v| &v[..]) + } else { + let idx = partial.at(0); + (&children[idx as usize]).as_ref().and_then(|child| self.lookup(partial.mid(1), child)) } } - }, - // 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<'a, 'key>(&'a self, key: &NibbleSlice<'key>) -> Option<&'a [u8]> where 'a: 'key { - let root_rlp = self.db.get(&self.root).expect("Trie root not found!"); - self.get_from_node(&root_rlp, key) + fn do_db_lookup<'x, 'key>(&'x self, hash: &H256, key: NibbleSlice<'key>) -> Option<&'x [u8]> where 'x: 'key { + self.db.get(hash).and_then(|node_rlp| self.get_from_db_node(&node_rlp, key)) } /// Recursible function to retrieve the value given a `node` and a partial `key`. `None` if no /// value exists for the key. /// /// Note: Not a public API; use Trie trait functions. - fn get_from_node<'a, 'key>(&'a self, node: &'a [u8], key: &NibbleSlice<'key>) -> Option<&'a [u8]> where 'a: 'key { - match Node::decoded(node) { - Node::Leaf(ref slice, ref value) if key == slice => Some(value), - Node::Extension(ref slice, ref item) if key.starts_with(slice) => { - self.get_from_node(self.get_raw_or_lookup(item), &key.mid(slice.len())) + fn get_from_db_node<'x, 'key>(&'x self, node: &'x [u8], key: NibbleSlice<'key>) -> Option<&'x [u8]> where 'x: 'key { + match RlpNode::decoded(node) { + RlpNode::Leaf(ref slice, ref value) if &key == slice => Some(value), + RlpNode::Extension(ref slice, ref item) if key.starts_with(slice) => { + self.get_from_db_node(self.get_raw_or_lookup(item), key.mid(slice.len())) }, - Node::Branch(ref nodes, value) => match key.is_empty() { + RlpNode::Branch(ref nodes, value) => match key.is_empty() { true => value, - false => self.get_from_node(self.get_raw_or_lookup(nodes[key.at(0) as usize]), &key.mid(1)) + false => self.get_from_db_node(self.get_raw_or_lookup(nodes[key.at(0) as usize]), key.mid(1)) }, _ => None } @@ -250,7 +425,7 @@ impl<'db> TrieDBMut<'db> { /// 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<'a>(&'a self, node: &'a [u8]) -> &'a [u8] { + fn get_raw_or_lookup<'x>(&'x self, node: &'x [u8]) -> &'x [u8] { // check if its sha3 + len let r = Rlp::new(node); match r.is_data() && r.size() == 32 { @@ -259,259 +434,275 @@ impl<'db> TrieDBMut<'db> { } } - /// Insert a `key` and `value` pair into the trie. - /// - /// Note: Not a public API; use Trie trait functions. - fn insert_ns(&mut self, key: &NibbleSlice, value: &[u8]) { - trace!("ADD: {:?} {:?}", key, value.pretty()); - // determine what the new root is, insert new nodes and remove old as necessary. - let mut todo: Journal = Journal::new(); - let root_rlp = self.augmented(self.db.get(&self.root).expect("Trie root not found!"), key, value, &mut todo); - self.apply(todo); - self.set_root_rlp(&root_rlp); - trace!("/"); + /// insert a key, value pair into the trie, creating new nodes if necessary. + fn insert_at(&mut self, handle: NodeHandle, partial: NibbleSlice, value: Bytes) -> (StorageHandle, bool) { + let h = match handle { + NodeHandle::InMemory(h) => h, + NodeHandle::Hash(h) => self.cache(h) + }; + let stored = self.storage.destroy(h); + let (new_stored, changed) = self.inspect(stored, move |trie, stored| { + trie.insert_inspector(stored, partial, value).as_action() + }).expect("Insertion never deletes."); + + (self.storage.alloc(new_stored), changed) } - /// Remove a `key` and `value` pair from the trie. - /// - /// Note: Not a public API; use Trie trait functions. - fn remove_ns(&mut self, key: &NibbleSlice) { - trace!("DELETE: {:?}", key); - // determine what the new root is, insert new nodes and remove old as necessary. - let mut todo: Journal = Journal::new(); - match self.cleared_from_slice(self.db.get(&self.root).expect("Trie root not found!"), key, &mut todo) { - Some(root_rlp) => { - self.apply(todo); - self.set_root_rlp(&root_rlp); - }, - None => { - trace!("no change needed"); + /// the insertion inspector. + fn insert_inspector(&mut self, node: Node, partial: NibbleSlice, value: Bytes) -> InsertAction { + trace!(target: "trie", "augmented (partial: {:?}, value: {:?})", partial, value.pretty()); + + match node { + Node::Empty => { + trace!(target: "trie", "empty: COMPOSE"); + InsertAction::Replace(Node::Leaf(partial.encoded(true), value)) + } + Node::Branch(mut children, stored_value) => { + trace!(target: "trie", "branch: ROUTE,AUGMENT"); + + if partial.is_empty() { + let unchanged = stored_value.as_ref() == Some(&value); + let branch = Node::Branch(children, Some(value)); + match unchanged { + true => InsertAction::Restore(branch), + false => InsertAction::Replace(branch), + } + } else { + let idx = partial.at(0) as usize; + let partial = partial.mid(1); + if let Some(child) = children[idx].take() { + // original had something there. recurse down into it. + let (new_child, changed) = self.insert_at(child, partial, value); + children[idx] = Some(new_child.into()); + if !changed { + // the new node we composed didn't change. that means our branch is untouched too. + return InsertAction::Restore(Node::Branch(children, stored_value)); + } + } else { + // original had nothing there. compose a leaf. + let leaf = self.storage.alloc(Stored::New(Node::Leaf(partial.encoded(true), value))); + children[idx] = Some(leaf.into()); + } + + InsertAction::Replace(Node::Branch(children, stored_value)) + } + } + Node::Leaf(encoded, stored_value) => { + let existing_key = NibbleSlice::from_encoded(&encoded).0; + let cp = partial.common_prefix(&existing_key); + if cp == existing_key.len() && cp == partial.len() { + trace!(target: "trie", "equivalent-leaf: REPLACE"); + // equivalent leaf. + match stored_value == value { + // unchanged. restore + true => InsertAction::Restore(Node::Leaf(encoded.clone(), value)), + false => InsertAction::Replace(Node::Leaf(encoded.clone(), value)), + } + } else if cp == 0 { + trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); + + // one of us isn't empty: transmute to branch here + let mut children = empty_children(); + let branch = if existing_key.is_empty() { + // always replace since branch isn't leaf. + Node::Branch(children, Some(stored_value)) + } else { + let idx = existing_key.at(0) as usize; + let new_leaf = Node::Leaf(existing_key.mid(1).encoded(true), stored_value); + children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); + + Node::Branch(children, None) + }; + + // always replace because whatever we get out here is not the branch we started with. + InsertAction::Replace(self.insert_inspector(branch, partial, value).unwrap_node()) + } else if cp == existing_key.len() { + trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + + // fully-shared prefix for an extension. + // make a stub branch and an extension. + let branch = Node::Branch(empty_children(), Some(stored_value)); + // augment the new branch. + let branch = self.insert_inspector(branch, partial.mid(cp), value).unwrap_node(); + + // always replace since we took a leaf and made an extension. + let branch_handle = self.storage.alloc(Stored::New(branch)).into(); + InsertAction::Replace(Node::Extension(existing_key.encoded(false), branch_handle)) + } else { + trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); + + // partially-shared prefix for an extension. + // start by making a leaf. + let low = Node::Leaf(existing_key.mid(cp).encoded(true), stored_value); + // augment it. this will result in the Leaf -> cp == 0 routine, + // which creates a branch. + let augmented_low = self.insert_inspector(low, partial.mid(cp), value).unwrap_node(); + + // make an extension using it. this is a replacement. + InsertAction::Replace(Node::Extension( + existing_key.encoded_leftmost(cp, false), + self.storage.alloc(Stored::New(augmented_low)).into() + )) + } + } + Node::Extension(encoded, child_branch) => { + let existing_key = NibbleSlice::from_encoded(&encoded).0; + let cp = partial.common_prefix(&existing_key); + if cp == 0 { + trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); + + // partial isn't empty: make a branch here + // extensions may not have empty partial keys. + assert!(!existing_key.is_empty()); + let idx = existing_key.at(0) as usize; + + let mut children = empty_children(); + children[idx] = if existing_key.len() == 1 { + // direct extension, just replace. + Some(child_branch) + } else { + // more work required after branching. + let ext = Node::Extension(existing_key.mid(1).encoded(false), child_branch); + Some(self.storage.alloc(Stored::New(ext)).into()) + }; + + // continue inserting. + InsertAction::Replace(self.insert_inspector(Node::Branch(children, None), partial, value).unwrap_node()) + } else if cp == existing_key.len() { + trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + + // fully-shared prefix. + + // insert into the child node. + let (new_child, changed) = self.insert_at(child_branch, partial.mid(cp), value); + let new_ext = Node::Extension(existing_key.encoded(false), new_child.into()); + + // if the child branch wasn't changed, meaning this extension remains the same. + match changed { + true => InsertAction::Replace(new_ext), + false => InsertAction::Restore(new_ext), + } + } else { + trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); + + // partially-shared. + let low = Node::Extension(existing_key.mid(cp).encoded(false), child_branch); + // augment the extension. this will take the cp == 0 path, creating a branch. + let augmented_low = self.insert_inspector(low, partial.mid(cp), value).unwrap_node(); + + // always replace, since this extension is not the one we started with. + // this is known because the partial key is only the common prefix. + InsertAction::Replace(Node::Extension( + existing_key.encoded_leftmost(cp, false), + self.storage.alloc(Stored::New(augmented_low)).into() + )) + } } } - trace!("/"); } - /// Compose a leaf node in RLP given the `partial` key and `value`. - fn compose_leaf(partial: &NibbleSlice, value: &[u8]) -> Bytes { - trace!("compose_leaf {:?} {:?} ({:?})", partial, value.pretty(), partial.encoded(true).pretty()); - let mut s = RlpStream::new_list(2); - s.append(&partial.encoded(true)); - s.append(&value); - let r = s.out(); - trace!("compose_leaf: -> {:?}", r.pretty()); - r + /// Remove a node from the trie based on key. + fn remove_at(&mut self, handle: NodeHandle, partial: NibbleSlice) -> Option<(StorageHandle, bool)> { + let stored = match handle { + NodeHandle::InMemory(h) => self.storage.destroy(h), + NodeHandle::Hash(h) => { + let handle = self.cache(h); + self.storage.destroy(handle) + } + }; + + self.inspect(stored, move |trie, node| trie.remove_inspector(node, partial)) + .map(|(new, changed)| (self.storage.alloc(new), changed)) } - /// Compose a raw extension/leaf node in RLP given the `partial` key, `raw_payload` and whether it `is_leaf`. - fn compose_raw(partial: &NibbleSlice, raw_payload: &[u8], is_leaf: bool) -> Bytes { - trace!("compose_raw {:?} {:?} {:?} ({:?})", partial, raw_payload.pretty(), is_leaf, partial.encoded(is_leaf)); - let mut s = RlpStream::new_list(2); - s.append(&partial.encoded(is_leaf)); - s.append_raw(raw_payload, 1); - let r = s.out(); - trace!("compose_raw: -> {:?}", r.pretty()); - r - } - - /// Compose a branch node in RLP with a particular `value` sitting in the value position (17th place). - fn compose_stub_branch(value: &[u8]) -> Bytes { - let mut s = RlpStream::new_list(17); - for _ in 0..16 { s.append_empty_data(); } - s.append(&value); - s.out() - } - - /// Compose an extension node's RLP with the `partial` key and `raw_payload`. - fn compose_extension(partial: &NibbleSlice, raw_payload: &[u8]) -> Bytes { - Self::compose_raw(partial, raw_payload, false) - } - - /// Return the bytes encoding the node represented by `rlp`. `journal` will record necessary - /// removal instructions from the backing database. - fn take_node<'a, 'rlp_view>(&'a self, rlp: &'rlp_view Rlp<'a>, journal: &mut Journal) -> &'a [u8] where 'a: 'rlp_view { - if rlp.is_list() { - trace!("take_node {:?} (inline)", rlp.as_raw().pretty()); - rlp.as_raw() - } - else if rlp.is_data() && rlp.size() == 32 { - let h = rlp.as_val(); - let r = self.db.get(&h).unwrap_or_else(||{ - println!("Node not found! rlp={:?}, node_hash={:?}", rlp.as_raw().pretty(), h); - println!("Journal: {:?}", journal); - panic!(); - }); - trace!("take_node {:?} (indirect for {:?})", rlp.as_raw().pretty(), r); - journal.delete_node_sha3(h); - r - } - else { - trace!("take_node {:?} (???)", rlp.as_raw().pretty()); - panic!("Empty or invalid node given?"); - } - } - - #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] - /// Determine the RLP of the node, assuming we're inserting `partial` into the - /// node currently of data `old`. This will *not* delete any hash of `old` from the database; - /// it will just return the new RLP that includes the new node. - /// - /// `journal` will record the database updates so as to make the returned RLP valid through inserting - /// and deleting nodes as necessary. - /// - /// **This operation will not insert the new node nor destroy the original.** - fn augmented(&self, old: &[u8], partial: &NibbleSlice, value: &[u8], journal: &mut Journal) -> Bytes { - trace!("augmented (old: {:?}, partial: {:?}, value: {:?})", old.pretty(), partial, value.pretty()); - // already have an extension. either fast_forward, cleve or transmute_to_branch. - let old_rlp = Rlp::new(old); - match old_rlp.prototype() { - Prototype::List(17) => { - trace!("branch: ROUTE,AUGMENT"); - // already have a branch. route and augment. - let mut s = RlpStream::new_list(17); - let index = if partial.is_empty() {16} else {partial.at(0) as usize}; - for i in 0..17 { - match index == i { - // not us - leave alone. - false => { s.append_raw(old_rlp.at(i).as_raw(), 1); }, - // branch-leaf entry - just replace. - true if i == 16 => { s.append(&value); }, - // original had empty slot - place a leaf there. - true if old_rlp.at(i).is_empty() => journal.new_node(Self::compose_leaf(&partial.mid(1), value), &mut s), - // original has something there already; augment. - true => { - let new = self.augmented(self.take_node(&old_rlp.at(i), journal), &partial.mid(1), value, journal); - journal.new_node(new, &mut s); + /// the removal inspector + fn remove_inspector(&mut self, node: Node, partial: NibbleSlice) -> Action { + match (node, partial.is_empty()) { + (Node::Empty, _) => Action::Delete, + (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), + (Node::Branch(children, _), true) => { + // always replace since we took the value out. + Action::Replace(self.fix(Node::Branch(children, None))) + } + (Node::Branch(mut children, value), false) => { + let idx = partial.at(0) as usize; + if let Some(child) = children[idx].take() { + trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); + match self.remove_at(child, partial.mid(1)) { + Some((new, changed)) => { + children[idx] = Some(new.into()); + let branch = Node::Branch(children, value); + match changed { + // child was changed, so we were too. + true => Action::Replace(branch), + // unchanged, so we are too. + false => Action::Restore(branch), + } + } + None => { + // the child we took was deleted. + // the node may need fixing. + trace!(target: "trie", "branch child deleted, partial={:?}", partial); + Action::Replace(self.fix(Node::Branch(children, value))) } } + } else { + // no change needed. + Action::Restore(Node::Branch(children, value)) + } + } + (Node::Leaf(encoded, value), _) => { + if NibbleSlice::from_encoded(&encoded).0 == partial { + // this is the node we were looking for. Let's delete it. + Action::Delete + } else { + // leaf the node alone. + trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::from_encoded(&encoded).0); + Action::Restore(Node::Leaf(encoded, value)) + } + } + (Node::Extension(encoded, child_branch), _) => { + let (cp, existing_len) = { + let existing_key = NibbleSlice::from_encoded(&encoded).0; + (existing_key.common_prefix(&partial), existing_key.len()) + }; + if cp == existing_len { + // try to remove from the child branch. + trace!(target: "trie", "removing from extension child, partial={:?}", partial); + match self.remove_at(child_branch, partial.mid(cp)) { + Some((new_child, changed)) => { + let new_child = new_child.into(); + + // if the child branch was unchanged, then the extension is too. + // otherwise, this extension may need fixing. + match changed { + true => Action::Replace(self.fix(Node::Extension(encoded, new_child))), + false => Action::Restore(Node::Extension(encoded, new_child)), + } + } + None => { + // the whole branch got deleted. + // that means that this extension is useless. + Action::Delete + } + } + } else { + // partway through an extension -- nothing to do here. + Action::Restore(Node::Extension(encoded, child_branch)) } - s.out() - }, - Prototype::List(2) => { - let existing_key_rlp = old_rlp.at(0); - let (existing_key, is_leaf) = NibbleSlice::from_encoded(existing_key_rlp.data()); - match (is_leaf, partial.common_prefix(&existing_key)) { - (true, cp) if cp == existing_key.len() && partial.len() == existing_key.len() => { - // equivalent-leaf: replace - trace!("equivalent-leaf: REPLACE"); - Self::compose_leaf(partial, value) - }, - (_, 0) => { - // one of us isn't empty: transmute to branch here - trace!("no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); - assert!(is_leaf || !existing_key.is_empty()); // extension nodes are not allowed to have empty partial keys. - let mut s = RlpStream::new_list(17); - let index = if existing_key.is_empty() {16} else {existing_key.at(0)}; - for i in 0..17 { - match is_leaf { - // not us - empty. - _ if index != i => { s.append_empty_data(); }, - // branch-value: just replace. - true if i == 16 => { s.append_raw(old_rlp.at(1).as_raw(), 1); }, - // direct extension: just replace. - false if existing_key.len() == 1 => { s.append_raw(old_rlp.at(1).as_raw(), 1); }, - // original has empty slot. - true => journal.new_node(Self::compose_leaf(&existing_key.mid(1), old_rlp.at(1).data()), &mut s), - // additional work required after branching. - false => journal.new_node(Self::compose_extension(&existing_key.mid(1), old_rlp.at(1).as_raw()), &mut s), - } - }; - self.augmented(&s.out(), partial, value, journal) - }, - (_, cp) if cp == existing_key.len() => { - trace!("complete-prefix (cp={:?}): AUGMENT-AT-END", cp); - // fully-shared prefix for this extension: - // transform to an extension + augmented version of onward node. - let downstream_node: Bytes = match is_leaf { - // no onward node because we're a leaf - create fake stub and use that. - true => self.augmented(&Self::compose_stub_branch(old_rlp.at(1).data()), &partial.mid(cp), value, journal), - false => self.augmented(self.take_node(&old_rlp.at(1), journal), &partial.mid(cp), value, journal), - }; - - trace!("create_extension partial: {:?}, downstream_node: {:?}", existing_key, downstream_node.pretty()); - let mut s = RlpStream::new_list(2); - s.append(&existing_key.encoded(false)); - journal.new_node(downstream_node, &mut s); - s.out() - }, - (_, cp) => { - // partially-shared prefix for this extension: - // split into two extensions, high and low, pass the - // low through augment with the value before inserting the result - // into high to create the new. - - // TODO: optimise by doing this without creating augmented_low. - - trace!("partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); - - // low (farther from root) - let low = Self::compose_raw(&existing_key.mid(cp), old_rlp.at(1).as_raw(), is_leaf); - let augmented_low = self.augmented(&low, &partial.mid(cp), value, journal); - - // high (closer to root) - let mut s = RlpStream::new_list(2); - s.append(&existing_key.encoded_leftmost(cp, false)); - journal.new_node(augmented_low, &mut s); - s.out() - }, - } - }, - Prototype::Data(0) => { - trace!("empty: COMPOSE"); - Self::compose_leaf(partial, value) - }, - _ => panic!("Invalid RLP for node: {:?}", old.pretty()), - } - } - - /// Given a `MaybeChanged` result `n`, return the node's RLP regardless of whether it changed. - fn encoded(n: MaybeChanged) -> Bytes { - match n { - MaybeChanged::Same(n) => n.encoded(), - MaybeChanged::Changed(b) => b, - } - } - - /// Fix the node payload's sizes in `n`, replacing any over-size payloads with the hashed reference - /// and placing the payload DB insertions in the `journal`. - fn fixed_indirection<'a>(n: Node<'a>, journal: &mut Journal) -> MaybeChanged<'a> { - match n { - Node::Extension(partial, payload) if payload.len() >= 32 && Rlp::new(payload).is_list() => { - // make indirect - MaybeChanged::Changed(Node::Extension(partial, &Node::decoded(payload).encoded_and_added(journal)).encoded()) - }, - Node::Branch(payloads, value) => { - // check each child isn't too big - // TODO OPTIMISE - should really check at the point of (re-)constructing the branch. - for i in 0..16 { - if payloads[i].len() >= 32 && Rlp::new(payloads[i]).is_list() { - let n = Node::decoded(payloads[i]).encoded_and_added(journal); - let mut new_nodes = payloads; - new_nodes[i] = &n; - return MaybeChanged::Changed(Node::Branch(new_nodes, value).encoded()) - } - } - MaybeChanged::Same(n) } - _ => MaybeChanged::Same(n), } } - /// Given a node `n` which may be in an _invalid state_, fix it such that it is then in a valid + /// Given a node which may be in an _invalid state_, fix it such that it is then in a valid /// state. /// /// _invalid state_ means: /// - Branch node where there is only a single entry; /// - Extension node followed by anything other than a Branch node. - /// - Extension node with a child which has too many bytes to be inline. - /// - /// `journal` will record the database updates so as to make the returned RLP valid through inserting - /// and deleting nodes as necessary. - /// - /// **This operation will not insert the new node nor destroy the original.** - fn fixed<'a, 'b>(&'a self, n: Node<'b>, journal: &mut Journal) -> MaybeChanged<'b> where 'a: 'b { - trace!("fixed node={:?}", n); - match n { - Node::Branch(nodes, node_value) => { + fn fix(&mut self, node: Node) -> Node { + match node { + Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. #[derive(Debug)] enum UsedIndex { @@ -521,170 +712,223 @@ impl<'db> TrieDBMut<'db> { }; let mut used_index = UsedIndex::None; for i in 0..16 { - match (nodes[i] == NULL_RLP, &used_index) { + match (children[i].is_none(), &used_index) { (false, &UsedIndex::None) => used_index = UsedIndex::One(i as u8), - (false, &UsedIndex::One(_)) => used_index = UsedIndex::Many, - (_, _) => {}, + (false, &UsedIndex::One(_)) => { + used_index = UsedIndex::Many; + break; + } + _ => continue, } } - trace!("branch: used_index={:?}, node_value={:?}", used_index, node_value); - match (used_index, node_value) { + + match (used_index, value) { (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), - (UsedIndex::One(a), None) => { // one onward node - // transmute to extension. - // TODO: OPTIMISE: - don't call fixed again but put the right node in straight away here. - // call fixed again since the transmute may cause invalidity. - let new_partial: [u8; 1] = [a; 1]; - MaybeChanged::Changed(Self::encoded(self.fixed(Node::Extension(NibbleSlice::new_offset(&new_partial[..], 1), nodes[a as usize]), journal))) - }, - (UsedIndex::None, Some(value)) => { // one leaf value - // transmute to leaf. - // call fixed again since the transmute may cause invalidity. - MaybeChanged::Changed(Self::encoded(self.fixed(Node::Leaf(NibbleSlice::new(&b""[..]), value), journal))) + (UsedIndex::One(a), None) => { + // only one onward node. make an extension. + let new_partial = NibbleSlice::new_offset(&[a], 1).encoded(false); + let new_node = Node::Extension(new_partial, children[a as usize].take().unwrap()); + self.fix(new_node) + } + (UsedIndex::None, Some(value)) => { + // make a leaf. + trace!(target: "trie", "fixing: branch -> leaf"); + Node::Leaf(NibbleSlice::new(&[]).encoded(true), value) + } + (_, value) => { + // all is well. + trace!(target: "trie", "fixing: restoring branch"); + Node::Branch(children, value) } - _ => { // onwards node(s) and/or leaf - // no transmute needed, but should still fix the indirection. - trace!("no-transmute: FIXINDIRECTION"); - Self::fixed_indirection(Node::Branch(nodes, node_value), journal) - }, } - }, - Node::Extension(partial, payload) => { - match Node::decoded(self.get_raw_or_lookup(payload)) { - Node::Extension(sub_partial, sub_payload) => { - // combine with node below - journal.delete_node(payload); - MaybeChanged::Changed(Self::encoded(Self::fixed_indirection(Node::Extension(NibbleSlice::new_composed(&partial, &sub_partial), sub_payload), journal))) - }, - Node::Leaf(sub_partial, sub_value) => { - // combine with node below - journal.delete_node(payload); - MaybeChanged::Changed(Self::encoded(Self::fixed_indirection(Node::Leaf(NibbleSlice::new_composed(&partial, &sub_partial), sub_value), journal))) - }, - // no change, might still have an oversize node inline - fix indirection - _ => Self::fixed_indirection(n, journal), + } + Node::Extension(partial, child) => { + let stored = match child { + NodeHandle::InMemory(h) => self.storage.destroy(h), + NodeHandle::Hash(h) => { + let handle = self.cache(h); + self.storage.destroy(handle) + } + }; + + let (child_node, maybe_hash) = match stored { + Stored::New(node) => (node, None), + Stored::Cached(node, hash) => (node, Some(hash)) + }; + + match child_node { + Node::Extension(sub_partial, sub_child) => { + // combine with node below. + if let Some(hash) = maybe_hash { + // delete the cached child since we are going to replace it. + self.death_row.insert(hash); + } + let partial = NibbleSlice::from_encoded(&partial).0; + let sub_partial = NibbleSlice::from_encoded(&sub_partial).0; + + let new_partial = NibbleSlice::new_composed(&partial, &sub_partial); + trace!(target: "trie", "fixing: extension combination. new_partial={:?}", new_partial); + self.fix(Node::Extension(new_partial.encoded(false), sub_child)) + } + Node::Leaf(sub_partial, value) => { + // combine with node below. + if let Some(hash) = maybe_hash { + // delete the cached child since we are going to replace it. + self.death_row.insert(hash); + } + let partial = NibbleSlice::from_encoded(&partial).0; + let sub_partial = NibbleSlice::from_encoded(&sub_partial).0; + + let new_partial = NibbleSlice::new_composed(&partial, &sub_partial); + trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", new_partial); + Node::Leaf(new_partial.encoded(true), value) + } + child_node => { + trace!(target: "trie", "fixing: restoring extension"); + + // reallocate the child node. + let stored = if let Some(hash) = maybe_hash { + Stored::Cached(child_node, hash) + } else { + Stored::New(child_node) + }; + + Node::Extension(partial, self.storage.alloc(stored).into()) + } } - }, - // leaf or empty. no change. - n => { MaybeChanged::Same(n) } + } + other => other, // only ext and branch need fixing. } } - /// Determine the RLP of the node, assuming we're removing `partial` from the - /// node currently of data `old`. This will *not* delete any hash of `old` from the database; - /// it will just return the new RLP that represents the new node. - /// `None` may be returned should no change be needed. - /// - /// `journal` will record the database updates so as to make the returned RLP valid through inserting - /// and deleting nodes as necessary. - /// - /// **This operation will not insert the new node nor destroy the original.** - fn cleared_from_slice(&self, old: &[u8], partial: &NibbleSlice, journal: &mut Journal) -> Option { - self.cleared(Node::decoded(old), partial, journal) + /// Commit the in-memory changes to disk, freeing their storage and + /// updating the state root. + pub fn commit(&mut self) { + let handle = match self.root_handle() { + NodeHandle::Hash(_) => return, // no changes necessary. + NodeHandle::InMemory(h) => h, + }; + + trace!(target: "trie", "Committing trie changes to db."); + + // kill all the nodes on death row. + trace!(target: "trie", "{:?} nodes to remove from db", self.death_row.len()); + for hash in self.death_row.drain() { + self.db.remove(&hash); + } + + match self.storage.destroy(handle) { + Stored::New(node) => { + let root_rlp = node.to_rlp(|child, stream| self.commit_node(child, stream)); + *self.root = self.db.insert(&root_rlp[..]); + self.hash_count += 1; + + trace!(target: "trie", "root node rlp: {:?}", (&root_rlp[..]).pretty()); + self.root_handle = NodeHandle::Hash(*self.root); + } + Stored::Cached(node, hash) => { + // probably won't happen, but update the root and move on. + *self.root = hash; + self.root_handle = NodeHandle::InMemory(self.storage.alloc(Stored::Cached(node, hash))); + } + } } - /// Compose the RLP of the node equivalent to `n` except with the `partial` key removed from its (sub-)trie. - /// - /// `journal` will record the database updates so as to make the returned RLP valid through inserting - /// and deleting nodes as necessary. - /// - /// **This operation will not insert the new node nor destroy the original.** - fn cleared(&self, n: Node, partial: &NibbleSlice, journal: &mut Journal) -> Option { - trace!("cleared old={:?}, partial={:?})", n, partial); - - match (n, partial.is_empty()) { - (Node::Empty, _) => None, - (Node::Branch(_, None), true) => { None }, - (Node::Branch(payloads, _), true) => Some(Self::encoded(self.fixed(Node::Branch(payloads, None), journal))), // matched as leaf-branch - give back fixed branch with it. - (Node::Branch(payloads, value), false) => { - // Branch with partial left - route, clear, fix. - let i: usize = partial.at(0) as usize; - trace!("branch-with-partial node[{:?}]={:?}", i, payloads[i].pretty()); - self.cleared(self.get_node(payloads[i]), &partial.mid(1), journal).map(|new_payload| { - trace!("branch-new-payload={:?}; delete-old={:?}", new_payload.pretty(), payloads[i].pretty()); - - // downsteam node needed to be changed. - journal.delete_node(payloads[i]); - // return fixed up new node. - let mut new_payloads = payloads; - new_payloads[i] = &new_payload; - Self::encoded(self.fixed(Node::Branch(new_payloads, value), journal)) - }) - }, - (Node::Leaf(node_partial, _), _) => { - trace!("leaf partial={:?}", node_partial); - match node_partial.common_prefix(partial) { - cp if cp == partial.len() => { // leaf to be deleted - delete it :) - trace!("matched-prefix (cp={:?}): REPLACE-EMPTY", cp); - Some(Node::Empty.encoded()) - }, - _ => None, // anything else and the key doesn't exit - no change. + /// commit a node, hashing it, committing it to the db, + /// and writing it to the rlp stream as necessary. + fn commit_node(&mut self, handle: NodeHandle, stream: &mut RlpStream) { + match handle { + NodeHandle::Hash(h) => stream.append(&h), + NodeHandle::InMemory(h) => match self.storage.destroy(h) { + Stored::Cached(_, h) => stream.append(&h), + Stored::New(node) => { + let node_rlp = node.to_rlp(|child, stream| self.commit_node(child, stream)); + if node_rlp.len() >= 32 { + let hash = self.db.insert(&node_rlp[..]); + self.hash_count += 1; + stream.append(&hash) + } else { + stream.append_raw(&node_rlp, 1) + } } - }, - (Node::Extension(node_partial, node_payload), _) => { - trace!("extension partial={:?}, payload={:?}", node_partial, node_payload.pretty()); - match node_partial.common_prefix(partial) { - cp if cp == node_partial.len() => { - trace!("matching-prefix (cp={:?}): SKIP,CLEAR,FIXUP", cp); - // key at end of extension - skip, clear, fix - self.cleared(self.get_node(node_payload), &partial.mid(node_partial.len()), journal).map(|new_payload| { - trace!("extension-new-payload={:?}; delete-old={:?}", new_payload.pretty(), node_payload.pretty()); - // downsteam node needed to be changed. - journal.delete_node(node_payload); - // return fixed up new node. - Self::encoded(self.fixed(Node::Extension(node_partial, &new_payload), journal)) - }) - }, - _ => None, // key in the middle of an extension - doesn't exist. - } - }, + } + }; + } + + // a hack to get the root node's handle + fn root_handle(&self) -> NodeHandle { + match self.root_handle { + NodeHandle::Hash(h) => NodeHandle::Hash(h), + NodeHandle::InMemory(StorageHandle(x)) => NodeHandle::InMemory(StorageHandle(x)), } } } -impl<'db> TrieMut for TrieDBMut<'db> { - fn root(&self) -> &H256 { &self.root } +impl<'a> TrieMut for TrieDBMut<'a> { + fn root(&mut self) -> &H256 { + self.commit(); + &self.root + } + + fn is_empty(&self) -> bool { + match self.root_handle { + NodeHandle::Hash(h) => h == SHA3_NULL_RLP, + NodeHandle::InMemory(ref h) => match self.storage[h] { + Node::Empty => true, + _ => false, + } + } + } + + fn get<'b, 'key>(&'b self, key: &'key [u8]) -> Option<&'b [u8]> where 'b: 'key { + self.lookup(NibbleSlice::new(key), &self.root_handle) + } fn contains(&self, key: &[u8]) -> bool { self.get(key).is_some() } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Option<&'a [u8]> where 'a: 'key { - self.do_lookup(&NibbleSlice::new(key)) - } - fn insert(&mut self, key: &[u8], value: &[u8]) { - match value.is_empty() { - false => self.insert_ns(&NibbleSlice::new(key), value), - true => self.remove_ns(&NibbleSlice::new(key)), + if value.is_empty() { + self.remove(key); + return; } + + let root_handle = self.root_handle(); + let (new_handle, _) = self.insert_at(root_handle, NibbleSlice::new(key), value.to_owned()); + self.root_handle = NodeHandle::InMemory(new_handle); } fn remove(&mut self, key: &[u8]) { - self.remove_ns(&NibbleSlice::new(key)); + let root_handle = self.root_handle(); + let key = NibbleSlice::new(key); + match self.remove_at(root_handle, key) { + Some((handle, _)) => { + self.root_handle = NodeHandle::InMemory(handle); + } + None => { + self.root_handle = NodeHandle::Hash(SHA3_NULL_RLP); + *self.root = SHA3_NULL_RLP; + } + }; } } -impl<'db> fmt::Debug for TrieDBMut<'db> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(writeln!(f, "c={:?} [", self.hash_count)); - let root_rlp = self.db.get(&self.root).expect("Trie root not found!"); - try!(self.fmt_all(Node::decoded(root_rlp), f, 0)); - writeln!(f, "]") +impl<'a> Drop for TrieDBMut<'a> { + fn drop(&mut self) { + self.commit(); } } #[cfg(test)] mod tests { - use triehash::*; + use triehash::trie_root; use hash::*; use hashdb::*; use memorydb::*; use super::*; - use nibbleslice::*; use rlp::*; use bytes::ToPretty; - use super::super::node::*; use super::super::trietraits::*; use super::super::standardmap::*; @@ -705,34 +949,12 @@ mod tests { } } - macro_rules! map({$($key:expr => $value:expr),+ } => { - { - let mut m = ::std::collections::HashMap::new(); - $( - m.insert($key, $value); - )+ - m - } - };); - #[test] fn playpen() { - - /*let maps = map!{ - "six-low" => StandardMap{alphabet: Alphabet::Low, min_key: 6, journal_key: 0, count: 1000}, - "six-mid" => StandardMap{alphabet: Alphabet::Mid, min_key: 6, journal_key: 0, count: 1000}, - "six-all" => StandardMap{alphabet: Alphabet::All, min_key: 6, journal_key: 0, count: 1000}, - "mix-mid" => StandardMap{alphabet: Alphabet::Mid, min_key: 1, journal_key: 5, count: 1000} - }; - for sm in maps { - let m = sm.1.make(); - let t = populate_trie(&m); - println!("{:?}: root={:?}, hash_count={:?}", sm.0, t.root(), t.hash_count); - };*/ -// panic!(); + ::log::init_log(); let mut seed = H256::new(); - for test_i in 0..1 { + for test_i in 0..10 { if test_i % 50 == 0 { debug!("{:?} of 10000 stress tests done", test_i); } @@ -748,30 +970,28 @@ mod tests { let mut memdb = MemoryDB::new(); let mut root = H256::new(); let mut memtrie = populate_trie(&mut memdb, &mut root, &x); - if *memtrie.root() != real || !memtrie.db_items_remaining().is_empty() { + + memtrie.commit(); + if *memtrie.root() != real { println!("TRIE MISMATCH"); println!(""); println!("{:?} vs {:?}", memtrie.root(), real); for i in &x { println!("{:?} -> {:?}", i.0.pretty(), i.1.pretty()); } - println!("{:?}", memtrie); } assert_eq!(*memtrie.root(), real); - assert!(memtrie.db_items_remaining().is_empty()); unpopulate_trie(&mut memtrie, &x); - if *memtrie.root() != SHA3_NULL_RLP || !memtrie.db_items_remaining().is_empty() { + memtrie.commit(); + if *memtrie.root() != SHA3_NULL_RLP { println!("- TRIE MISMATCH"); println!(""); - println!("remaining: {:?}", memtrie.db_items_remaining()); println!("{:?} vs {:?}", memtrie.root(), real); for i in &x { println!("{:?} -> {:?}", i.0.pretty(), i.1.pretty()); } - println!("{:?}", memtrie); } assert_eq!(*memtrie.root(), SHA3_NULL_RLP); - assert!(memtrie.db_items_remaining().is_empty()); } } @@ -779,9 +999,8 @@ mod tests { fn init() { let mut memdb = MemoryDB::new(); let mut root = H256::new(); - let t = TrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::new(&mut memdb, &mut root); assert_eq!(*t.root(), SHA3_NULL_RLP); - assert!(t.is_empty()); } #[test] @@ -802,8 +1021,6 @@ mod tests { let mut t1 = TrieDBMut::new(&mut memdb, &mut root); t1.insert(&[0x01, 0x23], &big_value.to_vec()); t1.insert(&[0x01, 0x34], &big_value.to_vec()); - println!("********************** keys remaining {:?}", t1.db_items_remaining()); - assert!(t1.db_items_remaining().is_empty()); let mut memdb2 = MemoryDB::new(); let mut root2 = H256::new(); let mut t2 = TrieDBMut::new(&mut memdb2, &mut root2); @@ -811,11 +1028,6 @@ mod tests { t2.insert(&[0x01, 0x23], &big_value.to_vec()); t2.insert(&[0x01, 0x34], &big_value.to_vec()); t2.remove(&[0x01]); - assert!(t2.db_items_remaining().is_empty()); - /*if t1.root() != t2.root()*/ { - trace!("{:?}", t1); - trace!("{:?}", t2); - } } #[test] @@ -856,21 +1068,6 @@ mod tests { ])); } - #[test] - fn insert_out_of_order() { - let mut memdb = MemoryDB::new(); - let mut root = H256::new(); - let mut t = TrieDBMut::new(&mut memdb, &mut root); - t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]); - t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]); - t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]); - assert_eq!(*t.root(), trie_root(vec![ - (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), - (vec![0x81u8, 0x23], vec![0x81u8, 0x23]), - (vec![0xf1u8, 0x23], vec![0xf1u8, 0x23]), - ])); - } - #[test] fn insert_value_into_branch_root() { let mut memdb = MemoryDB::new(); @@ -943,53 +1140,6 @@ mod tests { ])); } - #[test] - fn test_node_leaf() { - let k = vec![0x20u8, 0x01, 0x23, 0x45]; - let v: Vec = From::from("cat"); - let (slice, is_leaf) = NibbleSlice::from_encoded(&k); - assert_eq!(is_leaf, true); - let leaf = Node::Leaf(slice, &v); - let rlp = leaf.encoded(); - let leaf2 = Node::decoded(&rlp); - assert_eq!(leaf, leaf2); - } - - #[test] - fn test_node_extension() { - let k = vec![0x00u8, 0x01, 0x23, 0x45]; - // in extension, value must be valid rlp - let v = encode(&"cat"); - let (slice, is_leaf) = NibbleSlice::from_encoded(&k); - assert_eq!(is_leaf, false); - let ex = Node::Extension(slice, &v); - let rlp = ex.encoded(); - let ex2 = Node::decoded(&rlp); - assert_eq!(ex, ex2); - } - - #[test] - fn test_node_empty_branch() { - let null_rlp = NULL_RLP; - let branch = Node::Branch([&null_rlp; 16], None); - let rlp = branch.encoded(); - let branch2 = Node::decoded(&rlp); - println!("{:?}", rlp); - assert_eq!(branch, branch2); - } - - #[test] - fn test_node_branch() { - let k = encode(&"cat"); - let mut nodes: [&[u8]; 16] = unsafe { ::std::mem::uninitialized() }; - for i in 0..16 { nodes[i] = &k; } - let v: Vec = From::from("dog"); - let branch = Node::Branch(nodes, Some(&v)); - let rlp = branch.encoded(); - let branch2 = Node::decoded(&rlp); - assert_eq!(branch, branch2); - } - #[test] fn test_at_empty() { let mut memdb = MemoryDB::new(); @@ -1005,6 +1155,8 @@ mod tests { let mut t = TrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]); assert_eq!(t.get(&[0x1, 0x23]).unwrap(), &[0x1u8, 0x23]); + t.commit(); + assert_eq!(t.get(&[0x1, 0x23]).unwrap(), &[0x1u8, 0x23]); } #[test] @@ -1019,20 +1171,11 @@ mod tests { assert_eq!(t.get(&[0xf1, 0x23]).unwrap(), &[0xf1u8, 0x23]); assert_eq!(t.get(&[0x81, 0x23]).unwrap(), &[0x81u8, 0x23]); assert_eq!(t.get(&[0x82, 0x23]), None); - } - - #[test] - fn test_print_trie() { - let mut memdb = MemoryDB::new(); - let mut root = H256::new(); - let mut t = TrieDBMut::new(&mut memdb, &mut root); - t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]); - t.insert(&[0x02u8, 0x23], &[0x01u8, 0x23]); - t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]); - t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]); - println!("trie:"); - println!("{:?}", t); - //assert!(false); + t.commit(); + assert_eq!(t.get(&[0x01, 0x23]).unwrap(), &[0x01u8, 0x23]); + assert_eq!(t.get(&[0xf1, 0x23]).unwrap(), &[0xf1u8, 0x23]); + assert_eq!(t.get(&[0x81, 0x23]).unwrap(), &[0x81u8, 0x23]); + assert_eq!(t.get(&[0x82, 0x23]), None); } #[test] @@ -1050,12 +1193,12 @@ mod tests { let real = trie_root(x.clone()); let mut memdb = MemoryDB::new(); let mut root = H256::new(); - let memtrie = populate_trie(&mut memdb, &mut root, &x); + let mut memtrie = populate_trie(&mut memdb, &mut root, &x); let mut y = x.clone(); y.sort_by(|ref a, ref b| a.0.cmp(&b.0)); let mut memdb2 = MemoryDB::new(); let mut root2 = H256::new(); - let memtrie_sorted = populate_trie(&mut memdb2, &mut root2, &y); + let mut memtrie_sorted = populate_trie(&mut memdb2, &mut root2, &y); if *memtrie.root() != real || *memtrie_sorted.root() != real { println!("TRIE MISMATCH"); println!(""); @@ -1063,79 +1206,16 @@ mod tests { for i in &x { println!("{:?} -> {:?}", i.0.pretty(), i.1.pretty()); } - println!("{:?}", memtrie); println!("SORTED... {:?}", memtrie_sorted.root()); for i in &y { println!("{:?} -> {:?}", i.0.pretty(), i.1.pretty()); } - println!("{:?}", memtrie_sorted); } assert_eq!(*memtrie.root(), real); assert_eq!(*memtrie_sorted.root(), real); } } - #[test] - fn branching_test() { - use std::str::FromStr; - use rustc_serialize::hex::FromHex; - - let mut memdb = MemoryDB::new(); - let mut root = H256::new(); - let mut t = TrieDBMut::new(&mut memdb, &mut root); - t.insert(&"04110d816c380812a427968ece99b1c963dfbce6".from_hex().unwrap(), b"something"); - t.insert(&"095e7baea6a6c7c4c2dfeb977efac326af552d87".from_hex().unwrap(), b"something"); - t.insert(&"0a517d755cebbf66312b30fff713666a9cb917e0".from_hex().unwrap(), b"something"); - t.insert(&"24dd378f51adc67a50e339e8031fe9bd4aafab36".from_hex().unwrap(), b"something"); - t.insert(&"293f982d000532a7861ab122bdc4bbfd26bf9030".from_hex().unwrap(), b"something"); - t.insert(&"2cf5732f017b0cf1b1f13a1478e10239716bf6b5".from_hex().unwrap(), b"something"); - t.insert(&"31c640b92c21a1f1465c91070b4b3b4d6854195f".from_hex().unwrap(), b"something"); - t.insert(&"37f998764813b136ddf5a754f34063fd03065e36".from_hex().unwrap(), b"something"); - t.insert(&"37fa399a749c121f8a15ce77e3d9f9bec8020d7a".from_hex().unwrap(), b"something"); - t.insert(&"4f36659fa632310b6ec438dea4085b522a2dd077".from_hex().unwrap(), b"something"); - t.insert(&"62c01474f089b07dae603491675dc5b5748f7049".from_hex().unwrap(), b"something"); - t.insert(&"729af7294be595a0efd7d891c9e51f89c07950c7".from_hex().unwrap(), b"something"); - t.insert(&"83e3e5a16d3b696a0314b30b2534804dd5e11197".from_hex().unwrap(), b"something"); - t.insert(&"8703df2417e0d7c59d063caa9583cb10a4d20532".from_hex().unwrap(), b"something"); - t.insert(&"8dffcd74e5b5923512916c6a64b502689cfa65e1".from_hex().unwrap(), b"something"); - t.insert(&"95a4d7cccb5204733874fa87285a176fe1e9e240".from_hex().unwrap(), b"something"); - t.insert(&"99b2fcba8120bedd048fe79f5262a6690ed38c39".from_hex().unwrap(), b"something"); - t.insert(&"a4202b8b8afd5354e3e40a219bdc17f6001bf2cf".from_hex().unwrap(), b"something"); - t.insert(&"a94f5374fce5edbc8e2a8697c15331677e6ebf0b".from_hex().unwrap(), b"something"); - t.insert(&"a9647f4a0a14042d91dc33c0328030a7157c93ae".from_hex().unwrap(), b"something"); - t.insert(&"aa6cffe5185732689c18f37a7f86170cb7304c2a".from_hex().unwrap(), b"something"); - t.insert(&"aae4a2e3c51c04606dcb3723456e58f3ed214f45".from_hex().unwrap(), b"something"); - t.insert(&"c37a43e940dfb5baf581a0b82b351d48305fc885".from_hex().unwrap(), b"something"); - t.insert(&"d2571607e241ecf590ed94b12d87c94babe36db6".from_hex().unwrap(), b"something"); - t.insert(&"f735071cbee190d76b704ce68384fc21e389fbe7".from_hex().unwrap(), b"something"); - t.insert(&"04110d816c380812a427968ece99b1c963dfbce6".from_hex().unwrap(), &[]); - t.insert(&"095e7baea6a6c7c4c2dfeb977efac326af552d87".from_hex().unwrap(), &[]); - t.insert(&"0a517d755cebbf66312b30fff713666a9cb917e0".from_hex().unwrap(), &[]); - t.insert(&"24dd378f51adc67a50e339e8031fe9bd4aafab36".from_hex().unwrap(), &[]); - t.insert(&"293f982d000532a7861ab122bdc4bbfd26bf9030".from_hex().unwrap(), &[]); - t.insert(&"2cf5732f017b0cf1b1f13a1478e10239716bf6b5".from_hex().unwrap(), &[]); - t.insert(&"31c640b92c21a1f1465c91070b4b3b4d6854195f".from_hex().unwrap(), &[]); - t.insert(&"37f998764813b136ddf5a754f34063fd03065e36".from_hex().unwrap(), &[]); - t.insert(&"37fa399a749c121f8a15ce77e3d9f9bec8020d7a".from_hex().unwrap(), &[]); - t.insert(&"4f36659fa632310b6ec438dea4085b522a2dd077".from_hex().unwrap(), &[]); - t.insert(&"62c01474f089b07dae603491675dc5b5748f7049".from_hex().unwrap(), &[]); - t.insert(&"729af7294be595a0efd7d891c9e51f89c07950c7".from_hex().unwrap(), &[]); - t.insert(&"83e3e5a16d3b696a0314b30b2534804dd5e11197".from_hex().unwrap(), &[]); - t.insert(&"8703df2417e0d7c59d063caa9583cb10a4d20532".from_hex().unwrap(), &[]); - t.insert(&"8dffcd74e5b5923512916c6a64b502689cfa65e1".from_hex().unwrap(), &[]); - t.insert(&"95a4d7cccb5204733874fa87285a176fe1e9e240".from_hex().unwrap(), &[]); - t.insert(&"99b2fcba8120bedd048fe79f5262a6690ed38c39".from_hex().unwrap(), &[]); - t.insert(&"a4202b8b8afd5354e3e40a219bdc17f6001bf2cf".from_hex().unwrap(), &[]); - t.insert(&"a94f5374fce5edbc8e2a8697c15331677e6ebf0b".from_hex().unwrap(), &[]); - t.insert(&"a9647f4a0a14042d91dc33c0328030a7157c93ae".from_hex().unwrap(), &[]); - t.insert(&"aa6cffe5185732689c18f37a7f86170cb7304c2a".from_hex().unwrap(), &[]); - t.insert(&"aae4a2e3c51c04606dcb3723456e58f3ed214f45".from_hex().unwrap(), &[]); - t.insert(&"c37a43e940dfb5baf581a0b82b351d48305fc885".from_hex().unwrap(), &[]); - t.insert(&"d2571607e241ecf590ed94b12d87c94babe36db6".from_hex().unwrap(), &[]); - t.insert(&"f735071cbee190d76b704ce68384fc21e389fbe7".from_hex().unwrap(), &[]); - assert_eq!(*t.root(), H256::from_str("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap()); - } - #[test] fn test_trie_existing() { let mut root = H256::new(); @@ -1149,4 +1229,32 @@ mod tests { let _ = TrieDBMut::from_existing(&mut db, &mut root); } } -} + + #[test] + fn insert_empty() { + let mut seed = H256::new(); + let x = StandardMap { + alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), + min_key: 5, + journal_key: 0, + value_mode: ValueMode::Index, + count: 4, + }.make_with(&mut seed); + + let mut db = MemoryDB::new(); + let mut root = H256::new(); + let mut t = TrieDBMut::new(&mut db, &mut root); + for &(ref key, ref value) in &x { + t.insert(key, value); + } + + assert_eq!(*t.root(), trie_root(x.clone())); + + for &(ref key, _) in &x { + t.insert(key, &[]); + } + + assert!(t.is_empty()); + assert_eq!(*t.root(), SHA3_NULL_RLP); + } +} \ No newline at end of file diff --git a/util/src/trie/trietraits.rs b/util/src/trie/trietraits.rs index f4d53d158..1f899f139 100644 --- a/util/src/trie/trietraits.rs +++ b/util/src/trie/trietraits.rs @@ -18,7 +18,7 @@ use hash::H256; use rlp::SHA3_NULL_RLP; /// Trie-Item type. -pub type TrieItem<'a> = (Vec, &'a[u8]); +pub type TrieItem<'a> = (Vec, &'a [u8]); /// A key-value datastore implemented as a database-backed modified Merkle tree. pub trait Trie { @@ -41,10 +41,10 @@ pub trait Trie { /// A key-value datastore implemented as a database-backed modified Merkle tree. pub trait TrieMut { /// Return the root of the trie. - fn root(&self) -> &H256; + fn root(&mut self) -> &H256; /// Is the trie empty? - fn is_empty(&self) -> bool { *self.root() == SHA3_NULL_RLP } + fn is_empty(&self) -> bool; /// Does the trie contain a given key? fn contains(&self, key: &[u8]) -> bool;