From 730ed90fc1f561728a2de832bc551826e1a0a1ee Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Thu, 21 Nov 2019 14:05:15 +0100 Subject: [PATCH 001/118] next --- trie-db/src/traverse.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 trie-db/src/traverse.rs diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs new file mode 100644 index 00000000..50916bf1 --- /dev/null +++ b/trie-db/src/traverse.rs @@ -0,0 +1,35 @@ +// Copyright 2017, 2019 Parity Technologies +// +// Licensed under the Apache License, Version .0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Alternative to iterator for traversing a trie. +//! +//! The traversal stack is updatable, and this is therefore usable for +//! batch update of ordered key values. + +/// StackedNode can be updated. +pub enum StackedNode<'a> { + // after #34 do switch to &'a[u8] and NodePlan, for now just + // decode as needed + // Unchanged(&'a[u8]), + Unchanged(&'a[u8], Node<'a>), + Changed(OwnedNode), +} + +/// Visitor trait to implement when using `trie_visit`. +pub trait ProcessEncodedNode { + fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode, is_root: bool); + fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode, is_root: bool); +} + + From 76fc45f783cbf30569399673b1c73be91675e076 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Fri, 22 Nov 2019 18:35:54 +0100 Subject: [PATCH 002/118] Draft iterator, some very bad looking stuff. --- trie-db/src/lib.rs | 1 + trie-db/src/node.rs | 24 +++++ trie-db/src/traverse.rs | 197 ++++++++++++++++++++++++++++++++++++--- trie-db/src/triedbmut.rs | 106 ++++++++++++++------- 4 files changed, 283 insertions(+), 45 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 622fcedc..a0c63a62 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -81,6 +81,7 @@ pub mod recorder; mod fatdb; mod fatdbmut; mod iter_build; +mod traverse; mod iterator; mod lookup; mod nibble; diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 0a318e84..0d1ed7dc 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -208,4 +208,28 @@ impl> OwnedNode { pub fn node(&self) -> Node { self.plan.build(self.data.borrow()) } + + /// Get extension part of the node (partial) if any. + pub fn partial(&self) -> Option { + match &self.plan { + NodePlan::Branch { .. } + | NodePlan::Empty => None, + NodePlan::Leaf { partial, .. } + | NodePlan::NibbledBranch { partial, .. } + | NodePlan::Extension { partial, .. } => + Some(partial.build(self.data.borrow())), + } + } + + /// Try to access child. + pub fn child(&self, ix: u8) -> Option { + match &self.plan { + NodePlan::Leaf { .. } + | NodePlan::Extension { .. } + | NodePlan::Empty => None, + NodePlan::NibbledBranch { children, .. } + | NodePlan::Branch { children, .. } => + children[ix as usize].as_ref().map(|child| child.build(self.data.borrow())), + } + } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 50916bf1..9efb2a5c 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -12,24 +12,197 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Alternative to iterator for traversing a trie. +//! Traverse a trie, given a set of key. //! -//! The traversal stack is updatable, and this is therefore usable for +//! The traversal stack is updatable, and is therefore usable for //! batch update of ordered key values. +use crate::triedbmut::{Node, NibbleFullKey}; +use crate::node::{OwnedNode, NodePlan, NodeHandle}; +use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; +#[cfg(feature = "std")] +use std::borrow::Borrow; +#[cfg(not(feature = "std"))] +use core::borrow::Borrow; +use crate::{TrieLayout, TrieHash, CError, Result, TrieError}; +use crate::DBValue; +use hash_db::{HashDB, Prefix, EMPTY_PREFIX, Hasher}; +use crate::NodeCodec; +use ::core_::cmp::*; + + +type StorageHandle = Vec; + /// StackedNode can be updated. -pub enum StackedNode<'a> { - // after #34 do switch to &'a[u8] and NodePlan, for now just - // decode as needed - // Unchanged(&'a[u8]), - Unchanged(&'a[u8], Node<'a>), - Changed(OwnedNode), +/// A state can be use. +pub(crate) enum StackedNode + where + B: Borrow<[u8]>, +{ + /// Read node. + Unchanged(OwnedNode, S), + /// Modified node. + Changed(Node, S), +} + +impl StackedNode + where + B: Borrow<[u8]> + AsRef<[u8]>, + H: AsRef<[u8]>, +{ + /// Get extension part of the node (partial) if any. + pub fn partial(&self) -> Option { + match self { + StackedNode::Unchanged(node, ..) => node.partial(), + StackedNode::Changed(node, ..) => node.partial(), + } + } + + /// Try to access child. + pub fn child(&self, ix: u8) -> Option { + match self { + StackedNode::Unchanged(node, ..) => node.child(ix), + StackedNode::Changed(node, ..) => node.child(ix), + } + } + +} + +/// Visitor trait to implement when using `trie_traverse_key`. +pub(crate) trait ProcessStack + where + B: Borrow<[u8]>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]> + Into, +{ + /// Callback on enter a node, change can be applied here. + fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); + /// Same as `enter` but at terminal node element (terminal considering next key so branch is + /// possible here). + fn enter_terminal(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) + -> Option<(Node, S)>; + /// Callback on exit a node, commit action on change node should be applied here. + fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); + //fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode, parent: &mut StackedNode); + /// Same as `exit` but for root (very last exit call). + fn exit_root(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); } -/// Visitor trait to implement when using `trie_visit`. -pub trait ProcessEncodedNode { - fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode, is_root: bool); - fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode, is_root: bool); +/// The main entry point for traversing a trie by key. +pub(crate) fn trie_traverse_key<'a, T, I, K, V, S, B, F>( + db: &'a mut dyn HashDB, + root: &'a TrieHash, + elements: I, + callback: &mut F, +) -> Result<(), TrieHash, CError> + where + T: TrieLayout, + I: IntoIterator)>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]> + Into, + S: Default, + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, + F: ProcessStack, K, V, S>, +{ + // stack of traversed nodes + let mut stack: Vec<(StackedNode, S>, usize)> = Vec::with_capacity(32); + + // TODO EMCH do following update (used for error only) + let mut last_hash = root; + let root = if let Ok(root) = fetch::(db, root, EMPTY_PREFIX) { + + root + } else { + return Err(Box::new(TrieError::InvalidStateRoot(*root))); + }; +// stack.push(StackedNode::Unchanged(root, Default::default())); + + let mut prefix = NibbleVec::new(); + let mut current = StackedNode::, S>::Unchanged(root, Default::default()); + let mut common_depth = 0; + current.partial().map(|p| { + common_depth += p.len(); + prefix.append_partial(p.right()) + }); + + for (k, v) in elements { + let dest = NibbleFullKey::new(k.as_ref()); + let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + loop { + // TODO EMCH go down extension here + if common_depth >= dest_depth { + if let Some((new, s)) = callback.enter_terminal(&prefix, &mut current) { + stack.push((current, common_depth)); + new.partial().map(|p| { + common_depth += p.len(); + prefix.append_partial(p.right()) + }); + current = StackedNode::Changed(new, s); + }; + // go next key + break; + } else { + + // try go down + let next_index = dest.at(common_depth + 1); + let next_node = match current.child(next_index) { + Some(NodeHandle::Hash(handle_hash)) => { + let mut hash = as Default>::default(); + hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + fetch::(db, &hash, prefix.as_prefix())? + }, + Some(NodeHandle::Inline(node_encoded)) => { + // Instantiating B is only for inline node, still costy. + OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError(*last_hash, e)))? + }, + None => { + // advance key by breaking loop + break; + }, + }; + + callback.enter(&prefix, &mut current); + stack.push((current, common_depth)); + current = StackedNode::Unchanged(next_node, Default::default()); + current.partial().map(|p| { + common_depth += p.len(); + prefix.append_partial(p.right()) + }); + } + } + let mut common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); + common_depth = min(prefix.len(), common_depth); + while common_depth < prefix.len() { + // go up + if let Some((c, d)) = stack.pop() { + callback.exit(&prefix, &mut current); + current = c; + prefix.drop_lasts(prefix.len() - d); + } else { + callback.exit_root(&prefix, &mut current); + return Ok(()); + } + common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); + common_depth = min(prefix.len(), common_depth); + } + + } + + Ok(()) } +/// Fetch a node by hash, do not cache it. +fn fetch>( + db: &mut dyn HashDB, + hash: &TrieHash, + key: Prefix, +) -> Result, TrieHash, CError> { + let node_encoded = db.get(hash, key) + .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(*hash)))?; + Ok( + OwnedNode::new::(node_encoded) + .map_err(|e| Box::new(TrieError::DecoderError(*hash, e)))? + ) +} diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index fc8f47ee..3d94711d 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -45,24 +45,34 @@ use alloc::vec::Vec; // For lookups into the Node storage buffer. // This is deliberately non-copyable. #[cfg_attr(feature = "std", derive(Debug))] -struct StorageHandle(usize); +pub(crate) struct StorageHandle(usize); // Handles to nodes in the trie. #[cfg_attr(feature = "std", derive(Debug))] -enum NodeHandle { +pub(crate) enum NodeHandle { /// Loaded into memory. - InMemory(StorageHandle), + InMemory(SH), /// Either a hash or an inline node Hash(H), } -impl From for NodeHandle { +impl, SH: AsRef<[u8]>> NodeHandle { + /// Get a node handle ref to this handle. + fn as_ref(&self) -> crate::node::NodeHandle { + match self { + NodeHandle::InMemory(sh) => crate::node::NodeHandle::Inline(sh.as_ref()), + NodeHandle::Hash(h) => crate::node::NodeHandle::Hash(h.as_ref()), + } + } +} + +impl From for NodeHandle { fn from(handle: StorageHandle) -> Self { NodeHandle::InMemory(handle) } } -fn empty_children() -> Box<[Option>; 16]> { +fn empty_children() -> Box<[Option>; 16]> { Box::new([ None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, @@ -71,11 +81,12 @@ fn empty_children() -> Box<[Option>; 16]> { /// Type alias to indicate the nible covers a full key, /// therefore its left side is a full prefix. -type NibbleFullKey<'key> = NibbleSlice<'key>; +pub(crate) type NibbleFullKey<'key> = NibbleSlice<'key>; +// TODO EMCH make a local type alias with only H!! /// Node types in the Trie. #[cfg_attr(feature = "std", derive(Debug))] -enum Node { +pub(crate) enum Node { /// Empty node. Empty, /// A leaf node contains the end of a key and a value. @@ -86,14 +97,43 @@ enum 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(NodeKey, NodeHandle), + Extension(NodeKey, NodeHandle), /// A branch has up to 16 children and an optional value. - Branch(Box<[Option>; 16]>, Option), + Branch(Box<[Option>; 16]>, Option), /// Branch node with support for a nibble (to avoid extension node). - NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), + NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), +} + +impl Node { + /// Get extension part of the node (partial) if any. + pub fn partial(&self) -> Option { + match self { + Node::Branch { .. } + | Node::Empty => None, + Node::NibbledBranch(partial, ..) + | Node::Extension(partial, ..) + | Node::Leaf(partial, ..) + => Some(NibbleSlice::new_offset(&partial.1[..], partial.0)), + } + } +} + +impl, SH: AsRef<[u8]>> Node { + /// Try to access child. + pub fn child(&self, ix: u8) -> Option { + match self { + Node::Leaf { .. } + | Node::Extension { .. } + | Node::Empty => None, + Node::NibbledBranch ( _, children, _ ) + | Node::Branch ( children, .. ) => + children[ix as usize].as_ref().map(|child| child.as_ref()), + } + } } -impl Node + +impl Node where O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy @@ -104,7 +144,7 @@ where child: EncodedNodeHandle, db: &dyn HashDB, storage: &mut NodeStorage - ) -> Result, H::Out, C::Error> + ) -> Result, H::Out, C::Error> where C: NodeCodec, H: Hasher, @@ -184,7 +224,7 @@ where fn into_encoded(self, mut child_cb: F) -> Vec where C: NodeCodec, - F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, + F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { match self { @@ -242,9 +282,9 @@ where // post-inspect action. enum Action { // Replace a node with a new one. - Replace(Node), + Replace(Node), // Restore the original node. This trusts that the node is actually the original. - Restore(Node), + Restore(Node), // if it is a new node, just clears the storage. Delete, } @@ -252,9 +292,9 @@ enum Action { // post-insert action. Same as action without delete enum InsertAction { // Replace a node with a new one. - Replace(Node), + Replace(Node), // Restore the original node. - Restore(Node), + Restore(Node), } impl InsertAction { @@ -266,7 +306,7 @@ impl InsertAction { } // unwrap the node, disregarding replace or restore state. - fn unwrap_node(self) -> Node { + fn unwrap_node(self) -> Node { match self { InsertAction::Replace(n) | InsertAction::Restore(n) => n, } @@ -276,9 +316,9 @@ impl InsertAction { // What kind of node is stored here. enum Stored { // A new node. - New(Node), + New(Node), // A cached node, loaded from the DB. - Cached(Node, H), + Cached(Node, H), } /// Used to build a collection of child nodes from a collection of `NodeHandle`s @@ -323,9 +363,9 @@ impl NodeStorage { } impl<'a, H> Index<&'a StorageHandle> for NodeStorage { - type Output = Node; + type Output = Node; - fn index(&self, handle: &'a StorageHandle) -> &Node { + fn index(&self, handle: &'a StorageHandle) -> &Node { match self.nodes[handle.0] { Stored::New(ref node) => node, Stored::Cached(ref node, _) => node, @@ -375,7 +415,7 @@ where storage: NodeStorage>, db: &'a mut dyn HashDB, root: &'a mut TrieHash, - root_handle: NodeHandle>, + root_handle: NodeHandle, StorageHandle>, death_row: HashSet<(TrieHash, (ElasticArray36, Option))>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. @@ -459,7 +499,7 @@ where where F: FnOnce( &mut Self, - Node>, + Node, StorageHandle>, &mut NibbleFullKey, ) -> Result>, TrieHash, CError>, { @@ -487,7 +527,7 @@ where fn lookup<'x, 'key>( &'x self, mut partial: NibbleSlice<'key>, - handle: &NodeHandle>, + handle: &NodeHandle, StorageHandle>, ) -> Result, TrieHash, CError> where 'x: 'key { @@ -552,7 +592,7 @@ where /// Insert a key-value pair into the trie, creating new nodes if necessary. fn insert_at( &mut self, - handle: NodeHandle>, + handle: NodeHandle, StorageHandle>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option, @@ -573,7 +613,7 @@ where /// The insertion inspector. fn insert_inspector( &mut self, - node: Node>, + node: Node, StorageHandle>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option, @@ -919,7 +959,7 @@ where /// Removes a node from the trie based on key. fn remove_at( &mut self, - handle: NodeHandle>, + handle: NodeHandle, StorageHandle>, key: &mut NibbleFullKey, old_val: &mut Option, ) -> Result, TrieHash, CError> { @@ -943,7 +983,7 @@ where /// The removal inspector. fn remove_inspector( &mut self, - node: Node>, + node: Node, StorageHandle>, key: &mut NibbleFullKey, old_val: &mut Option, ) -> Result>, TrieHash, CError> { @@ -1124,9 +1164,9 @@ where /// - Extension node followed by anything other than a Branch node. fn fix( &mut self, - node: Node>, + node: Node, StorageHandle>, key: NibbleSlice, - ) -> Result>, TrieHash, CError> { + ) -> Result, StorageHandle>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -1410,7 +1450,7 @@ where /// `into_encoded` method of `Node`. fn commit_child( &mut self, - handle: NodeHandle>, + handle: NodeHandle, StorageHandle>, prefix: &mut NibbleVec, ) -> ChildReference> { match handle { @@ -1451,7 +1491,7 @@ where } // a hack to get the root node's handle - fn root_handle(&self) -> NodeHandle> { + fn root_handle(&self) -> NodeHandle, StorageHandle> { match self.root_handle { NodeHandle::Hash(h) => NodeHandle::Hash(h), NodeHandle::InMemory(StorageHandle(x)) => NodeHandle::InMemory(StorageHandle(x)), From 75f1c4d5088666975e7a2399fecaae7c000d0675 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sun, 24 Nov 2019 19:30:15 +0100 Subject: [PATCH 003/118] update fuzzer deps --- trie-db/fuzz/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 5a5da011..f5751ef0 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -10,7 +10,7 @@ cargo-fuzz = true [dependencies] memory-db = { path = "../../memory-db", version = "0.15.2" } -reference-trie = { path = "../../test-support/reference-trie", version = "0.15.2" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.16.0" } keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.2" } honggfuzz = "0.5" From d71a166c6bb396fb32e4397193aba17269dc68e8 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 26 Nov 2019 01:01:48 +0100 Subject: [PATCH 004/118] broken --- trie-db/fuzz/Cargo.toml | 1 + trie-db/src/node.rs | 10 +++++ trie-db/src/traverse.rs | 80 ++++++++++++++++++++++++++++++++-------- trie-db/src/triedbmut.rs | 2 +- 4 files changed, 76 insertions(+), 17 deletions(-) diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index f5751ef0..a8dfc58d 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -11,6 +11,7 @@ cargo-fuzz = true [dependencies] memory-db = { path = "../../memory-db", version = "0.15.2" } reference-trie = { path = "../../test-support/reference-trie", version = "0.16.0" } +reference-trie-old = { package = "reference-trie", version = "0.15.2" } keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.2" } honggfuzz = "0.5" diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 0d1ed7dc..b4ee4472 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -34,6 +34,16 @@ pub enum NodeHandle<'a> { Inline(&'a [u8]), } +impl<'a> NodeHandle<'a> { + /// Get underlying data. + fn data(&self) -> &[u8] { + match self { + NodeHandle::Hash(d) + | NodeHandle::Inline(d) => d, + } + } +} + /// Read a hash from a slice into a Hasher output. Returns None if the slice is the wrong length. pub fn decode_hash(data: &[u8]) -> Option { if data.len() != H::LENGTH { diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 9efb2a5c..0d63890d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Traverse a trie, given a set of key. +//! Traverse a trie. //! //! The traversal stack is updatable, and is therefore usable for //! batch update of ordered key values. @@ -35,20 +35,21 @@ type StorageHandle = Vec; /// StackedNode can be updated. /// A state can be use. -pub(crate) enum StackedNode +pub(crate) enum StackedNode where B: Borrow<[u8]>, + T: TrieLayout, { /// Read node. Unchanged(OwnedNode, S), /// Modified node. - Changed(Node, S), + Changed(Node, S), } -impl StackedNode +impl StackedNode where B: Borrow<[u8]> + AsRef<[u8]>, - H: AsRef<[u8]>, + T: TrieLayout, { /// Get extension part of the node (partial) if any. pub fn partial(&self) -> Option { @@ -65,30 +66,50 @@ impl StackedNode StackedNode::Changed(node, ..) => node.child(ix), } } +} + +impl StackedNode + where + B: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + + PartialEq + Eq + ::core_::hash::Hash + Send + Sync + Clone + Copy + Borrow<[u8]>, + T: TrieLayout, +{ + + /// Encode node + /// TODO EMCH can optimize unchange to their backed data by consuming. + pub fn into_encoded(&self) -> Vec { + match self { + StackedNode::Unchanged(node, ..) => node.data().to_vec(), + StackedNode::Changed(node, ..) => node.into_encoded::<_, T::Codec, T::Hash>( + |child, o_slice, o_index| { + child.as_ref().data().to_vec() + }), + } + } } /// Visitor trait to implement when using `trie_traverse_key`. -pub(crate) trait ProcessStack +pub(crate) trait ProcessStack where + T: TrieLayout, B: Borrow<[u8]>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]> + Into, { /// Callback on enter a node, change can be applied here. - fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); + fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); /// Same as `enter` but at terminal node element (terminal considering next key so branch is /// possible here). - fn enter_terminal(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) - -> Option<(Node, S)>; + fn enter_terminal(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) + -> Option<(Node, S)>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); - //fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode, parent: &mut StackedNode); + fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); + fn exit_root(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); } -/// The main entry point for traversing a trie by key. +/// The main entry point for traversing a trie by a set of keys. pub(crate) fn trie_traverse_key<'a, T, I, K, V, S, B, F>( db: &'a mut dyn HashDB, root: &'a TrieHash, @@ -102,10 +123,10 @@ pub(crate) fn trie_traverse_key<'a, T, I, K, V, S, B, F>( V: AsRef<[u8]> + Into, S: Default, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, - F: ProcessStack, K, V, S>, + F: ProcessStack, { // stack of traversed nodes - let mut stack: Vec<(StackedNode, S>, usize)> = Vec::with_capacity(32); + let mut stack: Vec<(StackedNode, usize)> = Vec::with_capacity(32); // TODO EMCH do following update (used for error only) let mut last_hash = root; @@ -118,7 +139,7 @@ pub(crate) fn trie_traverse_key<'a, T, I, K, V, S, B, F>( // stack.push(StackedNode::Unchanged(root, Default::default())); let mut prefix = NibbleVec::new(); - let mut current = StackedNode::, S>::Unchanged(root, Default::default()); + let mut current = StackedNode::::Unchanged(root, Default::default()); let mut common_depth = 0; current.partial().map(|p| { common_depth += p.len(); @@ -206,3 +227,30 @@ fn fetch>( .map_err(|e| Box::new(TrieError::DecoderError(*hash, e)))? ) } + +/// Contains ordered node change for this iteration. +pub struct BatchUpdate(pub Vec>); + +impl ProcessStack for BatchUpdate + where + T: TrieLayout, + B: Borrow<[u8]>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]> + Into, +{ + fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) { + } + + fn enter_terminal(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) + -> Option<(Node, S)> { + None + } + + fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) { + self.0.push(stacked.into_encoded()); + } + + fn exit_root(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) { + self.0.push(stacked.into_encoded()); + } +} diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 3d94711d..e31cf266 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -221,7 +221,7 @@ where } // TODO: parallelize - fn into_encoded(self, mut child_cb: F) -> Vec + pub(crate) fn into_encoded(self, mut child_cb: F) -> Vec where C: NodeCodec, F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, From d7cca49eef5015b7dc3dda9b58cb2d8801b0c228 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Nov 2019 11:56:40 +0100 Subject: [PATCH 005/118] glue types --- trie-db/src/node.rs | 2 +- trie-db/src/traverse.rs | 30 ++++++++++++++-------------- trie-db/src/triedbmut.rs | 42 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index b4ee4472..549a2551 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -36,7 +36,7 @@ pub enum NodeHandle<'a> { impl<'a> NodeHandle<'a> { /// Get underlying data. - fn data(&self) -> &[u8] { + pub(crate) fn data(&self) -> &[u8] { match self { NodeHandle::Hash(d) | NodeHandle::Inline(d) => d, diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 0d63890d..f03692f4 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -43,13 +43,14 @@ pub(crate) enum StackedNode /// Read node. Unchanged(OwnedNode, S), /// Modified node. - Changed(Node, S), + Changed(Node, B>, S), } impl StackedNode where B: Borrow<[u8]> + AsRef<[u8]>, T: TrieLayout, +// TrieHash: AsRef<[u8]>, { /// Get extension part of the node (partial) if any. pub fn partial(&self) -> Option { @@ -70,23 +71,20 @@ impl StackedNode impl StackedNode where - B: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug - + PartialEq + Eq + ::core_::hash::Hash + Send + Sync + Clone + Copy + Borrow<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]>, T: TrieLayout, { /// Encode node - /// TODO EMCH can optimize unchange to their backed data by consuming. - pub fn into_encoded(&self) -> Vec { + pub fn into_encoded(self) -> Vec { match self { StackedNode::Unchanged(node, ..) => node.data().to_vec(), StackedNode::Changed(node, ..) => node.into_encoded::<_, T::Codec, T::Hash>( |child, o_slice, o_index| { - child.as_ref().data().to_vec() + child.as_child_ref::() }), } } - } /// Visitor trait to implement when using `trie_traverse_key`. @@ -102,11 +100,11 @@ pub(crate) trait ProcessStack /// Same as `enter` but at terminal node element (terminal considering next key so branch is /// possible here). fn enter_terminal(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) - -> Option<(Node, S)>; + -> Option<(Node, B>, S)>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); + fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode); /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); + fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode); } /// The main entry point for traversing a trie by a set of keys. @@ -197,11 +195,11 @@ pub(crate) fn trie_traverse_key<'a, T, I, K, V, S, B, F>( while common_depth < prefix.len() { // go up if let Some((c, d)) = stack.pop() { - callback.exit(&prefix, &mut current); + callback.exit(&prefix, current); current = c; prefix.drop_lasts(prefix.len() - d); } else { - callback.exit_root(&prefix, &mut current); + callback.exit_root(&prefix, current); return Ok(()); } common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); @@ -234,7 +232,7 @@ pub struct BatchUpdate(pub Vec>); impl ProcessStack for BatchUpdate where T: TrieLayout, - B: Borrow<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]> + Into, { @@ -242,15 +240,15 @@ impl ProcessStack for BatchUpdate } fn enter_terminal(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) - -> Option<(Node, S)> { + -> Option<(Node, B>, S)> { None } - fn exit(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) { + fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode) { self.0.push(stacked.into_encoded()); } - fn exit_root(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) { + fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode) { self.0.push(stacked.into_encoded()); } } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index e31cf266..f420f365 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -58,7 +58,7 @@ pub(crate) enum NodeHandle { impl, SH: AsRef<[u8]>> NodeHandle { /// Get a node handle ref to this handle. - fn as_ref(&self) -> crate::node::NodeHandle { + pub(crate) fn as_ref(&self) -> crate::node::NodeHandle { match self { NodeHandle::InMemory(sh) => crate::node::NodeHandle::Inline(sh.as_ref()), NodeHandle::Hash(h) => crate::node::NodeHandle::Hash(h.as_ref()), @@ -66,6 +66,34 @@ impl, SH: AsRef<[u8]>> NodeHandle { } } +impl + Send + Copy, SH: AsRef<[u8]>> NodeHandle +where + H: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + + PartialEq + Eq + Hash + Send + Sync + Clone + Copy, + SH: AsRef<[u8]>, +{ + /// Get a child reference (this has a cost but doing + /// otherwhise requires changing codec trait). + pub(crate) fn as_child_ref

(self) -> ChildReference + where + H2: Hasher + { + match self { + NodeHandle::InMemory(sh) => { + let sh_ref = sh.as_ref(); + // TODO EMCH consider keeping the encoded buffer + // of node handle (need to apply everywhere). + let mut h = H2::Out::default(); + let len = sh_ref.len(); + h.as_mut()[..len].copy_from_slice(&sh_ref[..len]); + ChildReference::Inline(h, len) + }, + NodeHandle::Hash(h) => ChildReference::Hash(h), + } + } + +} + impl From for NodeHandle { fn from(handle: StorageHandle) -> Self { NodeHandle::InMemory(handle) @@ -105,6 +133,7 @@ pub(crate) enum Node { } impl Node { + /// Get extension part of the node (partial) if any. pub fn partial(&self) -> Option { match self { @@ -219,12 +248,19 @@ where }; Ok(node) } +} + +impl Node +where + O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + + PartialEq + Eq + Hash + Send + Sync + Clone + Copy +{ // TODO: parallelize pub(crate) fn into_encoded(self, mut child_cb: F) -> Vec where - C: NodeCodec, - F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, + C: NodeCodec, + F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { match self { From f1c97e0d8ded5f32558c251eb2883794f11589db Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Nov 2019 13:23:47 +0100 Subject: [PATCH 006/118] quick test to start implementing. --- test-support/reference-trie/src/lib.rs | 19 ++++++ trie-db/src/lib.rs | 2 +- trie-db/src/traverse.rs | 83 ++++++++++++++++++++++++-- trie-db/src/triedbmut.rs | 8 +-- 4 files changed, 101 insertions(+), 11 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 43c94e64..6fea64c4 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -37,6 +37,7 @@ pub use trie_db::{ NodeCodec, }; pub use trie_db::{Record, TrieLayout, TrieConfiguration, nibble_ops}; +pub use trie_db::traverse::{BatchUpdate, trie_traverse_key}; pub use trie_root::TrieStream; pub mod node { pub use trie_db::node::Node; @@ -1186,6 +1187,24 @@ pub fn compare_no_extension_insert_remove( assert_eq!(*t.root(), calc_root_no_extension(data2)); } +pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( + db: &'a mut dyn hash_db::HashDB, + root: &'a [u8; 32], + elements: I, + batch_update: &'a mut BatchUpdate, +) + where + I: IntoIterator)>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, +{ + // TODO return type ?? EMCH generally this need redesign as it expose to many internal types: + // probably expose a function from traverse + trie_traverse_key::(db, root, elements, batch_update).unwrap(); +} + + #[cfg(test)] mod tests { use super::*; diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index a0c63a62..b8b256b7 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -81,7 +81,7 @@ pub mod recorder; mod fatdb; mod fatdbmut; mod iter_build; -mod traverse; +pub mod traverse; mod iterator; mod lookup; mod nibble; diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index f03692f4..2e3e7741 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -35,7 +35,7 @@ type StorageHandle = Vec; /// StackedNode can be updated. /// A state can be use. -pub(crate) enum StackedNode +pub enum StackedNode where B: Borrow<[u8]>, T: TrieLayout, @@ -88,12 +88,12 @@ impl StackedNode } /// Visitor trait to implement when using `trie_traverse_key`. -pub(crate) trait ProcessStack +pub trait ProcessStack where T: TrieLayout, B: Borrow<[u8]>, K: AsRef<[u8]> + Ord, - V: AsRef<[u8]> + Into, + V: AsRef<[u8]>, { /// Callback on enter a node, change can be applied here. fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); @@ -108,7 +108,7 @@ pub(crate) trait ProcessStack } /// The main entry point for traversing a trie by a set of keys. -pub(crate) fn trie_traverse_key<'a, T, I, K, V, S, B, F>( +pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( db: &'a mut dyn HashDB, root: &'a TrieHash, elements: I, @@ -118,7 +118,7 @@ pub(crate) fn trie_traverse_key<'a, T, I, K, V, S, B, F>( T: TrieLayout, I: IntoIterator)>, K: AsRef<[u8]> + Ord, - V: AsRef<[u8]> + Into, + V: AsRef<[u8]>, S: Default, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, F: ProcessStack, @@ -234,7 +234,7 @@ impl ProcessStack for BatchUpdate T: TrieLayout, B: Borrow<[u8]> + AsRef<[u8]>, K: AsRef<[u8]> + Ord, - V: AsRef<[u8]> + Into, + V: AsRef<[u8]>, { fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) { } @@ -252,3 +252,74 @@ impl ProcessStack for BatchUpdate self.0.push(stacked.into_encoded()); } } + +#[cfg(test)] +mod tests { + use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, TrieMut, NodeCodec, + ReferenceNodeCodec, reference_trie_root, reference_trie_root_no_extension, + NoExtensionLayout, TrieLayout, trie_traverse_key_no_extension_build, BatchUpdate, + }; + + use memory_db::{MemoryDB, PrefixedKey}; + use keccak_hasher::KeccakHasher; + use DBValue; + use crate::triedbmut::tests::populate_trie_no_extension; + + fn compare_with_triedbmut( + x: &[(Vec, Vec)], + v: &[(Vec, Option>)], + ) { + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + populate_trie_no_extension(&mut db, &mut root, x).commit(); + { + let t = RefTrieDBNoExt::new(&db, &root); + println!("bef {:?}", t); + } + + let initial_root = root.clone(); + let initial_db = db.clone(); + // reference + { + let mut t = RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 0..v.len() { + let key: &[u8]= &v[i].0; + if let Some(val) = v[i].1.as_ref() { + t.insert(key, val.as_ref()).unwrap(); + } else { + t.remove(key).unwrap(); + } + } + } + { + let t = RefTrieDBNoExt::new(&db, &root); + println!("aft {:?}", t); + } + + + let reference_root = root.clone(); + + let mut batch_update = BatchUpdate(Default::default()); + trie_traverse_key_no_extension_build( + &mut db, &initial_root, v.iter().map(|(a, b)| (a, b.as_ref())), &mut batch_update); + + panic!("end {:?}", batch_update.0); + + } + + #[test] + fn dummy1() { + compare_with_triedbmut( + &[ + (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), + (vec![0xf1u8, 0x23], vec![0x01u8, 0x23]), + (vec![0x81u8, 0x23], vec![0x01u8, 0x23]), + ], + &[ + (vec![0x01u8, 0x23], Some(vec![0xffu8, 0x23])), + (vec![0xf1u8, 0x23], Some(vec![0xffu8, 0x23])), + (vec![0x81u8, 0x23], None), + ], + ); + } +} diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index f420f365..97b263b2 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -49,7 +49,7 @@ pub(crate) struct StorageHandle(usize); // Handles to nodes in the trie. #[cfg_attr(feature = "std", derive(Debug))] -pub(crate) enum NodeHandle { +pub enum NodeHandle { /// Loaded into memory. InMemory(SH), /// Either a hash or an inline node @@ -114,7 +114,7 @@ pub(crate) type NibbleFullKey<'key> = NibbleSlice<'key>; // TODO EMCH make a local type alias with only H!! /// Node types in the Trie. #[cfg_attr(feature = "std", derive(Debug))] -pub(crate) enum Node { +pub enum Node { /// Empty node. Empty, /// A leaf node contains the end of a key and a value. @@ -1640,7 +1640,7 @@ fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { } #[cfg(test)] -mod tests { +pub(crate) mod tests { use env_logger; use standardmap::*; use DBValue; @@ -1672,7 +1672,7 @@ mod tests { } } - fn populate_trie_no_extension<'db>( + pub(crate) fn populate_trie_no_extension<'db>( db: &'db mut dyn HashDB, root: &'db mut ::Out, v: &[(Vec, Vec)] From 5334f2f2f2993c2d12532347a77c3813450d21fc Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Nov 2019 20:11:20 +0100 Subject: [PATCH 007/118] code start to get rather awful. --- trie-db/src/node.rs | 91 ++++++++++++++++- trie-db/src/traverse.rs | 214 ++++++++++++++++++++++++++++++--------- trie-db/src/triedbmut.rs | 30 ++++++ 3 files changed, 285 insertions(+), 50 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 549a2551..a588cde2 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -17,7 +17,7 @@ use hash_db::Hasher; use nibble::NibbleSlice; use nibble::nibble_ops; use node_codec::NodeCodec; - +use crate::triedbmut::Node as TNode; use core_::borrow::Borrow; use core_::ops::Range; #[cfg(not(feature = "std"))] @@ -89,6 +89,19 @@ impl NodeHandlePlan { NodeHandlePlan::Inline(range) => NodeHandle::Inline(&data[range.clone()]), } } + + /// TODO + pub fn build_thandle + Default>(&self, data: &[u8]) -> crate::triedbmut::NodeHandle> { + match self { + NodeHandlePlan::Hash(range) => { + let mut hash = H::default(); + hash.as_mut().copy_from_slice(&data[range.clone()]); + crate::triedbmut::NodeHandle::Hash(hash) + }, + NodeHandlePlan::Inline(range) => crate::triedbmut::NodeHandle::InMemory((&data[range.clone()]).into()), + } + } + } /// A `NibbleSlicePlan` is a blueprint for decoding a nibble slice from a byte slice. The @@ -243,3 +256,79 @@ impl> OwnedNode { } } } + +impl> OwnedNode { + /// Set a value. + pub fn set_value + Default>(&mut self, new_value: &[u8]) -> Option>> { + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Leaf { partial, value } => { + if &data[value.clone()] == new_value { + return None; + } + Some(TNode::Leaf( + partial.build(data).into(), + new_value.into(), + )) + }, + NodePlan::Extension { .. } // TODO Extension + | NodePlan::Branch { .. } // TODO branch + | NodePlan::Empty => None, + NodePlan::NibbledBranch { partial, value, children } => { + if let Some(value) = value { + if &data[value.clone()] == new_value { + return None; + } + } + let mut child_slices = [ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]; + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); + } + + Some(TNode::NibbledBranch( + partial.build(data).into(), + Box::new(child_slices), + Some(new_value.into()), + )) + + }, + } + } + /// Remove a value. + pub fn remove_value + Default>(&mut self) -> Option>>> { + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Leaf { partial, value } => Some(None), + NodePlan::Extension { .. } // TODO Extension + | NodePlan::Branch { .. } // TODO branch + | NodePlan::Empty => None, + NodePlan::NibbledBranch { partial, value, children } => { + if value.is_none() { + return None; + } + let mut child_slices = [ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]; + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); + } + + Some(Some(TNode::NibbledBranch( + partial.build(data).into(), + Box::new(child_slices), + None, + ))) + }, + } + } + + +} diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 2e3e7741..d90ad80d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -43,13 +43,16 @@ pub enum StackedNode /// Read node. Unchanged(OwnedNode, S), /// Modified node. - Changed(Node, B>, S), + Changed(Node, Vec>, S), + /// Deleted node. + Deleted(S), } impl StackedNode where B: Borrow<[u8]> + AsRef<[u8]>, T: TrieLayout, + S: Clone, // TrieHash: AsRef<[u8]>, { /// Get extension part of the node (partial) if any. @@ -57,6 +60,7 @@ impl StackedNode match self { StackedNode::Unchanged(node, ..) => node.partial(), StackedNode::Changed(node, ..) => node.partial(), + StackedNode::Deleted(..) => None, } } @@ -65,6 +69,41 @@ impl StackedNode match self { StackedNode::Unchanged(node, ..) => node.child(ix), StackedNode::Changed(node, ..) => node.child(ix), + StackedNode::Deleted(..) => None, + } + } + + /// Set a value if the node can contain one. + pub fn set_value(&mut self, value: &[u8]) { + match self { + StackedNode::Unchanged(node, state) => { + if let Some(new) = node.set_value(value) { + *self = StackedNode::Changed(new, state.clone()); + } + }, + StackedNode::Changed(node, ..) => node.set_value(value), + StackedNode::Deleted(..) => (), + } + } + + /// Remove a value if the node contains one. + pub fn remove_value(&mut self) { + match self { + StackedNode::Unchanged(node, state) => { + match node.remove_value() { + Some(Some(new)) => + *self = StackedNode::Changed(new, state.clone()), + Some(None) => + *self = StackedNode::Deleted(state.clone()), + None => (), + } + }, + StackedNode::Changed(node, state) => { + if node.remove_value() { + *self = StackedNode::Deleted(state.clone()); + } + }, + StackedNode::Deleted(..) => (), } } } @@ -73,6 +112,7 @@ impl StackedNode where B: Borrow<[u8]> + AsRef<[u8]>, T: TrieLayout, + S: Clone, { /// Encode node @@ -83,6 +123,7 @@ impl StackedNode |child, o_slice, o_index| { child.as_child_ref::() }), + StackedNode::Deleted(..) => T::Codec::empty_node().to_vec(), } } } @@ -91,6 +132,7 @@ impl StackedNode pub trait ProcessStack where T: TrieLayout, + S: Clone, B: Borrow<[u8]>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, @@ -99,8 +141,14 @@ pub trait ProcessStack fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); /// Same as `enter` but at terminal node element (terminal considering next key so branch is /// possible here). - fn enter_terminal(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) - -> Option<(Node, B>, S)>; + fn enter_terminal( + &mut self, + prefix: &NibbleVec, + stacked: &mut StackedNode, + index_element: usize, + key_element: &[u8], + value_element: Option<&[u8]>, + ) -> Option<(Node, Vec>, S)>; /// Callback on exit a node, commit action on change node should be applied here. fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode); /// Same as `exit` but for root (very last exit call). @@ -119,7 +167,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( I: IntoIterator)>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, - S: Default, + S: Default + Clone, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, F: ProcessStack, { @@ -129,7 +177,6 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( // TODO EMCH do following update (used for error only) let mut last_hash = root; let root = if let Ok(root) = fetch::(db, root, EMPTY_PREFIX) { - root } else { return Err(Box::new(TrieError::InvalidStateRoot(*root))); @@ -139,75 +186,128 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let mut prefix = NibbleVec::new(); let mut current = StackedNode::::Unchanged(root, Default::default()); let mut common_depth = 0; + callback.enter(&prefix, &mut current); current.partial().map(|p| { common_depth += p.len(); prefix.append_partial(p.right()) }); - for (k, v) in elements { - let dest = NibbleFullKey::new(k.as_ref()); - let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + let mut switch_iter: bool; + for (el_index, (k, v)) in elements.into_iter().enumerate() { + let mut switch_iter = true; loop { + let mut common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); + common_depth = min(prefix.len(), common_depth); + + if common_depth < prefix.len() { + while common_depth < prefix.len() { + // go up + if let Some((c, d)) = stack.pop() { + callback.exit(&prefix, current); + current = c; + prefix.drop_lasts(d); + } else { + callback.exit_root(&prefix, current); + return Ok(()); + } + //common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); + common_depth = min(prefix.len(), common_depth); + } + if !switch_iter { + // going up not on a new key means we need to switch to next one + break; + } + } + + switch_iter = false; + let dest = NibbleFullKey::new(k.as_ref()); + let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + // TODO EMCH go down extension here - if common_depth >= dest_depth { - if let Some((new, s)) = callback.enter_terminal(&prefix, &mut current) { + debug_assert!(common_depth <= dest_depth); + if common_depth == dest_depth { + if let Some((new, s)) = callback.enter_terminal( + &prefix, + &mut current, + el_index, + k.as_ref(), + v.as_ref().map(|v| v.as_ref()), + ) { stack.push((current, common_depth)); new.partial().map(|p| { common_depth += p.len(); prefix.append_partial(p.right()) }); current = StackedNode::Changed(new, s); - }; - // go next key - break; + } else { + // go next key + break; + } } else { // try go down - let next_index = dest.at(common_depth + 1); + let next_index = dest.at(common_depth); let next_node = match current.child(next_index) { Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - fetch::(db, &hash, prefix.as_prefix())? + StackedNode::Unchanged( + fetch::(db, &hash, prefix.as_prefix())?, + Default::default(), + ) }, Some(NodeHandle::Inline(node_encoded)) => { - // Instantiating B is only for inline node, still costy. - OwnedNode::new::(B::from(node_encoded)) - .map_err(|e| Box::new(TrieError::DecoderError(*last_hash, e)))? + + StackedNode::Unchanged( + // Instantiating B is only for inline node, still costy. + OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError(*last_hash, e)))?, + Default::default(), + ) }, None => { - // advance key by breaking loop - break; + // this is a terminal node + if let Some((new, s)) = callback.enter_terminal( + &prefix, + &mut current, + el_index, + k.as_ref(), + v.as_ref().map(|v| v.as_ref()), + ) { + new.partial().map(|p| { + common_depth += p.len(); + prefix.append_partial(p.right()) + }); + StackedNode::Changed(new, s) + } else { + // go next key + break; + } }, }; callback.enter(&prefix, &mut current); - stack.push((current, common_depth)); - current = StackedNode::Unchanged(next_node, Default::default()); - current.partial().map(|p| { - common_depth += p.len(); - prefix.append_partial(p.right()) - }); - } - } - let mut common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); - common_depth = min(prefix.len(), common_depth); - while common_depth < prefix.len() { - // go up - if let Some((c, d)) = stack.pop() { - callback.exit(&prefix, current); - current = c; - prefix.drop_lasts(prefix.len() - d); - } else { - callback.exit_root(&prefix, current); - return Ok(()); + prefix.push(next_index); + let add_levels = next_node.partial().map(|p| { + prefix.append_partial(p.right()); + p.len() + }).unwrap_or(0) + 1; + common_depth += add_levels; + stack.push((current, add_levels)); + current = next_node; } - common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); - common_depth = min(prefix.len(), common_depth); } } + // empty stack + while let Some((c, d)) = stack.pop() { + callback.exit(&prefix, current); + current = c; + prefix.drop_lasts(d); + } + callback.exit_root(&prefix, current); + Ok(()) } @@ -232,6 +332,7 @@ pub struct BatchUpdate(pub Vec>); impl ProcessStack for BatchUpdate where T: TrieLayout, + S: Clone, B: Borrow<[u8]> + AsRef<[u8]>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, @@ -239,9 +340,24 @@ impl ProcessStack for BatchUpdate fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) { } - fn enter_terminal(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) - -> Option<(Node, B>, S)> { + fn enter_terminal( + &mut self, + prefix: &NibbleVec, + stacked: &mut StackedNode, + index_element: usize, + key_element: &[u8], + value_element: Option<&[u8]>, + ) -> Option<(Node, Vec>, S)> { + if key_element.len() * nibble_ops::NIBBLE_PER_BYTE == prefix.len() { + if let Some(value) = value_element { + stacked.set_value(value); + } else { + stacked.remove_value(); + } None + } else { + Some(unimplemented!()) + } } fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode) { @@ -311,14 +427,14 @@ mod tests { fn dummy1() { compare_with_triedbmut( &[ - (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), - (vec![0xf1u8, 0x23], vec![0x01u8, 0x23]), - (vec![0x81u8, 0x23], vec![0x01u8, 0x23]), + (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8, 0x23]), + (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), + (vec![0x01u8, 0x81u8, 0x23], vec![0x01u8, 0x25]), ], &[ - (vec![0x01u8, 0x23], Some(vec![0xffu8, 0x23])), - (vec![0xf1u8, 0x23], Some(vec![0xffu8, 0x23])), - (vec![0x81u8, 0x23], None), + (vec![0x01u8, 0x01u8, 0x24], Some(vec![0xffu8, 0x33])), + (vec![0x01u8, 0x81u8, 0x23], None), + (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], ); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 97b263b2..5c9bb03d 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -145,6 +145,36 @@ impl Node { => Some(NibbleSlice::new_offset(&partial.1[..], partial.0)), } } + + /// Get extension part of the node (partial) if any. + pub fn set_value(&mut self, value: &[u8]) { + match self { + Node::Extension(..) + | Node::Empty => (), + Node::Branch ( _, val ) + | Node::NibbledBranch(_, _, val) + => *val = Some(value.into()), + Node::Leaf(_, val) + => *val = value.into(), + } + } + + /// Return true if the node can be removed to. + pub fn remove_value(&mut self) -> bool { + match self { + Node::Extension(..) + | Node::Empty => false, + Node::Branch(encoded_children, val) + | Node::NibbledBranch(_, encoded_children, val) + => { + *val = None; + !encoded_children.iter().any(Option::is_some) + }, + Node::Leaf(..) + => true, + } + } + } impl, SH: AsRef<[u8]>> Node { From a1d1fee606722c21179f13e053356872c225e798 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Nov 2019 20:29:36 +0100 Subject: [PATCH 008/118] fix initial testing --- test-support/reference-trie/src/lib.rs | 1 + trie-db/src/traverse.rs | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 6fea64c4..967d21ee 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1188,6 +1188,7 @@ pub fn compare_no_extension_insert_remove( } pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( + // TODO db to non mutable db: &'a mut dyn hash_db::HashDB, root: &'a [u8; 32], elements: I, diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index d90ad80d..97900edb 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -394,10 +394,10 @@ mod tests { } let initial_root = root.clone(); - let initial_db = db.clone(); + let mut initial_db = db.clone(); // reference { - let mut t = RefTrieDBMutNoExt::new(&mut db, &mut root); + let mut t = RefTrieDBMutNoExt::from_existing(&mut db, &mut root).unwrap(); for i in 0..v.len() { let key: &[u8]= &v[i].0; if let Some(val) = v[i].1.as_ref() { @@ -412,13 +412,15 @@ mod tests { println!("aft {:?}", t); } - + for a in db.drain() { + println!("{:?}", a); + } let reference_root = root.clone(); let mut batch_update = BatchUpdate(Default::default()); trie_traverse_key_no_extension_build( - &mut db, &initial_root, v.iter().map(|(a, b)| (a, b.as_ref())), &mut batch_update); - + &mut initial_db, &initial_root, v.iter().map(|(a, b)| (a, b.as_ref())), &mut batch_update); + panic!("end {:?}", batch_update.0); } @@ -428,12 +430,13 @@ mod tests { compare_with_triedbmut( &[ (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8, 0x23]), - (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), (vec![0x01u8, 0x81u8, 0x23], vec![0x01u8, 0x25]), + (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), ], &[ - (vec![0x01u8, 0x01u8, 0x24], Some(vec![0xffu8, 0x33])), - (vec![0x01u8, 0x81u8, 0x23], None), + (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), +// (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), +// (vec![0x01u8, 0x81u8, 0x23], None), (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], ); From 08b629f2d011d5fadf1129758775a147f7089dc7 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 3 Dec 2019 20:22:07 +0100 Subject: [PATCH 009/118] make value change passing on direct value. Next extend case, and then case where branch in middle of partial. --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/node.rs | 75 +++++++++++++++++ trie-db/src/traverse.rs | 106 +++++++++++++++++++------ trie-db/src/triedbmut.rs | 61 ++++++++++++++ 4 files changed, 217 insertions(+), 27 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 967d21ee..e0cb0f34 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1192,7 +1192,7 @@ pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( db: &'a mut dyn hash_db::HashDB, root: &'a [u8; 32], elements: I, - batch_update: &'a mut BatchUpdate, + batch_update: &'a mut BatchUpdate<::Out>, ) where I: IntoIterator)>, diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index a588cde2..c2982a29 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -18,6 +18,7 @@ use nibble::NibbleSlice; use nibble::nibble_ops; use node_codec::NodeCodec; use crate::triedbmut::Node as TNode; +use crate::triedbmut::NodeHandle as TNodeHandle; use core_::borrow::Borrow; use core_::ops::Range; #[cfg(not(feature = "std"))] @@ -330,5 +331,79 @@ impl> OwnedNode { } } + /// Set a handle to a child node or remove it if handle is none. + /// Return possibly updated or removed node. + pub fn set_handle + Default>(&mut self, handle: Option>>, index: u8) + -> Option>>> { + let index = index as usize; + let data = &self.data.borrow(); + match &mut self.plan { + NodePlan::Empty => unreachable!("corner case cannot be handle in this function"), + NodePlan::Extension { .. } // TODO Extension + | NodePlan::Branch { .. } => unreachable!("function only for no extension trie"), + NodePlan::Leaf { partial, value } => { + if handle.is_some() { + let mut child_slices = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + child_slices[index] = handle; + + Some(Some(TNode::NibbledBranch( + partial.build(data).into(), + child_slices, + Some(data[value.clone()].into())), + )) + } else { + None + } + }, + NodePlan::NibbledBranch { partial, value, children } => { + if handle.is_none() && children[index].is_none() { + None + } else { + let del = handle.is_none() && children[index].is_some(); + let value = if let Some(value) = value.clone() { + Some(data[value.clone()].into()) + } else { + None + }; + + if handle.is_none() { + children[index] = None; + } + if del && !children.iter().any(Option::is_some) { + if let Some(value) = value { + Some(Some(TNode::Leaf( + partial.build(data).into(), + value, + ))) + } else { + Some(None) + } + } else { + + let mut child_slices = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); + } + child_slices[index] = handle; + Some(Some(TNode::NibbledBranch( + partial.build(data).into(), + child_slices, + value, + ))) + } + } + } + } + } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index d7a65bea..a6ecdf28 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -18,6 +18,7 @@ //! batch update of ordered key values. use crate::triedbmut::{Node, NibbleFullKey}; +use crate::triedbmut::NodeHandle as NodeHandleTrieMut; use crate::node::{OwnedNode, NodePlan, NodeHandle}; use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; #[cfg(feature = "std")] @@ -30,8 +31,8 @@ use hash_db::{HashDB, Prefix, EMPTY_PREFIX, Hasher}; use crate::NodeCodec; use ::core_::cmp::*; - type StorageHandle = Vec; +type OwnedNodeHandle = NodeHandleTrieMut; /// StackedNode can be updated. /// A state can be use. @@ -43,7 +44,7 @@ pub enum StackedNode /// Read node. Unchanged(OwnedNode, S), /// Modified node. - Changed(Node, Vec>, S), + Changed(Node, StorageHandle>, S), /// Deleted node. Deleted(S), } @@ -106,6 +107,28 @@ impl StackedNode StackedNode::Deleted(..) => (), } } + + /// Set a handle to a child node or remove it if handle is none. + pub fn set_handle(&mut self, handle: Option>>, index: u8) { + match self { + StackedNode::Unchanged(node, state) => { + match node.set_handle(handle, index) { + Some(Some(new)) => + *self = StackedNode::Changed(new, state.clone()), + Some(None) => + *self = StackedNode::Deleted(state.clone()), + None => (), + } + }, + StackedNode::Changed(node, state) => { + if node.set_handle(handle, index) { + *self = StackedNode::Deleted(state.clone()); + } + }, + StackedNode::Deleted(..) => unreachable!(), + } + } + } impl StackedNode @@ -150,7 +173,8 @@ pub trait ProcessStack value_element: Option<&[u8]>, ) -> Option<(Node, Vec>, S)>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode); + fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode) + -> Option>>>; /// Same as `exit` but for root (very last exit call). fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode); } @@ -172,7 +196,8 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( F: ProcessStack, { // stack of traversed nodes - let mut stack: Vec<(StackedNode, usize)> = Vec::with_capacity(32); + // first usize is depth, second usize is the parent index. + let mut stack: Vec<(StackedNode, usize, u8)> = Vec::with_capacity(32); // TODO EMCH do following update (used for error only) let mut last_hash = root; @@ -193,6 +218,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( }); let mut switch_iter: bool; + let mut next_index: u8 = 0; for (el_index, (k, v)) in elements.into_iter().enumerate() { let mut switch_iter = true; loop { @@ -202,11 +228,14 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if common_depth < prefix.len() { while common_depth < prefix.len() { // go up - if let Some((c, d)) = stack.pop() { - callback.exit(&prefix, current); - current = c; + if let Some((mut c, d, ix)) = stack.pop() { prefix.drop_lasts(d); + if let Some(handle) = callback.exit(&prefix, current) { + c.set_handle(handle, ix); + } + current = c; } else { + prefix.clear(); callback.exit_root(&prefix, current); return Ok(()); } @@ -233,7 +262,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( k.as_ref(), v.as_ref().map(|v| v.as_ref()), ) { - stack.push((current, common_depth)); + stack.push((current, common_depth, next_index)); new.partial().map(|p| { common_depth += p.len(); prefix.append_partial(p.right()) @@ -246,7 +275,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } else { // try go down - let next_index = dest.at(common_depth); + next_index = dest.at(common_depth); let next_node = match current.child(next_index) { Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); @@ -293,7 +322,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( p.len() }).unwrap_or(0) + 1; common_depth += add_levels; - stack.push((current, add_levels)); + stack.push((current, add_levels, next_index)); current = next_node; } } @@ -301,11 +330,14 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } // empty stack - while let Some((c, d)) = stack.pop() { - callback.exit(&prefix, current); - current = c; - prefix.drop_lasts(d); + while let Some((mut c, d, ix)) = stack.pop() { + prefix.drop_lasts(d); + if let Some(handle) = callback.exit(&prefix, current) { + c.set_handle(handle, ix); + } + current = c; } + prefix.clear(); callback.exit_root(&prefix, current); Ok(()) @@ -327,9 +359,9 @@ fn fetch>( } /// Contains ordered node change for this iteration. -pub struct BatchUpdate(pub Vec<(NibbleVec, Vec)>); +pub struct BatchUpdate(pub Vec<(NibbleVec, H, Vec)>); -impl ProcessStack for BatchUpdate +impl ProcessStack for BatchUpdate> where T: TrieLayout, S: Clone, @@ -360,19 +392,35 @@ impl ProcessStack for BatchUpdate } } - fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode) { + fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode) + -> Option>>> { + println!("exit prefix: {:?}", prefix); match stacked { - s@StackedNode::Changed(..) => { - self.0.push((prefix.clone(), s.into_encoded())); - }, - _ => (), + s@StackedNode::Changed(..) => Some(Some({ + println!("push exit"); + let encoded = s.into_encoded(); + if encoded.len() < 32 { + OwnedNodeHandle::InMemory(encoded) + } else { + let hash = ::hash(&encoded[..]); + // costy clone (could get read from here) + self.0.push((prefix.clone(), hash.clone(), encoded)); + OwnedNodeHandle::Hash(hash) + } + })), + StackedNode::Deleted(..) => Some(None), + _ => None, } } fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode) { + println!("exit prefix r: {:?}", prefix); match stacked { s@StackedNode::Changed(..) => { - self.0.push((prefix.clone(), s.into_encoded())); + let encoded = s.into_encoded(); + let hash = ::hash(&encoded[..]); + println!("push exit_roto"); + self.0.push((prefix.clone(), hash, encoded)); }, _ => (), } @@ -396,11 +444,12 @@ mod tests { type H256 = ::Out; fn memory_db_from_delta( - delta: Vec<(NibbleVec, Vec)>, + delta: Vec<(NibbleVec, H256, Vec)>, mdb: &mut MemoryDB, DBValue>, ) { - for (p, v) in delta { - mdb.insert(p.as_prefix(), v.as_slice()); + for (p, h, v) in delta { + // damn elastic array in value looks costy + mdb.emplace(h, p.as_prefix(), v[..].into()); } } @@ -446,7 +495,12 @@ mod tests { let mut batch_delta = initial_db; memory_db_from_delta(batch_update.0, &mut batch_delta); - assert_eq!(reference_delta.drain(), batch_delta.drain()); + // sort + let batch_delta: std::collections::BTreeMap<_, _> = batch_delta.drain().into_iter().collect(); + assert_eq!( + batch_delta, + reference_delta.drain().into_iter().filter(|(_, (_, rc))| rc >= &0).collect(), + ); panic!("end"); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 5c9bb03d..06bf2954 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -168,6 +168,8 @@ impl Node { | Node::NibbledBranch(_, encoded_children, val) => { *val = None; + // TODO store a boolean state to indicate the current + // number of set children !encoded_children.iter().any(Option::is_some) }, Node::Leaf(..) @@ -175,6 +177,65 @@ impl Node { } } + /// Return true if the node can be removed. + /// This is only for no extension trie (a variant would be + /// needed for trie with extension). + pub fn set_handle( + &mut self, + handle: Option>, + index: u8, + ) -> bool { + let index = index as usize; + let node = mem::replace(self, Node::Empty); + let node = match node { + Node::Extension(..) + | Node::Branch(..) => unreachable!("Only for no extension trie"), + // need to replace value before creating handle + // so it is a corner case TODO test case it + // (may be unreachable since changing to empty means + // we end the root process) + Node::Empty => unreachable!(), + Node::NibbledBranch(partial, mut encoded_children, val) => { + if handle.is_none() && encoded_children[index].is_some() { + Node::NibbledBranch(partial, encoded_children, val) + } else { + let del = handle.is_none() && encoded_children[index].is_some(); + encoded_children[index] = handle; + if del && !encoded_children.iter().any(Option::is_some) { + if let Some(val) = val { + // transform to leaf + Node::Leaf(partial, val) + } else { + Node::Empty + } + } else { + Node::NibbledBranch(partial, encoded_children, val) + } + } + }, + Node::Leaf(partial, val) => { + if handle.is_some() { + let mut children = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + children[index] = handle; + + Node::NibbledBranch(partial, children, Some(val)) + } else { + Node::Leaf(partial, val) + } + }, + }; + *self = node; + if let Node::Empty = self { + true + } else { + false + } + } } impl, SH: AsRef<[u8]>> Node { From 81fe6f0f5f08cea0adf0259589b2bbab1c4e27af Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 4 Dec 2019 10:31:06 +0100 Subject: [PATCH 010/118] Add removal to transaction, some very disturbing triedbmut comparison. --- trie-db/src/traverse.rs | 125 ++++++++++++++++++++++++++++----------- trie-db/src/triedbmut.rs | 5 +- 2 files changed, 94 insertions(+), 36 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index a6ecdf28..ebfb1e4d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -31,6 +31,9 @@ use hash_db::{HashDB, Prefix, EMPTY_PREFIX, Hasher}; use crate::NodeCodec; use ::core_::cmp::*; +// TODO make it deletion aware : do not stack unchanged key and pass them +// to exit when not needed. + type StorageHandle = Vec; type OwnedNodeHandle = NodeHandleTrieMut; @@ -173,16 +176,23 @@ pub trait ProcessStack value_element: Option<&[u8]>, ) -> Option<(Node, Vec>, S)>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode) + fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>>; /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode); + fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode, prev_hash: Option<&TrieHash>); +} + +struct StackItem, T: TrieLayout, S> { + node: StackedNode, + depth: usize, + parent_index: u8, + hash: Option>, } /// The main entry point for traversing a trie by a set of keys. pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( db: &'a mut dyn HashDB, - root: &'a TrieHash, + root_hash: &'a TrieHash, elements: I, callback: &mut F, ) -> Result<(), TrieHash, CError> @@ -197,15 +207,16 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( { // stack of traversed nodes // first usize is depth, second usize is the parent index. - let mut stack: Vec<(StackedNode, usize, u8)> = Vec::with_capacity(32); + let mut stack: Vec> = Vec::with_capacity(32); // TODO EMCH do following update (used for error only) - let mut last_hash = root; - let root = if let Ok(root) = fetch::(db, root, EMPTY_PREFIX) { + let mut last_hash: Option> = Some(*root_hash); + let root = if let Ok(root) = fetch::(db, root_hash, EMPTY_PREFIX) { root } else { - return Err(Box::new(TrieError::InvalidStateRoot(*root))); + return Err(Box::new(TrieError::InvalidStateRoot(*root_hash))); }; + let root_hash = Some(root_hash); // stack.push(StackedNode::Unchanged(root, Default::default())); let mut prefix = NibbleVec::new(); @@ -228,15 +239,15 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if common_depth < prefix.len() { while common_depth < prefix.len() { // go up - if let Some((mut c, d, ix)) = stack.pop() { - prefix.drop_lasts(d); - if let Some(handle) = callback.exit(&prefix, current) { - c.set_handle(handle, ix); + if let Some(StackItem{mut node, depth, parent_index, hash}) = stack.pop() { + prefix.drop_lasts(depth); + if let Some(handle) = callback.exit(&prefix, current, hash.as_ref()) { + node.set_handle(handle, parent_index); } - current = c; + current = node; } else { prefix.clear(); - callback.exit_root(&prefix, current); + callback.exit_root(&prefix, current, root_hash); return Ok(()); } //common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); @@ -262,7 +273,12 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( k.as_ref(), v.as_ref().map(|v| v.as_ref()), ) { - stack.push((current, common_depth, next_index)); + stack.push(StackItem { + node: current, + depth: common_depth, + parent_index: next_index, + hash: last_hash, + }); new.partial().map(|p| { common_depth += p.len(); prefix.append_partial(p.right()) @@ -280,17 +296,23 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - StackedNode::Unchanged( + let n = StackedNode::Unchanged( fetch::(db, &hash, prefix.as_prefix())?, Default::default(), - ) + ); + last_hash = Some(hash); + n }, Some(NodeHandle::Inline(node_encoded)) => { + last_hash = None; StackedNode::Unchanged( // Instantiating B is only for inline node, still costy. OwnedNode::new::(B::from(node_encoded)) - .map_err(|e| Box::new(TrieError::DecoderError(*last_hash, e)))?, + .map_err(|e| Box::new(TrieError::DecoderError( + last_hash.clone().unwrap_or_else(Default::default), + e, + )))?, Default::default(), ) }, @@ -322,7 +344,12 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( p.len() }).unwrap_or(0) + 1; common_depth += add_levels; - stack.push((current, add_levels, next_index)); + stack.push(StackItem { + node: current, + depth: add_levels, + parent_index: next_index, + hash: last_hash, + }); current = next_node; } } @@ -330,15 +357,15 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } // empty stack - while let Some((mut c, d, ix)) = stack.pop() { - prefix.drop_lasts(d); - if let Some(handle) = callback.exit(&prefix, current) { - c.set_handle(handle, ix); + while let Some(StackItem{mut node, depth, parent_index, hash}) = stack.pop() { + prefix.drop_lasts(depth); + if let Some(handle) = callback.exit(&prefix, current, hash.as_ref()) { + node.set_handle(handle, parent_index); } - current = c; + current = node; } prefix.clear(); - callback.exit_root(&prefix, current); + callback.exit_root(&prefix, current, root_hash); Ok(()) } @@ -359,7 +386,7 @@ fn fetch>( } /// Contains ordered node change for this iteration. -pub struct BatchUpdate(pub Vec<(NibbleVec, H, Vec)>); +pub struct BatchUpdate(pub Vec<(NibbleVec, H, Option>)>); impl ProcessStack for BatchUpdate> where @@ -392,7 +419,7 @@ impl ProcessStack for BatchUpdate> } } - fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode) + fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>> { println!("exit prefix: {:?}", prefix); match stacked { @@ -403,24 +430,40 @@ impl ProcessStack for BatchUpdate> OwnedNodeHandle::InMemory(encoded) } else { let hash = ::hash(&encoded[..]); + println!("push one"); // costy clone (could get read from here) - self.0.push((prefix.clone(), hash.clone(), encoded)); + self.0.push((prefix.clone(), hash.clone(), Some(encoded))); + if let Some(h) = prev_hash { + println!("rem one"); + self.0.push((prefix.clone(), h.clone(), None)); + } OwnedNodeHandle::Hash(hash) } })), - StackedNode::Deleted(..) => Some(None), + StackedNode::Deleted(..) => { + if let Some(h) = prev_hash { + println!("remd one"); + self.0.push((prefix.clone(), h.clone(), None)); + } + Some(None) + }, _ => None, } } - fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode) { + fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode, prev_hash: Option<&TrieHash>) { println!("exit prefix r: {:?}", prefix); match stacked { s@StackedNode::Changed(..) => { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); - println!("push exit_roto"); - self.0.push((prefix.clone(), hash, encoded)); + println!("pushr one"); + self.0.push((prefix.clone(), hash, Some(encoded))); + if let Some(h) = prev_hash { + // TODO this have been commented because it seems like triedbmut + // do not delete old root nodes. + //self.0.push((prefix.clone(), h.clone(), None)); + } }, _ => (), } @@ -444,12 +487,17 @@ mod tests { type H256 = ::Out; fn memory_db_from_delta( - delta: Vec<(NibbleVec, H256, Vec)>, + delta: Vec<(NibbleVec, H256, Option>)>, mdb: &mut MemoryDB, DBValue>, ) { for (p, h, v) in delta { - // damn elastic array in value looks costy - mdb.emplace(h, p.as_prefix(), v[..].into()); + if let Some(v) = v { + // damn elastic array in value looks costy + mdb.emplace(h, p.as_prefix(), v[..].into()); + } else { + println!("a remov: {:?}", h); + mdb.remove(&h, p.as_prefix()); + } } } @@ -479,6 +527,7 @@ mod tests { } } } + println!("AA {:?}", db.clone().drain()); { let t = RefTrieDBNoExt::new(&db, &root); println!("aft {:?}", t); @@ -499,7 +548,13 @@ mod tests { let batch_delta: std::collections::BTreeMap<_, _> = batch_delta.drain().into_iter().collect(); assert_eq!( batch_delta, - reference_delta.drain().into_iter().filter(|(_, (_, rc))| rc >= &0).collect(), + reference_delta.drain().into_iter() + // TODO there is something utterly wrong with + // triedbmut: nodes getting removed more than once + // and hash comming from nowhere + // from now simply skip -1 counted + .filter(|(_, (_, rc))| rc >= &0) + .collect(), ); panic!("end"); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 06bf2954..c437abfa 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -636,7 +636,8 @@ where Action::Replace(node) => Some((Stored::New(node), true)), Action::Delete => None, }, - Stored::Cached(node, hash) => match inspector(self, node, key)? { + Stored::Cached(node, hash) => { + match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::Cached(node, hash), false)), Action::Replace(node) => { self.death_row.insert((hash, key.left_owned())); @@ -646,6 +647,7 @@ where self.death_row.insert((hash, key.left_owned())); None } + } }, }) } @@ -1534,6 +1536,7 @@ where #[cfg(feature = "std")] trace!(target: "trie", "{:?} nodes to remove from db", self.death_row.len()); for (hash, prefix) in self.death_row.drain() { + println!("dr {:?}", &hash); self.db.remove(&hash, (&prefix.0[..], prefix.1)); } From b29fd4017f1c94ce1cab57fab21347aae124e143 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 4 Dec 2019 16:12:42 +0100 Subject: [PATCH 011/118] fix some niblle vec, by double indexing. Double indexing is convenient but not necessary. --- trie-db/src/nibble/nibbleslice.rs | 7 + trie-db/src/traverse.rs | 209 ++++++++++++++++++------------ trie-db/src/triedbmut.rs | 7 +- 3 files changed, 134 insertions(+), 89 deletions(-) diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index e544cdf5..9ab46f98 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -228,6 +228,13 @@ impl<'a> NibbleSlice<'a> { } } + /// Return length of left portion of `NibbleSlice`, if the slice + /// originates from a full key it will be the length of th `Prefix of + /// the node`. + pub fn left_len(&'a self) -> usize { + self.offset + } + /// Owned version of a `Prefix` from a `left` method call. pub fn left_owned(&'a self) -> (ElasticArray36, Option) { let (a, b) = self.left(); diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index ebfb1e4d..f873f95d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -30,6 +30,7 @@ use crate::DBValue; use hash_db::{HashDB, Prefix, EMPTY_PREFIX, Hasher}; use crate::NodeCodec; use ::core_::cmp::*; +use elastic_array::ElasticArray36; // TODO make it deletion aware : do not stack unchanged key and pass them // to exit when not needed. @@ -164,27 +165,31 @@ pub trait ProcessStack V: AsRef<[u8]>, { /// Callback on enter a node, change can be applied here. - fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode); + fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode); /// Same as `enter` but at terminal node element (terminal considering next key so branch is /// possible here). + /// TODO prefix semantic here is strange: it is prefix of child. fn enter_terminal( &mut self, - prefix: &NibbleVec, + prefix: NibbleSlice, stacked: &mut StackedNode, index_element: usize, key_element: &[u8], value_element: Option<&[u8]>, ) -> Option<(Node, Vec>, S)>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>>; /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode, prev_hash: Option<&TrieHash>); + fn exit_root(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>); } struct StackItem, T: TrieLayout, S> { node: StackedNode, + // depth of stack aka prefix size depth: usize, + // depth after item + depth_child: usize, parent_index: u8, hash: Option>, } @@ -219,55 +224,58 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let root_hash = Some(root_hash); // stack.push(StackedNode::Unchanged(root, Default::default())); - let mut prefix = NibbleVec::new(); let mut current = StackedNode::::Unchanged(root, Default::default()); let mut common_depth = 0; - callback.enter(&prefix, &mut current); - current.partial().map(|p| { - common_depth += p.len(); - prefix.append_partial(p.right()) - }); + let mut common_depth_child = current.partial().map(|p| { + p.len() + 1 + }).unwrap_or(1); + + let mut k: Option = None; + callback.enter(NibbleSlice::new(&[]), &mut current); - let mut switch_iter: bool; let mut next_index: u8 = 0; - for (el_index, (k, v)) in elements.into_iter().enumerate() { - let mut switch_iter = true; - loop { - let mut common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); - common_depth = min(prefix.len(), common_depth); + for (el_index, (next_k, v)) in elements.into_iter().enumerate() { + if let Some(k) = k.as_ref() { - if common_depth < prefix.len() { - while common_depth < prefix.len() { + let mut previous_common_depth_child = common_depth_child - 1; + let mut target_common_depth = + nibble_ops::biggest_depth(&k.as_ref()[..previous_common_depth_child / 2], next_k.as_ref()); + target_common_depth = min(common_depth, target_common_depth); + + if target_common_depth < previous_common_depth_child { + while target_common_depth < previous_common_depth_child { // go up - if let Some(StackItem{mut node, depth, parent_index, hash}) = stack.pop() { - prefix.drop_lasts(depth); - if let Some(handle) = callback.exit(&prefix, current, hash.as_ref()) { + if let Some(StackItem{mut node, depth, depth_child, parent_index, hash}) = stack.pop() { + target_common_depth = depth_child; + common_depth = depth; + previous_common_depth_child = depth_child - 1; + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(k.as_ref(), depth_child), + current, hash.as_ref(), + ) { node.set_handle(handle, parent_index); } current = node; } else { - prefix.clear(); - callback.exit_root(&prefix, current, root_hash); + callback.exit_root(NibbleSlice::new(&[]), current, root_hash); return Ok(()); } - //common_depth = nibble_ops::biggest_depth(prefix.inner(), k.as_ref()); - common_depth = min(prefix.len(), common_depth); - } - if !switch_iter { - // going up not on a new key means we need to switch to next one - break; } } + + } + + k = Some(next_k); + if let Some(k) = k.as_ref() { + let dest = NibbleFullKey::new(k.as_ref()); + let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - switch_iter = false; - let dest = NibbleFullKey::new(k.as_ref()); - let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - + loop { // TODO EMCH go down extension here debug_assert!(common_depth <= dest_depth); - if common_depth == dest_depth { + if common_depth_child - 1 == dest_depth { if let Some((new, s)) = callback.enter_terminal( - &prefix, + NibbleSlice::new_offset(k.as_ref(), common_depth_child - 1), &mut current, el_index, k.as_ref(), @@ -276,12 +284,13 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( stack.push(StackItem { node: current, depth: common_depth, + depth_child: common_depth_child, parent_index: next_index, hash: last_hash, }); + common_depth = common_depth_child; new.partial().map(|p| { - common_depth += p.len(); - prefix.append_partial(p.right()) + common_depth_child += p.len() + 1; }); current = StackedNode::Changed(new, s); } else { @@ -289,7 +298,6 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( break; } } else { - // try go down next_index = dest.at(common_depth); let next_node = match current.child(next_index) { @@ -297,7 +305,10 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); let n = StackedNode::Unchanged( - fetch::(db, &hash, prefix.as_prefix())?, + fetch::( + db, &hash, + NibbleSlice::new_offset(k.as_ref(), common_depth_child).left(), + )?, Default::default(), ); last_hash = Some(hash); @@ -305,7 +316,6 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( }, Some(NodeHandle::Inline(node_encoded)) => { last_hash = None; - StackedNode::Unchanged( // Instantiating B is only for inline node, still costy. OwnedNode::new::(B::from(node_encoded)) @@ -319,16 +329,12 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( None => { // this is a terminal node if let Some((new, s)) = callback.enter_terminal( - &prefix, + NibbleSlice::new_offset(k.as_ref(), common_depth_child - 1), &mut current, el_index, k.as_ref(), v.as_ref().map(|v| v.as_ref()), ) { - new.partial().map(|p| { - common_depth += p.len(); - prefix.append_partial(p.right()) - }); StackedNode::Changed(new, s) } else { // go next key @@ -337,35 +343,41 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( }, }; - callback.enter(&prefix, &mut current); - prefix.push(next_index); + callback.enter( + NibbleSlice::new_offset(k.as_ref(), common_depth_child - 1), + &mut current, + ); + let add_levels = next_node.partial().map(|p| { - prefix.append_partial(p.right()); - p.len() - }).unwrap_or(0) + 1; - common_depth += add_levels; - stack.push(StackItem { - node: current, - depth: add_levels, - parent_index: next_index, - hash: last_hash, - }); + p.len() + 1 + }).unwrap_or(1); + stack.push(StackItem { + node: current, + depth: common_depth, + depth_child: common_depth_child, + parent_index: next_index, + hash: last_hash, + }); + common_depth = common_depth_child; + common_depth_child += add_levels; current = next_node; } - } + }} } // empty stack - while let Some(StackItem{mut node, depth, parent_index, hash}) = stack.pop() { - prefix.drop_lasts(depth); - if let Some(handle) = callback.exit(&prefix, current, hash.as_ref()) { + while let Some(StackItem{mut node, depth, depth_child, parent_index, hash}) = stack.pop() { + if let Some(k) = k.as_ref() { + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(k.as_ref(), depth_child), + current, hash.as_ref(), + ) { node.set_handle(handle, parent_index); } current = node; - } - prefix.clear(); - callback.exit_root(&prefix, current, root_hash); + }} + callback.exit_root(NibbleSlice::new(&[]), current, root_hash); Ok(()) } @@ -376,9 +388,11 @@ fn fetch>( hash: &TrieHash, key: Prefix, ) -> Result, TrieHash, CError> { + println!("a {:?}", key); let node_encoded = db.get(hash, key) .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(*hash)))?; + println!("b"); Ok( OwnedNode::new::(node_encoded) .map_err(|e| Box::new(TrieError::DecoderError(*hash, e)))? @@ -386,7 +400,7 @@ fn fetch>( } /// Contains ordered node change for this iteration. -pub struct BatchUpdate(pub Vec<(NibbleVec, H, Option>)>); +pub struct BatchUpdate(pub Vec<((ElasticArray36, Option), H, Option>)>); impl ProcessStack for BatchUpdate> where @@ -396,18 +410,18 @@ impl ProcessStack for BatchUpdate> K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, { - fn enter(&mut self, prefix: &NibbleVec, stacked: &mut StackedNode) { + fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode) { } fn enter_terminal( &mut self, - prefix: &NibbleVec, + prefix: NibbleSlice, stacked: &mut StackedNode, index_element: usize, key_element: &[u8], value_element: Option<&[u8]>, ) -> Option<(Node, Vec>, S)> { - if key_element.len() * nibble_ops::NIBBLE_PER_BYTE == prefix.len() { + if key_element.len() * nibble_ops::NIBBLE_PER_BYTE == prefix.left_len() { if let Some(value) = value_element { stacked.set_value(value); } else { @@ -419,7 +433,7 @@ impl ProcessStack for BatchUpdate> } } - fn exit(&mut self, prefix: &NibbleVec, stacked: StackedNode, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>> { println!("exit prefix: {:?}", prefix); match stacked { @@ -432,10 +446,10 @@ impl ProcessStack for BatchUpdate> let hash = ::hash(&encoded[..]); println!("push one"); // costy clone (could get read from here) - self.0.push((prefix.clone(), hash.clone(), Some(encoded))); + self.0.push((prefix.left_owned(), hash.clone(), Some(encoded))); if let Some(h) = prev_hash { println!("rem one"); - self.0.push((prefix.clone(), h.clone(), None)); + self.0.push((prefix.left_owned(), h.clone(), None)); } OwnedNodeHandle::Hash(hash) } @@ -443,7 +457,7 @@ impl ProcessStack for BatchUpdate> StackedNode::Deleted(..) => { if let Some(h) = prev_hash { println!("remd one"); - self.0.push((prefix.clone(), h.clone(), None)); + self.0.push((prefix.left_owned(), h.clone(), None)); } Some(None) }, @@ -451,18 +465,16 @@ impl ProcessStack for BatchUpdate> } } - fn exit_root(&mut self, prefix: &NibbleVec, stacked: StackedNode, prev_hash: Option<&TrieHash>) { + fn exit_root(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) { println!("exit prefix r: {:?}", prefix); match stacked { s@StackedNode::Changed(..) => { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); println!("pushr one"); - self.0.push((prefix.clone(), hash, Some(encoded))); + self.0.push((prefix.left_owned(), hash, Some(encoded))); if let Some(h) = prev_hash { - // TODO this have been commented because it seems like triedbmut - // do not delete old root nodes. - //self.0.push((prefix.clone(), h.clone(), None)); + self.0.push((prefix.left_owned(), h.clone(), None)); } }, _ => (), @@ -483,20 +495,22 @@ mod tests { use DBValue; use hash_db::{EMPTY_PREFIX, Prefix, HashDB}; use crate::triedbmut::tests::populate_trie_no_extension; + use elastic_array::ElasticArray36; type H256 = ::Out; fn memory_db_from_delta( - delta: Vec<(NibbleVec, H256, Option>)>, + delta: Vec<((ElasticArray36, Option), H256, Option>)>, mdb: &mut MemoryDB, DBValue>, ) { for (p, h, v) in delta { if let Some(v) = v { + let prefix = (p.0.as_ref(), p.1); // damn elastic array in value looks costy - mdb.emplace(h, p.as_prefix(), v[..].into()); + mdb.emplace(h, prefix, v[..].into()); } else { - println!("a remov: {:?}", h); - mdb.remove(&h, p.as_prefix()); + let prefix = (p.0.as_ref(), p.1); + mdb.remove(&h, prefix); } } } @@ -513,6 +527,7 @@ mod tests { println!("bef {:?}", t); } + println!("AB {:?}", db.clone().drain()); let initial_root = root.clone(); let mut initial_db = db.clone(); // reference @@ -533,7 +548,7 @@ mod tests { println!("aft {:?}", t); } - let mut reference_delta = db; +// let mut reference_delta = db; let reference_root = root.clone(); @@ -543,9 +558,11 @@ mod tests { let mut batch_delta = initial_db; memory_db_from_delta(batch_update.0, &mut batch_delta); - +/* // sort let batch_delta: std::collections::BTreeMap<_, _> = batch_delta.drain().into_iter().collect(); + // TODO this revel an issue with triedbmut implementation (some incorrect RC and therefore + // node being kept). assert_eq!( batch_delta, reference_delta.drain().into_iter() @@ -553,9 +570,16 @@ mod tests { // triedbmut: nodes getting removed more than once // and hash comming from nowhere // from now simply skip -1 counted - .filter(|(_, (_, rc))| rc >= &0) +// .filter(|(_, (_, rc))| rc >= &0) .collect(), ); +*/ + + // test by checking both triedb only + let t1 = RefTrieDBNoExt::new(&batch_delta, &root).unwrap(); + let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); + assert_eq!(format!("{:?}", t1), format!("{:?}", t2)); + panic!("end"); @@ -577,4 +601,21 @@ mod tests { ], ); } + #[test] + fn dummy2() { + compare_with_triedbmut( + &[ + (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8; 32]), + (vec![0x01u8, 0x81u8, 0x23], vec![0x02u8; 32]), +// (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), + ], + &[ + (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8; 32])), +// (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), +// (vec![0x01u8, 0x81u8, 0x23], None), +// (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), + ], + ); + } + } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index c437abfa..98105ccf 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -636,8 +636,7 @@ where Action::Replace(node) => Some((Stored::New(node), true)), Action::Delete => None, }, - Stored::Cached(node, hash) => { - match inspector(self, node, key)? { + Stored::Cached(node, hash) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::Cached(node, hash), false)), Action::Replace(node) => { self.death_row.insert((hash, key.left_owned())); @@ -647,7 +646,6 @@ where self.death_row.insert((hash, key.left_owned())); None } - } }, }) } @@ -1536,7 +1534,6 @@ where #[cfg(feature = "std")] trace!(target: "trie", "{:?} nodes to remove from db", self.death_row.len()); for (hash, prefix) in self.death_row.drain() { - println!("dr {:?}", &hash); self.db.remove(&hash, (&prefix.0[..], prefix.1)); } @@ -1745,7 +1742,7 @@ pub(crate) mod tests { use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, NodeCodec, ReferenceNodeCodec, reference_trie_root, reference_trie_root_no_extension}; - fn populate_trie<'db>( + pub(crate) fn populate_trie<'db>( db: &'db mut dyn HashDB, root: &'db mut ::Out, v: &[(Vec, Vec)] From 4f2266734add13ad48c6d378e4ec57cc471efff3 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 4 Dec 2019 16:31:15 +0100 Subject: [PATCH 012/118] fix index for next child --- trie-db/src/traverse.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index f873f95d..423b247b 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -246,8 +246,8 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( while target_common_depth < previous_common_depth_child { // go up if let Some(StackItem{mut node, depth, depth_child, parent_index, hash}) = stack.pop() { - target_common_depth = depth_child; common_depth = depth; + common_depth_child = depth_child; previous_common_depth_child = depth_child - 1; if let Some(handle) = callback.exit( NibbleSlice::new_offset(k.as_ref(), depth_child), @@ -299,7 +299,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } } else { // try go down - next_index = dest.at(common_depth); + next_index = dest.at(common_depth_child - 1); let next_node = match current.child(next_index) { Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); @@ -595,7 +595,7 @@ mod tests { ], &[ (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), -// (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), + (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), // (vec![0x01u8, 0x81u8, 0x23], None), // (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], @@ -611,7 +611,7 @@ mod tests { ], &[ (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8; 32])), -// (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), + (vec![0x01u8, 0x81u8, 0x23], Some(vec![0xfeu8; 32])), // (vec![0x01u8, 0x81u8, 0x23], None), // (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], From d155f4b1eb222975bc584e53a2890d574d54a46a Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 4 Dec 2019 18:25:21 +0100 Subject: [PATCH 013/118] naive bench (need to check first in a test both root matches!!) --- trie-db/benches/bench.rs | 68 ++++++++++++++++++++++++++++++++++++++++ trie-db/src/traverse.rs | 9 ------ 2 files changed, 68 insertions(+), 9 deletions(-) diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index b9ee6d56..718a1955 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -32,6 +32,8 @@ criterion_group!(benches, trie_mut_build_b, trie_iteration, nibble_common_prefix, + trie_mut_same_key_single, + trie_mut_same_key_batch, ); criterion_main!(benches); @@ -463,3 +465,69 @@ fn trie_iteration(c: &mut Criterion) { }) ); } + +fn trie_mut_same_key_single(c: &mut Criterion) { + use memory_db::PrefixedKey; + use trie_db::TrieMut; + let data : Vec<(Vec, Vec)> = input_unsorted(29, 204800, 32); + + let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut root = Default::default(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 0..data.len() { + let key: &[u8]= &data[i].0; + let val: &[u8] = &data[i].1; + t.insert(key, val).unwrap(); + } + } + + + c.bench_function("trie_mut_same_key_single", move |b: &mut Bencher| + b.iter(|| { + let mut mdb = db.clone(); + let mut n_root = root.clone(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::from_existing(&mut mdb, &mut n_root).unwrap(); + for i in 0..data.len() { + let key: &[u8]= &data[i].0; + // change val to key + t.insert(key, key).unwrap(); + } + } + assert!(n_root != root); + })); +} + +fn trie_mut_same_key_batch(c: &mut Criterion) { + use memory_db::PrefixedKey; + use trie_db::TrieMut; + let data : Vec<(Vec, Vec)> = input_unsorted(29, 204800, 32); + + let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut root = Default::default(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 0..data.len() { + let key: &[u8]= &data[i].0; + let val: &[u8] = &data[i].1; + t.insert(key, val).unwrap(); + } + } + + + c.bench_function("trie_mut_same_key_batch", move |b: &mut Bencher| + b.iter(|| { + let mut mdb = db.clone(); + // sort + let data: std::collections::BTreeSet> = data.iter().map(|(a, _b)| a.clone()).collect(); + let mut batch_update = reference_trie::BatchUpdate(Default::default()); + reference_trie::trie_traverse_key_no_extension_build( + &mut mdb, &root, data.iter().map(|a| (a, Some(&a[..]))), &mut batch_update); + // rem root del + batch_update.0.pop(); + assert!(batch_update.0.last().unwrap().1 != root); + })); +} + + diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 423b247b..a17eb8ae 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -388,11 +388,9 @@ fn fetch>( hash: &TrieHash, key: Prefix, ) -> Result, TrieHash, CError> { - println!("a {:?}", key); let node_encoded = db.get(hash, key) .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(*hash)))?; - println!("b"); Ok( OwnedNode::new::(node_encoded) .map_err(|e| Box::new(TrieError::DecoderError(*hash, e)))? @@ -435,20 +433,16 @@ impl ProcessStack for BatchUpdate> fn exit(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>> { - println!("exit prefix: {:?}", prefix); match stacked { s@StackedNode::Changed(..) => Some(Some({ - println!("push exit"); let encoded = s.into_encoded(); if encoded.len() < 32 { OwnedNodeHandle::InMemory(encoded) } else { let hash = ::hash(&encoded[..]); - println!("push one"); // costy clone (could get read from here) self.0.push((prefix.left_owned(), hash.clone(), Some(encoded))); if let Some(h) = prev_hash { - println!("rem one"); self.0.push((prefix.left_owned(), h.clone(), None)); } OwnedNodeHandle::Hash(hash) @@ -456,7 +450,6 @@ impl ProcessStack for BatchUpdate> })), StackedNode::Deleted(..) => { if let Some(h) = prev_hash { - println!("remd one"); self.0.push((prefix.left_owned(), h.clone(), None)); } Some(None) @@ -466,12 +459,10 @@ impl ProcessStack for BatchUpdate> } fn exit_root(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) { - println!("exit prefix r: {:?}", prefix); match stacked { s@StackedNode::Changed(..) => { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); - println!("pushr one"); self.0.push((prefix.left_owned(), hash, Some(encoded))); if let Some(h) = prev_hash { self.0.push((prefix.left_owned(), h.clone(), None)); From 51fd864a7f0c5597f671f063da4642040d8ee434 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 6 Dec 2019 14:46:14 +0100 Subject: [PATCH 014/118] switch partial function to thickness --- trie-db/src/node.rs | 11 ++++----- trie-db/src/traverse.rs | 53 ++++++++++++++++++++++++++-------------- trie-db/src/triedbmut.rs | 14 ++++++++--- 3 files changed, 49 insertions(+), 29 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index c2982a29..375518d5 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -233,15 +233,14 @@ impl> OwnedNode { self.plan.build(self.data.borrow()) } - /// Get extension part of the node (partial) if any. - pub fn partial(&self) -> Option { + /// Get the depth of the node. + pub fn thickness(&self) -> usize { match &self.plan { NodePlan::Branch { .. } - | NodePlan::Empty => None, - NodePlan::Leaf { partial, .. } + | NodePlan::Empty => unimplemented!(), + NodePlan::Leaf { partial, .. } => partial.len(), | NodePlan::NibbledBranch { partial, .. } - | NodePlan::Extension { partial, .. } => - Some(partial.build(self.data.borrow())), + | NodePlan::Extension { partial, .. } => partial.len() + 1, } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index a17eb8ae..ff87d894 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -60,12 +60,13 @@ impl StackedNode S: Clone, // TrieHash: AsRef<[u8]>, { - /// Get extension part of the node (partial) if any. - pub fn partial(&self) -> Option { + /// Get trie length of node (eg branch is partial len plus child index, + /// a leaf is the size of its partial). + pub fn thickness(&self) -> usize { match self { - StackedNode::Unchanged(node, ..) => node.partial(), - StackedNode::Changed(node, ..) => node.partial(), - StackedNode::Deleted(..) => None, + StackedNode::Unchanged(node, ..) => node.thickness(), + StackedNode::Changed(node, ..) => node.thickness(), + StackedNode::Deleted(..) => 0, } } @@ -226,9 +227,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let mut current = StackedNode::::Unchanged(root, Default::default()); let mut common_depth = 0; - let mut common_depth_child = current.partial().map(|p| { - p.len() + 1 - }).unwrap_or(1); + let mut common_depth_child = current.thickness(); let mut k: Option = None; callback.enter(NibbleSlice::new(&[]), &mut current); @@ -273,9 +272,9 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( loop { // TODO EMCH go down extension here debug_assert!(common_depth <= dest_depth); - if common_depth_child - 1 == dest_depth { + if common_depth_child == dest_depth { if let Some((new, s)) = callback.enter_terminal( - NibbleSlice::new_offset(k.as_ref(), common_depth_child - 1), + NibbleSlice::new_offset(k.as_ref(), common_depth_child), &mut current, el_index, k.as_ref(), @@ -289,9 +288,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( hash: last_hash, }); common_depth = common_depth_child; - new.partial().map(|p| { - common_depth_child += p.len() + 1; - }); + common_depth_child += new.thickness(); current = StackedNode::Changed(new, s); } else { // go next key @@ -348,9 +345,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( &mut current, ); - let add_levels = next_node.partial().map(|p| { - p.len() + 1 - }).unwrap_or(1); + let add_levels = next_node.thickness(); stack.push(StackItem { node: current, depth: common_depth, @@ -403,7 +398,7 @@ pub struct BatchUpdate(pub Vec<((ElasticArray36, Option), H, Option ProcessStack for BatchUpdate> where T: TrieLayout, - S: Clone, + S: Clone + Default, B: Borrow<[u8]> + AsRef<[u8]>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, @@ -427,7 +422,26 @@ impl ProcessStack for BatchUpdate> } None } else { - Some(unimplemented!()) + if key_element.len() * nibble_ops::NIBBLE_PER_BYTE > prefix.left_len() { + if let Some(val) = value_element { + // dest is a leaf appended to terminal + let dest_leaf = ( + Node::new_leaf( + NibbleSlice::new_offset(key_element, prefix.left_len()), + val, + ), + Default::default(), + ); + // append to parent is done on exit through changed nature of the new leaf. + return Some(dest_leaf); + } else { + // nothing to delete. + return None; + } + panic!("TODO"); + } else { + Some(unimplemented!()) + } } } @@ -585,8 +599,9 @@ mod tests { (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), ], &[ +// (vec![0x01u8, 0x01u8, 0x23, 0x45], Some(vec![0xffu8, 0x33])), (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), - (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), +// (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), // (vec![0x01u8, 0x81u8, 0x23], None), // (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 98105ccf..9d41b28f 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -134,15 +134,16 @@ pub enum Node { impl Node { - /// Get extension part of the node (partial) if any. - pub fn partial(&self) -> Option { + /// Get the depth of the node. + pub fn thickness(&self) -> usize { match self { Node::Branch { .. } - | Node::Empty => None, + | Node::Empty => unimplemented!(), Node::NibbledBranch(partial, ..) | Node::Extension(partial, ..) + => 1 + (partial.1.len() * nibble_ops::NIBBLE_PER_BYTE) - partial.0, | Node::Leaf(partial, ..) - => Some(NibbleSlice::new_offset(&partial.1[..], partial.0)), + => (partial.1.len() * nibble_ops::NIBBLE_PER_BYTE) - partial.0, } } @@ -236,6 +237,11 @@ impl Node { false } } + + + pub fn new_leaf(prefix: NibbleSlice, value: &[u8]) -> Self { + Node::Leaf(prefix.to_stored(), value.into()) + } } impl, SH: AsRef<[u8]>> Node { From 41351f61c94cf379f453fd21b3c4919d2adcb5c2 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 6 Dec 2019 15:47:59 +0100 Subject: [PATCH 015/118] in progress (broken) --- trie-db/src/traverse.rs | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index ff87d894..26345a7c 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -287,6 +287,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( parent_index: next_index, hash: last_hash, }); + next_index = dest.at(common_depth_child); common_depth = common_depth_child; common_depth_child += new.thickness(); current = StackedNode::Changed(new, s); @@ -297,7 +298,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } else { // try go down next_index = dest.at(common_depth_child - 1); - let next_node = match current.child(next_index) { + let (next_node, new_node) = match current.child(next_index) { Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); @@ -309,11 +310,11 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( Default::default(), ); last_hash = Some(hash); - n + (n, false) }, Some(NodeHandle::Inline(node_encoded)) => { last_hash = None; - StackedNode::Unchanged( + (StackedNode::Unchanged( // Instantiating B is only for inline node, still costy. OwnedNode::new::(B::from(node_encoded)) .map_err(|e| Box::new(TrieError::DecoderError( @@ -321,18 +322,18 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( e, )))?, Default::default(), - ) + ), false) }, None => { // this is a terminal node if let Some((new, s)) = callback.enter_terminal( - NibbleSlice::new_offset(k.as_ref(), common_depth_child - 1), + NibbleSlice::new_offset(k.as_ref(), common_depth_child), &mut current, el_index, k.as_ref(), v.as_ref().map(|v| v.as_ref()), ) { - StackedNode::Changed(new, s) + (StackedNode::Changed(new, s), true) } else { // go next key break; @@ -341,7 +342,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( }; callback.enter( - NibbleSlice::new_offset(k.as_ref(), common_depth_child - 1), + NibbleSlice::new_offset(k.as_ref(), common_depth_child), &mut current, ); @@ -353,6 +354,9 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( parent_index: next_index, hash: last_hash, }); + if new_node { + next_index = dest.at(common_depth_child); + } common_depth = common_depth_child; common_depth_child += add_levels; current = next_node; @@ -562,6 +566,10 @@ mod tests { &mut initial_db, &initial_root, v.iter().map(|(a, b)| (a, b.as_ref())), &mut batch_update); let mut batch_delta = initial_db; + + batch_update.0.pop(); + let r2 = batch_update.0.last().unwrap().1; +// memory_db_from_delta(batch_update.0, &mut batch_delta); /* // sort @@ -580,10 +588,16 @@ mod tests { ); */ +// println!("{:?}", batch_delta.drain()); +// println!("{:?}", db.drain()); // test by checking both triedb only - let t1 = RefTrieDBNoExt::new(&batch_delta, &root).unwrap(); let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); - assert_eq!(format!("{:?}", t1), format!("{:?}", t2)); + println!("{:?}", t2); + let t2b = RefTrieDBNoExt::new(&batch_delta, &r2).unwrap(); + println!("{:?}", t2b); + +// let t1 = RefTrieDBNoExt::new(&batch_delta, &root).unwrap(); +// assert_eq!(format!("{:?}", t1), format!("{:?}", t2)); panic!("end"); @@ -599,8 +613,8 @@ mod tests { (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), ], &[ -// (vec![0x01u8, 0x01u8, 0x23, 0x45], Some(vec![0xffu8, 0x33])), - (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), + (vec![0x01u8, 0x01u8, 0x23, 0x45], Some(vec![0xffu8, 0x33])), +// (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), // (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), // (vec![0x01u8, 0x81u8, 0x23], None), // (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), From 9520c59d15654177f133b6ae0c38f7c8a4f69baf Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Mon, 9 Dec 2019 08:59:11 +0100 Subject: [PATCH 016/118] this needs rewrite --- trie-db/src/traverse.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 26345a7c..b4e9c360 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -252,9 +252,10 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( NibbleSlice::new_offset(k.as_ref(), depth_child), current, hash.as_ref(), ) { - node.set_handle(handle, parent_index); + node.set_handle(handle, next_index); } current = node; + next_index = parent_index; } else { callback.exit_root(NibbleSlice::new(&[]), current, root_hash); return Ok(()); @@ -346,7 +347,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( &mut current, ); - let add_levels = next_node.thickness(); + let mut add_levels = next_node.thickness(); stack.push(StackItem { node: current, depth: common_depth, @@ -356,6 +357,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( }); if new_node { next_index = dest.at(common_depth_child); + add_levels += 1; // needed for leaf into leaf (might not be needed for leaf into branch). } common_depth = common_depth_child; common_depth_child += add_levels; @@ -372,8 +374,9 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( NibbleSlice::new_offset(k.as_ref(), depth_child), current, hash.as_ref(), ) { - node.set_handle(handle, parent_index); + node.set_handle(handle, next_index); } + next_index = parent_index; current = node; }} callback.exit_root(NibbleSlice::new(&[]), current, root_hash); @@ -431,7 +434,7 @@ impl ProcessStack for BatchUpdate> // dest is a leaf appended to terminal let dest_leaf = ( Node::new_leaf( - NibbleSlice::new_offset(key_element, prefix.left_len()), + NibbleSlice::new_offset(key_element, prefix.left_len() + 1), val, ), Default::default(), From b415bf4e8c84d681e6223636923e4a2cbdc7214d Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 9 Dec 2019 12:18:44 +0100 Subject: [PATCH 017/118] refact loop, index are broken --- trie-db/src/node.rs | 11 +- trie-db/src/traverse.rs | 415 +++++++++++++++++++++------------------ trie-db/src/triedbmut.rs | 9 +- 3 files changed, 239 insertions(+), 196 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 375518d5..c2982a29 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -233,14 +233,15 @@ impl> OwnedNode { self.plan.build(self.data.borrow()) } - /// Get the depth of the node. - pub fn thickness(&self) -> usize { + /// Get extension part of the node (partial) if any. + pub fn partial(&self) -> Option { match &self.plan { NodePlan::Branch { .. } - | NodePlan::Empty => unimplemented!(), - NodePlan::Leaf { partial, .. } => partial.len(), + | NodePlan::Empty => None, + NodePlan::Leaf { partial, .. } | NodePlan::NibbledBranch { partial, .. } - | NodePlan::Extension { partial, .. } => partial.len() + 1, + | NodePlan::Extension { partial, .. } => + Some(partial.build(self.data.borrow())), } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index b4e9c360..6a472e88 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -53,6 +53,23 @@ pub enum StackedNode Deleted(S), } +pub struct StackedItem + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + + /// Interanl node representation. + pub node: StackedNode, + /// Hash used to access this node, for inline node and + /// new nodes this is None. + pub hash: Option>, + /// Index of prefix. + pub depth_prefix: usize, + /// Depth of node, it is prefix depth and partial depth. + pub depth: usize, +} + impl StackedNode where B: Borrow<[u8]> + AsRef<[u8]>, @@ -60,13 +77,12 @@ impl StackedNode S: Clone, // TrieHash: AsRef<[u8]>, { - /// Get trie length of node (eg branch is partial len plus child index, - /// a leaf is the size of its partial). - pub fn thickness(&self) -> usize { + /// Get extension part of the node (partial) if any. + pub fn partial(&self) -> Option { match self { - StackedNode::Unchanged(node, ..) => node.thickness(), - StackedNode::Changed(node, ..) => node.thickness(), - StackedNode::Deleted(..) => 0, + StackedNode::Unchanged(node, ..) => node.partial(), + StackedNode::Changed(node, ..) => node.partial(), + StackedNode::Deleted(..) => None, } } @@ -133,7 +149,6 @@ impl StackedNode StackedNode::Deleted(..) => unreachable!(), } } - } impl StackedNode @@ -165,19 +180,18 @@ pub trait ProcessStack K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, { - /// Callback on enter a node, change can be applied here. - fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode); + // /// Callback on enter a node, change can be applied here. + // fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode); /// Same as `enter` but at terminal node element (terminal considering next key so branch is /// possible here). /// TODO prefix semantic here is strange: it is prefix of child. fn enter_terminal( &mut self, - prefix: NibbleSlice, - stacked: &mut StackedNode, - index_element: usize, + stacked: &mut StackedItem, key_element: &[u8], value_element: Option<&[u8]>, - ) -> Option<(Node, Vec>, S)>; + state: TraverseState, + ) -> Option, Vec>>; /// Callback on exit a node, commit action on change node should be applied here. fn exit(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>>; @@ -185,14 +199,78 @@ pub trait ProcessStack fn exit_root(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>); } -struct StackItem, T: TrieLayout, S> { - node: StackedNode, - // depth of stack aka prefix size - depth: usize, - // depth after item - depth_child: usize, - parent_index: u8, - hash: Option>, +/// State when descending +pub enum TraverseState { + /// This is the right node for value. + ValueMatch, + /// after node + AfterNode, + /// Mid partial and index + MidPartial(usize), +} +/// Descend into a node, depending on callback it can produce a new node +/// (and change existing one). +/// It also returns a boolean indicating if we need to switch to a +/// different targetted key. +fn descend( + item: &mut StackedItem, + key: &K, + value: Option<&V>, + target_common_depth: usize, + callback: &mut F, +) -> (Option>, bool) + where + T: TrieLayout, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]>, + S: Default + Clone, + B: Borrow<[u8]>, + F: ProcessStack, +{ + if target_common_depth < item.depth_prefix { + debug_assert!(false, "Descend should not be call in this state"); + } + if target_common_depth < item.depth { + // insert into prefix + (callback.enter_terminal( + item, + key.as_ref(), + value.as_ref().map(|v| v.as_ref()), + TraverseState::MidPartial(target_common_depth), + ).map(|new_node| { + StackedItem { + node: StackedNode::Changed(new_node, Default::default()), + hash: None, + depth_prefix: item.depth_prefix, + depth: target_common_depth, + } + }), false) + } else if target_common_depth == item.depth { + // set value + let n = callback.enter_terminal( + item, + key.as_ref(), + value.as_ref().map(|v| v.as_ref()), + TraverseState::ValueMatch, + ); + debug_assert!(n.is_none()); + (None, true) + } else { + // extend + (callback.enter_terminal( + item, + key.as_ref(), + value.as_ref().map(|v| v.as_ref()), + TraverseState::AfterNode, + ).map(|new_node| { + StackedItem { + node: StackedNode::Changed(new_node, Default::default()), + hash: None, + depth_prefix: item.depth + 1, + depth: key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + } + }), true) + } } /// The main entry point for traversing a trie by a set of keys. @@ -213,173 +291,141 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( { // stack of traversed nodes // first usize is depth, second usize is the parent index. - let mut stack: Vec> = Vec::with_capacity(32); + let mut stack: Vec> = Vec::with_capacity(32); // TODO EMCH do following update (used for error only) - let mut last_hash: Option> = Some(*root_hash); let root = if let Ok(root) = fetch::(db, root_hash, EMPTY_PREFIX) { root } else { return Err(Box::new(TrieError::InvalidStateRoot(*root_hash))); }; - let root_hash = Some(root_hash); // stack.push(StackedNode::Unchanged(root, Default::default())); - let mut current = StackedNode::::Unchanged(root, Default::default()); - let mut common_depth = 0; - let mut common_depth_child = current.thickness(); + let current = StackedNode::::Unchanged(root, Default::default()); + let depth = current.partial().map(|p| p.len()).unwrap_or(0); + let mut current = StackedItem { + node: current, + hash: Some(*root_hash), + depth_prefix: 0, + depth, + }; let mut k: Option = None; - callback.enter(NibbleSlice::new(&[]), &mut current); - - let mut next_index: u8 = 0; - for (el_index, (next_k, v)) in elements.into_iter().enumerate() { - if let Some(k) = k.as_ref() { + let mut common_depth = 0; - let mut previous_common_depth_child = common_depth_child - 1; - let mut target_common_depth = - nibble_ops::biggest_depth(&k.as_ref()[..previous_common_depth_child / 2], next_k.as_ref()); + for (next_k, v) in elements.into_iter() { + if let Some(previous_key) = k { + let mut target_common_depth = nibble_ops::biggest_depth( + &previous_key.as_ref()[..current.depth / nibble_ops::NIBBLE_PER_BYTE], + next_k.as_ref(), + ); target_common_depth = min(common_depth, target_common_depth); - - if target_common_depth < previous_common_depth_child { - while target_common_depth < previous_common_depth_child { - // go up - if let Some(StackItem{mut node, depth, depth_child, parent_index, hash}) = stack.pop() { - common_depth = depth; - common_depth_child = depth_child; - previous_common_depth_child = depth_child - 1; - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(k.as_ref(), depth_child), - current, hash.as_ref(), - ) { - node.set_handle(handle, next_index); - } - current = node; - next_index = parent_index; - } else { - callback.exit_root(NibbleSlice::new(&[]), current, root_hash); - return Ok(()); + + while target_common_depth < current.depth { + // go up + if let Some(mut last) = stack.pop() { + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(previous_key.as_ref(), last.depth_prefix), + current.node, current.hash.as_ref(), + ) { + let child_index = NibbleSlice::new_offset(previous_key.as_ref(), 0) + .at(last.depth + 1); + last.node.set_handle(handle, child_index); } + current = last; + } else { + callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); + return Ok(()); } } - + k = Some(next_k); + common_depth = target_common_depth; } - k = Some(next_k); if let Some(k) = k.as_ref() { - let dest = NibbleFullKey::new(k.as_ref()); - let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - - loop { - // TODO EMCH go down extension here - debug_assert!(common_depth <= dest_depth); - if common_depth_child == dest_depth { - if let Some((new, s)) = callback.enter_terminal( - NibbleSlice::new_offset(k.as_ref(), common_depth_child), - &mut current, - el_index, - k.as_ref(), - v.as_ref().map(|v| v.as_ref()), - ) { - stack.push(StackItem { - node: current, - depth: common_depth, - depth_child: common_depth_child, - parent_index: next_index, - hash: last_hash, - }); - next_index = dest.at(common_depth_child); - common_depth = common_depth_child; - common_depth_child += new.thickness(); - current = StackedNode::Changed(new, s); + let dest = NibbleFullKey::new(k.as_ref()); + let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + + loop { + if dest_depth < current.depth_prefix { + // non terminal + let next_index = dest.at(current.depth + 1); + let depth_prefix = current.depth + 1; + let (node, hash) = match current.node.child(next_index) { + Some(NodeHandle::Hash(handle_hash)) => { + let mut hash = as Default>::default(); + hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + (StackedNode::Unchanged( + fetch::( + db, &hash, + NibbleSlice::new_offset(k.as_ref(), depth_prefix).left(), + )?, + Default::default(), + ), Some(hash)) + }, + Some(NodeHandle::Inline(node_encoded)) => { + (StackedNode::Unchanged( + // Instantiating B is only for inline node, still costy. + OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError( + current.hash.clone().unwrap_or_else(Default::default), + e, + )))?, + Default::default(), + ), None) + }, + None => { + unreachable!("Depth checked previously"); + }, + }; + let depth = depth_prefix + node.partial().map(|p| p.len()).unwrap_or(0); + stack.push(current); + current = StackedItem { + node, + hash, + depth_prefix, + depth, + }; } else { - // go next key - break; - } - } else { - // try go down - next_index = dest.at(common_depth_child - 1); - let (next_node, new_node) = match current.child(next_index) { - Some(NodeHandle::Hash(handle_hash)) => { - let mut hash = as Default>::default(); - hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - let n = StackedNode::Unchanged( - fetch::( - db, &hash, - NibbleSlice::new_offset(k.as_ref(), common_depth_child).left(), - )?, - Default::default(), - ); - last_hash = Some(hash); - (n, false) - }, - Some(NodeHandle::Inline(node_encoded)) => { - last_hash = None; - (StackedNode::Unchanged( - // Instantiating B is only for inline node, still costy. - OwnedNode::new::(B::from(node_encoded)) - .map_err(|e| Box::new(TrieError::DecoderError( - last_hash.clone().unwrap_or_else(Default::default), - e, - )))?, - Default::default(), - ), false) - }, - None => { - // this is a terminal node - if let Some((new, s)) = callback.enter_terminal( - NibbleSlice::new_offset(k.as_ref(), common_depth_child), - &mut current, - el_index, - k.as_ref(), - v.as_ref().map(|v| v.as_ref()), - ) { - (StackedNode::Changed(new, s), true) - } else { + // terminal case + match descend(&mut current, k, v.as_ref(), dest_depth, callback) { + (Some(new), next) => { + stack.push(current); + current = new; + if next { + break; + } + }, + (None, next) => { + debug_assert!(next); // go next key break; - } - }, - }; - - callback.enter( - NibbleSlice::new_offset(k.as_ref(), common_depth_child), - &mut current, - ); - - let mut add_levels = next_node.thickness(); - stack.push(StackItem { - node: current, - depth: common_depth, - depth_child: common_depth_child, - parent_index: next_index, - hash: last_hash, - }); - if new_node { - next_index = dest.at(common_depth_child); - add_levels += 1; // needed for leaf into leaf (might not be needed for leaf into branch). + }, + } } - common_depth = common_depth_child; - common_depth_child += add_levels; - current = next_node; } - }} - + } } - // empty stack - while let Some(StackItem{mut node, depth, depth_child, parent_index, hash}) = stack.pop() { - if let Some(k) = k.as_ref() { - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(k.as_ref(), depth_child), - current, hash.as_ref(), - ) { - node.set_handle(handle, next_index); + if let Some(previous_key) = k { + // go up + while let Some(mut last) = stack.pop() { + if stack.len() > 0 { + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(previous_key.as_ref(), last.depth_prefix), + current.node, current.hash.as_ref(), + ) { + let child_index = NibbleSlice::new_offset(previous_key.as_ref(), 0) + .at(last.depth + 1); + last.node.set_handle(handle, child_index); + } + current = last; + } else { + callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); + return Ok(()); + } } - next_index = parent_index; - current = node; - }} - callback.exit_root(NibbleSlice::new(&[]), current, root_hash); + } Ok(()) } @@ -410,34 +456,31 @@ impl ProcessStack for BatchUpdate> K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, { - fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode) { - } + //fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode) { + //} fn enter_terminal( &mut self, - prefix: NibbleSlice, - stacked: &mut StackedNode, - index_element: usize, + stacked: &mut StackedItem, key_element: &[u8], value_element: Option<&[u8]>, - ) -> Option<(Node, Vec>, S)> { - if key_element.len() * nibble_ops::NIBBLE_PER_BYTE == prefix.left_len() { - if let Some(value) = value_element { - stacked.set_value(value); - } else { - stacked.remove_value(); - } - None - } else { - if key_element.len() * nibble_ops::NIBBLE_PER_BYTE > prefix.left_len() { + state: TraverseState, + ) -> Option, Vec>> { + match state { + TraverseState::ValueMatch => { + if let Some(value) = value_element { + stacked.node.set_value(value); + } else { + stacked.node.remove_value(); + } + None + }, + TraverseState::AfterNode => { if let Some(val) = value_element { // dest is a leaf appended to terminal - let dest_leaf = ( - Node::new_leaf( - NibbleSlice::new_offset(key_element, prefix.left_len() + 1), - val, - ), - Default::default(), + let dest_leaf = Node::new_leaf( + NibbleSlice::new_offset(key_element, stacked.depth + 1), + val, ); // append to parent is done on exit through changed nature of the new leaf. return Some(dest_leaf); @@ -445,10 +488,10 @@ impl ProcessStack for BatchUpdate> // nothing to delete. return None; } - panic!("TODO"); - } else { - Some(unimplemented!()) - } + }, + TraverseState::MidPartial(mid_index) => { + unimplemented!(); + }, } } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 9d41b28f..6c349a63 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -134,16 +134,15 @@ pub enum Node { impl Node { - /// Get the depth of the node. - pub fn thickness(&self) -> usize { + /// Get extension part of the node (partial) if any. + pub fn partial(&self) -> Option { match self { Node::Branch { .. } - | Node::Empty => unimplemented!(), + | Node::Empty => None, Node::NibbledBranch(partial, ..) | Node::Extension(partial, ..) - => 1 + (partial.1.len() * nibble_ops::NIBBLE_PER_BYTE) - partial.0, | Node::Leaf(partial, ..) - => (partial.1.len() * nibble_ops::NIBBLE_PER_BYTE) - partial.0, + => Some(NibbleSlice::new_offset(&partial.1[..], partial.0)), } } From 86e974e808d25d85c6dcc4826db5c26f1d6d45bd Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 9 Dec 2019 13:33:31 +0100 Subject: [PATCH 018/118] Fix set value case --- trie-db/src/traverse.rs | 65 +++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 6a472e88..e1842c38 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -212,7 +212,7 @@ pub enum TraverseState { /// (and change existing one). /// It also returns a boolean indicating if we need to switch to a /// different targetted key. -fn descend( +fn descend_terminal( item: &mut StackedItem, key: &K, value: Option<&V>, @@ -227,9 +227,7 @@ fn descend( B: Borrow<[u8]>, F: ProcessStack, { - if target_common_depth < item.depth_prefix { - debug_assert!(false, "Descend should not be call in this state"); - } + debug_assert!(!(target_common_depth < item.depth_prefix), "Descend should not be call in this state"); if target_common_depth < item.depth { // insert into prefix (callback.enter_terminal( @@ -311,7 +309,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( }; let mut k: Option = None; - let mut common_depth = 0; + let mut previous_common_depth = 0; for (next_k, v) in elements.into_iter() { if let Some(previous_key) = k { @@ -319,13 +317,13 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( &previous_key.as_ref()[..current.depth / nibble_ops::NIBBLE_PER_BYTE], next_k.as_ref(), ); - target_common_depth = min(common_depth, target_common_depth); + target_common_depth = min(previous_common_depth, target_common_depth); while target_common_depth < current.depth { // go up if let Some(mut last) = stack.pop() { if let Some(handle) = callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), last.depth_prefix), + NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), current.node, current.hash.as_ref(), ) { let child_index = NibbleSlice::new_offset(previous_key.as_ref(), 0) @@ -338,20 +336,25 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( return Ok(()); } } - k = Some(next_k); - common_depth = target_common_depth; + previous_common_depth = target_common_depth; } + k = Some(next_k); if let Some(k) = k.as_ref() { let dest = NibbleFullKey::new(k.as_ref()); let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; loop { - if dest_depth < current.depth_prefix { + let child = if dest_depth > current.depth { + let next_index = dest.at(current.depth); + current.node.child(next_index) + } else { + None + }; + if dest_depth > current.depth && child.is_some() { // non terminal - let next_index = dest.at(current.depth + 1); let depth_prefix = current.depth + 1; - let (node, hash) = match current.node.child(next_index) { + let (node, hash) = match child { Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); @@ -388,7 +391,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( }; } else { // terminal case - match descend(&mut current, k, v.as_ref(), dest_depth, callback) { + match descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { (Some(new), next) => { stack.push(current); current = new; @@ -410,21 +413,19 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if let Some(previous_key) = k { // go up while let Some(mut last) = stack.pop() { - if stack.len() > 0 { - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), last.depth_prefix), - current.node, current.hash.as_ref(), - ) { - let child_index = NibbleSlice::new_offset(previous_key.as_ref(), 0) - .at(last.depth + 1); - last.node.set_handle(handle, child_index); - } - current = last; - } else { - callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); - return Ok(()); + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), + current.node, current.hash.as_ref(), + ) { + let child_index = NibbleSlice::new_offset(previous_key.as_ref(), 0) + .at(last.depth); + last.node.set_handle(handle, child_index); } + current = last; + } + callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); + return Ok(()); } Ok(()) @@ -646,7 +647,7 @@ mod tests { // assert_eq!(format!("{:?}", t1), format!("{:?}", t2)); - panic!("end"); + panic!("!!END!!"); } @@ -659,9 +660,9 @@ mod tests { (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), ], &[ - (vec![0x01u8, 0x01u8, 0x23, 0x45], Some(vec![0xffu8, 0x33])), -// (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), -// (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), +// (vec![0x01u8, 0x01u8, 0x23, 0x45], Some(vec![0xffu8, 0x33])), + (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), + (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), // (vec![0x01u8, 0x81u8, 0x23], None), // (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], @@ -673,12 +674,12 @@ mod tests { &[ (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8; 32]), (vec![0x01u8, 0x81u8, 0x23], vec![0x02u8; 32]), -// (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), + (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), ], &[ (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8; 32])), (vec![0x01u8, 0x81u8, 0x23], Some(vec![0xfeu8; 32])), -// (vec![0x01u8, 0x81u8, 0x23], None), + (vec![0x01u8, 0x81u8, 0x23], None), // (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], ); From d96a4834b7dfad1cac683451eb902efd238c0c7e Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 9 Dec 2019 21:02:27 +0100 Subject: [PATCH 019/118] set_handle need to handle case where we add a new branch in the middle of a partial (currently it does as if it is always append only). --- trie-db/benches/bench.rs | 2 - trie-db/fuzz/Cargo.toml | 4 + trie-db/fuzz/fuzz_targets/batch_update.rs | 8 + trie-db/fuzz/src/lib.rs | 57 ++++++ trie-db/src/node.rs | 51 +++++ trie-db/src/traverse.rs | 235 +++++++++++++++------- trie-db/src/triedbmut.rs | 30 ++- 7 files changed, 309 insertions(+), 78 deletions(-) create mode 100644 trie-db/fuzz/fuzz_targets/batch_update.rs diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 718a1955..f73afca5 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -529,5 +529,3 @@ fn trie_mut_same_key_batch(c: &mut Criterion) { assert!(batch_update.0.last().unwrap().1 != root); })); } - - diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index de4c2645..6594567d 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -47,3 +47,7 @@ path = "fuzz_targets/no_ext_insert.rs" [[bin]] name = "no_ext_insert_rem" path = "fuzz_targets/no_ext_insert_rem.rs" + +[[bin]] +name = "batch_update" +path = "fuzz_targets/batch_update.rs" diff --git a/trie-db/fuzz/fuzz_targets/batch_update.rs b/trie-db/fuzz/fuzz_targets/batch_update.rs new file mode 100644 index 00000000..2c71ddde --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/batch_update.rs @@ -0,0 +1,8 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + trie_db_fuzz::fuzz_batch_update(data); +}); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 7b89e12f..93ceda31 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -173,3 +173,60 @@ pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); compare_no_extension_insert_remove(data, memdb); } + +pub fn fuzz_batch_update(input: &[u8]) { + let data = fuzz_to_data(input); + let data = fuzz_removal(data); +println!("{:?}", data); + let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut root = Default::default(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 0..data.len() / 2 { + let key: &[u8]= &data[i].1; + let val: &[u8] = &data[i].2; + t.insert(key, val).unwrap(); + } + } + + let initial_root = root.clone(); + let mut initial_db = db.clone(); + + let mut sorted_data = std::collections::BTreeMap::new(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in data.len() / 2..data.len() { + let key: &[u8]= &data[i].1; + let val: &[u8] = &data[i].2; + if !data[i].0 { + sorted_data.insert(key, Some(val)); + t.insert(key, val).unwrap(); + } else { + sorted_data.insert(key, None); + // remove overwrite insert from fuzz_removal ordering, + // that is important + t.remove(key).unwrap(); + } + } + } + let mut batch_update = reference_trie::BatchUpdate( + Default::default(), + initial_root.clone(), + ); + reference_trie::trie_traverse_key_no_extension_build( + &mut initial_db, + &initial_root, sorted_data.into_iter(), &mut batch_update); + assert!(batch_update.1 == root); +} + +#[test] +fn test() { + let tests = [ + //vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], + //vec![0x0,0x0,0x4,0x8d,0x8d,0x4], + vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], + ]; + for v in tests.iter() { + fuzz_batch_update(&v[..]) + } +} diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index c2982a29..703a63ec 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -256,9 +256,59 @@ impl> OwnedNode { children[ix as usize].as_ref().map(|child| child.build(self.data.borrow())), } } + + /// Tell if it is the empty node. + pub fn is_empty(&self) -> bool { + if let NodePlan::Empty = &self.plan { + true + } else { + false + } + } + } impl> OwnedNode { + /// Set a partial TODO EMCH factor new node from existing on all those methods. + pub fn advance_partial + Default>(&mut self, nb: usize) -> Option>> { + if nb == 0 { + return None; + } + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Leaf { partial, value } => { + let mut partial = partial.build(data); + partial.advance(nb); + Some(TNode::Leaf( + partial.into(), + data[value.clone()].into(), + )) + }, + NodePlan::Extension { .. } // TODO Extension + | NodePlan::Branch { .. } // TODO branch + | NodePlan::Empty => None, + NodePlan::NibbledBranch { partial, value, children } => { + let mut partial = partial.build(data); + partial.advance(nb); + let mut child_slices = [ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]; + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); + } + + Some(TNode::NibbledBranch( + partial.into(), + Box::new(child_slices), + value.as_ref().map(|value| data[value.clone()].into()), + )) + }, + } + } + /// Set a value. pub fn set_value + Default>(&mut self, new_value: &[u8]) -> Option>> { let data = &self.data.borrow(); @@ -300,6 +350,7 @@ impl> OwnedNode { }, } } + /// Remove a value. pub fn remove_value + Default>(&mut self) -> Option>>> { let data = &self.data.borrow(); diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index e1842c38..93895f04 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -30,6 +30,7 @@ use crate::DBValue; use hash_db::{HashDB, Prefix, EMPTY_PREFIX, Hasher}; use crate::NodeCodec; use ::core_::cmp::*; +use ::core_::mem; use elastic_array::ElasticArray36; // TODO make it deletion aware : do not stack unchanged key and pass them @@ -68,6 +69,8 @@ pub struct StackedItem pub depth_prefix: usize, /// Depth of node, it is prefix depth and partial depth. pub depth: usize, + /// parent index (only relevant when parent is a branch). + pub parent_index: u8, } impl StackedNode @@ -77,6 +80,15 @@ impl StackedNode S: Clone, // TrieHash: AsRef<[u8]>, { + /// Get extension part of the node (partial) if any. + pub fn is_empty(&self) -> bool { + match self { + StackedNode::Unchanged(node, ..) => node.is_empty(), + StackedNode::Changed(node, ..) => node.is_empty(), + StackedNode::Deleted(..) => true, + } + } + /// Get extension part of the node (partial) if any. pub fn partial(&self) -> Option { match self { @@ -108,6 +120,20 @@ impl StackedNode } } + /// Change a partial if the node contains one. + pub fn advance_partial(&mut self, nb: usize) { + match self { + StackedNode::Unchanged(node, state) => { + if let Some(new) = node.advance_partial(nb) { + *self = StackedNode::Changed(new, state.clone()); + } + }, + StackedNode::Changed(node, ..) => node.advance_partial(nb), + StackedNode::Deleted(..) => (), + } + } + + /// Remove a value if the node contains one. pub fn remove_value(&mut self) { match self { @@ -176,7 +202,7 @@ pub trait ProcessStack where T: TrieLayout, S: Clone, - B: Borrow<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, { @@ -191,7 +217,7 @@ pub trait ProcessStack key_element: &[u8], value_element: Option<&[u8]>, state: TraverseState, - ) -> Option, Vec>>; + ) -> Option>; /// Callback on exit a node, commit action on change node should be applied here. fn exit(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>>; @@ -208,41 +234,38 @@ pub enum TraverseState { /// Mid partial and index MidPartial(usize), } + /// Descend into a node, depending on callback it can produce a new node /// (and change existing one). -/// It also returns a boolean indicating if we need to switch to a -/// different targetted key. fn descend_terminal( item: &mut StackedItem, key: &K, value: Option<&V>, - target_common_depth: usize, + dest_depth: usize, callback: &mut F, -) -> (Option>, bool) +) -> Option> where T: TrieLayout, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, S: Default + Clone, - B: Borrow<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]>, F: ProcessStack, { + let slice_dest = NibbleSlice::new_offset(key.as_ref(), item.depth_prefix); + // TODO optimize common_prefix function ?? + let target_common_depth = item.node.partial() + .map(|p| item.depth_prefix + p.common_prefix(&slice_dest)) + .unwrap_or(item.depth); debug_assert!(!(target_common_depth < item.depth_prefix), "Descend should not be call in this state"); if target_common_depth < item.depth { // insert into prefix - (callback.enter_terminal( + callback.enter_terminal( item, key.as_ref(), value.as_ref().map(|v| v.as_ref()), TraverseState::MidPartial(target_common_depth), - ).map(|new_node| { - StackedItem { - node: StackedNode::Changed(new_node, Default::default()), - hash: None, - depth_prefix: item.depth_prefix, - depth: target_common_depth, - } - }), false) + ) } else if target_common_depth == item.depth { // set value let n = callback.enter_terminal( @@ -252,22 +275,15 @@ fn descend_terminal( TraverseState::ValueMatch, ); debug_assert!(n.is_none()); - (None, true) + None } else { // extend - (callback.enter_terminal( + callback.enter_terminal( item, key.as_ref(), value.as_ref().map(|v| v.as_ref()), TraverseState::AfterNode, - ).map(|new_node| { - StackedItem { - node: StackedNode::Changed(new_node, Default::default()), - hash: None, - depth_prefix: item.depth + 1, - depth: key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - } - }), true) + ) } } @@ -306,10 +322,10 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( hash: Some(*root_hash), depth_prefix: 0, depth, + parent_index: 0, }; let mut k: Option = None; - let mut previous_common_depth = 0; for (next_k, v) in elements.into_iter() { if let Some(previous_key) = k { @@ -317,26 +333,25 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( &previous_key.as_ref()[..current.depth / nibble_ops::NIBBLE_PER_BYTE], next_k.as_ref(), ); - target_common_depth = min(previous_common_depth, target_common_depth); + target_common_depth = min(current.depth, target_common_depth); while target_common_depth < current.depth { // go up if let Some(mut last) = stack.pop() { - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), - current.node, current.hash.as_ref(), - ) { - let child_index = NibbleSlice::new_offset(previous_key.as_ref(), 0) - .at(last.depth + 1); - last.node.set_handle(handle, child_index); + if !last.node.is_empty() { + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), + current.node, current.hash.as_ref(), + ) { + last.node.set_handle(handle, current.parent_index); + } + current = last; } - current = last; } else { callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); return Ok(()); } } - previous_common_depth = target_common_depth; } k = Some(next_k); @@ -345,11 +360,11 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; loop { - let child = if dest_depth > current.depth { + let (child, parent_index) = if dest_depth > current.depth { let next_index = dest.at(current.depth); - current.node.child(next_index) + (current.node.child(next_index), next_index) } else { - None + (None, 0) }; if dest_depth > current.depth && child.is_some() { // non terminal @@ -388,23 +403,22 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( hash, depth_prefix, depth, + parent_index, }; } else { - // terminal case - match descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { - (Some(new), next) => { - stack.push(current); - current = new; - if next { - break; - } - }, - (None, next) => { - debug_assert!(next); - // go next key - break; - }, + // remove empties + while current.node.is_empty() && stack.len() > 0 { + if let Some(prev) = stack.pop() { + current = prev; + } } + // terminal case + if let Some(new) = descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { + stack.push(current); + current = new; + }; + // go next key + break; } } } @@ -413,16 +427,15 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if let Some(previous_key) = k { // go up while let Some(mut last) = stack.pop() { - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), - current.node, current.hash.as_ref(), - ) { - let child_index = NibbleSlice::new_offset(previous_key.as_ref(), 0) - .at(last.depth); - last.node.set_handle(handle, child_index); + if !last.node.is_empty() { + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), + current.node, current.hash.as_ref(), + ) { + last.node.set_handle(handle, current.parent_index); + } + current = last; } - current = last; - } callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); return Ok(()); @@ -447,7 +460,7 @@ fn fetch>( } /// Contains ordered node change for this iteration. -pub struct BatchUpdate(pub Vec<((ElasticArray36, Option), H, Option>)>); +pub struct BatchUpdate(pub Vec<((ElasticArray36, Option), H, Option>)>, pub H); impl ProcessStack for BatchUpdate> where @@ -466,7 +479,7 @@ impl ProcessStack for BatchUpdate> key_element: &[u8], value_element: Option<&[u8]>, state: TraverseState, - ) -> Option, Vec>> { + ) -> Option> { match state { TraverseState::ValueMatch => { if let Some(value) = value_element { @@ -478,20 +491,72 @@ impl ProcessStack for BatchUpdate> }, TraverseState::AfterNode => { if let Some(val) = value_element { + // corner case of empty trie. + let offset = if stacked.node.is_empty() { + 0 + } else { + 1 + }; // dest is a leaf appended to terminal let dest_leaf = Node::new_leaf( - NibbleSlice::new_offset(key_element, stacked.depth + 1), + NibbleSlice::new_offset(key_element, stacked.depth + offset), val, ); // append to parent is done on exit through changed nature of the new leaf. - return Some(dest_leaf); + return Some(StackedItem { + node: StackedNode::Changed(dest_leaf, Default::default()), + hash: None, + depth_prefix: stacked.depth + offset, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index: key_element.as_ref()[stacked.depth], + }); } else { // nothing to delete. return None; } }, TraverseState::MidPartial(mid_index) => { - unimplemented!(); + if let Some(val) = value_element { + // here we could create branch but this way we use valid node + let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { + let new_slice = NibbleSlice::new_offset( + &key_element[..mid_index / nibble_ops::NIBBLE_PER_BYTE], + stacked.depth_prefix, + ); + Node::new_leaf(new_slice, val) + } else { + let new_slice = NibbleSlice::new_offset( + &key_element[..], + stacked.depth_prefix, + ); + let owned = new_slice.to_stored_range(mid_index - stacked.depth_prefix); + // TODO EMCH refactor new_leaf to take NodeKey (stored) as input + Node::new_leaf(NibbleSlice::from_stored(&owned), val) + }; + let old_depth = stacked.depth; + stacked.depth = mid_index; + let mut child = mem::replace( + &mut stacked.node, + StackedNode::Changed(dest_branch, Default::default()), + ); + + let parent_index = child.partial() + .map(|p| p.at(mid_index - stacked.depth_prefix)).unwrap_or(0); + + child.advance_partial(1 + mid_index - stacked.depth_prefix); + // not setting child relation (will be set on exit) + let child = StackedItem { + node: child, + hash: None, + depth_prefix: 1 + mid_index, + depth: old_depth, + parent_index, + }; + return Some(child); + } else { + // nothing to delete. + return None; + } }, } } @@ -525,9 +590,11 @@ impl ProcessStack for BatchUpdate> fn exit_root(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) { match stacked { - s@StackedNode::Changed(..) => { + s@StackedNode::Deleted(..) + | s@StackedNode::Changed(..) => { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); + self.1 = hash.clone(); self.0.push((prefix.left_owned(), hash, Some(encoded))); if let Some(h) = prev_hash { self.0.push((prefix.left_owned(), h.clone(), None)); @@ -608,10 +675,16 @@ mod tests { let reference_root = root.clone(); - let mut batch_update = BatchUpdate(Default::default()); + let mut batch_update = BatchUpdate(Default::default(), initial_root.clone()); trie_traverse_key_no_extension_build( - &mut initial_db, &initial_root, v.iter().map(|(a, b)| (a, b.as_ref())), &mut batch_update); + &mut initial_db, + &initial_root, + v.iter().map(|(a, b)| (a, b.as_ref())), + &mut batch_update, + ); +// assert_eq!(batch_update.1, reference_root); + let mut batch_delta = initial_db; batch_update.0.pop(); @@ -656,13 +729,14 @@ mod tests { compare_with_triedbmut( &[ (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8, 0x23]), + (vec![0x01u8, 0x01u8, 0x23, 0x45, 0x67], vec![0xafu8, 0x33]), (vec![0x01u8, 0x81u8, 0x23], vec![0x01u8, 0x25]), (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), ], &[ -// (vec![0x01u8, 0x01u8, 0x23, 0x45], Some(vec![0xffu8, 0x33])), - (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), - (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), + (vec![0x01u8, 0x01u8, 0x23, 0x45], Some(vec![0xffu8, 0x33])), +// (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), +// (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), // (vec![0x01u8, 0x81u8, 0x23], None), // (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], @@ -684,5 +758,18 @@ mod tests { ], ); } + #[test] + fn dummy3() { + compare_with_triedbmut( + &[ + (vec![0x00u8], vec![0x00u8, 0]), + ], + &[ + (vec![0x04u8], Some(vec![0x01u8, 0x24])), +// (vec![0x32u8], Some(vec![0x01u8, 0x24])), + ], + ); + } + } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 6c349a63..aeabbc31 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -134,6 +134,15 @@ pub enum Node { impl Node { + /// Tell if it is the empty node. + pub fn is_empty(&self) -> bool { + if let Node::Empty = &self { + true + } else { + false + } + } + /// Get extension part of the node (partial) if any. pub fn partial(&self) -> Option { match self { @@ -146,7 +155,23 @@ impl Node { } } - /// Get extension part of the node (partial) if any. + /// Advance partial offset if there is a partial. + pub fn advance_partial(&mut self, nb: usize) { + match self { + Node::Extension(..) + | Node::Branch ( .. ) + | Node::Empty => (), + Node::NibbledBranch(partial, ..) + | Node::Leaf(partial, ..) => { + let mut new_partial: NibbleSlice = NibbleSlice::new_offset(partial.1.as_ref(), partial.0); + new_partial.advance(nb); + *partial = new_partial.into(); + }, + } + } + + + /// Set value to node if possible. pub fn set_value(&mut self, value: &[u8]) { match self { Node::Extension(..) @@ -196,7 +221,7 @@ impl Node { // we end the root process) Node::Empty => unreachable!(), Node::NibbledBranch(partial, mut encoded_children, val) => { - if handle.is_none() && encoded_children[index].is_some() { + if handle.is_none() && encoded_children[index].is_none() { Node::NibbledBranch(partial, encoded_children, val) } else { let del = handle.is_none() && encoded_children[index].is_some(); @@ -363,6 +388,7 @@ where Node::Empty => C::empty_node().to_vec(), Node::Leaf(partial, value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + println!("d{:?} {:?}", pr.right(), value); C::leaf_node(pr.right(), &value) }, Node::Extension(partial, child) => { From eab458b3611d40f509cdbe29d48c6cd891bfcf21 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 10 Dec 2019 18:49:12 +0100 Subject: [PATCH 020/118] fix dummy3 --- trie-db/fuzz/src/lib.rs | 2 + trie-db/src/node.rs | 28 ++++++++++++++ trie-db/src/traverse.rs | 84 +++++++++++++++++++++++++++++++++++----- trie-db/src/triedbmut.rs | 25 ++++++++++++ 4 files changed, 129 insertions(+), 10 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 93ceda31..e3915d03 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -213,6 +213,7 @@ println!("{:?}", data); Default::default(), initial_root.clone(), ); +// println!("{}", sorted_data.len()); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); @@ -223,6 +224,7 @@ println!("{:?}", data); fn test() { let tests = [ //vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], + //vec![0x0,0x0,0x0,0x0,0x8d,0x4], //vec![0x0,0x0,0x4,0x8d,0x8d,0x4], vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], ]; diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 703a63ec..3117efc4 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -457,4 +457,32 @@ impl> OwnedNode { } } } + + + /// Set handle to a mid branch, return changed self element + /// (new branch) and the old element (new child). + pub fn set_mid_handle + Default>( + &mut self, + handle: TNodeHandle>, + index: u8, + common: usize, + ) -> (TNode>, TNode>) { + let prev_partial = self.partial() + .expect("This function is only call on node with partial"); + let mut children = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + children[index as usize] = Some(handle); + let mid_branch = TNode::NibbledBranch( + prev_partial.to_stored_range(common), + children, + None, + ); + let child = self.advance_partial(common + 1) + .expect("This function is only call with common value"); + (mid_branch, child) + } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 93895f04..d6ca9030 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -155,6 +155,27 @@ impl StackedNode } } + /// Set a handle to a child node, changing existing to a new branch, and returning the new child. + pub fn set_mid_handle( + &mut self, + handle: OwnedNodeHandle>, + index: u8, + common_depth: usize, + ) -> Self { + match self { + StackedNode::Unchanged(node, state) => { + let (new, child) = node.set_mid_handle(handle, index, common_depth); + let result = StackedNode::Changed(child, state.clone()); + *self = StackedNode::Changed(new, state.clone()); + result + }, + StackedNode::Changed(node, state) => { + StackedNode::Changed(node.set_mid_handle(handle, index, common_depth), state.clone()) + }, + StackedNode::Deleted(..) => unreachable!(), + } + } + /// Set a handle to a child node or remove it if handle is none. pub fn set_handle(&mut self, handle: Option>>, index: u8) { match self { @@ -335,7 +356,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( ); target_common_depth = min(current.depth, target_common_depth); - while target_common_depth < current.depth { + while target_common_depth < current.depth_prefix { // go up if let Some(mut last) = stack.pop() { if !last.node.is_empty() { @@ -343,7 +364,27 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), current.node, current.hash.as_ref(), ) { - last.node.set_handle(handle, current.parent_index); + if last.depth >= current.depth_prefix { + if let Some(handle) = handle { + let common = current.depth_prefix - 1; + let parent_index = last.node.partial() + .map(|p| p.at(common)).unwrap_or(0); + let child = last.node + .set_mid_handle(handle, current.parent_index, common - last.depth_prefix); + current = StackedItem { + node: child, + hash: None, + depth: last.depth, + depth_prefix: common + 1, + parent_index + }; + last.depth = common; + stack.push(last); + continue; + } + } else { + last.node.set_handle(handle, current.parent_index); + } } current = last; } @@ -432,7 +473,27 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), current.node, current.hash.as_ref(), ) { - last.node.set_handle(handle, current.parent_index); + if last.depth >= current.depth_prefix { + if let Some(handle) = handle { + let common = current.depth_prefix - 1; + let parent_index = last.node.partial() + .map(|p| p.at(common)).unwrap_or(0); + let child = last.node + .set_mid_handle(handle, current.parent_index, common - last.depth_prefix); + current = StackedItem { + node: child, + hash: None, + depth: last.depth, + depth_prefix: common + 1, + parent_index + }; + last.depth = common; + stack.push(last); + continue; + } + } else { + last.node.set_handle(handle, current.parent_index); + } } current = last; } @@ -517,10 +578,13 @@ impl ProcessStack for BatchUpdate> }, TraverseState::MidPartial(mid_index) => { if let Some(val) = value_element { + // can differ from mid_index (then need on exit to create an additional child + // leaf for value) + let mid_dest = key_element.len() * nibble_ops::NIBBLE_PER_BYTE; // here we could create branch but this way we use valid node - let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { + let dest_branch = if mid_dest % nibble_ops::NIBBLE_PER_BYTE == 0 { let new_slice = NibbleSlice::new_offset( - &key_element[..mid_index / nibble_ops::NIBBLE_PER_BYTE], + &key_element[..mid_dest / nibble_ops::NIBBLE_PER_BYTE], stacked.depth_prefix, ); Node::new_leaf(new_slice, val) @@ -529,12 +593,12 @@ impl ProcessStack for BatchUpdate> &key_element[..], stacked.depth_prefix, ); - let owned = new_slice.to_stored_range(mid_index - stacked.depth_prefix); + let owned = new_slice.to_stored_range(mid_dest - stacked.depth_prefix); // TODO EMCH refactor new_leaf to take NodeKey (stored) as input Node::new_leaf(NibbleSlice::from_stored(&owned), val) }; let old_depth = stacked.depth; - stacked.depth = mid_index; + stacked.depth = mid_dest; let mut child = mem::replace( &mut stacked.node, StackedNode::Changed(dest_branch, Default::default()), @@ -687,8 +751,7 @@ mod tests { let mut batch_delta = initial_db; - batch_update.0.pop(); - let r2 = batch_update.0.last().unwrap().1; + let r2 = batch_update.1.clone(); // memory_db_from_delta(batch_update.0, &mut batch_delta); /* @@ -765,8 +828,9 @@ mod tests { (vec![0x00u8], vec![0x00u8, 0]), ], &[ + (vec![0x00u8], Some(vec![0x00u8, 0])), (vec![0x04u8], Some(vec![0x01u8, 0x24])), -// (vec![0x32u8], Some(vec![0x01u8, 0x24])), + (vec![0x32u8], Some(vec![0x01u8, 0x24])), ], ); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index aeabbc31..83da6488 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -262,6 +262,31 @@ impl Node { } } + /// Set handle to a mid branch, changing self element to this + /// new branch and returning the old element (new child). + pub fn set_mid_handle( + &mut self, + handle: NodeHandle, + index: u8, + common: usize, + ) -> Self { + let prev_partial = self.partial() + .expect("This function is only call on node with partial"); + let mut children = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + children[index as usize] = Some(handle); + let mid_branch = Node::NibbledBranch( + prev_partial.to_stored_range(common), + children, + None, + ); + self.advance_partial(common + 1); + mem::replace(self, mid_branch) + } pub fn new_leaf(prefix: NibbleSlice, value: &[u8]) -> Self { Node::Leaf(prefix.to_stored(), value.into()) From 5ea323d3501ae6eac8e31050a234afd226243da1 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 10 Dec 2019 20:32:09 +0100 Subject: [PATCH 021/118] add common condition where needed (avoid descending in trie when bad). Condition is duplicated and should be refactored. --- trie-db/fuzz/src/lib.rs | 4 ++- trie-db/src/traverse.rs | 60 ++++++++++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index e3915d03..ec45d07f 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -213,10 +213,12 @@ println!("{:?}", data); Default::default(), initial_root.clone(), ); -// println!("{}", sorted_data.len()); + println!("{}", sorted_data.len()); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); + println!("{:?}", batch_update.1); + println!("{:?}", root); assert!(batch_update.1 == root); } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index d6ca9030..b009f3c1 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -274,7 +274,7 @@ fn descend_terminal( F: ProcessStack, { let slice_dest = NibbleSlice::new_offset(key.as_ref(), item.depth_prefix); - // TODO optimize common_prefix function ?? + // TODO optimize common_prefix function ?? totally since needed in loop let target_common_depth = item.node.partial() .map(|p| item.depth_prefix + p.common_prefix(&slice_dest)) .unwrap_or(item.depth); @@ -399,12 +399,22 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if let Some(k) = k.as_ref() { let dest = NibbleFullKey::new(k.as_ref()); let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - + loop { - let (child, parent_index) = if dest_depth > current.depth { - let next_index = dest.at(current.depth); - (current.node.child(next_index), next_index) + let slice_dest = NibbleSlice::new_offset(k.as_ref(), current.depth_prefix); + + let target_common_depth = current.node.partial() + .map(|p| current.depth_prefix + p.common_prefix(&slice_dest)) + .unwrap_or(current.depth_prefix); + let (child, parent_index) = if target_common_depth == current.depth { + if dest_depth > current.depth { + let next_index = dest.at(current.depth); + (current.node.child(next_index), next_index) + } else { + (None, 0) + } } else { + // TODO here we could use this common depth to avoid double calc in descend!! (None, 0) }; if dest_depth > current.depth && child.is_some() { @@ -453,11 +463,28 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( current = prev; } } - // terminal case - if let Some(new) = descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { - stack.push(current); - current = new; - }; + if current.node.is_empty() { + // corner case of empty trie + if let Some(v) = v.as_ref() { + let leaf = Node::new_leaf( + NibbleSlice::new_offset(k.as_ref(), current.depth_prefix), + v.as_ref(), + ); + current = StackedItem { + node: StackedNode::Changed(leaf, Default::default()), + hash: current.hash, + depth_prefix: current.depth_prefix, + depth: k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - current.depth_prefix, + parent_index: current.parent_index, + } + } + } else { + // terminal case + if let Some(new) = descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { + stack.push(current); + current = new; + }; + } // go next key break; } @@ -747,7 +774,7 @@ mod tests { &mut batch_update, ); -// assert_eq!(batch_update.1, reference_root); + assert_eq!(batch_update.1, reference_root); let mut batch_delta = initial_db; @@ -825,12 +852,15 @@ mod tests { fn dummy3() { compare_with_triedbmut( &[ - (vec![0x00u8], vec![0x00u8, 0]), + //(vec![0x00u8], vec![0x00u8, 0]), + (vec![0x00u8], vec![4u8, 32]), ], &[ - (vec![0x00u8], Some(vec![0x00u8, 0])), - (vec![0x04u8], Some(vec![0x01u8, 0x24])), - (vec![0x32u8], Some(vec![0x01u8, 0x24])), +// (vec![0x00u8], Some(vec![0x00u8, 0])), + (vec![0x04u8], Some(vec![32u8, 26])), + (vec![0x20u8], Some(vec![26u8, 0])), + //(vec![0x04u8], Some(vec![0x01u8, 0x24])), + //(vec![0x32u8], Some(vec![0x01u8, 0x24])), ], ); } From bf5280bff08b08a278ce28195a925bdbf864fa82 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 10 Dec 2019 22:19:14 +0100 Subject: [PATCH 022/118] checkpoint --- trie-db/fuzz/src/lib.rs | 11 +++++++---- trie-db/src/traverse.rs | 27 +++++++++++++++++---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index ec45d07f..f48b453e 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -194,7 +194,8 @@ println!("{:?}", data); let mut sorted_data = std::collections::BTreeMap::new(); { - let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + let mut t = reference_trie::RefTrieDBMutNoExt::from_existing(&mut db, &mut root) + .unwrap(); for i in data.len() / 2..data.len() { let key: &[u8]= &data[i].1; let val: &[u8] = &data[i].2; @@ -225,9 +226,11 @@ println!("{:?}", data); #[test] fn test() { let tests = [ - //vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], - //vec![0x0,0x0,0x0,0x0,0x8d,0x4], - //vec![0x0,0x0,0x4,0x8d,0x8d,0x4], + vec![0x0,0x0,0x0,0x0,0xfe,0xff,0xff,0xd1,0x27], + vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], + vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], + vec![0x0,0x0,0x0,0x0,0x8d,0x4], + vec![0x0,0x0,0x4,0x8d,0x8d,0x4], vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], ]; for v in tests.iter() { diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index b009f3c1..8d4ff13f 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -273,6 +273,7 @@ fn descend_terminal( B: Borrow<[u8]> + AsRef<[u8]>, F: ProcessStack, { + let key_dest = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; let slice_dest = NibbleSlice::new_offset(key.as_ref(), item.depth_prefix); // TODO optimize common_prefix function ?? totally since needed in loop let target_common_depth = item.node.partial() @@ -287,7 +288,7 @@ fn descend_terminal( value.as_ref().map(|v| v.as_ref()), TraverseState::MidPartial(target_common_depth), ) - } else if target_common_depth == item.depth { + } else if key_dest == item.depth { // set value let n = callback.enter_terminal( item, @@ -367,10 +368,11 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if last.depth >= current.depth_prefix { if let Some(handle) = handle { let common = current.depth_prefix - 1; + let nibble_common = common - last.depth_prefix; let parent_index = last.node.partial() - .map(|p| p.at(common)).unwrap_or(0); + .map(|p| p.at(nibble_common)).unwrap_or(0); let child = last.node - .set_mid_handle(handle, current.parent_index, common - last.depth_prefix); + .set_mid_handle(handle, current.parent_index, nibble_common); current = StackedItem { node: child, hash: None, @@ -503,10 +505,11 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if last.depth >= current.depth_prefix { if let Some(handle) = handle { let common = current.depth_prefix - 1; + let nibble_common = common - last.depth_prefix; let parent_index = last.node.partial() - .map(|p| p.at(common)).unwrap_or(0); + .map(|p| p.at(nibble_common)).unwrap_or(0); let child = last.node - .set_mid_handle(handle, current.parent_index, common - last.depth_prefix); + .set_mid_handle(handle, current.parent_index, nibble_common); current = StackedItem { node: child, hash: None, @@ -590,13 +593,14 @@ impl ProcessStack for BatchUpdate> NibbleSlice::new_offset(key_element, stacked.depth + offset), val, ); + let parent_index = NibbleSlice::new(key_element).at(stacked.depth); // append to parent is done on exit through changed nature of the new leaf. return Some(StackedItem { node: StackedNode::Changed(dest_leaf, Default::default()), hash: None, depth_prefix: stacked.depth + offset, depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index: key_element.as_ref()[stacked.depth], + parent_index, }); } else { // nothing to delete. @@ -774,7 +778,7 @@ mod tests { &mut batch_update, ); - assert_eq!(batch_update.1, reference_root); + //assert_eq!(batch_update.1, reference_root); let mut batch_delta = initial_db; @@ -848,17 +852,20 @@ mod tests { ], ); } + #[test] fn dummy3() { compare_with_triedbmut( &[ //(vec![0x00u8], vec![0x00u8, 0]), - (vec![0x00u8], vec![4u8, 32]), + (vec![0x00u8], vec![4u8, 248]), ], &[ + (vec![0xfeu8], Some(vec![248u8, 0])), + (vec![0xffu8], Some(vec![0u8, 0])), // (vec![0x00u8], Some(vec![0x00u8, 0])), - (vec![0x04u8], Some(vec![32u8, 26])), - (vec![0x20u8], Some(vec![26u8, 0])), +// (vec![0x04u8], Some(vec![32u8, 26])), +// (vec![0x20u8], Some(vec![26u8, 0])), //(vec![0x04u8], Some(vec![0x01u8, 0x24])), //(vec![0x32u8], Some(vec![0x01u8, 0x24])), ], From a1d049c016b60a1c3571e1690d32782662f7224b Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 10 Dec 2019 22:57:07 +0100 Subject: [PATCH 023/118] dirty limit common (should be refact by an explicit set mid variable). --- trie-db/fuzz/src/lib.rs | 2 +- trie-db/src/traverse.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index f48b453e..5438e5d1 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -226,12 +226,12 @@ println!("{:?}", data); #[test] fn test() { let tests = [ + vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], vec![0x0,0x0,0x0,0x0,0xfe,0xff,0xff,0xd1,0x27], vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], vec![0x0,0x0,0x0,0x0,0x8d,0x4], vec![0x0,0x0,0x4,0x8d,0x8d,0x4], - vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], ]; for v in tests.iter() { fuzz_batch_update(&v[..]) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 8d4ff13f..bac80f99 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -349,6 +349,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let mut k: Option = None; + let mut limit_common = usize::max_value(); for (next_k, v) in elements.into_iter() { if let Some(previous_key) = k { let mut target_common_depth = nibble_ops::biggest_depth( @@ -356,6 +357,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( next_k.as_ref(), ); target_common_depth = min(current.depth, target_common_depth); + target_common_depth = min(limit_common, target_common_depth); while target_common_depth < current.depth_prefix { // go up @@ -405,6 +407,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( loop { let slice_dest = NibbleSlice::new_offset(k.as_ref(), current.depth_prefix); + limit_common = usize::max_value(); let target_common_depth = current.node.partial() .map(|p| current.depth_prefix + p.common_prefix(&slice_dest)) .unwrap_or(current.depth_prefix); @@ -485,6 +488,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if let Some(new) = descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { stack.push(current); current = new; + limit_common = target_common_depth; }; } // go next key From 3242b4554cb8f585629909c5b8066dfc507e2fe2 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 10 Dec 2019 23:40:18 +0100 Subject: [PATCH 024/118] need fusion on delete with branch one child no value. --- trie-db/fuzz/src/lib.rs | 8 +++++--- trie-db/src/node.rs | 3 +++ trie-db/src/traverse.rs | 16 +++++++--------- trie-db/src/triedbmut.rs | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 5438e5d1..b14011e8 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -214,18 +214,20 @@ println!("{:?}", data); Default::default(), initial_root.clone(), ); - println!("{}", sorted_data.len()); +// println!("{}", sorted_data.len()); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); - println!("{:?}", batch_update.1); - println!("{:?}", root); +// println!("{:?}", batch_update.1); +// println!("{:?}", root); assert!(batch_update.1 == root); } #[test] fn test() { let tests = [ + vec![0x0,0x0,0xfe,0x0,0xff,0xff,0xd1,0xd1,0x27,0x0], + vec![0x0,0xfe,0x41,0x0,0x0,0x80,0x0,0x0,0xff], vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], vec![0x0,0x0,0x0,0x0,0xfe,0xff,0xff,0xd1,0x27], vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 3117efc4..5ebdf4a7 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -426,6 +426,9 @@ impl> OwnedNode { if handle.is_none() { children[index] = None; } + // TODO also if del && value.is_none && children .count some + // is one -> then fuse a leaf with value of children and partial + // fusing branch and child. -> need a special return if del && !children.iter().any(Option::is_some) { if let Some(value) = value { Some(Some(TNode::Leaf( diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index bac80f99..68aa1f7b 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -353,10 +353,9 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( for (next_k, v) in elements.into_iter() { if let Some(previous_key) = k { let mut target_common_depth = nibble_ops::biggest_depth( - &previous_key.as_ref()[..current.depth / nibble_ops::NIBBLE_PER_BYTE], + previous_key.as_ref(), next_k.as_ref(), ); - target_common_depth = min(current.depth, target_common_depth); target_common_depth = min(limit_common, target_common_depth); while target_common_depth < current.depth_prefix { @@ -862,16 +861,15 @@ mod tests { compare_with_triedbmut( &[ //(vec![0x00u8], vec![0x00u8, 0]), - (vec![0x00u8], vec![4u8, 248]), + (vec![254u8], vec![4u8, 248]), +// (vec![0u8], vec![4u8, 248]), + (vec![255u8], vec![4u8, 248]), ], &[ - (vec![0xfeu8], Some(vec![248u8, 0])), - (vec![0xffu8], Some(vec![0u8, 0])), +// (vec![255u8], Some(vec![248u8, 0])), +// (vec![209u8], Some(vec![0u8, 0])), + (vec![254u8], None), // (vec![0x00u8], Some(vec![0x00u8, 0])), -// (vec![0x04u8], Some(vec![32u8, 26])), -// (vec![0x20u8], Some(vec![26u8, 0])), - //(vec![0x04u8], Some(vec![0x01u8, 0x24])), - //(vec![0x32u8], Some(vec![0x01u8, 0x24])), ], ); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 83da6488..6080c9a9 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -413,7 +413,7 @@ where Node::Empty => C::empty_node().to_vec(), Node::Leaf(partial, value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); - println!("d{:?} {:?}", pr.right(), value); +// println!("d{:?} {:?}", pr.right(), value); C::leaf_node(pr.right(), &value) }, Node::Extension(partial, child) => { From 15a030dbd0451f551e43d0529beb268afed38e3a Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 11 Dec 2019 11:45:12 +0100 Subject: [PATCH 025/118] checkpoint, before moving branch transform out of set_handle function (in a call before exit) --- trie-db/src/node.rs | 74 ++++++++++++++++++++++++++++++---------- trie-db/src/traverse.rs | 52 +++++++++++++++++++++++++--- trie-db/src/triedbmut.rs | 44 +++++++++++++++++------- 3 files changed, 135 insertions(+), 35 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 5ebdf4a7..8e32343b 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -383,9 +383,11 @@ impl> OwnedNode { } /// Set a handle to a child node or remove it if handle is none. - /// Return possibly updated or removed node. + /// Return possibly updated or removed node, and index of node to + /// fuse with in case after removal of handle a branch with a single + /// child and no value remain. pub fn set_handle + Default>(&mut self, handle: Option>>, index: u8) - -> Option>>> { + -> (Option>>>, Option) { let index = index as usize; let data = &self.data.borrow(); @@ -403,18 +405,18 @@ impl> OwnedNode { ]); child_slices[index] = handle; - Some(Some(TNode::NibbledBranch( + (Some(Some(TNode::NibbledBranch( partial.build(data).into(), child_slices, Some(data[value.clone()].into())), - )) + )), None) } else { - None + (None, None) } }, NodePlan::NibbledBranch { partial, value, children } => { if handle.is_none() && children[index].is_none() { - None + (None, None) } else { let del = handle.is_none() && children[index].is_some(); let value = if let Some(value) = value.clone() { @@ -426,20 +428,57 @@ impl> OwnedNode { if handle.is_none() { children[index] = None; } - // TODO also if del && value.is_none && children .count some - // is one -> then fuse a leaf with value of children and partial - // fusing branch and child. -> need a special return - if del && !children.iter().any(Option::is_some) { - if let Some(value) = value { - Some(Some(TNode::Leaf( + + if del { + let mut count = 0; + for c in children.iter() { + if c.is_some() { + count += 1; + } + if count > 1 { + break; + } + } + debug_assert!(!(count == 0 && value.is_none())); + if value.is_some() && count == 0 { + // transform to leaf + (Some(Some(TNode::Leaf( + partial.build(data).into(), + value.expect("Tested above"), + ))), None) + } else if value.is_none() && count == 1 { + let child_ix = children.iter().position(Option::is_some) + .expect("counted above"); + let mut child_slices = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + child_slices[child_ix] = children[child_ix].as_ref() + .map(|child| child.build_thandle(data)); + (Some(Some(TNode::NibbledBranch( partial.build(data).into(), + child_slices, value, - ))) + ))), Some(child_ix as u8)) } else { - Some(None) + let mut child_slices = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); + } + (Some(Some(TNode::NibbledBranch( + partial.build(data).into(), + child_slices, + value, + ))), None) } } else { - let mut child_slices = Box::new([ None, None, None, None, None, None, None, None, @@ -450,18 +489,17 @@ impl> OwnedNode { child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); } child_slices[index] = handle; - Some(Some(TNode::NibbledBranch( + (Some(Some(TNode::NibbledBranch( partial.build(data).into(), child_slices, value, - ))) + ))), None) } } } } } - /// Set handle to a mid branch, return changed self element /// (new branch) and the old element (new child). pub fn set_mid_handle + Default>( diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 68aa1f7b..f5c3f52a 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -177,25 +177,43 @@ impl StackedNode } /// Set a handle to a child node or remove it if handle is none. - pub fn set_handle(&mut self, handle: Option>>, index: u8) { + /// Returns index of node to fuse with in case after removal of handle + /// a branch with a single child and no value remain. + pub fn set_handle(&mut self, handle: Option>>, index: u8) -> Option { match self { StackedNode::Unchanged(node, state) => { - match node.set_handle(handle, index) { + let (change, fuse) = node.set_handle(handle, index); + match change { Some(Some(new)) => *self = StackedNode::Changed(new, state.clone()), Some(None) => *self = StackedNode::Deleted(state.clone()), None => (), } + fuse }, StackedNode::Changed(node, state) => { - if node.set_handle(handle, index) { + let (deleted, fuse) = node.set_handle(handle, index); + if deleted { *self = StackedNode::Deleted(state.clone()); } + fuse }, StackedNode::Deleted(..) => unreachable!(), } } + + /// Fuse changed node that need to be reduce to a child node + pub fn fuse_child(&mut self, child: OwnedNode, child_ix: u8) { + match self { + StackedNode::Unchanged(node, state) => unreachable!(), + StackedNode::Changed(node, state) => { + unimplemented!() + }, + StackedNode::Deleted(..) => unreachable!(), + } + } + } impl StackedNode @@ -525,7 +543,33 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( continue; } } else { - last.node.set_handle(handle, current.parent_index); + if let Some(fuse_index) = last.node.set_handle(handle, current.parent_index) { + let (child, hash) = match last.node.child(fuse_index) { + Some(NodeHandle::Hash(handle_hash)) => { + let mut hash = as Default>::default(); + hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + (fetch::( + db, &hash, + NibbleSlice::new_offset(previous_key.as_ref(), last.depth).left(), + )?, Some(hash)) + }, + Some(NodeHandle::Inline(node_encoded)) => { + (OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError( + last.hash.clone().unwrap_or_else(Default::default), + e, + )))?, None) + }, + None => unreachable!("correct index used"), + }; + // register delete + callback.exit( + NibbleSlice::new_offset(previous_key.as_ref(), last.depth), + StackedNode::Deleted(Default::default()), + hash.as_ref(), + ).expect("No new node on deleted allowed"); + last.node.fuse_child(child, fuse_index); + } } } current = last; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 6080c9a9..6aeb2baa 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -202,17 +202,19 @@ impl Node { } } - /// Return true if the node can be removed. + /// Return true if the node can be removed, + /// and index of node to fuse with in case after removal of handle + /// a branch with a single child and no value remain. /// This is only for no extension trie (a variant would be /// needed for trie with extension). pub fn set_handle( &mut self, handle: Option>, index: u8, - ) -> bool { + ) -> (bool, Option) { let index = index as usize; let node = mem::replace(self, Node::Empty); - let node = match node { + let (node, fuse) = match node { Node::Extension(..) | Node::Branch(..) => unreachable!("Only for no extension trie"), // need to replace value before creating handle @@ -222,19 +224,35 @@ impl Node { Node::Empty => unreachable!(), Node::NibbledBranch(partial, mut encoded_children, val) => { if handle.is_none() && encoded_children[index].is_none() { - Node::NibbledBranch(partial, encoded_children, val) + // unchanged + (Node::NibbledBranch(partial, encoded_children, val), None) } else { let del = handle.is_none() && encoded_children[index].is_some(); encoded_children[index] = handle; - if del && !encoded_children.iter().any(Option::is_some) { - if let Some(val) = val { + if del { + let mut count = 0; + for c in encoded_children.iter() { + if c.is_some() { + count += 1; + } + if count > 1 { + break; + } + } + + debug_assert!(!(count == 0 && val.is_none())); + if val.is_some() && count == 0 { // transform to leaf - Node::Leaf(partial, val) + (Node::Leaf(partial, val.expect("Test in above condition")), None) + } else if val.is_none() && count == 1 { + let child_ix = encoded_children.iter().position(Option::is_some) + .expect("counted above"); + (Node::NibbledBranch(partial, encoded_children, val), Some(child_ix as u8)) } else { - Node::Empty + (Node::NibbledBranch(partial, encoded_children, val), None) } } else { - Node::NibbledBranch(partial, encoded_children, val) + (Node::NibbledBranch(partial, encoded_children, val), None) } } }, @@ -248,18 +266,18 @@ impl Node { ]); children[index] = handle; - Node::NibbledBranch(partial, children, Some(val)) + (Node::NibbledBranch(partial, children, Some(val)), None) } else { - Node::Leaf(partial, val) + (Node::Leaf(partial, val), None) } }, }; *self = node; - if let Node::Empty = self { + (if let Node::Empty = self { true } else { false - } + }, fuse) } /// Set handle to a mid branch, changing self element to this From 4cbb4032d3b9e4190a97b1c15f9595d759a1dc30 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 11 Dec 2019 15:15:19 +0100 Subject: [PATCH 026/118] transform branch --- trie-db/src/nibble/nibbleslice.rs | 5 ++ trie-db/src/nibble/nibblevec.rs | 9 +++ trie-db/src/node.rs | 116 +++++++++------------------ trie-db/src/traverse.rs | 128 +++++++++++++++++++----------- trie-db/src/triedbmut.rs | 127 +++++++++++++++++++---------- 5 files changed, 217 insertions(+), 168 deletions(-) diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 9ab46f98..2cc02b2e 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -215,6 +215,11 @@ impl<'a> NibbleSlice<'a> { }) } + /// Return nibble as a tuple representation. + pub fn right_ref(self) -> (usize, &'a [u8]) { + (self.offset, self.data) + } + /// Return left portion of `NibbleSlice`, if the slice /// originates from a full key it will be the `Prefix of /// the node`. diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index fc77ee22..ad704f81 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -35,6 +35,15 @@ impl NibbleVec { } } + /// Make a new `NibbleVec`. + pub fn from(d: &[u8], l: usize) -> Self { + let mut v = Self::default(); + (0..d.len()).for_each(|i| v.inner.push(d[i])); + v.len = l; + v + } + + /// Length of the `NibbleVec`. #[inline(always)] pub fn len(&self) -> usize { self.len } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 8e32343b..63f7885a 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -245,6 +245,21 @@ impl> OwnedNode { } } + /// Get value part of the node (partial) if any. + pub fn value(&self) -> Option { + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Branch { .. } + | NodePlan::Extension { .. } + | NodePlan::Empty => None, + NodePlan::Leaf { value, .. } + => Some(data[value.clone()].into()), + | NodePlan::NibbledBranch { value, .. } + => value.as_ref().map(|v| data[v.clone()].into()), + } + } + + /// Try to access child. pub fn child(&self, ix: u8) -> Option { match &self.plan { @@ -383,11 +398,9 @@ impl> OwnedNode { } /// Set a handle to a child node or remove it if handle is none. - /// Return possibly updated or removed node, and index of node to - /// fuse with in case after removal of handle a branch with a single - /// child and no value remain. + /// Return possibly updated node. pub fn set_handle + Default>(&mut self, handle: Option>>, index: u8) - -> (Option>>>, Option) { + -> Option>> { let index = index as usize; let data = &self.data.borrow(); @@ -405,98 +418,43 @@ impl> OwnedNode { ]); child_slices[index] = handle; - (Some(Some(TNode::NibbledBranch( + Some(TNode::NibbledBranch( partial.build(data).into(), child_slices, Some(data[value.clone()].into())), - )), None) + ) } else { - (None, None) + None } }, NodePlan::NibbledBranch { partial, value, children } => { if handle.is_none() && children[index].is_none() { - (None, None) + None } else { - let del = handle.is_none() && children[index].is_some(); let value = if let Some(value) = value.clone() { Some(data[value.clone()].into()) } else { None }; - - if handle.is_none() { - children[index] = None; + let mut child_slices = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); } - if del { - let mut count = 0; - for c in children.iter() { - if c.is_some() { - count += 1; - } - if count > 1 { - break; - } - } - debug_assert!(!(count == 0 && value.is_none())); - if value.is_some() && count == 0 { - // transform to leaf - (Some(Some(TNode::Leaf( - partial.build(data).into(), - value.expect("Tested above"), - ))), None) - } else if value.is_none() && count == 1 { - let child_ix = children.iter().position(Option::is_some) - .expect("counted above"); - let mut child_slices = Box::new([ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]); - child_slices[child_ix] = children[child_ix].as_ref() - .map(|child| child.build_thandle(data)); - (Some(Some(TNode::NibbledBranch( - partial.build(data).into(), - child_slices, - value, - ))), Some(child_ix as u8)) - } else { - let mut child_slices = Box::new([ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]); - for i in 0..nibble_ops::NIBBLE_LENGTH { - child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); - } - (Some(Some(TNode::NibbledBranch( - partial.build(data).into(), - child_slices, - value, - ))), None) - } - } else { - let mut child_slices = Box::new([ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]); - for i in 0..nibble_ops::NIBBLE_LENGTH { - child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); - } - child_slices[index] = handle; - (Some(Some(TNode::NibbledBranch( - partial.build(data).into(), - child_slices, - value, - ))), None) - } + child_slices[index] = handle; + + Some(TNode::NibbledBranch( + partial.build(data).into(), + child_slices, + value, + )) } - } + }, } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index f5c3f52a..7b3c36bf 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -177,38 +177,46 @@ impl StackedNode } /// Set a handle to a child node or remove it if handle is none. - /// Returns index of node to fuse with in case after removal of handle - /// a branch with a single child and no value remain. - pub fn set_handle(&mut self, handle: Option>>, index: u8) -> Option { + pub fn set_handle(&mut self, handle: Option>>, index: u8) { match self { StackedNode::Unchanged(node, state) => { - let (change, fuse) = node.set_handle(handle, index); + let change = node.set_handle(handle, index); match change { - Some(Some(new)) => + Some(new) => *self = StackedNode::Changed(new, state.clone()), - Some(None) => - *self = StackedNode::Deleted(state.clone()), None => (), } - fuse }, StackedNode::Changed(node, state) => { - let (deleted, fuse) = node.set_handle(handle, index); + node.set_handle(handle, index); + }, + StackedNode::Deleted(..) => unreachable!(), + } + } + + /// Returns index of node to fuse with in case after removal of handle + /// a branch with a single child and no value remain. + pub fn fix_node(&mut self) -> Option { + match self { + StackedNode::Deleted(..) + | StackedNode::Unchanged(..) => None, + StackedNode::Changed(node, state) => { + let (deleted, fuse) = node.fix_node(); if deleted { *self = StackedNode::Deleted(state.clone()); } fuse }, - StackedNode::Deleted(..) => unreachable!(), } } - /// Fuse changed node that need to be reduce to a child node - pub fn fuse_child(&mut self, child: OwnedNode, child_ix: u8) { + /// Fuse changed node that need to be reduce to a child node, + /// returns additional length to prefix. + pub fn fuse_child(&mut self, child: OwnedNode, child_ix: u8) -> usize { match self { StackedNode::Unchanged(node, state) => unreachable!(), StackedNode::Changed(node, state) => { - unimplemented!() + node.fuse_child(child, child_ix) }, StackedNode::Deleted(..) => unreachable!(), } @@ -380,6 +388,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( // go up if let Some(mut last) = stack.pop() { if !last.node.is_empty() { + align_node(db, callback, &mut current, previous_key.as_ref())?; if let Some(handle) = callback.exit( NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), current.node, current.hash.as_ref(), @@ -410,6 +419,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( current = last; } } else { + align_node(db, callback, &mut current, previous_key.as_ref())?; callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); return Ok(()); } @@ -519,6 +529,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( // go up while let Some(mut last) = stack.pop() { if !last.node.is_empty() { + align_node(db, callback, &mut current, previous_key.as_ref())?; if let Some(handle) = callback.exit( NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), current.node, current.hash.as_ref(), @@ -543,38 +554,13 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( continue; } } else { - if let Some(fuse_index) = last.node.set_handle(handle, current.parent_index) { - let (child, hash) = match last.node.child(fuse_index) { - Some(NodeHandle::Hash(handle_hash)) => { - let mut hash = as Default>::default(); - hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - (fetch::( - db, &hash, - NibbleSlice::new_offset(previous_key.as_ref(), last.depth).left(), - )?, Some(hash)) - }, - Some(NodeHandle::Inline(node_encoded)) => { - (OwnedNode::new::(B::from(node_encoded)) - .map_err(|e| Box::new(TrieError::DecoderError( - last.hash.clone().unwrap_or_else(Default::default), - e, - )))?, None) - }, - None => unreachable!("correct index used"), - }; - // register delete - callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), last.depth), - StackedNode::Deleted(Default::default()), - hash.as_ref(), - ).expect("No new node on deleted allowed"); - last.node.fuse_child(child, fuse_index); - } + last.node.set_handle(handle, current.parent_index); } } current = last; } } + align_node(db, callback, &mut current, previous_key.as_ref())?; callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); return Ok(()); } @@ -582,6 +568,56 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( Ok(()) } +fn align_node<'a, T, K, V, S, B, F>( + db: &'a mut dyn HashDB, + callback: &mut F, + branch: &mut StackedItem, + key: &[u8], +) -> Result<(), TrieHash, CError> + where + T: TrieLayout, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]>, + S: Default + Clone, + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, + F: ProcessStack, +{ + if let Some(fuse_index) = branch.node.fix_node() { + let (child, hash) = match branch.node.child(fuse_index) { + Some(NodeHandle::Hash(handle_hash)) => { + let mut hash = as Default>::default(); + hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + // TODO conversion to NibbleVec is slow + let mut prefix: NibbleVec = NibbleVec::from(key, branch.depth); + prefix.push(fuse_index); + (fetch::( + db, + &hash, + prefix.as_prefix(), + )?, Some(hash)) + }, + Some(NodeHandle::Inline(node_encoded)) => { + (OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError( + branch.hash.clone().unwrap_or_else(Default::default), + e, + )))?, None) + }, + None => unreachable!("correct index used"), + }; + // register delete + callback.exit( + NibbleSlice::new_offset(key, branch.depth), + StackedNode::Deleted(Default::default()), + hash.as_ref(), + ).expect("No new node on deleted allowed"); + branch.depth += branch.node.fuse_child(child, fuse_index); + } + + Ok(()) +} + + /// Fetch a node by hash, do not cache it. fn fetch>( db: &mut dyn HashDB, @@ -904,16 +940,12 @@ mod tests { fn dummy3() { compare_with_triedbmut( &[ - //(vec![0x00u8], vec![0x00u8, 0]), - (vec![254u8], vec![4u8, 248]), -// (vec![0u8], vec![4u8, 248]), - (vec![255u8], vec![4u8, 248]), + (vec![2, 254u8], vec![4u8; 33]), + (vec![1, 254u8], vec![4u8; 33]), + (vec![1, 255u8], vec![5u8; 36]), ], &[ -// (vec![255u8], Some(vec![248u8, 0])), -// (vec![209u8], Some(vec![0u8, 0])), - (vec![254u8], None), -// (vec![0x00u8], Some(vec![0x00u8, 0])), + (vec![1, 254u8], None), ], ); } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 6aeb2baa..43999110 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -20,9 +20,11 @@ use super::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, decode_h use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; +use crate::node::OwnedNode; use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; use nibble::{NibbleVec, NibbleSlice, nibble_ops}; use elastic_array::ElasticArray36; +use ::core_::borrow::Borrow; use ::core_::mem; use ::core_::ops::Index; use ::core_::hash::Hash; @@ -202,19 +204,16 @@ impl Node { } } - /// Return true if the node can be removed, - /// and index of node to fuse with in case after removal of handle - /// a branch with a single child and no value remain. /// This is only for no extension trie (a variant would be /// needed for trie with extension). pub fn set_handle( &mut self, handle: Option>, index: u8, - ) -> (bool, Option) { + ) { let index = index as usize; let node = mem::replace(self, Node::Empty); - let (node, fuse) = match node { + *self = match node { Node::Extension(..) | Node::Branch(..) => unreachable!("Only for no extension trie"), // need to replace value before creating handle @@ -222,42 +221,15 @@ impl Node { // (may be unreachable since changing to empty means // we end the root process) Node::Empty => unreachable!(), + Node::NibbledBranch(partial, encoded_children, val) + if handle.is_none() && encoded_children[index].is_none() => { + Node::NibbledBranch(partial, encoded_children, val) + }, Node::NibbledBranch(partial, mut encoded_children, val) => { - if handle.is_none() && encoded_children[index].is_none() { - // unchanged - (Node::NibbledBranch(partial, encoded_children, val), None) - } else { - let del = handle.is_none() && encoded_children[index].is_some(); - encoded_children[index] = handle; - if del { - let mut count = 0; - for c in encoded_children.iter() { - if c.is_some() { - count += 1; - } - if count > 1 { - break; - } - } - - debug_assert!(!(count == 0 && val.is_none())); - if val.is_some() && count == 0 { - // transform to leaf - (Node::Leaf(partial, val.expect("Test in above condition")), None) - } else if val.is_none() && count == 1 { - let child_ix = encoded_children.iter().position(Option::is_some) - .expect("counted above"); - (Node::NibbledBranch(partial, encoded_children, val), Some(child_ix as u8)) - } else { - (Node::NibbledBranch(partial, encoded_children, val), None) - } - } else { - (Node::NibbledBranch(partial, encoded_children, val), None) - } - } + encoded_children[index] = handle; + Node::NibbledBranch(partial, encoded_children, val) }, - Node::Leaf(partial, val) => { - if handle.is_some() { + Node::Leaf(partial, val) if handle.is_some() => { let mut children = Box::new([ None, None, None, None, None, None, None, None, @@ -266,9 +238,54 @@ impl Node { ]); children[index] = handle; - (Node::NibbledBranch(partial, children, Some(val)), None) + Node::NibbledBranch(partial, children, Some(val)) + }, + n@Node::Leaf(..) => { + n + }, + }; + } + + /// Branch in invalid state should only be updated before write + /// to avoid multiple change of state. + /// Return true if the node can be removed, + /// and index of node to fuse with in case after removal of handle + /// a branch with a single child and no value remain. + /// This is only for no extension trie (a variant would be + /// needed for trie with extension). + pub fn fix_node( + &mut self, + ) -> (bool, Option) { + let node = mem::replace(self, Node::Empty); + let (node, fuse) = match node { + Node::Extension(..) + | Node::Branch(..) => unreachable!("Only for no extension trie"), + // need to replace value before creating handle + // so it is a corner case TODO test case it + // (may be unreachable since changing to empty means + // we end the root process) + n@Node::Empty + | n@Node::Leaf(..) => (n, None), + Node::NibbledBranch(partial, encoded_children, val) => { + let mut count = 0; + for c in encoded_children.iter() { + if c.is_some() { + count += 1; + } + if count > 1 { + break; + } + } + if val.is_some() && count == 0 { + (Node::Leaf(partial, val.expect("Tested above")), None) + } else if val.is_none() && count == 0 { + (Node::Empty, None) + } else if val.is_none() && count == 1 { + let child_ix = encoded_children.iter().position(Option::is_some) + .expect("counted above"); + (Node::NibbledBranch(partial, encoded_children, val), Some(child_ix as u8)) } else { - (Node::Leaf(partial, val), None) + (Node::NibbledBranch(partial, encoded_children, val), None) } }, }; @@ -280,6 +297,34 @@ impl Node { }, fuse) } + /// Fuse changed node that need to be reduce to a child node, + /// return additional length to prefix. + pub fn fuse_child>(&mut self, child: OwnedNode, child_ix: u8) -> usize { + let node = mem::replace(self, Node::Empty); + let (node, result) = match node { + Node::Extension(..) + | Node::Branch(..) => unreachable!("Only for no extension trie"), + // need to replace value before creating handle + // so it is a corner case TODO test case it + // (may be unreachable since changing to empty means + // we end the root process) + n@Node::Empty + | n@Node::Leaf(..) => unreachable!("method only for nodes resulting from fix node"), + Node::NibbledBranch(mut partial, _children, _val) => { + // TODO could use owned nibble slice or optimize this + combine_key(&mut partial, (nibble_ops::NIBBLE_PER_BYTE - 1, &[child_ix][..])); + let child_partial = child.partial(); + let result = child_partial.as_ref().map(|p| p.len()).unwrap_or(0) + 1; + combine_key(&mut partial, child_partial + .map(|n| n.right_ref()).unwrap_or((0,&[]))); + + (Node::Leaf(partial, child.value().expect("fuse child is resulting from fix node which check that")), result) + } + }; + *self = node; + result + } + /// Set handle to a mid branch, changing self element to this /// new branch and returning the old element (new child). pub fn set_mid_handle( From f946552a65c035a0d8dcc09ef99fa3ea7fc8521c Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 11 Dec 2019 16:10:25 +0100 Subject: [PATCH 027/118] fix fuse for branch case. --- trie-db/fuzz/src/lib.rs | 4 ++- trie-db/src/nibble/nibbleslice.rs | 4 ++- trie-db/src/node.rs | 32 +++++++++++++++++ trie-db/src/traverse.rs | 18 +++++++++- trie-db/src/triedbmut.rs | 60 ++++++++++++++++--------------- 5 files changed, 87 insertions(+), 31 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index b14011e8..22a5e1ad 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -214,7 +214,7 @@ println!("{:?}", data); Default::default(), initial_root.clone(), ); -// println!("{}", sorted_data.len()); + println!("{:?}", sorted_data); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); @@ -226,6 +226,8 @@ println!("{:?}", data); #[test] fn test() { let tests = [ + vec![0x0,0x0,0xff,0xff,0x0,0x0,0x4,0x8d,0x87,0xd1], + vec![0x0,0x0,0x0,0x0,0x4,0x0,0xff,0xd1,0x0,0x0,0xfe,0x0], vec![0x0,0x0,0xfe,0x0,0xff,0xff,0xd1,0xd1,0x27,0x0], vec![0x0,0xfe,0x41,0x0,0x0,0x80,0x0,0x0,0xff], vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 2cc02b2e..939414d7 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -249,7 +249,9 @@ impl<'a> NibbleSlice<'a> { impl<'a> Into for NibbleSlice<'a> { fn into(self) -> NodeKey { - (self.offset, self.data.into()) + let new_offset = self.offset % nibble_ops::NIBBLE_PER_BYTE; + let start = self.offset / nibble_ops::NIBBLE_PER_BYTE; + (new_offset, self.data[start..].into()) } } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 63f7885a..a0f8ebbe 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -324,6 +324,38 @@ impl> OwnedNode { } } + /// Set a partial TODO EMCH factor new node from existing on all those methods. + pub fn set_partial + Default>(&mut self, partial: NodeKey) -> Option>> { + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Leaf { value, .. } => { + Some(TNode::Leaf( + partial, + data[value.clone()].into(), + )) + }, + NodePlan::Extension { .. } // TODO Extension + | NodePlan::Branch { .. } // TODO branch + | NodePlan::Empty => None, + NodePlan::NibbledBranch { value, children, .. } => { + let mut child_slices = [ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]; + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); + } + Some(TNode::NibbledBranch( + partial, + Box::new(child_slices), + value.as_ref().map(|value| data[value.clone()].into()), + )) + }, + } + } + /// Set a value. pub fn set_value + Default>(&mut self, new_value: &[u8]) -> Option>> { let data = &self.data.borrow(); diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 7b3c36bf..13b9b08a 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -949,6 +949,22 @@ mod tests { ], ); } - +// (false, [255], [255, 0]), (false, [255], [0, 0]), (false, [0], [0, 4]), (false, [0], [4, 141]), (false, [4], [141, 135]), (true, [255], [255, 0]) + #[test] + fn dummy4() { + compare_with_triedbmut( + &[ + (vec![255u8], vec![255, 0]), +// (vec![4u8], vec![0, 255]), + (vec![0u8], vec![255, 209]), + (vec![4u8], vec![0, 4]), + ], + &[ + (vec![255u8], None), +// (vec![209u8], Some(vec![0, 0])), +// (vec![255u8], Some(vec![209, 0])), + ], + ); + } } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 43999110..d5410cea 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -134,6 +134,38 @@ pub enum Node { NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), } +impl + Default> Node> { + /// Fuse changed node that need to be reduce to a child node, + /// return additional length to prefix. + pub fn fuse_child>(&mut self, mut child: OwnedNode, child_ix: u8) -> usize { + let node = mem::replace(self, Node::Empty); + let (node, result) = match node { + Node::Extension(..) + | Node::Branch(..) => unreachable!("Only for no extension trie"), + // need to replace value before creating handle + // so it is a corner case TODO test case it + // (may be unreachable since changing to empty means + // we end the root process) + Node::Empty + | Node::Leaf(..) => unreachable!("method only for nodes resulting from fix node"), + Node::NibbledBranch(mut partial, _children, _val) => { + // TODO could use owned nibble slice or optimize this + combine_key(&mut partial, (nibble_ops::NIBBLE_PER_BYTE - 1, &[child_ix][..])); + let child_partial = child.partial(); + let result = child_partial.as_ref().map(|p| p.len()).unwrap_or(0) + 1; + combine_key(&mut partial, child_partial + .map(|n| n.right_ref()).unwrap_or((0,&[]))); + + (child.set_partial(partial) + .expect("checked child node before call is expected"), + result) + } + }; + *self = node; + result + } +} + impl Node { /// Tell if it is the empty node. @@ -297,34 +329,6 @@ impl Node { }, fuse) } - /// Fuse changed node that need to be reduce to a child node, - /// return additional length to prefix. - pub fn fuse_child>(&mut self, child: OwnedNode, child_ix: u8) -> usize { - let node = mem::replace(self, Node::Empty); - let (node, result) = match node { - Node::Extension(..) - | Node::Branch(..) => unreachable!("Only for no extension trie"), - // need to replace value before creating handle - // so it is a corner case TODO test case it - // (may be unreachable since changing to empty means - // we end the root process) - n@Node::Empty - | n@Node::Leaf(..) => unreachable!("method only for nodes resulting from fix node"), - Node::NibbledBranch(mut partial, _children, _val) => { - // TODO could use owned nibble slice or optimize this - combine_key(&mut partial, (nibble_ops::NIBBLE_PER_BYTE - 1, &[child_ix][..])); - let child_partial = child.partial(); - let result = child_partial.as_ref().map(|p| p.len()).unwrap_or(0) + 1; - combine_key(&mut partial, child_partial - .map(|n| n.right_ref()).unwrap_or((0,&[]))); - - (Node::Leaf(partial, child.value().expect("fuse child is resulting from fix node which check that")), result) - } - }; - *self = node; - result - } - /// Set handle to a mid branch, changing self element to this /// new branch and returning the old element (new child). pub fn set_mid_handle( From 7769ae1138341e536cc8b35960b13466d8ca6c5d Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 12 Dec 2019 11:54:35 +0100 Subject: [PATCH 028/118] feed a latest value for chained fusion --- trie-db/fuzz/src/lib.rs | 7 +- trie-db/src/nibble/nibblevec.rs | 7 +- trie-db/src/traverse.rs | 137 +++++++++++++++++++++++++------- trie-db/src/triedbmut.rs | 59 +++++++++++++- 4 files changed, 178 insertions(+), 32 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 22a5e1ad..ea1ba343 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -177,7 +177,7 @@ pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { pub fn fuzz_batch_update(input: &[u8]) { let data = fuzz_to_data(input); let data = fuzz_removal(data); -println!("{:?}", data); +println!("{}: {:?}", data.len(), data); let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { @@ -213,6 +213,7 @@ println!("{:?}", data); let mut batch_update = reference_trie::BatchUpdate( Default::default(), initial_root.clone(), + None, ); println!("{:?}", sorted_data); reference_trie::trie_traverse_key_no_extension_build( @@ -226,6 +227,8 @@ println!("{:?}", data); #[test] fn test() { let tests = [ + vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], +/* vec![0x0,0x80,0x0,0xd1,0x0,0x0,0x9a,0x4,0x4,0x0,0x0,0xc], vec![0x0,0x0,0xff,0xff,0x0,0x0,0x4,0x8d,0x87,0xd1], vec![0x0,0x0,0x0,0x0,0x4,0x0,0xff,0xd1,0x0,0x0,0xfe,0x0], vec![0x0,0x0,0xfe,0x0,0xff,0xff,0xd1,0xd1,0x27,0x0], @@ -235,7 +238,7 @@ fn test() { vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], vec![0x0,0x0,0x0,0x0,0x8d,0x4], - vec![0x0,0x0,0x4,0x8d,0x8d,0x4], + vec![0x0,0x0,0x4,0x8d,0x8d,0x4],*/ ]; for v in tests.iter() { fuzz_batch_update(&v[..]) diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index ad704f81..56065733 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -38,7 +38,12 @@ impl NibbleVec { /// Make a new `NibbleVec`. pub fn from(d: &[u8], l: usize) -> Self { let mut v = Self::default(); - (0..d.len()).for_each(|i| v.inner.push(d[i])); + let ix = l / nibble_ops::NIBBLE_PER_BYTE; + let pad = l % nibble_ops::NIBBLE_PER_BYTE; + (0..ix).for_each(|i| v.inner.push(d[i])); + if pad == 1 { + v.inner.push(nibble_ops::pad_left(d[ix])); + } v.len = l; v } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 13b9b08a..4849d3d4 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -270,6 +270,15 @@ pub trait ProcessStack -> Option>>>; /// Same as `exit` but for root (very last exit call). fn exit_root(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>); + + + /// Fetching a fuse latest, that is the latest changed value of a branch. + /// This is called once per level in case of removal/fusion of a branch with + /// a child + /// This is only needed if changes are made by process stack. + /// Access to this method means that the node need to be remove or invalidate (this is handled + /// by this method (no call to exit on it)). + fn fuse_latest_changed(&mut self, prefix: NibbleSlice, hash: &TrieHash) -> Option<&[u8]>; } /// State when descending @@ -491,7 +500,13 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } else { // remove empties while current.node.is_empty() && stack.len() > 0 { - if let Some(prev) = stack.pop() { + // TODO runing into this is not expected + if let Some(mut prev) = stack.pop() { + callback.exit( + NibbleSlice::new_offset(k.as_ref(), current.depth_prefix), + current.node, current.hash.as_ref(), + ).expect("no new node on empty"); + prev.node.set_handle(None, current.parent_index); current = prev; } } @@ -587,14 +602,27 @@ fn align_node<'a, T, K, V, S, B, F>( Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - // TODO conversion to NibbleVec is slow - let mut prefix: NibbleVec = NibbleVec::from(key, branch.depth); - prefix.push(fuse_index); - (fetch::( - db, + if let Some(node_encoded) = callback.fuse_latest_changed( + NibbleSlice::new_offset(key, branch.depth + 1), &hash, - prefix.as_prefix(), - )?, Some(hash)) + ) { + println!("LL"); + // costy encode decode round trip, but this is a corner case. + (OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError( + branch.hash.clone().unwrap_or_else(Default::default), + e, + )))?, None) + } else { + // TODO conversion to NibbleVec is slow + let mut prefix: NibbleVec = NibbleVec::from(key, branch.depth); + prefix.push(fuse_index); + (fetch::( + db, + &hash, + prefix.as_prefix(), + )?, Some(hash)) + } }, Some(NodeHandle::Inline(node_encoded)) => { (OwnedNode::new::(B::from(node_encoded)) @@ -634,7 +662,13 @@ fn fetch>( } /// Contains ordered node change for this iteration. -pub struct BatchUpdate(pub Vec<((ElasticArray36, Option), H, Option>)>, pub H); +/// The resulting root hash. +/// The latest changed node. +pub struct BatchUpdate( + pub Vec<((ElasticArray36, Option), H, Option>, bool)>, + pub H, + pub Option, +); impl ProcessStack for BatchUpdate> where @@ -742,23 +776,29 @@ impl ProcessStack for BatchUpdate> fn exit(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>> { match stacked { - s@StackedNode::Changed(..) => Some(Some({ - let encoded = s.into_encoded(); + StackedNode::Changed(node, _) => Some(Some({ + let encoded = node.into_encoded::<_, T::Codec, T::Hash>( + |child, o_slice, o_index| { + child.as_child_ref::() + } + ); if encoded.len() < 32 { OwnedNodeHandle::InMemory(encoded) } else { let hash = ::hash(&encoded[..]); + // register latest change + self.2 = Some(self.0.len()); // costy clone (could get read from here) - self.0.push((prefix.left_owned(), hash.clone(), Some(encoded))); + self.0.push((prefix.left_owned(), hash.clone(), Some(encoded), true)); if let Some(h) = prev_hash { - self.0.push((prefix.left_owned(), h.clone(), None)); + self.0.push((prefix.left_owned(), h.clone(), None, true)); } OwnedNodeHandle::Hash(hash) } })), StackedNode::Deleted(..) => { if let Some(h) = prev_hash { - self.0.push((prefix.left_owned(), h.clone(), None)); + self.0.push((prefix.left_owned(), h.clone(), None, true)); } Some(None) }, @@ -773,14 +813,28 @@ impl ProcessStack for BatchUpdate> let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); self.1 = hash.clone(); - self.0.push((prefix.left_owned(), hash, Some(encoded))); + self.0.push((prefix.left_owned(), hash, Some(encoded), true)); if let Some(h) = prev_hash { - self.0.push((prefix.left_owned(), h.clone(), None)); + self.0.push((prefix.left_owned(), h.clone(), None, true)); } }, _ => (), } } + + fn fuse_latest_changed(&mut self, prefix: NibbleSlice, hash: &TrieHash) -> Option<&[u8]> { + // consume anyway. + let last = self.2.take(); + if let Some(latest) = last { + let stored_slice = (&(self.0[latest].0).0[..], (self.0[latest].0).1); + if hash == &self.0[latest].1 && prefix.left() == stored_slice { + self.0[latest].3 = false; + self.0[latest].2.as_ref().map(|s| &s[..]) + } else { + None + } + } else { None } + } } #[cfg(test)] @@ -801,10 +855,11 @@ mod tests { type H256 = ::Out; fn memory_db_from_delta( - delta: Vec<((ElasticArray36, Option), H256, Option>)>, + delta: Vec<((ElasticArray36, Option), H256, Option>, bool)>, mdb: &mut MemoryDB, DBValue>, ) { - for (p, h, v) in delta { + for (p, h, v, d) in delta { + if d { if let Some(v) = v { let prefix = (p.0.as_ref(), p.1); // damn elastic array in value looks costy @@ -813,6 +868,7 @@ mod tests { let prefix = (p.0.as_ref(), p.1); mdb.remove(&h, prefix); } + } } } @@ -853,7 +909,7 @@ mod tests { let reference_root = root.clone(); - let mut batch_update = BatchUpdate(Default::default(), initial_root.clone()); + let mut batch_update = BatchUpdate(Default::default(), initial_root.clone(), None); trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, @@ -949,22 +1005,49 @@ mod tests { ], ); } -// (false, [255], [255, 0]), (false, [255], [0, 0]), (false, [0], [0, 4]), (false, [0], [4, 141]), (false, [4], [141, 135]), (true, [255], [255, 0]) + #[test] fn dummy4() { compare_with_triedbmut( &[ - (vec![255u8], vec![255, 0]), -// (vec![4u8], vec![0, 255]), - (vec![0u8], vec![255, 209]), - (vec![4u8], vec![0, 4]), +// (vec![212u8], vec![255, 209]), + (vec![0u8], vec![1; 32]), +// (vec![1u8], vec![0, 4]), + (vec![212u8], vec![2; 32]), +/* (vec![212u8], vec![0, 4]), + (vec![13u8], vec![0, 4]), + (vec![2u8], vec![0, 4]), + (vec![9u8], vec![0, 4]), + (vec![8u8], vec![1, 2]),*/ + ], + &[ +// (vec![0u8], Some(vec![1, 2])), +// (vec![1u8], Some(vec![1, 2])), +// (vec![8u8], Some(vec![1, 2])), + (vec![0u8], None), + (vec![212u8], Some(vec![3; 32])), +// (vec![154u8], Some(vec![209, 0])), + ], + ); + } + + #[test] + fn chained_fuse() { + compare_with_triedbmut( + &[ + (vec![0u8], vec![1; 32]), + (vec![0, 212], vec![2; 32]), + (vec![0, 212, 96], vec![3; 32]), + (vec![0, 212, 96, 88], vec![3; 32]), ], &[ - (vec![255u8], None), -// (vec![209u8], Some(vec![0, 0])), -// (vec![255u8], Some(vec![209, 0])), + (vec![0u8], None), + (vec![0, 212], None), + (vec![0, 212, 96], None), + (vec![0, 212, 96, 88], Some(vec![3; 32])), ], ); } + } diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index d5410cea..6cffece7 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -76,7 +76,7 @@ where { /// Get a child reference (this has a cost but doing /// otherwhise requires changing codec trait). - pub(crate) fn as_child_ref

(self) -> ChildReference + pub(crate) fn as_child_ref

(&self) -> ChildReference where H2: Hasher { @@ -90,7 +90,7 @@ where h.as_mut()[..len].copy_from_slice(&sh_ref[..len]); ChildReference::Inline(h, len) }, - NodeHandle::Hash(h) => ChildReference::Hash(h), + NodeHandle::Hash(h) => ChildReference::Hash(h.clone()), } } @@ -468,6 +468,61 @@ where O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy { +/* pub(crate) fn into_encoded_ref(&self, mut child_cb: F) -> Vec + where + C: NodeCodec, + F: FnMut(&NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, + H: Hasher, + { + match self { + Node::Empty => C::empty_node().to_vec(), + Node::Leaf(partial, value) => { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); +// println!("d{:?} {:?}", pr.right(), value); + C::leaf_node(pr.right(), &value) + }, + Node::Extension(partial, child) => { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + let it = pr.right_iter(); + let c = child_cb(child, Some(&pr), None); + C::extension_node( + it, + pr.len(), + c, + ) + }, + Node::Branch(children, value) => { + C::branch_node( + // map the `NodeHandle`s from the Branch to `ChildReferences` + children.iter() + .enumerate() + .map(|(i, maybe_child)| { + maybe_child.as_ref().map(|child| child_cb(&child, None, Some(i as u8))) + }), + value.as_ref().map(|v| &v[..]) + ) + }, + Node::NibbledBranch(partial, children, value) => { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + let it = pr.right_iter(); + C::branch_node_nibbled( + it, + pr.len(), + // map the `NodeHandle`s from the Branch to `ChildReferences` + children.iter() + .enumerate() + .map(|(i, maybe_child)| { + //let branch_index = [i as u8]; + maybe_child.as_ref().map(|child| { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + child_cb(&child, Some(&pr), Some(i as u8)) + }) + }), + value.as_ref().map(|v| &v[..]) + ) + }, + } + }*/ // TODO: parallelize pub(crate) fn into_encoded(self, mut child_cb: F) -> Vec From db250ee5e47725a20829a7cfec3725affdcd04a1 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 12 Dec 2019 12:13:33 +0100 Subject: [PATCH 029/118] fix prefix check --- trie-db/fuzz/src/lib.rs | 4 ++-- trie-db/src/traverse.rs | 33 +++++++++++++++++---------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index ea1ba343..46b78103 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -228,7 +228,7 @@ println!("{}: {:?}", data.len(), data); fn test() { let tests = [ vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], -/* vec![0x0,0x80,0x0,0xd1,0x0,0x0,0x9a,0x4,0x4,0x0,0x0,0xc], + vec![0x0,0x80,0x0,0xd1,0x0,0x0,0x9a,0x4,0x4,0x0,0x0,0xc], vec![0x0,0x0,0xff,0xff,0x0,0x0,0x4,0x8d,0x87,0xd1], vec![0x0,0x0,0x0,0x0,0x4,0x0,0xff,0xd1,0x0,0x0,0xfe,0x0], vec![0x0,0x0,0xfe,0x0,0xff,0xff,0xd1,0xd1,0x27,0x0], @@ -238,7 +238,7 @@ fn test() { vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], vec![0x0,0x0,0x0,0x0,0x8d,0x4], - vec![0x0,0x0,0x4,0x8d,0x8d,0x4],*/ + vec![0x0,0x0,0x4,0x8d,0x8d,0x4], ]; for v in tests.iter() { fuzz_batch_update(&v[..]) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 4849d3d4..4ebfd219 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -278,7 +278,7 @@ pub trait ProcessStack /// This is only needed if changes are made by process stack. /// Access to this method means that the node need to be remove or invalidate (this is handled /// by this method (no call to exit on it)). - fn fuse_latest_changed(&mut self, prefix: NibbleSlice, hash: &TrieHash) -> Option<&[u8]>; + fn fuse_latest_changed(&mut self, prefix: Prefix, hash: &TrieHash) -> Option<&[u8]>; } /// State when descending @@ -602,11 +602,14 @@ fn align_node<'a, T, K, V, S, B, F>( Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + // TODO conversion to NibbleVec is slow + let mut prefix: NibbleVec = NibbleVec::from(key, branch.depth); + prefix.push(fuse_index); + let prefix = prefix.as_prefix(); if let Some(node_encoded) = callback.fuse_latest_changed( - NibbleSlice::new_offset(key, branch.depth + 1), + prefix, &hash, ) { - println!("LL"); // costy encode decode round trip, but this is a corner case. (OwnedNode::new::(B::from(node_encoded)) .map_err(|e| Box::new(TrieError::DecoderError( @@ -614,13 +617,10 @@ fn align_node<'a, T, K, V, S, B, F>( e, )))?, None) } else { - // TODO conversion to NibbleVec is slow - let mut prefix: NibbleVec = NibbleVec::from(key, branch.depth); - prefix.push(fuse_index); (fetch::( db, &hash, - prefix.as_prefix(), + prefix, )?, Some(hash)) } }, @@ -822,12 +822,12 @@ impl ProcessStack for BatchUpdate> } } - fn fuse_latest_changed(&mut self, prefix: NibbleSlice, hash: &TrieHash) -> Option<&[u8]> { + fn fuse_latest_changed(&mut self, prefix: Prefix, hash: &TrieHash) -> Option<&[u8]> { // consume anyway. let last = self.2.take(); if let Some(latest) = last { let stored_slice = (&(self.0[latest].0).0[..], (self.0[latest].0).1); - if hash == &self.0[latest].1 && prefix.left() == stored_slice { + if hash == &self.0[latest].1 && prefix == stored_slice { self.0[latest].3 = false; self.0[latest].2.as_ref().map(|s| &s[..]) } else { @@ -1012,20 +1012,21 @@ mod tests { &[ // (vec![212u8], vec![255, 209]), (vec![0u8], vec![1; 32]), -// (vec![1u8], vec![0, 4]), + (vec![1u8], vec![0, 4]), (vec![212u8], vec![2; 32]), -/* (vec![212u8], vec![0, 4]), + (vec![212u8], vec![0, 4]), (vec![13u8], vec![0, 4]), (vec![2u8], vec![0, 4]), (vec![9u8], vec![0, 4]), - (vec![8u8], vec![1, 2]),*/ + (vec![8u8], vec![1, 2]), ], &[ // (vec![0u8], Some(vec![1, 2])), -// (vec![1u8], Some(vec![1, 2])), -// (vec![8u8], Some(vec![1, 2])), - (vec![0u8], None), - (vec![212u8], Some(vec![3; 32])), + (vec![0u8], Some(vec![0,8])), + (vec![1u8], Some(vec![1, 2])), + (vec![8u8], Some(vec![1, 2])), + (vec![12u8], None), + (vec![212u8], None), // (vec![154u8], Some(vec![209, 0])), ], ); From 18b21f421881543f1ef0465aeb9203acf2f98e24 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 12 Dec 2019 12:41:26 +0100 Subject: [PATCH 030/118] checkpoint --- trie-db/fuzz/src/lib.rs | 1 + trie-db/src/traverse.rs | 21 +++++---------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 46b78103..2af6070f 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -227,6 +227,7 @@ println!("{}: {:?}", data.len(), data); #[test] fn test() { let tests = [ +// vec![0x0,0x84,0xff,0xfb,0x7f,0xff,0xff,0xff,0xff,0x7f,0x70,0xff,0xff,0x7f,0x72,0xfd,0xc3,0x0,0x4,0xfb,0xff,0x10,0x10,0x10,0x10,0x10,0x9a,0x4], vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], vec![0x0,0x80,0x0,0xd1,0x0,0x0,0x9a,0x4,0x4,0x0,0x0,0xc], vec![0x0,0x0,0xff,0xff,0x0,0x0,0x4,0x8d,0x87,0xd1], diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 4ebfd219..71c62097 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1010,24 +1010,13 @@ mod tests { fn dummy4() { compare_with_triedbmut( &[ -// (vec![212u8], vec![255, 209]), - (vec![0u8], vec![1; 32]), - (vec![1u8], vec![0, 4]), - (vec![212u8], vec![2; 32]), - (vec![212u8], vec![0, 4]), - (vec![13u8], vec![0, 4]), - (vec![2u8], vec![0, 4]), - (vec![9u8], vec![0, 4]), - (vec![8u8], vec![1, 2]), + (vec![255u8, 251, 127, 255, 255], vec![255, 255]), + (vec![255, 255, 127, 112, 255], vec![0, 4]), + (vec![255, 127, 114, 253, 195], vec![1, 2]), ], &[ -// (vec![0u8], Some(vec![1, 2])), - (vec![0u8], Some(vec![0,8])), - (vec![1u8], Some(vec![1, 2])), - (vec![8u8], Some(vec![1, 2])), - (vec![12u8], None), - (vec![212u8], None), -// (vec![154u8], Some(vec![209, 0])), + (vec![0u8], Some(vec![4; 251])), + (vec![255, 251, 127, 255, 255], Some(vec![1, 2])), ], ); } From bd4bcd6bd8a6eec0ac3a2a02f9085b9576962fae Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 12 Dec 2019 21:19:42 +0100 Subject: [PATCH 031/118] split_child need to be a vecdeque --- trie-db/fuzz/src/lib.rs | 10 +-- trie-db/src/traverse.rs | 171 +++++++++++++++++++++------------------ trie-db/src/triedbmut.rs | 13 +++ 3 files changed, 109 insertions(+), 85 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 2af6070f..49c6537f 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -228,18 +228,18 @@ println!("{}: {:?}", data.len(), data); fn test() { let tests = [ // vec![0x0,0x84,0xff,0xfb,0x7f,0xff,0xff,0xff,0xff,0x7f,0x70,0xff,0xff,0x7f,0x72,0xfd,0xc3,0x0,0x4,0xfb,0xff,0x10,0x10,0x10,0x10,0x10,0x9a,0x4], - vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], +/* vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], vec![0x0,0x80,0x0,0xd1,0x0,0x0,0x9a,0x4,0x4,0x0,0x0,0xc], vec![0x0,0x0,0xff,0xff,0x0,0x0,0x4,0x8d,0x87,0xd1], vec![0x0,0x0,0x0,0x0,0x4,0x0,0xff,0xd1,0x0,0x0,0xfe,0x0], vec![0x0,0x0,0xfe,0x0,0xff,0xff,0xd1,0xd1,0x27,0x0], - vec![0x0,0xfe,0x41,0x0,0x0,0x80,0x0,0x0,0xff], + vec![0x0,0xfe,0x41,0x0,0x0,0x80,0x0,0x0,0xff],*/ vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], - vec![0x0,0x0,0x0,0x0,0xfe,0xff,0xff,0xd1,0x27], - vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], +// vec![0x0,0x0,0x0,0x0,0xfe,0xff,0xff,0xd1,0x27], +/* vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], vec![0x0,0x0,0x0,0x0,0x8d,0x4], - vec![0x0,0x0,0x4,0x8d,0x8d,0x4], + vec![0x0,0x0,0x4,0x8d,0x8d,0x4],*/ ]; for v in tests.iter() { fuzz_batch_update(&v[..]) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 71c62097..aca483a0 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -71,6 +71,8 @@ pub struct StackedItem pub depth: usize, /// parent index (only relevant when parent is a branch). pub parent_index: u8, + /// Tell if a split child has been created for this branch. + pub split_child: bool, } impl StackedNode @@ -299,7 +301,7 @@ fn descend_terminal( value: Option<&V>, dest_depth: usize, callback: &mut F, -) -> Option> +) -> (Option>, Option>) where T: TrieLayout, K: AsRef<[u8]> + Ord, @@ -317,12 +319,12 @@ fn descend_terminal( debug_assert!(!(target_common_depth < item.depth_prefix), "Descend should not be call in this state"); if target_common_depth < item.depth { // insert into prefix - callback.enter_terminal( + (None, callback.enter_terminal( item, key.as_ref(), value.as_ref().map(|v| v.as_ref()), TraverseState::MidPartial(target_common_depth), - ) + )) } else if key_dest == item.depth { // set value let n = callback.enter_terminal( @@ -332,15 +334,15 @@ fn descend_terminal( TraverseState::ValueMatch, ); debug_assert!(n.is_none()); - None + (None, None) } else { // extend - callback.enter_terminal( + (callback.enter_terminal( item, key.as_ref(), value.as_ref().map(|v| v.as_ref()), TraverseState::AfterNode, - ) + ), None) } } @@ -380,10 +382,12 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( depth_prefix: 0, depth, parent_index: 0, + split_child: false, }; let mut k: Option = None; + let mut split_child: Option> = None; let mut limit_common = usize::max_value(); for (next_k, v) in elements.into_iter() { if let Some(previous_key) = k { @@ -391,44 +395,32 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( previous_key.as_ref(), next_k.as_ref(), ); - target_common_depth = min(limit_common, target_common_depth); - + /*if target_common_depth == limit_common { + // try to see if more common in current node + // TODO this is redundant with start of descend. + let slice_dest = NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix); + target_common_depth = current.node.partial() + .map(|p| current.depth_prefix + p.common_prefix(&slice_dest)) + .unwrap_or(current.depth_prefix); + } else { + target_common_depth = min(limit_common, target_common_depth); + }*/ + //target_common_depth = min(limit_common, target_common_depth); while target_common_depth < current.depth_prefix { // go up if let Some(mut last) = stack.pop() { if !last.node.is_empty() { - align_node(db, callback, &mut current, previous_key.as_ref())?; + align_node(db, callback, &mut current, previous_key.as_ref(), &mut split_child)?; if let Some(handle) = callback.exit( NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), current.node, current.hash.as_ref(), ) { - if last.depth >= current.depth_prefix { - if let Some(handle) = handle { - let common = current.depth_prefix - 1; - let nibble_common = common - last.depth_prefix; - let parent_index = last.node.partial() - .map(|p| p.at(nibble_common)).unwrap_or(0); - let child = last.node - .set_mid_handle(handle, current.parent_index, nibble_common); - current = StackedItem { - node: child, - hash: None, - depth: last.depth, - depth_prefix: common + 1, - parent_index - }; - last.depth = common; - stack.push(last); - continue; - } - } else { - last.node.set_handle(handle, current.parent_index); - } + last.node.set_handle(handle, current.parent_index); } current = last; } } else { - align_node(db, callback, &mut current, previous_key.as_ref())?; + align_node(db, callback, &mut current, previous_key.as_ref(), &mut split_child)?; callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); return Ok(()); } @@ -450,6 +442,16 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let (child, parent_index) = if target_common_depth == current.depth { if dest_depth > current.depth { let next_index = dest.at(current.depth); + if current.split_child { + if next_index == split_child.as_ref().map(|c| c.parent_index) + .expect("split child set before") { + current.split_child = false; + stack.push(current); + current = split_child.take() + .expect("split child set before"); + continue; + } + } (current.node.child(next_index), next_index) } else { (None, 0) @@ -496,6 +498,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( depth_prefix, depth, parent_index, + split_child: false, }; } else { // remove empties @@ -523,15 +526,24 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( depth_prefix: current.depth_prefix, depth: k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - current.depth_prefix, parent_index: current.parent_index, + split_child: false, } } } else { // terminal case - if let Some(new) = descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { - stack.push(current); - current = new; - limit_common = target_common_depth; - }; + match descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { + (Some(new), _) => { + stack.push(current); + current = new; + limit_common = target_common_depth; + }, + (_, Some(split)) => { + assert!(split_child.is_none()); // TODO change to debug assert later or stack it + split_child = Some(split); + continue; + }, + _ => (), + } } // go next key break; @@ -544,38 +556,17 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( // go up while let Some(mut last) = stack.pop() { if !last.node.is_empty() { - align_node(db, callback, &mut current, previous_key.as_ref())?; + align_node(db, callback, &mut current, previous_key.as_ref(), &mut split_child)?; if let Some(handle) = callback.exit( NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), current.node, current.hash.as_ref(), ) { - if last.depth >= current.depth_prefix { - if let Some(handle) = handle { - let common = current.depth_prefix - 1; - let nibble_common = common - last.depth_prefix; - let parent_index = last.node.partial() - .map(|p| p.at(nibble_common)).unwrap_or(0); - let child = last.node - .set_mid_handle(handle, current.parent_index, nibble_common); - current = StackedItem { - node: child, - hash: None, - depth: last.depth, - depth_prefix: common + 1, - parent_index - }; - last.depth = common; - stack.push(last); - continue; - } - } else { - last.node.set_handle(handle, current.parent_index); - } + last.node.set_handle(handle, current.parent_index); } current = last; } } - align_node(db, callback, &mut current, previous_key.as_ref())?; + align_node(db, callback, &mut current, previous_key.as_ref(), &mut split_child)?; callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); return Ok(()); } @@ -588,6 +579,7 @@ fn align_node<'a, T, K, V, S, B, F>( callback: &mut F, branch: &mut StackedItem, key: &[u8], + split_child: &mut Option>, ) -> Result<(), TrieHash, CError> where T: TrieLayout, @@ -597,6 +589,17 @@ fn align_node<'a, T, K, V, S, B, F>( B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, F: ProcessStack, { + if branch.split_child { + branch.split_child = false; + let mut child = split_child.take() + .expect("trie correct parsing ensure it is set"); + align_node(db, callback, &mut child, key, split_child)?; + let handle = callback.exit( + NibbleSlice::new_offset(key, child.depth_prefix), + child.node, child.hash.as_ref(), + ).expect("split child is always a changed node"); + branch.node.set_handle(handle, child.parent_index); + } if let Some(fuse_index) = branch.node.fix_node() { let (child, hash) = match branch.node.child(fuse_index) { Some(NodeHandle::Hash(handle_hash)) => { @@ -718,6 +721,7 @@ impl ProcessStack for BatchUpdate> depth_prefix: stacked.depth + offset, depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, + split_child: false, }); } else { // nothing to delete. @@ -726,27 +730,23 @@ impl ProcessStack for BatchUpdate> }, TraverseState::MidPartial(mid_index) => { if let Some(val) = value_element { - // can differ from mid_index (then need on exit to create an additional child - // leaf for value) - let mid_dest = key_element.len() * nibble_ops::NIBBLE_PER_BYTE; - // here we could create branch but this way we use valid node - let dest_branch = if mid_dest % nibble_ops::NIBBLE_PER_BYTE == 0 { + let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { let new_slice = NibbleSlice::new_offset( - &key_element[..mid_dest / nibble_ops::NIBBLE_PER_BYTE], + &key_element[..mid_index / nibble_ops::NIBBLE_PER_BYTE], stacked.depth_prefix, ); - Node::new_leaf(new_slice, val) + Node::new_branch(new_slice) } else { let new_slice = NibbleSlice::new_offset( &key_element[..], stacked.depth_prefix, ); - let owned = new_slice.to_stored_range(mid_dest - stacked.depth_prefix); + let owned = new_slice.to_stored_range(mid_index - stacked.depth_prefix); // TODO EMCH refactor new_leaf to take NodeKey (stored) as input - Node::new_leaf(NibbleSlice::from_stored(&owned), val) + Node::new_branch(NibbleSlice::from_stored(&owned)) }; let old_depth = stacked.depth; - stacked.depth = mid_dest; + stacked.depth = mid_index; let mut child = mem::replace( &mut stacked.node, StackedNode::Changed(dest_branch, Default::default()), @@ -763,7 +763,9 @@ impl ProcessStack for BatchUpdate> depth_prefix: 1 + mid_index, depth: old_depth, parent_index, + split_child: stacked.split_child, }; + stacked.split_child = true; return Some(child); } else { // nothing to delete. @@ -961,20 +963,15 @@ mod tests { fn dummy1() { compare_with_triedbmut( &[ - (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8, 0x23]), - (vec![0x01u8, 0x01u8, 0x23, 0x45, 0x67], vec![0xafu8, 0x33]), - (vec![0x01u8, 0x81u8, 0x23], vec![0x01u8, 0x25]), - (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), + (vec![0x0u8], vec![4, 32]), ], &[ - (vec![0x01u8, 0x01u8, 0x23, 0x45], Some(vec![0xffu8, 0x33])), -// (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8, 0x33])), -// (vec![0x01u8, 0x81u8, 0x23], Some(vec![0x01u8, 0x35])), -// (vec![0x01u8, 0x81u8, 0x23], None), -// (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), + (vec![0x04u8], Some(vec![0xffu8, 0x33])), + (vec![32u8], Some(vec![0xffu8, 0x33])), ], ); } + #[test] fn dummy2() { compare_with_triedbmut( @@ -1021,6 +1018,20 @@ mod tests { ); } + #[test] + fn dummy5() { + compare_with_triedbmut( + &[ + (vec![0u8], vec![255, 209]), + (vec![4u8], vec![0, 255]), + ], + &[ + (vec![0u8], None), + (vec![209], Some(vec![0, 0])), + ], + ); + } + #[test] fn chained_fuse() { compare_with_triedbmut( diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 6cffece7..d96e1e9e 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -358,6 +358,19 @@ impl Node { pub fn new_leaf(prefix: NibbleSlice, value: &[u8]) -> Self { Node::Leaf(prefix.to_stored(), value.into()) } + + // TODO rename to empty branch, branch placeholder or something + pub fn new_branch(prefix: NibbleSlice) -> Self { + let children = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + + Node::NibbledBranch(prefix.to_stored(), children, None) + } + } impl, SH: AsRef<[u8]>> Node { From b5773c29e235f438a4d5bb64d5c2c1f0a115ae40 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 12 Dec 2019 21:31:43 +0100 Subject: [PATCH 032/118] actually a stack. --- trie-db/fuzz/src/lib.rs | 12 ++++++------ trie-db/src/traverse.rs | 18 +++++++++++------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 49c6537f..1b3e7835 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -227,19 +227,19 @@ println!("{}: {:?}", data.len(), data); #[test] fn test() { let tests = [ -// vec![0x0,0x84,0xff,0xfb,0x7f,0xff,0xff,0xff,0xff,0x7f,0x70,0xff,0xff,0x7f,0x72,0xfd,0xc3,0x0,0x4,0xfb,0xff,0x10,0x10,0x10,0x10,0x10,0x9a,0x4], -/* vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], + vec![0x0,0x84,0xff,0xfb,0x7f,0xff,0xff,0xff,0xff,0x7f,0x70,0xff,0xff,0x7f,0x72,0xfd,0xc3,0x0,0x4,0xfb,0xff,0x10,0x10,0x10,0x10,0x10,0x9a,0x4], + vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], vec![0x0,0x80,0x0,0xd1,0x0,0x0,0x9a,0x4,0x4,0x0,0x0,0xc], vec![0x0,0x0,0xff,0xff,0x0,0x0,0x4,0x8d,0x87,0xd1], vec![0x0,0x0,0x0,0x0,0x4,0x0,0xff,0xd1,0x0,0x0,0xfe,0x0], vec![0x0,0x0,0xfe,0x0,0xff,0xff,0xd1,0xd1,0x27,0x0], - vec![0x0,0xfe,0x41,0x0,0x0,0x80,0x0,0x0,0xff],*/ + vec![0x0,0xfe,0x41,0x0,0x0,0x80,0x0,0x0,0xff], vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], -// vec![0x0,0x0,0x0,0x0,0xfe,0xff,0xff,0xd1,0x27], -/* vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], + vec![0x0,0x0,0x0,0x0,0xfe,0xff,0xff,0xd1,0x27], + vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], vec![0x0,0x0,0x0,0x0,0x8d,0x4], - vec![0x0,0x0,0x4,0x8d,0x8d,0x4],*/ + vec![0x0,0x0,0x4,0x8d,0x8d,0x4], ]; for v in tests.iter() { fuzz_batch_update(&v[..]) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index aca483a0..4d88184a 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -32,6 +32,10 @@ use crate::NodeCodec; use ::core_::cmp::*; use ::core_::mem; use elastic_array::ElasticArray36; +//#[cfg(feature = "std")] +//use std::collections::VecDeque; +//#[cfg(not(feature = "std"))] +//use alloc::collections::vec_deque::VecDeque; // TODO make it deletion aware : do not stack unchanged key and pass them // to exit when not needed. @@ -387,7 +391,8 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let mut k: Option = None; - let mut split_child: Option> = None; + // TODO smal child that ? + let mut split_child: Vec> = Default::default(); let mut limit_common = usize::max_value(); for (next_k, v) in elements.into_iter() { if let Some(previous_key) = k { @@ -443,11 +448,11 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if dest_depth > current.depth { let next_index = dest.at(current.depth); if current.split_child { - if next_index == split_child.as_ref().map(|c| c.parent_index) + if next_index == split_child.last().map(|c| c.parent_index) .expect("split child set before") { current.split_child = false; stack.push(current); - current = split_child.take() + current = split_child.pop() .expect("split child set before"); continue; } @@ -538,8 +543,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( limit_common = target_common_depth; }, (_, Some(split)) => { - assert!(split_child.is_none()); // TODO change to debug assert later or stack it - split_child = Some(split); + split_child.push(split); continue; }, _ => (), @@ -579,7 +583,7 @@ fn align_node<'a, T, K, V, S, B, F>( callback: &mut F, branch: &mut StackedItem, key: &[u8], - split_child: &mut Option>, + split_child: &mut Vec>, ) -> Result<(), TrieHash, CError> where T: TrieLayout, @@ -591,7 +595,7 @@ fn align_node<'a, T, K, V, S, B, F>( { if branch.split_child { branch.split_child = false; - let mut child = split_child.take() + let mut child = split_child.pop() .expect("trie correct parsing ensure it is set"); align_node(db, callback, &mut child, key, split_child)?; let handle = callback.exit( From cce1c56fc10ec186b838e771883b8bd4c73f4e1c Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 12 Dec 2019 21:33:32 +0100 Subject: [PATCH 033/118] looks like it finally fuzz correctly. --- trie-db/fuzz/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 1b3e7835..f2763d71 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -177,7 +177,7 @@ pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { pub fn fuzz_batch_update(input: &[u8]) { let data = fuzz_to_data(input); let data = fuzz_removal(data); -println!("{}: {:?}", data.len(), data); +//println!("{}: {:?}", data.len(), data); let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { @@ -215,7 +215,7 @@ println!("{}: {:?}", data.len(), data); initial_root.clone(), None, ); - println!("{:?}", sorted_data); +//println!("{:?}", sorted_data); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); From 9368c287c72904712484dbf5e7e4fff1e5d0c543 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 09:22:36 +0100 Subject: [PATCH 034/118] checkpoint on failing prefix(need to be stored with branch partial on adding split info) --- trie-db/fuzz/src/lib.rs | 5 +++-- trie-db/src/traverse.rs | 28 ++++++++++++++++------------ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index f2763d71..6d198992 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -177,7 +177,7 @@ pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { pub fn fuzz_batch_update(input: &[u8]) { let data = fuzz_to_data(input); let data = fuzz_removal(data); -//println!("{}: {:?}", data.len(), data); +println!("{}: {:?}", data.len(), data); let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { @@ -215,7 +215,7 @@ pub fn fuzz_batch_update(input: &[u8]) { initial_root.clone(), None, ); -//println!("{:?}", sorted_data); +println!("{:?}", sorted_data); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); @@ -227,6 +227,7 @@ pub fn fuzz_batch_update(input: &[u8]) { #[test] fn test() { let tests = [ + vec![0x7f,0x2b,0x4,0x3a,0x89,0xfb,0x0,0x2e,0x70,0x0,0x0,0x2e,0x2,0x0,0x0,0x0,0x41,0xd1,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xff,0xcd,0x72,0xfb,0x7f,0xc3,0x0,0x9a,0xff,0xff,0xff,0x72,0xfb,0x7f,0xff,0x0,0x0,0x0,0x0,0x0,0x82,0x82,0x81,0x81,0x29,0x8f,0x7d,0x42,0x12,0xf7,0xb4,0x77,0xd6,0x65,0x91,0xff,0x96,0xa9,0xe0,0x64,0xbc,0xc9,0x8a,0x70,0xc,0x4,0x0,0x0,0xc3,0x0,0x0,0x0,0x0,0x0,0x0], vec![0x0,0x84,0xff,0xfb,0x7f,0xff,0xff,0xff,0xff,0x7f,0x70,0xff,0xff,0x7f,0x72,0xfd,0xc3,0x0,0x4,0xfb,0xff,0x10,0x10,0x10,0x10,0x10,0x9a,0x4], vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], vec![0x0,0x80,0x0,0xd1,0x0,0x0,0x9a,0x4,0x4,0x0,0x0,0xc], diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 4d88184a..732daf1d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -392,7 +392,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( let mut k: Option = None; // TODO smal child that ? - let mut split_child: Vec> = Default::default(); + let mut split_child: Vec<(StackedItem, Vec)> = Default::default(); let mut limit_common = usize::max_value(); for (next_k, v) in elements.into_iter() { if let Some(previous_key) = k { @@ -448,12 +448,13 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( if dest_depth > current.depth { let next_index = dest.at(current.depth); if current.split_child { - if next_index == split_child.last().map(|c| c.parent_index) + if next_index == split_child.last().map(|(c, _)| c.parent_index) .expect("split child set before") { current.split_child = false; stack.push(current); - current = split_child.pop() + let (ch, v) = split_child.pop() .expect("split child set before"); + current = ch; continue; } } @@ -543,7 +544,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( limit_common = target_common_depth; }, (_, Some(split)) => { - split_child.push(split); + split_child.push((split, k.as_ref().to_vec())); continue; }, _ => (), @@ -583,7 +584,7 @@ fn align_node<'a, T, K, V, S, B, F>( callback: &mut F, branch: &mut StackedItem, key: &[u8], - split_child: &mut Vec>, + split_child: &mut Vec<(StackedItem, Vec)>, ) -> Result<(), TrieHash, CError> where T: TrieLayout, @@ -595,7 +596,7 @@ fn align_node<'a, T, K, V, S, B, F>( { if branch.split_child { branch.split_child = false; - let mut child = split_child.pop() + let (mut child, check) = split_child.pop() .expect("trie correct parsing ensure it is set"); align_node(db, callback, &mut child, key, split_child)?; let handle = callback.exit( @@ -923,8 +924,8 @@ mod tests { &mut batch_update, ); - //assert_eq!(batch_update.1, reference_root); - + assert_eq!(batch_update.1, reference_root); +println!("{:?}", batch_update.0); let mut batch_delta = initial_db; let r2 = batch_update.1.clone(); @@ -1026,12 +1027,15 @@ mod tests { fn dummy5() { compare_with_triedbmut( &[ - (vec![0u8], vec![255, 209]), - (vec![4u8], vec![0, 255]), + (vec![4, 58, 137, 251, 0, 46, 112, 0, 0, 46, 2, 0], vec![255, 209]), + (vec![0, 0, 65, 209, 46, 0, 0, 0, 0, 0, 0, 0], vec![0, 255]), + (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], vec![0, 255]), ], &[ - (vec![0u8], None), - (vec![209], Some(vec![0, 0])), +// (vec![4, 58, 137, 251, 0, 46, 112, 0, 0, 46, 2, 0], None), + (vec![114, 251, 127, 255, 0, 0, 0, 0, 0, 130, 130, 129, 129, 41, 143, 125, 66, 18, 247], Some(vec![0, 0])), +// (vec![128, 255, 205, 114, 251, 127, 195, 0, 154, 255, 255, 255], Some(vec![0, 0])), +// (vec![180, 119, 214, 101, 145, 255, 150, 169, 224, 100, 188, 201, 138, 112, 12, 4, 0, 0, 195, 0, 0], Some(vec![0, 0])), ], ); } From 4690664e2f10d561b29776100f81e813bc44b251 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 10:57:09 +0100 Subject: [PATCH 035/118] recursive align node requires to rebuild prefix. --- trie-db/src/traverse.rs | 88 ++++++++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 23 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 732daf1d..e5deffc3 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -272,10 +272,10 @@ pub trait ProcessStack state: TraverseState, ) -> Option>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>>; /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>); + fn exit_root(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>); /// Fetching a fuse latest, that is the latest changed value of a branch. @@ -415,9 +415,9 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( // go up if let Some(mut last) = stack.pop() { if !last.node.is_empty() { - align_node(db, callback, &mut current, previous_key.as_ref(), &mut split_child)?; + align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; if let Some(handle) = callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), + NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix).left(), current.node, current.hash.as_ref(), ) { last.node.set_handle(handle, current.parent_index); @@ -425,8 +425,8 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( current = last; } } else { - align_node(db, callback, &mut current, previous_key.as_ref(), &mut split_child)?; - callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); + align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; + callback.exit_root(EMPTY_PREFIX, current.node, current.hash.as_ref()); return Ok(()); } } @@ -512,7 +512,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( // TODO runing into this is not expected if let Some(mut prev) = stack.pop() { callback.exit( - NibbleSlice::new_offset(k.as_ref(), current.depth_prefix), + NibbleSlice::new_offset(k.as_ref(), current.depth_prefix).left(), current.node, current.hash.as_ref(), ).expect("no new node on empty"); prev.node.set_handle(None, current.parent_index); @@ -561,9 +561,9 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( // go up while let Some(mut last) = stack.pop() { if !last.node.is_empty() { - align_node(db, callback, &mut current, previous_key.as_ref(), &mut split_child)?; + align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; if let Some(handle) = callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix), + NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix).left(), current.node, current.hash.as_ref(), ) { last.node.set_handle(handle, current.parent_index); @@ -571,8 +571,8 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( current = last; } } - align_node(db, callback, &mut current, previous_key.as_ref(), &mut split_child)?; - callback.exit_root(NibbleSlice::new(&[]), current.node, current.hash.as_ref()); + align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; + callback.exit_root(EMPTY_PREFIX, current.node, current.hash.as_ref()); return Ok(()); } @@ -584,6 +584,7 @@ fn align_node<'a, T, K, V, S, B, F>( callback: &mut F, branch: &mut StackedItem, key: &[u8], + mut prefix: Option<&mut NibbleVec>, split_child: &mut Vec<(StackedItem, Vec)>, ) -> Result<(), TrieHash, CError> where @@ -594,15 +595,33 @@ fn align_node<'a, T, K, V, S, B, F>( B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, F: ProcessStack, { + let init_prefix_len = prefix.as_ref().map(|p| p.len()) + .unwrap_or(0); if branch.split_child { branch.split_child = false; let (mut child, check) = split_child.pop() .expect("trie correct parsing ensure it is set"); - align_node(db, callback, &mut child, key, split_child)?; + let mut build_prefix: NibbleVec; + // Rebuild the right prefix by removing last nibble and adding parent child. + let prefix: &mut NibbleVec = if let Some(prefix) = prefix.as_mut() { + child.node.partial().map(|p| { + prefix.append_partial(p.right()); + }); + prefix + } else { + build_prefix = NibbleVec::from(key, child.depth_prefix - 1); + &mut build_prefix + }; + prefix.push(child.parent_index); + let len_prefix = prefix.len(); + align_node(db, callback, &mut child, key, Some(prefix), split_child)?; + prefix.drop_lasts(prefix.len() - len_prefix); let handle = callback.exit( - NibbleSlice::new_offset(key, child.depth_prefix), + prefix.as_prefix(), child.node, child.hash.as_ref(), ).expect("split child is always a changed node"); + // TODO if it is single handle, then fix node will reaccess that: + // that is one hash creation here and one access then deletion afterward. branch.node.set_handle(handle, child.parent_index); } if let Some(fuse_index) = branch.node.fix_node() { @@ -610,8 +629,22 @@ fn align_node<'a, T, K, V, S, B, F>( Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + let mut build_prefix: NibbleVec; + let prefix: &mut NibbleVec = if let Some(prefix) = prefix.as_mut() { + let len_prefix = prefix.len(); + if len_prefix > init_prefix_len { + prefix.drop_lasts(len_prefix - init_prefix_len); + } + branch.node.partial().map(|p| { + prefix.append_partial(p.right()); + }); + prefix + } else { + build_prefix = NibbleVec::from(key, branch.depth); + &mut build_prefix + }; + // TODO conversion to NibbleVec is slow - let mut prefix: NibbleVec = NibbleVec::from(key, branch.depth); prefix.push(fuse_index); let prefix = prefix.as_prefix(); if let Some(node_encoded) = callback.fuse_latest_changed( @@ -643,7 +676,7 @@ fn align_node<'a, T, K, V, S, B, F>( }; // register delete callback.exit( - NibbleSlice::new_offset(key, branch.depth), + NibbleSlice::new_offset(key, branch.depth).left(), StackedNode::Deleted(Default::default()), hash.as_ref(), ).expect("No new node on deleted allowed"); @@ -780,7 +813,7 @@ impl ProcessStack for BatchUpdate> } } - fn exit(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>> { match stacked { StackedNode::Changed(node, _) => Some(Some({ @@ -796,16 +829,16 @@ impl ProcessStack for BatchUpdate> // register latest change self.2 = Some(self.0.len()); // costy clone (could get read from here) - self.0.push((prefix.left_owned(), hash.clone(), Some(encoded), true)); + self.0.push((owned_prefix(&prefix), hash.clone(), Some(encoded), true)); if let Some(h) = prev_hash { - self.0.push((prefix.left_owned(), h.clone(), None, true)); + self.0.push((owned_prefix(&prefix), h.clone(), None, true)); } OwnedNodeHandle::Hash(hash) } })), StackedNode::Deleted(..) => { if let Some(h) = prev_hash { - self.0.push((prefix.left_owned(), h.clone(), None, true)); + self.0.push((owned_prefix(&prefix), h.clone(), None, true)); } Some(None) }, @@ -813,16 +846,16 @@ impl ProcessStack for BatchUpdate> } } - fn exit_root(&mut self, prefix: NibbleSlice, stacked: StackedNode, prev_hash: Option<&TrieHash>) { + fn exit_root(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) { match stacked { s@StackedNode::Deleted(..) | s@StackedNode::Changed(..) => { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); self.1 = hash.clone(); - self.0.push((prefix.left_owned(), hash, Some(encoded), true)); + self.0.push((owned_prefix(&prefix), hash, Some(encoded), true)); if let Some(h) = prev_hash { - self.0.push((prefix.left_owned(), h.clone(), None, true)); + self.0.push((owned_prefix(&prefix), h.clone(), None, true)); } }, _ => (), @@ -844,6 +877,15 @@ impl ProcessStack for BatchUpdate> } } + +fn owned_prefix(prefix: &Prefix) -> (ElasticArray36, Option) { + (prefix.0.into(), prefix.1) +} + +fn from_owned_prefix(prefix: &(ElasticArray36, Option)) -> Prefix { + (&prefix.0[..], prefix.1) +} + #[cfg(test)] mod tests { use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, TrieMut, NodeCodec, @@ -1032,7 +1074,7 @@ println!("{:?}", batch_update.0); (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], vec![0, 255]), ], &[ -// (vec![4, 58, 137, 251, 0, 46, 112, 0, 0, 46, 2, 0], None), + (vec![4, 58, 137, 251, 0, 46, 112, 0, 0, 46, 2, 0], None), (vec![114, 251, 127, 255, 0, 0, 0, 0, 0, 130, 130, 129, 129, 41, 143, 125, 66, 18, 247], Some(vec![0, 0])), // (vec![128, 255, 205, 114, 251, 127, 195, 0, 154, 255, 255, 255], Some(vec![0, 0])), // (vec![180, 119, 214, 101, 145, 255, 150, 169, 224, 100, 188, 201, 138, 112, 12, 4, 0, 0, 195, 0, 0], Some(vec![0, 0])), From 7d30e9310780352eba000d420922cc8009b3f1f8 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 11:05:20 +0100 Subject: [PATCH 036/118] test missing fuzz on bigger contents. --- trie-db/fuzz/fuzz_targets/batch_update.rs | 2 +- trie-db/fuzz/src/lib.rs | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/trie-db/fuzz/fuzz_targets/batch_update.rs b/trie-db/fuzz/fuzz_targets/batch_update.rs index 2c71ddde..db73eeb8 100644 --- a/trie-db/fuzz/fuzz_targets/batch_update.rs +++ b/trie-db/fuzz/fuzz_targets/batch_update.rs @@ -4,5 +4,5 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - trie_db_fuzz::fuzz_batch_update(data); + trie_db_fuzz::fuzz_batch_update(data, |_v| ()); }); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 6d198992..53d55c68 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -174,9 +174,13 @@ pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { compare_no_extension_insert_remove(data, memdb); } -pub fn fuzz_batch_update(input: &[u8]) { +pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { let data = fuzz_to_data(input); - let data = fuzz_removal(data); + let mut data = fuzz_removal(data); + for i in data.iter_mut() { + build_val(&mut i.2); + } + let data = data; println!("{}: {:?}", data.len(), data); let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); @@ -243,6 +247,7 @@ fn test() { vec![0x0,0x0,0x4,0x8d,0x8d,0x4], ]; for v in tests.iter() { - fuzz_batch_update(&v[..]) + fuzz_batch_update(&v[..], |_v| ()); + fuzz_batch_update(&v[..], |v| v.extend(&[4u8; 32])); } } From e60f218675b3b2b623e30edaccb655aef16ddbbb Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 11:40:00 +0100 Subject: [PATCH 037/118] yet another prefix fix --- trie-db/fuzz/src/lib.rs | 5 +++-- trie-db/src/traverse.rs | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 53d55c68..25fcc86f 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -181,7 +181,7 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { build_val(&mut i.2); } let data = data; -println!("{}: {:?}", data.len(), data); +//println!("{}: {:?}", data.len(), data); let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { @@ -219,7 +219,7 @@ println!("{}: {:?}", data.len(), data); initial_root.clone(), None, ); -println!("{:?}", sorted_data); +//println!("{:?}", sorted_data); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); @@ -231,6 +231,7 @@ println!("{:?}", sorted_data); #[test] fn test() { let tests = [ + vec![0x43,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x40,0x2,0x4,0x0,0x0,0xc7,0x8d,0x0,0x0,0xe0], vec![0x7f,0x2b,0x4,0x3a,0x89,0xfb,0x0,0x2e,0x70,0x0,0x0,0x2e,0x2,0x0,0x0,0x0,0x41,0xd1,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xff,0xcd,0x72,0xfb,0x7f,0xc3,0x0,0x9a,0xff,0xff,0xff,0x72,0xfb,0x7f,0xff,0x0,0x0,0x0,0x0,0x0,0x82,0x82,0x81,0x81,0x29,0x8f,0x7d,0x42,0x12,0xf7,0xb4,0x77,0xd6,0x65,0x91,0xff,0x96,0xa9,0xe0,0x64,0xbc,0xc9,0x8a,0x70,0xc,0x4,0x0,0x0,0xc3,0x0,0x0,0x0,0x0,0x0,0x0], vec![0x0,0x84,0xff,0xfb,0x7f,0xff,0xff,0xff,0xff,0x7f,0x70,0xff,0xff,0x7f,0x72,0xfd,0xc3,0x0,0x4,0xfb,0xff,0x10,0x10,0x10,0x10,0x10,0x9a,0x4], vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index e5deffc3..9ebb694d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -635,15 +635,15 @@ fn align_node<'a, T, K, V, S, B, F>( if len_prefix > init_prefix_len { prefix.drop_lasts(len_prefix - init_prefix_len); } - branch.node.partial().map(|p| { - prefix.append_partial(p.right()); - }); + prefix } else { - build_prefix = NibbleVec::from(key, branch.depth); + build_prefix = NibbleVec::from(key, branch.depth_prefix); &mut build_prefix }; - + branch.node.partial().map(|p| { + prefix.append_partial(p.right()); + }); // TODO conversion to NibbleVec is slow prefix.push(fuse_index); let prefix = prefix.as_prefix(); From 5b1b2c684fb5419b7bbbb795862accd6d28cae64 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 14:48:18 +0100 Subject: [PATCH 038/118] latest change value is a bad approach, running a heavy cost variant, but this should be drop in favor of calling exit for child and kepping 16 current reference. --- trie-db/fuzz/src/lib.rs | 4 ++-- trie-db/src/traverse.rs | 48 +++++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 25fcc86f..bec7c863 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -181,7 +181,7 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { build_val(&mut i.2); } let data = data; -//println!("{}: {:?}", data.len(), data); +println!("{}: {:?}", data.len(), data); let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { @@ -219,7 +219,7 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { initial_root.clone(), None, ); -//println!("{:?}", sorted_data); +println!("{:?}", sorted_data); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 9ebb694d..cb7d6ef7 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -284,6 +284,8 @@ pub trait ProcessStack /// This is only needed if changes are made by process stack. /// Access to this method means that the node need to be remove or invalidate (this is handled /// by this method (no call to exit on it)). + /// TODO EMCH this method should be removed, by only calling exit for child of branch when going + /// up (keeping a reference to each child instead of current only!!). fn fuse_latest_changed(&mut self, prefix: Prefix, hash: &TrieHash) -> Option<&[u8]>; } @@ -604,15 +606,18 @@ fn align_node<'a, T, K, V, S, B, F>( let mut build_prefix: NibbleVec; // Rebuild the right prefix by removing last nibble and adding parent child. let prefix: &mut NibbleVec = if let Some(prefix) = prefix.as_mut() { - child.node.partial().map(|p| { - prefix.append_partial(p.right()); - }); prefix } else { - build_prefix = NibbleVec::from(key, child.depth_prefix - 1); + build_prefix = NibbleVec::from(key, branch.depth_prefix); &mut build_prefix }; + branch.node.partial().map(|p| { + prefix.append_partial(p.right()); + }); prefix.push(child.parent_index); + child.node.partial().map(|p| { + prefix.append_partial(p.right()); + }); let len_prefix = prefix.len(); align_node(db, callback, &mut child, key, Some(prefix), split_child)?; prefix.drop_lasts(prefix.len() - len_prefix); @@ -635,7 +640,6 @@ fn align_node<'a, T, K, V, S, B, F>( if len_prefix > init_prefix_len { prefix.drop_lasts(len_prefix - init_prefix_len); } - prefix } else { build_prefix = NibbleVec::from(key, branch.depth_prefix); @@ -863,17 +867,14 @@ impl ProcessStack for BatchUpdate> } fn fuse_latest_changed(&mut self, prefix: Prefix, hash: &TrieHash) -> Option<&[u8]> { - // consume anyway. - let last = self.2.take(); - if let Some(latest) = last { - let stored_slice = (&(self.0[latest].0).0[..], (self.0[latest].0).1); + for latest in 0..self.0.len() { + let stored_slice = from_owned_prefix(&self.0[latest].0); if hash == &self.0[latest].1 && prefix == stored_slice { self.0[latest].3 = false; - self.0[latest].2.as_ref().map(|s| &s[..]) - } else { - None + return self.0[latest].2.as_ref().map(|s| &s[..]); } - } else { None } + } + None } } @@ -1066,18 +1067,23 @@ println!("{:?}", batch_update.0); } #[test] - fn dummy5() { + fn single_latest_change_value_does_not_work() { compare_with_triedbmut( &[ - (vec![4, 58, 137, 251, 0, 46, 112, 0, 0, 46, 2, 0], vec![255, 209]), - (vec![0, 0, 65, 209, 46, 0, 0, 0, 0, 0, 0, 0], vec![0, 255]), - (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], vec![0, 255]), + (vec![0, 0, 0, 0], vec![255;32]), + (vec![0, 0, 0, 3], vec![5; 32]), + (vec![0, 0, 6, 0], vec![6; 32]), + (vec![0, 0, 0, 170], vec![1; 32]), + (vec![0, 0, 73, 0], vec![2; 32]), + (vec![0, 0, 0, 0], vec![3; 32]), + (vec![0, 199, 141, 0], vec![4; 32]), ], &[ - (vec![4, 58, 137, 251, 0, 46, 112, 0, 0, 46, 2, 0], None), - (vec![114, 251, 127, 255, 0, 0, 0, 0, 0, 130, 130, 129, 129, 41, 143, 125, 66, 18, 247], Some(vec![0, 0])), -// (vec![128, 255, 205, 114, 251, 127, 195, 0, 154, 255, 255, 255], Some(vec![0, 0])), -// (vec![180, 119, 214, 101, 145, 255, 150, 169, 224, 100, 188, 201, 138, 112, 12, 4, 0, 0, 195, 0, 0], Some(vec![0, 0])), + (vec![0, 0, 0, 0], Some(vec![0; 32])), + (vec![0, 0, 199, 141], Some(vec![0; 32])), + (vec![0, 199, 141, 0], None), + (vec![12, 0, 128, 0, 0, 0, 0, 0, 0, 4, 64, 2, 4], Some(vec![0; 32])), + (vec![91], None), ], ); } From 444b1bdb8298b23dfdcdc34387c9f5492f1b0ed0 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 15:14:01 +0100 Subject: [PATCH 039/118] fix prefix again --- trie-db/fuzz/src/lib.rs | 5 +++-- trie-db/src/traverse.rs | 22 ++++++++++++++++++++-- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index bec7c863..7667b08b 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -181,7 +181,7 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { build_val(&mut i.2); } let data = data; -println!("{}: {:?}", data.len(), data); +//println!("{}: {:?}", data.len(), data); let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); let mut root = Default::default(); { @@ -219,7 +219,7 @@ println!("{}: {:?}", data.len(), data); initial_root.clone(), None, ); -println!("{:?}", sorted_data); +//println!("{:?}", sorted_data); reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), &mut batch_update); @@ -231,6 +231,7 @@ println!("{:?}", sorted_data); #[test] fn test() { let tests = [ + vec![0xff,0xd1,0x0,0x90,0x40,0xd4,0x8d,0x1,0x0,0x0,0xff,0x90,0x40,0xd4,0x8d,0x1,0x0,0x8d,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x6,0x8,0x15,0x1,0x4,0x0,0x8d,0x87,0xcf,0x0,0x3f,0xcb,0xd8,0xb9,0xa2,0x4d,0x9a,0xd6,0xd2,0x0,0x0,0x0,0x0,0x80,0x0,0x6,0x8,0x15,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xc7,0xd7,0xd7,0xfb,0x7f,0x83,0x3,0x37,0x37,0x37,0xb2,0xa8,0xb,0xf5,0x5a,0x50,0xb6,0x0,0xff,0x17,0x21,0x0], vec![0x43,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x40,0x2,0x4,0x0,0x0,0xc7,0x8d,0x0,0x0,0xe0], vec![0x7f,0x2b,0x4,0x3a,0x89,0xfb,0x0,0x2e,0x70,0x0,0x0,0x2e,0x2,0x0,0x0,0x0,0x41,0xd1,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xff,0xcd,0x72,0xfb,0x7f,0xc3,0x0,0x9a,0xff,0xff,0xff,0x72,0xfb,0x7f,0xff,0x0,0x0,0x0,0x0,0x0,0x82,0x82,0x81,0x81,0x29,0x8f,0x7d,0x42,0x12,0xf7,0xb4,0x77,0xd6,0x65,0x91,0xff,0x96,0xa9,0xe0,0x64,0xbc,0xc9,0x8a,0x70,0xc,0x4,0x0,0x0,0xc3,0x0,0x0,0x0,0x0,0x0,0x0], vec![0x0,0x84,0xff,0xfb,0x7f,0xff,0xff,0xff,0xff,0x7f,0x70,0xff,0xff,0x7f,0x72,0xfd,0xc3,0x0,0x4,0xfb,0xff,0x10,0x10,0x10,0x10,0x10,0x9a,0x4], diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index cb7d6ef7..abe2e986 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -615,9 +615,9 @@ fn align_node<'a, T, K, V, S, B, F>( prefix.append_partial(p.right()); }); prefix.push(child.parent_index); - child.node.partial().map(|p| { +/* child.node.partial().map(|p| { prefix.append_partial(p.right()); - }); + });*/ let len_prefix = prefix.len(); align_node(db, callback, &mut child, key, Some(prefix), split_child)?; prefix.drop_lasts(prefix.len() - len_prefix); @@ -648,6 +648,7 @@ fn align_node<'a, T, K, V, S, B, F>( branch.node.partial().map(|p| { prefix.append_partial(p.right()); }); + // TODO conversion to NibbleVec is slow prefix.push(fuse_index); let prefix = prefix.as_prefix(); @@ -1066,6 +1067,23 @@ println!("{:?}", batch_update.0); ); } + #[test] + fn dummy5() { + compare_with_triedbmut( + &[ + (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], vec![255, 255]), + (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], vec![0, 4]), + (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 208, 208, 208, 208, 208, 208], vec![1, 2]), + ], + &[ + (vec![0, 6, 8, 21, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 199, 215], Some(vec![4, 251])), + (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], None), + (vec![141, 135, 207, 0, 63, 203, 216, 185, 162, 77, 154, 214, 210, 0, 0, 0, 0, 128], Some(vec![49, 251])), + (vec![208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 6, 8, 21, 1, 4, 0], Some(vec![4, 21])), + ], + ); + } + #[test] fn single_latest_change_value_does_not_work() { compare_with_triedbmut( From d71fd2e02b3e5760e7369be7f74cc7b20e2e0483 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 15:34:50 +0100 Subject: [PATCH 040/118] fix delete when rec --- trie-db/fuzz/src/lib.rs | 1 + trie-db/src/traverse.rs | 34 +++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 7667b08b..96d73f80 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -231,6 +231,7 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { #[test] fn test() { let tests = [ + vec![0x0,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x1,0x8d,0x2c,0xd4,0x0,0x0,0x33,0x8a,0x20,0x80,0x9a,0x2c,0xd4,0x0,0x0,0x33,0x8a,0xff,0x8], vec![0xff,0xd1,0x0,0x90,0x40,0xd4,0x8d,0x1,0x0,0x0,0xff,0x90,0x40,0xd4,0x8d,0x1,0x0,0x8d,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x6,0x8,0x15,0x1,0x4,0x0,0x8d,0x87,0xcf,0x0,0x3f,0xcb,0xd8,0xb9,0xa2,0x4d,0x9a,0xd6,0xd2,0x0,0x0,0x0,0x0,0x80,0x0,0x6,0x8,0x15,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xc7,0xd7,0xd7,0xfb,0x7f,0x83,0x3,0x37,0x37,0x37,0xb2,0xa8,0xb,0xf5,0x5a,0x50,0xb6,0x0,0xff,0x17,0x21,0x0], vec![0x43,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x40,0x2,0x4,0x0,0x0,0xc7,0x8d,0x0,0x0,0xe0], vec![0x7f,0x2b,0x4,0x3a,0x89,0xfb,0x0,0x2e,0x70,0x0,0x0,0x2e,0x2,0x0,0x0,0x0,0x41,0xd1,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xff,0xcd,0x72,0xfb,0x7f,0xc3,0x0,0x9a,0xff,0xff,0xff,0x72,0xfb,0x7f,0xff,0x0,0x0,0x0,0x0,0x0,0x82,0x82,0x81,0x81,0x29,0x8f,0x7d,0x42,0x12,0xf7,0xb4,0x77,0xd6,0x65,0x91,0xff,0x96,0xa9,0xe0,0x64,0xbc,0xc9,0x8a,0x70,0xc,0x4,0x0,0x0,0xc3,0x0,0x0,0x0,0x0,0x0,0x0], diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index abe2e986..32a52921 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -630,11 +630,11 @@ fn align_node<'a, T, K, V, S, B, F>( branch.node.set_handle(handle, child.parent_index); } if let Some(fuse_index) = branch.node.fix_node() { + let mut build_prefix: NibbleVec; let (child, hash) = match branch.node.child(fuse_index) { Some(NodeHandle::Hash(handle_hash)) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - let mut build_prefix: NibbleVec; let prefix: &mut NibbleVec = if let Some(prefix) = prefix.as_mut() { let len_prefix = prefix.len(); if len_prefix > init_prefix_len { @@ -667,7 +667,7 @@ fn align_node<'a, T, K, V, S, B, F>( db, &hash, prefix, - )?, Some(hash)) + )?, Some((hash, prefix))) } }, Some(NodeHandle::Inline(node_encoded)) => { @@ -679,12 +679,14 @@ fn align_node<'a, T, K, V, S, B, F>( }, None => unreachable!("correct index used"), }; - // register delete - callback.exit( - NibbleSlice::new_offset(key, branch.depth).left(), - StackedNode::Deleted(Default::default()), - hash.as_ref(), - ).expect("No new node on deleted allowed"); + if let Some((hash, prefix)) = hash { + // register delete + callback.exit( + NibbleSlice::new_offset(key, branch.depth).left(), + StackedNode::Deleted(Default::default()), + Some(&hash), + ).expect("No new node on deleted allowed"); + } branch.depth += branch.node.fuse_child(child, fuse_index); } @@ -1068,7 +1070,7 @@ println!("{:?}", batch_update.0); } #[test] - fn dummy5() { + fn dummy6() { compare_with_triedbmut( &[ (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], vec![255, 255]), @@ -1084,6 +1086,20 @@ println!("{:?}", batch_update.0); ); } + #[test] + fn dummy5() { + compare_with_triedbmut( + &[ + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + ], + &[ + (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), + (vec![128], Some(vec![49, 251])), + ], + ); + } + #[test] fn single_latest_change_value_does_not_work() { compare_with_triedbmut( From b91ef04dc8718a1a71c3d4da7e65ab578dd15b51 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 15:38:45 +0100 Subject: [PATCH 041/118] missing no_std imports --- trie-db/src/node.rs | 2 ++ trie-db/src/traverse.rs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index a0f8ebbe..d2ed8454 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -23,6 +23,8 @@ use core_::borrow::Borrow; use core_::ops::Range; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; /// Partial node key type: offset and owned value of a nibbleslice. /// Offset is applied on first byte of array (bytes are right aligned). diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 32a52921..0a0f1674 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -25,6 +25,10 @@ use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; use std::borrow::Borrow; #[cfg(not(feature = "std"))] use core::borrow::Borrow; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; use crate::{TrieLayout, TrieHash, CError, Result, TrieError}; use crate::DBValue; use hash_db::{HashDB, Prefix, EMPTY_PREFIX, Hasher}; From e926664091b4ace729f1c24693e773977762ec86 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 13 Dec 2019 19:47:15 +0100 Subject: [PATCH 042/118] small bench change --- trie-db/benches/bench.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index f73afca5..ca95b8c7 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -521,10 +521,10 @@ fn trie_mut_same_key_batch(c: &mut Criterion) { let mut mdb = db.clone(); // sort let data: std::collections::BTreeSet> = data.iter().map(|(a, _b)| a.clone()).collect(); - let mut batch_update = reference_trie::BatchUpdate(Default::default()); + let mut batch_update = reference_trie::BatchUpdate(Default::default(), root.clone(), None); reference_trie::trie_traverse_key_no_extension_build( &mut mdb, &root, data.iter().map(|a| (a, Some(&a[..]))), &mut batch_update); - // rem root del + // rem root del TODO use returned root. batch_update.0.pop(); assert!(batch_update.0.last().unwrap().1 != root); })); From 074e18abf013052b717686bf41da2b71258a6a7f Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 6 Jan 2020 16:45:03 +0100 Subject: [PATCH 043/118] non mut backend db --- trie-db/src/traverse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index bb59743b..12af8e4d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -699,7 +699,7 @@ fn align_node<'a, T, K, V, S, B, F>( /// Fetch a node by hash, do not cache it. fn fetch>( - db: &mut dyn HashDB, + db: &dyn HashDB, hash: &TrieHash, key: Prefix, ) -> Result, TrieHash, CError> { From bf84a804c888c693b40c6fcc2e9d2b79a524746b Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 6 Jan 2020 16:47:48 +0100 Subject: [PATCH 044/118] correct rem mut from db --- trie-db/src/traverse.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 12af8e4d..ed421a0a 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -357,7 +357,7 @@ fn descend_terminal( /// The main entry point for traversing a trie by a set of keys. pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( - db: &'a mut dyn HashDB, + db: &'a dyn HashDB, root_hash: &'a TrieHash, elements: I, callback: &mut F, @@ -585,7 +585,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } fn align_node<'a, T, K, V, S, B, F>( - db: &'a mut dyn HashDB, + db: &'a dyn HashDB, callback: &mut F, branch: &mut StackedItem, key: &[u8], From d0ccce1ceb41d04c210a6af21f88892db2906efc Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 6 Jan 2020 18:12:41 +0100 Subject: [PATCH 045/118] Use hashdbref. --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/traverse.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index fb223c84..a12e31ac 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1190,7 +1190,7 @@ pub fn compare_no_extension_insert_remove( pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( // TODO db to non mutable - db: &'a mut dyn hash_db::HashDB, + db: &'a mut dyn hash_db::HashDBRef, root: &'a [u8; 32], elements: I, batch_update: &'a mut BatchUpdate<::Out>, diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index ed421a0a..dad3107b 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -31,7 +31,7 @@ use alloc::vec::Vec; use alloc::boxed::Box; use crate::{TrieLayout, TrieHash, CError, Result, TrieError}; use crate::{DBValue, nibble::BackingByteVec}; -use hash_db::{HashDB, Prefix, EMPTY_PREFIX, Hasher}; +use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX, Hasher}; use crate::NodeCodec; use ::core_::cmp::*; use ::core_::mem; @@ -357,7 +357,7 @@ fn descend_terminal( /// The main entry point for traversing a trie by a set of keys. pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( - db: &'a dyn HashDB, + db: &'a dyn HashDBRef, root_hash: &'a TrieHash, elements: I, callback: &mut F, @@ -585,7 +585,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } fn align_node<'a, T, K, V, S, B, F>( - db: &'a dyn HashDB, + db: &'a dyn HashDBRef, callback: &mut F, branch: &mut StackedItem, key: &[u8], @@ -699,7 +699,7 @@ fn align_node<'a, T, K, V, S, B, F>( /// Fetch a node by hash, do not cache it. fn fetch>( - db: &dyn HashDB, + db: &dyn HashDBRef, hash: &TrieHash, key: Prefix, ) -> Result, TrieHash, CError> { @@ -904,7 +904,7 @@ mod tests { use memory_db::{MemoryDB, PrefixedKey}; use keccak_hasher::KeccakHasher; use crate::{DBValue, nibble::BackingByteVec}; - use hash_db::{EMPTY_PREFIX, Prefix, HashDB}; + use hash_db::{EMPTY_PREFIX, Prefix, HashDBRef, HashDB}; use crate::triedbmut::tests::populate_trie_no_extension; type H256 = ::Out; @@ -1008,7 +1008,7 @@ println!("{:?}", batch_update.0); // assert_eq!(format!("{:?}", t1), format!("{:?}", t2)); - panic!("!!END!!"); +// panic!("!!END!!"); } From b5e5bd296caf2a14d6d9aadfbf69d53d2bdd5a65 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 8 Jan 2020 20:07:15 +0100 Subject: [PATCH 046/118] start refacto logic to stackednode for readability --- trie-db/src/node.rs | 11 +- trie-db/src/traverse.rs | 532 +++++++++++++++++++++++++++++++++++++-- trie-db/src/triedbmut.rs | 25 ++ 3 files changed, 549 insertions(+), 19 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 022ecebf..7df573b9 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -261,7 +261,6 @@ impl> OwnedNode { } } - /// Try to access child. pub fn child(&self, ix: u8) -> Option { match &self.plan { @@ -283,6 +282,16 @@ impl> OwnedNode { } } + /// Return number of children for this node. + pub fn number_child(&self) -> usize { + match &self.plan { + NodePlan::Leaf { .. } + | NodePlan::Empty => 0, + NodePlan::Extension { .. } => 1, + NodePlan::NibbledBranch { children, .. } + | NodePlan::Branch { children, .. } => children.len(), + } + } } impl> OwnedNode { diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index dad3107b..eb995fca 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -19,7 +19,7 @@ use crate::triedbmut::{Node, NibbleFullKey}; use crate::triedbmut::NodeHandle as NodeHandleTrieMut; -use crate::node::{OwnedNode, NodePlan, NodeHandle}; +use crate::node::{OwnedNode, NodePlan, NodeHandle, NodeKey}; use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; #[cfg(feature = "std")] use std::borrow::Borrow; @@ -61,6 +61,27 @@ pub enum StackedNode Deleted(S), } +impl StackedNode + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + fn is_deleted(&self) -> bool { + if let StackedNode::Deleted(..) = self { + true + } else { + false + } + } + fn is_unchanged(&self) -> bool { + if let StackedNode::Unchanged(..) = self { + true + } else { + false + } + } +} + pub struct StackedItem where B: Borrow<[u8]>, @@ -82,6 +103,285 @@ pub struct StackedItem pub split_child: bool, } +/// TODO use stacked item child internally +pub struct StackedItem2 + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + /// Internal node representation. + pub node: StackedNode, + /// Hash used to access this node, for inline node and + /// new nodes this is None. + pub hash: Option>, + /// Index of prefix. + pub depth_prefix: usize, + /// Depth of node, it is prefix depth and partial depth. + pub depth: usize, + /// parent index (only relevant when parent is a branch). + pub parent_index: u8, + pub split_child: Option<(Option>, Option)>, + /// Store first child, until `exit` get call, this is needed + /// to be able to fuse branch containing a single child (delay + /// `exit` call of first element after process of the second one). + /// Store child and the key use when storing (to calculate final + /// nibble, this is more memory costy than strictly necessary). + /// Note that split_child is always a first_child. + /// TODO rename to first modified child (non delete). + pub first_child: Option<(StackedItemChild, Vec)>, + /// If a two child where already asserted or the node is deleted. + pub did_first_child: bool, +} + + +/// Variant of stacked item to store first changed node. +pub struct StackedItemChild + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + /// Internal node representation. + pub node: StackedNode, + /// Hash used to access this node, for inline node and + /// new nodes this is None. + pub hash: Option>, + /// Index of prefix. + pub depth_prefix: usize, + /// Depth of node, it is prefix depth and partial depth. + pub depth: usize, + /// parent index (only relevant when parent is a branch). + pub parent_index: u8, +} + + + +impl From> for StackedItemChild + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + fn from(item: StackedItem2) -> Self { + let StackedItem2 { + node, + hash, + depth_prefix, + depth, + parent_index, + .. + } = item; + StackedItemChild { + node, + hash, + depth_prefix, + depth, + parent_index, + } + } +} + +impl StackedItem2 + where + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, + T: TrieLayout, + S: Clone + Default, +{ + fn split_child_index(&self) -> Option { + match self.split_child.as_ref() { + Some((Some(child), _)) => Some(child.parent_index), + Some((_, Some(parent_index))) => Some(*parent_index), + None => None, + _ => unreachable!("This pair is Either, TODO swith to enum"), + } + } + + fn is_split_child(&self, index: u8) -> bool { + self.split_child_index().map(|child_index| child_index == index).unwrap_or(false) + } + + fn descend_child(&mut self, index: u8, db: &dyn HashDBRef, key: &[u8]) -> Result< + Option>, + TrieHash, + CError + > { + Ok(if self.is_split_child(index) { + if let Some((Some(StackedItemChild { + node, + depth, + depth_prefix, + hash, + parent_index, + .. + }), None)) = self.split_child.take() { + self.split_child = Some((None, Some(parent_index))); + // from a split child key is none (partial changed on split) + Some(StackedItem2 { + node, + depth, + depth_prefix, + first_child: None, + split_child: None, + hash, + parent_index, + did_first_child: false, + }) + } else { + unreachable!("Broken split expectation, visited twice"); + } + } else { + if let Some(node_handle) = self.node.child(index) { + + let (node, hash) = match node_handle { + NodeHandle::Hash(handle_hash) => { + let mut hash = as Default>::default(); + hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + (StackedNode::Unchanged( + fetch::( + db, &hash, + NibbleSlice::new_offset(key, self.depth_prefix).left(), + )?, + Default::default(), + ), Some(hash)) + }, + NodeHandle::Inline(node_encoded) => { + // Instantiating B is only for inline node, still costy. + (StackedNode::Unchanged( + OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError( + self.hash.clone().unwrap_or_else(Default::default), + e, + )))?, + Default::default(), + ), None) + }, + }; + let depth_prefix = self.depth + 1; + let depth = depth_prefix + node.partial().map(|p| p.len()).unwrap_or(0); + Some(StackedItem2 { + node, + hash, + parent_index: index, + depth_prefix, + depth, + first_child: None, + split_child: None, + did_first_child: false, + }) + } else { + None + } + }) + } + + // replace self by new branch at split and adding to self as a stacked item child. + fn split_child(&mut self, mid_index: usize, index: u8, key: &[u8]) { + let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { + let new_slice = NibbleSlice::new_offset( + &key[..mid_index / nibble_ops::NIBBLE_PER_BYTE], + self.depth_prefix, + ); + Node::new_branch(new_slice) + } else { + let new_slice = NibbleSlice::new_offset( + &key[..], + self.depth_prefix, + ); + let owned = new_slice.to_stored_range(mid_index - self.depth_prefix); + // TODO EMCH refactor new_leaf to take BackingByteVec (stored) as input + Node::new_branch(NibbleSlice::from_stored(&owned)) + }; + + let old_depth = self.depth; + self.depth = mid_index; + let mut child = mem::replace( + &mut self.node, + StackedNode::Changed(dest_branch, Default::default()), + ); + + let parent_index = child.partial() + .map(|p| p.at(mid_index - self.depth_prefix)).unwrap_or(0); + + child.advance_partial(1 + mid_index - self.depth_prefix); + + // split occurs before visiting a single child + debug_assert!(self.first_child.is_none()); + debug_assert!(!self.did_first_child); + // ordering key also ensure + debug_assert!(self.split_child.is_none()); + + // not setting child relation (will be set on exit) + let child = StackedItemChild { + node: child, + hash: None, + depth_prefix: 1 + mid_index, + depth: old_depth, + parent_index, + }; + self.split_child = Some((Some(child), None)); + } + + fn append_child< + F: ProcessStack, + >(&mut self, child: StackedItemChild, prefix: Prefix, callback: &mut F) { + if let Some(handle) = callback.exit( + prefix, + child.node, + child.hash.as_ref(), + ) { + self.node.set_handle(handle, child.parent_index); + } + } + + fn process_split_child< + F: ProcessStack, + >(&mut self, key: &[u8], callback: &mut F) { + if let Some((Some(child), None)) = self.split_child.take() { + self.split_child = Some((None, Some(child.parent_index))); + // prefix is slice + let mut build_prefix = NibbleVec::from(key, self.depth); + build_prefix.push(child.parent_index); + self.append_child(child, build_prefix.as_prefix(), callback); + } + } + + fn process_first_child< + F: ProcessStack, + >(&mut self, callback: &mut F) { + if let Some((child, key)) = self.first_child.take() { + let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); + self.append_child(child, nibble_slice.left(), callback); + } + } + + fn process_child< + F: ProcessStack, + >(&mut self, child: StackedItemChild, key: &[u8], callback: &mut F) { + let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); + self.append_child(child.into(), nibble_slice.left(), callback); + } + + // consume branch and return item to attach to parent + fn fuse_branch< + F: ProcessStack, + >(&mut self, child: StackedItem2, key: &[u8], callback: &mut F) { + let child_depth = self.depth + 1 + child.node.partial().map(|p| p.len()).unwrap_or(0); + let to_rem = mem::replace(&mut self.node, child.node); + // delete current + callback.exit( + NibbleSlice::new_offset(key.as_ref(), self.depth_prefix).left(), + to_rem, self.hash.as_ref(), + ).expect("no new node on empty"); + + let partial = NibbleSlice::new_offset(key, self.depth_prefix) + .to_stored_range(child_depth - self.depth_prefix); + self.node.set_partial(partial); + self.hash = child.hash; + self.depth = child_depth; + self.did_first_child = false; + self.first_child = None; + self.split_child = None; + } +} + impl StackedNode where B: Borrow<[u8]> + AsRef<[u8]>, @@ -142,6 +442,19 @@ impl StackedNode } } + /// Set a new partial. + pub fn set_partial(&mut self, partial: NodeKey) { + match self { + StackedNode::Unchanged(node, state) => { + if let Some(new) = node.set_partial(partial) { + *self = StackedNode::Changed(new, state.clone()); + } + }, + StackedNode::Changed(node, ..) => node.set_partial(partial), + StackedNode::Deleted(..) => (), + } + } + /// Remove a value if the node contains one. pub fn remove_value(&mut self) { @@ -203,14 +516,13 @@ impl StackedNode } } - /// Returns index of node to fuse with in case after removal of handle - /// a branch with a single child and no value remain. - pub fn fix_node(&mut self) -> Option { + /// Returns index of node to fuse with if fused require. + pub fn fix_node(&mut self, pending: (Option, Option)) -> Option { match self { StackedNode::Deleted(..) | StackedNode::Unchanged(..) => None, StackedNode::Changed(node, state) => { - let (deleted, fuse) = node.fix_node(); + let (deleted, fuse) = node.fix_node(pending); if deleted { *self = StackedNode::Deleted(state.clone()); } @@ -254,13 +566,11 @@ impl StackedNode } /// Visitor trait to implement when using `trie_traverse_key`. -pub trait ProcessStack +pub trait ProcessStack where T: TrieLayout, S: Clone, B: Borrow<[u8]> + AsRef<[u8]>, - K: AsRef<[u8]> + Ord, - V: AsRef<[u8]>, { // /// Callback on enter a node, change can be applied here. // fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode); @@ -317,7 +627,7 @@ fn descend_terminal( V: AsRef<[u8]>, S: Default + Clone, B: Borrow<[u8]> + AsRef<[u8]>, - F: ProcessStack, + F: ProcessStack, { let key_dest = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; let slice_dest = NibbleSlice::new_offset(key.as_ref(), item.depth_prefix); @@ -355,6 +665,196 @@ fn descend_terminal( } } +/// The main entry point for traversing a trie by a set of keys. +pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( + db: &'a dyn HashDBRef, + root_hash: &'a TrieHash, + elements: I, + callback: &mut F, +) -> Result<(), TrieHash, CError> + where + T: TrieLayout, + I: IntoIterator)>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]>, + S: Default + Clone, + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, + F: ProcessStack, +{ + // Stack of traversed nodes + let mut stack: Vec> = Vec::with_capacity(32); + + let root = if let Ok(root) = fetch::(db, root_hash, EMPTY_PREFIX) { + root + } else { + return Err(Box::new(TrieError::InvalidStateRoot(*root_hash))); + }; + + // TODO encapsulate fetch in stacked item, also split or others + let current = StackedNode::::Unchanged(root, Default::default()); + let depth = current.partial().map(|p| p.len()).unwrap_or(0); + let mut current = StackedItem2 { + node: current, + hash: Some(*root_hash), + depth_prefix: 0, + depth, + parent_index: 0, + first_child: None, + split_child: None, + did_first_child: false, + }; + + let mut queried_element: Option<(K, Option)> = None; + + let mut target_common_depth = 0; + + for next_element in elements.into_iter() { + if let Some((key, value)) = queried_element { + + + target_common_depth = nibble_ops::biggest_depth( + key.as_ref(), + next_element.0.as_ref(), + ); + + // unstack nodes if needed + while target_common_depth < current.depth_prefix { + + // TODO check if fuse (num child is 1). + // child change or addition + if let Some(mut parent) = stack.pop() { + let first_child_index = (current.first_child.as_ref().map(|c| c.0.parent_index), current.split_child_index()); + // needed also to resolve + if let Some(fuse_index) = current.node.fix_node(first_child_index) { + let (child, hash, child_key) = if Some(fuse_index) == first_child_index.0 { + let (child, child_key) = current.first_child.take().expect("first_child_index is some"); + (child.node, child.hash, Some(child_key)) + } else { match current.node.child(fuse_index) { + Some(NodeHandle::Hash(handle_hash)) => { + let mut hash = as Default>::default(); + hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + (StackedNode::Unchanged( + fetch::( + db, &hash, + NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), + )?, + Default::default(), + ), Some(hash), None) + }, + Some(NodeHandle::Inline(node_encoded)) => { + // Instantiating B is only for inline node, still costy. + (StackedNode::Unchanged( + OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError( + current.hash.clone().unwrap_or_else(Default::default), + e, + )))?, + Default::default(), + ), None, None) + }, + None => unreachable!("fix_node call only return existing index"), + }}; + // delete current + callback.exit( + NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), + current.node, current.hash.as_ref(), + ).expect("no new node on empty"); + + let child_depth = current.depth + child.partial().map(|p| p.len()).unwrap_or(0); + if !current.did_first_child { + let child_key = child_key.as_ref().map(|k| k.as_ref()).unwrap_or(key.as_ref()); + let partial = NibbleSlice::new_offset(child_key, current.depth_prefix) + .to_stored_range(child_depth - current.depth_prefix); + let mut child = child; + child.set_partial(partial); + // second child, just update current and process as a standard node. + current = StackedItem2 { + node: child, + depth_prefix: current.depth_prefix, + depth: child_depth, + hash, + parent_index: current.parent_index, + did_first_child: current.did_first_child, + first_child: current.first_child, + split_child: current.split_child, + }; + } else { + // set first child + parent.first_child = Some((StackedItemChild { + node: child, + depth_prefix: current.depth_prefix, + depth: child_depth, + hash, + parent_index: current.parent_index, + }, key.as_ref().to_vec())); + + current = parent; + // TODO should we avoid this early exit by setting current to delete (and don't + // delete current before). + continue; + } + } + if let Some((parent_first, key_first)) = parent.first_child.take() { + // parent first from split is replaced when going down in this case. + debug_assert!(parent_first.parent_index != current.parent_index); + // parent got two changed child we can apply exit (no way to fuse this with + // top or + if parent_first.parent_index < current.parent_index { + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(&key_first[..], parent.depth + 1).left(), + parent_first.node, parent_first.hash.as_ref(), + ) { + parent.node.set_handle(handle, parent_first.parent_index); + } + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), + current.node, current.hash.as_ref(), + ) { + parent.node.set_handle(handle, current.parent_index); + } + } else if parent_first.parent_index > current.parent_index { + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), + current.node, current.hash.as_ref(), + ) { + parent.node.set_handle(handle, current.parent_index); + } + // parent_first is from a split + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(&key_first[..], parent.depth + 1).left(), + parent_first.node, parent_first.hash.as_ref(), + ) { + parent.node.set_handle(handle, parent_first.parent_index); + } + } + parent.did_first_child = true; + } else if parent.did_first_child || current.node.is_deleted() { + // process exit, as we already assert two child, no need to store in case of parent + // fusing. + if let Some(handle) = callback.exit( + NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), + current.node, current.hash.as_ref(), + ) { + parent.node.set_handle(handle, current.parent_index); + } + } else { + // store in parent first child and process later. + parent.first_child = Some((current.into(), key.as_ref().to_vec())); + } + current = parent; + } else { + unimplemented!("root case"); + return Ok(()); + } + } + + } + queried_element = Some(next_element); + } + + Ok(()) +} + /// The main entry point for traversing a trie by a set of keys. pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( db: &'a dyn HashDBRef, @@ -369,7 +869,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( V: AsRef<[u8]>, S: Default + Clone, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, - F: ProcessStack, + F: ProcessStack, { // stack of traversed nodes // first usize is depth, second usize is the parent index. @@ -584,7 +1084,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( Ok(()) } -fn align_node<'a, T, K, V, S, B, F>( +fn align_node<'a, T, S, B, F>( db: &'a dyn HashDBRef, callback: &mut F, branch: &mut StackedItem, @@ -594,11 +1094,9 @@ fn align_node<'a, T, K, V, S, B, F>( ) -> Result<(), TrieHash, CError> where T: TrieLayout, - K: AsRef<[u8]> + Ord, - V: AsRef<[u8]>, S: Default + Clone, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, - F: ProcessStack, + F: ProcessStack, { let init_prefix_len = prefix.as_ref().map(|p| p.len()) .unwrap_or(0); @@ -632,7 +1130,7 @@ fn align_node<'a, T, K, V, S, B, F>( // that is one hash creation here and one access then deletion afterward. branch.node.set_handle(handle, child.parent_index); } - if let Some(fuse_index) = branch.node.fix_node() { + if let Some(fuse_index) = branch.node.fix_node((None, None)) { let mut build_prefix: NibbleVec; let (child, hash) = match branch.node.child(fuse_index) { Some(NodeHandle::Hash(handle_hash)) => { @@ -721,13 +1219,11 @@ pub struct BatchUpdate( pub Option, ); -impl ProcessStack for BatchUpdate> +impl ProcessStack for BatchUpdate> where T: TrieLayout, S: Clone + Default, B: Borrow<[u8]> + AsRef<[u8]>, - K: AsRef<[u8]> + Ord, - V: AsRef<[u8]>, { //fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode) { //} diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 8be6896d..4f9bab63 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -205,6 +205,18 @@ impl Node { } } + /// Set partial if possible. + pub fn set_partial(&mut self, new_partial: NodeKey) { + match self { + Node::Branch ( .. ) + | Node::Empty => (), + Node::Extension(partial, ..) + | Node::NibbledBranch(partial, ..) + | Node::Leaf(partial, ..) => { + *partial = new_partial; + }, + } + } /// Set value to node if possible. pub fn set_value(&mut self, value: &[u8]) { @@ -286,8 +298,11 @@ impl Node { /// a branch with a single child and no value remain. /// This is only for no extension trie (a variant would be /// needed for trie with extension). + /// Pending parameter indicates a node to be added or modified due to buffered + /// addition or split child). pub fn fix_node( &mut self, + pending: (Option, Option), ) -> (bool, Option) { let node = mem::replace(self, Node::Empty); let (node, fuse) = match node { @@ -301,6 +316,16 @@ impl Node { | n@Node::Leaf(..) => (n, None), Node::NibbledBranch(partial, encoded_children, val) => { let mut count = 0; + if let Some(pending) = pending.0 { + if encoded_children[pending as usize].is_none() { + count += 1; + } + } + if let Some(pending) = pending.1 { + if encoded_children[pending as usize].is_none() { + count += 1; + } + } for c in encoded_children.iter() { if c.is_some() { count += 1; From 146fbc9f15de6b16341edc162ec1b07c005569ee Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Thu, 9 Jan 2020 10:15:21 +0100 Subject: [PATCH 047/118] fuse with stacked item methods --- trie-db/src/traverse.rs | 119 +++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 70 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index eb995fca..5eb23f7a 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -198,7 +198,38 @@ impl StackedItem2 self.split_child_index().map(|child_index| child_index == index).unwrap_or(false) } - fn descend_child(&mut self, index: u8, db: &dyn HashDBRef, key: &[u8]) -> Result< + fn is_first_child(&self, index: u8) -> bool { + self.first_child.map(|child| child.parent_index == index).unwrap_or(false) + } + + // take first child (used for fusing, otherwhise process_first_child is probably what you want) + fn take_first_child(&mut self) -> Option<(StackedItem2, Vec)> { + // descending in first child is only for fusizg node + // so we do not update self first child status (will be deleted). + if let Some((StackedItemChild { + node, + depth, + depth_prefix, + hash, + parent_index, + .. + }, child_key)) = self.first_child.take() { + Some((StackedItem2 { + node, + depth, + depth_prefix, + first_child: None, + split_child: None, + hash, + parent_index, + did_first_child: false, + }, child_key)) + } else { + None + } + } + + fn descend_child(&mut self, index: u8, db: &dyn HashDBRef, prefix: Prefix) -> Result< Option>, TrieHash, CError @@ -236,8 +267,8 @@ impl StackedItem2 hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); (StackedNode::Unchanged( fetch::( - db, &hash, - NibbleSlice::new_offset(key, self.depth_prefix).left(), + db, &hash, prefix, +// NibbleSlice::new_offset(key, self.depth_prefix).left(), )?, Default::default(), ), Some(hash)) @@ -723,76 +754,24 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( // TODO check if fuse (num child is 1). // child change or addition if let Some(mut parent) = stack.pop() { - let first_child_index = (current.first_child.as_ref().map(|c| c.0.parent_index), current.split_child_index()); + let first_child_index = current.first_child.as_ref().map(|c| c.0.parent_index); // needed also to resolve - if let Some(fuse_index) = current.node.fix_node(first_child_index) { - let (child, hash, child_key) = if Some(fuse_index) == first_child_index.0 { - let (child, child_key) = current.first_child.take().expect("first_child_index is some"); - (child.node, child.hash, Some(child_key)) - } else { match current.node.child(fuse_index) { - Some(NodeHandle::Hash(handle_hash)) => { - let mut hash = as Default>::default(); - hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - (StackedNode::Unchanged( - fetch::( - db, &hash, - NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), - )?, - Default::default(), - ), Some(hash), None) - }, - Some(NodeHandle::Inline(node_encoded)) => { - // Instantiating B is only for inline node, still costy. - (StackedNode::Unchanged( - OwnedNode::new::(B::from(node_encoded)) - .map_err(|e| Box::new(TrieError::DecoderError( - current.hash.clone().unwrap_or_else(Default::default), - e, - )))?, - Default::default(), - ), None, None) - }, - None => unreachable!("fix_node call only return existing index"), - }}; - // delete current - callback.exit( - NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), - current.node, current.hash.as_ref(), - ).expect("no new node on empty"); - - let child_depth = current.depth + child.partial().map(|p| p.len()).unwrap_or(0); - if !current.did_first_child { - let child_key = child_key.as_ref().map(|k| k.as_ref()).unwrap_or(key.as_ref()); - let partial = NibbleSlice::new_offset(child_key, current.depth_prefix) - .to_stored_range(child_depth - current.depth_prefix); - let mut child = child; - child.set_partial(partial); - // second child, just update current and process as a standard node. - current = StackedItem2 { - node: child, - depth_prefix: current.depth_prefix, - depth: child_depth, - hash, - parent_index: current.parent_index, - did_first_child: current.did_first_child, - first_child: current.first_child, - split_child: current.split_child, - }; + if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_index())) { + // try first child + if let Some((child, child_key)) = current.take_first_child() { + debug_assert!(child.parent_index == fuse_index); + // + current.fuse_branch(child, child_key.as_ref(), callback); } else { - // set first child - parent.first_child = Some((StackedItemChild { - node: child, - depth_prefix: current.depth_prefix, - depth: child_depth, - hash, - parent_index: current.parent_index, - }, key.as_ref().to_vec())); - - current = parent; - // TODO should we avoid this early exit by setting current to delete (and don't - // delete current before). - continue; + let mut prefix = NibbleVec::from(key.as_ref(), current.depth); + prefix.push(fuse_index); + let child = current.descend_child(fuse_index, db, prefix.as_prefix())? + .expect("result of first child is define if consistent db"); + child.node.partial().map(|p| prefix.append_partial(p)); + current.fuse_branch(child, prefix.inner(), callback); } + // fuse child opteration did switch current context. + continue; } if let Some((parent_first, key_first)) = parent.first_child.take() { // parent first from split is replaced when going down in this case. From aff087683e8701e4eb7b094c6cf5667136e4ebe5 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Thu, 9 Jan 2020 10:25:27 +0100 Subject: [PATCH 048/118] fix compile --- trie-db/src/traverse.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 5eb23f7a..05ac77ce 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -199,7 +199,7 @@ impl StackedItem2 } fn is_first_child(&self, index: u8) -> bool { - self.first_child.map(|child| child.parent_index == index).unwrap_or(false) + self.first_child.as_ref().map(|child| child.0.parent_index == index).unwrap_or(false) } // take first child (used for fusing, otherwhise process_first_child is probably what you want) @@ -767,7 +767,7 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( prefix.push(fuse_index); let child = current.descend_child(fuse_index, db, prefix.as_prefix())? .expect("result of first child is define if consistent db"); - child.node.partial().map(|p| prefix.append_partial(p)); + child.node.partial().map(|p| prefix.append_partial(p.right())); current.fuse_branch(child, prefix.inner(), callback); } // fuse child opteration did switch current context. From 90874a59e17fa2a887c9ba9c05bb3dc28d795fd9 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Sat, 11 Jan 2020 10:41:27 +0100 Subject: [PATCH 049/118] seemlessly implemented path up. --- trie-db/src/traverse.rs | 81 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 05ac77ce..b94dd836 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -120,6 +120,7 @@ pub struct StackedItem2 pub depth: usize, /// parent index (only relevant when parent is a branch). pub parent_index: u8, + // TODO rational for keeping index is very border line pub split_child: Option<(Option>, Option)>, /// Store first child, until `exit` get call, this is needed /// to be able to fuse branch containing a single child (delay @@ -185,7 +186,7 @@ impl StackedItem2 T: TrieLayout, S: Clone + Default, { - fn split_child_index(&self) -> Option { + fn split_child_fuse_index(&self) -> Option { match self.split_child.as_ref() { Some((Some(child), _)) => Some(child.parent_index), Some((_, Some(parent_index))) => Some(*parent_index), @@ -194,6 +195,18 @@ impl StackedItem2 } } + + fn split_child_index(&self) -> Option { + match self.split_child.as_ref() { + Some((Some(child), _)) => Some(child.parent_index), + _ => None, + } + } + + fn first_child_index(&self) -> Option { + self.first_child.as_ref().map(|c| c.0.parent_index) + } + fn is_split_child(&self, index: u8) -> bool { self.split_child_index().map(|child_index| child_index == index).unwrap_or(false) } @@ -385,11 +398,39 @@ impl StackedItem2 fn process_child< F: ProcessStack, - >(&mut self, child: StackedItemChild, key: &[u8], callback: &mut F) { + >(&mut self, mut child: StackedItem2, key: &[u8], callback: &mut F) { + // TODO switch to debug assert if correct asumption or call process first + // child ordered with split child. + assert!(child.first_child.is_none(), "guaranted by call to fix_node"); + // split child can be unprocessed (when going up it is kept after second node + // in expectation of other children process. + child.process_split_child(key, callback); let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); self.append_child(child.into(), nibble_slice.left(), callback); } + fn process_root< + F: ProcessStack, + >(mut self, key: &[u8], callback: &mut F) { + if let Some(first_child_index) = self.first_child_index() { + if let Some(split_child_index) = self.split_child_index() { + if split_child_index > first_child_index { + self.process_first_child(callback); + self.process_split_child(key, callback); + } else { + self.process_split_child(key, callback); + self.process_first_child(callback); + } + } else { + self.process_first_child(callback); + } + } + callback.exit_root( + self.node, + self.hash.as_ref(), + ) + } + // consume branch and return item to attach to parent fn fuse_branch< F: ProcessStack, @@ -619,7 +660,7 @@ pub trait ProcessStack fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>>; /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>); + fn exit_root(&mut self, stacked: StackedNode, prev_hash: Option<&TrieHash>); /// Fetching a fuse latest, that is the latest changed value of a branch. @@ -756,7 +797,7 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( if let Some(mut parent) = stack.pop() { let first_child_index = current.first_child.as_ref().map(|c| c.0.parent_index); // needed also to resolve - if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_index())) { + if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { // try first child if let Some((child, child_key)) = current.take_first_child() { debug_assert!(child.parent_index == fuse_index); @@ -810,19 +851,40 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( } else if parent.did_first_child || current.node.is_deleted() { // process exit, as we already assert two child, no need to store in case of parent // fusing. + // Deletion case is guaranted by ordering of input (fix delete only if no first + // and no split). if let Some(handle) = callback.exit( NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), current.node, current.hash.as_ref(), ) { parent.node.set_handle(handle, current.parent_index); } + } else if let Some(first_child_index) = parent.first_child_index() { + debug_assert!(first_child_index < current.parent_index); + parent.did_first_child = true; + // process both first child and current. + if let Some(split_child_index) = parent.split_child_index() { + debug_assert!(split_child_index != first_child_index); + debug_assert!(split_child_index != current.parent_index); + if split_child_index < first_child_index { + parent.process_split_child(key.as_ref(), callback); + } + parent.process_first_child(callback); + if split_child_index < current.parent_index { + parent.process_split_child(key.as_ref(), callback); + } + parent.process_child(current, key.as_ref(), callback); + } else { + parent.process_first_child(callback); + parent.process_child(current, key.as_ref(), callback); + } } else { - // store in parent first child and process later. + // first node visited, store in parent first child and process later. parent.first_child = Some((current.into(), key.as_ref().to_vec())); } current = parent; } else { - unimplemented!("root case"); + current.process_root(key.as_ref(), callback); return Ok(()); } } @@ -910,7 +972,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } } else { align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; - callback.exit_root(EMPTY_PREFIX, current.node, current.hash.as_ref()); + callback.exit_root(current.node, current.hash.as_ref()); return Ok(()); } } @@ -1056,7 +1118,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } } align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; - callback.exit_root(EMPTY_PREFIX, current.node, current.hash.as_ref()); + callback.exit_root(current.node, current.hash.as_ref()); return Ok(()); } @@ -1331,7 +1393,8 @@ impl ProcessStack for BatchUpdate> } } - fn exit_root(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) { + fn exit_root(&mut self, stacked: StackedNode, prev_hash: Option<&TrieHash>) { + let prefix = EMPTY_PREFIX; match stacked { s@StackedNode::Deleted(..) | s@StackedNode::Changed(..) => { From cc149bc730be14251b12d820fad0e6693d560992 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Mon, 13 Jan 2020 12:21:31 +0100 Subject: [PATCH 050/118] traverse2 algo ok (there is some issue to debug but generally fine algo). Next replace and remove lot of unused code, next clean a bit before debugging and make it work. --- trie-db/src/traverse.rs | 162 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 148 insertions(+), 14 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index b94dd836..201d1cbb 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -317,7 +317,7 @@ impl StackedItem2 } // replace self by new branch at split and adding to self as a stacked item child. - fn split_child(&mut self, mid_index: usize, index: u8, key: &[u8]) { + fn split_child(&mut self, mid_index: usize, key: &[u8]) { let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { let new_slice = NibbleSlice::new_offset( &key[..mid_index / nibble_ops::NIBBLE_PER_BYTE], @@ -656,6 +656,20 @@ pub trait ProcessStack value_element: Option<&[u8]>, state: TraverseState, ) -> Option>; + + /// Descend node, it should (if we want update): + /// - return a new child for the new value. + /// - replace `self` by a new branch with `self` as its split child + /// and a new child for the new value. + /// - change value of `self` only. + fn enter_terminal2( + &mut self, + stacked: &mut StackedItem2, + key_element: &[u8], + value_element: Option<&[u8]>, + state: TraverseState, + ) -> Option>; + /// Callback on exit a node, commit action on change node should be applied here. fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>>; @@ -690,7 +704,6 @@ fn descend_terminal( item: &mut StackedItem, key: &K, value: Option<&V>, - dest_depth: usize, callback: &mut F, ) -> (Option>, Option>) where @@ -703,7 +716,6 @@ fn descend_terminal( { let key_dest = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; let slice_dest = NibbleSlice::new_offset(key.as_ref(), item.depth_prefix); - // TODO optimize common_prefix function ?? totally since needed in loop let target_common_depth = item.node.partial() .map(|p| item.depth_prefix + p.common_prefix(&slice_dest)) .unwrap_or(item.depth); @@ -776,18 +788,63 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( did_first_child: false, }; - let mut queried_element: Option<(K, Option)> = None; - - let mut target_common_depth = 0; - - for next_element in elements.into_iter() { - if let Some((key, value)) = queried_element { + let mut previous_key: Option = None; + for next_query in elements.into_iter().map(|e| Some(e)).chain(None) { + let mut next_key = None; + // PATH DOWN descending in next_query. + if let Some((key, value)) = next_query { + let dest_slice = NibbleFullKey::new(key.as_ref()); + let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - target_common_depth = nibble_ops::biggest_depth( + loop { + let common_index = current.node.partial() + .map(|current_partial| { + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + current.depth_prefix + current_partial.common_prefix(&target_partial) + }).unwrap_or(current.depth_prefix); + // TODO not sure >= or just >. + if common_index == current.depth_prefix && dest_depth >= current.depth { + let next_index = dest_slice.at(current.depth); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth); + if let Some(child) = current.descend_child(next_index, db, prefix.left())? { + current = child; + } else { + break; + } + } else { + break; + } + } + let traverse_state = if dest_depth < current.depth { + // split child (insert in current prefix + let mid_index = current.node.partial() + .map(|current_partial| { + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + current.depth_prefix + current_partial.common_prefix(&target_partial) + }).expect("Covered by previous iteration for well formed trie"); + TraverseState::MidPartial(mid_index) + } else if dest_depth > current.depth { + // over callback + TraverseState::AfterNode + } else { + // value replace callback + TraverseState::ValueMatch + }; + callback.enter_terminal2( + &mut current, key.as_ref(), - next_element.0.as_ref(), + value.as_ref().map(|v| v.as_ref()), + traverse_state, ); + next_key = Some(key) + } + // PATH UP over the previous key and value + if let Some(key) = previous_key { + let target_common_depth = next_key.as_ref().map(|next| nibble_ops::biggest_depth( + key.as_ref(), + next.as_ref(), + )).unwrap_or(0); // last element goes up to root // unstack nodes if needed while target_common_depth < current.depth_prefix { @@ -890,7 +947,7 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( } } - queried_element = Some(next_element); + previous_key = next_key; } Ok(()) @@ -1083,7 +1140,7 @@ pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( } } else { // terminal case - match descend_terminal(&mut current, k, v.as_ref(), dest_depth, callback) { + match descend_terminal(&mut current, k, v.as_ref(), callback) { (Some(new), _) => { stack.push(current); current = new; @@ -1262,9 +1319,9 @@ pub struct BatchUpdate( impl ProcessStack for BatchUpdate> where + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, S: Clone + Default, - B: Borrow<[u8]> + AsRef<[u8]>, { //fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode) { //} @@ -1360,6 +1417,83 @@ impl ProcessStack for BatchUpdate> } } + fn enter_terminal2( + &mut self, + stacked: &mut StackedItem2, + key_element: &[u8], + value_element: Option<&[u8]>, + state: TraverseState, + ) -> Option> { + match state { + TraverseState::ValueMatch => { + if let Some(value) = value_element { + stacked.node.set_value(value); + } else { + stacked.node.remove_value(); + } + None + }, + TraverseState::AfterNode => { + if let Some(val) = value_element { + // corner case of empty trie. + let offset = if stacked.node.is_empty() { + 0 + } else { + 1 + }; + // dest is a leaf appended to terminal + let dest_leaf = Node::new_leaf( + NibbleSlice::new_offset(key_element, stacked.depth + offset), + val, + ); + let parent_index = NibbleSlice::new(key_element).at(stacked.depth); + // append to parent is done on exit through changed nature of the new leaf. + return Some(StackedItem2 { + node: StackedNode::Changed(dest_leaf, Default::default()), + hash: None, + depth_prefix: stacked.depth + offset, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + split_child: None, + first_child: None, + did_first_child: false, + }); + } else { + // nothing to delete. + return None; + } + }, + TraverseState::MidPartial(mid_index) => { + if let Some(value) = value_element { + stacked.split_child(mid_index, key_element); + // TODO not sure on index + let parent_index = NibbleSlice::new(key_element).at(mid_index); + let child = Node::new_leaf( + // TODO not sure on '1 +' + NibbleSlice::new_offset(key_element, 1 + mid_index), + value.as_ref(), + ); + let child = StackedItem2 { + node: StackedNode::Changed(child, Default::default()), + hash: None, + depth_prefix: 1 + mid_index, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + split_child: None, + first_child: None, + did_first_child: false, + }; + return Some(child); + } else { + // nothing to delete. + return None; + } + }, + } + + } + + fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>> { match stacked { From 9ca5c56c4355a2bd2cad4be2c91176701fae148c Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Tue, 14 Jan 2020 17:11:05 +0100 Subject: [PATCH 051/118] Remove unused state. Only expose update traverse function (no inner traits). Remove some unused code. --- test-support/reference-trie/src/lib.rs | 13 +- trie-db/benches/bench.rs | 16 +- trie-db/fuzz/src/lib.rs | 13 +- trie-db/src/lib.rs | 2 +- trie-db/src/nibble/mod.rs | 3 + trie-db/src/node.rs | 39 +- trie-db/src/traverse.rs | 941 +++++-------------------- trie-db/src/triedbmut.rs | 34 - 8 files changed, 199 insertions(+), 862 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index a12e31ac..4c0d88fc 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -28,6 +28,7 @@ use trie_db::{ TrieBuilder, TrieRoot, Partial, + OwnedPrefix, }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; @@ -38,7 +39,7 @@ pub use trie_db::{ encode_compact, decode_compact, }; pub use trie_db::{Record, TrieLayout, TrieConfiguration, nibble_ops}; -pub use trie_db::traverse::{BatchUpdate, trie_traverse_key}; +pub use trie_db::traverse::batch_update; pub use trie_root::TrieStream; pub mod node { pub use trie_db::node::Node; @@ -1191,21 +1192,17 @@ pub fn compare_no_extension_insert_remove( pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( // TODO db to non mutable db: &'a mut dyn hash_db::HashDBRef, - root: &'a [u8; 32], + root: &'a ::Out, elements: I, - batch_update: &'a mut BatchUpdate<::Out>, -) +) -> (::Out, impl Iterator::Out, Option>)>) where I: IntoIterator)>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, { - // TODO return type ?? EMCH generally this need redesign as it expose to many internal types: - // probably expose a function from traverse - trie_traverse_key::(db, root, elements, batch_update).unwrap(); + batch_update::(db, root, elements).unwrap() } - #[cfg(test)] mod tests { diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index ca95b8c7..9d0674fc 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -108,7 +108,6 @@ fn root_b_big_v(c: &mut Criterion) { ); } - fn root_a_small_v(c: &mut Criterion) { let data : Vec, Vec)>> = vec![ input2(29, 204800, 32), @@ -172,7 +171,6 @@ fn root_old(c: &mut Criterion) { ); } - fn root_new(c: &mut Criterion) { let data : Vec, Vec)>> = vec![ input(1, 5120), @@ -357,8 +355,6 @@ fn trie_mut_ref_root_b(c: &mut Criterion) { data); } - - fn trie_mut_a(c: &mut Criterion) { use trie_db::TrieMut; use memory_db::HashKey; @@ -482,7 +478,6 @@ fn trie_mut_same_key_single(c: &mut Criterion) { } } - c.bench_function("trie_mut_same_key_single", move |b: &mut Bencher| b.iter(|| { let mut mdb = db.clone(); @@ -515,17 +510,14 @@ fn trie_mut_same_key_batch(c: &mut Criterion) { } } - c.bench_function("trie_mut_same_key_batch", move |b: &mut Bencher| b.iter(|| { let mut mdb = db.clone(); // sort let data: std::collections::BTreeSet> = data.iter().map(|(a, _b)| a.clone()).collect(); - let mut batch_update = reference_trie::BatchUpdate(Default::default(), root.clone(), None); - reference_trie::trie_traverse_key_no_extension_build( - &mut mdb, &root, data.iter().map(|a| (a, Some(&a[..]))), &mut batch_update); - // rem root del TODO use returned root. - batch_update.0.pop(); - assert!(batch_update.0.last().unwrap().1 != root); + let (calc_root, _payload) = reference_trie::trie_traverse_key_no_extension_build( + &mut mdb, &root, data.iter().map(|a| (a, Some(&a[..]))) + ); + assert!(calc_root != root); })); } diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 96d73f80..8be4ce4f 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -214,18 +214,15 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { } } } - let mut batch_update = reference_trie::BatchUpdate( - Default::default(), - initial_root.clone(), - None, - ); //println!("{:?}", sorted_data); - reference_trie::trie_traverse_key_no_extension_build( + let (calc_root, _payload) = reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, - &initial_root, sorted_data.into_iter(), &mut batch_update); + &initial_root, + sorted_data.into_iter(), + ); // println!("{:?}", batch_update.1); // println!("{:?}", root); - assert!(batch_update.1 == root); + assert!(calc_root == root); } #[test] diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 76b47263..7631d2be 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -97,7 +97,7 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibble::{NibbleSlice, NibbleVec, nibble_ops}; +pub use self::nibble::{NibbleSlice, NibbleVec, nibble_ops, OwnedPrefix}; pub use crate::node_codec::{NodeCodec, Partial}; pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 84db2a39..1af793c6 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -148,6 +148,9 @@ pub mod nibble_ops { /// Backing storage for `NibbleVec`s. pub(crate) type BackingByteVec = smallvec::SmallVec<[u8; 36]>; +/// Backing storage for `Prefix`. +pub type OwnedPrefix = (BackingByteVec, Option); + /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. /// Nibbles are always left aligned, so making a `NibbleVec` from /// a `NibbleSlice` can get costy. diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 7df573b9..e2f2b234 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -37,16 +37,6 @@ pub enum NodeHandle<'a> { Inline(&'a [u8]), } -impl<'a> NodeHandle<'a> { - /// Get underlying data. - pub(crate) fn data(&self) -> &[u8] { - match self { - NodeHandle::Hash(d) - | NodeHandle::Inline(d) => d, - } - } -} - /// Read a hash from a slice into a Hasher output. Returns None if the slice is the wrong length. pub fn decode_hash(data: &[u8]) -> Option { if data.len() != H::LENGTH { @@ -413,7 +403,7 @@ impl> OwnedNode { pub fn remove_value + Default>(&mut self) -> Option>>> { let data = &self.data.borrow(); match &self.plan { - NodePlan::Leaf { partial, value } => Some(None), + NodePlan::Leaf { .. } => Some(None), NodePlan::Extension { .. } // TODO Extension | NodePlan::Branch { .. } // TODO branch | NodePlan::Empty => None, @@ -500,31 +490,4 @@ impl> OwnedNode { }, } } - - /// Set handle to a mid branch, return changed self element - /// (new branch) and the old element (new child). - pub fn set_mid_handle + Default>( - &mut self, - handle: TNodeHandle>, - index: u8, - common: usize, - ) -> (TNode>, TNode>) { - let prev_partial = self.partial() - .expect("This function is only call on node with partial"); - let mut children = Box::new([ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]); - children[index as usize] = Some(handle); - let mid_branch = TNode::NibbledBranch( - prev_partial.to_stored_range(common), - children, - None, - ); - let child = self.advance_partial(common + 1) - .expect("This function is only call with common value"); - (mid_branch, child) - } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 201d1cbb..94f2c0a8 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -19,7 +19,7 @@ use crate::triedbmut::{Node, NibbleFullKey}; use crate::triedbmut::NodeHandle as NodeHandleTrieMut; -use crate::node::{OwnedNode, NodePlan, NodeHandle, NodeKey}; +use crate::node::{OwnedNode, NodeHandle, NodeKey}; use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; #[cfg(feature = "std")] use std::borrow::Borrow; @@ -30,51 +30,37 @@ use alloc::vec::Vec; #[cfg(not(feature = "std"))] use alloc::boxed::Box; use crate::{TrieLayout, TrieHash, CError, Result, TrieError}; -use crate::{DBValue, nibble::BackingByteVec}; -use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX, Hasher}; +use crate::nibble::{BackingByteVec, OwnedPrefix}; +use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; use crate::NodeCodec; use ::core_::cmp::*; use ::core_::mem; -//#[cfg(feature = "std")] -//use std::collections::VecDeque; -//#[cfg(not(feature = "std"))] -//use alloc::collections::vec_deque::VecDeque; - -// TODO make it deletion aware : do not stack unchanged key and pass them -// to exit when not needed. type StorageHandle = Vec; type OwnedNodeHandle = NodeHandleTrieMut; /// StackedNode can be updated. /// A state can be use. -pub enum StackedNode +enum StackedNode where B: Borrow<[u8]>, T: TrieLayout, { /// Read node. - Unchanged(OwnedNode, S), + Unchanged(OwnedNode), /// Modified node. - Changed(Node, StorageHandle>, S), + Changed(Node, StorageHandle>), /// Deleted node. - Deleted(S), + Deleted, } -impl StackedNode +impl StackedNode where B: Borrow<[u8]>, T: TrieLayout, { fn is_deleted(&self) -> bool { - if let StackedNode::Deleted(..) = self { - true - } else { - false - } - } - fn is_unchanged(&self) -> bool { - if let StackedNode::Unchanged(..) = self { + if let StackedNode::Deleted = self { true } else { false @@ -82,46 +68,34 @@ impl StackedNode } } -pub struct StackedItem - where - B: Borrow<[u8]>, - T: TrieLayout, -{ - - /// Interanl node representation. - pub node: StackedNode, - /// Hash used to access this node, for inline node and - /// new nodes this is None. - pub hash: Option>, - /// Index of prefix. - pub depth_prefix: usize, - /// Depth of node, it is prefix depth and partial depth. - pub depth: usize, - /// parent index (only relevant when parent is a branch). - pub parent_index: u8, - /// Tell if a split child has been created for this branch. - pub split_child: bool, -} - -/// TODO use stacked item child internally -pub struct StackedItem2 +/// Item on stack it contains updatable traverse +/// specific field to manage update that split +/// partial from a node, and for buffering the first +/// child of a node to avoid storing node that will +/// be change later by a fuse operation (removing +/// a branch without value and a single child). +struct StackedItem where B: Borrow<[u8]>, T: TrieLayout, { /// Internal node representation. - pub node: StackedNode, + node: StackedNode, /// Hash used to access this node, for inline node and /// new nodes this is None. - pub hash: Option>, + hash: Option>, /// Index of prefix. - pub depth_prefix: usize, + depth_prefix: usize, /// Depth of node, it is prefix depth and partial depth. - pub depth: usize, + depth: usize, /// parent index (only relevant when parent is a branch). - pub parent_index: u8, - // TODO rational for keeping index is very border line - pub split_child: Option<(Option>, Option)>, + parent_index: u8, + /// Store split child to create after we did insert a branch + /// into a partial. We keep index (second field) in case we descend + /// into this split child: TODO rational seems bad (we descend into the + /// branch so when we call fix we pass this anyway. TODO in fixnode + /// add a check for this index not being set (only if from second field). + split_child: Option<(Option>, Option)>, /// Store first child, until `exit` get call, this is needed /// to be able to fuse branch containing a single child (delay /// `exit` call of first element after process of the second one). @@ -129,40 +103,38 @@ pub struct StackedItem2 /// nibble, this is more memory costy than strictly necessary). /// Note that split_child is always a first_child. /// TODO rename to first modified child (non delete). - pub first_child: Option<(StackedItemChild, Vec)>, + first_child: Option<(StackedItemChild, Vec)>, /// If a two child where already asserted or the node is deleted. - pub did_first_child: bool, + did_first_child: bool, } /// Variant of stacked item to store first changed node. -pub struct StackedItemChild +struct StackedItemChild where B: Borrow<[u8]>, T: TrieLayout, { /// Internal node representation. - pub node: StackedNode, + node: StackedNode, /// Hash used to access this node, for inline node and /// new nodes this is None. - pub hash: Option>, + hash: Option>, /// Index of prefix. - pub depth_prefix: usize, + depth_prefix: usize, /// Depth of node, it is prefix depth and partial depth. - pub depth: usize, + depth: usize, /// parent index (only relevant when parent is a branch). - pub parent_index: u8, + parent_index: u8, } - - -impl From> for StackedItemChild +impl From> for StackedItemChild where B: Borrow<[u8]>, T: TrieLayout, { - fn from(item: StackedItem2) -> Self { - let StackedItem2 { + fn from(item: StackedItem) -> Self { + let StackedItem { node, hash, depth_prefix, @@ -180,11 +152,10 @@ impl From> for StackedItemChild } } -impl StackedItem2 +impl StackedItem where B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, - S: Clone + Default, { fn split_child_fuse_index(&self) -> Option { match self.split_child.as_ref() { @@ -195,7 +166,6 @@ impl StackedItem2 } } - fn split_child_index(&self) -> Option { match self.split_child.as_ref() { Some((Some(child), _)) => Some(child.parent_index), @@ -211,12 +181,8 @@ impl StackedItem2 self.split_child_index().map(|child_index| child_index == index).unwrap_or(false) } - fn is_first_child(&self, index: u8) -> bool { - self.first_child.as_ref().map(|child| child.0.parent_index == index).unwrap_or(false) - } - // take first child (used for fusing, otherwhise process_first_child is probably what you want) - fn take_first_child(&mut self) -> Option<(StackedItem2, Vec)> { + fn take_first_child(&mut self) -> Option<(StackedItem, Vec)> { // descending in first child is only for fusizg node // so we do not update self first child status (will be deleted). if let Some((StackedItemChild { @@ -227,7 +193,7 @@ impl StackedItem2 parent_index, .. }, child_key)) = self.first_child.take() { - Some((StackedItem2 { + Some((StackedItem { node, depth, depth_prefix, @@ -243,7 +209,7 @@ impl StackedItem2 } fn descend_child(&mut self, index: u8, db: &dyn HashDBRef, prefix: Prefix) -> Result< - Option>, + Option>, TrieHash, CError > { @@ -258,7 +224,7 @@ impl StackedItem2 }), None)) = self.split_child.take() { self.split_child = Some((None, Some(parent_index))); // from a split child key is none (partial changed on split) - Some(StackedItem2 { + Some(StackedItem { node, depth, depth_prefix, @@ -281,10 +247,7 @@ impl StackedItem2 (StackedNode::Unchanged( fetch::( db, &hash, prefix, -// NibbleSlice::new_offset(key, self.depth_prefix).left(), - )?, - Default::default(), - ), Some(hash)) + )?), Some(hash)) }, NodeHandle::Inline(node_encoded) => { // Instantiating B is only for inline node, still costy. @@ -293,14 +256,13 @@ impl StackedItem2 .map_err(|e| Box::new(TrieError::DecoderError( self.hash.clone().unwrap_or_else(Default::default), e, - )))?, - Default::default(), - ), None) + )))? + ), None) }, }; let depth_prefix = self.depth + 1; let depth = depth_prefix + node.partial().map(|p| p.len()).unwrap_or(0); - Some(StackedItem2 { + Some(StackedItem { node, hash, parent_index: index, @@ -338,7 +300,7 @@ impl StackedItem2 self.depth = mid_index; let mut child = mem::replace( &mut self.node, - StackedNode::Changed(dest_branch, Default::default()), + StackedNode::Changed(dest_branch), ); let parent_index = child.partial() @@ -364,8 +326,8 @@ impl StackedItem2 } fn append_child< - F: ProcessStack, - >(&mut self, child: StackedItemChild, prefix: Prefix, callback: &mut F) { + F: ProcessStack, + >(&mut self, child: StackedItemChild, prefix: Prefix, callback: &mut F) { if let Some(handle) = callback.exit( prefix, child.node, @@ -376,7 +338,7 @@ impl StackedItem2 } fn process_split_child< - F: ProcessStack, + F: ProcessStack, >(&mut self, key: &[u8], callback: &mut F) { if let Some((Some(child), None)) = self.split_child.take() { self.split_child = Some((None, Some(child.parent_index))); @@ -388,7 +350,7 @@ impl StackedItem2 } fn process_first_child< - F: ProcessStack, + F: ProcessStack, >(&mut self, callback: &mut F) { if let Some((child, key)) = self.first_child.take() { let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); @@ -397,8 +359,8 @@ impl StackedItem2 } fn process_child< - F: ProcessStack, - >(&mut self, mut child: StackedItem2, key: &[u8], callback: &mut F) { + F: ProcessStack, + >(&mut self, mut child: StackedItem, key: &[u8], callback: &mut F) { // TODO switch to debug assert if correct asumption or call process first // child ordered with split child. assert!(child.first_child.is_none(), "guaranted by call to fix_node"); @@ -410,7 +372,7 @@ impl StackedItem2 } fn process_root< - F: ProcessStack, + F: ProcessStack, >(mut self, key: &[u8], callback: &mut F) { if let Some(first_child_index) = self.first_child_index() { if let Some(split_child_index) = self.split_child_index() { @@ -433,8 +395,8 @@ impl StackedItem2 // consume branch and return item to attach to parent fn fuse_branch< - F: ProcessStack, - >(&mut self, child: StackedItem2, key: &[u8], callback: &mut F) { + F: ProcessStack, + >(&mut self, child: StackedItem, key: &[u8], callback: &mut F) { let child_depth = self.depth + 1 + child.node.partial().map(|p| p.len()).unwrap_or(0); let to_rem = mem::replace(&mut self.node, child.node); // delete current @@ -454,242 +416,179 @@ impl StackedItem2 } } -impl StackedNode +impl StackedNode where B: Borrow<[u8]> + AsRef<[u8]>, T: TrieLayout, - S: Clone, -// TrieHash: AsRef<[u8]>, { /// Get extension part of the node (partial) if any. - pub fn is_empty(&self) -> bool { + fn is_empty(&self) -> bool { match self { - StackedNode::Unchanged(node, ..) => node.is_empty(), - StackedNode::Changed(node, ..) => node.is_empty(), - StackedNode::Deleted(..) => true, + StackedNode::Unchanged(node) => node.is_empty(), + StackedNode::Changed(node) => node.is_empty(), + StackedNode::Deleted => true, } } /// Get extension part of the node (partial) if any. - pub fn partial(&self) -> Option { + fn partial(&self) -> Option { match self { - StackedNode::Unchanged(node, ..) => node.partial(), - StackedNode::Changed(node, ..) => node.partial(), - StackedNode::Deleted(..) => None, + StackedNode::Unchanged(node) => node.partial(), + StackedNode::Changed(node) => node.partial(), + StackedNode::Deleted => None, } } /// Try to access child. - pub fn child(&self, ix: u8) -> Option { + fn child(&self, ix: u8) -> Option { match self { - StackedNode::Unchanged(node, ..) => node.child(ix), - StackedNode::Changed(node, ..) => node.child(ix), - StackedNode::Deleted(..) => None, + StackedNode::Unchanged(node) => node.child(ix), + StackedNode::Changed(node) => node.child(ix), + StackedNode::Deleted => None, } } /// Set a value if the node can contain one. - pub fn set_value(&mut self, value: &[u8]) { + fn set_value(&mut self, value: &[u8]) { match self { - StackedNode::Unchanged(node, state) => { + StackedNode::Unchanged(node) => { if let Some(new) = node.set_value(value) { - *self = StackedNode::Changed(new, state.clone()); + *self = StackedNode::Changed(new); } }, - StackedNode::Changed(node, ..) => node.set_value(value), - StackedNode::Deleted(..) => (), + StackedNode::Changed(node) => node.set_value(value), + StackedNode::Deleted => (), } } /// Change a partial if the node contains one. - pub fn advance_partial(&mut self, nb: usize) { + fn advance_partial(&mut self, nb: usize) { match self { - StackedNode::Unchanged(node, state) => { + StackedNode::Unchanged(node) => { if let Some(new) = node.advance_partial(nb) { - *self = StackedNode::Changed(new, state.clone()); + *self = StackedNode::Changed(new); } }, - StackedNode::Changed(node, ..) => node.advance_partial(nb), - StackedNode::Deleted(..) => (), + StackedNode::Changed(node) => node.advance_partial(nb), + StackedNode::Deleted => (), } } /// Set a new partial. - pub fn set_partial(&mut self, partial: NodeKey) { + fn set_partial(&mut self, partial: NodeKey) { match self { - StackedNode::Unchanged(node, state) => { + StackedNode::Unchanged(node) => { if let Some(new) = node.set_partial(partial) { - *self = StackedNode::Changed(new, state.clone()); + *self = StackedNode::Changed(new); } }, - StackedNode::Changed(node, ..) => node.set_partial(partial), - StackedNode::Deleted(..) => (), + StackedNode::Changed(node) => node.set_partial(partial), + StackedNode::Deleted => (), } } /// Remove a value if the node contains one. - pub fn remove_value(&mut self) { + fn remove_value(&mut self) { match self { - StackedNode::Unchanged(node, state) => { + StackedNode::Unchanged(node) => { match node.remove_value() { Some(Some(new)) => - *self = StackedNode::Changed(new, state.clone()), + *self = StackedNode::Changed(new), Some(None) => - *self = StackedNode::Deleted(state.clone()), + *self = StackedNode::Deleted, None => (), } }, - StackedNode::Changed(node, state) => { + StackedNode::Changed(node) => { if node.remove_value() { - *self = StackedNode::Deleted(state.clone()); + *self = StackedNode::Deleted; } }, - StackedNode::Deleted(..) => (), - } - } - - /// Set a handle to a child node, changing existing to a new branch, and returning the new child. - pub fn set_mid_handle( - &mut self, - handle: OwnedNodeHandle>, - index: u8, - common_depth: usize, - ) -> Self { - match self { - StackedNode::Unchanged(node, state) => { - let (new, child) = node.set_mid_handle(handle, index, common_depth); - let result = StackedNode::Changed(child, state.clone()); - *self = StackedNode::Changed(new, state.clone()); - result - }, - StackedNode::Changed(node, state) => { - StackedNode::Changed(node.set_mid_handle(handle, index, common_depth), state.clone()) - }, - StackedNode::Deleted(..) => unreachable!(), + StackedNode::Deleted => (), } } /// Set a handle to a child node or remove it if handle is none. - pub fn set_handle(&mut self, handle: Option>>, index: u8) { + fn set_handle(&mut self, handle: Option>>, index: u8) { match self { - StackedNode::Unchanged(node, state) => { + StackedNode::Unchanged(node) => { let change = node.set_handle(handle, index); match change { - Some(new) => - *self = StackedNode::Changed(new, state.clone()), + Some(new) => *self = StackedNode::Changed(new), None => (), } }, - StackedNode::Changed(node, state) => { + StackedNode::Changed(node) => { node.set_handle(handle, index); }, - StackedNode::Deleted(..) => unreachable!(), + StackedNode::Deleted => unreachable!(), } } /// Returns index of node to fuse with if fused require. - pub fn fix_node(&mut self, pending: (Option, Option)) -> Option { + fn fix_node(&mut self, pending: (Option, Option)) -> Option { match self { - StackedNode::Deleted(..) + StackedNode::Deleted | StackedNode::Unchanged(..) => None, - StackedNode::Changed(node, state) => { + StackedNode::Changed(node) => { let (deleted, fuse) = node.fix_node(pending); if deleted { - *self = StackedNode::Deleted(state.clone()); + *self = StackedNode::Deleted; } fuse }, } } - - /// Fuse changed node that need to be reduce to a child node, - /// returns additional length to prefix. - pub fn fuse_child(&mut self, child: OwnedNode, child_ix: u8) -> usize { - match self { - StackedNode::Unchanged(node, state) => unreachable!(), - StackedNode::Changed(node, state) => { - node.fuse_child(child, child_ix) - }, - StackedNode::Deleted(..) => unreachable!(), - } - } - } -impl StackedNode +impl StackedNode where B: Borrow<[u8]> + AsRef<[u8]>, T: TrieLayout, - S: Clone, { /// Encode node - pub fn into_encoded(self) -> Vec { + fn into_encoded(self) -> Vec { match self { - StackedNode::Unchanged(node, ..) => node.data().to_vec(), - StackedNode::Changed(node, ..) => node.into_encoded::<_, T::Codec, T::Hash>( - |child, o_slice, o_index| { + StackedNode::Unchanged(node) => node.data().to_vec(), + StackedNode::Changed(node) => node.into_encoded::<_, T::Codec, T::Hash>( + |child, _o_slice, _o_index| { child.as_child_ref::() }), - StackedNode::Deleted(..) => T::Codec::empty_node().to_vec(), + StackedNode::Deleted => T::Codec::empty_node().to_vec(), } } } /// Visitor trait to implement when using `trie_traverse_key`. -pub trait ProcessStack +trait ProcessStack where T: TrieLayout, - S: Clone, B: Borrow<[u8]> + AsRef<[u8]>, { - // /// Callback on enter a node, change can be applied here. - // fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode); - /// Same as `enter` but at terminal node element (terminal considering next key so branch is - /// possible here). - /// TODO prefix semantic here is strange: it is prefix of child. - fn enter_terminal( - &mut self, - stacked: &mut StackedItem, - key_element: &[u8], - value_element: Option<&[u8]>, - state: TraverseState, - ) -> Option>; - /// Descend node, it should (if we want update): /// - return a new child for the new value. /// - replace `self` by a new branch with `self` as its split child /// and a new child for the new value. /// - change value of `self` only. - fn enter_terminal2( + fn enter_terminal( &mut self, - stacked: &mut StackedItem2, + stacked: &mut StackedItem, key_element: &[u8], value_element: Option<&[u8]>, state: TraverseState, - ) -> Option>; + ) -> Option>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>>; /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, stacked: StackedNode, prev_hash: Option<&TrieHash>); - - - /// Fetching a fuse latest, that is the latest changed value of a branch. - /// This is called once per level in case of removal/fusion of a branch with - /// a child - /// This is only needed if changes are made by process stack. - /// Access to this method means that the node need to be remove or invalidate (this is handled - /// by this method (no call to exit on it)). - /// TODO EMCH this method should be removed, by only calling exit for child of branch when going - /// up (keeping a reference to each child instead of current only!!). - fn fuse_latest_changed(&mut self, prefix: Prefix, hash: &TrieHash) -> Option<&[u8]>; + fn exit_root(&mut self, stacked: StackedNode, prev_hash: Option<&TrieHash>); } /// State when descending -pub enum TraverseState { +enum TraverseState { /// This is the right node for value. ValueMatch, /// after node @@ -698,59 +597,8 @@ pub enum TraverseState { MidPartial(usize), } -/// Descend into a node, depending on callback it can produce a new node -/// (and change existing one). -fn descend_terminal( - item: &mut StackedItem, - key: &K, - value: Option<&V>, - callback: &mut F, -) -> (Option>, Option>) - where - T: TrieLayout, - K: AsRef<[u8]> + Ord, - V: AsRef<[u8]>, - S: Default + Clone, - B: Borrow<[u8]> + AsRef<[u8]>, - F: ProcessStack, -{ - let key_dest = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - let slice_dest = NibbleSlice::new_offset(key.as_ref(), item.depth_prefix); - let target_common_depth = item.node.partial() - .map(|p| item.depth_prefix + p.common_prefix(&slice_dest)) - .unwrap_or(item.depth); - debug_assert!(!(target_common_depth < item.depth_prefix), "Descend should not be call in this state"); - if target_common_depth < item.depth { - // insert into prefix - (None, callback.enter_terminal( - item, - key.as_ref(), - value.as_ref().map(|v| v.as_ref()), - TraverseState::MidPartial(target_common_depth), - )) - } else if key_dest == item.depth { - // set value - let n = callback.enter_terminal( - item, - key.as_ref(), - value.as_ref().map(|v| v.as_ref()), - TraverseState::ValueMatch, - ); - debug_assert!(n.is_none()); - (None, None) - } else { - // extend - (callback.enter_terminal( - item, - key.as_ref(), - value.as_ref().map(|v| v.as_ref()), - TraverseState::AfterNode, - ), None) - } -} - /// The main entry point for traversing a trie by a set of keys. -pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( +fn trie_traverse_key<'a, T, I, K, V, B, F>( db: &'a dyn HashDBRef, root_hash: &'a TrieHash, elements: I, @@ -761,12 +609,11 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( I: IntoIterator)>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, - S: Default + Clone, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, - F: ProcessStack, + F: ProcessStack, { // Stack of traversed nodes - let mut stack: Vec> = Vec::with_capacity(32); + let mut stack: Vec> = Vec::with_capacity(32); let root = if let Ok(root) = fetch::(db, root_hash, EMPTY_PREFIX) { root @@ -775,9 +622,9 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( }; // TODO encapsulate fetch in stacked item, also split or others - let current = StackedNode::::Unchanged(root, Default::default()); + let current = StackedNode::::Unchanged(root); let depth = current.partial().map(|p| p.len()).unwrap_or(0); - let mut current = StackedItem2 { + let mut current = StackedItem { node: current, hash: Some(*root_hash), depth_prefix: 0, @@ -831,7 +678,7 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( // value replace callback TraverseState::ValueMatch }; - callback.enter_terminal2( + callback.enter_terminal( &mut current, key.as_ref(), value.as_ref().map(|v| v.as_ref()), @@ -952,346 +799,6 @@ pub fn trie_traverse_key2<'a, T, I, K, V, S, B, F>( Ok(()) } - -/// The main entry point for traversing a trie by a set of keys. -pub fn trie_traverse_key<'a, T, I, K, V, S, B, F>( - db: &'a dyn HashDBRef, - root_hash: &'a TrieHash, - elements: I, - callback: &mut F, -) -> Result<(), TrieHash, CError> - where - T: TrieLayout, - I: IntoIterator)>, - K: AsRef<[u8]> + Ord, - V: AsRef<[u8]>, - S: Default + Clone, - B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, - F: ProcessStack, -{ - // stack of traversed nodes - // first usize is depth, second usize is the parent index. - let mut stack: Vec> = Vec::with_capacity(32); - - // TODO EMCH do following update (used for error only) - let root = if let Ok(root) = fetch::(db, root_hash, EMPTY_PREFIX) { - root - } else { - return Err(Box::new(TrieError::InvalidStateRoot(*root_hash))); - }; -// stack.push(StackedNode::Unchanged(root, Default::default())); - - let current = StackedNode::::Unchanged(root, Default::default()); - let depth = current.partial().map(|p| p.len()).unwrap_or(0); - let mut current = StackedItem { - node: current, - hash: Some(*root_hash), - depth_prefix: 0, - depth, - parent_index: 0, - split_child: false, - }; - - let mut k: Option = None; - - // TODO smal child that ? - let mut split_child: Vec<(StackedItem, Vec)> = Default::default(); - let mut limit_common = usize::max_value(); - for (next_k, v) in elements.into_iter() { - if let Some(previous_key) = k { - let mut target_common_depth = nibble_ops::biggest_depth( - previous_key.as_ref(), - next_k.as_ref(), - ); - /*if target_common_depth == limit_common { - // try to see if more common in current node - // TODO this is redundant with start of descend. - let slice_dest = NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix); - target_common_depth = current.node.partial() - .map(|p| current.depth_prefix + p.common_prefix(&slice_dest)) - .unwrap_or(current.depth_prefix); - } else { - target_common_depth = min(limit_common, target_common_depth); - }*/ - //target_common_depth = min(limit_common, target_common_depth); - while target_common_depth < current.depth_prefix { - // go up - if let Some(mut last) = stack.pop() { - if !last.node.is_empty() { - align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix).left(), - current.node, current.hash.as_ref(), - ) { - last.node.set_handle(handle, current.parent_index); - } - current = last; - } - } else { - align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; - callback.exit_root(current.node, current.hash.as_ref()); - return Ok(()); - } - } - } - k = Some(next_k); - - if let Some(k) = k.as_ref() { - let dest = NibbleFullKey::new(k.as_ref()); - let dest_depth = k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - - loop { - let slice_dest = NibbleSlice::new_offset(k.as_ref(), current.depth_prefix); - - limit_common = usize::max_value(); - let target_common_depth = current.node.partial() - .map(|p| current.depth_prefix + p.common_prefix(&slice_dest)) - .unwrap_or(current.depth_prefix); - let (child, parent_index) = if target_common_depth == current.depth { - if dest_depth > current.depth { - let next_index = dest.at(current.depth); - if current.split_child { - if next_index == split_child.last().map(|(c, _)| c.parent_index) - .expect("split child set before") { - current.split_child = false; - stack.push(current); - let (ch, v) = split_child.pop() - .expect("split child set before"); - current = ch; - continue; - } - } - (current.node.child(next_index), next_index) - } else { - (None, 0) - } - } else { - // TODO here we could use this common depth to avoid double calc in descend!! - (None, 0) - }; - if dest_depth > current.depth && child.is_some() { - // non terminal - let depth_prefix = current.depth + 1; - let (node, hash) = match child { - Some(NodeHandle::Hash(handle_hash)) => { - let mut hash = as Default>::default(); - hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - (StackedNode::Unchanged( - fetch::( - db, &hash, - NibbleSlice::new_offset(k.as_ref(), depth_prefix).left(), - )?, - Default::default(), - ), Some(hash)) - }, - Some(NodeHandle::Inline(node_encoded)) => { - (StackedNode::Unchanged( - // Instantiating B is only for inline node, still costy. - OwnedNode::new::(B::from(node_encoded)) - .map_err(|e| Box::new(TrieError::DecoderError( - current.hash.clone().unwrap_or_else(Default::default), - e, - )))?, - Default::default(), - ), None) - }, - None => { - unreachable!("Depth checked previously"); - }, - }; - let depth = depth_prefix + node.partial().map(|p| p.len()).unwrap_or(0); - stack.push(current); - current = StackedItem { - node, - hash, - depth_prefix, - depth, - parent_index, - split_child: false, - }; - } else { - // remove empties - while current.node.is_empty() && stack.len() > 0 { - // TODO runing into this is not expected - if let Some(mut prev) = stack.pop() { - callback.exit( - NibbleSlice::new_offset(k.as_ref(), current.depth_prefix).left(), - current.node, current.hash.as_ref(), - ).expect("no new node on empty"); - prev.node.set_handle(None, current.parent_index); - current = prev; - } - } - if current.node.is_empty() { - // corner case of empty trie - if let Some(v) = v.as_ref() { - let leaf = Node::new_leaf( - NibbleSlice::new_offset(k.as_ref(), current.depth_prefix), - v.as_ref(), - ); - current = StackedItem { - node: StackedNode::Changed(leaf, Default::default()), - hash: current.hash, - depth_prefix: current.depth_prefix, - depth: k.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - current.depth_prefix, - parent_index: current.parent_index, - split_child: false, - } - } - } else { - // terminal case - match descend_terminal(&mut current, k, v.as_ref(), callback) { - (Some(new), _) => { - stack.push(current); - current = new; - limit_common = target_common_depth; - }, - (_, Some(split)) => { - split_child.push((split, k.as_ref().to_vec())); - continue; - }, - _ => (), - } - } - // go next key - break; - } - } - } - } - - if let Some(previous_key) = k { - // go up - while let Some(mut last) = stack.pop() { - if !last.node.is_empty() { - align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(previous_key.as_ref(), current.depth_prefix).left(), - current.node, current.hash.as_ref(), - ) { - last.node.set_handle(handle, current.parent_index); - } - current = last; - } - } - align_node(db, callback, &mut current, previous_key.as_ref(), None, &mut split_child)?; - callback.exit_root(current.node, current.hash.as_ref()); - return Ok(()); - } - - Ok(()) -} - -fn align_node<'a, T, S, B, F>( - db: &'a dyn HashDBRef, - callback: &mut F, - branch: &mut StackedItem, - key: &[u8], - mut prefix: Option<&mut NibbleVec>, - split_child: &mut Vec<(StackedItem, Vec)>, -) -> Result<(), TrieHash, CError> - where - T: TrieLayout, - S: Default + Clone, - B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, - F: ProcessStack, -{ - let init_prefix_len = prefix.as_ref().map(|p| p.len()) - .unwrap_or(0); - if branch.split_child { - branch.split_child = false; - let (mut child, check) = split_child.pop() - .expect("trie correct parsing ensure it is set"); - let mut build_prefix: NibbleVec; - // Rebuild the right prefix by removing last nibble and adding parent child. - let prefix: &mut NibbleVec = if let Some(prefix) = prefix.as_mut() { - prefix - } else { - build_prefix = NibbleVec::from(key, branch.depth_prefix); - &mut build_prefix - }; - branch.node.partial().map(|p| { - prefix.append_partial(p.right()); - }); - prefix.push(child.parent_index); -/* child.node.partial().map(|p| { - prefix.append_partial(p.right()); - });*/ - let len_prefix = prefix.len(); - align_node(db, callback, &mut child, key, Some(prefix), split_child)?; - prefix.drop_lasts(prefix.len() - len_prefix); - let handle = callback.exit( - prefix.as_prefix(), - child.node, child.hash.as_ref(), - ).expect("split child is always a changed node"); - // TODO if it is single handle, then fix node will reaccess that: - // that is one hash creation here and one access then deletion afterward. - branch.node.set_handle(handle, child.parent_index); - } - if let Some(fuse_index) = branch.node.fix_node((None, None)) { - let mut build_prefix: NibbleVec; - let (child, hash) = match branch.node.child(fuse_index) { - Some(NodeHandle::Hash(handle_hash)) => { - let mut hash = as Default>::default(); - hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - let prefix: &mut NibbleVec = if let Some(prefix) = prefix.as_mut() { - let len_prefix = prefix.len(); - if len_prefix > init_prefix_len { - prefix.drop_lasts(len_prefix - init_prefix_len); - } - prefix - } else { - build_prefix = NibbleVec::from(key, branch.depth_prefix); - &mut build_prefix - }; - branch.node.partial().map(|p| { - prefix.append_partial(p.right()); - }); - - // TODO conversion to NibbleVec is slow - prefix.push(fuse_index); - let prefix = prefix.as_prefix(); - if let Some(node_encoded) = callback.fuse_latest_changed( - prefix, - &hash, - ) { - // costy encode decode round trip, but this is a corner case. - (OwnedNode::new::(B::from(node_encoded)) - .map_err(|e| Box::new(TrieError::DecoderError( - branch.hash.clone().unwrap_or_else(Default::default), - e, - )))?, None) - } else { - (fetch::( - db, - &hash, - prefix, - )?, Some((hash, prefix))) - } - }, - Some(NodeHandle::Inline(node_encoded)) => { - (OwnedNode::new::(B::from(node_encoded)) - .map_err(|e| Box::new(TrieError::DecoderError( - branch.hash.clone().unwrap_or_else(Default::default), - e, - )))?, None) - }, - None => unreachable!("correct index used"), - }; - if let Some((hash, prefix)) = hash { - // register delete - callback.exit( - NibbleSlice::new_offset(key, branch.depth).left(), - StackedNode::Deleted(Default::default()), - Some(&hash), - ).expect("No new node on deleted allowed"); - } - branch.depth += branch.node.fuse_child(child, fuse_index); - } - - Ok(()) -} - /// Fetch a node by hash, do not cache it. fn fetch>( @@ -1311,28 +818,24 @@ fn fetch>( /// Contains ordered node change for this iteration. /// The resulting root hash. /// The latest changed node. -pub struct BatchUpdate( - pub Vec<((BackingByteVec, Option), H, Option>, bool)>, - pub H, - pub Option, +struct BatchUpdate( + Vec<(OwnedPrefix, H, Option>)>, + H, + Option, ); -impl ProcessStack for BatchUpdate> +impl ProcessStack for BatchUpdate> where B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, - S: Clone + Default, { - //fn enter(&mut self, prefix: NibbleSlice, stacked: &mut StackedNode) { - //} - fn enter_terminal( &mut self, - stacked: &mut StackedItem, + stacked: &mut StackedItem, key_element: &[u8], value_element: Option<&[u8]>, state: TraverseState, - ) -> Option> { + ) -> Option> { match state { TraverseState::ValueMatch => { if let Some(value) = value_element { @@ -1358,98 +861,7 @@ impl ProcessStack for BatchUpdate> let parent_index = NibbleSlice::new(key_element).at(stacked.depth); // append to parent is done on exit through changed nature of the new leaf. return Some(StackedItem { - node: StackedNode::Changed(dest_leaf, Default::default()), - hash: None, - depth_prefix: stacked.depth + offset, - depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index, - split_child: false, - }); - } else { - // nothing to delete. - return None; - } - }, - TraverseState::MidPartial(mid_index) => { - if let Some(val) = value_element { - let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { - let new_slice = NibbleSlice::new_offset( - &key_element[..mid_index / nibble_ops::NIBBLE_PER_BYTE], - stacked.depth_prefix, - ); - Node::new_branch(new_slice) - } else { - let new_slice = NibbleSlice::new_offset( - &key_element[..], - stacked.depth_prefix, - ); - let owned = new_slice.to_stored_range(mid_index - stacked.depth_prefix); - // TODO EMCH refactor new_leaf to take BackingByteVec (stored) as input - Node::new_branch(NibbleSlice::from_stored(&owned)) - }; - let old_depth = stacked.depth; - stacked.depth = mid_index; - let mut child = mem::replace( - &mut stacked.node, - StackedNode::Changed(dest_branch, Default::default()), - ); - - let parent_index = child.partial() - .map(|p| p.at(mid_index - stacked.depth_prefix)).unwrap_or(0); - - child.advance_partial(1 + mid_index - stacked.depth_prefix); - // not setting child relation (will be set on exit) - let child = StackedItem { - node: child, - hash: None, - depth_prefix: 1 + mid_index, - depth: old_depth, - parent_index, - split_child: stacked.split_child, - }; - stacked.split_child = true; - return Some(child); - } else { - // nothing to delete. - return None; - } - }, - } - } - - fn enter_terminal2( - &mut self, - stacked: &mut StackedItem2, - key_element: &[u8], - value_element: Option<&[u8]>, - state: TraverseState, - ) -> Option> { - match state { - TraverseState::ValueMatch => { - if let Some(value) = value_element { - stacked.node.set_value(value); - } else { - stacked.node.remove_value(); - } - None - }, - TraverseState::AfterNode => { - if let Some(val) = value_element { - // corner case of empty trie. - let offset = if stacked.node.is_empty() { - 0 - } else { - 1 - }; - // dest is a leaf appended to terminal - let dest_leaf = Node::new_leaf( - NibbleSlice::new_offset(key_element, stacked.depth + offset), - val, - ); - let parent_index = NibbleSlice::new(key_element).at(stacked.depth); - // append to parent is done on exit through changed nature of the new leaf. - return Some(StackedItem2 { - node: StackedNode::Changed(dest_leaf, Default::default()), + node: StackedNode::Changed(dest_leaf), hash: None, depth_prefix: stacked.depth + offset, depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, @@ -1473,8 +885,8 @@ impl ProcessStack for BatchUpdate> NibbleSlice::new_offset(key_element, 1 + mid_index), value.as_ref(), ); - let child = StackedItem2 { - node: StackedNode::Changed(child, Default::default()), + let child = StackedItem { + node: StackedNode::Changed(child), hash: None, depth_prefix: 1 + mid_index, depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, @@ -1494,12 +906,12 @@ impl ProcessStack for BatchUpdate> } - fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) -> Option>>> { match stacked { - StackedNode::Changed(node, _) => Some(Some({ + StackedNode::Changed(node) => Some(Some({ let encoded = node.into_encoded::<_, T::Codec, T::Hash>( - |child, o_slice, o_index| { + |child, _o_slice, _o_index| { child.as_child_ref::() } ); @@ -1510,16 +922,16 @@ impl ProcessStack for BatchUpdate> // register latest change self.2 = Some(self.0.len()); // costy clone (could get read from here) - self.0.push((owned_prefix(&prefix), hash.clone(), Some(encoded), true)); + self.0.push((owned_prefix(&prefix), hash.clone(), Some(encoded))); if let Some(h) = prev_hash { - self.0.push((owned_prefix(&prefix), h.clone(), None, true)); + self.0.push((owned_prefix(&prefix), h.clone(), None)); } OwnedNodeHandle::Hash(hash) } })), - StackedNode::Deleted(..) => { + StackedNode::Deleted => { if let Some(h) = prev_hash { - self.0.push((owned_prefix(&prefix), h.clone(), None, true)); + self.0.push((owned_prefix(&prefix), h.clone(), None)); } Some(None) }, @@ -1527,33 +939,22 @@ impl ProcessStack for BatchUpdate> } } - fn exit_root(&mut self, stacked: StackedNode, prev_hash: Option<&TrieHash>) { + fn exit_root(&mut self, stacked: StackedNode, prev_hash: Option<&TrieHash>) { let prefix = EMPTY_PREFIX; match stacked { - s@StackedNode::Deleted(..) + s@StackedNode::Deleted | s@StackedNode::Changed(..) => { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); self.1 = hash.clone(); - self.0.push((owned_prefix(&prefix), hash, Some(encoded), true)); + self.0.push((owned_prefix(&prefix), hash, Some(encoded))); if let Some(h) = prev_hash { - self.0.push((owned_prefix(&prefix), h.clone(), None, true)); + self.0.push((owned_prefix(&prefix), h.clone(), None)); } }, _ => (), } } - - fn fuse_latest_changed(&mut self, prefix: Prefix, hash: &TrieHash) -> Option<&[u8]> { - for latest in 0..self.0.len() { - let stored_slice = from_owned_prefix(&self.0[latest].0); - if hash == &self.0[latest].1 && prefix == stored_slice { - self.0[latest].3 = false; - return self.0[latest].2.as_ref().map(|s| &s[..]); - } - } - None - } } @@ -1561,32 +962,57 @@ fn owned_prefix(prefix: &Prefix) -> (BackingByteVec, Option) { (prefix.0.into(), prefix.1) } -fn from_owned_prefix(prefix: &(BackingByteVec, Option)) -> Prefix { +/// Extract prefix from a owned prefix. +pub fn from_owned_prefix(prefix: &OwnedPrefix) -> Prefix { (&prefix.0[..], prefix.1) } +/// Update trie, returning deltas and root. +/// TODO this put all in memory in a vec: we could stream the process +/// (would be really good for very big updates). -> then remove root +/// from result and Batch update (is simply latest hash of iter (given +/// delete after insert)). +pub fn batch_update<'a, T, I, K, V, B>( + db: &'a dyn HashDBRef, + root_hash: &'a TrieHash, + elements: I, +) -> Result<(TrieHash, impl Iterator, Option>)>), TrieHash, CError> + where + T: TrieLayout, + I: IntoIterator)>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, +{ + let mut batch_update = BatchUpdate( + Default::default(), + root_hash.clone(), + None, + ); + trie_traverse_key::(db, root_hash, elements, &mut batch_update)?; + // TODO when remove third elt of batchupdate the map gets useless + Ok((batch_update.1, batch_update.0.into_iter().map(|i| (i.0, i.1, i.2)))) +} + #[cfg(test)] mod tests { - use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, TrieMut, NodeCodec, - ReferenceNodeCodec, reference_trie_root, reference_trie_root_no_extension, - NoExtensionLayout, TrieLayout, trie_traverse_key_no_extension_build, BatchUpdate, - NibbleVec, + use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, TrieMut, + trie_traverse_key_no_extension_build, }; use memory_db::{MemoryDB, PrefixedKey}; use keccak_hasher::KeccakHasher; - use crate::{DBValue, nibble::BackingByteVec}; - use hash_db::{EMPTY_PREFIX, Prefix, HashDBRef, HashDB}; + use crate::{DBValue, OwnedPrefix}; + use hash_db::HashDB; use crate::triedbmut::tests::populate_trie_no_extension; type H256 = ::Out; fn memory_db_from_delta( - delta: Vec<((BackingByteVec, Option), H256, Option>, bool)>, + delta: impl Iterator>)>, mdb: &mut MemoryDB, DBValue>, ) { - for (p, h, v, d) in delta { - if d { + for (p, h, v) in delta { if let Some(v) = v { let prefix = (p.0.as_ref(), p.1); // damn elastic array in value looks costy @@ -1595,7 +1021,6 @@ mod tests { let prefix = (p.0.as_ref(), p.1); mdb.remove(&h, prefix); } - } } } @@ -1634,23 +1059,17 @@ mod tests { // let mut reference_delta = db; - let reference_root = root.clone(); - - let mut batch_update = BatchUpdate(Default::default(), initial_root.clone(), None); - trie_traverse_key_no_extension_build( + let (calc_root, payload) = trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, v.iter().map(|(a, b)| (a, b.as_ref())), - &mut batch_update, ); - assert_eq!(batch_update.1, reference_root); -println!("{:?}", batch_update.0); + assert_eq!(calc_root, root); let mut batch_delta = initial_db; - let r2 = batch_update.1.clone(); // - memory_db_from_delta(batch_update.0, &mut batch_delta); + memory_db_from_delta(payload, &mut batch_delta); /* // sort let batch_delta: std::collections::BTreeMap<_, _> = batch_delta.drain().into_iter().collect(); @@ -1673,7 +1092,7 @@ println!("{:?}", batch_update.0); // test by checking both triedb only let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); println!("{:?}", t2); - let t2b = RefTrieDBNoExt::new(&batch_delta, &r2).unwrap(); + let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); println!("{:?}", t2b); // let t1 = RefTrieDBNoExt::new(&batch_delta, &root).unwrap(); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 4f9bab63..faf19f5e 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -20,13 +20,11 @@ use super::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, decode_h use crate::node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; -use crate::node::OwnedNode; use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; use crate::nibble::{NibbleVec, NibbleSlice, nibble_ops, BackingByteVec}; use crate::core_::convert::TryFrom; use crate::core_::mem; use crate::core_::ops::Index; -use crate::core_::borrow::Borrow; use crate::core_::hash::Hash; use crate::core_::result; @@ -135,38 +133,6 @@ pub enum Node { NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), } -impl + Default> Node> { - /// Fuse changed node that need to be reduce to a child node, - /// return additional length to prefix. - pub fn fuse_child>(&mut self, mut child: OwnedNode, child_ix: u8) -> usize { - let node = mem::replace(self, Node::Empty); - let (node, result) = match node { - Node::Extension(..) - | Node::Branch(..) => unreachable!("Only for no extension trie"), - // need to replace value before creating handle - // so it is a corner case TODO test case it - // (may be unreachable since changing to empty means - // we end the root process) - Node::Empty - | Node::Leaf(..) => unreachable!("method only for nodes resulting from fix node"), - Node::NibbledBranch(mut partial, _children, _val) => { - // TODO could use owned nibble slice or optimize this - combine_key(&mut partial, (nibble_ops::NIBBLE_PER_BYTE - 1, &[child_ix][..])); - let child_partial = child.partial(); - let result = child_partial.as_ref().map(|p| p.len()).unwrap_or(0) + 1; - combine_key(&mut partial, child_partial - .map(|n| n.right_ref()).unwrap_or((0,&[]))); - - (child.set_partial(partial) - .expect("checked child node before call is expected"), - result) - } - }; - *self = node; - result - } -} - impl Node { /// Tell if it is the empty node. From 404164d9d4375d3b0936533c3dc4e6792b1dbae6 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 15 Jan 2020 11:30:04 +0100 Subject: [PATCH 052/118] empty trie case. --- trie-db/src/node.rs | 10 ++++- trie-db/src/traverse.rs | 81 ++++++++++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index e2f2b234..ed6e6d69 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -361,6 +361,12 @@ impl> OwnedNode { pub fn set_value + Default>(&mut self, new_value: &[u8]) -> Option>> { let data = &self.data.borrow(); match &self.plan { + NodePlan::Empty => { + Some(TNode::Leaf( + Default::default(), + new_value.into(), + )) + }, NodePlan::Leaf { partial, value } => { if &data[value.clone()] == new_value { return None; @@ -371,8 +377,8 @@ impl> OwnedNode { )) }, NodePlan::Extension { .. } // TODO Extension - | NodePlan::Branch { .. } // TODO branch - | NodePlan::Empty => None, + // TODO branch + | NodePlan::Branch { .. } => None, NodePlan::NibbledBranch { partial, value, children } => { if let Some(value) = value { if &data[value.clone()] == new_value { diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 94f2c0a8..bf28be81 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -637,30 +637,33 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let mut previous_key: Option = None; - for next_query in elements.into_iter().map(|e| Some(e)).chain(None) { + for next_query in elements.into_iter().map(|e| Some(e)).chain(Some(None)) { let mut next_key = None; + let last = next_query.is_none(); // PATH DOWN descending in next_query. if let Some((key, value)) = next_query { let dest_slice = NibbleFullKey::new(key.as_ref()); let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - - loop { - let common_index = current.node.partial() - .map(|current_partial| { - let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); - current.depth_prefix + current_partial.common_prefix(&target_partial) - }).unwrap_or(current.depth_prefix); - // TODO not sure >= or just >. - if common_index == current.depth_prefix && dest_depth >= current.depth { - let next_index = dest_slice.at(current.depth); - let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth); - if let Some(child) = current.descend_child(next_index, db, prefix.left())? { - current = child; + if !current.node.is_empty() { + // corner case do not descend in empty node + loop { + let common_index = current.node.partial() + .map(|current_partial| { + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + current.depth_prefix + current_partial.common_prefix(&target_partial) + }).unwrap_or(current.depth_prefix); + // TODO not sure >= or just >. + if common_index == current.depth_prefix && dest_depth >= current.depth { + let next_index = dest_slice.at(current.depth); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth + 1); + if let Some(child) = current.descend_child(next_index, db, prefix.left())? { + current = child; + } else { + break; + } } else { break; } - } else { - break; } } let traverse_state = if dest_depth < current.depth { @@ -678,12 +681,15 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // value replace callback TraverseState::ValueMatch }; - callback.enter_terminal( + if let Some(new_child) = callback.enter_terminal( &mut current, key.as_ref(), value.as_ref().map(|v| v.as_ref()), traverse_state, - ); + ) { + stack.push(current); + current = new_child; + } next_key = Some(key) } // PATH UP over the previous key and value @@ -694,7 +700,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( )).unwrap_or(0); // last element goes up to root // unstack nodes if needed - while target_common_depth < current.depth_prefix { + while last || target_common_depth < current.depth_prefix { // TODO check if fuse (num child is 1). // child change or addition @@ -792,7 +798,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( return Ok(()); } } - } previous_key = next_key; } @@ -846,6 +851,7 @@ impl ProcessStack for BatchUpdate> None }, TraverseState::AfterNode => { + if let Some(val) = value_element { // corner case of empty trie. let offset = if stacked.node.is_empty() { @@ -859,8 +865,7 @@ impl ProcessStack for BatchUpdate> val, ); let parent_index = NibbleSlice::new(key_element).at(stacked.depth); - // append to parent is done on exit through changed nature of the new leaf. - return Some(StackedItem { + let mut new_child = StackedItem { node: StackedNode::Changed(dest_leaf), hash: None, depth_prefix: stacked.depth + offset, @@ -869,7 +874,16 @@ impl ProcessStack for BatchUpdate> split_child: None, first_child: None, did_first_child: false, - }); + }; + return if stacked.node.is_empty() { + // replace empty. + new_child.hash = stacked.hash; + *stacked = new_child; + None + } else { + // append to parent is done on exit through changed nature of the new leaf. + Some(new_child) + }; } else { // nothing to delete. return None; @@ -1102,7 +1116,24 @@ mod tests { // panic!("!!END!!"); } - + #[test] + fn empty_node_null_key() { + compare_with_triedbmut( + &[], + &[ + (vec![], Some(vec![0xffu8, 0x33])), + ], + ); + } + #[test] + fn empty_node_with_key() { + compare_with_triedbmut( + &[], + &[ + (vec![0x04u8], Some(vec![0xffu8, 0x33])), + ], + ); + } #[test] fn dummy1() { compare_with_triedbmut( @@ -1111,7 +1142,7 @@ mod tests { ], &[ (vec![0x04u8], Some(vec![0xffu8, 0x33])), - (vec![32u8], Some(vec![0xffu8, 0x33])), + //(vec![32u8], Some(vec![0xffu8, 0x33])), ], ); } From 61faf97f211e0f75176043402df99f7a9e1bf049 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 15 Jan 2020 12:46:03 +0100 Subject: [PATCH 053/118] insert null at non empty --- trie-db/src/traverse.rs | 63 ++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index bf28be81..280e180c 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -386,6 +386,8 @@ impl StackedItem } else { self.process_first_child(callback); } + } else { + self.process_split_child(key, callback); } callback.exit_root( self.node, @@ -644,6 +646,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if let Some((key, value)) = next_query { let dest_slice = NibbleFullKey::new(key.as_ref()); let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + let mut descend_mid_index = None; if !current.node.is_empty() { // corner case do not descend in empty node loop { @@ -658,15 +661,19 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth + 1); if let Some(child) = current.descend_child(next_index, db, prefix.left())? { current = child; - } else { - break; } } else { + if common_index < current.depth { + descend_mid_index = Some(common_index); + } break; } } } - let traverse_state = if dest_depth < current.depth { + let traverse_state = if let Some(mid_index) = descend_mid_index { + TraverseState::MidPartial(mid_index) + } else if dest_depth < current.depth { + // TODO this might be unreachable from previous loop // split child (insert in current prefix let mid_index = current.node.partial() .map(|current_partial| { @@ -892,24 +899,35 @@ impl ProcessStack for BatchUpdate> TraverseState::MidPartial(mid_index) => { if let Some(value) = value_element { stacked.split_child(mid_index, key_element); - // TODO not sure on index - let parent_index = NibbleSlice::new(key_element).at(mid_index); + let (offset, parent_index) = if key_element.len() == 0 { + // corner case of adding at top of trie + (0, 0) + } else { + // TODO not sure on index + (1, NibbleSlice::new(key_element).at(mid_index)) + }; let child = Node::new_leaf( // TODO not sure on '1 +' - NibbleSlice::new_offset(key_element, 1 + mid_index), + NibbleSlice::new_offset(key_element, offset + mid_index), value.as_ref(), ); - let child = StackedItem { - node: StackedNode::Changed(child), - hash: None, - depth_prefix: 1 + mid_index, - depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index, - split_child: None, - first_child: None, - did_first_child: false, - }; - return Some(child); + return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { + // set value in new branch + stacked.node.set_value(value); + None + } else { + let child = StackedItem { + node: StackedNode::Changed(child), + hash: None, + depth_prefix: offset + mid_index, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + split_child: None, + first_child: None, + did_first_child: false, + }; + Some(child) + } } else { // nothing to delete. return None; @@ -1126,6 +1144,17 @@ mod tests { ); } #[test] + fn non_empty_node_null_key() { + compare_with_triedbmut( + &[ + (vec![0x0u8], vec![4, 32]), + ], + &[ + (vec![], Some(vec![0xffu8, 0x33])), + ], + ); + } + #[test] fn empty_node_with_key() { compare_with_triedbmut( &[], From 18eec1500002f44562549bc79fe9392ccfc88af3 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 16 Jan 2020 21:13:18 +0100 Subject: [PATCH 054/118] next failing test --- trie-db/src/traverse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 280e180c..66f3cb05 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1171,7 +1171,7 @@ mod tests { ], &[ (vec![0x04u8], Some(vec![0xffu8, 0x33])), - //(vec![32u8], Some(vec![0xffu8, 0x33])), + (vec![32u8], Some(vec![0xffu8, 0x33])), ], ); } From 3d61d3be11743910fd995052fecefb87b0453896 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 17 Jan 2020 12:28:01 +0100 Subject: [PATCH 055/118] fix dummy1 --- trie-db/src/node.rs | 12 +++ trie-db/src/traverse.rs | 214 ++++++++++++++++++++------------------- trie-db/src/triedbmut.rs | 11 ++ 3 files changed, 134 insertions(+), 103 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 93337dfe..10230d5c 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -237,6 +237,18 @@ impl> OwnedNode { } } + /// Tell if there is a value defined at this node position. + pub fn has_value(&self) -> bool { + match &self.plan { + NodePlan::Extension { .. } + | NodePlan::Empty => false, + NodePlan::Leaf { .. } => true, + NodePlan::Branch { value, .. } + | NodePlan::NibbledBranch { value, .. } => value.is_some(), + } + } + + /// Get value part of the node (partial) if any. pub fn value(&self) -> Option { let data = &self.data.borrow(); diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 66f3cb05..42cbca66 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -310,9 +310,9 @@ impl StackedItem // split occurs before visiting a single child debug_assert!(self.first_child.is_none()); - debug_assert!(!self.did_first_child); + // debug_assert!(!self.did_first_child); // ordering key also ensure - debug_assert!(self.split_child.is_none()); + debug_assert!(self.split_child_index().is_none()); // not setting child relation (will be set on exit) let child = StackedItemChild { @@ -450,6 +450,15 @@ impl StackedNode } } + /// Tell if there is a value defined at this node position. + fn has_value(&self) -> bool { + match self { + StackedNode::Unchanged(node) => node.has_value(), + StackedNode::Changed(node) => node.has_value(), + StackedNode::Deleted => false, + } + } + /// Set a value if the node can contain one. fn set_value(&mut self, value: &[u8]) { match self { @@ -640,72 +649,15 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let mut previous_key: Option = None; for next_query in elements.into_iter().map(|e| Some(e)).chain(Some(None)) { - let mut next_key = None; - let last = next_query.is_none(); - // PATH DOWN descending in next_query. - if let Some((key, value)) = next_query { - let dest_slice = NibbleFullKey::new(key.as_ref()); - let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - let mut descend_mid_index = None; - if !current.node.is_empty() { - // corner case do not descend in empty node - loop { - let common_index = current.node.partial() - .map(|current_partial| { - let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); - current.depth_prefix + current_partial.common_prefix(&target_partial) - }).unwrap_or(current.depth_prefix); - // TODO not sure >= or just >. - if common_index == current.depth_prefix && dest_depth >= current.depth { - let next_index = dest_slice.at(current.depth); - let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth + 1); - if let Some(child) = current.descend_child(next_index, db, prefix.left())? { - current = child; - } - } else { - if common_index < current.depth { - descend_mid_index = Some(common_index); - } - break; - } - } - } - let traverse_state = if let Some(mid_index) = descend_mid_index { - TraverseState::MidPartial(mid_index) - } else if dest_depth < current.depth { - // TODO this might be unreachable from previous loop - // split child (insert in current prefix - let mid_index = current.node.partial() - .map(|current_partial| { - let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); - current.depth_prefix + current_partial.common_prefix(&target_partial) - }).expect("Covered by previous iteration for well formed trie"); - TraverseState::MidPartial(mid_index) - } else if dest_depth > current.depth { - // over callback - TraverseState::AfterNode - } else { - // value replace callback - TraverseState::ValueMatch - }; - if let Some(new_child) = callback.enter_terminal( - &mut current, - key.as_ref(), - value.as_ref().map(|v| v.as_ref()), - traverse_state, - ) { - stack.push(current); - current = new_child; - } - next_key = Some(key) - } + // PATH UP over the previous key and value - if let Some(key) = previous_key { - let target_common_depth = next_key.as_ref().map(|next| nibble_ops::biggest_depth( + if let Some(key) = previous_key.as_ref() { + let target_common_depth = next_query.as_ref().map(|(next, _)| nibble_ops::biggest_depth( key.as_ref(), next.as_ref(), )).unwrap_or(0); // last element goes up to root + let last = next_query.is_none(); // unstack nodes if needed while last || target_common_depth < current.depth_prefix { @@ -731,41 +683,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // fuse child opteration did switch current context. continue; } - if let Some((parent_first, key_first)) = parent.first_child.take() { - // parent first from split is replaced when going down in this case. - debug_assert!(parent_first.parent_index != current.parent_index); - // parent got two changed child we can apply exit (no way to fuse this with - // top or - if parent_first.parent_index < current.parent_index { - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(&key_first[..], parent.depth + 1).left(), - parent_first.node, parent_first.hash.as_ref(), - ) { - parent.node.set_handle(handle, parent_first.parent_index); - } - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), - current.node, current.hash.as_ref(), - ) { - parent.node.set_handle(handle, current.parent_index); - } - } else if parent_first.parent_index > current.parent_index { - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), - current.node, current.hash.as_ref(), - ) { - parent.node.set_handle(handle, current.parent_index); - } - // parent_first is from a split - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(&key_first[..], parent.depth + 1).left(), - parent_first.node, parent_first.hash.as_ref(), - ) { - parent.node.set_handle(handle, parent_first.parent_index); - } - } - parent.did_first_child = true; - } else if parent.did_first_child || current.node.is_deleted() { + if parent.did_first_child || current.node.is_deleted() { // process exit, as we already assert two child, no need to store in case of parent // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first @@ -796,8 +714,27 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( parent.process_child(current, key.as_ref(), callback); } } else { - // first node visited, store in parent first child and process later. - parent.first_child = Some((current.into(), key.as_ref().to_vec())); + if let Some(split_child_index) = parent.split_child_index() { + if split_child_index < current.parent_index { + // this could not be fuse (split cannot be deleted), + // no stacking of first child + if split_child_index < current.parent_index { + parent.process_split_child(key.as_ref(), callback); + } + parent.did_first_child = true; + } + } + if !parent.did_first_child && parent.node.has_value() { + // this could not be fuse (delete value occurs before), + // no stacking of first child + parent.did_first_child = true; + } + if parent.did_first_child { + parent.process_child(current, key.as_ref(), callback); + } else { + // first node visited on a fusable element, store in parent first child and process later. + parent.first_child = Some((current.into(), key.as_ref().to_vec())); + } } current = parent; } else { @@ -806,7 +743,66 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } } } - previous_key = next_key; + + // PATH DOWN descending in next_query. + if let Some((key, value)) = next_query { + let dest_slice = NibbleFullKey::new(key.as_ref()); + let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + let mut descend_mid_index = None; + if !current.node.is_empty() { + // corner case do not descend in empty node (else condition) + loop { + let common_index = current.node.partial() + .map(|current_partial| { + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + current.depth_prefix + current_partial.common_prefix(&target_partial) + }).unwrap_or(current.depth_prefix); + // TODO not sure >= or just >. + if common_index == current.depth && dest_depth > current.depth { + let next_index = dest_slice.at(current.depth); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth + 1); + if let Some(child) = current.descend_child(next_index, db, prefix.left())? { + current = child; + } else { + break; + } + } else { + if common_index < current.depth { + descend_mid_index = Some(common_index); + } + break; + } + } + } + let traverse_state = if let Some(mid_index) = descend_mid_index { + TraverseState::MidPartial(mid_index) + } else if dest_depth < current.depth { + // TODO this might be unreachable from previous loop + // split child (insert in current prefix -> try fuzzing on unreachable + let mid_index = current.node.partial() + .map(|current_partial| { + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + current.depth_prefix + current_partial.common_prefix(&target_partial) + }).expect("Covered by previous iteration for well formed trie"); + TraverseState::MidPartial(mid_index) + } else if dest_depth > current.depth { + // over callback + TraverseState::AfterNode + } else { + // value replace callback + TraverseState::ValueMatch + }; + if let Some(new_child) = callback.enter_terminal( + &mut current, + key.as_ref(), + value.as_ref().map(|v| v.as_ref()), + traverse_state, + ) { + stack.push(current); + current = new_child; + } + previous_key = Some(key); + } } Ok(()) @@ -1165,17 +1161,29 @@ mod tests { } #[test] fn dummy1() { + compare_with_triedbmut( + &[ + (vec![0x04u8], vec![4, 32]), + ], + &[ + (vec![0x06u8], Some(vec![0xffu8, 0x33])), + (vec![0x08u8], Some(vec![0xffu8, 0x33])), + ], + ); + } + #[test] + fn two_recursive_mid_insert() { compare_with_triedbmut( &[ (vec![0x0u8], vec![4, 32]), ], &[ (vec![0x04u8], Some(vec![0xffu8, 0x33])), - (vec![32u8], Some(vec![0xffu8, 0x33])), + (vec![0x20u8], Some(vec![0xffu8, 0x33])), + //(vec![0x06u8], Some(vec![0xffu8, 0x33])), ], ); } - #[test] fn dummy2() { compare_with_triedbmut( diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 03260b02..254db187 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -185,6 +185,17 @@ impl Node { } } + /// Set value to node if possible. + pub(crate) fn has_value(&self) -> bool { + match self { + Node::Extension(..) + | Node::Empty => false, + Node::Branch (_, val ) + | Node::NibbledBranch(_, _, val) => val.is_some(), + Node::Leaf(_, _) => true, + } + } + /// Set value to node if possible. pub(crate) fn set_value(&mut self, value: &[u8]) { match self { From ee2225c48d67ee298f7cce620fd0c76af2ef3786 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 17 Jan 2020 12:42:24 +0100 Subject: [PATCH 056/118] change process child to always include previous nodes --- trie-db/src/traverse.rs | 55 ++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 42cbca66..96540136 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -353,6 +353,11 @@ impl StackedItem F: ProcessStack, >(&mut self, callback: &mut F) { if let Some((child, key)) = self.first_child.take() { + if let Some(split_child_index) = self.split_child_index() { + if split_child_index < child.parent_index { + self.process_split_child(key.as_ref(), callback); + } + } let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); self.append_child(child, nibble_slice.left(), callback); } @@ -364,6 +369,17 @@ impl StackedItem // TODO switch to debug assert if correct asumption or call process first // child ordered with split child. assert!(child.first_child.is_none(), "guaranted by call to fix_node"); + + if let Some(first_child_index) = self.first_child_index() { + if first_child_index < child.parent_index { + self.process_first_child(callback); + } + } + if let Some(split_child_index) = self.split_child_index() { + if split_child_index < child.parent_index { + self.process_split_child(key.as_ref(), callback); + } + } // split child can be unprocessed (when going up it is kept after second node // in expectation of other children process. child.process_split_child(key, callback); @@ -374,21 +390,8 @@ impl StackedItem fn process_root< F: ProcessStack, >(mut self, key: &[u8], callback: &mut F) { - if let Some(first_child_index) = self.first_child_index() { - if let Some(split_child_index) = self.split_child_index() { - if split_child_index > first_child_index { - self.process_first_child(callback); - self.process_split_child(key, callback); - } else { - self.process_split_child(key, callback); - self.process_first_child(callback); - } - } else { - self.process_first_child(callback); - } - } else { - self.process_split_child(key, callback); - } + self.process_first_child(callback); + self.process_split_child(key.as_ref(), callback); callback.exit_root( self.node, self.hash.as_ref(), @@ -697,30 +700,10 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } else if let Some(first_child_index) = parent.first_child_index() { debug_assert!(first_child_index < current.parent_index); parent.did_first_child = true; - // process both first child and current. - if let Some(split_child_index) = parent.split_child_index() { - debug_assert!(split_child_index != first_child_index); - debug_assert!(split_child_index != current.parent_index); - if split_child_index < first_child_index { - parent.process_split_child(key.as_ref(), callback); - } - parent.process_first_child(callback); - if split_child_index < current.parent_index { - parent.process_split_child(key.as_ref(), callback); - } - parent.process_child(current, key.as_ref(), callback); - } else { - parent.process_first_child(callback); - parent.process_child(current, key.as_ref(), callback); - } + parent.process_child(current, key.as_ref(), callback); } else { if let Some(split_child_index) = parent.split_child_index() { if split_child_index < current.parent_index { - // this could not be fuse (split cannot be deleted), - // no stacking of first child - if split_child_index < current.parent_index { - parent.process_split_child(key.as_ref(), callback); - } parent.did_first_child = true; } } From 126d64de1e9f275e3ae52bd1c4dd6be118cd1c58 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 17 Jan 2020 13:08:04 +0100 Subject: [PATCH 057/118] missing stack, fix dummy 2. --- trie-db/src/traverse.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 96540136..2ceff03d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -501,7 +501,6 @@ impl StackedNode } } - /// Remove a value if the node contains one. fn remove_value(&mut self) { match self { @@ -745,6 +744,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let next_index = dest_slice.at(current.depth); let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth + 1); if let Some(child) = current.descend_child(next_index, db, prefix.left())? { + stack.push(current); current = child; } else { break; @@ -932,11 +932,11 @@ impl ProcessStack for BatchUpdate> let hash = ::hash(&encoded[..]); // register latest change self.2 = Some(self.0.len()); - // costy clone (could get read from here) - self.0.push((owned_prefix(&prefix), hash.clone(), Some(encoded))); if let Some(h) = prev_hash { self.0.push((owned_prefix(&prefix), h.clone(), None)); } + // costy clone (could get read from here) + self.0.push((owned_prefix(&prefix), hash.clone(), Some(encoded))); OwnedNodeHandle::Hash(hash) } })), @@ -958,10 +958,10 @@ impl ProcessStack for BatchUpdate> let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); self.1 = hash.clone(); - self.0.push((owned_prefix(&prefix), hash, Some(encoded))); if let Some(h) = prev_hash { self.0.push((owned_prefix(&prefix), h.clone(), None)); } + self.0.push((owned_prefix(&prefix), hash, Some(encoded))); }, _ => (), } From 380031bc318e429d8982030675d870d259c248b3 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 17 Jan 2020 13:15:12 +0100 Subject: [PATCH 058/118] fuse not happening in dummy 3. --- trie-db/src/traverse.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 2ceff03d..e67989ce 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1183,21 +1183,30 @@ mod tests { ], ); } - + #[test] + fn delete_to_empty() { + compare_with_triedbmut( + &[ + (vec![1, 254u8], vec![4u8; 33]), + ], + &[ + (vec![1, 254u8], None), + ], + ); + } #[test] fn dummy3() { compare_with_triedbmut( &[ (vec![2, 254u8], vec![4u8; 33]), (vec![1, 254u8], vec![4u8; 33]), - (vec![1, 255u8], vec![5u8; 36]), + // (vec![1, 255u8], vec![5u8; 36]), ], &[ (vec![1, 254u8], None), ], ); } - #[test] fn dummy4() { compare_with_triedbmut( From 50a8c5afb50fbe9daad1c01eafd5e257e296a5eb Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 21 Jan 2020 16:41:15 +0100 Subject: [PATCH 059/118] fix node apply on root too. --- trie-db/src/traverse.rs | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index e67989ce..2200e193 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -662,29 +662,28 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let last = next_query.is_none(); // unstack nodes if needed while last || target_common_depth < current.depth_prefix { - + let first_child_index = current.first_child.as_ref().map(|c| c.0.parent_index); + // needed also to resolve + if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { + // try first child + if let Some((child, child_key)) = current.take_first_child() { + debug_assert!(child.parent_index == fuse_index); + // + current.fuse_branch(child, child_key.as_ref(), callback); + } else { + let mut prefix = NibbleVec::from(key.as_ref(), current.depth); + prefix.push(fuse_index); + let child = current.descend_child(fuse_index, db, prefix.as_prefix())? + .expect("result of first child is define if consistent db"); + child.node.partial().map(|p| prefix.append_partial(p.right())); + current.fuse_branch(child, prefix.inner(), callback); + } + // fuse child operation did switch current context. + continue; + } // TODO check if fuse (num child is 1). // child change or addition if let Some(mut parent) = stack.pop() { - let first_child_index = current.first_child.as_ref().map(|c| c.0.parent_index); - // needed also to resolve - if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { - // try first child - if let Some((child, child_key)) = current.take_first_child() { - debug_assert!(child.parent_index == fuse_index); - // - current.fuse_branch(child, child_key.as_ref(), callback); - } else { - let mut prefix = NibbleVec::from(key.as_ref(), current.depth); - prefix.push(fuse_index); - let child = current.descend_child(fuse_index, db, prefix.as_prefix())? - .expect("result of first child is define if consistent db"); - child.node.partial().map(|p| prefix.append_partial(p.right())); - current.fuse_branch(child, prefix.inner(), callback); - } - // fuse child opteration did switch current context. - continue; - } if parent.did_first_child || current.node.is_deleted() { // process exit, as we already assert two child, no need to store in case of parent // fusing. From 4713cfec3936d2f1b0ce013b3b7cf96930c18b68 Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 21 Jan 2020 18:43:43 +0100 Subject: [PATCH 060/118] fix dummy 4 (by processing children) --- trie-db/src/traverse.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 2200e193..03d5f155 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -366,9 +366,6 @@ impl StackedItem fn process_child< F: ProcessStack, >(&mut self, mut child: StackedItem, key: &[u8], callback: &mut F) { - // TODO switch to debug assert if correct asumption or call process first - // child ordered with split child. - assert!(child.first_child.is_none(), "guaranted by call to fix_node"); if let Some(first_child_index) = self.first_child_index() { if first_child_index < child.parent_index { @@ -382,6 +379,7 @@ impl StackedItem } // split child can be unprocessed (when going up it is kept after second node // in expectation of other children process. + child.process_first_child(callback); child.process_split_child(key, callback); let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); self.append_child(child.into(), nibble_slice.left(), callback); @@ -713,7 +711,11 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if parent.did_first_child { parent.process_child(current, key.as_ref(), callback); } else { + current.process_first_child(callback); + // split child is after first child (would be processed otherwhise). + current.process_split_child(key.as_ref(), callback); // first node visited on a fusable element, store in parent first child and process later. + // Process an eventual split child (index after current). parent.first_child = Some((current.into(), key.as_ref().to_vec())); } } @@ -1194,7 +1196,7 @@ mod tests { ); } #[test] - fn dummy3() { + fn fuse_root_node() { compare_with_triedbmut( &[ (vec![2, 254u8], vec![4u8; 33]), From 2600b55c2be272e8df930051c226de8bc3aba39c Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 21 Jan 2020 19:13:22 +0100 Subject: [PATCH 061/118] at commit 5 --- trie-db/src/traverse.rs | 19 ++++++++++--------- trie-db/src/triedbmut.rs | 5 ++++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 03d5f155..a31261e9 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -308,8 +308,8 @@ impl StackedItem child.advance_partial(1 + mid_index - self.depth_prefix); - // split occurs before visiting a single child - debug_assert!(self.first_child.is_none()); +// // split occurs before visiting a single child +// debug_assert!(self.first_child.is_none()); // debug_assert!(!self.did_first_child); // ordering key also ensure debug_assert!(self.split_child_index().is_none()); @@ -333,6 +333,9 @@ impl StackedItem child.node, child.hash.as_ref(), ) { + if self.split_child_fuse_index() == Some(child.parent_index) { + self.split_child = None; + } self.node.set_handle(handle, child.parent_index); } } @@ -687,12 +690,10 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first // and no split). - if let Some(handle) = callback.exit( - NibbleSlice::new_offset(key.as_ref(), current.depth_prefix).left(), - current.node, current.hash.as_ref(), - ) { - parent.node.set_handle(handle, current.parent_index); - } + current.process_first_child(callback); + current.process_split_child(key.as_ref(), callback); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + parent.append_child(current.into(), prefix.left(), callback); } else if let Some(first_child_index) = parent.first_child_index() { debug_assert!(first_child_index < current.parent_index); parent.did_first_child = true; @@ -1248,7 +1249,7 @@ mod tests { ], &[ (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), - (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), + // (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), (vec![128], Some(vec![49, 251])), ], ); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 254db187..3696d894 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -294,14 +294,17 @@ impl Node { | n@Node::Leaf(..) => (n, None), Node::NibbledBranch(partial, encoded_children, val) => { let mut count = 0; + let mut other_index = None; if let Some(pending) = pending.0 { if encoded_children[pending as usize].is_none() { count += 1; + other_index = Some(pending); } } if let Some(pending) = pending.1 { if encoded_children[pending as usize].is_none() { count += 1; + other_index = Some(pending); } } for c in encoded_children.iter() { @@ -318,7 +321,7 @@ impl Node { (Node::Empty, None) } else if val.is_none() && count == 1 { let child_ix = encoded_children.iter().position(Option::is_some) - .expect("counted above"); + .unwrap_or_else(|| other_index.expect("counted above") as usize); (Node::NibbledBranch(partial, encoded_children, val), Some(child_ix as u8)) } else { (Node::NibbledBranch(partial, encoded_children, val), None) From 236538a5aa2544498deb8269068d77dac461c34e Mon Sep 17 00:00:00 2001 From: cheme Date: Tue, 21 Jan 2020 21:32:53 +0100 Subject: [PATCH 062/118] error for dummy5 when insterting into the partial of a deleted node. --- trie-db/src/traverse.rs | 45 ++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index a31261e9..69901ef4 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -279,7 +279,14 @@ impl StackedItem } // replace self by new branch at split and adding to self as a stacked item child. - fn split_child(&mut self, mid_index: usize, key: &[u8]) { + fn split_child< + F: ProcessStack, + >(&mut self, mid_index: usize, key: &[u8], callback: &mut F) { + // if self got split child or first child, they can be processed + // (we went up and this is a next key) (ordering) + self.process_first_child_then_split(callback); + // or it means we need to store key to + debug_assert!(self.split_child_index().is_none()); let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { let new_slice = NibbleSlice::new_offset( &key[..mid_index / nibble_ops::NIBBLE_PER_BYTE], @@ -296,6 +303,8 @@ impl StackedItem Node::new_branch(NibbleSlice::from_stored(&owned)) }; + + let old_depth = self.depth; self.depth = mid_index; let mut child = mem::replace( @@ -307,12 +316,11 @@ impl StackedItem .map(|p| p.at(mid_index - self.depth_prefix)).unwrap_or(0); child.advance_partial(1 + mid_index - self.depth_prefix); - // // split occurs before visiting a single child // debug_assert!(self.first_child.is_none()); // debug_assert!(!self.did_first_child); // ordering key also ensure - debug_assert!(self.split_child_index().is_none()); + // debug_assert!(self.split_child_index().is_none()); // not setting child relation (will be set on exit) let child = StackedItemChild { @@ -355,6 +363,18 @@ impl StackedItem fn process_first_child< F: ProcessStack, >(&mut self, callback: &mut F) { + self.process_first_child_inner(callback, false) + } + + fn process_first_child_then_split< + F: ProcessStack, + >(&mut self, callback: &mut F) { + self.process_first_child_inner(callback, true) + } + + fn process_first_child_inner< + F: ProcessStack, + >(&mut self, callback: &mut F, always_split: bool) { if let Some((child, key)) = self.first_child.take() { if let Some(split_child_index) = self.split_child_index() { if split_child_index < child.parent_index { @@ -363,6 +383,11 @@ impl StackedItem } let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); self.append_child(child, nibble_slice.left(), callback); + if always_split { + self.process_split_child(key.as_ref(), callback); + } + // TODO trie remove other unneeded assign. + self.did_first_child = true; } } @@ -728,13 +753,15 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } } + + // PATH DOWN descending in next_query. if let Some((key, value)) = next_query { let dest_slice = NibbleFullKey::new(key.as_ref()); let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; let mut descend_mid_index = None; if !current.node.is_empty() { - // corner case do not descend in empty node (else condition) + // corner case do not descend in empty node (else condition) TODO covered by empty_trie?? loop { let common_index = current.node.partial() .map(|current_partial| { @@ -879,7 +906,7 @@ impl ProcessStack for BatchUpdate> }, TraverseState::MidPartial(mid_index) => { if let Some(value) = value_element { - stacked.split_child(mid_index, key_element); + stacked.split_child(mid_index, key_element, self); let (offset, parent_index) = if key_element.len() == 0 { // corner case of adding at top of trie (0, 0) @@ -1078,7 +1105,7 @@ mod tests { v.iter().map(|(a, b)| (a, b.as_ref())), ); - assert_eq!(calc_root, root); +// assert_eq!(calc_root, root); let mut batch_delta = initial_db; // @@ -1112,7 +1139,7 @@ mod tests { // assert_eq!(format!("{:?}", t1), format!("{:?}", t2)); -// panic!("!!END!!"); + panic!("!!END!!"); } #[test] @@ -1248,8 +1275,8 @@ mod tests { (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), ], &[ - (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), - // (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), + // (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), (vec![128], Some(vec![49, 251])), ], ); From 0ed0a8e6e1a739b266aeee8ba0035d86c47b6230 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 09:33:29 +0100 Subject: [PATCH 063/118] tiny fix, previous commit where bad, fundamental issue is not going down properly on split child operation (not calling child process). --- trie-db/src/traverse.rs | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 69901ef4..dc5417c6 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -714,7 +714,8 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // process exit, as we already assert two child, no need to store in case of parent // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first - // and no split). + // and no split). TODO the number of calls to process first and split is wrong: + // should be once after fix_node only: that is especially for append_child case. current.process_first_child(callback); current.process_split_child(key.as_ref(), callback); let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); @@ -795,7 +796,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( .map(|current_partial| { let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); current.depth_prefix + current_partial.common_prefix(&target_partial) - }).expect("Covered by previous iteration for well formed trie"); + }).unwrap_or(current.depth_prefix); TraverseState::MidPartial(mid_index) } else if dest_depth > current.depth { // over callback @@ -1097,7 +1098,6 @@ mod tests { println!("aft {:?}", t); } -// let mut reference_delta = db; let (calc_root, payload) = trie_traverse_key_no_extension_build( &mut initial_db, @@ -1108,40 +1108,17 @@ mod tests { // assert_eq!(calc_root, root); let mut batch_delta = initial_db; -// - memory_db_from_delta(payload, &mut batch_delta); -/* - // sort - let batch_delta: std::collections::BTreeMap<_, _> = batch_delta.drain().into_iter().collect(); - // TODO this revel an issue with triedbmut implementation (some incorrect RC and therefore - // node being kept). - assert_eq!( - batch_delta, - reference_delta.drain().into_iter() - // TODO there is something utterly wrong with - // triedbmut: nodes getting removed more than once - // and hash comming from nowhere - // from now simply skip -1 counted -// .filter(|(_, (_, rc))| rc >= &0) - .collect(), - ); -*/ -// println!("{:?}", batch_delta.drain()); -// println!("{:?}", db.drain()); + memory_db_from_delta(payload, &mut batch_delta); // test by checking both triedb only let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); println!("{:?}", t2); let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); println!("{:?}", t2b); -// let t1 = RefTrieDBNoExt::new(&batch_delta, &root).unwrap(); -// assert_eq!(format!("{:?}", t1), format!("{:?}", t2)); - - panic!("!!END!!"); - } + #[test] fn empty_node_null_key() { compare_with_triedbmut( @@ -1275,7 +1252,7 @@ mod tests { (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), ], &[ - // (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), + (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), (vec![128], Some(vec![49, 251])), ], From ed4a50dca7c58f0b5d975b3e531210231874106a Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 12:20:33 +0100 Subject: [PATCH 064/118] issue with fuse, we do not go up the deleted node --- trie-db/src/traverse.rs | 155 +++++++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 56 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index dc5417c6..3bf87793 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -53,7 +53,7 @@ enum StackedNode /// Deleted node. Deleted, } - +/* impl StackedNode where B: Borrow<[u8]>, @@ -67,7 +67,7 @@ impl StackedNode } } } - +*/ /// Item on stack it contains updatable traverse /// specific field to manage update that split /// partial from a node, and for buffering the first @@ -104,8 +104,9 @@ struct StackedItem /// Note that split_child is always a first_child. /// TODO rename to first modified child (non delete). first_child: Option<(StackedItemChild, Vec)>, - /// If a two child where already asserted or the node is deleted. - did_first_child: bool, + /// true when the value can be deleted and only more + /// than one branch cannot be deleted. + can_fuse: bool, } @@ -201,7 +202,7 @@ impl StackedItem split_child: None, hash, parent_index, - did_first_child: false, + can_fuse: false, }, child_key)) } else { None @@ -232,7 +233,7 @@ impl StackedItem split_child: None, hash, parent_index, - did_first_child: false, + can_fuse: true, }) } else { unreachable!("Broken split expectation, visited twice"); @@ -270,7 +271,7 @@ impl StackedItem depth, first_child: None, split_child: None, - did_first_child: false, + can_fuse: true, }) } else { None @@ -279,14 +280,14 @@ impl StackedItem } // replace self by new branch at split and adding to self as a stacked item child. - fn split_child< + fn do_split_child< F: ProcessStack, >(&mut self, mid_index: usize, key: &[u8], callback: &mut F) { - // if self got split child or first child, they can be processed - // (we went up and this is a next key) (ordering) - self.process_first_child_then_split(callback); +// // if self got split child, it can be processed +// // (we went up and this is a next key) (ordering) +// self.process_first_child_then_split(callback); // or it means we need to store key to - debug_assert!(self.split_child_index().is_none()); +// debug_assert!(self.split_child_index().is_none()); let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { let new_slice = NibbleSlice::new_offset( &key[..mid_index / nibble_ops::NIBBLE_PER_BYTE], @@ -303,8 +304,6 @@ impl StackedItem Node::new_branch(NibbleSlice::from_stored(&owned)) }; - - let old_depth = self.depth; self.depth = mid_index; let mut child = mem::replace( @@ -318,7 +317,7 @@ impl StackedItem child.advance_partial(1 + mid_index - self.depth_prefix); // // split occurs before visiting a single child // debug_assert!(self.first_child.is_none()); - // debug_assert!(!self.did_first_child); + // debug_assert!(!self.cannot_fuse); // ordering key also ensure // debug_assert!(self.split_child_index().is_none()); @@ -330,6 +329,7 @@ impl StackedItem depth: old_depth, parent_index, }; + self.can_fuse = false; self.split_child = Some((Some(child), None)); } @@ -387,7 +387,7 @@ impl StackedItem self.process_split_child(key.as_ref(), callback); } // TODO trie remove other unneeded assign. - self.did_first_child = true; + self.can_fuse = false; } } @@ -441,7 +441,7 @@ impl StackedItem self.node.set_partial(partial); self.hash = child.hash; self.depth = child_depth; - self.did_first_child = false; + self.can_fuse = false; self.first_child = None; self.split_child = None; } @@ -671,7 +671,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( parent_index: 0, first_child: None, split_child: None, - did_first_child: false, + can_fuse: true, }; let mut previous_key: Option = None; @@ -710,7 +710,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // TODO check if fuse (num child is 1). // child change or addition if let Some(mut parent) = stack.pop() { - if parent.did_first_child || current.node.is_deleted() { + if !parent.can_fuse || current.node.is_empty() { // process exit, as we already assert two child, no need to store in case of parent // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first @@ -722,20 +722,20 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( parent.append_child(current.into(), prefix.left(), callback); } else if let Some(first_child_index) = parent.first_child_index() { debug_assert!(first_child_index < current.parent_index); - parent.did_first_child = true; + parent.can_fuse = false; parent.process_child(current, key.as_ref(), callback); } else { if let Some(split_child_index) = parent.split_child_index() { if split_child_index < current.parent_index { - parent.did_first_child = true; + parent.can_fuse = false; } } - if !parent.did_first_child && parent.node.has_value() { + if parent.can_fuse && parent.node.has_value() { // this could not be fuse (delete value occurs before), // no stacking of first child - parent.did_first_child = true; + parent.can_fuse = false; } - if parent.did_first_child { + if !parent.can_fuse { parent.process_child(current, key.as_ref(), callback); } else { current.process_first_child(callback); @@ -748,10 +748,18 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } current = parent; } else { + current.process_first_child(callback); + current.process_split_child(key.as_ref(), callback); current.process_root(key.as_ref(), callback); return Ok(()); } } + if !current.can_fuse { + current.process_first_child(callback); + if target_common_depth < current.depth { + current.process_split_child(key.as_ref(), callback); + } + } } @@ -764,6 +772,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if !current.node.is_empty() { // corner case do not descend in empty node (else condition) TODO covered by empty_trie?? loop { + // TODO check if first common index is simple target_common? of previous go up. let common_index = current.node.partial() .map(|current_partial| { let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); @@ -889,7 +898,7 @@ impl ProcessStack for BatchUpdate> parent_index, split_child: None, first_child: None, - did_first_child: false, + can_fuse: false, }; return if stacked.node.is_empty() { // replace empty. @@ -907,35 +916,47 @@ impl ProcessStack for BatchUpdate> }, TraverseState::MidPartial(mid_index) => { if let Some(value) = value_element { - stacked.split_child(mid_index, key_element, self); - let (offset, parent_index) = if key_element.len() == 0 { - // corner case of adding at top of trie - (0, 0) - } else { - // TODO not sure on index - (1, NibbleSlice::new(key_element).at(mid_index)) - }; - let child = Node::new_leaf( - // TODO not sure on '1 +' - NibbleSlice::new_offset(key_element, offset + mid_index), - value.as_ref(), - ); - return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { - // set value in new branch - stacked.node.set_value(value); + if stacked.node.is_empty() { + let child = Node::new_leaf( + NibbleSlice::new_offset(key_element, stacked.depth_prefix), + value.as_ref(), + ); + stacked.node = StackedNode::Changed(child); + stacked.depth = key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + stacked.can_fuse = false; None } else { - let child = StackedItem { - node: StackedNode::Changed(child), - hash: None, - depth_prefix: offset + mid_index, - depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index, - split_child: None, - first_child: None, - did_first_child: false, + stacked.do_split_child(mid_index, key_element, self); + let (offset, parent_index) = if key_element.len() == 0 { + // corner case of adding at top of trie + (0, 0) + } else { + // TODO not sure on index + (1, NibbleSlice::new(key_element).at(mid_index)) }; - Some(child) + let child = Node::new_leaf( + // TODO not sure on '1 +' + NibbleSlice::new_offset(key_element, offset + mid_index), + value.as_ref(), + ); + return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { + + // set value in new branch + stacked.node.set_value(value); + None + } else { + let child = StackedItem { + node: StackedNode::Changed(child), + hash: None, + depth_prefix: offset + mid_index, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + split_child: None, + first_child: None, + can_fuse: false, + }; + Some(child) + } } } else { // nothing to delete. @@ -1105,10 +1126,9 @@ mod tests { v.iter().map(|(a, b)| (a, b.as_ref())), ); -// assert_eq!(calc_root, root); - let mut batch_delta = initial_db; - + assert_eq!(calc_root, root); + let mut batch_delta = initial_db; memory_db_from_delta(payload, &mut batch_delta); // test by checking both triedb only let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); @@ -1116,7 +1136,7 @@ mod tests { let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); println!("{:?}", t2b); - panic!("!!END!!"); +// panic!("!!END!!"); } #[test] @@ -1169,7 +1189,6 @@ mod tests { &[ (vec![0x04u8], Some(vec![0xffu8, 0x33])), (vec![0x20u8], Some(vec![0xffu8, 0x33])), - //(vec![0x06u8], Some(vec![0xffu8, 0x33])), ], ); } @@ -1244,6 +1263,30 @@ mod tests { ], ); } + #[test] + fn dummy_51() { + compare_with_triedbmut( + &[ + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + ], + &[ + (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), + (vec![128], Some(vec![49, 251])), + ], + ); + } + #[test] + fn emptied_then_insert() { + compare_with_triedbmut( + &[ + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + ], + &[ + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), + (vec![128], Some(vec![49, 251])), + ], + ); + } #[test] fn dummy5() { From cf26a673260d54bfd23c3e440a885d66f7cc52ba Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 14:08:40 +0100 Subject: [PATCH 065/118] need refactor --- trie-db/src/traverse.rs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 3bf87793..03a2b385 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -158,6 +158,20 @@ impl StackedItem B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, { + // function is only call when going up, meaning + // traverse_key depth + 1 is already visited. + fn can_fuse(&self, traverse_key: &[u8]) -> bool { + //if let StackedNode::Changedself.node. + unimplemented!() + } + + // TODO remove, here for debugging + // child_ix is only for non delete (delete is always applied before) + fn test_can_fuse(&self, ref_key: &[u8], child_ix: Option) -> bool { + self.can_fuse + } + + fn split_child_fuse_index(&self) -> Option { match self.split_child.as_ref() { Some((Some(child), _)) => Some(child.parent_index), @@ -329,7 +343,6 @@ impl StackedItem depth: old_depth, parent_index, }; - self.can_fuse = false; self.split_child = Some((Some(child), None)); } @@ -710,7 +723,12 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // TODO check if fuse (num child is 1). // child change or addition if let Some(mut parent) = stack.pop() { - if !parent.can_fuse || current.node.is_empty() { + if current.node.is_empty() { + current.process_first_child(callback); + current.process_split_child(key.as_ref(), callback); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + parent.append_child(current.into(), prefix.left(), callback); + } else if !parent.test_can_fuse(key.as_ref(), Some(current.parent_index)) { // process exit, as we already assert two child, no need to store in case of parent // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first @@ -735,7 +753,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // no stacking of first child parent.can_fuse = false; } - if !parent.can_fuse { + if !parent.test_can_fuse(key.as_ref(), Some(current.parent_index)) { parent.process_child(current, key.as_ref(), callback); } else { current.process_first_child(callback); @@ -754,7 +772,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( return Ok(()); } } - if !current.can_fuse { + if !current.test_can_fuse(key.as_ref(), None) { current.process_first_child(callback); if target_common_depth < current.depth { current.process_split_child(key.as_ref(), callback); @@ -943,6 +961,7 @@ impl ProcessStack for BatchUpdate> // set value in new branch stacked.node.set_value(value); + stacked.can_fuse = false; None } else { let child = StackedItem { @@ -955,6 +974,8 @@ impl ProcessStack for BatchUpdate> first_child: None, can_fuse: false, }; + // TODO this does not work but needed to process node after go up. + stacked.can_fuse = false; Some(child) } } From bff4d4cd738fbf2fe3d2e9ae7a27987d78f4244c Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 16:33:52 +0100 Subject: [PATCH 066/118] passing test by disgusting code duplication on fix nodes. --- trie-db/src/traverse.rs | 69 ++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 03a2b385..40c51a97 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -167,6 +167,8 @@ impl StackedItem // TODO remove, here for debugging // child_ix is only for non delete (delete is always applied before) + // This function checks if this node can fuse in the future, in respect + // to a given child to append or expecting all child to be processed. fn test_can_fuse(&self, ref_key: &[u8], child_ix: Option) -> bool { self.can_fuse } @@ -299,7 +301,7 @@ impl StackedItem >(&mut self, mid_index: usize, key: &[u8], callback: &mut F) { // // if self got split child, it can be processed // // (we went up and this is a next key) (ordering) -// self.process_first_child_then_split(callback); + self.process_first_child_then_split(callback); // or it means we need to store key to // debug_assert!(self.split_child_index().is_none()); let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { @@ -400,7 +402,7 @@ impl StackedItem self.process_split_child(key.as_ref(), callback); } // TODO trie remove other unneeded assign. - self.can_fuse = false; + //self.can_fuse = false; } } @@ -424,6 +426,7 @@ impl StackedItem child.process_split_child(key, callback); let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); self.append_child(child.into(), nibble_slice.left(), callback); + self.can_fuse = false; } fn process_root< @@ -691,6 +694,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( for next_query in elements.into_iter().map(|e| Some(e)).chain(Some(None)) { + let mut skip_down = false; // PATH UP over the previous key and value if let Some(key) = previous_key.as_ref() { let target_common_depth = next_query.as_ref().map(|(next, _)| nibble_ops::biggest_depth( @@ -698,9 +702,15 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( next.as_ref(), )).unwrap_or(0); // last element goes up to root +/* let target_common_depth = if current.node.is_empty() { + min(target_common_depth, current.depth_prefix) + } else { + target_common_depth + };*/ + let last = next_query.is_none(); // unstack nodes if needed - while last || target_common_depth < current.depth_prefix { + while last || target_common_depth < current.depth_prefix || current.node.is_empty() { let first_child_index = current.first_child.as_ref().map(|c| c.0.parent_index); // needed also to resolve if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { @@ -740,7 +750,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( parent.append_child(current.into(), prefix.left(), callback); } else if let Some(first_child_index) = parent.first_child_index() { debug_assert!(first_child_index < current.parent_index); - parent.can_fuse = false; parent.process_child(current, key.as_ref(), callback); } else { if let Some(split_child_index) = parent.split_child_index() { @@ -766,20 +775,57 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } current = parent; } else { - current.process_first_child(callback); - current.process_split_child(key.as_ref(), callback); - current.process_root(key.as_ref(), callback); - return Ok(()); + if last { + current.process_first_child(callback); + current.process_split_child(key.as_ref(), callback); + current.process_root(key.as_ref(), callback); + return Ok(()); + } else { + if let Some((key, Some(value))) = next_query.as_ref() { + let child = Node::new_leaf( + NibbleSlice::new_offset(key.as_ref(), 0), + value.as_ref(), + ); + current.node = StackedNode::Changed(child); + current.depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + current.can_fuse = false; + } + // move to next key + skip_down = true; + break; + } + } + let first_child_index = current.first_child.as_ref().map(|c| c.0.parent_index); + // needed also to resolve + if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { + // try first child + if let Some((child, child_key)) = current.take_first_child() { + debug_assert!(child.parent_index == fuse_index); + // + current.fuse_branch(child, child_key.as_ref(), callback); + } else { + let mut prefix = NibbleVec::from(key.as_ref(), current.depth); + prefix.push(fuse_index); + let child = current.descend_child(fuse_index, db, prefix.as_prefix())? + .expect("result of first child is define if consistent db"); + child.node.partial().map(|p| prefix.append_partial(p.right())); + current.fuse_branch(child, prefix.inner(), callback); + } + // fuse child operation did switch current context. + continue; } } - if !current.test_can_fuse(key.as_ref(), None) { +/* if !current.test_can_fuse(key.as_ref(), None) { current.process_first_child(callback); if target_common_depth < current.depth { current.process_split_child(key.as_ref(), callback); } - } + }*/ } + if skip_down { + continue; + } // PATH DOWN descending in next_query. @@ -935,6 +981,7 @@ impl ProcessStack for BatchUpdate> TraverseState::MidPartial(mid_index) => { if let Some(value) = value_element { if stacked.node.is_empty() { + unreachable!(); let child = Node::new_leaf( NibbleSlice::new_offset(key_element, stacked.depth_prefix), value.as_ref(), @@ -974,8 +1021,6 @@ impl ProcessStack for BatchUpdate> first_child: None, can_fuse: false, }; - // TODO this does not work but needed to process node after go up. - stacked.can_fuse = false; Some(child) } } From d34d566400602ef437a7048b46e22f84e171886d Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 16:42:00 +0100 Subject: [PATCH 067/118] no reg test --- trie-db/src/traverse.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 40c51a97..6bd871c8 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1235,6 +1235,19 @@ mod tests { ); } #[test] + fn simple_fuse() { + compare_with_triedbmut( + &[ + (vec![0x04u8], vec![4, 32]), + (vec![0x04, 0x04], vec![4, 33]), + (vec![0x04, 0x04, 0x04], vec![4, 35]), + ], + &[ + (vec![0x04u8, 0x04], None), + ], + ); + } + #[test] fn dummy1() { compare_with_triedbmut( &[ From abe43c7d425ab106a51980ef2659d29f0d95abbc Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 18:34:34 +0100 Subject: [PATCH 068/118] fix fuse node, code needs refactoring --- trie-db/fuzz/src/lib.rs | 1 + trie-db/src/traverse.rs | 27 +++++++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 6963502c..8918142b 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -418,6 +418,7 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { #[test] fn test() { let tests = [ + vec![0x0,0x80,0xd4,0xd4,0xd4,0xd4,0x3a,0x3a,0x3f,0x0,0x3f,0x0], vec![0x0,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x1,0x8d,0x2c,0xd4,0x0,0x0,0x33,0x8a,0x20,0x80,0x9a,0x2c,0xd4,0x0,0x0,0x33,0x8a,0xff,0x8], vec![0xff,0xd1,0x0,0x90,0x40,0xd4,0x8d,0x1,0x0,0x0,0xff,0x90,0x40,0xd4,0x8d,0x1,0x0,0x8d,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x6,0x8,0x15,0x1,0x4,0x0,0x8d,0x87,0xcf,0x0,0x3f,0xcb,0xd8,0xb9,0xa2,0x4d,0x9a,0xd6,0xd2,0x0,0x0,0x0,0x0,0x80,0x0,0x6,0x8,0x15,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xc7,0xd7,0xd7,0xfb,0x7f,0x83,0x3,0x37,0x37,0x37,0xb2,0xa8,0xb,0xf5,0x5a,0x50,0xb6,0x0,0xff,0x17,0x21,0x0], vec![0x43,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x40,0x2,0x4,0x0,0x0,0xc7,0x8d,0x0,0x0,0xe0], diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index cc35c2e5..dd05f283 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -716,8 +716,10 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // try first child if let Some((child, child_key)) = current.take_first_child() { debug_assert!(child.parent_index == fuse_index); - // - current.fuse_branch(child, child_key.as_ref(), callback); + let mut prefix = NibbleVec::from(child_key.as_ref(), current.depth); + prefix.push(fuse_index); + child.node.partial().map(|p| prefix.append_partial(p.right())); + current.fuse_branch(child, prefix.inner(), callback); } else { let mut prefix = NibbleVec::from(key.as_ref(), current.depth); prefix.push(fuse_index); @@ -800,8 +802,11 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // try first child if let Some((child, child_key)) = current.take_first_child() { debug_assert!(child.parent_index == fuse_index); - // - current.fuse_branch(child, child_key.as_ref(), callback); + // TODO probably no use in storing child_key here + let mut prefix = NibbleVec::from(child_key.as_ref(), current.depth); + prefix.push(fuse_index); + child.node.partial().map(|p| prefix.append_partial(p.right())); + current.fuse_branch(child, prefix.inner(), callback); } else { let mut prefix = NibbleVec::from(key.as_ref(), current.depth); prefix.push(fuse_index); @@ -1341,6 +1346,20 @@ mod tests { ], ); } + #[test] + fn dummy7() { + compare_with_triedbmut( + &[ + (vec![212], vec![212, 212]), + ], + &[ + (vec![58], Some(vec![63, 0])), + (vec![63], None), + (vec![212], None), + ], + ); + } + #[test] fn dummy_51() { compare_with_triedbmut( From fae39d6792c1f1090010e600d7b330688a56aa60 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 19:11:02 +0100 Subject: [PATCH 069/118] process after fix when in middle node. --- trie-db/fuzz/src/lib.rs | 2 ++ trie-db/src/traverse.rs | 46 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 8918142b..a2239be2 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -418,6 +418,8 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { #[test] fn test() { let tests = [ + vec![0x1,0x0,0x0,0x0,0x8,0xc,0x8,0x8,0x8,0x0,0x3d,0x0,0x9d,0x4,0x4e], + vec![0x1,0x0,0x0,0x0,0x8,0x8,0x8,0x8,0x8,0x0,0x80,0x0,0x9d,0x4,0x4e], vec![0x0,0x80,0xd4,0xd4,0xd4,0xd4,0x3a,0x3a,0x3f,0x0,0x3f,0x0], vec![0x0,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x1,0x8d,0x2c,0xd4,0x0,0x0,0x33,0x8a,0x20,0x80,0x9a,0x2c,0xd4,0x0,0x0,0x33,0x8a,0xff,0x8], vec![0xff,0xd1,0x0,0x90,0x40,0xd4,0x8d,0x1,0x0,0x0,0xff,0x90,0x40,0xd4,0x8d,0x1,0x0,0x8d,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x6,0x8,0x15,0x1,0x4,0x0,0x8d,0x87,0xcf,0x0,0x3f,0xcb,0xd8,0xb9,0xa2,0x4d,0x9a,0xd6,0xd2,0x0,0x0,0x0,0x0,0x80,0x0,0x6,0x8,0x15,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xc7,0xd7,0xd7,0xfb,0x7f,0x83,0x3,0x37,0x37,0x37,0xb2,0xa8,0xb,0xf5,0x5a,0x50,0xb6,0x0,0xff,0x17,0x21,0x0], diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index dd05f283..ef184051 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -368,7 +368,11 @@ impl StackedItem if let Some((Some(child), None)) = self.split_child.take() { self.split_child = Some((None, Some(child.parent_index))); // prefix is slice - let mut build_prefix = NibbleVec::from(key, self.depth); + let mut build_prefix = NibbleVec::from(key, self.depth_prefix); + // TODO do same for first child (would need bench) -> here we + // should have a reusable nibblevec to avoid this allocation + // everywhere but would relieve a stored Vec !! + self.node.partial().map(|p| build_prefix.append_partial(p.right())); build_prefix.push(child.parent_index); self.append_child(child, build_prefix.as_prefix(), callback); } @@ -818,6 +822,13 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // fuse child operation did switch current context. continue; } + // no fix if middle then process buffed + if target_common_depth < current.depth { + // TODO this can probably remove a lot of those calls TODO check especially + // calls in going down path at split child. + current.process_first_child(callback); + current.process_split_child(key.as_ref(), callback) + } } /* if !current.test_can_fuse(key.as_ref(), None) { current.process_first_child(callback); @@ -1347,7 +1358,7 @@ mod tests { ); } #[test] - fn dummy7() { + fn fuse_with_child_partial() { compare_with_triedbmut( &[ (vec![212], vec![212, 212]), @@ -1360,6 +1371,37 @@ mod tests { ); } + #[test] + fn dummy7() { + compare_with_triedbmut( + &[ + (vec![0], vec![0, 212]), + (vec![8, 8], vec![0, 212]), + ], + &[ + (vec![0], None), + (vec![8, 0], Some(vec![63, 0])), + (vec![128], None), + ], + ); + } + + #[test] + fn dummy8() { + compare_with_triedbmut( + &[ + (vec![0], vec![0, 212]), + (vec![8, 8], vec![0, 212]), + ], + &[ + (vec![0], None), + (vec![8, 0], Some(vec![63, 0])), + (vec![128], Some(vec![63, 0])), + ], + ); + } + + #[test] fn dummy_51() { compare_with_triedbmut( From b1072ae768e9f5e5c79e2d2ede539ccdb6ee29d1 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 19:41:38 +0100 Subject: [PATCH 070/118] up is also when not entering loop. --- trie-db/src/traverse.rs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index ef184051..3e040592 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -822,14 +822,15 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // fuse child operation did switch current context. continue; } - // no fix if middle then process buffed - if target_common_depth < current.depth { - // TODO this can probably remove a lot of those calls TODO check especially - // calls in going down path at split child. - current.process_first_child(callback); - current.process_split_child(key.as_ref(), callback) - } } + // no fix if middle then process buffed + if target_common_depth < current.depth { + // TODO this can probably remove a lot of those calls TODO check especially + // calls in going down path at split child. + current.process_first_child(callback); + current.process_split_child(key.as_ref(), callback) + } + /* if !current.test_can_fuse(key.as_ref(), None) { current.process_first_child(callback); if target_common_depth < current.depth { @@ -1401,6 +1402,22 @@ mod tests { ); } + #[test] + fn dummy9() { + compare_with_triedbmut( + &[ + (vec![0], vec![0, 212]), + (vec![1], vec![111, 22]), + ], + &[ + (vec![0], None), + (vec![5], Some(vec![63, 0])), + (vec![14], None), + (vec![64], Some(vec![63, 0])), + ], + ); + } + #[test] fn dummy_51() { From 04a9009fa6d84b45244714cdd173f5ba98447902 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 22 Jan 2020 20:45:29 +0100 Subject: [PATCH 071/118] we can fuse a fuse branch. --- trie-db/src/traverse.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 3e040592..a0dd8549 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -460,7 +460,7 @@ impl StackedItem self.node.set_partial(partial); self.hash = child.hash; self.depth = child_depth; - self.can_fuse = false; + self.can_fuse = true; self.first_child = None; self.split_child = None; } @@ -1458,6 +1458,24 @@ mod tests { ); } + #[test] + fn dummy_big() { + compare_with_triedbmut( + &[ + (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], vec![1, 2]), + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], vec![1, 2]), + + ], + &[ + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], None), + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 208], Some(vec![4; 32])), +// (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 151, 144, 144, 144, 144, 144, 144, 144], Some(vec![4, 251])), + (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], None), + ], + ); + } + + #[test] fn single_latest_change_value_does_not_work() { compare_with_triedbmut( From 16ff5b43005b2212c1d1cc04a59283834602ca43 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 10:02:29 +0200 Subject: [PATCH 072/118] remove first key storage --- trie-db/fuzz/Cargo.toml | 4 +-- trie-db/src/traverse.rs | 65 +++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 9169d699..7e0ecd95 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -10,8 +10,8 @@ cargo-fuzz = true [dependencies] hash-db = { path = "../../hash-db", version = "0.15.2" } -memory-db = { path = "../../memory-db", version = "0.18.1" } -reference-trie = { path = "../../test-support/reference-trie", version = "0.19.0" } +memory-db = { path = "../../memory-db", version = "0.20.1" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.20.1" } keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.2" } [dependencies.trie-db] diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index a0dd8549..ce2cc2e6 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Traverse a trie. +//! Traverse a trie following a given set of keys. //! //! The traversal stack is updatable, and is therefore usable for //! batch update of ordered key values. @@ -102,7 +102,7 @@ struct StackedItem /// nibble, this is more memory costy than strictly necessary). /// Note that split_child is always a first_child. /// TODO rename to first modified child (non delete). - first_child: Option<(StackedItemChild, Vec)>, + first_child: Option>, /// true when the value can be deleted and only more /// than one branch cannot be deleted. can_fuse: bool, @@ -190,7 +190,7 @@ impl StackedItem } fn first_child_index(&self) -> Option { - self.first_child.as_ref().map(|c| c.0.parent_index) + self.first_child.as_ref().map(|c| c.parent_index) } fn is_split_child(&self, index: u8) -> bool { @@ -198,18 +198,18 @@ impl StackedItem } // take first child (used for fusing, otherwhise process_first_child is probably what you want) - fn take_first_child(&mut self) -> Option<(StackedItem, Vec)> { + fn take_first_child(&mut self) -> Option> { // descending in first child is only for fusizg node // so we do not update self first child status (will be deleted). - if let Some((StackedItemChild { + if let Some(StackedItemChild { node, depth, depth_prefix, hash, parent_index, .. - }, child_key)) = self.first_child.take() { - Some((StackedItem { + }) = self.first_child.take() { + Some(StackedItem { node, depth, depth_prefix, @@ -218,7 +218,7 @@ impl StackedItem hash, parent_index, can_fuse: false, - }, child_key)) + }) } else { None } @@ -300,7 +300,7 @@ impl StackedItem >(&mut self, mid_index: usize, key: &[u8], callback: &mut F) { // // if self got split child, it can be processed // // (we went up and this is a next key) (ordering) - self.process_first_child_then_split(callback); + self.process_first_child_then_split(key, callback); // or it means we need to store key to // debug_assert!(self.split_child_index().is_none()); let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { @@ -380,20 +380,21 @@ impl StackedItem fn process_first_child< F: ProcessStack, - >(&mut self, callback: &mut F) { - self.process_first_child_inner(callback, false) + >(&mut self, key: &[u8], callback: &mut F) { + self.process_first_child_inner(key, callback, false) } + // TODO single call remove the function fn process_first_child_then_split< F: ProcessStack, - >(&mut self, callback: &mut F) { - self.process_first_child_inner(callback, true) + >(&mut self, key: &[u8], callback: &mut F) { + self.process_first_child_inner(key, callback, true) } fn process_first_child_inner< F: ProcessStack, - >(&mut self, callback: &mut F, always_split: bool) { - if let Some((child, key)) = self.first_child.take() { + >(&mut self, key: &[u8], callback: &mut F, always_split: bool) { + if let Some(child) = self.first_child.take() { if let Some(split_child_index) = self.split_child_index() { if split_child_index < child.parent_index { self.process_split_child(key.as_ref(), callback); @@ -415,7 +416,7 @@ impl StackedItem if let Some(first_child_index) = self.first_child_index() { if first_child_index < child.parent_index { - self.process_first_child(callback); + self.process_first_child(key, callback); } } if let Some(split_child_index) = self.split_child_index() { @@ -425,7 +426,7 @@ impl StackedItem } // split child can be unprocessed (when going up it is kept after second node // in expectation of other children process. - child.process_first_child(callback); + child.process_first_child(key, callback); child.process_split_child(key, callback); let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); self.append_child(child.into(), nibble_slice.left(), callback); @@ -435,8 +436,8 @@ impl StackedItem fn process_root< F: ProcessStack, >(mut self, key: &[u8], callback: &mut F) { - self.process_first_child(callback); - self.process_split_child(key.as_ref(), callback); + self.process_first_child(key, callback); + self.process_split_child(key, callback); callback.exit_root( self.node, self.hash.as_ref(), @@ -714,13 +715,13 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let last = next_query.is_none(); // unstack nodes if needed while last || target_common_depth < current.depth_prefix || current.node.is_empty() { - let first_child_index = current.first_child.as_ref().map(|c| c.0.parent_index); + let first_child_index = current.first_child.as_ref().map(|c| c.parent_index); // TODO function for that to use // needed also to resolve if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { // try first child - if let Some((child, child_key)) = current.take_first_child() { + if let Some(child) = current.take_first_child() { debug_assert!(child.parent_index == fuse_index); - let mut prefix = NibbleVec::from(child_key.as_ref(), current.depth); + let mut prefix = NibbleVec::from(key.as_ref(), current.depth); prefix.push(fuse_index); child.node.partial().map(|p| prefix.append_partial(p.right())); current.fuse_branch(child, prefix.inner(), callback); @@ -739,7 +740,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // child change or addition if let Some(mut parent) = stack.pop() { if current.node.is_empty() { - current.process_first_child(callback); + current.process_first_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); @@ -749,7 +750,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // Deletion case is guaranted by ordering of input (fix delete only if no first // and no split). TODO the number of calls to process first and split is wrong: // should be once after fix_node only: that is especially for append_child case. - current.process_first_child(callback); + current.process_first_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); @@ -770,18 +771,18 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if !parent.test_can_fuse(key.as_ref(), Some(current.parent_index)) { parent.process_child(current, key.as_ref(), callback); } else { - current.process_first_child(callback); + current.process_first_child(key.as_ref(), callback); // split child is after first child (would be processed otherwhise). current.process_split_child(key.as_ref(), callback); // first node visited on a fusable element, store in parent first child and process later. // Process an eventual split child (index after current). - parent.first_child = Some((current.into(), key.as_ref().to_vec())); + parent.first_child = Some(current.into()); } } current = parent; } else { if last { - current.process_first_child(callback); + current.process_first_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); current.process_root(key.as_ref(), callback); return Ok(()); @@ -800,14 +801,14 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( break; } } - let first_child_index = current.first_child.as_ref().map(|c| c.0.parent_index); + let first_child_index = current.first_child.as_ref().map(|c| c.parent_index); // TODO there is a function for that // needed also to resolve if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { // try first child - if let Some((child, child_key)) = current.take_first_child() { + if let Some(child) = current.take_first_child() { debug_assert!(child.parent_index == fuse_index); // TODO probably no use in storing child_key here - let mut prefix = NibbleVec::from(child_key.as_ref(), current.depth); + let mut prefix = NibbleVec::from(key.as_ref(), current.depth); prefix.push(fuse_index); child.node.partial().map(|p| prefix.append_partial(p.right())); current.fuse_branch(child, prefix.inner(), callback); @@ -827,7 +828,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if target_common_depth < current.depth { // TODO this can probably remove a lot of those calls TODO check especially // calls in going down path at split child. - current.process_first_child(callback); + current.process_first_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback) } @@ -931,7 +932,7 @@ fn fetch>( struct BatchUpdate( Vec<(OwnedPrefix, H, Option>)>, H, - Option, + Option, // TODO EMCH remove?? ); impl ProcessStack for BatchUpdate> From d36aa6c49790dcfcea0a09e6fd242bda0298e57a Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 10:31:46 +0200 Subject: [PATCH 073/118] simplify split child, new field for compat most likely useless --- trie-db/src/traverse.rs | 43 +++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index ce2cc2e6..4d8a2b8b 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -94,7 +94,11 @@ struct StackedItem /// into this split child: TODO rational seems bad (we descend into the /// branch so when we call fix we pass this anyway. TODO in fixnode /// add a check for this index not being set (only if from second field). - split_child: Option<(Option>, Option)>, + /// That is the only situation where we got a modified item that may need + /// a to be iterated on at a next iteration. + split_child: Option>, + /// Did we descend in a split child. + in_split_child: bool, /// Store first child, until `exit` get call, this is needed /// to be able to fuse branch containing a single child (delay /// `exit` call of first element after process of the second one). @@ -172,19 +176,9 @@ impl StackedItem self.can_fuse } - - fn split_child_fuse_index(&self) -> Option { - match self.split_child.as_ref() { - Some((Some(child), _)) => Some(child.parent_index), - Some((_, Some(parent_index))) => Some(*parent_index), - None => None, - _ => unreachable!("This pair is Either, TODO swith to enum"), - } - } - fn split_child_index(&self) -> Option { match self.split_child.as_ref() { - Some((Some(child), _)) => Some(child.parent_index), + Some(child) => Some(child.parent_index), _ => None, } } @@ -215,6 +209,7 @@ impl StackedItem depth_prefix, first_child: None, split_child: None, + in_split_child: false, hash, parent_index, can_fuse: false, @@ -230,15 +225,15 @@ impl StackedItem CError > { Ok(if self.is_split_child(index) { - if let Some((Some(StackedItemChild { + if let Some(StackedItemChild { node, depth, depth_prefix, hash, parent_index, .. - }), None)) = self.split_child.take() { - self.split_child = Some((None, Some(parent_index))); + }) = self.split_child.take() { + self.in_split_child = true; // from a split child key is none (partial changed on split) Some(StackedItem { node, @@ -246,6 +241,7 @@ impl StackedItem depth_prefix, first_child: None, split_child: None, + in_split_child: false, hash, parent_index, can_fuse: true, @@ -286,6 +282,7 @@ impl StackedItem depth, first_child: None, split_child: None, + in_split_child: false, can_fuse: true, }) } else { @@ -344,7 +341,7 @@ impl StackedItem depth: old_depth, parent_index, }; - self.split_child = Some((Some(child), None)); + self.split_child = Some(child); } fn append_child< @@ -355,8 +352,9 @@ impl StackedItem child.node, child.hash.as_ref(), ) { - if self.split_child_fuse_index() == Some(child.parent_index) { + if self.in_split_child { self.split_child = None; + self.in_split_child = false; } self.node.set_handle(handle, child.parent_index); } @@ -365,8 +363,8 @@ impl StackedItem fn process_split_child< F: ProcessStack, >(&mut self, key: &[u8], callback: &mut F) { - if let Some((Some(child), None)) = self.split_child.take() { - self.split_child = Some((None, Some(child.parent_index))); + if let Some(child) = self.split_child.take() { + self.in_split_child = true; // TODO EMCH look wrong // prefix is slice let mut build_prefix = NibbleVec::from(key, self.depth_prefix); // TODO do same for first child (would need bench) -> here we @@ -691,6 +689,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( parent_index: 0, first_child: None, split_child: None, + in_split_child: false, can_fuse: true, }; @@ -717,7 +716,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( while last || target_common_depth < current.depth_prefix || current.node.is_empty() { let first_child_index = current.first_child.as_ref().map(|c| c.parent_index); // TODO function for that to use // needed also to resolve - if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { + if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_index())) { // try first child if let Some(child) = current.take_first_child() { debug_assert!(child.parent_index == fuse_index); @@ -803,7 +802,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } let first_child_index = current.first_child.as_ref().map(|c| c.parent_index); // TODO there is a function for that // needed also to resolve - if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_fuse_index())) { + if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_index())) { // try first child if let Some(child) = current.take_first_child() { debug_assert!(child.parent_index == fuse_index); @@ -978,6 +977,7 @@ impl ProcessStack for BatchUpdate> depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, split_child: None, + in_split_child: false, first_child: None, can_fuse: false, }; @@ -1035,6 +1035,7 @@ impl ProcessStack for BatchUpdate> depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, split_child: None, + in_split_child: false, first_child: None, can_fuse: false, }; From d81ef375d1361e62e0e374d7d72379bfcfdf0da2 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 10:34:04 +0200 Subject: [PATCH 074/118] indeed, removed is_in_slpit_child --- trie-db/src/traverse.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 4d8a2b8b..cb6e3159 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -97,8 +97,6 @@ struct StackedItem /// That is the only situation where we got a modified item that may need /// a to be iterated on at a next iteration. split_child: Option>, - /// Did we descend in a split child. - in_split_child: bool, /// Store first child, until `exit` get call, this is needed /// to be able to fuse branch containing a single child (delay /// `exit` call of first element after process of the second one). @@ -209,7 +207,6 @@ impl StackedItem depth_prefix, first_child: None, split_child: None, - in_split_child: false, hash, parent_index, can_fuse: false, @@ -233,7 +230,6 @@ impl StackedItem parent_index, .. }) = self.split_child.take() { - self.in_split_child = true; // from a split child key is none (partial changed on split) Some(StackedItem { node, @@ -241,7 +237,6 @@ impl StackedItem depth_prefix, first_child: None, split_child: None, - in_split_child: false, hash, parent_index, can_fuse: true, @@ -282,7 +277,6 @@ impl StackedItem depth, first_child: None, split_child: None, - in_split_child: false, can_fuse: true, }) } else { @@ -352,10 +346,6 @@ impl StackedItem child.node, child.hash.as_ref(), ) { - if self.in_split_child { - self.split_child = None; - self.in_split_child = false; - } self.node.set_handle(handle, child.parent_index); } } @@ -364,7 +354,6 @@ impl StackedItem F: ProcessStack, >(&mut self, key: &[u8], callback: &mut F) { if let Some(child) = self.split_child.take() { - self.in_split_child = true; // TODO EMCH look wrong // prefix is slice let mut build_prefix = NibbleVec::from(key, self.depth_prefix); // TODO do same for first child (would need bench) -> here we @@ -689,7 +678,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( parent_index: 0, first_child: None, split_child: None, - in_split_child: false, can_fuse: true, }; @@ -977,7 +965,6 @@ impl ProcessStack for BatchUpdate> depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, split_child: None, - in_split_child: false, first_child: None, can_fuse: false, }; @@ -1035,7 +1022,6 @@ impl ProcessStack for BatchUpdate> depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, split_child: None, - in_split_child: false, first_child: None, can_fuse: false, }; From 96c42bd0ede1d887a9588af7889682bbca4c6700 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 10:37:35 +0200 Subject: [PATCH 075/118] rename --- trie-db/src/traverse.rs | 91 ++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 47 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index cb6e3159..d51867d9 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -89,11 +89,8 @@ struct StackedItem depth: usize, /// parent index (only relevant when parent is a branch). parent_index: u8, - /// Store split child to create after we did insert a branch - /// into a partial. We keep index (second field) in case we descend - /// into this split child: TODO rational seems bad (we descend into the - /// branch so when we call fix we pass this anyway. TODO in fixnode - /// add a check for this index not being set (only if from second field). + /// Store split child data created when adding a new node in an existing + /// partial. /// That is the only situation where we got a modified item that may need /// a to be iterated on at a next iteration. split_child: Option>, @@ -102,9 +99,9 @@ struct StackedItem /// `exit` call of first element after process of the second one). /// Store child and the key use when storing (to calculate final /// nibble, this is more memory costy than strictly necessary). - /// Note that split_child is always a first_child. + /// Note that split_child is always a first_modified_child. /// TODO rename to first modified child (non delete). - first_child: Option>, + first_modified_child: Option>, /// true when the value can be deleted and only more /// than one branch cannot be deleted. can_fuse: bool, @@ -181,16 +178,16 @@ impl StackedItem } } - fn first_child_index(&self) -> Option { - self.first_child.as_ref().map(|c| c.parent_index) + fn first_modified_child_index(&self) -> Option { + self.first_modified_child.as_ref().map(|c| c.parent_index) } fn is_split_child(&self, index: u8) -> bool { self.split_child_index().map(|child_index| child_index == index).unwrap_or(false) } - // take first child (used for fusing, otherwhise process_first_child is probably what you want) - fn take_first_child(&mut self) -> Option> { + // take first child (used for fusing, otherwhise process_first_modified_child is probably what you want) + fn take_first_modified_child(&mut self) -> Option> { // descending in first child is only for fusizg node // so we do not update self first child status (will be deleted). if let Some(StackedItemChild { @@ -200,12 +197,12 @@ impl StackedItem hash, parent_index, .. - }) = self.first_child.take() { + }) = self.first_modified_child.take() { Some(StackedItem { node, depth, depth_prefix, - first_child: None, + first_modified_child: None, split_child: None, hash, parent_index, @@ -235,7 +232,7 @@ impl StackedItem node, depth, depth_prefix, - first_child: None, + first_modified_child: None, split_child: None, hash, parent_index, @@ -275,7 +272,7 @@ impl StackedItem parent_index: index, depth_prefix, depth, - first_child: None, + first_modified_child: None, split_child: None, can_fuse: true, }) @@ -291,7 +288,7 @@ impl StackedItem >(&mut self, mid_index: usize, key: &[u8], callback: &mut F) { // // if self got split child, it can be processed // // (we went up and this is a next key) (ordering) - self.process_first_child_then_split(key, callback); + self.process_first_modified_child_then_split(key, callback); // or it means we need to store key to // debug_assert!(self.split_child_index().is_none()); let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { @@ -322,7 +319,7 @@ impl StackedItem child.advance_partial(1 + mid_index - self.depth_prefix); // // split occurs before visiting a single child -// debug_assert!(self.first_child.is_none()); +// debug_assert!(self.first_modified_child.is_none()); // debug_assert!(!self.cannot_fuse); // ordering key also ensure // debug_assert!(self.split_child_index().is_none()); @@ -365,23 +362,23 @@ impl StackedItem } } - fn process_first_child< + fn process_first_modified_child< F: ProcessStack, >(&mut self, key: &[u8], callback: &mut F) { - self.process_first_child_inner(key, callback, false) + self.process_first_modified_child_inner(key, callback, false) } // TODO single call remove the function - fn process_first_child_then_split< + fn process_first_modified_child_then_split< F: ProcessStack, >(&mut self, key: &[u8], callback: &mut F) { - self.process_first_child_inner(key, callback, true) + self.process_first_modified_child_inner(key, callback, true) } - fn process_first_child_inner< + fn process_first_modified_child_inner< F: ProcessStack, >(&mut self, key: &[u8], callback: &mut F, always_split: bool) { - if let Some(child) = self.first_child.take() { + if let Some(child) = self.first_modified_child.take() { if let Some(split_child_index) = self.split_child_index() { if split_child_index < child.parent_index { self.process_split_child(key.as_ref(), callback); @@ -401,9 +398,9 @@ impl StackedItem F: ProcessStack, >(&mut self, mut child: StackedItem, key: &[u8], callback: &mut F) { - if let Some(first_child_index) = self.first_child_index() { - if first_child_index < child.parent_index { - self.process_first_child(key, callback); + if let Some(first_modified_child_index) = self.first_modified_child_index() { + if first_modified_child_index < child.parent_index { + self.process_first_modified_child(key, callback); } } if let Some(split_child_index) = self.split_child_index() { @@ -413,7 +410,7 @@ impl StackedItem } // split child can be unprocessed (when going up it is kept after second node // in expectation of other children process. - child.process_first_child(key, callback); + child.process_first_modified_child(key, callback); child.process_split_child(key, callback); let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); self.append_child(child.into(), nibble_slice.left(), callback); @@ -423,7 +420,7 @@ impl StackedItem fn process_root< F: ProcessStack, >(mut self, key: &[u8], callback: &mut F) { - self.process_first_child(key, callback); + self.process_first_modified_child(key, callback); self.process_split_child(key, callback); callback.exit_root( self.node, @@ -449,7 +446,7 @@ impl StackedItem self.hash = child.hash; self.depth = child_depth; self.can_fuse = true; - self.first_child = None; + self.first_modified_child = None; self.split_child = None; } } @@ -676,7 +673,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( depth_prefix: 0, depth, parent_index: 0, - first_child: None, + first_modified_child: None, split_child: None, can_fuse: true, }; @@ -702,11 +699,11 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let last = next_query.is_none(); // unstack nodes if needed while last || target_common_depth < current.depth_prefix || current.node.is_empty() { - let first_child_index = current.first_child.as_ref().map(|c| c.parent_index); // TODO function for that to use + let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO function for that to use // needed also to resolve - if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_index())) { + if let Some(fuse_index) = current.node.fix_node((first_modified_child_index, current.split_child_index())) { // try first child - if let Some(child) = current.take_first_child() { + if let Some(child) = current.take_first_modified_child() { debug_assert!(child.parent_index == fuse_index); let mut prefix = NibbleVec::from(key.as_ref(), current.depth); prefix.push(fuse_index); @@ -727,7 +724,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // child change or addition if let Some(mut parent) = stack.pop() { if current.node.is_empty() { - current.process_first_child(key.as_ref(), callback); + current.process_first_modified_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); @@ -737,12 +734,12 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // Deletion case is guaranted by ordering of input (fix delete only if no first // and no split). TODO the number of calls to process first and split is wrong: // should be once after fix_node only: that is especially for append_child case. - current.process_first_child(key.as_ref(), callback); + current.process_first_modified_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); - } else if let Some(first_child_index) = parent.first_child_index() { - debug_assert!(first_child_index < current.parent_index); + } else if let Some(first_modified_child_index) = parent.first_modified_child_index() { + debug_assert!(first_modified_child_index < current.parent_index); parent.process_child(current, key.as_ref(), callback); } else { if let Some(split_child_index) = parent.split_child_index() { @@ -758,18 +755,18 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if !parent.test_can_fuse(key.as_ref(), Some(current.parent_index)) { parent.process_child(current, key.as_ref(), callback); } else { - current.process_first_child(key.as_ref(), callback); + current.process_first_modified_child(key.as_ref(), callback); // split child is after first child (would be processed otherwhise). current.process_split_child(key.as_ref(), callback); // first node visited on a fusable element, store in parent first child and process later. // Process an eventual split child (index after current). - parent.first_child = Some(current.into()); + parent.first_modified_child = Some(current.into()); } } current = parent; } else { if last { - current.process_first_child(key.as_ref(), callback); + current.process_first_modified_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); current.process_root(key.as_ref(), callback); return Ok(()); @@ -788,11 +785,11 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( break; } } - let first_child_index = current.first_child.as_ref().map(|c| c.parent_index); // TODO there is a function for that + let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO there is a function for that // needed also to resolve - if let Some(fuse_index) = current.node.fix_node((first_child_index, current.split_child_index())) { + if let Some(fuse_index) = current.node.fix_node((first_modified_child_index, current.split_child_index())) { // try first child - if let Some(child) = current.take_first_child() { + if let Some(child) = current.take_first_modified_child() { debug_assert!(child.parent_index == fuse_index); // TODO probably no use in storing child_key here let mut prefix = NibbleVec::from(key.as_ref(), current.depth); @@ -815,12 +812,12 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if target_common_depth < current.depth { // TODO this can probably remove a lot of those calls TODO check especially // calls in going down path at split child. - current.process_first_child(key.as_ref(), callback); + current.process_first_modified_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback) } /* if !current.test_can_fuse(key.as_ref(), None) { - current.process_first_child(callback); + current.process_first_modified_child(callback); if target_common_depth < current.depth { current.process_split_child(key.as_ref(), callback); } @@ -965,7 +962,7 @@ impl ProcessStack for BatchUpdate> depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, split_child: None, - first_child: None, + first_modified_child: None, can_fuse: false, }; return if stacked.node.is_empty() { @@ -1022,7 +1019,7 @@ impl ProcessStack for BatchUpdate> depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, split_child: None, - first_child: None, + first_modified_child: None, can_fuse: false, }; Some(child) From 14a3592a73c79da58071b83f7201b8d0fa38000e Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 11:46:53 +0200 Subject: [PATCH 076/118] remove struct redundancy and some renaming. --- trie-db/src/traverse.rs | 414 ++++++++++++++++++---------------------- 1 file changed, 187 insertions(+), 227 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index d51867d9..c6fddb00 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -38,9 +38,9 @@ use crate::rstd::{cmp, mem}; type StorageHandle = Vec; type OwnedNodeHandle = NodeHandleTrieMut; -/// StackedNode can be updated. +/// StackedNodeState can be updated. /// A state can be use. -enum StackedNode +enum StackedNodeState where B: Borrow<[u8]>, T: TrieLayout, @@ -53,13 +53,13 @@ enum StackedNode Deleted, } /* -impl StackedNode +impl StackedNodeState where B: Borrow<[u8]>, T: TrieLayout, { fn is_deleted(&self) -> bool { - if let StackedNode::Deleted = self { + if let StackedNodeState::Deleted = self { true } else { false @@ -78,30 +78,24 @@ struct StackedItem B: Borrow<[u8]>, T: TrieLayout, { - /// Internal node representation. - node: StackedNode, - /// Hash used to access this node, for inline node and - /// new nodes this is None. - hash: Option>, - /// Index of prefix. - depth_prefix: usize, - /// Depth of node, it is prefix depth and partial depth. - depth: usize, - /// parent index (only relevant when parent is a branch). - parent_index: u8, + /// the given node item. + item: StackedNode, /// Store split child data created when adding a new node in an existing /// partial. /// That is the only situation where we got a modified item that may need /// a to be iterated on at a next iteration. - split_child: Option>, + /// Note that this cannot be fuse with `first_modified_child` because of + /// the case where a split child is at an high child index than the first + /// modified and will later be deleted, leading to a single child without + /// value fuse branch trigger. + split_child: Option>, /// Store first child, until `exit` get call, this is needed /// to be able to fuse branch containing a single child (delay /// `exit` call of first element after process of the second one). /// Store child and the key use when storing (to calculate final /// nibble, this is more memory costy than strictly necessary). /// Note that split_child is always a first_modified_child. - /// TODO rename to first modified child (non delete). - first_modified_child: Option>, + first_modified_child: Option>, /// true when the value can be deleted and only more /// than one branch cannot be deleted. can_fuse: bool, @@ -109,13 +103,13 @@ struct StackedItem /// Variant of stacked item to store first changed node. -struct StackedItemChild +struct StackedNode where B: Borrow<[u8]>, T: TrieLayout, { /// Internal node representation. - node: StackedNode, + node: StackedNodeState, /// Hash used to access this node, for inline node and /// new nodes this is None. hash: Option>, @@ -127,27 +121,13 @@ struct StackedItemChild parent_index: u8, } -impl From> for StackedItemChild +impl From> for StackedNode where B: Borrow<[u8]>, T: TrieLayout, { fn from(item: StackedItem) -> Self { - let StackedItem { - node, - hash, - depth_prefix, - depth, - parent_index, - .. - } = item; - StackedItemChild { - node, - hash, - depth_prefix, - depth, - parent_index, - } + item.item } } @@ -159,7 +139,7 @@ impl StackedItem // function is only call when going up, meaning // traverse_key depth + 1 is already visited. fn can_fuse(&self, traverse_key: &[u8]) -> bool { - //if let StackedNode::Changedself.node. + //if let StackedNodeState::Changedself.node. unimplemented!() } @@ -190,23 +170,12 @@ impl StackedItem fn take_first_modified_child(&mut self) -> Option> { // descending in first child is only for fusizg node // so we do not update self first child status (will be deleted). - if let Some(StackedItemChild { - node, - depth, - depth_prefix, - hash, - parent_index, - .. - }) = self.first_modified_child.take() { + if let Some(item) = self.first_modified_child.take() { Some(StackedItem { - node, - depth, - depth_prefix, - first_modified_child: None, - split_child: None, - hash, - parent_index, - can_fuse: false, + item, + first_modified_child: None, + split_child: None, + can_fuse: false, }) } else { None @@ -219,59 +188,50 @@ impl StackedItem CError > { Ok(if self.is_split_child(index) { - if let Some(StackedItemChild { - node, - depth, - depth_prefix, - hash, - parent_index, - .. - }) = self.split_child.take() { + if let Some(item) = self.split_child.take() { // from a split child key is none (partial changed on split) Some(StackedItem { - node, - depth, - depth_prefix, + item, first_modified_child: None, split_child: None, - hash, - parent_index, can_fuse: true, }) } else { unreachable!("Broken split expectation, visited twice"); } } else { - if let Some(node_handle) = self.node.child(index) { + if let Some(node_handle) = self.item.node.child(index) { let (node, hash) = match node_handle { NodeHandle::Hash(handle_hash) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - (StackedNode::Unchanged( + (StackedNodeState::Unchanged( fetch::( db, &hash, prefix, )?), Some(hash)) }, NodeHandle::Inline(node_encoded) => { // Instantiating B is only for inline node, still costy. - (StackedNode::Unchanged( + (StackedNodeState::Unchanged( OwnedNode::new::(B::from(node_encoded)) .map_err(|e| Box::new(TrieError::DecoderError( - self.hash.clone().unwrap_or_else(Default::default), + self.item.hash.clone().unwrap_or_else(Default::default), e, )))? ), None) }, }; - let depth_prefix = self.depth + 1; + let depth_prefix = self.item.depth + 1; let depth = depth_prefix + node.partial().map(|p| p.len()).unwrap_or(0); Some(StackedItem { - node, - hash, - parent_index: index, - depth_prefix, - depth, + item: StackedNode { + node, + hash, + parent_index: index, + depth_prefix, + depth, + }, first_modified_child: None, split_child: None, can_fuse: true, @@ -294,30 +254,30 @@ impl StackedItem let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { let new_slice = NibbleSlice::new_offset( &key[..mid_index / nibble_ops::NIBBLE_PER_BYTE], - self.depth_prefix, + self.item.depth_prefix, ); Node::new_branch(new_slice) } else { let new_slice = NibbleSlice::new_offset( &key[..], - self.depth_prefix, + self.item.depth_prefix, ); - let owned = new_slice.to_stored_range(mid_index - self.depth_prefix); + let owned = new_slice.to_stored_range(mid_index - self.item.depth_prefix); // TODO EMCH refactor new_leaf to take BackingByteVec (stored) as input Node::new_branch(NibbleSlice::from_stored(&owned)) }; - let old_depth = self.depth; - self.depth = mid_index; + let old_depth = self.item.depth; + self.item.depth = mid_index; let mut child = mem::replace( - &mut self.node, - StackedNode::Changed(dest_branch), + &mut self.item.node, + StackedNodeState::Changed(dest_branch), ); let parent_index = child.partial() - .map(|p| p.at(mid_index - self.depth_prefix)).unwrap_or(0); + .map(|p| p.at(mid_index - self.item.depth_prefix)).unwrap_or(0); - child.advance_partial(1 + mid_index - self.depth_prefix); + child.advance_partial(1 + mid_index - self.item.depth_prefix); // // split occurs before visiting a single child // debug_assert!(self.first_modified_child.is_none()); // debug_assert!(!self.cannot_fuse); @@ -325,25 +285,26 @@ impl StackedItem // debug_assert!(self.split_child_index().is_none()); // not setting child relation (will be set on exit) - let child = StackedItemChild { + let child = StackedNode { node: child, hash: None, depth_prefix: 1 + mid_index, depth: old_depth, parent_index, }; + debug_assert!(self.first_modified_child.is_none()); self.split_child = Some(child); } fn append_child< F: ProcessStack, - >(&mut self, child: StackedItemChild, prefix: Prefix, callback: &mut F) { + >(&mut self, child: StackedNode, prefix: Prefix, callback: &mut F) { if let Some(handle) = callback.exit( prefix, child.node, child.hash.as_ref(), ) { - self.node.set_handle(handle, child.parent_index); + self.item.node.set_handle(handle, child.parent_index); } } @@ -352,11 +313,11 @@ impl StackedItem >(&mut self, key: &[u8], callback: &mut F) { if let Some(child) = self.split_child.take() { // prefix is slice - let mut build_prefix = NibbleVec::from(key, self.depth_prefix); + let mut build_prefix = NibbleVec::from(key, self.item.depth_prefix); // TODO do same for first child (would need bench) -> here we // should have a reusable nibblevec to avoid this allocation // everywhere but would relieve a stored Vec !! - self.node.partial().map(|p| build_prefix.append_partial(p.right())); + self.item.node.partial().map(|p| build_prefix.append_partial(p.right())); build_prefix.push(child.parent_index); self.append_child(child, build_prefix.as_prefix(), callback); } @@ -399,12 +360,12 @@ impl StackedItem >(&mut self, mut child: StackedItem, key: &[u8], callback: &mut F) { if let Some(first_modified_child_index) = self.first_modified_child_index() { - if first_modified_child_index < child.parent_index { + if first_modified_child_index < child.item.parent_index { self.process_first_modified_child(key, callback); } } if let Some(split_child_index) = self.split_child_index() { - if split_child_index < child.parent_index { + if split_child_index < child.item.parent_index { self.process_split_child(key.as_ref(), callback); } } @@ -412,7 +373,7 @@ impl StackedItem // in expectation of other children process. child.process_first_modified_child(key, callback); child.process_split_child(key, callback); - let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); + let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.item.depth_prefix); self.append_child(child.into(), nibble_slice.left(), callback); self.can_fuse = false; } @@ -423,8 +384,8 @@ impl StackedItem self.process_first_modified_child(key, callback); self.process_split_child(key, callback); callback.exit_root( - self.node, - self.hash.as_ref(), + self.item.node, + self.item.hash.as_ref(), ) } @@ -432,26 +393,26 @@ impl StackedItem fn fuse_branch< F: ProcessStack, >(&mut self, child: StackedItem, key: &[u8], callback: &mut F) { - let child_depth = self.depth + 1 + child.node.partial().map(|p| p.len()).unwrap_or(0); - let to_rem = mem::replace(&mut self.node, child.node); + let child_depth = self.item.depth + 1 + child.item.node.partial().map(|p| p.len()).unwrap_or(0); + let to_rem = mem::replace(&mut self.item.node, child.item.node); // delete current callback.exit( - NibbleSlice::new_offset(key.as_ref(), self.depth_prefix).left(), - to_rem, self.hash.as_ref(), + NibbleSlice::new_offset(key.as_ref(), self.item.depth_prefix).left(), + to_rem, self.item.hash.as_ref(), ).expect("no new node on empty"); - let partial = NibbleSlice::new_offset(key, self.depth_prefix) - .to_stored_range(child_depth - self.depth_prefix); - self.node.set_partial(partial); - self.hash = child.hash; - self.depth = child_depth; + let partial = NibbleSlice::new_offset(key, self.item.depth_prefix) + .to_stored_range(child_depth - self.item.depth_prefix); + self.item.node.set_partial(partial); + self.item.hash = child.item.hash; + self.item.depth = child_depth; self.can_fuse = true; self.first_modified_child = None; self.split_child = None; } } -impl StackedNode +impl StackedNodeState where B: Borrow<[u8]> + AsRef<[u8]>, T: TrieLayout, @@ -459,125 +420,125 @@ impl StackedNode /// Get extension part of the node (partial) if any. fn is_empty(&self) -> bool { match self { - StackedNode::Unchanged(node) => node.is_empty(), - StackedNode::Changed(node) => node.is_empty(), - StackedNode::Deleted => true, + StackedNodeState::Unchanged(node) => node.is_empty(), + StackedNodeState::Changed(node) => node.is_empty(), + StackedNodeState::Deleted => true, } } /// Get extension part of the node (partial) if any. fn partial(&self) -> Option { match self { - StackedNode::Unchanged(node) => node.partial(), - StackedNode::Changed(node) => node.partial(), - StackedNode::Deleted => None, + StackedNodeState::Unchanged(node) => node.partial(), + StackedNodeState::Changed(node) => node.partial(), + StackedNodeState::Deleted => None, } } /// Try to access child. fn child(&self, ix: u8) -> Option { match self { - StackedNode::Unchanged(node) => node.child(ix), - StackedNode::Changed(node) => node.child(ix), - StackedNode::Deleted => None, + StackedNodeState::Unchanged(node) => node.child(ix), + StackedNodeState::Changed(node) => node.child(ix), + StackedNodeState::Deleted => None, } } /// Tell if there is a value defined at this node position. fn has_value(&self) -> bool { match self { - StackedNode::Unchanged(node) => node.has_value(), - StackedNode::Changed(node) => node.has_value(), - StackedNode::Deleted => false, + StackedNodeState::Unchanged(node) => node.has_value(), + StackedNodeState::Changed(node) => node.has_value(), + StackedNodeState::Deleted => false, } } /// Set a value if the node can contain one. fn set_value(&mut self, value: &[u8]) { match self { - StackedNode::Unchanged(node) => { + StackedNodeState::Unchanged(node) => { if let Some(new) = node.set_value(value) { - *self = StackedNode::Changed(new); + *self = StackedNodeState::Changed(new); } }, - StackedNode::Changed(node) => node.set_value(value), - StackedNode::Deleted => (), + StackedNodeState::Changed(node) => node.set_value(value), + StackedNodeState::Deleted => (), } } /// Change a partial if the node contains one. fn advance_partial(&mut self, nb: usize) { match self { - StackedNode::Unchanged(node) => { + StackedNodeState::Unchanged(node) => { if let Some(new) = node.advance_partial(nb) { - *self = StackedNode::Changed(new); + *self = StackedNodeState::Changed(new); } }, - StackedNode::Changed(node) => node.advance_partial(nb), - StackedNode::Deleted => (), + StackedNodeState::Changed(node) => node.advance_partial(nb), + StackedNodeState::Deleted => (), } } /// Set a new partial. fn set_partial(&mut self, partial: NodeKey) { match self { - StackedNode::Unchanged(node) => { + StackedNodeState::Unchanged(node) => { if let Some(new) = node.set_partial(partial) { - *self = StackedNode::Changed(new); + *self = StackedNodeState::Changed(new); } }, - StackedNode::Changed(node) => node.set_partial(partial), - StackedNode::Deleted => (), + StackedNodeState::Changed(node) => node.set_partial(partial), + StackedNodeState::Deleted => (), } } /// Remove a value if the node contains one. fn remove_value(&mut self) { match self { - StackedNode::Unchanged(node) => { + StackedNodeState::Unchanged(node) => { match node.remove_value() { Some(Some(new)) => - *self = StackedNode::Changed(new), + *self = StackedNodeState::Changed(new), Some(None) => - *self = StackedNode::Deleted, + *self = StackedNodeState::Deleted, None => (), } }, - StackedNode::Changed(node) => { + StackedNodeState::Changed(node) => { if node.remove_value() { - *self = StackedNode::Deleted; + *self = StackedNodeState::Deleted; } }, - StackedNode::Deleted => (), + StackedNodeState::Deleted => (), } } /// Set a handle to a child node or remove it if handle is none. fn set_handle(&mut self, handle: Option>>, index: u8) { match self { - StackedNode::Unchanged(node) => { + StackedNodeState::Unchanged(node) => { let change = node.set_handle(handle, index); match change { - Some(new) => *self = StackedNode::Changed(new), + Some(new) => *self = StackedNodeState::Changed(new), None => (), } }, - StackedNode::Changed(node) => { + StackedNodeState::Changed(node) => { node.set_handle(handle, index); }, - StackedNode::Deleted => unreachable!(), + StackedNodeState::Deleted => unreachable!(), } } /// Returns index of node to fuse with if fused require. fn fix_node(&mut self, pending: (Option, Option)) -> Option { match self { - StackedNode::Deleted - | StackedNode::Unchanged(..) => None, - StackedNode::Changed(node) => { + StackedNodeState::Deleted + | StackedNodeState::Unchanged(..) => None, + StackedNodeState::Changed(node) => { let (deleted, fuse) = node.fix_node(pending); if deleted { - *self = StackedNode::Deleted; + *self = StackedNodeState::Deleted; } fuse }, @@ -585,7 +546,7 @@ impl StackedNode } } -impl StackedNode +impl StackedNodeState where B: Borrow<[u8]> + AsRef<[u8]>, T: TrieLayout, @@ -594,12 +555,12 @@ impl StackedNode /// Encode node fn into_encoded(self) -> Vec { match self { - StackedNode::Unchanged(node) => node.data().to_vec(), - StackedNode::Changed(node) => node.into_encoded::<_, T::Codec, T::Hash>( + StackedNodeState::Unchanged(node) => node.data().to_vec(), + StackedNodeState::Changed(node) => node.into_encoded::<_, T::Codec, T::Hash>( |child, _o_slice, _o_index| { child.as_child_ref::() }), - StackedNode::Deleted => T::Codec::empty_node().to_vec(), + StackedNodeState::Deleted => T::Codec::empty_node().to_vec(), } } } @@ -624,10 +585,10 @@ trait ProcessStack ) -> Option>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<&TrieHash>) -> Option>>>; /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, stacked: StackedNode, prev_hash: Option<&TrieHash>); + fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<&TrieHash>); } /// State when descending @@ -665,14 +626,16 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( }; // TODO encapsulate fetch in stacked item, also split or others - let current = StackedNode::::Unchanged(root); + let current = StackedNodeState::::Unchanged(root); let depth = current.partial().map(|p| p.len()).unwrap_or(0); let mut current = StackedItem { - node: current, - hash: Some(*root_hash), - depth_prefix: 0, - depth, - parent_index: 0, + item: StackedNode { + node: current, + hash: Some(*root_hash), + depth_prefix: 0, + depth, + parent_index: 0, + }, first_modified_child: None, split_child: None, can_fuse: true, @@ -698,23 +661,23 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let last = next_query.is_none(); // unstack nodes if needed - while last || target_common_depth < current.depth_prefix || current.node.is_empty() { + while last || target_common_depth < current.item.depth_prefix || current.item.node.is_empty() { let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO function for that to use // needed also to resolve - if let Some(fuse_index) = current.node.fix_node((first_modified_child_index, current.split_child_index())) { + if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { // try first child if let Some(child) = current.take_first_modified_child() { - debug_assert!(child.parent_index == fuse_index); - let mut prefix = NibbleVec::from(key.as_ref(), current.depth); + debug_assert!(child.item.parent_index == fuse_index); + let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); prefix.push(fuse_index); - child.node.partial().map(|p| prefix.append_partial(p.right())); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); current.fuse_branch(child, prefix.inner(), callback); } else { - let mut prefix = NibbleVec::from(key.as_ref(), current.depth); + let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); prefix.push(fuse_index); let child = current.descend_child(fuse_index, db, prefix.as_prefix())? .expect("result of first child is define if consistent db"); - child.node.partial().map(|p| prefix.append_partial(p.right())); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); current.fuse_branch(child, prefix.inner(), callback); } // fuse child operation did switch current context. @@ -723,12 +686,12 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // TODO check if fuse (num child is 1). // child change or addition if let Some(mut parent) = stack.pop() { - if current.node.is_empty() { + if current.item.node.is_empty() { current.process_first_modified_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); - let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); - } else if !parent.test_can_fuse(key.as_ref(), Some(current.parent_index)) { + } else if !parent.test_can_fuse(key.as_ref(), Some(current.item.parent_index)) { // process exit, as we already assert two child, no need to store in case of parent // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first @@ -736,23 +699,23 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // should be once after fix_node only: that is especially for append_child case. current.process_first_modified_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); - let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); } else if let Some(first_modified_child_index) = parent.first_modified_child_index() { - debug_assert!(first_modified_child_index < current.parent_index); + debug_assert!(first_modified_child_index < current.item.parent_index); parent.process_child(current, key.as_ref(), callback); } else { if let Some(split_child_index) = parent.split_child_index() { - if split_child_index < current.parent_index { + if split_child_index < current.item.parent_index { parent.can_fuse = false; } } - if parent.can_fuse && parent.node.has_value() { + if parent.can_fuse && parent.item.node.has_value() { // this could not be fuse (delete value occurs before), // no stacking of first child parent.can_fuse = false; } - if !parent.test_can_fuse(key.as_ref(), Some(current.parent_index)) { + if !parent.test_can_fuse(key.as_ref(), Some(current.item.parent_index)) { parent.process_child(current, key.as_ref(), callback); } else { current.process_first_modified_child(key.as_ref(), callback); @@ -761,6 +724,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // first node visited on a fusable element, store in parent first child and process later. // Process an eventual split child (index after current). parent.first_modified_child = Some(current.into()); +// debug_assert!(parent.split_child.is_none()); } } current = parent; @@ -776,8 +740,8 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( NibbleSlice::new_offset(key.as_ref(), 0), value.as_ref(), ); - current.node = StackedNode::Changed(child); - current.depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + current.item.node = StackedNodeState::Changed(child); + current.item.depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; current.can_fuse = false; } // move to next key @@ -787,21 +751,21 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO there is a function for that // needed also to resolve - if let Some(fuse_index) = current.node.fix_node((first_modified_child_index, current.split_child_index())) { + if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { // try first child if let Some(child) = current.take_first_modified_child() { - debug_assert!(child.parent_index == fuse_index); + debug_assert!(child.item.parent_index == fuse_index); // TODO probably no use in storing child_key here - let mut prefix = NibbleVec::from(key.as_ref(), current.depth); + let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); prefix.push(fuse_index); - child.node.partial().map(|p| prefix.append_partial(p.right())); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); current.fuse_branch(child, prefix.inner(), callback); } else { - let mut prefix = NibbleVec::from(key.as_ref(), current.depth); + let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); prefix.push(fuse_index); let child = current.descend_child(fuse_index, db, prefix.as_prefix())? .expect("result of first child is define if consistent db"); - child.node.partial().map(|p| prefix.append_partial(p.right())); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); current.fuse_branch(child, prefix.inner(), callback); } // fuse child operation did switch current context. @@ -809,7 +773,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } } // no fix if middle then process buffed - if target_common_depth < current.depth { + if target_common_depth < current.item.depth { // TODO this can probably remove a lot of those calls TODO check especially // calls in going down path at split child. current.process_first_modified_child(key.as_ref(), callback); @@ -834,19 +798,19 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let dest_slice = NibbleFullKey::new(key.as_ref()); let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; let mut descend_mid_index = None; - if !current.node.is_empty() { + if !current.item.node.is_empty() { // corner case do not descend in empty node (else condition) TODO covered by empty_trie?? loop { // TODO check if first common index is simple target_common? of previous go up. - let common_index = current.node.partial() + let common_index = current.item.node.partial() .map(|current_partial| { - let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); - current.depth_prefix + current_partial.common_prefix(&target_partial) - }).unwrap_or(current.depth_prefix); + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); + current.item.depth_prefix + current_partial.common_prefix(&target_partial) + }).unwrap_or(current.item.depth_prefix); // TODO not sure >= or just >. - if common_index == current.depth && dest_depth > current.depth { - let next_index = dest_slice.at(current.depth); - let prefix = NibbleSlice::new_offset(key.as_ref(), current.depth + 1); + if common_index == current.item.depth && dest_depth > current.item.depth { + let next_index = dest_slice.at(current.item.depth); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth + 1); if let Some(child) = current.descend_child(next_index, db, prefix.left())? { stack.push(current); current = child; @@ -854,7 +818,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( break; } } else { - if common_index < current.depth { + if common_index < current.item.depth { descend_mid_index = Some(common_index); } break; @@ -863,16 +827,16 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } let traverse_state = if let Some(mid_index) = descend_mid_index { TraverseState::MidPartial(mid_index) - } else if dest_depth < current.depth { + } else if dest_depth < current.item.depth { // TODO this might be unreachable from previous loop // split child (insert in current prefix -> try fuzzing on unreachable - let mid_index = current.node.partial() + let mid_index = current.item.node.partial() .map(|current_partial| { - let target_partial = NibbleSlice::new_offset(key.as_ref(), current.depth_prefix); - current.depth_prefix + current_partial.common_prefix(&target_partial) - }).unwrap_or(current.depth_prefix); + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); + current.item.depth_prefix + current_partial.common_prefix(&target_partial) + }).unwrap_or(current.item.depth_prefix); TraverseState::MidPartial(mid_index) - } else if dest_depth > current.depth { + } else if dest_depth > current.item.depth { // over callback TraverseState::AfterNode } else { @@ -934,9 +898,9 @@ impl ProcessStack for BatchUpdate> match state { TraverseState::ValueMatch => { if let Some(value) = value_element { - stacked.node.set_value(value); + stacked.item.node.set_value(value); } else { - stacked.node.remove_value(); + stacked.item.node.remove_value(); } None }, @@ -944,30 +908,32 @@ impl ProcessStack for BatchUpdate> if let Some(val) = value_element { // corner case of empty trie. - let offset = if stacked.node.is_empty() { + let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; // dest is a leaf appended to terminal let dest_leaf = Node::new_leaf( - NibbleSlice::new_offset(key_element, stacked.depth + offset), + NibbleSlice::new_offset(key_element, stacked.item.depth + offset), val, ); - let parent_index = NibbleSlice::new(key_element).at(stacked.depth); + let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); let mut new_child = StackedItem { - node: StackedNode::Changed(dest_leaf), - hash: None, - depth_prefix: stacked.depth + offset, - depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index, + item: StackedNode { + node: StackedNodeState::Changed(dest_leaf), + hash: None, + depth_prefix: stacked.item.depth + offset, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + }, split_child: None, first_modified_child: None, can_fuse: false, }; - return if stacked.node.is_empty() { + return if stacked.item.node.is_empty() { // replace empty. - new_child.hash = stacked.hash; + new_child.item.hash = stacked.item.hash; *stacked = new_child; None } else { @@ -981,16 +947,8 @@ impl ProcessStack for BatchUpdate> }, TraverseState::MidPartial(mid_index) => { if let Some(value) = value_element { - if stacked.node.is_empty() { + if stacked.item.node.is_empty() { unreachable!(); - let child = Node::new_leaf( - NibbleSlice::new_offset(key_element, stacked.depth_prefix), - value.as_ref(), - ); - stacked.node = StackedNode::Changed(child); - stacked.depth = key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - stacked.can_fuse = false; - None } else { stacked.do_split_child(mid_index, key_element, self); let (offset, parent_index) = if key_element.len() == 0 { @@ -1008,16 +966,18 @@ impl ProcessStack for BatchUpdate> return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { // set value in new branch - stacked.node.set_value(value); + stacked.item.node.set_value(value); stacked.can_fuse = false; None } else { let child = StackedItem { - node: StackedNode::Changed(child), - hash: None, - depth_prefix: offset + mid_index, - depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index, + item: StackedNode { + node: StackedNodeState::Changed(child), + hash: None, + depth_prefix: offset + mid_index, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + }, split_child: None, first_modified_child: None, can_fuse: false, @@ -1035,10 +995,10 @@ impl ProcessStack for BatchUpdate> } - fn exit(&mut self, prefix: Prefix, stacked: StackedNode, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<&TrieHash>) -> Option>>> { match stacked { - StackedNode::Changed(node) => Some(Some({ + StackedNodeState::Changed(node) => Some(Some({ let encoded = node.into_encoded::<_, T::Codec, T::Hash>( |child, _o_slice, _o_index| { child.as_child_ref::() @@ -1058,7 +1018,7 @@ impl ProcessStack for BatchUpdate> OwnedNodeHandle::Hash(hash) } })), - StackedNode::Deleted => { + StackedNodeState::Deleted => { if let Some(h) = prev_hash { self.0.push((owned_prefix(&prefix), h.clone(), None)); } @@ -1068,11 +1028,11 @@ impl ProcessStack for BatchUpdate> } } - fn exit_root(&mut self, stacked: StackedNode, prev_hash: Option<&TrieHash>) { + fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<&TrieHash>) { let prefix = EMPTY_PREFIX; match stacked { - s@StackedNode::Deleted - | s@StackedNode::Changed(..) => { + s@StackedNodeState::Deleted + | s@StackedNodeState::Changed(..) => { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); self.1 = hash.clone(); From 0ab56d489bae0026bb69b59c9bb896827eab9c72 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 11:49:35 +0200 Subject: [PATCH 077/118] small cleanup --- trie-db/src/traverse.rs | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index c6fddb00..afe084ca 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -34,6 +34,7 @@ use crate::nibble::{BackingByteVec, OwnedPrefix}; use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; use crate::NodeCodec; use crate::rstd::{cmp, mem}; +use crate::rstd::boxed::Box; type StorageHandle = Vec; type OwnedNodeHandle = NodeHandleTrieMut; @@ -101,7 +102,6 @@ struct StackedItem can_fuse: bool, } - /// Variant of stacked item to store first changed node. struct StackedNode where @@ -136,21 +136,6 @@ impl StackedItem B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, { - // function is only call when going up, meaning - // traverse_key depth + 1 is already visited. - fn can_fuse(&self, traverse_key: &[u8]) -> bool { - //if let StackedNodeState::Changedself.node. - unimplemented!() - } - - // TODO remove, here for debugging - // child_ix is only for non delete (delete is always applied before) - // This function checks if this node can fuse in the future, in respect - // to a given child to append or expecting all child to be processed. - fn test_can_fuse(&self, ref_key: &[u8], child_ix: Option) -> bool { - self.can_fuse - } - fn split_child_index(&self) -> Option { match self.split_child.as_ref() { Some(child) => Some(child.parent_index), @@ -691,7 +676,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( current.process_split_child(key.as_ref(), callback); let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); - } else if !parent.test_can_fuse(key.as_ref(), Some(current.item.parent_index)) { + } else if !parent.can_fuse { // process exit, as we already assert two child, no need to store in case of parent // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first @@ -715,7 +700,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // no stacking of first child parent.can_fuse = false; } - if !parent.test_can_fuse(key.as_ref(), Some(current.item.parent_index)) { + if !parent.can_fuse { parent.process_child(current, key.as_ref(), callback); } else { current.process_first_modified_child(key.as_ref(), callback); From 3cc8f60dbb056af97ac9c4ca0da487ecb03f1e9d Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 11:57:29 +0200 Subject: [PATCH 078/118] stack to smallvec --- trie-db/src/traverse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index afe084ca..ce27aeed 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -602,7 +602,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( F: ProcessStack, { // Stack of traversed nodes - let mut stack: Vec> = Vec::with_capacity(32); + let mut stack: smallvec::SmallVec<[StackedItem; 32]> = Default::default(); let root = if let Ok(root) = fetch::(db, root_hash, EMPTY_PREFIX) { root From bb2e81674680799266685d19d9b405e8843f7e84 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 11:59:31 +0200 Subject: [PATCH 079/118] small value --- trie-db/src/traverse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index ce27aeed..5fbe9d53 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -602,7 +602,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( F: ProcessStack, { // Stack of traversed nodes - let mut stack: smallvec::SmallVec<[StackedItem; 32]> = Default::default(); + let mut stack: smallvec::SmallVec<[StackedItem; 16]> = Default::default(); let root = if let Ok(root) = fetch::(db, root_hash, EMPTY_PREFIX) { root From d0e4bc4f696864ccdbd49455a7ec35ba42949863 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 17:04:18 +0200 Subject: [PATCH 080/118] adding owned prefix for deletion --- trie-db/benches/bench.rs | 1 + trie-db/src/nibble/leftnibbleslice.rs | 25 +++++++++- trie-db/src/nibble/nibblevec.rs | 1 - trie-db/src/traverse.rs | 70 ++++++++++++--------------- 4 files changed, 57 insertions(+), 40 deletions(-) diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 2a7e1069..5c0ae98b 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -528,6 +528,7 @@ fn trie_mut_same_key_single(c: &mut Criterion) { } fn trie_mut_same_key_batch(c: &mut Criterion) { + //use memory_db::HashKey; use memory_db::PrefixedKey; use trie_db::TrieMut; let data : Vec<(Vec, Vec)> = input_unsorted(29, 204800, 32); diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index c31301be..b9bfaf2b 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -15,6 +15,7 @@ use crate::rstd::cmp::{self, Ordering}; use crate::nibble::{nibble_ops::{self, NIBBLE_PER_BYTE}, NibbleSlice}; +use hash_db::Prefix; /// A representation of a nibble slice which is left-aligned. The regular `NibbleSlice` is /// right-aligned, meaning it does not support efficient truncation from the right side. @@ -34,6 +35,16 @@ impl<'a> LeftNibbleSlice<'a> { } } + /// Constructs a byte-aligned nibble slice of a given size. + /// TODO EMCH may end up unused + pub fn from(bytes: &'a [u8], len: usize) -> Self { + LeftNibbleSlice { + bytes, + len, + } + } + + /// Returns the length of the slice in nibbles. pub fn len(&self) -> usize { self.len @@ -92,6 +103,18 @@ impl<'a> LeftNibbleSlice<'a> { // If common nibble prefix is the same, finally compare lengths. self.len().cmp(&other.len()) } + + /// Get `Prefix` representation of this `NibbleVec`. + /// TODO EMCH for test, may become unused. + pub fn as_prefix(&self) -> Prefix { + let split = self.len / nibble_ops::NIBBLE_PER_BYTE; + let pos = (self.len % nibble_ops::NIBBLE_PER_BYTE) as u8; + if pos == 0 { + (&self.bytes[..split], None) + } else { + (&self.bytes[..split], Some(nibble_ops::pad_left(self.bytes[split]))) + } + } } impl<'a> PartialEq for LeftNibbleSlice<'a> { @@ -226,4 +249,4 @@ mod tests { Ordering::Equal ); } -} \ No newline at end of file +} diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index a29b6a22..06cc33d3 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -48,7 +48,6 @@ impl NibbleVec { v } - /// Length of the `NibbleVec`. #[inline(always)] pub fn len(&self) -> usize { self.len } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 5fbe9d53..12398efc 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -20,20 +20,18 @@ use crate::triedbmut::{Node, NibbleFullKey}; use crate::triedbmut::NodeHandle as NodeHandleTrieMut; use crate::node::{OwnedNode, NodeHandle, NodeKey}; -use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; +use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice, LeftNibbleSlice}; #[cfg(feature = "std")] use std::borrow::Borrow; #[cfg(not(feature = "std"))] use core::borrow::Borrow; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -#[cfg(not(feature = "std"))] -use alloc::boxed::Box; use crate::{TrieLayout, TrieHash, CError, Result, TrieError}; use crate::nibble::{BackingByteVec, OwnedPrefix}; use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; use crate::NodeCodec; -use crate::rstd::{cmp, mem}; +use crate::rstd::mem; use crate::rstd::boxed::Box; type StorageHandle = Vec; @@ -110,9 +108,9 @@ struct StackedNode { /// Internal node representation. node: StackedNodeState, - /// Hash used to access this node, for inline node and + /// Hash and prefix used to access this node, for inline node (except root) and /// new nodes this is None. - hash: Option>, + hash: Option<(TrieHash, OwnedPrefix)>, /// Index of prefix. depth_prefix: usize, /// Depth of node, it is prefix depth and partial depth. @@ -194,14 +192,14 @@ impl StackedItem (StackedNodeState::Unchanged( fetch::( db, &hash, prefix, - )?), Some(hash)) + )?), Some((hash, owned_prefix(&prefix)))) }, NodeHandle::Inline(node_encoded) => { // Instantiating B is only for inline node, still costy. (StackedNodeState::Unchanged( OwnedNode::new::(B::from(node_encoded)) .map_err(|e| Box::new(TrieError::DecoderError( - self.item.hash.clone().unwrap_or_else(Default::default), + self.item.hash.clone().map(|i| i.0).unwrap_or_else(Default::default), e, )))? ), None) @@ -287,7 +285,7 @@ impl StackedItem if let Some(handle) = callback.exit( prefix, child.node, - child.hash.as_ref(), + child.hash, ) { self.item.node.set_handle(handle, child.parent_index); } @@ -370,7 +368,7 @@ impl StackedItem self.process_split_child(key, callback); callback.exit_root( self.item.node, - self.item.hash.as_ref(), + self.item.hash, ) } @@ -383,7 +381,7 @@ impl StackedItem // delete current callback.exit( NibbleSlice::new_offset(key.as_ref(), self.item.depth_prefix).left(), - to_rem, self.item.hash.as_ref(), + to_rem, self.item.hash.take(), ).expect("no new node on empty"); let partial = NibbleSlice::new_offset(key, self.item.depth_prefix) @@ -570,10 +568,10 @@ trait ProcessStack ) -> Option>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) -> Option>>>; /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<&TrieHash>); + fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>); } /// State when descending @@ -616,7 +614,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let mut current = StackedItem { item: StackedNode { node: current, - hash: Some(*root_hash), + hash: Some((*root_hash, owned_prefix(&EMPTY_PREFIX))), depth_prefix: 0, depth, parent_index: 0, @@ -652,11 +650,12 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { // try first child if let Some(child) = current.take_first_modified_child() { - debug_assert!(child.item.parent_index == fuse_index); + unreachable!(); +/* debug_assert!(child.item.parent_index == fuse_index); let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); prefix.push(fuse_index); child.item.node.partial().map(|p| prefix.append_partial(p.right())); - current.fuse_branch(child, prefix.inner(), callback); + current.fuse_branch(child, prefix.inner(), callback);*/ } else { let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); prefix.push(fuse_index); @@ -740,7 +739,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // try first child if let Some(child) = current.take_first_modified_child() { debug_assert!(child.item.parent_index == fuse_index); - // TODO probably no use in storing child_key here let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); prefix.push(fuse_index); child.item.node.partial().map(|p| prefix.append_partial(p.right())); @@ -777,7 +775,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( continue; } - // PATH DOWN descending in next_query. if let Some((key, value)) = next_query { let dest_slice = NibbleFullKey::new(key.as_ref()); @@ -813,14 +810,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let traverse_state = if let Some(mid_index) = descend_mid_index { TraverseState::MidPartial(mid_index) } else if dest_depth < current.item.depth { - // TODO this might be unreachable from previous loop - // split child (insert in current prefix -> try fuzzing on unreachable - let mid_index = current.item.node.partial() - .map(|current_partial| { - let target_partial = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); - current.item.depth_prefix + current_partial.common_prefix(&target_partial) - }).unwrap_or(current.item.depth_prefix); - TraverseState::MidPartial(mid_index) + unreachable!(); } else if dest_depth > current.item.depth { // over callback TraverseState::AfterNode @@ -918,7 +908,7 @@ impl ProcessStack for BatchUpdate> }; return if stacked.item.node.is_empty() { // replace empty. - new_child.item.hash = stacked.item.hash; + new_child.item.hash = stacked.item.hash.take(); *stacked = new_child; None } else { @@ -980,32 +970,32 @@ impl ProcessStack for BatchUpdate> } - fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<&TrieHash>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) -> Option>>> { match stacked { StackedNodeState::Changed(node) => Some(Some({ + if let Some((h, p)) = prev_hash { + self.0.push((p, h, None)); + } let encoded = node.into_encoded::<_, T::Codec, T::Hash>( |child, _o_slice, _o_index| { child.as_child_ref::() } ); - if encoded.len() < 32 { + if encoded.len() < ::LENGTH { OwnedNodeHandle::InMemory(encoded) } else { let hash = ::hash(&encoded[..]); // register latest change self.2 = Some(self.0.len()); - if let Some(h) = prev_hash { - self.0.push((owned_prefix(&prefix), h.clone(), None)); - } // costy clone (could get read from here) self.0.push((owned_prefix(&prefix), hash.clone(), Some(encoded))); OwnedNodeHandle::Hash(hash) } })), StackedNodeState::Deleted => { - if let Some(h) = prev_hash { - self.0.push((owned_prefix(&prefix), h.clone(), None)); + if let Some((h, p)) = prev_hash { + self.0.push((p, h.clone(), None)); } Some(None) }, @@ -1013,7 +1003,7 @@ impl ProcessStack for BatchUpdate> } } - fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<&TrieHash>) { + fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { let prefix = EMPTY_PREFIX; match stacked { s@StackedNodeState::Deleted @@ -1021,8 +1011,8 @@ impl ProcessStack for BatchUpdate> let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); self.1 = hash.clone(); - if let Some(h) = prev_hash { - self.0.push((owned_prefix(&prefix), h.clone(), None)); + if let Some((h, p)) = prev_hash { + self.0.push((p, h.clone(), None)); } self.0.push((owned_prefix(&prefix), hash, Some(encoded))); }, @@ -1087,6 +1077,7 @@ mod tests { mdb: &mut MemoryDB, DBValue>, ) { for (p, h, v) in delta { + println!("p{:?}, {:?}, {:?}", p, h, v); if let Some(v) = v { let prefix = (p.0.as_ref(), p.1); // damn elastic array in value looks costy @@ -1147,8 +1138,11 @@ mod tests { println!("{:?}", t2); let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); println!("{:?}", t2b); + + println!("{:?}", db.clone().drain()); + println!("{:?}", batch_delta.clone().drain()); + assert!(db == batch_delta); -// panic!("!!END!!"); } #[test] From 011f17985d6ad965c36930f74983a9e1488f981a Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 18:20:32 +0200 Subject: [PATCH 081/118] spotted some error --- memory-db/src/lib.rs | 9 +++++++-- trie-db/src/node.rs | 2 +- trie-db/src/traverse.rs | 9 ++++----- trie-db/src/triedbmut.rs | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 67e67246..738fbbaf 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -137,8 +137,13 @@ impl PartialEq> for MemoryDB T: Eq + MaybeDebug, { fn eq(&self, other: &MemoryDB) -> bool { - for a in self.data.iter() { - match other.data.get(&a.0) { + let (s, other) = if self.data.len() > other.data.len() { + (&self.data, &other.data) + } else { + (&other.data, &self.data) + }; + for a in s.iter() { + match other.get(&a.0) { Some(v) if v != a.1 => return false, None => return false, _ => (), diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index de31e163..6e574b65 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -487,7 +487,7 @@ impl> OwnedNode { None, None, None, None, None, None, None, None, ]); - for i in 0..nibble_ops::NIBBLE_LENGTH { + for i in 0..nibble_ops::NIBBLE_LENGTH { // TODO skip index child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 12398efc..9bf64ba1 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -644,10 +644,10 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let last = next_query.is_none(); // unstack nodes if needed - while last || target_common_depth < current.item.depth_prefix || current.item.node.is_empty() { + while last || target_common_depth < current.item.depth_prefix || current.item.node.is_empty() { // TODO EMCH rename is_empty to is deleted let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO function for that to use // needed also to resolve - if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { + if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { // TODO EMCH rename fix_node to need_fuse // try first child if let Some(child) = current.take_first_modified_child() { unreachable!(); @@ -671,8 +671,8 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // child change or addition if let Some(mut parent) = stack.pop() { if current.item.node.is_empty() { - current.process_first_modified_child(key.as_ref(), callback); - current.process_split_child(key.as_ref(), callback); + current.process_first_modified_child(key.as_ref(), callback); // TODO is it of any use in a deleted node + current.process_split_child(key.as_ref(), callback); // TODO is it of any use?? (assert none instead??) let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); } else if !parent.can_fuse { @@ -1142,7 +1142,6 @@ mod tests { println!("{:?}", db.clone().drain()); println!("{:?}", batch_delta.clone().drain()); assert!(db == batch_delta); - } #[test] diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 3f2ba8ec..65651795 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -271,7 +271,7 @@ impl Node { &mut self, pending: (Option, Option), ) -> (bool, Option) { - let node = mem::replace(self, Node::Empty); + let node = mem::replace(self, Node::Empty); // TODO EMCH rewrite to avoid this mem replace. let (node, fuse) = match node { Node::Extension(..) | Node::Branch(..) => unreachable!("Only for no extension trie"), @@ -296,7 +296,7 @@ impl Node { other_index = Some(pending); } } - for c in encoded_children.iter() { + for c in encoded_children.iter() { // that is damn costy, a bitmap would be better TODO EMCH consider storing a bitmap if c.is_some() { count += 1; } From 598a604f9166ee4a78174bfff0ee8768addc92b5 Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 29 Apr 2020 20:47:54 +0200 Subject: [PATCH 082/118] still a db prefix error, but things starts to make more sense. --- trie-db/src/traverse.rs | 120 +++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 39 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 9bf64ba1..07e4eb45 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -375,19 +375,18 @@ impl StackedItem // consume branch and return item to attach to parent fn fuse_branch< F: ProcessStack, - >(&mut self, child: StackedItem, key: &[u8], callback: &mut F) { + >(&mut self, mut child: StackedItem, key: &[u8], callback: &mut F) { let child_depth = self.item.depth + 1 + child.item.node.partial().map(|p| p.len()).unwrap_or(0); - let to_rem = mem::replace(&mut self.item.node, child.item.node); - // delete current + let _ = mem::replace(&mut self.item.node, child.item.node); + // delete child callback.exit( - NibbleSlice::new_offset(key.as_ref(), self.item.depth_prefix).left(), - to_rem, self.item.hash.take(), + EMPTY_PREFIX, + StackedNodeState::Deleted, child.item.hash.take(), ).expect("no new node on empty"); let partial = NibbleSlice::new_offset(key, self.item.depth_prefix) .to_stored_range(child_depth - self.item.depth_prefix); self.item.node.set_partial(partial); - self.item.hash = child.item.hash; self.item.depth = child_depth; self.can_fuse = true; self.first_modified_child = None; @@ -629,8 +628,59 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( for next_query in elements.into_iter().map(|e| Some(e)).chain(Some(None)) { let mut skip_down = false; + let last = next_query.is_none(); + // PATH UP over the previous key and value if let Some(key) = previous_key.as_ref() { + + // first remove deleted. + if current.item.node.is_empty() { + current.process_first_modified_child(key.as_ref(), callback); // TODO is it of any use in a deleted node + current.process_split_child(key.as_ref(), callback); // TODO is it of any use?? (assert none instead??) + if let Some(mut parent) = stack.pop() { + let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); + parent.append_child(current.into(), prefix.left(), callback); + current = parent; + } else { + // empty trie, next additional value is therefore a leaf + // and delete this one (done in append_child otherwhise) + debug_assert!(current.item.depth_prefix == 0); + if let Some((key, Some(value))) = next_query.as_ref() { + callback.exit( + EMPTY_PREFIX, + current.item.node, + current.item.hash, + ); + let leaf = Node::new_leaf( + NibbleSlice::new(key.as_ref()), + value.as_ref(), + ); + current = StackedItem { + item: StackedNode { + node: StackedNodeState::Changed(leaf), + hash: None, + depth_prefix: 0, + depth: key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index: 0, + }, + first_modified_child: None, + split_child: None, + can_fuse: true, + }; + continue; + } else { + if last { + callback.exit_root( + current.item.node, + current.item.hash, + ); + return Ok(()); + } + continue; + } + } + } + let target_common_depth = next_query.as_ref().map(|(next, _)| nibble_ops::biggest_depth( key.as_ref(), next.as_ref(), @@ -641,41 +691,32 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } else { target_common_depth };*/ - - let last = next_query.is_none(); - // unstack nodes if needed - while last || target_common_depth < current.item.depth_prefix || current.item.node.is_empty() { // TODO EMCH rename is_empty to is deleted - let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO function for that to use - // needed also to resolve - if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { // TODO EMCH rename fix_node to need_fuse - // try first child - if let Some(child) = current.take_first_modified_child() { - unreachable!(); -/* debug_assert!(child.item.parent_index == fuse_index); - let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); - prefix.push(fuse_index); - child.item.node.partial().map(|p| prefix.append_partial(p.right())); - current.fuse_branch(child, prefix.inner(), callback);*/ - } else { - let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); - prefix.push(fuse_index); - let child = current.descend_child(fuse_index, db, prefix.as_prefix())? - .expect("result of first child is define if consistent db"); - child.item.node.partial().map(|p| prefix.append_partial(p.right())); - current.fuse_branch(child, prefix.inner(), callback); - } - // fuse child operation did switch current context. - continue; + let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO function for that to use + // needed also to resolve + if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { // TODO EMCH rename fix_node to need_fuse + // try first child + if let Some(child) = current.take_first_modified_child() { + debug_assert!(child.item.parent_index == fuse_index); + let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); + prefix.push(fuse_index); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); + current.fuse_branch(child, prefix.inner(), callback); + } else { + let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); + prefix.push(fuse_index); + let child = current.descend_child(fuse_index, db, prefix.as_prefix())? + .expect("result of first child is define if consistent db"); // TODO EMCH Error here do!!! + child.item.node.partial().map(|p| prefix.append_partial(p.right())); + current.fuse_branch(child, prefix.inner(), callback); } + } + // unstack nodes if needed + while last || target_common_depth < current.item.depth_prefix { // TODO EMCH rename is_empty to is deleted + // TODO check if fuse (num child is 1). // child change or addition if let Some(mut parent) = stack.pop() { - if current.item.node.is_empty() { - current.process_first_modified_child(key.as_ref(), callback); // TODO is it of any use in a deleted node - current.process_split_child(key.as_ref(), callback); // TODO is it of any use?? (assert none instead??) - let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); - parent.append_child(current.into(), prefix.left(), callback); - } else if !parent.can_fuse { + if !parent.can_fuse { // process exit, as we already assert two child, no need to store in case of parent // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first @@ -702,9 +743,9 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if !parent.can_fuse { parent.process_child(current, key.as_ref(), callback); } else { - current.process_first_modified_child(key.as_ref(), callback); + current.process_first_modified_child(key.as_ref(), callback); // TODO this is super confusing an assert no firt please // split child is after first child (would be processed otherwhise). - current.process_split_child(key.as_ref(), callback); + current.process_split_child(key.as_ref(), callback); // TODO same // first node visited on a fusable element, store in parent first child and process later. // Process an eventual split child (index after current). parent.first_modified_child = Some(current.into()); @@ -1065,6 +1106,7 @@ mod tests { }; use memory_db::{MemoryDB, PrefixedKey}; +// use memory_db::{MemoryDB, HashKey as PrefixedKey}; use keccak_hasher::KeccakHasher; use crate::{DBValue, OwnedPrefix}; use hash_db::HashDB; From 16157c22793bd57e37dbaf1b81f66629564813c5 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Apr 2020 16:46:42 +0200 Subject: [PATCH 083/118] fix remaining tests --- trie-db/src/traverse.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 07e4eb45..919fdc7e 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -328,8 +328,12 @@ impl StackedItem self.process_split_child(key.as_ref(), callback); } } - let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.depth_prefix); - self.append_child(child, nibble_slice.left(), callback); + + let mut build_prefix = NibbleVec::from(key, self.item.depth_prefix); + self.item.node.partial().map(|p| build_prefix.append_partial(p.right())); + build_prefix.push(child.parent_index); + self.append_child(child, build_prefix.as_prefix(), callback); + if always_split { self.process_split_child(key.as_ref(), callback); } From 75220739ccae408f99b49c792c0c7c42215aba0b Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Apr 2020 17:01:06 +0200 Subject: [PATCH 084/118] Add db compare to fuzzer --- trie-db/fuzz/fuzz_targets/batch_update.rs | 2 +- trie-db/fuzz/src/lib.rs | 25 +++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/trie-db/fuzz/fuzz_targets/batch_update.rs b/trie-db/fuzz/fuzz_targets/batch_update.rs index db73eeb8..55acd606 100644 --- a/trie-db/fuzz/fuzz_targets/batch_update.rs +++ b/trie-db/fuzz/fuzz_targets/batch_update.rs @@ -4,5 +4,5 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - trie_db_fuzz::fuzz_batch_update(data, |_v| ()); + trie_db_fuzz::fuzz_batch_update(data, |_v| (), true); }); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index a2239be2..6a75eebf 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -364,7 +364,7 @@ fn test_generate_proof( (root, proof, items) } -pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { +pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec), compare_db: bool) { let data = fuzz_to_data(input); let mut data = fuzz_removal(data); for i in data.iter_mut() { @@ -405,14 +405,27 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec)) { } } //println!("{:?}", sorted_data); - let (calc_root, _payload) = reference_trie::trie_traverse_key_no_extension_build( + let (calc_root, payload) = reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), ); -// println!("{:?}", batch_update.1); -// println!("{:?}", root); assert!(calc_root == root); + + if compare_db { + for (p, h, v) in payload { + use hash_db::HashDB; + if let Some(v) = v { + let prefix = (p.0.as_ref(), p.1); + initial_db.emplace(h, prefix, v[..].into()); + } else { + let prefix = (p.0.as_ref(), p.1); + initial_db.remove(&h, prefix); + } + } + + assert!(initial_db == db); + } } #[test] @@ -440,7 +453,7 @@ fn test() { vec![0x0,0x0,0x4,0x8d,0x8d,0x4], ]; for v in tests.iter() { - fuzz_batch_update(&v[..], |_v| ()); - fuzz_batch_update(&v[..], |v| v.extend(&[4u8; 32])); + fuzz_batch_update(&v[..], |_v| (), false); + fuzz_batch_update(&v[..], |v| v.extend(&[4u8; 32]), true); } } From cc5c02a73f74470f4a25599918c6f57a8e97be03 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Apr 2020 17:32:44 +0200 Subject: [PATCH 085/118] make batch update struct suitable for migration case. --- test-support/reference-trie/src/lib.rs | 3 +- trie-db/fuzz/fuzz_targets/batch_update.rs | 6 +- trie-db/src/nibble/leftnibbleslice.rs | 10 ---- trie-db/src/traverse.rs | 68 ++++++++++++----------- 4 files changed, 41 insertions(+), 46 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 8b788d8f..31f88959 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1202,7 +1202,8 @@ pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( V: AsRef<[u8]>, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, { - batch_update::(db, root, elements).unwrap() + let (root, values) = batch_update::(db, root, elements).unwrap(); + (root, values.into_iter()) } #[cfg(test)] diff --git a/trie-db/fuzz/fuzz_targets/batch_update.rs b/trie-db/fuzz/fuzz_targets/batch_update.rs index 55acd606..185e631c 100644 --- a/trie-db/fuzz/fuzz_targets/batch_update.rs +++ b/trie-db/fuzz/fuzz_targets/batch_update.rs @@ -3,6 +3,8 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - // fuzzed code goes here - trie_db_fuzz::fuzz_batch_update(data, |_v| (), true); + // use next line to favor complex structure and inline data + // trie_db_fuzz::fuzz_batch_update(data, |_v| (), false); + // use next line to favor db prefix verification + trie_db_fuzz::fuzz_batch_update(data, |v| v.extend(&[4u8; 32]), true); }); diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index b9bfaf2b..bacdbbc7 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -35,16 +35,6 @@ impl<'a> LeftNibbleSlice<'a> { } } - /// Constructs a byte-aligned nibble slice of a given size. - /// TODO EMCH may end up unused - pub fn from(bytes: &'a [u8], len: usize) -> Self { - LeftNibbleSlice { - bytes, - len, - } - } - - /// Returns the length of the slice in nibbles. pub fn len(&self) -> usize { self.len diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 919fdc7e..5f78cbcd 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -20,7 +20,7 @@ use crate::triedbmut::{Node, NibbleFullKey}; use crate::triedbmut::NodeHandle as NodeHandleTrieMut; use crate::node::{OwnedNode, NodeHandle, NodeKey}; -use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice, LeftNibbleSlice}; +use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; #[cfg(feature = "std")] use std::borrow::Borrow; #[cfg(not(feature = "std"))] @@ -897,16 +897,16 @@ fn fetch>( /// Contains ordered node change for this iteration. /// The resulting root hash. /// The latest changed node. -struct BatchUpdate( - Vec<(OwnedPrefix, H, Option>)>, - H, - Option, // TODO EMCH remove?? -); +struct BatchUpdate { + register_update: C, + root: H, +} -impl ProcessStack for BatchUpdate> +impl ProcessStack for BatchUpdate, C> where B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, + C: FnMut((OwnedPrefix, TrieHash, Option>)), { fn enter_terminal( &mut self, @@ -1014,13 +1014,13 @@ impl ProcessStack for BatchUpdate> } - fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) -> Option>>> { + let register = &mut self.register_update; match stacked { StackedNodeState::Changed(node) => Some(Some({ if let Some((h, p)) = prev_hash { - self.0.push((p, h, None)); + register((p, h, None)); } let encoded = node.into_encoded::<_, T::Codec, T::Hash>( |child, _o_slice, _o_index| { @@ -1031,16 +1031,14 @@ impl ProcessStack for BatchUpdate> OwnedNodeHandle::InMemory(encoded) } else { let hash = ::hash(&encoded[..]); - // register latest change - self.2 = Some(self.0.len()); // costy clone (could get read from here) - self.0.push((owned_prefix(&prefix), hash.clone(), Some(encoded))); + register((owned_prefix(&prefix), hash.clone(), Some(encoded))); OwnedNodeHandle::Hash(hash) } })), StackedNodeState::Deleted => { if let Some((h, p)) = prev_hash { - self.0.push((p, h.clone(), None)); + register((p, h.clone(), None)); } Some(None) }, @@ -1050,16 +1048,17 @@ impl ProcessStack for BatchUpdate> fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { let prefix = EMPTY_PREFIX; + let register = &mut self.register_update; match stacked { s@StackedNodeState::Deleted | s@StackedNodeState::Changed(..) => { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); - self.1 = hash.clone(); + self.root = hash.clone(); if let Some((h, p)) = prev_hash { - self.0.push((p, h.clone(), None)); + register((p, h.clone(), None)); } - self.0.push((owned_prefix(&prefix), hash, Some(encoded))); + register((owned_prefix(&prefix), hash, Some(encoded))); }, _ => (), } @@ -1077,15 +1076,11 @@ pub fn from_owned_prefix(prefix: &OwnedPrefix) -> Prefix { } /// Update trie, returning deltas and root. -/// TODO this put all in memory in a vec: we could stream the process -/// (would be really good for very big updates). -> then remove root -/// from result and Batch update (is simply latest hash of iter (given -/// delete after insert)). pub fn batch_update<'a, T, I, K, V, B>( db: &'a dyn HashDBRef, root_hash: &'a TrieHash, elements: I, -) -> Result<(TrieHash, impl Iterator, Option>)>), TrieHash, CError> +) -> Result<(TrieHash, Vec<(OwnedPrefix, TrieHash, Option>)>), TrieHash, CError> where T: TrieLayout, I: IntoIterator)>, @@ -1093,14 +1088,15 @@ pub fn batch_update<'a, T, I, K, V, B>( V: AsRef<[u8]>, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, { - let mut batch_update = BatchUpdate( - Default::default(), - root_hash.clone(), - None, - ); + let mut dest = Vec::new(); + let mut batch_update = BatchUpdate { + register_update: |update| { + dest.push(update) + }, + root: root_hash.clone(), + }; trie_traverse_key::(db, root_hash, elements, &mut batch_update)?; - // TODO when remove third elt of batchupdate the map gets useless - Ok((batch_update.1, batch_update.0.into_iter().map(|i| (i.0, i.1, i.2)))) + Ok((batch_update.root, dest)) } #[cfg(test)] @@ -1199,6 +1195,7 @@ mod tests { ], ); } + #[test] fn non_empty_node_null_key() { compare_with_triedbmut( @@ -1210,6 +1207,7 @@ mod tests { ], ); } + #[test] fn empty_node_with_key() { compare_with_triedbmut( @@ -1219,6 +1217,7 @@ mod tests { ], ); } + #[test] fn simple_fuse() { compare_with_triedbmut( @@ -1232,6 +1231,7 @@ mod tests { ], ); } + #[test] fn dummy1() { compare_with_triedbmut( @@ -1244,6 +1244,7 @@ mod tests { ], ); } + #[test] fn two_recursive_mid_insert() { compare_with_triedbmut( @@ -1256,6 +1257,7 @@ mod tests { ], ); } + #[test] fn dummy2() { compare_with_triedbmut( @@ -1268,10 +1270,10 @@ mod tests { (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8; 32])), (vec![0x01u8, 0x81u8, 0x23], Some(vec![0xfeu8; 32])), (vec![0x01u8, 0x81u8, 0x23], None), -// (vec![0x01u8, 0xf1u8, 0x23], Some(vec![0xffu8, 0x34])), ], ); } + #[test] fn delete_to_empty() { compare_with_triedbmut( @@ -1283,19 +1285,20 @@ mod tests { ], ); } + #[test] fn fuse_root_node() { compare_with_triedbmut( &[ (vec![2, 254u8], vec![4u8; 33]), (vec![1, 254u8], vec![4u8; 33]), - // (vec![1, 255u8], vec![5u8; 36]), ], &[ (vec![1, 254u8], None), ], ); } + #[test] fn dummy4() { compare_with_triedbmut( @@ -1327,6 +1330,7 @@ mod tests { ], ); } + #[test] fn fuse_with_child_partial() { compare_with_triedbmut( @@ -1387,7 +1391,6 @@ mod tests { ); } - #[test] fn dummy_51() { compare_with_triedbmut( @@ -1400,6 +1403,7 @@ mod tests { ], ); } + #[test] fn emptied_then_insert() { compare_with_triedbmut( @@ -1438,13 +1442,11 @@ mod tests { &[ (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], None), (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 208], Some(vec![4; 32])), -// (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 151, 144, 144, 144, 144, 144, 144, 144], Some(vec![4, 251])), (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], None), ], ); } - #[test] fn single_latest_change_value_does_not_work() { compare_with_triedbmut( From 5fa5f001a5dfb0d487f95d7c138b6d2e05ab2096 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Apr 2020 18:26:32 +0200 Subject: [PATCH 086/118] switch from option to enum --- test-support/reference-trie/src/lib.rs | 7 +- trie-db/src/nibble/leftnibbleslice.rs | 12 -- trie-db/src/traverse.rs | 275 ++++++++++++++----------- 3 files changed, 161 insertions(+), 133 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 31f88959..6e58a9ad 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -33,7 +33,7 @@ use trie_db::{ }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -use trie_db::traverse::batch_update; +use trie_db::traverse::{batch_update, InputAction}; pub use trie_db::{ decode_compact, encode_compact, @@ -1202,6 +1202,11 @@ pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( V: AsRef<[u8]>, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, { + let elements = elements.into_iter().map(|(k, v)| (k, if let Some(v) = v { + InputAction::Insert(v) + } else { + InputAction::Delete + })); let (root, values) = batch_update::(db, root, elements).unwrap(); (root, values.into_iter()) } diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index bacdbbc7..44963a22 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -93,18 +93,6 @@ impl<'a> LeftNibbleSlice<'a> { // If common nibble prefix is the same, finally compare lengths. self.len().cmp(&other.len()) } - - /// Get `Prefix` representation of this `NibbleVec`. - /// TODO EMCH for test, may become unused. - pub fn as_prefix(&self) -> Prefix { - let split = self.len / nibble_ops::NIBBLE_PER_BYTE; - let pos = (self.len % nibble_ops::NIBBLE_PER_BYTE) as u8; - if pos == 0 { - (&self.bytes[..split], None) - } else { - (&self.bytes[..split], Some(nibble_ops::pad_left(self.bytes[split]))) - } - } } impl<'a> PartialEq for LeftNibbleSlice<'a> { diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 5f78cbcd..5af1805e 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -566,7 +566,7 @@ trait ProcessStack &mut self, stacked: &mut StackedItem, key_element: &[u8], - value_element: Option<&[u8]>, + action: InputAction<&[u8]>, state: TraverseState, ) -> Option>; @@ -587,6 +587,26 @@ enum TraverseState { MidPartial(usize), } +/// Action for a key to traverse. +pub enum InputAction { + /// Delete a value if it exists. + Delete, + /// Insert a value. If value is already define, + /// it will be overwrite. + Insert(V), +} + +impl> InputAction { + + /// Alternative to `std::convert::AsRef`. + pub fn as_ref(&self) -> InputAction<&[u8]> { + match self { + InputAction::Insert(v) => InputAction::Insert(v.as_ref()), + InputAction::Delete => InputAction::Delete, + } + } +} + /// The main entry point for traversing a trie by a set of keys. fn trie_traverse_key<'a, T, I, K, V, B, F>( db: &'a dyn HashDBRef, @@ -596,7 +616,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( ) -> Result<(), TrieHash, CError> where T: TrieLayout, - I: IntoIterator)>, + I: Iterator)>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, @@ -649,38 +669,41 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // empty trie, next additional value is therefore a leaf // and delete this one (done in append_child otherwhise) debug_assert!(current.item.depth_prefix == 0); - if let Some((key, Some(value))) = next_query.as_ref() { - callback.exit( - EMPTY_PREFIX, - current.item.node, - current.item.hash, - ); - let leaf = Node::new_leaf( - NibbleSlice::new(key.as_ref()), - value.as_ref(), - ); - current = StackedItem { - item: StackedNode { - node: StackedNodeState::Changed(leaf), - hash: None, - depth_prefix: 0, - depth: key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index: 0, - }, - first_modified_child: None, - split_child: None, - can_fuse: true, - }; - continue; - } else { - if last { + match next_query.as_ref() { + Some((key, InputAction::Insert(value))) => { + callback.exit( + EMPTY_PREFIX, + current.item.node, + current.item.hash, + ); + let leaf = Node::new_leaf( + NibbleSlice::new(key.as_ref()), + value.as_ref(), + ); + current = StackedItem { + item: StackedNode { + node: StackedNodeState::Changed(leaf), + hash: None, + depth_prefix: 0, + depth: key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index: 0, + }, + first_modified_child: None, + split_child: None, + can_fuse: true, + }; + continue; + }, + Some((_key, InputAction::Delete)) => { + continue; + }, + None => { callback.exit_root( current.item.node, current.item.hash, ); return Ok(()); - } - continue; + }, } } } @@ -764,14 +787,18 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( current.process_root(key.as_ref(), callback); return Ok(()); } else { - if let Some((key, Some(value))) = next_query.as_ref() { - let child = Node::new_leaf( - NibbleSlice::new_offset(key.as_ref(), 0), - value.as_ref(), - ); - current.item.node = StackedNodeState::Changed(child); - current.item.depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - current.can_fuse = false; + match next_query.as_ref() { + Some((key, InputAction::Insert(value))) => { + let child = Node::new_leaf( + NibbleSlice::new_offset(key.as_ref(), 0), + value.as_ref(), + ); + current.item.node = StackedNodeState::Changed(child); + current.item.depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + current.can_fuse = false; + }, + Some((_key, InputAction::Delete)) => unreachable!(), + None => (), } // move to next key skip_down = true; @@ -866,7 +893,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if let Some(new_child) = callback.enter_terminal( &mut current, key.as_ref(), - value.as_ref().map(|v| v.as_ref()), + value.as_ref(), traverse_state, ) { stack.push(current); @@ -912,102 +939,110 @@ impl ProcessStack for BatchUpdate, C> &mut self, stacked: &mut StackedItem, key_element: &[u8], - value_element: Option<&[u8]>, + action: InputAction<&[u8]>, state: TraverseState, ) -> Option> { match state { TraverseState::ValueMatch => { - if let Some(value) = value_element { - stacked.item.node.set_value(value); - } else { - stacked.item.node.remove_value(); + match action { + InputAction::Insert(value) => { + stacked.item.node.set_value(value); + }, + InputAction::Delete => { + stacked.item.node.remove_value(); + }, } None }, TraverseState::AfterNode => { - - if let Some(val) = value_element { - // corner case of empty trie. - let offset = if stacked.item.node.is_empty() { - 0 - } else { - 1 - }; - // dest is a leaf appended to terminal - let dest_leaf = Node::new_leaf( - NibbleSlice::new_offset(key_element, stacked.item.depth + offset), - val, - ); - let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); - let mut new_child = StackedItem { - item: StackedNode { - node: StackedNodeState::Changed(dest_leaf), - hash: None, - depth_prefix: stacked.item.depth + offset, - depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index, - }, - split_child: None, - first_modified_child: None, - can_fuse: false, - }; - return if stacked.item.node.is_empty() { - // replace empty. - new_child.item.hash = stacked.item.hash.take(); - *stacked = new_child; - None - } else { - // append to parent is done on exit through changed nature of the new leaf. - Some(new_child) - }; - } else { - // nothing to delete. - return None; - } - }, - TraverseState::MidPartial(mid_index) => { - if let Some(value) = value_element { - if stacked.item.node.is_empty() { - unreachable!(); - } else { - stacked.do_split_child(mid_index, key_element, self); - let (offset, parent_index) = if key_element.len() == 0 { - // corner case of adding at top of trie - (0, 0) + match action { + InputAction::Insert(val) => { + // corner case of empty trie. + let offset = if stacked.item.node.is_empty() { + 0 } else { - // TODO not sure on index - (1, NibbleSlice::new(key_element).at(mid_index)) + 1 }; - let child = Node::new_leaf( - // TODO not sure on '1 +' - NibbleSlice::new_offset(key_element, offset + mid_index), - value.as_ref(), + // dest is a leaf appended to terminal + let dest_leaf = Node::new_leaf( + NibbleSlice::new_offset(key_element, stacked.item.depth + offset), + val, ); - return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { - - // set value in new branch - stacked.item.node.set_value(value); - stacked.can_fuse = false; + let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); + let mut new_child = StackedItem { + item: StackedNode { + node: StackedNodeState::Changed(dest_leaf), + hash: None, + depth_prefix: stacked.item.depth + offset, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + }, + split_child: None, + first_modified_child: None, + can_fuse: false, + }; + return if stacked.item.node.is_empty() { + // replace empty. + new_child.item.hash = stacked.item.hash.take(); + *stacked = new_child; None } else { - let child = StackedItem { - item: StackedNode { - node: StackedNodeState::Changed(child), - hash: None, - depth_prefix: offset + mid_index, - depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index, - }, - split_child: None, - first_modified_child: None, - can_fuse: false, + // append to parent is done on exit through changed nature of the new leaf. + Some(new_child) + } + }, + InputAction::Delete => { + // nothing to delete. + None + }, + } + }, + TraverseState::MidPartial(mid_index) => { + match action { + InputAction::Insert(value) => { + if stacked.item.node.is_empty() { + unreachable!(); + } else { + stacked.do_split_child(mid_index, key_element, self); + let (offset, parent_index) = if key_element.len() == 0 { + // corner case of adding at top of trie + (0, 0) + } else { + // TODO not sure on index + (1, NibbleSlice::new(key_element).at(mid_index)) }; - Some(child) + let child = Node::new_leaf( + // TODO not sure on '1 +' + NibbleSlice::new_offset(key_element, offset + mid_index), + value.as_ref(), + ); + return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { + + // set value in new branch + stacked.item.node.set_value(value); + stacked.can_fuse = false; + None + } else { + let child = StackedItem { + item: StackedNode { + node: StackedNodeState::Changed(child), + hash: None, + depth_prefix: offset + mid_index, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + }, + split_child: None, + first_modified_child: None, + can_fuse: false, + }; + Some(child) + } } - } - } else { - // nothing to delete. - return None; + }, + InputAction::Delete => { + // nothing to delete. + None + }, } }, } @@ -1083,7 +1118,7 @@ pub fn batch_update<'a, T, I, K, V, B>( ) -> Result<(TrieHash, Vec<(OwnedPrefix, TrieHash, Option>)>), TrieHash, CError> where T: TrieLayout, - I: IntoIterator)>, + I: Iterator)>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, From cbc46857894b1cde04200753426ddfd714f21ea7 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Apr 2020 19:43:43 +0200 Subject: [PATCH 087/118] remove unreachable branch --- trie-db/src/nibble/leftnibbleslice.rs | 1 - trie-db/src/traverse.rs | 47 +++++++++++++++------------ 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index 44963a22..e7243efc 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -15,7 +15,6 @@ use crate::rstd::cmp::{self, Ordering}; use crate::nibble::{nibble_ops::{self, NIBBLE_PER_BYTE}, NibbleSlice}; -use hash_db::Prefix; /// A representation of a nibble slice which is left-aligned. The regular `NibbleSlice` is /// right-aligned, meaning it does not support efficient truncation from the right side. diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 5af1805e..a0f333e7 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -566,7 +566,7 @@ trait ProcessStack &mut self, stacked: &mut StackedItem, key_element: &[u8], - action: InputAction<&[u8]>, + action: InputAction<&[u8], &TrieHash>, state: TraverseState, ) -> Option>; @@ -588,21 +588,31 @@ enum TraverseState { } /// Action for a key to traverse. -pub enum InputAction { +pub enum InputAction { /// Delete a value if it exists. Delete, /// Insert a value. If value is already define, /// it will be overwrite. Insert(V), + /// Detach trie content at a given. + /// Handle detached nodes is managed by process stack logic. + Detach, + /// Attach a trie with given hash. + /// Handle of conflict is managed by process stack logic. + /// More common strategie would be to replace content and handle + /// the replaced content as a detached content is handled. + Attach(H), } -impl> InputAction { +impl, H> InputAction { /// Alternative to `std::convert::AsRef`. - pub fn as_ref(&self) -> InputAction<&[u8]> { + pub fn as_ref(&self) -> InputAction<&[u8], &H> { match self { InputAction::Insert(v) => InputAction::Insert(v.as_ref()), InputAction::Delete => InputAction::Delete, + InputAction::Attach(attach_root) => InputAction::Attach(&attach_root), + InputAction::Detach => InputAction::Detach, } } } @@ -616,7 +626,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( ) -> Result<(), TrieHash, CError> where T: TrieLayout, - I: Iterator)>, + I: Iterator>)>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, @@ -694,7 +704,9 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( }; continue; }, - Some((_key, InputAction::Delete)) => { + Some((_key, InputAction::Attach(attach_root))) => unimplemented!("TOOD ATTACH/DETACH"), // this is a replace by this root node, just need to rework the partial of the node. + Some((_key, InputAction::Detach)) + | Some((_key, InputAction::Delete)) => { continue; }, None => { @@ -787,19 +799,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( current.process_root(key.as_ref(), callback); return Ok(()); } else { - match next_query.as_ref() { - Some((key, InputAction::Insert(value))) => { - let child = Node::new_leaf( - NibbleSlice::new_offset(key.as_ref(), 0), - value.as_ref(), - ); - current.item.node = StackedNodeState::Changed(child); - current.item.depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; - current.can_fuse = false; - }, - Some((_key, InputAction::Delete)) => unreachable!(), - None => (), - } // move to next key skip_down = true; break; @@ -939,7 +938,7 @@ impl ProcessStack for BatchUpdate, C> &mut self, stacked: &mut StackedItem, key_element: &[u8], - action: InputAction<&[u8]>, + action: InputAction<&[u8], &TrieHash>, state: TraverseState, ) -> Option> { match state { @@ -951,6 +950,8 @@ impl ProcessStack for BatchUpdate, C> InputAction::Delete => { stacked.item.node.remove_value(); }, + InputAction::Attach(attach_root) => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), } None }, @@ -995,6 +996,8 @@ impl ProcessStack for BatchUpdate, C> // nothing to delete. None }, + InputAction::Attach(attach_root) => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), } }, TraverseState::MidPartial(mid_index) => { @@ -1043,6 +1046,8 @@ impl ProcessStack for BatchUpdate, C> // nothing to delete. None }, + InputAction::Attach(attach_root) => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), } }, } @@ -1118,7 +1123,7 @@ pub fn batch_update<'a, T, I, K, V, B>( ) -> Result<(TrieHash, Vec<(OwnedPrefix, TrieHash, Option>)>), TrieHash, CError> where T: TrieLayout, - I: Iterator)>, + I: Iterator>)>, K: AsRef<[u8]> + Ord, V: AsRef<[u8]>, B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, From 262e197143611d49c050ddd388df15bc405e0784 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 30 Apr 2020 20:51:39 +0200 Subject: [PATCH 088/118] produced node order test, there is some mismatch, probably need some additional check when doing first and fuse to order them. --- trie-db/src/traverse.rs | 70 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index a0f333e7..ae90bc6a 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -704,7 +704,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( }; continue; }, - Some((_key, InputAction::Attach(attach_root))) => unimplemented!("TOOD ATTACH/DETACH"), // this is a replace by this root node, just need to rework the partial of the node. + Some((_key, InputAction::Attach(_attach_root))) => unimplemented!("TOOD ATTACH/DETACH"), // this is a replace by this root node, just need to rework the partial of the node. Some((_key, InputAction::Detach)) | Some((_key, InputAction::Delete)) => { continue; @@ -950,7 +950,7 @@ impl ProcessStack for BatchUpdate, C> InputAction::Delete => { stacked.item.node.remove_value(); }, - InputAction::Attach(attach_root) => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Attach(_attach_root) => unimplemented!("TOOD ATTACH/DETACH"), InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), } None @@ -996,7 +996,7 @@ impl ProcessStack for BatchUpdate, C> // nothing to delete. None }, - InputAction::Attach(attach_root) => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Attach(_attach_root) => unimplemented!("TOOD ATTACH/DETACH"), InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), } }, @@ -1046,7 +1046,7 @@ impl ProcessStack for BatchUpdate, C> // nothing to delete. None }, - InputAction::Attach(attach_root) => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Attach(_attach_root) => unimplemented!("TOOD ATTACH/DETACH"), InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), } }, @@ -1151,6 +1151,7 @@ mod tests { use crate::{DBValue, OwnedPrefix}; use hash_db::HashDB; use crate::triedbmut::tests::populate_trie_no_extension; + use crate::nibble::NibbleSlice; type H256 = ::Out; @@ -1158,8 +1159,66 @@ mod tests { delta: impl Iterator>)>, mdb: &mut MemoryDB, DBValue>, ) { + let mut previous_prefix = None; + let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix, prev_delete: bool, is_delet: bool| { + println!("{:?} -> {:?}", previous, next); + if previous == next { + // we can have two same value if it is deletion then creation + assert!(prev_delete && !is_delet); + return; + } + let prev_slice = NibbleSlice::new(previous.0.as_slice()); + let p_at = |i| { + if i < prev_slice.len() { + Some(prev_slice.at(i)) + } else if i == prev_slice.len() { + previous.1 + } else { + None + } + }; + + let next_slice = NibbleSlice::new(next.0.as_slice()); + let n_at = |i| { + if i < next_slice.len() { + Some(next_slice.at(i)) + } else if i == next_slice.len() { + next.1 + } else { + None + } + }; + let mut i = 0; + loop { + match (p_at(i), n_at(i)) { + (Some(p), Some(n)) => { + if p < n { + break; + } else if p == n { + i += 1; + } else { + panic!("Misordered results"); + } + }, + (Some(p), None) => { + // moving upward is fine + break; + }, + (None, Some(p)) => { + // next is bellow first, that is not correct + panic!("Misordered results"); + }, + (None, None) => { + unreachable!("equality tested firsthand") + }, + } + } + }; for (p, h, v) in delta { - println!("p{:?}, {:?}, {:?}", p, h, v); + let is_delete = v.is_none(); + previous_prefix.as_ref().map(|(prev, p_is_del)| cp_prefix(prev, &p, *p_is_del, is_delete)); + + //println!("p{:?}, {:?}, {:?}", p, h, v); if let Some(v) = v { let prefix = (p.0.as_ref(), p.1); // damn elastic array in value looks costy @@ -1168,6 +1227,7 @@ mod tests { let prefix = (p.0.as_ref(), p.1); mdb.remove(&h, prefix); } + previous_prefix = Some((p, is_delete)); } } From 626da6264bc506a5f6541362a73a87b27be9ed19 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 1 May 2020 09:41:54 +0200 Subject: [PATCH 089/118] conclusion that ordering of delete will need additional buffer, and that would not be worth or to be added in ProcessStack specific implementation. --- trie-db/src/traverse.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index ae90bc6a..a06781ba 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1159,14 +1159,22 @@ mod tests { delta: impl Iterator>)>, mdb: &mut MemoryDB, DBValue>, ) { - let mut previous_prefix = None; - let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix, prev_delete: bool, is_delet: bool| { + // Ordering logic is almost always correct between delet and create (delete first in case of + // same location), there is one exception: deleting a node resulting to fusing a parent, then + // the parent fusing can write at a prior index. + // Therefore a `ProcessStack` that need ordering for delete will need a buffer. + // Such buffer will be at maximum the size of the stack depth minus one or the number of child + // in a branch (but since it is triggered by consecutive node fuse it should really be small). + // Then we limit this test to insert here. + let mut previous_prefix_insert = None; + //let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix, prev_delete: bool, is_delet: bool| { + let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix| { println!("{:?} -> {:?}", previous, next); - if previous == next { +/* if previous == next { // we can have two same value if it is deletion then creation assert!(prev_delete && !is_delet); return; - } + }*/ let prev_slice = NibbleSlice::new(previous.0.as_slice()); let p_at = |i| { if i < prev_slice.len() { @@ -1209,14 +1217,17 @@ mod tests { panic!("Misordered results"); }, (None, None) => { - unreachable!("equality tested firsthand") + panic!("Two consecutive action at same node") + //unreachable!("equality tested firsthand") }, } } }; for (p, h, v) in delta { let is_delete = v.is_none(); - previous_prefix.as_ref().map(|(prev, p_is_del)| cp_prefix(prev, &p, *p_is_del, is_delete)); + if !is_delete { + previous_prefix_insert.as_ref().map(|prev| cp_prefix(prev, &p)); + } //println!("p{:?}, {:?}, {:?}", p, h, v); if let Some(v) = v { @@ -1227,7 +1238,9 @@ mod tests { let prefix = (p.0.as_ref(), p.1); mdb.remove(&h, prefix); } - previous_prefix = Some((p, is_delete)); + if !is_delete { + previous_prefix_insert = Some(p); + } } } @@ -1360,6 +1373,7 @@ mod tests { #[test] fn dummy2() { + // TODO CHEME what happen if set same value as existing!!! -> could skip alloc compare_with_triedbmut( &[ (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8; 32]), From 57e231e25850f44892212b35fbe0241562ef9b99 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 1 May 2020 12:50:02 +0200 Subject: [PATCH 090/118] change interface --- trie-db/src/traverse.rs | 98 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 11 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index a06781ba..3e964ac0 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -567,12 +567,15 @@ trait ProcessStack stacked: &mut StackedItem, key_element: &[u8], action: InputAction<&[u8], &TrieHash>, + fetched_node: Option>, state: TraverseState, ) -> Option>; /// Callback on exit a node, commit action on change node should be applied here. fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) -> Option>>>; + /// Callback on a detached node. + fn exit_detached(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>); /// Same as `exit` but for root (very last exit call). fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>); } @@ -607,12 +610,13 @@ pub enum InputAction { impl, H> InputAction { /// Alternative to `std::convert::AsRef`. - pub fn as_ref(&self) -> InputAction<&[u8], &H> { + /// Retun optionally a reference to a hash to an node to fetch for this action. + pub fn as_ref(&self) -> (InputAction<&[u8], &H>, Option<&H>) { match self { - InputAction::Insert(v) => InputAction::Insert(v.as_ref()), - InputAction::Delete => InputAction::Delete, - InputAction::Attach(attach_root) => InputAction::Attach(&attach_root), - InputAction::Detach => InputAction::Detach, + InputAction::Insert(v) => (InputAction::Insert(v.as_ref()), None), + InputAction::Delete => (InputAction::Delete, None), + InputAction::Attach(attach_root) => (InputAction::Attach(&attach_root), Some(&attach_root)), + InputAction::Detach => (InputAction::Detach, None), } } } @@ -704,9 +708,28 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( }; continue; }, - Some((_key, InputAction::Attach(_attach_root))) => unimplemented!("TOOD ATTACH/DETACH"), // this is a replace by this root node, just need to rework the partial of the node. + Some((_key, InputAction::Attach(attach_root))) => { + // TODO nooop on attach empty + let root_node = fetch::(db, &attach_root, EMPTY_PREFIX)?; + let depth = root_node.partial().map(|p| p.len()).unwrap_or(0); + current = StackedItem { + item: StackedNode { + node: StackedNodeState::Unchanged(root_node), + hash: None, + depth_prefix: 0, + depth, + parent_index: 0, + }, + first_modified_child: None, + split_child: None, + can_fuse: true, + }; + continue; + }, Some((_key, InputAction::Detach)) | Some((_key, InputAction::Delete)) => { + // TODO EMCH could also detach a empty trie root (way to match a serie of detach with + // their keys) continue; }, None => { @@ -889,10 +912,18 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // value replace callback TraverseState::ValueMatch }; + let (value, do_fetch) = value.as_ref(); + let fetch = if let Some(hash) = do_fetch { + let hash = fetch::(db, &hash, EMPTY_PREFIX)?; + Some(hash) + } else { + None + }; if let Some(new_child) = callback.enter_terminal( &mut current, key.as_ref(), - value.as_ref(), + value, + fetch, traverse_state, ) { stack.push(current); @@ -923,22 +954,25 @@ fn fetch>( /// Contains ordered node change for this iteration. /// The resulting root hash. /// The latest changed node. -struct BatchUpdate { +struct BatchUpdate { register_update: C, + register_detached: D, root: H, } -impl ProcessStack for BatchUpdate, C> +impl ProcessStack for BatchUpdate, C, D> where B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, C: FnMut((OwnedPrefix, TrieHash, Option>)), + D: FnMut((OwnedPrefix, TrieHash)), { fn enter_terminal( &mut self, stacked: &mut StackedItem, key_element: &[u8], action: InputAction<&[u8], &TrieHash>, + fetched_node: Option>, state: TraverseState, ) -> Option> { match state { @@ -950,8 +984,26 @@ impl ProcessStack for BatchUpdate, C> InputAction::Delete => { stacked.item.node.remove_value(); }, - InputAction::Attach(_attach_root) => unimplemented!("TOOD ATTACH/DETACH"), - InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Attach(attach_root) => { + /* + // replace existing node, attached node is aligned + // so unchanged, but we write it as Changed to process parent + // hash change (not that in this case it cost us a unnecessary node delete + // and insert). + let attached_node = StackedNodeState::Changed( + fetch::(db, &attach_root, EMPTY_PREFIX) + )?; + let detached = mem::replace(&mut stacked.item.node, attached_node); + // TODO remove prefix from detached and run exit_detached on it. + // : todo defined (probably will manage prefix resizet their and all. + register_detached(detached); + //), Some((hash, owned_prefix(&prefix)))) +*/ + unimplemented!("TOOD ATTACH/DETACH") + }, + InputAction::Detach => { +unimplemented!("TOOD ATTACH/DETACH") + }, } None }, @@ -1103,6 +1155,27 @@ impl ProcessStack for BatchUpdate, C> _ => (), } } + + fn exit_detached(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { + let register_del = &mut self.register_update; + let register = &mut self.register_detached; + match stacked { + s@StackedNodeState::Deleted + | s@StackedNodeState::Changed(..) => { + // same as root: also hash inline nodes. + if let Some((h, p)) = prev_hash { + register_del((p, h.clone(), None)); + } + let encoded = s.into_encoded(); + let hash = ::hash(&encoded[..]); + register((owned_prefix(&prefix), hash)); + }, + StackedNodeState::Unchanged(node) => { + let hash = ::hash(node.data()); + register((owned_prefix(&prefix), hash)); + }, + } + } } @@ -1133,6 +1206,9 @@ pub fn batch_update<'a, T, I, K, V, B>( register_update: |update| { dest.push(update) }, + register_detached: |update| { + unimplemented!("TODO") + }, root: root_hash.clone(), }; trie_traverse_key::(db, root_hash, elements, &mut batch_update)?; From a95ad2a3cce4b89bf5417c07338855d0fdb1dc5f Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 1 May 2020 17:47:14 +0200 Subject: [PATCH 091/118] initial implementation, will need fuzzing and tests. --- test-support/reference-trie/src/lib.rs | 10 +- trie-db/benches/bench.rs | 2 +- trie-db/fuzz/src/lib.rs | 2 +- trie-db/src/node.rs | 2 +- trie-db/src/traverse.rs | 190 ++++++++++++++++++++----- trie-db/src/triedbmut.rs | 2 +- 6 files changed, 167 insertions(+), 41 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 6e58a9ad..cbe75f71 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1195,7 +1195,11 @@ pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( db: &'a mut dyn hash_db::HashDBRef, root: &'a ::Out, elements: I, -) -> (::Out, impl Iterator::Out, Option>)>) +) -> ( + ::Out, + impl Iterator::Out, Option>)>, + impl Iterator, OwnedPrefix, ::Out)>, +) where I: IntoIterator)>, K: AsRef<[u8]> + Ord, @@ -1207,8 +1211,8 @@ pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( } else { InputAction::Delete })); - let (root, values) = batch_update::(db, root, elements).unwrap(); - (root, values.into_iter()) + let (root, values, detached_root) = batch_update::(db, root, elements).unwrap(); + (root, values.into_iter(), detached_root.into_iter()) } #[cfg(test)] diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index 5c0ae98b..7b1b5e59 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -549,7 +549,7 @@ fn trie_mut_same_key_batch(c: &mut Criterion) { let mut mdb = db.clone(); // sort let data: std::collections::BTreeSet> = data.iter().map(|(a, _b)| a.clone()).collect(); - let (calc_root, _payload) = reference_trie::trie_traverse_key_no_extension_build( + let (calc_root, _payload, _detached) = reference_trie::trie_traverse_key_no_extension_build( &mut mdb, &root, data.iter().map(|a| (a, Some(&a[..]))) ); assert!(calc_root != root); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 6a75eebf..fea3eb4a 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -405,7 +405,7 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec), compare_db: } } //println!("{:?}", sorted_data); - let (calc_root, payload) = reference_trie::trie_traverse_key_no_extension_build( + let (calc_root, payload, _detached) = reference_trie::trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, sorted_data.into_iter(), diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 6e574b65..bfd1ac72 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -191,7 +191,7 @@ impl NodePlan { /// An `OwnedNode` is an owned type from which a `Node` can be constructed which borrows data from /// the `OwnedNode`. This is useful for trie iterators. #[cfg_attr(feature = "std", derive(Debug))] -#[derive(PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct OwnedNode> { data: D, plan: NodePlan, diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 3e964ac0..e9e200f0 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -16,6 +16,11 @@ //! //! The traversal stack is updatable, and is therefore usable for //! batch update of ordered key values. +//! +//! Note that this part of trie crate currently do not support extension +//! node. Regarding how the code is designed, implementing extension should +//! be done by using a tuple of extension and branch node as a branch (storing +//! an additional hash in branch and only adapting fetch and write methods). use crate::triedbmut::{Node, NibbleFullKey}; use crate::triedbmut::NodeHandle as NodeHandleTrieMut; @@ -575,7 +580,7 @@ trait ProcessStack fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) -> Option>>>; /// Callback on a detached node. - fn exit_detached(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>); + fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>); /// Same as `exit` but for root (very last exit call). fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>); } @@ -965,14 +970,14 @@ impl ProcessStack for BatchUpdate, C, D> B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, C: FnMut((OwnedPrefix, TrieHash, Option>)), - D: FnMut((OwnedPrefix, TrieHash)), + D: FnMut((Vec, OwnedPrefix, TrieHash)), { fn enter_terminal( &mut self, stacked: &mut StackedItem, key_element: &[u8], action: InputAction<&[u8], &TrieHash>, - fetched_node: Option>, + fetched_node: Option>, // TODO EMCH try with unowned prefix or exit_detached with owned variant state: TraverseState, ) -> Option> { match state { @@ -985,24 +990,41 @@ impl ProcessStack for BatchUpdate, C, D> stacked.item.node.remove_value(); }, InputAction::Attach(attach_root) => { - /* - // replace existing node, attached node is aligned - // so unchanged, but we write it as Changed to process parent - // hash change (not that in this case it cost us a unnecessary node delete - // and insert). - let attached_node = StackedNodeState::Changed( - fetch::(db, &attach_root, EMPTY_PREFIX) - )?; - let detached = mem::replace(&mut stacked.item.node, attached_node); - // TODO remove prefix from detached and run exit_detached on it. - // : todo defined (probably will manage prefix resizet their and all. - register_detached(detached); - //), Some((hash, owned_prefix(&prefix)))) -*/ - unimplemented!("TOOD ATTACH/DETACH") + let prefix_nibble = NibbleSlice::new_offset(&key_element[..], stacked.item.depth_prefix); + let prefix = prefix_nibble.left(); + let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. + let mut to_attach = StackedNodeState::Unchanged(to_attach); + //if stacked.item.node.partial().map(|p| p.len()).unwrap_or(0) != 0 { + if stacked.item.depth_prefix != stacked.item.depth { + match to_attach.partial() { + Some(partial) if partial.len() > 0 => { + let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix).into(); + crate::triedbmut::combine_key(&mut build_partial, partial.right_ref()); + to_attach.set_partial(build_partial); + }, + _ => { + if let Some(partial) = stacked.item.node.partial() { + to_attach.set_partial(partial.into()); + } + }, + } + stacked.item.node.advance_partial(stacked.item.depth - stacked.item.depth_prefix); + } + let detached = mem::replace(&mut stacked.item.node, to_attach); + let detached_hash = mem::replace(&mut stacked.item.hash, Some((attach_root.clone(), owned_prefix(&EMPTY_PREFIX)))); + stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); + self.exit_detached(key_element, prefix, detached, detached_hash); }, InputAction::Detach => { -unimplemented!("TOOD ATTACH/DETACH") + let prefix_nibble = NibbleSlice::new_offset(&key_element[..], stacked.item.depth_prefix); + let prefix = prefix_nibble.left(); + let to_attach = StackedNodeState::Deleted; + if stacked.item.depth_prefix != stacked.item.depth { + stacked.item.node.advance_partial(stacked.item.depth - stacked.item.depth_prefix); + } + let detached = mem::replace(&mut stacked.item.node, to_attach); + let detached_hash = mem::replace(&mut stacked.item.hash, None); + self.exit_detached(key_element, prefix, detached, detached_hash); }, } None @@ -1048,8 +1070,58 @@ unimplemented!("TOOD ATTACH/DETACH") // nothing to delete. None }, - InputAction::Attach(_attach_root) => unimplemented!("TOOD ATTACH/DETACH"), - InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Attach(attach_root) => { + // TODO factor with insert + let offset = if stacked.item.node.is_empty() { + 0 + } else { + 1 + }; + let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); + let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH mack action ref another type to ensure resolution. + let mut to_attach = StackedNodeState::Unchanged(to_attach); + let depth_prefix = stacked.item.depth + offset; + match to_attach.partial() { + Some(partial) if partial.len() > 0 => { + let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, depth_prefix).into(); + crate::triedbmut::combine_key(&mut build_partial, partial.right_ref()); + to_attach.set_partial(build_partial); + }, + _ => { + let partial: NodeKey = NibbleSlice::new_offset(key_element, depth_prefix).into(); + to_attach.set_partial(partial.into()); + }, + }; + + let depth = depth_prefix + to_attach.partial().map(|p| p.len()).unwrap_or(0); + let prefix_nibble = NibbleSlice::new_offset(&key_element[..], depth_prefix); + let prefix = prefix_nibble.left(); + let mut new_child = StackedItem { + item: StackedNode { + node: to_attach, + hash: Some((attach_root.clone(), owned_prefix(&prefix))), + depth_prefix, + depth, + parent_index, + }, + split_child: None, + first_modified_child: None, + can_fuse: false, + }; + return if stacked.item.node.is_empty() { + // replace empty. + new_child.item.hash = stacked.item.hash.take(); + *stacked = new_child; + None + } else { + // append to parent is done on exit through changed nature of the new leaf. + Some(new_child) + } + }, + InputAction::Detach => { + // nothing to detach + None + }, } }, TraverseState::MidPartial(mid_index) => { @@ -1098,8 +1170,44 @@ unimplemented!("TOOD ATTACH/DETACH") // nothing to delete. None }, - InputAction::Attach(_attach_root) => unimplemented!("TOOD ATTACH/DETACH"), - InputAction::Detach => unimplemented!("TOOD ATTACH/DETACH"), + InputAction::Attach(attach_root) => { + let prefix_nibble = NibbleSlice::new(&key_element[..]); + let prefix = prefix_nibble.left(); + let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. + let mut to_attach = StackedNodeState::Unchanged(to_attach); + match to_attach.partial() { + Some(partial) if partial.len() > 0 => { + let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix + mid_index).into(); + crate::triedbmut::combine_key(&mut build_partial, partial.right_ref()); + to_attach.set_partial(build_partial); + }, + _ => { + let build_partial = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix + mid_index); + if build_partial.len() > 0 { + to_attach.set_partial(build_partial.into()); + } + }, + } + if mid_index > 0 { + stacked.item.node.advance_partial(mid_index); + } + let mut detached = mem::replace(&mut stacked.item.node, to_attach); + detached.advance_partial(mid_index); + let detached_hash = mem::replace(&mut stacked.item.hash, Some((attach_root.clone(), owned_prefix(&EMPTY_PREFIX)))); + stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); + self.exit_detached(key_element, prefix, detached, detached_hash); + None + }, + InputAction::Detach => { + let prefix_nibble = NibbleSlice::new(&key_element[..]); + let prefix = prefix_nibble.left(); + let to_attach = StackedNodeState::Deleted; + let mut detached = mem::replace(&mut stacked.item.node, to_attach); + detached.advance_partial(mid_index); + let detached_hash = mem::replace(&mut stacked.item.hash, None); + self.exit_detached(key_element, prefix, detached, detached_hash); + None + }, } }, } @@ -1156,23 +1264,32 @@ unimplemented!("TOOD ATTACH/DETACH") } } - fn exit_detached(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { - let register_del = &mut self.register_update; + fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { + let register_up = &mut self.register_update; let register = &mut self.register_detached; match stacked { s@StackedNodeState::Deleted | s@StackedNodeState::Changed(..) => { // same as root: also hash inline nodes. if let Some((h, p)) = prev_hash { - register_del((p, h.clone(), None)); + register_up((p, h.clone(), None)); } let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); - register((owned_prefix(&prefix), hash)); + // Note that those updates are messing the updates ordering, could be better to register + // them with the detached item (TODO would need a dedicated struct). + register_up((owned_prefix(&EMPTY_PREFIX), hash.clone(), Some(encoded))); + register((key_element.to_vec(), owned_prefix(&prefix), hash)); }, - StackedNodeState::Unchanged(node) => { - let hash = ::hash(node.data()); - register((owned_prefix(&prefix), hash)); + s@StackedNodeState::Unchanged(..) => { + let hash = if let Some((not_inline, previous_prefix)) = prev_hash { + debug_assert!(prefix == from_owned_prefix(&previous_prefix)); + not_inline + } else { + let encoded = s.into_encoded(); + ::hash(&encoded[..]) + }; + register((key_element.to_vec(), owned_prefix(&prefix), hash)); }, } } @@ -1193,7 +1310,11 @@ pub fn batch_update<'a, T, I, K, V, B>( db: &'a dyn HashDBRef, root_hash: &'a TrieHash, elements: I, -) -> Result<(TrieHash, Vec<(OwnedPrefix, TrieHash, Option>)>), TrieHash, CError> +) -> Result<( + TrieHash, + Vec<(OwnedPrefix, TrieHash, Option>)>, + Vec<(Vec, OwnedPrefix, TrieHash)>, +),TrieHash, CError> where T: TrieLayout, I: Iterator>)>, @@ -1202,17 +1323,18 @@ pub fn batch_update<'a, T, I, K, V, B>( B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, { let mut dest = Vec::new(); + let mut dest_detached = Vec::new(); let mut batch_update = BatchUpdate { register_update: |update| { dest.push(update) }, register_detached: |update| { - unimplemented!("TODO") + dest_detached.push(update) }, root: root_hash.clone(), }; trie_traverse_key::(db, root_hash, elements, &mut batch_update)?; - Ok((batch_update.root, dest)) + Ok((batch_update.root, dest, dest_detached)) } #[cfg(test)] @@ -1354,7 +1476,7 @@ mod tests { } - let (calc_root, payload) = trie_traverse_key_no_extension_build( + let (calc_root, payload, _detached) = trie_traverse_key_no_extension_build( &mut initial_db, &initial_root, v.iter().map(|(a, b)| (a, b.as_ref())), diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 65651795..76ba75ab 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1926,7 +1926,7 @@ where } /// combine two NodeKeys -fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { +pub(crate) fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { debug_assert!(start.0 < nibble_ops::NIBBLE_PER_BYTE); debug_assert!(end.0 < nibble_ops::NIBBLE_PER_BYTE); let final_offset = (start.0 + end.0) % nibble_ops::NIBBLE_PER_BYTE; From 93f118e5ea73bcd99e3934730889265b91f40563 Mon Sep 17 00:00:00 2001 From: cheme Date: Sat, 2 May 2020 13:13:16 +0200 Subject: [PATCH 092/118] added some test, next is fixing this --- test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/traverse.rs | 121 ++++++++++++++++++++++--- 2 files changed, 108 insertions(+), 15 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index cbe75f71..11353fdc 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -33,7 +33,7 @@ use trie_db::{ }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -use trie_db::traverse::{batch_update, InputAction}; +pub use trie_db::traverse::{batch_update, InputAction}; pub use trie_db::{ decode_compact, encode_compact, diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index e9e200f0..621347e1 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -22,6 +22,8 @@ //! be done by using a tuple of extension and branch node as a branch (storing //! an additional hash in branch and only adapting fetch and write methods). +// TODO CHEME what happen if set same value as existing!!! -> could skip alloc + use crate::triedbmut::{Node, NibbleFullKey}; use crate::triedbmut::NodeHandle as NodeHandleTrieMut; use crate::node::{OwnedNode, NodeHandle, NodeKey}; @@ -1339,8 +1341,8 @@ pub fn batch_update<'a, T, I, K, V, B>( #[cfg(test)] mod tests { - use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, TrieMut, - trie_traverse_key_no_extension_build, + use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, TrieMut, InputAction, + trie_traverse_key_no_extension_build, NoExtensionLayout, batch_update, }; use memory_db::{MemoryDB, PrefixedKey}; @@ -1497,6 +1499,75 @@ mod tests { assert!(db == batch_delta); } + fn compare_with_triedbmut_detach( + x: &[(Vec, Vec)], + d: &Vec, + ) { + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + populate_trie_no_extension(&mut db, &mut root, x).commit(); + { + let t = RefTrieDBNoExt::new(&db, &root); + println!("bef {:?}", t); + } + let initial_root = root.clone(); + let mut initial_db = db.clone(); + // reference + { + let mut t = RefTrieDBMutNoExt::from_existing(&mut db, &mut root).unwrap(); + for i in 0..x.len() { + if x[i].0.starts_with(d) { + let key: &[u8]= &x[i].0; + t.remove(key).unwrap(); + } + } + } + { + let t = RefTrieDBNoExt::new(&db, &root); + println!("aft {:?}", t); + } + let elements = Some(d.clone()).into_iter().map(|k| (k, InputAction::, _>::Detach)); + let (calc_root, payload, detached_root) = batch_update::( + &initial_db, + &initial_root, + elements, + ).unwrap(); + + assert_eq!(calc_root, root); + + let mut batch_delta = initial_db.clone(); + memory_db_from_delta(payload.into_iter(), &mut batch_delta); + // test by checking both triedb only + let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t2); + let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); + println!("{:?}", t2b); + + println!("{:?}", db.clone().drain()); + println!("{:?}", batch_delta.clone().drain()); + assert!(db == batch_delta); + + // attach back + let elements = detached_root.into_iter().map(|(k, _prefix, root)| (k, InputAction::, _>::Attach(root))); + let (calc_root, payload, detached_root) = batch_update::( + &db, + &root, + elements, + ).unwrap(); + assert!(detached_root.is_empty()); + assert!(calc_root == initial_root); + memory_db_from_delta(payload.into_iter(), &mut batch_delta); + // test by checking both triedb only + let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t2); + let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); + println!("{:?}", t2b); + + println!("{:?}", db.clone().drain()); + println!("{:?}", batch_delta.clone().drain()); + assert!(initial_db == batch_delta); + } + #[test] fn empty_node_null_key() { compare_with_triedbmut( @@ -1505,18 +1576,21 @@ mod tests { (vec![], Some(vec![0xffu8, 0x33])), ], ); + compare_with_triedbmut_detach(&[], &vec![]); } #[test] fn non_empty_node_null_key() { + let db = &[ + (vec![0x0u8], vec![4, 32]), + ]; compare_with_triedbmut( - &[ - (vec![0x0u8], vec![4, 32]), - ], + db, &[ (vec![], Some(vec![0xffu8, 0x33])), ], ); + compare_with_triedbmut_detach(db, &vec![]); } #[test] @@ -1545,15 +1619,19 @@ mod tests { #[test] fn dummy1() { + let db = &[ + (vec![0x04u8], vec![4, 32]), + ]; compare_with_triedbmut( - &[ - (vec![0x04u8], vec![4, 32]), - ], + db, &[ (vec![0x06u8], Some(vec![0xffu8, 0x33])), (vec![0x08u8], Some(vec![0xffu8, 0x33])), ], ); + compare_with_triedbmut_detach(db, &vec![0x04u8]); + compare_with_triedbmut_detach(db, &vec![0x04u8, 0x01]); + compare_with_triedbmut_detach(db, &vec![]); } #[test] @@ -1571,19 +1649,34 @@ mod tests { #[test] fn dummy2() { - // TODO CHEME what happen if set same value as existing!!! -> could skip alloc + let db = &[ + (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8; 32]), + (vec![0x01u8, 0x81u8, 0x23], vec![0x02u8; 32]), + (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), + ]; compare_with_triedbmut( - &[ - (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8; 32]), - (vec![0x01u8, 0x81u8, 0x23], vec![0x02u8; 32]), - (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), - ], + db, &[ (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8; 32])), (vec![0x01u8, 0x81u8, 0x23], Some(vec![0xfeu8; 32])), (vec![0x01u8, 0x81u8, 0x23], None), ], ); + compare_with_triedbmut_detach(db, &vec![]); + compare_with_triedbmut_detach(db, &vec![0x02]); + compare_with_triedbmut_detach(db, &vec![0x01u8]); + compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81]); + compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81, 0x23]); + } + #[test] + fn dettach_middle() { + let db = &[ + (vec![0x00u8, 0x01, 0x23], vec![0x01u8; 32]), + (vec![0x00, 0x01, 0x81u8, 0x23], vec![0x02u8; 32]), + (vec![0x00, 0x01, 0xf1u8, 0x23], vec![0x01u8, 0x24]), + ]; + compare_with_triedbmut_detach(db, &vec![0x00u8]); + compare_with_triedbmut_detach(db, &vec![0x00u8, 0x01, 0x81]); } #[test] From 3698e2ea970d8c81e7d51b259a6bbd89fc2103dc Mon Sep 17 00:00:00 2001 From: cheme Date: Sun, 3 May 2020 13:41:37 +0200 Subject: [PATCH 093/118] attached special case --- trie-db/src/traverse.rs | 216 +++++++++++++++++++++++----------------- 1 file changed, 127 insertions(+), 89 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 621347e1..fe3a29fb 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -53,6 +53,9 @@ enum StackedNodeState { /// Read node. Unchanged(OwnedNode), + /// Read node, attached, we need to update + /// parent hash or root. TODO EMCH add the old root!!(as vec maybe) + UnchangedAttached(OwnedNode), /// Modified node. Changed(Node, StorageHandle>), /// Deleted node. @@ -414,6 +417,7 @@ impl StackedNodeState fn is_empty(&self) -> bool { match self { StackedNodeState::Unchanged(node) => node.is_empty(), + StackedNodeState::UnchangedAttached(node) => node.is_empty(), StackedNodeState::Changed(node) => node.is_empty(), StackedNodeState::Deleted => true, } @@ -423,6 +427,7 @@ impl StackedNodeState fn partial(&self) -> Option { match self { StackedNodeState::Unchanged(node) => node.partial(), + StackedNodeState::UnchangedAttached(node) => node.partial(), StackedNodeState::Changed(node) => node.partial(), StackedNodeState::Deleted => None, } @@ -432,6 +437,7 @@ impl StackedNodeState fn child(&self, ix: u8) -> Option { match self { StackedNodeState::Unchanged(node) => node.child(ix), + StackedNodeState::UnchangedAttached(node) => node.child(ix), StackedNodeState::Changed(node) => node.child(ix), StackedNodeState::Deleted => None, } @@ -441,6 +447,7 @@ impl StackedNodeState fn has_value(&self) -> bool { match self { StackedNodeState::Unchanged(node) => node.has_value(), + StackedNodeState::UnchangedAttached(node) => node.has_value(), StackedNodeState::Changed(node) => node.has_value(), StackedNodeState::Deleted => false, } @@ -449,7 +456,8 @@ impl StackedNodeState /// Set a value if the node can contain one. fn set_value(&mut self, value: &[u8]) { match self { - StackedNodeState::Unchanged(node) => { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { if let Some(new) = node.set_value(value) { *self = StackedNodeState::Changed(new); } @@ -462,7 +470,8 @@ impl StackedNodeState /// Change a partial if the node contains one. fn advance_partial(&mut self, nb: usize) { match self { - StackedNodeState::Unchanged(node) => { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { if let Some(new) = node.advance_partial(nb) { *self = StackedNodeState::Changed(new); } @@ -475,7 +484,8 @@ impl StackedNodeState /// Set a new partial. fn set_partial(&mut self, partial: NodeKey) { match self { - StackedNodeState::Unchanged(node) => { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { if let Some(new) = node.set_partial(partial) { *self = StackedNodeState::Changed(new); } @@ -488,7 +498,8 @@ impl StackedNodeState /// Remove a value if the node contains one. fn remove_value(&mut self) { match self { - StackedNodeState::Unchanged(node) => { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { match node.remove_value() { Some(Some(new)) => *self = StackedNodeState::Changed(new), @@ -509,7 +520,8 @@ impl StackedNodeState /// Set a handle to a child node or remove it if handle is none. fn set_handle(&mut self, handle: Option>>, index: u8) { match self { - StackedNodeState::Unchanged(node) => { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { let change = node.set_handle(handle, index); match change { Some(new) => *self = StackedNodeState::Changed(new), @@ -527,6 +539,7 @@ impl StackedNodeState fn fix_node(&mut self, pending: (Option, Option)) -> Option { match self { StackedNodeState::Deleted + | StackedNodeState::UnchangedAttached(..) | StackedNodeState::Unchanged(..) => None, StackedNodeState::Changed(node) => { let (deleted, fuse) = node.fix_node(pending); @@ -537,18 +550,12 @@ impl StackedNodeState }, } } -} - -impl StackedNodeState - where - B: Borrow<[u8]> + AsRef<[u8]>, - T: TrieLayout, -{ /// Encode node fn into_encoded(self) -> Vec { match self { - StackedNodeState::Unchanged(node) => node.data().to_vec(), + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => node.data().to_vec(), StackedNodeState::Changed(node) => node.into_encoded::<_, T::Codec, T::Hash>( |child, _o_slice, _o_index| { child.as_child_ref::() @@ -921,6 +928,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( }; let (value, do_fetch) = value.as_ref(); let fetch = if let Some(hash) = do_fetch { + // This is fetching node to attach, it is a root and then uses EMPTY_PREFIX let hash = fetch::(db, &hash, EMPTY_PREFIX)?; Some(hash) } else { @@ -995,7 +1003,7 @@ impl ProcessStack for BatchUpdate, C, D> let prefix_nibble = NibbleSlice::new_offset(&key_element[..], stacked.item.depth_prefix); let prefix = prefix_nibble.left(); let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. - let mut to_attach = StackedNodeState::Unchanged(to_attach); + let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); //if stacked.item.node.partial().map(|p| p.len()).unwrap_or(0) != 0 { if stacked.item.depth_prefix != stacked.item.depth { match to_attach.partial() { @@ -1081,7 +1089,7 @@ impl ProcessStack for BatchUpdate, C, D> }; let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH mack action ref another type to ensure resolution. - let mut to_attach = StackedNodeState::Unchanged(to_attach); + let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); let depth_prefix = stacked.item.depth + offset; match to_attach.partial() { Some(partial) if partial.len() > 0 => { @@ -1176,7 +1184,7 @@ impl ProcessStack for BatchUpdate, C, D> let prefix_nibble = NibbleSlice::new(&key_element[..]); let prefix = prefix_nibble.left(); let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. - let mut to_attach = StackedNodeState::Unchanged(to_attach); + let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); match to_attach.partial() { Some(partial) if partial.len() > 0 => { let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix + mid_index).into(); @@ -1201,6 +1209,9 @@ impl ProcessStack for BatchUpdate, C, D> None }, InputAction::Detach => { + if stacked.item.node.is_empty() { + unreachable!("we should not iterate in middle of an empty; this needs fix"); + } let prefix_nibble = NibbleSlice::new(&key_element[..]); let prefix = prefix_nibble.left(); let to_attach = StackedNodeState::Deleted; @@ -1244,6 +1255,14 @@ impl ProcessStack for BatchUpdate, C, D> } Some(None) }, + StackedNodeState::UnchangedAttached(node) => Some(Some({ + let encoded = node.data().to_vec(); + if encoded.len() < ::LENGTH { + OwnedNodeHandle::InMemory(encoded) + } else { + OwnedNodeHandle::Hash(::hash(&encoded[..])) + } + })), _ => None, } } @@ -1262,11 +1281,17 @@ impl ProcessStack for BatchUpdate, C, D> } register((owned_prefix(&prefix), hash, Some(encoded))); }, + StackedNodeState::UnchangedAttached(node) => { + let encoded = node.data().to_vec(); + let hash = ::hash(&encoded[..]); + self.root = hash.clone(); + }, _ => (), } } fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { + let is_empty_node = stacked.is_empty(); let register_up = &mut self.register_update; let register = &mut self.register_detached; match stacked { @@ -1276,22 +1301,27 @@ impl ProcessStack for BatchUpdate, C, D> if let Some((h, p)) = prev_hash { register_up((p, h.clone(), None)); } - let encoded = s.into_encoded(); - let hash = ::hash(&encoded[..]); - // Note that those updates are messing the updates ordering, could be better to register - // them with the detached item (TODO would need a dedicated struct). - register_up((owned_prefix(&EMPTY_PREFIX), hash.clone(), Some(encoded))); - register((key_element.to_vec(), owned_prefix(&prefix), hash)); - }, - s@StackedNodeState::Unchanged(..) => { - let hash = if let Some((not_inline, previous_prefix)) = prev_hash { - debug_assert!(prefix == from_owned_prefix(&previous_prefix)); - not_inline - } else { + if !is_empty_node { let encoded = s.into_encoded(); - ::hash(&encoded[..]) - }; - register((key_element.to_vec(), owned_prefix(&prefix), hash)); + let hash = ::hash(&encoded[..]); + // Note that those updates are messing the updates ordering, could be better to register + // them with the detached item (TODO would need a dedicated struct). + register_up((owned_prefix(&EMPTY_PREFIX), hash.clone(), Some(encoded))); + register((key_element.to_vec(), owned_prefix(&prefix), hash)); + } + }, + s@StackedNodeState::UnchangedAttached(..) + | s@StackedNodeState::Unchanged(..) => { + if !is_empty_node { + let hash = if let Some((not_inline, previous_prefix)) = prev_hash { + debug_assert!(prefix == from_owned_prefix(&previous_prefix)); + not_inline + } else { + let encoded = s.into_encoded(); + ::hash(&encoded[..]) + }; + register((key_element.to_vec(), owned_prefix(&prefix), hash)); + } }, } } @@ -1358,6 +1388,7 @@ mod tests { fn memory_db_from_delta( delta: impl Iterator>)>, mdb: &mut MemoryDB, DBValue>, + check_order: bool, ) { // Ordering logic is almost always correct between delet and create (delete first in case of // same location), there is one exception: deleting a node resulting to fusing a parent, then @@ -1369,57 +1400,59 @@ mod tests { let mut previous_prefix_insert = None; //let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix, prev_delete: bool, is_delet: bool| { let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix| { - println!("{:?} -> {:?}", previous, next); -/* if previous == next { - // we can have two same value if it is deletion then creation - assert!(prev_delete && !is_delet); - return; - }*/ - let prev_slice = NibbleSlice::new(previous.0.as_slice()); - let p_at = |i| { - if i < prev_slice.len() { - Some(prev_slice.at(i)) - } else if i == prev_slice.len() { - previous.1 - } else { - None - } - }; + if check_order { + println!("{:?} -> {:?}", previous, next); + /* if previous == next { + // we can have two same value if it is deletion then creation + assert!(prev_delete && !is_delet); + return; + }*/ + let prev_slice = NibbleSlice::new(previous.0.as_slice()); + let p_at = |i| { + if i < prev_slice.len() { + Some(prev_slice.at(i)) + } else if i == prev_slice.len() { + previous.1 + } else { + None + } + }; - let next_slice = NibbleSlice::new(next.0.as_slice()); - let n_at = |i| { - if i < next_slice.len() { - Some(next_slice.at(i)) - } else if i == next_slice.len() { - next.1 - } else { - None - } - }; - let mut i = 0; - loop { - match (p_at(i), n_at(i)) { - (Some(p), Some(n)) => { - if p < n { + let next_slice = NibbleSlice::new(next.0.as_slice()); + let n_at = |i| { + if i < next_slice.len() { + Some(next_slice.at(i)) + } else if i == next_slice.len() { + next.1 + } else { + None + } + }; + let mut i = 0; + loop { + match (p_at(i), n_at(i)) { + (Some(p), Some(n)) => { + if p < n { + break; + } else if p == n { + i += 1; + } else { + panic!("Misordered results"); + } + }, + (Some(p), None) => { + // moving upward is fine break; - } else if p == n { - i += 1; - } else { + }, + (None, Some(p)) => { + // next is bellow first, that is not correct panic!("Misordered results"); - } - }, - (Some(p), None) => { - // moving upward is fine - break; - }, - (None, Some(p)) => { - // next is bellow first, that is not correct - panic!("Misordered results"); - }, - (None, None) => { - panic!("Two consecutive action at same node") - //unreachable!("equality tested firsthand") - }, + }, + (None, None) => { + panic!("Two consecutive action at same node") + //unreachable!("equality tested firsthand") + }, + } } } }; @@ -1487,7 +1520,7 @@ mod tests { assert_eq!(calc_root, root); let mut batch_delta = initial_db; - memory_db_from_delta(payload, &mut batch_delta); + memory_db_from_delta(payload, &mut batch_delta, true); // test by checking both triedb only let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); println!("{:?}", t2); @@ -1536,7 +1569,7 @@ mod tests { assert_eq!(calc_root, root); let mut batch_delta = initial_db.clone(); - memory_db_from_delta(payload.into_iter(), &mut batch_delta); + memory_db_from_delta(payload.into_iter(), &mut batch_delta, false); // test by checking both triedb only let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); println!("{:?}", t2); @@ -1545,25 +1578,27 @@ mod tests { println!("{:?}", db.clone().drain()); println!("{:?}", batch_delta.clone().drain()); - assert!(db == batch_delta); + // TODO this cannot be test because we have attached content + // in db TODO split batches of update (then we could compare ordering again). + //assert!(db == batch_delta); // attach back let elements = detached_root.into_iter().map(|(k, _prefix, root)| (k, InputAction::, _>::Attach(root))); let (calc_root, payload, detached_root) = batch_update::( - &db, - &root, + &batch_delta, + &calc_root, elements, ).unwrap(); - assert!(detached_root.is_empty()); - assert!(calc_root == initial_root); - memory_db_from_delta(payload.into_iter(), &mut batch_delta); + //assert!(detached_root.is_empty()); + memory_db_from_delta(payload.into_iter(), &mut batch_delta, false); // test by checking both triedb only - let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); + let t2 = RefTrieDBNoExt::new(&initial_db, &initial_root).unwrap(); println!("{:?}", t2); let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); println!("{:?}", t2b); + assert!(calc_root == initial_root); - println!("{:?}", db.clone().drain()); + println!("{:?}", initial_db.clone().drain()); println!("{:?}", batch_delta.clone().drain()); assert!(initial_db == batch_delta); } @@ -1591,6 +1626,9 @@ mod tests { ], ); compare_with_triedbmut_detach(db, &vec![]); + compare_with_triedbmut_detach(&[ + (vec![0x01u8, 0x23], vec![4, 32]), + ], &vec![0x01u8]); } #[test] From d652a1c5a329072fa448f194ffc5a1fc4ca52041 Mon Sep 17 00:00:00 2001 From: cheme Date: Sun, 3 May 2020 14:45:21 +0200 Subject: [PATCH 094/118] fix detach middle condition, all detach/attach code was using mid_index as relative to node but it is absolute to root: next fix that). --- trie-db/src/traverse.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index fe3a29fb..58d0f35d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1120,7 +1120,8 @@ impl ProcessStack for BatchUpdate, C, D> }; return if stacked.item.node.is_empty() { // replace empty. - new_child.item.hash = stacked.item.hash.take(); + let detached_hash = mem::replace(&mut new_child.item.hash, stacked.item.hash.take()); + self.exit_detached(key_element, EMPTY_PREFIX, StackedNodeState::::Deleted, detached_hash); *stacked = new_child; None } else { @@ -1209,16 +1210,19 @@ impl ProcessStack for BatchUpdate, C, D> None }, InputAction::Detach => { - if stacked.item.node.is_empty() { - unreachable!("we should not iterate in middle of an empty; this needs fix"); + if mid_index == NibbleSlice::new(key_element).len() { + // on a path do a switch + if stacked.item.node.is_empty() { + unreachable!("we should not iterate in middle of an empty; this needs fix"); + } + let prefix_nibble = NibbleSlice::new(&key_element[..]); + let prefix = prefix_nibble.left(); + let to_attach = StackedNodeState::Deleted; + let mut detached = mem::replace(&mut stacked.item.node, to_attach); + detached.advance_partial(mid_index); + let detached_hash = mem::replace(&mut stacked.item.hash, None); + self.exit_detached(key_element, prefix, detached, detached_hash); } - let prefix_nibble = NibbleSlice::new(&key_element[..]); - let prefix = prefix_nibble.left(); - let to_attach = StackedNodeState::Deleted; - let mut detached = mem::replace(&mut stacked.item.node, to_attach); - detached.advance_partial(mid_index); - let detached_hash = mem::replace(&mut stacked.item.hash, None); - self.exit_detached(key_element, prefix, detached, detached_hash); None }, } @@ -1600,6 +1604,7 @@ mod tests { println!("{:?}", initial_db.clone().drain()); println!("{:?}", batch_delta.clone().drain()); + batch_delta.purge(); assert!(initial_db == batch_delta); } @@ -1701,7 +1706,7 @@ mod tests { ], ); compare_with_triedbmut_detach(db, &vec![]); - compare_with_triedbmut_detach(db, &vec![0x02]); + compare_with_triedbmut_detach(db, &vec![0x02]); // TODO !!! compare_with_triedbmut_detach(db, &vec![0x01u8]); compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81]); compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81, 0x23]); From f0179c2c4f4256635c29cc48f654a74da72c7a57 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 4 May 2020 10:14:53 +0200 Subject: [PATCH 095/118] fix tests --- trie-db/src/traverse.rs | 209 ++++++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 104 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 58d0f35d..c7720461 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1104,12 +1104,13 @@ impl ProcessStack for BatchUpdate, C, D> }; let depth = depth_prefix + to_attach.partial().map(|p| p.len()).unwrap_or(0); - let prefix_nibble = NibbleSlice::new_offset(&key_element[..], depth_prefix); - let prefix = prefix_nibble.left(); + //let prefix_nibble = NibbleSlice::new_offset(&key_element[..], depth_prefix); + //let prefix = prefix_nibble.left(); let mut new_child = StackedItem { item: StackedNode { node: to_attach, - hash: Some((attach_root.clone(), owned_prefix(&prefix))), + // Attach root is unprefixed + hash: Some((attach_root.clone(), owned_prefix(&EMPTY_PREFIX))), depth_prefix, depth, parent_index, @@ -1200,10 +1201,10 @@ impl ProcessStack for BatchUpdate, C, D> }, } if mid_index > 0 { - stacked.item.node.advance_partial(mid_index); + stacked.item.node.advance_partial(mid_index - stacked.item.depth_prefix); } let mut detached = mem::replace(&mut stacked.item.node, to_attach); - detached.advance_partial(mid_index); + //detached.advance_partial(mid_index); let detached_hash = mem::replace(&mut stacked.item.hash, Some((attach_root.clone(), owned_prefix(&EMPTY_PREFIX)))); stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); self.exit_detached(key_element, prefix, detached, detached_hash); @@ -1219,7 +1220,7 @@ impl ProcessStack for BatchUpdate, C, D> let prefix = prefix_nibble.left(); let to_attach = StackedNodeState::Deleted; let mut detached = mem::replace(&mut stacked.item.node, to_attach); - detached.advance_partial(mid_index); + detached.advance_partial(mid_index - stacked.item.depth_prefix); let detached_hash = mem::replace(&mut stacked.item.hash, None); self.exit_detached(key_element, prefix, detached, detached_hash); } @@ -1376,11 +1377,11 @@ pub fn batch_update<'a, T, I, K, V, B>( #[cfg(test)] mod tests { use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, TrieMut, InputAction, - trie_traverse_key_no_extension_build, NoExtensionLayout, batch_update, + trie_traverse_key_no_extension_build, NoExtensionLayout, batch_update, }; use memory_db::{MemoryDB, PrefixedKey}; -// use memory_db::{MemoryDB, HashKey as PrefixedKey}; + // use memory_db::{MemoryDB, HashKey as PrefixedKey}; use keccak_hasher::KeccakHasher; use crate::{DBValue, OwnedPrefix}; use hash_db::HashDB; @@ -1406,10 +1407,10 @@ mod tests { let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix| { if check_order { println!("{:?} -> {:?}", previous, next); - /* if previous == next { - // we can have two same value if it is deletion then creation - assert!(prev_delete && !is_delet); - return; + /* if previous == next { + // we can have two same value if it is deletion then creation + assert!(prev_delete && !is_delet); + return; }*/ let prev_slice = NibbleSlice::new(previous.0.as_slice()); let p_at = |i| { @@ -1454,7 +1455,7 @@ mod tests { }, (None, None) => { panic!("Two consecutive action at same node") - //unreachable!("equality tested firsthand") + //unreachable!("equality tested firsthand") }, } } @@ -1520,7 +1521,7 @@ mod tests { &initial_root, v.iter().map(|(a, b)| (a, b.as_ref())), ); - + assert_eq!(calc_root, root); let mut batch_delta = initial_db; @@ -1569,7 +1570,7 @@ mod tests { &initial_root, elements, ).unwrap(); - + assert_eq!(calc_root, root); let mut batch_delta = initial_db.clone(); @@ -1613,7 +1614,7 @@ mod tests { compare_with_triedbmut( &[], &[ - (vec![], Some(vec![0xffu8, 0x33])), + (vec![], Some(vec![0xffu8, 0x33])), ], ); compare_with_triedbmut_detach(&[], &vec![]); @@ -1627,7 +1628,7 @@ mod tests { compare_with_triedbmut( db, &[ - (vec![], Some(vec![0xffu8, 0x33])), + (vec![], Some(vec![0xffu8, 0x33])), ], ); compare_with_triedbmut_detach(db, &vec![]); @@ -1641,7 +1642,7 @@ mod tests { compare_with_triedbmut( &[], &[ - (vec![0x04u8], Some(vec![0xffu8, 0x33])), + (vec![0x04u8], Some(vec![0xffu8, 0x33])), ], ); } @@ -1650,12 +1651,12 @@ mod tests { fn simple_fuse() { compare_with_triedbmut( &[ - (vec![0x04u8], vec![4, 32]), - (vec![0x04, 0x04], vec![4, 33]), - (vec![0x04, 0x04, 0x04], vec![4, 35]), + (vec![0x04u8], vec![4, 32]), + (vec![0x04, 0x04], vec![4, 33]), + (vec![0x04, 0x04, 0x04], vec![4, 35]), ], &[ - (vec![0x04u8, 0x04], None), + (vec![0x04u8, 0x04], None), ], ); } @@ -1668,8 +1669,8 @@ mod tests { compare_with_triedbmut( db, &[ - (vec![0x06u8], Some(vec![0xffu8, 0x33])), - (vec![0x08u8], Some(vec![0xffu8, 0x33])), + (vec![0x06u8], Some(vec![0xffu8, 0x33])), + (vec![0x08u8], Some(vec![0xffu8, 0x33])), ], ); compare_with_triedbmut_detach(db, &vec![0x04u8]); @@ -1681,11 +1682,11 @@ mod tests { fn two_recursive_mid_insert() { compare_with_triedbmut( &[ - (vec![0x0u8], vec![4, 32]), + (vec![0x0u8], vec![4, 32]), ], &[ - (vec![0x04u8], Some(vec![0xffu8, 0x33])), - (vec![0x20u8], Some(vec![0xffu8, 0x33])), + (vec![0x04u8], Some(vec![0xffu8, 0x33])), + (vec![0x20u8], Some(vec![0xffu8, 0x33])), ], ); } @@ -1700,13 +1701,13 @@ mod tests { compare_with_triedbmut( db, &[ - (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8; 32])), - (vec![0x01u8, 0x81u8, 0x23], Some(vec![0xfeu8; 32])), - (vec![0x01u8, 0x81u8, 0x23], None), + (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8; 32])), + (vec![0x01u8, 0x81u8, 0x23], Some(vec![0xfeu8; 32])), + (vec![0x01u8, 0x81u8, 0x23], None), ], ); compare_with_triedbmut_detach(db, &vec![]); - compare_with_triedbmut_detach(db, &vec![0x02]); // TODO !!! + compare_with_triedbmut_detach(db, &vec![0x02]); compare_with_triedbmut_detach(db, &vec![0x01u8]); compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81]); compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81, 0x23]); @@ -1726,10 +1727,10 @@ mod tests { fn delete_to_empty() { compare_with_triedbmut( &[ - (vec![1, 254u8], vec![4u8; 33]), + (vec![1, 254u8], vec![4u8; 33]), ], &[ - (vec![1, 254u8], None), + (vec![1, 254u8], None), ], ); } @@ -1738,11 +1739,11 @@ mod tests { fn fuse_root_node() { compare_with_triedbmut( &[ - (vec![2, 254u8], vec![4u8; 33]), - (vec![1, 254u8], vec![4u8; 33]), + (vec![2, 254u8], vec![4u8; 33]), + (vec![1, 254u8], vec![4u8; 33]), ], &[ - (vec![1, 254u8], None), + (vec![1, 254u8], None), ], ); } @@ -1751,13 +1752,13 @@ mod tests { fn dummy4() { compare_with_triedbmut( &[ - (vec![255u8, 251, 127, 255, 255], vec![255, 255]), - (vec![255, 255, 127, 112, 255], vec![0, 4]), - (vec![255, 127, 114, 253, 195], vec![1, 2]), + (vec![255u8, 251, 127, 255, 255], vec![255, 255]), + (vec![255, 255, 127, 112, 255], vec![0, 4]), + (vec![255, 127, 114, 253, 195], vec![1, 2]), ], &[ - (vec![0u8], Some(vec![4; 251])), - (vec![255, 251, 127, 255, 255], Some(vec![1, 2])), + (vec![0u8], Some(vec![4; 251])), + (vec![255, 251, 127, 255, 255], Some(vec![1, 2])), ], ); } @@ -1766,15 +1767,15 @@ mod tests { fn dummy6() { compare_with_triedbmut( &[ - (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], vec![255, 255]), - (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], vec![0, 4]), - (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 208, 208, 208, 208, 208, 208], vec![1, 2]), + (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], vec![255, 255]), + (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], vec![0, 4]), + (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 208, 208, 208, 208, 208, 208], vec![1, 2]), ], &[ - (vec![0, 6, 8, 21, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 199, 215], Some(vec![4, 251])), - (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], None), - (vec![141, 135, 207, 0, 63, 203, 216, 185, 162, 77, 154, 214, 210, 0, 0, 0, 0, 128], Some(vec![49, 251])), - (vec![208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 6, 8, 21, 1, 4, 0], Some(vec![4, 21])), + (vec![0, 6, 8, 21, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 199, 215], Some(vec![4, 251])), + (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], None), + (vec![141, 135, 207, 0, 63, 203, 216, 185, 162, 77, 154, 214, 210, 0, 0, 0, 0, 128], Some(vec![49, 251])), + (vec![208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 6, 8, 21, 1, 4, 0], Some(vec![4, 21])), ], ); } @@ -1783,12 +1784,12 @@ mod tests { fn fuse_with_child_partial() { compare_with_triedbmut( &[ - (vec![212], vec![212, 212]), + (vec![212], vec![212, 212]), ], &[ - (vec![58], Some(vec![63, 0])), - (vec![63], None), - (vec![212], None), + (vec![58], Some(vec![63, 0])), + (vec![63], None), + (vec![212], None), ], ); } @@ -1797,13 +1798,13 @@ mod tests { fn dummy7() { compare_with_triedbmut( &[ - (vec![0], vec![0, 212]), - (vec![8, 8], vec![0, 212]), + (vec![0], vec![0, 212]), + (vec![8, 8], vec![0, 212]), ], &[ - (vec![0], None), - (vec![8, 0], Some(vec![63, 0])), - (vec![128], None), + (vec![0], None), + (vec![8, 0], Some(vec![63, 0])), + (vec![128], None), ], ); } @@ -1812,13 +1813,13 @@ mod tests { fn dummy8() { compare_with_triedbmut( &[ - (vec![0], vec![0, 212]), - (vec![8, 8], vec![0, 212]), + (vec![0], vec![0, 212]), + (vec![8, 8], vec![0, 212]), ], &[ - (vec![0], None), - (vec![8, 0], Some(vec![63, 0])), - (vec![128], Some(vec![63, 0])), + (vec![0], None), + (vec![8, 0], Some(vec![63, 0])), + (vec![128], Some(vec![63, 0])), ], ); } @@ -1827,14 +1828,14 @@ mod tests { fn dummy9() { compare_with_triedbmut( &[ - (vec![0], vec![0, 212]), - (vec![1], vec![111, 22]), + (vec![0], vec![0, 212]), + (vec![1], vec![111, 22]), ], &[ - (vec![0], None), - (vec![5], Some(vec![63, 0])), - (vec![14], None), - (vec![64], Some(vec![63, 0])), + (vec![0], None), + (vec![5], Some(vec![63, 0])), + (vec![14], None), + (vec![64], Some(vec![63, 0])), ], ); } @@ -1843,11 +1844,11 @@ mod tests { fn dummy_51() { compare_with_triedbmut( &[ - (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), ], &[ - (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), - (vec![128], Some(vec![49, 251])), + (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), + (vec![128], Some(vec![49, 251])), ], ); } @@ -1856,11 +1857,11 @@ mod tests { fn emptied_then_insert() { compare_with_triedbmut( &[ - (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), ], &[ - (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), - (vec![128], Some(vec![49, 251])), + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), + (vec![128], Some(vec![49, 251])), ], ); } @@ -1869,12 +1870,12 @@ mod tests { fn dummy5() { compare_with_triedbmut( &[ - (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), ], &[ - (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), - (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), - (vec![128], Some(vec![49, 251])), + (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), + (vec![128], Some(vec![49, 251])), ], ); } @@ -1883,14 +1884,14 @@ mod tests { fn dummy_big() { compare_with_triedbmut( &[ - (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], vec![1, 2]), - (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], vec![1, 2]), + (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], vec![1, 2]), + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], vec![1, 2]), ], &[ - (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], None), - (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 208], Some(vec![4; 32])), - (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], None), + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], None), + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 208], Some(vec![4; 32])), + (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], None), ], ); } @@ -1899,20 +1900,20 @@ mod tests { fn single_latest_change_value_does_not_work() { compare_with_triedbmut( &[ - (vec![0, 0, 0, 0], vec![255;32]), - (vec![0, 0, 0, 3], vec![5; 32]), - (vec![0, 0, 6, 0], vec![6; 32]), - (vec![0, 0, 0, 170], vec![1; 32]), - (vec![0, 0, 73, 0], vec![2; 32]), - (vec![0, 0, 0, 0], vec![3; 32]), - (vec![0, 199, 141, 0], vec![4; 32]), + (vec![0, 0, 0, 0], vec![255;32]), + (vec![0, 0, 0, 3], vec![5; 32]), + (vec![0, 0, 6, 0], vec![6; 32]), + (vec![0, 0, 0, 170], vec![1; 32]), + (vec![0, 0, 73, 0], vec![2; 32]), + (vec![0, 0, 0, 0], vec![3; 32]), + (vec![0, 199, 141, 0], vec![4; 32]), ], &[ - (vec![0, 0, 0, 0], Some(vec![0; 32])), - (vec![0, 0, 199, 141], Some(vec![0; 32])), - (vec![0, 199, 141, 0], None), - (vec![12, 0, 128, 0, 0, 0, 0, 0, 0, 4, 64, 2, 4], Some(vec![0; 32])), - (vec![91], None), + (vec![0, 0, 0, 0], Some(vec![0; 32])), + (vec![0, 0, 199, 141], Some(vec![0; 32])), + (vec![0, 199, 141, 0], None), + (vec![12, 0, 128, 0, 0, 0, 0, 0, 0, 4, 64, 2, 4], Some(vec![0; 32])), + (vec![91], None), ], ); } @@ -1921,17 +1922,17 @@ mod tests { fn chained_fuse() { compare_with_triedbmut( &[ - (vec![0u8], vec![1; 32]), - (vec![0, 212], vec![2; 32]), - (vec![0, 212, 96], vec![3; 32]), - (vec![0, 212, 96, 88], vec![3; 32]), + (vec![0u8], vec![1; 32]), + (vec![0, 212], vec![2; 32]), + (vec![0, 212, 96], vec![3; 32]), + (vec![0, 212, 96, 88], vec![3; 32]), ], &[ - (vec![0u8], None), - (vec![0, 212], None), - (vec![0, 212, 96], None), - (vec![0, 212, 96, 88], Some(vec![3; 32])), + (vec![0u8], None), + (vec![0, 212], None), + (vec![0, 212, 96], None), + (vec![0, 212, 96, 88], Some(vec![3; 32])), ], ); } -} + } From 934b57b5790d0ad5b00267ad19267717923c582f Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 4 May 2020 12:04:29 +0200 Subject: [PATCH 096/118] init a fuzzer, next we need to handle detached to remove prefix (then we can test completness of detached when swap due to fuse). --- trie-db/fuzz/Cargo.toml | 4 + trie-db/fuzz/fuzz_targets/detach_attach.rs | 10 +++ trie-db/fuzz/src/lib.rs | 90 +++++++++++++++++++++- trie-db/src/traverse.rs | 33 +++++++- 4 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 trie-db/fuzz/fuzz_targets/detach_attach.rs diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 7e0ecd95..c2690178 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -68,3 +68,7 @@ path = "fuzz_targets/trie_proof_invalid.rs" [[bin]] name = "batch_update" path = "fuzz_targets/batch_update.rs" + +[[bin]] +name = "detach_attach" +path = "fuzz_targets/detach_attach.rs" diff --git a/trie-db/fuzz/fuzz_targets/detach_attach.rs b/trie-db/fuzz/fuzz_targets/detach_attach.rs new file mode 100644 index 00000000..d4991e33 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/detach_attach.rs @@ -0,0 +1,10 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // use next line to favor complex structure and inline data + trie_db_fuzz::fuzz_detach_attach(data, |_v| (), false); + // use next line to favor db prefix verification + // trie_db_fuzz::fuzz_detach_attach(data, |v| v.extend(&[4u8; 32]), true); +}); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index fea3eb4a..ef5ea4bf 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -20,7 +20,7 @@ use reference_trie::{ calc_root_no_extension, compare_no_extension_insert_remove, ExtensionLayout, - //NoExtensionLayout, + NoExtensionLayout, batch_update, InputAction, proof::{generate_proof, verify_proof}, reference_trie_root, RefTrieDBMut, @@ -72,7 +72,7 @@ fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { result } -fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec,Vec)> { +fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec, Vec)> { let mut res = Vec::new(); let mut torem = None; for (a, d) in data.into_iter().enumerate() { @@ -428,6 +428,92 @@ pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec), compare_db: } } +pub fn fuzz_detach_attach(input: &[u8], build_val: fn(&mut Vec), compare_db: bool) { + //x: &[(Vec, Vec)], + //d: &Vec, + let mut data = fuzz_to_data(input); + if data.len() == 0 { + return; + } + for i in data.iter_mut() { + build_val(&mut i.1); + } + let x = &data[1..]; + let d = &data[0].0; + //println!("{:?}\n d {:?}", x, d); + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 1..x.len() { + let key: &[u8]= &x[i].0; + let val: &[u8] = &x[i].1; + t.insert(key, val).unwrap(); + } + } + let initial_root = root.clone(); + let initial_db = db.clone(); + // reference + { + let mut t = RefTrieDBMutNoExt::from_existing(&mut db, &mut root).unwrap(); + for i in 1..x.len() { + if x[i].0.starts_with(d) { + let key: &[u8]= &x[i].0; + t.remove(key).unwrap(); + } + } + } + let elements = Some(d.clone()).into_iter().map(|k| (k, InputAction::, _>::Detach)); + let (calc_root, payload, detached_root) = batch_update::( + &initial_db, + &initial_root, + elements, + ).unwrap(); + + assert_eq!(calc_root, root); + + let mut batch_delta = initial_db.clone(); + for (p, h, v) in payload { + use hash_db::HashDB; + if let Some(v) = v { + let prefix = (p.0.as_ref(), p.1); + batch_delta.emplace(h, prefix, v[..].into()); + } else { + let prefix = (p.0.as_ref(), p.1); + batch_delta.remove(&h, prefix); + } + } + + // attach back + let elements = detached_root.into_iter().map(|(k, _prefix, root)| (k, InputAction::, _>::Attach(root))); + let (calc_root, payload, detached_root) = batch_update::( + &batch_delta, + &calc_root, + elements, + ).unwrap(); + if detached_root.is_empty() { + if compare_db { + for (p, h, v) in payload { + use hash_db::HashDB; + if let Some(v) = v { + let prefix = (p.0.as_ref(), p.1); + batch_delta.emplace(h, prefix, v[..].into()); + } else { + let prefix = (p.0.as_ref(), p.1); + batch_delta.remove(&h, prefix); + } + } + batch_delta.purge(); + assert!(initial_db == batch_delta); + } + assert!(calc_root == initial_root); + } else { + // case where there was a node fuse due to dettach + // TODO inject manually detached node and compare + } +} + + #[test] fn test() { let tests = [ diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index c7720461..b8173928 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1187,14 +1187,20 @@ impl ProcessStack for BatchUpdate, C, D> let prefix = prefix_nibble.left(); let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); + /*let (offset, parent_index) = if stacked.item.depth_prefix == 0 { + // corner case of adding at top of trie + (0, 0) + } else { + (1, NibbleSlice::new(key_element).at(mid_index)) + };*/ match to_attach.partial() { Some(partial) if partial.len() > 0 => { - let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix + mid_index).into(); + let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix).into(); crate::triedbmut::combine_key(&mut build_partial, partial.right_ref()); to_attach.set_partial(build_partial); }, _ => { - let build_partial = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix + mid_index); + let build_partial = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix); if build_partial.len() > 0 { to_attach.set_partial(build_partial.into()); } @@ -1311,6 +1317,7 @@ impl ProcessStack for BatchUpdate, C, D> let hash = ::hash(&encoded[..]); // Note that those updates are messing the updates ordering, could be better to register // them with the detached item (TODO would need a dedicated struct). + // TODO when implementing prefix migration: use a correct prefix here register_up((owned_prefix(&EMPTY_PREFIX), hash.clone(), Some(encoded))); register((key_element.to_vec(), owned_prefix(&prefix), hash)); } @@ -1323,7 +1330,10 @@ impl ProcessStack for BatchUpdate, C, D> not_inline } else { let encoded = s.into_encoded(); - ::hash(&encoded[..]) + let hash = ::hash(&encoded[..]); + // TODO when implementing prefix migration: use a correct prefix here + register_up((owned_prefix(&EMPTY_PREFIX), hash.clone(), Some(encoded))); + hash }; register((key_element.to_vec(), owned_prefix(&prefix), hash)); } @@ -1712,6 +1722,23 @@ mod tests { compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81]); compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81, 0x23]); } + + #[test] + fn dummy2_2() { + compare_with_triedbmut_detach(&[ + (vec![0], vec![0, 0]), + (vec![1], vec![0, 0]), + (vec![8], vec![1, 0]), + ], &vec![0]); + /* Detach does fuse a branch, and + * then when attaching it will swap. + * compare_with_triedbmut_detach(&[ + (vec![0], vec![50, 0]), + (vec![8], vec![0, 50]), + (vec![50], vec![0, 42]), + ], &vec![0]);*/ + } + #[test] fn dettach_middle() { let db = &[ From 61abb5a90d1be7aa48faee1bb4aaa9975f79ee54 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 4 May 2020 12:21:24 +0200 Subject: [PATCH 097/118] fuzz error ignored on attach, will be explicit with db unprefixing --- trie-db/src/traverse.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index b8173928..88d48045 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1326,7 +1326,8 @@ impl ProcessStack for BatchUpdate, C, D> | s@StackedNodeState::Unchanged(..) => { if !is_empty_node { let hash = if let Some((not_inline, previous_prefix)) = prev_hash { - debug_assert!(prefix == from_owned_prefix(&previous_prefix)); + // debug_assert!(prefix == from_owned_prefix(&previous_prefix)); TODO this does not + // hold in fuzzer, see what is wrong not_inline } else { let encoded = s.into_encoded(); From 97ffa9201dadf5f4129884d4dcf6013c8c69e40f Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 7 May 2020 16:08:52 +0200 Subject: [PATCH 098/118] detach prefixed key on query prefix (so all dettached trie is at this prefix). Attached trie is prerequisite to be prefixed at attached location. --- memory-db/src/lib.rs | 31 +++++++++++++- trie-db/fuzz/fuzz_targets/detach_attach.rs | 4 +- trie-db/src/traverse.rs | 48 +++++++++++++++------- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 738fbbaf..d9fce0e5 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -33,6 +33,7 @@ use std::{ mem, marker::PhantomData, cmp::Eq, + cmp, borrow::Borrow, }; @@ -48,6 +49,7 @@ use core::{ mem, marker::PhantomData, cmp::Eq, + cmp, borrow::Borrow, }; @@ -162,9 +164,17 @@ impl Eq for MemoryDB {} pub trait KeyFunction { - type Key: Send + Sync + Clone + hash::Hash + Eq; + type Key: Send + Sync + Clone + hash::Hash + Eq + AsRef<[u8]>; fn key(hash: &H::Out, prefix: Prefix) -> Self::Key; + fn hash(key: &Self::Key) -> H::Out { + let mut hash = H::Out::default(); + let key_len = key.as_ref().len(); + let l = cmp::min(H::LENGTH, key_len); + hash.as_mut()[H::LENGTH - l..].copy_from_slice(&key.as_ref()[key_len - l..]); + hash + } + fn unprefix(key: &Self::Key, prefix: Prefix) -> Option; } /// Key function that only uses the hash @@ -188,6 +198,9 @@ impl KeyFunction for HashKey { fn key(hash: &H::Out, prefix: Prefix) -> H::Out { hash_key::(hash, prefix) } + fn unprefix(key: &Self::Key, _prefix: Prefix) -> Option { + Some(Self::hash(key)) + } } /// Make database key from hash only. @@ -216,6 +229,19 @@ impl KeyFunction for PrefixedKey { fn key(hash: &H::Out, prefix: Prefix) -> Vec { prefixed_key::(hash, prefix) } + fn unprefix(key: &Self::Key, prefix: Prefix) -> Option { + let prefix_len = prefix.0.len() + prefix.1.as_ref().map(|_| 1).unwrap_or(0); + if key.len() < H::LENGTH + prefix_len { + None + } else if key.len() == H::LENGTH + prefix_len { + Some(key[key.len() - H::LENGTH..].to_vec()) + } else { + let other_prefix_len = key.len() - H::LENGTH - prefix_len; + let mut res = key[..other_prefix_len].to_vec(); + res.extend_from_slice(&key[key.len() - H::LENGTH..]); + Some(res) + } + } } /// Derive a database key from hash value of the node (key) and the node prefix. @@ -242,6 +268,9 @@ impl KeyFunction for LegacyPrefixedKey { fn key(hash: &H::Out, prefix: Prefix) -> Vec { legacy_prefixed_key::(hash, prefix) } + fn unprefix(key: &Self::Key, _prefix: Prefix) -> Option { + Some(Self::hash(key).as_ref().to_vec()) + } } /// Legacy method for db using previous version of prefix encoding. diff --git a/trie-db/fuzz/fuzz_targets/detach_attach.rs b/trie-db/fuzz/fuzz_targets/detach_attach.rs index d4991e33..6affac7f 100644 --- a/trie-db/fuzz/fuzz_targets/detach_attach.rs +++ b/trie-db/fuzz/fuzz_targets/detach_attach.rs @@ -4,7 +4,7 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // use next line to favor complex structure and inline data - trie_db_fuzz::fuzz_detach_attach(data, |_v| (), false); + //trie_db_fuzz::fuzz_detach_attach(data, |_v| (), false); // use next line to favor db prefix verification - // trie_db_fuzz::fuzz_detach_attach(data, |v| v.extend(&[4u8; 32]), true); + trie_db_fuzz::fuzz_detach_attach(data, |v| v.extend(&[4u8; 32]), true); }); diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 88d48045..ce1e620d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -22,7 +22,8 @@ //! be done by using a tuple of extension and branch node as a branch (storing //! an additional hash in branch and only adapting fetch and write methods). -// TODO CHEME what happen if set same value as existing!!! -> could skip alloc +// TODO CHEME what happen if set same value as existing!!! -> could skip alloc -> ensure we keep +// unchange!! -> Same for set_partial, it does change on setting identical value use crate::triedbmut::{Node, NibbleFullKey}; use crate::triedbmut::NodeHandle as NodeHandleTrieMut; @@ -722,9 +723,10 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( }; continue; }, - Some((_key, InputAction::Attach(attach_root))) => { + Some((key, InputAction::Attach(attach_root))) => { // TODO nooop on attach empty - let root_node = fetch::(db, &attach_root, EMPTY_PREFIX)?; + let root_prefix = (key.as_ref(), None); + let root_node = fetch::(db, &attach_root, root_prefix)?; let depth = root_node.partial().map(|p| p.len()).unwrap_or(0); current = StackedItem { item: StackedNode { @@ -928,8 +930,9 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( }; let (value, do_fetch) = value.as_ref(); let fetch = if let Some(hash) = do_fetch { - // This is fetching node to attach, it is a root and then uses EMPTY_PREFIX - let hash = fetch::(db, &hash, EMPTY_PREFIX)?; + // This is fetching node to attach + let root_prefix = (key.as_ref(), None); + let hash = fetch::(db, &hash, root_prefix)?; Some(hash) } else { None @@ -1021,7 +1024,10 @@ impl ProcessStack for BatchUpdate, C, D> stacked.item.node.advance_partial(stacked.item.depth - stacked.item.depth_prefix); } let detached = mem::replace(&mut stacked.item.node, to_attach); - let detached_hash = mem::replace(&mut stacked.item.hash, Some((attach_root.clone(), owned_prefix(&EMPTY_PREFIX)))); + let detached_hash = mem::replace( + &mut stacked.item.hash, + Some((attach_root.clone(), owned_prefix(&(key_element, None)))), + ); stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); self.exit_detached(key_element, prefix, detached, detached_hash); }, @@ -1109,8 +1115,8 @@ impl ProcessStack for BatchUpdate, C, D> let mut new_child = StackedItem { item: StackedNode { node: to_attach, - // Attach root is unprefixed - hash: Some((attach_root.clone(), owned_prefix(&EMPTY_PREFIX))), + // Attach root is prefixed at attach key + hash: Some((attach_root.clone(), owned_prefix(&(key_element, None)))), depth_prefix, depth, parent_index, @@ -1211,7 +1217,10 @@ impl ProcessStack for BatchUpdate, C, D> } let mut detached = mem::replace(&mut stacked.item.node, to_attach); //detached.advance_partial(mid_index); - let detached_hash = mem::replace(&mut stacked.item.hash, Some((attach_root.clone(), owned_prefix(&EMPTY_PREFIX)))); + let detached_hash = mem::replace( + &mut stacked.item.hash, + Some((attach_root.clone(), owned_prefix(&(key_element, None)))), + ); stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); self.exit_detached(key_element, prefix, detached, detached_hash); None @@ -1301,7 +1310,10 @@ impl ProcessStack for BatchUpdate, C, D> } } + // TODO EMCHh seems like prefix param is useless!!! fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { + let detached_prefix = (key_element, None); + let is_empty_node = stacked.is_empty(); let register_up = &mut self.register_update; let register = &mut self.register_detached; @@ -1317,8 +1329,7 @@ impl ProcessStack for BatchUpdate, C, D> let hash = ::hash(&encoded[..]); // Note that those updates are messing the updates ordering, could be better to register // them with the detached item (TODO would need a dedicated struct). - // TODO when implementing prefix migration: use a correct prefix here - register_up((owned_prefix(&EMPTY_PREFIX), hash.clone(), Some(encoded))); + register_up((owned_prefix(&detached_prefix), hash.clone(), Some(encoded))); register((key_element.to_vec(), owned_prefix(&prefix), hash)); } }, @@ -1326,14 +1337,12 @@ impl ProcessStack for BatchUpdate, C, D> | s@StackedNodeState::Unchanged(..) => { if !is_empty_node { let hash = if let Some((not_inline, previous_prefix)) = prev_hash { - // debug_assert!(prefix == from_owned_prefix(&previous_prefix)); TODO this does not // hold in fuzzer, see what is wrong not_inline } else { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); - // TODO when implementing prefix migration: use a correct prefix here - register_up((owned_prefix(&EMPTY_PREFIX), hash.clone(), Some(encoded))); + register_up((owned_prefix(&detached_prefix), hash.clone(), Some(encoded))); hash }; register((key_element.to_vec(), owned_prefix(&prefix), hash)); @@ -1740,6 +1749,15 @@ mod tests { ], &vec![0]);*/ } + #[test] + fn dummy2_23() { + compare_with_triedbmut_detach(&[ + (vec![0], vec![3; 40]), + (vec![1], vec![2; 40]), + (vec![8], vec![1; 40]), + ], &vec![0]); + } + #[test] fn dettach_middle() { let db = &[ @@ -1963,4 +1981,4 @@ mod tests { ], ); } - } +} From cbdcb7f7c39598069400e311ead242a533fd0417 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 7 May 2020 18:10:53 +0200 Subject: [PATCH 099/118] unefficient implementation of dettached trie unprefixing --- memory-db/src/lib.rs | 27 ------ test-support/reference-trie/src/lib.rs | 2 +- trie-db/src/traverse.rs | 116 +++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 28 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index d9fce0e5..5120e559 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -167,14 +167,6 @@ pub trait KeyFunction { type Key: Send + Sync + Clone + hash::Hash + Eq + AsRef<[u8]>; fn key(hash: &H::Out, prefix: Prefix) -> Self::Key; - fn hash(key: &Self::Key) -> H::Out { - let mut hash = H::Out::default(); - let key_len = key.as_ref().len(); - let l = cmp::min(H::LENGTH, key_len); - hash.as_mut()[H::LENGTH - l..].copy_from_slice(&key.as_ref()[key_len - l..]); - hash - } - fn unprefix(key: &Self::Key, prefix: Prefix) -> Option; } /// Key function that only uses the hash @@ -198,9 +190,6 @@ impl KeyFunction for HashKey { fn key(hash: &H::Out, prefix: Prefix) -> H::Out { hash_key::(hash, prefix) } - fn unprefix(key: &Self::Key, _prefix: Prefix) -> Option { - Some(Self::hash(key)) - } } /// Make database key from hash only. @@ -229,19 +218,6 @@ impl KeyFunction for PrefixedKey { fn key(hash: &H::Out, prefix: Prefix) -> Vec { prefixed_key::(hash, prefix) } - fn unprefix(key: &Self::Key, prefix: Prefix) -> Option { - let prefix_len = prefix.0.len() + prefix.1.as_ref().map(|_| 1).unwrap_or(0); - if key.len() < H::LENGTH + prefix_len { - None - } else if key.len() == H::LENGTH + prefix_len { - Some(key[key.len() - H::LENGTH..].to_vec()) - } else { - let other_prefix_len = key.len() - H::LENGTH - prefix_len; - let mut res = key[..other_prefix_len].to_vec(); - res.extend_from_slice(&key[key.len() - H::LENGTH..]); - Some(res) - } - } } /// Derive a database key from hash value of the node (key) and the node prefix. @@ -268,9 +244,6 @@ impl KeyFunction for LegacyPrefixedKey { fn key(hash: &H::Out, prefix: Prefix) -> Vec { legacy_prefixed_key::(hash, prefix) } - fn unprefix(key: &Self::Key, _prefix: Prefix) -> Option { - Some(Self::hash(key).as_ref().to_vec()) - } } /// Legacy method for db using previous version of prefix encoding. diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 11353fdc..2738e6ca 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -33,7 +33,7 @@ use trie_db::{ }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::traverse::{batch_update, InputAction}; +pub use trie_db::traverse::{batch_update, InputAction, unprefixed_detached_trie, prefixed_detached_trie}; pub use trie_db::{ decode_compact, encode_compact, diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index ce1e620d..503fca40 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1394,10 +1394,109 @@ pub fn batch_update<'a, T, I, K, V, B>( Ok((batch_update.root, dest, dest_detached)) } + + +/// For a dettached trie root, remove prefix. +/// Usually the prefix is the key path used to detach the node +/// for a key function that attach prefix. +/// This transformation is not needed if there is no prefix +/// added by the key function of the backend db. +/// This method maitain all trie key in memory, a different implementation +/// should be use if inner mutability is possible (trie iterator touch each +/// key only once so it should be safe to delete immediatly). +pub fn unprefixed_detached_trie( + source_db: &mut dyn hash_db::HashDB>, + mut target_db: Option<&mut dyn hash_db::HashDB>>, + root: TrieHash, + prefix: &[u8], +) -> Result<(),TrieHash, CError> + where + T: TrieLayout, +{ + let mapped_source = PrefixedDBRef::(source_db, prefix); + let input = crate::TrieDB::::new(&mapped_source, &root)?; + let mut keys: Vec<(NibbleVec, TrieHash)> = Vec::new(); + for elt in crate::TrieDBNodeIterator::new(&input)? { + if let Ok((prefix, Some(hash), _)) = elt { + keys.push((prefix, hash)); + } + } + let mut prefix_slice = prefix.to_vec(); + let prefix_slice_len = prefix_slice.len(); + for (prefix, hash) in keys.into_iter() { + let prefix = prefix.as_prefix(); + prefix_slice.truncate(prefix_slice_len); + prefix_slice.extend_from_slice(prefix.0); + let prefix_source = (prefix_slice.as_ref(), prefix.1); + if let Some(value) = source_db.get(&hash, prefix_source) { + source_db.remove(&hash, prefix_source); + if let Some(target) = target_db.as_mut() { + target.emplace(hash, prefix, value); + } else { + source_db.emplace(hash, prefix, value); + } + } + } + Ok(()) +} + +struct PrefixedDBRef<'a, T: TrieLayout>(&'a mut dyn hash_db::HashDB>, &'a [u8]); + +impl<'a, T: TrieLayout> hash_db::HashDBRef> for PrefixedDBRef<'a, T> { + fn get(&self, key: &TrieHash, prefix: Prefix) -> Option> { + let mut prefix_slice = self.1.to_vec(); + prefix_slice.extend_from_slice(prefix.0); + self.0.get(key, (prefix_slice.as_ref(), prefix.1)) + } + + fn contains(&self, key: &TrieHash, prefix: Prefix) -> bool { + let mut prefix_slice = self.1.to_vec(); + prefix_slice.extend_from_slice(prefix.0); + self.0.contains(key, (prefix_slice.as_ref(), prefix.1)) + } +} + +pub fn prefixed_detached_trie( + source_db: &mut dyn hash_db::HashDB>, + mut target_db: Option<&mut dyn hash_db::HashDB>>, + root: TrieHash, + prefix: &[u8], +) -> Result<(),TrieHash, CError> + where + T: TrieLayout, +{ + let input = crate::TrieDB::::new(&source_db, &root)?; + let mut keys: Vec<(NibbleVec, TrieHash)> = Vec::new(); + for elt in crate::TrieDBNodeIterator::new(&input)? { + if let Ok((prefix, Some(hash), _)) = elt { + keys.push((prefix, hash)); + } + } + let mut prefix_slice = prefix.to_vec(); + let prefix_slice_len = prefix_slice.len(); + for (prefix, hash) in keys.into_iter() { + let prefix = prefix.as_prefix(); + if let Some(value) = source_db.get(&hash, prefix) { + prefix_slice.truncate(prefix_slice_len); + prefix_slice.extend_from_slice(prefix.0); + let prefix_dest = (prefix_slice.as_ref(), prefix.1); + source_db.remove(&hash, prefix); + if let Some(target) = target_db.as_mut() { + target.emplace(hash, prefix_dest, value); + } else { + source_db.emplace(hash, prefix_dest, value); + } + } + } + Ok(()) +} + + #[cfg(test)] mod tests { use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, TrieMut, InputAction, trie_traverse_key_no_extension_build, NoExtensionLayout, batch_update, + unprefixed_detached_trie, prefixed_detached_trie, }; use memory_db::{MemoryDB, PrefixedKey}; @@ -1601,6 +1700,23 @@ mod tests { let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); println!("{:?}", t2b); + if let Some((k, _, d_root)) = detached_root.iter().next() { + unprefixed_detached_trie::( + &mut batch_delta, + None, + d_root.clone(), + d.as_ref(), + ).unwrap(); + let t2b = RefTrieDBNoExt::new(&batch_delta, d_root).unwrap(); + println!("{:?}", t2b); + prefixed_detached_trie::( + &mut batch_delta, + None, + d_root.clone(), + d.as_ref(), + ).unwrap(); + } + println!("{:?}", db.clone().drain()); println!("{:?}", batch_delta.clone().drain()); // TODO this cannot be test because we have attached content From 202c9fe5c91ca76dbcc0ab8848a5c94292817f42 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 7 May 2020 18:20:29 +0200 Subject: [PATCH 100/118] actually wrong todo --- trie-db/src/traverse.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 503fca40..1220f23f 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -1215,7 +1215,7 @@ impl ProcessStack for BatchUpdate, C, D> if mid_index > 0 { stacked.item.node.advance_partial(mid_index - stacked.item.depth_prefix); } - let mut detached = mem::replace(&mut stacked.item.node, to_attach); + let detached = mem::replace(&mut stacked.item.node, to_attach); //detached.advance_partial(mid_index); let detached_hash = mem::replace( &mut stacked.item.hash, @@ -1310,7 +1310,6 @@ impl ProcessStack for BatchUpdate, C, D> } } - // TODO EMCHh seems like prefix param is useless!!! fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { let detached_prefix = (key_element, None); From 1d18c9b9ec6060c8f0431b32e980979e1f896d93 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 12:24:16 +0200 Subject: [PATCH 101/118] check on change for partial set. --- trie-db/src/node.rs | 18 +++++++++++++----- trie-db/src/traverse.rs | 8 ++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index bfd1ac72..d2dcccd9 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -332,19 +332,27 @@ impl> OwnedNode { } /// Set a partial TODO EMCH factor new node from existing on all those methods. - pub(crate) fn set_partial + Default>(&mut self, partial: NodeKey) -> Option>> { + pub(crate) fn set_partial + Default>(&mut self, new_partial: NodeKey) -> Option>> { let data = &self.data.borrow(); match &self.plan { - NodePlan::Leaf { value, .. } => { + NodePlan::Leaf { value, partial } => { + let partial = partial.build(data); + if partial == NibbleSlice::from_stored(&new_partial) { + return None; + } Some(TNode::Leaf( - partial, + new_partial, data[value.clone()].into(), )) }, NodePlan::Extension { .. } // TODO Extension | NodePlan::Branch { .. } // TODO branch | NodePlan::Empty => None, - NodePlan::NibbledBranch { value, children, .. } => { + NodePlan::NibbledBranch { value, children, partial } => { + let partial = partial.build(data); + if partial == NibbleSlice::from_stored(&new_partial) { + return None; + } let mut child_slices = [ None, None, None, None, None, None, None, None, @@ -355,7 +363,7 @@ impl> OwnedNode { child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); } Some(TNode::NibbledBranch( - partial, + new_partial, Box::new(child_slices), value.as_ref().map(|value| data[value.clone()].into()), )) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 1220f23f..1af7ac1a 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -22,9 +22,6 @@ //! be done by using a tuple of extension and branch node as a branch (storing //! an additional hash in branch and only adapting fetch and write methods). -// TODO CHEME what happen if set same value as existing!!! -> could skip alloc -> ensure we keep -// unchange!! -> Same for set_partial, it does change on setting identical value - use crate::triedbmut::{Node, NibbleFullKey}; use crate::triedbmut::NodeHandle as NodeHandleTrieMut; use crate::node::{OwnedNode, NodeHandle, NodeKey}; @@ -1278,6 +1275,9 @@ impl ProcessStack for BatchUpdate, C, D> StackedNodeState::UnchangedAttached(node) => Some(Some({ let encoded = node.data().to_vec(); if encoded.len() < ::LENGTH { + if let Some((h, p)) = prev_hash { + register((p, h, None)); + } OwnedNodeHandle::InMemory(encoded) } else { OwnedNodeHandle::Hash(::hash(&encoded[..])) @@ -1849,7 +1849,7 @@ mod tests { } #[test] - fn dummy2_2() { + fn dummy2_20() { compare_with_triedbmut_detach(&[ (vec![0], vec![0, 0]), (vec![1], vec![0, 0]), From d588543df2b9ca434fdec3bde2dd13caf94eb2f9 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 14:43:51 +0200 Subject: [PATCH 102/118] no need to put root, it is use from prev_hash --- trie-db/src/traverse.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 1af7ac1a..5959c28b 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -52,7 +52,7 @@ enum StackedNodeState /// Read node. Unchanged(OwnedNode), /// Read node, attached, we need to update - /// parent hash or root. TODO EMCH add the old root!!(as vec maybe) + /// parent hash or root. UnchangedAttached(OwnedNode), /// Modified node. Changed(Node, StorageHandle>), @@ -1336,7 +1336,6 @@ impl ProcessStack for BatchUpdate, C, D> | s@StackedNodeState::Unchanged(..) => { if !is_empty_node { let hash = if let Some((not_inline, previous_prefix)) = prev_hash { - // hold in fuzzer, see what is wrong not_inline } else { let encoded = s.into_encoded(); From 4677da83a937a71a05a4b8d490c719ef2216f448 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 15:03:32 +0200 Subject: [PATCH 103/118] facto child slice build --- trie-db/src/node.rs | 102 +++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index d2dcccd9..c216d6b8 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -20,6 +20,10 @@ use crate::triedbmut::Node as TNode; use crate::triedbmut::NodeHandle as TNodeHandle; use crate::rstd::{borrow::Borrow, ops::Range, boxed::Box, vec::Vec}; + +/// Owned handle to a node, to use when there is no caching. +pub type StorageHandle = Vec; + /// Partial node key type: offset and owned value of a nibbleslice. /// Offset is applied on first byte of array (bytes are right aligned). pub type NodeKey = (usize, nibble::BackingByteVec); @@ -77,8 +81,7 @@ impl NodeHandlePlan { } } - /// TODO - pub fn build_thandle + Default>(&self, data: &[u8]) -> crate::triedbmut::NodeHandle> { + fn build_owned_handle + Default>(&self, data: &[u8]) -> crate::triedbmut::NodeHandle { match self { NodeHandlePlan::Hash(range) => { let mut hash = H::default(); @@ -291,8 +294,35 @@ impl> OwnedNode { } impl> OwnedNode { + + + fn init_child_slice + Default>( + data: &[u8], + children: &[Option; nibble_ops::NIBBLE_LENGTH], + skip_index: Option, + ) -> Box<[Option>; 16]> { + let mut child_slices = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + if let Some(skip) = skip_index { + for i in 0..nibble_ops::NIBBLE_LENGTH { + if i != skip { + child_slices[i] = children[i].as_ref().map(|child| child.build_owned_handle(data)); + } + } + } else { + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_owned_handle(data)); + } + } + child_slices + } + /// Set a partial TODO EMCH factor new node from existing on all those methods. - pub(crate) fn advance_partial + Default>(&mut self, nb: usize) -> Option>> { + pub(crate) fn advance_partial + Default>(&mut self, nb: usize) -> Option> { if nb == 0 { return None; } @@ -312,19 +342,10 @@ impl> OwnedNode { NodePlan::NibbledBranch { partial, value, children } => { let mut partial = partial.build(data); partial.advance(nb); - let mut child_slices = [ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]; - for i in 0..nibble_ops::NIBBLE_LENGTH { - child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); - } Some(TNode::NibbledBranch( partial.into(), - Box::new(child_slices), + Self::init_child_slice(data, children, None), value.as_ref().map(|value| data[value.clone()].into()), )) }, @@ -332,7 +353,7 @@ impl> OwnedNode { } /// Set a partial TODO EMCH factor new node from existing on all those methods. - pub(crate) fn set_partial + Default>(&mut self, new_partial: NodeKey) -> Option>> { + pub(crate) fn set_partial + Default>(&mut self, new_partial: NodeKey) -> Option> { let data = &self.data.borrow(); match &self.plan { NodePlan::Leaf { value, partial } => { @@ -353,18 +374,9 @@ impl> OwnedNode { if partial == NibbleSlice::from_stored(&new_partial) { return None; } - let mut child_slices = [ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]; - for i in 0..nibble_ops::NIBBLE_LENGTH { - child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); - } Some(TNode::NibbledBranch( new_partial, - Box::new(child_slices), + Self::init_child_slice(data, children, None), value.as_ref().map(|value| data[value.clone()].into()), )) }, @@ -372,7 +384,7 @@ impl> OwnedNode { } /// Set a value. - pub(crate) fn set_value + Default>(&mut self, new_value: &[u8]) -> Option>> { + pub(crate) fn set_value + Default>(&mut self, new_value: &[u8]) -> Option> { let data = &self.data.borrow(); match &self.plan { NodePlan::Empty => { @@ -399,28 +411,18 @@ impl> OwnedNode { return None; } } - let mut child_slices = [ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]; - for i in 0..nibble_ops::NIBBLE_LENGTH { - child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); - } Some(TNode::NibbledBranch( partial.build(data).into(), - Box::new(child_slices), + Self::init_child_slice(data, children, None), Some(new_value.into()), )) - }, } } /// Remove a value. - pub(crate) fn remove_value + Default>(&mut self) -> Option>>> { + pub(crate) fn remove_value + Default>(&mut self) -> Option>> { let data = &self.data.borrow(); match &self.plan { NodePlan::Leaf { .. } => Some(None), @@ -431,19 +433,10 @@ impl> OwnedNode { if value.is_none() { return None; } - let mut child_slices = [ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]; - for i in 0..nibble_ops::NIBBLE_LENGTH { - child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); - } Some(Some(TNode::NibbledBranch( partial.build(data).into(), - Box::new(child_slices), + Self::init_child_slice(data, children, None), None, ))) }, @@ -452,8 +445,8 @@ impl> OwnedNode { /// Set a handle to a child node or remove it if handle is none. /// Return possibly updated node. - pub(crate) fn set_handle + Default>(&mut self, handle: Option>>, index: u8) - -> Option>> { + pub(crate) fn set_handle + Default>(&mut self, handle: Option>, index: u8) + -> Option> { let index = index as usize; let data = &self.data.borrow(); @@ -489,16 +482,7 @@ impl> OwnedNode { } else { None }; - let mut child_slices = Box::new([ - None, None, None, None, - None, None, None, None, - None, None, None, None, - None, None, None, None, - ]); - for i in 0..nibble_ops::NIBBLE_LENGTH { // TODO skip index - child_slices[i] = children[i].as_ref().map(|child| child.build_thandle(data)); - } - + let mut child_slices = Self::init_child_slice(data, children, Some(index)); child_slices[index] = handle; Some(TNode::NibbledBranch( From fd8ed5006cc86d703e197cc76aa5d006072afb54 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 16:06:58 +0200 Subject: [PATCH 104/118] type aliases --- memory-db/src/lib.rs | 1 - trie-db/src/lib.rs | 16 +-- trie-db/src/node.rs | 66 +++++++----- trie-db/src/traverse.rs | 19 ++-- trie-db/src/triedbmut.rs | 227 +++++++++++++++++++-------------------- 5 files changed, 161 insertions(+), 168 deletions(-) diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 5120e559..00718cc2 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -33,7 +33,6 @@ use std::{ mem, marker::PhantomData, cmp::Eq, - cmp, borrow::Borrow, }; diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 2e08296d..67e2f5c2 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -35,7 +35,7 @@ mod rstd { } #[cfg(feature = "std")] -use self::rstd::{fmt, Error}; +use self::rstd::fmt; use hash_db::MaybeDebug; use self::rstd::{boxed::Box, vec::Vec}; @@ -122,24 +122,10 @@ impl fmt::Display for TrieError where T: MaybeDebug, E: MaybeDebug { } } -#[cfg(feature = "std")] -impl Error for TrieError where T: fmt::Debug, E: Error { - fn description(&self) -> &str { - match *self { - TrieError::InvalidStateRoot(_) => "Invalid state root", - TrieError::IncompleteDatabase(_) => "Incomplete database", - TrieError::ValueAtIncompleteKey(_, _) => "Value at incomplete key", - TrieError::DecoderError(_, ref err) => err.description(), - TrieError::InvalidHash(_, _) => "Encoded node contains invalid hash reference", - } - } -} - /// Trie result type. /// Boxed to avoid copying around extra space for the `Hasher`s `Out` on successful queries. pub type Result = crate::rstd::result::Result>>; - /// Trie-Item type used for iterators over trie data. pub type TrieItem<'a, U, E> = Result<(Vec, DBValue), U, E>; diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index c216d6b8..6e47c09f 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -16,14 +16,21 @@ use hash_db::Hasher; use crate::nibble::{self, NibbleSlice}; use crate::nibble::nibble_ops; use crate::node_codec::NodeCodec; -use crate::triedbmut::Node as TNode; -use crate::triedbmut::NodeHandle as TNodeHandle; use crate::rstd::{borrow::Borrow, ops::Range, boxed::Box, vec::Vec}; +/// This was only implemented for trie without extension, it could +/// be implemented for trie and extension in the future but is not +/// at this point. +const NO_EXTENSION_ONLY: &str = "trie without extension implemented only"; + /// Owned handle to a node, to use when there is no caching. pub type StorageHandle = Vec; +type TNode = crate::triedbmut::NodeMut; + +type TNodeHandle = crate::triedbmut::NodeHandleMut; + /// Partial node key type: offset and owned value of a nibbleslice. /// Offset is applied on first byte of array (bytes are right aligned). pub type NodeKey = (usize, nibble::BackingByteVec); @@ -81,17 +88,16 @@ impl NodeHandlePlan { } } - fn build_owned_handle + Default>(&self, data: &[u8]) -> crate::triedbmut::NodeHandle { + fn build_owned_handle + Default>(&self, data: &[u8]) -> TNodeHandle { match self { NodeHandlePlan::Hash(range) => { let mut hash = H::default(); hash.as_mut().copy_from_slice(&data[range.clone()]); - crate::triedbmut::NodeHandle::Hash(hash) + TNodeHandle::Hash(hash) }, - NodeHandlePlan::Inline(range) => crate::triedbmut::NodeHandle::InMemory((&data[range.clone()]).into()), + NodeHandlePlan::Inline(range) => TNodeHandle::InMemory((&data[range.clone()]).into()), } } - } /// A `NibbleSlicePlan` is a blueprint for decoding a nibble slice from a byte slice. The @@ -300,7 +306,7 @@ impl> OwnedNode { data: &[u8], children: &[Option; nibble_ops::NIBBLE_LENGTH], skip_index: Option, - ) -> Box<[Option>; 16]> { + ) -> Box<[Option>; 16]> { let mut child_slices = Box::new([ None, None, None, None, None, None, None, None, @@ -321,8 +327,8 @@ impl> OwnedNode { child_slices } - /// Set a partial TODO EMCH factor new node from existing on all those methods. - pub(crate) fn advance_partial + Default>(&mut self, nb: usize) -> Option> { + /// Remove n first byte from the existing partial, return updated node if updated. + pub(crate) fn advance_partial + Default>(&mut self, nb: usize) -> Option> { if nb == 0 { return None; } @@ -336,8 +342,8 @@ impl> OwnedNode { data[value.clone()].into(), )) }, - NodePlan::Extension { .. } // TODO Extension - | NodePlan::Branch { .. } // TODO branch + NodePlan::Extension { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), + NodePlan::Branch { .. } | NodePlan::Empty => None, NodePlan::NibbledBranch { partial, value, children } => { let mut partial = partial.build(data); @@ -352,8 +358,8 @@ impl> OwnedNode { } } - /// Set a partial TODO EMCH factor new node from existing on all those methods. - pub(crate) fn set_partial + Default>(&mut self, new_partial: NodeKey) -> Option> { + /// Set a partial and return new node if changed. + pub(crate) fn set_partial + Default>(&mut self, new_partial: NodeKey) -> Option> { let data = &self.data.borrow(); match &self.plan { NodePlan::Leaf { value, partial } => { @@ -366,8 +372,8 @@ impl> OwnedNode { data[value.clone()].into(), )) }, - NodePlan::Extension { .. } // TODO Extension - | NodePlan::Branch { .. } // TODO branch + NodePlan::Extension { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), + NodePlan::Branch { .. } | NodePlan::Empty => None, NodePlan::NibbledBranch { value, children, partial } => { let partial = partial.build(data); @@ -383,8 +389,8 @@ impl> OwnedNode { } } - /// Set a value. - pub(crate) fn set_value + Default>(&mut self, new_value: &[u8]) -> Option> { + /// Set a value and return new node if changed. + pub(crate) fn set_value + Default>(&mut self, new_value: &[u8]) -> Option> { let data = &self.data.borrow(); match &self.plan { NodePlan::Empty => { @@ -402,9 +408,8 @@ impl> OwnedNode { new_value.into(), )) }, - NodePlan::Extension { .. } // TODO Extension - // TODO branch - | NodePlan::Branch { .. } => None, + NodePlan::Extension { .. } => None, + NodePlan::Branch { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), NodePlan::NibbledBranch { partial, value, children } => { if let Some(value) = value { if &data[value.clone()] == new_value { @@ -421,13 +426,16 @@ impl> OwnedNode { } } - /// Remove a value. - pub(crate) fn remove_value + Default>(&mut self) -> Option>> { + /// Remove a value, return the change if something did change either node deleted or new value + /// for node. + /// Note that we are allowed to return a branch with no value and a single child (would need to + /// be fix depending on calling context (there could be some appending afterward)). + pub(crate) fn remove_value + Default>(&mut self) -> Option>> { let data = &self.data.borrow(); match &self.plan { NodePlan::Leaf { .. } => Some(None), - NodePlan::Extension { .. } // TODO Extension - | NodePlan::Branch { .. } // TODO branch + NodePlan::Branch { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), + NodePlan::Extension { .. } | NodePlan::Empty => None, NodePlan::NibbledBranch { partial, value, children } => { if value.is_none() { @@ -445,15 +453,15 @@ impl> OwnedNode { /// Set a handle to a child node or remove it if handle is none. /// Return possibly updated node. - pub(crate) fn set_handle + Default>(&mut self, handle: Option>, index: u8) - -> Option> { + pub(crate) fn set_handle + Default>(&mut self, handle: Option>, index: u8) + -> Option> { let index = index as usize; let data = &self.data.borrow(); match &mut self.plan { - NodePlan::Empty => unreachable!("corner case cannot be handle in this function"), - NodePlan::Extension { .. } // TODO Extension - | NodePlan::Branch { .. } => unreachable!("function only for no extension trie"), + NodePlan::Empty => unreachable!("Do not add handle to empty but replace the node instead"), + NodePlan::Extension { .. } + | NodePlan::Branch { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), NodePlan::Leaf { partial, value } => { if handle.is_some() { let mut child_slices = Box::new([ diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 5959c28b..7dcb8b15 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -22,9 +22,8 @@ //! be done by using a tuple of extension and branch node as a branch (storing //! an additional hash in branch and only adapting fetch and write methods). -use crate::triedbmut::{Node, NibbleFullKey}; -use crate::triedbmut::NodeHandle as NodeHandleTrieMut; -use crate::node::{OwnedNode, NodeHandle, NodeKey}; +use crate::triedbmut::{NibbleFullKey}; +use crate::node::{OwnedNode, NodeHandle, NodeKey, StorageHandle}; use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; #[cfg(feature = "std")] use std::borrow::Borrow; @@ -39,8 +38,10 @@ use crate::NodeCodec; use crate::rstd::mem; use crate::rstd::boxed::Box; -type StorageHandle = Vec; -type OwnedNodeHandle = NodeHandleTrieMut; + +type Node = crate::triedbmut::NodeMut; + +type OwnedNodeHandle = crate::triedbmut::NodeHandleMut; /// StackedNodeState can be updated. /// A state can be use. @@ -55,7 +56,7 @@ enum StackedNodeState /// parent hash or root. UnchangedAttached(OwnedNode), /// Modified node. - Changed(Node, StorageHandle>), + Changed(Node>), /// Deleted node. Deleted, } @@ -556,7 +557,7 @@ impl StackedNodeState | StackedNodeState::Unchanged(node) => node.data().to_vec(), StackedNodeState::Changed(node) => node.into_encoded::<_, T::Codec, T::Hash>( |child, _o_slice, _o_index| { - child.as_child_ref::() + child.into_child_ref::() }), StackedNodeState::Deleted => T::Codec::empty_node().to_vec(), } @@ -1254,7 +1255,7 @@ impl ProcessStack for BatchUpdate, C, D> } let encoded = node.into_encoded::<_, T::Codec, T::Hash>( |child, _o_slice, _o_index| { - child.as_child_ref::() + child.into_child_ref::() } ); if encoded.len() < ::LENGTH { @@ -1335,7 +1336,7 @@ impl ProcessStack for BatchUpdate, C, D> s@StackedNodeState::UnchangedAttached(..) | s@StackedNodeState::Unchanged(..) => { if !is_empty_node { - let hash = if let Some((not_inline, previous_prefix)) = prev_hash { + let hash = if let Some((not_inline, _previous_prefix)) = prev_hash { not_inline } else { let encoded = s.into_encoded(); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 76ba75ab..8945846a 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -42,24 +42,24 @@ pub(crate) struct StorageHandle(usize); // Handles to nodes in the trie. #[cfg_attr(feature = "std", derive(Debug))] -pub enum NodeHandle { +pub enum NodeHandleMut { /// Loaded into memory. InMemory(SH), /// Either a hash or an inline node Hash(H), } -impl, SH: AsRef<[u8]>> NodeHandle { +impl, SH: AsRef<[u8]>> NodeHandleMut { /// Get a node handle ref to this handle. pub(crate) fn as_ref(&self) -> crate::node::NodeHandle { match self { - NodeHandle::InMemory(sh) => crate::node::NodeHandle::Inline(sh.as_ref()), - NodeHandle::Hash(h) => crate::node::NodeHandle::Hash(h.as_ref()), + NodeHandleMut::InMemory(sh) => crate::node::NodeHandle::Inline(sh.as_ref()), + NodeHandleMut::Hash(h) => crate::node::NodeHandle::Hash(h.as_ref()), } } } -impl + Send + Copy, SH: AsRef<[u8]>> NodeHandle +impl + Send + Copy, SH: AsRef<[u8]>> NodeHandleMut where H: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy, @@ -67,33 +67,30 @@ where { /// Get a child reference (this has a cost but doing /// otherwhise requires changing codec trait). - pub(crate) fn as_child_ref

(&self) -> ChildReference + pub(crate) fn into_child_ref

(self) -> ChildReference where H2: Hasher { match self { - NodeHandle::InMemory(sh) => { + NodeHandleMut::InMemory(sh) => { let sh_ref = sh.as_ref(); - // TODO EMCH consider keeping the encoded buffer - // of node handle (need to apply everywhere). let mut h = H2::Out::default(); let len = sh_ref.len(); h.as_mut()[..len].copy_from_slice(&sh_ref[..len]); ChildReference::Inline(h, len) }, - NodeHandle::Hash(h) => ChildReference::Hash(h.clone()), + NodeHandleMut::Hash(h) => ChildReference::Hash(h), } } - } -impl From for NodeHandle { +impl From for NodeHandle { fn from(handle: StorageHandle) -> Self { NodeHandle::InMemory(handle) } } -fn empty_children() -> Box<[Option>; 16]> { +fn empty_children() -> Box<[Option>; 16]> { Box::new([ None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, @@ -104,9 +101,8 @@ fn empty_children() -> Box<[Option>; 16]> { /// therefore its left side is a full prefix. pub(crate) type NibbleFullKey<'key> = NibbleSlice<'key>; -// TODO EMCH make a local type alias with only H!! /// Node types in the Trie. -pub(crate) enum Node { +pub(crate) enum NodeMut { /// Empty node. Empty, /// A leaf node contains the end of a key and a value. @@ -117,17 +113,17 @@ pub(crate) enum 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(NodeKey, NodeHandle), + Extension(NodeKey, NodeHandleMut), /// A branch has up to 16 children and an optional value. - Branch(Box<[Option>; 16]>, Option), + Branch(Box<[Option>; 16]>, Option), /// Branch node with support for a nibble (to avoid extension node). - NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), + NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), } -impl Node { +impl NodeMut { /// Tell if it is the empty node. pub(crate) fn is_empty(&self) -> bool { - if let Node::Empty = &self { + if let NodeMut::Empty = &self { true } else { false @@ -137,11 +133,11 @@ impl Node { /// Get extension part of the node (partial) if any. pub(crate) fn partial(&self) -> Option { match self { - Node::Branch { .. } - | Node::Empty => None, - Node::NibbledBranch(partial, ..) - | Node::Extension(partial, ..) - | Node::Leaf(partial, ..) + NodeMut::Branch { .. } + | NodeMut::Empty => None, + NodeMut::NibbledBranch(partial, ..) + | NodeMut::Extension(partial, ..) + | NodeMut::Leaf(partial, ..) => Some(NibbleSlice::new_offset(&partial.1[..], partial.0)), } } @@ -149,11 +145,11 @@ impl Node { /// Advance partial offset if there is a partial. pub(crate) fn advance_partial(&mut self, nb: usize) { match self { - Node::Extension(..) - | Node::Branch ( .. ) - | Node::Empty => (), - Node::NibbledBranch(partial, ..) - | Node::Leaf(partial, ..) => { + NodeMut::Extension(..) + | NodeMut::Branch ( .. ) + | NodeMut::Empty => (), + NodeMut::NibbledBranch(partial, ..) + | NodeMut::Leaf(partial, ..) => { let mut new_partial: NibbleSlice = NibbleSlice::new_offset(partial.1.as_ref(), partial.0); new_partial.advance(nb); *partial = new_partial.into(); @@ -164,11 +160,11 @@ impl Node { /// Set partial if possible. pub(crate) fn set_partial(&mut self, new_partial: NodeKey) { match self { - Node::Branch ( .. ) - | Node::Empty => (), - Node::Extension(partial, ..) - | Node::NibbledBranch(partial, ..) - | Node::Leaf(partial, ..) => { + NodeMut::Branch ( .. ) + | NodeMut::Empty => (), + NodeMut::Extension(partial, ..) + | NodeMut::NibbledBranch(partial, ..) + | NodeMut::Leaf(partial, ..) => { *partial = new_partial; }, } @@ -177,23 +173,23 @@ impl Node { /// Set value to node if possible. pub(crate) fn has_value(&self) -> bool { match self { - Node::Extension(..) - | Node::Empty => false, - Node::Branch (_, val ) - | Node::NibbledBranch(_, _, val) => val.is_some(), - Node::Leaf(_, _) => true, + NodeMut::Extension(..) + | NodeMut::Empty => false, + NodeMut::Branch (_, val ) + | NodeMut::NibbledBranch(_, _, val) => val.is_some(), + NodeMut::Leaf(_, _) => true, } } /// Set value to node if possible. pub(crate) fn set_value(&mut self, value: &[u8]) { match self { - Node::Extension(..) - | Node::Empty => (), - Node::Branch ( _, val ) - | Node::NibbledBranch(_, _, val) + NodeMut::Extension(..) + | NodeMut::Empty => (), + NodeMut::Branch ( _, val ) + | NodeMut::NibbledBranch(_, _, val) => *val = Some(value.into()), - Node::Leaf(_, val) + NodeMut::Leaf(_, val) => *val = value.into(), } } @@ -201,17 +197,17 @@ impl Node { /// Return true if the node can be removed to. pub(crate) fn remove_value(&mut self) -> bool { match self { - Node::Extension(..) - | Node::Empty => false, - Node::Branch(encoded_children, val) - | Node::NibbledBranch(_, encoded_children, val) + NodeMut::Extension(..) + | NodeMut::Empty => false, + NodeMut::Branch(encoded_children, val) + | NodeMut::NibbledBranch(_, encoded_children, val) => { *val = None; // TODO store a boolean state to indicate the current // number of set children !encoded_children.iter().any(Option::is_some) }, - Node::Leaf(..) + NodeMut::Leaf(..) => true, } } @@ -220,28 +216,28 @@ impl Node { /// needed for trie with extension). pub(crate) fn set_handle( &mut self, - handle: Option>, + handle: Option>, index: u8, ) { let index = index as usize; - let node = mem::replace(self, Node::Empty); + let node = mem::replace(self, NodeMut::Empty); *self = match node { - Node::Extension(..) - | Node::Branch(..) => unreachable!("Only for no extension trie"), + NodeMut::Extension(..) + | NodeMut::Branch(..) => unreachable!("Only for no extension trie"), // need to replace value before creating handle // so it is a corner case TODO test case it // (may be unreachable since changing to empty means // we end the root process) - Node::Empty => unreachable!(), - Node::NibbledBranch(partial, encoded_children, val) + NodeMut::Empty => unreachable!(), + NodeMut::NibbledBranch(partial, encoded_children, val) if handle.is_none() && encoded_children[index].is_none() => { - Node::NibbledBranch(partial, encoded_children, val) + NodeMut::NibbledBranch(partial, encoded_children, val) }, - Node::NibbledBranch(partial, mut encoded_children, val) => { + NodeMut::NibbledBranch(partial, mut encoded_children, val) => { encoded_children[index] = handle; - Node::NibbledBranch(partial, encoded_children, val) + NodeMut::NibbledBranch(partial, encoded_children, val) }, - Node::Leaf(partial, val) if handle.is_some() => { + NodeMut::Leaf(partial, val) if handle.is_some() => { let mut children = Box::new([ None, None, None, None, None, None, None, None, @@ -250,11 +246,9 @@ impl Node { ]); children[index] = handle; - Node::NibbledBranch(partial, children, Some(val)) - }, - n@Node::Leaf(..) => { - n + NodeMut::NibbledBranch(partial, children, Some(val)) }, + n@NodeMut::Leaf(..) => n, }; } @@ -271,17 +265,17 @@ impl Node { &mut self, pending: (Option, Option), ) -> (bool, Option) { - let node = mem::replace(self, Node::Empty); // TODO EMCH rewrite to avoid this mem replace. + let node = mem::replace(self, NodeMut::Empty); // TODO EMCH rewrite to avoid this mem replace. let (node, fuse) = match node { - Node::Extension(..) - | Node::Branch(..) => unreachable!("Only for no extension trie"), + NodeMut::Extension(..) + | NodeMut::Branch(..) => unreachable!("Only for no extension trie"), // need to replace value before creating handle // so it is a corner case TODO test case it // (may be unreachable since changing to empty means // we end the root process) - n@Node::Empty - | n@Node::Leaf(..) => (n, None), - Node::NibbledBranch(partial, encoded_children, val) => { + n@NodeMut::Empty + | n@NodeMut::Leaf(..) => (n, None), + NodeMut::NibbledBranch(partial, encoded_children, val) => { let mut count = 0; let mut other_index = None; if let Some(pending) = pending.0 { @@ -305,20 +299,20 @@ impl Node { } } if val.is_some() && count == 0 { - (Node::Leaf(partial, val.expect("Tested above")), None) + (NodeMut::Leaf(partial, val.expect("Tested above")), None) } else if val.is_none() && count == 0 { - (Node::Empty, None) + (NodeMut::Empty, None) } else if val.is_none() && count == 1 { let child_ix = encoded_children.iter().position(Option::is_some) .unwrap_or_else(|| other_index.expect("counted above") as usize); - (Node::NibbledBranch(partial, encoded_children, val), Some(child_ix as u8)) + (NodeMut::NibbledBranch(partial, encoded_children, val), Some(child_ix as u8)) } else { - (Node::NibbledBranch(partial, encoded_children, val), None) + (NodeMut::NibbledBranch(partial, encoded_children, val), None) } }, }; *self = node; - (if let Node::Empty = self { + (if let NodeMut::Empty = self { true } else { false @@ -326,7 +320,7 @@ impl Node { } pub(crate) fn new_leaf(prefix: NibbleSlice, value: &[u8]) -> Self { - Node::Leaf(prefix.to_stored(), value.into()) + NodeMut::Leaf(prefix.to_stored(), value.into()) } // TODO rename to empty branch, branch placeholder or something @@ -338,19 +332,19 @@ impl Node { None, None, None, None, ]); - Node::NibbledBranch(prefix.to_stored(), children, None) + NodeMut::NibbledBranch(prefix.to_stored(), children, None) } } -impl, SH: AsRef<[u8]>> Node { +impl, SH: AsRef<[u8]>> NodeMut { /// Try to access child. pub fn child(&self, ix: u8) -> Option { match self { - Node::Leaf { .. } - | Node::Extension { .. } - | Node::Empty => None, - Node::NibbledBranch ( _, children, _ ) - | Node::Branch ( children, .. ) => + NodeMut::Leaf { .. } + | NodeMut::Extension { .. } + | NodeMut::Empty => None, + NodeMut::NibbledBranch ( _, children, _ ) + | NodeMut::Branch ( children, .. ) => children[ix as usize].as_ref().map(|child| child.as_ref()), } } @@ -370,7 +364,7 @@ impl<'a> Debug for ToHex<'a> { } #[cfg(feature = "std")] -impl Debug for Node { +impl Debug for NodeMut { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Self::Empty => write!(fmt, "Empty"), @@ -386,7 +380,12 @@ impl Debug for Node { } } -impl Node + +type NodeHandle = NodeHandleMut; + +type Node = NodeMut; + +impl Node where O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy @@ -397,7 +396,7 @@ impl Node child: EncodedNodeHandle, db: &dyn HashDB, storage: &mut NodeStorage - ) -> Result, H::Out, C::Error> + ) -> Result, H::Out, C::Error> where C: NodeCodec, H: Hasher, @@ -474,7 +473,7 @@ impl Node } } -impl Node +impl NodeMut where O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy @@ -539,17 +538,17 @@ where pub(crate) fn into_encoded(self, mut child_cb: F) -> Vec where C: NodeCodec, - F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, + F: FnMut(NodeHandleMut, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { match self { - Node::Empty => C::empty_node().to_vec(), - Node::Leaf(partial, value) => { + NodeMut::Empty => C::empty_node().to_vec(), + NodeMut::Leaf(partial, value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); // println!("d{:?} {:?}", pr.right(), value); C::leaf_node(pr.right(), &value) }, - Node::Extension(partial, child) => { + NodeMut::Extension(partial, child) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); let c = child_cb(child, Some(&pr), None); @@ -559,7 +558,7 @@ where c, ) }, - Node::Branch(mut children, value) => { + NodeMut::Branch(mut children, value) => { C::branch_node( // map the `NodeHandle`s from the Branch to `ChildReferences` children.iter_mut() @@ -571,7 +570,7 @@ where value.as_ref().map(|v| &v[..]) ) }, - Node::NibbledBranch(partial, mut children, value) => { + NodeMut::NibbledBranch(partial, mut children, value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); C::branch_node_nibbled( @@ -598,9 +597,9 @@ where // post-inspect action. enum Action { // Replace a node with a new one. - Replace(Node), + Replace(Node), // Restore the original node. This trusts that the node is actually the original. - Restore(Node), + Restore(Node), // if it is a new node, just clears the storage. Delete, } @@ -608,9 +607,9 @@ enum Action { // post-insert action. Same as action without delete enum InsertAction { // Replace a node with a new one. - Replace(Node), + Replace(Node), // Restore the original node. - Restore(Node), + Restore(Node), } impl InsertAction { @@ -622,7 +621,7 @@ impl InsertAction { } // unwrap the node, disregarding replace or restore state. - fn unwrap_node(self) -> Node { + fn unwrap_node(self) -> Node { match self { InsertAction::Replace(n) | InsertAction::Restore(n) => n, } @@ -632,9 +631,9 @@ impl InsertAction { // What kind of node is stored here. enum Stored { // A new node. - New(Node), + New(Node), // A cached node, loaded from the DB. - Cached(Node, H), + Cached(Node, H), } /// Used to build a collection of child nodes from a collection of `NodeHandle`s @@ -708,9 +707,9 @@ impl NodeStorage { } impl<'a, H> Index<&'a StorageHandle> for NodeStorage { - type Output = Node; + type Output = Node; - fn index(&self, handle: &'a StorageHandle) -> &Node { + fn index(&self, handle: &'a StorageHandle) -> &Node { match self.nodes[handle.0] { Stored::New(ref node) => node, Stored::Cached(ref node, _) => node, @@ -752,7 +751,7 @@ where storage: NodeStorage>, db: &'a mut dyn HashDB, root: &'a mut TrieHash, - root_handle: NodeHandle, StorageHandle>, + root_handle: NodeHandle>, death_row: HashSet<(TrieHash, (BackingByteVec, Option))>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. @@ -836,7 +835,7 @@ where where F: FnOnce( &mut Self, - Node, StorageHandle>, + Node>, &mut NibbleFullKey, ) -> Result>, TrieHash, CError>, { @@ -865,7 +864,7 @@ where fn lookup<'x, 'key>( &'x self, mut partial: NibbleSlice<'key>, - handle: &NodeHandle, StorageHandle>, + handle: &NodeHandle>, ) -> Result, TrieHash, CError> where 'x: 'key { @@ -930,7 +929,7 @@ where /// Insert a key-value pair into the trie, creating new nodes if necessary. fn insert_at( &mut self, - handle: NodeHandle, StorageHandle>, + handle: NodeHandle>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option, @@ -951,7 +950,7 @@ where /// The insertion inspector. fn insert_inspector( &mut self, - node: Node, StorageHandle>, + node: Node>, key: &mut NibbleFullKey, value: DBValue, old_val: &mut Option, @@ -1297,7 +1296,7 @@ where /// Removes a node from the trie based on key. fn remove_at( &mut self, - handle: NodeHandle, StorageHandle>, + handle: NodeHandle>, key: &mut NibbleFullKey, old_val: &mut Option, ) -> Result, TrieHash, CError> { @@ -1321,7 +1320,7 @@ where /// The removal inspector. fn remove_inspector( &mut self, - node: Node, StorageHandle>, + node: Node>, key: &mut NibbleFullKey, old_val: &mut Option, ) -> Result>, TrieHash, CError> { @@ -1502,9 +1501,9 @@ where /// - Extension node followed by anything other than a Branch node. fn fix( &mut self, - node: Node, StorageHandle>, + node: Node>, key: NibbleSlice, - ) -> Result, StorageHandle>, TrieHash, CError> { + ) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -1788,7 +1787,7 @@ where /// `into_encoded` method of `Node`. fn commit_child( &mut self, - handle: NodeHandle, StorageHandle>, + handle: NodeHandle>, prefix: &mut NibbleVec, ) -> ChildReference> { match handle { @@ -1829,7 +1828,7 @@ where } // a hack to get the root node's handle - fn root_handle(&self) -> NodeHandle, StorageHandle> { + fn root_handle(&self) -> NodeHandle> { match self.root_handle { NodeHandle::Hash(h) => NodeHandle::Hash(h), NodeHandle::InMemory(StorageHandle(x)) => NodeHandle::InMemory(StorageHandle(x)), @@ -2426,7 +2425,7 @@ pub(crate) mod tests { #[test] fn nice_debug_for_node() { use super::Node; - let e: Node = Node::Leaf((1, vec![1, 2, 3].into()), vec![4, 5, 6]); + let e: Node = Node::Leaf((1, vec![1, 2, 3].into()), vec![4, 5, 6]); assert_eq!(format!("{:?}", e), "Leaf((1, 010203), 040506)"); } From 8e89d2576d490e5822082b5b1bda6ec3b5987477 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 16:17:15 +0200 Subject: [PATCH 105/118] minor changes --- trie-db/src/lib.rs | 5 +++++ trie-db/src/node.rs | 6 +----- trie-db/src/traverse.rs | 5 ++--- trie-db/src/triedbmut.rs | 23 +++++++---------------- 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 67e2f5c2..1f1de66a 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -445,3 +445,8 @@ pub trait TrieConfiguration: Sized + TrieLayout { pub type TrieHash = <::Hash as Hasher>::Out; /// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. pub type CError = <::Codec as NodeCodec>::Error; + +/// This was only implemented for trie without extension, it could +/// be implemented for trie and extension in the future but is not +/// at this point. +const NO_EXTENSION_ONLY: &str = "trie without extension implemented only"; diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index 6e47c09f..7a112b51 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -13,17 +13,13 @@ // limitations under the License. use hash_db::Hasher; +use crate::NO_EXTENSION_ONLY; use crate::nibble::{self, NibbleSlice}; use crate::nibble::nibble_ops; use crate::node_codec::NodeCodec; use crate::rstd::{borrow::Borrow, ops::Range, boxed::Box, vec::Vec}; -/// This was only implemented for trie without extension, it could -/// be implemented for trie and extension in the future but is not -/// at this point. -const NO_EXTENSION_ONLY: &str = "trie without extension implemented only"; - /// Owned handle to a node, to use when there is no caching. pub type StorageHandle = Vec; diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 7dcb8b15..f9a352f9 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -248,15 +248,14 @@ impl StackedItem &key[..mid_index / nibble_ops::NIBBLE_PER_BYTE], self.item.depth_prefix, ); - Node::new_branch(new_slice) + Node::empty_branch(new_slice) } else { let new_slice = NibbleSlice::new_offset( &key[..], self.item.depth_prefix, ); let owned = new_slice.to_stored_range(mid_index - self.item.depth_prefix); - // TODO EMCH refactor new_leaf to take BackingByteVec (stored) as input - Node::new_branch(NibbleSlice::from_stored(&owned)) + Node::empty_branch(NibbleSlice::from_stored(&owned)) }; let old_depth = self.item.depth; diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 8945846a..c6ddd6d7 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -14,10 +14,10 @@ //! In-memory trie representation. -use super::{DBValue, node::NodeKey}; -use super::{Result, TrieError, TrieMut, TrieLayout, TrieHash, CError}; -use super::lookup::Lookup; -use super::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, decode_hash}; +use crate::{DBValue, node::NodeKey, NO_EXTENSION_ONLY}; +use crate::{Result, TrieError, TrieMut, TrieLayout, TrieHash, CError}; +use crate::lookup::Lookup; +use crate::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, decode_hash}; use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; use hashbrown::HashSet; @@ -223,12 +223,8 @@ impl NodeMut { let node = mem::replace(self, NodeMut::Empty); *self = match node { NodeMut::Extension(..) - | NodeMut::Branch(..) => unreachable!("Only for no extension trie"), - // need to replace value before creating handle - // so it is a corner case TODO test case it - // (may be unreachable since changing to empty means - // we end the root process) - NodeMut::Empty => unreachable!(), + | NodeMut::Branch(..) => unimplemented!("{}", NO_EXTENSION_ONLY), + NodeMut::Empty => unreachable!("Do not add handle to empty but replace the node instead"), NodeMut::NibbledBranch(partial, encoded_children, val) if handle.is_none() && encoded_children[index].is_none() => { NodeMut::NibbledBranch(partial, encoded_children, val) @@ -269,10 +265,6 @@ impl NodeMut { let (node, fuse) = match node { NodeMut::Extension(..) | NodeMut::Branch(..) => unreachable!("Only for no extension trie"), - // need to replace value before creating handle - // so it is a corner case TODO test case it - // (may be unreachable since changing to empty means - // we end the root process) n@NodeMut::Empty | n@NodeMut::Leaf(..) => (n, None), NodeMut::NibbledBranch(partial, encoded_children, val) => { @@ -323,8 +315,7 @@ impl NodeMut { NodeMut::Leaf(prefix.to_stored(), value.into()) } - // TODO rename to empty branch, branch placeholder or something - pub(crate) fn new_branch(prefix: NibbleSlice) -> Self { + pub(crate) fn empty_branch(prefix: NibbleSlice) -> Self { let children = Box::new([ None, None, None, None, None, None, None, None, From 12e4273389a83d6575512e1bf92dd5d0432d83bf Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 16:33:56 +0200 Subject: [PATCH 106/118] dropping some todos --- trie-db/src/triedbmut.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index c6ddd6d7..8e5204b3 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -203,8 +203,6 @@ impl NodeMut { | NodeMut::NibbledBranch(_, encoded_children, val) => { *val = None; - // TODO store a boolean state to indicate the current - // number of set children !encoded_children.iter().any(Option::is_some) }, NodeMut::Leaf(..) @@ -261,7 +259,7 @@ impl NodeMut { &mut self, pending: (Option, Option), ) -> (bool, Option) { - let node = mem::replace(self, NodeMut::Empty); // TODO EMCH rewrite to avoid this mem replace. + let node = mem::replace(self, NodeMut::Empty); let (node, fuse) = match node { NodeMut::Extension(..) | NodeMut::Branch(..) => unreachable!("Only for no extension trie"), @@ -282,7 +280,7 @@ impl NodeMut { other_index = Some(pending); } } - for c in encoded_children.iter() { // that is damn costy, a bitmap would be better TODO EMCH consider storing a bitmap + for c in encoded_children.iter() { if c.is_some() { count += 1; } From 06f4295ebdae33e723d07fa196c047cf2a1fa6b6 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 16:58:53 +0200 Subject: [PATCH 107/118] move fetch --- trie-db/src/traverse.rs | 94 ++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 54 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index f9a352f9..560b1053 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -198,10 +198,9 @@ impl StackedItem NodeHandle::Hash(handle_hash) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); - (StackedNodeState::Unchanged( - fetch::( + (StackedNodeState::fetch( db, &hash, prefix, - )?), Some((hash, owned_prefix(&prefix)))) + )?, Some((hash, owned_prefix(&prefix)))) }, NodeHandle::Inline(node_encoded) => { // Instantiating B is only for inline node, still costy. @@ -240,7 +239,8 @@ impl StackedItem >(&mut self, mid_index: usize, key: &[u8], callback: &mut F) { // // if self got split child, it can be processed // // (we went up and this is a next key) (ordering) - self.process_first_modified_child_then_split(key, callback); + self.process_first_modified_child(key, callback); + self.process_split_child(key, callback); // or it means we need to store key to // debug_assert!(self.split_child_index().is_none()); let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { @@ -305,9 +305,6 @@ impl StackedItem if let Some(child) = self.split_child.take() { // prefix is slice let mut build_prefix = NibbleVec::from(key, self.item.depth_prefix); - // TODO do same for first child (would need bench) -> here we - // should have a reusable nibblevec to avoid this allocation - // everywhere but would relieve a stored Vec !! self.item.node.partial().map(|p| build_prefix.append_partial(p.right())); build_prefix.push(child.parent_index); self.append_child(child, build_prefix.as_prefix(), callback); @@ -317,19 +314,6 @@ impl StackedItem fn process_first_modified_child< F: ProcessStack, >(&mut self, key: &[u8], callback: &mut F) { - self.process_first_modified_child_inner(key, callback, false) - } - - // TODO single call remove the function - fn process_first_modified_child_then_split< - F: ProcessStack, - >(&mut self, key: &[u8], callback: &mut F) { - self.process_first_modified_child_inner(key, callback, true) - } - - fn process_first_modified_child_inner< - F: ProcessStack, - >(&mut self, key: &[u8], callback: &mut F, always_split: bool) { if let Some(child) = self.first_modified_child.take() { if let Some(split_child_index) = self.split_child_index() { if split_child_index < child.parent_index { @@ -341,12 +325,6 @@ impl StackedItem self.item.node.partial().map(|p| build_prefix.append_partial(p.right())); build_prefix.push(child.parent_index); self.append_child(child, build_prefix.as_prefix(), callback); - - if always_split { - self.process_split_child(key.as_ref(), callback); - } - // TODO trie remove other unneeded assign. - //self.can_fuse = false; } } @@ -561,6 +539,32 @@ impl StackedNodeState StackedNodeState::Deleted => T::Codec::empty_node().to_vec(), } } + + /// Fetch by hash, no caching. + fn fetch( + db: &dyn HashDBRef, + hash: &TrieHash, + key: Prefix, + ) -> Result, CError> { + Ok(StackedNodeState::Unchanged( + Self::fetch_node(db, hash, key)? + )) + } + + /// Fetch a node by hash. + fn fetch_node( + db: &dyn HashDBRef, + hash: &TrieHash, + key: Prefix, + ) -> Result, TrieHash, CError> { + let node_encoded = db.get(hash, key) + .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(*hash)))?; + + Ok( + OwnedNode::new::(node_encoded) + .map_err(|e| Box::new(TrieError::DecoderError(*hash, e)))? + ) + } } /// Visitor trait to implement when using `trie_traverse_key`. @@ -579,7 +583,7 @@ trait ProcessStack stacked: &mut StackedItem, key_element: &[u8], action: InputAction<&[u8], &TrieHash>, - fetched_node: Option>, + to_attach_node: Option>, state: TraverseState, ) -> Option>; @@ -651,14 +655,12 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // Stack of traversed nodes let mut stack: smallvec::SmallVec<[StackedItem; 16]> = Default::default(); - let root = if let Ok(root) = fetch::(db, root_hash, EMPTY_PREFIX) { + let current = if let Ok(root) = StackedNodeState::fetch(db, root_hash, EMPTY_PREFIX) { root } else { return Err(Box::new(TrieError::InvalidStateRoot(*root_hash))); }; - // TODO encapsulate fetch in stacked item, also split or others - let current = StackedNodeState::::Unchanged(root); let depth = current.partial().map(|p| p.len()).unwrap_or(0); let mut current = StackedItem { item: StackedNode { @@ -723,11 +725,11 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( Some((key, InputAction::Attach(attach_root))) => { // TODO nooop on attach empty let root_prefix = (key.as_ref(), None); - let root_node = fetch::(db, &attach_root, root_prefix)?; - let depth = root_node.partial().map(|p| p.len()).unwrap_or(0); + let node = StackedNodeState::::fetch(db, &attach_root, root_prefix)?; + let depth = node.partial().map(|p| p.len()).unwrap_or(0); current = StackedItem { item: StackedNode { - node: StackedNodeState::Unchanged(root_node), + node, hash: None, depth_prefix: 0, depth, @@ -929,7 +931,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let fetch = if let Some(hash) = do_fetch { // This is fetching node to attach let root_prefix = (key.as_ref(), None); - let hash = fetch::(db, &hash, root_prefix)?; + let hash = StackedNodeState::<_, T>::fetch_node(db, &hash, root_prefix)?; Some(hash) } else { None @@ -951,21 +953,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( Ok(()) } -/// Fetch a node by hash, do not cache it. -fn fetch>( - db: &dyn HashDBRef, - hash: &TrieHash, - key: Prefix, -) -> Result, TrieHash, CError> { - let node_encoded = db.get(hash, key) - .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(*hash)))?; - - Ok( - OwnedNode::new::(node_encoded) - .map_err(|e| Box::new(TrieError::DecoderError(*hash, e)))? - ) -} - /// Contains ordered node change for this iteration. /// The resulting root hash. /// The latest changed node. @@ -987,7 +974,7 @@ impl ProcessStack for BatchUpdate, C, D> stacked: &mut StackedItem, key_element: &[u8], action: InputAction<&[u8], &TrieHash>, - fetched_node: Option>, // TODO EMCH try with unowned prefix or exit_detached with owned variant + to_attach_node: Option>, // TODO EMCH try with unowned prefix or exit_detached with owned variant state: TraverseState, ) -> Option> { match state { @@ -1002,7 +989,7 @@ impl ProcessStack for BatchUpdate, C, D> InputAction::Attach(attach_root) => { let prefix_nibble = NibbleSlice::new_offset(&key_element[..], stacked.item.depth_prefix); let prefix = prefix_nibble.left(); - let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. + let to_attach = to_attach_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); //if stacked.item.node.partial().map(|p| p.len()).unwrap_or(0) != 0 { if stacked.item.depth_prefix != stacked.item.depth { @@ -1091,7 +1078,7 @@ impl ProcessStack for BatchUpdate, C, D> 1 }; let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); - let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH mack action ref another type to ensure resolution. + let to_attach = to_attach_node.expect("Fetch node is always resolved"); // TODO EMCH mack action ref another type to ensure resolution. let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); let depth_prefix = stacked.item.depth + offset; match to_attach.partial() { @@ -1188,7 +1175,7 @@ impl ProcessStack for BatchUpdate, C, D> InputAction::Attach(attach_root) => { let prefix_nibble = NibbleSlice::new(&key_element[..]); let prefix = prefix_nibble.left(); - let to_attach = fetched_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. + let to_attach = to_attach_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); /*let (offset, parent_index) = if stacked.item.depth_prefix == 0 { // corner case of adding at top of trie @@ -1241,7 +1228,6 @@ impl ProcessStack for BatchUpdate, C, D> } }, } - } fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) From a09f42ae0a3f74335803de5b4793394daf510e9a Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 17:32:10 +0200 Subject: [PATCH 108/118] move fuse branch to item operation --- trie-db/src/traverse.rs | 181 ++++++++++++++++++--------------------- trie-db/src/triedbmut.rs | 15 ++-- 2 files changed, 90 insertions(+), 106 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 560b1053..39f5e64f 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -382,6 +382,34 @@ impl StackedItem self.first_modified_child = None; self.split_child = None; } + + fn fuse_node>( + &mut self, + key: &[u8], + db: &dyn HashDBRef, + callback: &mut F, + ) -> Result<(), TrieHash, CError> { + let first_modified_child_index = self.first_modified_child_index(); + // needed also to resolve + if let Some(fuse_index) = self.item.node.fuse_node((first_modified_child_index, self.split_child_index())) { + // try first child + if let Some(child) = self.take_first_modified_child() { + debug_assert!(child.item.parent_index == fuse_index); + let mut prefix = NibbleVec::from(key.as_ref(), self.item.depth); + prefix.push(fuse_index); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); + self.fuse_branch(child, prefix.inner(), callback); + } else { + let mut prefix = NibbleVec::from(key.as_ref(), self.item.depth); + prefix.push(fuse_index); + let child = self.descend_child(fuse_index, db, prefix.as_prefix())? + .expect("result of first child is define if consistent db"); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); + self.fuse_branch(child, prefix.inner(), callback); + } + } + Ok(()) + } } impl StackedNodeState @@ -511,14 +539,14 @@ impl StackedNodeState } } - /// Returns index of node to fuse with if fused require. - fn fix_node(&mut self, pending: (Option, Option)) -> Option { + /// Returns index of node to fuse if node was fused. + fn fuse_node(&mut self, pending: (Option, Option)) -> Option { match self { StackedNodeState::Deleted | StackedNodeState::UnchangedAttached(..) | StackedNodeState::Unchanged(..) => None, StackedNodeState::Changed(node) => { - let (deleted, fuse) = node.fix_node(pending); + let (deleted, fuse) = node.try_fuse_node(pending); if deleted { *self = StackedNodeState::Deleted; } @@ -652,6 +680,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, F: ProcessStack, { + // Stack of traversed nodes let mut stack: smallvec::SmallVec<[StackedItem; 16]> = Default::default(); @@ -687,8 +716,8 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // first remove deleted. if current.item.node.is_empty() { - current.process_first_modified_child(key.as_ref(), callback); // TODO is it of any use in a deleted node - current.process_split_child(key.as_ref(), callback); // TODO is it of any use?? (assert none instead??) + assert!(current.first_modified_child.is_none()); + assert!(current.split_child.is_none()); if let Some(mut parent) = stack.pop() { let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); parent.append_child(current.into(), prefix.left(), callback); @@ -723,28 +752,27 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( continue; }, Some((key, InputAction::Attach(attach_root))) => { - // TODO nooop on attach empty let root_prefix = (key.as_ref(), None); let node = StackedNodeState::::fetch(db, &attach_root, root_prefix)?; - let depth = node.partial().map(|p| p.len()).unwrap_or(0); - current = StackedItem { - item: StackedNode { - node, - hash: None, - depth_prefix: 0, - depth, - parent_index: 0, - }, - first_modified_child: None, - split_child: None, - can_fuse: true, - }; + if !node.is_empty() { + let depth = node.partial().map(|p| p.len()).unwrap_or(0); + current = StackedItem { + item: StackedNode { + node, + hash: None, + depth_prefix: 0, + depth, + parent_index: 0, + }, + first_modified_child: None, + split_child: None, + can_fuse: true, + }; + } continue; }, Some((_key, InputAction::Detach)) | Some((_key, InputAction::Delete)) => { - // TODO EMCH could also detach a empty trie root (way to match a serie of detach with - // their keys) continue; }, None => { @@ -768,25 +796,8 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } else { target_common_depth };*/ - let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO function for that to use - // needed also to resolve - if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { // TODO EMCH rename fix_node to need_fuse - // try first child - if let Some(child) = current.take_first_modified_child() { - debug_assert!(child.item.parent_index == fuse_index); - let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); - prefix.push(fuse_index); - child.item.node.partial().map(|p| prefix.append_partial(p.right())); - current.fuse_branch(child, prefix.inner(), callback); - } else { - let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); - prefix.push(fuse_index); - let child = current.descend_child(fuse_index, db, prefix.as_prefix())? - .expect("result of first child is define if consistent db"); // TODO EMCH Error here do!!! - child.item.node.partial().map(|p| prefix.append_partial(p.right())); - current.fuse_branch(child, prefix.inner(), callback); - } - } + + current.fuse_node(key.as_ref(), db, callback)?; // unstack nodes if needed while last || target_common_depth < current.item.depth_prefix { // TODO EMCH rename is_empty to is deleted @@ -842,27 +853,8 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( break; } } - let first_modified_child_index = current.first_modified_child.as_ref().map(|c| c.parent_index); // TODO there is a function for that - // needed also to resolve - if let Some(fuse_index) = current.item.node.fix_node((first_modified_child_index, current.split_child_index())) { - // try first child - if let Some(child) = current.take_first_modified_child() { - debug_assert!(child.item.parent_index == fuse_index); - let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); - prefix.push(fuse_index); - child.item.node.partial().map(|p| prefix.append_partial(p.right())); - current.fuse_branch(child, prefix.inner(), callback); - } else { - let mut prefix = NibbleVec::from(key.as_ref(), current.item.depth); - prefix.push(fuse_index); - let child = current.descend_child(fuse_index, db, prefix.as_prefix())? - .expect("result of first child is define if consistent db"); - child.item.node.partial().map(|p| prefix.append_partial(p.right())); - current.fuse_branch(child, prefix.inner(), callback); - } - // fuse child operation did switch current context. - continue; - } + + current.fuse_node(key.as_ref(), db, callback)?; } // no fix if middle then process buffed if target_common_depth < current.item.depth { @@ -1129,43 +1121,40 @@ impl ProcessStack for BatchUpdate, C, D> TraverseState::MidPartial(mid_index) => { match action { InputAction::Insert(value) => { - if stacked.item.node.is_empty() { - unreachable!(); + assert!(!stacked.item.node.is_empty()); + stacked.do_split_child(mid_index, key_element, self); + let (offset, parent_index) = if key_element.len() == 0 { + // corner case of adding at top of trie + (0, 0) } else { - stacked.do_split_child(mid_index, key_element, self); - let (offset, parent_index) = if key_element.len() == 0 { - // corner case of adding at top of trie - (0, 0) - } else { - // TODO not sure on index - (1, NibbleSlice::new(key_element).at(mid_index)) - }; - let child = Node::new_leaf( - // TODO not sure on '1 +' - NibbleSlice::new_offset(key_element, offset + mid_index), - value.as_ref(), - ); - return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { + // TODO not sure on index + (1, NibbleSlice::new(key_element).at(mid_index)) + }; + let child = Node::new_leaf( + // TODO not sure on '1 +' + NibbleSlice::new_offset(key_element, offset + mid_index), + value.as_ref(), + ); + return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { - // set value in new branch - stacked.item.node.set_value(value); - stacked.can_fuse = false; - None - } else { - let child = StackedItem { - item: StackedNode { - node: StackedNodeState::Changed(child), - hash: None, - depth_prefix: offset + mid_index, - depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, - parent_index, - }, - split_child: None, - first_modified_child: None, - can_fuse: false, - }; - Some(child) - } + // set value in new branch + stacked.item.node.set_value(value); + stacked.can_fuse = false; + None + } else { + let child = StackedItem { + item: StackedNode { + node: StackedNodeState::Changed(child), + hash: None, + depth_prefix: offset + mid_index, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + }, + split_child: None, + first_modified_child: None, + can_fuse: false, + }; + Some(child) } }, InputAction::Delete => { diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 8e5204b3..2fec9f45 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -246,16 +246,11 @@ impl NodeMut { }; } - /// Branch in invalid state should only be updated before write - /// to avoid multiple change of state. - /// Return true if the node can be removed, - /// and index of node to fuse with in case after removal of handle - /// a branch with a single child and no value remain. - /// This is only for no extension trie (a variant would be - /// needed for trie with extension). - /// Pending parameter indicates a node to be added or modified due to buffered - /// addition or split child). - pub(crate) fn fix_node( + /// Try to fuse a node, possibly changing a branch into a leaf. + /// If node was deleted, true is returned. + /// If node can be fused with a child, it is unchanged and the + /// child index is returned. + pub(crate) fn try_fuse_node( &mut self, pending: (Option, Option), ) -> (bool, Option) { From 25a4e015de0cb6d8ff774c58fabbf0fe7d8a5c39 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 17:35:31 +0200 Subject: [PATCH 109/118] move fuse branch to item operation --- trie-db/src/traverse.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 39f5e64f..4fdf6d48 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -791,25 +791,16 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( next.as_ref(), )).unwrap_or(0); // last element goes up to root -/* let target_common_depth = if current.node.is_empty() { - min(target_common_depth, current.depth_prefix) - } else { - target_common_depth - };*/ - current.fuse_node(key.as_ref(), db, callback)?; // unstack nodes if needed - while last || target_common_depth < current.item.depth_prefix { // TODO EMCH rename is_empty to is deleted - - // TODO check if fuse (num child is 1). + while last || target_common_depth < current.item.depth_prefix { // child change or addition if let Some(mut parent) = stack.pop() { if !parent.can_fuse { // process exit, as we already assert two child, no need to store in case of parent // fusing. // Deletion case is guaranted by ordering of input (fix delete only if no first - // and no split). TODO the number of calls to process first and split is wrong: - // should be once after fix_node only: that is especially for append_child case. + // and no split). current.process_first_modified_child(key.as_ref(), callback); current.process_split_child(key.as_ref(), callback); let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); @@ -831,9 +822,8 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if !parent.can_fuse { parent.process_child(current, key.as_ref(), callback); } else { - current.process_first_modified_child(key.as_ref(), callback); // TODO this is super confusing an assert no firt please - // split child is after first child (would be processed otherwhise). - current.process_split_child(key.as_ref(), callback); // TODO same + debug_assert!(current.first_modified_child.is_none()); + debug_assert!(current.split_child.is_none()); // first node visited on a fusable element, store in parent first child and process later. // Process an eventual split child (index after current). parent.first_modified_child = Some(current.into()); From 1b7c315e714ac3f627b36df2096991a38d179965 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 17:37:42 +0200 Subject: [PATCH 110/118] move fuse branch to item operation --- trie-db/src/traverse.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 4fdf6d48..bcda8fbf 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -822,8 +822,10 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( if !parent.can_fuse { parent.process_child(current, key.as_ref(), callback); } else { - debug_assert!(current.first_modified_child.is_none()); - debug_assert!(current.split_child.is_none()); + current.process_first_modified_child(key.as_ref(), callback); + // split child is after first child (would be processed otherwhise), so no need to + // order the two instructions. + current.process_split_child(key.as_ref(), callback); // first node visited on a fusable element, store in parent first child and process later. // Process an eventual split child (index after current). parent.first_modified_child = Some(current.into()); From fd29a061475c7b5e0d603e2c6d7cbce2a0f42aae Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 17:37:52 +0200 Subject: [PATCH 111/118] move fuse branch to item operation --- trie-db/src/traverse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index bcda8fbf..f58d1e60 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -829,7 +829,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // first node visited on a fusable element, store in parent first child and process later. // Process an eventual split child (index after current). parent.first_modified_child = Some(current.into()); -// debug_assert!(parent.split_child.is_none()); + debug_assert!(parent.split_child.is_none()); } } current = parent; From e89f0317b1dbecaba1a6c8faf9c843f4587ddb4b Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 17:38:03 +0200 Subject: [PATCH 112/118] move fuse branch to item operation --- trie-db/src/traverse.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index f58d1e60..d612a4e8 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -829,7 +829,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( // first node visited on a fusable element, store in parent first child and process later. // Process an eventual split child (index after current). parent.first_modified_child = Some(current.into()); - debug_assert!(parent.split_child.is_none()); } } current = parent; From 30722d01df2efcc673f060ce4914c4605435df6f Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 18:13:44 +0200 Subject: [PATCH 113/118] split payload for attach content to be able to preserve ordering --- test-support/reference-trie/src/lib.rs | 4 +- trie-db/fuzz/fuzz_targets/batch_update.rs | 4 +- trie-db/fuzz/src/lib.rs | 10 +- trie-db/src/traverse.rs | 135 +++++++++------------- 4 files changed, 61 insertions(+), 92 deletions(-) diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 2738e6ca..23b09ff3 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1211,8 +1211,8 @@ pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( } else { InputAction::Delete })); - let (root, values, detached_root) = batch_update::(db, root, elements).unwrap(); - (root, values.into_iter(), detached_root.into_iter()) + let (root, values, values_detached, detached_root) = batch_update::(db, root, elements).unwrap(); + (root, values.into_iter().chain(values_detached.into_iter()), detached_root.into_iter()) } #[cfg(test)] diff --git a/trie-db/fuzz/fuzz_targets/batch_update.rs b/trie-db/fuzz/fuzz_targets/batch_update.rs index 185e631c..0f5ef521 100644 --- a/trie-db/fuzz/fuzz_targets/batch_update.rs +++ b/trie-db/fuzz/fuzz_targets/batch_update.rs @@ -4,7 +4,7 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // use next line to favor complex structure and inline data - // trie_db_fuzz::fuzz_batch_update(data, |_v| (), false); + trie_db_fuzz::fuzz_batch_update(data, |_v| (), false); // use next line to favor db prefix verification - trie_db_fuzz::fuzz_batch_update(data, |v| v.extend(&[4u8; 32]), true); + //trie_db_fuzz::fuzz_batch_update(data, |v| v.extend(&[4u8; 32]), true); }); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index ef5ea4bf..e1d97852 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -464,7 +464,7 @@ pub fn fuzz_detach_attach(input: &[u8], build_val: fn(&mut Vec), compare_db: } } let elements = Some(d.clone()).into_iter().map(|k| (k, InputAction::, _>::Detach)); - let (calc_root, payload, detached_root) = batch_update::( + let (calc_root, payload, payload_det, detached_root) = batch_update::( &initial_db, &initial_root, elements, @@ -473,7 +473,7 @@ pub fn fuzz_detach_attach(input: &[u8], build_val: fn(&mut Vec), compare_db: assert_eq!(calc_root, root); let mut batch_delta = initial_db.clone(); - for (p, h, v) in payload { + for (p, h, v) in payload.into_iter().chain(payload_det) { use hash_db::HashDB; if let Some(v) = v { let prefix = (p.0.as_ref(), p.1); @@ -486,14 +486,14 @@ pub fn fuzz_detach_attach(input: &[u8], build_val: fn(&mut Vec), compare_db: // attach back let elements = detached_root.into_iter().map(|(k, _prefix, root)| (k, InputAction::, _>::Attach(root))); - let (calc_root, payload, detached_root) = batch_update::( + let (calc_root, payload, payload_det, detached_root) = batch_update::( &batch_delta, &calc_root, elements, ).unwrap(); if detached_root.is_empty() { if compare_db { - for (p, h, v) in payload { + for (p, h, v) in payload.into_iter().chain(payload_det) { use hash_db::HashDB; if let Some(v) = v { let prefix = (p.0.as_ref(), p.1); @@ -509,7 +509,7 @@ pub fn fuzz_detach_attach(input: &[u8], build_val: fn(&mut Vec), compare_db: assert!(calc_root == initial_root); } else { // case where there was a node fuse due to dettach - // TODO inject manually detached node and compare + // we could inject manually detached node and compare } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index d612a4e8..7b607e1d 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -847,20 +847,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( current.fuse_node(key.as_ref(), db, callback)?; } - // no fix if middle then process buffed - if target_common_depth < current.item.depth { - // TODO this can probably remove a lot of those calls TODO check especially - // calls in going down path at split child. - current.process_first_modified_child(key.as_ref(), callback); - current.process_split_child(key.as_ref(), callback) - } - -/* if !current.test_can_fuse(key.as_ref(), None) { - current.process_first_modified_child(callback); - if target_common_depth < current.depth { - current.process_split_child(key.as_ref(), callback); - } - }*/ } if skip_down { @@ -872,43 +858,39 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let dest_slice = NibbleFullKey::new(key.as_ref()); let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; let mut descend_mid_index = None; - if !current.item.node.is_empty() { - // corner case do not descend in empty node (else condition) TODO covered by empty_trie?? - loop { - // TODO check if first common index is simple target_common? of previous go up. - let common_index = current.item.node.partial() - .map(|current_partial| { - let target_partial = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); - current.item.depth_prefix + current_partial.common_prefix(&target_partial) - }).unwrap_or(current.item.depth_prefix); - // TODO not sure >= or just >. - if common_index == current.item.depth && dest_depth > current.item.depth { - let next_index = dest_slice.at(current.item.depth); - let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth + 1); - if let Some(child) = current.descend_child(next_index, db, prefix.left())? { - stack.push(current); - current = child; - } else { - break; - } + loop { + let common_index = current.item.node.partial() + .map(|current_partial| { + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); + current.item.depth_prefix + current_partial.common_prefix(&target_partial) + }).unwrap_or(current.item.depth_prefix); + if common_index == current.item.depth && dest_depth > current.item.depth { + let next_index = dest_slice.at(current.item.depth); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth + 1); + if let Some(child) = current.descend_child(next_index, db, prefix.left())? { + stack.push(current); + current = child; } else { - if common_index < current.item.depth { - descend_mid_index = Some(common_index); - } break; } + } else { + if common_index < current.item.depth { + descend_mid_index = Some(common_index); + } + break; } } let traverse_state = if let Some(mid_index) = descend_mid_index { TraverseState::MidPartial(mid_index) - } else if dest_depth < current.item.depth { - unreachable!(); - } else if dest_depth > current.item.depth { - // over callback - TraverseState::AfterNode } else { - // value replace callback - TraverseState::ValueMatch + debug_assert!(dest_depth >= current.item.depth); + if dest_depth > current.item.depth { + // over callback + TraverseState::AfterNode + } else { + // value replace callback + TraverseState::ValueMatch + } }; let (value, do_fetch) = value.as_ref(); let fetch = if let Some(hash) = do_fetch { @@ -939,17 +921,19 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( /// Contains ordered node change for this iteration. /// The resulting root hash. /// The latest changed node. -struct BatchUpdate { +struct BatchUpdate { register_update: C, + register_update_attach_detach: CD, register_detached: D, root: H, } -impl ProcessStack for BatchUpdate, C, D> +impl ProcessStack for BatchUpdate, C, CD, D> where B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, T: TrieLayout, C: FnMut((OwnedPrefix, TrieHash, Option>)), + CD: FnMut((OwnedPrefix, TrieHash, Option>)), D: FnMut((Vec, OwnedPrefix, TrieHash)), { fn enter_terminal( @@ -957,7 +941,7 @@ impl ProcessStack for BatchUpdate, C, D> stacked: &mut StackedItem, key_element: &[u8], action: InputAction<&[u8], &TrieHash>, - to_attach_node: Option>, // TODO EMCH try with unowned prefix or exit_detached with owned variant + to_attach_node: Option>, state: TraverseState, ) -> Option> { match state { @@ -972,9 +956,9 @@ impl ProcessStack for BatchUpdate, C, D> InputAction::Attach(attach_root) => { let prefix_nibble = NibbleSlice::new_offset(&key_element[..], stacked.item.depth_prefix); let prefix = prefix_nibble.left(); - let to_attach = to_attach_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. + let to_attach = to_attach_node + .expect("Attachment resolution must be done before calling this function"); let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); - //if stacked.item.node.partial().map(|p| p.len()).unwrap_or(0) != 0 { if stacked.item.depth_prefix != stacked.item.depth { match to_attach.partial() { Some(partial) if partial.len() > 0 => { @@ -1016,11 +1000,7 @@ impl ProcessStack for BatchUpdate, C, D> match action { InputAction::Insert(val) => { // corner case of empty trie. - let offset = if stacked.item.node.is_empty() { - 0 - } else { - 1 - }; + let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; // dest is a leaf appended to terminal let dest_leaf = Node::new_leaf( NibbleSlice::new_offset(key_element, stacked.item.depth + offset), @@ -1054,14 +1034,10 @@ impl ProcessStack for BatchUpdate, C, D> None }, InputAction::Attach(attach_root) => { - // TODO factor with insert - let offset = if stacked.item.node.is_empty() { - 0 - } else { - 1 - }; + let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); - let to_attach = to_attach_node.expect("Fetch node is always resolved"); // TODO EMCH mack action ref another type to ensure resolution. + let to_attach = to_attach_node + .expect("Attachment resolution must be done before calling this function"); let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); let depth_prefix = stacked.item.depth + offset; match to_attach.partial() { @@ -1077,8 +1053,6 @@ impl ProcessStack for BatchUpdate, C, D> }; let depth = depth_prefix + to_attach.partial().map(|p| p.len()).unwrap_or(0); - //let prefix_nibble = NibbleSlice::new_offset(&key_element[..], depth_prefix); - //let prefix = prefix_nibble.left(); let mut new_child = StackedItem { item: StackedNode { node: to_attach, @@ -1118,11 +1092,9 @@ impl ProcessStack for BatchUpdate, C, D> // corner case of adding at top of trie (0, 0) } else { - // TODO not sure on index (1, NibbleSlice::new(key_element).at(mid_index)) }; let child = Node::new_leaf( - // TODO not sure on '1 +' NibbleSlice::new_offset(key_element, offset + mid_index), value.as_ref(), ); @@ -1155,14 +1127,9 @@ impl ProcessStack for BatchUpdate, C, D> InputAction::Attach(attach_root) => { let prefix_nibble = NibbleSlice::new(&key_element[..]); let prefix = prefix_nibble.left(); - let to_attach = to_attach_node.expect("Fetch node is always resolved"); // TODO EMCH make action ref another type to ensure resolution. + let to_attach = to_attach_node + .expect("Attachment resolution must be done before calling this function"); let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); - /*let (offset, parent_index) = if stacked.item.depth_prefix == 0 { - // corner case of adding at top of trie - (0, 0) - } else { - (1, NibbleSlice::new(key_element).at(mid_index)) - };*/ match to_attach.partial() { Some(partial) if partial.len() > 0 => { let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix).into(); @@ -1193,7 +1160,7 @@ impl ProcessStack for BatchUpdate, C, D> if mid_index == NibbleSlice::new(key_element).len() { // on a path do a switch if stacked.item.node.is_empty() { - unreachable!("we should not iterate in middle of an empty; this needs fix"); + unreachable!("we should not iterate in middle of an empty; this is a bug"); } let prefix_nibble = NibbleSlice::new(&key_element[..]); let prefix = prefix_nibble.left(); @@ -1280,7 +1247,7 @@ impl ProcessStack for BatchUpdate, C, D> let detached_prefix = (key_element, None); let is_empty_node = stacked.is_empty(); - let register_up = &mut self.register_update; + let register_up = &mut self.register_update_attach_detach; let register = &mut self.register_detached; match stacked { s@StackedNodeState::Deleted @@ -1292,8 +1259,6 @@ impl ProcessStack for BatchUpdate, C, D> if !is_empty_node { let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); - // Note that those updates are messing the updates ordering, could be better to register - // them with the detached item (TODO would need a dedicated struct). register_up((owned_prefix(&detached_prefix), hash.clone(), Some(encoded))); register((key_element.to_vec(), owned_prefix(&prefix), hash)); } @@ -1334,6 +1299,7 @@ pub fn batch_update<'a, T, I, K, V, B>( ) -> Result<( TrieHash, Vec<(OwnedPrefix, TrieHash, Option>)>, + Vec<(OwnedPrefix, TrieHash, Option>)>, Vec<(Vec, OwnedPrefix, TrieHash)>, ),TrieHash, CError> where @@ -1344,18 +1310,22 @@ pub fn batch_update<'a, T, I, K, V, B>( B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, { let mut dest = Vec::new(); + let mut dest2 = Vec::new(); let mut dest_detached = Vec::new(); let mut batch_update = BatchUpdate { register_update: |update| { dest.push(update) }, + register_update_attach_detach: |update| { + dest2.push(update) + }, register_detached: |update| { dest_detached.push(update) }, root: root_hash.clone(), }; trie_traverse_key::(db, root_hash, elements, &mut batch_update)?; - Ok((batch_update.root, dest, dest_detached)) + Ok((batch_update.root, dest, dest2, dest_detached)) } @@ -1648,7 +1618,7 @@ mod tests { println!("aft {:?}", t); } let elements = Some(d.clone()).into_iter().map(|k| (k, InputAction::, _>::Detach)); - let (calc_root, payload, detached_root) = batch_update::( + let (calc_root, payload, payload_detached, detached_root) = batch_update::( &initial_db, &initial_root, elements, @@ -1657,7 +1627,8 @@ mod tests { assert_eq!(calc_root, root); let mut batch_delta = initial_db.clone(); - memory_db_from_delta(payload.into_iter(), &mut batch_delta, false); + memory_db_from_delta(payload.into_iter(), &mut batch_delta, true); + memory_db_from_delta(payload_detached.into_iter(), &mut batch_delta, false); // test by checking both triedb only let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); println!("{:?}", t2); @@ -1683,19 +1654,17 @@ mod tests { println!("{:?}", db.clone().drain()); println!("{:?}", batch_delta.clone().drain()); - // TODO this cannot be test because we have attached content - // in db TODO split batches of update (then we could compare ordering again). - //assert!(db == batch_delta); // attach back let elements = detached_root.into_iter().map(|(k, _prefix, root)| (k, InputAction::, _>::Attach(root))); - let (calc_root, payload, detached_root) = batch_update::( + let (calc_root, payload, payload_detached, detached_root) = batch_update::( &batch_delta, &calc_root, elements, ).unwrap(); //assert!(detached_root.is_empty()); - memory_db_from_delta(payload.into_iter(), &mut batch_delta, false); + memory_db_from_delta(payload.into_iter(), &mut batch_delta, true); + memory_db_from_delta(payload_detached.into_iter(), &mut batch_delta, false); // test by checking both triedb only let t2 = RefTrieDBNoExt::new(&initial_db, &initial_root).unwrap(); println!("{:?}", t2); From 190f8e1b78527bda0eb0f8cc20bb2165cd25d770 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 18:26:27 +0200 Subject: [PATCH 114/118] removing special condition --- trie-db/src/traverse.rs | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 7b607e1d..916c98e5 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -60,21 +60,7 @@ enum StackedNodeState /// Deleted node. Deleted, } -/* -impl StackedNodeState - where - B: Borrow<[u8]>, - T: TrieLayout, -{ - fn is_deleted(&self) -> bool { - if let StackedNodeState::Deleted = self { - true - } else { - false - } - } -} -*/ + /// Item on stack it contains updatable traverse /// specific field to manage update that split /// partial from a node, and for buffering the first @@ -118,7 +104,7 @@ struct StackedNode /// Internal node representation. node: StackedNodeState, /// Hash and prefix used to access this node, for inline node (except root) and - /// new nodes this is None. + /// new nodes this is None. TODO EMCH rename hash: Option<(TrieHash, OwnedPrefix)>, /// Index of prefix. depth_prefix: usize, @@ -708,13 +694,12 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( for next_query in elements.into_iter().map(|e| Some(e)).chain(Some(None)) { - let mut skip_down = false; let last = next_query.is_none(); // PATH UP over the previous key and value if let Some(key) = previous_key.as_ref() { - // first remove deleted. + // Pop deleted (empty) nodes. if current.item.node.is_empty() { assert!(current.first_modified_child.is_none()); assert!(current.split_child.is_none()); @@ -723,8 +708,8 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( parent.append_child(current.into(), prefix.left(), callback); current = parent; } else { - // empty trie, next additional value is therefore a leaf - // and delete this one (done in append_child otherwhise) + // Empty trie, next additional value is therefore a leaf. + // Also delete this entry (could have been something else than empty before). debug_assert!(current.item.depth_prefix == 0); match next_query.as_ref() { Some((key, InputAction::Insert(value))) => { @@ -840,7 +825,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( return Ok(()); } else { // move to next key - skip_down = true; break; } } @@ -849,10 +833,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } } - if skip_down { - continue; - } - // PATH DOWN descending in next_query. if let Some((key, value)) = next_query { let dest_slice = NibbleFullKey::new(key.as_ref()); From 5431bfe0d8742e7e8dfa885f92d43a3fcab9a468 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 18:47:56 +0200 Subject: [PATCH 115/118] skip a iter --- trie-db/src/traverse.rs | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 916c98e5..5a59eab7 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -159,7 +159,7 @@ impl StackedItem None } } - + fn descend_child(&mut self, index: u8, db: &dyn HashDBRef, prefix: Prefix) -> Result< Option>, TrieHash, @@ -374,10 +374,11 @@ impl StackedItem key: &[u8], db: &dyn HashDBRef, callback: &mut F, - ) -> Result<(), TrieHash, CError> { + ) -> Result, CError> { let first_modified_child_index = self.first_modified_child_index(); + let (deleted, fuse_index) = self.item.node.fuse_node((first_modified_child_index, self.split_child_index())); // needed also to resolve - if let Some(fuse_index) = self.item.node.fuse_node((first_modified_child_index, self.split_child_index())) { + if let Some(fuse_index) = fuse_index { // try first child if let Some(child) = self.take_first_modified_child() { debug_assert!(child.item.parent_index == fuse_index); @@ -393,8 +394,10 @@ impl StackedItem child.item.node.partial().map(|p| prefix.append_partial(p.right())); self.fuse_branch(child, prefix.inner(), callback); } + Ok(true) + } else { + Ok(deleted) } - Ok(()) } } @@ -526,17 +529,17 @@ impl StackedNodeState } /// Returns index of node to fuse if node was fused. - fn fuse_node(&mut self, pending: (Option, Option)) -> Option { + fn fuse_node(&mut self, pending: (Option, Option)) -> (bool, Option) { match self { StackedNodeState::Deleted | StackedNodeState::UnchangedAttached(..) - | StackedNodeState::Unchanged(..) => None, + | StackedNodeState::Unchanged(..) => (false, None), StackedNodeState::Changed(node) => { let (deleted, fuse) = node.try_fuse_node(pending); if deleted { *self = StackedNodeState::Deleted; } - fuse + (deleted, fuse) }, } } @@ -777,6 +780,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( )).unwrap_or(0); // last element goes up to root current.fuse_node(key.as_ref(), db, callback)?; + // unstack nodes if needed while last || target_common_depth < current.item.depth_prefix { // child change or addition @@ -829,7 +833,10 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( } } - current.fuse_node(key.as_ref(), db, callback)?; + if current.fuse_node(key.as_ref(), db, callback)? { + // no need to try to go down when fuse succeed + continue; + } } } @@ -838,6 +845,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let dest_slice = NibbleFullKey::new(key.as_ref()); let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; let mut descend_mid_index = None; + loop { let common_index = current.item.node.partial() .map(|current_partial| { @@ -980,7 +988,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> match action { InputAction::Insert(val) => { // corner case of empty trie. - let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; + let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; // dest is a leaf appended to terminal let dest_leaf = Node::new_leaf( NibbleSlice::new_offset(key_element, stacked.item.depth + offset), @@ -1014,7 +1022,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> None }, InputAction::Attach(attach_root) => { - let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; + let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); let to_attach = to_attach_node .expect("Attachment resolution must be done before calling this function"); @@ -1129,7 +1137,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> let detached = mem::replace(&mut stacked.item.node, to_attach); //detached.advance_partial(mid_index); let detached_hash = mem::replace( - &mut stacked.item.hash, + &mut stacked.item.hash, Some((attach_root.clone(), owned_prefix(&(key_element, None)))), ); stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); @@ -1199,7 +1207,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> _ => None, } } - + fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { let prefix = EMPTY_PREFIX; let register = &mut self.register_update; @@ -1262,12 +1270,12 @@ impl ProcessStack for BatchUpdate, C, CD, D> } -fn owned_prefix(prefix: &Prefix) -> (BackingByteVec, Option) { +fn owned_prefix(prefix: &Prefix) -> (BackingByteVec, Option) { (prefix.0.into(), prefix.1) } /// Extract prefix from a owned prefix. -pub fn from_owned_prefix(prefix: &OwnedPrefix) -> Prefix { +pub fn from_owned_prefix(prefix: &OwnedPrefix) -> Prefix { (&prefix.0[..], prefix.1) } From 0bd5c2a3f5a8517a4b00847a5bff4cca18bc77d7 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 19:17:23 +0200 Subject: [PATCH 116/118] refact condition --- trie-db/src/traverse.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 5a59eab7..af4cb8c6 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -845,7 +845,6 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let dest_slice = NibbleFullKey::new(key.as_ref()); let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; let mut descend_mid_index = None; - loop { let common_index = current.item.node.partial() .map(|current_partial| { @@ -868,18 +867,18 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( break; } } + let traverse_state = if let Some(mid_index) = descend_mid_index { TraverseState::MidPartial(mid_index) + } else if dest_depth > current.item.depth { + // over callback + TraverseState::AfterNode } else { - debug_assert!(dest_depth >= current.item.depth); - if dest_depth > current.item.depth { - // over callback - TraverseState::AfterNode - } else { - // value replace callback - TraverseState::ValueMatch - } + debug_assert!(dest_depth == current.item.depth); + // value replace callback + TraverseState::ValueMatch }; + let (value, do_fetch) = value.as_ref(); let fetch = if let Some(hash) = do_fetch { // This is fetching node to attach From 379bf92720e23b2465badd4739c7d2ba75115c06 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 11 May 2020 19:32:58 +0200 Subject: [PATCH 117/118] renaming --- trie-db/src/traverse.rs | 84 ++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index af4cb8c6..a42c5627 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -104,8 +104,8 @@ struct StackedNode /// Internal node representation. node: StackedNodeState, /// Hash and prefix used to access this node, for inline node (except root) and - /// new nodes this is None. TODO EMCH rename - hash: Option<(TrieHash, OwnedPrefix)>, + /// new nodes this is None. + previous_db_handle: Option<(TrieHash, OwnedPrefix)>, /// Index of prefix. depth_prefix: usize, /// Depth of node, it is prefix depth and partial depth. @@ -180,7 +180,7 @@ impl StackedItem } else { if let Some(node_handle) = self.item.node.child(index) { - let (node, hash) = match node_handle { + let (node, previous_db_handle) = match node_handle { NodeHandle::Hash(handle_hash) => { let mut hash = as Default>::default(); hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); @@ -193,7 +193,7 @@ impl StackedItem (StackedNodeState::Unchanged( OwnedNode::new::(B::from(node_encoded)) .map_err(|e| Box::new(TrieError::DecoderError( - self.item.hash.clone().map(|i| i.0).unwrap_or_else(Default::default), + self.item.previous_db_handle.clone().map(|i| i.0).unwrap_or_else(Default::default), e, )))? ), None) @@ -204,7 +204,7 @@ impl StackedItem Some(StackedItem { item: StackedNode { node, - hash, + previous_db_handle, parent_index: index, depth_prefix, depth, @@ -264,7 +264,7 @@ impl StackedItem // not setting child relation (will be set on exit) let child = StackedNode { node: child, - hash: None, + previous_db_handle: None, depth_prefix: 1 + mid_index, depth: old_depth, parent_index, @@ -279,7 +279,7 @@ impl StackedItem if let Some(handle) = callback.exit( prefix, child.node, - child.hash, + child.previous_db_handle, ) { self.item.node.set_handle(handle, child.parent_index); } @@ -344,7 +344,7 @@ impl StackedItem self.process_split_child(key, callback); callback.exit_root( self.item.node, - self.item.hash, + self.item.previous_db_handle, ) } @@ -357,7 +357,7 @@ impl StackedItem // delete child callback.exit( EMPTY_PREFIX, - StackedNodeState::Deleted, child.item.hash.take(), + StackedNodeState::Deleted, child.item.previous_db_handle.take(), ).expect("no new node on empty"); let partial = NibbleSlice::new_offset(key, self.item.depth_prefix) @@ -605,12 +605,12 @@ trait ProcessStack ) -> Option>; /// Callback on exit a node, commit action on change node should be applied here. - fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>) -> Option>>>; /// Callback on a detached node. - fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>); + fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>); /// Same as `exit` but for root (very last exit call). - fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>); + fn exit_root(&mut self, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>); } /// State when descending @@ -683,7 +683,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( let mut current = StackedItem { item: StackedNode { node: current, - hash: Some((*root_hash, owned_prefix(&EMPTY_PREFIX))), + previous_db_handle: Some((*root_hash, owned_prefix(&EMPTY_PREFIX))), depth_prefix: 0, depth, parent_index: 0, @@ -719,7 +719,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( callback.exit( EMPTY_PREFIX, current.item.node, - current.item.hash, + current.item.previous_db_handle, ); let leaf = Node::new_leaf( NibbleSlice::new(key.as_ref()), @@ -728,7 +728,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( current = StackedItem { item: StackedNode { node: StackedNodeState::Changed(leaf), - hash: None, + previous_db_handle: None, depth_prefix: 0, depth: key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index: 0, @@ -747,7 +747,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( current = StackedItem { item: StackedNode { node, - hash: None, + previous_db_handle: None, depth_prefix: 0, depth, parent_index: 0, @@ -766,7 +766,7 @@ fn trie_traverse_key<'a, T, I, K, V, B, F>( None => { callback.exit_root( current.item.node, - current.item.hash, + current.item.previous_db_handle, ); return Ok(()); }, @@ -962,12 +962,12 @@ impl ProcessStack for BatchUpdate, C, CD, D> stacked.item.node.advance_partial(stacked.item.depth - stacked.item.depth_prefix); } let detached = mem::replace(&mut stacked.item.node, to_attach); - let detached_hash = mem::replace( - &mut stacked.item.hash, + let detached_db = mem::replace( + &mut stacked.item.previous_db_handle, Some((attach_root.clone(), owned_prefix(&(key_element, None)))), ); stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); - self.exit_detached(key_element, prefix, detached, detached_hash); + self.exit_detached(key_element, prefix, detached, detached_db); }, InputAction::Detach => { let prefix_nibble = NibbleSlice::new_offset(&key_element[..], stacked.item.depth_prefix); @@ -977,8 +977,8 @@ impl ProcessStack for BatchUpdate, C, CD, D> stacked.item.node.advance_partial(stacked.item.depth - stacked.item.depth_prefix); } let detached = mem::replace(&mut stacked.item.node, to_attach); - let detached_hash = mem::replace(&mut stacked.item.hash, None); - self.exit_detached(key_element, prefix, detached, detached_hash); + let detached_db = mem::replace(&mut stacked.item.previous_db_handle, None); + self.exit_detached(key_element, prefix, detached, detached_db); }, } None @@ -997,7 +997,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> let mut new_child = StackedItem { item: StackedNode { node: StackedNodeState::Changed(dest_leaf), - hash: None, + previous_db_handle: None, depth_prefix: stacked.item.depth + offset, depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, @@ -1008,7 +1008,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> }; return if stacked.item.node.is_empty() { // replace empty. - new_child.item.hash = stacked.item.hash.take(); + new_child.item.previous_db_handle = stacked.item.previous_db_handle.take(); *stacked = new_child; None } else { @@ -1044,7 +1044,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> item: StackedNode { node: to_attach, // Attach root is prefixed at attach key - hash: Some((attach_root.clone(), owned_prefix(&(key_element, None)))), + previous_db_handle: Some((attach_root.clone(), owned_prefix(&(key_element, None)))), depth_prefix, depth, parent_index, @@ -1055,8 +1055,8 @@ impl ProcessStack for BatchUpdate, C, CD, D> }; return if stacked.item.node.is_empty() { // replace empty. - let detached_hash = mem::replace(&mut new_child.item.hash, stacked.item.hash.take()); - self.exit_detached(key_element, EMPTY_PREFIX, StackedNodeState::::Deleted, detached_hash); + let detached_db = mem::replace(&mut new_child.item.previous_db_handle, stacked.item.previous_db_handle.take()); + self.exit_detached(key_element, EMPTY_PREFIX, StackedNodeState::::Deleted, detached_db); *stacked = new_child; None } else { @@ -1095,7 +1095,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> let child = StackedItem { item: StackedNode { node: StackedNodeState::Changed(child), - hash: None, + previous_db_handle: None, depth_prefix: offset + mid_index, depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, parent_index, @@ -1135,12 +1135,12 @@ impl ProcessStack for BatchUpdate, C, CD, D> } let detached = mem::replace(&mut stacked.item.node, to_attach); //detached.advance_partial(mid_index); - let detached_hash = mem::replace( - &mut stacked.item.hash, + let detached_db = mem::replace( + &mut stacked.item.previous_db_handle, Some((attach_root.clone(), owned_prefix(&(key_element, None)))), ); stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); - self.exit_detached(key_element, prefix, detached, detached_hash); + self.exit_detached(key_element, prefix, detached, detached_db); None }, InputAction::Detach => { @@ -1154,8 +1154,8 @@ impl ProcessStack for BatchUpdate, C, CD, D> let to_attach = StackedNodeState::Deleted; let mut detached = mem::replace(&mut stacked.item.node, to_attach); detached.advance_partial(mid_index - stacked.item.depth_prefix); - let detached_hash = mem::replace(&mut stacked.item.hash, None); - self.exit_detached(key_element, prefix, detached, detached_hash); + let detached_db = mem::replace(&mut stacked.item.previous_db_handle, None); + self.exit_detached(key_element, prefix, detached, detached_db); } None }, @@ -1164,12 +1164,12 @@ impl ProcessStack for BatchUpdate, C, CD, D> } } - fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) + fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>) -> Option>>> { let register = &mut self.register_update; match stacked { StackedNodeState::Changed(node) => Some(Some({ - if let Some((h, p)) = prev_hash { + if let Some((h, p)) = prev_db { register((p, h, None)); } let encoded = node.into_encoded::<_, T::Codec, T::Hash>( @@ -1187,7 +1187,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> } })), StackedNodeState::Deleted => { - if let Some((h, p)) = prev_hash { + if let Some((h, p)) = prev_db { register((p, h.clone(), None)); } Some(None) @@ -1195,7 +1195,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> StackedNodeState::UnchangedAttached(node) => Some(Some({ let encoded = node.data().to_vec(); if encoded.len() < ::LENGTH { - if let Some((h, p)) = prev_hash { + if let Some((h, p)) = prev_db { register((p, h, None)); } OwnedNodeHandle::InMemory(encoded) @@ -1207,7 +1207,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> } } - fn exit_root(&mut self, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { + fn exit_root(&mut self, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>) { let prefix = EMPTY_PREFIX; let register = &mut self.register_update; match stacked { @@ -1216,7 +1216,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> let encoded = s.into_encoded(); let hash = ::hash(&encoded[..]); self.root = hash.clone(); - if let Some((h, p)) = prev_hash { + if let Some((h, p)) = prev_db { register((p, h.clone(), None)); } register((owned_prefix(&prefix), hash, Some(encoded))); @@ -1230,7 +1230,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> } } - fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_hash: Option<(TrieHash, OwnedPrefix)>) { + fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>) { let detached_prefix = (key_element, None); let is_empty_node = stacked.is_empty(); @@ -1240,7 +1240,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> s@StackedNodeState::Deleted | s@StackedNodeState::Changed(..) => { // same as root: also hash inline nodes. - if let Some((h, p)) = prev_hash { + if let Some((h, p)) = prev_db { register_up((p, h.clone(), None)); } if !is_empty_node { @@ -1253,7 +1253,7 @@ impl ProcessStack for BatchUpdate, C, CD, D> s@StackedNodeState::UnchangedAttached(..) | s@StackedNodeState::Unchanged(..) => { if !is_empty_node { - let hash = if let Some((not_inline, _previous_prefix)) = prev_hash { + let hash = if let Some((not_inline, _previous_prefix)) = prev_db { not_inline } else { let encoded = s.into_encoded(); From d9ea0078ea8f5276c28f2b0e7da2a3d505dddefd Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 29 Apr 2021 15:55:33 +0200 Subject: [PATCH 118/118] few doc --- trie-db/src/traverse.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs index 96cffda1..4260757a 100644 --- a/trie-db/src/traverse.rs +++ b/trie-db/src/traverse.rs @@ -79,7 +79,7 @@ struct StackedItem /// That is the only situation where we got a modified item that may need /// a to be iterated on at a next iteration. /// Note that this cannot be fuse with `first_modified_child` because of - /// the case where a split child is at an high child index than the first + /// the case where a split child is at a bigger child index than the first /// modified and will later be deleted, leading to a single child without /// value fuse branch trigger. split_child: Option>, @@ -90,7 +90,7 @@ struct StackedItem /// nibble, this is more memory costy than strictly necessary). /// Note that split_child is always a first_modified_child. first_modified_child: Option>, - /// true when the value can be deleted and only more + /// true as long as the value can be deleted and no more /// than one branch cannot be deleted. can_fuse: bool, }