diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index 249ae4eb..e8bfc1a5 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2018 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/memory-db/CHANGELOG.md b/memory-db/CHANGELOG.md index cac856b2..e4db4ff3 100644 --- a/memory-db/CHANGELOG.md +++ b/memory-db/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog]. ## [Unreleased] +## [0.28.0] - 2021-10-19 +- Change in api bound. [#142](https://github.com/paritytech/trie/pull/142) + ## [0.27.0] - 2021-07-02 - Update `parity-util-mem` to 0.10. [#137](https://github.com/paritytech/trie/pull/137) diff --git a/memory-db/Cargo.toml b/memory-db/Cargo.toml index bd1bcf6f..779083da 100644 --- a/memory-db/Cargo.toml +++ b/memory-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "memory-db" -version = "0.27.0" +version = "0.28.0" authors = ["Parity Technologies "] description = "In-memory implementation of hash-db, useful for tests" repository = "https://github.com/paritytech/trie" diff --git a/memory-db/benches/bench.rs b/memory-db/benches/bench.rs index 49ed0dc2..d3fd41d1 100644 --- a/memory-db/benches/bench.rs +++ b/memory-db/benches/bench.rs @@ -45,7 +45,7 @@ fn instantiation(b: &mut Criterion) { fn compare_to_null_embedded_in_struct(b: &mut Criterion) { struct X { a_hash: ::Out, - }; + } let x = X { a_hash: KeccakHasher::hash(&[0u8][..]), }; diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 31b97f51..9fcc8b07 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -282,10 +282,10 @@ pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec Default for MemoryDB +impl Default for MemoryDB where H: KeyHasher, - T: From<&'a [u8]>, + T: for<'a> From<&'a [u8]>, KF: KeyFunction, M: MemTracker + Default, { @@ -337,15 +337,15 @@ where } } -impl<'a, H, KF, T, M> MemoryDB +impl MemoryDB where H: KeyHasher, - T: From<&'a [u8]>, + T: for<'a> From<&'a [u8]>, KF: KeyFunction, M: MemTracker + Default, { /// Create a new `MemoryDB` from a given null key/data - pub fn from_null_node(null_key: &'a [u8], null_node_data: T) -> Self { + pub fn from_null_node(null_key: &[u8], null_node_data: T) -> Self { MemoryDB { data: HashMap::default(), hashed_null_node: H::hash(null_key), @@ -356,7 +356,7 @@ where } /// Create a new instance of `Self`. - pub fn new(data: &'a [u8]) -> Self { + pub fn new(data: &[u8]) -> Self { Self::from_null_node(data, data.into()) } @@ -544,8 +544,8 @@ where impl HashDB for MemoryDB where H: KeyHasher, - T: Default + PartialEq + for<'a> From<&'a [u8]> + Clone + Send + Sync, - KF: Send + Sync + KeyFunction, + T: Default + PartialEq + AsRef<[u8]> + for<'a> From<&'a [u8]> + Clone + Send + Sync, + KF: KeyFunction + Send + Sync, M: MemTracker + Send + Sync, { fn get(&self, key: &H::Out, prefix: Prefix) -> Option { @@ -628,7 +628,7 @@ where impl HashDBRef for MemoryDB where H: KeyHasher, - T: Default + PartialEq + for<'a> From<&'a [u8]> + Clone + Send + Sync, + T: Default + PartialEq + AsRef<[u8]> + for<'a> From<&'a [u8]> + Clone + Send + Sync, KF: KeyFunction + Send + Sync, M: MemTracker + Send + Sync, { @@ -651,7 +651,7 @@ where impl AsHashDB for MemoryDB where H: KeyHasher, - T: Default + PartialEq + for<'a> From<&'a[u8]> + Clone + Send + Sync, + T: Default + PartialEq + AsRef<[u8]> + for<'a> From<&'a[u8]> + Clone + Send + Sync, KF: KeyFunction + Send + Sync, M: MemTracker + Send + Sync, { diff --git a/test-support/reference-trie/CHANGELOG.md b/test-support/reference-trie/CHANGELOG.md index 8ab71ff0..661b0a3c 100644 --- a/test-support/reference-trie/CHANGELOG.md +++ b/test-support/reference-trie/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog]. ## [Unreleased] +## [0.24.0] - 2021-10-19 +- Additional test codec substrate like. [#142](https://github.com/paritytech/trie/pull/142) + ## [0.23.0] - 2021-01-27 - Update parity-scale-codec to 2.0. diff --git a/test-support/reference-trie/Cargo.toml b/test-support/reference-trie/Cargo.toml index e61de60d..30dc021e 100644 --- a/test-support/reference-trie/Cargo.toml +++ b/test-support/reference-trie/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reference-trie" -version = "0.23.0" +version = "0.24.0" authors = ["Parity Technologies "] description = "Simple reference trie format" repository = "https://github.com/paritytech/trie/" @@ -11,8 +11,8 @@ edition = "2018" hash-db = { path = "../../hash-db" , version = "0.15.2"} hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.15.2" } keccak-hasher = { path = "../keccak-hasher", version = "0.15.3" } -trie-db = { path = "../../trie-db", default-features = false, version = "0.22.0" } -trie-root = { path = "../../trie-root", default-features = false, version = "0.16.0" } +trie-db = { path = "../../trie-db", default-features = false, version = "0.23.0" } +trie-root = { path = "../../trie-root", default-features = false, version = "0.17.0" } parity-scale-codec = { version = "2", features = ["derive"] } [dev-dependencies] diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 37a5f914..d9ab2f7a 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2018 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,15 +14,16 @@ //! Reference implementation of a streamer. +mod substrate_like; + use std::fmt; use std::iter::once; use std::marker::PhantomData; use std::ops::Range; use parity_scale_codec::{Decode, Input, Output, Encode, Compact, Error as CodecError}; use trie_root::Hasher; - use trie_db::{ - node::{NibbleSlicePlan, NodePlan, NodeHandlePlan}, + node::{NibbleSlicePlan, NodePlan, Value, ValuePlan, NodeHandlePlan}, triedbmut::ChildReference, DBValue, trie_visit, @@ -31,26 +32,62 @@ use trie_db::{ Partial, }; use std::borrow::Borrow; -use keccak_hasher::KeccakHasher; use trie_db::{ nibble_ops, NodeCodec, - Trie, TrieConfiguration, + Trie, TrieConfiguration, TrieDB, TrieDBMut, TrieLayout, TrieMut, }; pub use trie_root::TrieStream; +use trie_root::Value as TrieStreamValue; pub mod node { pub use trie_db::node::Node; } +pub use substrate_like::{trie_constants, ReferenceTrieStreamNoExt, + HashedValueNoExt, HashedValueNoExtThreshold, + NodeCodec as ReferenceNodeCodecNoExtMeta}; + + +/// Reference hasher is a keccak hasher. +pub type RefHasher = keccak_hasher::KeccakHasher; + +/// Apply a test method on every test layouts. +#[macro_export] +macro_rules! test_layouts { + ($test:ident, $test_internal:ident) => { + #[test] + fn $test() { + $test_internal::(); + $test_internal::(); + $test_internal::(); + $test_internal::(); + } + }; +} + +/// Apply a test method on every test layouts. +#[macro_export] +macro_rules! test_layouts_no_meta { + ($test:ident, $test_internal:ident) => { + #[test] + fn $test() { + $test_internal::(); + $test_internal::(); + } + }; +} + /// Trie layout using extension nodes. +#[derive(Default, Clone)] pub struct ExtensionLayout; impl TrieLayout for ExtensionLayout { const USE_EXTENSION: bool = true; const ALLOW_EMPTY: bool = false; - type Hash = KeccakHasher; - type Codec = ReferenceNodeCodec; + const MAX_INLINE_VALUE: Option = None; + type Hash = RefHasher; + type Codec = ReferenceNodeCodec; } impl TrieConfiguration for ExtensionLayout { } @@ -59,27 +96,42 @@ impl TrieConfiguration for ExtensionLayout { } /// generic hasher. pub struct GenericNoExtensionLayout(PhantomData); +impl Default for GenericNoExtensionLayout { + fn default() -> Self { + GenericNoExtensionLayout(PhantomData) + } +} + +impl Clone for GenericNoExtensionLayout { + fn clone(&self) -> Self { + GenericNoExtensionLayout(PhantomData) + } +} + impl TrieLayout for GenericNoExtensionLayout { const USE_EXTENSION: bool = false; const ALLOW_EMPTY: bool = false; + const MAX_INLINE_VALUE: Option = None; type Hash = H; type Codec = ReferenceNodeCodecNoExt; } -/// Trie that allows empty values +/// Trie that allows empty values. +#[derive(Default, Clone)] pub struct AllowEmptyLayout; impl TrieLayout for AllowEmptyLayout { const USE_EXTENSION: bool = true; const ALLOW_EMPTY: bool = true; - type Hash = KeccakHasher; - type Codec = ReferenceNodeCodec; + const MAX_INLINE_VALUE: Option = None; + type Hash = RefHasher; + type Codec = ReferenceNodeCodec; } impl TrieConfiguration for GenericNoExtensionLayout { } /// Trie layout without extension nodes. -pub type NoExtensionLayout = GenericNoExtensionLayout; +pub type NoExtensionLayout = GenericNoExtensionLayout; /// Children bitmap codec for radix 16 trie. pub struct Bitmap(u16); @@ -110,7 +162,6 @@ impl Bitmap { } pub type RefTrieDB<'a> = trie_db::TrieDB<'a, ExtensionLayout>; -pub type RefTrieDBNoExt<'a> = trie_db::TrieDB<'a, NoExtensionLayout>; pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, ExtensionLayout>; pub type RefTrieDBMutNoExt<'a> = trie_db::TrieDBMut<'a, NoExtensionLayout>; pub type RefTrieDBMutAllowEmpty<'a> = trie_db::TrieDBMut<'a, AllowEmptyLayout>; @@ -121,28 +172,46 @@ pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, ExtensionLayout>; pub type RefLookup<'a, Q> = trie_db::Lookup<'a, ExtensionLayout, Q>; pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, NoExtensionLayout, Q>; -pub fn reference_trie_root(input: I) -> ::Out where +pub fn reference_trie_root(input: I) -> ::Out where I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::trie_root::(input) + if T::USE_EXTENSION { + trie_root::trie_root::(input, Default::default()) + } else { + trie_root::trie_root_no_extension::(input, Default::default()) + } +} + +fn data_sorted_unique(input: I) -> Vec<(A, B)> + where + I: IntoIterator, +{ + let mut m = std::collections::BTreeMap::new(); + for (k,v) in input { + let _ = m.insert(k,v); // latest value for uniqueness + } + m.into_iter().collect() } -fn reference_trie_root_unhashed(input: I) -> Vec where +pub fn reference_trie_root_iter_build(input: I) -> ::Out where + T: TrieLayout, I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::unhashed_trie::(input) + let mut cb = trie_db::TrieRoot::::default(); + trie_visit::(data_sorted_unique(input), &mut cb); + cb.root.unwrap_or_default() } -pub fn reference_trie_root_no_extension(input: I) -> ::Out where +fn reference_trie_root_unhashed(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::trie_root_no_extension::(input) + trie_root::unhashed_trie::(input, Default::default()) } fn reference_trie_root_unhashed_no_extension(input: I) -> Vec where @@ -150,7 +219,7 @@ fn reference_trie_root_unhashed_no_extension(input: I) -> Vec where A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::unhashed_trie_no_extension::(input) + trie_root::unhashed_trie_no_extension::(input, Default::default()) } const EMPTY_TRIE: u8 = 0; @@ -164,11 +233,12 @@ const LEAF_NODE_LAST: u8 = EXTENSION_NODE_OFFSET - 1; const EXTENSION_NODE_LAST: u8 = BRANCH_NODE_NO_VALUE - 1; // Constant use with no extensino trie codec. -const EMPTY_TRIE_NO_EXT: u8 = 0; const NIBBLE_SIZE_BOUND_NO_EXT: usize = u16::max_value() as usize; +const FIRST_PREFIX: u8 = 0b_00 << 6; const LEAF_PREFIX_MASK_NO_EXT: u8 = 0b_01 << 6; const BRANCH_WITHOUT_MASK_NO_EXT: u8 = 0b_10 << 6; const BRANCH_WITH_MASK_NO_EXT: u8 = 0b_11 << 6; +const EMPTY_TRIE_NO_EXT: u8 = FIRST_PREFIX | 0b_00; /// Create a leaf/extension node, encoding a number of nibbles. Note that this /// cannot handle a number of nibbles that is zero or greater than 125 and if @@ -195,23 +265,6 @@ enum NodeKindNoExt { BranchWithValue, } -/// Create a leaf or branch node header followed by its encoded partial nibbles. -fn fuse_nibbles_node_no_extension<'a>( - nibbles: &'a [u8], - kind: NodeKindNoExt, -) -> impl Iterator + 'a { - let size = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibbles.len()); - - let iter_start = match kind { - NodeKindNoExt::Leaf => size_and_prefix_iterator(size, LEAF_PREFIX_MASK_NO_EXT), - NodeKindNoExt::BranchNoValue => size_and_prefix_iterator(size, BRANCH_WITHOUT_MASK_NO_EXT), - NodeKindNoExt::BranchWithValue => size_and_prefix_iterator(size, BRANCH_WITH_MASK_NO_EXT), - }; - iter_start - .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) - .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) -} - /// Encoding of branch header and children bitmap (for trie stream radix 16). /// For stream variant with extension. fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { @@ -265,23 +318,27 @@ impl TrieStream for ReferenceTrieStream { self.buffer.push(EMPTY_TRIE); } - fn append_leaf(&mut self, key: &[u8], value: &[u8]) { - self.buffer.extend(fuse_nibbles_node(key, true)); - value.encode_to(&mut self.buffer); + fn append_leaf(&mut self, key: &[u8], value: TrieStreamValue) { + if let TrieStreamValue::Inline(value) = value { + self.buffer.extend(fuse_nibbles_node(key, true)); + value.encode_to(&mut self.buffer); + } else { + unreachable!("This stream do not allow external value node") + } } fn begin_branch( &mut self, maybe_key: Option<&[u8]>, - maybe_value: Option<&[u8]>, + maybe_value: Option, has_children: impl Iterator, ) { - self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); + self.buffer.extend(&branch_node(!matches!(maybe_value, None), has_children)); if let Some(partial) = maybe_key { // should not happen self.buffer.extend(fuse_nibbles_node(partial, false)); } - if let Some(value) = maybe_value { + if let Some(TrieStreamValue::Inline(value)) = maybe_value { value.encode_to(&mut self.buffer); } } @@ -301,70 +358,6 @@ impl TrieStream for ReferenceTrieStream { fn out(self) -> Vec { self.buffer } } -/// Reference implementation of a `TrieStream` without extension. -#[derive(Default, Clone)] -pub struct ReferenceTrieStreamNoExt { - buffer: Vec -} - -impl TrieStream for ReferenceTrieStreamNoExt { - fn new() -> Self { - ReferenceTrieStreamNoExt { - buffer: Vec::new() - } - } - - fn append_empty_data(&mut self) { - self.buffer.push(EMPTY_TRIE_NO_EXT); - } - - fn append_leaf(&mut self, key: &[u8], value: &[u8]) { - self.buffer.extend(fuse_nibbles_node_no_extension(key, NodeKindNoExt::Leaf)); - value.encode_to(&mut self.buffer); - } - - fn begin_branch( - &mut self, - maybe_key: Option<&[u8]>, - maybe_value: Option<&[u8]>, - has_children: impl Iterator - ) { - if let Some(partial) = maybe_key { - if maybe_value.is_some() { - self.buffer.extend( - fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchWithValue) - ); - } else { - self.buffer.extend( - fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchNoValue) - ); - } - let bitmap = branch_node_bit_mask(has_children); - self.buffer.extend([bitmap.0, bitmap.1].iter()); - } else { - // should not happen - self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); - } - if let Some(value) = maybe_value { - value.encode_to(&mut self.buffer); - } - } - - fn append_extension(&mut self, _key: &[u8]) { - // should not happen - } - - fn append_substream(&mut self, other: Self) { - let data = other.out(); - match data.len() { - 0..=31 => data.encode_to(&mut self.buffer), - _ => H::hash(&data).as_ref().encode_to(&mut self.buffer), - } - } - - fn out(self) -> Vec { self.buffer } -} - /// A node header. #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum NodeHeader { @@ -645,7 +638,7 @@ impl NodeCodec for ReferenceNodeCodec { let value = if has_value { let count = >::decode(&mut input)?.0 as usize; - Some(input.take(count)?) + Some(ValuePlan::Inline(input.take(count)?)) } else { None }; @@ -692,7 +685,7 @@ impl NodeCodec for ReferenceNodeCodec { let value = input.take(count)?; Ok(NodePlan::Leaf { partial: NibbleSlicePlan::new(partial, partial_padding), - value, + value: ValuePlan::Inline(value), }) } } @@ -706,9 +699,15 @@ impl NodeCodec for ReferenceNodeCodec { &[EMPTY_TRIE] } - fn leaf_node(partial: Partial, value: &[u8]) -> Vec { + fn leaf_node(partial: Partial, value: Value) -> Vec { let mut output = partial_to_key(partial, LEAF_NODE_OFFSET, LEAF_NODE_OVER); - value.encode_to(&mut output); + match value { + Value::Inline(value) => { + Compact(value.len() as u32).encode_to(&mut output); + output.extend_from_slice(value); + }, + _ => unimplemented!("unsupported"), + } output } @@ -733,15 +732,20 @@ impl NodeCodec for ReferenceNodeCodec { fn branch_node( children: impl Iterator>>>, - maybe_value: Option<&[u8]>, + maybe_value: Option, ) -> Vec { let mut output = vec![0; BITMAP_LENGTH + 1]; let mut prefix: [u8; 3] = [0; 3]; - let have_value = if let Some(value) = maybe_value { - value.encode_to(&mut output); - true - } else { - false + let have_value = match maybe_value { + Some(Value::Inline(value)) => { + Compact(value.len() as u32).encode_to(&mut output); + output.extend_from_slice(value); + true + }, + None => { + false + }, + _ => unimplemented!("unsupported"), }; let has_children = children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { @@ -763,10 +767,10 @@ impl NodeCodec for ReferenceNodeCodec { _partial: impl Iterator, _number_nibble: usize, _children: impl Iterator>>>, - _maybe_value: Option<&[u8]>) -> Vec { - unreachable!() + _maybe_value: Option, + ) -> Vec { + unreachable!("codec with extension branch") } - } impl NodeCodec for ReferenceNodeCodecNoExt { @@ -777,10 +781,14 @@ impl NodeCodec for ReferenceNodeCodecNoExt { H::hash(::empty_node()) } - fn decode_plan(data: &[u8]) -> ::std::result::Result { + fn decode_plan(data: &[u8]) -> Result { + if data.len() < 1 { + return Err(CodecError::from("Empty encoded node.")); + } let mut input = ByteSliceInput::new(data); - match NodeHeaderNoExt::decode(&mut input)? { - NodeHeaderNoExt::Null => Ok(NodePlan::Empty), + + Ok(match NodeHeaderNoExt::decode(&mut input)? { + NodeHeaderNoExt::Null => NodePlan::Empty, NodeHeaderNoExt::Branch(has_value, nibble_count) => { let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; // check that the padding is valid (if any) @@ -795,7 +803,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { let bitmap = Bitmap::decode(&data[bitmap_range])?; let value = if has_value { let count = >::decode(&mut input)?.0 as usize; - Some(input.take(count)?) + Some(ValuePlan::Inline(input.take(count)?)) } else { None }; @@ -814,11 +822,11 @@ impl NodeCodec for ReferenceNodeCodecNoExt { }); } } - Ok(NodePlan::NibbledBranch { + NodePlan::NibbledBranch { partial: NibbleSlicePlan::new(partial, partial_padding), value, children, - }) + } } NodeHeaderNoExt::Leaf(nibble_count) => { let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; @@ -831,13 +839,14 @@ impl NodeCodec for ReferenceNodeCodecNoExt { )?; let partial_padding = nibble_ops::number_padding(nibble_count); let count = >::decode(&mut input)?.0 as usize; - let value = input.take(count)?; - Ok(NodePlan::Leaf { + let value = ValuePlan::Inline(input.take(count)?); + + NodePlan::Leaf { partial: NibbleSlicePlan::new(partial, partial_padding), value, - }) + } } - } + }) } fn is_empty_node(data: &[u8]) -> bool { @@ -848,9 +857,15 @@ impl NodeCodec for ReferenceNodeCodecNoExt { &[EMPTY_TRIE_NO_EXT] } - fn leaf_node(partial: Partial, value: &[u8]) -> Vec { + fn leaf_node(partial: Partial, value: Value) -> Vec { let mut output = partial_encode(partial, NodeKindNoExt::Leaf); - value.encode_to(&mut output); + match value { + Value::Inline(value) => { + Compact(value.len() as u32).encode_to(&mut output); + output.extend_from_slice(value); + }, + Value::Node(..) => unimplemented!("No support for inner hashed value"), + } output } @@ -859,41 +874,47 @@ impl NodeCodec for ReferenceNodeCodecNoExt { _nbnibble: usize, _child: ChildReference<::Out>, ) -> Vec { - unreachable!() + unreachable!("no extension codec") } fn branch_node( _children: impl Iterator::Out>>>>, - _maybe_value: Option<&[u8]>, + _maybe_value: Option, ) -> Vec { - unreachable!() + unreachable!("no extension codec") } fn branch_node_nibbled( partial: impl Iterator, number_nibble: usize, children: impl Iterator>>>, - maybe_value: Option<&[u8]>, + maybe_value: Option, ) -> Vec { - let mut output = if maybe_value.is_some() { + let mut output = if maybe_value.is_none() { partial_from_iterator_encode( partial, number_nibble, - NodeKindNoExt::BranchWithValue, + NodeKindNoExt::BranchNoValue, ) } else { partial_from_iterator_encode( partial, number_nibble, - NodeKindNoExt::BranchNoValue, + NodeKindNoExt::BranchWithValue, ) }; let bitmap_index = output.len(); let mut bitmap: [u8; BITMAP_LENGTH] = [0; BITMAP_LENGTH]; (0..BITMAP_LENGTH).for_each(|_| output.push(0)); - if let Some(value) = maybe_value { - value.encode_to(&mut output); - }; + match maybe_value { + Some(Value::Inline(value)) => { + Compact(value.len() as u32).encode_to(&mut output); + output.extend_from_slice(value); + }, + Some(Value::Node(..)) => unimplemented!("No support for inner hashed value"), + None => (), + } + Bitmap::encode(children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); @@ -909,23 +930,22 @@ impl NodeCodec for ReferenceNodeCodecNoExt { .copy_from_slice(&bitmap.as_ref()[..BITMAP_LENGTH]); output } - } /// Compare trie builder and in memory trie. -pub fn compare_implementations + Eq> ( +pub fn compare_implementations ( data: Vec<(Vec, Vec)>, - mut memdb: X, - mut hashdb: X, -) { - let root_new = { - let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) - }; + mut memdb: DB, + mut hashdb: DB, +) + where + T: TrieLayout, + DB : hash_db::HashDB + Eq, +{ + let root_new = calc_root_build::(data.clone(), &mut hashdb); let root = { let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for i in 0..data.len() { t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); } @@ -935,7 +955,7 @@ pub fn compare_implementations + Eq> if root_new != root { { let db : &dyn hash_db::HashDB<_, _> = &hashdb; - let t = RefTrieDB::new(&db, &root_new).unwrap(); + let t = TrieDB::::new(&db, &root_new).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { println!("a:{:x?}", a); @@ -943,7 +963,7 @@ pub fn compare_implementations + Eq> } { let db : &dyn hash_db::HashDB<_, _> = &memdb; - let t = RefTrieDB::new(&db, &root).unwrap(); + let t = TrieDB::::new(&db, &root).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { println!("a:{:x?}", a); @@ -957,18 +977,14 @@ pub fn compare_implementations + Eq> } /// Compare trie builder and trie root implementations. -pub fn compare_root( +pub fn compare_root>( data: Vec<(Vec, Vec)>, - mut memdb: impl hash_db::HashDB, + mut memdb: DB, ) { - let root_new = { - let mut cb = TrieRoot::::default(); - trie_visit::(data.clone().into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) - }; + let root_new = reference_trie_root_iter_build::(data.clone()); let root = { let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = trie_db::TrieDBMut::::new(&mut memdb, &mut root); for i in 0..data.len() { t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); } @@ -983,7 +999,7 @@ pub fn compare_unhashed( data: Vec<(Vec, Vec)>, ) { let root_new = { - let mut cb = trie_db::TrieRootUnhashed::::default(); + let mut cb = trie_db::TrieRootUnhashed::::default(); trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; @@ -998,7 +1014,7 @@ pub fn compare_unhashed_no_extension( data: Vec<(Vec, Vec)>, ) { let root_new = { - let mut cb = trie_db::TrieRootUnhashed::::default(); + let mut cb = trie_db::TrieRootUnhashed::::default(); trie_visit::(data.clone().into_iter(), &mut cb); cb.root.unwrap_or(Default::default()) }; @@ -1008,121 +1024,52 @@ pub fn compare_unhashed_no_extension( } /// Trie builder root calculation utility. -pub fn calc_root( - data: I, -) -> ::Out - where - I: IntoIterator, - A: AsRef<[u8]> + Ord + fmt::Debug, - B: AsRef<[u8]> + fmt::Debug, -{ - let mut cb = TrieRoot::::default(); - trie_visit::(data.into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) -} - -/// Trie builder root calculation utility. -/// This uses the variant without extension nodes. -pub fn calc_root_no_extension( +pub fn calc_root( data: I, -) -> ::Out +) -> ::Out where + T: TrieLayout, I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - let mut cb = TrieRoot::::default(); - trie_db::trie_visit::(data.into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) + let mut cb = TrieRoot::::default(); + trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or_default() } /// Trie builder trie building utility. -pub fn calc_root_build( +pub fn calc_root_build( data: I, hashdb: &mut DB -) -> ::Out - where - I: IntoIterator, - A: AsRef<[u8]> + Ord + fmt::Debug, - B: AsRef<[u8]> + fmt::Debug, - DB: hash_db::HashDB -{ - let mut cb = TrieBuilder::new(hashdb); - trie_visit::(data.into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) -} - -/// Trie builder trie building utility. -/// This uses the variant without extension nodes. -pub fn calc_root_build_no_extension( - data: I, - hashdb: &mut DB, -) -> ::Out +) -> ::Out where + T: TrieLayout, I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, - DB: hash_db::HashDB + DB: hash_db::HashDB, { - let mut cb = TrieBuilder::new(hashdb); - trie_db::trie_visit::(data.into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) -} - -/// Compare trie builder and in memory trie. -/// This uses the variant without extension nodes. -pub fn compare_implementations_no_extension( - data: Vec<(Vec, Vec)>, - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, -) { - let root_new = { - let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(data.clone().into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) - }; - let root = { - let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); - for i in 0..data.len() { - t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); - } - *t.root() - }; - - if root != root_new { - { - let db : &dyn hash_db::HashDB<_, _> = &memdb; - let t = RefTrieDBNoExt::new(&db, &root).unwrap(); - println!("{:?}", t); - for a in t.iter().unwrap() { - println!("a:{:?}", a); - } - } - { - let db : &dyn hash_db::HashDB<_, _> = &hashdb; - let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); - println!("{:?}", t); - for a in t.iter().unwrap() { - println!("a:{:?}", a); - } - } - } - - assert_eq!(root, root_new); + let mut cb = TrieBuilder::::new(hashdb); + trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or_default() } /// `compare_implementations_no_extension` for unordered input (trie_root does /// ordering before running when trie_build expect correct ordering). -pub fn compare_implementations_no_extension_unordered( +pub fn compare_implementations_unordered ( data: Vec<(Vec, Vec)>, - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, -) { + mut memdb: DB, + mut hashdb: DB, +) + where + T: TrieLayout, + DB : hash_db::HashDB + Eq, +{ let mut b_map = std::collections::btree_map::BTreeMap::new(); let root = { let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for i in 0..data.len() { t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); b_map.insert(data[i].0.clone(), data[i].1.clone()); @@ -1130,15 +1077,15 @@ pub fn compare_implementations_no_extension_unordered( *t.root() }; let root_new = { - let mut cb = TrieBuilder::new(&mut hashdb); - trie_visit::(b_map.into_iter(), &mut cb); - cb.root.unwrap_or(Default::default()) + let mut cb = TrieBuilder::::new(&mut hashdb); + trie_visit::(b_map.into_iter(), &mut cb); + cb.root.unwrap_or_default() }; if root != root_new { { let db : &dyn hash_db::HashDB<_, _> = &memdb; - let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + let t = TrieDB::::new(&db, &root).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { println!("a:{:?}", a); @@ -1146,7 +1093,7 @@ pub fn compare_implementations_no_extension_unordered( } { let db : &dyn hash_db::HashDB<_, _> = &hashdb; - let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); + let t = TrieDB::::new(&db, &root_new).unwrap(); println!("{:?}", t); for a in t.iter().unwrap() { println!("a:{:?}", a); @@ -1159,21 +1106,26 @@ pub fn compare_implementations_no_extension_unordered( /// Testing utility that uses some periodic removal over /// its input test data. -pub fn compare_no_extension_insert_remove( +pub fn compare_insert_remove>( data: Vec<(bool, Vec, Vec)>, - mut memdb: impl hash_db::HashDB, -) { + mut memdb: DB, +) + where + T: TrieLayout, + DB : hash_db::HashDB + Eq, +{ + let mut data2 = std::collections::BTreeMap::new(); let mut root = Default::default(); let mut a = 0; { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.commit(); } while a < data.len() { // new triemut every 3 element root = { - let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); + let mut t = TrieDBMut::::from_existing(&mut memdb, &mut root).unwrap(); for _ in 0..3 { if data[a].0 { // remove @@ -1194,10 +1146,10 @@ pub fn compare_no_extension_insert_remove( *t.root() }; } - let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); + let mut t = TrieDBMut::::from_existing(&mut memdb, &mut root).unwrap(); // we are testing the RefTrie code here so we do not sort or check uniqueness // before. - assert_eq!(*t.root(), calc_root_no_extension(data2)); + assert_eq!(*t.root(), calc_root::(data2)); } #[cfg(test)] @@ -1228,10 +1180,10 @@ mod tests { fn too_big_nibble_length() { // + 1 for 0 added byte of nibble encode let input = vec![0u8; (NIBBLE_SIZE_BOUND_NO_EXT as usize + 1) / 2 + 1]; - let enc = as NodeCodec> - ::leaf_node(((0, 0), &input), &[1]); - let dec = as NodeCodec> - ::decode(&enc).unwrap(); + let enc = as NodeCodec> + ::leaf_node(((0, 0), &input), Value::Inline(&[1])); + let dec = as NodeCodec> + ::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl, _) = dec { Some(sl) } else { None }; diff --git a/test-support/reference-trie/src/substrate_like.rs b/test-support/reference-trie/src/substrate_like.rs new file mode 100644 index 00000000..225f2ad9 --- /dev/null +++ b/test-support/reference-trie/src/substrate_like.rs @@ -0,0 +1,553 @@ +// Copyright 2017, 2021 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +//! Codec and layout configuration similar to upstream default substrate one. + +use super::*; +use super::CodecError as Error; +use super::NodeCodec as NodeCodecT; +use trie_db::node::Value; + +/// No extension trie with no hashed value. +pub struct HashedValueNoExt; + +/// No extension trie which stores value above a static size +/// as external node. +pub struct HashedValueNoExtThreshold; + +impl TrieLayout for HashedValueNoExt { + const USE_EXTENSION: bool = false; + const ALLOW_EMPTY: bool = false; + const MAX_INLINE_VALUE: Option = None; + + type Hash = RefHasher; + type Codec = ReferenceNodeCodecNoExtMeta; +} + +impl TrieLayout for HashedValueNoExtThreshold { + const USE_EXTENSION: bool = false; + const ALLOW_EMPTY: bool = false; + const MAX_INLINE_VALUE: Option = Some(1); + + type Hash = RefHasher; + type Codec = ReferenceNodeCodecNoExtMeta; +} + +/// Constants specific to encoding with external value node support. +pub mod trie_constants { + const FIRST_PREFIX: u8 = 0b_00 << 6; + pub const NIBBLE_SIZE_BOUND: usize = u16::max_value() as usize; + pub const LEAF_PREFIX_MASK: u8 = 0b_01 << 6; + pub const BRANCH_WITHOUT_MASK: u8 = 0b_10 << 6; + pub const BRANCH_WITH_MASK: u8 = 0b_11 << 6; + pub const EMPTY_TRIE: u8 = FIRST_PREFIX | (0b_00 << 4); + pub const ALT_HASHING_LEAF_PREFIX_MASK: u8 = FIRST_PREFIX | (0b_1 << 5); + pub const ALT_HASHING_BRANCH_WITH_MASK: u8 = FIRST_PREFIX | (0b_01 << 4); + pub const ESCAPE_COMPACT_HEADER: u8 = EMPTY_TRIE | 0b_00_01; +} + +#[derive(Default, Clone)] +pub struct NodeCodec(PhantomData); + +impl NodeCodec { + fn decode_plan_inner_hashed( + data: &[u8], + ) -> Result { + let mut input = ByteSliceInput::new(data); + + let header = NodeHeader::decode(&mut input)?; + let contains_hash = header.contains_hash_of_value(); + + let branch_has_value = if let NodeHeader::Branch(has_value, _) = &header { + *has_value + } else { + // alt_hash_branch + true + }; + + match header { + NodeHeader::Null => Ok(NodePlan::Empty), + NodeHeader::HashedValueBranch(nibble_count) + | NodeHeader::Branch(_, nibble_count) => { + let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; + // check that the padding is valid (if any) + if padding && nibble_ops::pad_left(data[input.offset]) != 0 { + return Err(CodecError::from("Bad format")); + } + let partial = input.take( + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, + )?; + let partial_padding = nibble_ops::number_padding(nibble_count); + let bitmap_range = input.take(BITMAP_LENGTH)?; + let bitmap = Bitmap::decode(&data[bitmap_range])?; + let value = if branch_has_value { + Some(if contains_hash { + ValuePlan::Node(input.take(H::LENGTH)?) + } else { + let count = >::decode(&mut input)?.0 as usize; + ValuePlan::Inline(input.take(count)?) + }) + } else { + None + }; + let mut children = [ + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + ]; + for i in 0..nibble_ops::NIBBLE_LENGTH { + if bitmap.value_at(i) { + let count = >::decode(&mut input)?.0 as usize; + let range = input.take(count)?; + children[i] = Some(if count == H::LENGTH { + NodeHandlePlan::Hash(range) + } else { + NodeHandlePlan::Inline(range) + }); + } + } + Ok(NodePlan::NibbledBranch { + partial: NibbleSlicePlan::new(partial, partial_padding), + value, + children, + }) + }, + NodeHeader::HashedValueLeaf(nibble_count) + | NodeHeader::Leaf(nibble_count) => { + let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; + // check that the padding is valid (if any) + if padding && nibble_ops::pad_left(data[input.offset]) != 0 { + return Err(CodecError::from("Bad format")); + } + let partial = input.take( + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, + )?; + let partial_padding = nibble_ops::number_padding(nibble_count); + let value = if contains_hash { + ValuePlan::Node(input.take(H::LENGTH)?) + } else { + let count = >::decode(&mut input)?.0 as usize; + ValuePlan::Inline(input.take(count)?) + }; + + Ok(NodePlan::Leaf { + partial: NibbleSlicePlan::new(partial, partial_padding), + value, + }) + } + } + } +} + +impl NodeCodecT for NodeCodec + where + H: Hasher, +{ + const ESCAPE_HEADER: Option = Some(trie_constants::ESCAPE_COMPACT_HEADER); + type Error = Error; + type HashOut = H::Out; + + fn hashed_null_node() -> ::Out { + H::hash(::empty_node()) + } + + fn decode_plan(data: &[u8]) -> Result { + Self::decode_plan_inner_hashed(data) + } + + fn is_empty_node(data: &[u8]) -> bool { + data == ::empty_node() + } + + fn empty_node() -> &'static [u8] { + &[trie_constants::EMPTY_TRIE] + } + + fn leaf_node(partial: Partial, value: Value) -> Vec { + let contains_hash = matches!(&value, Value::Node(..)); + let mut output = if contains_hash { + partial_encode(partial, NodeKind::HashedValueLeaf) + } else { + partial_encode(partial, NodeKind::Leaf) + }; + match value { + Value::Inline(value) => { + Compact(value.len() as u32).encode_to(&mut output); + output.extend_from_slice(value); + }, + Value::Node(hash, _) => { + debug_assert!(hash.len() == H::LENGTH); + output.extend_from_slice(hash); + }, + } + output + } + + fn extension_node( + _partial: impl Iterator, + _nbnibble: usize, + _child: ChildReference<::Out>, + ) -> Vec { + unreachable!("Codec without extension.") + } + + fn branch_node( + _children: impl Iterator::Out>>>>, + _maybe_value: Option, + ) -> Vec { + unreachable!("Codec without extension.") + } + + fn branch_node_nibbled( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator::Out>>>>, + value: Option, + ) -> Vec { + let contains_hash = matches!(&value, Some(Value::Node(..))); + let mut output = match (&value, contains_hash) { + (&None, _) => { + partial_from_iterator_encode(partial, number_nibble, NodeKind::BranchNoValue) + }, + (_, false) => { + partial_from_iterator_encode(partial, number_nibble, NodeKind::BranchWithValue) + }, + (_, true) => { + partial_from_iterator_encode(partial, number_nibble, NodeKind::HashedValueBranch) + }, + }; + + let bitmap_index = output.len(); + let mut bitmap: [u8; BITMAP_LENGTH] = [0; BITMAP_LENGTH]; + (0..BITMAP_LENGTH).for_each(|_|output.push(0)); + match value { + Some(Value::Inline(value)) => { + Compact(value.len() as u32).encode_to(&mut output); + output.extend_from_slice(value); + }, + Some(Value::Node(hash, _)) => { + debug_assert!(hash.len() == H::LENGTH); + output.extend_from_slice(hash); + }, + None => (), + } + Bitmap::encode(children.map(|maybe_child| match maybe_child.borrow() { + Some(ChildReference::Hash(h)) => { + h.as_ref().encode_to(&mut output); + true + } + &Some(ChildReference::Inline(inline_data, len)) => { + inline_data.as_ref()[..len].encode_to(&mut output); + true + } + None => false, + }), bitmap.as_mut()); + output[bitmap_index..bitmap_index + BITMAP_LENGTH] + .copy_from_slice(&bitmap[..BITMAP_LENGTH]); + output + } +} + +// utils + +/// Encode and allocate node type header (type and size), and partial value. +/// It uses an iterator over encoded partial bytes as input. +fn partial_from_iterator_encode>( + partial: I, + nibble_count: usize, + node_kind: NodeKind, +) -> Vec { + let nibble_count = std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, nibble_count); + + let mut output = Vec::with_capacity(4 + (nibble_count / nibble_ops::NIBBLE_PER_BYTE)); + match node_kind { + NodeKind::Leaf => NodeHeader::Leaf(nibble_count).encode_to(&mut output), + NodeKind::BranchWithValue => NodeHeader::Branch(true, nibble_count).encode_to(&mut output), + NodeKind::BranchNoValue => NodeHeader::Branch(false, nibble_count).encode_to(&mut output), + NodeKind::HashedValueLeaf => NodeHeader::HashedValueLeaf(nibble_count).encode_to(&mut output), + NodeKind::HashedValueBranch => NodeHeader::HashedValueBranch(nibble_count) + .encode_to(&mut output), + }; + output.extend(partial); + output +} + +/// Encode and allocate node type header (type and size), and partial value. +/// Same as `partial_from_iterator_encode` but uses non encoded `Partial` as input. +fn partial_encode(partial: Partial, node_kind: NodeKind) -> Vec { + let number_nibble_encoded = (partial.0).0 as usize; + let nibble_count = partial.1.len() * nibble_ops::NIBBLE_PER_BYTE + number_nibble_encoded; + + let nibble_count = std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, nibble_count); + + let mut output = Vec::with_capacity(3 + partial.1.len()); + match node_kind { + NodeKind::Leaf => NodeHeader::Leaf(nibble_count).encode_to(&mut output), + NodeKind::BranchWithValue => NodeHeader::Branch(true, nibble_count).encode_to(&mut output), + NodeKind::BranchNoValue => NodeHeader::Branch(false, nibble_count).encode_to(&mut output), + NodeKind::HashedValueLeaf => NodeHeader::HashedValueLeaf(nibble_count).encode_to(&mut output), + NodeKind::HashedValueBranch => NodeHeader::HashedValueBranch(nibble_count) + .encode_to(&mut output), + }; + if number_nibble_encoded > 0 { + output.push(nibble_ops::pad_right((partial.0).1)); + } + output.extend_from_slice(&partial.1[..]); + output +} + +/// A node header. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub(crate) enum NodeHeader { + Null, + // contains wether there is a value and nibble count + Branch(bool, usize), + // contains nibble count + Leaf(usize), + // contains nibble count. + HashedValueBranch(usize), + // contains nibble count. + HashedValueLeaf(usize), +} + +impl NodeHeader { + fn contains_hash_of_value(&self) -> bool { + match self { + NodeHeader::HashedValueBranch(_) + | NodeHeader::HashedValueLeaf(_) => true, + _ => false, + } + } +} + +/// NodeHeader without content +pub(crate) enum NodeKind { + Leaf, + BranchNoValue, + BranchWithValue, + HashedValueLeaf, + HashedValueBranch, +} + +impl Encode for NodeHeader { + fn encode_to(&self, output: &mut T) { + match self { + NodeHeader::Null => output.push_byte(trie_constants::EMPTY_TRIE), + NodeHeader::Branch(true, nibble_count) => + encode_size_and_prefix(*nibble_count, trie_constants::BRANCH_WITH_MASK, 2, output), + NodeHeader::Branch(false, nibble_count) => + encode_size_and_prefix(*nibble_count, trie_constants::BRANCH_WITHOUT_MASK, 2, output), + NodeHeader::Leaf(nibble_count) => + encode_size_and_prefix(*nibble_count, trie_constants::LEAF_PREFIX_MASK, 2, output), + NodeHeader::HashedValueBranch(nibble_count) => + encode_size_and_prefix(*nibble_count, trie_constants::ALT_HASHING_BRANCH_WITH_MASK, 4, output), + NodeHeader::HashedValueLeaf(nibble_count) => + encode_size_and_prefix(*nibble_count, trie_constants::ALT_HASHING_LEAF_PREFIX_MASK, 3, output), + } + } +} + +impl parity_scale_codec::EncodeLike for NodeHeader {} + +impl Decode for NodeHeader { + fn decode(input: &mut I) -> Result { + let i = input.read_byte()?; + if i == trie_constants::EMPTY_TRIE { + return Ok(NodeHeader::Null); + } + match i & (0b11 << 6) { + trie_constants::LEAF_PREFIX_MASK => Ok(NodeHeader::Leaf(decode_size(i, input, 2)?)), + trie_constants::BRANCH_WITH_MASK => Ok(NodeHeader::Branch(true, decode_size(i, input, 2)?)), + trie_constants::BRANCH_WITHOUT_MASK => Ok(NodeHeader::Branch(false, decode_size(i, input, 2)?)), + trie_constants::EMPTY_TRIE => { + if i & (0b111 << 5) == trie_constants::ALT_HASHING_LEAF_PREFIX_MASK { + Ok(NodeHeader::HashedValueLeaf(decode_size(i, input, 3)?)) + } else if i & (0b1111 << 4) == trie_constants::ALT_HASHING_BRANCH_WITH_MASK { + Ok(NodeHeader::HashedValueBranch(decode_size(i, input, 4)?)) + } else { + // do not allow any special encoding + Err("Unallowed encoding".into()) + } + }, + _ => unreachable!(), + } + } +} + +/// Returns an iterator over encoded bytes for node header and size. +/// Size encoding allows unlimited, length inefficient, representation, but +/// is bounded to 16 bit maximum value to avoid possible DOS. +pub(crate) fn size_and_prefix_iterator( + size: usize, + prefix: u8, + prefix_mask: usize, +) -> impl Iterator { + let size = std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, size); + + let max_value = 255u8 >> prefix_mask; + let l1 = std::cmp::min(max_value as usize - 1, size); + let (first_byte, mut rem) = if size == l1 { + (once(prefix + l1 as u8), 0) + } else { + (once(prefix + max_value as u8), size - l1) + }; + let next_bytes = move || { + if rem > 0 { + if rem < 256 { + let result = rem - 1; + rem = 0; + Some(result as u8) + } else { + rem = rem.saturating_sub(255); + Some(255) + } + } else { + None + } + }; + first_byte.chain(std::iter::from_fn(next_bytes)) +} + +/// Encodes size and prefix to a stream output (prefix on 2 first bit only). +fn encode_size_and_prefix(size: usize, prefix: u8, prefix_mask: usize, out: &mut W) + where W: Output + ?Sized, +{ + for b in size_and_prefix_iterator(size, prefix, prefix_mask) { + out.push_byte(b) + } +} + +/// Decode size only from stream input and header byte. +fn decode_size( + first: u8, + input: &mut impl Input, + prefix_mask: usize, +) -> Result { + let max_value = 255u8 >> prefix_mask; + let mut result = (first & max_value) as usize; + if result < max_value as usize { + return Ok(result); + } + result -= 1; + while result <= trie_constants::NIBBLE_SIZE_BOUND { + let n = input.read_byte()? as usize; + if n < 255 { + return Ok(result + n + 1); + } + result += 255; + } + Ok(trie_constants::NIBBLE_SIZE_BOUND) +} + +/// Reference implementation of a `TrieStream` without extension. +#[derive(Default, Clone)] +pub struct ReferenceTrieStreamNoExt { + /// Current node buffer. + buffer: Vec, +} + +/// Create a leaf/branch node, encoding a number of nibbles. +fn fuse_nibbles_node<'a>(nibbles: &'a [u8], kind: NodeKind) -> impl Iterator + 'a { + let size = std::cmp::min(trie_constants::NIBBLE_SIZE_BOUND, nibbles.len()); + + let iter_start = match kind { + NodeKind::Leaf => size_and_prefix_iterator(size, trie_constants::LEAF_PREFIX_MASK, 2), + NodeKind::BranchNoValue => size_and_prefix_iterator(size, trie_constants::BRANCH_WITHOUT_MASK, 2), + NodeKind::BranchWithValue => size_and_prefix_iterator(size, trie_constants::BRANCH_WITH_MASK, 2), + NodeKind::HashedValueLeaf => + size_and_prefix_iterator(size, trie_constants::ALT_HASHING_LEAF_PREFIX_MASK, 3), + NodeKind::HashedValueBranch => + size_and_prefix_iterator(size, trie_constants::ALT_HASHING_BRANCH_WITH_MASK, 4), + }; + iter_start + .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) + .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) +} + +use trie_root::Value as TrieStreamValue; +impl TrieStream for ReferenceTrieStreamNoExt { + + fn new() -> Self { + Self { + buffer: Vec::new(), + } + } + + fn append_empty_data(&mut self) { + self.buffer.push(trie_constants::EMPTY_TRIE); + } + + fn append_leaf(&mut self, key: &[u8], value: TrieStreamValue) { + let kind = match &value { + TrieStreamValue::Inline(..) => NodeKind::Leaf, + TrieStreamValue::Node(..) => NodeKind::HashedValueLeaf, + }; + self.buffer.extend(fuse_nibbles_node(key, kind)); + match &value { + TrieStreamValue::Inline(value) => { + Compact(value.len() as u32).encode_to(&mut self.buffer); + self.buffer.extend_from_slice(value); + }, + TrieStreamValue::Node(hash) => { + self.buffer.extend_from_slice(hash.as_slice()); + }, + }; + } + + fn begin_branch( + &mut self, + maybe_partial: Option<&[u8]>, + maybe_value: Option, + has_children: impl Iterator, + ) { + if let Some(partial) = maybe_partial { + let kind = match &maybe_value { + None => NodeKind::BranchNoValue, + Some(TrieStreamValue::Inline(..)) => NodeKind::BranchWithValue, + Some(TrieStreamValue::Node(..)) => NodeKind::HashedValueBranch, + }; + + self.buffer.extend(fuse_nibbles_node(partial, kind)); + let bm = branch_node_bit_mask(has_children); + self.buffer.extend([bm.0,bm.1].iter()); + } else { + unreachable!("trie stream codec only for no extension trie"); + } + match maybe_value { + None => (), + Some(TrieStreamValue::Inline(value)) => { + Compact(value.len() as u32).encode_to(&mut self.buffer); + self.buffer.extend_from_slice(value); + }, + Some(TrieStreamValue::Node(hash)) => { + self.buffer.extend_from_slice(hash.as_slice()); + }, + } + } + + fn append_extension(&mut self, _key: &[u8]) { + unreachable!("trie stream codec only for no extension trie"); + } + + fn append_substream(&mut self, other: Self) { + let data = other.out(); + match data.len() { + 0..=31 => data.encode_to(&mut self.buffer), + _ => H::hash(&data).as_ref().encode_to(&mut self.buffer), + } + } + + fn out(self) -> Vec { self.buffer } +} diff --git a/test-support/trie-bench/CHANGELOG.md b/test-support/trie-bench/CHANGELOG.md index b8e3101a..758c3a6c 100644 --- a/test-support/trie-bench/CHANGELOG.md +++ b/test-support/trie-bench/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog]. ## [Unreleased] +## [0.29.0] - 2021-10-19 +- Updated memory-db, triedb and trie-root. [#142](https://github.com/paritytech/trie/pull/142) + ## [0.28.0] - 2021-07-02 - Updated memory-db to 0.27. [#139](https://github.com/paritytech/trie/pull/139) - Updated parity-scale-codec to 2.0. [#137](https://github.com/paritytech/trie/pull/137) diff --git a/test-support/trie-bench/Cargo.toml b/test-support/trie-bench/Cargo.toml index 0fcc4d08..e7978251 100644 --- a/test-support/trie-bench/Cargo.toml +++ b/test-support/trie-bench/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "trie-bench" description = "Standard benchmarking suite for tries" -version = "0.28.0" +version = "0.29.0" authors = ["Parity Technologies "] repository = "https://github.com/paritytech/trie/" license = "Apache-2.0" @@ -11,8 +11,8 @@ edition = "2018" keccak-hasher = { path = "../keccak-hasher", version = "0.15.2" } trie-standardmap = { path = "../trie-standardmap", version = "0.15.2" } hash-db = { path = "../../hash-db" , version = "0.15.2"} -memory-db = { path = "../../memory-db", version = "0.27.0" } -trie-root = { path = "../../trie-root", version = "0.16.0" } -trie-db = { path = "../../trie-db", version = "0.22.0" } +memory-db = { path = "../../memory-db", version = "0.28.0" } +trie-root = { path = "../../trie-root", version = "0.17.0" } +trie-db = { path = "../../trie-db", version = "0.23.0" } criterion = "0.3.3" parity-scale-codec = { version = "2" } diff --git a/test-support/trie-bench/src/lib.rs b/test-support/trie-bench/src/lib.rs index 41750de5..526900bc 100644 --- a/test-support/trie-bench/src/lib.rs +++ b/test-support/trie-bench/src/lib.rs @@ -39,14 +39,16 @@ impl ::std::fmt::Debug for TrieInsertionList { fn benchmark(b: &mut Criterion, name: &str, content: Vec<(Vec, Vec)>) where - ::Out: 'static + ::Out: 'static, { let funs = vec![ - Fun::new("Closed", |b, d: &TrieInsertionList| b.iter(&mut ||{ - trie_root::(d.0.clone()) + Fun::new("Closed", |b, d: &TrieInsertionList| b.iter(&mut || { + trie_root::(d.0.clone(), Default::default()) })), - Fun::new("Fill", |b, d: &TrieInsertionList| b.iter(&mut ||{ - let mut memdb = MemoryDB::<_, HashKey, _>::new(&L::Codec::empty_node()[..]); + Fun::new("Fill", |b, d: &TrieInsertionList| b.iter(&mut || { + let mut memdb = MemoryDB::<_, HashKey, _>::new( + L::Codec::empty_node(), + ); let mut root = >::default(); let mut t = TrieDBMut::::new(&mut memdb, &mut root); for i in d.0.iter() { @@ -54,7 +56,9 @@ where } })), Fun::new("Iter", |b, d: &TrieInsertionList| { - let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&L::Codec::empty_node()[..]); + let mut memdb = MemoryDB::<_, HashKey<_>, _>::new( + L::Codec::empty_node(), + ); let mut root = >::default(); { let mut t = TrieDBMut::::new(&mut memdb, &mut root); @@ -100,7 +104,11 @@ fn random_value(seed: &mut ::Out) -> Vec { } } -pub fn standard_benchmark(b: &mut Criterion, name: &str) { +pub fn standard_benchmark(b: &mut Criterion, name: &str) + where + L: TrieLayout + 'static, + S: TrieStream, +{ // Typical ethereum transaction payload passing through `verify_block_integrity()` close to block #6317032; // 140 iteams, avg length 157bytes, total 22033bytes payload (expected root: 0xc1382bbef81d10a41d325e2873894b61162fb1e6167cafc663589283194acfda) diff --git a/trie-db/CHANGELOG.md b/trie-db/CHANGELOG.md index eba3dbf8..98aa56e4 100644 --- a/trie-db/CHANGELOG.md +++ b/trie-db/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog]. ## [Unreleased] +## [0.23.0] - 2021-10-19 +- Support for value nodes. [#142](https://github.com/paritytech/trie/pull/142) + ## [0.22.6] - 2021-07-02 - Updated `hashbrown` to 0.11. [#131](https://github.com/paritytech/trie/pull/131) diff --git a/trie-db/Cargo.toml b/trie-db/Cargo.toml index b9cbb042..cb145467 100644 --- a/trie-db/Cargo.toml +++ b/trie-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trie-db" -version = "0.22.6" +version = "0.23.0" authors = ["Parity Technologies "] description = "Merkle-Patricia Trie generic over key hasher and node encoding" repository = "https://github.com/paritytech/trie" diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 9a831179..6acce9c7 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -10,8 +10,8 @@ cargo-fuzz = true [dependencies] hash-db = { path = "../../hash-db", version = "0.15.2" } -memory-db = { path = "../../memory-db", version = "0.21.0" } -reference-trie = { path = "../../test-support/reference-trie", version = "0.21.0" } +memory-db = { path = "../../memory-db", version = "0.26.0" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.24.0" } keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.2" } [dependencies.trie-db] @@ -28,11 +28,6 @@ members = ["."] name = "trie_root_new" path = "fuzz_targets/trie_root_new.rs" -[[bin]] -name = "trie_unhashed_no_ext" -path = "fuzz_targets/trie_unhashed_no_ext.rs" - - [[bin]] name = "trie_root" path = "fuzz_targets/trie_root.rs" @@ -61,6 +56,10 @@ path = "fuzz_targets/seek_iter.rs" name = "trie_proof_valid" path = "fuzz_targets/trie_proof_valid.rs" +[[bin]] +name = "trie_codec_proof" +path = "fuzz_targets/trie_codec_proof.rs" + [[bin]] name = "trie_proof_invalid" path = "fuzz_targets/trie_proof_invalid.rs" diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs index 4922e057..149d59da 100644 --- a/trie-db/fuzz/fuzz_targets/no_ext_insert.rs +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs @@ -5,5 +5,5 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - fuzz_that_no_extension_insert(data); + fuzz_that_no_extension_insert::(data); }); diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs index 608e25fc..1b40103a 100644 --- a/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs @@ -5,5 +5,5 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - fuzz_that_no_extension_insert_remove(data); + fuzz_that_no_extension_insert_remove::(data); }); diff --git a/trie-db/fuzz/fuzz_targets/prefix_iter.rs b/trie-db/fuzz/fuzz_targets/prefix_iter.rs index 612989d2..3c2aa999 100644 --- a/trie-db/fuzz/fuzz_targets/prefix_iter.rs +++ b/trie-db/fuzz/fuzz_targets/prefix_iter.rs @@ -4,5 +4,5 @@ use trie_db_fuzz::fuzz_prefix_iter; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - fuzz_prefix_iter(data); + fuzz_prefix_iter::(data); }); diff --git a/trie-db/fuzz/fuzz_targets/seek_iter.rs b/trie-db/fuzz/fuzz_targets/seek_iter.rs index f303b283..ef2116f4 100644 --- a/trie-db/fuzz/fuzz_targets/seek_iter.rs +++ b/trie-db/fuzz/fuzz_targets/seek_iter.rs @@ -4,5 +4,5 @@ use trie_db_fuzz::fuzz_seek_iter; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - fuzz_seek_iter(data); + fuzz_seek_iter::(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_codec_proof.rs b/trie-db/fuzz/fuzz_targets/trie_codec_proof.rs new file mode 100644 index 00000000..ba7e92b6 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_codec_proof.rs @@ -0,0 +1,8 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use trie_db_fuzz::fuzz_that_trie_codec_proofs; + +fuzz_target!(|data: &[u8]| { + fuzz_that_trie_codec_proofs::(data); +}); diff --git a/trie-db/fuzz/fuzz_targets/trie_proof_invalid.rs b/trie-db/fuzz/fuzz_targets/trie_proof_invalid.rs index 565e7eb9..3112e353 100644 --- a/trie-db/fuzz/fuzz_targets/trie_proof_invalid.rs +++ b/trie-db/fuzz/fuzz_targets/trie_proof_invalid.rs @@ -5,5 +5,5 @@ use libfuzzer_sys::fuzz_target; use trie_db_fuzz::fuzz_that_verify_rejects_invalid_proofs; fuzz_target!(|data: &[u8]| { - fuzz_that_verify_rejects_invalid_proofs(data); -}); \ No newline at end of file + fuzz_that_verify_rejects_invalid_proofs::(data); +}); diff --git a/trie-db/fuzz/fuzz_targets/trie_proof_valid.rs b/trie-db/fuzz/fuzz_targets/trie_proof_valid.rs index 44f2b120..2c4141a0 100644 --- a/trie-db/fuzz/fuzz_targets/trie_proof_valid.rs +++ b/trie-db/fuzz/fuzz_targets/trie_proof_valid.rs @@ -5,5 +5,5 @@ use libfuzzer_sys::fuzz_target; use trie_db_fuzz::fuzz_that_verify_accepts_valid_proofs; fuzz_target!(|data: &[u8]| { - fuzz_that_verify_accepts_valid_proofs(data); -}); \ No newline at end of file + fuzz_that_verify_accepts_valid_proofs::(data); +}); diff --git a/trie-db/fuzz/fuzz_targets/trie_root.rs b/trie-db/fuzz/fuzz_targets/trie_root.rs index 32b65f83..75178bc2 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root.rs @@ -5,5 +5,5 @@ use trie_db_fuzz::fuzz_that_reference_trie_root; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - fuzz_that_reference_trie_root(data); + fuzz_that_reference_trie_root::(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs index 00763e26..902933a7 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs @@ -5,5 +5,5 @@ use trie_db_fuzz::fuzz_that_reference_trie_root_fix_length; use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - fuzz_that_reference_trie_root_fix_length(data); + fuzz_that_reference_trie_root_fix_length::(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_new.rs b/trie-db/fuzz/fuzz_targets/trie_root_new.rs index 9e9d41fa..05b6e3a3 100644 --- a/trie-db/fuzz/fuzz_targets/trie_root_new.rs +++ b/trie-db/fuzz/fuzz_targets/trie_root_new.rs @@ -6,5 +6,5 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { // fuzzed code goes here - fuzz_that_compare_implementations(data); + fuzz_that_compare_implementations::(data); }); diff --git a/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs b/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs deleted file mode 100644 index dba4cc68..00000000 --- a/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs +++ /dev/null @@ -1,10 +0,0 @@ - -#![no_main] - -use trie_db_fuzz::fuzz_that_unhashed_no_extension; -use libfuzzer_sys::fuzz_target; - -fuzz_target!(|data: &[u8]| { - // fuzzed code goes here - fuzz_that_unhashed_no_extension(data); -}); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 4b9d0efc..6b8d0d43 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -14,22 +14,17 @@ use hash_db::Hasher; -use keccak_hasher::KeccakHasher; use memory_db::{HashKey, MemoryDB, PrefixedKey}; use reference_trie::{ - calc_root_no_extension, - compare_no_extension_insert_remove, - ExtensionLayout, - NoExtensionLayout, - proof::{generate_proof, verify_proof}, - reference_trie_root, - RefTrieDBMut, - RefTrieDBMutNoExt, - RefTrieDBNoExt, - TrieDBIterator, + calc_root, + reference_trie_root_iter_build as reference_trie_root, + compare_insert_remove, }; use std::convert::TryInto; -use trie_db::{DBValue, Trie, TrieDB, TrieDBMut, TrieLayout, TrieMut}; +use trie_db::{DBValue, Trie, TrieDB, TrieLayout, TrieMut, + TrieDBIterator, TrieDBMut, +}; +use trie_db::proof::{generate_proof, verify_proof}; fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { let mut result = Vec::new(); @@ -94,26 +89,26 @@ fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec,Vec)> { res } -pub fn fuzz_that_reference_trie_root(input: &[u8]) { +pub fn fuzz_that_reference_trie_root(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for a in 0..data.len() { t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); } - assert_eq!(*t.root(), reference_trie_root(data)); + assert_eq!(*t.root(), reference_trie_root::(data)); } -pub fn fuzz_that_reference_trie_root_fix_length(input: &[u8]) { +pub fn fuzz_that_reference_trie_root_fix_length(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data_fix_length(input)); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for a in 0..data.len() { t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); } - assert_eq!(*t.root(), reference_trie_root(data)); + assert_eq!(*t.root(), reference_trie_root::(data)); } fn fuzz_to_data_fix_length(input: &[u8]) -> Vec<(Vec,Vec)> { @@ -141,25 +136,20 @@ fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { m.into_iter().collect() } -pub fn fuzz_that_compare_implementations(input: &[u8]) { +pub fn fuzz_that_compare_implementations(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data(input)); //println!("data:{:?}", &data); let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_implementations(data, memdb, hashdb); -} - -pub fn fuzz_that_unhashed_no_extension(input: &[u8]) { - let data = data_sorted_unique(fuzz_to_data(input)); - reference_trie::compare_unhashed_no_extension(data); + let hashdb = MemoryDB::, DBValue>::default(); + reference_trie::compare_implementations::(data, memdb, hashdb); } -pub fn fuzz_that_no_extension_insert(input: &[u8]) { +pub fn fuzz_that_no_extension_insert(input: &[u8]) { let data = fuzz_to_data(input); //println!("data{:?}", data); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for a in 0..data.len() { t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); } @@ -167,24 +157,24 @@ pub fn fuzz_that_no_extension_insert(input: &[u8]) { // before. let data = data_sorted_unique(fuzz_to_data(input)); //println!("data{:?}", data); - assert_eq!(*t.root(), calc_root_no_extension(data)); + assert_eq!(*t.root(), calc_root::(data)); } -pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { +pub fn fuzz_that_no_extension_insert_remove(input: &[u8]) { let data = fuzz_to_data(input); let data = fuzz_removal(data); let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - compare_no_extension_insert_remove(data, memdb); + compare_insert_remove::(data, memdb); } -pub fn fuzz_seek_iter(input: &[u8]) { +pub fn fuzz_seek_iter(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data_fix_length(input)); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for a in 0..data.len() { t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); } @@ -203,7 +193,7 @@ pub fn fuzz_seek_iter(input: &[u8]) { let mut iter_res = Vec::new(); let mut error = 0; { - let trie = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = trie.iter().unwrap(); if let Ok(_) = iter.seek(prefix) { } else { @@ -227,13 +217,13 @@ pub fn fuzz_seek_iter(input: &[u8]) { assert_eq!(error, 0); } -pub fn fuzz_prefix_iter(input: &[u8]) { +pub fn fuzz_prefix_iter(input: &[u8]) { let data = data_sorted_unique(fuzz_to_data_fix_length(input)); let mut memdb = MemoryDB::<_, HashKey<_>, _>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for a in 0..data.len() { t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); } @@ -252,7 +242,7 @@ pub fn fuzz_prefix_iter(input: &[u8]) { let mut iter_res = Vec::new(); let mut error = 0; { - let trie = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let iter = TrieDBIterator::new_prefixed(&trie, prefix).unwrap(); for x in iter { @@ -273,7 +263,27 @@ pub fn fuzz_prefix_iter(input: &[u8]) { assert_eq!(error, 0); } -pub fn fuzz_that_verify_accepts_valid_proofs(input: &[u8]) { +pub fn fuzz_that_verify_accepts_valid_proofs(input: &[u8]) { + let mut data = fuzz_to_data(input); + // Split data into 3 parts: + // - the first 1/3 is added to the trie and not included in the proof + // - the second 1/3 is added to the trie and included in the proof + // - the last 1/3 is not added to the trie and the proof proves non-inclusion of them + let mut keys = data[(data.len() / 3)..] + .iter() + .map(|(key, _)| key.clone()) + .collect::>(); + data.truncate(data.len() * 2 / 3); + + let data = data_sorted_unique(data); + keys.sort(); + keys.dedup(); + + let (root, proof, items) = test_generate_proof::(data, keys); + assert!(verify_proof::(&root, &proof, items.iter(), Default::default()).is_ok()); +} + +pub fn fuzz_that_trie_codec_proofs(input: &[u8]) { let mut data = fuzz_to_data(input); // Split data into 3 parts: // - the first 1/3 is added to the trie and not included in the proof @@ -289,11 +299,10 @@ pub fn fuzz_that_verify_accepts_valid_proofs(input: &[u8]) { keys.sort(); keys.dedup(); - let (root, proof, items) = test_generate_proof::(data, keys); - assert!(verify_proof::(&root, &proof, items.iter()).is_ok()); + test_trie_codec_proof::(data, keys); } -pub fn fuzz_that_verify_rejects_invalid_proofs(input: &[u8]) { +pub fn fuzz_that_verify_rejects_invalid_proofs(input: &[u8]) { if input.len() < 4 { return; } @@ -321,7 +330,7 @@ pub fn fuzz_that_verify_rejects_invalid_proofs(input: &[u8]) { return; } - let (root, proof, mut items) = test_generate_proof::(data, keys); + let (root, proof, mut items) = test_generate_proof::(data, keys); // Make one item at random incorrect. let items_idx = random_int % items.len(); @@ -330,7 +339,7 @@ pub fn fuzz_that_verify_rejects_invalid_proofs(input: &[u8]) { (_, value) if value.is_some() => *value = None, (_, value) => *value = Some(DBValue::new()), } - assert!(verify_proof::(&root, &proof, items.iter()).is_err()); + assert!(verify_proof::(&root, &proof, items.iter(), Default::default()).is_err()); } fn test_generate_proof( @@ -363,3 +372,73 @@ fn test_generate_proof( (root, proof, items) } + +fn test_trie_codec_proof( + entries: Vec<(Vec, Vec)>, + keys: Vec>, +) +{ + use hash_db::{HashDB, EMPTY_PREFIX}; + use trie_db::{ + Recorder, + encode_compact, decode_compact, + }; + + // Populate DB with full trie from entries. + let (db, root) = { + let mut db = , _>>::default(); + let mut root = Default::default(); + { + let mut trie = >::new(&mut db, &mut root); + for (key, value) in entries { + trie.insert(&key, &value).unwrap(); + } + } + (db, root) + }; + let expected_root = root; + // Lookup items in trie while recording traversed nodes. + let mut recorder = Recorder::new(); + let items = { + let mut items = Vec::with_capacity(keys.len()); + let trie = >::new(&db, &root).unwrap(); + for key in keys { + let value = trie.get_with(key.as_slice(), &mut recorder).unwrap(); + items.push((key, value)); + } + items + }; + + // Populate a partial trie DB with recorded nodes. + let mut partial_db = , _>>::default(); + for record in recorder.drain() { + if L::USE_META { + partial_db.alt_insert( + EMPTY_PREFIX, + &record.data, + record.meta.resolve_alt_hashing::(), + ); + } else { + partial_db.emplace(record.hash, EMPTY_PREFIX, record.data); + } + } + + // Compactly encode the partial trie DB. + let compact_trie = { + let trie = >::new(&partial_db, &root).unwrap(); + encode_compact::(&trie).unwrap() + }; + + let expected_used = compact_trie.len(); + // Reconstruct the partial DB from the compact encoding. + let mut db = , _>>::default(); + let (root, used) = decode_compact::(&mut db, &compact_trie).unwrap(); + assert_eq!(root, expected_root); + assert_eq!(used, expected_used); + + // Check that lookups for all items succeed. + let trie = >::new(&db, &root).unwrap(); + for (key, expected_value) in items { + assert_eq!(trie.get(key.as_slice()).unwrap(), expected_value); + } +} diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index e5d4a198..895145b8 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2020 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,8 +13,8 @@ // limitations under the License. use hash_db::{HashDBRef, Hasher}; -use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query, - TrieLayout, CError, TrieHash}; +use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieDBKeyIterator, TrieItem, + TrieKeyItem, TrieIterator, Query, TrieLayout, CError, TrieHash}; use crate::rstd::boxed::Box; @@ -71,9 +71,17 @@ where > { FatDBIterator::::new(&self.raw).map(|iter| Box::new(iter) as Box<_>) } + + fn key_iter<'a>(&'a self) -> Result< + Box, CError>> + 'a>, + TrieHash, + CError, + > { + FatDBKeyIterator::::new(&self.raw).map(|iter| Box::new(iter) as Box<_>) + } } -/// Itarator over inserted pairs of key values. +/// Iterator over inserted pairs of key values. pub struct FatDBIterator<'db, L> where L: TrieLayout, @@ -125,3 +133,53 @@ where }) } } + +/// Iterator over inserted keys. +pub struct FatDBKeyIterator<'db, L> +where + L: TrieLayout, +{ + trie_iterator: TrieDBKeyIterator<'db, L>, + trie: &'db TrieDB<'db, L>, +} + +impl<'db, L> FatDBKeyIterator<'db, L> +where + L: TrieLayout, +{ + /// Creates new iterator. + pub fn new(trie: &'db TrieDB) -> Result, CError> { + Ok(FatDBKeyIterator { + trie_iterator: TrieDBKeyIterator::new(trie)?, + trie, + }) + } +} + +impl<'db, L> TrieIterator for FatDBKeyIterator<'db, L> +where + L: TrieLayout, +{ + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + let hashed_key = L::Hash::hash(key); + self.trie_iterator.seek(hashed_key.as_ref()) + } +} + +impl<'db, L> Iterator for FatDBKeyIterator<'db, L> +where + L: TrieLayout, +{ + type Item = TrieKeyItem<'db, TrieHash, CError>; + + fn next(&mut self) -> Option { + self.trie_iterator.next() + .map(|res| { + res.map(|hash| { + let aux_hash = L::Hash::hash(&hash); + self.trie.db().get(&aux_hash, Default::default()) + .expect("Missing fatdb hash") + }) + }) + } +} diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index 4e8d575c..00ae4d32 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -13,7 +13,8 @@ // limitations under the License. use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; -use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayout, TrieHash, CError}; +use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayout, TrieHash, CError, + Value}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// Additionaly it stores inserted hash-key mappings for later retrieval. @@ -80,7 +81,7 @@ where &mut self, key: &[u8], value: &[u8], - ) -> Result, TrieHash, CError> { + ) -> Result>, TrieHash, CError> { let hash = L::Hash::hash(key); let out = self.raw.insert(hash.as_ref(), value)?; let db = self.raw.db_mut(); @@ -93,7 +94,7 @@ where Ok(out) } - fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + fn remove(&mut self, key: &[u8]) -> Result>, TrieHash, CError> { let hash = L::Hash::hash(key); let out = self.raw.remove(hash.as_ref())?; diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 1f5f4747..33f89a07 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -18,12 +18,13 @@ //! See `trie_visit` function. use hash_db::{Hasher, HashDB, Prefix}; -use crate::rstd::{cmp::max, marker::PhantomData, vec::Vec}; +use crate::rstd::{cmp::max, vec::Vec, marker::PhantomData}; use crate::triedbmut::{ChildReference}; use crate::nibble::NibbleSlice; use crate::nibble::nibble_ops; use crate::node_codec::NodeCodec; -use crate::{TrieLayout, TrieHash}; +use crate::{TrieLayout, TrieHash, DBValue}; +use crate::node::Value; macro_rules! exponential_out { (@3, [$($inpp:expr),*]) => { exponential_out!(@2, [$($inpp,)* $($inpp),*]) }; @@ -45,7 +46,7 @@ type ArrayNode = [CacheNode>; 16]; /// Note that it is not memory optimal (all depth are allocated even if some are empty due /// to node partial). /// Three field are used, a cache over the children, an optional associated value and the depth. -struct CacheAccum (Vec<(ArrayNode, Option, usize)>, PhantomData); +struct CacheAccum (Vec<(ArrayNode, Option, usize)>); /// Initially allocated cache depth. const INITIAL_DEPTH: usize = 10; @@ -58,11 +59,11 @@ impl CacheAccum fn new() -> Self { let v = Vec::with_capacity(INITIAL_DEPTH); - CacheAccum(v, PhantomData) + CacheAccum(v) } #[inline(always)] - fn set_cache_value(&mut self, depth:usize, value: Option) { + fn set_cache_value(&mut self, depth: usize, value: Option) { if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { self.0.push((new_vec_slice_buffer(), None, depth)); } @@ -114,13 +115,7 @@ impl CacheAccum self.0.len() == 1 } - #[inline(always)] - fn reset_depth(&mut self, depth: usize) { - debug_assert!(self.0[self.0.len() - 1].2 == depth); - self.0.pop(); - } - - fn flush_value ( + fn flush_value( &mut self, callback: &mut impl ProcessEncodedNode>, target_depth: usize, @@ -129,11 +124,22 @@ impl CacheAccum let nibble_value = nibble_ops::left_nibble_at(&k2.as_ref()[..], target_depth); // is it a branch value (two candidate same ix) let nkey = NibbleSlice::new_offset(&k2.as_ref()[..], target_depth + 1); - let encoded = T::Codec::leaf_node(nkey.right(), &v2.as_ref()[..]); let pr = NibbleSlice::new_offset( &k2.as_ref()[..], k2.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - nkey.len(), ); + + let hashed; + let value = if let Some(value) = Value::new_inline(v2.as_ref(), T::MAX_INLINE_VALUE) { + value + } else { + hashed = callback.process_inner_hashed_value( + (k2.as_ref(), None), + v2.as_ref(), + ); + Value::Node(hashed.as_ref(), None) + }; + let encoded = T::Codec::leaf_node(nkey.right(), value); let hash = callback.process(pr.left(), encoded, false); // insert hash in branch (first level branch only at this point) @@ -142,7 +148,6 @@ impl CacheAccum fn flush_branch( &mut self, - no_extension: bool, callback: &mut impl ProcessEncodedNode>, ref_branch: impl AsRef<[u8]> + Ord, new_depth: usize, @@ -150,7 +155,6 @@ impl CacheAccum ) { while self.last_depth() > new_depth || is_last && !self.is_empty() { - let lix = self.last_depth(); let llix = max(self.last_last_depth(), new_depth); @@ -167,11 +171,11 @@ impl CacheAccum None }; - let h = if no_extension { + let h = if T::USE_EXTENSION { + self.standard_extension(&ref_branch.as_ref()[..], callback, lix, is_root, nkey) + } else { // encode branch self.no_extension(&ref_branch.as_ref()[..], callback, lix, is_root, nkey) - } else { - self.standard_extension(&ref_branch.as_ref()[..], callback, lix, is_root, nkey) }; if !is_root { // put hash in parent @@ -193,14 +197,33 @@ impl CacheAccum let last = self.0.len() - 1; assert_eq!(self.0[last].2, branch_d); + let (children, v, depth) = self.0.pop().expect("checked"); + + debug_assert!(branch_d == depth); + let pr = NibbleSlice::new_offset(&key_branch, branch_d); + + let hashed; + let value = if let Some(v) = v.as_ref() { + Some(if let Some(value) = Value::new_inline(v.as_ref(), T::MAX_INLINE_VALUE) { + value + } else { + let mut prefix = NibbleSlice::new_offset(&key_branch, 0); + prefix.advance(branch_d); + hashed = callback.process_inner_hashed_value( + prefix.left(), + v.as_ref(), + ); + Value::Node(hashed.as_ref(), None) + }) + } else { + None + }; + // encode branch - let v = self.0[last].1.take(); let encoded = T::Codec::branch_node( - self.0[last].0.as_ref().iter(), - v.as_ref().map(|v| v.as_ref()), + children.iter(), + value, ); - self.reset_depth(branch_d); - let pr = NibbleSlice::new_offset(&key_branch, branch_d); let branch_hash = callback.process(pr.left(), encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { @@ -221,26 +244,38 @@ impl CacheAccum branch_d: usize, is_root: bool, nkey: Option<(usize, usize)>, - ) -> ChildReference> { - let last = self.0.len() - 1; - debug_assert!(self.0[last].2 == branch_d); + ) -> ChildReference> { + let (children, v, depth) = self.0.pop().expect("checked"); + + debug_assert!(branch_d == depth); // encode branch - let v = self.0[last].1.take(); - let nkeyix = nkey.unwrap_or((0, 0)); + let nkeyix = nkey.unwrap_or((branch_d, 0)); let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); + let hashed; + let value = if let Some(v) = v.as_ref() { + Some(if let Some(value) = Value::new_inline(v.as_ref(), T::MAX_INLINE_VALUE) { + value + } else { + let mut prefix = NibbleSlice::new_offset(&key_branch, 0); + prefix.advance(branch_d); + hashed = callback.process_inner_hashed_value( + prefix.left(), + v.as_ref(), + ); + Value::Node(hashed.as_ref(), None) + }) + } else { + None + }; + let encoded = T::Codec::branch_node_nibbled( pr.right_range_iter(nkeyix.1), nkeyix.1, - self.0[last].0.as_ref().iter(), v.as_ref().map(|v| v.as_ref())); - self.reset_depth(branch_d); - let ext_length = nkey.as_ref().map(|nkeyix| nkeyix.0).unwrap_or(0); - let pr = NibbleSlice::new_offset( - &key_branch, - branch_d - ext_length, + children.iter(), + value, ); callback.process(pr.left(), encoded, is_root) } - } /// Function visiting trie from key value inputs with a `ProccessEncodedNode` callback. @@ -255,7 +290,6 @@ pub fn trie_visit(input: I, callback: &mut F) B: AsRef<[u8]>, F: ProcessEncodedNode>, { - let no_extension = !T::USE_EXTENSION; let mut depth_queue = CacheAccum::::new(); // compare iter ordering let mut iter_input = input.into_iter(); @@ -280,7 +314,7 @@ pub fn trie_visit(input: I, callback: &mut F) // do not put with next, previous is last of a branch depth_queue.flush_value(callback, last_depth, &previous_value); let ref_branches = previous_value.0; - depth_queue.flush_branch(no_extension, callback, ref_branches, depth_item, false); + depth_queue.flush_branch(callback, ref_branches, depth_item, false); } previous_value = (k, v); @@ -291,16 +325,29 @@ pub fn trie_visit(input: I, callback: &mut F) // one single element corner case let (k2, v2) = previous_value; let nkey = NibbleSlice::new_offset(&k2.as_ref()[..], last_depth); - let encoded = T::Codec::leaf_node(nkey.right(), &v2.as_ref()[..]); let pr = NibbleSlice::new_offset( &k2.as_ref()[..], k2.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - nkey.len(), ); + + let hashed; + let value = if let Some(value) = Value::new_inline(v2.as_ref(), T::MAX_INLINE_VALUE) { + value + } else { + hashed = callback.process_inner_hashed_value( + (k2.as_ref(), None), + v2.as_ref(), + ); + Value::Node(hashed.as_ref(), None) + }; + + + let encoded = T::Codec::leaf_node(nkey.right(), value); callback.process(pr.left(), encoded, true); } else { depth_queue.flush_value(callback, last_depth, &previous_value); let ref_branches = previous_value.0; - depth_queue.flush_branch(no_extension, callback, ref_branches, 0, true); + depth_queue.flush_branch(callback, ref_branches, 0, true); } } else { // nothing null root corner case @@ -317,35 +364,49 @@ pub trait ProcessEncodedNode { /// but usually it should be the Hash of encoded node. /// This is not something direcly related to encoding but is here for /// optimisation purpose (builder hash_db does return this value). - fn process(&mut self, prefix: Prefix, encoded_node: Vec, is_root: bool) -> ChildReference; + fn process( + &mut self, + prefix: Prefix, + encoded_node: Vec, + is_root: bool, + ) -> ChildReference; + + /// Callback for hashed value in encoded node. + fn process_inner_hashed_value( + &mut self, + prefix: Prefix, + value: &[u8], + ) -> HO; } /// Get trie root and insert visited node in a hash_db. /// As for all `ProcessEncodedNode` implementation, it /// is only for full trie parsing (not existing trie). -pub struct TrieBuilder<'a, H, HO, V, DB> { +pub struct TrieBuilder<'a, T: TrieLayout, DB> { db: &'a mut DB, - pub root: Option, - _ph: PhantomData<(H, V)>, + pub root: Option>, } -impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { +impl<'a, T: TrieLayout, DB> TrieBuilder<'a, T, DB> { pub fn new(db: &'a mut DB) -> Self { - TrieBuilder { db, root: None, _ph: PhantomData } + TrieBuilder { db, root: None } } } -impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> - for TrieBuilder<'a, H, ::Out, V, DB> { +impl<'a, T, DB> ProcessEncodedNode> for TrieBuilder<'a, T, DB> + where + T: TrieLayout, + DB: HashDB, +{ fn process( &mut self, prefix: Prefix, encoded_node: Vec, is_root: bool, - ) -> ChildReference<::Out> { + ) -> ChildReference> { let len = encoded_node.len(); - if !is_root && len < ::LENGTH { - let mut h = <::Out as Default>::default(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); return ChildReference::Inline(h, len); @@ -356,51 +417,66 @@ impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> }; ChildReference::Hash(hash) } + + fn process_inner_hashed_value( + &mut self, + prefix: Prefix, + value: &[u8], + ) -> TrieHash { + self.db.insert(prefix, value) + } } /// Calculate the trie root of the trie. -pub struct TrieRoot { +pub struct TrieRoot { /// The resulting root. - pub root: Option, - _ph: PhantomData, + pub root: Option>, } -impl Default for TrieRoot { +impl Default for TrieRoot { fn default() -> Self { - TrieRoot { root: None, _ph: PhantomData } + TrieRoot { root: None } } } -impl ProcessEncodedNode<::Out> for TrieRoot::Out> { +impl ProcessEncodedNode> for TrieRoot { fn process( &mut self, _: Prefix, encoded_node: Vec, is_root: bool, - ) -> ChildReference<::Out> { + ) -> ChildReference> { let len = encoded_node.len(); - if !is_root && len < ::LENGTH { - let mut h = <::Out as Default>::default(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); return ChildReference::Inline(h, len); } - let hash = ::hash(&encoded_node[..]); + let hash = ::hash(encoded_node.as_slice()); if is_root { self.root = Some(hash); }; ChildReference::Hash(hash) } + + fn process_inner_hashed_value( + &mut self, + _prefix: Prefix, + value: &[u8], + ) -> TrieHash { + ::hash(value) + } } /// Get the trie root node encoding. -pub struct TrieRootUnhashed { +pub struct TrieRootUnhashed { /// The resulting encoded root. pub root: Option>, - _ph: PhantomData, + _ph: PhantomData, } -impl Default for TrieRootUnhashed { +impl Default for TrieRootUnhashed { fn default() -> Self { TrieRootUnhashed { root: None, _ph: PhantomData } } @@ -409,64 +485,82 @@ impl Default for TrieRootUnhashed { #[cfg(feature = "std")] /// Calculate the trie root of the trie. /// Print a debug trace. -pub struct TrieRootPrint { +pub struct TrieRootPrint { /// The resulting root. - pub root: Option, - _ph: PhantomData, + pub root: Option>, + _ph: PhantomData, } #[cfg(feature = "std")] -impl Default for TrieRootPrint { +impl Default for TrieRootPrint { fn default() -> Self { TrieRootPrint { root: None, _ph: PhantomData } } } #[cfg(feature = "std")] -impl ProcessEncodedNode<::Out> for TrieRootPrint::Out> { +impl ProcessEncodedNode> for TrieRootPrint { fn process( &mut self, p: Prefix, encoded_node: Vec, is_root: bool, - ) -> ChildReference<::Out> { + ) -> ChildReference> { println!("Encoded node: {:x?}", &encoded_node); println!(" with prefix: {:x?}", &p); let len = encoded_node.len(); - if !is_root && len < ::LENGTH { - let mut h = <::Out as Default>::default(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); println!(" inline len {}", len); return ChildReference::Inline(h, len); } - let hash = ::hash(&encoded_node[..]); + let hash = ::hash(encoded_node.as_slice()); if is_root { self.root = Some(hash); }; println!(" hashed to {:x?}", hash.as_ref()); ChildReference::Hash(hash) } + + fn process_inner_hashed_value( + &mut self, + _prefix: Prefix, + value: &[u8], + ) -> TrieHash { + println!("Hashed node: {:x?}", &value); + ::hash(value) + } } -impl ProcessEncodedNode<::Out> for TrieRootUnhashed { +impl ProcessEncodedNode> for TrieRootUnhashed { fn process( &mut self, _: Prefix, encoded_node: Vec, is_root: bool, - ) -> ChildReference<::Out> { + ) -> ChildReference<::Out> { let len = encoded_node.len(); - if !is_root && len < ::LENGTH { - let mut h = <::Out as Default>::default(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); return ChildReference::Inline(h, len); } - let hash = ::hash(&encoded_node[..]); + let hash = ::hash(encoded_node.as_slice()); + if is_root { self.root = Some(encoded_node); }; ChildReference::Hash(hash) } + + fn process_inner_hashed_value( + &mut self, + _prefix: Prefix, + value: &[u8], + ) -> TrieHash { + ::hash(value) + } } diff --git a/trie-db/src/iterator.rs b/trie-db/src/iterator.rs index dd84dacf..3dd80650 100644 --- a/trie-db/src/iterator.rs +++ b/trie-db/src/iterator.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2020 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ // limitations under the License. use super::{CError, DBValue, Result, Trie, TrieHash, TrieIterator, TrieLayout}; -use hash_db::{Hasher, EMPTY_PREFIX}; +use hash_db::{Hasher, EMPTY_PREFIX, Prefix}; use crate::triedb::TrieDB; use crate::node::{NodePlan, NodeHandle, OwnedNode}; use crate::nibble::{NibbleSlice, NibbleVec, nibble_ops}; @@ -61,6 +61,27 @@ pub struct TrieDBNodeIterator<'a, L: TrieLayout> { key_nibbles: NibbleVec, } +/// When there is guaranties the storage backend do not change, +/// this can be use to suspend and restore the iterator. +pub struct SuspendedTrieDBNodeIterator { + trail: Vec>, + key_nibbles: NibbleVec, +} + +impl SuspendedTrieDBNodeIterator { + /// Restore iterator. + pub fn unsafe_restore<'a>( + self, + db: &'a TrieDB<'a, L>, + ) -> TrieDBNodeIterator<'a, L> { + TrieDBNodeIterator { + db, + trail: self.trail, + key_nibbles: self.key_nibbles, + } + } +} + impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { /// Create a new iterator. pub fn new(db: &'a TrieDB) -> Result, TrieHash, CError> { @@ -86,10 +107,25 @@ impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { node: Rc::new(node), }); } + + /// Suspend iterator. Warning this does not hold guaranties it can be restored later. + /// Restoring requires that trie backend does not change. + pub fn suspend(self) -> SuspendedTrieDBNodeIterator { + SuspendedTrieDBNodeIterator { + trail: self.trail, + key_nibbles: self.key_nibbles, + } + } + + /// Fetch value by hash at a current node height + pub fn fetch_value(&self, key: &[u8], prefix: Prefix) -> Option { + let mut res = TrieHash::::default(); + res.as_mut().copy_from_slice(key); + self.db.db().get(&res, prefix) + } } impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { - /// Seek a node position at 'key' for iterator. /// Returns true if the cursor is at or after the key, but still shares /// a common prefix with the key, return false if the key do not @@ -279,6 +315,11 @@ impl<'a, L: TrieLayout> TrieDBNodeIterator<'a, L> { self.trail.clear(); Ok(()) } + + /// Access inner hash db. + pub fn db(&self) -> &dyn hash_db::HashDBRef { + self.db.db() + } } impl<'a, L: TrieLayout> TrieIterator for TrieDBNodeIterator<'a, L> { @@ -376,7 +417,7 @@ impl<'a, L: TrieLayout> Iterator for TrieDBNodeIterator<'a, L> { crumb.increment(); return Some(Ok(( self.key_nibbles.clone(), - crumb.hash, + crumb.hash.clone(), crumb.node.clone() ))); }, diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 37bfe64e..501cdf55 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2019 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -58,8 +58,8 @@ mod node_codec; mod trie_codec; pub use hash_db::{HashDB, HashDBRef, Hasher}; -pub use self::triedb::{TrieDB, TrieDBIterator}; -pub use self::triedbmut::{TrieDBMut, ChildReference}; +pub use self::triedb::{TrieDB, TrieDBIterator, TrieDBKeyIterator}; +pub use self::triedbmut::{TrieDBMut, ChildReference, Value}; pub use self::sectriedbmut::SecTrieDBMut; pub use self::sectriedb::SecTrieDB; pub use self::fatdb::{FatDB, FatDBIterator}; @@ -93,8 +93,9 @@ pub enum TrieError { /// The first parameter is the byte-aligned part of the prefix and the second parameter is the /// remaining nibble. ValueAtIncompleteKey(Vec, u8), - /// Corrupt Trie item + /// Corrupt Trie item. DecoderError(T, E), + /// Hash is not value. InvalidHash(T, Vec), } @@ -132,6 +133,9 @@ pub type Result = crate::rstd::result::Result>>; /// Trie-Item type used for iterators over trie data. pub type TrieItem<'a, U, E> = Result<(Vec, DBValue), U, E>; +/// Trie-Item type used for iterators over trie key only. +pub type TrieKeyItem<'a, U, E> = Result, U, E>; + /// Description of what kind of query will be made to the trie. /// /// This is implemented for any &mut recorder (where the query will return @@ -204,6 +208,13 @@ pub trait Trie { TrieHash, CError >; + + /// Returns a depth-first iterator over the keys of elemets of trie. + fn key_iter<'a>(&'a self) -> Result< + Box, CError >> + 'a>, + TrieHash, + CError + >; } /// A key-value datastore implemented as a database-backed modified Merkle tree. @@ -231,11 +242,11 @@ pub trait TrieMut { &mut self, key: &[u8], value: &[u8], - ) -> Result, TrieHash, CError>; + ) -> Result>, TrieHash, CError>; /// Remove a `key` from the trie. Equivalent to making it equal to the empty /// value. Returns the old value associated with this key, if it existed. - fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError>; + fn remove(&mut self, key: &[u8]) -> Result>, TrieHash, CError>; } /// A trie iterator that also supports random access (`seek()`). @@ -320,6 +331,14 @@ impl<'db, L: TrieLayout> Trie for TrieKinds<'db, L> { > { wrapper!(self, iter,) } + + fn key_iter<'a>(&'a self) -> Result< + Box, CError>> + 'a>, + TrieHash, + CError, + > { + wrapper!(self, key_iter,) + } } impl<'db, L> TrieFactory @@ -384,6 +403,10 @@ pub trait TrieLayout { const USE_EXTENSION: bool; /// If true, the trie will allow empty values into `TrieDBMut` const ALLOW_EMPTY: bool = false; + /// Threshold above which an external node should be + /// use to store a node value. + const MAX_INLINE_VALUE: Option; + /// Hasher to use for this trie. type Hash: Hasher; /// Codec to use (needs to match hasher and nibble ops). @@ -396,32 +419,32 @@ pub trait TrieLayout { pub trait TrieConfiguration: Sized + TrieLayout { /// Operation to build a trie db from its ordered iterator over its key/values. fn trie_build(db: &mut DB, input: I) -> ::Out where - DB: HashDB, - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, + DB: HashDB, + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, { - let mut cb = TrieBuilder::new(db); + let mut cb = TrieBuilder::::new(db); trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or_default() } /// Determines a trie root given its ordered contents, closed form. fn trie_root(input: I) -> ::Out where - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, { - let mut cb = TrieRoot::::default(); + let mut cb = TrieRoot::::default(); trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or_default() } /// Determines a trie root node's data given its ordered contents, closed form. fn trie_root_unhashed(input: I) -> Vec where - I: IntoIterator, - A: AsRef<[u8]> + Ord, - B: AsRef<[u8]>, + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, { - let mut cb = TrieRootUnhashed::::default(); + let mut cb = TrieRootUnhashed::::default(); trie_visit::(input.into_iter(), &mut cb); cb.root.unwrap_or_default() } diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index e46bf8f1..3bbcbc08 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -14,9 +14,9 @@ //! Trie lookup via HashDB. -use hash_db::HashDBRef; +use hash_db::{HashDBRef, Prefix}; use crate::nibble::NibbleSlice; -use crate::node::{Node, NodeHandle, decode_hash}; +use crate::node::{Node, NodeHandle, decode_hash, Value}; use crate::node_codec::NodeCodec; use crate::rstd::boxed::Box; use super::{DBValue, Result, TrieError, Query, TrieLayout, CError, TrieHash}; @@ -36,6 +36,23 @@ where L: TrieLayout, Q: Query, { + fn decode(mut self, v: Value, prefix: Prefix, depth: u32) -> Result, CError> { + match v { + Value::Inline(value) => Ok(self.query.decode(value)), + Value::Node(_, Some(value)) => Ok(self.query.decode(value.as_slice())), + Value::Node(hash, None) => { + let mut res = TrieHash::::default(); + res.as_mut().copy_from_slice(hash); + if let Some(value) = self.db.get(&res, prefix) { + self.query.record(&res, &value, depth); + Ok(self.query.decode(value.as_slice())) + } else { + Err(Box::new(TrieError::IncompleteDatabase(res))) + } + }, + } + } + /// Look up the given key. If the value is found, it will be passed to the given /// function to decode or copy. pub fn look_up( @@ -43,11 +60,15 @@ where key: NibbleSlice, ) -> Result, TrieHash, CError> { let mut partial = key; - let mut hash = self.hash; let mut key_nibbles = 0; + let mut full_key = key.clone(); + full_key.advance(key.len()); + let full_key = full_key.left(); + // this loop iterates through non-inline nodes. for depth in 0.. { + let hash = self.hash; let node_data = match self.db.get(&hash, key.mid(key_nibbles).left()) { Some(value) => value, None => return Err(Box::new(match depth { @@ -71,7 +92,7 @@ where let next_node = match decoded { Node::Leaf(slice, value) => { return Ok(match slice == partial { - true => Some(self.query.decode(value)), + true => Some(self.decode(value, full_key, depth)?), false => None, }) } @@ -85,7 +106,11 @@ where } } Node::Branch(children, value) => match partial.is_empty() { - true => return Ok(value.map(move |val| self.query.decode(val))), + true => if let Some(value) = value { + return Ok(Some(self.decode(value, full_key, depth)?)); + } else { + return Ok(None); + }, false => match children[partial.at(0) as usize] { Some(x) => { partial = partial.mid(1); @@ -101,7 +126,11 @@ where } match partial.len() == slice.len() { - true => return Ok(value.map(move |val| self.query.decode(val))), + true => if let Some(value) = value { + return Ok(Some(self.decode(value, full_key, depth)?)); + } else { + return Ok(None); + }, false => match children[partial.at(slice.len()) as usize] { Some(x) => { partial = partial.mid(slice.len() + 1); @@ -118,7 +147,7 @@ where // check if new node data is inline or hash. match next_node { NodeHandle::Hash(data) => { - hash = decode_hash::(data) + self.hash = decode_hash::(data) .ok_or_else(|| Box::new(TrieError::InvalidHash(hash, data.to_vec())))?; break; }, diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 3a9a3910..90f1d0a0 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -14,7 +14,9 @@ //! Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. -use crate::rstd::{cmp::*, fmt}; +#[cfg(feature = "std")] +use crate::rstd::fmt; +use crate::rstd::cmp::*; use super::{nibble_ops, NibbleSlice, NibbleSliceIterator, BackingByteVec}; use crate::node::NodeKey; use crate::node_codec::Partial; diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index ef50e8d6..a3d29070 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2018 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ use hash_db::Hasher; use crate::nibble::{self, NibbleSlice}; use crate::nibble::nibble_ops; use crate::node_codec::NodeCodec; +use crate::DBValue; use crate::rstd::{borrow::Borrow, ops::Range}; @@ -40,6 +41,31 @@ pub fn decode_hash(data: &[u8]) -> Option { Some(hash) } +/// Value representation in `Node`. +#[derive(Eq, PartialEq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum Value<'a> { + /// Value byte slice as stored in a trie node. + Inline(&'a [u8]), + /// Hash byte slice as stored in a trie node, + /// and the actual value when accessed. + Node(&'a [u8], Option), +} + +impl<'a> Value<'a> { + pub(crate) fn new_inline(value: &'a [u8], threshold: Option) -> Option { + if let Some(threshold) = threshold { + if value.len() >= threshold as usize { + return None; + } else { + Some(Value::Inline(value)) + } + } else { + Some(Value::Inline(value)) + } + } +} + /// Type of node in the trie and essential information thereof. #[derive(Eq, PartialEq, Clone)] #[cfg_attr(feature = "std", derive(Debug))] @@ -47,14 +73,14 @@ pub enum Node<'a> { /// Null trie node; could be an empty root or an empty branch entry. Empty, /// Leaf node; has key slice and value. Value may not be empty. - Leaf(NibbleSlice<'a>, &'a [u8]), + Leaf(NibbleSlice<'a>, Value<'a>), /// Extension node; has key slice and node data. Data may not be null. Extension(NibbleSlice<'a>, NodeHandle<'a>), /// Branch node; has slice of child nodes (each possibly null) /// and an optional immediate node data. - Branch([Option>; nibble_ops::NIBBLE_LENGTH], Option<&'a [u8]>), + Branch([Option>; nibble_ops::NIBBLE_LENGTH], Option>), /// Branch node with support for a nibble (when extension nodes are not used). - NibbledBranch(NibbleSlice<'a>, [Option>; nibble_ops::NIBBLE_LENGTH], Option<&'a [u8]>), + NibbledBranch(NibbleSlice<'a>, [Option>; nibble_ops::NIBBLE_LENGTH], Option>), } /// A `NodeHandlePlan` is a decoding plan for constructing a `NodeHandle` from an encoded trie @@ -108,6 +134,27 @@ impl NibbleSlicePlan { } } +/// Plan for value representation in `NodePlan`. +#[derive(Eq, PartialEq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +pub enum ValuePlan { + /// Range for byte representation in encoded node. + Inline(Range), + /// Range for hash in encoded node and original + /// value size. + Node(Range), +} + +impl ValuePlan { + /// Build a value slice by decoding a byte slice according to the plan. + pub fn build<'a, 'b>(&'a self, data: &'b [u8]) -> Value<'b> { + match self { + ValuePlan::Inline(range) => Value::Inline(&data[range.clone()]), + ValuePlan::Node(range) => Value::Node(&data[range.clone()], None), + } + } +} + /// A `NodePlan` is a blueprint for decoding a node from a byte slice. The `NodePlan` is created /// by parsing an encoded node and can be reused multiple times. This is useful as a `Node` borrows /// from a byte slice and this struct does not. @@ -122,7 +169,7 @@ pub enum NodePlan { /// Leaf node; has a partial key plan and value. Leaf { partial: NibbleSlicePlan, - value: Range, + value: ValuePlan, }, /// Extension node; has a partial key plan and child data. Extension { @@ -132,13 +179,13 @@ pub enum NodePlan { /// Branch node; has slice of child nodes (each possibly null) /// and an optional immediate node data. Branch { - value: Option>, + value: Option, children: [Option; nibble_ops::NIBBLE_LENGTH], }, /// Branch node with support for a nibble (when extension nodes are not used). NibbledBranch { partial: NibbleSlicePlan, - value: Option>, + value: Option, children: [Option; nibble_ops::NIBBLE_LENGTH], }, } @@ -151,7 +198,7 @@ impl NodePlan { match self { NodePlan::Empty => Node::Empty, NodePlan::Leaf { partial, value } => - Node::Leaf(partial.build(data), &data[value.clone()]), + Node::Leaf(partial.build(data), value.build(data)), NodePlan::Extension { partial, child } => Node::Extension(partial.build(data), child.build(data)), NodePlan::Branch { value, children } => { @@ -159,19 +206,41 @@ impl NodePlan { for i in 0..nibble_ops::NIBBLE_LENGTH { child_slices[i] = children[i].as_ref().map(|child| child.build(data)); } - let value_slice = value.clone().map(|value| &data[value]); - Node::Branch(child_slices, value_slice) + Node::Branch(child_slices, value.as_ref().map(|v| v.build(data))) }, NodePlan::NibbledBranch { partial, value, children } => { let mut child_slices = [None; nibble_ops::NIBBLE_LENGTH]; for i in 0..nibble_ops::NIBBLE_LENGTH { child_slices[i] = children[i].as_ref().map(|child| child.build(data)); } - let value_slice = value.clone().map(|value| &data[value]); - Node::NibbledBranch(partial.build(data), child_slices, value_slice) + Node::NibbledBranch(partial.build(data), child_slices, value.as_ref().map(|v| v.build(data))) }, } } + + /// Access value plan from node plan, return `None` for + /// node that cannot contain a `ValuePlan`. + pub fn value_plan(&self) -> Option<&ValuePlan> { + match self { + NodePlan::Extension { .. } + | NodePlan::Empty => None, + NodePlan::Leaf { value, .. } => Some(value), + NodePlan::Branch { value, .. } + | NodePlan::NibbledBranch { value, .. } => value.as_ref(), + } + } + + /// Mutable ccess value plan from node plan, return `None` for + /// node that cannot contain a `ValuePlan`. + pub fn value_plan_mut(&mut self) -> Option<&mut ValuePlan> { + match self { + NodePlan::Extension { .. } + | NodePlan::Empty => None, + NodePlan::Leaf { value, .. } => Some(value), + NodePlan::Branch { value, .. } + | NodePlan::NibbledBranch { value, .. } => value.as_mut(), + } + } } /// An `OwnedNode` is an owned type from which a `Node` can be constructed which borrows data from @@ -200,6 +269,11 @@ impl> OwnedNode { &self.plan } + /// Returns a mutable reference to the node decode plan. + pub fn node_plan_mut(&mut self) -> &mut NodePlan { + &mut self.plan + } + /// Construct a `Node` by borrowing data from this struct. pub fn node(&self) -> Node { self.plan.build(self.data.borrow()) diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index ad33ebf5..4768d042 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -15,9 +15,8 @@ //! Generic trait for trie node encoding/decoding. Takes a `hash_db::Hasher` //! to parametrize the hashes used in the codec. -use crate::MaybeDebug; -use crate::node::{Node, NodePlan}; -use crate::ChildReference; +use crate::node::{Node, NodePlan, Value}; +use crate::{MaybeDebug, ChildReference}; use crate::rstd::{borrow::Borrow, Error, hash, vec::Vec}; @@ -29,7 +28,13 @@ use crate::rstd::{borrow::Borrow, Error, hash, vec::Vec}; pub type Partial<'a> = ((u8, u8), &'a[u8]); /// Trait for trie node encoding/decoding. +/// Uses a type parameter to allow registering +/// positions without colling decode plan. pub trait NodeCodec: Sized { + /// Escape header byte sequence to indicate next node is a + /// branch or leaf with hash of value, followed by the value node. + const ESCAPE_HEADER: Option = None; + /// Codec error type. type Error: Error; @@ -44,7 +49,7 @@ pub trait NodeCodec: Sized { fn decode_plan(data: &[u8]) -> Result; /// Decode bytes to a `Node`. Returns `Self::E` on failure. - fn decode(data: &[u8]) -> Result { + fn decode<'a>(data: &'a [u8]) -> Result, Self::Error> { Ok(Self::decode_plan(data)?.build(data)) } @@ -55,7 +60,7 @@ pub trait NodeCodec: Sized { fn empty_node() -> &'static [u8]; /// Returns an encoded leaf node - fn leaf_node(partial: Partial, value: &[u8]) -> Vec; + fn leaf_node(partial: Partial, value: Value) -> Vec; /// Returns an encoded extension node /// Note that number_nibble is the number of element of the iterator @@ -71,7 +76,7 @@ pub trait NodeCodec: Sized { /// Takes an iterator yielding `ChildReference` and an optional value. fn branch_node( children: impl Iterator>>>, - value: Option<&[u8]>, + value: Option, ) -> Vec; /// Returns an encoded branch node with a possible partial path. @@ -80,6 +85,6 @@ pub trait NodeCodec: Sized { partial: impl Iterator, number_nibble: usize, children: impl Iterator>>>, - value: Option<&[u8]> + value: Option, ) -> Vec; } diff --git a/trie-db/src/proof/generate.rs b/trie-db/src/proof/generate.rs index 817e9333..fded1cee 100644 --- a/trie-db/src/proof/generate.rs +++ b/trie-db/src/proof/generate.rs @@ -1,4 +1,4 @@ -// Copyright 2019 Parity Technologies +// Copyright 2019, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,20 +15,22 @@ //! Generation of compact proofs for Merkle-Patricia tries. use crate::rstd::{ - boxed::Box, convert::TryInto, marker::PhantomData, ops::Range, vec, vec::Vec, + boxed::Box, convert::TryInto, marker::PhantomData, vec, vec::Vec, }; use hash_db::Hasher; use crate::{ - CError, ChildReference, nibble::LeftNibbleSlice, nibble_ops::NIBBLE_LENGTH, NibbleSlice, node::{NodeHandle, NodeHandlePlan, NodePlan, OwnedNode}, NodeCodec, Recorder, + CError, ChildReference, nibble::LeftNibbleSlice, nibble_ops::NIBBLE_LENGTH, NibbleSlice, + node::{NodeHandle, NodeHandlePlan, NodePlan, OwnedNode, Value, ValuePlan}, NodeCodec, Recorder, Result as TrieResult, Trie, TrieError, TrieHash, - TrieLayout, + TrieLayout, Record, }; struct StackEntry<'a, C: NodeCodec> { /// The prefix is the nibble path to the node in the trie. prefix: LeftNibbleSlice<'a>, + /// Stacked node. node: OwnedNode>, /// The hash of the node or None if it is referenced inline. node_hash: Option, @@ -75,13 +77,24 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { /// Encode this entry to an encoded trie node with data properly omitted. fn encode_node(mut self) -> TrieResult, C::HashOut, C::Error> { + let omit_value = self.omit_value; let node_data = self.node.data(); - Ok(match self.node.node_plan() { + let value_with_omission = | + value_range: ValuePlan, + | -> Option + { + if !omit_value { + Some(value_range.build(&node_data)) + } else { + None + } + }; + let encoded = match self.node.node_plan() { NodePlan::Empty => node_data.to_vec(), - NodePlan::Leaf { .. } if !self.omit_value => node_data.to_vec(), + NodePlan::Leaf { .. } if !omit_value => node_data.to_vec(), NodePlan::Leaf { partial, value: _ } => { let partial = partial.build(node_data); - C::leaf_node(partial.right(), &[]) + C::leaf_node(partial.right(), Value::Inline(&[])) } NodePlan::Extension { .. } if self.child_index == 0 => node_data.to_vec(), NodePlan::Extension { partial: partial_plan, child: _ } => { @@ -95,7 +108,7 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { C::extension_node( partial.right_iter(), partial.len(), - child + child, ) } NodePlan::Branch { value, children } => { @@ -107,7 +120,7 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { )?; C::branch_node( self.children.into_iter(), - value_with_omission(node_data, value, self.omit_value) + value.clone().map(value_with_omission).flatten(), ) }, NodePlan::NibbledBranch { partial: partial_plan, value, children } => { @@ -122,10 +135,11 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { partial.right_iter(), partial.len(), self.children.into_iter(), - value_with_omission(node_data, value, self.omit_value) + value.clone().map(value_with_omission).flatten(), ) }, - }) + }; + Ok(encoded) } /// Populate the remaining references in `children` with references copied from @@ -208,9 +222,9 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { the encoding of the proof node is always smaller than the raw node as data is \ only stripped" ); - &mut hash.as_mut()[..encoded_child.len()].copy_from_slice(encoded_child); + hash.as_mut()[..encoded_child.len()].copy_from_slice(encoded_child); ChildReference::Inline(hash, encoded_child.len()) - } + }, } } } @@ -249,6 +263,7 @@ pub fn generate_proof<'a, T, L, I, K>(trie: &T, keys: I) // Perform the trie lookup for the next key, recording the sequence of nodes traversed. let mut recorder = Recorder::new(); let expected_value = trie.get_with(key_bytes, &mut recorder)?; + // Remember recorded entry is node query order and hashed value after its node. let mut recorded_nodes = recorder.drain().into_iter().peekable(); // Skip over recorded nodes already on the stack. Their indexes into the respective vector @@ -277,6 +292,7 @@ pub fn generate_proof<'a, T, L, I, K>(trie: &T, keys: I) &mut entry.children, &key, entry.prefix.len(), + &mut recorded_nodes, )?, // If stack is empty, descend into the root node. None => Step::Descend { @@ -327,6 +343,25 @@ pub fn generate_proof<'a, T, L, I, K>(trie: &T, keys: I) }; stack.push(child_entry); } + Step::FoundHashedValue(value) => { + assert_eq!( + Some(&value), + expected_value.as_ref(), + "expected_value is found using `trie_db::Lookup`; \ + value is found by traversing the same nodes recorded during the lookup \ + using the same logic; \ + thus the values found must be equal" + ); + assert!( + recorded_nodes.next().is_none(), + "the recorded nodes are only recorded on the lookup path to the current \ + key; \ + recorded nodes is the minimal sequence of trie nodes on the lookup path; \ + the value was found by traversing recorded nodes, so there must be none \ + remaining" + ); + break; + }, Step::FoundValue(value) => { assert_eq!( value, @@ -350,7 +385,7 @@ pub fn generate_proof<'a, T, L, I, K>(trie: &T, keys: I) } } - unwind_stack(&mut stack, &mut proof_nodes, None)?; + unwind_stack::(&mut stack, &mut proof_nodes, None)?; Ok(proof_nodes) } @@ -360,6 +395,20 @@ enum Step<'a> { child: NodeHandle<'a>, }, FoundValue(Option<&'a [u8]>), + FoundHashedValue(Vec), +} + +fn resolve_value( + recorded_nodes: &mut dyn Iterator>, +) -> TrieResult, C::HashOut, C::Error> +{ + if let Some(resolve_value) = recorded_nodes.next() { + Ok(Step::FoundHashedValue(resolve_value.data)) + } else { + Err(Box::new( + TrieError::IncompleteDatabase(C::HashOut::default()) + )) + } } /// Determine the next algorithmic step to take by matching the current key against the current top @@ -372,6 +421,7 @@ fn match_key_to_node<'a, C: NodeCodec>( children: &mut [Option>], key: &LeftNibbleSlice, prefix_len: usize, + recorded_nodes: &mut dyn Iterator>, ) -> TrieResult, C::HashOut, C::Error> { Ok(match node_plan { @@ -381,8 +431,16 @@ fn match_key_to_node<'a, C: NodeCodec>( if key.contains(&partial, prefix_len) && key.len() == prefix_len + partial.len() { - *omit_value = true; - Step::FoundValue(Some(&node_data[value_range.clone()])) + match value_range { + ValuePlan::Inline(value_range) => { + *omit_value = true; + Step::FoundValue(Some(&node_data[value_range.clone()])) + }, + ValuePlan::Node(..) => { + *omit_value = true; + resolve_value::(recorded_nodes)? + }, + } } else { Step::FoundValue(None) } @@ -401,7 +459,7 @@ fn match_key_to_node<'a, C: NodeCodec>( NodePlan::Branch { value, children: child_handles } => match_key_to_branch_node::( node_data, - value, + value.as_ref(), &child_handles, omit_value, child_index, @@ -409,11 +467,12 @@ fn match_key_to_node<'a, C: NodeCodec>( key, prefix_len, NibbleSlice::new(&[]), + recorded_nodes )?, NodePlan::NibbledBranch { partial: partial_plan, value, children: child_handles } => match_key_to_branch_node::( node_data, - value, + value.as_ref(), &child_handles, omit_value, child_index, @@ -421,13 +480,14 @@ fn match_key_to_node<'a, C: NodeCodec>( key, prefix_len, partial_plan.build(node_data), + recorded_nodes, )?, }) } fn match_key_to_branch_node<'a, 'b, C: NodeCodec>( node_data: &'a [u8], - value_range: &'b Option>, + value_range: Option<&'b ValuePlan>, child_handles: &'b [Option; NIBBLE_LENGTH], omit_value: &mut bool, child_index: &mut usize, @@ -435,6 +495,7 @@ fn match_key_to_branch_node<'a, 'b, C: NodeCodec>( key: &'b LeftNibbleSlice<'b>, prefix_len: usize, partial: NibbleSlice<'b>, + recorded_nodes: &mut dyn Iterator>, ) -> TrieResult, C::HashOut, C::Error> { if !key.contains(&partial, prefix_len) { @@ -442,8 +503,17 @@ fn match_key_to_branch_node<'a, 'b, C: NodeCodec>( } if key.len() == prefix_len + partial.len() { - *omit_value = true; - let value = value_range.clone().map(|range| &node_data[range]); + let value = match value_range { + Some(ValuePlan::Inline(range)) => { + *omit_value = true; + Some(&node_data[range.clone()]) + }, + Some(ValuePlan::Node(..)) => { + *omit_value = true; + return resolve_value::(recorded_nodes); + }, + None => None, + }; return Ok(Step::FoundValue(value)); } @@ -480,19 +550,6 @@ fn match_key_to_branch_node<'a, 'b, C: NodeCodec>( } } -fn value_with_omission<'a>( - node_data: &'a [u8], - value_range: &Option>, - omit: bool -) -> Option<&'a [u8]> -{ - if omit { - None - } else { - value_range.clone().map(|range| &node_data[range]) - } -} - /// Unwind the stack until the given key is prefixed by the entry at the top of the stack. If the /// key is None, unwind the stack completely. As entries are popped from the stack, they are /// encoded into proof nodes and added to the finalized proof. diff --git a/trie-db/src/proof/verify.rs b/trie-db/src/proof/verify.rs index eba4cb6a..95c55087 100644 --- a/trie-db/src/proof/verify.rs +++ b/trie-db/src/proof/verify.rs @@ -1,3 +1,5 @@ +// Copyright 2019, 2021 Parity Technologies +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -17,7 +19,7 @@ use crate::rstd::{ }; use crate::{ CError, ChildReference, nibble::LeftNibbleSlice, nibble_ops::NIBBLE_LENGTH, - node::{Node, NodeHandle}, NodeCodec, TrieHash, TrieLayout, + node::{Node, Value, NodeHandle}, NodeCodec, TrieHash, TrieLayout, }; use hash_db::Hasher; @@ -94,36 +96,38 @@ impl std::error::Error for } } -struct StackEntry<'a, C: NodeCodec> { +struct StackEntry<'a, L: TrieLayout> { /// The prefix is the nibble path to the node in the trie. prefix: LeftNibbleSlice<'a>, node: Node<'a>, is_inline: bool, /// The value associated with this trie node. - value: Option<&'a [u8]>, + value: Option>, /// 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]. child_index: usize, /// The child references to use in reconstructing the trie nodes. - children: Vec>>, - _marker: PhantomData, + children: Vec>>>, + /// Technical to attach lifetime to entry. + next_value_hash: Option>, + _marker: PhantomData, } -impl<'a, C: NodeCodec> StackEntry<'a, C> { +impl<'a, L: TrieLayout> StackEntry<'a, L> { fn new(node_data: &'a [u8], prefix: LeftNibbleSlice<'a>, is_inline: bool) - -> Result> + -> Result, CError>> { - let node = C::decode(node_data) + let node = L::Codec::decode(&node_data[..]) .map_err(Error::DecodeError)?; - let children_len = match node { + let children_len = match &node { Node::Empty | Node::Leaf(..) => 0, Node::Extension(..) => 1, Node::Branch(..) | Node::NibbledBranch(..) => NIBBLE_LENGTH, }; - let value = match node { + let value = match &node { Node::Empty | Node::Extension(_, _) => None, - Node::Leaf(_, value) => Some(value), - Node::Branch(_, value) | Node::NibbledBranch(_, _, value) => value, + Node::Leaf(_, value) => Some(value.clone()), + Node::Branch(_, value) | Node::NibbledBranch(_, _, value) => value.clone(), }; Ok(StackEntry { node, @@ -132,45 +136,54 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { value, child_index: 0, children: vec![None; children_len], + next_value_hash: None, _marker: PhantomData::default(), }) } + fn value(&self) -> Option { + if let Some(hash) = self.next_value_hash.as_ref() { + Some(Value::Node(hash.as_ref(), None)) + } else { + self.value.clone() + } + } + /// Encode this entry to an encoded trie node with data properly reconstructed. - fn encode_node(mut self) -> Result, Error> { + fn encode_node(mut self) -> Result, Error, CError>> { self.complete_children()?; Ok(match self.node { Node::Empty => - C::empty_node().to_vec(), + L::Codec::empty_node().to_vec(), Node::Leaf(partial, _) => { - let value = self.value + let value = self.value() .expect( "value is assigned to Some in StackEntry::new; \ value is only ever reassigned in the ValueMatch::MatchesLeaf match \ clause, which assigns only to Some" ); - C::leaf_node(partial.right(), value) + L::Codec::leaf_node(partial.right(), value) } Node::Extension(partial, _) => { let child = self.children[0] .expect("the child must be completed since child_index is 1"); - C::extension_node( + L::Codec::extension_node( partial.right_iter(), partial.len(), - child + child, ) } Node::Branch(_, _) => - C::branch_node( + L::Codec::branch_node( self.children.iter(), - self.value, + self.value(), ), Node::NibbledBranch(partial, _, _) => - C::branch_node_nibbled( + L::Codec::branch_node_nibbled( partial.right_iter(), partial.len(), self.children.iter(), - self.value, + self.value(), ), }) } @@ -179,9 +192,9 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { &mut self, child_prefix: LeftNibbleSlice<'a>, proof_iter: &mut I, - ) -> Result> + ) -> Result, CError>> where - I: Iterator>, + I: Iterator, { match self.node { Node::Extension(_, child) => { @@ -212,7 +225,7 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { } /// Populate the remaining references in `children` with references copied the node itself. - fn complete_children(&mut self) -> Result<(), Error> { + fn complete_children(&mut self) -> Result<(), Error, CError>> { match self.node { Node::Extension(_, child) if self.child_index == 0 => { let child_ref = child.try_into() @@ -239,9 +252,9 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { proof_iter: &mut I, child: NodeHandle<'a>, prefix: LeftNibbleSlice<'a>, - ) -> Result> + ) -> Result, CError>> where - I: Iterator>, + I: Iterator, { match child { NodeHandle::Inline(data) => { @@ -254,7 +267,7 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { } } NodeHandle::Hash(data) => { - let mut hash = C::HashOut::default(); + let mut hash = TrieHash::::default(); if data.len() != hash.as_ref().len() { return Err(Error::InvalidChildReference(data.to_vec())); } @@ -264,28 +277,40 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { } } + fn set_value(&mut self, value: &'a [u8]) { + self.value = if L::MAX_INLINE_VALUE.map(|max| value.len() < max as usize).unwrap_or(true) { + Some(Value::Inline(value)) + } else { + let hash = L::Hash::hash(value); + self.next_value_hash = Some(hash); + // will be replace on encode + None + }; + } + fn advance_item(&mut self, items_iter: &mut Peekable) - -> Result, Error> + -> Result, Error, CError>> where - I: Iterator)> + I: Iterator)>, { let step = loop { if let Some((key_bytes, value)) = items_iter.peek().cloned() { let key = LeftNibbleSlice::new(key_bytes); if key.starts_with(&self.prefix) { match match_key_to_node(&key, self.prefix.len(), &self.node) { - ValueMatch::MatchesLeaf => { - if value.is_none() { - return Err(Error::ValueMismatch(key_bytes.to_vec())); - } - self.value = value; - } - ValueMatch::MatchesBranch => - self.value = value, - ValueMatch::NotFound => - if value.is_some() { - return Err(Error::ValueMismatch(key_bytes.to_vec())); - }, + ValueMatch::MatchesLeaf => if let Some(value) = value { + self.set_value(value); + } else { + return Err(Error::ValueMismatch(key_bytes.to_vec())); + }, + ValueMatch::MatchesBranch => if let Some(value) = value { + self.set_value(value); + } else { + self.value = None; + }, + ValueMatch::NotFound => if value.is_some() { + return Err(Error::ValueMismatch(key_bytes.to_vec())); + }, ValueMatch::NotOmitted => return Err(Error::ExtraneousValue(key_bytes.to_vec())), ValueMatch::IsChild(child_prefix) => @@ -325,10 +350,13 @@ fn match_key_to_node<'a>(key: &LeftNibbleSlice<'a>, prefix_len: usize, node: &No Node::Leaf(partial, value) => { if key.contains(partial, prefix_len) && key.len() == prefix_len + partial.len() { - if value.is_empty() { - ValueMatch::MatchesLeaf - } else { - ValueMatch::NotOmitted + match value { + Value::Node(..) => ValueMatch::NotOmitted, + Value::Inline(value) => if value.is_empty() { + ValueMatch::MatchesLeaf + } else { + ValueMatch::NotOmitted + } } } else { ValueMatch::NotFound @@ -342,11 +370,11 @@ fn match_key_to_node<'a>(key: &LeftNibbleSlice<'a>, prefix_len: usize, node: &No } } Node::Branch(children, value) => { - match_key_to_branch_node(key, prefix_len, children, value) + match_key_to_branch_node(key, prefix_len, children, value.as_ref()) } Node::NibbledBranch(partial, children, value) => { if key.contains(partial, prefix_len) { - match_key_to_branch_node(key, prefix_len + partial.len(), children, value) + match_key_to_branch_node(key, prefix_len + partial.len(), children, value.as_ref()) } else { ValueMatch::NotFound } @@ -361,7 +389,7 @@ fn match_key_to_branch_node<'a>( key: &LeftNibbleSlice<'a>, prefix_plus_partial_len: usize, children: &[Option; NIBBLE_LENGTH], - value: &Option<&[u8]>, + value: Option<&Value>, ) -> ValueMatch<'a> { if key.len() == prefix_plus_partial_len { @@ -388,11 +416,14 @@ enum Step<'a> { } /// Verify a compact proof for key-value pairs in a trie given a root hash. -pub fn verify_proof<'a, L, I, K, V>(root: &::Out, proof: &[Vec], items: I) - -> Result<(), Error, CError>> +pub fn verify_proof<'a, L, I, K, V>( + root: &::Out, + proof: &[Vec], + items: I, +) -> Result<(), Error, CError>> where L: TrieLayout, - I: IntoIterator)>, + I: IntoIterator)>, K: 'a + AsRef<[u8]>, V: 'a + AsRef<[u8]>, { @@ -418,22 +449,23 @@ pub fn verify_proof<'a, L, I, K, V>(root: &::Out, proof: &[Ve } // Iterate simultaneously in order through proof nodes and key-value pairs to verify. - let mut proof_iter = proof.iter(); + let mut proof_iter = proof.iter().map(|i| i.as_slice()); let mut items_iter = items.into_iter().peekable(); // A stack of child references to fill in omitted branch children for later trie nodes in the // proof. - let mut stack: Vec> = Vec::new(); + let mut stack: Vec> = Vec::new(); let root_node = match proof_iter.next() { Some(node) => node, None => return Err(Error::IncompleteProof), }; - let mut last_entry = StackEntry::new( + let mut last_entry = StackEntry::::new( root_node, LeftNibbleSlice::new(&[]), - false + false, )?; + loop { // Insert omitted value. match last_entry.advance_item(&mut items_iter)? { @@ -451,7 +483,7 @@ pub fn verify_proof<'a, L, I, K, V>(root: &::Out, proof: &[Ve return Err(Error::InvalidChildReference(node_data)); } let mut hash = >::default(); - &mut hash.as_mut()[..node_data.len()].copy_from_slice(node_data.as_ref()); + hash.as_mut()[..node_data.len()].copy_from_slice(node_data.as_ref()); ChildReference::Inline(hash, node_data.len()) } else { let hash = L::Hash::hash(&node_data); @@ -482,4 +514,4 @@ pub fn verify_proof<'a, L, I, K, V>(root: &::Out, proof: &[Ve } Ok(()) -} \ No newline at end of file +} diff --git a/trie-db/src/recorder.rs b/trie-db/src/recorder.rs index a6220451..2a08ac57 100644 --- a/trie-db/src/recorder.rs +++ b/trie-db/src/recorder.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2020 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index 8b047135..c1505a7c 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -14,8 +14,10 @@ use hash_db::{HashDBRef, Hasher}; use crate::rstd::boxed::Box; +use crate::DBValue; use super::triedb::TrieDB; -use super::{Result, DBValue, Trie, TrieItem, TrieIterator, Query, TrieLayout, CError, TrieHash}; +use super::{Result, Trie, TrieItem, TrieKeyItem, TrieIterator, Query, TrieLayout, + CError, TrieHash}; /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// @@ -81,4 +83,12 @@ where > { TrieDB::iter(&self.raw) } + + fn key_iter<'a>(&'a self) -> Result< + Box, CError>> + 'a>, + TrieHash, + CError, + > { + TrieDB::key_iter(&self.raw) + } } diff --git a/trie-db/src/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index d5be23cd..6c704417 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -13,7 +13,7 @@ // limitations under the License. use hash_db::{HashDB, Hasher}; -use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayout, TrieHash, CError}; +use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayout, TrieHash, CError, Value}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// @@ -21,14 +21,14 @@ use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayout, TrieHash, CError}; /// object. pub struct SecTrieDBMut<'db, L> where - L: TrieLayout + L: TrieLayout, { - raw: TrieDBMut<'db, L> + raw: TrieDBMut<'db, L>, } impl<'db, L> SecTrieDBMut<'db, L> where - L: TrieLayout + L: TrieLayout, { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. @@ -79,11 +79,11 @@ where fn insert( &mut self, key: &[u8], value: &[u8], - ) -> Result, TrieHash, CError> { + ) -> Result>, TrieHash, CError> { self.raw.insert(&L::Hash::hash(key).as_ref(), value) } - fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + fn remove(&mut self, key: &[u8]) -> Result>, TrieHash, CError> { self.raw.remove(&L::Hash::hash(key).as_ref()) } } diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 68d956bb..43c4d389 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -1,4 +1,4 @@ -// Copyright 2019, 2020 Parity Technologies +// Copyright 2019, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -25,11 +25,11 @@ //! expected to save roughly (n - 1) hashes in size where n is the number of nodes in the partial //! trie. -use hash_db::HashDB; +use hash_db::{HashDB, Prefix}; use crate::{ CError, ChildReference, DBValue, NibbleVec, NodeCodec, Result, TrieHash, TrieError, TrieDB, TrieDBNodeIterator, TrieLayout, - nibble_ops::NIBBLE_LENGTH, node::{Node, NodeHandle, NodeHandlePlan, NodePlan, OwnedNode}, + nibble_ops::NIBBLE_LENGTH, node::{Node, NodeHandle, NodeHandlePlan, NodePlan, ValuePlan, OwnedNode}, }; use crate::rstd::{ boxed::Box, convert::TryInto, marker::PhantomData, rc::Rc, result, vec, vec::Vec, @@ -38,12 +38,15 @@ use crate::rstd::{ struct EncoderStackEntry { /// The prefix is the nibble path to the node in the trie. prefix: NibbleVec, + /// Node in memory content. 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]. child_index: usize, /// Flags indicating whether each child is omitted in the encoded node. omit_children: Vec, + /// Skip value if value node is after. + omit_value: bool, /// The encoding of the subtrie nodes rooted at this entry, which is built up in /// `encode_compact`. output_index: usize, @@ -95,9 +98,20 @@ impl EncoderStackEntry { } /// Generates the encoding of the subtrie rooted at this entry. - fn encode_node(&self) -> Result, C::HashOut, C::Error> { + fn encode_node(&mut self) -> Result, C::HashOut, C::Error> { let node_data = self.node.data(); - Ok(match self.node.node_plan() { + let mut modified_node_plan; + let node_plan = if self.omit_value { + modified_node_plan = self.node.node_plan().clone(); + if let Some(value) = modified_node_plan.value_plan_mut() { + // 0 length value. + *value = ValuePlan::Inline(0 .. 0); + } + &modified_node_plan + } else { + self.node.node_plan() + }; + let mut encoded = match node_plan { NodePlan::Empty | NodePlan::Leaf { .. } => node_data.to_vec(), NodePlan::Extension { partial, child: _ } => { if !self.omit_children[0] { @@ -111,7 +125,7 @@ impl EncoderStackEntry { NodePlan::Branch { value, children } => { C::branch_node( Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value.clone().map(|range| &node_data[range]) + value.as_ref().map(|v| v.build(node_data)), ) } NodePlan::NibbledBranch { partial, value, children } => { @@ -120,10 +134,19 @@ impl EncoderStackEntry { partial.right_iter(), partial.len(), Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value.clone().map(|range| &node_data[range]) + value.as_ref().map(|v| v.build(node_data)), ) } - }) + }; + + if self.omit_value { + if let Some(header) = C::ESCAPE_HEADER { + encoded.insert(0, header); + } else { + return Err(Box::new(TrieError::InvalidStateRoot(Default::default()))); + } + } + Ok(encoded) } /// Generate the list of child references for a branch node with certain children omitted. @@ -158,6 +181,29 @@ impl EncoderStackEntry { } } +/// Dettached value if included does write a reserved header, +/// followed by node encoded with 0 length value and the value +/// as a standalone vec. +fn detached_value( + value: &ValuePlan, + node_data: &[u8], + node_prefix: Prefix, + val_fetcher: &TrieDBNodeIterator, +) -> Option> { + let fetched; + match value { + ValuePlan::Node(hash_plan) => { + if let Some(value) = val_fetcher.fetch_value(&node_data[hash_plan.clone()], node_prefix) { + fetched = value; + } else { + return None; + } + }, + _ => return None, + } + Some(fetched) +} + /// Generates a compact representation of the partial trie stored in the given DB. The encoding /// is a vector of mutated trie nodes with those child references omitted. The mutated trie nodes /// are listed in pre-order traversal order so that the full nodes can be efficiently @@ -167,7 +213,7 @@ impl EncoderStackEntry { /// references. pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CError> where - L: TrieLayout + L: TrieLayout, { let mut output = Vec::new(); @@ -179,13 +225,13 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE // - It yields at least one node. // - The first node yielded is the root node with an empty prefix and is not inline. // - The prefixes yielded are in strictly increasing lexographic order. - let iter = TrieDBNodeIterator::new(db)?; + let mut iter = TrieDBNodeIterator::new(db)?; // Following from the guarantees about TrieDBNodeIterator, we guarantee that after the first // iteration of the loop below, the stack always has at least one entry and the bottom (front) // of the stack is the root node, which is not inline. Furthermore, the iterator is not empty, // so at least one iteration always occurs. - for item in iter { + while let Some(item) = iter.next() { match item { Ok((prefix, node_hash, node)) => { // Skip inline nodes, as they cannot contain hash references to other nodes by @@ -216,22 +262,31 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE } } - let children_len = match node.node_plan() { - NodePlan::Empty | NodePlan::Leaf { .. } => 0, - NodePlan::Extension { .. } => 1, - NodePlan::Branch { .. } | NodePlan::NibbledBranch { .. } => NIBBLE_LENGTH, + let (children_len, detached_value) = match node.node_plan() { + NodePlan::Empty => (0, None), + NodePlan::Leaf { value, .. } => (0, detached_value(value, node.data(), prefix.as_prefix(), &iter)), + NodePlan::Extension { .. } => (1, None), + NodePlan::NibbledBranch { value: Some(value), .. } + | NodePlan::Branch { value: Some(value), .. } => (NIBBLE_LENGTH, detached_value(value, node.data(), prefix.as_prefix(), &iter)), + NodePlan::NibbledBranch { value: None, .. } + | NodePlan::Branch { value: None, .. } => (NIBBLE_LENGTH, None), }; + stack.push(EncoderStackEntry { prefix, node, child_index: 0, omit_children: vec![false; children_len], + omit_value: detached_value.is_some(), output_index: output.len(), _marker: PhantomData::default(), }); // Insert a placeholder into output which will be replaced when this new entry is // popped from the stack. output.push(Vec::new()); + if let Some(value) = detached_value { + output.push(value); + } } Err(err) => match *err { // If we hit an IncompleteDatabaseError, just ignore it and continue encoding the @@ -243,7 +298,7 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE } } - while let Some(entry) = stack.pop() { + while let Some(mut entry) = stack.pop() { output[entry.output_index] = entry.encode_node()?; } @@ -257,6 +312,8 @@ struct DecoderStackEntry<'a, C: NodeCodec> { child_index: usize, /// The reconstructed child references. children: Vec>>, + /// A value attached as a node. The node will need to use its hash as value. + attached_value: Option<&'a [u8]>, _marker: PhantomData, } @@ -347,12 +404,13 @@ 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(self, attached_hash: Option<&[u8]>) -> Vec { + let attached_hash = attached_hash.map(|h| crate::node::Value::Node(h, None)); match self.node { Node::Empty => C::empty_node().to_vec(), Node::Leaf(partial, value) => - C::leaf_node(partial.right(), value), + C::leaf_node(partial.right(), attached_hash.unwrap_or(value)), Node::Extension(partial, _) => C::extension_node( partial.right_iter(), @@ -361,13 +419,24 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { .expect("required by method precondition; qed"), ), Node::Branch(_, value) => - C::branch_node(self.children.into_iter(), value), + C::branch_node( + self.children.into_iter(), + if attached_hash.is_some() { + attached_hash + } else { + value + }, + ), Node::NibbledBranch(partial, _, value) => C::branch_node_nibbled( partial.right_iter(), partial.len(), self.children.iter(), - value, + if attached_hash.is_some() { + attached_hash + } else { + value + }, ), } } @@ -385,21 +454,21 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { // /// This function makes the assumption that all child references in an inline trie node are inline /// references. -pub fn decode_compact(db: &mut DB, encoded: &[Vec]) +pub fn decode_compact(db: &mut DB, encoded: &[Vec]) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, - DB: HashDB, + DB: HashDB, { - decode_compact_from_iter::(db, 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: I) +pub fn decode_compact_from_iter<'a, L, DB, I>(db: &mut DB, encoded: I) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, - DB: HashDB, + DB: HashDB, I: IntoIterator, { // The stack of nodes through a path in the trie. Each entry is a child node of the preceding @@ -409,8 +478,15 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) // 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.into_iter().enumerate() { - let node = L::Codec::decode(encoded_node) + let mut iter = encoded.into_iter().enumerate(); + while let Some((i, encoded_node)) = iter.next() { + let mut attached_node = 0; + if let Some(header) = L::Codec::ESCAPE_HEADER { + if encoded_node.starts_with(&[header]) { + attached_node = 1; + } + } + let node = L::Codec::decode(&encoded_node[attached_node..]) .map_err(|err| Box::new(TrieError::DecoderError(>::default(), err)))?; let children_len = match node { @@ -422,9 +498,19 @@ 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], + attached_value: None, _marker: PhantomData::default(), }; + if attached_node > 0 { + // Read value + if let Some((_, fetched_value)) = iter.next() { + last_entry.attached_value = Some(fetched_value); + } else { + return Err(Box::new(TrieError::IncompleteDatabase(>::default()))); + } + } + loop { if !last_entry.advance_child_index()? { last_entry.push_to_prefix(&mut prefix); @@ -434,8 +520,14 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) // Since `advance_child_index` returned true, the preconditions for `encode_node` are // satisfied. - let node_data = last_entry.encode_node(); - let node_hash = db.insert(prefix.as_prefix(), node_data.as_ref()); + let hash = last_entry.attached_value.as_ref().map(|value| { + db.insert(prefix.as_prefix(), value) + }); + let node_data = last_entry.encode_node(hash.as_ref().map(|h| h.as_ref())); + let node_hash = db.insert( + prefix.as_prefix(), + node_data.as_ref(), + ); if let Some(entry) = stack.pop() { last_entry = entry; diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index d9cfac1c..f696adbc 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2020 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,10 +16,12 @@ use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; use crate::nibble::NibbleSlice; use crate::iterator::TrieDBNodeIterator; use crate::rstd::boxed::Box; -use super::node::{NodeHandle, Node, OwnedNode, decode_hash}; +use crate::DBValue; +use super::node::{NodeHandle, Node, Value, OwnedNode, decode_hash}; use super::lookup::Lookup; -use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, +use super::{Result, Trie, TrieItem, TrieKeyItem, TrieError, TrieIterator, Query, TrieLayout, CError, TrieHash}; +#[cfg(feature = "std")] use super::nibble::NibbleVec; #[cfg(feature = "std")] @@ -74,8 +76,19 @@ where } } + /// `new_with_layout`, but do not check root presence, if missing + /// this will fail at first node access. + pub fn new_unchecked( + db: &'db dyn HashDBRef, + root: &'db TrieHash, + ) -> Self { + TrieDB {db, root, hash_count: 0} + } + /// Get the backing database. - pub fn db(&'db self) -> &'db dyn HashDBRef { self.db } + pub fn db(&'db self) -> &'db dyn HashDBRef { + self.db + } /// Given some node-describing data `node`, and node key return the actual node RLP. /// This could be a simple identity operation in the case that the node is sufficiently small, @@ -142,6 +155,14 @@ where > { TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>) } + + fn key_iter<'a>(&'a self)-> Result< + Box, CError>> + 'a>, + TrieHash, + CError, + > { + TrieDBKeyIterator::new(self).map(|iter| Box::new(iter) as Box<_>) + } } @@ -169,28 +190,29 @@ where self.partial_key.as_prefix() ) { Ok((owned_node, _node_hash)) => match owned_node.node() { - Node::Leaf(slice, value) => - match (f.debug_struct("Node::Leaf"), self.index) { - (ref mut d, Some(i)) => d.field("index", &i), - (ref mut d, _) => d, + Node::Leaf(slice, value) => { + let mut disp = f.debug_struct("Node::Leaf"); + if let Some(i) = self.index { + disp.field("index", &i); } - .field("slice", &slice) - .field("value", &value) - .finish(), + disp.field("slice", &slice) + .field("value", &value); + disp.finish() + }, Node::Extension(slice, item) => { - match (f.debug_struct("Node::Extension"), self.index) { - (ref mut d, Some(i)) => d.field("index", &i), - (ref mut d, _) => d, + let mut disp = f.debug_struct("Node::Extension"); + if let Some(i) = self.index { + disp.field("index", &i); } - .field("slice", &slice) + disp.field("slice", &slice) .field("item", &TrieAwareDebugNode { trie: self.trie, node_key: item, partial_key: self.partial_key .clone_append_optional_slice_and_nibble(Some(&slice), None), index: None, - }) - .finish() + }); + disp.finish() }, Node::Branch(ref nodes, ref value) => { let nodes: Vec> = nodes.into_iter() @@ -204,13 +226,13 @@ where .clone_append_optional_slice_and_nibble(None, Some(i as u8)), }) .collect(); - match (f.debug_struct("Node::Branch"), self.index) { - (ref mut d, Some(ref i)) => d.field("index", i), - (ref mut d, _) => d, + let mut disp = f.debug_struct("Node::Branch"); + if let Some(i) = self.index { + disp.field("index", &i); } - .field("nodes", &nodes) - .field("value", &value) - .finish() + disp.field("nodes", &nodes) + .field("value", &value); + disp.finish() }, Node::NibbledBranch(slice, nodes, value) => { let nodes: Vec> = nodes.iter() @@ -223,16 +245,19 @@ where partial_key: self.partial_key .clone_append_optional_slice_and_nibble(Some(&slice), Some(i as u8)), }).collect(); - match (f.debug_struct("Node::NibbledBranch"), self.index) { - (ref mut d, Some(ref i)) => d.field("index", i), - (ref mut d, _) => d, + let mut disp = f.debug_struct("Node::NibbledBranch"); + if let Some(i) = self.index { + disp.field("index", &i); } - .field("slice", &slice) + disp.field("slice", &slice) .field("nodes", &nodes) - .field("value", &value) - .finish() + .field("value", &value); + disp.finish() + }, + Node::Empty => { + let mut disp = f.debug_struct("Node::Empty"); + disp.finish() }, - Node::Empty => f.debug_struct("Node::Empty").finish(), }, Err(e) => f.debug_struct("BROKEN_NODE") .field("index", &self.index) @@ -266,6 +291,29 @@ pub struct TrieDBIterator<'a, L: TrieLayout> { inner: TrieDBNodeIterator<'a, L>, } +/// Iterator for going through all of key with values in the trie in pre-order traversal order. +pub struct TrieDBKeyIterator<'a, L: TrieLayout> { + inner: TrieDBNodeIterator<'a, L>, +} + +/// When there is guaranties the storage backend do not change, +/// this can be use to suspend and restore the iterator. +pub struct SuspendedTrieDBKeyIterator { + inner: crate::iterator::SuspendedTrieDBNodeIterator, +} + +impl SuspendedTrieDBKeyIterator { + /// Restore iterator. + pub fn unsafe_restore<'a>( + self, + db: &'a TrieDB<'a, L>, + ) -> TrieDBKeyIterator<'a, L> { + TrieDBKeyIterator { + inner: self.inner.unsafe_restore(db), + } + } +} + impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { /// Create a new iterator. pub fn new(db: &'a TrieDB) -> Result, TrieHash, CError> { @@ -307,6 +355,55 @@ impl<'a, L: TrieLayout> TrieIterator for TrieDBIterator<'a, L> { } } +impl<'a, L: TrieLayout> TrieDBKeyIterator<'a, L> { + /// Create a new iterator. + pub fn new(db: &'a TrieDB) -> Result, TrieHash, CError> { + let inner = TrieDBNodeIterator::new(db)?; + Ok(TrieDBKeyIterator { inner }) + } + + /// Suspend iterator. Warning this does not hold guaranties it can be restore later. + /// Restoring require that trie backend did not change. + pub fn suspend(self) -> SuspendedTrieDBKeyIterator { + SuspendedTrieDBKeyIterator { + inner: self.inner.suspend(), + } + } + + /// Create a new iterator, but limited to a given prefix. + pub fn new_prefixed(db: &'a TrieDB, prefix: &[u8]) -> Result, TrieHash, CError> { + let mut inner = TrieDBNodeIterator::new(db)?; + inner.prefix(prefix)?; + + Ok(TrieDBKeyIterator { + inner, + }) + } + + /// Create a new iterator, but limited to a given prefix. + /// It then do a seek operation from prefixed context (using `seek` lose + /// prefix context by default). + pub fn new_prefixed_then_seek( + db: &'a TrieDB, + prefix: &[u8], + start_at: &[u8], + ) -> Result, TrieHash, CError> { + let mut inner = TrieDBNodeIterator::new(db)?; + inner.prefix_then_seek(prefix, start_at)?; + + Ok(TrieDBKeyIterator { + inner, + }) + } +} + +impl<'a, L: TrieLayout> TrieIterator for TrieDBKeyIterator<'a, L> { + /// Position the iterator on the first element with key >= `key` + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + TrieIterator::seek(&mut self.inner, key) + } +} + impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { type Item = TrieItem<'a, TrieHash, CError>; @@ -326,7 +423,62 @@ impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { } _ => None, }; - if let Some(value) = maybe_value { + if maybe_value.is_none() { + continue; + } + let (key_slice, maybe_extra_nibble) = prefix.as_prefix(); + let key = key_slice.to_vec(); + if let Some(extra_nibble) = maybe_extra_nibble { + return Some(Err(Box::new( + TrieError::ValueAtIncompleteKey(key, extra_nibble) + ))); + } + let value = match maybe_value.expect("None checked above.") { + Value::Node(hash, None) => { + if let Some(value) = self.inner.fetch_value(&hash, (key_slice, None)) { + value + } else { + let mut res = TrieHash::::default(); + res.as_mut().copy_from_slice(hash); + return Some(Err(Box::new( + TrieError::IncompleteDatabase(res) + ))); + } + }, + Value::Inline(value) => value.to_vec(), + Value::Node(_hash, Some(value)) => value, + }; + return Some(Ok((key, value))); + }, + Err(err) => return Some(Err(err)), + } + } + None + } +} + +impl<'a, L: TrieLayout> Iterator for TrieDBKeyIterator<'a, L> { + type Item = TrieKeyItem<'a, TrieHash, CError>; + + fn next(&mut self) -> Option { + while let Some(item) = self.inner.next() { + match item { + Ok((mut prefix, _, node)) => { + let maybe_value = match node.node() { + Node::Leaf(partial, value) => { + prefix.append_partial(partial.right()); + Some(value) + } + Node::Branch(_, value) => value, + Node::NibbledBranch(partial, _, value) => { + prefix.append_partial(partial.right()); + value + } + _ => None, + }; + if maybe_value.is_none() { + continue; + } else { let (key_slice, maybe_extra_nibble) = prefix.as_prefix(); let key = key_slice.to_vec(); if let Some(extra_nibble) = maybe_extra_nibble { @@ -334,7 +486,7 @@ impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { TrieError::ValueAtIncompleteKey(key, extra_nibble) ))); } - return Some(Ok((key, value.to_vec()))); + return Some(Ok(key)); } }, Err(err) => return Some(Err(err)), diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 5f5126b9..47a44ec4 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2020 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,7 +17,8 @@ use super::{DBValue, node::NodeKey}; use super::{Result, TrieError, TrieMut, TrieLayout, TrieHash, CError}; use super::lookup::Lookup; -use super::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, decode_hash}; +use super::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, + Value as EncodedValue, decode_hash}; use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; use hashbrown::HashSet; @@ -25,7 +26,7 @@ use hashbrown::HashSet; use crate::node_codec::NodeCodec; use crate::nibble::{NibbleVec, NibbleSlice, nibble_ops, BackingByteVec}; use crate::rstd::{ - boxed::Box, convert::TryFrom, hash::Hash, mem, ops::Index, result, vec::Vec, VecDeque, + boxed::Box, convert::TryFrom, mem, ops::Index, result, vec::Vec, VecDeque, }; #[cfg(feature = "std")] @@ -55,7 +56,7 @@ impl From for NodeHandle { } } -fn empty_children() -> Box<[Option>; 16]> { +fn empty_children() -> Box<[Option>; nibble_ops::NIBBLE_LENGTH]> { Box::new([ None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, @@ -66,23 +67,134 @@ fn empty_children() -> Box<[Option>; 16]> { /// therefore its left side is a full prefix. type NibbleFullKey<'key> = NibbleSlice<'key>; +/// Value representation for Node. +#[derive(Clone, Eq)] +pub enum Value { + /// Value bytes inlined in a trie node. + Inline(DBValue), + /// Hash of value bytes and value bytes when accessed. + Node(TrieHash, Option), + /// Hash of value bytes if calculated and value bytes. + /// The hash may be undefined until it node is added + /// to the db. + NewNode(Option>, DBValue), +} + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Value::Inline(v), Value::Inline(ov)) => v == ov, + (Value::Node(h, _), Value::Node(oh, _)) => h == oh, + (Value::NewNode(Some(h), _), Value::NewNode(Some(oh), _)) => h == oh, + (Value::NewNode(_, v), Value::NewNode(_, ov)) => v == ov, + // Note that for uncalculated hash we do not calculate it and default to true. + // This is rather similar to default Eq implementation. + _ => false, + } + } +} + +impl<'a, L: TrieLayout> From> for Value { + fn from(v: EncodedValue<'a>) -> Self { + match v { + EncodedValue::Inline(value) => Value::Inline(value.to_vec()), + EncodedValue::Node(hash, value) => { + let mut h = TrieHash::::default(); + h.as_mut().copy_from_slice(hash); + Value::Node(h, value) + }, + } + } +} + +impl From<(DBValue, Option)> for Value { + fn from((v, threshold): (DBValue, Option)) -> Self { + match v { + value => if threshold.map(|threshold| value.len() >= threshold as usize).unwrap_or(false) { + Value::NewNode(None, value.to_vec()) + } else { + Value::Inline(value.to_vec()) + }, + } + } +} + +enum NodeToEncode<'a, H> { + Node(&'a [u8]), + TrieNode(NodeHandle), +} + +impl Value { + fn new(value: DBValue, new_threshold: Option) -> Self { + (value, new_threshold).into() + } + + fn into_encoded<'a, F>( + &'a mut self, + partial: Option<&NibbleSlice>, + f: &mut F, + ) -> EncodedValue<'a> + where + F: FnMut(NodeToEncode>, Option<&NibbleSlice>, Option) -> ChildReference>, + { + if let Value::NewNode(hash, value) = self { + let new_hash = if let ChildReference::Hash(hash) = f(NodeToEncode::Node(value.as_slice()), partial, None) { + hash + } else { + unreachable!("Value node can never be inlined; qed") + }; + if let Some(h) = hash.as_ref() { + debug_assert!(h == &new_hash); + } else { + *hash = Some(new_hash); + } + } + let value = match &*self { + Value::Inline(value) => EncodedValue::Inline(value.as_slice()), + Value::Node(hash, _value) => EncodedValue::Node(hash.as_ref(), None), + Value::NewNode(Some(hash), _value) => EncodedValue::Node(hash.as_ref(), None), + Value::NewNode(None, _value) => unreachable!("New external value are always added before encoding anode"), + }; + value + } + + fn in_memory_fetched_value( + &self, + prefix: Prefix, + db: &dyn HashDB, + ) -> Result, TrieHash, CError> { + Ok(Some(match self { + Value::Inline(value) => value.clone(), + Value::NewNode(_, value) => value.clone(), + Value::Node(_, Some(value)) => value.clone(), + Value::Node(hash, None) => { + if let Some(value) = db.get(hash, prefix) { + value + } else { + return Err(Box::new(TrieError::IncompleteDatabase(hash.clone()))); + } + }, + })) + } +} + /// Node types in the Trie. -enum Node { +enum Node { /// Empty node. Empty, /// A leaf node contains the end of a key and a value. /// This key is encoded from a `NibbleSlice`, meaning it contains /// a flag indicating it is a leaf. - Leaf(NodeKey, DBValue), + Leaf(NodeKey, Value), /// An extension contains a shared portion of a key and a child node. /// The shared portion is encoded from a `NibbleSlice` meaning it contains /// a flag indicating it is an extension. /// The child node is always a branch. - Extension(NodeKey, NodeHandle), + Extension(NodeKey, NodeHandle>), /// A branch has up to 16 children and an optional value. - Branch(Box<[Option>; 16]>, Option), + Branch(Box<[Option>>; nibble_ops::NIBBLE_LENGTH]>, Option>), /// Branch node with support for a nibble (to avoid extension node). - NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), + NibbledBranch(NodeKey, Box<[Option>>; nibble_ops::NIBBLE_LENGTH]>, Option>), } #[cfg(feature = "std")] @@ -99,46 +211,53 @@ impl<'a> Debug for ToHex<'a> { } #[cfg(feature = "std")] -impl Debug for Node { +impl Debug for Value { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::Inline(value) => write!(fmt, "Some({:?})", ToHex(value)), + Self::Node(hash, _) => write!(fmt, "Hash({:?})", ToHex(hash.as_ref())), + Self::NewNode(Some(hash), _) => write!(fmt, "Hash({:?})", ToHex(hash.as_ref())), + Self::NewNode(_hash, value) => write!(fmt, "Some({:?})", ToHex(value)), + } + } +} + +#[cfg(feature = "std")] +impl Debug for Node + where L::Hash: Debug, +{ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Self::Empty => write!(fmt, "Empty"), Self::Leaf((ref a, ref b), ref c) => - write!(fmt, "Leaf({:?}, {:?})", (a, ToHex(&*b)), ToHex(&*c)), + write!(fmt, "Leaf({:?}, {:?})", (a, ToHex(&*b)), c), Self::Extension((ref a, ref b), ref c) => write!(fmt, "Extension({:?}, {:?})", (a, ToHex(&*b)), c), Self::Branch(ref a, ref b) => - write!(fmt, "Branch({:?}, {:?}", a, b.as_ref().map(Vec::as_slice).map(ToHex)), + write!(fmt, "Branch({:?}, {:?}", a, b), Self::NibbledBranch((ref a, ref b), ref c, ref d) => - write!(fmt, "NibbledBranch({:?}, {:?}, {:?})", (a, ToHex(&*b)), c, d.as_ref().map(Vec::as_slice).map(ToHex)), + write!(fmt, "NibbledBranch({:?}, {:?}, {:?})", (a, ToHex(&*b)), c, d), } } } -impl Node -where - O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug - + PartialEq + Eq + Hash + Send + Sync + Clone + Copy +impl Node { // load an inline node into memory or get the hash to do the lookup later. - fn inline_or_hash( - parent_hash: H::Out, + fn inline_or_hash( + parent_hash: TrieHash, child: EncodedNodeHandle, - db: &dyn HashDB, - storage: &mut NodeStorage - ) -> Result, H::Out, C::Error> - where - C: NodeCodec, - H: Hasher, - { + db: &dyn HashDB, + storage: &mut NodeStorage, + ) -> Result>, TrieHash, CError> { let handle = match child { EncodedNodeHandle::Hash(data) => { - let hash = decode_hash::(data) + let hash = decode_hash::(data) .ok_or_else(|| Box::new(TrieError::InvalidHash(parent_hash, data.to_vec())))?; NodeHandle::Hash(hash) }, EncodedNodeHandle::Inline(data) => { - let child = Node::from_encoded::(parent_hash, data, db, storage)?; + let child = Node::from_encoded(parent_hash, data, db, storage)?; NodeHandle::InMemory(storage.alloc(Stored::New(child))) }, }; @@ -146,29 +265,24 @@ where } // Decode a node from encoded bytes. - fn from_encoded<'a, 'b, C, H>( - node_hash: H::Out, + fn from_encoded<'a, 'b>( + node_hash: TrieHash, data: &'a[u8], - db: &dyn HashDB, - storage: &'b mut NodeStorage, - ) -> Result - where - C: NodeCodec, H: Hasher, - { - let encoded_node = C::decode(data) + db: &dyn HashDB, + storage: &'b mut NodeStorage, + ) -> Result, CError> { + let encoded_node = L::Codec::decode(data) .map_err(|e| Box::new(TrieError::DecoderError(node_hash, e)))?; let node = match encoded_node { EncodedNode::Empty => Node::Empty, - EncodedNode::Leaf(k, v) => Node::Leaf(k.into(), v.to_vec()), - EncodedNode::Extension(key, cb) => { - Node::Extension( - key.into(), - Self::inline_or_hash::(node_hash, cb, db, storage)? - ) - }, + EncodedNode::Leaf(k, v) => Node::Leaf(k.into(), v.into()), + EncodedNode::Extension(key, cb) => Node::Extension( + key.into(), + Self::inline_or_hash(node_hash, cb, db, storage)?, + ), EncodedNode::Branch(encoded_children, val) => { let mut child = |i:usize| match encoded_children[i] { - Some(child) => Self::inline_or_hash::(node_hash, child, db, storage) + Some(child) => Self::inline_or_hash(node_hash, child, db, storage) .map(Some), None => Ok(None), }; @@ -180,11 +294,11 @@ where child(12)?, child(13)?, child(14)?, child(15)?, ]); - Node::Branch(children, val.map(|v| v.to_vec())) + Node::Branch(children, val.map(Into::into)) }, EncodedNode::NibbledBranch(k, encoded_children, val) => { let mut child = |i:usize| match encoded_children[i] { - Some(child) => Self::inline_or_hash::(node_hash, child, db, storage) + Some(child) => Self::inline_or_hash(node_hash, child, db, storage) .map(Some), None => Ok(None), }; @@ -196,51 +310,54 @@ where child(12)?, child(13)?, child(14)?, child(15)?, ]); - Node::NibbledBranch(k.into(), children, val.map(|v| v.to_vec())) + Node::NibbledBranch(k.into(), children, val.map(Into::into)) }, }; Ok(node) } // TODO: parallelize - fn into_encoded(self, mut child_cb: F) -> Vec + /// Here `child_cb` should process the first parameter to either insert an external + /// node value or to encode and add a new branch child node. + fn into_encoded(self, mut child_cb: F) -> Vec where - C: NodeCodec, - F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, - H: Hasher, + F: FnMut(NodeToEncode>, Option<&NibbleSlice>, Option) -> ChildReference>, { match self { - Node::Empty => C::empty_node().to_vec(), - Node::Leaf(partial, value) => { + Node::Empty => L::Codec::empty_node().to_vec(), + Node::Leaf(partial, mut value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); - C::leaf_node(pr.right(), &value) + let value = value.into_encoded::(Some(&pr), &mut child_cb); + L::Codec::leaf_node(pr.right(), value) }, Node::Extension(partial, child) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); - let c = child_cb(child, Some(&pr), None); - C::extension_node( + let c = child_cb(NodeToEncode::TrieNode(child), Some(&pr), None); + L::Codec::extension_node( it, pr.len(), c, ) }, - Node::Branch(mut children, value) => { - C::branch_node( + Node::Branch(mut children, mut value) => { + let value = value.as_mut().map(|v| v.into_encoded::(None, &mut child_cb)); + L::Codec::branch_node( // map the `NodeHandle`s from the Branch to `ChildReferences` children.iter_mut() .map(Option::take) .enumerate() .map(|(i, maybe_child)| { - maybe_child.map(|child| child_cb(child, None, Some(i as u8))) + maybe_child.map(|child| child_cb(NodeToEncode::TrieNode(child), None, Some(i as u8))) }), - value.as_ref().map(|v| &v[..]) + value, ) }, - Node::NibbledBranch(partial, mut children, value) => { + Node::NibbledBranch(partial, mut children, mut value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + let value = value.as_mut().map(|v| v.into_encoded::(Some(&pr), &mut child_cb)); let it = pr.right_iter(); - C::branch_node_nibbled( + L::Codec::branch_node_nibbled( it, pr.len(), // map the `NodeHandle`s from the Branch to `ChildReferences` @@ -251,10 +368,10 @@ where //let branch_index = [i as u8]; maybe_child.map(|child| { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); - child_cb(child, Some(&pr), Some(i as u8)) + child_cb(NodeToEncode::TrieNode(child), Some(&pr), Some(i as u8)) }) }), - value.as_ref().map(|v| &v[..]) + value, ) }, } @@ -262,25 +379,25 @@ where } // post-inspect action. -enum Action { +enum Action { // Replace a node with a new one. - Replace(Node), + Replace(Node), // Restore the original node. This trusts that the node is actually the original. - Restore(Node), + Restore(Node), // if it is a new node, just clears the storage. Delete, } // post-insert action. Same as action without delete -enum InsertAction { +enum InsertAction { // Replace a node with a new one. - Replace(Node), + Replace(Node), // Restore the original node. - Restore(Node), + Restore(Node), } -impl InsertAction { - fn into_action(self) -> Action { +impl InsertAction { + fn into_action(self) -> Action { match self { InsertAction::Replace(n) => Action::Replace(n), InsertAction::Restore(n) => Action::Restore(n), @@ -288,7 +405,7 @@ impl InsertAction { } // unwrap the node, disregarding replace or restore state. - fn unwrap_node(self) -> Node { + fn unwrap_node(self) -> Node { match self { InsertAction::Replace(n) | InsertAction::Restore(n) => n, } @@ -296,11 +413,11 @@ impl InsertAction { } // What kind of node is stored here. -enum Stored { +enum Stored { // A new node. - New(Node), + New(Node), // A cached node, loaded from the DB. - Cached(Node, H), + Cached(Node, TrieHash), } /// Used to build a collection of child nodes from a collection of `NodeHandle`s @@ -331,7 +448,7 @@ impl<'a, HO> TryFrom> for ChildReference if data.len() > hash.as_ref().len() { return Err(data.to_vec()); } - &mut hash.as_mut()[..data.len()].copy_from_slice(data); + hash.as_mut()[..data.len()].copy_from_slice(data); Ok(ChildReference::Inline(hash, data.len())) } } @@ -339,12 +456,13 @@ impl<'a, HO> TryFrom> for ChildReference } /// Compact and cache-friendly storage for Trie nodes. -struct NodeStorage { - nodes: Vec>, +struct NodeStorage { + nodes: Vec>, free_indices: VecDeque, } -impl NodeStorage { +impl NodeStorage +{ /// Create a new storage. fn empty() -> Self { NodeStorage { @@ -354,7 +472,7 @@ impl NodeStorage { } /// Allocate a new node in the storage. - fn alloc(&mut self, stored: Stored) -> StorageHandle { + fn alloc(&mut self, stored: Stored) -> StorageHandle { if let Some(idx) = self.free_indices.pop_front() { self.nodes[idx] = stored; StorageHandle(idx) @@ -365,7 +483,7 @@ impl NodeStorage { } /// Remove a node from the storage, consuming the handle and returning the node. - fn destroy(&mut self, handle: StorageHandle) -> Stored { + fn destroy(&mut self, handle: StorageHandle) -> Stored { let idx = handle.0; self.free_indices.push_back(idx); @@ -373,10 +491,10 @@ impl NodeStorage { } } -impl<'a, H> Index<&'a StorageHandle> for NodeStorage { - type Output = Node; +impl<'a, L: TrieLayout> Index<&'a StorageHandle> for NodeStorage { + type Output = Node; - fn index(&self, handle: &'a StorageHandle) -> &Node { + fn index(&self, handle: &'a StorageHandle) -> &Node { match self.nodes[handle.0] { Stored::New(ref node) => node, Stored::Cached(ref node, _) => node, @@ -415,7 +533,7 @@ pub struct TrieDBMut<'a, L> where L: TrieLayout, { - storage: NodeStorage>, + storage: NodeStorage, db: &'a mut dyn HashDB, root: &'a mut TrieHash, root_handle: NodeHandle>, @@ -482,11 +600,11 @@ where ) -> Result, CError> { let node_encoded = self.db.get(&hash, key) .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; - let node = Node::from_encoded::( + let node = Node::from_encoded( hash, &node_encoded, &*self.db, - &mut self.storage + &mut self.storage, )?; Ok(self.storage.alloc(Stored::Cached(node, hash))) } @@ -495,18 +613,18 @@ where // If restored or replaced, returns the new node along with a flag of whether it was changed. fn inspect( &mut self, - stored: Stored>, + stored: Stored, key: &mut NibbleFullKey, inspector: F, - ) -> Result>, bool)>, TrieHash, CError> + ) -> Result, bool)>, TrieHash, CError> where F: FnOnce( &mut Self, - Node>, + Node, &mut NibbleFullKey, - ) -> Result>, TrieHash, CError>, + ) -> Result, TrieHash, CError>, { - let current_key = key.clone(); + let current_key = *key; Ok(match stored { Stored::New(node) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::New(node), false)), @@ -531,28 +649,30 @@ where fn lookup<'x, 'key>( &'x self, mut partial: NibbleSlice<'key>, + full_key: &'key [u8], handle: &NodeHandle>, ) -> Result, TrieHash, CError> where 'x: 'key { let mut handle = handle; + let prefix = (full_key, None); loop { - let (mid, child) = match *handle { - NodeHandle::Hash(ref hash) => return Lookup:: { + let (mid, child) = match handle { + NodeHandle::Hash(hash) => return Lookup:: { db: &self.db, query: |v: &[u8]| v.to_vec(), hash: *hash, }.look_up(partial), - NodeHandle::InMemory(ref handle) => match self.storage[handle] { + NodeHandle::InMemory(handle) => match &self.storage[handle] { Node::Empty => return Ok(None), - Node::Leaf(ref key, ref value) => { + Node::Leaf(key, value) => { if NibbleSlice::from_stored(key) == partial { - return Ok(Some(value.to_vec())); + return Ok(value.in_memory_fetched_value(prefix, self.db)?); } else { return Ok(None); } }, - Node::Extension(ref slice, ref child) => { + Node::Extension(slice, child) => { let slice = NibbleSlice::from_stored(slice); if partial.starts_with(&slice) { (slice.len(), child) @@ -560,9 +680,13 @@ where return Ok(None); } }, - Node::Branch(ref children, ref value) => { + Node::Branch(children, value) => { if partial.is_empty() { - return Ok(value.as_ref().map(|v| v.to_vec())); + return Ok(if let Some(v) = value.as_ref() { + v.in_memory_fetched_value(prefix, self.db)? + } else { + None + }) } else { let idx = partial.at(0); match children[idx as usize].as_ref() { @@ -571,10 +695,14 @@ where } } }, - Node::NibbledBranch(ref slice, ref children, ref value) => { + Node::NibbledBranch(slice, children, value) => { let slice = NibbleSlice::from_stored(slice); if slice == partial { - return Ok(value.as_ref().map(|v| v.to_vec())); + return Ok(if let Some(v) = value.as_ref() { + v.in_memory_fetched_value(prefix, self.db)? + } else { + None + }) } else if partial.starts_with(&slice) { let idx = partial.at(0); match children[idx as usize].as_ref() { @@ -599,7 +727,7 @@ where handle: NodeHandle>, key: &mut NibbleFullKey, value: DBValue, - old_val: &mut Option, + old_val: &mut Option>, ) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, @@ -614,14 +742,28 @@ where Ok((self.storage.alloc(new_stored), changed)) } + fn replace_old_value(&mut self, old_value: &mut Option>, stored_value: Option>, prefix: Prefix) { + match &stored_value { + Some(Value::NewNode(Some(hash), _)) // also removing new node in case commit is called multiple times + | Some(Value::Node(hash, _)) => { + self.death_row.insert(( + hash.clone(), + (prefix.0.into(), prefix.1), + )); + }, + _ => (), + } + *old_value = stored_value; + } + /// The insertion inspector. fn insert_inspector( &mut self, - node: Node>, + node: Node, key: &mut NibbleFullKey, value: DBValue, - old_val: &mut Option, - ) -> Result>, TrieHash, CError> { + old_val: &mut Option>, + ) -> Result, TrieHash, CError> { let partial = *key; #[cfg(feature = "std")] @@ -631,6 +773,7 @@ where Node::Empty => { #[cfg(feature = "std")] trace!(target: "trie", "empty: COMPOSE"); + let value = Value::new(value, L::MAX_INLINE_VALUE); InsertAction::Replace(Node::Leaf(partial.to_stored(), value)) }, Node::Branch(mut children, stored_value) => { @@ -639,9 +782,11 @@ where trace!(target: "trie", "branch: ROUTE,AUGMENT"); if partial.is_empty() { - let unchanged = stored_value.as_ref() == Some(&value); - let branch = Node::Branch(children, Some(value)); - *old_val = stored_value; + let value = Some(Value::new(value, L::MAX_INLINE_VALUE)); + let unchanged = stored_value == value; + let branch = Node::Branch(children, value); + + self.replace_old_value(old_val, stored_value, key.left()); match unchanged { true => InsertAction::Restore(branch), @@ -661,8 +806,10 @@ where } } else { // Original had nothing there. compose a leaf. + let value = Value::new(value, L::MAX_INLINE_VALUE); let leaf = self.storage.alloc( Stored::New(Node::Leaf(key.to_stored(), value)) + ); children[idx] = Some(leaf.into()); } @@ -678,13 +825,17 @@ where let common = partial.common_prefix(&existing_key); if common == existing_key.len() && common == partial.len() { - let unchanged = stored_value.as_ref() == Some(&value); + let value = Some(Value::new(value, L::MAX_INLINE_VALUE)); + let unchanged = stored_value == value; let branch = Node::NibbledBranch( existing_key.to_stored(), children, - Some(value), + value, ); - *old_val = stored_value; + + let mut key_val = key.clone(); + key_val.advance(existing_key.len()); + self.replace_old_value(old_val, stored_value, key_val.left()); match unchanged { true => InsertAction::Restore(branch), @@ -707,19 +858,19 @@ where let mut children = empty_children(); let alloc_storage = self.storage.alloc(Stored::New(low)); - children[ix as usize] = Some(alloc_storage.into()); + let value = Value::new(value, L::MAX_INLINE_VALUE); if partial.len() - common == 0 { InsertAction::Replace(Node::NibbledBranch( existing_key.to_stored_range(common), children, Some(value), - ) - ) + )) } else { let ix = partial.at(common); let stored_leaf = Node::Leaf(partial.mid(common + 1).to_stored(), value); + let leaf = self.storage.alloc(Stored::New(stored_leaf)); children[ix as usize] = Some(leaf.into()); @@ -727,11 +878,8 @@ where existing_key.to_stored_range(common), children, None, - ) - ) - + )) } - } else { // Append after common == existing_key and partial > common #[cfg(feature = "std")] @@ -754,9 +902,11 @@ where } } else { // Original had nothing there. compose a leaf. + let value = Value::new(value, L::MAX_INLINE_VALUE); let leaf = self.storage.alloc( Stored::New(Node::Leaf(key.to_stored(), value)), ); + children[idx] = Some(leaf.into()); } InsertAction::Replace(Node::NibbledBranch( @@ -773,9 +923,11 @@ where #[cfg(feature = "std")] trace!(target: "trie", "equivalent-leaf: REPLACE"); // equivalent leaf. + let value = Value::new(value, L::MAX_INLINE_VALUE); let unchanged = stored_value == value; - *old_val = Some(stored_value); - + let mut key_val = key.clone(); + key_val.advance(existing_key.len()); + self.replace_old_value(old_val, Some(stored_value), key_val.left()); match unchanged { // unchanged. restore true => InsertAction::Restore(Node::Leaf(encoded.clone(), value)), @@ -847,8 +999,8 @@ where let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); // always replace since we took a leaf and made an extension. - let branch_handle = self.storage.alloc(Stored::New(branch)).into(); - InsertAction::Replace(Node::Extension(existing_key.to_stored(), branch_handle)) + let leaf = self.storage.alloc(Stored::New(branch)); + InsertAction::Replace(Node::Extension(existing_key.to_stored(), leaf.into())) } else { debug_assert!(L::USE_EXTENSION); #[cfg(feature = "std")] @@ -873,7 +1025,7 @@ where // make an extension using it. this is a replacement. InsertAction::Replace(Node::Extension( existing_key.to_stored_range(common), - self.storage.alloc(Stored::New(augmented_low)).into() + self.storage.alloc(Stored::New(augmented_low)).into(), )) } }, @@ -901,7 +1053,8 @@ where // direct extension, just replace. Some(child_branch) } else { - // more work required after branching. + // No need to register set branch (was here before). + // Note putting a branch in extension requires fix. let ext = Node::Extension(existing_key.mid(1).to_stored(), child_branch); Some(self.storage.alloc(Stored::New(ext)).into()) }; @@ -923,6 +1076,7 @@ where // insert into the child node. key.advance(common); let (new_child, changed) = self.insert_at(child_branch, key, value, old_val)?; + let new_ext = Node::Extension(existing_key.to_stored(), new_child.into()); // if the child branch wasn't changed, meaning this extension remains the same. @@ -953,7 +1107,7 @@ where // this is known because the partial key is only the common prefix. InsertAction::Replace(Node::Extension( existing_key.to_stored_range(common), - self.storage.alloc(Stored::New(augmented_low)).into() + self.storage.alloc(Stored::New(augmented_low)).into(), )) } }, @@ -965,7 +1119,7 @@ where &mut self, handle: NodeHandle>, key: &mut NibbleFullKey, - old_val: &mut Option, + old_val: &mut Option>, ) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), @@ -987,25 +1141,28 @@ where /// The removal inspector. fn remove_inspector( &mut self, - node: Node>, + node: Node, key: &mut NibbleFullKey, - old_val: &mut Option, - ) -> Result>, TrieHash, CError> { + old_val: &mut Option>, + ) -> Result, TrieHash, CError> { let partial = *key; Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, - (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), - (Node::NibbledBranch(n, c, None), true) => - Action::Restore(Node::NibbledBranch(n, c, None)), - (Node::Branch(children, Some(val)), true) => { - *old_val = Some(val); + (Node::Branch(c, None), true) => { + Action::Restore(Node::Branch(c, None)) + }, + (Node::NibbledBranch(n, c, None), true) => { + Action::Restore(Node::NibbledBranch(n, c, None)) + }, + (Node::Branch(children, val), true) => { + self.replace_old_value(old_val, val, key.left()); // always replace since we took the value out. - Action::Replace(self.fix(Node::Branch(children, None), key.clone())?) + Action::Replace(self.fix(Node::Branch(children, None), *key)?) }, - (Node::NibbledBranch(n, children, Some(val)), true) => { - *old_val = Some(val); + (Node::NibbledBranch(n, children, val), true) => { + self.replace_old_value(old_val, val, key.left()); // always replace since we took the value out. - Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.clone())?) + Action::Replace(self.fix(Node::NibbledBranch(n, children, None), *key)?) }, (Node::Branch(mut children, value), false) => { let idx = partial.at(0) as usize; @@ -1050,10 +1207,11 @@ where if common == existing_length && common == partial.len() { // replace val - if let Some(val) = value { - *old_val = Some(val); - - let f = self.fix(Node::NibbledBranch(encoded, children, None), key.clone()); + if let Some(value) = value { + let mut key_val = key.clone(); + key_val.advance(existing_length); + self.replace_old_value(old_val, Some(value), key_val.left()); + let f = self.fix(Node::NibbledBranch(encoded, children, None), *key); Action::Replace(f?) } else { Action::Restore(Node::NibbledBranch(encoded, children, None)) @@ -1106,9 +1264,12 @@ where } }, (Node::Leaf(encoded, value), _) => { - if NibbleSlice::from_stored(&encoded) == partial { + let existing_key = NibbleSlice::from_stored(&encoded); + if existing_key == partial { // this is the node we were looking for. Let's delete it. - *old_val = Some(value); + let mut key_val = key.clone(); + key_val.advance(existing_key.len()); + self.replace_old_value(old_val, Some(value), key_val.left()); Action::Delete } else { // leaf the node alone. @@ -1135,15 +1296,13 @@ where key.advance(common); match self.remove_at(child_branch, key, old_val)? { Some((new_child, changed)) => { - let new_child = new_child.into(); - // if the child branch was unchanged, then the extension is too. // otherwise, this extension may need fixing. match changed { true => Action::Replace( - self.fix(Node::Extension(encoded, new_child), prefix)? + self.fix(Node::Extension(encoded, new_child.into()), prefix)? ), - false => Action::Restore(Node::Extension(encoded, new_child)), + false => Action::Restore(Node::Extension(encoded, new_child.into())), } } None => { @@ -1168,17 +1327,17 @@ where /// - Extension node followed by anything other than a Branch node. fn fix( &mut self, - node: Node>, + node: Node, key: NibbleSlice, - ) -> Result>, TrieHash, CError> { + ) -> Result, TrieHash, CError> { self.fix_inner(node, key, false) } fn fix_inner( &mut self, - node: Node>, + node: Node, key: NibbleSlice, recurse_extension: bool, - ) -> Result>, TrieHash, CError> { + ) -> Result, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. @@ -1384,8 +1543,8 @@ where "fixing: extension combination. new_partial={:?}", partial, ); - - self.fix_inner(Node::Extension(partial, sub_child), key, true) + + self.fix_inner(Node::Extension(partial, sub_child), key.into(), true) } Node::Leaf(sub_partial, value) => { // combine with node below. @@ -1444,17 +1603,31 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let mut k = NibbleVec::new(); - let encoded_root = node.into_encoded::<_, L::Codec, L::Hash>( - |child, o_slice, o_index| { + + let encoded_root = node.into_encoded( + |node, o_slice, o_index| { let mov = k.append_optional_slice_and_nibble(o_slice, o_index); - let cr = self.commit_child(child, &mut k); - k.drop_lasts(mov); - cr - } + match node { + NodeToEncode::Node(value) => { + let value_hash = self.db.insert(k.as_prefix(), value); + k.drop_lasts(mov); + ChildReference::Hash(value_hash) + }, + NodeToEncode::TrieNode(child) => { + let result = self.commit_child(child, &mut k); + k.drop_lasts(mov); + result + }, + } + }, ); #[cfg(feature = "std")] trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - *self.root = self.db.insert(EMPTY_PREFIX, &encoded_root[..]); + + *self.root = self.db.insert( + EMPTY_PREFIX, + &encoded_root[..], + ); self.hash_count += 1; self.root_handle = NodeHandle::Hash(*self.root); @@ -1487,19 +1660,31 @@ where Stored::New(node) => { let encoded = { let commit_child = | - node_handle, + node: NodeToEncode>, o_slice: Option<&NibbleSlice>, o_index: Option | { let mov = prefix.append_optional_slice_and_nibble(o_slice, o_index); - let cr = self.commit_child(node_handle, prefix); - prefix.drop_lasts(mov); - cr + match node { + NodeToEncode::Node(value) => { + let value_hash = self.db.insert(prefix.as_prefix(), value); + prefix.drop_lasts(mov); + ChildReference::Hash(value_hash) + }, + NodeToEncode::TrieNode(node_handle) => { + let result = self.commit_child(node_handle, prefix); + prefix.drop_lasts(mov); + result + }, + } }; - node.into_encoded::<_, L::Codec, L::Hash>(commit_child) + node.into_encoded(commit_child) }; if encoded.len() >= L::Hash::LENGTH { - let hash = self.db.insert(prefix.as_prefix(), &encoded[..]); + let hash = self.db.insert( + prefix.as_prefix(), + &encoded[..], + ); self.hash_count +=1; ChildReference::Hash(hash) } else { @@ -1525,7 +1710,6 @@ where } } - impl<'a, L> TrieMut for TrieDBMut<'a, L> where L: TrieLayout, @@ -1548,14 +1732,14 @@ where fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, TrieHash, CError> where 'x: 'key { - self.lookup(NibbleSlice::new(key), &self.root_handle) + self.lookup(NibbleSlice::new(key), key, &self.root_handle) } fn insert( &mut self, key: &[u8], value: &[u8], - ) -> Result, TrieHash, CError> { + ) -> Result>, TrieHash, CError> { if !L::ALLOW_EMPTY && value.is_empty() { return self.remove(key) } let mut old_val = None; @@ -1578,15 +1762,15 @@ where Ok(old_val) } - fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + fn remove(&mut self, key: &[u8]) -> Result>, TrieHash, CError> { #[cfg(feature = "std")] trace!(target: "trie", "remove: key={:#x?}", key); let root_handle = self.root_handle(); - let mut key = NibbleSlice::new(key); + let mut key_slice = NibbleSlice::new(key); let mut old_val = None; - match self.remove_at(root_handle, &mut key, &mut old_val)? { + match self.remove_at(root_handle, &mut key_slice, &mut old_val)? { Some((handle, _changed)) => { #[cfg(feature = "std")] trace!(target: "trie", "remove: altered trie={}", _changed); @@ -1647,11 +1831,4 @@ mod tests { test_comb((0, &a), (1, &b), (1, &[0x01, 0x23, 0x46, 0x78][..])); test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); } - - #[test] - fn nice_debug_for_node() { - use super::Node; - let e: Node = Node::Leaf((1, vec![1, 2, 3].into()), vec![4, 5, 6]); - assert_eq!(format!("{:?}", e), "Leaf((1, 010203), 040506)"); - } } diff --git a/trie-db/test/Cargo.toml b/trie-db/test/Cargo.toml index 2feeefea..3d176e04 100644 --- a/trie-db/test/Cargo.toml +++ b/trie-db/test/Cargo.toml @@ -12,15 +12,15 @@ name = "bench" harness = false [dependencies] -trie-db = { path = "..", version = "0.22.1"} +trie-db = { path = "..", version = "0.23.0"} hash-db = { path = "../../hash-db", version = "0.15.2"} rustc-hex = { version = "2.1.0" } -memory-db = { path = "../../memory-db", version = "0.27.0" } +memory-db = { path = "../../memory-db", version = "0.28.0" } rand = { version = "0.8", default-features = false, features = ["small_rng"] } -trie-root = { path = "../../trie-root", version = "0.16.0"} +trie-root = { path = "../../trie-root", version = "0.17.0"} trie-standardmap = { path = "../../test-support/trie-standardmap", version = "0.15.2" } keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.2" } -reference-trie = { path = "../../test-support/reference-trie", version = "0.23.0" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.24.0" } hex-literal = "0.3" criterion = "0.3" env_logger = "0.9" diff --git a/trie-db/test/benches/bench.rs b/trie-db/test/benches/bench.rs index e2a63523..ee656574 100644 --- a/trie-db/test/benches/bench.rs +++ b/trie-db/test/benches/bench.rs @@ -16,6 +16,7 @@ use criterion::{criterion_group, criterion_main, Bencher, black_box, Criterion}; use trie_db::{NibbleSlice, proof::{generate_proof, verify_proof}, Trie}; use trie_standardmap::{Alphabet, StandardMap, ValueMode}; +use reference_trie::ExtensionLayout as Layout; criterion_group!(benches, root_old, @@ -75,7 +76,7 @@ fn root_a_big_v(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -96,7 +97,7 @@ fn root_b_big_v(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -118,7 +119,7 @@ fn root_a_small_v(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -139,7 +140,7 @@ fn root_b_small_v(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -160,7 +161,7 @@ fn root_old(c: &mut Criterion) { .iter() .map(|v| (&v.0, &v.1)); - reference_trie::reference_trie_root(inputc); + reference_trie::reference_trie_root::(inputc); }), data, ); @@ -185,7 +186,7 @@ fn root_new(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -287,7 +288,7 @@ fn trie_mut_root_a(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data); } @@ -306,7 +307,7 @@ fn trie_mut_root_b(c: &mut Criterion) { .map(|v| (&v.0, &v.1)) .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data); } @@ -326,7 +327,7 @@ fn trie_mut_ref_root_a(c: &mut Criterion) { .map(|v| (&v.0, &v.1)) .collect::>(); - reference_trie::reference_trie_root(inputc); + reference_trie::reference_trie_root_iter_build::(inputc); }), data); } @@ -346,7 +347,7 @@ fn trie_mut_ref_root_b(c: &mut Criterion) { .map(|v| (&v.0, &v.1)) .collect::>(); - reference_trie::reference_trie_root(inputc); + reference_trie::reference_trie_root_iter_build::(inputc); }), data); } @@ -366,7 +367,7 @@ fn trie_mut_a(c: &mut Criterion) { let mut root = Default::default(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); - let mut trie = reference_trie::RefTrieDBMut::new(&mut mdb, &mut root); + let mut trie = trie_db::TrieDBMut::::new(&mut mdb, &mut root); for (key, value) in datac { trie.insert(&key, &value) .expect("changes trie: insertion to trie is not allowed to fail within runtime"); @@ -390,7 +391,7 @@ fn trie_mut_b(c: &mut Criterion) { let mut root = Default::default(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); - let mut trie = reference_trie::RefTrieDBMut::new(&mut mdb, &mut root); + let mut trie = trie_db::TrieDBMut::::new(&mut mdb, &mut root); for (key, value) in datac { trie.insert(&key, &value) .expect("changes trie: insertion to trie is not allowed to fail within runtime"); @@ -416,7 +417,7 @@ fn trie_mut_build_a(c: &mut Criterion) { .collect::>(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); - reference_trie::calc_root_build(inputc, &mut mdb); + reference_trie::calc_root_build::(inputc, &mut mdb); }), data); } @@ -438,7 +439,7 @@ fn trie_mut_build_b(c: &mut Criterion) { .collect::>(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); - reference_trie::calc_root_build(inputc, &mut mdb); + reference_trie::calc_root_build::(inputc, &mut mdb); }), data); } @@ -449,11 +450,11 @@ fn trie_iteration(c: &mut Criterion) { let input = input2(29, 204800, 32); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); - let root = reference_trie::calc_root_build(input, &mut mdb); + let root = reference_trie::calc_root_build::(input, &mut mdb); c.bench_function("trie_iteration", move |b: &mut Bencher| b.iter(|| { - let trie = reference_trie::RefTrieDB::new(&mdb, &root).unwrap(); + let trie = trie_db::TrieDB::::new(&mdb, &root).unwrap(); let mut iter = trie_db::TrieDBNodeIterator::new(&trie).unwrap(); assert!(iter.all(|result| result.is_ok())); }) @@ -475,9 +476,9 @@ fn trie_proof_verification(c: &mut Criterion) { keys.dedup(); let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); - let root = reference_trie::calc_root_build(data, &mut mdb); + let root = reference_trie::calc_root_build::(data, &mut mdb); - let trie = reference_trie::RefTrieDB::new(&mdb, &root).unwrap(); + let trie = trie_db::TrieDB::::new(&mdb, &root).unwrap(); let proof = generate_proof(&trie, keys.iter()).unwrap(); let items = keys.into_iter() .map(|key| { @@ -488,10 +489,10 @@ fn trie_proof_verification(c: &mut Criterion) { c.bench_function("trie_proof_verification", move |b: &mut Bencher| b.iter(|| { - verify_proof::( + verify_proof::( &root, &proof, - items.iter() + items.iter(), ).unwrap(); }) ); diff --git a/trie-db/test/src/fatdb.rs b/trie-db/test/src/fatdb.rs index 1e8f6ff8..cdde42c0 100644 --- a/trie-db/test/src/fatdb.rs +++ b/trie-db/test/src/fatdb.rs @@ -14,12 +14,11 @@ use memory_db::{MemoryDB, HashKey}; use trie_db::{DBValue, Trie, TrieMut}; -use keccak_hasher::KeccakHasher; -use reference_trie::{RefFatDBMut, RefFatDB}; +use reference_trie::{RefFatDBMut, RefFatDB, RefHasher}; #[test] fn fatdb_to_trie() { - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { let mut t = RefFatDBMut::new(&mut memdb, &mut root); @@ -31,4 +30,8 @@ fn fatdb_to_trie() { t.iter().unwrap().map(Result::unwrap).collect::>(), vec![(vec![0x01u8, 0x23], vec![0x01u8, 0x23])] ); + assert_eq!( + t.key_iter().unwrap().map(Result::unwrap).collect::>(), + vec![vec![0x01u8, 0x23]] + ); } diff --git a/trie-db/test/src/fatdbmut.rs b/trie-db/test/src/fatdbmut.rs index 0ea2bb99..9e747acb 100644 --- a/trie-db/test/src/fatdbmut.rs +++ b/trie-db/test/src/fatdbmut.rs @@ -14,13 +14,12 @@ use memory_db::{MemoryDB, HashKey}; use hash_db::{Hasher, EMPTY_PREFIX}; -use keccak_hasher::KeccakHasher; -use reference_trie::{RefFatDBMut, RefTrieDB}; +use reference_trie::{RefFatDBMut, RefTrieDB, RefHasher}; use trie_db::{Trie, TrieMut}; #[test] fn fatdbmut_to_trie() { - let mut memdb = MemoryDB::, _>::default(); + let mut memdb = MemoryDB::, _>::default(); let mut root = Default::default(); { let mut t = RefFatDBMut::new(&mut memdb, &mut root); @@ -28,19 +27,19 @@ fn fatdbmut_to_trie() { } let t = RefTrieDB::new(&memdb, &root).unwrap(); assert_eq!( - t.get(&KeccakHasher::hash(&[0x01u8, 0x23])), + t.get(&RefHasher::hash(&[0x01u8, 0x23])), Ok(Some(vec![0x01u8, 0x23])), ); } #[test] fn fatdbmut_insert_remove_key_mapping() { - let mut memdb = MemoryDB::, _>::default(); + let mut memdb = MemoryDB::, _>::default(); let mut root = Default::default(); let key = [0x01u8, 0x23]; let val = [0x01u8, 0x24]; - let key_hash = KeccakHasher::hash(&key); - let aux_hash = KeccakHasher::hash(&key_hash); + let key_hash = RefHasher::hash(&key); + let aux_hash = RefHasher::hash(&key_hash); let mut t = RefFatDBMut::new(&mut memdb, &mut root); t.insert(&key, &val).unwrap(); assert_eq!(t.get(&key), Ok(Some(val.to_vec()))); diff --git a/trie-db/test/src/iter_build.rs b/trie-db/test/src/iter_build.rs index f892f3fa..b2db50fe 100644 --- a/trie-db/test/src/iter_build.rs +++ b/trie-db/test/src/iter_build.rs @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use trie_db::DBValue; +use trie_db::{DBValue, TrieLayout}; use memory_db::{MemoryDB, HashKey, PrefixedKey}; -use keccak_hasher::KeccakHasher; +use reference_trie::{RefHasher, test_layouts, + ExtensionLayout, NoExtensionLayout, HashedValueNoExt, HashedValueNoExtThreshold}; #[test] fn trie_root_empty () { @@ -36,48 +37,20 @@ fn root_extension_one () { ]); } -fn test_iter(data: Vec<(Vec, Vec)>) { - use reference_trie::{RefTrieDBMut, RefTrieDB}; - use trie_db::{TrieMut, Trie}; +fn test_iter(data: Vec<(Vec, Vec)>) { + use trie_db::{TrieMut, Trie, TrieDBMut, TrieDB}; - let mut db = MemoryDB::, DBValue>::default(); + let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut db, &mut root); + let mut t = TrieDBMut::::new(&mut db, &mut root); for i in 0..data.len() { let key: &[u8]= &data[i].0; let value: &[u8] = &data[i].1; t.insert(key, value).unwrap(); } } - let t = RefTrieDB::new(&db, &root).unwrap(); - for (i, kv) in t.iter().unwrap().enumerate() { - let (k, v) = kv.unwrap(); - let key: &[u8]= &data[i].0; - let value: &[u8] = &data[i].1; - assert_eq!(k, key); - assert_eq!(v, value); - } - for (k, v) in data.into_iter() { - assert_eq!(&t.get(&k[..]).unwrap().unwrap()[..], &v[..]); - } -} - -fn test_iter_no_extension(data: Vec<(Vec, Vec)>) { - use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt}; - use trie_db::{TrieMut, Trie}; - - let mut db = MemoryDB::, DBValue>::default(); - let mut root = Default::default(); - { - let mut t = RefTrieDBMutNoExt::new(&mut db, &mut root); - for i in 0..data.len() { - let key: &[u8]= &data[i].0; - let value: &[u8] = &data[i].1; - t.insert(key, value).unwrap(); - } - } - let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + let t = TrieDB::::new(&db, &root).unwrap(); for (i, kv) in t.iter().unwrap().enumerate() { let (k, v) = kv.unwrap(); let key: &[u8]= &data[i].0; @@ -91,46 +64,48 @@ fn test_iter_no_extension(data: Vec<(Vec, Vec)>) { } fn compare_implementations(data: Vec<(Vec, Vec)>) { - test_iter(data.clone()); - test_iter_no_extension(data.clone()); - compare_implementations_h(data.clone()); + test_iter::(data.clone()); + test_iter::(data.clone()); + test_iter::(data.clone()); + test_iter::(data.clone()); compare_implementations_prefixed(data.clone()); - compare_implementations_no_extension(data.clone()); - compare_implementations_no_extension_prefixed(data.clone()); + compare_implementations_h(data.clone()); } fn compare_implementations_prefixed(data: Vec<(Vec, Vec)>) { + compare_implementations_prefixed_internal::(data.clone()); + compare_implementations_prefixed_internal::(data.clone()); + compare_implementations_prefixed_internal::(data.clone()); + compare_implementations_prefixed_internal::(data.clone()); +} +fn compare_implementations_prefixed_internal(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_implementations(data, memdb, hashdb); + let hashdb = MemoryDB::, DBValue, _>::default(); + reference_trie::compare_implementations::(data, memdb, hashdb); } fn compare_implementations_h(data: Vec<(Vec, Vec)>) { - let memdb = MemoryDB::<_, HashKey<_>, _>::default(); - let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_implementations(data, memdb, hashdb); + compare_implementations_h_internal::(data.clone()); + compare_implementations_h_internal::(data.clone()); + compare_implementations_h_internal::(data.clone()); + compare_implementations_h_internal::(data.clone()); } -fn compare_implementations_no_extension(data: Vec<(Vec, Vec)>) { +fn compare_implementations_h_internal(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); - let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_implementations_no_extension(data, memdb, hashdb); -} -fn compare_implementations_no_extension_prefixed(data: Vec<(Vec, Vec)>) { - let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_implementations_no_extension(data, memdb, hashdb); + let hashdb = MemoryDB::, DBValue, _>::default(); + reference_trie::compare_implementations::(data.clone(), memdb, hashdb); } fn compare_implementations_no_extension_unordered(data: Vec<(Vec, Vec)>) { let memdb = MemoryDB::<_, HashKey<_>, _>::default(); - let hashdb = MemoryDB::, DBValue>::default(); - reference_trie::compare_implementations_no_extension_unordered(data, memdb, hashdb); + let hashdb = MemoryDB::, DBValue, _>::default(); + reference_trie::compare_implementations_unordered::(data, memdb, hashdb); } -fn compare_no_extension_insert_remove(data: Vec<(bool, Vec, Vec)>) { +fn compare_insert_remove(data: Vec<(bool, Vec, Vec)>) { let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - reference_trie::compare_no_extension_insert_remove(data, memdb); + reference_trie::compare_insert_remove::(data, memdb); } -fn compare_root(data: Vec<(Vec, Vec)>) { - let memdb = MemoryDB::<_, HashKey<_>, _>::default(); - reference_trie::compare_root(data, memdb); +fn compare_root(data: Vec<(Vec, Vec)>) { + let memdb = MemoryDB::, _>::default(); + reference_trie::compare_root::(data, memdb); } fn compare_unhashed(data: Vec<(Vec, Vec)>) { reference_trie::compare_unhashed(data); @@ -158,9 +133,9 @@ fn trie_middle_node2 () { (vec![1u8, 2u8, 3u8, 5u8, 3u8], vec![7u8;32]), ]); } -#[test] -fn root_extension_bis () { - compare_root(vec![ +test_layouts!(root_extension_bis, root_extension_bis_internal); +fn root_extension_bis_internal() { + compare_root::(vec![ (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;32]), (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), ]); @@ -264,31 +239,31 @@ fn fuzz_no_extension3 () { } #[test] fn fuzz_no_extension4 () { - compare_implementations_no_extension(vec![ + compare_implementations(vec![ (vec![0x01, 0x56], vec![0x1]), (vec![0x02, 0x42], vec![0x2]), (vec![0x02, 0x50], vec![0x3]), ]); } -#[test] -fn fuzz_no_extension_insert_remove_1 () { +test_layouts!(fuzz_no_extension_insert_remove_1, fuzz_no_extension_insert_remove_1_internal); +fn fuzz_no_extension_insert_remove_1_internal() { let data = vec![ (false, vec![0], vec![251, 255]), (false, vec![0, 1], vec![251, 255]), (false, vec![0, 1, 2], vec![255; 32]), (true, vec![0, 1], vec![0, 251]), ]; - compare_no_extension_insert_remove(data); + compare_insert_remove::(data); } -#[test] -fn fuzz_no_extension_insert_remove_2 () { +test_layouts!(fuzz_no_extension_insert_remove_2, fuzz_no_extension_insert_remove_2_internal); +fn fuzz_no_extension_insert_remove_2_internal() { let data = vec![ (false, vec![0x00], vec![0xfd, 0xff]), (false, vec![0x10, 0x00], vec![1;32]), (false, vec![0x11, 0x10], vec![0;32]), (true, vec![0x10, 0x00], vec![]) ]; - compare_no_extension_insert_remove(data); + compare_insert_remove::(data); } #[test] fn two_bytes_nibble_length () { @@ -296,21 +271,23 @@ fn two_bytes_nibble_length () { (vec![00u8], vec![0]), (vec![01u8;64], vec![0;32]), ]; - compare_implementations_no_extension(data.clone()); - compare_implementations_no_extension_prefixed(data.clone()); + compare_implementations(data.clone()); } #[test] #[should_panic] fn too_big_nibble_length_old () { - compare_implementations_h(vec![ - (vec![01u8;64], vec![0;32]), - ]); + compare_implementations_prefixed_internal::( + vec![(vec![01u8;64], vec![0;32])], + ); } #[test] fn too_big_nibble_length_new () { - compare_implementations_no_extension(vec![ + // this is valid for no_ext code only, + // the other one got maximal length in encoding. + let data = vec![ (vec![01u8;((u16::max_value() as usize + 1) / 2) + 1], vec![0;32]), - ]); + ]; + compare_implementations_prefixed_internal::(data.clone()); } #[test] fn polka_re_test () { diff --git a/trie-db/test/src/iterator.rs b/trie-db/test/src/iterator.rs index 2cc56068..db4f09d6 100644 --- a/trie-db/test/src/iterator.rs +++ b/trie-db/test/src/iterator.rs @@ -15,39 +15,25 @@ use trie_db::{ DBValue, TrieError, TrieMut, TrieIterator, TrieDBNodeIterator, NibbleSlice, NibbleVec, - node::Node, + node::{Node, Value}, TrieDB, TrieLayout, }; use hex_literal::hex; use hash_db::{HashDB, Hasher}; -use keccak_hasher::KeccakHasher; -use reference_trie::{ - RefTrieDB, RefTrieDBMut, -}; -use reference_trie::{RefTrieDBNoExt, RefTrieDBMutNoExt}; +use reference_trie::test_layouts; -type MemoryDB = memory_db::MemoryDB, DBValue>; +type MemoryDB = memory_db::MemoryDB< + ::Hash, + memory_db::PrefixedKey<::Hash>, + DBValue, +>; -fn build_trie_db_with_extension(pairs: &[(Vec, Vec)]) - -> (MemoryDB, ::Out) +fn build_trie_db(pairs: &[(Vec, Vec)]) + -> (MemoryDB, ::Out) { - let mut memdb = MemoryDB::default(); + let mut memdb = MemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - for (x, y) in pairs.iter() { - t.insert(x, y).unwrap(); - } - } - (memdb, root) -} - -fn build_trie_db_without_extension(pairs: &[(Vec, Vec)]) - -> (MemoryDB, ::Out) -{ - let mut memdb = MemoryDB::default(); - let mut root = Default::default(); - { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = trie_db::TrieDBMut::::new(&mut memdb, &mut root); for (x, y) in pairs.iter() { t.insert(x, y).unwrap(); } @@ -65,147 +51,143 @@ fn nibble_vec>(bytes: T, len: usize) -> NibbleVec { v } -#[test] -fn iterator_works_with_extension() { +test_layouts!(iterator_works, iterator_works_internal); +fn iterator_works_internal() { let pairs = vec![ (hex!("01").to_vec(), b"aaaa".to_vec()), (hex!("0123").to_vec(), b"bbbb".to_vec()), (hex!("02").to_vec(), vec![1; 32]), ]; - let (memdb, root) = build_trie_db_with_extension(&pairs); - let trie = RefTrieDB::new(&memdb, &root).unwrap(); + let (memdb, root) = build_trie_db::(&pairs); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); - match iter.next() { - Some(Ok((prefix, Some(_), node))) => { - assert_eq!(prefix, nibble_vec(hex!(""), 0)); - match node.node() { - Node::Extension(partial, _) => - assert_eq!(partial, NibbleSlice::new_offset(&hex!("00")[..], 1)), - _ => panic!("unexpected node"), + if T::USE_EXTENSION { + match iter.next() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!(""), 0)); + match node.node() { + Node::Extension(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("00")[..], 1)), + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - match iter.next() { - Some(Ok((prefix, Some(_), node))) => { - assert_eq!(prefix, nibble_vec(hex!("00"), 1)); - match node.node() { - Node::Branch(_, _) => {}, - _ => panic!("unexpected node"), + match iter.next() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!("00"), 1)); + match node.node() { + Node::Branch(_, _) => {}, + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - match iter.next() { - Some(Ok((prefix, None, node))) => { - assert_eq!(prefix, nibble_vec(hex!("01"), 2)); - match node.node() { - Node::Branch(_, _) => {}, - _ => panic!("unexpected node"), + match iter.next() { + Some(Ok((prefix, None, node))) => { + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + match node.node() { + Node::Branch(_, _) => {}, + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - match iter.next() { - Some(Ok((prefix, None, node))) => { - assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); - match node.node() { - Node::Leaf(partial, _) => - assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), - _ => panic!("unexpected node"), + match iter.next() { + Some(Ok((prefix, None, node))) => { + assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - match iter.next() { - Some(Ok((prefix, Some(_), node))) => { - assert_eq!(prefix, nibble_vec(hex!("02"), 2)); - match node.node() { - Node::Leaf(partial, _) => - assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), - _ => panic!("unexpected node"), + match iter.next() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!("02"), 2)); + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - assert!(iter.next().is_none()); -} - -#[test] -fn iterator_works_without_extension() { - let pairs = vec![ - (hex!("01").to_vec(), b"aaaa".to_vec()), - (hex!("0123").to_vec(), b"bbbb".to_vec()), - (hex!("02").to_vec(), vec![1; 32]), - ]; - - let (memdb, root) = build_trie_db_without_extension(&pairs); - let trie = RefTrieDBNoExt::new(&memdb, &root).unwrap(); - let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); - - match iter.next() { - Some(Ok((prefix, Some(_), node))) => { - assert_eq!(prefix, nibble_vec(hex!(""), 0)); - match node.node() { - Node::NibbledBranch(partial, _, _) => - assert_eq!(partial, NibbleSlice::new_offset(&hex!("00")[..], 1)), - _ => panic!("unexpected node"), + assert!(iter.next().is_none()); + } else { + let can_expand = T::MAX_INLINE_VALUE.unwrap_or(T::Hash::LENGTH as u32) < T::Hash::LENGTH as u32; + match iter.next() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!(""), 0)); + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("00")[..], 1)), + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - match iter.next() { - Some(Ok((prefix, None, node))) => { - assert_eq!(prefix, nibble_vec(hex!("01"), 2)); - match node.node() { - Node::NibbledBranch(partial, _, _) => - assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), - _ => panic!("unexpected node"), + match iter.next() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - match iter.next() { - Some(Ok((prefix, None, node))) => { - assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); - match node.node() { - Node::Leaf(partial, _) => - assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), - _ => panic!("unexpected node"), + match iter.next() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), + _ => panic!("unexpected node"), + } } - } - _ => panic!("unexpected item"), - } + _ => panic!("unexpected item"), + } - match iter.next() { - Some(Ok((prefix, Some(_), node))) => { - assert_eq!(prefix, nibble_vec(hex!("02"), 2)); - match node.node() { - Node::Leaf(partial, _) => - assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), - _ => panic!("unexpected node"), + match iter.next() { + Some(Ok((prefix, Some(_), node))) => { + assert_eq!(prefix, nibble_vec(hex!("02"), 2)); + match node.node() { + Node::Leaf(partial, _) => + assert_eq!(partial, NibbleSlice::new(&hex!("")[..])), + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - assert!(iter.next().is_none()); + assert!(iter.next().is_none()); + } } -#[test] -fn iterator_over_empty_works() { - let (memdb, root) = build_trie_db_with_extension(&[]); - let trie = RefTrieDB::new(&memdb, &root).unwrap(); +test_layouts!(iterator_over_empty_works, iterator_over_empty_works_internal); +fn iterator_over_empty_works_internal() { + let (memdb, root) = build_trie_db::(&[]); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); match iter.next() { @@ -222,16 +204,16 @@ fn iterator_over_empty_works() { assert!(iter.next().is_none()); } -#[test] -fn seek_works_with_extension() { +test_layouts!(seek_works, seek_works_internal); +fn seek_works_internal() { let pairs = vec![ (hex!("01").to_vec(), b"aaaa".to_vec()), (hex!("0123").to_vec(), b"bbbb".to_vec()), (hex!("02").to_vec(), vec![1; 32]), ]; - let (memdb, root) = build_trie_db_with_extension(&pairs); - let trie = RefTrieDB::new(&memdb, &root).unwrap(); + let (memdb, root) = build_trie_db::(&pairs); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); TrieIterator::seek(&mut iter, &hex!("")[..]).unwrap(); @@ -266,55 +248,10 @@ fn seek_works_with_extension() { assert!(iter.next().is_none()); } - -#[test] -fn seek_works_without_extension() { - let pairs = vec![ - (hex!("01").to_vec(), b"aaaa".to_vec()), - (hex!("0123").to_vec(), b"bbbb".to_vec()), - (hex!("02").to_vec(), vec![1; 32]), - ]; - - let (memdb, root) = build_trie_db_without_extension(&pairs); - let trie = RefTrieDBNoExt::new(&memdb, &root).unwrap(); - let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); - - TrieIterator::seek(&mut iter, &hex!("")[..]).unwrap(); - match iter.next() { - Some(Ok((prefix, _, _))) => - assert_eq!(prefix, nibble_vec(hex!(""), 0)), - _ => panic!("unexpected item"), - } - - TrieIterator::seek(&mut iter, &hex!("00")[..]).unwrap(); - match iter.next() { - Some(Ok((prefix, _, _))) => - assert_eq!(prefix, nibble_vec(hex!("01"), 2)), - _ => panic!("unexpected item"), - } - - TrieIterator::seek(&mut iter, &hex!("01")[..]).unwrap(); - match iter.next() { - Some(Ok((prefix, _, _))) => - assert_eq!(prefix, nibble_vec(hex!("01"), 2)), - _ => panic!("unexpected item"), - } - - TrieIterator::seek(&mut iter, &hex!("02")[..]).unwrap(); - match iter.next() { - Some(Ok((prefix, _, _))) => - assert_eq!(prefix, nibble_vec(hex!("02"), 2)), - _ => panic!("unexpected item"), - } - - TrieIterator::seek(&mut iter, &hex!("03")[..]).unwrap(); - assert!(iter.next().is_none()); -} - -#[test] -fn seek_over_empty_works() { - let (memdb, root) = build_trie_db_with_extension(&[]); - let trie = RefTrieDB::new(&memdb, &root).unwrap(); +test_layouts!(seek_over_empty_works, seek_over_empty_works_internal); +fn seek_over_empty_works_internal() { + let (memdb, root) = build_trie_db::(&[]); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); TrieIterator::seek(&mut iter, &hex!("")[..]).unwrap(); @@ -333,8 +270,8 @@ fn seek_over_empty_works() { assert!(iter.next().is_none()); } -#[test] -fn iterate_over_incomplete_db() { +test_layouts!(iterate_over_incomplete_db, iterate_over_incomplete_db_internal); +fn iterate_over_incomplete_db_internal() { let pairs = vec![ (hex!("01").to_vec(), b"aaaa".to_vec()), (hex!("0123").to_vec(), b"bbbb".to_vec()), @@ -342,11 +279,11 @@ fn iterate_over_incomplete_db() { (hex!("03").to_vec(), vec![2; 32]), ]; - let (mut memdb, root) = build_trie_db_with_extension(&pairs); + let (mut memdb, root) = build_trie_db::(&pairs); // Look up the leaf node with prefix "02". let leaf_hash = { - let trie = RefTrieDB::new(&memdb, &root).unwrap(); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); TrieIterator::seek(&mut iter, &hex!("02")[..]).unwrap(); @@ -367,30 +304,39 @@ fn iterate_over_incomplete_db() { // Seek to missing node returns error. { - let trie = RefTrieDB::new(&memdb, &root).unwrap(); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); match TrieIterator::seek(&mut iter, &hex!("02")[..]) { - Err(ref err) if **err == TrieError::IncompleteDatabase(leaf_hash) => {}, + Err(e) => { + if let TrieError::IncompleteDatabase(err_hash) = *e { + assert_eq!(err_hash.as_ref(), leaf_hash.as_ref()); + } + }, _ => panic!("expected IncompleteDatabase error"), } } // Iterate over missing node works. { - let trie = RefTrieDB::new(&memdb, &root).unwrap(); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); TrieIterator::seek(&mut iter, &hex!("0130")[..]).unwrap(); match iter.next() { - Some(Err(ref err)) if **err == TrieError::IncompleteDatabase(leaf_hash) => {}, + Some(Err(e)) => { + if let TrieError::IncompleteDatabase(err_hash) = *e { + assert_eq!(err_hash.as_ref(), leaf_hash.as_ref()); + } + }, _ => panic!("expected IncompleteDatabase error"), } match iter.next() { Some(Ok((_, _, node))) => { match node.node() { - Node::Leaf(_, v) => - assert_eq!(&v[..], &vec![2; 32][..]), + Node::Leaf(_, v) => if !matches!(v, Value::Node(..)) { + assert_eq!(v, Value::Inline(&vec![2; 32][..])); + }, _ => panic!("unexpected node"), } } @@ -401,82 +347,54 @@ fn iterate_over_incomplete_db() { } } -#[test] -fn prefix_works_with_extension() { +test_layouts!(prefix_works, prefix_works_internal); +fn prefix_works_internal() { + let can_expand = T::MAX_INLINE_VALUE.unwrap_or(T::Hash::LENGTH as u32) < T::Hash::LENGTH as u32; let pairs = vec![ (hex!("01").to_vec(), b"aaaa".to_vec()), (hex!("0123").to_vec(), b"bbbb".to_vec()), (hex!("02").to_vec(), vec![1; 32]), ]; - let (memdb, root) = build_trie_db_with_extension(&pairs); - let trie = RefTrieDB::new(&memdb, &root).unwrap(); + let (memdb, root) = build_trie_db::(&pairs); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); iter.prefix(&hex!("01").to_vec()[..]).unwrap(); - match iter.next() { - Some(Ok((prefix, None, node))) => { - assert_eq!(prefix, nibble_vec(hex!("01"), 2)); - match node.node() { - Node::Branch(_, _) => {}, - _ => panic!("unexpected node"), + if T::USE_EXTENSION { + match iter.next() { + Some(Ok((prefix, None, node))) => { + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + match node.node() { + Node::Branch(_, _) => {}, + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), - } - - match iter.next() { - Some(Ok((prefix, None, node))) => { - assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); - match node.node() { - Node::Leaf(partial, _) => - assert_eq!(partial, NibbleSlice::new_offset(&hex!("03")[..], 1)), - _ => panic!("unexpected node"), + } else { + match iter.next() { + Some(Ok((prefix, hash, node))) => { + if !can_expand { + debug_assert!(hash.is_none()); + } + assert_eq!(prefix, nibble_vec(hex!("01"), 2)); + match node.node() { + Node::NibbledBranch(partial, _, _) => + assert_eq!(partial, NibbleSlice::new_offset(&hex!("")[..], 0)), + _ => panic!("unexpected node"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), } - assert!(iter.next().is_none()); - - let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); - iter.prefix(&hex!("0010").to_vec()[..]).unwrap(); - assert!(iter.next().is_none()); - let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); - iter.prefix(&hex!("10").to_vec()[..]).unwrap(); - assert!(iter.next().is_none()); - -} - -#[test] -fn prefix_works_without_extension() { - let pairs = vec![ - (hex!("01").to_vec(), b"aaaa".to_vec()), - (hex!("0123").to_vec(), b"bbbb".to_vec()), - (hex!("02").to_vec(), vec![1; 32]), - ]; - - let (memdb, root) = build_trie_db_without_extension(&pairs); - let trie = RefTrieDBNoExt::new(&memdb, &root).unwrap(); - let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); - - iter.prefix(&hex!("01").to_vec()[..]).unwrap(); - match iter.next() { - Some(Ok((prefix, None, node))) => { - assert_eq!(prefix, nibble_vec(hex!("01"), 2)); - match node.node() { - Node::NibbledBranch(partial, _, _) => - assert_eq!(partial, NibbleSlice::new_offset(&hex!("")[..], 0)), - _ => panic!("unexpected node"), + Some(Ok((prefix, hash, node))) => { + if !can_expand { + debug_assert!(hash.is_none()); } - } - _ => panic!("unexpected item"), - } - - match iter.next() { - Some(Ok((prefix, None, node))) => { assert_eq!(prefix, nibble_vec(hex!("0120"), 3)); match node.node() { Node::Leaf(partial, _) => @@ -495,13 +413,12 @@ fn prefix_works_without_extension() { let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); iter.prefix(&hex!("10").to_vec()[..]).unwrap(); assert!(iter.next().is_none()); - } -#[test] -fn prefix_over_empty_works() { - let (memdb, root) = build_trie_db_with_extension(&[]); - let trie = RefTrieDB::new(&memdb, &root).unwrap(); +test_layouts!(prefix_over_empty_works, prefix_over_empty_works_internal); +fn prefix_over_empty_works_internal() { + let (memdb, root) = build_trie_db::(&[]); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = TrieDBNodeIterator::new(&trie).unwrap(); iter.prefix(&hex!("")[..]).unwrap(); match iter.next() { diff --git a/trie-db/test/src/proof.rs b/trie-db/test/src/proof.rs index a25ee19c..f271d3b4 100644 --- a/trie-db/test/src/proof.rs +++ b/trie-db/test/src/proof.rs @@ -14,7 +14,7 @@ use hash_db::Hasher; use reference_trie::{ - ExtensionLayout, NoExtensionLayout, + NoExtensionLayout, test_layouts, }; use trie_db::{ @@ -22,7 +22,12 @@ use trie_db::{ proof::{generate_proof, verify_proof, VerifyError}, Trie, }; -type MemoryDB = memory_db::MemoryDB, DBValue>; +type MemoryDB = memory_db::MemoryDB< + ::Hash, + memory_db::HashKey<::Hash>, + DBValue, +>; + fn test_entries() -> Vec<(&'static [u8], &'static [u8])> { vec![ @@ -49,7 +54,7 @@ fn test_generate_proof( { // Populate DB with full trie from entries. let (db, root) = { - let mut db = >::default(); + let mut db = >::default(); let mut root = Default::default(); { let mut trie = >::new(&mut db, &mut root); @@ -70,28 +75,29 @@ fn test_generate_proof( (root, proof, items) } -#[test] -fn trie_proof_works_with_ext() { - let (root, proof, items) = test_generate_proof::( - test_entries(), +test_layouts!(trie_proof_works2, trie_proof_works_internal2); +fn trie_proof_works_internal2() { + let (root, proof, items) = test_generate_proof::( + vec![ + // "do" is at a hash-referenced branch node. + (&b"do"[..], b"verb"), + // "dog" is at a hash-referenced branch node. + (b"dog", b"puppy"), + ], vec![ b"do", b"dog", - b"doge", - b"bravo", - b"alfabet", // None, not found under leaf node - b"d", // None, witness is extension node with omitted child - b"do\x10", // None, empty branch child - b"halp", // None, witness is extension node with non-omitted child ], ); - verify_proof::(&root, &proof, items.iter()).unwrap(); + verify_proof::(&root, &proof, items.iter()).unwrap(); } -#[test] -fn trie_proof_works_without_ext() { - let (root, proof, items) = test_generate_proof::( + + +test_layouts!(trie_proof_works, trie_proof_works_internal); +fn trie_proof_works_internal() { + let (root, proof, items) = test_generate_proof::( test_entries(), vec![ b"do", @@ -105,12 +111,12 @@ fn trie_proof_works_without_ext() { ], ); - verify_proof::(&root, &proof, items.iter()).unwrap(); + verify_proof::(&root, &proof, items.iter()).unwrap(); } -#[test] -fn trie_proof_works_for_empty_trie() { - let (root, proof, items) = test_generate_proof::( +test_layouts!(trie_proof_works_for_empty_trie, trie_proof_works_for_empty_trie_internal); +fn trie_proof_works_for_empty_trie_internal() { + let (root, proof, items) = test_generate_proof::( vec![], vec![ b"alpha", @@ -119,12 +125,12 @@ fn trie_proof_works_for_empty_trie() { ], ); - verify_proof::(&root, &proof, items.iter()).unwrap(); + verify_proof::(&root, &proof, items.iter()).unwrap(); } -#[test] -fn test_verify_duplicate_keys() { - let (root, proof, _) = test_generate_proof::( +test_layouts!(test_verify_duplicate_keys, test_verify_duplicate_keys_internal); +fn test_verify_duplicate_keys_internal() { + let (root, proof, _) = test_generate_proof::( test_entries(), vec![b"bravo"], ); @@ -133,15 +139,22 @@ fn test_verify_duplicate_keys() { (b"bravo", Some(b"bravo")), (b"bravo", Some(b"bravo")), ]; - assert_eq!( - verify_proof::(&root, &proof, items.iter()), - Err(VerifyError::DuplicateKey(b"bravo".to_vec())) + assert!( + if let Err(VerifyError::DuplicateKey(key)) = verify_proof::( + &root, + &proof, + items.iter(), + ) { + key == b"bravo".to_vec() + } else { + false + } ); } -#[test] -fn test_verify_extraneous_node() { - let (root, proof, _) = test_generate_proof::( +test_layouts!(test_verify_extraneaous_node, test_verify_extraneaous_node_internal); +fn test_verify_extraneaous_node_internal() { + let (root, proof, _) = test_generate_proof::( test_entries(), vec![b"bravo", b"do"], ); @@ -149,15 +162,15 @@ fn test_verify_extraneous_node() { let items = vec![ (b"bravo", Some(b"bravo")), ]; - assert_eq!( - verify_proof::(&root, &proof, items.iter()), + assert!(matches!( + verify_proof::(&root, &proof, items.iter()), Err(VerifyError::ExtraneousNode) - ); + )); } -#[test] -fn test_verify_extraneous_value() { - let (root, proof, _) = test_generate_proof::( +test_layouts!(test_verify_extraneaous_value, test_verify_extraneaous_value_internal); +fn test_verify_extraneaous_value_internal() { + let (root, proof, _) = test_generate_proof::( test_entries(), vec![b"doge"], ); @@ -166,9 +179,16 @@ fn test_verify_extraneous_value() { (&b"do"[..], Some(&b"verb"[..])), (&b"doge"[..], Some(&[0; 32][..])), ]; - assert_eq!( - verify_proof::(&root, &proof, items.iter()), - Err(VerifyError::ExtraneousValue(b"do".to_vec())) + assert!( + if let Err(VerifyError::ExtraneousValue(val)) = verify_proof::( + &root, + &proof, + items.iter(), + ) { + val == b"do".to_vec() + } else { + false + } ); } @@ -189,27 +209,31 @@ fn test_verify_extraneous_hash_reference() { } } -#[test] -fn test_verify_invalid_child_reference() { - let (root, proof, _) = test_generate_proof::( +test_layouts!(test_verify_invalid_child_reference, test_verify_invalid_child_reference_internal); +fn test_verify_invalid_child_reference_internal() { + let (root, proof, _) = test_generate_proof::( test_entries(), vec![b"bravo"], ); + if T::MAX_INLINE_VALUE.map(|t| t as usize <= b"bravo".len() ).unwrap_or(false) { + // node will not be inline: ignore test + return; + } // InvalidChildReference because "bravo" is in an inline leaf node and a 32-byte value cannot // fit in an inline leaf. let items = vec![ (b"bravo", Some([0; 32])), ]; - match verify_proof::(&root, &proof, items.iter()) { + match verify_proof::(&root, &proof, items.iter()) { Err(VerifyError::InvalidChildReference(_)) => {} result => panic!("expected VerifyError::InvalidChildReference, got {:?}", result), } } -#[test] -fn test_verify_value_mismatch_some_to_none() { - let (root, proof, _) = test_generate_proof::( +test_layouts!(test_verify_value_mismatch_some_to_none, test_verify_value_mismatch_some_to_none_internal); +fn test_verify_value_mismatch_some_to_none_internal() { + let (root, proof, _) = test_generate_proof::( test_entries(), vec![b"horse"], ); @@ -218,15 +242,22 @@ fn test_verify_value_mismatch_some_to_none() { (&b"horse"[..], Some(&b"stallion"[..])), (&b"halp"[..], Some(&b"plz"[..])), ]; - assert_eq!( - verify_proof::(&root, &proof, items.iter()), - Err(VerifyError::ValueMismatch(b"halp".to_vec())) + assert!( + if let Err(VerifyError::ValueMismatch(val)) = verify_proof::( + &root, + &proof, + items.iter(), + ) { + val == b"halp".to_vec() + } else { + false + } ); } -#[test] -fn test_verify_value_mismatch_none_to_some() { - let (root, proof, _) = test_generate_proof::( +test_layouts!(test_verify_value_mismatch_none_to_some, test_verify_value_mismatch_none_to_some_internal); +fn test_verify_value_mismatch_none_to_some_internal() { + let (root, proof, _) = test_generate_proof::( test_entries(), vec![b"alfa", b"bravo"], ); @@ -235,29 +266,36 @@ fn test_verify_value_mismatch_none_to_some() { (&b"alfa"[..], Some(&[0; 32][..])), (&b"bravo"[..], None), ]; - assert_eq!( - verify_proof::(&root, &proof, items.iter()), - Err(VerifyError::ValueMismatch(b"bravo".to_vec())) + assert!( + if let Err(VerifyError::ValueMismatch(val)) = verify_proof::( + &root, + &proof, + items.iter(), + ) { + val == b"bravo".to_vec() + } else { + false + } ); } -#[test] -fn test_verify_incomplete_proof() { - let (root, mut proof, items) = test_generate_proof::( +test_layouts!(test_verify_incomplete_proof, test_verify_incomplete_proof_internal); +fn test_verify_incomplete_proof_internal() { + let (root, mut proof, items) = test_generate_proof::( test_entries(), vec![b"alfa"], ); proof.pop(); - assert_eq!( - verify_proof::(&root, &proof, items.iter()), + assert!(matches!( + verify_proof::(&root, &proof, items.iter()), Err(VerifyError::IncompleteProof) - ); + )); } -#[test] -fn test_verify_root_mismatch() { - let (root, proof, _) = test_generate_proof::( +test_layouts!(test_verify_root_mismatch, test_verify_root_mismatch_internal); +fn test_verify_root_mismatch_internal() { + let (root, proof, _) = test_generate_proof::( test_entries(), vec![b"bravo"], ); @@ -265,21 +303,21 @@ fn test_verify_root_mismatch() { let items = vec![ (b"bravo", Some("incorrect")), ]; - match verify_proof::(&root, &proof, items.iter()) { + match verify_proof::(&root, &proof, items.iter()) { Err(VerifyError::RootMismatch(_)) => {} result => panic!("expected VerifyError::RootMismatch, got {:?}", result), } } -#[test] -fn test_verify_decode_error() { - let (root, mut proof, items) = test_generate_proof::( +test_layouts!(test_verify_decode_error, test_verify_decode_error_internal); +fn test_verify_decode_error_internal() { + let (root, mut proof, items) = test_generate_proof::( test_entries(), vec![b"bravo"], ); proof.insert(0, b"this is not a trie node".to_vec()); - match verify_proof::(&root, &proof, items.iter()) { + match verify_proof::(&root, &proof, items.iter()) { Err(VerifyError::DecodeError(_)) => {} result => panic!("expected VerifyError::DecodeError, got {:?}", result), } diff --git a/trie-db/test/src/recorder.rs b/trie-db/test/src/recorder.rs index faf1a07d..489adad2 100644 --- a/trie-db/test/src/recorder.rs +++ b/trie-db/test/src/recorder.rs @@ -16,8 +16,7 @@ use memory_db::{MemoryDB, HashKey}; use hash_db::Hasher; -use keccak_hasher::KeccakHasher; -use reference_trie::{RefTrieDB, RefTrieDBMut}; +use reference_trie::{RefHasher, RefTrieDBMut, RefTrieDB}; use trie_db::{Trie, TrieMut, Recorder, Record}; #[test] @@ -27,7 +26,7 @@ fn basic_recorder() { let node1 = vec![1, 2, 3, 4]; let node2 = vec![4, 5, 6, 7, 8, 9, 10]; - let (hash1, hash2) = (KeccakHasher::hash(&node1), KeccakHasher::hash(&node2)); + let (hash1, hash2) = (RefHasher::hash(&node1), RefHasher::hash(&node2)); basic.record(&hash1, &node1, 0); basic.record(&hash2, &node2, 456); @@ -43,7 +42,6 @@ fn basic_recorder() { depth: 456, }; - assert_eq!(basic.drain(), vec![record1, record2]); } @@ -54,8 +52,8 @@ fn basic_recorder_min_depth() { let node1 = vec![1, 2, 3, 4]; let node2 = vec![4, 5, 6, 7, 8, 9, 10]; - let hash1 = KeccakHasher::hash(&node1); - let hash2 = KeccakHasher::hash(&node2); + let hash1 = RefHasher::hash(&node1); + let hash2 = RefHasher::hash(&node2); basic.record(&hash1, &node1, 0); basic.record(&hash2, &node2, 456); @@ -72,7 +70,7 @@ fn basic_recorder_min_depth() { #[test] fn trie_record() { - let mut db = MemoryDB::, _>::default(); + let mut db = MemoryDB::, _>::default(); let mut root = Default::default(); { let mut x = RefTrieDBMut::new(&mut db, &mut root); diff --git a/trie-db/test/src/sectriedb.rs b/trie-db/test/src/sectriedb.rs index c1635bac..bebca667 100644 --- a/trie-db/test/src/sectriedb.rs +++ b/trie-db/test/src/sectriedb.rs @@ -14,17 +14,16 @@ use memory_db::{MemoryDB, HashKey}; use hash_db::Hasher; -use keccak_hasher::KeccakHasher; -use reference_trie::{RefTrieDBMut, RefSecTrieDB}; +use reference_trie::{RefTrieDBMut, RefSecTrieDB, RefHasher}; use trie_db::{DBValue, Trie, TrieMut}; #[test] fn trie_to_sectrie() { - let mut db = MemoryDB::, DBValue>::default(); + let mut db = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMut::new(&mut db, &mut root); - t.insert(&KeccakHasher::hash(&[0x01u8, 0x23]), &[0x01u8, 0x23]).unwrap(); + t.insert(&RefHasher::hash(&[0x01u8, 0x23]), &[0x01u8, 0x23]).unwrap(); } let t = RefSecTrieDB::new(&db, &root).unwrap(); assert_eq!(t.get(&[0x01u8, 0x23]).unwrap().unwrap(), vec![0x01u8, 0x23]); diff --git a/trie-db/test/src/sectriedbmut.rs b/trie-db/test/src/sectriedbmut.rs index b2b60731..e65a5b49 100644 --- a/trie-db/test/src/sectriedbmut.rs +++ b/trie-db/test/src/sectriedbmut.rs @@ -15,13 +15,12 @@ use memory_db::{MemoryDB, HashKey}; use hash_db::Hasher; -use keccak_hasher::KeccakHasher; -use reference_trie::{RefTrieDB, RefSecTrieDBMut}; +use reference_trie::{RefTrieDB, RefSecTrieDBMut, RefHasher}; use trie_db::{DBValue, Trie, TrieMut}; #[test] fn sectrie_to_trie() { - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { let mut t = RefSecTrieDBMut::new(&mut memdb, &mut root); @@ -29,7 +28,7 @@ fn sectrie_to_trie() { } let t = RefTrieDB::new(&memdb, &root).unwrap(); assert_eq!( - t.get(&KeccakHasher::hash(&[0x01u8, 0x23])).unwrap().unwrap(), + t.get(&RefHasher::hash(&[0x01u8, 0x23])).unwrap().unwrap(), vec![0x01u8, 0x23], ); } diff --git a/trie-db/test/src/trie_codec.rs b/trie-db/test/src/trie_codec.rs index e4aa2181..a5099717 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -18,11 +18,14 @@ use trie_db::{ Trie, TrieMut, TrieDB, TrieError, TrieDBMut, TrieLayout, Recorder, }; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; -use reference_trie::{ - ExtensionLayout, NoExtensionLayout, -}; +use reference_trie::test_layouts; + -type MemoryDB = memory_db::MemoryDB, DBValue>; +type MemoryDB = memory_db::MemoryDB< + ::Hash, + memory_db::HashKey<::Hash>, + DBValue, +>; fn test_encode_compact( entries: Vec<(&'static [u8], &'static [u8])>, @@ -31,7 +34,7 @@ fn test_encode_compact( { // Populate DB with full trie from entries. let (db, root) = { - let mut db = >::default(); + let mut db = >::default(); let mut root = Default::default(); { let mut trie = >::new(&mut db, &mut root); @@ -55,7 +58,7 @@ fn test_encode_compact( }; // Populate a partial trie DB with recorded nodes. - let mut partial_db = MemoryDB::default(); + let mut partial_db = MemoryDB::::default(); for record in recorder.drain() { partial_db.insert(EMPTY_PREFIX, &record.data); } @@ -76,8 +79,8 @@ fn test_decode_compact( expected_used: usize, ) { // Reconstruct the partial DB from the compact encoding. - let mut db = MemoryDB::default(); - let (root, used) = decode_compact::(&mut db, encoded).unwrap(); + let mut db = MemoryDB::::default(); + let (root, used) = decode_compact::(&mut db, encoded).unwrap(); assert_eq!(root, expected_root); assert_eq!(used, expected_used); @@ -88,9 +91,9 @@ fn test_decode_compact( } } -#[test] -fn trie_compact_encoding_works_with_ext() { - let (root, mut encoded, items) = test_encode_compact::( +test_layouts!(trie_compact_encoding_works, trie_compact_encoding_works_internal); +fn trie_compact_encoding_works_internal() { + let (root, mut encoded, items) = test_encode_compact::( vec![ // "alfa" is at a hash-referenced leaf node. (b"alfa", &[0; 32]), @@ -118,45 +121,12 @@ 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); -} - -#[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"), - ], - 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 - ], - ); - - 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); } -#[test] -fn trie_decoding_fails_with_incomplete_database() { - let (_, encoded, _) = test_encode_compact::( +test_layouts!(trie_decoding_fails_with_incomplete_database, trie_decoding_fails_with_incomplete_database_internal); +fn trie_decoding_fails_with_incomplete_database_internal() { + let (_, encoded, _) = test_encode_compact::( vec![ (b"alfa", &[0; 32]), (b"bravo", b"bravo"), @@ -169,8 +139,8 @@ fn trie_decoding_fails_with_incomplete_database() { assert!(encoded.len() > 1); // Reconstruct the partial DB from the compact encoding. - let mut db = MemoryDB::default(); - match decode_compact::(&mut db, &encoded[..encoded.len() - 1]) { + let mut db = MemoryDB::::default(); + match decode_compact::(&mut db, &encoded[..encoded.len() - 1]) { Err(err) => match *err { TrieError::IncompleteDatabase(_) => {} _ => panic!("got unexpected TrieError"), diff --git a/trie-db/test/src/triedb.rs b/trie-db/test/src/triedb.rs index 4dffaf0e..f4afc7f1 100644 --- a/trie-db/test/src/triedb.rs +++ b/trie-db/test/src/triedb.rs @@ -13,57 +13,34 @@ // limitations under the License. use memory_db::{MemoryDB, PrefixedKey}; -use keccak_hasher::KeccakHasher; -use trie_db::{DBValue, Trie, TrieMut, NibbleSlice}; -use reference_trie::{RefTrieDB, RefTrieDBMut, RefLookup}; -use reference_trie::{RefTrieDBNoExt, RefTrieDBMutNoExt}; +use trie_db::{TrieDB, TrieDBMut, Lookup, Trie, TrieMut, NibbleSlice, TrieLayout, + DBValue}; +use reference_trie::test_layouts; use hex_literal::hex; -#[test] -fn iterator_works() { - let pairs = vec![ - (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()), - (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), - ]; - - let mut memdb = MemoryDB::, DBValue>::default(); - let mut root = Default::default(); - { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - for (x, y) in &pairs { - t.insert(x, y).unwrap(); - } - } - - let trie = RefTrieDB::new(&memdb, &root).unwrap(); +type PrefixedMemoryDB = MemoryDB::< + ::Hash, + PrefixedKey<::Hash>, + DBValue, +>; - let iter = trie.iter().unwrap(); - let mut iter_pairs = Vec::new(); - for pair in iter { - let (key, value) = pair.unwrap(); - iter_pairs.push((key, value.to_vec())); - } - - assert_eq!(pairs, iter_pairs); -} - -#[test] -fn iterator_works_without_extension() { +test_layouts!(iterator_works, iterator_works_internal); +fn iterator_works_internal() { let pairs = vec![ (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()), - (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), + (hex!("0103000000000010000469").to_vec(), hex!("ffffffffff").to_vec()), ]; - let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for (x, y) in &pairs { t.insert(x, y).unwrap(); } } - let trie = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let iter = trie.iter().unwrap(); let mut iter_pairs = Vec::new(); @@ -75,23 +52,23 @@ fn iterator_works_without_extension() { assert_eq!(pairs, iter_pairs); } -#[test] -fn iterator_seek_works() { +test_layouts!(iterator_seek_works, iterator_seek_works_internal); +fn iterator_seek_works_internal() { let pairs = vec![ (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()), (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), ]; - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for (x, y) in &pairs { t.insert(x, y).unwrap(); } } - let t = RefTrieDB::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); assert_eq!( @@ -118,44 +95,8 @@ fn iterator_seek_works() { ); } -#[test] -fn iterator_seek_works_without_extension() { - let pairs = vec![ - (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()), - (hex!("0103000000000000000469").to_vec(), hex!("ffffffffff").to_vec()), - ]; - - let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); - let mut root = Default::default(); - { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); - for (x, y) in &pairs { - t.insert(x, y).unwrap(); - } - } - - let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); - - let mut iter = t.iter().unwrap(); - assert_eq!( - iter.next().unwrap().unwrap(), - (hex!("0103000000000000000464").to_vec(), hex!("fffffffffe").to_vec()) - ); - iter.seek(&hex!("00")[..]).unwrap(); - assert_eq!( - pairs, - iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>(), - ); - let mut iter = t.iter().unwrap(); - iter.seek(&hex!("0103000000000000000465")[..]).unwrap(); - assert_eq!( - &pairs[1..], - &iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()[..], - ); -} - -#[test] -fn iterator() { +test_layouts!(iterator, iterator_internal); +fn iterator_internal() { let d = vec![ b"A".to_vec(), b"AA".to_vec(), @@ -163,16 +104,16 @@ fn iterator() { b"B".to_vec(), ]; - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for x in &d { t.insert(x, x).unwrap(); } } - let t = RefTrieDB::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); assert_eq!( d.iter() .map(|i| i.clone()) @@ -185,34 +126,8 @@ fn iterator() { assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); } -#[test] -fn iterator_without_extension() { - let d = vec![ - b"A".to_vec(), - b"AA".to_vec(), - b"AB".to_vec(), - b"B".to_vec(), - ]; - - let mut memdb = MemoryDB::, DBValue>::default(); - let mut root = Default::default(); - { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); - for x in &d { - t.insert(x, x).unwrap(); - } - } - - let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); - assert_eq!( - d.iter().map(|i| i.clone()).collect::>(), - t.iter().unwrap().map(|x| x.unwrap().0).collect::>(), - ); - assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); -} - -#[test] -fn iterator_seek() { +test_layouts!(iterator_seek, iterator_seek_internal); +fn iterator_seek_internal() { let d = vec![ b"A".to_vec(), b"AA".to_vec(), @@ -226,16 +141,16 @@ fn iterator_seek() { vec![3; 32], ]; - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for (k, val) in d.iter().zip(vals.iter()) { t.insert(k, val.as_slice()).unwrap(); } } - let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); assert_eq!(iter.next().unwrap().unwrap(), (b"A".to_vec(), vals[0].clone())); iter.seek(b"!").unwrap(); @@ -273,40 +188,24 @@ fn iterator_seek() { assert_eq!(&vals[4..], &iter.map(|x| x.unwrap().1).collect::>()[..]); } -#[test] -fn get_length_with_extension() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(get_length_with_extension, get_length_with_extension_internal); +fn get_length_with_extension_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(b"A", b"ABC").unwrap(); t.insert(b"B", b"ABCBAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(); } - let t = RefTrieDB::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); assert_eq!(t.get_with(b"A", |x: &[u8]| x.len()).unwrap(), Some(3)); assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()).unwrap(), Some(32)); assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None); } -#[test] -fn get_length_without_extension() { - let mut memdb = MemoryDB::, DBValue>::default(); - let mut root = Default::default(); - { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); - t.insert(b"A", b"ABC").unwrap(); - t.insert(b"B", b"ABCBA").unwrap(); - } - - let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); - assert_eq!(t.get_with(b"A", |x: &[u8]| x.len()).unwrap(), Some(3)); - assert_eq!(t.get_with(b"B", |x: &[u8]| x.len()).unwrap(), Some(5)); - assert_eq!(t.get_with(b"C", |x: &[u8]| x.len()).unwrap(), None); -} - -#[test] -fn debug_output_supports_pretty_print() { +test_layouts!(debug_output_supports_pretty_print, debug_output_supports_pretty_print_internal); +fn debug_output_supports_pretty_print_internal() { let d = vec![ b"A".to_vec(), b"AA".to_vec(), @@ -314,18 +213,19 @@ fn debug_output_supports_pretty_print() { b"B".to_vec(), ]; - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); let root = { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for x in &d { t.insert(x, x).unwrap(); } t.root().clone() }; - let t = RefTrieDB::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); - assert_eq!(format!("{:#?}", t), + if T::USE_EXTENSION { + assert_eq!(format!("{:#?}", t), "TrieDB { hash_count: 0, root: Node::Extension { @@ -341,60 +241,70 @@ fn debug_output_supports_pretty_print() { Node::Leaf { index: 1, slice: , - value: [ - 65, - 65, - ], + value: Inline( + [ + 65, + 65, + ], + ), }, Node::Leaf { index: 2, slice: , - value: [ - 65, - 66, - ], + value: Inline( + [ + 65, + 66, + ], + ), }, ], value: None, }, ], value: Some( - [ - 65, - ], + Inline( + [ + 65, + ], + ), ), }, Node::Leaf { index: 2, slice: , - value: [ - 66, - ], + value: Inline( + [ + 66, + ], + ), }, ], value: None, }, }, -}"); - +}") + } else { + // untested without extension + }; } -#[test] -fn test_lookup_with_corrupt_data_returns_decoder_error() { +test_layouts!(test_lookup_with_corrupt_data_returns_decoder_error, test_lookup_with_corrupt_data_returns_decoder_error_internal); +fn test_lookup_with_corrupt_data_returns_decoder_error_internal() { - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(b"A", b"ABC").unwrap(); t.insert(b"B", b"ABCBA").unwrap(); } - let t = RefTrieDB::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); // query for an invalid data type to trigger an error let q = |x: &[u8]| x.len() < 64; - let lookup = RefLookup { db: t.db(), query: q, hash: root }; + let lookup = Lookup:: { db: t.db(), query: q, hash: root }; let query_result = lookup.look_up(NibbleSlice::new(b"A")); assert_eq!(query_result.unwrap().unwrap(), true); } diff --git a/trie-db/test/src/triedbmut.rs b/trie-db/test/src/triedbmut.rs index 4c3949d7..4e9f61d7 100644 --- a/trie-db/test/src/triedbmut.rs +++ b/trie-db/test/src/triedbmut.rs @@ -15,40 +15,27 @@ use env_logger; use trie_standardmap::*; use log::debug; -use trie_db::{DBValue, TrieMut, NodeCodec,}; use memory_db::{MemoryDB, PrefixedKey}; use hash_db::{Hasher, HashDB}; -use keccak_hasher::KeccakHasher; -use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMutAllowEmpty, RefTrieDBMut, - ReferenceNodeCodec, reference_trie_root, reference_trie_root_no_extension}; - -fn populate_trie<'db>( - db: &'db mut dyn HashDB, - root: &'db mut ::Out, +use trie_db::{TrieDBMut, TrieMut, NodeCodec, + TrieLayout, DBValue, Value, TrieError}; +use reference_trie::{ExtensionLayout, NoExtensionLayout, + RefHasher, test_layouts, ReferenceNodeCodec, HashedValueNoExt, HashedValueNoExtThreshold, + ReferenceNodeCodecNoExt, reference_trie_root_iter_build as reference_trie_root}; + +type PrefixedMemoryDB = MemoryDB::< + ::Hash, + PrefixedKey<::Hash>, + DBValue, +>; + +fn populate_trie<'db, T: TrieLayout>( + db: &'db mut dyn HashDB, + root: &'db mut ::Out, v: &[(Vec, Vec)] -) -> RefTrieDBMut<'db> { - let mut t = RefTrieDBMut::new(db, root); - for i in 0..v.len() { - let key: &[u8]= &v[i].0; - let val: &[u8] = &v[i].1; - t.insert(key, val).unwrap(); - } - t -} +) -> TrieDBMut<'db, T> { + let mut t = TrieDBMut::::new(db, root); -fn unpopulate_trie<'db>(t: &mut RefTrieDBMut<'db>, v: &[(Vec, Vec)]) { - for i in v { - let key: &[u8]= &i.0; - t.remove(key).unwrap(); - } -} - -fn populate_trie_no_extension<'db>( - db: &'db mut dyn HashDB, - root: &'db mut ::Out, - v: &[(Vec, Vec)] -) -> RefTrieDBMutNoExt<'db> { - let mut t = RefTrieDBMutNoExt::new(db, root); for i in 0..v.len() { let key: &[u8]= &v[i].0; let val: &[u8] = &v[i].1; @@ -57,68 +44,39 @@ fn populate_trie_no_extension<'db>( t } -fn unpopulate_trie_no_extension<'db>(t: &mut RefTrieDBMutNoExt<'db>, v: &[(Vec, Vec)]) { - for i in v { +fn unpopulate_trie<'db, T: TrieLayout>(t: &mut TrieDBMut<'db, T>, v: &[(Vec, Vec)]) -> bool { + for (_ix, i) in v.into_iter().enumerate() { let key: &[u8]= &i.0; - t.remove(key).unwrap(); + if t.remove(key).is_err() { + return false; + } } + true } -fn reference_hashed_null_node() -> ::Out { - as NodeCodec>::hashed_null_node() +fn reference_hashed_null_node() -> ::Out { + if T::USE_EXTENSION { + as NodeCodec>::hashed_null_node() + } else { + as NodeCodec>::hashed_null_node() + } } #[test] fn playpen() { env_logger::init(); - let mut seed = Default::default(); - for test_i in 0..10_000 { - if test_i % 50 == 0 { - debug!("{:?} of 10000 stress tests done", test_i); - } - let x = StandardMap { - alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), - min_key: 5, - journal_key: 0, - value_mode: ValueMode::Index, - count: 100, - }.make_with(&mut seed); - - let real = reference_trie_root(x.clone()); - let mut memdb = MemoryDB::, DBValue>::default(); - let mut root = Default::default(); - let mut memtrie = populate_trie(&mut memdb, &mut root, &x); - - memtrie.commit(); - if *memtrie.root() != real { - println!("TRIE MISMATCH"); - println!(); - println!("{:?} vs {:?}", memtrie.root(), real); - for i in &x { - println!("{:#x?} -> {:#x?}", i.0, i.1); - } - } - assert_eq!(*memtrie.root(), real); - unpopulate_trie(&mut memtrie, &x); - memtrie.commit(); - let hashed_null_node = reference_hashed_null_node(); - if *memtrie.root() != hashed_null_node { - println!("- TRIE MISMATCH"); - println!(); - println!("{:#x?} vs {:#x?}", memtrie.root(), hashed_null_node); - for i in &x { - println!("{:#x?} -> {:#x?}", i.0, i.1); - } - } - assert_eq!(*memtrie.root(), hashed_null_node); - } - - // no_extension - let mut seed = Default::default(); + playpen_internal::(); + playpen_internal::(); + playpen_internal::(); + playpen_internal::(); +} +fn playpen_internal() { + let mut seed = [0u8;32]; for test_i in 0..10_000 { if test_i % 50 == 0 { debug!("{:?} of 10000 stress tests done", test_i); } + let initial_seed = seed.clone(); let x = StandardMap { alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), min_key: 5, @@ -127,10 +85,10 @@ fn playpen() { count: 100, }.make_with(&mut seed); - let real = reference_trie_root_no_extension(x.clone()); - let mut memdb = MemoryDB::, DBValue>::default(); + let real = reference_trie_root::(x.clone()); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut memtrie = populate_trie_no_extension(&mut memdb, &mut root, &x); + let mut memtrie = populate_trie::(&mut memdb, &mut root, &x); memtrie.commit(); if *memtrie.root() != real { @@ -142,9 +100,9 @@ fn playpen() { } } assert_eq!(*memtrie.root(), real); - unpopulate_trie_no_extension(&mut memtrie, &x); + assert!(unpopulate_trie(&mut memtrie, &x), "{:?}", (test_i, initial_seed)); memtrie.commit(); - let hashed_null_node = reference_hashed_null_node(); + let hashed_null_node = reference_hashed_null_node::(); if *memtrie.root() != hashed_null_node { println!("- TRIE MISMATCH"); println!(); @@ -157,35 +115,35 @@ fn playpen() { } } -#[test] -fn init() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(init, init_internal); +fn init_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - let hashed_null_node = reference_hashed_null_node(); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); + let hashed_null_node = reference_hashed_null_node::(); assert_eq!(*t.root(), hashed_null_node); } -#[test] -fn insert_on_empty() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(insert_on_empty, insert_on_empty_internal); +fn insert_on_empty_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); assert_eq!( *t.root(), - reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ]), + reference_trie_root::(vec![(vec![0x01u8, 0x23], vec![0x01u8, 0x23])]), ); } -#[test] -fn remove_to_empty() { +test_layouts!(remove_to_empty, remove_to_empty_internal); +fn remove_to_empty_internal() { let big_value = b"00000000000000000000000000000000"; - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01], big_value).unwrap(); t.insert(&[0x01, 0x23], big_value).unwrap(); @@ -197,16 +155,45 @@ fn remove_to_empty() { assert_eq!(memdb.keys().len(), 0); } -#[test] -fn remove_to_empty_no_extension() { +test_layouts!(remove_to_empty_checked, remove_to_empty_checked_internal); +fn remove_to_empty_checked_internal() { + let big_value = b"00000000000000000000000000000000"; + + let mut memdb = PrefixedMemoryDB::::default(); + let mut root = Default::default(); + { + let mut t = TrieDBMut::::new(&mut memdb, &mut root); + + t.insert(&[0x01], big_value).unwrap(); + t.insert(&[0x01, 0x23], big_value).unwrap(); + t.insert(&[0x01, 0x34], big_value).unwrap(); + t.commit(); + assert_eq!( + t.get(&[0x01]).unwrap(), + Some(big_value.to_vec()), + ); + assert_eq!( + t.get(&[0x01, 0x34]).unwrap(), + Some(big_value.to_vec()), + ); + t.commit(); + t.remove(&[0x01]).unwrap(); + t.remove(&[0x01, 0x23]).unwrap(); + t.remove(&[0x01, 0x34]).unwrap(); + } + assert_eq!(memdb.keys().len(), 0); +} + +test_layouts!(remove_to_empty_no_extension, remove_to_empty_no_extension_internal); +fn remove_to_empty_no_extension_internal() { let big_value = b"00000000000000000000000000000000"; let big_value2 = b"00000000000000000000000000000002"; let big_value3 = b"00000000000000000000000000000004"; - let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01, 0x23], big_value3).unwrap(); t.insert(&[0x01], big_value2).unwrap(); @@ -214,149 +201,157 @@ fn remove_to_empty_no_extension() { t.remove(&[0x01]).unwrap(); // commit on drop } - assert_eq!(&root[..], &reference_trie::calc_root_no_extension(vec![ + assert_eq!(&root, &reference_trie::calc_root::(vec![ (vec![0x01u8, 0x23], big_value3.to_vec()), (vec![0x01u8, 0x34], big_value.to_vec()), - ])[..]); + ])); } -#[test] -fn insert_replace_root() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(insert_replace_root, insert_replace_root_internal); +fn insert_replace_root_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x23u8, 0x45]).unwrap(); assert_eq!( *t.root(), - reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ]), + reference_trie_root::(vec![(vec![0x01u8, 0x23], vec![0x23u8, 0x45])]), ); } -#[test] -fn insert_make_branch_root() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(insert_make_branch_root, insert_make_branch_root_internal); +fn insert_make_branch_root_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x11u8, 0x23], &[0x11u8, 0x23]).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root::(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), (vec![0x11u8, 0x23], vec![0x11u8, 0x23]) ])); } -#[test] -fn insert_into_branch_root() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(insert_into_branch_root, insert_into_branch_root_internal); +fn insert_into_branch_root_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root::(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), (vec![0x81u8, 0x23], vec![0x81u8, 0x23]), (vec![0xf1u8, 0x23], vec![0xf1u8, 0x23]), ])); } -#[test] -fn insert_value_into_branch_root() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(insert_value_into_branch_root, insert_value_into_branch_root_internal); +fn insert_value_into_branch_root_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[], &[0x0]).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root::(vec![ (vec![], vec![0x0]), (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), ])); } -#[test] -fn insert_split_leaf() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(insert_split_leaf, insert_split_leaf_internal); +fn insert_split_leaf_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x34], &[0x01u8, 0x34]).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root::(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), (vec![0x01u8, 0x34], vec![0x01u8, 0x34]), ])); } -#[test] -fn insert_split_extenstion() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(insert_split_extenstion, insert_split_extenstion_internal); +fn insert_split_extenstion_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01, 0x23, 0x45], &[0x01]).unwrap(); t.insert(&[0x01, 0xf3, 0x45], &[0x02]).unwrap(); t.insert(&[0x01, 0xf3, 0xf5], &[0x03]).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root::(vec![ (vec![0x01, 0x23, 0x45], vec![0x01]), (vec![0x01, 0xf3, 0x45], vec![0x02]), (vec![0x01, 0xf3, 0xf5], vec![0x03]), ])); } -#[test] -fn insert_big_value() { +test_layouts!(insert_big_value, insert_big_value_internal); +fn insert_big_value_internal() { let big_value0 = b"00000000000000000000000000000000"; let big_value1 = b"11111111111111111111111111111111"; - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], big_value0).unwrap(); t.insert(&[0x11u8, 0x23], big_value1).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root::(vec![ (vec![0x01u8, 0x23], big_value0.to_vec()), (vec![0x11u8, 0x23], big_value1.to_vec()) ])); } -#[test] -fn insert_duplicate_value() { +test_layouts!(insert_duplicate_value, insert_duplicate_value_internal); +fn insert_duplicate_value_internal() { let big_value = b"00000000000000000000000000000000"; - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], big_value).unwrap(); t.insert(&[0x11u8, 0x23], big_value).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root::(vec![ (vec![0x01u8, 0x23], big_value.to_vec()), (vec![0x11u8, 0x23], big_value.to_vec()) ])); } -#[test] -fn test_at_empty() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(test_at_empty, test_at_empty_internal); +fn test_at_empty_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let t = RefTrieDBMut::new(&mut memdb, &mut root); + let t = TrieDBMut::::new(&mut memdb, &mut root); assert_eq!(t.get(&[0x5]).unwrap(), None); } -#[test] -fn test_at_one() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(test_at_one_and_two, test_at_one_and_two_internal); +fn test_at_one_and_two_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); - assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), vec![0x1u8, 0x23]); - t.commit(); + { + let mut t = TrieDBMut::::new(&mut memdb, &mut root); + t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); + assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), vec![0x1u8, 0x23]); + t.commit(); + assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), vec![0x1u8, 0x23]); + t.insert(&[0x01u8, 0x23, 0x00], &[0x01u8, 0x24]).unwrap(); + } + let mut t = TrieDBMut::::from_existing(&mut memdb, &mut root).unwrap(); + t.insert(&[0x01u8, 0x23, 0x00], &[0x01u8, 0x25]).unwrap(); + // This test that middle node get resolved correctly (modified + // triedbmut node due to change of child node). assert_eq!(t.get(&[0x1, 0x23]).unwrap().unwrap(), vec![0x1u8, 0x23]); } -#[test] -fn test_at_three() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(test_at_three, test_at_three_internal); +fn test_at_three_internal() { + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); @@ -373,18 +368,18 @@ fn test_at_three() { #[test] fn test_nibbled_branch_changed_value() { - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23, 0x11], &[0xf1u8, 0x23]).unwrap(); assert_eq!(t.get(&[0x01u8, 0x23]).unwrap(), Some(vec![0x01u8, 0x23])); } -#[test] -fn stress() { +test_layouts!(stress, stress_internal); +fn stress_internal() { let mut seed = Default::default(); - for _ in 0..50 { + for _ in 0..1000 { let x = StandardMap { alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), min_key: 5, @@ -393,15 +388,15 @@ fn stress() { count: 4, }.make_with(&mut seed); - let real = reference_trie_root(x.clone()); - let mut memdb = MemoryDB::, DBValue>::default(); + let real = reference_trie_root::(x.clone()); + let mut memdb = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut memtrie = populate_trie(&mut memdb, &mut root, &x); + let mut memtrie = populate_trie::(&mut memdb, &mut root, &x); let mut y = x.clone(); y.sort_by(|ref a, ref b| a.0.cmp(&b.0)); - let mut memdb2 = MemoryDB::, DBValue>::default(); + let mut memdb2 = PrefixedMemoryDB::::default(); let mut root2 = Default::default(); - let mut memtrie_sorted = populate_trie(&mut memdb2, &mut root2, &y); + let mut memtrie_sorted = populate_trie::(&mut memdb2, &mut root2, &y); if *memtrie.root() != real || *memtrie_sorted.root() != real { println!("TRIE MISMATCH"); println!(); @@ -419,22 +414,22 @@ fn stress() { } } -#[test] -fn test_trie_existing() { - let mut db = MemoryDB::, DBValue>::default(); +test_layouts!(test_trie_existing, test_trie_existing_internal); +fn test_trie_existing_internal() { + let mut db = PrefixedMemoryDB::::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut db, &mut root); + let mut t = TrieDBMut::::new(&mut db, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } { - let _ = RefTrieDBMut::from_existing(&mut db, &mut root); + let _ = TrieDBMut::::from_existing(&mut db, &mut root); } } -#[test] -fn insert_empty_denied() { +test_layouts!(insert_empty, insert_empty_internal); +fn insert_empty_internal() { let mut seed = Default::default(); let x = StandardMap { alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), @@ -444,36 +439,27 @@ fn insert_empty_denied() { count: 4, }.make_with(&mut seed); - let mut db = MemoryDB::, DBValue>::default(); + let mut db = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut db, &mut root); + let mut t = TrieDBMut::::new(&mut db, &mut root); for &(ref key, ref value) in &x { t.insert(key, value).unwrap(); } - assert_eq!(*t.root(), reference_trie_root(x.clone())); + assert_eq!(*t.root(), reference_trie_root::(x.clone())); for &(ref key, _) in &x { t.insert(key, &[]).unwrap(); } assert!(t.is_empty()); - let hashed_null_node = reference_hashed_null_node(); + let hashed_null_node = reference_hashed_null_node::(); assert_eq!(*t.root(), hashed_null_node); } -#[test] -fn insert_empty_allowed() { - let mut db = MemoryDB::, DBValue>::default(); - let mut root = Default::default(); - let mut t = RefTrieDBMutAllowEmpty::new(&mut db, &mut root); - t.insert(b"test", &[]).unwrap(); - assert_eq!(*t.root(), reference_trie_root(vec![(b"test".to_vec(), Vec::new())])); - assert_eq!(t.get(b"test").unwrap(), Some(Vec::new())); -} - -#[test] -fn return_old_values() { +test_layouts!(return_old_values, return_old_values_internal); +fn return_old_values_internal() { + let threshold = T::MAX_INLINE_VALUE; let mut seed = Default::default(); let x = StandardMap { alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), @@ -483,15 +469,175 @@ fn return_old_values() { count: 2, }.make_with(&mut seed); - let mut db = MemoryDB::, DBValue>::default(); + let mut db = PrefixedMemoryDB::::default(); let mut root = Default::default(); - let mut t = RefTrieDBMut::new(&mut db, &mut root); + let mut t = TrieDBMut::::new(&mut db, &mut root); for &(ref key, ref value) in &x { - assert!(t.insert(key, value).unwrap().is_none()); - assert_eq!(t.insert(key, value).unwrap(), Some(value.clone())); + assert!(t.insert(key, value).unwrap() == None); + if threshold.map(|t| value.len() < t as usize).unwrap_or(true) { + assert_eq!(t.insert(key, value).unwrap(), Some(Value::Inline(value.clone()))); + } else { + assert!(matches!(t.insert(key, value).unwrap(), Some(Value::NewNode(..)))); + } } for (key, value) in x { - assert_eq!(t.remove(&key).unwrap(), Some(value)); - assert!(t.remove(&key).unwrap().is_none()); + if threshold.map(|t| value.len() < t as usize).unwrap_or(true) { + assert_eq!(t.remove(&key).unwrap(), Some(Value::Inline(value))); + } else { + assert!(matches!(t.remove(&key).unwrap(), Some(Value::NewNode(..)))); + } + assert_eq!(t.remove(&key).unwrap(), None); + } +} + +#[test] +fn insert_empty_allowed() { + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + let mut t = reference_trie::RefTrieDBMutAllowEmpty::new(&mut db, &mut root); + t.insert(b"test", &[]).unwrap(); + + assert_eq!(*t.root(), reference_trie_root::( + vec![(b"test".to_vec(), Vec::new())], + )); + assert_eq!(t.get(b"test").unwrap(), Some(Vec::new())); +} + +#[test] +fn register_proof_without_value() { + use trie_db::TrieDB; + use std::collections::HashMap; + use std::cell::RefCell; + use reference_trie::HashedValueNoExtThreshold; + use hash_db::{Prefix, AsHashDB}; + + type Layout = HashedValueNoExtThreshold; + type MemoryDB = memory_db::MemoryDB< + RefHasher, + PrefixedKey, + DBValue, + >; + let x = [ + (b"test1".to_vec(), vec![1;32]), // inline + (b"test1234".to_vec(), vec![2;36]), + (b"te".to_vec(), vec![3;32]), + ]; + + let mut memdb = MemoryDB::default(); + let mut root = Default::default(); + let _ = populate_trie::(&mut memdb, &mut root, &x); + { + let trie = TrieDB::::new(&memdb, &root).unwrap(); + println!("{:?}", trie); + } + + struct ProofRecorder { + db: MemoryDB, + record: RefCell, Vec>>, + } + // Only to test without threads. + unsafe impl Send for ProofRecorder { } + unsafe impl Sync for ProofRecorder { } + + impl HashDB for ProofRecorder { + fn get(&self, key: &::Out, prefix: Prefix) -> Option { + let v = self.db.get(key, prefix); + if let Some(v) = v.as_ref() { + self.record.borrow_mut() + .entry(key[..].to_vec()) + .or_insert_with(|| v.clone()); + } + v + } + + fn contains(&self, key: &::Out, prefix: Prefix) -> bool { + self.get(key, prefix).is_some() + } + + fn emplace(&mut self, key: ::Out, prefix: Prefix, value: DBValue) { + self.db.emplace(key, prefix, value) + } + + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> ::Out { + self.db.insert(prefix, value) + } + + fn remove(&mut self, key: &::Out, prefix: Prefix) { + self.db.remove(key, prefix) + } + } + + impl AsHashDB for ProofRecorder { + fn as_hash_db(&self) -> &dyn HashDB { + self + } + fn as_hash_db_mut<'a>(&'a mut self) -> &'a mut (dyn HashDB + 'a) { + self + } + } + + let mut memdb = ProofRecorder { + db: memdb, + record: Default::default(), + }; + + let root_proof = root.clone(); + { + let mut trie = TrieDBMut::::from_existing(&mut memdb, &mut root) + .unwrap(); + // touch te value (test1 remains untouch). + trie.get(b"te").unwrap(); + // cut test_1234 prefix + trie.insert(b"test12", &[2u8;36][..]).unwrap(); + // remove 1234 + trie.remove(b"test1234").unwrap(); + + // proof should contain value for 'te' only. + } + + type MemoryDBProof = memory_db::MemoryDB< + RefHasher, + memory_db::HashKey, + DBValue, + >; + let mut memdb_from_proof = MemoryDBProof::default(); + for (_key, value) in memdb.record.into_inner().into_iter() { + memdb_from_proof.insert( + hash_db::EMPTY_PREFIX, + value.as_slice(), + ); + } + + let db_unpacked = memdb_from_proof.clone(); + let root_unpacked = root_proof.clone(); + + let mut memdb_from_proof = db_unpacked.clone(); + let mut root_proof = root_unpacked.clone(); + { + let mut trie = TrieDBMut::::from_existing( + &mut memdb_from_proof, + &mut root_proof, + ).unwrap(); + trie.get(b"te").unwrap(); + trie.insert(b"test12", &[2u8;36][..]).unwrap(); + trie.remove(b"test1234").unwrap(); + } + + let mut memdb_from_proof = db_unpacked.clone(); + let mut root_proof = root_unpacked.clone(); + { + use trie_db::Trie; + let trie = TrieDB::::new(&memdb_from_proof, &root_proof).unwrap(); + assert!(trie.get(b"te").unwrap().is_some()); + assert!(matches!(trie.get(b"test1").map_err(|e| *e), Err(TrieError::IncompleteDatabase(..)))); + } + + { + let trie = TrieDBMut::::from_existing( + &mut memdb_from_proof, + &mut root_proof, + ).unwrap(); + assert!(trie.get(b"te").unwrap().is_some()); + assert!(matches!(trie.get(b"test1").map_err(|e| *e), Err(TrieError::IncompleteDatabase(..)))); } } diff --git a/trie-root/CHANGELOG.md b/trie-root/CHANGELOG.md index 7f8c5594..5ed2e4dd 100644 --- a/trie-root/CHANGELOG.md +++ b/trie-root/CHANGELOG.md @@ -6,5 +6,8 @@ The format is based on [Keep a Changelog]. ## [Unreleased] -## [0.20.0] - 2020-02-07 +## [0.17.0] - 2021-10-19 +- Support for value nodes. [#142](https://github.com/paritytech/trie/pull/142) + +## [0.16.0] - 2020-02-07 - Update reference-trie to v0.20.0 [#78](https://github.com/paritytech/trie/pull/78) diff --git a/trie-root/Cargo.toml b/trie-root/Cargo.toml index 3664a9c4..126ff87a 100644 --- a/trie-root/Cargo.toml +++ b/trie-root/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trie-root" -version = "0.16.0" +version = "0.17.0" authors = ["Parity Technologies "] description = "In-memory patricia trie operations" repository = "https://github.com/paritytech/trie" diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index ece22e7b..3f4175ab 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -40,6 +40,29 @@ use self::rstd::*; pub use hash_db::Hasher; +/// Different possible value to use for node encoding. +#[derive(Clone)] +pub enum Value<'a> { + /// Contains a full value. + Inline(&'a [u8]), + /// Contains hash of a value. + Node(Vec), +} + +impl<'a> Value<'a> { + fn new(value: &'a [u8], threshold: Option) -> Value<'a> { + if let Some(threshold) = threshold { + if value.len() >= threshold as usize { + Value::Node(H::hash(value).as_ref().to_vec()) + } else { + Value::Inline(value) + } + } else { + Value::Inline(value) + } + } +} + /// Byte-stream oriented trait for constructing closed-form tries. pub trait TrieStream { /// Construct a new `TrieStream` @@ -51,16 +74,16 @@ pub trait TrieStream { fn begin_branch( &mut self, maybe_key: Option<&[u8]>, - maybe_value: Option<&[u8]>, + maybe_value: Option, has_children: impl Iterator, ); /// Append an empty child node. Optional. fn append_empty_child(&mut self) {} /// Wrap up a Branch node portion of a `TrieStream` and append the value /// stored on the Branch (if any). - fn end_branch(&mut self, _value: Option<&[u8]>) {} + fn end_branch(&mut self, _value: Option) {} /// Append a Leaf node - fn append_leaf(&mut self, key: &[u8], value: &[u8]); + fn append_leaf(&mut self, key: &[u8], value: Value); /// Append an Extension node fn append_extension(&mut self, key: &[u8]); /// Append a Branch of Extension substream @@ -93,17 +116,17 @@ fn shared_prefix_length(first: &[T], second: &[T]) -> usize { /// let root = hex!["0807d5393ae7f349481063ebb5dbaf6bda58db282a385ca97f37dccba717cb79"]; /// assert_eq!(trie_root::(v), root); /// ``` -pub fn trie_root(input: I) -> H::Out where +pub fn trie_root(input: I, threshold: Option) -> H::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, S: TrieStream, { - trie_root_inner::(input, false) + trie_root_inner::(input, false, threshold) } -fn trie_root_inner(input: I, no_extension: bool) -> H::Out where +fn trie_root_inner(input: I, no_extension: bool, threshold: Option) -> H::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, @@ -133,37 +156,37 @@ fn trie_root_inner(input: I, no_extension: bool) -> H::Out where .collect::>(); let mut stream = S::new(); - build_trie::(&input, 0, &mut stream, no_extension); + build_trie::(&input, 0, &mut stream, no_extension, threshold); H::hash(&stream.out()) } /// Variant of `trie_root` for patricia trie without extension node. /// See [`trie_root`]. -pub fn trie_root_no_extension(input: I) -> H::Out where +pub fn trie_root_no_extension(input: I, threshold: Option) -> H::Out where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, S: TrieStream, { - trie_root_inner::(input, true) + trie_root_inner::(input, true, threshold) } //#[cfg(test)] // consider feature="std" /// Method similar to `trie_root` but returning the root encoded /// node instead of its hash. /// Mainly use for testing or debugging. -pub fn unhashed_trie(input: I) -> Vec where +pub fn unhashed_trie(input: I, threshold: Option) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, S: TrieStream, { - unhashed_trie_inner::(input, false) + unhashed_trie_inner::(input, false, threshold) } -fn unhashed_trie_inner(input: I, no_extension: bool) -> Vec where +fn unhashed_trie_inner(input: I, no_extension: bool, threshold: Option) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, @@ -192,20 +215,20 @@ fn unhashed_trie_inner(input: I, no_extension: bool) -> Vec w .collect::>(); let mut stream = S::new(); - build_trie::(&input, 0, &mut stream, no_extension); + build_trie::(&input, 0, &mut stream, no_extension, threshold); stream.out() } /// Variant of `unhashed_trie` for patricia trie without extension node. /// See [`unhashed_trie`]. -pub fn unhashed_trie_no_extension(input: I) -> Vec where +pub fn unhashed_trie_no_extension(input: I, threshold: Option) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, S: TrieStream, { - unhashed_trie_inner::(input, true) + unhashed_trie_inner::(input, true, threshold) } /// Generates a key-hashed (secure) trie root hash for a vector of key-value tuples. @@ -225,7 +248,7 @@ pub fn unhashed_trie_no_extension(input: I) -> Vec where /// let root = hex!["d6e02b2bd48aa04fd2ad87cfac1144a29ca7f7dc60f4526c7b7040763abe3d43"]; /// assert_eq!(sec_trie_root::(v), root); /// ``` -pub fn sec_trie_root(input: I) -> H::Out where +pub fn sec_trie_root(input: I, threshold: Option) -> H::Out where I: IntoIterator, A: AsRef<[u8]>, B: AsRef<[u8]>, @@ -233,12 +256,12 @@ pub fn sec_trie_root(input: I) -> H::Out where H::Out: Ord, S: TrieStream, { - trie_root::(input.into_iter().map(|(k, v)| (H::hash(k.as_ref()), v))) + trie_root::(input.into_iter().map(|(k, v)| (H::hash(k.as_ref()), v)), threshold) } /// Takes a slice of key/value tuples where the key is a slice of nibbles /// and encodes it into the provided `Stream`. -fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_extension: bool) where +fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_extension: bool, threshold: Option) where A: AsRef<[u8]>, B: AsRef<[u8]>, H: Hasher, @@ -248,7 +271,10 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex // No input, just append empty data. 0 => stream.append_empty_data(), // Leaf node; append the remainder of the key and the value. Done. - 1 => stream.append_leaf(&input[0].0.as_ref()[cursor..], &input[0].1.as_ref() ), + 1 => { + let value = Value::new::(input[0].1.as_ref(), threshold); + stream.append_leaf(&input[0].0.as_ref()[cursor..], value ) + }, // We have multiple items in the input. Figure out if we should add an // extension node or a branch node. _ => { @@ -276,6 +302,7 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex shared_nibble_count, stream, no_extension, + threshold, ); return; } else { (cursor, None) }; @@ -304,7 +331,8 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex } // Put out the node header: - stream.begin_branch(o_branch_slice, value, shared_nibble_counts.iter().map(|&n| n > 0)); + let value = value.map(|v| Value::new::(v, threshold)); + stream.begin_branch(o_branch_slice, value.clone(), shared_nibble_counts.iter().map(|&n| n > 0)); // Fill in each slot in the branch node. We don't need to bother with empty slots // since they were registered in the header. @@ -316,6 +344,7 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_ex cursor + 1, stream, no_extension, + threshold.clone(), ); begin += count; } else { @@ -333,6 +362,7 @@ fn build_trie_trampoline( cursor: usize, stream: &mut S, no_extension: bool, + threshold: Option, ) where A: AsRef<[u8]>, B: AsRef<[u8]>, @@ -340,6 +370,6 @@ fn build_trie_trampoline( S: TrieStream, { let mut substream = S::new(); - build_trie::(input, cursor, &mut substream, no_extension); + build_trie::(input, cursor, &mut substream, no_extension, threshold); stream.append_substream::(substream); } diff --git a/trie-root/test/Cargo.toml b/trie-root/test/Cargo.toml index 21f806d8..2276a5be 100644 --- a/trie-root/test/Cargo.toml +++ b/trie-root/test/Cargo.toml @@ -9,9 +9,9 @@ categories = [ ] edition = "2018" [dependencies] -trie-root = { path = "..", version = "0.16.0" } +trie-root = { path = "..", version = "0.17.0" } hash-db = { path = "../../hash-db", version = "0.15.2" } hex-literal = "0.3" keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.2" } trie-standardmap = { path = "../../test-support/trie-standardmap", version = "0.15.2" } -reference-trie = { path = "../../test-support/reference-trie", version = "0.23.0" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.24.0" } diff --git a/trie-root/test/src/lib.rs b/trie-root/test/src/lib.rs index b4f4c98e..95c67f3d 100644 --- a/trie-root/test/src/lib.rs +++ b/trie-root/test/src/lib.rs @@ -30,7 +30,7 @@ mod test { ]; let root = hex!["d6e02b2bd48aa04fd2ad87cfac1144a29ca7f7dc60f4526c7b7040763abe3d43"]; - assert_eq!(sec_trie_root::(v), root); + assert_eq!(sec_trie_root::(v, Default::default()), root); } #[test] @@ -42,6 +42,6 @@ mod test { ]; let root = hex!["0807d5393ae7f349481063ebb5dbaf6bda58db282a385ca97f37dccba717cb79"]; - assert_eq!(trie_root::(v), root); + assert_eq!(trie_root::(v, Default::default()), root); } }