From 649693deb84fdaec146c0b24bfc1e660cde9650c Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Mon, 18 Jan 2021 11:59:42 +0100 Subject: [PATCH 01/31] Expose decode with an iterator. --- trie-db/src/trie_codec.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index b1cfea9f..32650897 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -390,6 +390,17 @@ pub fn decode_compact(db: &mut DB, encoded: &[Vec]) where L: TrieLayout, DB: HashDB, +{ + decode_compact_from_iter::(db, &mut encoded.iter().map(Vec::as_slice)) +} + +/// Variant of 'decode_compact' that accept an iterator of encoded nodes as input. +pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: &'a mut I) + -> Result<(TrieHash, usize), TrieHash, CError> + where + L: TrieLayout, + DB: HashDB, + I: Iterator, { // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. @@ -398,8 +409,8 @@ pub fn decode_compact(db: &mut DB, encoded: &[Vec]) // The prefix of the next item to be read from the slice of encoded items. let mut prefix = NibbleVec::new(); - for (i, encoded_node) in encoded.iter().enumerate() { - let node = L::Codec::decode(encoded_node) + for (i, mut encoded_node) in encoded.enumerate() { + let node = L::Codec::decode(&mut encoded_node) .map_err(|err| Box::new(TrieError::DecoderError(>::default(), err)))?; let children_len = match node { From a3234319e33353a638ea7ef984278e45c71dee62 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Mon, 18 Jan 2021 12:02:00 +0100 Subject: [PATCH 02/31] reexport --- trie-db/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 2b3b3d40..37bfe64e 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -71,7 +71,7 @@ pub use crate::node_codec::{NodeCodec, Partial}; pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; pub use crate::iterator::TrieDBNodeIterator; -pub use crate::trie_codec::{decode_compact, encode_compact}; +pub use crate::trie_codec::{decode_compact, decode_compact_from_iter, encode_compact}; #[cfg(feature = "std")] pub use crate::iter_build::TrieRootPrint; From b5c1e9db6afa96db59798d1bc78d1db99d78f35d Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 18 Jan 2021 13:02:58 +0100 Subject: [PATCH 03/31] Update trie-db/src/trie_codec.rs Co-authored-by: Andronik Ordian --- trie-db/src/trie_codec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 32650897..884c790e 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -409,8 +409,8 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: &'a mut I // The prefix of the next item to be read from the slice of encoded items. let mut prefix = NibbleVec::new(); - for (i, mut encoded_node) in encoded.enumerate() { - let node = L::Codec::decode(&mut encoded_node) + for (i, encoded_node) in encoded.into_iter().enumerate() { + let node = L::Codec::decode(encoded_node) .map_err(|err| Box::new(TrieError::DecoderError(>::default(), err)))?; let children_len = match node { From 094a1dd5c8355530484249d47ba5f46d4a00e9f0 Mon Sep 17 00:00:00 2001 From: Emeric Chevalier Date: Mon, 18 Jan 2021 13:12:09 +0100 Subject: [PATCH 04/31] Apply suggestion : IntoIter usage. --- trie-db/src/trie_codec.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 32650897..68d956bb 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -391,16 +391,16 @@ pub fn decode_compact(db: &mut DB, encoded: &[Vec]) L: TrieLayout, DB: HashDB, { - decode_compact_from_iter::(db, &mut encoded.iter().map(Vec::as_slice)) + decode_compact_from_iter::(db, encoded.iter().map(Vec::as_slice)) } /// Variant of 'decode_compact' that accept an iterator of encoded nodes as input. -pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: &'a mut I) +pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, DB: HashDB, - I: Iterator, + I: IntoIterator, { // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. @@ -409,8 +409,8 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: &'a mut I // The prefix of the next item to be read from the slice of encoded items. let mut prefix = NibbleVec::new(); - for (i, mut encoded_node) in encoded.enumerate() { - let node = L::Codec::decode(&mut encoded_node) + for (i, encoded_node) in encoded.into_iter().enumerate() { + let node = L::Codec::decode(encoded_node) .map_err(|err| Box::new(TrieError::DecoderError(>::default(), err)))?; let children_len = match node { From 70c0d38b7d635696a13819bfab2c0fb965146c69 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 21 Jan 2021 12:47:03 +0100 Subject: [PATCH 05/31] encode compact without specific values. --- trie-db/src/iterator.rs | 2 + trie-db/src/lib.rs | 2 +- trie-db/src/trie_codec.rs | 94 ++++++++++++++++++++++++++++- trie-db/test/src/trie_codec.rs | 104 +++++++++++++++++++++------------ 4 files changed, 162 insertions(+), 40 deletions(-) diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index 1c1830b6..18539ae5 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -257,6 +257,8 @@ impl<'a, L: TrieLayout> TrieIterator for TrieDBNodeIterator<'a, L> { } impl<'a, L: TrieLayout> Iterator for TrieDBNodeIterator<'a, L> { + // TODO should be Result<(&'a NibbleVec, ...)> also rc is useless: should be &'a + // Also should return direction (down up or sibling), many time we recheck externally. type Item = Result<(NibbleVec, Option>, Rc>), TrieHash, CError>; fn next(&mut self) -> Option { diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 37bfe64e..06390909 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -71,7 +71,7 @@ pub use crate::node_codec::{NodeCodec, Partial}; pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; pub use crate::iterator::TrieDBNodeIterator; -pub use crate::trie_codec::{decode_compact, decode_compact_from_iter, encode_compact}; +pub use crate::trie_codec::{decode_compact, decode_compact_from_iter, encode_compact, encode_compact_skip_values}; #[cfg(feature = "std")] pub use crate::iter_build::TrieRootPrint; diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 68d956bb..72e7ec41 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -30,10 +30,12 @@ use crate::{ CError, ChildReference, DBValue, NibbleVec, NodeCodec, Result, TrieHash, TrieError, TrieDB, TrieDBNodeIterator, TrieLayout, nibble_ops::NIBBLE_LENGTH, node::{Node, NodeHandle, NodeHandlePlan, NodePlan, OwnedNode}, + nibble::LeftNibbleSlice, }; use crate::rstd::{ boxed::Box, convert::TryInto, marker::PhantomData, rc::Rc, result, vec, vec::Vec, }; +use crate::rstd::cmp::Ordering; struct EncoderStackEntry { /// The prefix is the nibble path to the node in the trie. @@ -44,6 +46,8 @@ struct EncoderStackEntry { child_index: usize, /// Flags indicating whether each child is omitted in the encoded node. omit_children: Vec, + /// Flags indicating whether we should omit value in the encoded node. + omit_value: bool, /// The encoding of the subtrie nodes rooted at this entry, which is built up in /// `encode_compact`. output_index: usize, @@ -96,8 +100,13 @@ impl EncoderStackEntry { /// Generates the encoding of the subtrie rooted at this entry. fn encode_node(&self) -> Result, C::HashOut, C::Error> { + let empty_value = Some(&[][..]); let node_data = self.node.data(); Ok(match self.node.node_plan() { + NodePlan::Leaf { partial, value: _value } if self.omit_value => { + let partial = partial.build(node_data); + C::leaf_node(partial.right(), &[][..]) + }, NodePlan::Empty | NodePlan::Leaf { .. } => node_data.to_vec(), NodePlan::Extension { partial, child: _ } => { if !self.omit_children[0] { @@ -109,18 +118,28 @@ impl EncoderStackEntry { } } NodePlan::Branch { value, children } => { + let value = if self.omit_value { + empty_value + } else { + value.clone().map(|range| &node_data[range]) + }; C::branch_node( Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value.clone().map(|range| &node_data[range]) + value, ) } NodePlan::NibbledBranch { partial, value, children } => { + let value = if self.omit_value { + empty_value + } else { + value.clone().map(|range| &node_data[range]) + }; let partial = partial.build(node_data); C::branch_node_nibbled( partial.right_iter(), partial.len(), Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value.clone().map(|range| &node_data[range]) + value, ) } }) @@ -167,8 +186,24 @@ impl EncoderStackEntry { /// references. pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CError> where - L: TrieLayout + L: TrieLayout, +{ + encode_compact_skip_values(db, core::iter::empty()) +} + +/// Variant of 'encode_compact' where values for given key (those are required to be already know +/// when checking the produce proof with 'decode_compact_with_values') are skipped. +/// Skipped values are encoded as a 0 length value. +/// +/// The iterator need to be sorted. +pub fn encode_compact_skip_values<'a, L, I>(db: &TrieDB, to_skip: I) -> Result>, TrieHash, CError> + where + L: TrieLayout, + I: IntoIterator, { + + let mut to_skip = SkipKeys::new(to_skip.into_iter()); + let mut output = Vec::new(); // The stack of nodes through a path in the trie. Each entry is a child node of the preceding @@ -221,11 +256,13 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE NodePlan::Extension { .. } => 1, NodePlan::Branch { .. } | NodePlan::NibbledBranch { .. } => NIBBLE_LENGTH, }; + let omit_value = to_skip.skip_new_node_value(&prefix, &node); stack.push(EncoderStackEntry { prefix, node, child_index: 0, omit_children: vec![false; children_len], + omit_value, output_index: output.len(), _marker: PhantomData::default(), }); @@ -250,6 +287,57 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE Ok(output) } +struct SkipKeys<'a, I> { + keys: I, + next_key: Option<&'a [u8]>, +} + +impl<'a, I: Iterator> SkipKeys<'a, I> { + fn new(mut keys: I) -> Self { + let next_key = keys.next(); + SkipKeys { + keys, + next_key, + } + } + + fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> bool { + if let Some(next) = self.next_key { + + let mut node_key = prefix.clone(); + match node.node_plan() { + NodePlan::NibbledBranch{partial, value: Some(_), ..} + | NodePlan::Leaf {partial, ..} => { + let node_data = node.data(); + let partial = partial.build(node_data); + node_key.append_partial(partial.right()); + }, + _ => (), + }; + + // comparison is redundant with previous checks, could be optimized. + let node_key = LeftNibbleSlice::new(node_key.inner()).truncate(node_key.len()); + let next = LeftNibbleSlice::new(next); + let (move_next, result) = match next.cmp(&node_key) { + Ordering::Less => (true, false), + Ordering::Greater => (false, false), + Ordering::Equal => { + (true, true) + }, + }; + if move_next { + self.next_key = self.keys.next(); + if !result { + return self.skip_new_node_value(prefix, node); + } + } + result + } else { + false + } + } +} + struct DecoderStackEntry<'a, C: NodeCodec> { node: Node<'a>, /// The next entry in the stack is a child of the preceding entry at this index. For branch diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index e4aa2181..561fd606 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -14,19 +14,21 @@ use trie_db::{ - DBValue, encode_compact, decode_compact, + DBValue, encode_compact_skip_values, decode_compact, Trie, TrieMut, TrieDB, TrieError, TrieDBMut, TrieLayout, Recorder, }; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use reference_trie::{ ExtensionLayout, NoExtensionLayout, }; +use std::collections::BTreeSet; type MemoryDB = memory_db::MemoryDB, DBValue>; fn test_encode_compact( entries: Vec<(&'static [u8], &'static [u8])>, keys: Vec<&'static [u8]>, + skip_keys: BTreeSet<&'static [u8]>, ) -> (::Out, Vec>, Vec<(&'static [u8], Option)>) { // Populate DB with full trie from entries. @@ -63,7 +65,7 @@ fn test_encode_compact( // Compactly encode the partial trie DB. let compact_trie = { let trie = >::new(&partial_db, &root).unwrap(); - encode_compact::(&trie).unwrap() + encode_compact_skip_values::(&trie, skip_keys.iter().map(|k| *k)).unwrap() }; (root, compact_trie, items) @@ -88,24 +90,28 @@ fn test_decode_compact( } } +fn test_set() -> Vec<(&'static [u8], &'static [u8])> { + vec![ + // "alfa" is at a hash-referenced leaf node. + (b"alfa", &[0; 32]), + // "bravo" is at an inline leaf node. + (b"bravo", b"bravo"), + // "do" is at a hash-referenced branch node. + (b"do", b"verb"), + // "dog" is at an inline leaf node. + (b"dog", b"puppy"), + // "doge" is at a hash-referenced leaf node. + (b"doge", &[0; 32]), + // extension node "o" (plus nibble) to next branch. + (b"horse", b"stallion"), + (b"house", b"building"), + ] +} + #[test] fn trie_compact_encoding_works_with_ext() { let (root, mut encoded, items) = test_encode_compact::( - vec![ - // "alfa" is at a hash-referenced leaf node. - (b"alfa", &[0; 32]), - // "bravo" is at an inline leaf node. - (b"bravo", b"bravo"), - // "do" is at a hash-referenced branch node. - (b"do", b"verb"), - // "dog" is at an inline leaf node. - (b"dog", b"puppy"), - // "doge" is at a hash-referenced leaf node. - (b"doge", &[0; 32]), - // extension node "o" (plus nibble) to next branch. - (b"horse", b"stallion"), - (b"house", b"building"), - ], + test_set(), vec![ b"do", b"dog", @@ -115,6 +121,7 @@ fn trie_compact_encoding_works_with_ext() { b"do\x10", // None, empty branch child b"halp", // None, witness is extension node with non-omitted child ], + BTreeSet::new(), ); encoded.push(Vec::new()); // Add an extra item to ensure it is not read. @@ -124,21 +131,7 @@ fn trie_compact_encoding_works_with_ext() { #[test] fn trie_compact_encoding_works_without_ext() { let (root, mut encoded, items) = test_encode_compact::( - vec![ - // "alfa" is at a hash-referenced leaf node. - (b"alfa", &[0; 32]), - // "bravo" is at an inline leaf node. - (b"bravo", b"bravo"), - // "do" is at a hash-referenced branch node. - (b"do", b"verb"), - // "dog" is at an inline leaf node. - (b"dog", b"puppy"), - // "doge" is at a hash-referenced leaf node. - (b"doge", &[0; 32]), - // extension node "o" (plus nibble) to next branch. - (b"horse", b"stallion"), - (b"house", b"building"), - ], + test_set(), vec![ b"do", b"dog", @@ -148,6 +141,7 @@ fn trie_compact_encoding_works_without_ext() { b"do\x10", // None, witness is empty branch child b"halp", // None, witness is branch partial ], + BTreeSet::new(), ); encoded.push(Vec::new()); // Add an extra item to ensure it is not read. @@ -155,15 +149,53 @@ fn trie_compact_encoding_works_without_ext() { } #[test] -fn trie_decoding_fails_with_incomplete_database() { - let (_, encoded, _) = test_encode_compact::( +fn trie_compact_encoding_skip_values() { + let mut to_skip = BTreeSet::new(); + to_skip.extend(&[&b"doge"[..], &b"aaaaaa"[..], &b"do"[..], &b"b"[..]]); + // doge and do will be skip (32 + 4 bytes) + let skip_len = 36; + let (root_no_skip, encoded_no_skip, items_no_skip) = test_encode_compact::( + test_set(), + vec![ + b"do", + b"dog", + b"doge", + b"bravo", + b"d", // None, witness is a branch partial + b"do\x10", // None, witness is empty branch child + b"halp", // None, witness is branch partial + ], + BTreeSet::new(), + ); + let (root, encoded, items) = test_encode_compact::( + test_set(), vec![ - (b"alfa", &[0; 32]), - (b"bravo", b"bravo"), + b"do", + b"dog", + b"doge", + b"bravo", + b"d", // None, witness is a branch partial + b"do\x10", // None, witness is empty branch child + b"halp", // None, witness is branch partial ], + to_skip, + ); + assert_eq!(root_no_skip, root); + assert_eq!(items_no_skip, items); + assert_eq!( + encoded_no_skip.iter().map(|e| e.len()).sum::(), + encoded.iter().map(|e| e.len()).sum::() + skip_len, + ); +} + +#[test] +fn trie_decoding_fails_with_incomplete_database() { + let (_, encoded, _) = test_encode_compact::( + test_set(), vec![ b"alfa", ], + BTreeSet::new(), ); assert!(encoded.len() > 1); From c0763e497a859119db2a210d3e91369aaa773118 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 21 Jan 2021 14:23:33 +0100 Subject: [PATCH 06/31] insert on decode --- trie-db/src/lib.rs | 3 +- trie-db/src/trie_codec.rs | 102 ++++++++++++++++++++++++++++++--- trie-db/test/src/trie_codec.rs | 42 ++++++++++---- 3 files changed, 129 insertions(+), 18 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 06390909..2770a561 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -71,7 +71,8 @@ pub use crate::node_codec::{NodeCodec, Partial}; pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; pub use crate::iterator::TrieDBNodeIterator; -pub use crate::trie_codec::{decode_compact, decode_compact_from_iter, encode_compact, encode_compact_skip_values}; +pub use crate::trie_codec::{encode_compact, encode_compact_skip_values, + decode_compact, decode_compact_from_iter, decode_compact_with_skipped_values}; #[cfg(feature = "std")] pub use crate::iter_build::TrieRootPrint; diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 72e7ec41..1d2ab723 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -338,6 +338,60 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { } } +struct SkipKeyValues<'a, I> { + key_values: I, + next_key_value: Option<(&'a [u8], &'a [u8])>, +} + +impl<'a, I: Iterator> SkipKeyValues<'a, I> { + fn new(mut key_values: I) -> Self { + let next_key_value = key_values.next(); + SkipKeyValues { + key_values, + next_key_value, + } + } + + fn skip_new_node_value( + &mut self, + prefix: &mut NibbleVec, + entry: &mut DecoderStackEntry<'a, C>, + ) { + if let Some((next, next_value)) = self.next_key_value { + let original_length = prefix.len(); + match entry.node { + Node::Leaf(partial, _) => { + prefix.append_partial(partial.right()); + } + Node::NibbledBranch(partial, _, Some(_value)) => { + prefix.append_partial(partial.right()); + } + _ => return, + } + // comparison is redundant with previous checks, could be optimized. + let node_key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); + let next = LeftNibbleSlice::new(next); + let (move_next, result) = match next.cmp(&node_key) { + Ordering::Less => (true, false), + Ordering::Greater => (false, false), + Ordering::Equal => { + (true, true) + }, + }; + prefix.drop_lasts(prefix.len() - original_length); + if result { + entry.inserted_value = Some(next_value); + } + if move_next { + self.next_key_value = self.key_values.next(); + if !result { + self.skip_new_node_value(prefix, entry); + } + } + } + } +} + struct DecoderStackEntry<'a, C: NodeCodec> { node: Node<'a>, /// The next entry in the stack is a child of the preceding entry at this index. For branch @@ -345,6 +399,8 @@ struct DecoderStackEntry<'a, C: NodeCodec> { child_index: usize, /// The reconstructed child references. children: Vec>>, + /// Value to insert. + inserted_value: Option<&'a [u8]>, _marker: PhantomData, } @@ -435,12 +491,16 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { /// /// Preconditions: /// - if node is an extension node, then `children[0]` is Some. - fn encode_node(self) -> Vec { + fn encode_node(mut self) -> Vec { match self.node { Node::Empty => C::empty_node().to_vec(), - Node::Leaf(partial, value) => - C::leaf_node(partial.right(), value), + Node::Leaf(partial, mut value) => { + if let Some(inserted_value) = self.inserted_value.take() { + value = inserted_value; + } + C::leaf_node(partial.right(), value) + }, Node::Extension(partial, _) => C::extension_node( partial.right_iter(), @@ -448,15 +508,23 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { self.children[0] .expect("required by method precondition; qed"), ), - Node::Branch(_, value) => - C::branch_node(self.children.into_iter(), value), - Node::NibbledBranch(partial, _, value) => + Node::Branch(_, mut value) => { + if let Some(inserted_value) = self.inserted_value.take() { + value = Some(inserted_value); + } + C::branch_node(self.children.into_iter(), value) + }, + Node::NibbledBranch(partial, _, mut value) => { + if let Some(inserted_value) = self.inserted_value.take() { + value = Some(inserted_value); + } C::branch_node_nibbled( partial.right_iter(), partial.len(), self.children.iter(), value, - ), + ) + }, } } } @@ -489,11 +557,29 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) L: TrieLayout, DB: HashDB, I: IntoIterator, +{ + decode_compact_with_skipped_values::(db, encoded, core::iter::empty()) +} + +/// Variant of 'decode_compact' that inject some known key values. +/// Values are only added if the existing one is a zero length value, +/// to match 'encode_compact_skip_values'. +/// +/// Skipped key values must be ordered. +pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, V>(db: &mut DB, encoded: I, skipped: V) + -> Result<(TrieHash, usize), TrieHash, CError> + where + L: TrieLayout, + DB: HashDB, + I: IntoIterator, + V: IntoIterator, { // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. let mut stack: Vec> = Vec::new(); + let mut skipped = SkipKeyValues::new(skipped.into_iter()); + // The prefix of the next item to be read from the slice of encoded items. let mut prefix = NibbleVec::new(); @@ -510,10 +596,12 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) node, child_index: 0, children: vec![None; children_len], + inserted_value: None, _marker: PhantomData::default(), }; loop { + skipped.skip_new_node_value(&mut prefix, &mut last_entry); if !last_entry.advance_child_index()? { last_entry.push_to_prefix(&mut prefix); stack.push(last_entry); diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 561fd606..15354a46 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -14,8 +14,9 @@ use trie_db::{ - DBValue, encode_compact_skip_values, decode_compact, + DBValue, encode_compact_skip_values, decode_compact_with_skipped_values, Trie, TrieMut, TrieDB, TrieError, TrieDBMut, TrieLayout, Recorder, + decode_compact, }; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use reference_trie::{ @@ -28,7 +29,7 @@ type MemoryDB = memory_db::MemoryDB, DBValue>; fn test_encode_compact( entries: Vec<(&'static [u8], &'static [u8])>, keys: Vec<&'static [u8]>, - skip_keys: BTreeSet<&'static [u8]>, + skip_keys: &BTreeSet<&'static [u8]>, ) -> (::Out, Vec>, Vec<(&'static [u8], Option)>) { // Populate DB with full trie from entries. @@ -76,10 +77,15 @@ fn test_decode_compact( items: Vec<(&'static [u8], Option)>, expected_root: ::Out, expected_used: usize, + skipped_values: &BTreeSet<(&'static [u8], &'static [u8])>, ) { // Reconstruct the partial DB from the compact encoding. let mut db = MemoryDB::default(); - let (root, used) = decode_compact::(&mut db, encoded).unwrap(); + let (root, used) = decode_compact_with_skipped_values::( + &mut db, + encoded.iter().map(Vec::as_slice), + skipped_values.iter().map(|kv| (kv.0, kv.1)), + ).unwrap(); assert_eq!(root, expected_root); assert_eq!(used, expected_used); @@ -121,11 +127,11 @@ fn trie_compact_encoding_works_with_ext() { b"do\x10", // None, empty branch child b"halp", // None, witness is extension node with non-omitted child ], - BTreeSet::new(), + &BTreeSet::new(), ); encoded.push(Vec::new()); // Add an extra item to ensure it is not read. - test_decode_compact::(&encoded, items, root, encoded.len() - 1); + test_decode_compact::(&encoded, items, root, encoded.len() - 1, &BTreeSet::new()); } #[test] @@ -141,11 +147,11 @@ fn trie_compact_encoding_works_without_ext() { b"do\x10", // None, witness is empty branch child b"halp", // None, witness is branch partial ], - BTreeSet::new(), + &BTreeSet::new(), ); encoded.push(Vec::new()); // Add an extra item to ensure it is not read. - test_decode_compact::(&encoded, items, root, encoded.len() - 1); + test_decode_compact::(&encoded, items, root, encoded.len() - 1, &BTreeSet::new()); } #[test] @@ -165,7 +171,7 @@ fn trie_compact_encoding_skip_values() { b"do\x10", // None, witness is empty branch child b"halp", // None, witness is branch partial ], - BTreeSet::new(), + &BTreeSet::new(), ); let (root, encoded, items) = test_encode_compact::( test_set(), @@ -178,7 +184,7 @@ fn trie_compact_encoding_skip_values() { b"do\x10", // None, witness is empty branch child b"halp", // None, witness is branch partial ], - to_skip, + &to_skip, ); assert_eq!(root_no_skip, root); assert_eq!(items_no_skip, items); @@ -186,6 +192,22 @@ fn trie_compact_encoding_skip_values() { encoded_no_skip.iter().map(|e| e.len()).sum::(), encoded.iter().map(|e| e.len()).sum::() + skip_len, ); + let mut encoded = encoded; + encoded.push(Vec::new()); // Add an extra item to ensure it is not read. + let mut skipped_values = BTreeSet::new(); + skipped_values.extend(&[ + (&b"doge"[..], &[0; 32][..]), + (&b"do"[..], &b"verb"[..]), + (&b"aaaa"[..], &b"dummy"[..]), + (&b"b"[..], &b"dummy"[..]), + ]); + test_decode_compact::( + &encoded, + items, + root, + encoded.len() - 1, + &skipped_values, + ); } #[test] @@ -195,7 +217,7 @@ fn trie_decoding_fails_with_incomplete_database() { vec![ b"alfa", ], - BTreeSet::new(), + &BTreeSet::new(), ); assert!(encoded.len() > 1); From ca296d82762a4cff15fc7cd05d2276037f75d994 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 22 Jan 2021 14:02:18 +0100 Subject: [PATCH 07/31] Allow lazy value fetch. --- trie-db/src/trie_codec.rs | 60 +++++++++++++++++++++++----------- trie-db/test/src/trie_codec.rs | 2 +- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 1d2ab723..1ecf516c 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -192,7 +192,7 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE } /// Variant of 'encode_compact' where values for given key (those are required to be already know -/// when checking the produce proof with 'decode_compact_with_values') are skipped. +/// when checking the produce proof with 'decode_compact_with_skipped_values') are skipped. /// Skipped values are encoded as a 0 length value. /// /// The iterator need to be sorted. @@ -338,12 +338,16 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { } } -struct SkipKeyValues<'a, I> { +struct SkipKeyValues<'a, I, F> { key_values: I, - next_key_value: Option<(&'a [u8], &'a [u8])>, + next_key_value: Option<(&'a [u8], F)>, } -impl<'a, I: Iterator> SkipKeyValues<'a, I> { +impl< + 'a, + F: LazyValue<'a>, + I: Iterator +> SkipKeyValues<'a, I, F> { fn new(mut key_values: I) -> Self { let next_key_value = key_values.next(); SkipKeyValues { @@ -355,7 +359,7 @@ impl<'a, I: Iterator> SkipKeyValues<'a, I> { fn skip_new_node_value( &mut self, prefix: &mut NibbleVec, - entry: &mut DecoderStackEntry<'a, C>, + entry: &mut DecoderStackEntry<'a, C, F>, ) { if let Some((next, next_value)) = self.next_key_value { let original_length = prefix.len(); @@ -392,7 +396,7 @@ impl<'a, I: Iterator> SkipKeyValues<'a, I> { } } -struct DecoderStackEntry<'a, C: NodeCodec> { +struct DecoderStackEntry<'a, C: NodeCodec, F> { node: Node<'a>, /// The next entry in the stack is a child of the preceding entry at this index. For branch /// nodes, the index is in [0, NIBBLE_LENGTH] and for extension nodes, the index is in [0, 1]. @@ -400,11 +404,11 @@ struct DecoderStackEntry<'a, C: NodeCodec> { /// The reconstructed child references. children: Vec>>, /// Value to insert. - inserted_value: Option<&'a [u8]>, + inserted_value: Option, _marker: PhantomData, } -impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { +impl<'a, C: NodeCodec, F: LazyValue<'a>> DecoderStackEntry<'a, C, F> { /// Advance the child index until either it exceeds the number of children or the child is /// marked as omitted. Omitted children are indicated by an empty inline reference. For each /// child that is passed over and not omitted, copy over the child reference from the node to @@ -491,13 +495,13 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { /// /// Preconditions: /// - if node is an extension node, then `children[0]` is Some. - fn encode_node(mut self) -> Vec { - match self.node { + fn encode_node(mut self) -> Option> { + Some(match self.node { Node::Empty => C::empty_node().to_vec(), Node::Leaf(partial, mut value) => { if let Some(inserted_value) = self.inserted_value.take() { - value = inserted_value; + value = inserted_value.fetch()?; } C::leaf_node(partial.right(), value) }, @@ -510,13 +514,13 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { ), Node::Branch(_, mut value) => { if let Some(inserted_value) = self.inserted_value.take() { - value = Some(inserted_value); + value = Some(inserted_value.fetch()?); } C::branch_node(self.children.into_iter(), value) }, Node::NibbledBranch(partial, _, mut value) => { if let Some(inserted_value) = self.inserted_value.take() { - value = Some(inserted_value); + value = Some(inserted_value.fetch()?); } C::branch_node_nibbled( partial.right_iter(), @@ -525,7 +529,7 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { value, ) }, - } + }) } } @@ -558,7 +562,23 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) DB: HashDB, I: IntoIterator, { - decode_compact_with_skipped_values::(db, encoded, core::iter::empty()) + decode_compact_with_skipped_values::(db, encoded, core::iter::empty()) +} + + +/// Simple fetcher for values to insert in proof when running +/// `decode_compact_with_skipped_values`. +pub trait LazyValue<'a>: Copy { + /// Get actual value as bytes. + /// If value cannot be fetch return `None`, resulting + /// in an error in `decode_compact_with_skipped_values`. + fn fetch(&self) -> Option<&'a [u8]>; +} + +impl<'a> LazyValue<'a> for &'a [u8] { + fn fetch(&self) -> Option<&'a [u8]> { + Some(self) + } } /// Variant of 'decode_compact' that inject some known key values. @@ -566,17 +586,18 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) /// to match 'encode_compact_skip_values'. /// /// Skipped key values must be ordered. -pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, V>(db: &mut DB, encoded: I, skipped: V) +pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, V>(db: &mut DB, encoded: I, skipped: V) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, DB: HashDB, I: IntoIterator, - V: IntoIterator, + F: LazyValue<'a>, + V: IntoIterator, { // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. - let mut stack: Vec> = Vec::new(); + let mut stack: Vec> = Vec::new(); let mut skipped = SkipKeyValues::new(skipped.into_iter()); @@ -610,7 +631,8 @@ pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, V>(db: &mut DB, encod // Since `advance_child_index` returned true, the preconditions for `encode_node` are // satisfied. - let node_data = last_entry.encode_node(); + let node_data = last_entry.encode_node() + .ok_or(Box::new(TrieError::IncompleteDatabase(>::default())))?; let node_hash = db.insert(prefix.as_prefix(), node_data.as_ref()); if let Some(entry) = stack.pop() { diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 15354a46..af5f95ac 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -81,7 +81,7 @@ fn test_decode_compact( ) { // Reconstruct the partial DB from the compact encoding. let mut db = MemoryDB::default(); - let (root, used) = decode_compact_with_skipped_values::( + let (root, used) = decode_compact_with_skipped_values::( &mut db, encoded.iter().map(Vec::as_slice), skipped_values.iter().map(|kv| (kv.0, kv.1)), From c64e6c3e3a5a70539fa409f2354b1d8fb216c4ce Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 22 Jan 2021 14:48:58 +0100 Subject: [PATCH 08/31] usable api --- trie-db/src/trie_codec.rs | 53 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 1ecf516c..68967fc4 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -34,8 +34,8 @@ use crate::{ }; use crate::rstd::{ boxed::Box, convert::TryInto, marker::PhantomData, rc::Rc, result, vec, vec::Vec, + borrow::Cow, cmp::Ordering, mem, }; -use crate::rstd::cmp::Ordering; struct EncoderStackEntry { /// The prefix is the nibble path to the node in the trie. @@ -361,7 +361,7 @@ impl< prefix: &mut NibbleVec, entry: &mut DecoderStackEntry<'a, C, F>, ) { - if let Some((next, next_value)) = self.next_key_value { + if let Some((next, _next_value)) = &self.next_key_value { let original_length = prefix.len(); match entry.node { Node::Leaf(partial, _) => { @@ -384,7 +384,7 @@ impl< }; prefix.drop_lasts(prefix.len() - original_length); if result { - entry.inserted_value = Some(next_value); + entry.inserted_value = mem::take(&mut self.next_key_value).map(|next| next.1); } if move_next { self.next_key_value = self.key_values.next(); @@ -499,11 +499,13 @@ impl<'a, C: NodeCodec, F: LazyValue<'a>> DecoderStackEntry<'a, C, F> { Some(match self.node { Node::Empty => C::empty_node().to_vec(), - Node::Leaf(partial, mut value) => { + Node::Leaf(partial, value) => { if let Some(inserted_value) = self.inserted_value.take() { - value = inserted_value.fetch()?; + let value = inserted_value.fetch()?; + C::leaf_node(partial.right(), value.as_ref()) + } else { + C::leaf_node(partial.right(), value) } - C::leaf_node(partial.right(), value) }, Node::Extension(partial, _) => C::extension_node( @@ -512,22 +514,31 @@ impl<'a, C: NodeCodec, F: LazyValue<'a>> DecoderStackEntry<'a, C, F> { self.children[0] .expect("required by method precondition; qed"), ), - Node::Branch(_, mut value) => { + Node::Branch(_, value) => { if let Some(inserted_value) = self.inserted_value.take() { - value = Some(inserted_value.fetch()?); + let value = inserted_value.fetch()?; + C::branch_node(self.children.into_iter(), Some(value.as_ref())) + } else { + C::branch_node(self.children.into_iter(), value) } - C::branch_node(self.children.into_iter(), value) }, - Node::NibbledBranch(partial, _, mut value) => { + Node::NibbledBranch(partial, _, value) => { if let Some(inserted_value) = self.inserted_value.take() { - value = Some(inserted_value.fetch()?); + let value = inserted_value.fetch()?; + C::branch_node_nibbled( + partial.right_iter(), + partial.len(), + self.children.iter(), + Some(value.as_ref()), + ) + } else { + C::branch_node_nibbled( + partial.right_iter(), + partial.len(), + self.children.iter(), + value, + ) } - C::branch_node_nibbled( - partial.right_iter(), - partial.len(), - self.children.iter(), - value, - ) }, }) } @@ -568,16 +579,16 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) /// Simple fetcher for values to insert in proof when running /// `decode_compact_with_skipped_values`. -pub trait LazyValue<'a>: Copy { +pub trait LazyValue<'a> { /// Get actual value as bytes. /// If value cannot be fetch return `None`, resulting /// in an error in `decode_compact_with_skipped_values`. - fn fetch(&self) -> Option<&'a [u8]>; + fn fetch(&self) -> Option>; } impl<'a> LazyValue<'a> for &'a [u8] { - fn fetch(&self) -> Option<&'a [u8]> { - Some(self) + fn fetch(&self) -> Option> { + Some(Cow::Borrowed(self)) } } From 6b58a32af5ecfdd6cf0a5ad56634bf2a50babfde Mon Sep 17 00:00:00 2001 From: cheme Date: Wed, 27 Jan 2021 11:12:46 +0100 Subject: [PATCH 09/31] Use cow from alloc for no_std --- trie-db/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 2770a561..72017abb 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -27,8 +27,8 @@ mod rstd { #[cfg(not(feature = "std"))] mod rstd { - pub use core::{borrow, convert, cmp, iter, fmt, hash, marker, mem, ops, result}; - pub use alloc::{boxed, rc, vec}; + pub use core::{convert, cmp, iter, fmt, hash, marker, mem, ops, result}; + pub use alloc::{borrow, boxed, rc, vec}; pub use alloc::collections::VecDeque; pub trait Error {} impl Error for T {} From 5322f0c075ea1586c1c83f44ec95b7f0caccbde0 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Jan 2021 10:46:40 +0100 Subject: [PATCH 10/31] expose LazyValue --- trie-db/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 72017abb..664d9e3f 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -72,7 +72,8 @@ pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; pub use crate::iterator::TrieDBNodeIterator; pub use crate::trie_codec::{encode_compact, encode_compact_skip_values, - decode_compact, decode_compact_from_iter, decode_compact_with_skipped_values}; + decode_compact, decode_compact_from_iter, decode_compact_with_skipped_values, + LazyValue}; #[cfg(feature = "std")] pub use crate::iter_build::TrieRootPrint; From f7b772cf813e6b13e9a4454d7352762ab9c05cf1 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Jan 2021 12:07:16 +0100 Subject: [PATCH 11/31] add alternative enum --- trie-db/src/trie_codec.rs | 77 +++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 06d1e3e6..53267da9 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -340,7 +340,8 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { enum ValuesInsert<'a, I, F> { KnownKeys(SkipKeyValues<'a, I, F>), - // EncodedKeys + // TODO knownkeys with escaping. + EncodedKeys(F), } struct SkipKeyValues<'a, I, F> { @@ -360,43 +361,55 @@ impl< next_key_value, } } +} +impl< + 'a, + F: LazyFetcher<'a>, + I: Iterator +> ValuesInsert<'a, I, F> { fn skip_new_node_value( &mut self, prefix: &mut NibbleVec, entry: &mut DecoderStackEntry<'a, C, F>, ) { - if let Some((next, _next_value)) = &self.next_key_value { - let original_length = prefix.len(); - match entry.node { - Node::Leaf(partial, _) => { - prefix.append_partial(partial.right()); - } - Node::NibbledBranch(partial, _, Some(_value)) => { - prefix.append_partial(partial.right()); - } - _ => return, - } - // comparison is redundant with previous checks, could be optimized. - let node_key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); - let next = LeftNibbleSlice::new(next); - let (move_next, result) = match next.cmp(&node_key) { - Ordering::Less => (true, false), - Ordering::Greater => (false, false), - Ordering::Equal => { - (true, true) - }, - }; - prefix.drop_lasts(prefix.len() - original_length); - if result { - entry.inserted_value = mem::take(&mut self.next_key_value); - } - if move_next { - self.next_key_value = self.key_values.next(); - if !result { - self.skip_new_node_value(prefix, entry); + match self { + ValuesInsert::KnownKeys(skipped_keys) => { + if let Some((next, _next_value)) = &skipped_keys.next_key_value { + let original_length = prefix.len(); + match entry.node { + Node::Leaf(partial, _) => { + prefix.append_partial(partial.right()); + } + Node::NibbledBranch(partial, _, Some(_value)) => { + prefix.append_partial(partial.right()); + } + _ => return, + } + // comparison is redundant with previous checks, could be optimized. + let node_key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); + let next = LeftNibbleSlice::new(next); + let (move_next, result) = match next.cmp(&node_key) { + Ordering::Less => (true, false), + Ordering::Greater => (false, false), + Ordering::Equal => { + (true, true) + }, + }; + prefix.drop_lasts(prefix.len() - original_length); + if result { + entry.inserted_value = mem::take(&mut skipped_keys.next_key_value); + } + if move_next { + skipped_keys.next_key_value = skipped_keys.key_values.next(); + if !result { + self.skip_new_node_value(prefix, entry); + } + } } - } + }, + ValuesInsert::EncodedKeys(_fetcher) => { + }, } } } @@ -619,7 +632,7 @@ pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, V>(db: &mut DB, en // entry. let mut stack: Vec> = Vec::new(); - let mut skipped = SkipKeyValues::new(skipped.into_iter()); + let mut skipped = ValuesInsert::KnownKeys(SkipKeyValues::new(skipped.into_iter())); // The prefix of the next item to be read from the slice of encoded items. let mut prefix = NibbleVec::new(); From 0203860b5b777983aec7c09601ae497b57904f4d Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Jan 2021 15:30:37 +0100 Subject: [PATCH 12/31] resolve ineserted_value immediatly --- trie-db/src/trie_codec.rs | 78 +++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 19 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 53267da9..124cdedc 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -363,6 +363,28 @@ impl< } } +/// Since empty value is not a very common case, its encoding +/// will start by a byte sequence to avoid escaping too often +/// on valid value. +/// +/// The sequence is escape character followed by 'Esc'. +/// The repeating character for case where the sequence is part +/// of the content, is the first bit defined here. +const EMPTY_ESCAPE_SEQUENCE: &'static [u8] = b"Esc"; + +#[test] +fn escape_bytes_check() { + assert_eq!(EMPTY_ESCAPE_SEQUENCE, [27, 69, 115, 99]); +} + +/// Get empty escaped value (either empty or value starting with +/// empty prefix minus end escape character). +/// +/// If escaped return the decoded value. +fn decode_empty_escaped(value: &[u8]) -> Option<&[u8]> { + unimplemented!() +} + impl< 'a, F: LazyFetcher<'a>, @@ -371,20 +393,18 @@ impl< fn skip_new_node_value( &mut self, prefix: &mut NibbleVec, - entry: &mut DecoderStackEntry<'a, C, F>, - ) { + entry: &mut DecoderStackEntry<'a, C>, + ) -> bool { match self { ValuesInsert::KnownKeys(skipped_keys) => { if let Some((next, _next_value)) = &skipped_keys.next_key_value { let original_length = prefix.len(); match entry.node { - Node::Leaf(partial, _) => { - prefix.append_partial(partial.right()); - } - Node::NibbledBranch(partial, _, Some(_value)) => { + Node::Leaf(partial, _value) + | Node::NibbledBranch(partial, _, Some(_value)) => { prefix.append_partial(partial.right()); } - _ => return, + _ => return true, } // comparison is redundant with previous checks, could be optimized. let node_key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); @@ -398,7 +418,13 @@ impl< }; prefix.drop_lasts(prefix.len() - original_length); if result { - entry.inserted_value = mem::take(&mut skipped_keys.next_key_value); + if let Some((key, fetcher)) = mem::take(&mut skipped_keys.next_key_value) { + if let Some(value) = fetcher.fetch(key) { + entry.inserted_value = Some(value); + } else { + return false; + } + } } if move_next { skipped_keys.next_key_value = skipped_keys.key_values.next(); @@ -409,12 +435,27 @@ impl< } }, ValuesInsert::EncodedKeys(_fetcher) => { + unimplemented!() +/* match entry.node { + Node::Leaf(partial, value) + | Node::NibbledBranch(partial, _, Some(value)) => { + let new_value: Cow<[u8]> = if value.len() == 0 { + } else if let Some(new_value) = decode_empty_escaped(value) { + new_value.into() + } else { + return; + } + } + _ => return, + } +*/ }, } + true } } -struct DecoderStackEntry<'a, C: NodeCodec, F> { +struct DecoderStackEntry<'a, C: NodeCodec> { node: Node<'a>, /// The next entry in the stack is a child of the preceding entry at this index. For branch /// nodes, the index is in [0, NIBBLE_LENGTH] and for extension nodes, the index is in [0, 1]. @@ -422,11 +463,11 @@ struct DecoderStackEntry<'a, C: NodeCodec, F> { /// The reconstructed child references. children: Vec>>, /// Value to insert. - inserted_value: Option<(&'a [u8], F)>, + inserted_value: Option>, _marker: PhantomData, } -impl<'a, C: NodeCodec, F: LazyFetcher<'a>> DecoderStackEntry<'a, C, F> { +impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { /// Advance the child index until either it exceeds the number of children or the child is /// marked as omitted. Omitted children are indicated by an empty inline reference. For each /// child that is passed over and not omitted, copy over the child reference from the node to @@ -519,8 +560,7 @@ impl<'a, C: NodeCodec, F: LazyFetcher<'a>> DecoderStackEntry<'a, C, F> { C::empty_node().to_vec(), Node::Leaf(partial, value) => { if let Some(inserted_value) = self.inserted_value.take() { - let value = inserted_value.1.fetch(inserted_value.0)?; - C::leaf_node(partial.right(), value.as_ref()) + C::leaf_node(partial.right(), inserted_value.as_ref()) } else { C::leaf_node(partial.right(), value) } @@ -534,20 +574,18 @@ impl<'a, C: NodeCodec, F: LazyFetcher<'a>> DecoderStackEntry<'a, C, F> { ), Node::Branch(_, value) => { if let Some(inserted_value) = self.inserted_value.take() { - let value = inserted_value.1.fetch(inserted_value.0)?; - C::branch_node(self.children.into_iter(), Some(value.as_ref())) + C::branch_node(self.children.into_iter(), Some(inserted_value.as_ref())) } else { C::branch_node(self.children.into_iter(), value) } }, Node::NibbledBranch(partial, _, value) => { if let Some(inserted_value) = self.inserted_value.take() { - let value = inserted_value.1.fetch(inserted_value.0)?; C::branch_node_nibbled( partial.right_iter(), partial.len(), self.children.iter(), - Some(value.as_ref()), + Some(inserted_value.as_ref()), ) } else { C::branch_node_nibbled( @@ -630,7 +668,7 @@ pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, V>(db: &mut DB, en { // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. - let mut stack: Vec> = Vec::new(); + let mut stack: Vec> = Vec::new(); let mut skipped = ValuesInsert::KnownKeys(SkipKeyValues::new(skipped.into_iter())); @@ -655,7 +693,9 @@ pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, V>(db: &mut DB, en }; loop { - skipped.skip_new_node_value(&mut prefix, &mut last_entry); + if !skipped.skip_new_node_value(&mut prefix, &mut last_entry) { + return Err(Box::new(TrieError::IncompleteDatabase(>::default()))); + } if !last_entry.advance_child_index()? { last_entry.push_to_prefix(&mut prefix); stack.push(last_entry); From 88a677602f0236f2fe034a5b35c6253e64b4c4e7 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Jan 2021 16:14:54 +0100 Subject: [PATCH 13/31] escaping code --- trie-db/src/nibble/leftnibbleslice.rs | 11 +++- trie-db/src/trie_codec.rs | 94 ++++++++++++++++++++++++--- 2 files changed, 95 insertions(+), 10 deletions(-) diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index c31301be..f7d5d31d 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -92,6 +92,15 @@ impl<'a> LeftNibbleSlice<'a> { // If common nibble prefix is the same, finally compare lengths. self.len().cmp(&other.len()) } + + /// If nibble are aligned (true for any value), return slice to the key. + pub fn as_slice(&self) -> Option<&'a [u8]> { + if self.len % NIBBLE_PER_BYTE == 0 { + Some(&self.bytes[..self.len / NIBBLE_PER_BYTE]) + } else { + None + } + } } impl<'a> PartialEq for LeftNibbleSlice<'a> { @@ -226,4 +235,4 @@ mod tests { Ordering::Equal ); } -} \ No newline at end of file +} diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 124cdedc..179accba 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -377,12 +377,80 @@ fn escape_bytes_check() { assert_eq!(EMPTY_ESCAPE_SEQUENCE, [27, 69, 115, 99]); } +/// Escape encode value. +/// This allows using the encoded empty value to define +/// a skipped value. +/// +/// So we redefine the empty value as a sequence of byte. +/// Se we redefine this sequence with n character appended by appending another character. +/// Such that: +/// [] -> [27, 69, 115, 99] +/// [27, 69, 115, 99] -> [27, 69, 115, 99, 27] +/// [27, 69, 115, 99, 27] -> [27, 69, 115, 99, 27, 27] +/// +/// When escaped return the escaped value. +fn encode_empty_escape(value: &[u8]) -> Option> { + if value.len() == 0 { + return Some(EMPTY_ESCAPE_SEQUENCE.into()); + } + + if value.starts_with(EMPTY_ESCAPE_SEQUENCE) { + let mut i = EMPTY_ESCAPE_SEQUENCE.len(); + while Some(&EMPTY_ESCAPE_SEQUENCE[0]) == value.get(i) { + i += 1; + } + if i == value.len() { + let mut value = value.to_vec(); + value.push(EMPTY_ESCAPE_SEQUENCE[0]); + // escaped escape sequence + return Some(value.into()); + } + } + None +} + /// Get empty escaped value (either empty or value starting with /// empty prefix minus end escape character). /// /// If escaped return the decoded value. fn decode_empty_escaped(value: &[u8]) -> Option<&[u8]> { - unimplemented!() + if value.starts_with(EMPTY_ESCAPE_SEQUENCE) { + let mut i = EMPTY_ESCAPE_SEQUENCE.len(); + if value.len() == i { + // escaped empty + return Some(&[]) + } + while Some(&EMPTY_ESCAPE_SEQUENCE[0]) == value.get(i) { + i += 1; + } + if i == value.len() { + // escaped escape sequence + return Some(&value[..value.len() - 1]); + } + } + None +} + +#[test] +fn escape_empty_value() { + let test_set = [ + (&[][..], Some(&[27u8, 69, 115, 99][..])), + (&[27u8, 69, 115], None), + (&[27, 69, 115, 100], None), + (&[27, 69, 115, 99], Some(&[27, 69, 115, 99, 27])), + (&[27, 69, 115, 99, 100], None), + (&[27, 69, 115, 99, 27], Some(&[27, 69, 115, 99, 27, 27])), + (&[27, 69, 115, 99, 27, 100], None), + ]; + + for (input, output) in test_set.iter() { + let encoded = encode_empty_escape(input); + assert_eq!(&encoded.as_ref().map(Cow::as_ref), output); + if let Some(encoded) = output { + let decoded = decode_empty_escaped(encoded); + assert_eq!(decoded, Some(*input)); + } + } } impl< @@ -434,21 +502,29 @@ impl< } } }, - ValuesInsert::EncodedKeys(_fetcher) => { - unimplemented!() -/* match entry.node { + ValuesInsert::EncodedKeys(fetcher) => { + match entry.node { Node::Leaf(partial, value) | Node::NibbledBranch(partial, _, Some(value)) => { - let new_value: Cow<[u8]> = if value.len() == 0 { + if value.len() == 0 { + let original_length = prefix.len(); + prefix.append_partial(partial.right()); + let key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); + if let Some(value) = fetcher.fetch(key.as_slice().expect("Values have keys")) { + entry.inserted_value = Some(value); + prefix.drop_lasts(prefix.len() - original_length); + } else { + prefix.drop_lasts(prefix.len() - original_length); + return false; + } } else if let Some(new_value) = decode_empty_escaped(value) { - new_value.into() + entry.inserted_value = Some(new_value.into()); } else { - return; + return true; } } - _ => return, + _ => return true, } -*/ }, } true From 75c29b976a1a9f6d9204c4b9558aac8d5784203c Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Jan 2021 16:33:39 +0100 Subject: [PATCH 14/31] factor --- trie-db/src/trie_codec.rs | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 179accba..87257430 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -340,7 +340,6 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { enum ValuesInsert<'a, I, F> { KnownKeys(SkipKeyValues<'a, I, F>), - // TODO knownkeys with escaping. EncodedKeys(F), } @@ -742,12 +741,43 @@ pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, V>(db: &mut DB, en F: LazyFetcher<'a>, V: IntoIterator, { + let skipped = ValuesInsert::KnownKeys(SkipKeyValues::new(skipped.into_iter())); + decode_compact_with_skipped_inner::(db, encoded.into_iter(), skipped) +} + +/// Variant of 'decode_compact' that try to fetch value when they where +/// skipped. +/// Skipped values are identified by being a 0 length value, the actual 0 length value +/// is escaped. +pub fn decode_compact_with_encoded_skipped_values<'a, L, DB, T, I, F>(db: &mut DB, encoded: I, fetcher: F) + -> Result<(TrieHash, usize), TrieHash, CError> + where + L: TrieLayout, + DB: HashDB, + I: IntoIterator, + F: LazyFetcher<'a>, +{ + let skipped = ValuesInsert::EncodedKeys(fetcher); + decode_compact_with_skipped_inner::>(db, encoded.into_iter(), skipped) +} + +fn decode_compact_with_skipped_inner<'a, L, DB, T, I, F, V>( + db: &mut DB, + encoded: I, + mut skipped: ValuesInsert<'a, V, F>, +) -> Result<(TrieHash, usize), TrieHash, CError> + where + L: TrieLayout, + DB: HashDB, + I: Iterator, + F: LazyFetcher<'a>, + V: Iterator, +{ + // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. let mut stack: Vec> = Vec::new(); - let mut skipped = ValuesInsert::KnownKeys(SkipKeyValues::new(skipped.into_iter())); - // The prefix of the next item to be read from the slice of encoded items. let mut prefix = NibbleVec::new(); From 932f76ffca434f98725731976d8d5739fa5b7f82 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Jan 2021 17:19:33 +0100 Subject: [PATCH 15/31] do not allow non skip value to be replaced. --- trie-db/src/lib.rs | 4 ++- trie-db/src/trie_codec.rs | 55 ++++++++++++++++++++++++---------- trie-db/test/src/trie_codec.rs | 15 +++++----- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 08dffbd0..3415142b 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -22,6 +22,7 @@ extern crate alloc; mod rstd { pub use std::{borrow, boxed, cmp, convert, fmt, hash, iter, marker, mem, ops, rc, result, vec}; pub use std::collections::VecDeque; + pub use std::collections::BTreeMap; pub use std::error::Error; } @@ -30,6 +31,7 @@ mod rstd { pub use core::{convert, cmp, iter, fmt, hash, marker, mem, ops, result}; pub use alloc::{borrow, boxed, rc, vec}; pub use alloc::collections::VecDeque; + pub use alloc::collections::btree_map::BTreeMap; pub trait Error {} impl Error for T {} } @@ -73,7 +75,7 @@ pub use crate::iter_build::{trie_visit, ProcessEncodedNode, pub use crate::iterator::TrieDBNodeIterator; pub use crate::trie_codec::{encode_compact, encode_compact_skip_values, decode_compact, decode_compact_from_iter, decode_compact_with_skipped_values, - LazyFetcher}; + decode_compact_with_encoded_skipped_values, LazyFetcher}; #[cfg(feature = "std")] pub use crate::iter_build::TrieRootPrint; diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 87257430..9ea9a90b 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -345,18 +345,20 @@ enum ValuesInsert<'a, I, F> { struct SkipKeyValues<'a, I, F> { key_values: I, - next_key_value: Option<(&'a [u8], F)>, + fetcher: F, + next_key_value: Option<&'a [u8]>, } impl< 'a, F: LazyFetcher<'a>, - I: Iterator + I: Iterator > SkipKeyValues<'a, I, F> { - fn new(mut key_values: I) -> Self { + fn new(mut key_values: I, fetcher: F) -> Self { let next_key_value = key_values.next(); SkipKeyValues { key_values, + fetcher, next_key_value, } } @@ -455,8 +457,8 @@ fn escape_empty_value() { impl< 'a, F: LazyFetcher<'a>, - I: Iterator -> ValuesInsert<'a, I, F> { + V: Iterator +> ValuesInsert<'a, V, F> { fn skip_new_node_value( &mut self, prefix: &mut NibbleVec, @@ -464,11 +466,15 @@ impl< ) -> bool { match self { ValuesInsert::KnownKeys(skipped_keys) => { - if let Some((next, _next_value)) = &skipped_keys.next_key_value { + if let Some(next) = &skipped_keys.next_key_value { let original_length = prefix.len(); + let mut empty_value = false; match entry.node { - Node::Leaf(partial, _value) - | Node::NibbledBranch(partial, _, Some(_value)) => { + Node::Leaf(partial, value) + | Node::NibbledBranch(partial, _, Some(value)) => { + if value.len() == 0 { + empty_value = true; + } prefix.append_partial(partial.right()); } _ => return true, @@ -484,15 +490,19 @@ impl< }, }; prefix.drop_lasts(prefix.len() - original_length); - if result { - if let Some((key, fetcher)) = mem::take(&mut skipped_keys.next_key_value) { - if let Some(value) = fetcher.fetch(key) { + if result && empty_value { + if let Some(key) = mem::take(&mut skipped_keys.next_key_value) { + if let Some(value) = skipped_keys.fetcher.fetch(key) { entry.inserted_value = Some(value); } else { return false; } } } + if result && !empty_value { + // expected skip value was not skip, can be harmless, but consider invalid + return false; + } if move_next { skipped_keys.next_key_value = skipped_keys.key_values.next(); if !result { @@ -704,7 +714,7 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) DB: HashDB, I: IntoIterator, { - decode_compact_with_skipped_values::(db, encoded, core::iter::empty()) + decode_compact_with_skipped_values::(db, encoded, (), core::iter::empty()) } @@ -717,6 +727,12 @@ pub trait LazyFetcher<'a> { fn fetch(&self, key: &[u8]) -> Option>; } +impl<'a> LazyFetcher<'a> for () { + fn fetch(&self, _key: &[u8]) -> Option> { + None + } +} + impl<'a> LazyFetcher<'a> for (&'a [u8], &'a [u8]) { fn fetch(&self, key: &[u8]) -> Option> { if key == self.0 { @@ -727,21 +743,28 @@ impl<'a> LazyFetcher<'a> for (&'a [u8], &'a [u8]) { } } +impl<'a> LazyFetcher<'a> for &'a crate::rstd::BTreeMap<&'a [u8], &'a [u8]> { + fn fetch(&self, key: &[u8]) -> Option> { + self.get(key).map(|value| Cow::Borrowed(*value)) + } +} + + /// Variant of 'decode_compact' that inject some known key values. /// Values are only added if the existing one is a zero length value, /// to match 'encode_compact_skip_values'. /// /// Skipped key values must be ordered. -pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, V>(db: &mut DB, encoded: I, skipped: V) +pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, V>(db: &mut DB, encoded: I, fetcher: F, skipped: V) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, DB: HashDB, I: IntoIterator, F: LazyFetcher<'a>, - V: IntoIterator, + V: IntoIterator, { - let skipped = ValuesInsert::KnownKeys(SkipKeyValues::new(skipped.into_iter())); + let skipped = ValuesInsert::KnownKeys(SkipKeyValues::new(skipped.into_iter(), fetcher)); decode_compact_with_skipped_inner::(db, encoded.into_iter(), skipped) } @@ -771,7 +794,7 @@ fn decode_compact_with_skipped_inner<'a, L, DB, T, I, F, V>( DB: HashDB, I: Iterator, F: LazyFetcher<'a>, - V: Iterator, + V: Iterator, { // The stack of nodes through a path in the trie. Each entry is a child node of the preceding diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 81316817..7ed558b1 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -22,7 +22,7 @@ use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use reference_trie::{ ExtensionLayout, NoExtensionLayout, }; -use std::collections::BTreeSet; +use std::collections::{BTreeSet, BTreeMap}; type MemoryDB = memory_db::MemoryDB, DBValue>; @@ -77,14 +77,15 @@ fn test_decode_compact( items: Vec<(&'static [u8], Option)>, expected_root: ::Out, expected_used: usize, - skipped_values: &BTreeSet<(&'static [u8], &'static [u8])>, + skipped_values: &BTreeMap<&'static [u8], &'static [u8]>, ) { // Reconstruct the partial DB from the compact encoding. let mut db = MemoryDB::default(); let (root, used) = decode_compact_with_skipped_values::( &mut db, encoded.iter().map(Vec::as_slice), - skipped_values.iter().map(|kv| (kv.0, (kv.0, kv.1))), + skipped_values, + skipped_values.keys().map(|k| *k), ).unwrap(); assert_eq!(root, expected_root); assert_eq!(used, expected_used); @@ -131,7 +132,7 @@ fn trie_compact_encoding_works_with_ext() { ); encoded.push(Vec::new()); // Add an extra item to ensure it is not read. - test_decode_compact::(&encoded, items, root, encoded.len() - 1, &BTreeSet::new()); + test_decode_compact::(&encoded, items, root, encoded.len() - 1, &BTreeMap::new()); } #[test] @@ -151,7 +152,7 @@ fn trie_compact_encoding_works_without_ext() { ); encoded.push(Vec::new()); // Add an extra item to ensure it is not read. - test_decode_compact::(&encoded, items, root, encoded.len() - 1, &BTreeSet::new()); + test_decode_compact::(&encoded, items, root, encoded.len() - 1, &BTreeMap::new()); } #[test] @@ -194,8 +195,8 @@ fn trie_compact_encoding_skip_values() { ); let mut encoded = encoded; encoded.push(Vec::new()); // Add an extra item to ensure it is not read. - let mut skipped_values = BTreeSet::new(); - skipped_values.extend(&[ + let mut skipped_values = BTreeMap::new(); + skipped_values.extend(vec![ (&b"doge"[..], &[0; 32][..]), (&b"do"[..], &b"verb"[..]), (&b"aaaa"[..], &b"dummy"[..]), From e9f63a7ecb55c144012b10a03ae9f98f4ebc958c Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Jan 2021 17:38:34 +0100 Subject: [PATCH 16/31] factor a bit --- trie-db/src/trie_codec.rs | 76 ++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 9ea9a90b..517d7dbe 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -340,7 +340,7 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { enum ValuesInsert<'a, I, F> { KnownKeys(SkipKeyValues<'a, I, F>), - EncodedKeys(F), + EscapedKeys(F), } struct SkipKeyValues<'a, I, F> { @@ -459,26 +459,38 @@ impl< F: LazyFetcher<'a>, V: Iterator > ValuesInsert<'a, V, F> { + fn escaped_value( + &self, + ) -> bool { + match self { + ValuesInsert::KnownKeys(..) => false, + ValuesInsert::EscapedKeys(..) => true + } + } + fn skip_new_node_value( &mut self, prefix: &mut NibbleVec, entry: &mut DecoderStackEntry<'a, C>, ) -> bool { + + let original_length = prefix.len(); + let (partial, empty_value, escaped_value) = match entry.node { + Node::Leaf(partial, value) + | Node::NibbledBranch(partial, _, Some(value)) => { + (partial, value.is_empty(), if self.escaped_value() { + decode_empty_escaped(value) + } else { + None + }) + }, + _ => return true, + }; + match self { ValuesInsert::KnownKeys(skipped_keys) => { if let Some(next) = &skipped_keys.next_key_value { - let original_length = prefix.len(); - let mut empty_value = false; - match entry.node { - Node::Leaf(partial, value) - | Node::NibbledBranch(partial, _, Some(value)) => { - if value.len() == 0 { - empty_value = true; - } - prefix.append_partial(partial.right()); - } - _ => return true, - } + prefix.append_partial(partial.right()); // comparison is redundant with previous checks, could be optimized. let node_key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); let next = LeftNibbleSlice::new(next); @@ -506,36 +518,28 @@ impl< if move_next { skipped_keys.next_key_value = skipped_keys.key_values.next(); if !result { - self.skip_new_node_value(prefix, entry); + return self.skip_new_node_value(prefix, entry); } } } }, - ValuesInsert::EncodedKeys(fetcher) => { - match entry.node { - Node::Leaf(partial, value) - | Node::NibbledBranch(partial, _, Some(value)) => { - if value.len() == 0 { - let original_length = prefix.len(); - prefix.append_partial(partial.right()); - let key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); - if let Some(value) = fetcher.fetch(key.as_slice().expect("Values have keys")) { - entry.inserted_value = Some(value); - prefix.drop_lasts(prefix.len() - original_length); - } else { - prefix.drop_lasts(prefix.len() - original_length); - return false; - } - } else if let Some(new_value) = decode_empty_escaped(value) { - entry.inserted_value = Some(new_value.into()); - } else { - return true; - } + ValuesInsert::EscapedKeys(fetcher) => { + if empty_value { + prefix.append_partial(partial.right()); + let key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); + if let Some(value) = fetcher.fetch(key.as_slice().expect("Values have keys")) { + entry.inserted_value = Some(value); + prefix.drop_lasts(prefix.len() - original_length); + } else { + prefix.drop_lasts(prefix.len() - original_length); + return false; } - _ => return true, } }, } + if let Some(new_value) = escaped_value { + entry.inserted_value = Some(new_value.into()); + } true } } @@ -780,7 +784,7 @@ pub fn decode_compact_with_encoded_skipped_values<'a, L, DB, T, I, F>(db: &mut D I: IntoIterator, F: LazyFetcher<'a>, { - let skipped = ValuesInsert::EncodedKeys(fetcher); + let skipped = ValuesInsert::EscapedKeys(fetcher); decode_compact_with_skipped_inner::>(db, encoded.into_iter(), skipped) } From afc2fd5e276bfeed415259ab50fa4dada7df55f3 Mon Sep 17 00:00:00 2001 From: cheme Date: Thu, 28 Jan 2021 18:45:14 +0100 Subject: [PATCH 17/31] Todo write test and others variant --- trie-db/src/lib.rs | 3 +- trie-db/src/trie_codec.rs | 127 +++++++++++++++++++++++---------- trie-db/test/src/trie_codec.rs | 6 ++ 3 files changed, 96 insertions(+), 40 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 3415142b..778e07ba 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -75,7 +75,8 @@ pub use crate::iter_build::{trie_visit, ProcessEncodedNode, pub use crate::iterator::TrieDBNodeIterator; pub use crate::trie_codec::{encode_compact, encode_compact_skip_values, decode_compact, decode_compact_from_iter, decode_compact_with_skipped_values, - decode_compact_with_encoded_skipped_values, LazyFetcher}; + decode_compact_with_encoded_skipped_values, encode_compact_skip_all_values, + LazyFetcher}; #[cfg(feature = "std")] pub use crate::iter_build::TrieRootPrint; diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 517d7dbe..07a36eba 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -48,6 +48,8 @@ struct EncoderStackEntry { omit_children: Vec, /// Flags indicating whether we should omit value in the encoded node. omit_value: bool, + /// Flags indicating whether we should escape a value. + escape_value: bool, /// The encoding of the subtrie nodes rooted at this entry, which is built up in /// `encode_compact`. output_index: usize, @@ -103,11 +105,21 @@ impl EncoderStackEntry { let empty_value = Some(&[][..]); let node_data = self.node.data(); Ok(match self.node.node_plan() { - NodePlan::Leaf { partial, value: _value } if self.omit_value => { + NodePlan::Empty => node_data.to_vec(), + NodePlan::Leaf { partial, value } => { let partial = partial.build(node_data); - C::leaf_node(partial.right(), &[][..]) + if self.omit_value { + C::leaf_node(partial.right(), &[][..]) + } else if self.escape_value { + if let Some(escaped) = encode_empty_escape(&node_data[value.clone()]) { + C::leaf_node(partial.right(), &escaped[..]) + } else { + node_data.to_vec() + } + } else { + node_data.to_vec() + } }, - NodePlan::Empty | NodePlan::Leaf { .. } => node_data.to_vec(), NodePlan::Extension { partial, child: _ } => { if !self.omit_children[0] { node_data.to_vec() @@ -119,13 +131,24 @@ impl EncoderStackEntry { } NodePlan::Branch { value, children } => { let value = if self.omit_value { - empty_value + empty_value.map(Cow::Borrowed) } else { - value.clone().map(|range| &node_data[range]) + value.clone().map(|range| { + let node_data = &node_data[range]; + if self.escape_value { + if let Some(escaped) = encode_empty_escape(node_data) { + escaped + } else { + node_data.into() + } + } else { + node_data.into() + } + }) }; C::branch_node( Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value, + value.as_ref().map(|v| &v[..]), ) } NodePlan::NibbledBranch { partial, value, children } => { @@ -201,9 +224,25 @@ pub fn encode_compact_skip_values<'a, L, I>(db: &TrieDB, to_skip: I) -> Resul L: TrieLayout, I: IntoIterator, { + let to_skip = ValuesRemove::KnownKeys(SkipKeys::new(to_skip.into_iter())); + encode_compact_skip_values_inner(db, to_skip) +} + +/// Variant of 'encode_compact' where all values are removed and replace by empty value. +pub fn encode_compact_skip_all_values<'a, L, I>(db: &TrieDB) -> Result>, TrieHash, CError> + where + L: TrieLayout, +{ + let to_skip = ValuesRemove::AllEscaped; + encode_compact_skip_values_inner::>(db, to_skip) +} - let mut to_skip = SkipKeys::new(to_skip.into_iter()); +fn encode_compact_skip_values_inner<'a, L, I>(db: &TrieDB, mut to_skip: ValuesRemove<'a, I>) -> Result>, TrieHash, CError> + where + L: TrieLayout, + I: Iterator, +{ let mut output = Vec::new(); // The stack of nodes through a path in the trie. Each entry is a child node of the preceding @@ -256,13 +295,14 @@ pub fn encode_compact_skip_values<'a, L, I>(db: &TrieDB, to_skip: I) -> Resul NodePlan::Extension { .. } => 1, NodePlan::Branch { .. } | NodePlan::NibbledBranch { .. } => NIBBLE_LENGTH, }; - let omit_value = to_skip.skip_new_node_value(&prefix, &node); + let (omit_value, escape_value) = to_skip.skip_new_node_value(&prefix, &node); stack.push(EncoderStackEntry { prefix, node, child_index: 0, omit_children: vec![false; children_len], omit_value, + escape_value, output_index: output.len(), _marker: PhantomData::default(), }); @@ -287,6 +327,11 @@ pub fn encode_compact_skip_values<'a, L, I>(db: &TrieDB, to_skip: I) -> Resul Ok(output) } +enum ValuesRemove<'a, I> { + KnownKeys(SkipKeys<'a, I>), + AllEscaped, +} + struct SkipKeys<'a, I> { keys: I, next_key: Option<&'a [u8]>, @@ -300,41 +345,45 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { next_key, } } +} - fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> bool { - if let Some(next) = self.next_key { - - let mut node_key = prefix.clone(); - match node.node_plan() { - NodePlan::NibbledBranch{partial, value: Some(_), ..} - | NodePlan::Leaf {partial, ..} => { - let node_data = node.data(); - let partial = partial.build(node_data); - node_key.append_partial(partial.right()); - }, - _ => (), - }; +impl<'a, I: Iterator> ValuesRemove<'a, I> { + fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> (bool, bool) { + match self { + ValuesRemove::KnownKeys(to_skip) => { + if let Some(next) = to_skip.next_key { + let mut node_key = prefix.clone(); + match node.node_plan() { + NodePlan::NibbledBranch{partial, value: Some(_), ..} + | NodePlan::Leaf {partial, ..} => { + let node_data = node.data(); + let partial = partial.build(node_data); + node_key.append_partial(partial.right()); + }, + _ => (), + }; - // comparison is redundant with previous checks, could be optimized. - let node_key = LeftNibbleSlice::new(node_key.inner()).truncate(node_key.len()); - let next = LeftNibbleSlice::new(next); - let (move_next, result) = match next.cmp(&node_key) { - Ordering::Less => (true, false), - Ordering::Greater => (false, false), - Ordering::Equal => { - (true, true) - }, - }; - if move_next { - self.next_key = self.keys.next(); - if !result { - return self.skip_new_node_value(prefix, node); + // comparison is redundant with previous checks, could be optimized. + let node_key = LeftNibbleSlice::new(node_key.inner()).truncate(node_key.len()); + let next = LeftNibbleSlice::new(next); + match next.cmp(&node_key) { + Ordering::Less => { + to_skip.next_key = to_skip.keys.next(); + return self.skip_new_node_value(prefix, node); + }, + Ordering::Equal => { + to_skip.next_key = to_skip.keys.next(); + return (true, false); + }, + Ordering::Greater => (), + }; } - } - result - } else { - false + }, + ValuesRemove::AllEscaped => { + return (true, false); + }, } + (false, false) } } diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 7ed558b1..c93fa579 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -211,6 +211,12 @@ fn trie_compact_encoding_skip_values() { ); } +#[test] +fn trie_compact_encoding_skip_all_values() { + let mut values = BTreeMap::new(); + values.extend(test_set()); +} + #[test] fn trie_decoding_fails_with_incomplete_database() { let (_, encoded, _) = test_encode_compact::( From 5cc77bd35e81432bc84f68e3ca7503fbea1c7bdd Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 10:20:47 +0100 Subject: [PATCH 18/31] test and fix --- trie-db/src/trie_codec.rs | 10 ++- trie-db/test/src/trie_codec.rs | 146 ++++++++++++++++++++++----------- 2 files changed, 108 insertions(+), 48 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 07a36eba..fd11e855 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -229,7 +229,7 @@ pub fn encode_compact_skip_values<'a, L, I>(db: &TrieDB, to_skip: I) -> Resul } /// Variant of 'encode_compact' where all values are removed and replace by empty value. -pub fn encode_compact_skip_all_values<'a, L, I>(db: &TrieDB) -> Result>, TrieHash, CError> +pub fn encode_compact_skip_all_values<'a, L>(db: &TrieDB) -> Result>, TrieHash, CError> where L: TrieLayout, { @@ -380,7 +380,13 @@ impl<'a, I: Iterator> ValuesRemove<'a, I> { } }, ValuesRemove::AllEscaped => { - return (true, false); + match node.node_plan() { + NodePlan::NibbledBranch{value: Some(_), ..} + | NodePlan::Leaf {..} => { + return (true, false); + }, + _ => (), + }; }, } (false, false) diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index c93fa579..22fc28c2 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -14,7 +14,7 @@ use trie_db::{ - DBValue, encode_compact_skip_values, decode_compact_with_skipped_values, + DBValue, Trie, TrieMut, TrieDB, TrieError, TrieDBMut, TrieLayout, Recorder, decode_compact, }; @@ -26,10 +26,15 @@ use std::collections::{BTreeSet, BTreeMap}; type MemoryDB = memory_db::MemoryDB, DBValue>; +enum EncodeType<'a> { + SkipKeys(&'a BTreeSet<&'static [u8]>), + All, + None, +} fn test_encode_compact( entries: Vec<(&'static [u8], &'static [u8])>, keys: Vec<&'static [u8]>, - skip_keys: &BTreeSet<&'static [u8]>, + encode_type: EncodeType, ) -> (::Out, Vec>, Vec<(&'static [u8], Option)>) { // Populate DB with full trie from entries. @@ -66,27 +71,59 @@ fn test_encode_compact( // Compactly encode the partial trie DB. let compact_trie = { let trie = >::new(&partial_db, &root).unwrap(); - encode_compact_skip_values::(&trie, skip_keys.iter().map(|k| *k)).unwrap() + match encode_type { + EncodeType::None => { + trie_db::encode_compact::(&trie).unwrap() + }, + EncodeType::All => { + trie_db::encode_compact_skip_all_values::(&trie).unwrap() + }, + EncodeType::SkipKeys(skip_keys) => { + trie_db::encode_compact_skip_values::(&trie, skip_keys.iter().map(|k| *k)).unwrap() + }, + } }; (root, compact_trie, items) } - +enum DecodeType<'a> { + None, + SkippedValues(&'a BTreeMap<&'static [u8], &'static [u8]>), + Escaped(&'a BTreeMap<&'static [u8], &'static [u8]>), +} fn test_decode_compact( encoded: &[Vec], items: Vec<(&'static [u8], Option)>, expected_root: ::Out, expected_used: usize, - skipped_values: &BTreeMap<&'static [u8], &'static [u8]>, + decode_type: DecodeType, ) { // Reconstruct the partial DB from the compact encoding. let mut db = MemoryDB::default(); - let (root, used) = decode_compact_with_skipped_values::( - &mut db, - encoded.iter().map(Vec::as_slice), - skipped_values, - skipped_values.keys().map(|k| *k), - ).unwrap(); + let (root, used) = match decode_type { + DecodeType::SkippedValues(skipped_values) => { + trie_db::decode_compact_with_skipped_values::( + &mut db, + encoded.iter().map(Vec::as_slice), + skipped_values, + skipped_values.keys().map(|k| *k), + ) + }, + DecodeType::None => { + trie_db::decode_compact_from_iter::( + &mut db, + encoded.iter().map(Vec::as_slice), + ) + }, + DecodeType::Escaped(fetcher) => { + trie_db::decode_compact_with_encoded_skipped_values::( + &mut db, + encoded.iter().map(Vec::as_slice), + fetcher, + ) + }, + }.unwrap(); + assert_eq!(root, expected_root); assert_eq!(used, expected_used); @@ -115,6 +152,19 @@ fn test_set() -> Vec<(&'static [u8], &'static [u8])> { ] } +// ok proof elements to test with test_set +fn test_proof_default() -> Vec<&'static [u8]> { + vec![ + b"do", + b"dog", + b"doge", + b"bravo", + b"d", // None, witness is a branch partial + b"do\x10", // None, witness is empty branch child + b"halp", // None, witness is branch partial + ] +} + #[test] fn trie_compact_encoding_works_with_ext() { let (root, mut encoded, items) = test_encode_compact::( @@ -128,31 +178,23 @@ fn trie_compact_encoding_works_with_ext() { b"do\x10", // None, empty branch child b"halp", // None, witness is extension node with non-omitted child ], - &BTreeSet::new(), + EncodeType::None, ); encoded.push(Vec::new()); // Add an extra item to ensure it is not read. - test_decode_compact::(&encoded, items, root, encoded.len() - 1, &BTreeMap::new()); + test_decode_compact::(&encoded, items, root, encoded.len() - 1, DecodeType::None); } #[test] fn trie_compact_encoding_works_without_ext() { let (root, mut encoded, items) = test_encode_compact::( test_set(), - vec![ - b"do", - b"dog", - b"doge", - b"bravo", - b"d", // None, witness is a branch partial - b"do\x10", // None, witness is empty branch child - b"halp", // None, witness is branch partial - ], - &BTreeSet::new(), + test_proof_default(), + EncodeType::None, ); encoded.push(Vec::new()); // Add an extra item to ensure it is not read. - test_decode_compact::(&encoded, items, root, encoded.len() - 1, &BTreeMap::new()); + test_decode_compact::(&encoded, items, root, encoded.len() - 1, DecodeType::None); } #[test] @@ -163,29 +205,13 @@ fn trie_compact_encoding_skip_values() { let skip_len = 36; let (root_no_skip, encoded_no_skip, items_no_skip) = test_encode_compact::( test_set(), - vec![ - b"do", - b"dog", - b"doge", - b"bravo", - b"d", // None, witness is a branch partial - b"do\x10", // None, witness is empty branch child - b"halp", // None, witness is branch partial - ], - &BTreeSet::new(), + test_proof_default(), + EncodeType::None, ); let (root, encoded, items) = test_encode_compact::( test_set(), - vec![ - b"do", - b"dog", - b"doge", - b"bravo", - b"d", // None, witness is a branch partial - b"do\x10", // None, witness is empty branch child - b"halp", // None, witness is branch partial - ], - &to_skip, + test_proof_default(), + EncodeType::SkipKeys(&to_skip), ); assert_eq!(root_no_skip, root); assert_eq!(items_no_skip, items); @@ -207,7 +233,7 @@ fn trie_compact_encoding_skip_values() { items, root, encoded.len() - 1, - &skipped_values, + DecodeType::SkippedValues(&skipped_values), ); } @@ -215,6 +241,34 @@ fn trie_compact_encoding_skip_values() { fn trie_compact_encoding_skip_all_values() { let mut values = BTreeMap::new(); values.extend(test_set()); + let (root_no_skip, _encoded_no_skip, items_no_skip) = test_encode_compact::( + test_set(), + test_proof_default(), + EncodeType::None, + ); + let (root, encoded, items) = test_encode_compact::( + test_set(), + test_proof_default(), + EncodeType::All, + ); + assert_eq!(root_no_skip, root); + assert_eq!(items_no_skip, items); + let mut encoded = encoded; + encoded.push(Vec::new()); // Add an extra item to ensure it is not read. + test_decode_compact::( + &encoded, + items.clone(), + root, + encoded.len() - 1, + DecodeType::Escaped(&values), + ); + test_decode_compact::( + &encoded, + items, + root, + encoded.len() - 1, + DecodeType::SkippedValues(&values), + ); } #[test] @@ -224,7 +278,7 @@ fn trie_decoding_fails_with_incomplete_database() { vec![ b"alfa", ], - &BTreeSet::new(), + EncodeType::None, ); assert!(encoded.len() > 1); From c2e032742298f2c5a085b06fa930799e86304780 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 10:34:43 +0100 Subject: [PATCH 19/31] None enum. --- trie-db/src/trie_codec.rs | 77 +++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index fd11e855..3e46f06b 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -211,7 +211,8 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE where L: TrieLayout, { - encode_compact_skip_values(db, core::iter::empty()) + let to_skip = ValuesRemove::None; + encode_compact_skip_values_inner::>(db, to_skip) } /// Variant of 'encode_compact' where values for given key (those are required to be already know @@ -330,6 +331,7 @@ fn encode_compact_skip_values_inner<'a, L, I>(db: &TrieDB, mut to_skip: Value enum ValuesRemove<'a, I> { KnownKeys(SkipKeys<'a, I>), AllEscaped, + None, } struct SkipKeys<'a, I> { @@ -350,6 +352,7 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { impl<'a, I: Iterator> ValuesRemove<'a, I> { fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> (bool, bool) { match self { + ValuesRemove::None => (), ValuesRemove::KnownKeys(to_skip) => { if let Some(next) = to_skip.next_key { let mut node_key = prefix.clone(); @@ -394,6 +397,7 @@ impl<'a, I: Iterator> ValuesRemove<'a, I> { } enum ValuesInsert<'a, I, F> { + None, KnownKeys(SkipKeyValues<'a, I, F>), EscapedKeys(F), } @@ -518,6 +522,7 @@ impl< &self, ) -> bool { match self { + ValuesInsert::None => false, ValuesInsert::KnownKeys(..) => false, ValuesInsert::EscapedKeys(..) => true } @@ -543,6 +548,7 @@ impl< }; match self { + ValuesInsert::None => (), ValuesInsert::KnownKeys(skipped_keys) => { if let Some(next) = &skipped_keys.next_key_value { prefix.append_partial(partial.right()); @@ -773,42 +779,10 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) DB: HashDB, I: IntoIterator, { - decode_compact_with_skipped_values::(db, encoded, (), core::iter::empty()) -} - - -/// Simple fetcher for values to insert in proof when running -/// `decode_compact_with_skipped_values`. -pub trait LazyFetcher<'a> { - /// Get actual value as bytes. - /// If value cannot be fetch return `None`, resulting - /// in an error in `decode_compact_with_skipped_values`. - fn fetch(&self, key: &[u8]) -> Option>; -} - -impl<'a> LazyFetcher<'a> for () { - fn fetch(&self, _key: &[u8]) -> Option> { - None - } + let skipped = ValuesInsert::, ()>::None; + decode_compact_with_skipped_inner::(db, encoded.into_iter(), skipped) } -impl<'a> LazyFetcher<'a> for (&'a [u8], &'a [u8]) { - fn fetch(&self, key: &[u8]) -> Option> { - if key == self.0 { - Some(Cow::Borrowed(self.1)) - } else { - None - } - } -} - -impl<'a> LazyFetcher<'a> for &'a crate::rstd::BTreeMap<&'a [u8], &'a [u8]> { - fn fetch(&self, key: &[u8]) -> Option> { - self.get(key).map(|value| Cow::Borrowed(*value)) - } -} - - /// Variant of 'decode_compact' that inject some known key values. /// Values are only added if the existing one is a zero length value, /// to match 'encode_compact_skip_values'. @@ -910,3 +884,36 @@ fn decode_compact_with_skipped_inner<'a, L, DB, T, I, F, V>( Err(Box::new(TrieError::IncompleteDatabase(>::default()))) } + +/// Simple fetcher for values to insert in proof when running +/// `decode_compact_with_skipped_values`. +pub trait LazyFetcher<'a> { + /// Get actual value as bytes. + /// If value cannot be fetch return `None`, resulting + /// in an error in `decode_compact_with_skipped_values`. + fn fetch(&self, key: &[u8]) -> Option>; +} + +impl<'a> LazyFetcher<'a> for () { + fn fetch(&self, _key: &[u8]) -> Option> { + None + } +} + +impl<'a> LazyFetcher<'a> for (&'a [u8], &'a [u8]) { + fn fetch(&self, key: &[u8]) -> Option> { + if key == self.0 { + Some(Cow::Borrowed(self.1)) + } else { + None + } + } +} + +impl<'a> LazyFetcher<'a> for &'a crate::rstd::BTreeMap<&'a [u8], &'a [u8]> { + fn fetch(&self, key: &[u8]) -> Option> { + self.get(key).map(|value| Cow::Borrowed(*value)) + } +} + + From 56453ce8a92d30651c61dc1df989c6e9aaaae3fa Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 11:14:04 +0100 Subject: [PATCH 20/31] conditional instead of iterator would be better. --- trie-db/src/trie_codec.rs | 66 ++++++++++++++++++++++++---------- trie-db/test/src/trie_codec.rs | 15 +++++++- 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 3e46f06b..aa67841f 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -220,12 +220,20 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE /// Skipped values are encoded as a 0 length value. /// /// The iterator need to be sorted. -pub fn encode_compact_skip_values<'a, L, I>(db: &TrieDB, to_skip: I) -> Result>, TrieHash, CError> +pub fn encode_compact_skip_values<'a, L, I>( + db: &TrieDB, + to_skip: I, + escape_empty: bool, +) -> Result>, TrieHash, CError> where L: TrieLayout, I: IntoIterator, { - let to_skip = ValuesRemove::KnownKeys(SkipKeys::new(to_skip.into_iter())); + let to_skip = if escape_empty { + ValuesRemove::KnownKeysEscaped(SkipKeys::new(to_skip.into_iter())) + } else { + ValuesRemove::KnownKeys(SkipKeys::new(to_skip.into_iter())) + }; encode_compact_skip_values_inner(db, to_skip) } @@ -330,6 +338,7 @@ fn encode_compact_skip_values_inner<'a, L, I>(db: &TrieDB, mut to_skip: Value enum ValuesRemove<'a, I> { KnownKeys(SkipKeys<'a, I>), + KnownKeysEscaped(SkipKeys<'a, I>), AllEscaped, None, } @@ -350,21 +359,39 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { } impl<'a, I: Iterator> ValuesRemove<'a, I> { + fn escaped_value( + &self, + ) -> bool { + match self { + ValuesRemove::KnownKeysEscaped(..) => true, + // all remove means that escape on remaining values + // is not needed. + ValuesRemove::AllEscaped => false, + ValuesRemove::None => false, + ValuesRemove::KnownKeys(..) => false, + } + } + + // return (omit_value, escape_value) fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> (bool, bool) { + + let (partial, escaped_value) = match node.node_plan() { + NodePlan::NibbledBranch{partial, value: Some(_), ..} + | NodePlan::Leaf {partial, ..} => { + (partial, self.escaped_value()) + }, + _ => return (false, false), + }; + match self { ValuesRemove::None => (), - ValuesRemove::KnownKeys(to_skip) => { + ValuesRemove::KnownKeysEscaped(to_skip) + | ValuesRemove::KnownKeys(to_skip) => { if let Some(next) = to_skip.next_key { let mut node_key = prefix.clone(); - match node.node_plan() { - NodePlan::NibbledBranch{partial, value: Some(_), ..} - | NodePlan::Leaf {partial, ..} => { - let node_data = node.data(); - let partial = partial.build(node_data); - node_key.append_partial(partial.right()); - }, - _ => (), - }; + let node_data = node.data(); + let partial = partial.build(node_data); + node_key.append_partial(partial.right()); // comparison is redundant with previous checks, could be optimized. let node_key = LeftNibbleSlice::new(node_key.inner()).truncate(node_key.len()); @@ -392,7 +419,8 @@ impl<'a, I: Iterator> ValuesRemove<'a, I> { }; }, } - (false, false) + + (false, escaped_value) } } @@ -788,14 +816,18 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) /// to match 'encode_compact_skip_values'. /// /// Skipped key values must be ordered. -pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, V>(db: &mut DB, encoded: I, fetcher: F, skipped: V) - -> Result<(TrieHash, usize), TrieHash, CError> +pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, K>( + db: &mut DB, + encoded: I, + fetcher: F, + skipped: K, +) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, DB: HashDB, I: IntoIterator, F: LazyFetcher<'a>, - V: IntoIterator, + K: IntoIterator, { let skipped = ValuesInsert::KnownKeys(SkipKeyValues::new(skipped.into_iter(), fetcher)); decode_compact_with_skipped_inner::(db, encoded.into_iter(), skipped) @@ -915,5 +947,3 @@ impl<'a> LazyFetcher<'a> for &'a crate::rstd::BTreeMap<&'a [u8], &'a [u8]> { self.get(key).map(|value| Cow::Borrowed(*value)) } } - - diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 22fc28c2..3597efde 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -28,9 +28,11 @@ type MemoryDB = memory_db::MemoryDB, DBValue>; enum EncodeType<'a> { SkipKeys(&'a BTreeSet<&'static [u8]>), + SkipKeysEscaped(&'a BTreeSet<&'static [u8]>), All, None, } + fn test_encode_compact( entries: Vec<(&'static [u8], &'static [u8])>, keys: Vec<&'static [u8]>, @@ -79,7 +81,18 @@ fn test_encode_compact( trie_db::encode_compact_skip_all_values::(&trie).unwrap() }, EncodeType::SkipKeys(skip_keys) => { - trie_db::encode_compact_skip_values::(&trie, skip_keys.iter().map(|k| *k)).unwrap() + trie_db::encode_compact_skip_values::( + &trie, + skip_keys.iter().map(|k| *k), + false, + ).unwrap() + }, + EncodeType::SkipKeysEscaped(skip_keys) => { + trie_db::encode_compact_skip_values::( + &trie, + skip_keys.iter().map(|k| *k), + true, + ).unwrap() }, } }; From 75b85e7ae977ef5f4f1f3287bf8f5e6a337289c3 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 15:14:09 +0100 Subject: [PATCH 21/31] fix for branch usage --- trie-db/src/lib.rs | 3 +- trie-db/src/node.rs | 8 ++ trie-db/src/trie_codec.rs | 191 ++++++++++++++++++++++++++++----- trie-db/test/src/trie_codec.rs | 81 +++++++++++++- 4 files changed, 256 insertions(+), 27 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 778e07ba..4a7c89b0 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -76,7 +76,8 @@ pub use crate::iterator::TrieDBNodeIterator; pub use crate::trie_codec::{encode_compact, encode_compact_skip_values, decode_compact, decode_compact_from_iter, decode_compact_with_skipped_values, decode_compact_with_encoded_skipped_values, encode_compact_skip_all_values, - LazyFetcher}; + LazyFetcher, compact_conditions, encode_compact_skip_conditional, + encode_compact_skip_conditional_with_key}; #[cfg(feature = "std")] pub use crate::iter_build::TrieRootPrint; diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index ef50e8d6..a0c90045 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -95,6 +95,14 @@ impl NibbleSlicePlan { } } + /// A empty nibbleslice. + pub fn empty() -> Self { + NibbleSlicePlan { + bytes: 0..0, + offset: 0, + } + } + /// Returns the nibble length of the slice. pub fn len(&self) -> usize { (self.bytes.end - self.bytes.start) * nibble_ops::NIBBLE_PER_BYTE - self.offset diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index aa67841f..ba6e1dbc 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -153,16 +153,27 @@ impl EncoderStackEntry { } NodePlan::NibbledBranch { partial, value, children } => { let value = if self.omit_value { - empty_value + empty_value.map(Cow::Borrowed) } else { - value.clone().map(|range| &node_data[range]) + value.clone().map(|range| { + let node_data = &node_data[range]; + if self.escape_value { + if let Some(escaped) = encode_empty_escape(node_data) { + escaped + } else { + node_data.into() + } + } else { + node_data.into() + } + }) }; let partial = partial.build(node_data); C::branch_node_nibbled( partial.right_iter(), partial.len(), Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value, + value.as_ref().map(|v| &v[..]), ) } }) @@ -212,7 +223,7 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE L: TrieLayout, { let to_skip = ValuesRemove::None; - encode_compact_skip_values_inner::>(db, to_skip) + encode_compact_skip_values_inner::, ()>(db, to_skip) } /// Variant of 'encode_compact' where values for given key (those are required to be already know @@ -234,7 +245,7 @@ pub fn encode_compact_skip_values<'a, L, I>( } else { ValuesRemove::KnownKeys(SkipKeys::new(to_skip.into_iter())) }; - encode_compact_skip_values_inner(db, to_skip) + encode_compact_skip_values_inner::<_, _, ()>(db, to_skip) } /// Variant of 'encode_compact' where all values are removed and replace by empty value. @@ -243,14 +254,55 @@ pub fn encode_compact_skip_all_values<'a, L>(db: &TrieDB) -> Result>(db, to_skip) + encode_compact_skip_values_inner::, ()>(db, to_skip) +} + +/// Variant of 'encode_compact' where values are removed +/// for a given condition. +/// Condition uses values as parameters. +pub fn encode_compact_skip_conditional<'a, L, F>( + db: &TrieDB, + // TODO not &mut and implement for &mut. + value_skip_condition: &mut F, + escape_values: bool, +) -> Result>, TrieHash, CError> + where + L: TrieLayout, + F: FnMut(&[u8]) -> bool, +{ + let to_skip = if escape_values { + ValuesRemove::Conditional(NoKeyCondition(value_skip_condition)) + } else { + ValuesRemove::ConditionalNoEscape(NoKeyCondition(value_skip_condition)) + }; + encode_compact_skip_values_inner::, _>(db, to_skip) } +/// Variant of 'encode_compact' where values are removed +/// for a given condition. +/// Condition uses key and values as parameters. +pub fn encode_compact_skip_conditional_with_key<'a, L, F>( + db: &TrieDB, + value_skip_condition: &mut F, + escape_values: bool, +) -> Result>, TrieHash, CError> + where + L: TrieLayout, + F: FnMut(&NibbleVec, &[u8]) -> bool, +{ + let to_skip = if escape_values { + ValuesRemove::Conditional(value_skip_condition) + } else { + ValuesRemove::ConditionalNoEscape(value_skip_condition) + }; + encode_compact_skip_values_inner::, _>(db, to_skip) +} -fn encode_compact_skip_values_inner<'a, L, I>(db: &TrieDB, mut to_skip: ValuesRemove<'a, I>) -> Result>, TrieHash, CError> +fn encode_compact_skip_values_inner<'a, L, I, F>(db: &TrieDB, mut to_skip: ValuesRemove<'a, I, F>) -> Result>, TrieHash, CError> where L: TrieLayout, I: Iterator, + F: ValuesRemoveCondition, { let mut output = Vec::new(); @@ -336,13 +388,53 @@ fn encode_compact_skip_values_inner<'a, L, I>(db: &TrieDB, mut to_skip: Value Ok(output) } -enum ValuesRemove<'a, I> { +enum ValuesRemove<'a, I, F> { + Conditional(F), + // only make sense if the condition does collect the keys. + // so they are known. + ConditionalNoEscape(F), KnownKeys(SkipKeys<'a, I>), KnownKeysEscaped(SkipKeys<'a, I>), AllEscaped, None, } +trait ValuesRemoveCondition { + const NEED_KEY: bool; + + fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool; +} + +impl ValuesRemoveCondition for () { + const NEED_KEY: bool = false; + + fn check(&mut self, _key: &NibbleVec, _value: &[u8]) -> bool { + false + } +} + +impl ValuesRemoveCondition for F + where F: FnMut(&NibbleVec, &[u8]) -> bool, +{ + const NEED_KEY: bool = true; + + fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool { + self(key, value) + } +} + +struct NoKeyCondition<'a, F>(&'a mut F); + +impl<'a, F> ValuesRemoveCondition for NoKeyCondition<'a, F> + where F: FnMut(&[u8]) -> bool, +{ + const NEED_KEY: bool = false; + + fn check(&mut self, _key: &NibbleVec, value: &[u8]) -> bool { + self.0(value) + } +} + struct SkipKeys<'a, I> { keys: I, next_key: Option<&'a [u8]>, @@ -358,27 +450,35 @@ impl<'a, I: Iterator> SkipKeys<'a, I> { } } -impl<'a, I: Iterator> ValuesRemove<'a, I> { +impl<'a, I: Iterator, F> ValuesRemove<'a, I, F> + where + F: ValuesRemoveCondition, +{ fn escaped_value( &self, ) -> bool { match self { - ValuesRemove::KnownKeysEscaped(..) => true, + ValuesRemove::KnownKeysEscaped(..) + | ValuesRemove::Conditional(..) => true, // all remove means that escape on remaining values // is not needed. - ValuesRemove::AllEscaped => false, - ValuesRemove::None => false, - ValuesRemove::KnownKeys(..) => false, + ValuesRemove::AllEscaped + | ValuesRemove::ConditionalNoEscape(..) + | ValuesRemove::None + | ValuesRemove::KnownKeys(..) => false, } } // return (omit_value, escape_value) fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> (bool, bool) { - let (partial, escaped_value) = match node.node_plan() { - NodePlan::NibbledBranch{partial, value: Some(_), ..} - | NodePlan::Leaf {partial, ..} => { - (partial, self.escaped_value()) + let (partial, escaped_value, value) = match node.node_plan() { + NodePlan::NibbledBranch{ partial, value: Some(value), ..} + | NodePlan::Leaf {partial, value} => { + (partial.clone(), self.escaped_value(), value) + }, + NodePlan::Branch{ value: Some(value), ..} => { + (crate::node::NibbleSlicePlan::empty(), self.escaped_value(), value) }, _ => return (false, false), }; @@ -388,7 +488,7 @@ impl<'a, I: Iterator> ValuesRemove<'a, I> { ValuesRemove::KnownKeysEscaped(to_skip) | ValuesRemove::KnownKeys(to_skip) => { if let Some(next) = to_skip.next_key { - let mut node_key = prefix.clone(); + let mut node_key = prefix.clone(); // TODO use &mut prefix?? let node_data = node.data(); let partial = partial.build(node_data); node_key.append_partial(partial.right()); @@ -410,13 +510,20 @@ impl<'a, I: Iterator> ValuesRemove<'a, I> { } }, ValuesRemove::AllEscaped => { - match node.node_plan() { - NodePlan::NibbledBranch{value: Some(_), ..} - | NodePlan::Leaf {..} => { - return (true, false); - }, - _ => (), - }; + return (true, false); + }, + ValuesRemove::ConditionalNoEscape(condition) + | ValuesRemove::Conditional(condition) => { + let node_data = node.data(); + let value = &node_data[value.clone()]; + if F::NEED_KEY { + let mut node_key = prefix.clone(); + let partial = partial.build(node_data); + node_key.append_partial(partial.right()); + return (condition.check(&node_key, value), escaped_value); + } else { + return (condition.check(prefix, value), escaped_value); + } }, } @@ -572,6 +679,13 @@ impl< None }) }, + Node::Branch(_, Some(value)) => { + (crate::nibble::NibbleSlice::new(&[]), value.is_empty(), if self.escaped_value() { + decode_empty_escaped(value) + } else { + None + }) + }, _ => return true, }; @@ -947,3 +1061,30 @@ impl<'a> LazyFetcher<'a> for &'a crate::rstd::BTreeMap<&'a [u8], &'a [u8]> { self.get(key).map(|value| Cow::Borrowed(*value)) } } + +/// Implementation of condition to use for removing values. +pub mod compact_conditions { + use super::*; + + /// Treshold size condition for removing values from proof. + pub fn skip_treshold(treshold: usize) -> impl FnMut(&[u8]) -> bool { + move |value: &[u8]| { + value.len() > treshold + } + } + + /// Treshold size condition for removing values from proof. + pub fn skip_treshold_collect_keys<'a>( + treshold: usize, + keys: &'a mut Vec>, + ) -> impl FnMut(&NibbleVec, &[u8]) -> bool + 'a { + move |key: &NibbleVec, value: &[u8]| { + if value.len() > treshold { + keys.push(key.as_prefix().0.to_vec()); + true + } else { + false + } + } + } +} diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 3597efde..dc473ec2 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -20,7 +20,7 @@ use trie_db::{ }; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; use reference_trie::{ - ExtensionLayout, NoExtensionLayout, + ExtensionLayout, NoExtensionLayout, AllowEmptyLayout, }; use std::collections::{BTreeSet, BTreeMap}; @@ -29,6 +29,9 @@ type MemoryDB = memory_db::MemoryDB, DBValue>; enum EncodeType<'a> { SkipKeys(&'a BTreeSet<&'static [u8]>), SkipKeysEscaped(&'a BTreeSet<&'static [u8]>), + Treshold(usize), + TresholdEscaped(usize), + TresholdCollect(usize, &'a mut Vec>), All, None, } @@ -94,16 +97,39 @@ fn test_encode_compact( true, ).unwrap() }, + EncodeType::Treshold(treshold) => { + trie_db::encode_compact_skip_conditional::( + &trie, + &mut trie_db::compact_conditions::skip_treshold(treshold), + false, + ).unwrap() + }, + EncodeType::TresholdCollect(treshold, keys) => { + trie_db::encode_compact_skip_conditional_with_key::( + &trie, + &mut trie_db::compact_conditions::skip_treshold_collect_keys(treshold, keys), + false, + ).unwrap() + }, + EncodeType::TresholdEscaped(treshold) => { + trie_db::encode_compact_skip_conditional::( + &trie, + &mut trie_db::compact_conditions::skip_treshold(treshold), + true, + ).unwrap() + }, } }; (root, compact_trie, items) } + enum DecodeType<'a> { None, SkippedValues(&'a BTreeMap<&'static [u8], &'static [u8]>), Escaped(&'a BTreeMap<&'static [u8], &'static [u8]>), } + fn test_decode_compact( encoded: &[Vec], items: Vec<(&'static [u8], Option)>, @@ -306,3 +332,56 @@ fn trie_decoding_fails_with_incomplete_database() { _ => panic!("decode was unexpectedly successful"), } } + + +#[test] +fn trie_encode_skip_condition() { + let additional_values = vec![ + (&b"dumy1"[..], &b""[..]), + (&b"dumy1_"[..], &[1; 32]), // force parent to be not inline + (b"dumy2", b"Esc"), + (&b"dumy2_"[..], &[2; 32]), // force parent to be not inline + (b"dumy3", b"Esc"), + (&b"dumy3_"[..], &[3; 32]), // force parent to be not inline + (b"dumy4", b"Esc"), + (&b"dumy4_"[..], &[4; 32]), // force parent to be not inline + (b"dumy5", b"Escd"), + (&b"dumy5_"[..], &[5; 32]), // force parent to be not inline + ]; + let mut test_set = test_set(); + test_set.extend(additional_values.iter().cloned()); + let mut test_proof_default = test_proof_default(); + test_proof_default.extend(additional_values.iter().filter_map(|kv| + if kv.0.len() == 5 { + Some(kv.0) + } else { + None + } + )); + let (_, encoded, _) = test_encode_compact::( + test_set.clone(), + test_proof_default.clone(), + EncodeType::None, + ); + + let none_size = encoded.iter().map(|e| e.len()).sum::(); + let mut keys = Vec::new(); + let (_, encoded, _) = test_encode_compact::( + test_set.clone(), + test_proof_default.clone(), + EncodeType::TresholdCollect(26, &mut keys), + ); + let six_size = encoded.iter().map(|e| e.len()).sum::(); + assert_eq!(keys, vec![b"doge".to_vec()]); + // only one 32 byte value skipped + assert_eq!(none_size, six_size + 32); + let (_, encoded, _) = test_encode_compact::( + test_set.clone(), + test_proof_default.clone(), + EncodeType::TresholdEscaped(26), + ); + let six_escaped_size = encoded.iter().map(|e| e.len()).sum::(); + // from additional value: +4 for escapped empty and +3 for each starting + // escape seq + assert_eq!(none_size, six_escaped_size + 32 - 4 - 3); +} From 258f5bf9c125bac2cfc6d892464fdca93b64a9ac Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 15:30:18 +0100 Subject: [PATCH 22/31] use enum instead of pair of bool --- trie-db/src/trie_codec.rs | 111 ++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index ba6e1dbc..670df451 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -37,6 +37,12 @@ use crate::rstd::{ borrow::Cow, cmp::Ordering, mem, }; +enum OmitValue { + OmitValue, + EscapValue, + None, +} + struct EncoderStackEntry { /// The prefix is the nibble path to the node in the trie. prefix: NibbleVec, @@ -46,10 +52,8 @@ struct EncoderStackEntry { child_index: usize, /// Flags indicating whether each child is omitted in the encoded node. omit_children: Vec, - /// Flags indicating whether we should omit value in the encoded node. - omit_value: bool, - /// Flags indicating whether we should escape a value. - escape_value: bool, + /// Enum indicating whether we should omit value in the encoded node. + omit_value: OmitValue, /// The encoding of the subtrie nodes rooted at this entry, which is built up in /// `encode_compact`. output_index: usize, @@ -102,22 +106,26 @@ impl EncoderStackEntry { /// Generates the encoding of the subtrie rooted at this entry. fn encode_node(&self) -> Result, C::HashOut, C::Error> { - let empty_value = Some(&[][..]); let node_data = self.node.data(); Ok(match self.node.node_plan() { NodePlan::Empty => node_data.to_vec(), NodePlan::Leaf { partial, value } => { let partial = partial.build(node_data); - if self.omit_value { - C::leaf_node(partial.right(), &[][..]) - } else if self.escape_value { - if let Some(escaped) = encode_empty_escape(&node_data[value.clone()]) { - C::leaf_node(partial.right(), &escaped[..]) - } else { + + match self.omit_value { + OmitValue::OmitValue => { + C::leaf_node(partial.right(), &[][..]) + }, + OmitValue::EscapValue => { + if let Some(escaped) = encode_empty_escape(&node_data[value.clone()]) { + C::leaf_node(partial.right(), &escaped[..]) + } else { + node_data.to_vec() + } + }, + OmitValue::None => { node_data.to_vec() - } - } else { - node_data.to_vec() + }, } }, NodePlan::Extension { partial, child: _ } => { @@ -130,44 +138,48 @@ impl EncoderStackEntry { } } NodePlan::Branch { value, children } => { - let value = if self.omit_value { - empty_value.map(Cow::Borrowed) - } else { - value.clone().map(|range| { - let node_data = &node_data[range]; - if self.escape_value { + let value = value.clone().map(|range| { + let node_data = &node_data[range]; + match self.omit_value { + OmitValue::OmitValue => { + Cow::Borrowed(&[][..]) + }, + OmitValue::EscapValue => { if let Some(escaped) = encode_empty_escape(node_data) { escaped } else { node_data.into() } - } else { + }, + OmitValue::None => { node_data.into() - } - }) - }; + }, + } + }); C::branch_node( Self::branch_children(node_data, &children, &self.omit_children)?.iter(), value.as_ref().map(|v| &v[..]), ) } NodePlan::NibbledBranch { partial, value, children } => { - let value = if self.omit_value { - empty_value.map(Cow::Borrowed) - } else { - value.clone().map(|range| { - let node_data = &node_data[range]; - if self.escape_value { + let value = value.clone().map(|range| { + let node_data = &node_data[range]; + match self.omit_value { + OmitValue::OmitValue => { + Cow::Borrowed(&[][..]) + }, + OmitValue::EscapValue => { if let Some(escaped) = encode_empty_escape(node_data) { escaped } else { node_data.into() } - } else { + }, + OmitValue::None => { node_data.into() - } - }) - }; + }, + } + }); let partial = partial.build(node_data); C::branch_node_nibbled( partial.right_iter(), @@ -356,14 +368,13 @@ fn encode_compact_skip_values_inner<'a, L, I, F>(db: &TrieDB, mut to_skip: Va NodePlan::Extension { .. } => 1, NodePlan::Branch { .. } | NodePlan::NibbledBranch { .. } => NIBBLE_LENGTH, }; - let (omit_value, escape_value) = to_skip.skip_new_node_value(&prefix, &node); + let omit_value = to_skip.skip_new_node_value(&prefix, &node); stack.push(EncoderStackEntry { prefix, node, child_index: 0, omit_children: vec![false; children_len], omit_value, - escape_value, output_index: output.len(), _marker: PhantomData::default(), }); @@ -456,21 +467,21 @@ impl<'a, I: Iterator, F> ValuesRemove<'a, I, F> { fn escaped_value( &self, - ) -> bool { + ) -> OmitValue { match self { ValuesRemove::KnownKeysEscaped(..) - | ValuesRemove::Conditional(..) => true, + | ValuesRemove::Conditional(..) => OmitValue::EscapValue, // all remove means that escape on remaining values // is not needed. ValuesRemove::AllEscaped | ValuesRemove::ConditionalNoEscape(..) | ValuesRemove::None - | ValuesRemove::KnownKeys(..) => false, + | ValuesRemove::KnownKeys(..) => OmitValue::None, } } // return (omit_value, escape_value) - fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> (bool, bool) { + fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> OmitValue { let (partial, escaped_value, value) = match node.node_plan() { NodePlan::NibbledBranch{ partial, value: Some(value), ..} @@ -480,7 +491,7 @@ impl<'a, I: Iterator, F> ValuesRemove<'a, I, F> NodePlan::Branch{ value: Some(value), ..} => { (crate::node::NibbleSlicePlan::empty(), self.escaped_value(), value) }, - _ => return (false, false), + _ => return OmitValue::None, }; match self { @@ -503,14 +514,14 @@ impl<'a, I: Iterator, F> ValuesRemove<'a, I, F> }, Ordering::Equal => { to_skip.next_key = to_skip.keys.next(); - return (true, false); + return OmitValue::OmitValue; }, Ordering::Greater => (), }; } }, ValuesRemove::AllEscaped => { - return (true, false); + return OmitValue::OmitValue; }, ValuesRemove::ConditionalNoEscape(condition) | ValuesRemove::Conditional(condition) => { @@ -520,14 +531,22 @@ impl<'a, I: Iterator, F> ValuesRemove<'a, I, F> let mut node_key = prefix.clone(); let partial = partial.build(node_data); node_key.append_partial(partial.right()); - return (condition.check(&node_key, value), escaped_value); + return if condition.check(&node_key, value) { + OmitValue::OmitValue + } else { + escaped_value + }; } else { - return (condition.check(prefix, value), escaped_value); + return if condition.check(&prefix, value) { + OmitValue::OmitValue + } else { + escaped_value + }; } }, } - (false, escaped_value) + escaped_value } } From c377a1c99a3de8deb5c8744f29c8c8aa8a85219d Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 15:46:32 +0100 Subject: [PATCH 23/31] replace test on skip key by conditional implementation --- trie-db/src/trie_codec.rs | 50 +++++++++++++++++++++++++++------- trie-db/test/src/trie_codec.rs | 12 +++++--- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 670df451..f9ad9571 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -37,15 +37,10 @@ use crate::rstd::{ borrow::Cow, cmp::Ordering, mem, }; -enum OmitValue { - OmitValue, - EscapValue, - None, -} - struct EncoderStackEntry { /// The prefix is the nibble path to the node in the trie. prefix: NibbleVec, + /// Node stacked. node: Rc>, /// The next entry in the stack is a child of the preceding entry at this index. For branch /// nodes, the index is in [0, NIBBLE_LENGTH] and for extension nodes, the index is in [0, 1]. @@ -60,6 +55,12 @@ struct EncoderStackEntry { _marker: PhantomData, } +enum OmitValue { + OmitValue, + EscapeValue, + None, +} + impl EncoderStackEntry { /// Given the prefix of the next child node, identify its index and advance `child_index` to /// that. For a given entry, this must be called sequentially only with strictly increasing @@ -116,7 +117,7 @@ impl EncoderStackEntry { OmitValue::OmitValue => { C::leaf_node(partial.right(), &[][..]) }, - OmitValue::EscapValue => { + OmitValue::EscapeValue => { if let Some(escaped) = encode_empty_escape(&node_data[value.clone()]) { C::leaf_node(partial.right(), &escaped[..]) } else { @@ -144,7 +145,7 @@ impl EncoderStackEntry { OmitValue::OmitValue => { Cow::Borrowed(&[][..]) }, - OmitValue::EscapValue => { + OmitValue::EscapeValue => { if let Some(escaped) = encode_empty_escape(node_data) { escaped } else { @@ -168,7 +169,7 @@ impl EncoderStackEntry { OmitValue::OmitValue => { Cow::Borrowed(&[][..]) }, - OmitValue::EscapValue => { + OmitValue::EscapeValue => { if let Some(escaped) = encode_empty_escape(node_data) { escaped } else { @@ -470,7 +471,7 @@ impl<'a, I: Iterator, F> ValuesRemove<'a, I, F> ) -> OmitValue { match self { ValuesRemove::KnownKeysEscaped(..) - | ValuesRemove::Conditional(..) => OmitValue::EscapValue, + | ValuesRemove::Conditional(..) => OmitValue::EscapeValue, // all remove means that escape on remaining values // is not needed. ValuesRemove::AllEscaped @@ -1106,4 +1107,33 @@ pub mod compact_conditions { } } } + + /// Skip keys from an iterator. + pub fn skip_given_ordered_keys<'a>( + iter: impl IntoIterator + 'a, + ) -> impl FnMut(&NibbleVec, &[u8]) -> bool + 'a { + let mut iter = iter.into_iter(); + let mut next_key = iter.next(); + move |node_key: &NibbleVec, _value: &[u8]| { + loop { + if let Some(next) = next_key { + // comparison is redundant with previous checks, could be optimized. + let node_key = LeftNibbleSlice::new(node_key.inner()).truncate(node_key.len()); + let next = LeftNibbleSlice::new(next); + match next.cmp(&node_key) { + Ordering::Less => { + next_key = iter.next(); + }, + Ordering::Equal => { + next_key = iter.next(); + return true; + }, + Ordering::Greater => break, + }; + } + } + + false + } + } } diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index dc473ec2..4268fc61 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -84,16 +84,20 @@ fn test_encode_compact( trie_db::encode_compact_skip_all_values::(&trie).unwrap() }, EncodeType::SkipKeys(skip_keys) => { - trie_db::encode_compact_skip_values::( + trie_db::encode_compact_skip_conditional_with_key::( &trie, - skip_keys.iter().map(|k| *k), + &mut trie_db::compact_conditions::skip_given_ordered_keys( + skip_keys.iter().map(|k| *k), + ), false, ).unwrap() }, EncodeType::SkipKeysEscaped(skip_keys) => { - trie_db::encode_compact_skip_values::( + trie_db::encode_compact_skip_conditional_with_key::( &trie, - skip_keys.iter().map(|k| *k), + &mut trie_db::compact_conditions::skip_given_ordered_keys( + skip_keys.iter().map(|k| *k), + ), true, ).unwrap() }, From e6c2fa9a958320fd98d92b07f9964241a152f3fa Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 15:58:27 +0100 Subject: [PATCH 24/31] first step in removing enum --- trie-db/src/lib.rs | 2 +- trie-db/src/trie_codec.rs | 106 ++++++++++----------------------- trie-db/test/src/trie_codec.rs | 18 ------ 3 files changed, 33 insertions(+), 93 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 4a7c89b0..9508b9cd 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -73,7 +73,7 @@ pub use crate::node_codec::{NodeCodec, Partial}; pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; pub use crate::iterator::TrieDBNodeIterator; -pub use crate::trie_codec::{encode_compact, encode_compact_skip_values, +pub use crate::trie_codec::{encode_compact, decode_compact, decode_compact_from_iter, decode_compact_with_skipped_values, decode_compact_with_encoded_skipped_values, encode_compact_skip_all_values, LazyFetcher, compact_conditions, encode_compact_skip_conditional, diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index f9ad9571..c142805c 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -236,29 +236,7 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE L: TrieLayout, { let to_skip = ValuesRemove::None; - encode_compact_skip_values_inner::, ()>(db, to_skip) -} - -/// Variant of 'encode_compact' where values for given key (those are required to be already know -/// when checking the produce proof with 'decode_compact_with_skipped_values') are skipped. -/// Skipped values are encoded as a 0 length value. -/// -/// The iterator need to be sorted. -pub fn encode_compact_skip_values<'a, L, I>( - db: &TrieDB, - to_skip: I, - escape_empty: bool, -) -> Result>, TrieHash, CError> - where - L: TrieLayout, - I: IntoIterator, -{ - let to_skip = if escape_empty { - ValuesRemove::KnownKeysEscaped(SkipKeys::new(to_skip.into_iter())) - } else { - ValuesRemove::KnownKeys(SkipKeys::new(to_skip.into_iter())) - }; - encode_compact_skip_values_inner::<_, _, ()>(db, to_skip) + encode_compact_skip_values_inner::(db, to_skip) } /// Variant of 'encode_compact' where all values are removed and replace by empty value. @@ -267,7 +245,7 @@ pub fn encode_compact_skip_all_values<'a, L>(db: &TrieDB) -> Result, ()>(db, to_skip) + encode_compact_skip_values_inner::(db, to_skip) } /// Variant of 'encode_compact' where values are removed @@ -288,7 +266,7 @@ pub fn encode_compact_skip_conditional<'a, L, F>( } else { ValuesRemove::ConditionalNoEscape(NoKeyCondition(value_skip_condition)) }; - encode_compact_skip_values_inner::, _>(db, to_skip) + encode_compact_skip_values_inner::(db, to_skip) } /// Variant of 'encode_compact' where values are removed @@ -308,13 +286,12 @@ pub fn encode_compact_skip_conditional_with_key<'a, L, F>( } else { ValuesRemove::ConditionalNoEscape(value_skip_condition) }; - encode_compact_skip_values_inner::, _>(db, to_skip) + encode_compact_skip_values_inner::(db, to_skip) } -fn encode_compact_skip_values_inner<'a, L, I, F>(db: &TrieDB, mut to_skip: ValuesRemove<'a, I, F>) -> Result>, TrieHash, CError> +fn encode_compact_skip_values_inner<'a, L, F>(db: &TrieDB, mut to_skip: ValuesRemove) -> Result>, TrieHash, CError> where L: TrieLayout, - I: Iterator, F: ValuesRemoveCondition, { let mut output = Vec::new(); @@ -400,25 +377,29 @@ fn encode_compact_skip_values_inner<'a, L, I, F>(db: &TrieDB, mut to_skip: Va Ok(output) } -enum ValuesRemove<'a, I, F> { +enum ValuesRemove { Conditional(F), // only make sense if the condition does collect the keys. // so they are known. ConditionalNoEscape(F), - KnownKeys(SkipKeys<'a, I>), - KnownKeysEscaped(SkipKeys<'a, I>), AllEscaped, None, } trait ValuesRemoveCondition { + const ESCAPE: bool; + const REMOVE_NONE: bool; + const REMOVE_ALL: bool; const NEED_KEY: bool; fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool; } impl ValuesRemoveCondition for () { + const REMOVE_NONE: bool = true; + const REMOVE_ALL: bool = false; const NEED_KEY: bool = false; + const ESCAPE: bool = false; fn check(&mut self, _key: &NibbleVec, _value: &[u8]) -> bool { false @@ -428,7 +409,10 @@ impl ValuesRemoveCondition for () { impl ValuesRemoveCondition for F where F: FnMut(&NibbleVec, &[u8]) -> bool, { + const REMOVE_NONE: bool = false; + const REMOVE_ALL: bool = false; const NEED_KEY: bool = true; + const ESCAPE: bool = false; fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool { self(key, value) @@ -440,44 +424,42 @@ struct NoKeyCondition<'a, F>(&'a mut F); impl<'a, F> ValuesRemoveCondition for NoKeyCondition<'a, F> where F: FnMut(&[u8]) -> bool, { + const REMOVE_NONE: bool = false; + const REMOVE_ALL: bool = false; const NEED_KEY: bool = false; + const ESCAPE: bool = false; fn check(&mut self, _key: &NibbleVec, value: &[u8]) -> bool { self.0(value) } } -struct SkipKeys<'a, I> { - keys: I, - next_key: Option<&'a [u8]>, -} +struct Escape<'a, F>(&'a mut F); -impl<'a, I: Iterator> SkipKeys<'a, I> { - fn new(mut keys: I) -> Self { - let next_key = keys.next(); - SkipKeys { - keys, - next_key, - } +impl<'a, F> ValuesRemoveCondition for Escape<'a, F> + where F: ValuesRemoveCondition, +{ + const REMOVE_NONE: bool = F::REMOVE_NONE; + const REMOVE_ALL: bool = F::REMOVE_ALL; + const NEED_KEY: bool = F::NEED_KEY; + const ESCAPE: bool = true; + + fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool { + self.0.check(key, value) } } -impl<'a, I: Iterator, F> ValuesRemove<'a, I, F> - where - F: ValuesRemoveCondition, -{ +impl<'a, F: ValuesRemoveCondition> ValuesRemove { fn escaped_value( &self, ) -> OmitValue { match self { - ValuesRemove::KnownKeysEscaped(..) - | ValuesRemove::Conditional(..) => OmitValue::EscapeValue, + ValuesRemove::Conditional(..) => OmitValue::EscapeValue, // all remove means that escape on remaining values // is not needed. ValuesRemove::AllEscaped | ValuesRemove::ConditionalNoEscape(..) - | ValuesRemove::None - | ValuesRemove::KnownKeys(..) => OmitValue::None, + | ValuesRemove::None => OmitValue::None, } } @@ -497,30 +479,6 @@ impl<'a, I: Iterator, F> ValuesRemove<'a, I, F> match self { ValuesRemove::None => (), - ValuesRemove::KnownKeysEscaped(to_skip) - | ValuesRemove::KnownKeys(to_skip) => { - if let Some(next) = to_skip.next_key { - let mut node_key = prefix.clone(); // TODO use &mut prefix?? - let node_data = node.data(); - let partial = partial.build(node_data); - node_key.append_partial(partial.right()); - - // comparison is redundant with previous checks, could be optimized. - let node_key = LeftNibbleSlice::new(node_key.inner()).truncate(node_key.len()); - let next = LeftNibbleSlice::new(next); - match next.cmp(&node_key) { - Ordering::Less => { - to_skip.next_key = to_skip.keys.next(); - return self.skip_new_node_value(prefix, node); - }, - Ordering::Equal => { - to_skip.next_key = to_skip.keys.next(); - return OmitValue::OmitValue; - }, - Ordering::Greater => (), - }; - } - }, ValuesRemove::AllEscaped => { return OmitValue::OmitValue; }, diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 4268fc61..a3335f50 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -28,8 +28,6 @@ type MemoryDB = memory_db::MemoryDB, DBValue>; enum EncodeType<'a> { SkipKeys(&'a BTreeSet<&'static [u8]>), - SkipKeysEscaped(&'a BTreeSet<&'static [u8]>), - Treshold(usize), TresholdEscaped(usize), TresholdCollect(usize, &'a mut Vec>), All, @@ -92,22 +90,6 @@ fn test_encode_compact( false, ).unwrap() }, - EncodeType::SkipKeysEscaped(skip_keys) => { - trie_db::encode_compact_skip_conditional_with_key::( - &trie, - &mut trie_db::compact_conditions::skip_given_ordered_keys( - skip_keys.iter().map(|k| *k), - ), - true, - ).unwrap() - }, - EncodeType::Treshold(treshold) => { - trie_db::encode_compact_skip_conditional::( - &trie, - &mut trie_db::compact_conditions::skip_treshold(treshold), - false, - ).unwrap() - }, EncodeType::TresholdCollect(treshold, keys) => { trie_db::encode_compact_skip_conditional_with_key::( &trie, From ccb936820c887abf66e4d7fa369d2384f81fdd75 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 16:56:29 +0100 Subject: [PATCH 25/31] remove useless mut ptremove useless mut ptrr --- trie-db/src/lib.rs | 4 +- trie-db/src/trie_codec.rs | 227 ++++++++++++++++----------------- trie-db/test/src/trie_codec.rs | 10 +- 3 files changed, 118 insertions(+), 123 deletions(-) diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 9508b9cd..2cc2f8f6 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -74,8 +74,8 @@ pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; pub use crate::iterator::TrieDBNodeIterator; pub use crate::trie_codec::{encode_compact, - decode_compact, decode_compact_from_iter, decode_compact_with_skipped_values, - decode_compact_with_encoded_skipped_values, encode_compact_skip_all_values, + decode_compact, decode_compact_from_iter, decode_compact_with_known_values, + decode_compact_for_encoded_skipped_values, encode_compact_skip_all_values, LazyFetcher, compact_conditions, encode_compact_skip_conditional, encode_compact_skip_conditional_with_key}; diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index c142805c..59c6c821 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -235,8 +235,7 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE where L: TrieLayout, { - let to_skip = ValuesRemove::None; - encode_compact_skip_values_inner::(db, to_skip) + encode_compact_skip_values_inner::(db, ()) } /// Variant of 'encode_compact' where all values are removed and replace by empty value. @@ -244,8 +243,7 @@ pub fn encode_compact_skip_all_values<'a, L>(db: &TrieDB) -> Result(db, to_skip) + encode_compact_skip_values_inner::(db, All) } /// Variant of 'encode_compact' where values are removed @@ -253,20 +251,19 @@ pub fn encode_compact_skip_all_values<'a, L>(db: &TrieDB) -> Result( db: &TrieDB, - // TODO not &mut and implement for &mut. - value_skip_condition: &mut F, + value_skip_condition: F, escape_values: bool, ) -> Result>, TrieHash, CError> where L: TrieLayout, F: FnMut(&[u8]) -> bool, { - let to_skip = if escape_values { - ValuesRemove::Conditional(NoKeyCondition(value_skip_condition)) + let to_skip = NoKeyCondition(value_skip_condition); + if escape_values { + encode_compact_skip_values_inner::(db, Escape(to_skip)) } else { - ValuesRemove::ConditionalNoEscape(NoKeyCondition(value_skip_condition)) - }; - encode_compact_skip_values_inner::(db, to_skip) + encode_compact_skip_values_inner::(db, to_skip) + } } /// Variant of 'encode_compact' where values are removed @@ -274,22 +271,22 @@ pub fn encode_compact_skip_conditional<'a, L, F>( /// Condition uses key and values as parameters. pub fn encode_compact_skip_conditional_with_key<'a, L, F>( db: &TrieDB, - value_skip_condition: &mut F, + value_skip_condition: F, escape_values: bool, ) -> Result>, TrieHash, CError> where L: TrieLayout, F: FnMut(&NibbleVec, &[u8]) -> bool, { - let to_skip = if escape_values { - ValuesRemove::Conditional(value_skip_condition) + let to_skip = WithKeyCondition(value_skip_condition); + if escape_values { + encode_compact_skip_values_inner::(db, Escape(to_skip)) } else { - ValuesRemove::ConditionalNoEscape(value_skip_condition) - }; - encode_compact_skip_values_inner::(db, to_skip) + encode_compact_skip_values_inner::(db, to_skip) + } } -fn encode_compact_skip_values_inner<'a, L, F>(db: &TrieDB, mut to_skip: ValuesRemove) -> Result>, TrieHash, CError> +fn encode_compact_skip_values_inner<'a, L, F>(db: &TrieDB, mut to_skip: F) -> Result>, TrieHash, CError> where L: TrieLayout, F: ValuesRemoveCondition, @@ -377,145 +374,144 @@ fn encode_compact_skip_values_inner<'a, L, F>(db: &TrieDB, mut to_skip: Value Ok(output) } -enum ValuesRemove { - Conditional(F), - // only make sense if the condition does collect the keys. - // so they are known. - ConditionalNoEscape(F), - AllEscaped, - None, -} - trait ValuesRemoveCondition { - const ESCAPE: bool; + const ESCAPE: OmitValue; const REMOVE_NONE: bool; const REMOVE_ALL: bool; const NEED_KEY: bool; - fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool; + fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool; + + // return (omit_value, escape_value) + fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> OmitValue { + + if Self::REMOVE_ALL { + return OmitValue::OmitValue; + } + if Self::REMOVE_NONE { + return Self::ESCAPE; + } + let (partial, value) = match node.node_plan() { + NodePlan::NibbledBranch{ partial, value: Some(value), ..} + | NodePlan::Leaf {partial, value} => { + (partial.clone(), value) + }, + NodePlan::Branch{ value: Some(value), ..} => { + (crate::node::NibbleSlicePlan::empty(), value) + }, + _ => return OmitValue::None, + }; + + let node_data = node.data(); + let value = &node_data[value.clone()]; + if Self::NEED_KEY { + let mut node_key = prefix.clone(); + let partial = partial.build(node_data); + node_key.append_partial(partial.right()); + return if self.check(&node_key, value) { + OmitValue::OmitValue + } else { + Self::ESCAPE + }; + } else { + return if self.check(&prefix, value) { + OmitValue::OmitValue + } else { + Self::ESCAPE + }; + } + } } impl ValuesRemoveCondition for () { const REMOVE_NONE: bool = true; const REMOVE_ALL: bool = false; const NEED_KEY: bool = false; - const ESCAPE: bool = false; + const ESCAPE: OmitValue = OmitValue::None; fn check(&mut self, _key: &NibbleVec, _value: &[u8]) -> bool { false } } -impl ValuesRemoveCondition for F +struct All; + +impl ValuesRemoveCondition for All { + const REMOVE_NONE: bool = false; + const REMOVE_ALL: bool = true; + const NEED_KEY: bool = false; + const ESCAPE: OmitValue = OmitValue::None; + + fn check(&mut self, _key: &NibbleVec, _value: &[u8]) -> bool { + true + } +} + +struct WithKeyCondition(F); + +impl ValuesRemoveCondition for WithKeyCondition where F: FnMut(&NibbleVec, &[u8]) -> bool, { const REMOVE_NONE: bool = false; const REMOVE_ALL: bool = false; const NEED_KEY: bool = true; - const ESCAPE: bool = false; + const ESCAPE: OmitValue = OmitValue::None; fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool { - self(key, value) + self.0(key, value) } } -struct NoKeyCondition<'a, F>(&'a mut F); +struct NoKeyCondition(F); -impl<'a, F> ValuesRemoveCondition for NoKeyCondition<'a, F> +impl ValuesRemoveCondition for NoKeyCondition where F: FnMut(&[u8]) -> bool, { const REMOVE_NONE: bool = false; const REMOVE_ALL: bool = false; const NEED_KEY: bool = false; - const ESCAPE: bool = false; + const ESCAPE: OmitValue = OmitValue::None; fn check(&mut self, _key: &NibbleVec, value: &[u8]) -> bool { self.0(value) } } -struct Escape<'a, F>(&'a mut F); +struct Escape(F); -impl<'a, F> ValuesRemoveCondition for Escape<'a, F> +impl ValuesRemoveCondition for Escape where F: ValuesRemoveCondition, { const REMOVE_NONE: bool = F::REMOVE_NONE; const REMOVE_ALL: bool = F::REMOVE_ALL; const NEED_KEY: bool = F::NEED_KEY; - const ESCAPE: bool = true; + const ESCAPE: OmitValue = OmitValue::EscapeValue; fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool { self.0.check(key, value) } } -impl<'a, F: ValuesRemoveCondition> ValuesRemove { - fn escaped_value( - &self, - ) -> OmitValue { - match self { - ValuesRemove::Conditional(..) => OmitValue::EscapeValue, - // all remove means that escape on remaining values - // is not needed. - ValuesRemove::AllEscaped - | ValuesRemove::ConditionalNoEscape(..) - | ValuesRemove::None => OmitValue::None, - } - } - - // return (omit_value, escape_value) - fn skip_new_node_value(&mut self, prefix: &NibbleVec, node: &Rc>) -> OmitValue { - - let (partial, escaped_value, value) = match node.node_plan() { - NodePlan::NibbledBranch{ partial, value: Some(value), ..} - | NodePlan::Leaf {partial, value} => { - (partial.clone(), self.escaped_value(), value) - }, - NodePlan::Branch{ value: Some(value), ..} => { - (crate::node::NibbleSlicePlan::empty(), self.escaped_value(), value) - }, - _ => return OmitValue::None, - }; - - match self { - ValuesRemove::None => (), - ValuesRemove::AllEscaped => { - return OmitValue::OmitValue; - }, - ValuesRemove::ConditionalNoEscape(condition) - | ValuesRemove::Conditional(condition) => { - let node_data = node.data(); - let value = &node_data[value.clone()]; - if F::NEED_KEY { - let mut node_key = prefix.clone(); - let partial = partial.build(node_data); - node_key.append_partial(partial.right()); - return if condition.check(&node_key, value) { - OmitValue::OmitValue - } else { - escaped_value - }; - } else { - return if condition.check(&prefix, value) { - OmitValue::OmitValue - } else { - escaped_value - }; - } - }, - } +impl<'a, F> ValuesRemoveCondition for &'a mut F + where F: ValuesRemoveCondition, +{ + const REMOVE_NONE: bool = F::REMOVE_NONE; + const REMOVE_ALL: bool = F::REMOVE_ALL; + const NEED_KEY: bool = F::NEED_KEY; + const ESCAPE: OmitValue = F::ESCAPE; - escaped_value + fn check(&mut self, key: &NibbleVec, value: &[u8]) -> bool { + (*self).check(key, value) } } enum ValuesInsert<'a, I, F> { None, - KnownKeys(SkipKeyValues<'a, I, F>), + KnownKeys(InsertAt<'a, I, F>), EscapedKeys(F), } -struct SkipKeyValues<'a, I, F> { +struct InsertAt<'a, I, F> { key_values: I, fetcher: F, next_key_value: Option<&'a [u8]>, @@ -525,10 +521,10 @@ impl< 'a, F: LazyFetcher<'a>, I: Iterator -> SkipKeyValues<'a, I, F> { +> InsertAt<'a, I, F> { fn new(mut key_values: I, fetcher: F) -> Self { let next_key_value = key_values.next(); - SkipKeyValues { + InsertAt { key_values, fetcher, next_key_value, @@ -900,19 +896,20 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) I: IntoIterator, { let skipped = ValuesInsert::, ()>::None; - decode_compact_with_skipped_inner::(db, encoded.into_iter(), skipped) + decode_compact_inner::(db, encoded.into_iter(), skipped) } /// Variant of 'decode_compact' that inject some known key values. /// Values are only added if the existing one is a zero length value, -/// to match 'encode_compact_skip_values'. +/// if the value exist and is not a zero length value, an error +/// is returned. /// -/// Skipped key values must be ordered. -pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, K>( +/// Known key in input must be ordered. +pub fn decode_compact_with_known_values<'a, L, DB, T, I, F, K>( db: &mut DB, encoded: I, fetcher: F, - skipped: K, + known_keys: K, ) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, @@ -921,15 +918,15 @@ pub fn decode_compact_with_skipped_values<'a, L, DB, T, I, F, K>( F: LazyFetcher<'a>, K: IntoIterator, { - let skipped = ValuesInsert::KnownKeys(SkipKeyValues::new(skipped.into_iter(), fetcher)); - decode_compact_with_skipped_inner::(db, encoded.into_iter(), skipped) + let known = ValuesInsert::KnownKeys(InsertAt::new(known_keys.into_iter(), fetcher)); + decode_compact_inner::(db, encoded.into_iter(), known) } -/// Variant of 'decode_compact' that try to fetch value when they where +/// Variant of 'decode_compact' that try to fetch value when they are /// skipped. -/// Skipped values are identified by being a 0 length value, the actual 0 length value -/// is escaped. -pub fn decode_compact_with_encoded_skipped_values<'a, L, DB, T, I, F>(db: &mut DB, encoded: I, fetcher: F) +/// Skipped values are encoded into a 0 length value, +/// actual 0 length value is escaped. +pub fn decode_compact_for_encoded_skipped_values<'a, L, DB, T, I, F>(db: &mut DB, encoded: I, fetcher: F) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, @@ -938,10 +935,10 @@ pub fn decode_compact_with_encoded_skipped_values<'a, L, DB, T, I, F>(db: &mut D F: LazyFetcher<'a>, { let skipped = ValuesInsert::EscapedKeys(fetcher); - decode_compact_with_skipped_inner::>(db, encoded.into_iter(), skipped) + decode_compact_inner::>(db, encoded.into_iter(), skipped) } -fn decode_compact_with_skipped_inner<'a, L, DB, T, I, F, V>( +fn decode_compact_inner<'a, L, DB, T, I, F, V>( db: &mut DB, encoded: I, mut skipped: ValuesInsert<'a, V, F>, @@ -953,7 +950,6 @@ fn decode_compact_with_skipped_inner<'a, L, DB, T, I, F, V>( F: LazyFetcher<'a>, V: Iterator, { - // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. let mut stack: Vec> = Vec::new(); @@ -1009,12 +1005,11 @@ fn decode_compact_with_skipped_inner<'a, L, DB, T, I, F, V>( Err(Box::new(TrieError::IncompleteDatabase(>::default()))) } -/// Simple fetcher for values to insert in proof when running -/// `decode_compact_with_skipped_values`. +/// Simple lazy access to values to insert in proof. pub trait LazyFetcher<'a> { /// Get actual value as bytes. /// If value cannot be fetch return `None`, resulting - /// in an error in `decode_compact_with_skipped_values`. + /// in an error in the decode method. fn fetch(&self, key: &[u8]) -> Option>; } diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index a3335f50..4ecde65e 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -84,7 +84,7 @@ fn test_encode_compact( EncodeType::SkipKeys(skip_keys) => { trie_db::encode_compact_skip_conditional_with_key::( &trie, - &mut trie_db::compact_conditions::skip_given_ordered_keys( + trie_db::compact_conditions::skip_given_ordered_keys( skip_keys.iter().map(|k| *k), ), false, @@ -127,7 +127,7 @@ fn test_decode_compact( let mut db = MemoryDB::default(); let (root, used) = match decode_type { DecodeType::SkippedValues(skipped_values) => { - trie_db::decode_compact_with_skipped_values::( + trie_db::decode_compact_with_known_values::( &mut db, encoded.iter().map(Vec::as_slice), skipped_values, @@ -141,7 +141,7 @@ fn test_decode_compact( ) }, DecodeType::Escaped(fetcher) => { - trie_db::decode_compact_with_encoded_skipped_values::( + trie_db::decode_compact_for_encoded_skipped_values::( &mut db, encoded.iter().map(Vec::as_slice), fetcher, @@ -225,7 +225,7 @@ fn trie_compact_encoding_works_without_ext() { #[test] fn trie_compact_encoding_skip_values() { let mut to_skip = BTreeSet::new(); - to_skip.extend(&[&b"doge"[..], &b"aaaaaa"[..], &b"do"[..], &b"b"[..]]); + to_skip.extend(&[&b"doge"[..], &b"aaaaaa"[..], &b"do"[..], &b"b"[..]]); // doge and do will be skip (32 + 4 bytes) let skip_len = 36; let (root_no_skip, encoded_no_skip, items_no_skip) = test_encode_compact::( @@ -336,7 +336,7 @@ fn trie_encode_skip_condition() { ]; let mut test_set = test_set(); test_set.extend(additional_values.iter().cloned()); - let mut test_proof_default = test_proof_default(); + let mut test_proof_default = test_proof_default(); test_proof_default.extend(additional_values.iter().filter_map(|kv| if kv.0.len() == 5 { Some(kv.0) From 0bd7f1e10ccbf2e39a4b173b8c82b0338ee1bdf3 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 16:57:10 +0100 Subject: [PATCH 26/31] missing one --- trie-db/test/src/trie_codec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 4ecde65e..b954a6de 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -93,14 +93,14 @@ fn test_encode_compact( EncodeType::TresholdCollect(treshold, keys) => { trie_db::encode_compact_skip_conditional_with_key::( &trie, - &mut trie_db::compact_conditions::skip_treshold_collect_keys(treshold, keys), + trie_db::compact_conditions::skip_treshold_collect_keys(treshold, keys), false, ).unwrap() }, EncodeType::TresholdEscaped(treshold) => { trie_db::encode_compact_skip_conditional::( &trie, - &mut trie_db::compact_conditions::skip_treshold(treshold), + trie_db::compact_conditions::skip_treshold(treshold), true, ).unwrap() }, From 9f9cfd58c143d2e3509417dc85c4dce97e1de176 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 16:58:54 +0100 Subject: [PATCH 27/31] remove TODO --- trie-db/src/iterator.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index 18539ae5..1c1830b6 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -257,8 +257,6 @@ impl<'a, L: TrieLayout> TrieIterator for TrieDBNodeIterator<'a, L> { } impl<'a, L: TrieLayout> Iterator for TrieDBNodeIterator<'a, L> { - // TODO should be Result<(&'a NibbleVec, ...)> also rc is useless: should be &'a - // Also should return direction (down up or sibling), many time we recheck externally. type Item = Result<(NibbleVec, Option>, Rc>), TrieHash, CError>; fn next(&mut self) -> Option { From 074fc04ccdee4dfac4e94e717b95fcf79dbd1fc0 Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 29 Jan 2021 17:11:13 +0100 Subject: [PATCH 28/31] Allows to decode without escaping --- trie-db/src/trie_codec.rs | 30 ++++++++++++++++++++---------- trie-db/test/src/trie_codec.rs | 1 + 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 59c6c821..caab9bd3 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -508,7 +508,8 @@ impl<'a, F> ValuesRemoveCondition for &'a mut F enum ValuesInsert<'a, I, F> { None, KnownKeys(InsertAt<'a, I, F>), - EscapedKeys(F), + EscapedValues(F), + NonEscapedValues(F), } struct InsertAt<'a, I, F> { @@ -631,9 +632,10 @@ impl< &self, ) -> bool { match self { - ValuesInsert::None => false, - ValuesInsert::KnownKeys(..) => false, - ValuesInsert::EscapedKeys(..) => true + ValuesInsert::NonEscapedValues(..) + | ValuesInsert::KnownKeys(..) + | ValuesInsert::None => false, + ValuesInsert::EscapedValues(..) => true } } @@ -700,7 +702,8 @@ impl< } } }, - ValuesInsert::EscapedKeys(fetcher) => { + ValuesInsert::NonEscapedValues(fetcher) + | ValuesInsert::EscapedValues(fetcher) => { if empty_value { prefix.append_partial(partial.right()); let key = LeftNibbleSlice::new(prefix.inner()).truncate(prefix.len()); @@ -924,17 +927,24 @@ pub fn decode_compact_with_known_values<'a, L, DB, T, I, F, K>( /// Variant of 'decode_compact' that try to fetch value when they are /// skipped. -/// Skipped values are encoded into a 0 length value, -/// actual 0 length value is escaped. -pub fn decode_compact_for_encoded_skipped_values<'a, L, DB, T, I, F>(db: &mut DB, encoded: I, fetcher: F) - -> Result<(TrieHash, usize), TrieHash, CError> +/// Skipped values are encoded into a 0 length value. +pub fn decode_compact_for_encoded_skipped_values<'a, L, DB, T, I, F>( + db: &mut DB, + encoded: I, + fetcher: F, + escaped_value: bool, +) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, DB: HashDB, I: IntoIterator, F: LazyFetcher<'a>, { - let skipped = ValuesInsert::EscapedKeys(fetcher); + let skipped = if escaped_value { + ValuesInsert::EscapedValues(fetcher) + } else { + ValuesInsert::NonEscapedValues(fetcher) + }; decode_compact_inner::>(db, encoded.into_iter(), skipped) } diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index b954a6de..263e96dc 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -145,6 +145,7 @@ fn test_decode_compact( &mut db, encoded.iter().map(Vec::as_slice), fetcher, + true, ) }, }.unwrap(); From ccb001e53234adb85b0c00b031bbc56151018d43 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 1 Feb 2021 10:48:12 +0100 Subject: [PATCH 29/31] Api to allow escape decoding with known keys. --- trie-db/src/trie_codec.rs | 12 ++++++++++-- trie-db/test/src/trie_codec.rs | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index caab9bd3..8752a3b8 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -508,6 +508,7 @@ impl<'a, F> ValuesRemoveCondition for &'a mut F enum ValuesInsert<'a, I, F> { None, KnownKeys(InsertAt<'a, I, F>), + EscapedKnownKeys(InsertAt<'a, I, F>), EscapedValues(F), NonEscapedValues(F), } @@ -634,6 +635,7 @@ impl< match self { ValuesInsert::NonEscapedValues(..) | ValuesInsert::KnownKeys(..) + | ValuesInsert::EscapedKnownKeys(..) | ValuesInsert::None => false, ValuesInsert::EscapedValues(..) => true } @@ -667,7 +669,8 @@ impl< match self { ValuesInsert::None => (), - ValuesInsert::KnownKeys(skipped_keys) => { + ValuesInsert::EscapedKnownKeys(skipped_keys) + | ValuesInsert::KnownKeys(skipped_keys) => { if let Some(next) = &skipped_keys.next_key_value { prefix.append_partial(partial.right()); // comparison is redundant with previous checks, could be optimized. @@ -913,6 +916,7 @@ pub fn decode_compact_with_known_values<'a, L, DB, T, I, F, K>( encoded: I, fetcher: F, known_keys: K, + escaped_value: bool, ) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, @@ -921,7 +925,11 @@ pub fn decode_compact_with_known_values<'a, L, DB, T, I, F, K>( F: LazyFetcher<'a>, K: IntoIterator, { - let known = ValuesInsert::KnownKeys(InsertAt::new(known_keys.into_iter(), fetcher)); + let known = if escaped_value { + ValuesInsert::EscapedKnownKeys(InsertAt::new(known_keys.into_iter(), fetcher)) + } else { + ValuesInsert::KnownKeys(InsertAt::new(known_keys.into_iter(), fetcher)) + }; decode_compact_inner::(db, encoded.into_iter(), known) } diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index 263e96dc..d6c7bbab 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -132,6 +132,7 @@ fn test_decode_compact( encoded.iter().map(Vec::as_slice), skipped_values, skipped_values.keys().map(|k| *k), + false, ) }, DecodeType::None => { From d7b29e53cbf047e987a833b5992f24278014b811 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 1 Feb 2021 11:04:50 +0100 Subject: [PATCH 30/31] fix --- trie-db/src/trie_codec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 8752a3b8..e3b27d2c 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -635,9 +635,9 @@ impl< match self { ValuesInsert::NonEscapedValues(..) | ValuesInsert::KnownKeys(..) - | ValuesInsert::EscapedKnownKeys(..) | ValuesInsert::None => false, - ValuesInsert::EscapedValues(..) => true + ValuesInsert::EscapedKnownKeys(..) + | ValuesInsert::EscapedValues(..) => true } } From 8b8c6481c3360678b734b0f08a5360216bb53039 Mon Sep 17 00:00:00 2001 From: cheme Date: Mon, 1 Feb 2021 19:37:16 +0100 Subject: [PATCH 31/31] fix iter --- trie-db/src/trie_codec.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index e3b27d2c..70795916 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -1086,22 +1086,20 @@ pub mod compact_conditions { let mut iter = iter.into_iter(); let mut next_key = iter.next(); move |node_key: &NibbleVec, _value: &[u8]| { - loop { - if let Some(next) = next_key { - // comparison is redundant with previous checks, could be optimized. - let node_key = LeftNibbleSlice::new(node_key.inner()).truncate(node_key.len()); - let next = LeftNibbleSlice::new(next); - match next.cmp(&node_key) { - Ordering::Less => { - next_key = iter.next(); - }, - Ordering::Equal => { - next_key = iter.next(); - return true; - }, - Ordering::Greater => break, - }; - } + while let Some(next) = next_key { + // comparison is redundant with previous checks, could be optimized. + let node_key = LeftNibbleSlice::new(node_key.inner()).truncate(node_key.len()); + let next = LeftNibbleSlice::new(next); + match next.cmp(&node_key) { + Ordering::Less => { + next_key = iter.next(); + }, + Ordering::Equal => { + next_key = iter.next(); + return true; + }, + Ordering::Greater => break, + }; } false