diff --git a/Cargo.toml b/Cargo.toml index c2a1e083..bb252db9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "hash-db", "memory-db", "hash256-std-hasher", + "ordered-trie", "test-support/keccak-hasher", "test-support/reference-trie", "test-support/trie-standardmap", diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index 249ae4eb..0603b65b 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -64,6 +64,48 @@ pub trait Hasher: Sync + Send { fn hash(x: &[u8]) -> Self::Out; } +/// Small trait for to allow using buffer of type [u8; H::LENGTH * 2]. +pub trait BinaryHasher: Hasher { + /// Hash for the empty content (is hash(&[])). + const NULL_HASH: &'static [u8]; + + /// State buffer for hashing. + type Buffer; + + fn init_buffer() -> Self::Buffer; + fn reset_buffer(buf: &mut Self::Buffer); + fn buffer_hash(buff: &mut Self::Buffer, x: &[u8]); + + /// After calling `buffer_finalize`, one do not have to call `reset_buffer`. + fn buffer_finalize(buff: &mut Self::Buffer) -> Self::Out; +} + +#[cfg(feature = "std")] +/// Test function to use on any binary buffer implementation. +pub fn test_binary_hasher() { + let size = ::LENGTH * 2; + let half_size = ::LENGTH / 2; + let mut val = vec![0u8; size]; + val[0] = 1; + let mut buf = ::init_buffer(); + H::buffer_hash(&mut buf, &val[..half_size]); + H::buffer_hash(&mut buf, &val[half_size..::LENGTH]); + let three = core::cmp::min(3, half_size); + H::buffer_hash(&mut buf, &val[::LENGTH..::LENGTH + three]); + H::buffer_hash(&mut buf, &val[::LENGTH + three..]); + let h = H::buffer_finalize(&mut buf); + let h2 = H::hash(&val[..]); + assert_eq!(h, h2); + H::buffer_hash(&mut buf, &val[..]); + let h = H::buffer_finalize(&mut buf); + assert_eq!(h, h2); + let null_hash = H::hash(&[]); + H::reset_buffer(&mut buf); + let null_hash2 = H::buffer_finalize(&mut buf); + assert_eq!(H::NULL_HASH, null_hash.as_ref()); + assert_eq!(H::NULL_HASH, null_hash2.as_ref()); +} + /// Trait modelling a plain datastore whose key is a fixed type. /// The caller should ensure that a key only corresponds to /// one value. @@ -185,3 +227,80 @@ impl<'a, K, V> AsPlainDB for &'a mut dyn PlainDB { fn as_plain_db(&self) -> &dyn PlainDB { &**self } fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (dyn PlainDB + 'b) { &mut **self } } + +/// Same as HashDB but can modify the value upon storage, and apply +/// `HasherHybrid`. +pub trait HashDBHybrid: Send + Sync + HashDB { + /// `HashDB` is often use to load content from encoded node. + /// This will not preserve insertion done through `insert_branch_hybrid` calls + /// and break the proof. + /// This function allows to use a callback (usually the call back + /// will check the encoded value with codec and for branch it will + /// emplace over the hash_hybrid key) for changing key of some content. + /// Callback is allow to fail (since it will decode some node this indicates + /// invalid content earlier), in this case we return false. + fn insert_hybrid( + &mut self, + prefix: Prefix, + value: &[u8], + callback: fn(&[u8]) -> core::result::Result, ()>, + ) -> bool; + + /// Insert a datum item into the DB and return the datum's hash for a later lookup. Insertions + /// are counted and the equivalent number of `remove()`s must be performed before the data + /// is considered dead. + fn insert_branch_hybrid< + I: Iterator>, + >( + &mut self, + prefix: Prefix, + value: &[u8], + no_child_value: &[u8], + nb_children: usize, + children: I, + buffer: &mut ::Buffer, + ) -> H::Out; + + /// Insert with data from a proof. + /// As a result, this function can fail. + fn insert_branch_hybrid_proof< + I: Iterator>, + I2: Iterator, + >( + &mut self, + prefix: Prefix, + value: &[u8], + no_child_value: &[u8], + nb_children: usize, + children: I, + additional_hashes: I2, + buffer: &mut ::Buffer, + ) -> Option; +} + +pub trait HasherHybrid: BinaryHasher { + type InnerHasher: BinaryHasher; + + /// Alternate hash with hybrid hashing allowed. + fn hash_hybrid< + I: Iterator::Out>>, + >( + encoded_node: &[u8], + nb_children: usize, + children: I, + buffer: &mut ::Buffer, + ) -> Self::Out; + + /// Calculate hash from a proof, this can fail. + fn hash_hybrid_proof< + I: Iterator::Out>>, + I2: Iterator::Out>, + >( + x: &[u8], + nb_children: usize, + children: I, + additional_hashes: I2, + buffer: &mut ::Buffer, + ) -> Option; + +} diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 31b97f51..ad78f891 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -23,7 +23,7 @@ mod malloc_size_of; pub use malloc_size_of::*; use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef, Hasher as KeyHasher, - AsHashDB, AsPlainDB, Prefix}; + AsHashDB, AsPlainDB, Prefix, HashDBHybrid, HasherHybrid, BinaryHasher}; use parity_util_mem::{MallocSizeOf, MallocSizeOfOps, MallocShallowSizeOf}; #[cfg(feature = "std")] use std::{ @@ -34,6 +34,7 @@ use std::{ marker::PhantomData, cmp::Eq, borrow::Borrow, + result, }; #[cfg(not(feature = "std"))] @@ -49,6 +50,8 @@ use core::{ marker::PhantomData, cmp::Eq, borrow::Borrow, + ops::Range, + result, }; #[cfg(not(feature = "std"))] @@ -625,6 +628,86 @@ where } } +impl HashDBHybrid for MemoryDB +where + H: HasherHybrid, + T: Default + PartialEq + for<'a> From<&'a [u8]> + Clone + Send + Sync, + KF: Send + Sync + KeyFunction, + M: MemTracker + Send + Sync, +{ + fn insert_hybrid( + &mut self, + prefix: Prefix, + value: &[u8], + callback: fn(&[u8]) -> result::Result, ()>, + ) -> bool { + if let Ok(result) = callback(value) { + if let Some(key) = result { + >::emplace(self, key, prefix, T::from(value)); + } else { + >::insert(self, prefix, value); + } + true + } else { + false + } + } + + fn insert_branch_hybrid< + I: Iterator>, + > ( + &mut self, + prefix: Prefix, + value: &[u8], + child_proof_header: &[u8], + nb_children: usize, + children: I, + buff: &mut ::Buffer, + ) -> H::Out { + if T::from(value) == self.null_node_data { + return self.hashed_null_node.clone(); + } + + let key = H::hash_hybrid( + child_proof_header, + nb_children, + children, + buff, + ); + HashDB::emplace(self, key, prefix, value.into()); + key + } + + fn insert_branch_hybrid_proof< + I: Iterator>, + I2: Iterator, + > ( + &mut self, + prefix: Prefix, + value: &[u8], + child_proof_header: &[u8], + nb_children: usize, + children: I, + additional_hashes: I2, + buff: &mut ::Buffer, + ) -> Option { + if T::from(value) == self.null_node_data { + return Some(self.hashed_null_node.clone()); + } + + H::hash_hybrid_proof( + child_proof_header, + nb_children, + children, + additional_hashes, + buff, + ).map(|key| { + HashDB::emplace(self, key, prefix, value.into()); + key + }) + } +} + impl HashDBRef for MemoryDB where H: KeyHasher, diff --git a/ordered-trie/Cargo.toml b/ordered-trie/Cargo.toml new file mode 100644 index 00000000..74b18f94 --- /dev/null +++ b/ordered-trie/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "ordered-trie" +version = "0.19.2" +authors = ["Parity Technologies "] +description = "Binary tree generic for ordered values (stack or fifo)" +repository = "https://github.com/paritytech/trie" +license = "Apache-2.0" +edition = "2018" + +[dependencies] +hash-db = { path = "../hash-db", default-features = false, version = "0.15.2"} +smallvec = "1.0.0" + +[dev-dependencies] +keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.15.2"} +#criterion = "0.2.8" + + +[features] +default = ["std"] +std = [ + "hash-db/std", +] diff --git a/ordered-trie/src/lib.rs b/ordered-trie/src/lib.rs new file mode 100644 index 00000000..bc4e136b --- /dev/null +++ b/ordered-trie/src/lib.rs @@ -0,0 +1,1368 @@ +// Copyright 2020 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. + +#![cfg_attr(not(feature = "std"), no_std)] + +//! This crate contains implementation of trie/tree based on ordered sequential key only. +//! +//! Current use case is a fixed length tree. + +#[cfg(not(feature = "std"))] +extern crate alloc; + +#[cfg(feature = "std")] +mod rstd { + pub use std::{borrow, boxed, cmp, convert, fmt, hash, iter, marker, mem, ops, rc, result, vec}; + pub use std::collections::VecDeque; + pub use std::collections::BTreeMap; + pub use std::error::Error; +} + +#[cfg(not(feature = "std"))] +mod rstd { + pub use core::{borrow, convert, cmp, iter, fmt, hash, marker, mem, ops, result}; + pub use alloc::{boxed, rc, vec}; + pub use alloc::collections::VecDeque; + pub trait Error {} + impl Error for T {} +} + + +use crate::rstd::vec::Vec; + +use hash_db::{Hasher, HasherHybrid, BinaryHasher}; +use crate::rstd::marker::PhantomData; + + +pub type DBValue = Vec; + +#[derive(PartialEq, Eq, Debug)] +/// A binary trie with guaranties +/// of content being in a fix range +/// of sequential values. +pub struct SequenceBinaryTree { + // Metadata (needs to be validated) + + /// Nb deleted values at end. + end: K, + end_depth: usize, + + // depth of the full tree (maximum depth) + depth: usize, + // memmoïze 2^depth + length: K, + + _ph: PhantomData, +} + +impl Default for SequenceBinaryTree { + fn default() -> Self { + SequenceBinaryTree { + end: 0, + end_depth: 0, + depth: 0, + length: 0, + _ph: PhantomData, + } + } +} + +fn depth(nb: usize) -> usize { + (0usize.leading_zeros() - nb.leading_zeros()) as usize +} + +fn right_at(value: usize, index: usize) -> bool { + value & (1 << index) != 0 +} + +impl SequenceBinaryTree { + pub fn new(number: usize) -> Self { + let len = number; + if len == 0 { + SequenceBinaryTree { + end: 0, + end_depth: 0, + depth: 0, + length: 0, + _ph: PhantomData, + } + } else { + let length = len.next_power_of_two(); + let end = length - number; + let end_depth = depth(end); + let depth = depth(length - 1); + SequenceBinaryTree { + end, + end_depth, + depth, + length, + _ph: PhantomData, + } + } + } + + fn nb_elements(&self) -> usize { + self.length - self.end + } + + #[cfg(test)] + fn resize_append(&mut self, mut nb: usize) { + if nb == 0 { + return; + } + if self.length == 0 { + *self = Self::new(nb); + return; + } + while nb > self.end { + nb -= self.end; + self.depth += 1; + if self.length == 0 { + self.length += 1; + self.end = 1; + } else { + self.end = self.length; + self.length *= 2; + } + } + self.end -= nb; + self.end_depth = depth(self.end); + } + + fn depth_index(&self, index: usize) -> usize { + let tmp = (!0usize << (self.depth - self.end_depth)) | (index >> self.end_depth); + if !tmp == 0 { + let mut nb_skip = 0; + for i in 0..self.end_depth { + // -1 related to redundancy of first level of main depth (depth in number of change of level) + let ix = self.end_depth - i - 1; + if self.end & (1 << ix) != 0 { + // this is a skip + nb_skip += 1; + } else { + // continue only if right (like first if condition) + if index & (1 << ix) == 0 { + break; + } + } + } + self.depth - nb_skip + } else { + self.depth + } + } + + /// Resolve the tree path for a given index. + pub fn path_node_key>(&self, index: usize) -> KN { + let tmp = (!0usize << (self.depth - self.end_depth)) | (index >> self.end_depth); + if !tmp == 0 { + let mut result: KN = (index, self.depth).into(); + for i in 0..self.end_depth { + // -1 related to redundancy of first level of main depth (depth in number of change of level) + let ix = self.end_depth - i - 1; + if self.end & (1 << ix) != 0 { + // this is a skip + let ix = result.depth() - ix - 1; + result.remove_at(ix); + } else { + // continue only if right (like first if condition) + if index & (1 << ix) == 0 { + break; + } + } + } + result + } else { + (index, self.depth).into() + } + } + + pub fn iter_depth(&self, from: Option) -> impl Iterator { + if let Some(_from) = from { + unimplemented!(); + } + let nb_elements = self.nb_elements(); + let mut index = 0; + let mut depth = self.depth; + let length = self.length; + let mut end = UsizeKeyNode::from((self.end, self.end_depth)); + let mut next_skip = length - if end.depth > 0 { + 1usize << end.depth // two time deletion range + } else { + 0 + }; + crate::rstd::iter::from_fn(move || { + if index < nb_elements { + if index == next_skip { + while end.pop_front() == Some(true) { + depth -= 1; + } + while end.nibble_at(0) == Some(false) { + end.pop_front(); + } + if end.depth > 0 { + next_skip += 1usize << end.depth + } + } + index += 1; + Some(depth) + } else { + None + } + }) + } + + pub fn iter_path_node_key(&self) -> impl Iterator + where + KN: KeyNode + From<(usize, usize)> + Clone, + { + let nb_elements = self.nb_elements(); + let mut index = 0; + let length = self.length; + let mut end = KN::from((self.end, self.end_depth)); + let mut next_skip = length - if end.depth() > 0 { + 1usize << end.depth() // two time deletion range + } else { + 0 + }; + let mut key: KN = (0, self.depth).into(); + crate::rstd::iter::from_fn(move || { + if index < nb_elements { + if index == next_skip { + while end.pop_front() == Some(true) { + let ix = key.depth() - end.depth() - 1; + key.remove_at(ix); + } + while end.nibble_at(0) == Some(false) { + end.pop_front(); + } + if end.depth() > 0 { + next_skip += 1usize << end.depth() + } + } + let result = key.clone(); + key.increment_no_increase(); + index += 1; + Some(result) + } else { + None + } + }) + } +} + +/// key of node is a sequence of one bit nibbles. +/// This do not implement any key alignment logic, +/// using it for the sequence trie should always +/// use `iter_path_node_key` or `path_node_key` +/// function to instantiate. +pub trait KeyNode { + fn depth(&self) -> usize; + // return nibble at depth (bin tree so return bool) + fn nibble_at(&self, depth: usize) -> Option; + // last is leaf + fn pop_back(&mut self) -> Option; + fn push_back(&mut self, nibble: bool); + fn pop_front(&mut self) -> Option; + fn push_front(&mut self, nibble: bool); + fn remove_at(&mut self, depth: usize); + fn increment_no_increase(&mut self); + fn starts_with(&self, other: &Self) -> bool; + fn common_depth(&self, other: &Self) -> usize; +} + +#[cfg(test)] +mod vec_keynode { + use crate::*; + + #[derive(Clone, Debug)] + // please do not use, only for test of (usize, K) + pub(crate) struct VecKeyNode(pub(crate) std::collections::VecDeque); + + impl KeyNode for VecKeyNode { + fn increment_no_increase(&mut self) { + for i in (0..self.0.len()).rev() { + match self.0.get_mut(i) { + Some(v) => { + if !*v { + *v = true; + break; + } + }, + None => { + unreachable!("should only be call when guaranties to not increase depth"); + }, + } + } + } + fn depth(&self) -> usize { + self.0.len() + } + + fn nibble_at(&self, depth: usize) -> Option { + self.0.get(depth).cloned() + } + fn pop_back(&mut self) -> Option { + self.0.pop_back() + } + fn push_back(&mut self, nibble: bool) { + self.0.push_back(nibble) + } + fn pop_front(&mut self) -> Option { + self.0.pop_front() + } + fn push_front(&mut self, nibble: bool) { + self.0.push_front(nibble) + } + fn remove_at(&mut self, index: usize) { + self.0.remove(index); + } + fn starts_with(&self, other: &Self) -> bool { + // clone but it is test method only. + let mut tr = self.0.clone(); + tr.truncate(other.0.len()); + tr == other.0 + } + fn common_depth(&self, other: &Self) -> usize { + let bound = rstd::cmp::min(self.0.len(), other.0.len()); + let mut depth = 0; + for i in 0..bound { + if self.0[i] == other.0[i] { + depth += 1; + } else { + break; + } + } + depth + } + } + + impl From<(usize, usize)> for VecKeyNode { + fn from((key, depth): (usize, usize)) -> Self { + if depth == 0 { + return VecKeyNode(std::collections::VecDeque::new()); + } + VecKeyNode( + (1..=depth).map(|i| crate::right_at(key, depth - i)).collect() + ) + } + } + + impl Into for VecKeyNode { + fn into(self) -> usize { + let mut result = 0; + let depth = self.depth(); + self.0.into_iter().enumerate().for_each(|(i, b)| if b { + result = result | (1 << depth - (i + 1)); + }); + result + } + } +} + +#[derive(Clone, Copy, Debug)] +pub struct UsizeKeyNode { + value: usize, + depth: usize, +} + +// first is len, second is key +impl KeyNode for UsizeKeyNode { + fn depth(&self) -> usize { + self.depth + } + fn increment_no_increase(&mut self) { + self.value += 1; + } + fn nibble_at(&self, depth: usize) -> Option { + if depth < self.depth { + Some(right_at(self.value, self.depth - 1 - depth)) + } else { + None + } + } + fn pop_back(&mut self) -> Option { + if self.depth == 0 { + return None; + } + let result = self.value & 1; + self.depth -= 1; + self.value = self.value >> 1; + Some(result != 0) + } + fn push_back(&mut self, nibble: bool) { + self.value = self.value << 1; + self.value = self.value | (nibble as usize); + self.depth +=1; + } + fn pop_front(&mut self) -> Option { + if self.depth == 0 { + return None; + } + let result = self.value & (1 << (self.depth - 1)); + self.value = self.value & !(1 << (self.depth - 1)); + self.depth -= 1; + Some(result != 0) + } + fn push_front(&mut self, nibble: bool) { + self.depth += 1; + self.value = self.value | ((nibble as usize) << (self.depth - 1)); + } + + fn remove_at(&mut self, index: usize) { + if index >= self.depth { + return; + } + if index == 0 { + self.pop_front(); + return; + } + if index == self.depth - 1 { + self.pop_back(); + return; + } + let right = self.value & !(!0usize << self.depth - index); + self.value = self.value & (!0usize << self.depth - index); + self.value = self.value >> 1; + self.depth -= 1; + self.value = self.value | right; + } + + fn starts_with(&self, other: &Self) -> bool { + if self.depth < other.depth { + false + } else { + self.value >> (self.depth - other.depth) == other.value + } + } + fn common_depth(&self, other: &Self) -> usize { +/* let bound = crate::rstd::cmp::min(self.depth, other.depth); + let mut depth = 0; + for i in 0..bound { + if self.nibble_at(i) == other.nibble_at(i) { + depth += 1; + } else { + break; + } + } + depth*/ + let (big, small) = if self.depth < other.depth { + (other, self) + } else { + (self, other) + }; + // end is not common + let big_v = big.value >> (big.depth - small.depth); + let diff = big_v ^ small.value; + small.depth - (0usize.leading_zeros() - diff.leading_zeros()) as usize + } +} + +impl From<(usize, usize)> for UsizeKeyNode { + fn from((value, depth): (usize, usize)) -> Self { + // let value = value & !((!0) << depth); + UsizeKeyNode { value, depth } + } +} + +impl Into for UsizeKeyNode { + fn into(self) -> usize { + self.value + } +} + +pub trait ProcessNode { + /// Callback for an empty trie, return byte representation + /// of the hash for the empty trie. + fn process_empty_trie(&mut self) -> &[u8]; + /// Process two child node to produce the parent one. + fn process(&mut self, key: &KN, child1: &[u8], child2: &[u8]) -> HO; + /// callback on the calculated root. + fn register_root(&mut self, root: &HO); +} + +pub trait ProcessNodeProof: ProcessNode { + fn register_proof_hash(&mut self, hash: &HO); +} + +/// Does only proccess hash on its buffer. +/// Correct buffer size is expected, this is unchecked. +pub struct HashOnly<'a, H: BinaryHasher>(&'a mut H::Buffer); + +impl<'a, H: BinaryHasher> HashOnly<'a, H> { + pub fn new(buff: &'a mut H::Buffer) -> Self { + HashOnly(buff) + } +} + +impl<'a, H: BinaryHasher, KN> ProcessNode for HashOnly<'a, H> { + fn process_empty_trie(&mut self) -> &[u8] { + H::NULL_HASH + } + fn process(&mut self, _key: &KN, child1: &[u8], child2: &[u8]) -> H::Out { + H::reset_buffer(&mut self.0); + H::buffer_hash(&mut self.0, child1); + H::buffer_hash(&mut self.0, child2); + H::buffer_finalize(&mut self.0) + } + fn register_root(&mut self, _root: &H::Out) { } +} + +pub struct HashProof<'a, H: BinaryHasher, I, KN> { + buffer: &'a mut H::Buffer, + // `I` is a iterator over the depth of every element included in the proof. + // Can be obtain from `iter_path_node_key` filtered by contained elements. + to_prove: I, + state: MultiProofState, + additional_hash: Vec, + // when we do not need to calculate the root but just the additional hashes + // we can set it to true to avoid useless hashes round. + additional_hash_only: bool, +} + +// We need some read ahead to manage state. +struct MultiProofState { + current_key: Option, + next_key1: Option, + next_key2: Option, + // going up a join key means droping current in favor of stack, + // if stack empty move forward instead. + join1: Option, + join2: Option, + stack: smallvec::SmallVec<[(KN, usize);4]>, + is_empty: bool, +} + +enum MultiProofResult { + RegisterLeft, + RegisterRight, + DoNothing, + DoNothingNoStartWith, +} + +impl MultiProofState { + fn new(next_keys: &mut impl Iterator) -> Self { + let mut result = MultiProofState { + current_key: None, + next_key1: None, + next_key2: None, + join1: None, + join2: None, + stack: Default::default(), + is_empty: false, + }; + result.current_key = next_keys.next(); + if result.current_key.is_none() { + result.is_empty = true; + } + result.next_key1 = next_keys.next(); + result.next_key2 = next_keys.next(); + result.refresh_join1(); + result.refresh_join2(); + result + } + fn refresh_join1(&mut self) { + self.join1 = self.current_key.as_ref() + .and_then(|c| self.next_key1.as_ref().map(|n| n.common_depth(c))); + } + fn refresh_join2(&mut self) { + self.join2 = self.next_key1.as_ref() + .and_then(|n1| self.next_key2.as_ref().map(|n2| n2.common_depth(n1))); + } + + fn new_key(&mut self, key: &KN, next_keys: &mut impl Iterator) -> MultiProofResult { + let depth = key.depth(); + let start_with_current = self.current_key.as_ref().map(|c| c.starts_with(key)).unwrap_or(false); + if start_with_current { + // join management + if Some(depth) == self.join1 { + if let Some(join2) = self.join2 { + let stack_join = self.stack.last().map(|s| s.1); + if stack_join.map(|sj| join2 > sj).unwrap_or(true) { + // move fw, keep current. + // next_1 is dropped. + self.next_key1 = self.next_key2.take(); + self.refresh_join1(); + self.next_key2 = next_keys.next(); + self.refresh_join2(); + return MultiProofResult::DoNothing; + } + } + // from stack + if let Some((stack_hash, stack_join)) = self.stack.pop() { + // current is dropped. + self.current_key = Some(stack_hash); + debug_assert!({ + self.refresh_join1(); + Some(stack_join) == self.join1 + }); + self.join1 = Some(stack_join); + } else { + // fuse last interval + self.join1 = None; + self.join2 = None; + self.next_key1 = None; + self.next_key2 = None; + } + return MultiProofResult::DoNothing; + } else { + // no matching join1 depth exclude sibling case. + if self.current_key.as_ref() + .expect("start_with_current").nibble_at(key.depth()).expect("starts with") { + return MultiProofResult::RegisterLeft; + } else { + return MultiProofResult::RegisterRight; + } + } + } + + let start_with_next = self.next_key1.as_ref().map(|n| n.starts_with(key)).unwrap_or(false); + // next interval management + if start_with_next { + let mut sibling = false; + if let Some(join2) = self.join2 { + if join2 == depth { + // next is sibling, skip it and do not register. + // next2 is dropped + self.next_key2 = next_keys.next(); + self.refresh_join2(); + sibling = true; + } + } + + // sibling || is just to skip next nible query as it is unused. + let right = sibling || self.next_key1.as_ref() + .expect("start_with_current").nibble_at(key.depth()).expect("starts with"); + if let Some(join1) = self.join1 { + if let Some(join2) = self.join2 { + if join2 > join1 { + // shift and stack + self.stack.push((self.current_key.take().expect("no next without current"), join1)); + self.current_key = self.next_key1.take(); + self.next_key1 = self.next_key2.take(); + self.next_key2 = next_keys.next(); + + debug_assert!({ + self.refresh_join1(); + self.join1 == self.join2 + }); + self.join1 = self.join2; + self.refresh_join2(); + } else { + // keep interval + } + } else { + // no next_key2, keep interval + } + } else { + unreachable!("next is defined (start_with_next)"); + } + if !sibling { + if right { + return MultiProofResult::RegisterLeft; + } else { + return MultiProofResult::RegisterRight; + } + } + return MultiProofResult::DoNothing; + } + MultiProofResult::DoNothingNoStartWith + } +} + +impl<'a, H: BinaryHasher, KN: KeyNode, I: Iterator> HashProof<'a, H, I, KN> { + pub fn new( + buffer: &'a mut H::Buffer, + mut to_prove: I, + additional_hash_only: bool, + ) -> Self { + let state = MultiProofState::new(&mut to_prove); + HashProof { + buffer, + to_prove, + state, + additional_hash: Vec::new(), + additional_hash_only, + } + } + pub fn take_additional_hash(&mut self) -> Vec { + crate::rstd::mem::replace(&mut self.additional_hash, Vec::new()) + } +} + +impl<'a, H: BinaryHasher, KN: KeyNode, I: Iterator> ProcessNode for HashProof<'a, H, I, KN> { + fn process_empty_trie(&mut self) -> &[u8] { + H::NULL_HASH + } + fn process(&mut self, key: &KN, child1: &[u8], child2: &[u8]) -> H::Out { + let mut skip_hashing = false; + match self.state.new_key(key, &mut self.to_prove) { + MultiProofResult::DoNothingNoStartWith => (), + MultiProofResult::DoNothing => { + skip_hashing = self.additional_hash_only + }, + MultiProofResult::RegisterLeft => { + let mut to_push = H::Out::default(); + to_push.as_mut().copy_from_slice(child1); + self.additional_hash.push(to_push); + skip_hashing = self.additional_hash_only; + }, + MultiProofResult::RegisterRight => { + let mut to_push = H::Out::default(); + to_push.as_mut().copy_from_slice(child2); + self.additional_hash.push(to_push); + skip_hashing = self.additional_hash_only; + }, + } + + if skip_hashing { + Default::default() + } else { + let mut h = HashOnly::::new(self.buffer); + h.process(key, child1, child2) + } + } + fn register_root(&mut self, root: &H::Out) { + if self.state.is_empty { + self.additional_hash.push(root.clone()); + } + } +} + +// This only include hash, for including hashed value or inline node, just map the process over the +// input iterator (note that for inline node we need to attach this inline info to the tree so it +// only make sense for small trie or fix length trie). +/// Returns a calculated hash +pub fn trie_root(layout: &SequenceBinaryTree, input: I, callback: &mut F) -> HO + where + HO: Default + AsRef<[u8]> + AsMut<[u8]>, + KN: KeyNode + Into + From<(usize, usize)> + Clone, + I: Iterator, + F: ProcessNode, +{ + let mut iter = input.into_iter().zip(layout.iter_path_node_key::()); + debug_assert!({ + let (r, s) = iter.size_hint(); + if s == Some(r) { + layout.nb_elements() == r + } else { + true + } + }); + let mut depth1 = layout.depth; + let mut child1 = if let Some((child, key)) = iter.next() { + debug_assert!(key.depth() == depth1); + child + } else { + debug_assert!(layout.nb_elements() == 0); + let mut result = HO::default(); + result.as_mut().copy_from_slice(callback.process_empty_trie()); + return result; + }; + debug_assert!(layout.depth_index(0) == layout.depth); + // use a stack that match 16 element without allocation, that is 4 element depth + let mut stack = smallvec::SmallVec::<[(HO, usize);4]>::new(); + let mut key: KN = (0, depth1).into(); + loop { + let last_stack_depth = stack.last().map(|e|e.1); + if Some(depth1) == last_stack_depth { + // process over stack + let (child2, _depth2) = stack.pop().expect("checked above"); + key.pop_back(); + // stacked on at left + let parent = callback.process(&key, child2.as_ref(), child1.as_ref()); + depth1 = key.depth(); + child1 = parent; + } else { + if let Some((child2, key2)) = iter.next() { + key = key2; + if key.depth() == depth1 { + key.pop_back(); + // iter one at right + let parent = callback.process(&key, child1.as_ref(), child2.as_ref()); + depth1 = key.depth(); + child1 = parent; + } else { + stack.push((child1, depth1)); + child1 = child2; + depth1 = key.depth(); + } + } else { + break; + } + } + } + debug_assert!(stack.is_empty()); + callback.register_root(&child1); + child1 +} + +/// Returns a calculated hash +pub fn trie_root_from_proof( + layout: &SequenceBinaryTree, + input: I, + additional_hash: I2, + callback: &mut F, + allow_additionals_hashes: bool, +) -> Option + where + HO: Default + AsRef<[u8]> + AsMut<[u8]>, + KN: KeyNode + Into + From<(usize, usize)> + Clone, + I: IntoIterator, + I2: IntoIterator, + F: ProcessNode, +{ + if layout.nb_elements() == 0 { + if !allow_additionals_hashes && additional_hash.into_iter().next().is_some() { + return None; + } else { + let mut result = HO::default(); + result.as_mut().copy_from_slice(callback.process_empty_trie()); + return Some(result); + } + } + + let mut items = input.into_iter(); + let mut additional_hash = additional_hash.into_iter(); + let mut current; + if let Some(c) = items.next() { + current = c; + } else { + // when no item, root is first from additional hashes + if let Some(h) = additional_hash.next() { + if allow_additionals_hashes || additional_hash.next().is_none() { + callback.register_root(&h); + return Some(h); + } + } + return None; + } + let mut next = items.next(); + let calc_common_depth = |current: &(KN, HO), next: &Option<(KN, HO)>| { + next.as_ref().map(|n| current.0.common_depth(&n.0)) + }; + let mut common_depth = calc_common_depth(¤t, &next); + let mut stack = smallvec::SmallVec::<[((KN, HO), usize);4]>::new(); + + while let Some(right) = current.0.pop_back() { + let depth = current.0.depth(); + if Some(depth) == stack.last().as_ref().map(|s| s.1) { + let ((stack_key, stack_hash), _stack_depth) = stack.pop().expect("tested in condition"); + debug_assert!(right == true); + debug_assert!(stack_key.starts_with(¤t.0) && current.0.starts_with(&stack_key)); + current.1 = callback.process(¤t.0, stack_hash.as_ref(), current.1.as_ref()); + continue; + } + if Some(depth) == common_depth { + let (key_next, hash_next) = next.take().expect("common depth is some"); + stack.push((current, depth)); + current = (key_next, hash_next); + next = items.next(); + common_depth = calc_common_depth(¤t, &next); + continue; + } + if let Some(other) = additional_hash.next() { + if right { + current.1 = callback.process(¤t.0, other.as_ref(), current.1.as_ref()); + } else { + current.1 = callback.process(¤t.0, current.1.as_ref(), other.as_ref()); + } + } else { + return None; + } + } + + debug_assert!(current.0.depth() == 0); + if !allow_additionals_hashes && additional_hash.next().is_some() { + None + } else { + callback.register_root(¤t.1); + Some(current.1) + } +} + +/// Hasher hybrid using an ordered trie +pub struct OrderedTrieHasher(PhantomData<(H, HH)>); + +impl> HasherHybrid for OrderedTrieHasher { + type InnerHasher = HH; + + fn hash_hybrid_proof< + I: Iterator::Out>>, + I2: Iterator::Out>, + >( + header: &[u8], + nb_children: usize, + children: I, + additional_hashes: I2, + buffer: &mut HH::Buffer, + ) -> Option { + let seq_trie = SequenceBinaryTree::new(nb_children); + + let mut callback_read_proof = HashOnly::::new(buffer); + // proof node, UsizeKeyNode should be big enough for hasher hybrid + // case. + let iter_key = seq_trie.iter_path_node_key::(); + let iter = children + .zip(iter_key) + .filter_map(|(value, key)| if let Some(value) = value { + Some((key, value)) + } else { + None + }); + let hash = if let Some(hash) = crate::trie_root_from_proof( + &seq_trie, + iter, + additional_hashes, + &mut callback_read_proof, + false, + ) { + hash + } else { + return None; + }; + let mut hash_buf2 = ::init_buffer(); + ::buffer_hash(&mut hash_buf2, header); + ::buffer_hash(&mut hash_buf2, hash.as_ref()); + Some(H::buffer_finalize(&mut hash_buf2)) + } + + fn hash_hybrid< + I: Iterator::Out>>, + >( + header: &[u8], + nb_children: usize, + children: I, + buffer: &mut HH::Buffer, + ) -> H::Out { + let seq_trie = SequenceBinaryTree::new(nb_children); + + let mut callback_read_proof = HashOnly::::new(buffer); + let iter = children.filter_map(|v| v); + let hash = crate::trie_root::<_, UsizeKeyNode, _, _>(&seq_trie, iter, &mut callback_read_proof); + let mut hash_buf2 = ::init_buffer(); + ::buffer_hash(&mut hash_buf2, header); + ::buffer_hash(&mut hash_buf2, hash.as_ref()); + H::buffer_finalize(&mut hash_buf2) + } + +} + +impl BinaryHasher for OrderedTrieHasher { + const NULL_HASH: &'static [u8] = ::NULL_HASH; + type Buffer = ::Buffer; + fn init_buffer() -> Self::Buffer { + ::init_buffer() + } + fn reset_buffer(buf: &mut Self::Buffer) { + ::reset_buffer(buf) + } + fn buffer_hash(buff: &mut Self::Buffer, x: &[u8]) { + ::buffer_hash(buff, x) + } + fn buffer_finalize(buff: &mut Self::Buffer) -> Self::Out { + ::buffer_finalize(buff) + } +} + +impl Hasher for OrderedTrieHasher { + type Out = ::Out; + type StdHasher = ::StdHasher; + const LENGTH: usize = ::LENGTH; + + #[inline] + fn hash(x: &[u8]) -> Self::Out { + ::hash(x) + } +} + +#[cfg(test)] +mod test { + + use keccak_hasher::KeccakHasher; + use super::*; + use crate::vec_keynode::VecKeyNode; + + #[test] + fn test_depth() { + /* + (0, 0), + (1, 1), + (2, 2), + (3, 2), + (4, 3), + (5, 3), + (7, 3), + (8, 4), + */ + assert_eq!(depth(0), 0); + assert_eq!(depth(1), 1); + assert_eq!(depth(2), 2); + assert_eq!(depth(3), 2); + assert_eq!(depth(4), 3); + assert_eq!(depth(7), 3); + assert_eq!(depth(8), 4); + assert_eq!(depth(9), 4); + assert_eq!(depth(u16::max_value() as usize - 1), 16); + assert_eq!(depth(u16::max_value() as usize), 16); + } + + #[test] + fn key_node_test() { + let test = |start: usize, end: bool| { + let depth = depth(start); + let mut v = VecKeyNode::from((start, depth)); + let mut u = UsizeKeyNode::from((start, depth)); + assert_eq!(v.nibble_at(start), u.nibble_at(start)); + if !end { + assert_eq!(u.push_back(true), v.push_back(true)); + } + assert_eq!(u.pop_back(), v.pop_back()); + if !end { + assert_eq!(u.push_back(false), v.push_back(false)); + } + assert_eq!(u.pop_back(), v.pop_back()); + assert_eq!(u.push_front(true), v.push_front(true)); + assert_eq!(u.pop_front(), v.pop_front()); + assert_eq!(u.push_front(false), v.push_front(false)); + assert_eq!(u.pop_front(), v.pop_front()); + if !end { + assert_eq!(start, u.into()); + assert_eq!(start, v.clone().into()); + } + assert_eq!(u.pop_back(), v.pop_back()); + let u: usize = u.into(); + assert_eq!(u, v.into()); + }; + let t: VecKeyNode = (5, 4).into(); + let t: Vec = t.0.into_iter().collect(); + assert_eq!(t, vec![false, true, false, true]); + let t: std::collections::VecDeque = [false, true, false, true].iter().cloned().collect(); + assert_eq!(5usize, VecKeyNode(t).into()); + for i in 0..17 { + test(i, false); + } + test(usize::max_value() - 1, true); + } + + type Tree = super::SequenceBinaryTree; + + #[test] + fn test_max_depth() { + let values = [ + (0, 0), + (1, 0), + (2, 1), + (3, 2), + (4, 2), + (5, 3), + (8, 3), + (9, 4), + (16, 4), + (17, 5), + (32, 5), + ]; + let mut tree = Tree::default(); + let mut prev = 0; + for (nb, depth) in values.iter().cloned() { + let inc = nb - prev; + prev = nb; + tree.resize_append(inc); + assert_eq!(tree.depth, depth); + let tree2 = Tree::new(nb); + assert_eq!(tree2.depth, depth); + assert_eq!(tree, tree2); + } + } + + #[test] + fn test_depth_index() { + // 8 trie + let tree = Tree::new(7); + assert_eq!(tree.depth_index(3), 3); + assert_eq!(tree.depth_index(4), 3); + assert_eq!(tree.depth_index(6), 2); + let tree = Tree::new(6); + assert_eq!(tree.depth_index(0), 3); + assert_eq!(tree.depth_index(3), 3); + assert_eq!(tree.depth_index(4), 2); + assert_eq!(tree.depth_index(5), 2); + let tree = Tree::new(5); + assert_eq!(tree.depth_index(3), 3); + assert_eq!(tree.depth_index(4), 1); + // 16 trie + let tree = Tree::new(12); + assert_eq!(tree.depth_index(7), 4); + assert_eq!(tree.depth_index(8), 3); + assert_eq!(tree.depth_index(11), 3); + let tree = Tree::new(11); + assert_eq!(tree.depth_index(7), 4); + assert_eq!(tree.depth_index(8), 3); + assert_eq!(tree.depth_index(9), 3); + assert_eq!(tree.depth_index(10), 2); + let tree = Tree::new(10); + assert_eq!(tree.depth_index(7), 4); + assert_eq!(tree.depth_index(8), 2); + assert_eq!(tree.depth_index(9), 2); + let tree = Tree::new(9); + assert_eq!(tree.depth_index(7), 4); + assert_eq!(tree.depth_index(8), 1); + // 32 trie TODO + } + + #[test] + fn test_depth_iter() { + // cases corresponding to test_depth, TODO add more + for nb in 0usize..16 { + let mut n = 0; + let tree = Tree::new(nb); + for (i, (d, k)) in tree.iter_depth(None) + .zip(tree.iter_path_node_key::()) + .enumerate() { + n += 1; + assert_eq!(d, tree.depth_index(i)); + assert_eq!(d, k.depth); + let k2: UsizeKeyNode = tree.path_node_key(i); + assert_eq!(k.depth, k2.depth); + assert_eq!(k.value, k2.value); + } + assert_eq!(n, nb); + } + } + + fn hashes(l: usize) -> Vec<[u8;32]> { + (0..l).map(|i| { + let mut hash = ::Out::default(); + let v = (i as u64).to_be_bytes(); + hash.as_mut()[..8].copy_from_slice(&v[..]); + hash + }).collect() + } + + fn base16_roots() -> Vec<[u8;32]> { + let hashes = hashes(16); + let mut result = Vec::<[u8;32]>::new(); + + let khash = |a: &[u8], b: &[u8]| { + let mut v = Vec::new(); + v.extend_from_slice(a); + v.extend_from_slice(b); + ::hash(v.as_ref()) + }; + let mut hash = ::Out::default(); + hash.as_mut()[..].copy_from_slice(KeccakHasher::NULL_HASH); + result.push(hash); + result.push(hashes[0].clone()); + let base2 = khash(hashes[0].as_ref(), hashes[1].as_ref()); + result.push(base2); + result.push(khash( + base2.as_ref(), + hashes[2].as_ref(), + )); + let base4 = khash( + base2.as_ref(), + khash(hashes[2].as_ref(), hashes[3].as_ref()).as_ref(), + ); + result.push(base4); + result.push(khash( + base4.as_ref(), + hashes[4].as_ref(), + )); + let base2 = khash(hashes[4].as_ref(), hashes[5].as_ref()); + result.push(khash( + base4.as_ref(), + base2.as_ref(), + )); + result.push(khash( + base4.as_ref(), + khash( + base2.as_ref(), + hashes[6].as_ref(), + ).as_ref(), + )); + let base8 = khash( + base4.as_ref(), + khash( + base2.as_ref(), + khash(hashes[6].as_ref(), hashes[7].as_ref()).as_ref(), + ).as_ref(), + ); + result.push(base8); + result.push(khash( + base8.as_ref(), + hashes[8].as_ref(), + )); + let base2 = khash(hashes[8].as_ref(), hashes[9].as_ref()); + result.push(khash( + base8.as_ref(), + base2.as_ref(), + )); + result.push(khash( + base8.as_ref(), + khash( + base2.as_ref(), + hashes[10].as_ref(), + ).as_ref(), + )); + let base4 = khash( + base2.as_ref(), + khash(hashes[10].as_ref(), hashes[11].as_ref()).as_ref(), + ); + result.push(khash( + base8.as_ref(), + base4.as_ref(), + )); + result.push(khash( + base8.as_ref(), + khash( + base4.as_ref(), + hashes[12].as_ref(), + ).as_ref(), + )); + let base2 = khash(hashes[12].as_ref(), hashes[13].as_ref()); + result.push(khash( + base8.as_ref(), + khash( + base4.as_ref(), + base2.as_ref(), + ).as_ref(), + )); + result.push(khash( + base8.as_ref(), + khash( + base4.as_ref(), + khash( + base2.as_ref(), + hashes[14].as_ref(), + ).as_ref(), + ).as_ref(), + )); + result.push(khash( + base8.as_ref(), + khash( + base4.as_ref(), + khash( + base2.as_ref(), + khash(hashes[14].as_ref(), hashes[15].as_ref()).as_ref(), + ).as_ref(), + ).as_ref(), + )); + result + } + + #[test] + fn test_hash_only() { + let result = base16_roots(); + for l in 0..17 { + let tree = Tree::new(l); + let mut hash_buf = ::init_buffer(); + let mut callback = HashOnly::::new(&mut hash_buf); + let hashes: Vec<_> = hashes(l); + let root = trie_root::<_, UsizeKeyNode, _, _>(&tree, hashes.into_iter(), &mut callback); + assert_eq!(root.as_ref(), &result[l][..]); + } + } + + #[test] + fn test_one_element_proof() { + let result = base16_roots(); + for l in 0..17 { + let tree = Tree::new(l); + let mut hash_buf = ::init_buffer(); + let mut hash_buf2 = ::init_buffer(); + let mut hash_buf3 = ::init_buffer(); + let mut callback_read_proof = HashOnly::::new(&mut hash_buf2); + let hashes: Vec<_> = hashes(l); + for p in 0..l { + let to_prove = vec![tree.path_node_key::(p)]; + let mut callback = HashProof::::new(&mut hash_buf, to_prove.into_iter(), false); + let to_prove = vec![tree.path_node_key::(p)]; + let root = trie_root::<_, UsizeKeyNode, _, _>(&tree, hashes.clone().into_iter(), &mut callback); + let mut callback2 = HashProof::::new(&mut hash_buf3, to_prove.into_iter(), true); + let _ = trie_root::<_, UsizeKeyNode, _, _>(&tree, hashes.clone().into_iter(), &mut callback2); + assert_eq!(root.as_ref(), &result[l][..]); + let additional_hash = callback.take_additional_hash(); + let additional_hash_2 = callback2.take_additional_hash(); + assert_eq!(additional_hash, additional_hash_2); + let proof_items = vec![(tree.path_node_key::(p), hashes[p].clone())]; + let root = trie_root_from_proof::<_, UsizeKeyNode, _, _, _>( + &tree, + proof_items, + additional_hash.into_iter(), + &mut callback_read_proof, + false, + ); + println!("{}, {}", l, p); + assert!(root.is_some()); + assert_eq!(root.unwrap().as_ref(), &result[l][..]); + } + } + } + + #[test] + fn test_multiple_elements_proof() { + let result = base16_roots(); + let tests = [ + (1, &[][..]), + (1, &[0][..]), + (4, &[][..]), + (4, &[1][..]), + (4, &[1, 2][..]), + (4, &[1, 2, 3][..]), + (13, &[1, 2, 3][..]), + (13, &[2, 3, 4][..]), + (13, &[2, 5][..]), + (13, &[2, 5, 11][..]), + (13, &[2, 11][..]), + (13, &[11, 12][..]), + (13, &[10, 12][..]), + (13, &[2, 11, 12][..]), + ]; + for (l, ps) in tests.iter() { + let l = *l; + let ps = *ps; + let tree = Tree::new(l); + let mut hash_buf = ::init_buffer(); + let mut hash_buf2 = ::init_buffer(); + let mut hash_buf3 = ::init_buffer(); + let mut callback_read_proof = HashOnly::::new(&mut hash_buf2); + let hashes: Vec<_> = hashes(l); + let mut to_prove = Vec::new(); + let mut proof_items = Vec::new(); + for p in ps { + let p = *p; + to_prove.push(tree.path_node_key::(p)); + proof_items.push((tree.path_node_key::(p), hashes[p].clone())); + } + let mut callback = HashProof::::new(&mut hash_buf, to_prove.clone().into_iter(), false); + let mut callback2 = HashProof::::new(&mut hash_buf3, to_prove.into_iter(), true); + let root = trie_root::<_, UsizeKeyNode, _, _>(&tree, hashes.clone().into_iter(), &mut callback); + let _ = trie_root::<_, UsizeKeyNode, _, _>(&tree, hashes.clone().into_iter(), &mut callback2); + assert_eq!(root.as_ref(), &result[l][..]); + let additional_hash = callback.take_additional_hash(); + let additional_hash2 = callback2.take_additional_hash(); + assert_eq!(additional_hash, additional_hash2); + let root = trie_root_from_proof::<_, UsizeKeyNode, _, _, _>( + &tree, + proof_items, + additional_hash.into_iter(), + &mut callback_read_proof, + false, + ); + println!("{}, {:?}", l, ps); + assert!(root.is_some()); + assert_eq!(root.unwrap().as_ref(), &result[l][..]); + } + } +} diff --git a/test-support/keccak-hasher/Cargo.toml b/test-support/keccak-hasher/Cargo.toml index 0cf02116..474dcd8e 100644 --- a/test-support/keccak-hasher/Cargo.toml +++ b/test-support/keccak-hasher/Cargo.toml @@ -11,6 +11,7 @@ edition = "2018" tiny-keccak = { version = "2.0.2", features = ["keccak"] } hash-db = { path = "../../hash-db", default-features = false, version = "0.15.2" } hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.15.2" } +ordered-trie = { path = "../../ordered-trie", default-features = false, version = "0.19.2"} [features] default = ["std"] diff --git a/test-support/keccak-hasher/src/lib.rs b/test-support/keccak-hasher/src/lib.rs index f5371da4..ce066c1c 100644 --- a/test-support/keccak-hasher/src/lib.rs +++ b/test-support/keccak-hasher/src/lib.rs @@ -14,7 +14,8 @@ //! Hasher implementation for the Keccak-256 hash -use hash_db::Hasher; +use hash_db::{Hasher, BinaryHasher}; +//use hash_db::FixHash; use tiny_keccak::{Hasher as _, Keccak}; use hash256_std_hasher::Hash256StdHasher; @@ -37,6 +38,37 @@ impl Hasher for KeccakHasher { } } +impl BinaryHasher for KeccakHasher { + const NULL_HASH: &'static [u8] = &[197, 210, 70, 1, 134, 247, 35, 60, 146, + 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, + 250, 216, 4, 93, 133, 164, 112]; + type Buffer = Keccak; + + fn init_buffer() -> Self::Buffer { + Keccak::v256() + } + + fn reset_buffer(buff: &mut Self::Buffer) { + let _ = core::mem::replace(buff, Self::init_buffer()); + } + + fn buffer_hash(buff: &mut Self::Buffer, x: &[u8]) { + buff.update(&x[..]) + } + + fn buffer_finalize(buff: &mut Self::Buffer) -> Self::Out { + let mut res: [u8; 32] = [0; 32]; + let k = core::mem::replace(buff, Self::init_buffer()); + k.finalize(&mut res); + res + } +} + +#[test] +fn test_keccack_hasher() { + hash_db::test_binary_hasher::() +} + #[cfg(test)] mod tests { use super::*; diff --git a/test-support/reference-trie/Cargo.toml b/test-support/reference-trie/Cargo.toml index 84a6c7a9..2094e200 100644 --- a/test-support/reference-trie/Cargo.toml +++ b/test-support/reference-trie/Cargo.toml @@ -13,8 +13,15 @@ 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" } +blake2-rfc = { version = "0.2.18", default-features = false } +blake3 = { version = "0.3.7", default-features = false } +ordered-trie = { path = "../../ordered-trie", default-features = false, version = "0.19.2"} parity-scale-codec = { version = "2.0.0", features = ["derive"] } +# Deps for proof size build +trie-standardmap = { path = "../trie-standardmap", version = "0.15.2" } +memory-db = { path = "../../memory-db", version = "0.26.0" } + [dev-dependencies] trie-bench = { path = "../trie-bench", version = "0.27.0" } criterion = "0.3.3" @@ -23,10 +30,16 @@ criterion = "0.3.3" name = "bench" harness = false +[[bin]] +name = "proof_size" +path = "src/compare_proof_size.rs" + [features] default = ["std"] # no actual support for std, only to avoid a cargo issues std = [ "trie-db/std", "trie-root/std", + "blake2-rfc/std", + "blake3/std", ] diff --git a/test-support/reference-trie/src/compare_proof_size.rs b/test-support/reference-trie/src/compare_proof_size.rs new file mode 100644 index 00000000..c2fe7f00 --- /dev/null +++ b/test-support/reference-trie/src/compare_proof_size.rs @@ -0,0 +1,153 @@ +// Copyright 2020 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. + +use trie_standardmap::{Alphabet, StandardMap, ValueMode}; +use hash_db::{ + HashDB, + EMPTY_PREFIX, +}; +use memory_db::{ + MemoryDB, + PrefixedKey, + HashKey, +}; +use reference_trie::{ + ExtensionLayoutHybrid, + ExtensionLayout, +}; +use trie_db::{ + TrieDBMut, + TrieMut, + TrieDB, + Trie, + TrieLayout, + Recorder, + encode_compact, + decode_compact, +}; +use parity_scale_codec::{Encode, Compact}; + +type DBValue = Vec; + +fn main() { + let trie_size = [100, 1000, 10_000, 100_000]; + let number_key = [1, 10, 100, 1000, 10_000]; + let size_value = 32; + for s in trie_size.iter() { + compare(*s, &number_key[..], size_value, true) + } +} + +fn compare(trie_size: u32, number_key: &[usize], size_value: usize, check_proof: bool) { + + let mut seed = Default::default(); + let x = StandardMap { + alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), + min_key: size_value, + journal_key: 0, + value_mode: ValueMode::Index, + count: trie_size, + }.make_with(&mut seed); + let mut memdb = MemoryDB::<::Hash, PrefixedKey<_>, DBValue>::default(); + let mut root = Default::default(); + { + let mut t = TrieDBMut::::new(&mut memdb, &mut root); + for i in 0..x.len() { + let key: &[u8]= &x[i].0; + let val: &[u8] = &x[i].1; + t.insert(key, val).unwrap(); + } + t.commit(); + } + let trie = >::new(&memdb, &root).unwrap(); + for n in number_key { + if *n <= trie_size as usize { + // we test only existing key, missing key should have better overall compression(could try with pure random) + compare_inner(&trie, trie_size, &x[..*n], "standard", check_proof) + } + } + + let mut memdb = MemoryDB::<::Hash, PrefixedKey<_>, DBValue>::default(); + let mut root = Default::default(); + { + let mut thyb = TrieDBMut::::new(&mut memdb, &mut root); + for i in 0..x.len() { + let key: &[u8]= &x[i].0; + let val: &[u8] = &x[i].1; + thyb.insert(key, val).unwrap(); + } + thyb.commit(); + } + let trie = >::new(&memdb, &root).unwrap(); + for n in number_key { + if *n <= trie_size as usize { + compare_inner(&trie, trie_size, &x[..*n], "hybrid", check_proof) + } + } +} + + +fn compare_inner(trie: &TrieDB, trie_size: u32, keys: &[(Vec, Vec)], lay: &str, check_proof: bool) { + let mut recorder = Recorder::new(); + for (key, _) in keys { + let _ = trie.get_with(key.as_slice(), &mut recorder).unwrap(); + } + + let mut std_proof_len = 0; + let mut partial_db = , _>>::default(); + let mut nb_elt = 0; + for record in recorder.drain() { + std_proof_len += compact_size(record.data.len()); + std_proof_len += record.data.len(); + partial_db.emplace(record.hash, EMPTY_PREFIX, record.data); + nb_elt += 1; + } + std_proof_len += compact_size(nb_elt); + let compact_trie = { + let partial_trie = >::new(&partial_db, &trie.root()).unwrap(); + encode_compact::(&partial_trie).unwrap() + }; + + if check_proof { + let mut memdb = , _>>::default(); + let (root, nbs) = decode_compact::(&mut memdb, &compact_trie).unwrap(); + println!("nb node in proof {}", nbs); + let trie = >::new(&memdb, &root).unwrap(); + for (key, value) in keys { + let v = trie.get(key).unwrap(); + assert_eq!(v.as_ref(), Some(value)); + } + } + let mut compact_proof_len = compact_size(compact_trie.len()); + for node in compact_trie.iter() { + compact_proof_len += compact_size(node.len()); + compact_proof_len += node.len(); + } + + let ratio: f64 = compact_proof_len as f64 / std_proof_len as f64; + println!( + "On {} {} size trie, {} proof, non compact: {} compact: {} ratio: {}", + lay, + trie_size, + keys.len(), + std_proof_len, + compact_proof_len, + ratio, + ); +} + +fn compact_size(len: usize) -> usize { + // TODO switch to range, this is slow + Compact::(len as u64).encode().len() +} diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 37a5f914..a9ad034b 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -26,16 +26,26 @@ use trie_db::{ triedbmut::ChildReference, DBValue, trie_visit, + TrieBuilderHybrid, TrieBuilder, TrieRoot, + TrieRootHybrid, Partial, + HasherHybrid, + BinaryHasher, + ChildProofHeader, + HashesPlan, + binary_additional_hashes, + TrieDB, + TrieDBMut, + Bitmap, BITMAP_LENGTH, }; use std::borrow::Borrow; -use keccak_hasher::KeccakHasher; use trie_db::{ - nibble_ops, NodeCodec, - Trie, TrieConfiguration, +// decode_compact, encode_compact, HashDBHybridDyn, + nibble_ops, NodeCodec, NodeCodecHybrid, + Trie, TrieConfiguration, HashDBHybrid, TrieLayout, TrieMut, }; pub use trie_root::TrieStream; @@ -43,90 +53,124 @@ pub mod node { pub use trie_db::node::Node; } +/// Reference hasher is a keccak hasher with hybrid ordered trie implementation. +pub type RefHasher = ordered_trie::OrderedTrieHasher; +//pub type RefHasher = ordered_trie::OrderedTrieHasher; +//pub type RefHasher = ordered_trie::OrderedTrieHasher; +//use keccak_hasher::KeccakHasher; +//pub type RefHasher = ordered_trie::OrderedTrieHasher; + +/// 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::(); + } + }; +} + /// Trie layout using extension nodes. pub struct ExtensionLayout; impl TrieLayout for ExtensionLayout { const USE_EXTENSION: bool = true; const ALLOW_EMPTY: bool = false; - type Hash = KeccakHasher; - type Codec = ReferenceNodeCodec; + const HYBRID_HASH: bool = false; + type Hash = RefHasher; + type Codec = ReferenceNodeCodec; } impl TrieConfiguration for ExtensionLayout { } +/// Trie layout using extension nodes. +pub struct ExtensionLayoutHybrid; + +impl TrieLayout for ExtensionLayoutHybrid { + const USE_EXTENSION: bool = true; + const ALLOW_EMPTY: bool = false; + const HYBRID_HASH: bool = true; + type Hash = RefHasher; + type Codec = ReferenceNodeCodec; +} + +impl TrieConfiguration for ExtensionLayoutHybrid { } + /// Trie layout without extension nodes, allowing /// generic hasher. pub struct GenericNoExtensionLayout(PhantomData); -impl TrieLayout for GenericNoExtensionLayout { +impl TrieLayout for GenericNoExtensionLayout { const USE_EXTENSION: bool = false; const ALLOW_EMPTY: bool = false; + const HYBRID_HASH: bool = false; type Hash = H; type Codec = ReferenceNodeCodecNoExt; } +impl TrieConfiguration for GenericNoExtensionLayout { } + /// Trie that allows empty values pub struct AllowEmptyLayout; impl TrieLayout for AllowEmptyLayout { const USE_EXTENSION: bool = true; const ALLOW_EMPTY: bool = true; - type Hash = KeccakHasher; - type Codec = ReferenceNodeCodec; + const HYBRID_HASH: bool = false; + type Hash = RefHasher; + type Codec = ReferenceNodeCodec; } -impl TrieConfiguration for GenericNoExtensionLayout { } - /// Trie layout without extension nodes. -pub type NoExtensionLayout = GenericNoExtensionLayout; - -/// Children bitmap codec for radix 16 trie. -pub struct Bitmap(u16); +pub type NoExtensionLayout = GenericNoExtensionLayout; -const BITMAP_LENGTH: usize = 2; - -impl Bitmap { +/// Trie layout without extension nodes, allowing +/// generic hasher. +pub struct GenericNoExtensionLayoutHybrid(PhantomData); - fn decode(data: &[u8]) -> Result { - Ok(u16::decode(&mut &data[..]) - .map(|v| Bitmap(v))?) - } +impl TrieLayout for GenericNoExtensionLayoutHybrid { + const USE_EXTENSION: bool = false; + const HYBRID_HASH: bool = true; + type Hash = H; + type Codec = ReferenceNodeCodecNoExt; +} - fn value_at(&self, i: usize) -> bool { - self.0 & (1u16 << i) != 0 - } +impl TrieConfiguration for GenericNoExtensionLayoutHybrid { } - fn encode>(has_children: I , output: &mut [u8]) { - let mut bitmap: u16 = 0; - let mut cursor: u16 = 1; - for v in has_children { - if v { bitmap |= cursor } - cursor <<= 1; - } - output[0] = (bitmap % 256) as u8; - output[1] = (bitmap / 256) as u8; - } -} +/// Trie layout without extension nodes. +pub type NoExtensionLayoutHybrid = GenericNoExtensionLayoutHybrid; -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>; +pub type RefTrieDB<'a> = TrieDB<'a, ExtensionLayout>; +pub type RefTrieDBMut<'a> = TrieDBMut<'a, ExtensionLayout>; +pub type RefTrieDBMutNoExt<'a> = TrieDBMut<'a, NoExtensionLayout>; +pub type RefTrieDBMutAllowEmpty<'a> = TrieDBMut<'a, AllowEmptyLayout>; pub type RefFatDB<'a> = trie_db::FatDB<'a, ExtensionLayout>; pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, ExtensionLayout>; pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, ExtensionLayout>; pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, ExtensionLayout>; pub type RefLookup<'a, Q> = trie_db::Lookup<'a, ExtensionLayout, Q>; +pub type RefLookupHybrid<'a, Q> = trie_db::Lookup<'a, ExtensionLayoutHybrid, Q>; pub type RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, NoExtensionLayout, Q>; +pub type RefLookupNoExtHybrid<'a, Q> = trie_db::Lookup<'a, NoExtensionLayoutHybrid, 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::HYBRID_HASH { + unimplemented!("trie_root does not implement hybrid hash, iter_build does") + } + if T::USE_EXTENSION { + trie_root::trie_root::(input) + } else { + trie_root::trie_root_no_extension::(input) + } } fn reference_trie_root_unhashed(input: I) -> Vec where @@ -134,15 +178,38 @@ fn reference_trie_root_unhashed(input: I) -> Vec where A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, { - trie_root::unhashed_trie::(input) + trie_root::unhashed_trie::(input) +} + +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() } -pub fn reference_trie_root_no_extension(input: I) -> ::Out 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::trie_root_no_extension::(input) + match T::HYBRID_HASH { + true => { + let mut cb = trie_db::TrieRootHybrid::::default(); + trie_visit::(data_sorted_unique(input), &mut cb); + cb.root.unwrap_or_default() + }, + false => { + let mut cb = trie_db::TrieRoot::::default(); + trie_visit::(data_sorted_unique(input), &mut cb); + cb.root.unwrap_or_default() + }, + } } fn reference_trie_root_unhashed_no_extension(input: I) -> Vec where @@ -150,7 +217,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) } const EMPTY_TRIE: u8 = 0; @@ -382,6 +449,13 @@ enum NodeHeaderNoExt { Leaf(usize), } +impl NodeHeader { + fn is_branch(first_byte: u8) -> bool { + first_byte == BRANCH_NODE_NO_VALUE + || first_byte == BRANCH_NODE_NO_VALUE + } +} + impl Encode for NodeHeader { fn encode_to(&self, output: &mut T) { match self { @@ -474,6 +548,14 @@ impl Decode for NodeHeader { } } +impl NodeHeaderNoExt { + fn is_branch(first_byte: u8) -> bool { + let first_byte = first_byte & (0b11 << 6); + first_byte == BRANCH_WITHOUT_MASK_NO_EXT + || first_byte == BRANCH_WITH_MASK_NO_EXT + } +} + impl Decode for NodeHeaderNoExt { fn decode(input: &mut I) -> Result { let i = input.read_byte()?; @@ -622,26 +704,18 @@ impl<'a> Input for ByteSliceInput<'a> { } } -// NOTE: what we'd really like here is: -// `impl NodeCodec for RlpNodeCodec where ::Out: Decodable` -// but due to the current limitations of Rust const evaluation we can't do -// `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. -// Perhaps one day soon? -impl NodeCodec for ReferenceNodeCodec { - type Error = CodecError; - type HashOut = H::Out; - - fn hashed_null_node() -> ::Out { - H::hash(::empty_node()) - } - - fn decode_plan(data: &[u8]) -> ::std::result::Result { +impl ReferenceNodeCodec { + fn decode_plan_internal( + data: &[u8], + is_proof: bool, + ) -> ::std::result::Result<(NodePlan, usize), ::Error> { + let mut result_offset = 0; let mut input = ByteSliceInput::new(data); - match NodeHeader::decode(&mut input)? { - NodeHeader::Null => Ok(NodePlan::Empty), + let node = match NodeHeader::decode(&mut input)? { + NodeHeader::Null => NodePlan::Empty, NodeHeader::Branch(has_value) => { let bitmap_range = input.take(BITMAP_LENGTH)?; - let bitmap = Bitmap::decode(&data[bitmap_range])?; + let bitmap = Bitmap::decode(&data[bitmap_range]); let value = if has_value { let count = >::decode(&mut input)?.0 as usize; @@ -649,22 +723,27 @@ impl NodeCodec for ReferenceNodeCodec { } else { None }; + result_offset = input.offset; 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) + if is_proof { + children[i] = Some(NodeHandlePlan::Inline(Range { start: 0, end: 0 })); } else { - NodeHandlePlan::Inline(range) - }); + 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::Branch { value, children }) + NodePlan::Branch { value, children } } NodeHeader::Extension(nibble_count) => { let partial = input.take( @@ -678,10 +757,10 @@ impl NodeCodec for ReferenceNodeCodec { } else { NodeHandlePlan::Inline(range) }; - Ok(NodePlan::Extension { + NodePlan::Extension { partial: NibbleSlicePlan::new(partial, partial_padding), child - }) + } } NodeHeader::Leaf(nibble_count) => { let partial = input.take( @@ -690,12 +769,31 @@ impl NodeCodec for ReferenceNodeCodec { 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 { + NodePlan::Leaf { partial: NibbleSlicePlan::new(partial, partial_padding), value, - }) + } } - } + }; + Ok((node, result_offset)) + } +} + +// NOTE: what we'd really like here is: +// `impl NodeCodec for RlpNodeCodec where ::Out: Decodable` +// but due to the current limitations of Rust const evaluation we can't do +// `const HASHED_NULL_NODE: ::Out = ::Out( … … )`. +// Perhaps one day soon? +impl NodeCodec for ReferenceNodeCodec { + type Error = CodecError; + type HashOut = H::Out; + + fn hashed_null_node() -> ::Out { + H::hash(::empty_node()) + } + + fn decode_plan(data: &[u8]) -> ::std::result::Result { + Ok(Self::decode_plan_internal(data, false)?.0) } fn is_empty_node(data: &[u8]) -> bool { @@ -735,6 +833,98 @@ impl NodeCodec for ReferenceNodeCodec { children: impl Iterator>>>, maybe_value: Option<&[u8]>, ) -> Vec { + Self::branch_node_internal(children, maybe_value, None, false, true).0 + } + + fn branch_node_nibbled( + _partial: impl Iterator, + _number_nibble: usize, + _children: impl Iterator>>>, + _maybe_value: Option<&[u8]>, + ) -> Vec { + unreachable!() + } + +} + +impl NodeCodecHybrid for ReferenceNodeCodec { + type AdditionalHashesPlan = HashesPlan; + + fn decode_plan_compact_proof(data: &[u8]) -> Result<(NodePlan, Option<(Bitmap, Self::AdditionalHashesPlan)>), Self::Error> { + let (node, offset) = Self::decode_plan_internal(data, true)?; + decode_plan_compact_proof_internal(data, offset, node, H::LENGTH) + } + + fn branch_node_common( + children: impl Iterator>>>, + maybe_value: Option<&[u8]>, + register_children: Option<&mut [Option>]>, + ) -> (Vec, ChildProofHeader) { + Self::branch_node_internal(children, maybe_value, register_children, true, true) + } + + fn branch_node_nibbled_common( + _partial: impl Iterator, + _number_nibble: usize, + _children: impl Iterator>>>, + _maybe_value: Option<&[u8]>, + _register_children: Option<&mut [Option>]>, + ) -> (Vec, ChildProofHeader) { + unreachable!() + } + + fn branch_node_for_hash( + children: impl Iterator>>>, + maybe_value: Option<&[u8]>, + ) -> Vec { + Self::branch_node_internal(children, maybe_value, None, true, false).0 + } + + fn branch_node_nibbled_for_hash( + _partial: impl Iterator, + _number_nibble: usize, + _children: impl Iterator>>>, + _maybe_value: Option<&[u8]>, + ) -> Vec { + unreachable!() + } + + fn encode_compact_proof( + hash_proof_header: Vec, + children: &[Option>], + in_proof: &[bool], + hash_buf: &mut BH::Buffer, + ) -> Vec { + encode_proof_internal::(hash_proof_header, children, hash_buf, in_proof) + } + + fn need_hybrid_proof(data: &[u8]) -> Result, ()> { + if data.len() > 0 { + if NodeHeader::is_branch(data[0]) { + let (node, offset) = Self::decode_plan_internal(data, false).map_err(|_| ())?; + let header = ChildProofHeader::Range( Range { + start: 0, + end: offset, + }); + return Ok(Some((node, header))) + } + } + Ok(None) + } + + fn codec_error(desc: &'static str) -> Self::Error { + desc.into() + } +} + +impl ReferenceNodeCodec { + fn branch_node_internal( + children: impl Iterator::HashOut>>>>, + maybe_value: Option<&[u8]>, + mut register_children: Option<&mut [Option>]>, + hybrid: bool, + encode_children: bool, + ) -> (Vec, ChildProofHeader) { let mut output = vec![0; BITMAP_LENGTH + 1]; let mut prefix: [u8; 3] = [0; 3]; let have_value = if let Some(value) = maybe_value { @@ -743,44 +933,163 @@ impl NodeCodec for ReferenceNodeCodec { } else { false }; + let mut ix = 0; + let ix = &mut ix; + let mut register_children = register_children.as_mut(); + let register_children = &mut register_children; + let common = if encode_children && hybrid { + ChildProofHeader::Range(Range { + start: 0, + end: output.len(), + }) + } else { + ChildProofHeader::Unused + }; + + let mut child_ix = output.len(); let has_children = children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { - h.as_ref().encode_to(&mut output); + if let Some(ranges) = register_children { + // this assume scale codec put len on one byte, which is the + // case for reasonable hash length. + let encode_size_offset = 1; + ranges[*ix] = Some(Range { + start: child_ix + encode_size_offset, + end: child_ix + encode_size_offset + h.as_ref().len(), + }); + child_ix += encode_size_offset + h.as_ref().len(); + *ix += 1; + } + if encode_children { + h.as_ref().encode_to(&mut output); + } true } &Some(ChildReference::Inline(inline_data, len)) => { - inline_data.as_ref()[..len].encode_to(&mut output); + if let Some(ranges) = register_children { + let encode_size_offset = 1; + ranges[*ix] = Some(Range { + start: child_ix + encode_size_offset, + end: child_ix + encode_size_offset + len, + }); + child_ix += encode_size_offset + len; + *ix += 1; + } + if encode_children { + inline_data.as_ref()[..len].encode_to(&mut output); + } true } - None => false, + None => { + if register_children.is_some() { + *ix += 1; + } + false + }, }); branch_node_buffered(have_value, has_children, prefix.as_mut()); output[0..BITMAP_LENGTH + 1].copy_from_slice(prefix.as_ref()); - output + (output, common) } - - fn branch_node_nibbled( - _partial: impl Iterator, - _number_nibble: usize, - _children: impl Iterator>>>, - _maybe_value: Option<&[u8]>) -> Vec { - unreachable!() - } - } -impl NodeCodec for ReferenceNodeCodecNoExt { - type Error = CodecError; - type HashOut = ::Out; +fn decode_plan_compact_proof_internal( + data: &[u8], + mut offset: usize, + mut node: NodePlan, + hash_len: usize, +) -> Result<(NodePlan, Option<(Bitmap, HashesPlan)>), CodecError> { + let hashes_plan = match &mut node { + NodePlan::Branch{children, ..} | NodePlan::NibbledBranch{children, ..} => { + if data.len() < offset + 3 { + return Err(CodecError::from("Decode branch, missing proof headers")); + } + let keys_position = Bitmap::decode(&data[offset..offset + BITMAP_LENGTH]); + offset += BITMAP_LENGTH; + + let nb_additional; + // read inline nodes. + loop { + let nb = data[offset] as usize; + offset += 1; + if nb >= 128 { + nb_additional = nb - 128; + break; + } + // 2 for inline index and next elt length. + if data.len() < offset + nb + 2 { + return Err(CodecError::from("Decode branch, missing proof inline data")); + } + let ix = data[offset] as usize; + offset += 1; + let inline = offset..offset + nb; + if ix >= nibble_ops::NIBBLE_LENGTH { + return Err(CodecError::from("Decode branch, invalid inline index")); + } + children[ix] = Some(NodeHandlePlan::Inline(inline)); + offset += nb; + } + let additional_len = nb_additional * hash_len; + if data.len() < offset + additional_len { + return Err(CodecError::from("Decode branch, missing child proof hashes")); + } + Some((keys_position, HashesPlan::new(nb_additional, offset, hash_len))) + }, + _ => None, + }; + Ok((node, hashes_plan)) +} - fn hashed_null_node() -> ::Out { - H::hash(::empty_node()) +fn encode_proof_internal( + mut result: Vec, + children: &[Option>], + hash_buf: &mut H::Buffer, + in_proof: &[bool], +) -> Vec { + let bitmap_start = result.len(); + result.push(0u8); + result.push(0u8); + // Write all inline nodes, that need to be proved. + for (ix, child) in children.iter().enumerate() { + if let Some(ChildReference::Inline(h, nb)) = child.borrow() { + if *nb > 0 { + if in_proof[ix] { + debug_assert!(*nb < 128); + result.push(*nb as u8); + result.push(ix as u8); + result.extend_from_slice(&h.as_ref()[..*nb]); + } + } else { + debug_assert!(in_proof[ix]); + } + } } + // We write a bitmap containing all children node that are included in the binary + // child proof construction. + // In practice, that is inline values and ommited compacted values). + Bitmap::encode(in_proof.iter().map(|b| *b), &mut result[bitmap_start..]); + + let additional_hashes = binary_additional_hashes::( + &children[..], + &in_proof[..], + hash_buf, + ); + result.push((additional_hashes.len() as u8) | 128); // first bit at one indicates we are on additional hashes + for hash in additional_hashes { + result.extend_from_slice(hash.as_ref()); + } + result +} - fn decode_plan(data: &[u8]) -> ::std::result::Result { +impl ReferenceNodeCodecNoExt { + fn decode_plan_internal( + data: &[u8], + is_proof: bool, + ) -> ::std::result::Result<(NodePlan, usize), ::Error> { + let mut result_offset = 0; let mut input = ByteSliceInput::new(data); - match NodeHeaderNoExt::decode(&mut input)? { - NodeHeaderNoExt::Null => Ok(NodePlan::Empty), + let node = 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) @@ -792,7 +1101,7 @@ impl NodeCodec for ReferenceNodeCodecNoExt { )?; let partial_padding = nibble_ops::number_padding(nibble_count); let bitmap_range = input.take(BITMAP_LENGTH)?; - let bitmap = Bitmap::decode(&data[bitmap_range])?; + let bitmap = Bitmap::decode(&data[bitmap_range]); let value = if has_value { let count = >::decode(&mut input)?.0 as usize; Some(input.take(count)?) @@ -803,22 +1112,27 @@ impl NodeCodec for ReferenceNodeCodecNoExt { None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ]; + result_offset = input.offset; 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) + if is_proof { + children[i] = Some(NodeHandlePlan::Inline(Range { start: 0, end: 0 })); } else { - NodeHandlePlan::Inline(range) - }); + 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 { + NodePlan::NibbledBranch { partial: NibbleSlicePlan::new(partial, partial_padding), value, children, - }) + } } NodeHeaderNoExt::Leaf(nibble_count) => { let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; @@ -832,12 +1146,26 @@ 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 { + NodePlan::Leaf { partial: NibbleSlicePlan::new(partial, partial_padding), value, - }) + } } - } + }; + Ok((node, result_offset)) + } +} + +impl NodeCodec for ReferenceNodeCodecNoExt { + type Error = CodecError; + type HashOut = ::Out; + + fn decode_plan(data: &[u8]) -> ::std::result::Result { + Ok(Self::decode_plan_internal(data, false)?.0) + } + + fn hashed_null_node() -> ::Out { + H::hash(::empty_node()) } fn is_empty_node(data: &[u8]) -> bool { @@ -875,6 +1203,20 @@ impl NodeCodec for ReferenceNodeCodecNoExt { children: impl Iterator>>>, maybe_value: Option<&[u8]>, ) -> Vec { + Self::branch_node_nibbled_internal(partial, number_nibble, children, maybe_value, None, false, true).0 + } +} + +impl ReferenceNodeCodecNoExt { + fn branch_node_nibbled_internal( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator::HashOut>>>>, + maybe_value: Option<&[u8]>, + mut register_children: Option<&mut [Option>]>, + hybrid: bool, + encode_children: bool, + ) -> (Vec, ChildProofHeader) { let mut output = if maybe_value.is_some() { partial_from_iterator_encode( partial, @@ -894,38 +1236,162 @@ impl NodeCodec for ReferenceNodeCodecNoExt { if let Some(value) = maybe_value { value.encode_to(&mut output); }; + let mut ix = 0; + let ix = &mut ix; + let mut register_children = register_children.as_mut(); + let register_children = &mut register_children; + let common = if encode_children && hybrid { + ChildProofHeader::Range(Range { + start: 0, + end: output.len(), + }) + } else { + ChildProofHeader::Unused + }; + + let mut child_ix = output.len(); Bitmap::encode(children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { - h.as_ref().encode_to(&mut output); + if let Some(ranges) = register_children { + // this assume scale codec put len on one byte, which is the + // case for reasonable hash length. + let encode_size_offset = 1; + ranges[*ix] = Some(Range { + start: child_ix + encode_size_offset, + end: child_ix + encode_size_offset + h.as_ref().len(), + }); + child_ix += encode_size_offset + h.as_ref().len(); + *ix += 1; + } + if encode_children { + h.as_ref().encode_to(&mut output); + } true } &Some(ChildReference::Inline(inline_data, len)) => { - inline_data.as_ref()[..len].encode_to(&mut output); + if let Some(ranges) = register_children { + let encode_size_offset = 1; + ranges[*ix] = Some(Range { + start: child_ix + encode_size_offset, + end: child_ix + encode_size_offset + len, + }); + child_ix += encode_size_offset + len; + *ix += 1; + } + if encode_children { + inline_data.as_ref()[..len].encode_to(&mut output); + } true } - None => false, + None => { + if register_children.is_some() { + *ix += 1; + } + false + }, }), bitmap.as_mut()); output[bitmap_index..bitmap_index + BITMAP_LENGTH] .copy_from_slice(&bitmap.as_ref()[..BITMAP_LENGTH]); - output + (output, common) + } +} + +impl NodeCodecHybrid for ReferenceNodeCodecNoExt { + type AdditionalHashesPlan = HashesPlan; + + fn decode_plan_compact_proof(data: &[u8]) -> Result<(NodePlan, Option<(Bitmap, Self::AdditionalHashesPlan)>), Self::Error> { + let (node, offset) = Self::decode_plan_internal(data, true)?; + decode_plan_compact_proof_internal(data, offset, node, H::LENGTH) + } + + fn branch_node_common( + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>, + _register_children: Option<&mut [Option>]>, + ) -> (Vec, ChildProofHeader) { + unreachable!() + } + + fn branch_node_nibbled_common( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator>>>, + maybe_value: Option<&[u8]>, + register_children: Option<&mut [Option>]>, + ) -> (Vec, ChildProofHeader) { + Self::branch_node_nibbled_internal( + partial, + number_nibble, + children, + maybe_value, + register_children, + true, + true, + ) + } + + fn branch_node_for_hash( + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>, + ) -> Vec { + unreachable!() + } + + fn branch_node_nibbled_for_hash( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator>>>, + maybe_value: Option<&[u8]>, + ) -> Vec { + Self::branch_node_nibbled_internal( + partial, + number_nibble, + children, + maybe_value, + None, + true, + false, + ).0 + } + + fn encode_compact_proof( + hash_proof_header: Vec, + children: &[Option>], + in_proof: &[bool], + hash_buf: &mut BH::Buffer, + ) -> Vec { + encode_proof_internal::(hash_proof_header, children, hash_buf, in_proof) + } + + fn need_hybrid_proof(data: &[u8]) -> Result, ()> { + if data.len() > 0 { + if NodeHeaderNoExt::is_branch(data[0]) { + let (node, offset) = Self::decode_plan_internal(data, false).map_err(|_| ())?; + let header = ChildProofHeader::Range( Range { + start: 0, + end: offset, + }); + return Ok(Some((node, header))) + } + } + Ok(None) } + fn codec_error(desc: &'static str) -> Self::Error { + desc.into() + } } /// Compare trie builder and in memory trie. -pub fn compare_implementations + Eq> ( +pub fn compare_implementations + Eq> ( 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()) - }; + 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 +1401,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 +1409,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 +1423,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 +1445,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 +1460,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,137 +1470,72 @@ 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()) + if T::HYBRID_HASH { + let mut cb = TrieRootHybrid::::default(); + trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or_default() + } else { + 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 +) -> ::Out where + T: TrieLayout, I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, - DB: hash_db::HashDB + DB: HashDBHybrid, { - 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 - 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_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); - } - } + if T::HYBRID_HASH { + let mut cb = TrieBuilderHybrid::new(hashdb); + trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or_default() + } else { + let mut cb = TrieBuilder::new(hashdb); + trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or_default() } - - assert_eq!(root, root_new); } /// `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 + Eq> ( data: Vec<(Vec, Vec)>, - mut memdb: impl hash_db::HashDB, - mut hashdb: impl hash_db::HashDB, + mut memdb: X, + mut hashdb: X, ) { 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()); } *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 root_new = calc_root_build::(b_map.into_iter(), &mut hashdb); 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 +1543,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 +1556,21 @@ 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, ) { 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 +1591,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,9 +1625,9 @@ 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> + let enc = as NodeCodec> ::leaf_node(((0, 0), &input), &[1]); - let dec = as NodeCodec> + let dec = as NodeCodec> ::decode(&enc).unwrap(); let o_sl = if let Node::Leaf(sl, _) = dec { Some(sl) @@ -1263,3 +1660,109 @@ mod tests { } } } + +pub mod blake2 { + use hash_db::{Hasher, BinaryHasher}; + use hash256_std_hasher::Hash256StdHasher; + + /// Concrete implementation of Hasher using Blake2b 256-bit hashes + #[derive(Debug)] + pub struct Blake2Hasher; + + impl Hasher for Blake2Hasher { + type Out = [u8; 32]; + type StdHasher = Hash256StdHasher; + const LENGTH: usize = 32; + + fn hash(x: &[u8]) -> Self::Out { + let mut dest = [0u8; 32]; + dest.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], x).as_bytes()); + dest + } + } + + impl BinaryHasher for Blake2Hasher { + const NULL_HASH: &'static [u8] = &[14, 87, 81, 192, 38, 229, + 67, 178, 232, 171, 46, 176, 96, 153, 218, 161, 209, 229, 223, + 71, 119, 143, 119, 135, 250, 171, 69, 205, 241, 47, 227, 168]; + type Buffer = blake2_rfc::blake2b::Blake2b; + + fn init_buffer() -> Self::Buffer { + blake2_rfc::blake2b::Blake2b::new(32) + } + + fn reset_buffer(buff: &mut Self::Buffer) { + let _ = core::mem::replace(buff, Self::init_buffer()); + } + + fn buffer_hash(buff: &mut Self::Buffer, x: &[u8]) { + buff.update(&x[..]) + } + + fn buffer_finalize(buff: &mut Self::Buffer) -> Self::Out { + let mut res: [u8; 32] = [0; 32]; + let k = core::mem::replace(buff, Self::init_buffer()); + res.copy_from_slice(k.finalize().as_bytes()); + res + } + + } + + #[test] + fn test_blake2b_hasher() { + hash_db::test_binary_hasher::() + } +} + + +pub mod blake3 { + use hash_db::{Hasher, BinaryHasher}; + use hash256_std_hasher::Hash256StdHasher; + + /// Concrete implementation of Hasher using Blake2b 256-bit hashes + #[derive(Debug)] + pub struct Blake3Hasher; + + impl Hasher for Blake3Hasher { + type Out = [u8; 32]; + type StdHasher = Hash256StdHasher; + const LENGTH: usize = 32; + + fn hash(x: &[u8]) -> Self::Out { + blake3::hash(x).into() + } + } + + impl BinaryHasher for Blake3Hasher { + const NULL_HASH: &'static [u8] = &[175, 19, 73, 185, + 245, 249, 161, 166, 160, 64, 77, 234, 54, 220, 201, + 73, 155, 203, 37, 201, 173, 193, 18, 183, 204, 154, + 147, 202, 228, 31, 50, 98]; + + type Buffer = blake3::Hasher; + + fn init_buffer() -> Self::Buffer { + blake3::Hasher::new() + } + + fn reset_buffer(buff: &mut Self::Buffer) { + buff.reset(); + } + + fn buffer_hash(buff: &mut Self::Buffer, x: &[u8]) { + buff.update(&x[..]); + } + + fn buffer_finalize(buff: &mut Self::Buffer) -> Self::Out { + let result = buff.finalize(); + // finalize do not reset state + buff.reset(); + result.into() + } + } + + #[test] + fn test_blake3_hasher() { + hash_db::test_binary_hasher::() + } +} diff --git a/trie-db/Cargo.toml b/trie-db/Cargo.toml index 64d8803e..d95ad9a7 100644 --- a/trie-db/Cargo.toml +++ b/trie-db/Cargo.toml @@ -12,6 +12,7 @@ log = "0.4" smallvec = "1.0.0" hash-db = { path = "../hash-db", default-features = false, version = "0.15.2"} hashbrown = { version = "0.9.1", default-features = false, features = ["ahash"] } +ordered-trie = { path = "../ordered-trie", default-features = false, version = "0.19.2"} rustc-hex = { version = "2.1.0", default-features = false, optional = true } [features] diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 9a831179..4f93f7cb 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -10,7 +10,7 @@ cargo-fuzz = true [dependencies] hash-db = { path = "../../hash-db", version = "0.15.2" } -memory-db = { path = "../../memory-db", version = "0.21.0" } +memory-db = { path = "../../memory-db", version = "0.26.0" } reference-trie = { path = "../../test-support/reference-trie", version = "0.21.0" } keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.2" } @@ -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..642c0566 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..f7e5d0fd 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..f898f4a5 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, + calc_root, proof::{generate_proof, verify_proof}, - reference_trie_root, - RefTrieDBMut, - RefTrieDBMutNoExt, - RefTrieDBNoExt, + reference_trie_root_iter_build as reference_trie_root, + TrieDBMut, TrieDBIterator, + compare_insert_remove, }; use std::convert::TryInto; -use trie_db::{DBValue, Trie, TrieDB, TrieDBMut, TrieLayout, TrieMut}; +use trie_db::{DBValue, Trie, TrieDB, TrieLayout, TrieMut}; 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); + 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); -} - -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,7 @@ 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 @@ -289,11 +279,30 @@ 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()); + let (root, proof, items) = test_generate_proof::(data, keys); + assert!(verify_proof::(&root, &proof, items.iter()).is_ok()); } -pub fn fuzz_that_verify_rejects_invalid_proofs(input: &[u8]) { +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 + // - 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(); + + test_trie_codec_proof::(data, keys); +} + +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()).is_err()); } fn test_generate_proof( @@ -363,3 +372,75 @@ 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 reference_trie::{ + 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() { + 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); + } +} + + + +#[test] +fn debug_error() { + let input = [0x0,0x0,0x0,0xcd,0xf8,0xff,0xff,0xff,0x0,0xcd,0x2]; +// let input = [0x40,0x0,0xe6,0xff,0xff,0x40,0x0,0xe6,0xff,0xff,0x2b]; + fuzz_that_verify_accepts_valid_proofs::(&input[..]); + fuzz_that_trie_codec_proofs::(&input[..]); +} diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index 4e8d575c..f1d5337f 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; +use hash_db::{Hasher, EMPTY_PREFIX}; +use crate::node_codec::HashDBHybridDyn as HashDB; use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayout, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 1f5f4747..5e0f8338 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -17,13 +17,14 @@ //! implementation. //! See `trie_visit` function. -use hash_db::{Hasher, HashDB, Prefix}; -use crate::rstd::{cmp::max, marker::PhantomData, vec::Vec}; +use hash_db::{Hasher, HashDB, Prefix, HasherHybrid, HashDBHybrid, BinaryHasher}; +use crate::rstd::{cmp::max, marker::PhantomData, vec::Vec, EmptyIter, ops::Range}; use crate::triedbmut::{ChildReference}; use crate::nibble::NibbleSlice; use crate::nibble::nibble_ops; -use crate::node_codec::NodeCodec; +use crate::node_codec::{NodeCodec, NodeCodecHybrid, ChildProofHeader}; use crate::{TrieLayout, TrieHash}; +use crate::rstd::borrow::Borrow; macro_rules! exponential_out { (@3, [$($inpp:expr),*]) => { exponential_out!(@2, [$($inpp,)* $($inpp),*]) }; @@ -50,6 +51,15 @@ struct CacheAccum (Vec<(ArrayNode, Option, usize)>, Phan /// Initially allocated cache depth. const INITIAL_DEPTH: usize = 10; +#[inline] +fn register_children_buf() -> Option<[Option>; 16]> { + if T::HYBRID_HASH { + Some(Default::default()) + } else { + None + } +} + impl CacheAccum where T: TrieLayout, @@ -134,7 +144,8 @@ impl CacheAccum &k2.as_ref()[..], k2.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - nkey.len(), ); - let hash = callback.process(pr.left(), encoded, false); + let iter: Option<(EmptyIter>, _)> = None; + let hash = callback.process(pr.left(), (encoded, ChildProofHeader::Unused), false, iter); // insert hash in branch (first level branch only at this point) self.set_node(target_depth, nibble_value as usize, Some(hash)); @@ -195,19 +206,38 @@ impl CacheAccum // 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()), - ); - self.reset_depth(branch_d); + + let mut register_children = register_children_buf::(); + let encoded = if let Some(register_children) = register_children.as_mut() { + T::Codec::branch_node_common( + self.0[last].0.as_ref().iter(), + v.as_ref().map(|v| v.as_ref()), + Some(register_children.as_mut()) + ) + } else { + (T::Codec::branch_node( + self.0[last].0.as_ref().iter(), + v.as_ref().map(|v| v.as_ref()), + ), ChildProofHeader::Unused) + }; let pr = NibbleSlice::new_offset(&key_branch, branch_d); - let branch_hash = callback.process(pr.left(), encoded, is_root && nkey.is_none()); + let branch_hash = if T::HYBRID_HASH { + let len = self.0[last].0.as_ref().iter().filter(|v| v.is_some()).count(); + let children = self.0[last].0.as_ref().iter(); + callback.process(pr.left(), encoded, is_root && nkey.is_none(), Some((children, len))) + } else { + let iter: Option<(EmptyIter>, _)> = None; + callback.process(pr.left(), encoded, is_root && nkey.is_none(), iter) + }; + self.reset_depth(branch_d); if let Some(nkeyix) = nkey { let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); let nib = pr.right_range_iter(nkeyix.1); let encoded = T::Codec::extension_node(nib, nkeyix.1, branch_hash); - callback.process(pr.left(), encoded, is_root) + let iter: Option<(EmptyIter>, _)> = None; + let h = callback.process(pr.left(), (encoded, ChildProofHeader::Unused), is_root, iter); + h } else { branch_hash } @@ -226,19 +256,33 @@ impl CacheAccum debug_assert!(self.0[last].2 == branch_d); // 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 mut register_children = register_children_buf::(); let pr = NibbleSlice::new_offset(&key_branch, nkeyix.0); - 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())); + let encoded = if let Some(register_children) = register_children.as_mut() { + T::Codec::branch_node_nibbled_common( + pr.right_range_iter(nkeyix.1), + nkeyix.1, + self.0[last].0.as_ref().iter(), v.as_ref().map(|v| v.as_ref()), + Some(register_children.as_mut()), + ) + } else { + (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()), + ), ChildProofHeader::Unused) + }; + let result = if T::HYBRID_HASH { + let len = self.0[last].0.as_ref().iter().filter(|v| v.is_some()).count(); + let children = self.0[last].0.as_ref().iter(); + callback.process(pr.left(), encoded, is_root, Some((children, len))) + } else { + let iter: Option<(EmptyIter>, _)> = None; + callback.process(pr.left(), encoded, is_root, iter) + }; 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, - ); - callback.process(pr.left(), encoded, is_root) + result } } @@ -296,15 +340,17 @@ pub fn trie_visit(input: I, callback: &mut F) &k2.as_ref()[..], k2.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - nkey.len(), ); - callback.process(pr.left(), encoded, true); + let iter: Option<(EmptyIter>, _)> = None; + callback.process(pr.left(), (encoded, ChildProofHeader::Unused), true, iter); } 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); } } else { + let iter: Option<(EmptyIter>, _)> = None; // nothing null root corner case - callback.process(hash_db::EMPTY_PREFIX, T::Codec::empty_node().to_vec(), true); + callback.process(hash_db::EMPTY_PREFIX, (T::Codec::empty_node().to_vec(), ChildProofHeader::Unused), true, iter); } } @@ -317,7 +363,13 @@ 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, ChildProofHeader), + is_root: bool, + hybrid_hash: Option<(impl Iterator>>>, usize)>, + ) -> ChildReference; } /// Get trie root and insert visited node in a hash_db. @@ -335,13 +387,31 @@ impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { } } +/// 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 TrieBuilderHybrid<'a, H: HasherHybrid, HO, V, DB> { + db: &'a mut DB, + pub root: Option, + buffer: ::Buffer, + _ph: PhantomData<(H, V)>, +} + +impl<'a, H: HasherHybrid, HO, V, DB> TrieBuilderHybrid<'a, H, HO, V, DB> { + pub fn new(db: &'a mut DB) -> Self { + TrieBuilderHybrid { db, root: None, buffer: H::InnerHasher::init_buffer(), _ph: PhantomData } + } +} + + impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> for TrieBuilder<'a, H, ::Out, V, DB> { fn process( &mut self, prefix: Prefix, - encoded_node: Vec, + (encoded_node, _common): (Vec, ChildProofHeader), is_root: bool, + _hybrid_hash: Option<(impl Iterator>>>, usize)>, ) -> ChildReference<::Out> { let len = encoded_node.len(); if !is_root && len < ::LENGTH { @@ -358,6 +428,48 @@ impl<'a, H: Hasher, V, DB: HashDB> ProcessEncodedNode<::Out> } } +impl<'a, H: HasherHybrid, V, DB: HashDBHybrid> ProcessEncodedNode<::Out> + for TrieBuilderHybrid<'a, H, ::Out, V, DB> { + fn process( + &mut self, + prefix: Prefix, + (encoded_node, common): (Vec, ChildProofHeader), + is_root: bool, + hybrid_hash: Option<(impl Iterator>>>, usize)>, + ) -> ChildReference<::Out> { + let len = encoded_node.len(); + 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 = if let Some((children, nb_children)) = hybrid_hash { + let iter = children + .filter_map(|v| match v.borrow().as_ref() { + Some(ChildReference::Hash(v)) => Some(Some(v.clone())), + Some(ChildReference::Inline(v, _l)) => Some(Some(v.clone())), + None => None, + }); + self.db.insert_branch_hybrid( + prefix, + &encoded_node[..], + common.header(&encoded_node[..]), + nb_children, + iter, + &mut self.buffer, + ) + } else { + self.db.insert(prefix, &encoded_node[..]) + }; + if is_root { + self.root = Some(hash.clone()); + }; + ChildReference::Hash(hash) + } +} + /// Calculate the trie root of the trie. pub struct TrieRoot { /// The resulting root. @@ -375,8 +487,9 @@ impl ProcessEncodedNode<::Out> for TrieRoot, + (encoded_node, _common): (Vec, ChildProofHeader), is_root: bool, + _hybrid_hash: Option<(impl Iterator>>>, usize)>, ) -> ChildReference<::Out> { let len = encoded_node.len(); if !is_root && len < ::LENGTH { @@ -393,6 +506,59 @@ impl ProcessEncodedNode<::Out> for TrieRoot { + /// The resulting root. + pub root: Option, + buffer: ::Buffer, +} + +impl Default for TrieRootHybrid { + fn default() -> Self { + TrieRootHybrid { root: None, buffer: H::InnerHasher::init_buffer() } + } +} + +impl ProcessEncodedNode<::Out> for TrieRootHybrid::Out> { + fn process( + &mut self, + _: Prefix, + (encoded_node, common): (Vec, ChildProofHeader), + is_root: bool, + hybrid_hash: Option<(impl Iterator>>>, usize)>, + ) -> ChildReference<::Out> { + let len = encoded_node.len(); + 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 = if let Some((children, nb_children)) = hybrid_hash { + let iter = children + .filter_map(|v| match v.borrow().as_ref() { + Some(ChildReference::Hash(v)) => Some(Some(v.clone())), + Some(ChildReference::Inline(v, _l)) => Some(Some(v.clone())), + None => None, + }); + ::hash_hybrid( + common.header(&encoded_node[..]), + nb_children, + iter, + &mut self.buffer, + ) + } else { + ::hash(&encoded_node[..]) + }; + + if is_root { + self.root = Some(hash.clone()); + }; + ChildReference::Hash(hash) + } +} + + /// Get the trie root node encoding. pub struct TrieRootUnhashed { /// The resulting encoded root. @@ -406,6 +572,19 @@ impl Default for TrieRootUnhashed { } } +/// Get the trie root node encoding. +pub struct TrieRootUnhashedHybrid { + /// The resulting encoded root. + pub root: Option>, + buffer: ::Buffer, +} + +impl Default for TrieRootUnhashedHybrid { + fn default() -> Self { + TrieRootUnhashedHybrid { root: None, buffer: H::InnerHasher::init_buffer() } + } +} + #[cfg(feature = "std")] /// Calculate the trie root of the trie. /// Print a debug trace. @@ -427,8 +606,9 @@ impl ProcessEncodedNode<::Out> for TrieRootPrint, + (encoded_node, _common): (Vec, ChildProofHeader), is_root: bool, + _hybrid_hash: Option<(impl Iterator>>>, usize)>, ) -> ChildReference<::Out> { println!("Encoded node: {:x?}", &encoded_node); println!(" with prefix: {:x?}", &p); @@ -453,8 +633,9 @@ impl ProcessEncodedNode<::Out> for TrieRootUnhashed { fn process( &mut self, _: Prefix, - encoded_node: Vec, + (encoded_node, _common): (Vec, ChildProofHeader), is_root: bool, + _hybrid_hash: Option<(impl Iterator>>>, usize)>, ) -> ChildReference<::Out> { let len = encoded_node.len(); if !is_root && len < ::LENGTH { @@ -470,3 +651,42 @@ impl ProcessEncodedNode<::Out> for TrieRootUnhashed { ChildReference::Hash(hash) } } + +impl ProcessEncodedNode<::Out> for TrieRootUnhashedHybrid { + fn process( + &mut self, + _: Prefix, + (encoded_node, common): (Vec, ChildProofHeader), + is_root: bool, + hybrid_hash: Option<(impl Iterator>>>, usize)>, + ) -> ChildReference<::Out> { + let len = encoded_node.len(); + 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 = if let Some((children, nb_children)) = hybrid_hash { + let iter = children + .filter_map(|v| match v.borrow().as_ref() { + Some(ChildReference::Hash(v)) => Some(Some(v.clone())), + Some(ChildReference::Inline(v, _l)) => Some(Some(v.clone())), + None => None, + }); + ::hash_hybrid( + common.header(&encoded_node[..]), + nb_children, + iter, + &mut self.buffer, + ) + } else { + ::hash(&encoded_node[..]) + }; + + if is_root { + self.root = Some(encoded_node); + }; + ChildReference::Hash(hash) + } +} diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 37bfe64e..25da53d3 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -22,12 +22,15 @@ extern crate alloc; mod rstd { pub use std::{borrow, boxed, cmp, convert, fmt, hash, iter, marker, mem, ops, rc, result, vec}; pub use std::collections::VecDeque; + pub use std::collections::BTreeMap; pub use std::error::Error; + pub use std::iter::Empty as EmptyIter; } #[cfg(not(feature = "std"))] mod rstd { pub use core::{borrow, convert, cmp, iter, fmt, hash, marker, mem, ops, result}; + pub use core::iter::Empty as EmptyIter; pub use alloc::{boxed, rc, vec}; pub use alloc::collections::VecDeque; pub trait Error {} @@ -57,7 +60,7 @@ mod nibble; mod node_codec; mod trie_codec; -pub use hash_db::{HashDB, HashDBRef, Hasher}; +pub use hash_db::{HashDB, HashDBRef, Hasher, BinaryHasher, HasherHybrid, HashDBHybrid}; pub use self::triedb::{TrieDB, TrieDBIterator}; pub use self::triedbmut::{TrieDBMut, ChildReference}; pub use self::sectriedbmut::SecTrieDBMut; @@ -67,11 +70,13 @@ pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; pub use self::nibble::{NibbleSlice, NibbleVec, nibble_ops}; -pub use crate::node_codec::{NodeCodec, Partial}; -pub use crate::iter_build::{trie_visit, ProcessEncodedNode, - TrieBuilder, TrieRoot, TrieRootUnhashed}; +pub use crate::node_codec::{NodeCodec, NodeCodecHybrid, Partial, HashDBHybridDyn, ChildProofHeader, + Bitmap, BITMAP_LENGTH, HashesPlan, hybrid_hash_node_adapter}; +pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieRootUnhashedHybrid, + TrieBuilder, TrieRoot, TrieRootUnhashed, TrieRootHybrid, TrieBuilderHybrid}; pub use crate::iterator::TrieDBNodeIterator; -pub use crate::trie_codec::{decode_compact, decode_compact_from_iter, encode_compact}; +pub use crate::trie_codec::{decode_compact, decode_compact_from_iter, + encode_compact, binary_additional_hashes}; #[cfg(feature = "std")] pub use crate::iter_build::TrieRootPrint; @@ -347,7 +352,7 @@ where /// Create new mutable instance of Trie. pub fn create( &self, - db: &'db mut dyn HashDB, + db: &'db mut dyn HashDBHybridDyn, root: &'db mut TrieHash, ) -> Box + 'db> { match self.spec { @@ -360,7 +365,7 @@ where /// Create new mutable instance of trie and check for errors. pub fn from_existing( &self, - db: &'db mut dyn HashDB, + db: &'db mut dyn HashDBHybridDyn, root: &'db mut TrieHash, ) -> Result + 'db>, TrieHash, CError> { match self.spec { @@ -384,10 +389,15 @@ pub trait TrieLayout { const USE_EXTENSION: bool; /// If true, the trie will allow empty values into `TrieDBMut` const ALLOW_EMPTY: bool = false; + /// Does the layout implement a hybrid hash. + /// Note that if does not, the `NodeCodecHybrid` hash + /// associated codec only really need to implement `NodeCodec` + /// and dummy implementation can be used. + const HYBRID_HASH: bool; /// Hasher to use for this trie. - type Hash: Hasher; + type Hash: HasherHybrid; /// Codec to use (needs to match hasher and nibble ops). - type Codec: NodeCodec::Out>; + type Codec: NodeCodecHybrid::Out>; } /// This trait associates a trie definition with preferred methods. @@ -396,14 +406,20 @@ 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, + DB: HashDB + HashDBHybrid, I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, { - let mut cb = TrieBuilder::new(db); - trie_visit::(input.into_iter(), &mut cb); - cb.root.unwrap_or_default() + if Self::HYBRID_HASH { + let mut cb = TrieBuilderHybrid::new(db); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or_default() + } else { + 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 @@ -411,9 +427,15 @@ pub trait TrieConfiguration: Sized + TrieLayout { A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, { - let mut cb = TrieRoot::::default(); - trie_visit::(input.into_iter(), &mut cb); - cb.root.unwrap_or_default() + if Self::HYBRID_HASH { + let mut cb = TrieRootHybrid::::default(); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or_default() + } else { + 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 @@ -421,9 +443,15 @@ pub trait TrieConfiguration: Sized + TrieLayout { A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, { - let mut cb = TrieRootUnhashed::::default(); - trie_visit::(input.into_iter(), &mut cb); - cb.root.unwrap_or_default() + if Self::HYBRID_HASH { + let mut cb = TrieRootUnhashedHybrid::::default(); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or_default() + } else { + let mut cb = TrieRootUnhashed::::default(); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or_default() + } } /// Encoding of index as a key (when reusing general trie for /// indexed trie). diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index ef50e8d6..6dda4101 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -57,6 +57,16 @@ pub enum Node<'a> { NibbledBranch(NibbleSlice<'a>, [Option>; nibble_ops::NIBBLE_LENGTH], Option<&'a [u8]>), } +impl<'a> Node<'a> { + /// Check if this is a branch node plan. + pub fn is_branch(&self) -> bool { + match self { + Node::Branch(..) | Node::NibbledBranch(..) => true, + _ => false, + } + } +} + /// A `NodeHandlePlan` is a decoding plan for constructing a `NodeHandle` from an encoded trie /// node. This is used as a substructure of `NodePlan`. See `NodePlan` for details. #[derive(Debug, Clone, PartialEq, Eq)] @@ -75,6 +85,27 @@ impl NodeHandlePlan { NodeHandlePlan::Inline(range) => NodeHandle::Inline(&data[range.clone()]), } } + + /// Range of node handle definition in encoded data. + pub fn as_hash<'a, 'b, HOut: Default + AsMut<[u8]>>(&'a self, encoded_node: &[u8]) -> HOut { + let mut dest = HOut::default(); + + match self { + NodeHandlePlan::Inline(range) + | NodeHandlePlan::Hash(range) => { + dest.as_mut()[..range.len()].copy_from_slice(&encoded_node[range.clone()]); + }, + } + dest + } + + /// Check if it is an inline node. + pub fn is_inline(&self) -> bool { + match self { + NodeHandlePlan::Hash(_) => false, + NodeHandlePlan::Inline(_) => true, + } + } } /// A `NibbleSlicePlan` is a blueprint for decoding a nibble slice from a byte slice. The @@ -172,6 +203,14 @@ impl NodePlan { }, } } + + /// Check if this is a branch node plan. + pub fn is_branch(&self) -> bool { + match self { + NodePlan::Branch{..} | NodePlan::NibbledBranch{..} => true, + _ => false, + } + } } /// An `OwnedNode` is an owned type from which a `Node` can be constructed which borrows data from diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index ad33ebf5..4399f85b 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -18,9 +18,9 @@ use crate::MaybeDebug; use crate::node::{Node, NodePlan}; use crate::ChildReference; +use hash_db::{HasherHybrid, BinaryHasher}; -use crate::rstd::{borrow::Borrow, Error, hash, vec::Vec}; - +use crate::rstd::{borrow::Borrow, Error, hash, vec::Vec, ops::Range}; /// Representation of a nible slice (right aligned). /// It contains a right aligned padded first byte (first pair element is the number of nibbles @@ -83,3 +83,328 @@ pub trait NodeCodec: Sized { value: Option<&[u8]> ) -> Vec; } + +/// Trait for handling hybrid proof. +/// This adds methods to basic node codec in order to support: +/// - storage encoding with existing `NodeCodec` methods +/// - encode a proof specific representation. (usually the common representation and +/// the merkle proof of the children stored encoded hash). +/// - Intermediate optional common representation shared between storage +pub trait NodeCodecHybrid: NodeCodec { + /// Sequence of hashes needed for the children proof verification. + type AdditionalHashesPlan: Iterator>; + + /// Technical function to implement `decode_compact_proof`. + fn decode_plan_compact_proof(data: &[u8]) -> Result<( + NodePlan, + Option<(Bitmap, Self::AdditionalHashesPlan)>, + ), Self::Error>; + + /// Decode a `Node` from a proof. The node can miss some branch that + /// are not use by the proof, in this case they are stored as an inline + /// zero length child. + /// + /// With resulting node is attached a bitmap of children included in + /// the proof calculation and a sequence of additianal node for proof + /// verification. + /// In practice this contains inline node that are in the proof and + /// and ommitted children hash (compacted hash). + fn decode_compact_proof(data: &[u8]) -> Result<( + Node, + Option<(Bitmap, HashesIter)>, + ), Self::Error> { + let (plan, hashes) = Self::decode_plan_compact_proof(data)?; + let hashes = hashes.map(|(bitmap, hashes)| (bitmap, HashesIter::new(data, hashes))); + Ok((plan.build(data), hashes)) + } + + /// Build compact proof encoding from branch info. + /// + /// - `hash_proof_header`: the part common with the header info from hash. + /// It can be calculated from `branch_node_common` through + /// `ChildProofHeader` call, or directly by `branch_node_for_hash`. + /// - `children`: contains all children, with compact (ommited children) defined as + /// a null length inline node. + /// - `hash_buff`: technical buffer for the hasher of the second level proof. + /// - `in_proof`: position of nodes that are included in proof, this includes + /// hash nodes (can be deducted from unsized children) and inline nodes. + /// The children to be include in the proof are therefore the compacted one and the + /// inline nodes only. + /// The other children value are needed because they can be included into the additional + /// hash, and are required for intermediate hash calculation. + fn encode_compact_proof( + hash_proof_header: Vec, + children: &[Option>], + in_proof: &[bool], + hash_buf: &mut H::Buffer, + ) -> Vec; + + /// Does the encoded content need a hybrid proof. + /// With current hybrid proof capability this is strictly + /// the same as checking if the node is a branch. + /// It return the proof header and the NodePlan if hybrid proof is needed. + fn need_hybrid_proof(data: &[u8]) -> crate::rstd::result::Result, ()>; + + /// Returns branch node encoded for storage, and additional information for hash calculation. + /// + /// Takes an iterator yielding `ChildReference` and an optional value + /// as input, the third input is an output container needed for hash calculation. + fn branch_node_common( + children: impl Iterator>>>, + value: Option<&[u8]>, + register_children: Option<&mut [Option>]>, + ) -> (Vec, ChildProofHeader); + + /// Variant of `branch_node_common` but with a nibble. + /// + /// `number_nibble` is the partial path length, it replaces the one + /// use by `extension_node`. + fn branch_node_nibbled_common( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator>>>, + value: Option<&[u8]>, + register_children: Option<&mut [Option>]>, + ) -> (Vec, ChildProofHeader); + + /// Returns branch node encoded information for hash. + /// Result is the same as `branch_node_common().1.header(branch_node_common().0`. + fn branch_node_for_hash( + children: impl Iterator>>>, + value: Option<&[u8]>, + ) -> Vec; + + /// Variant of `branch_node_for_hash` but with a nibble. + fn branch_node_nibbled_for_hash( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator>>>, + value: Option<&[u8]>, + ) -> Vec; + + /// Return a error from a static description. + /// Depending on implementation it is fine drop the description + /// and act as a default error semantic. + fn codec_error(desc: &'static str) -> Self::Error; +} + +/// Information to fetch bytes that needs to be include when calculating a node hash. +/// The node hash is the hash of these information and the merkle root of its children. +#[derive(Clone)] +pub enum ChildProofHeader { + /// No need for hybrid hash. + Unused, + /// Range over the branch encoded for storage. + Range(Range), + /// Allocated in case we cannot use a range. + Allocated(Vec), +} + +impl ChildProofHeader { + pub fn header<'a>(&'a self, encoded: &'a [u8]) -> &'a [u8] { + match self { + ChildProofHeader::Unused => encoded, + ChildProofHeader::Range(range) => &encoded[range.clone()], + ChildProofHeader::Allocated(buff) => &buff[..], + } + } +} + +use hash_db::{HashDB, Prefix, HashDBRef, Hasher, HashDBHybrid}; + +pub trait HashDBHybridDyn: Send + Sync + HashDB { + /// Insert a datum item into the DB and return the datum's hash for a later lookup. Insertions + /// are counted and the equivalent number of `remove()`s must be performed before the data + /// is considered dead. + fn insert_branch_hybrid( + &mut self, + prefix: Prefix, + value: &[u8], + children: &[Option>], + common: ChildProofHeader, + buffer: &mut ::Buffer, + ) -> H::Out; +} + +impl> HashDBHybridDyn for C { + fn insert_branch_hybrid( + &mut self, + prefix: Prefix, + value: &[u8], + children: &[Option>], + common: ChildProofHeader, + buffer: &mut ::Buffer, + ) -> H::Out { + let nb_children = children.iter().filter(|v| v.is_some()).count(); + let children = children.iter().map(|o_range| o_range.as_ref().map(|range| { + let mut dest = H::Out::default(); + dest.as_mut()[..range.len()].copy_from_slice(&value[range.clone()]); + dest + })); + + >::insert_branch_hybrid( + self, + prefix, + value, + common.header(value), + nb_children, + children, + buffer, + ) + } +} + +impl<'a, H: Hasher, T> HashDBRef for &'a dyn HashDBHybridDyn { + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { + self.as_hash_db().get(key, prefix) + } + + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + self.as_hash_db().contains(key, prefix) + } +} + +impl<'a, H: Hasher, T> HashDBRef for &'a mut dyn HashDBHybridDyn { + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { + self.as_hash_db().get(key, prefix) + } + + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + self.as_hash_db().contains(key, prefix) + } +} + +/// Children bitmap codec for radix 16 trie. +pub struct Bitmap(u16); + +/// Length of a 16 element bitmap. +pub const BITMAP_LENGTH: usize = 2; + +impl Bitmap { + + pub fn decode(data: &[u8]) -> Self { + let map = data[0] as u16 + data[1] as u16 * 256; + Bitmap(map) + } + + pub fn value_at(&self, i: usize) -> bool { + self.0 & (1u16 << i) != 0 + } + + pub fn encode>(has_children: I , output: &mut [u8]) { + let mut bitmap: u16 = 0; + let mut cursor: u16 = 1; + for v in has_children { + if v { bitmap |= cursor } + cursor <<= 1; + } + output[0] = (bitmap % 256) as u8; + output[1] = (bitmap / 256) as u8; + } +} + + +/// Simple implementation of a additional hash iterator based +/// upon a sequential encoding of known length. +pub struct HashesPlan { + hash_len: usize, + /// we use two size counter to implement `size_hint`. + end: usize, + offset: usize, +} + +impl HashesPlan { + pub fn new(nb_child: usize, offset: usize, hash_len: usize) -> Self { + HashesPlan { + end: offset + (hash_len * nb_child), + hash_len, + offset, + } + } +} + +/// Iterator over additional hashes +/// upon a sequential encoding of known length. +pub struct HashesIter<'a, I, HO> { + data: &'a [u8], + ranges: I, + buffer: HO, +} + +impl<'a, I, HO: Default> HashesIter<'a, I, HO> { + pub fn new(data: &'a [u8], ranges: I) -> Self { + HashesIter { + ranges, + data, + buffer: HO::default(), + } + } +} + +impl<'a, I, HO> Iterator for HashesIter<'a, I, HO> + where + I: Iterator>, + HO: AsMut<[u8]> + Clone, +{ + type Item = HO; + + fn next(&mut self) -> Option { + if let Some(range) = self.ranges.next() { + self.buffer.as_mut().copy_from_slice(&self.data[range]); + Some(self.buffer.clone()) + } else { + None + } + } +} + +impl Iterator for HashesPlan { + type Item = Range; + + fn next(&mut self) -> Option { + if self.offset < self.end { + self.offset += self.hash_len; + Some(Range { + start: self.offset - self.hash_len, + end: self.offset + }) + } else { + None + } + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.end / self.hash_len; + (size, Some(size)) + } +} + +/// Adapter standard implementation to use with `HashDBInsertComplex` function. +/// This can be use as a callback to insert encoded node into a hashdb when +/// using `insert_hybrid` method. +pub fn hybrid_hash_node_adapter, Hasher: HasherHybrid>( + encoded_node: &[u8] +) -> crate::rstd::result::Result, ()> { + Codec::need_hybrid_proof(encoded_node).map(|hybrid| + if let Some((node, common)) = hybrid { + match node { + NodePlan::Branch { children, .. } | NodePlan::NibbledBranch { children, .. } => { + let nb_children = children.iter().filter(|v| v.is_some()).count(); + let children = children.iter().map(|o_range| o_range.as_ref().map(|range| { + range.as_hash(encoded_node) + })); + let mut buf = ::InnerHasher::init_buffer(); + Some(Hasher::hash_hybrid( + common.header(encoded_node), + nb_children, + children, + &mut buf, + )) + }, + _ => unreachable!("hybrid only touch branch node"), + } + } else { + None + } + ) +} diff --git a/trie-db/src/proof/generate.rs b/trie-db/src/proof/generate.rs index 817e9333..f2c1c62d 100644 --- a/trie-db/src/proof/generate.rs +++ b/trie-db/src/proof/generate.rs @@ -18,15 +18,15 @@ use crate::rstd::{ boxed::Box, convert::TryInto, marker::PhantomData, ops::Range, vec, vec::Vec, }; -use hash_db::Hasher; +use hash_db::{Hasher, BinaryHasher, HasherHybrid}; use crate::{ CError, ChildReference, nibble::LeftNibbleSlice, nibble_ops::NIBBLE_LENGTH, NibbleSlice, node::{NodeHandle, NodeHandlePlan, NodePlan, OwnedNode}, NodeCodec, Recorder, Result as TrieResult, Trie, TrieError, TrieHash, - TrieLayout, + TrieLayout, NodeCodecHybrid, }; -struct StackEntry<'a, C: NodeCodec> { +struct StackEntry<'a, C: NodeCodec, H> { /// The prefix is the nibble path to the node in the trie. prefix: LeftNibbleSlice<'a>, node: OwnedNode>, @@ -41,15 +41,23 @@ struct StackEntry<'a, C: NodeCodec> { children: Vec>>, /// The index into the proof vector that the encoding of this entry should be placed at. output_index: Option, - _marker: PhantomData, + is_inline: bool, + /// Flags indicating whether each child is into the proof, this is all `omit_children` + /// child plus inline nodes. + in_proof_children: [bool; NIBBLE_LENGTH], + _marker: PhantomData<(C, H)>, } -impl<'a, C: NodeCodec> StackEntry<'a, C> { +impl<'a, C: NodeCodecHybrid, H: HasherHybrid> StackEntry<'a, C, H> + where + H: HasherHybrid, +{ fn new( prefix: LeftNibbleSlice<'a>, node_data: Vec, node_hash: Option, output_index: Option, + is_inline: bool, ) -> TrieResult { let node = OwnedNode::new::(node_data) @@ -69,12 +77,17 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { child_index: 0, children: vec![None; children_len], output_index, + is_inline, + in_proof_children: [false; NIBBLE_LENGTH], _marker: PhantomData::default(), }) } /// Encode this entry to an encoded trie node with data properly omitted. - fn encode_node(mut self) -> TrieResult, C::HashOut, C::Error> { + fn encode_node( + mut self, + hybrid: &mut Option<::Buffer>, + ) -> TrieResult, C::HashOut, C::Error> { let node_data = self.node.data(); Ok(match self.node.node_plan() { NodePlan::Empty => node_data.to_vec(), @@ -99,18 +112,35 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { ) } NodePlan::Branch { value, children } => { + let mut hash_buf = hybrid.as_mut(); + let hybrid = hash_buf.is_some(); Self::complete_branch_children( node_data, children, self.child_index, - &mut self.children + &mut self.children, )?; - C::branch_node( - self.children.into_iter(), - value_with_omission(node_data, value, self.omit_value) - ) + if !self.is_inline && hybrid { + let hash_proof_header = C::branch_node_for_hash( + self.children.iter(), + value_with_omission(node_data, value, self.omit_value), + ); + C::encode_compact_proof::( + hash_proof_header, + &self.children[..], + &self.in_proof_children[..], + hash_buf.as_mut().expect("hybrid is true"), + ) + } else { + C::branch_node( + self.children.into_iter(), + value_with_omission(node_data, value, self.omit_value), + ) + } }, NodePlan::NibbledBranch { partial: partial_plan, value, children } => { + let mut hash_buf = hybrid.as_mut(); + let hybrid = hash_buf.is_some(); let partial = partial_plan.build(node_data); Self::complete_branch_children( node_data, @@ -118,12 +148,27 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { self.child_index, &mut self.children )?; - C::branch_node_nibbled( - partial.right_iter(), - partial.len(), - self.children.into_iter(), - value_with_omission(node_data, value, self.omit_value) - ) + if !self.is_inline && hybrid { + let hash_proof_header = C::branch_node_nibbled_for_hash( + partial.right_iter(), + partial.len(), + self.children.iter(), + value_with_omission(node_data, value, self.omit_value), + ); + C::encode_compact_proof::( + hash_proof_header, + &self.children[..], + &self.in_proof_children[..], + hash_buf.as_mut().expect("hybrid is true"), + ) + } else { + C::branch_node_nibbled( + partial.right_iter(), + partial.len(), + self.children.into_iter(), + value_with_omission(node_data, value, self.omit_value), + ) + } }, }) } @@ -166,14 +211,14 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { thus they are never descended into; \ thus set_child will not be called on an entry with one of these types" ), - NodePlan::Extension { child, .. } => { + NodePlan::Extension { child, .. } => { assert_eq!( self.child_index, 0, "extension nodes only have one child; \ set_child is called when the only child is popped from the stack; \ child_index is 0 before child is pushed to the stack; qed" ); - Some(Self::replacement_child_ref(encoded_child, child)) + (false, Some(Self::replacement_child_ref(encoded_child, child))) } NodePlan::Branch { children, .. } | NodePlan::NibbledBranch { children, .. } => { assert!( @@ -182,12 +227,15 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { set_child is called when the only child is popped from the stack; \ child_index is (trie: &T, keys: I) .collect::>(); keys.sort(); keys.dedup(); + let mut hybrid = if L::HYBRID_HASH { + Some(<::InnerHasher as hash_db::BinaryHasher>::init_buffer()) + } else { + None + }; + let hybrid = &mut hybrid; + // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. - let mut stack = >>::new(); + let mut stack = >>::new(); // The mutated trie nodes comprising the final proof. let mut proof_nodes = Vec::new(); @@ -244,7 +299,7 @@ pub fn generate_proof<'a, T, L, I, K>(trie: &T, keys: I) let key = LeftNibbleSlice::new(key_bytes); // Unwind the stack until the new entry is a child of the last entry on the stack. - unwind_stack(&mut stack, &mut proof_nodes, Some(&key))?; + unwind_stack(&mut stack, &mut proof_nodes, Some(&key), hybrid)?; // Perform the trie lookup for the next key, recording the sequence of nodes traversed. let mut recorder = Recorder::new(); @@ -309,6 +364,7 @@ pub fn generate_proof<'a, T, L, I, K>(trie: &T, keys: I) child_record.data, Some(child_record.hash), Some(output_index), + false, )? } NodeHandle::Inline(data) => { @@ -322,6 +378,7 @@ pub fn generate_proof<'a, T, L, I, K>(trie: &T, keys: I) data.to_vec(), None, None, + true, )? } }; @@ -350,7 +407,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, hybrid)?; Ok(proof_nodes) } @@ -496,11 +553,14 @@ fn value_with_omission<'a>( /// 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. -fn unwind_stack( - stack: &mut Vec>, +fn unwind_stack( + stack: &mut Vec>, proof_nodes: &mut Vec>, maybe_key: Option<&LeftNibbleSlice>, + hybrid: &mut Option<::Buffer>, ) -> TrieResult<(), C::HashOut, C::Error> + where + H: HasherHybrid, { while let Some(entry) = stack.pop() { match maybe_key { @@ -512,7 +572,7 @@ fn unwind_stack( _ => { // Pop and finalize node from the stack. let index = entry.output_index; - let encoded = entry.encode_node()?; + let encoded = entry.encode_node(hybrid)?; if let Some(parent_entry) = stack.last_mut() { parent_entry.set_child(&encoded); } diff --git a/trie-db/src/proof/verify.rs b/trie-db/src/proof/verify.rs index eba4cb6a..4516e4a5 100644 --- a/trie-db/src/proof/verify.rs +++ b/trie-db/src/proof/verify.rs @@ -13,13 +13,14 @@ //! Verification of compact proofs for Merkle-Patricia tries. use crate::rstd::{ - convert::TryInto, iter::Peekable, marker::PhantomData, result::Result, vec, vec::Vec, + convert::TryInto, iter::Peekable, marker::PhantomData, result::Result, vec::Vec, }; use crate::{ CError, ChildReference, nibble::LeftNibbleSlice, nibble_ops::NIBBLE_LENGTH, - node::{Node, NodeHandle}, NodeCodec, TrieHash, TrieLayout, + node::{Node, NodeHandle}, NodeCodecHybrid, TrieHash, TrieLayout, ChildProofHeader, }; -use hash_db::Hasher; +use hash_db::{Hasher, BinaryHasher, HasherHybrid}; +use crate::node_codec::{Bitmap, HashesIter}; /// Errors that may occur during proof verification. Most of the errors types simply indicate that @@ -94,7 +95,7 @@ impl std::error::Error for } } -struct StackEntry<'a, C: NodeCodec> { +struct StackEntry<'a, C: NodeCodecHybrid, H> { /// The prefix is the nibble path to the node in the trie. prefix: LeftNibbleSlice<'a>, node: Node<'a>, @@ -105,43 +106,52 @@ struct StackEntry<'a, C: NodeCodec> { /// 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: [Option>; NIBBLE_LENGTH], + /// Proof info if a hybrid proof is needed. + hybrid: Option<(Bitmap, HashesIter<'a, C::AdditionalHashesPlan, C::HashOut>)>, + _marker: PhantomData<(C, H)>, } -impl<'a, C: NodeCodec> StackEntry<'a, C> { - fn new(node_data: &'a [u8], prefix: LeftNibbleSlice<'a>, is_inline: bool) +impl<'a, C: NodeCodecHybrid, H: BinaryHasher> StackEntry<'a, C, H> + where + H: BinaryHasher, +{ + fn new(node_data: &'a [u8], prefix: LeftNibbleSlice<'a>, is_inline: bool, hybrid: bool) -> Result> { - let node = C::decode(node_data) - .map_err(Error::DecodeError)?; - let children_len = match node { - Node::Empty | Node::Leaf(..) => 0, - Node::Extension(..) => 1, - Node::Branch(..) | Node::NibbledBranch(..) => NIBBLE_LENGTH, + let children = [None; NIBBLE_LENGTH]; + let (node, hybrid) = if !is_inline && hybrid { + let encoded_node = node_data; + C::decode_compact_proof(encoded_node) + .map_err(Error::DecodeError)? + } else { + (C::decode(node_data) + .map_err(Error::DecodeError)?, None) }; let value = match node { Node::Empty | Node::Extension(_, _) => None, Node::Leaf(_, value) => Some(value), Node::Branch(_, value) | Node::NibbledBranch(_, _, value) => value, }; + Ok(StackEntry { node, is_inline, prefix, value, child_index: 0, - children: vec![None; children_len], + children, + hybrid, _marker: PhantomData::default(), }) } /// 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<(Vec, ChildProofHeader), Error> { self.complete_children()?; Ok(match self.node { Node::Empty => - C::empty_node().to_vec(), + (C::empty_node().to_vec(), ChildProofHeader::Unused), Node::Leaf(partial, _) => { let value = self.value .expect( @@ -149,29 +159,33 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { value is only ever reassigned in the ValueMatch::MatchesLeaf match \ clause, which assigns only to Some" ); - C::leaf_node(partial.right(), value) + (C::leaf_node(partial.right(), value), ChildProofHeader::Unused) } Node::Extension(partial, _) => { let child = self.children[0] .expect("the child must be completed since child_index is 1"); - C::extension_node( + (C::extension_node( partial.right_iter(), partial.len(), child - ) + ), ChildProofHeader::Unused) } - Node::Branch(_, _) => - C::branch_node( + Node::Branch(_, _) => { + C::branch_node_common( self.children.iter(), self.value, - ), - Node::NibbledBranch(partial, _, _) => - C::branch_node_nibbled( + None, + ) + }, + Node::NibbledBranch(partial, _, _) => { + C::branch_node_nibbled_common( partial.right_iter(), partial.len(), self.children.iter(), self.value, - ), + None, + ) + }, }) } @@ -179,6 +193,7 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { &mut self, child_prefix: LeftNibbleSlice<'a>, proof_iter: &mut I, + hybrid: bool, ) -> Result> where I: Iterator>, @@ -187,7 +202,7 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { Node::Extension(_, child) => { // Guaranteed because of sorted keys order. assert_eq!(self.child_index, 0); - Self::make_child_entry(proof_iter, child, child_prefix) + Self::make_child_entry(proof_iter, child, child_prefix, hybrid) } Node::Branch(children, _) | Node::NibbledBranch(_, children, _) => { // because this is a branch @@ -205,7 +220,7 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { } let child = children[self.child_index] .expect("guaranteed by advance_item"); - Self::make_child_entry(proof_iter, child, child_prefix) + Self::make_child_entry(proof_iter, child, child_prefix, hybrid) } _ => panic!("cannot have children"), } @@ -239,6 +254,7 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { proof_iter: &mut I, child: NodeHandle<'a>, prefix: LeftNibbleSlice<'a>, + hybrid: bool, ) -> Result> where I: Iterator>, @@ -248,9 +264,9 @@ impl<'a, C: NodeCodec> StackEntry<'a, C> { if data.is_empty() { let node_data = proof_iter.next() .ok_or(Error::IncompleteProof)?; - StackEntry::new(node_data, prefix, false) + StackEntry::new(node_data, prefix, false, hybrid) } else { - StackEntry::new(data, prefix, true) + StackEntry::new(data, prefix, true, hybrid) } } NodeHandle::Hash(data) => { @@ -423,7 +439,7 @@ pub fn verify_proof<'a, L, I, K, V>(root: &::Out, proof: &[Ve // 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, @@ -432,19 +448,27 @@ pub fn verify_proof<'a, L, I, K, V>(root: &::Out, proof: &[Ve let mut last_entry = StackEntry::new( root_node, LeftNibbleSlice::new(&[]), - false + false, + L::HYBRID_HASH, )?; + let mut hybrid_buf = if L::HYBRID_HASH { + Some(::InnerHasher::init_buffer()) + } else { None }; loop { // Insert omitted value. match last_entry.advance_item(&mut items_iter)? { Step::Descend(child_prefix) => { - let next_entry = last_entry.advance_child_index(child_prefix, &mut proof_iter)?; + let next_entry = last_entry.advance_child_index( + child_prefix, + &mut proof_iter, + L::HYBRID_HASH, + )?; stack.push(last_entry); last_entry = next_entry; } Step::UnwindStack => { let is_inline = last_entry.is_inline; - let node_data = last_entry.encode_node()?; + let (node_data, common) = last_entry.encode_node()?; let child_ref = if is_inline { if node_data.len() > L::Hash::LENGTH { @@ -454,8 +478,42 @@ pub fn verify_proof<'a, L, I, K, V>(root: &::Out, proof: &[Ve &mut 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); - ChildReference::Hash(hash) + ChildReference::Hash(if let Some((bitmap_keys, additional_hash)) = last_entry.hybrid { + let children = last_entry.children; + let nb_children = children.iter().filter(|v| v.is_some()).count(); + let children = children.iter() + .enumerate() + .filter_map(|(ix, v)| { + v.as_ref().map(|v| (ix, v.clone())) + }) + .map(|(ix, child_ref)| { + // Use of bitmap_keys here to avoid + // adding a reference that is ommitted + // from the proof. + if bitmap_keys.value_at(ix) { + Some(match child_ref { + ChildReference::Hash(h) => h, + ChildReference::Inline(h, _) => h, + }) + } else { + None + } + }); + + if let Some(h) = L::Hash::hash_hybrid_proof( + &common.header(node_data.as_slice())[..], + nb_children, + children, + additional_hash.into_iter(), + hybrid_buf.as_mut().expect("Initialized for hybrid above"), + ) { + h + } else { + return Err(Error::DecodeError(L::Codec::codec_error("Invalid branch encoding for proof"))); + } + } else { + L::Hash::hash(&node_data) + }) }; if let Some(entry) = stack.pop() { @@ -482,4 +540,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/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index d5be23cd..cf69ce34 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -12,7 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hash_db::{HashDB, Hasher}; +use hash_db::{Hasher}; +use crate::node_codec::HashDBHybridDyn as HashDB; use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayout, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. diff --git a/trie-db/src/trie_codec.rs b/trie-db/src/trie_codec.rs index 68d956bb..91eb21e1 100644 --- a/trie-db/src/trie_codec.rs +++ b/trie-db/src/trie_codec.rs @@ -25,16 +25,18 @@ //! 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::{HasherHybrid, HashDBHybrid, BinaryHasher}; use crate::{ CError, ChildReference, DBValue, NibbleVec, NodeCodec, Result, - TrieHash, TrieError, TrieDB, TrieDBNodeIterator, TrieLayout, + TrieHash, TrieError, TrieDB, TrieDBNodeIterator, TrieLayout, NodeCodecHybrid, nibble_ops::NIBBLE_LENGTH, node::{Node, NodeHandle, NodeHandlePlan, NodePlan, OwnedNode}, }; +use crate::node_codec::{Bitmap, ChildProofHeader}; use crate::rstd::{ boxed::Box, convert::TryInto, marker::PhantomData, rc::Rc, result, vec, vec::Vec, + ops::Range, }; - +use ordered_trie::{SequenceBinaryTree, HashProof, trie_root, UsizeKeyNode}; struct EncoderStackEntry { /// The prefix is the nibble path to the node in the trie. prefix: NibbleVec, @@ -43,14 +45,17 @@ struct EncoderStackEntry { /// 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, + omit_children: [bool; NIBBLE_LENGTH], + /// Flags indicating whether each child is into the proof, this is all `omit_children` + /// child plus inline nodes. + in_proof_children: [bool; NIBBLE_LENGTH], /// The encoding of the subtrie nodes rooted at this entry, which is built up in /// `encode_compact`. output_index: usize, _marker: PhantomData, } -impl EncoderStackEntry { +impl EncoderStackEntry { /// Given the prefix of the next child node, identify its index and advance `child_index` to /// that. For a given entry, this must be called sequentially only with strictly increasing /// child prefixes. Returns an error if the child prefix is not a child of this entry or if @@ -94,8 +99,15 @@ impl EncoderStackEntry { Ok(()) } + /// Generates the encoding of the subtrie rooted at this entry. - fn encode_node(&self) -> Result, C::HashOut, C::Error> { + fn encode_node( + &self, + hybrid: &mut Option, + ) -> Result, C::HashOut, C::Error> + where + H: HasherHybrid, + { let node_data = self.node.data(); Ok(match self.node.node_plan() { NodePlan::Empty | NodePlan::Leaf { .. } => node_data.to_vec(), @@ -107,22 +119,52 @@ impl EncoderStackEntry { let empty_child = ChildReference::Inline(C::HashOut::default(), 0); C::extension_node(partial.right_iter(), partial.len(), empty_child) } - } + }, NodePlan::Branch { value, children } => { - C::branch_node( - Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value.clone().map(|range| &node_data[range]) - ) - } + let children = Self::branch_children(node_data, &children, &self.omit_children[..])?; + if let Some(hash_buf) = hybrid.as_mut() { + let hash_proof_header = C::branch_node_for_hash( + children.iter(), + value.clone().map(|range| &node_data[range]), + ); + C::encode_compact_proof::( + hash_proof_header, + &children[..], + &self.in_proof_children[..], + hash_buf, + ) + } else { + C::branch_node( + children.iter(), + value.clone().map(|range| &node_data[range]), + ) + } + }, NodePlan::NibbledBranch { partial, value, children } => { + let children = Self::branch_children(node_data, &children, &self.omit_children[..])?; let partial = partial.build(node_data); - C::branch_node_nibbled( - partial.right_iter(), - partial.len(), - Self::branch_children(node_data, &children, &self.omit_children)?.iter(), - value.clone().map(|range| &node_data[range]) - ) - } + if let Some(hash_buf) = hybrid.as_mut() { + let hash_proof_header = C::branch_node_nibbled_for_hash( + partial.right_iter(), + partial.len(), + children.iter(), + value.clone().map(|range| &node_data[range]), + ); + C::encode_compact_proof::( + hash_proof_header, + &children[..], + &self.in_proof_children, + hash_buf, + ) + } else { + C::branch_node_nibbled( + partial.right_iter(), + partial.len(), + children.iter(), + value.clone().map(|range| &node_data[range]), + ) + } + }, }) } @@ -171,6 +213,13 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE { let mut output = Vec::new(); + let mut hybrid = if L::HYBRID_HASH { + Some(::init_buffer()) + } else { + None + }; + let hybrid = &mut hybrid; + // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. let mut stack: Vec> = Vec::new(); @@ -191,6 +240,25 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE // Skip inline nodes, as they cannot contain hash references to other nodes by // assumption. if node_hash.is_none() { + // still need to register them when in proof path for hybrid. + if L::HYBRID_HASH { + loop { + if let Some(last_entry) = stack.last_mut() { + if prefix.starts_with(&last_entry.prefix) { + let child_index = prefix.at(prefix.len() - 1) as usize; + last_entry.in_proof_children[child_index] = true; + break; + } else { + // unstack as in not inline case + if let Some(last_entry) = stack.pop() { + output[last_entry.output_index] = last_entry.encode_node::( + hybrid, + )?; + } + } + } else { break; } + } + } continue; } @@ -208,24 +276,23 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE TrieDBNodeIterator or this function" ); last_entry.omit_children[last_entry.child_index] = true; + last_entry.in_proof_children[last_entry.child_index] = true; last_entry.child_index += 1; stack.push(last_entry); break; } else { - output[last_entry.output_index] = last_entry.encode_node()?; + output[last_entry.output_index] = last_entry.encode_node::( + hybrid, + )?; } } - let children_len = match node.node_plan() { - NodePlan::Empty | NodePlan::Leaf { .. } => 0, - NodePlan::Extension { .. } => 1, - NodePlan::Branch { .. } | NodePlan::NibbledBranch { .. } => NIBBLE_LENGTH, - }; stack.push(EncoderStackEntry { prefix, node, child_index: 0, - omit_children: vec![false; children_len], + omit_children: [false; NIBBLE_LENGTH], + in_proof_children: [false; NIBBLE_LENGTH], output_index: output.len(), _marker: PhantomData::default(), }); @@ -244,23 +311,27 @@ pub fn encode_compact(db: &TrieDB) -> Result>, TrieHash, CE } while let Some(entry) = stack.pop() { - output[entry.output_index] = entry.encode_node()?; + output[entry.output_index] = entry.encode_node::( + hybrid, + )?; } Ok(output) } -struct DecoderStackEntry<'a, C: NodeCodec> { +struct DecoderStackEntry<'a, C: NodeCodec, F> { node: Node<'a>, /// The next entry in the stack is a child of the preceding entry at this index. For branch /// nodes, the index is in [0, NIBBLE_LENGTH] and for extension nodes, the index is in [0, 1]. child_index: usize, /// The reconstructed child references. children: Vec>>, + /// Hybrid proof input + hybrid: Option<(Bitmap, F)>, _marker: PhantomData, } -impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { +impl<'a, C: NodeCodecHybrid, F> DecoderStackEntry<'a, C, F> { /// Advance the child index until either it exceeds the number of children or the child is /// marked as omitted. Omitted children are indicated by an empty inline reference. For each /// child that is passed over and not omitted, copy over the child reference from the node to @@ -286,20 +357,42 @@ impl<'a, C: NodeCodec> DecoderStackEntry<'a, C> { self.child_index += 1; } Node::Branch(children, _) | Node::NibbledBranch(_, children, _) => { - while self.child_index < NIBBLE_LENGTH { - match children[self.child_index] { - Some(NodeHandle::Inline(data)) if data.is_empty() => - return Ok(false), - Some(child) => { - let child_ref = child.try_into() - .map_err(|hash| Box::new( - TrieError::InvalidHash(C::HashOut::default(), hash) - ))?; - self.children[self.child_index] = Some(child_ref); + if let Some((bitmap_keys, _)) = self.hybrid.as_ref() { + while self.child_index < NIBBLE_LENGTH { + match children[self.child_index] { + Some(NodeHandle::Inline(data)) if data.is_empty() + // Use of bitmap_keys here to avoid going into + // a child that is ommitted from the binary hash proof. + && bitmap_keys.value_at(self.child_index) => { + return Ok(false); + }, + Some(child) => { + let child_ref = child.try_into() + .map_err(|hash| Box::new( + TrieError::InvalidHash(C::HashOut::default(), hash) + ))?; + self.children[self.child_index] = Some(child_ref); + }, + None => {}, } - None => {} + self.child_index += 1; + } + } else { + while self.child_index < NIBBLE_LENGTH { + match children[self.child_index] { + Some(NodeHandle::Inline(data)) if data.is_empty() => + return Ok(false), + Some(child) => { + let child_ref = child.try_into() + .map_err(|hash| Box::new( + TrieError::InvalidHash(C::HashOut::default(), hash) + ))?; + self.children[self.child_index] = Some(child_ref); + } + None => {} + } + self.child_index += 1; } - self.child_index += 1; } } _ => {} @@ -347,28 +440,47 @@ 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, register_children: Option<&mut [Option>]>) -> (Vec, ChildProofHeader) { match self.node { Node::Empty => - C::empty_node().to_vec(), + (C::empty_node().to_vec(), ChildProofHeader::Unused), Node::Leaf(partial, value) => - C::leaf_node(partial.right(), value), + (C::leaf_node(partial.right(), value), ChildProofHeader::Unused), Node::Extension(partial, _) => - C::extension_node( + (C::extension_node( partial.right_iter(), partial.len(), self.children[0] .expect("required by method precondition; qed"), - ), - Node::Branch(_, value) => - C::branch_node(self.children.into_iter(), value), - Node::NibbledBranch(partial, _, value) => - C::branch_node_nibbled( + ), ChildProofHeader::Unused), + Node::Branch(_, value) => if let Some(register_children) = register_children { + C::branch_node_common( + self.children.into_iter(), + value, + Some(register_children), + ) + } else { + (C::branch_node( + self.children.into_iter(), + value, + ), ChildProofHeader::Unused) + }, + Node::NibbledBranch(partial, _, value) => if let Some(register_children) = register_children { + C::branch_node_nibbled_common( partial.right_iter(), partial.len(), self.children.iter(), value, - ), + Some(register_children), + ) + } else { + (C::branch_node_nibbled( + partial.right_iter(), + partial.len(), + self.children.iter(), + value, + ), ChildProofHeader::Unused) + }, } } } @@ -389,7 +501,7 @@ pub fn decode_compact(db: &mut DB, encoded: &[Vec]) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, - DB: HashDB, + DB: HashDBHybrid, { decode_compact_from_iter::(db, encoded.iter().map(Vec::as_slice)) } @@ -399,19 +511,27 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) -> Result<(TrieHash, usize), TrieHash, CError> where L: TrieLayout, - DB: HashDB, + DB: HashDBHybrid, I: IntoIterator, { // The stack of nodes through a path in the trie. Each entry is a child node of the preceding // entry. - let mut stack: Vec> = Vec::new(); + let mut stack: Vec> = Vec::new(); // 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) - .map_err(|err| Box::new(TrieError::DecoderError(>::default(), err)))?; + let (node, hybrid) = if L::HYBRID_HASH { + L::Codec::decode_compact_proof(encoded_node) + .map_err(|err| Box::new(TrieError::DecoderError(>::default(), err)))? + } else { + ( + L::Codec::decode(encoded_node) + .map_err(|err| Box::new(TrieError::DecoderError(>::default(), err)))?, + None, + ) + }; let children_len = match node { Node::Empty | Node::Leaf(..) => 0, @@ -422,9 +542,13 @@ 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], + hybrid, _marker: PhantomData::default(), }; + let mut hybrid_buf = if L::HYBRID_HASH { + Some(::InnerHasher::init_buffer()) + } else { None }; loop { if !last_entry.advance_child_index()? { last_entry.push_to_prefix(&mut prefix); @@ -432,10 +556,54 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) break; } + let mut register_children: [Option<_>; NIBBLE_LENGTH]; + let mut register_children = if last_entry.hybrid.is_some() { + register_children = Default::default(); + Some(&mut register_children[..]) + } else { + None + }; + let hybrid = last_entry.hybrid.take(); // 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 (node_data, common) = last_entry.encode_node(register_children.as_mut().map(|r| r.as_mut())); + let node_hash = if let Some((_bitmap_keys, additional_hashes)) = hybrid { + let children = register_children.expect("Set to some if hybrid"); + let nb_children = children.iter().filter(|v| v.is_some()).count(); + let children = children.iter() + .filter_map(|v| v.clone()) + .map(|range| { + let len = range.end - range.start; + // We know that these are not ommitted node, they are + // rebuild at this point (otherwhise we could query + // bitmap_keys to ensure that). + if len > 0 { + let mut v = TrieHash::::default(); + v.as_mut()[..len].copy_from_slice(&node_data[range]); + Some(v) + } else { + None + } + }); + if let Some(hash) = db.insert_branch_hybrid_proof( + prefix.as_prefix(), + &node_data[..], + common.header(&node_data[..]), + nb_children, + children, + additional_hashes, + hybrid_buf.as_mut().expect("Initialized for hybrid above"), + ) { + hash + } else { + return Err(Box::new(TrieError::DecoderError( + >::default(), + L::Codec::codec_error("Invalid encoding on hybrid hash calculation"), + ))) + } + } else { + db.insert(prefix.as_prefix(), node_data.as_ref()) + }; if let Some(entry) = stack.pop() { last_entry = entry; @@ -451,3 +619,39 @@ pub fn decode_compact_from_iter<'a, L, DB, T, I>(db: &mut DB, encoded: I) Err(Box::new(TrieError::IncompleteDatabase(>::default()))) } + +/// Returns the additional hashes requires for a proof. +/// - `children`, the children for a branch +/// - `in_proof_children`, for all children, +/// indicates if it is included in the proof (inline node or +/// compacted node). +/// - `hash_buf` a buffer of the right size to compute the hash. +pub fn binary_additional_hashes( + children: &[Option>], + in_proof_children: &[bool], + hash_buf: &mut H::Buffer, +) -> Vec { + let nb_children = children.iter().filter(|v| v.is_some()).count(); + let tree = SequenceBinaryTree::new(nb_children); + + let to_prove = children.iter().zip(in_proof_children.iter()) + .filter_map(|(o_child, in_proof)| o_child.as_ref().map(|_| *in_proof)) + // correct iteration over binary tree + .zip(tree.iter_path_node_key::()) + .filter_map(|(in_proof, ix_key)| if in_proof { + Some(ix_key) + } else { + None + }); + + let mut callback = HashProof::::new(hash_buf, to_prove, true); + let hashes = children.iter() + .filter_map(|o_child| o_child.as_ref()) + .map(|child| match child { + ChildReference::Hash(h) => h.clone(), + ChildReference::Inline(h, _) => h.clone(), + }); + // Note that since call back skip some calculation, root result cannot be use in this case. + let _root = trie_root::<_, UsizeKeyNode, _, _>(&tree, hashes.clone().into_iter(), &mut callback); + callback.take_additional_hash() +} diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 279bafda..6132c253 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -35,12 +35,11 @@ use crate::rstd::{fmt, vec::Vec}; /// # Example /// ```ignore /// use hash_db::Hasher; -/// use reference_trie::{RefTrieDBMut, RefTrieDB, Trie, TrieMut}; +/// use reference_trie::{RefTrieDBMut, RefTrieDB, Trie, TrieMut, RefHasher}; /// use trie_db::DBValue; -/// use keccak_hasher::KeccakHasher; /// use memory_db::*; /// -/// let mut memdb = MemoryDB::, _>::default(); +/// let mut memdb = MemoryDB::, _>::default(); /// let mut root = Default::default(); /// RefTrieDBMut::new(&mut memdb, &mut root).insert(b"foo", b"bar").unwrap(); /// let t = RefTrieDB::new(&memdb, &root).unwrap(); diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 6338ba0f..3cbe8dcf 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -19,13 +19,15 @@ use super::{Result, TrieError, TrieMut, TrieLayout, TrieHash, CError}; use super::lookup::Lookup; use super::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, decode_hash}; -use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; +use crate::node_codec::HashDBHybridDyn; +use hash_db::{Hasher, Prefix, EMPTY_PREFIX, BinaryHasher, HasherHybrid}; use hashbrown::HashSet; -use crate::node_codec::NodeCodec; +use crate::node_codec::{NodeCodec, NodeCodecHybrid, ChildProofHeader}; use crate::nibble::{NibbleVec, NibbleSlice, nibble_ops, BackingByteVec}; use crate::rstd::{ boxed::Box, convert::TryFrom, hash::Hash, mem, ops::Index, result, vec::Vec, VecDeque, + ops::Range, }; #[cfg(feature = "std")] @@ -124,7 +126,7 @@ where fn inline_or_hash( parent_hash: H::Out, child: EncodedNodeHandle, - db: &dyn HashDB, + db: &dyn HashDBHybridDyn, storage: &mut NodeStorage ) -> Result, H::Out, C::Error> where @@ -149,7 +151,7 @@ where fn from_encoded<'a, 'b, C, H>( node_hash: H::Out, data: &'a[u8], - db: &dyn HashDB, + db: &dyn HashDBHybridDyn, storage: &'b mut NodeStorage, ) -> Result where @@ -203,59 +205,84 @@ where } // TODO: parallelize - fn into_encoded(self, mut child_cb: F) -> Vec - where - C: NodeCodec, + fn into_encoded( + self, + mut child_cb: F, + register_children: Option<&mut [Option>]>, + ) -> (Vec, ChildProofHeader) where + C: NodeCodecHybrid, F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { match self { - Node::Empty => C::empty_node().to_vec(), + Node::Empty => (C::empty_node().to_vec(), ChildProofHeader::Unused), Node::Leaf(partial, value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); - C::leaf_node(pr.right(), &value) + (C::leaf_node(pr.right(), &value), ChildProofHeader::Unused) }, 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( + (C::extension_node( it, pr.len(), c, - ) + ), ChildProofHeader::Unused) }, Node::Branch(mut children, value) => { - C::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))) - }), - value.as_ref().map(|v| &v[..]) - ) + // map the `NodeHandle`s from the Branch to `ChildReferences` + let children = children.iter_mut() + .map(Option::take) + .enumerate() + .map(|(i, maybe_child)| { + maybe_child.map(|child| child_cb(child, None, Some(i as u8))) + }); + if let Some(register_children) = register_children { + C::branch_node_common( + // map the `NodeHandle`s from the Branch to `ChildReferences` + children, + value.as_ref().map(|v| &v[..]), + Some(register_children), + ) + } else { + (C::branch_node( + children, + value.as_ref().map(|v| &v[..]), + ), ChildProofHeader::Unused) + } }, Node::NibbledBranch(partial, mut children, value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); - C::branch_node_nibbled( - it, - pr.len(), - // map the `NodeHandle`s from the Branch to `ChildReferences` - children.iter_mut() - .map(Option::take) - .enumerate() - .map(|(i, maybe_child)| { - //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)) - }) - }), - value.as_ref().map(|v| &v[..]) - ) + // map the `NodeHandle`s from the Branch to `ChildReferences` + let children = children.iter_mut() + .map(Option::take) + .enumerate() + .map(|(i, maybe_child)| { + //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)) + }) + }); + + if let Some(register_children) = register_children { + C::branch_node_nibbled_common( + it, + pr.len(), + children, + value.as_ref().map(|v| &v[..]), + Some(register_children), + ) + } else { + (C::branch_node_nibbled( + it, + pr.len(), + children, + value.as_ref().map(|v| &v[..]), + ), ChildProofHeader::Unused) + } }, } } @@ -384,7 +411,7 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { } } -/// A `Trie` implementation using a generic `HashDB` backing database. +/// A `Trie` implementation using a generic `HashDBHybridDyn` backing database. /// /// Use it as a `TrieMut` trait object. You can use `db()` to get the backing database object. /// Note that changes are not committed to the database until `commit` is called. @@ -395,16 +422,15 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { /// # Example /// ```ignore /// use hash_db::Hasher; -/// use reference_trie::{RefTrieDBMut, TrieMut}; +/// use reference_trie::{RefTrieDBMut, TrieMut, RefHasher}; /// use trie_db::DBValue; -/// use keccak_hasher::KeccakHasher; /// use memory_db::*; /// -/// 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); /// assert!(t.is_empty()); -/// assert_eq!(*t.root(), KeccakHasher::hash(&[0u8][..])); +/// assert_eq!(*t.root(), RefHasher::hash(&[0u8][..])); /// t.insert(b"foo", b"bar").unwrap(); /// assert!(t.contains(b"foo").unwrap()); /// assert_eq!(t.get(b"foo").unwrap().unwrap(), b"bar".to_vec()); @@ -416,21 +442,34 @@ where L: TrieLayout, { storage: NodeStorage>, - db: &'a mut dyn HashDB, + db: &'a mut dyn HashDBHybridDyn, root: &'a mut TrieHash, root_handle: NodeHandle>, death_row: HashSet<(TrieHash, (BackingByteVec, Option))>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. hash_count: usize, + hybrid_hash_buffer: Option<<::InnerHasher as BinaryHasher>::Buffer>, } impl<'a, L> TrieDBMut<'a, L> where L: TrieLayout, { + #[inline] + fn register_children_buf(node: &Node>) -> Option<[Option>; 16]> { + match node { + Node::NibbledBranch(..) | Node::Branch(..) => if L::HYBRID_HASH { + Some(Default::default()) + } else { + None + }, + _ => None, + } + + } /// Create a new trie with backing database `db` and empty `root`. - pub fn new(db: &'a mut dyn HashDB, root: &'a mut TrieHash) -> Self { + pub fn new(db: &'a mut dyn HashDBHybridDyn, root: &'a mut TrieHash) -> Self { *root = L::Codec::hashed_null_node(); let root_handle = NodeHandle::Hash(L::Codec::hashed_null_node()); @@ -441,13 +480,20 @@ where root_handle, death_row: HashSet::new(), hash_count: 0, + hybrid_hash_buffer: None, + } + } + + fn hybrid_hash_buffer_lazy_init(&mut self) { + if self.hybrid_hash_buffer.is_none() { + self.hybrid_hash_buffer = Some(::InnerHasher::init_buffer()) } } /// Create a new trie with the backing database `db` and `root. /// Returns an error if `root` does not exist. pub fn from_existing( - db: &'a mut dyn HashDB, + db: &'a mut dyn HashDBHybridDyn, root: &'a mut TrieHash, ) -> Result, CError> { if !db.contains(root, EMPTY_PREFIX) { @@ -462,15 +508,16 @@ where root_handle, death_row: HashSet::new(), hash_count: 0, + hybrid_hash_buffer: None, }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDB { + pub fn db(&self) -> &dyn HashDBHybridDyn { self.db } /// Get the backing database mutably. - pub fn db_mut(&mut self) -> &mut dyn HashDB { + pub fn db_mut(&mut self) -> &mut dyn HashDBHybridDyn { self.db } @@ -1179,7 +1226,7 @@ where None, One(u8), Many, - }; + } let mut used_index = UsedIndex::None; for i in 0..16 { match (children[i].is_none(), &used_index) { @@ -1225,7 +1272,7 @@ where None, One(u8), Many, - }; + } let mut used_index = UsedIndex::None; for i in 0..16 { match (children[i].is_none(), &used_index) { @@ -1422,17 +1469,30 @@ where match self.storage.destroy(handle) { Stored::New(node) => { let mut k = NibbleVec::new(); - let encoded_root = node.into_encoded::<_, L::Codec, L::Hash>( + let mut register_children = Self::register_children_buf(&node); + let (encoded_root, no_node) = node.into_encoded::<_, L::Codec, L::Hash>( |child, 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 - } + }, + register_children.as_mut().map(|a| &mut a[..]), ); #[cfg(feature = "std")] trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - *self.root = self.db.insert(EMPTY_PREFIX, &encoded_root[..]); + if let Some(children) = register_children { + self.hybrid_hash_buffer_lazy_init(); + *self.root = self.db.insert_branch_hybrid( + EMPTY_PREFIX, + &encoded_root[..], + &children[..], + no_node, + self.hybrid_hash_buffer.as_mut().expect("Lazy init above"), + ) + } else { + *self.root = self.db.insert(EMPTY_PREFIX, &encoded_root[..]); + } self.hash_count += 1; self.root_handle = NodeHandle::Hash(*self.root); @@ -1463,7 +1523,8 @@ where match self.storage.destroy(storage_handle) { Stored::Cached(_, hash) => ChildReference::Hash(hash), Stored::New(node) => { - let encoded = { + let mut register_children = Self::register_children_buf(&node); + let (encoded, no_node) = { let commit_child = | node_handle, o_slice: Option<&NibbleSlice>, @@ -1474,10 +1535,25 @@ where prefix.drop_lasts(mov); cr }; - node.into_encoded::<_, L::Codec, L::Hash>(commit_child) + node.into_encoded::<_, L::Codec, L::Hash>( + commit_child, + register_children.as_mut().map(|a| &mut a[..]), + ) }; if encoded.len() >= L::Hash::LENGTH { - let hash = self.db.insert(prefix.as_prefix(), &encoded[..]); + let hash = if let Some(children) = register_children { + self.hybrid_hash_buffer_lazy_init(); + self.db.insert_branch_hybrid( + prefix.as_prefix(), + &encoded[..], + &children[..], + no_node, + self.hybrid_hash_buffer.as_mut().expect("Lazy init above"), + ) + } else { + self.db.insert(prefix.as_prefix(), &encoded[..]) + }; + self.hash_count +=1; ChildReference::Hash(hash) } else { diff --git a/trie-db/test/Cargo.toml b/trie-db/test/Cargo.toml index 00301870..0b05869b 100644 --- a/trie-db/test/Cargo.toml +++ b/trie-db/test/Cargo.toml @@ -25,3 +25,4 @@ hex-literal = "0.3" criterion = "0.3" env_logger = "0.8" log = "0.4" +ordered-trie = { path = "../../ordered-trie", default-features = false, version = "0.19.2"} diff --git a/trie-db/test/benches/bench.rs b/trie-db/test/benches/bench.rs index e2a63523..0e03872a 100644 --- a/trie-db/test/benches/bench.rs +++ b/trie-db/test/benches/bench.rs @@ -14,8 +14,9 @@ use criterion::{criterion_group, criterion_main, Bencher, black_box, Criterion}; -use trie_db::{NibbleSlice, proof::{generate_proof, verify_proof}, Trie}; +use trie_db::{NibbleSlice, proof::{generate_proof, verify_proof}, TrieLayout}; use trie_standardmap::{Alphabet, StandardMap, ValueMode}; +use reference_trie::ExtensionLayout as Layout; criterion_group!(benches, root_old, @@ -35,6 +36,12 @@ criterion_group!(benches, trie_iteration, nibble_common_prefix, trie_proof_verification, + proof_build_dataset_standard, + proof_build_dataset_hybrid, + proof_build_compacting_standard, + proof_build_compacting_hybrid, + proof_build_change_standard, + proof_build_change_hybrid, ); criterion_main!(benches); @@ -75,7 +82,7 @@ fn root_a_big_v(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -96,7 +103,7 @@ fn root_b_big_v(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -118,7 +125,7 @@ fn root_a_small_v(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -139,7 +146,7 @@ fn root_b_small_v(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -160,7 +167,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 +192,7 @@ fn root_new(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data, ); @@ -287,7 +294,7 @@ fn trie_mut_root_a(c: &mut Criterion) { .collect::>(); - reference_trie::calc_root(inputc); + reference_trie::calc_root::(inputc); }), data); } @@ -306,7 +313,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 +333,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 +353,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 +373,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 +397,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 +423,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 +445,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 +456,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())); }) @@ -462,6 +469,7 @@ fn trie_iteration(c: &mut Criterion) { fn trie_proof_verification(c: &mut Criterion) { use memory_db::HashKey; + use trie_db::Trie; let mut data = input_unsorted(29, 204800, 32); let mut keys = data[(data.len() / 3)..] @@ -475,9 +483,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,7 +496,7 @@ 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() @@ -496,3 +504,162 @@ fn trie_proof_verification(c: &mut Criterion) { }) ); } + +// bench build triedbmut as in proof size main from reference trie +// parameters are hadcoded. +fn proof_build_dataset(c: &mut Criterion, trie_size: u32, size_value: usize) { + use memory_db::PrefixedKey; + use trie_db::TrieMut; + + let mut seed = Default::default(); + let x = StandardMap { + alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), + min_key: size_value, + journal_key: 0, + value_mode: ValueMode::Index, + count: trie_size, + }.make_with(&mut seed); + let mut memdb = memory_db::MemoryDB::<::Hash, PrefixedKey<_>, Vec>::default(); + let mut root = Default::default(); + + c.bench_function("proof_build_dataset", move |b: &mut Bencher| + b.iter(|| { + let mut t = trie_db::TrieDBMut::::new(&mut memdb, &mut root); + for i in 0..x.len() { + let key: &[u8]= &x[i].0; + let val: &[u8] = &x[i].1; + t.insert(key, val).unwrap(); + } + t.commit(); + }) + ); +} + +fn proof_build_dataset_standard(c: &mut Criterion) { + let trie_size = 1000; + let values_size = 32; + proof_build_dataset::(c, trie_size, values_size) +} + +fn proof_build_dataset_hybrid(c: &mut Criterion) { + let trie_size = 1000; + let values_size = 32; + proof_build_dataset::(c, trie_size, values_size) +} + +fn proof_build_compacting(c: &mut Criterion, trie_size: u32, size_value: usize, number_key: usize) { + use memory_db::{PrefixedKey, HashKey}; + use trie_db::TrieMut; + + let mut seed = Default::default(); + let x = StandardMap { + alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), + min_key: size_value, + journal_key: 0, + value_mode: ValueMode::Index, + count: trie_size, + }.make_with(&mut seed); + let mut memdb = memory_db::MemoryDB::<::Hash, PrefixedKey<_>, Vec>::default(); + let mut root = Default::default(); + { + let mut t = trie_db::TrieDBMut::::new(&mut memdb, &mut root); + for i in 0..x.len() { + let key: &[u8]= &x[i].0; + let val: &[u8] = &x[i].1; + t.insert(key, val).unwrap(); + } + t.commit(); + } + + use trie_db::{Trie, TrieDB}; + use hash_db::{EMPTY_PREFIX, HashDB}; + + let keys = &x[..number_key]; + let trie = >::new(&memdb, &root).unwrap(); + let mut recorder = trie_db::Recorder::new(); + for (key, _) in keys { + let _ = trie.get_with(key.as_slice(), &mut recorder).unwrap(); + } + + let mut partial_db = , _>>::default(); + for record in recorder.drain() { + partial_db.emplace(record.hash, EMPTY_PREFIX, record.data); + } + let partial_trie = >::new(&partial_db, &trie.root()).unwrap(); + + c.bench_function("proof_build_compacting", move |b: &mut Bencher| + b.iter(|| { + trie_db::encode_compact::(&partial_trie).unwrap() + }) + ); +} + +fn proof_build_compacting_standard(c: &mut Criterion) { + let trie_size = 1000; + let proof_keys = 10; + let values_size = 32; + proof_build_compacting::(c, trie_size, values_size, proof_keys) +} + +fn proof_build_compacting_hybrid(c: &mut Criterion) { + let trie_size = 1000; + let proof_keys = 10; + let values_size = 32; + proof_build_compacting::(c, trie_size, values_size, proof_keys) +} + +fn proof_build_change(c: &mut Criterion, trie_size: u32, size_value: usize, number_key: usize) { + use memory_db::PrefixedKey; + use trie_db::TrieMut; + + let mut seed = Default::default(); + let x = StandardMap { + alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), + min_key: size_value, + journal_key: 0, + value_mode: ValueMode::Index, + count: trie_size, + }.make_with(&mut seed); + let mut memdb = memory_db::MemoryDB::<::Hash, PrefixedKey<_>, Vec>::default(); + let mut root = Default::default(); + { + let mut t = trie_db::TrieDBMut::::new(&mut memdb, &mut root); + for i in 0..x.len() { + let key: &[u8]= &x[i].0; + let val: &[u8] = &x[i].1; + t.insert(key, val).unwrap(); + } + t.commit(); + } + + let keys = &x[..number_key]; + let value = vec![213u8; size_value]; + + c.bench_function("proof_build_change", move |b: &mut Bencher| + b.iter(|| { + let mut memdb = memdb.clone(); + let mut root = root.clone(); + let mut t = trie_db::TrieDBMut::::from_existing(&mut memdb, &mut root).unwrap(); + for i in 0..keys.len() { + let key: &[u8]= &keys[i].0; + let val: &[u8] = &value[..]; + t.insert(key, val).unwrap(); + } + t.commit(); + }) + ); +} + +fn proof_build_change_standard(c: &mut Criterion) { + let trie_size = 1000; + let change_keys = 10; + let values_size = 32; + proof_build_change::(c, trie_size, values_size, change_keys) +} + +fn proof_build_change_hybrid(c: &mut Criterion) { + let trie_size = 1000; + let change_keys = 10; + let values_size = 32; + proof_build_change::(c, trie_size, values_size, change_keys) +} diff --git a/trie-db/test/src/fatdb.rs b/trie-db/test/src/fatdb.rs index 1e8f6ff8..0439fbbc 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); 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..be802bc7 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, ExtensionLayoutHybrid, + NoExtensionLayout, ExtensionLayout, NoExtensionLayoutHybrid}; #[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,49 @@ 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()); + test_iter::(data.clone()); + test_iter::(data.clone()); + test_iter::(data.clone()); + test_iter::(data.clone()); compare_implementations_h(data.clone()); compare_implementations_prefixed(data.clone()); - compare_implementations_no_extension(data.clone()); - compare_implementations_no_extension_prefixed(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.clone(), memdb.clone(), hashdb.clone()); + 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 +134,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 +240,33 @@ 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 +274,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()); + compare_implementations_prefixed(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![ + let data = vec![ (vec![01u8;((u16::max_value() as usize + 1) / 2) + 1], vec![0;32]), - ]); + ]; + compare_implementations_prefixed_internal::(data.clone()); + 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..6e66040c 100644 --- a/trie-db/test/src/iterator.rs +++ b/trie-db/test/src/iterator.rs @@ -15,39 +15,22 @@ use trie_db::{ DBValue, TrieError, TrieMut, TrieIterator, TrieDBNodeIterator, NibbleSlice, NibbleVec, - node::Node, + node::Node, 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) -{ - 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) +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 = 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 +48,136 @@ 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 { + 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, 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"), + } } + _ => 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()); + 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,61 +194,16 @@ fn iterator_over_empty_works() { assert!(iter.next().is_none()); } -#[test] -fn seek_works_with_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_with_extension(&pairs); - let trie = RefTrieDB::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_works_without_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_without_extension(&pairs); - let trie = RefTrieDBNoExt::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(); @@ -311,10 +238,10 @@ fn seek_works_without_extension() { 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 +260,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 +269,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,23 +294,31 @@ 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() { @@ -401,78 +336,43 @@ fn iterate_over_incomplete_db() { } } -#[test] -fn prefix_works_with_extension() { +test_layouts!(prefix_works, prefix_works_internal); +fn prefix_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(); 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"), - } - } - _ => 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"), + 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"), - } - - 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"), + } else { + 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"), + } } + _ => panic!("unexpected item"), } - _ => panic!("unexpected item"), } match iter.next() { @@ -495,13 +395,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..41aba20a 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::{ @@ -70,28 +70,9 @@ fn test_generate_proof( (root, proof, items) } -#[test] -fn trie_proof_works_with_ext() { - let (root, proof, items) = test_generate_proof::( - test_entries(), - 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(); -} - -#[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 +86,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 +100,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 +114,18 @@ 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 +133,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 +150,12 @@ 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,9 +176,9 @@ 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"], ); @@ -201,15 +188,15 @@ fn test_verify_invalid_child_reference() { 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 +205,18 @@ 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 +225,32 @@ 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 +258,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..7000992b 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; 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); @@ -54,8 +53,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,10 +71,22 @@ fn basic_recorder_min_depth() { #[test] fn trie_record() { + type KeccakHasher = ordered_trie::OrderedTrieHasher; + struct Layout; + + impl trie_db::TrieLayout for Layout { + const USE_EXTENSION: bool = true; + const HYBRID_HASH: bool = false; + type Hash = KeccakHasher; + type Codec = reference_trie::ReferenceNodeCodec; + } + + impl trie_db::TrieConfiguration for Layout { } + let mut db = MemoryDB::, _>::default(); let mut root = Default::default(); { - let mut x = RefTrieDBMut::new(&mut db, &mut root); + let mut x = trie_db::TrieDBMut::::new(&mut db, &mut root); x.insert(b"dog", b"cat").unwrap(); x.insert(b"lunch", b"time").unwrap(); @@ -87,7 +98,7 @@ fn trie_record() { x.insert(b"yo ho ho", b"and a bottle of rum").unwrap(); } - let trie = RefTrieDB::new(&db, &root).unwrap(); + let trie = trie_db::TrieDB::::new(&db, &root).unwrap(); let mut recorder = Recorder::new(); trie.get_with(b"pirate", &mut recorder).unwrap().unwrap(); 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..2394b3fa 100644 --- a/trie-db/test/src/trie_codec.rs +++ b/trie-db/test/src/trie_codec.rs @@ -18,9 +18,7 @@ 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>; @@ -57,7 +55,7 @@ fn test_encode_compact( // Populate a partial trie DB with recorded nodes. let mut partial_db = MemoryDB::default(); for record in recorder.drain() { - partial_db.insert(EMPTY_PREFIX, &record.data); + partial_db.emplace(record.hash, EMPTY_PREFIX, record.data); } // Compactly encode the partial trie DB. @@ -88,9 +86,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 +116,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"), @@ -170,7 +135,7 @@ fn trie_decoding_fails_with_incomplete_database() { // Reconstruct the partial DB from the compact encoding. let mut db = MemoryDB::default(); - match decode_compact::(&mut db, &encoded[..encoded.len() - 1]) { + 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 3def6759..12fb9908 100644 --- a/trie-db/test/src/triedb.rs +++ b/trie-db/test/src/triedb.rs @@ -13,29 +13,28 @@ // 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() { +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()), ]; - 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 trie = RefTrieDB::new(&memdb, &root).unwrap(); + let trie = TrieDB::::new(&memdb, &root).unwrap(); let iter = trie.iter().unwrap(); let mut iter_pairs = Vec::new(); @@ -47,51 +46,23 @@ fn iterator_works() { assert_eq!(pairs, iter_pairs); } -#[test] -fn iterator_works_without_extension() { +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::<_, PrefixedKey<_>, _>::default(); + let mut memdb = MemoryDB::, DBValue>::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 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_seek_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 t = RefTrieDB::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); assert_eq!( @@ -118,44 +89,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 +98,16 @@ fn iterator() { b"B".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 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,8 +120,8 @@ fn iterator() { assert_eq!(d, t.iter().unwrap().map(|x| x.unwrap().1).collect::>()); } -#[test] -fn iterator_without_extension() { +test_layouts!(iterator_seek, iterator_seek_internal); +fn iterator_seek_internal() { let d = vec![ b"A".to_vec(), b"AA".to_vec(), @@ -194,42 +129,16 @@ fn iterator_without_extension() { b"B".to_vec(), ]; - 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 = TrieDBMut::::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() { - 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(); + let t = TrieDB::::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); assert_eq!(iter.next().unwrap().unwrap(), (b"A".to_vec(), b"A".to_vec())); iter.seek(b"!").unwrap(); @@ -257,40 +166,24 @@ fn iterator_seek() { assert_eq!(&d[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 = 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); 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(), @@ -298,18 +191,19 @@ fn debug_output_supports_pretty_print() { b"B".to_vec(), ]; - let mut memdb = MemoryDB::, DBValue>::default(); + let mut memdb = MemoryDB::, DBValue>::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 { @@ -359,26 +253,28 @@ fn debug_output_supports_pretty_print() { 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 = 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); 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 7103af76..6d5fc248 100644 --- a/trie-db/test/src/triedbmut.rs +++ b/trie-db/test/src/triedbmut.rs @@ -15,19 +15,20 @@ 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 hash_db::Hasher; +use trie_db::{TrieDBMut, TrieMut, NodeCodec, HashDBHybridDyn, + TrieLayout, DBValue}; +use reference_trie::{ExtensionLayout, ExtensionLayoutHybrid, NoExtensionLayout, + NoExtensionLayoutHybrid, RefHasher, test_layouts, ReferenceNodeCodec, + ReferenceNodeCodecNoExt, reference_trie_root_iter_build as reference_trie_root}; + +fn populate_trie<'db, T: TrieLayout>( + db: &'db mut dyn HashDBHybridDyn, + root: &'db mut ::Out, v: &[(Vec, Vec)] -) -> RefTrieDBMut<'db> { - let mut t = RefTrieDBMut::new(db, root); +) -> TrieDBMut<'db, T> { + let mut t = TrieDBMut::::new(db, root); for i in 0..v.len() { let key: &[u8]= &v[i].0; let val: &[u8] = &v[i].1; @@ -36,41 +37,30 @@ fn populate_trie<'db>( t } -fn unpopulate_trie<'db>(t: &mut RefTrieDBMut<'db>, v: &[(Vec, Vec)]) { +fn unpopulate_trie<'db, T: TrieLayout>(t: &mut TrieDBMut<'db, T>, 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; - t.insert(key, val).unwrap(); - } - t -} - -fn unpopulate_trie_no_extension<'db>(t: &mut RefTrieDBMutNoExt<'db>, v: &[(Vec, Vec)]) { - for i in v { - let key: &[u8]= &i.0; - t.remove(key).unwrap(); +fn reference_hashed_null_node() -> ::Out { + if T::USE_EXTENSION { + as NodeCodec>::hashed_null_node() + } else { + as NodeCodec>::hashed_null_node() } } -fn reference_hashed_null_node() -> ::Out { - as NodeCodec>::hashed_null_node() -} - #[test] fn playpen() { env_logger::init(); + playpen_internal::(); + playpen_internal::(); + playpen_internal::(); + playpen_internal::(); +} +fn playpen_internal() { let mut seed = Default::default(); for test_i in 0..10 { if test_i % 50 == 0 { @@ -84,10 +74,10 @@ fn playpen() { count: 100, }.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 = MemoryDB::, DBValue>::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); memtrie.commit(); if *memtrie.root() != real { @@ -101,50 +91,7 @@ fn playpen() { 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(); - for test_i in 0..10 { - 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_no_extension(x.clone()); - let mut memdb = MemoryDB::, DBValue>::default(); - let mut root = Default::default(); - let mut memtrie = populate_trie_no_extension(&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_no_extension(&mut memtrie, &x); - 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 +104,35 @@ fn playpen() { } } -#[test] -fn init() { - let mut memdb = MemoryDB::, DBValue>::default(); +test_layouts!(init, init_internal); +fn init_internal() { + let mut memdb = MemoryDB::, DBValue>::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 = 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); 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 = 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); t.insert(&[0x01], big_value).unwrap(); t.insert(&[0x01, 0x23], big_value).unwrap(); @@ -197,8 +144,8 @@ fn remove_to_empty() { assert_eq!(memdb.keys().len(), 0); } -#[test] -fn remove_to_empty_no_extension() { +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"; @@ -206,7 +153,7 @@ fn remove_to_empty_no_extension() { let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::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 +161,149 @@ 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 = 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); 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 = 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); 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 = 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); 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 = 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); 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 = 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); 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 = 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); 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 = 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); 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 = 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); 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 = MemoryDB::, DBValue>::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, test_at_one_internal); +fn test_at_one_internal() { + 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); 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]); } -#[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 = 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); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0xf1u8, 0x23], &[0xf1u8, 0x23]).unwrap(); t.insert(&[0x81u8, 0x23], &[0x81u8, 0x23]).unwrap(); @@ -371,8 +318,8 @@ fn test_at_three() { assert_eq!(t.get(&[0x82, 0x23]).unwrap(), None); } -#[test] -fn stress() { +test_layouts!(stress, stress_internal); +fn stress_internal() { let mut seed = Default::default(); for _ in 0..50 { let x = StandardMap { @@ -383,15 +330,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 = MemoryDB::, DBValue>::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 = MemoryDB::, DBValue>::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!(); @@ -409,22 +356,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 = 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); 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()), @@ -434,36 +381,26 @@ fn insert_empty_denied() { count: 4, }.make_with(&mut seed); - 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 &(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 mut seed = Default::default(); let x = StandardMap { alphabet: Alphabet::Custom(b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_".to_vec()), @@ -473,9 +410,9 @@ fn return_old_values() { count: 2, }.make_with(&mut seed); - 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 &(ref key, ref value) in &x { assert!(t.insert(key, value).unwrap().is_none()); assert_eq!(t.insert(key, value).unwrap(), Some(value.clone())); @@ -485,3 +422,16 @@ fn return_old_values() { assert!(t.remove(&key).unwrap().is_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())); +}