diff --git a/hash-db/Cargo.toml b/hash-db/Cargo.toml index 6bf38a1b..d453445a 100644 --- a/hash-db/Cargo.toml +++ b/hash-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hash-db" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "Trait for hash-keyed databases." license = "Apache-2.0" diff --git a/hash-db/src/lib.rs b/hash-db/src/lib.rs index 2d321585..cfd2a067 100644 --- a/hash-db/src/lib.rs +++ b/hash-db/src/lib.rs @@ -20,37 +20,47 @@ use std::fmt::Debug; #[cfg(feature = "std")] use std::hash; +#[cfg(not(feature = "std"))] +use core::hash; + #[cfg(feature = "std")] pub trait MaybeDebug: Debug {} #[cfg(feature = "std")] impl MaybeDebug for T {} - -#[cfg(not(feature = "std"))] -use core::hash; #[cfg(not(feature = "std"))] pub trait MaybeDebug {} #[cfg(not(feature = "std"))] impl MaybeDebug for T {} -/// Empty prefix constant. -pub static EMPTY_PREFIX: Prefix<'static> = &[]; +/// A trie node prefix, it is the nibble path from the trie root +/// to the trie node. +/// For a node containing no partial key value it is the full key. +/// For a value node or node containing a partial key, it is the full key minus its node partial +/// nibbles (the node key can be split into prefix and node partial). +/// Therefore it is always the leftmost portion of the node key, so its internal representation +/// is a non expanded byte slice followed by a last padded byte representation. +/// The padded byte is an optional padded value. +pub type Prefix<'a> = (&'a[u8], Option); -/// Prefix byte information for avoding rc. -pub type Prefix<'a> = &'a[u8]; +/// An empty prefix constant. +/// Can be use when the prefix is not use internally +/// or for root nodes. +pub static EMPTY_PREFIX: Prefix<'static> = (&[], None); /// Trait describing an object that can hash a slice of bytes. Used to abstract /// other types over the hashing algorithm. Defines a single `hash` method and an /// `Out` associated type with the necessary bounds. pub trait Hasher: Sync + Send { /// The output type of the `Hasher` - type Out: AsRef<[u8]> + AsMut<[u8]> + Default + MaybeDebug + PartialEq + Eq + hash::Hash + Send + Sync + Clone + Copy; - /// What to use to build `HashMap`s with this `Hasher` + type Out: AsRef<[u8]> + AsMut<[u8]> + Default + MaybeDebug + PartialEq + Eq + + hash::Hash + Send + Sync + Clone + Copy; + /// What to use to build `HashMap`s with this `Hasher`. type StdHasher: Sync + Send + Default + hash::Hasher; - /// The length in bytes of the `Hasher` output + /// The length in bytes of the `Hasher` output. const LENGTH: usize; - /// Compute the hash of the provided slice of bytes returning the `Out` type of the `Hasher` + /// Compute the hash of the provided slice of bytes returning the `Out` type of the `Hasher`. fn hash(x: &[u8]) -> Self::Out; } @@ -101,42 +111,47 @@ impl<'a, K, V> PlainDBRef for &'a mut dyn PlainDB { pub trait HashDB: Send + Sync + AsHashDB { /// Look up a given hash into the bytes that hash to it, returning None if the /// hash is not known. - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option; + fn get(&self, key: &H::Out, prefix: Prefix) -> Option; /// Check for the existance of a hash-key. - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool; + fn contains(&self, key: &H::Out, prefix: Prefix) -> 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(&mut self, prefix: &[u8], value: &[u8]) -> H::Out; + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out; /// Like `insert()`, except you provide the key and the data is all moved. - fn emplace(&mut self, key: H::Out, prefix: &[u8], value: T); + fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T); - /// Remove a datum previously inserted. Insertions can be "owed" such that the same number of `insert()`s may - /// happen without the data being eventually being inserted into the DB. It can be "owed" more than once. - fn remove(&mut self, key: &H::Out, prefix: &[u8]); + /// Remove a datum previously inserted. Insertions can be "owed" such that the same number of + /// `insert()`s may happen without the data being eventually being inserted into the DB. + /// It can be "owed" more than once. + fn remove(&mut self, key: &H::Out, prefix: Prefix); } /// Trait for immutable reference of HashDB. pub trait HashDBRef { /// Look up a given hash into the bytes that hash to it, returning None if the /// hash is not known. - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option; + fn get(&self, key: &H::Out, prefix: Prefix) -> Option; /// Check for the existance of a hash-key. - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool; + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool; } impl<'a, H: Hasher, T> HashDBRef for &'a dyn HashDB { - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option { HashDB::get(*self, key, prefix) } - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool { HashDB::contains(*self, key, prefix) } + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { HashDB::get(*self, key, prefix) } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + HashDB::contains(*self, key, prefix) + } } impl<'a, H: Hasher, T> HashDBRef for &'a mut dyn HashDB { - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option { HashDB::get(*self, key, prefix) } - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool { HashDB::contains(*self, key, prefix) } + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { HashDB::get(*self, key, prefix) } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { + HashDB::contains(*self, key, prefix) + } } /// Upcast trait for HashDB. @@ -155,15 +170,18 @@ pub trait AsPlainDB { fn as_plain_db_mut<'a>(&'a mut self) -> &'a mut (dyn PlainDB + 'a); } -// NOTE: There used to be a `impl AsHashDB for T` but that does not work with generics. See https://stackoverflow.com/questions/48432842/implementing-a-trait-for-reference-and-non-reference-types-causes-conflicting-im -// This means we need concrete impls of AsHashDB in several places, which somewhat defeats the point of the trait. +// NOTE: There used to be a `impl AsHashDB for T` but that does not work with generics. +// See https://stackoverflow.com/questions/48432842/ +// implementing-a-trait-for-reference-and-non-reference-types-causes-conflicting-im +// This means we need concrete impls of AsHashDB in several places, which somewhat defeats +// the point of the trait. impl<'a, H: Hasher, T> AsHashDB for &'a mut dyn HashDB { fn as_hash_db(&self) -> &dyn HashDB { &**self } fn as_hash_db_mut<'b>(&'b mut self) -> &'b mut (dyn HashDB + 'b) { &mut **self } } #[cfg(feature = "std")] -impl<'a, K, V> AsPlainDB for &'a mut PlainDB { - fn as_plain_db(&self) -> &PlainDB { &**self } - fn as_plain_db_mut<'b>(&'b mut self) -> &'b mut (PlainDB + 'b) { &mut **self } +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 } } diff --git a/hash256-std-hasher/Cargo.toml b/hash256-std-hasher/Cargo.toml index b3e0b030..6ee6ea29 100644 --- a/hash256-std-hasher/Cargo.toml +++ b/hash256-std-hasher/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "hash256-std-hasher" description = "Standard library hasher for 256-bit prehashed keys." -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] license = "Apache-2.0" homepage = "https://github.com/paritytech/trie" diff --git a/hash256-std-hasher/src/lib.rs b/hash256-std-hasher/src/lib.rs index 849db0fc..22192293 100644 --- a/hash256-std-hasher/src/lib.rs +++ b/hash256-std-hasher/src/lib.rs @@ -13,7 +13,6 @@ // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] #[macro_use] extern crate crunchy; diff --git a/memory-db/Cargo.toml b/memory-db/Cargo.toml index a5be306f..5c19421f 100644 --- a/memory-db/Cargo.toml +++ b/memory-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "memory-db" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "In-memory implementation of hash-db, useful for tests" repository = "https://github.com/paritytech/trie" @@ -9,11 +9,11 @@ license = "Apache-2.0" [dependencies] heapsize = { version = "0.4", optional = true } parity-util-mem = { version = "0.2", default-features = false } -hash-db = { path = "../hash-db", default-features = false, version = "0.14.0"} +hash-db = { path = "../hash-db", default-features = false, version = "0.15.0"} hashmap_core = { version = "0.1" } [dev-dependencies] -keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.14.0"} +keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.15.0"} criterion = "0.2.8" [features] diff --git a/memory-db/benches/bench.rs b/memory-db/benches/bench.rs index 20cb9e55..8aa3120e 100644 --- a/memory-db/benches/bench.rs +++ b/memory-db/benches/bench.rs @@ -24,12 +24,13 @@ extern crate memory_db; extern crate keccak_hasher; use memory_db::MemoryDB; +use memory_db::HashKey; use keccak_hasher::KeccakHasher; -use hash_db::{HashDB, Hasher}; +use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; fn instantiation(b: &mut Criterion) { b.bench_function("instantiation", |b| b.iter(|| { - MemoryDB::>::default(); + MemoryDB::, Vec>::default(); })); } @@ -52,19 +53,19 @@ fn compare_to_null_in_const(b: &mut Criterion) { } fn contains_with_non_null_key(b: &mut Criterion) { - let mut m = MemoryDB::>::default(); + let mut m = MemoryDB::, Vec>::default(); let key = KeccakHasher::hash(b"abc"); - m.insert(b"abcefghijklmnopqrstuvxyz"); + m.insert(EMPTY_PREFIX, b"abcefghijklmnopqrstuvxyz"); b.bench_function("contains_with_non_null_key", |b| b.iter(|| { - m.contains(&key); + m.contains(&key, EMPTY_PREFIX); })); } fn contains_with_null_key(b: &mut Criterion) { - let mut m = MemoryDB::>::default(); + let mut m = MemoryDB::, Vec>::default(); let null_key = KeccakHasher::hash(&[0u8][..]); - m.insert(b"abcefghijklmnopqrstuvxyz"); + m.insert(EMPTY_PREFIX, b"abcefghijklmnopqrstuvxyz"); b.bench_function("contains_with_null_key", |b| b.iter(|| { - m.contains(&null_key); + m.contains(&null_key, EMPTY_PREFIX); })); } diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index d5b37d1c..6749c205 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -15,7 +15,6 @@ //! Reference-counted memory-based `HashDB` implementation. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(alloc))] extern crate hash_db; extern crate parity_util_mem; @@ -28,7 +27,8 @@ extern crate hashmap_core; extern crate alloc; #[cfg(test)] extern crate keccak_hasher; -use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef, Hasher as KeyHasher, AsHashDB, AsPlainDB}; +use hash_db::{HashDB, HashDBRef, PlainDB, PlainDBRef, Hasher as KeyHasher, + AsHashDB, AsPlainDB, Prefix}; use parity_util_mem::{MallocSizeOf, MallocSizeOfOps}; #[cfg(feature = "deprecated")] #[cfg(feature = "std")] @@ -84,38 +84,38 @@ impl MaybeDebug for T {} /// extern crate keccak_hasher; /// extern crate memory_db; /// -/// use hash_db::{Hasher, HashDB}; +/// use hash_db::{Hasher, HashDB, EMPTY_PREFIX}; /// use keccak_hasher::KeccakHasher; /// use memory_db::{MemoryDB, HashKey}; /// fn main() { /// let mut m = MemoryDB::, Vec>::default(); /// let d = "Hello world!".as_bytes(); /// -/// let k = m.insert(&[], d); -/// assert!(m.contains(&k, &[])); -/// assert_eq!(m.get(&k, &[]).unwrap(), d); +/// let k = m.insert(EMPTY_PREFIX, d); +/// assert!(m.contains(&k, EMPTY_PREFIX)); +/// assert_eq!(m.get(&k, EMPTY_PREFIX).unwrap(), d); /// -/// m.insert(&[], d); -/// assert!(m.contains(&k, &[])); +/// m.insert(EMPTY_PREFIX, d); +/// assert!(m.contains(&k, EMPTY_PREFIX)); /// -/// m.remove(&k, &[]); -/// assert!(m.contains(&k, &[])); +/// m.remove(&k, EMPTY_PREFIX); +/// assert!(m.contains(&k, EMPTY_PREFIX)); /// -/// m.remove(&k, &[]); -/// assert!(!m.contains(&k, &[])); +/// m.remove(&k, EMPTY_PREFIX); +/// assert!(!m.contains(&k, EMPTY_PREFIX)); /// -/// m.remove(&k, &[]); -/// assert!(!m.contains(&k, &[])); +/// m.remove(&k, EMPTY_PREFIX); +/// assert!(!m.contains(&k, EMPTY_PREFIX)); /// -/// m.insert(&[], d); -/// assert!(!m.contains(&k, &[])); +/// m.insert(EMPTY_PREFIX, d); +/// assert!(!m.contains(&k, EMPTY_PREFIX)); -/// m.insert(&[], d); -/// assert!(m.contains(&k, &[])); -/// assert_eq!(m.get(&k, &[]).unwrap(), d); +/// m.insert(EMPTY_PREFIX, d); +/// assert!(m.contains(&k, EMPTY_PREFIX)); +/// assert_eq!(m.get(&k, EMPTY_PREFIX).unwrap(), d); /// -/// m.remove(&k, &[]); -/// assert!(!m.contains(&k, &[])); +/// m.remove(&k, EMPTY_PREFIX); +/// assert!(!m.contains(&k, EMPTY_PREFIX)); /// } /// ``` #[derive(Clone)] @@ -160,46 +160,83 @@ impl Eq for MemoryDB pub trait KeyFunction { type Key: Send + Sync + Clone + hash::Hash + Eq; - fn key(hash: &H::Out, prefix: &[u8]) -> Self::Key; + fn key(hash: &H::Out, prefix: Prefix) -> Self::Key; } -/// Make database key from hash and prefix. -pub fn prefixed_key(key: &H::Out, prefix: &[u8]) -> Vec { - let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.len()); - prefixed_key.extend_from_slice(prefix); - prefixed_key.extend_from_slice(key.as_ref()); - prefixed_key -} - -/// Make database key from hash only. -pub fn hash_key(key: &H::Out, _prefix: &[u8]) -> H::Out { - key.clone() -} - -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] /// Key function that only uses the hash pub struct HashKey(PhantomData); impl KeyFunction for HashKey { type Key = H::Out; - fn key(hash: &H::Out, prefix: &[u8]) -> H::Out { + fn key(hash: &H::Out, prefix: Prefix) -> H::Out { hash_key::(hash, prefix) } } -#[derive(Clone,Debug)] +/// Make database key from hash only. +pub fn hash_key(key: &H::Out, _prefix: Prefix) -> H::Out { + key.clone() +} + +#[derive(Clone, Debug)] /// Key function that concatenates prefix and hash. pub struct PrefixedKey(PhantomData); impl KeyFunction for PrefixedKey { type Key = Vec; - fn key(hash: &H::Out, prefix: &[u8]) -> Vec { + fn key(hash: &H::Out, prefix: Prefix) -> Vec { prefixed_key::(hash, prefix) } } +/// Derive a database key from hash value of the node (key) and the node prefix. +pub fn prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { + let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); + prefixed_key.extend_from_slice(prefix.0); + if let Some(last) = prefix.1 { + prefixed_key.push(last); + } + prefixed_key.extend_from_slice(key.as_ref()); + prefixed_key +} + +#[derive(Clone, Debug)] +/// Key function that concatenates prefix and hash. +/// This is doing useless computation and should only be +/// used for legacy purpose. +/// It shall be remove in the future. +pub struct LegacyPrefixedKey(PhantomData); + +impl KeyFunction for LegacyPrefixedKey { + type Key = Vec; + + fn key(hash: &H::Out, prefix: Prefix) -> Vec { + legacy_prefixed_key::(hash, prefix) + } +} + +/// Legacy method for db using previous version of prefix encoding. +/// Only for trie radix 16 trie. +pub fn legacy_prefixed_key(key: &H::Out, prefix: Prefix) -> Vec { + let mut prefixed_key = Vec::with_capacity(key.as_ref().len() + prefix.0.len() + 1); + if let Some(last) = prefix.1 { + let mut prev = 0x01u8; + for i in prefix.0.iter() { + prefixed_key.push((prev << 4) + (*i >> 4)); + prev = *i; + } + prefixed_key.push((prev << 4) + (last >> 4)); + } else { + prefixed_key.push(0); + prefixed_key.extend_from_slice(prefix.0); + } + prefixed_key.extend_from_slice(key.as_ref()); + prefixed_key +} + impl<'a, H, KF, T> Default for MemoryDB where H: KeyHasher, @@ -220,7 +257,7 @@ where { /// Remove an element and delete it from storage if reference count reaches zero. /// If the value was purged, return the old value. - pub fn remove_and_purge(&mut self, key: &::Out, prefix: &[u8]) -> Option { + pub fn remove_and_purge(&mut self, key: &::Out, prefix: Prefix) -> Option { if key == &self.hashed_null_node { return None; } @@ -269,17 +306,17 @@ where /// extern crate keccak_hasher; /// extern crate memory_db; /// - /// use hash_db::{Hasher, HashDB}; + /// use hash_db::{Hasher, HashDB, EMPTY_PREFIX}; /// use keccak_hasher::KeccakHasher; /// use memory_db::{MemoryDB, HashKey}; /// /// fn main() { /// let mut m = MemoryDB::, Vec>::default(); /// let hello_bytes = "Hello world!".as_bytes(); - /// let hash = m.insert(&[], hello_bytes); - /// assert!(m.contains(&hash, &[])); + /// let hash = m.insert(EMPTY_PREFIX, hello_bytes); + /// assert!(m.contains(&hash, EMPTY_PREFIX)); /// m.clear(); - /// assert!(!m.contains(&hash, &[])); + /// assert!(!m.contains(&hash, EMPTY_PREFIX)); /// } /// ``` pub fn clear(&mut self) { @@ -301,7 +338,7 @@ where /// /// Even when Some is returned, the data is only guaranteed to be useful /// when the refs > 0. - pub fn raw(&self, key: &::Out, prefix: &[u8]) -> Option<(&T, i32)> { + pub fn raw(&self, key: &::Out, prefix: Prefix) -> Option<(&T, i32)> { if key == &self.hashed_null_node { return Some((&self.null_node_data, 1)); } @@ -395,8 +432,6 @@ where } } - - impl PlainDB for MemoryDB where H: KeyHasher, @@ -463,7 +498,7 @@ where T: Default + PartialEq + for<'a> From<&'a [u8]> + Clone + Send + Sync, KF: Send + Sync + KeyFunction, { - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option { + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { if key == &self.hashed_null_node { return Some(self.null_node_data.clone()); } @@ -475,7 +510,7 @@ where } } - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool { + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { if key == &self.hashed_null_node { return true; } @@ -487,7 +522,7 @@ where } } - fn emplace(&mut self, key: H::Out, prefix: &[u8], value: T) { + fn emplace(&mut self, key: H::Out, prefix: Prefix, value: T) { if value == self.null_node_data { return; } @@ -507,7 +542,7 @@ where } } - fn insert(&mut self, prefix: &[u8], value: &[u8]) -> H::Out { + fn insert(&mut self, prefix: Prefix, value: &[u8]) -> H::Out { if T::from(value) == self.null_node_data { return self.hashed_null_node.clone(); } @@ -517,7 +552,7 @@ where key } - fn remove(&mut self, key: &H::Out, prefix: &[u8]) { + fn remove(&mut self, key: &H::Out, prefix: Prefix) { if key == &self.hashed_null_node { return; } @@ -541,8 +576,8 @@ where T: Default + PartialEq + for<'a> From<&'a [u8]> + Clone + Send + Sync, KF: Send + Sync + KeyFunction, { - fn get(&self, key: &H::Out, prefix: &[u8]) -> Option { HashDB::get(self, key, prefix) } - fn contains(&self, key: &H::Out, prefix: &[u8]) -> bool { HashDB::contains(self, key, prefix) } + fn get(&self, key: &H::Out, prefix: Prefix) -> Option { HashDB::get(self, key, prefix) } + fn contains(&self, key: &H::Out, prefix: Prefix) -> bool { HashDB::contains(self, key, prefix) } } impl AsPlainDB for MemoryDB @@ -569,6 +604,7 @@ where #[cfg(test)] mod tests { use super::{MemoryDB, HashDB, KeyHasher, HashKey}; + use hash_db::EMPTY_PREFIX; use keccak_hasher::KeccakHasher; #[test] @@ -577,52 +613,55 @@ mod tests { let hello_key = KeccakHasher::hash(hello_bytes); let mut m = MemoryDB::, Vec>::default(); - m.remove(&hello_key, &[]); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, -1); + m.remove(&hello_key, EMPTY_PREFIX); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, -1); m.purge(); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, -1); - m.insert(&[], hello_bytes); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, 0); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, -1); + m.insert(EMPTY_PREFIX, hello_bytes); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, 0); m.purge(); - assert_eq!(m.raw(&hello_key, &[]), None); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX), None); let mut m = MemoryDB::, Vec>::default(); - assert!(m.remove_and_purge(&hello_key, &[]).is_none()); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, -1); - m.insert(&[], hello_bytes); - m.insert(&[], hello_bytes); - assert_eq!(m.raw(&hello_key, &[]).unwrap().1, 1); - assert_eq!(&*m.remove_and_purge(&hello_key, &[]).unwrap(), hello_bytes); - assert_eq!(m.raw(&hello_key, &[]), None); - assert!(m.remove_and_purge(&hello_key, &[]).is_none()); + assert!(m.remove_and_purge(&hello_key, EMPTY_PREFIX).is_none()); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, -1); + m.insert(EMPTY_PREFIX, hello_bytes); + m.insert(EMPTY_PREFIX, hello_bytes); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX).unwrap().1, 1); + assert_eq!(&*m.remove_and_purge(&hello_key, EMPTY_PREFIX).unwrap(), hello_bytes); + assert_eq!(m.raw(&hello_key, EMPTY_PREFIX), None); + assert!(m.remove_and_purge(&hello_key, EMPTY_PREFIX).is_none()); } #[test] fn consolidate() { let mut main = MemoryDB::, Vec>::default(); let mut other = MemoryDB::, Vec>::default(); - let remove_key = other.insert(&[], b"doggo"); - main.remove(&remove_key, &[]); + let remove_key = other.insert(EMPTY_PREFIX, b"doggo"); + main.remove(&remove_key, EMPTY_PREFIX); - let insert_key = other.insert(&[], b"arf"); - main.emplace(insert_key, &[], "arf".as_bytes().to_vec()); + let insert_key = other.insert(EMPTY_PREFIX, b"arf"); + main.emplace(insert_key, EMPTY_PREFIX, "arf".as_bytes().to_vec()); - let negative_remove_key = other.insert(&[], b"negative"); - other.remove(&negative_remove_key, &[]); // ref cnt: 0 - other.remove(&negative_remove_key, &[]); // ref cnt: -1 - main.remove(&negative_remove_key, &[]); // ref cnt: -1 + let negative_remove_key = other.insert(EMPTY_PREFIX, b"negative"); + other.remove(&negative_remove_key, EMPTY_PREFIX); // ref cnt: 0 + other.remove(&negative_remove_key, EMPTY_PREFIX); // ref cnt: -1 + main.remove(&negative_remove_key, EMPTY_PREFIX); // ref cnt: -1 main.consolidate(other); - assert_eq!(main.raw(&remove_key, &[]).unwrap(), (&"doggo".as_bytes().to_vec(), 0)); - assert_eq!(main.raw(&insert_key, &[]).unwrap(), (&"arf".as_bytes().to_vec(), 2)); - assert_eq!(main.raw(&negative_remove_key, &[]).unwrap(), (&"negative".as_bytes().to_vec(), -2)); + assert_eq!(main.raw(&remove_key, EMPTY_PREFIX).unwrap(), (&"doggo".as_bytes().to_vec(), 0)); + assert_eq!(main.raw(&insert_key, EMPTY_PREFIX).unwrap(), (&"arf".as_bytes().to_vec(), 2)); + assert_eq!( + main.raw(&negative_remove_key, EMPTY_PREFIX).unwrap(), + (&"negative".as_bytes().to_vec(), -2), + ); } #[test] fn default_works() { let mut db = MemoryDB::, Vec>::default(); let hashed_null_node = KeccakHasher::hash(&[0u8][..]); - assert_eq!(db.insert(&[], &[0u8][..]), hashed_null_node); + assert_eq!(db.insert(EMPTY_PREFIX, &[0u8][..]), hashed_null_node); } } diff --git a/test-support/keccak-hasher/Cargo.toml b/test-support/keccak-hasher/Cargo.toml index 587bb7af..7b2cfea4 100644 --- a/test-support/keccak-hasher/Cargo.toml +++ b/test-support/keccak-hasher/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "keccak-hasher" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "Keccak-256 implementation of the Hasher trait" repository = "https://github.com/paritytech/parity/" @@ -8,8 +8,8 @@ license = "Apache-2.0" [dependencies] tiny-keccak = "1.4.2" -hash-db = { path = "../../hash-db", default-features = false, version = "0.14.0" } -hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.14.0" } +hash-db = { path = "../../hash-db", default-features = false, version = "0.15.0" } +hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.15.0" } [features] default = ["std"] diff --git a/test-support/reference-trie/Cargo.toml b/test-support/reference-trie/Cargo.toml index 72ab2bfd..757c0e0d 100644 --- a/test-support/reference-trie/Cargo.toml +++ b/test-support/reference-trie/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reference-trie" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "Simple reference trie format" repository = "https://github.com/paritytech/trie/" @@ -8,11 +8,11 @@ license = "Apache-2.0" edition = "2018" [dependencies] -hash-db = { path = "../../hash-db" , version = "0.14.0" } -hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.14.0" } -keccak-hasher = { path = "../keccak-hasher", version = "0.14.0" } -trie-db = { path = "../../trie-db", default-features = false, version = "0.14.0" } -trie-root = { path = "../../trie-root", default-features = false, version = "0.14.0" } +hash-db = { path = "../../hash-db" , version = "0.15.0"} +hash256-std-hasher = { path = "../../hash256-std-hasher", version = "0.15.0" } +keccak-hasher = { path = "../keccak-hasher", version = "0.15.0" } +trie-db = { path = "../../trie-db", default-features = false, version = "0.15.0"} +trie-root = { path = "../../trie-root", default-features = false, version = "0.15.0" } parity-scale-codec = { version = "1.0.3", features = ["derive"] } [dev-dependencies] diff --git a/test-support/reference-trie/benches/bench.rs b/test-support/reference-trie/benches/bench.rs index ed183d78..943f2eee 100644 --- a/test-support/reference-trie/benches/bench.rs +++ b/test-support/reference-trie/benches/bench.rs @@ -24,8 +24,7 @@ extern crate trie_bench; fn benchmark(c: &mut Criterion) { trie_bench::standard_benchmark::< - keccak_hasher::KeccakHasher, - reference_trie::ReferenceNodeCodec, + reference_trie::ExtensionLayout, reference_trie::ReferenceTrieStream, >(c, "ref"); } diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 1649cc03..a81bd582 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -15,25 +15,92 @@ //! Reference implementation of a streamer. use std::fmt; -use std::error::Error as StdError; use std::iter::once; +use std::marker::PhantomData; use parity_scale_codec::{Decode, Input, Output, Encode, Compact, Error as CodecError}; use trie_root::Hasher; -use trie_db::{node::Node, triedbmut::ChildReference, DBValue}; +use trie_db::{ + node::Node, + triedbmut::ChildReference, + DBValue, + trie_visit, + TrieBuilder, + TrieRoot, + Partial, +}; +use std::borrow::Borrow; use keccak_hasher::KeccakHasher; -pub use trie_db::{Trie, TrieMut, NibbleSlice, NodeCodec, Recorder, Record}; +pub use trie_db::{Trie, TrieMut, NibbleSlice, Recorder, NodeCodec}; +pub use trie_db::{Record, TrieLayout, TrieConfiguration, nibble_ops}; pub use trie_root::TrieStream; -pub type RefTrieDB<'a> = trie_db::TrieDB<'a, keccak_hasher::KeccakHasher, ReferenceNodeCodec>; -pub type RefTrieDBMut<'a> = trie_db::TrieDBMut<'a, KeccakHasher, ReferenceNodeCodec>; -pub type RefFatDB<'a> = trie_db::FatDB<'a, KeccakHasher, ReferenceNodeCodec>; -pub type RefFatDBMut<'a> = trie_db::FatDBMut<'a, KeccakHasher, ReferenceNodeCodec>; -pub type RefSecTrieDB<'a> = trie_db::SecTrieDB<'a, KeccakHasher, ReferenceNodeCodec>; -pub type RefSecTrieDBMut<'a> = trie_db::SecTrieDBMut<'a, KeccakHasher, ReferenceNodeCodec>; -pub type RefLookup<'a, Q> = trie_db::Lookup<'a, KeccakHasher, ReferenceNodeCodec, Q>; +/// Trie layout using extension nodes. +pub struct ExtensionLayout; -pub fn ref_trie_root(input: I) -> ::Out where +impl TrieLayout for ExtensionLayout { + const USE_EXTENSION: bool = true; + type Hash = keccak_hasher::KeccakHasher; + type Codec = ReferenceNodeCodec; +} + +impl TrieConfiguration for ExtensionLayout { } + +/// Trie layout without extension nodes, allowing +/// generic hasher. +pub struct GenericNoExtensionLayout(PhantomData); + +impl TrieLayout for GenericNoExtensionLayout { + const USE_EXTENSION: bool = false; + type Hash = H; + type Codec = ReferenceNodeCodecNoExt; +} + +impl TrieConfiguration for GenericNoExtensionLayout { } + +/// Trie layout without extension nodes. +pub type NoExtensionLayout = GenericNoExtensionLayout; + +/// Children bitmap codec for radix 16 trie. +pub struct Bitmap(u16); + +const BITMAP_LENGTH: usize = 2; + +impl Bitmap { + + fn decode(data: &[u8]) -> Result { + Ok(u16::decode(&mut &data[..]) + .map(|v| Bitmap(v))?) + } + + fn value_at(&self, i: usize) -> bool { + self.0 & (1u16 << i) != 0 + } + + 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; + } +} + +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 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 RefLookupNoExt<'a, Q> = trie_db::Lookup<'a, NoExtensionLayout, Q>; + +pub fn reference_trie_root(input: I) -> ::Out where I: IntoIterator, A: AsRef<[u8]> + Ord + fmt::Debug, B: AsRef<[u8]> + fmt::Debug, @@ -41,6 +108,30 @@ pub fn ref_trie_root(input: I) -> ::Out where trie_root::trie_root::(input) } +fn reference_trie_root_unhashed(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, +{ + trie_root::unhashed_trie::(input) +} + +pub fn reference_trie_root_no_extension(input: I) -> ::Out where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, +{ + trie_root::trie_root_no_extension::(input) +} + +fn reference_trie_root_unhashed_no_extension(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, +{ + trie_root::unhashed_trie_no_extension::(input) +} + const EMPTY_TRIE: u8 = 0; const LEAF_NODE_OFFSET: u8 = 1; const EXTENSION_NODE_OFFSET: u8 = 128; @@ -51,11 +142,21 @@ const EXTENSION_NODE_OVER: u8 = BRANCH_NODE_NO_VALUE - EXTENSION_NODE_OFFSET; const LEAF_NODE_LAST: u8 = EXTENSION_NODE_OFFSET - 1; const EXTENSION_NODE_LAST: u8 = BRANCH_NODE_NO_VALUE - 1; +// Constant use with no extensino trie codec. +const EMPTY_TRIE_NO_EXT: u8 = 0; +const NIBBLE_SIZE_BOUND_NO_EXT: usize = u16::max_value() as usize; +const LEAF_PREFIX_MASK_NO_EXT: u8 = 0b_01 << 6; +const BRANCH_WITHOUT_MASK_NO_EXT: u8 = 0b_10 << 6; +const BRANCH_WITH_MASK_NO_EXT: u8 = 0b_11 << 6; + /// Create a leaf/extension node, encoding a number of nibbles. Note that this /// cannot handle a number of nibbles that is zero or greater than 125 and if /// you attempt to do so *IT WILL PANIC*. fn fuse_nibbles_node<'a>(nibbles: &'a [u8], leaf: bool) -> impl Iterator + 'a { - debug_assert!(nibbles.len() < LEAF_NODE_OVER.min(EXTENSION_NODE_OVER) as usize, "nibbles length too long. what kind of size of key are you trying to include in the trie!?!"); + debug_assert!( + nibbles.len() < LEAF_NODE_OVER.min(EXTENSION_NODE_OVER) as usize, + "nibbles length too long. what kind of size of key are you trying to include in the trie!?!" + ); let first_byte = if leaf { LEAF_NODE_OFFSET } else { @@ -67,22 +168,66 @@ fn fuse_nibbles_node<'a>(nibbles: &'a [u8], leaf: bool) -> impl Iterator) -> [u8; 3] { +enum NodeKindNoExt { + Leaf, + BranchNoValue, + BranchWithValue, +} + +/// Create a leaf or branch node header followed by its encoded partial nibbles. +fn fuse_nibbles_node_no_extension<'a>( + nibbles: &'a [u8], + kind: NodeKindNoExt, +) -> impl Iterator + 'a { + let size = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibbles.len()); + + let iter_start = match kind { + NodeKindNoExt::Leaf => size_and_prefix_iterator(size, LEAF_PREFIX_MASK_NO_EXT), + NodeKindNoExt::BranchNoValue => size_and_prefix_iterator(size, BRANCH_WITHOUT_MASK_NO_EXT), + NodeKindNoExt::BranchWithValue => size_and_prefix_iterator(size, BRANCH_WITH_MASK_NO_EXT), + }; + iter_start + .chain(if nibbles.len() % 2 == 1 { Some(nibbles[0]) } else { None }) + .chain(nibbles[nibbles.len() % 2..].chunks(2).map(|ch| ch[0] << 4 | ch[1])) +} + +/// Encoding of branch header and children bitmap (for trie stream radix 16). +/// For stream variant with extension. +fn branch_node(has_value: bool, has_children: impl Iterator) -> [u8; 3] { + let mut result = [0, 0, 0]; + branch_node_buffered(has_value, has_children, &mut result[..]); + result +} + +/// Encoding of branch header and children bitmap for any radix. +/// For codec/stream variant with extension. +fn branch_node_buffered>( + has_value: bool, + has_children: I, + output: &mut[u8], +) { let first = if has_value { BRANCH_NODE_WITH_VALUE } else { BRANCH_NODE_NO_VALUE }; + output[0] = first; + Bitmap::encode(has_children, &mut output[1..]); +} + +/// Encoding of children bitmap (for trie stream radix 16). +/// For stream variant without extension. +fn branch_node_bit_mask(has_children: impl Iterator) -> (u8, u8) { let mut bitmap: u16 = 0; let mut cursor: u16 = 1; for v in has_children { if v { bitmap |= cursor } cursor <<= 1; } - [first, (bitmap % 256 ) as u8, (bitmap / 256 ) as u8] + ((bitmap % 256 ) as u8, (bitmap / 256 ) as u8) } -/// Reference implementation of a `TrieStream`. +/// Reference implementation of a `TrieStream` with extension nodes. #[derive(Default, Clone)] pub struct ReferenceTrieStream { buffer: Vec @@ -104,8 +249,17 @@ impl TrieStream for ReferenceTrieStream { value.encode_to(&mut self.buffer); } - fn begin_branch(&mut self, maybe_value: Option<&[u8]>, has_children: impl Iterator) { + fn begin_branch( + &mut self, + maybe_key: Option<&[u8]>, + maybe_value: Option<&[u8]>, + has_children: impl Iterator, + ) { self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); + if let Some(partial) = maybe_key { + // should not happen + self.buffer.extend(fuse_nibbles_node(partial, false)); + } if let Some(value) = maybe_value { value.encode_to(&mut self.buffer); } @@ -118,7 +272,71 @@ impl TrieStream for ReferenceTrieStream { fn append_substream(&mut self, other: Self) { let data = other.out(); match data.len() { - 0...31 => data.encode_to(&mut self.buffer), + 0..=31 => data.encode_to(&mut self.buffer), + _ => H::hash(&data).as_ref().encode_to(&mut self.buffer), + } + } + + fn out(self) -> Vec { self.buffer } +} + +/// Reference implementation of a `TrieStream` without extension. +#[derive(Default, Clone)] +pub struct ReferenceTrieStreamNoExt { + buffer: Vec +} + +impl TrieStream for ReferenceTrieStreamNoExt { + fn new() -> Self { + ReferenceTrieStreamNoExt { + buffer: Vec::new() + } + } + + fn append_empty_data(&mut self) { + self.buffer.push(EMPTY_TRIE_NO_EXT); + } + + fn append_leaf(&mut self, key: &[u8], value: &[u8]) { + self.buffer.extend(fuse_nibbles_node_no_extension(key, NodeKindNoExt::Leaf)); + value.encode_to(&mut self.buffer); + } + + fn begin_branch( + &mut self, + maybe_key: Option<&[u8]>, + maybe_value: Option<&[u8]>, + has_children: impl Iterator + ) { + if let Some(partial) = maybe_key { + if maybe_value.is_some() { + self.buffer.extend( + fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchWithValue) + ); + } else { + self.buffer.extend( + fuse_nibbles_node_no_extension(partial, NodeKindNoExt::BranchNoValue) + ); + } + let bitmap = branch_node_bit_mask(has_children); + self.buffer.extend([bitmap.0, bitmap.1].iter()); + } else { + // should not happen + self.buffer.extend(&branch_node(maybe_value.is_some(), has_children)); + } + if let Some(value) = maybe_value { + value.encode_to(&mut self.buffer); + } + } + + fn append_extension(&mut self, _key: &[u8]) { + // should not happen + } + + fn append_substream(&mut self, other: Self) { + let data = other.out(); + match data.len() { + 0..=31 => data.encode_to(&mut self.buffer), _ => H::hash(&data).as_ref().encode_to(&mut self.buffer), } } @@ -135,14 +353,108 @@ enum NodeHeader { Leaf(usize), } +/// A node header no extension. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum NodeHeaderNoExt { + Null, + Branch(bool, usize), + Leaf(usize), +} + impl Encode for NodeHeader { fn encode_to(&self, output: &mut T) { match self { NodeHeader::Null => output.push_byte(EMPTY_TRIE), NodeHeader::Branch(true) => output.push_byte(BRANCH_NODE_WITH_VALUE), NodeHeader::Branch(false) => output.push_byte(BRANCH_NODE_NO_VALUE), - NodeHeader::Leaf(nibble_count) => output.push_byte(LEAF_NODE_OFFSET + *nibble_count as u8), - NodeHeader::Extension(nibble_count) => output.push_byte(EXTENSION_NODE_OFFSET + *nibble_count as u8), + NodeHeader::Leaf(nibble_count) => + output.push_byte(LEAF_NODE_OFFSET + *nibble_count as u8), + NodeHeader::Extension(nibble_count) => + output.push_byte(EXTENSION_NODE_OFFSET + *nibble_count as u8), + } + } +} + +/// Encode and allocate node type header (type and size), and partial value. +/// It uses an iterator over encoded partial bytes as input. +fn size_and_prefix_iterator(size: usize, prefix: u8) -> impl Iterator { + let size = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, size); + + let l1 = std::cmp::min(62, size); + let (first_byte, mut rem) = if size == l1 { + (once(prefix + l1 as u8), 0) + } else { + (once(prefix + 63), size - l1) + }; + let next_bytes = move || { + if rem > 0 { + if rem < 256 { + let result = rem - 1; + rem = 0; + Some(result as u8) + } else { + rem = rem.saturating_sub(255); + Some(255) + } + } else { + None + } + }; + first_byte.chain(::std::iter::from_fn(next_bytes)) +} + +fn encode_size_and_prefix(size: usize, prefix: u8, out: &mut impl Output) { + for b in size_and_prefix_iterator(size, prefix) { + out.push_byte(b) + } +} + +fn decode_size(first: u8, input: &mut I) -> Result { + let mut result = (first & 255u8 >> 2) as usize; + if result < 63 { + return Ok(result); + } + result -= 1; + while result <= NIBBLE_SIZE_BOUND_NO_EXT { + let n = input.read_byte()? as usize; + if n < 255 { + return Ok(result + n + 1); + } + result += 255; + } + Err("Size limit reached for a nibble slice".into()) +} + +#[test] +fn test_encoding_simple_trie() { + for prefix in [ + LEAF_PREFIX_MASK_NO_EXT, + BRANCH_WITHOUT_MASK_NO_EXT, + BRANCH_WITH_MASK_NO_EXT, + ].iter() { + for i in (0..1000).chain(NIBBLE_SIZE_BOUND_NO_EXT - 2..NIBBLE_SIZE_BOUND_NO_EXT + 2) { + let mut output = Vec::new(); + encode_size_and_prefix(i, *prefix, &mut output); + let input = &mut &output[..]; + let first = input.read_byte().unwrap(); + assert_eq!(first & (0b11 << 6), *prefix); + let v = decode_size(first, input); + assert_eq!(Ok(std::cmp::min(i, NIBBLE_SIZE_BOUND_NO_EXT)), v); + } + + } +} + +impl Encode for NodeHeaderNoExt { + fn encode_to(&self, output: &mut T) { + match self { + NodeHeaderNoExt::Null => output.push_byte(EMPTY_TRIE_NO_EXT), + NodeHeaderNoExt::Branch(true, nibble_count) => + encode_size_and_prefix(*nibble_count, BRANCH_WITH_MASK_NO_EXT, output), + NodeHeaderNoExt::Branch(false, nibble_count) => + encode_size_and_prefix(*nibble_count, BRANCH_WITHOUT_MASK_NO_EXT, output), + NodeHeaderNoExt::Leaf(nibble_count) => + encode_size_and_prefix(*nibble_count, LEAF_PREFIX_MASK_NO_EXT, output), } } } @@ -153,34 +465,43 @@ impl Decode for NodeHeader { EMPTY_TRIE => NodeHeader::Null, BRANCH_NODE_NO_VALUE => NodeHeader::Branch(false), BRANCH_NODE_WITH_VALUE => NodeHeader::Branch(true), - i @ LEAF_NODE_OFFSET ... LEAF_NODE_LAST => NodeHeader::Leaf((i - LEAF_NODE_OFFSET) as usize), - i @ EXTENSION_NODE_OFFSET ... EXTENSION_NODE_LAST => NodeHeader::Extension((i - EXTENSION_NODE_OFFSET) as usize), + i @ LEAF_NODE_OFFSET ..= LEAF_NODE_LAST => + NodeHeader::Leaf((i - LEAF_NODE_OFFSET) as usize), + i @ EXTENSION_NODE_OFFSET ..= EXTENSION_NODE_LAST => + NodeHeader::Extension((i - EXTENSION_NODE_OFFSET) as usize), }) } } +impl Decode for NodeHeaderNoExt { + fn decode(input: &mut I) -> Result { + let i = input.read_byte()?; + if i == EMPTY_TRIE_NO_EXT { + return Ok(NodeHeaderNoExt::Null); + } + match i & (0b11 << 6) { + LEAF_PREFIX_MASK_NO_EXT => + Ok(NodeHeaderNoExt::Leaf(decode_size(i, input)?)), + BRANCH_WITHOUT_MASK_NO_EXT => + Ok(NodeHeaderNoExt::Branch(false, decode_size(i, input)?)), + BRANCH_WITH_MASK_NO_EXT => + Ok(NodeHeaderNoExt::Branch(true, decode_size(i, input)?)), + // do not allow any special encoding + _ => Err("Unknown type of node".into()), + } + } +} + /// Simple reference implementation of a `NodeCodec`. #[derive(Default, Clone)] pub struct ReferenceNodeCodec; -#[derive(Debug, PartialEq, Eq, Clone)] -/// Error concerning the Parity-Codec based decoder. -pub enum ReferenceError { - /// Bad format. - BadFormat, -} - -impl StdError for ReferenceError { - fn description(&self) -> &str { - "codec error" - } -} - -impl fmt::Display for ReferenceError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self, f) - } -} +/// Simple reference implementation of a `NodeCodec`. +/// Even if implementation follows initial specification of +/// https://github.com/w3f/polkadot-re-spec/issues/8, this may +/// not follow it in the future, it is mainly the testing codec without extension node. +#[derive(Default, Clone)] +pub struct ReferenceNodeCodecNoExt; fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { if input.len() < count { @@ -191,69 +512,140 @@ fn take<'a>(input: &mut &'a[u8], count: usize) -> Option<&'a[u8]> { Some(r) } -fn partial_to_key(partial: &[u8], offset: u8, over: u8) -> Vec { - let nibble_count = (partial.len() - 1) * 2 + if partial[0] & 16 == 16 { 1 } else { 0 }; +fn partial_to_key(partial: Partial, offset: u8, over: u8) -> Vec { + let number_nibble_encoded = (partial.0).0 as usize; + let nibble_count = partial.1.len() * nibble_ops::NIBBLE_PER_BYTE + number_nibble_encoded; assert!(nibble_count < over as usize); let mut output = vec![offset + nibble_count as u8]; - if nibble_count % 2 == 1 { - output.push(partial[0] & 0x0f); + if number_nibble_encoded > 0 { + output.push(nibble_ops::pad_right((partial.0).1)); + } + output.extend_from_slice(&partial.1[..]); + output +} + +fn partial_from_iterator_to_key>( + partial: I, + nibble_count: usize, + offset: u8, + over: u8, +) -> Vec { + assert!(nibble_count < over as usize); + let mut output = Vec::with_capacity(1 + (nibble_count / nibble_ops::NIBBLE_PER_BYTE)); + output.push(offset + nibble_count as u8); + output.extend(partial); + output +} + +fn partial_from_iterator_encode>( + partial: I, + nibble_count: usize, + node_kind: NodeKindNoExt, +) -> Vec { + let nibble_count = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibble_count); + + let mut output = Vec::with_capacity(3 + (nibble_count / nibble_ops::NIBBLE_PER_BYTE)); + match node_kind { + NodeKindNoExt::Leaf => + NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchWithValue => + NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchNoValue => + NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), + }; + output.extend(partial); + output +} + +fn partial_encode(partial: Partial, node_kind: NodeKindNoExt) -> Vec { + let number_nibble_encoded = (partial.0).0 as usize; + let nibble_count = partial.1.len() * nibble_ops::NIBBLE_PER_BYTE + number_nibble_encoded; + + let nibble_count = ::std::cmp::min(NIBBLE_SIZE_BOUND_NO_EXT, nibble_count); + + let mut output = Vec::with_capacity(3 + partial.1.len()); + match node_kind { + NodeKindNoExt::Leaf => + NodeHeaderNoExt::Leaf(nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchWithValue => + NodeHeaderNoExt::Branch(true, nibble_count).encode_to(&mut output), + NodeKindNoExt::BranchNoValue => + NodeHeaderNoExt::Branch(false, nibble_count).encode_to(&mut output), + }; + if number_nibble_encoded > 0 { + output.push(nibble_ops::pad_right((partial.0).1)); } - output.extend_from_slice(&partial[1..]); + output.extend_from_slice(&partial.1[..]); output } // 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 = ReferenceError; +// 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; - fn hashed_null_node() -> ::Out { - KeccakHasher::hash(&[0u8][..]) + fn hashed_null_node() -> ::Out { + H::hash(>::empty_node()) } fn decode(data: &[u8]) -> ::std::result::Result { let input = &mut &*data; - match NodeHeader::decode(input).map_err(|_| ReferenceError::BadFormat)? { + match NodeHeader::decode(input)? { NodeHeader::Null => Ok(Node::Empty), NodeHeader::Branch(has_value) => { - let bitmap = u16::decode(input).map_err(|_| ReferenceError::BadFormat)?; + let bitmap_slice = take(input, BITMAP_LENGTH) + .ok_or(CodecError::from("Bad format"))?; + let bitmap = Bitmap::decode(&bitmap_slice[..])?; + let value = if has_value { - let count = >::decode(input).map_err(|_| ReferenceError::BadFormat)?.0 as usize; - Some(take(input, count).ok_or(ReferenceError::BadFormat)?) + let count = >::decode(input)?.0 as usize; + Some(take(input, count).ok_or(CodecError::from("Bad format"))?) } else { None }; let mut children = [None; 16]; - let mut pot_cursor = 1; - for i in 0..16 { - if bitmap & pot_cursor != 0 { - let count = >::decode(input).map_err(|_| ReferenceError::BadFormat)?.0 as usize; - children[i] = Some(take(input, count).ok_or(ReferenceError::BadFormat)?); + + for i in 0..nibble_ops::NIBBLE_LENGTH { + if bitmap.value_at(i) { + let count = >::decode(input)?.0 as usize; + children[i] = Some(take(input, count).ok_or(CodecError::from("Bad format"))?); } - pot_cursor <<= 1; } Ok(Node::Branch(children, value)) } NodeHeader::Extension(nibble_count) => { - let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); - let count = >::decode(input).map_err(|_| ReferenceError::BadFormat)?.0 as usize; - Ok(Node::Extension(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) + let nibble_data = take( + input, + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, + ).ok_or(CodecError::from("Bad format"))?; + let nibble_slice = NibbleSlice::new_offset(nibble_data, + nibble_ops::number_padding(nibble_count)); + let count = >::decode(input)?.0 as usize; + Ok(Node::Extension(nibble_slice, take(input, count) + .ok_or(CodecError::from("Bad format"))?)) } NodeHeader::Leaf(nibble_count) => { - let nibble_data = take(input, (nibble_count + 1) / 2).ok_or(ReferenceError::BadFormat)?; - let nibble_slice = NibbleSlice::new_offset(nibble_data, nibble_count % 2); - let count = >::decode(input).map_err(|_| ReferenceError::BadFormat)?.0 as usize; - Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(ReferenceError::BadFormat)?)) + let nibble_data = take( + input, + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, + ).ok_or(CodecError::from("Bad format"))?; + let nibble_slice = NibbleSlice::new_offset( + nibble_data, + nibble_ops::number_padding(nibble_count), + ); + let count = >::decode(input)?.0 as usize; + Ok(Node::Leaf(nibble_slice, take(input, count) + .ok_or(CodecError::from("Bad format"))?)) } } } - fn try_decode_hash(data: &[u8]) -> Option<::Out> { - if data.len() == KeccakHasher::LENGTH { - let mut r = ::Out::default(); + fn try_decode_hash(data: &[u8]) -> Option<::Out> { + if data.len() == H::LENGTH { + let mut r = ::Out::default(); r.as_mut().copy_from_slice(data); Some(r) } else { @@ -262,50 +654,542 @@ impl NodeCodec for ReferenceNodeCodec { } fn is_empty_node(data: &[u8]) -> bool { - data == &[EMPTY_TRIE][..] + data == >::empty_node() } - fn empty_node() -> Vec { - vec![EMPTY_TRIE] + fn empty_node() -> &'static[u8] { + &[EMPTY_TRIE] } - fn leaf_node(partial: &[u8], value: &[u8]) -> Vec { + fn leaf_node(partial: Partial, value: &[u8]) -> Vec { let mut output = partial_to_key(partial, LEAF_NODE_OFFSET, LEAF_NODE_OVER); value.encode_to(&mut output); output } - fn ext_node(partial: &[u8], child: ChildReference<::Out>) -> Vec { - let mut output = partial_to_key(partial, EXTENSION_NODE_OFFSET, EXTENSION_NODE_OVER); + fn extension_node( + partial: impl Iterator, + number_nibble: usize, + child: ChildReference<::Out>, + ) -> Vec { + let mut output = partial_from_iterator_to_key( + partial, + number_nibble, + EXTENSION_NODE_OFFSET, + EXTENSION_NODE_OVER, + ); match child { ChildReference::Hash(h) => h.as_ref().encode_to(&mut output), - ChildReference::Inline(inline_data, len) => (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), + ChildReference::Inline(inline_data, len) => + (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output), }; output } - fn branch_node(children: I, maybe_value: Option) -> Vec where - I: IntoIterator::Out>>> + Iterator::Out>>> - { - let mut output = vec![0, 0, 0]; + fn branch_node( + children: impl Iterator::Out>>>>, + maybe_value: Option<&[u8]>, + ) -> Vec { + let mut output = vec![0; BITMAP_LENGTH + 1]; + let mut prefix: [u8; 3] = [0; 3]; let have_value = if let Some(value) = maybe_value { - (&*value).encode_to(&mut output); + value.encode_to(&mut output); true } else { false }; - let prefix = branch_node(have_value, children.map(|maybe_child| match maybe_child { + let has_children = children.map(|maybe_child| match maybe_child.borrow() { + Some(ChildReference::Hash(h)) => { + h.as_ref().encode_to(&mut output); + true + } + &Some(ChildReference::Inline(inline_data, len)) => { + inline_data.as_ref()[..len].encode_to(&mut output); + true + } + None => false, + }); + branch_node_buffered(have_value, has_children, prefix.as_mut()); + output[0..BITMAP_LENGTH + 1].copy_from_slice(prefix.as_ref()); + output + } + + fn branch_node_nibbled( + _partial: impl Iterator, + _number_nibble: usize, + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>) -> Vec { + unreachable!() + } + +} + +impl NodeCodec for ReferenceNodeCodecNoExt { + type Error = CodecError; + + fn hashed_null_node() -> ::Out { + H::hash(>::empty_node()) + } + + fn decode(data: &[u8]) -> ::std::result::Result { + let input = &mut &*data; + let head = NodeHeaderNoExt::decode(input)?; + match head { + NodeHeaderNoExt::Null => Ok(Node::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) + if padding && nibble_ops::pad_left(input[0]) != 0 { + return Err(CodecError::from("Bad format")); + } + let nibble_data = take( + input, + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, + ).ok_or(CodecError::from("Bad format"))?; + let nibble_slice = NibbleSlice::new_offset( + nibble_data, + nibble_ops::number_padding(nibble_count), + ); + let bitmap_slice = take( + input, + BITMAP_LENGTH, + ).ok_or(CodecError::from("Bad format"))?; + let bitmap = Bitmap::decode(&bitmap_slice[..])?; + let value = if has_value { + let count = >::decode(input)?.0 as usize; + Some(take(input, count).ok_or(CodecError::from("Bad format"))?) + } else { + None + }; + let mut children = [None; 16]; + + for i in 0..nibble_ops::NIBBLE_LENGTH { + if bitmap.value_at(i) { + let count = >::decode(input)?.0 as usize; + children[i] = Some(take(input, count).ok_or(CodecError::from("Bad format"))?); + } + } + Ok(Node::NibbledBranch(nibble_slice, children, value)) + } + NodeHeaderNoExt::Leaf(nibble_count) => { + let padding = nibble_count % nibble_ops::NIBBLE_PER_BYTE != 0; + // check that the padding is valid (if any) + if padding && nibble_ops::pad_left(input[0]) != 0 { + return Err(CodecError::from("Bad format")); + } + let nibble_data = take( + input, + (nibble_count + (nibble_ops::NIBBLE_PER_BYTE - 1)) / nibble_ops::NIBBLE_PER_BYTE, + ).ok_or(CodecError::from("Bad format"))?; + let nibble_slice = NibbleSlice::new_offset( + nibble_data, + nibble_ops::number_padding(nibble_count), + ); + let count = >::decode(input)?.0 as usize; + Ok(Node::Leaf(nibble_slice, take(input, count).ok_or(CodecError::from("Bad format"))?)) + } + } + } + + fn try_decode_hash(data: &[u8]) -> Option<::Out> { + >::try_decode_hash(data) + } + + fn is_empty_node(data: &[u8]) -> bool { + data == >::empty_node() + } + + fn empty_node() -> &'static [u8] { + &[EMPTY_TRIE_NO_EXT] + } + + fn leaf_node(partial: Partial, value: &[u8]) -> Vec { + let mut output = partial_encode(partial, NodeKindNoExt::Leaf); + value.encode_to(&mut output); + output + } + + fn extension_node( + _partial: impl Iterator, + _nbnibble: usize, + _child: ChildReference<::Out>, + ) -> Vec { + unreachable!() + } + + fn branch_node( + _children: impl Iterator::Out>>>>, + _maybe_value: Option<&[u8]>, + ) -> Vec { + unreachable!() + } + + fn branch_node_nibbled( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator::Out>>>>, + maybe_value: Option<&[u8]>, + ) -> Vec { + let mut output = if maybe_value.is_some() { + partial_from_iterator_encode( + partial, + number_nibble, + NodeKindNoExt::BranchWithValue, + ) + } else { + partial_from_iterator_encode( + partial, + number_nibble, + NodeKindNoExt::BranchNoValue, + ) + }; + let bitmap_index = output.len(); + let mut bitmap: [u8; BITMAP_LENGTH] = [0; BITMAP_LENGTH]; + (0..BITMAP_LENGTH).for_each(|_| output.push(0)); + if let Some(value) = maybe_value { + value.encode_to(&mut output); + }; + Bitmap::encode(children.map(|maybe_child| match maybe_child.borrow() { Some(ChildReference::Hash(h)) => { h.as_ref().encode_to(&mut output); true } - Some(ChildReference::Inline(inline_data, len)) => { - (&AsRef::<[u8]>::as_ref(&inline_data)[..len]).encode_to(&mut output); + &Some(ChildReference::Inline(inline_data, len)) => { + inline_data.as_ref()[..len].encode_to(&mut output); true } None => false, - })); - output[0..3].copy_from_slice(&prefix[..]); + }), bitmap.as_mut()); + output[bitmap_index..bitmap_index + BITMAP_LENGTH] + .copy_from_slice(&bitmap.as_ref()[..BITMAP_LENGTH]); output } + +} + +/// Compare trie builder and in memory trie. +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 = { + let mut root = Default::default(); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + for i in 0..data.len() { + t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); + } + t.commit(); + t.root().clone() + }; + if root_new != root { + { + let db : &dyn hash_db::HashDB<_, _> = &hashdb; + let t = RefTrieDB::new(&db, &root_new).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:x?}", a); + } + } + { + let db : &dyn hash_db::HashDB<_, _> = &memdb; + let t = RefTrieDB::new(&db, &root).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:x?}", a); + } + } + } + + assert_eq!(root, root_new); + // compare db content for key fuzzing + assert!(memdb == hashdb); +} + +/// Compare trie builder and trie root implementations. +pub fn compare_root( + data: Vec<(Vec, Vec)>, + mut memdb: impl hash_db::HashDB, +) { + let root_new = { + let mut cb = TrieRoot::::default(); + trie_visit::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = { + let mut root = Default::default(); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + for i in 0..data.len() { + t.insert(&data[i].0[..], &data[i].1[..]).unwrap(); + } + t.root().clone() + }; + + assert_eq!(root, root_new); +} + +/// Compare trie builder and trie root unhashed implementations. +pub fn compare_unhashed( + data: Vec<(Vec, Vec)>, +) { + let root_new = { + let mut cb = trie_db::TrieRootUnhashed::::default(); + trie_visit::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = reference_trie_root_unhashed(data); + + assert_eq!(root, root_new); +} + +/// Compare trie builder and trie root unhashed implementations. +/// This uses the variant without extension nodes. +pub fn compare_unhashed_no_extension( + data: Vec<(Vec, Vec)>, +) { + let root_new = { + let mut cb = trie_db::TrieRootUnhashed::::default(); + trie_visit::(data.clone().into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + let root = reference_trie_root_unhashed_no_extension(data); + + assert_eq!(root, root_new); +} + +/// 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( + data: I, +) -> ::Out + where + 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()) +} + +/// Trie builder trie building utility. +pub fn calc_root_build( + data: I, + hashdb: &mut DB +) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]> + Ord + fmt::Debug, + B: AsRef<[u8]> + fmt::Debug, + DB: hash_db::HashDB +{ + let mut cb = TrieBuilder::new(hashdb); + trie_visit::(data.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) +} + +/// Trie builder trie building utility. +/// This uses the variant without extension nodes. +pub fn calc_root_build_no_extension( + data: I, + hashdb: &mut DB, +) -> ::Out + 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().clone() + }; + + if root != root_new { + { + let db : &dyn hash_db::HashDB<_, _> = &memdb; + let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + { + let db : &dyn hash_db::HashDB<_, _> = &hashdb; + let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + } + + assert_eq!(root, root_new); +} + +/// `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( + data: Vec<(Vec, Vec)>, + mut memdb: impl hash_db::HashDB, + mut hashdb: impl hash_db::HashDB, +) { + 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); + 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().clone() + }; + let root_new = { + let mut cb = TrieBuilder::new(&mut hashdb); + trie_visit::(b_map.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + }; + + if root != root_new { + { + let db : &dyn hash_db::HashDB<_, _> = &memdb; + let t = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + { + let db : &dyn hash_db::HashDB<_, _> = &hashdb; + let t = RefTrieDBNoExt::new(&db, &root_new).unwrap(); + println!("{:?}", t); + for a in t.iter().unwrap() { + println!("a:{:?}", a); + } + } + } + + assert_eq!(root, root_new); +} + +/// Testing utility that uses some periodic removal over +/// its input test data. +pub fn compare_no_extension_insert_remove( + data: Vec<(bool, Vec, Vec)>, + mut memdb: impl hash_db::HashDB, +) { + 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); + t.commit(); + } + while a < data.len() { + // new triemut every 3 element + root = { + let mut t = RefTrieDBMutNoExt::from_existing(&mut memdb, &mut root).unwrap(); + for _ in 0..3 { + if data[a].0 { + // remove + t.remove(&data[a].1[..]).unwrap(); + data2.remove(&data[a].1[..]); + } else { + // add + t.insert(&data[a].1[..], &data[a].2[..]).unwrap(); + data2.insert(&data[a].1[..], &data[a].2[..]); + } + + a += 1; + if a == data.len() { + break; + } + } + t.commit(); + *t.root() + }; + } + let mut t = RefTrieDBMutNoExt::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)); +} + +#[test] +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 = > + ::leaf_node(((0, 0), &input), &[1]); + let dec = > + ::decode(&enc).unwrap(); + let o_sl = if let Node::Leaf(sl, _) = dec { + Some(sl) + } else { None }; + assert!(o_sl.is_some()); +} + +#[test] +fn size_encode_limit_values () { + let sizes = [0, 1, 62, 63, 64, 317, 318, 319, 572, 573, 574]; + let encs = [ + vec![0], + vec![1], + vec![0x3e], + vec![0x3f, 0], + vec![0x3f, 1], + vec![0x3f, 0xfe], + vec![0x3f, 0xff, 0], + vec![0x3f, 0xff, 1], + vec![0x3f, 0xff, 0xfe], + vec![0x3f, 0xff, 0xff, 0], + vec![0x3f, 0xff, 0xff, 1], + ]; + for i in 0..sizes.len() { + let mut enc = Vec::new(); + encode_size_and_prefix(sizes[i], 0, &mut enc); + assert_eq!(enc, encs[i]); + let s_dec = decode_size(encs[i][0], &mut &encs[i][1..]); + assert_eq!(s_dec, Ok(sizes[i])); + } } diff --git a/test-support/trie-bench/Cargo.toml b/test-support/trie-bench/Cargo.toml index c573dc66..9d0cd891 100644 --- a/test-support/trie-bench/Cargo.toml +++ b/test-support/trie-bench/Cargo.toml @@ -8,11 +8,11 @@ license = "Apache-2.0" edition = "2018" [dependencies] -keccak-hasher = { path = "../keccak-hasher", version = "0.14.0" } -trie-standardmap = { path = "../trie-standardmap", version = "0.14.0" } -hash-db = { path = "../../hash-db" , version = "0.14.0"} -memory-db = { path = "../../memory-db", version = "0.14.0" } -trie-root = { path = "../../trie-root", version = "0.14.0" } -trie-db = { path = "../../trie-db", version = "0.14.0" } +keccak-hasher = { path = "../keccak-hasher", version = "0.15.0" } +trie-standardmap = { path = "../trie-standardmap", version = "0.15.0" } +hash-db = { path = "../../hash-db" , version = "0.15.0"} +memory-db = { path = "../../memory-db", version = "0.15.0" } +trie-root = { path = "../../trie-root", version = "0.15.0" } +trie-db = { path = "../../trie-db", version = "0.15.0" } criterion = "0.2.8" parity-scale-codec = { version = "1.0.3" } diff --git a/test-support/trie-bench/src/lib.rs b/test-support/trie-bench/src/lib.rs index 0b14dccb..41750de5 100644 --- a/test-support/trie-bench/src/lib.rs +++ b/test-support/trie-bench/src/lib.rs @@ -19,7 +19,8 @@ use criterion::{Criterion, black_box, Fun}; use keccak_hasher::KeccakHasher; use hash_db::Hasher; use memory_db::{MemoryDB, HashKey}; -use trie_db::{NodeCodec, TrieDB, TrieDBMut, Trie, TrieMut}; +use trie_db::{NodeCodec, TrieDB, TrieDBMut, Trie, TrieMut, TrieLayout, TrieHash}; +use std::default::Default; use trie_root::{TrieStream, trie_root}; use trie_standardmap::*; @@ -36,33 +37,33 @@ impl ::std::fmt::Debug for TrieInsertionList { } } -fn benchmark, S: TrieStream>(b: &mut Criterion, name: &str, content: Vec<(Vec, Vec)>) +fn benchmark(b: &mut Criterion, name: &str, content: Vec<(Vec, Vec)>) where - ::Out: 'static + ::Out: 'static { let funs = vec![ Fun::new("Closed", |b, d: &TrieInsertionList| b.iter(&mut ||{ - trie_root::(d.0.clone()) + trie_root::(d.0.clone()) })), Fun::new("Fill", |b, d: &TrieInsertionList| b.iter(&mut ||{ - let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&N::empty_node()[..]); - let mut root = H::Out::default(); - let mut t = TrieDBMut::::new(&mut memdb, &mut root); + let mut memdb = MemoryDB::<_, HashKey, _>::new(&L::Codec::empty_node()[..]); + let mut root = >::default(); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for i in d.0.iter() { t.insert(&i.0, &i.1).unwrap(); } })), Fun::new("Iter", |b, d: &TrieInsertionList| { - let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&N::empty_node()[..]); - let mut root = H::Out::default(); + let mut memdb = MemoryDB::<_, HashKey<_>, _>::new(&L::Codec::empty_node()[..]); + let mut root = >::default(); { - let mut t = TrieDBMut::::new(&mut memdb, &mut root); + let mut t = TrieDBMut::::new(&mut memdb, &mut root); for i in d.0.iter() { t.insert(&i.0, &i.1).unwrap(); } } b.iter(&mut ||{ - let t = TrieDB::::new(&memdb, &root).unwrap(); + let t = TrieDB::::new(&memdb, &root).unwrap(); for n in t.iter().unwrap() { black_box(n).unwrap(); } @@ -99,7 +100,7 @@ fn random_value(seed: &mut ::Out) -> Vec { } } -pub fn standard_benchmark, S: TrieStream>(b: &mut Criterion, name: &str) { +pub fn standard_benchmark(b: &mut Criterion, name: &str) { // Typical ethereum transaction payload passing through `verify_block_integrity()` close to block #6317032; // 140 iteams, avg length 157bytes, total 22033bytes payload (expected root: 0xc1382bbef81d10a41d325e2873894b61162fb1e6167cafc663589283194acfda) @@ -250,7 +251,7 @@ pub fn standard_benchmark, S: TrieStream>( .enumerate() .map(|(i, v)| (Compact(i as u32).encode(), v) ) .collect::>(); - benchmark::(b, &format!("{}.typical_txs", name), d); + benchmark::(b, &format!("{}.typical_txs", name), d); let st = StandardMap { alphabet: Alphabet::All, @@ -259,7 +260,7 @@ pub fn standard_benchmark, S: TrieStream>( value_mode: ValueMode::Mirror, count: 1000, }; - benchmark::(b, &format!("{}.32_mir_1k", name), st.make()); + benchmark::(b, &format!("{}.32_mir_1k", name), st.make()); let st = StandardMap { alphabet: Alphabet::All, @@ -268,7 +269,7 @@ pub fn standard_benchmark, S: TrieStream>( value_mode: ValueMode::Random, count: 1000, }; - benchmark::(b, &format!("{}.32_ran_1k", name), st.make()); + benchmark::(b, &format!("{}.32_ran_1k", name), st.make()); let mut d: Vec<(Vec, Vec)> = Vec::new(); let mut seed = ::Out::default(); @@ -278,7 +279,7 @@ pub fn standard_benchmark, S: TrieStream>( d.push((k, v)) } - benchmark::(b, &format!("{}.six_high_1k", name), d); + benchmark::(b, &format!("{}.six_high_1k", name), d); let alphabet = b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_"; let mut d: Vec<(Vec, Vec)> = Vec::new(); @@ -288,7 +289,7 @@ pub fn standard_benchmark, S: TrieStream>( let v = random_value(&mut seed); d.push((k, v)) } - benchmark::(b, &format!("{}.six_mid_1k", name), d); + benchmark::(b, &format!("{}.six_mid_1k", name), d); let alphabet = b"@QWERTYUIOPASDFGHJKLZXCVBNM[/]^_"; let mut d: Vec<(Vec, Vec)> = Vec::new(); @@ -299,7 +300,7 @@ pub fn standard_benchmark, S: TrieStream>( d.push((k, v)) } - benchmark::(b, &format!("{}.random_mid_1k", name), d); + benchmark::(b, &format!("{}.random_mid_1k", name), d); let alphabet = b"abcdef"; let mut d: Vec<(Vec, Vec)> = Vec::new(); @@ -310,5 +311,5 @@ pub fn standard_benchmark, S: TrieStream>( d.push((k, v)) } - benchmark::(b, &format!("{}.six_low_1k", name), d); + benchmark::(b, &format!("{}.six_low_1k", name), d); } diff --git a/test-support/trie-standardmap/Cargo.toml b/test-support/trie-standardmap/Cargo.toml index 2cca564b..e07efe2a 100644 --- a/test-support/trie-standardmap/Cargo.toml +++ b/test-support/trie-standardmap/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "trie-standardmap" description = "Standard test map for profiling tries" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] license = "Apache-2.0" [dependencies] -keccak-hasher = { path = "../keccak-hasher", version = "0.14.0"} -hash-db = { path = "../../hash-db" , version = "0.14.0"} +keccak-hasher = { path = "../keccak-hasher", version = "0.15.0"} +hash-db = { path = "../../hash-db" , version = "0.15.0"} diff --git a/trie-db/Cargo.toml b/trie-db/Cargo.toml index 7b5de948..8969e88d 100644 --- a/trie-db/Cargo.toml +++ b/trie-db/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trie-db" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "Merkle-Patricia Trie generic over key hasher and node encoding" repository = "https://github.com/paritytech/trie" @@ -10,19 +10,21 @@ license = "Apache-2.0" log = "0.4" rand = { version = "0.6", default-features = false } elastic-array = { version = "0.10", default-features = false } -hash-db = { path = "../hash-db", default-features = false, version = "0.14.0"} +hash-db = { path = "../hash-db", default-features = false, version = "0.15.0"} hashmap_core = { version = "0.1" } [dev-dependencies] env_logger = "0.6" -memory-db = { path = "../memory-db", version = "0.14.0" } -trie-root = { path = "../trie-root", version = "0.14.0"} -trie-standardmap = { path = "../test-support/trie-standardmap", version = "0.14.0" } -keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.14.0" } +memory-db = { path = "../memory-db", version = "0.15.0" } +trie-root = { path = "../trie-root", version = "0.15.0"} +trie-standardmap = { path = "../test-support/trie-standardmap", version = "0.15.0" } +keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.15.0" } # DISABLE the following line when publishing until cyclic dependencies are resolved https://github.com/rust-lang/cargo/issues/4242 -reference-trie = { path = "../test-support/reference-trie", version = "0.14.0" } +reference-trie = { path = "../test-support/reference-trie", version = "0.15.0" } hex-literal = "0.1" criterion = "0.2.8" +parity-codec = "3.0" +parity-codec-derive = "3.0" [features] default = ["std"] diff --git a/trie-db/benches/bench.rs b/trie-db/benches/bench.rs index fc419b77..0033aa4c 100644 --- a/trie-db/benches/bench.rs +++ b/trie-db/benches/bench.rs @@ -14,13 +14,30 @@ #[macro_use] extern crate criterion; -use criterion::{Criterion, black_box}; -criterion_group!(benches, nibble_common_prefix); +use criterion::{Criterion, black_box, Bencher}; +criterion_group!(benches, + root_old, + root_new, + root_a_big_v, + root_b_big_v, + root_a_small_v, + root_b_small_v, + trie_mut_ref_root_a, + trie_mut_ref_root_b, + trie_mut_root_a, + trie_mut_root_b, + trie_mut_a, + trie_mut_b, + trie_mut_build_a, + trie_mut_build_b, + nibble_common_prefix, +); criterion_main!(benches); extern crate trie_standardmap; extern crate trie_db; - +extern crate memory_db; +extern crate rand; use trie_standardmap::{Alphabet, StandardMap, ValueMode}; use trie_db::NibbleSlice; @@ -32,13 +49,399 @@ fn nibble_common_prefix(b: &mut Criterion) { value_mode: ValueMode::Mirror, count: 255, }; - let (keys, values): (Vec<_>, Vec<_>) = st.make().iter().cloned().unzip(); - let mixed: Vec<_> = keys.iter().zip(values.iter().rev()).map(|pair| { - (NibbleSlice::new(pair.0), NibbleSlice::new(pair.1)) - }).collect(); - b.bench_function("nibble_common_prefix", |b| b.iter(&mut ||{ - for (left, right) in mixed.iter() { - let _ = black_box(left.common_prefix(&right)); - } - })); + let (keys, values): (Vec<_>, Vec<_>) = st.make().into_iter().unzip(); + b.bench_function("nibble_common_prefix", move |b| { + let mixed: Vec<_> = keys.iter().zip(values.iter().rev()).map(|pair| { + (NibbleSlice::new(pair.0), NibbleSlice::new(pair.1)) + }).collect(); + + b.iter(&mut || { + for (left, right) in mixed.iter() { + let _ = black_box(left.common_prefix(&right)); + } + }) + }); +} + +fn root_a_big_v(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input2(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("root_a_big_v", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }), + data, + ); +} + +fn root_b_big_v(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input2(29, 204800, 512), + ]; + + c.bench_function_over_inputs("root_b_big_v", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }), + data, + ); +} + + +fn root_a_small_v(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input2(29, 204800, 32), + ]; + + c.bench_function_over_inputs("root_a_small_v", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }), + data, + ); +} + +fn root_b_small_v(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input2(29, 204800 / 2, 32 * 2), + ]; + + c.bench_function_over_inputs("root_b_small_v", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }), + data, + ); +} + +fn root_old(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input(1, 5120), + input(41, 10240), + input(18, 102400), + input(29, 204800), + ]; + + c.bench_function_over_inputs("root_old", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)); + + reference_trie::reference_trie_root(inputc); + }), + data, + ); +} + + +fn root_new(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input(1, 5120), + input(41, 10240), + input(18, 102400), + input(29, 204800), + ]; + + c.bench_function_over_inputs("root_new", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }), + data, + ); +} + +fn fuzz_to_data(input: Vec) -> Vec<(Vec, Vec)> { + let mut result = Vec::new(); + // enc = (minkeylen, maxkeylen (min max up to 32), datas) + // fix data len 2 bytes + let minkeylen = 1; + let maxkeylen = 32; + let mut ix = 0; + loop { + let keylen = if let Some(v) = input.get(ix) { + let mut v = *v & 31u8; + v = v + 1; + v = std::cmp::max(minkeylen, v); + v = std::cmp::min(maxkeylen, v); + v as usize + } else { break }; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = if input.len() > ix + 2 { + input[ix..ix + 2].to_vec() + } else { break }; + ix += 2; + result.push((key, val)); + } + result +} + +fn fuzz_to_data2(input: Vec, vl: usize) -> Vec<(Vec, Vec)> { + let mut result = Vec::new(); + let mut ix = 0; + loop { + let keylen = 32; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = vec![input[ix];vl]; + result.push((key, val)); + } + result +} + + +fn data_sorted_unique(input: Vec<(Vec, Vec)>) -> Vec<(Vec, Vec)> { + let mut m = std::collections::BTreeMap::new(); + for (k, v) in input.into_iter() { + let _ = m.insert(k, v); // latest value for uniqueness + } + m.into_iter().collect() +} + +fn input(seed: u64, len: usize) -> Vec<(Vec, Vec)> { + use rand::SeedableRng; + use rand::RngCore; + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); + let mut data = vec![0u8; len]; + rng.fill_bytes(&mut data[..]); + let data = data_sorted_unique(fuzz_to_data(data)); + data +} + +fn input2(seed: u64, len: usize, value_length: usize) -> Vec<(Vec, Vec)> { + use rand::SeedableRng; + use rand::RngCore; + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); + let mut data = vec![0u8; len]; + rng.fill_bytes(&mut data[..]); + let data = data_sorted_unique(fuzz_to_data2(data, value_length)); + data +} + +fn input_unsorted(seed: u64, len: usize, value_length: usize) -> Vec<(Vec, Vec)> { + use rand::SeedableRng; + use rand::RngCore; + let mut rng = rand::rngs::SmallRng::seed_from_u64(seed); + let mut data = vec![0u8; len]; + rng.fill_bytes(&mut data[..]); + fuzz_to_data2(data, value_length) +} + +fn trie_mut_root_a(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input_unsorted(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("trie_mut_root_a", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data_sorted_unique(data.clone()); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v|(&v.0, &v.1)) + .collect::>(); + + + reference_trie::calc_root(inputc); + }), + data); +} + +fn trie_mut_root_b(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input_unsorted(29, 204800, 32), + ]; + + c.bench_function_over_inputs("trie_mut_root_b", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data_sorted_unique(data.clone()); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + reference_trie::calc_root(inputc); + }), + data); +} + +fn trie_mut_ref_root_a(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + input_unsorted(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("trie_mut_ref_root_a", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); // no need to sort for trie_root, see implementation + + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + reference_trie::reference_trie_root(inputc); + }), + data); +} + +fn trie_mut_ref_root_b(c: &mut Criterion) { + let data : Vec, Vec)>> = vec![ + //input_unsorted(29, 204800, 512), + input_unsorted(29, 204800, 32), + ]; + + c.bench_function_over_inputs("trie_mut_ref_root_b", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); // no need to sort for trie_root, see implementation + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + reference_trie::reference_trie_root(inputc); + }), + data); +} + + + +fn trie_mut_a(c: &mut Criterion) { + use trie_db::TrieMut; + use memory_db::HashKey; + let data : Vec, Vec)>> = vec![ + input_unsorted(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("trie_mut_a", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); + + let mut root = Default::default(); + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + let mut trie = reference_trie::RefTrieDBMut::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"); + } + + }), + data); +} + +fn trie_mut_b(c: &mut Criterion) { + use trie_db::TrieMut; + use memory_db::HashKey; + let data : Vec, Vec)>> = vec![ + //input_unsorted(29, 204800, 512), + input_unsorted(29, 204800, 32), + ]; + + c.bench_function_over_inputs("trie_mut_b", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data.clone(); + + let mut root = Default::default(); + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + let mut trie = reference_trie::RefTrieDBMut::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"); + } + + }), + data); +} + +fn trie_mut_build_a(c: &mut Criterion) { + use memory_db::HashKey; + let data : Vec, Vec)>> = vec![ + input_unsorted(29, 204800 / 2, 512 * 2), + ]; + + c.bench_function_over_inputs("trie_mut_build_a", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data_sorted_unique(data.clone()); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + reference_trie::calc_root_build(inputc, &mut mdb); + }), + data); +} + +fn trie_mut_build_b(c: &mut Criterion) { + use memory_db::HashKey; + let data : Vec, Vec)>> = vec![ + //input_unsorted(29, 204800, 512), + input_unsorted(29, 204800, 32), + ]; + + c.bench_function_over_inputs("trie_mut_build_b", |b: &mut Bencher, data: &Vec<(Vec, Vec)>| + b.iter(|| { + let datac:Vec<(Vec, Vec)> = data_sorted_unique(data.clone()); + // this is in `reference_trie_root` added here to make things comparable + let inputc = datac + .iter() + .map(|v| (&v.0, &v.1)) + .collect::>(); + + let mut mdb = memory_db::MemoryDB::<_, HashKey<_>, _>::default(); + reference_trie::calc_root_build(inputc, &mut mdb); + }), + data); } diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml new file mode 100644 index 00000000..68a8e71d --- /dev/null +++ b/trie-db/fuzz/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "trie-db-fuzz" +version = "0.0.1" +authors = [] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +memory-db = { path = "../../memory-db", version = "0.15.0" } +reference-trie = { path = "../../test-support/reference-trie", version = "0.15.0" } +keccak-hasher = { path = "../../test-support/keccak-hasher", version = "0.15.0" } +honggfuzz = "0.5" + +[dependencies.trie-db] +path = ".." + +[dependencies.libfuzzer-sys] +git = "https://github.com/rust-fuzz/libfuzzer-sys.git" + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +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" + +[[bin]] +name = "trie_root_fix_len" +path = "fuzz_targets/trie_root_fix_len.rs" + +[[bin]] +name = "no_ext_insert" +path = "fuzz_targets/no_ext_insert.rs" + +[[bin]] +name = "no_ext_insert_rem" +path = "fuzz_targets/no_ext_insert_rem.rs" + +[[bin]] +name = "hfuzz_trie_root_new" +path = "hongfuzz_targets/trie_root_new.rs" diff --git a/trie-db/fuzz/fuzz_targets/no_ext_insert.rs b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs new file mode 100644 index 00000000..4922e057 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert.rs @@ -0,0 +1,9 @@ +#![no_main] + +use trie_db_fuzz::fuzz_that_no_extension_insert; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + 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 new file mode 100644 index 00000000..608e25fc --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/no_ext_insert_rem.rs @@ -0,0 +1,9 @@ +#![no_main] + +use trie_db_fuzz::fuzz_that_no_extension_insert_remove; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_no_extension_insert_remove(data); +}); diff --git a/trie-db/fuzz/fuzz_targets/trie_root.rs b/trie-db/fuzz/fuzz_targets/trie_root.rs new file mode 100644 index 00000000..32b65f83 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_root.rs @@ -0,0 +1,9 @@ + +#![no_main] + +use trie_db_fuzz::fuzz_that_reference_trie_root; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + 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 new file mode 100644 index 00000000..00763e26 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_root_fix_len.rs @@ -0,0 +1,9 @@ + +#![no_main] + +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); +}); diff --git a/trie-db/fuzz/fuzz_targets/trie_root_new.rs b/trie-db/fuzz/fuzz_targets/trie_root_new.rs new file mode 100644 index 00000000..9e9d41fa --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_root_new.rs @@ -0,0 +1,10 @@ + +#![no_main] + +use trie_db_fuzz::fuzz_that_compare_implementations; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // fuzzed code goes here + 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 new file mode 100644 index 00000000..dba4cc68 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/trie_unhashed_no_ext.rs @@ -0,0 +1,10 @@ + +#![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/hongfuzz_targets/trie_root_new.rs b/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs new file mode 100644 index 00000000..9e954954 --- /dev/null +++ b/trie-db/fuzz/hongfuzz_targets/trie_root_new.rs @@ -0,0 +1,12 @@ + +use honggfuzz::fuzz; +use trie_db_fuzz::fuzz_that_compare_impl; + +fn main() { + loop { + fuzz!(|data: &[u8]| { + // fuzzed code goes here + fuzz_that_compare_impl(data); + }); + } +} diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs new file mode 100644 index 00000000..7b89e12f --- /dev/null +++ b/trie-db/fuzz/src/lib.rs @@ -0,0 +1,175 @@ +// Copyright 2019 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 memory_db::{MemoryDB, HashKey, PrefixedKey}; +use reference_trie::{ + RefTrieDBMutNoExt, + RefTrieDBMut, + reference_trie_root, + calc_root_no_extension, + compare_no_extension_insert_remove, +}; +use trie_db::{TrieMut, DBValue}; +use keccak_hasher::KeccakHasher; + + + +fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { + let mut result = Vec::new(); + // enc = (minkeylen, maxkeylen (min max up to 32), datas) + // fix data len 2 bytes + let mut minkeylen = if let Some(v) = input.get(0) { + let mut v = *v & 31u8; + v = v + 1; + v + } else { return result; }; + let mut maxkeylen = if let Some(v) = input.get(1) { + let mut v = *v & 31u8; + v = v + 1; + v + } else { return result; }; + + if maxkeylen < minkeylen { + let v = minkeylen; + minkeylen = maxkeylen; + maxkeylen = v; + } + let mut ix = 2; + loop { + let keylen = if let Some(v) = input.get(ix) { + let mut v = *v & 31u8; + v = v + 1; + v = std::cmp::max(minkeylen, v); + v = std::cmp::min(maxkeylen, v); + v as usize + } else { break }; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = if input.len() > ix + 2 { + input[ix..ix+2].to_vec() + } else { break }; + result.push((key,val)); + } + result +} + +fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec,Vec)> { + let mut res = Vec::new(); + let mut torem = None; + for (a, d) in data.into_iter().enumerate() { + if a % 7 == 6 { + // a random removal some time + res.push((true, d.0, d.1)); + } else { + if a % 5 == 0 { + torem = Some((true, d.0.clone(), d.1.clone())); + } + res.push((false, d.0, d.1)); + if a % 5 == 4 { + if let Some(v) = torem.take() { + res.push(v); + } + } + } + } + res +} + +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); + for a in 0..data.len() { + t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); + } + assert_eq!(*t.root(), reference_trie_root(data)); +} + +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); + for a in 0..data.len() { + t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); + } + assert_eq!(*t.root(), reference_trie_root(data)); +} + +fn fuzz_to_data_fix_length(input: &[u8]) -> Vec<(Vec,Vec)> { + let mut result = Vec::new(); + let mut ix = 0; + loop { + let keylen = 32; + let key = if input.len() > ix + keylen { + input[ix..ix+keylen].to_vec() + } else { break }; + ix += keylen; + let val = if input.len() > ix + 2 { + input[ix..ix+2].to_vec() + } else { break }; + result.push((key,val)); + } + result +} + +fn data_sorted_unique(input: Vec<(Vec,Vec)>) -> Vec<(Vec,Vec)> { + let mut m = std::collections::BTreeMap::new(); + for (k,v) in input.into_iter() { + let _ = m.insert(k,v); // latest value for uniqueness + } + m.into_iter().collect() +} + +pub fn fuzz_that_compare_implementations(input: &[u8]) { + let data = data_sorted_unique(fuzz_to_data(input)); + //println!("data:{:?}", &data); + let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + let hashdb = MemoryDB::, DBValue>::default(); + reference_trie::compare_implementations(data, memdb, hashdb); +} + +pub fn fuzz_that_unhashed_no_extension(input: &[u8]) { + let data = data_sorted_unique(fuzz_to_data(input)); + reference_trie::compare_unhashed_no_extension(data); +} + +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); + for a in 0..data.len() { + t.insert(&data[a].0[..], &data[a].1[..]).unwrap(); + } + // we are testing the RefTrie code here so we do not sort or check uniqueness + // before. + let data = data_sorted_unique(fuzz_to_data(input)); + //println!("data{:?}", data); + assert_eq!(*t.root(), calc_root_no_extension(data)); +} + +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); +} diff --git a/trie-db/src/fatdb.rs b/trie-db/src/fatdb.rs index 822e2af1..1ffd0d95 100644 --- a/trie-db/src/fatdb.rs +++ b/trie-db/src/fatdb.rs @@ -13,8 +13,8 @@ // limitations under the License. use hash_db::{HashDBRef, Hasher}; -use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query}; -use node_codec::NodeCodec; +use super::{Result, DBValue, TrieDB, Trie, TrieDBIterator, TrieItem, TrieIterator, Query, + TrieLayout, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -23,76 +23,72 @@ use alloc::boxed::Box; /// Additionaly it stores inserted hash-key mappings for later retrieval. /// /// Use it as a `Trie` or `TrieMut` trait object. -pub struct FatDB<'db, H, C> +pub struct FatDB<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayout, { - raw: TrieDB<'db, H, C>, + raw: TrieDB<'db, L>, } -impl<'db, H, C> FatDB<'db, H, C> +impl<'db, L> FatDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. pub fn new( - db: &'db dyn HashDBRef, - root: &'db H::Out, - ) -> Result { + db: &'db dyn HashDBRef, + root: &'db TrieHash, + ) -> Result, CError> { Ok(FatDB { raw: TrieDB::new(db, root)? }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDBRef { self.raw.db() } + pub fn db(&self) -> &dyn HashDBRef { self.raw.db() } } -impl<'db, H, C> Trie for FatDB<'db, H, C> +impl<'db, L> Trie for FatDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { - fn root(&self) -> &H::Out { self.raw.root() } + fn root(&self) -> &TrieHash { self.raw.root() } - fn contains(&self, key: &[u8]) -> Result { - self.raw.contains(H::hash(key).as_ref()) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.raw.contains(L::Hash::hash(key).as_ref()) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, H::Out, C::Error> + fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) + -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get_with(H::hash(key).as_ref(), query) + self.raw.get_with(L::Hash::hash(key).as_ref(), query) } fn iter<'a>(&'a self) -> Result< - Box> + 'a>, - ::Out, - C::Error, + Box, CError>> + 'a>, + TrieHash, + CError, > { - FatDBIterator::::new(&self.raw).map(|iter| Box::new(iter) as Box<_>) + FatDBIterator::::new(&self.raw).map(|iter| Box::new(iter) as Box<_>) } } /// Itarator over inserted pairs of key values. -pub struct FatDBIterator<'db, H, C> +pub struct FatDBIterator<'db, L> where - H: Hasher + 'db, - C: NodeCodec + 'db + L: TrieLayout, { - trie_iterator: TrieDBIterator<'db, H, C>, - trie: &'db TrieDB<'db, H, C>, + trie_iterator: TrieDBIterator<'db, L>, + trie: &'db TrieDB<'db, L>, } -impl<'db, H, C> FatDBIterator<'db, H, C> +impl<'db, L> FatDBIterator<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { /// Creates new iterator. - pub fn new(trie: &'db TrieDB) -> Result { + pub fn new(trie: &'db TrieDB) -> Result, CError> { Ok(FatDBIterator { trie_iterator: TrieDBIterator::new(trie)?, trie: trie, @@ -100,30 +96,33 @@ where } } -impl<'db, H, C> TrieIterator for FatDBIterator<'db, H, C> +impl<'db, L> TrieIterator for FatDBIterator<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { - fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, C::Error> { - let hashed_key = H::hash(key); + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { + let hashed_key = L::Hash::hash(key); self.trie_iterator.seek(hashed_key.as_ref()) } } -impl<'db, H, C> Iterator for FatDBIterator<'db, H, C> +impl<'db, L> Iterator for FatDBIterator<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { - type Item = TrieItem<'db, H::Out, C::Error>; + type Item = TrieItem<'db, TrieHash, CError>; fn next(&mut self) -> Option { self.trie_iterator.next() .map(|res| { res.map(|(hash, value)| { - let aux_hash = H::hash(&hash); - (self.trie.db().get(&aux_hash, &[]).expect("Missing fatdb hash").into_vec(), value) + let aux_hash = L::Hash::hash(&hash); + ( + self.trie.db().get(&aux_hash, Default::default()) + .expect("Missing fatdb hash") + .into_vec(), + value, + ) }) }) } diff --git a/trie-db/src/fatdbmut.rs b/trie-db/src/fatdbmut.rs index f2720b8a..943c4f1c 100644 --- a/trie-db/src/fatdbmut.rs +++ b/trie-db/src/fatdbmut.rs @@ -12,31 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hash_db::{HashDB, Hasher}; -use super::{Result, DBValue, TrieDBMut, TrieMut}; -use node_codec::NodeCodec; +use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; +use super::{Result, DBValue, TrieDBMut, TrieMut, TrieLayout, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// Additionaly it stores inserted hash-key mappings for later retrieval. /// /// Use it as a `Trie` or `TrieMut` trait object. -pub struct FatDBMut<'db, H, C> +pub struct FatDBMut<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayout, { - raw: TrieDBMut<'db, H, C>, + raw: TrieDBMut<'db, L>, } -impl<'db, H, C> FatDBMut<'db, H, C> +impl<'db, L> FatDBMut<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut dyn HashDB, root: &'db mut H::Out) -> Self { + pub fn new(db: &'db mut dyn HashDB, root: &'db mut TrieHash) -> Self { FatDBMut { raw: TrieDBMut::new(db, root) } } @@ -44,63 +41,66 @@ where /// /// Returns an error if root does not exist. pub fn from_existing( - db: &'db mut dyn HashDB, - root: &'db mut H::Out, - ) -> Result { + db: &'db mut dyn HashDB, + root: &'db mut TrieHash + ) -> Result, CError> { Ok(FatDBMut { raw: TrieDBMut::from_existing(db, root)? }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDB { + pub fn db(&self) -> &dyn HashDB { self.raw.db() } /// Get the backing database. - pub fn db_mut(&mut self) -> &mut dyn HashDB { + pub fn db_mut(&mut self) -> &mut dyn HashDB { self.raw.db_mut() } } -impl<'db, H, C> TrieMut for FatDBMut<'db, H, C> +impl<'db, L> TrieMut for FatDBMut<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { - fn root(&mut self) -> &H::Out { self.raw.root() } + fn root(&mut self) -> &TrieHash { self.raw.root() } fn is_empty(&self) -> bool { self.raw.is_empty() } - fn contains(&self, key: &[u8]) -> Result { - self.raw.contains(H::hash(key).as_ref()) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.raw.contains(L::Hash::hash(key).as_ref()) } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, H::Out, C::Error> + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get(H::hash(key).as_ref()) + self.raw.get(L::Hash::hash(key).as_ref()) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error> { - let hash = H::hash(key); + fn insert( + &mut self, + key: &[u8], + value: &[u8], + ) -> Result, TrieHash, CError> { + let hash = L::Hash::hash(key); let out = self.raw.insert(hash.as_ref(), value)?; let db = self.raw.db_mut(); // insert if it doesn't exist. if out.is_none() { - let aux_hash = H::hash(hash.as_ref()); - db.emplace(aux_hash, &[], DBValue::from_slice(key)); + let aux_hash = L::Hash::hash(hash.as_ref()); + db.emplace(aux_hash, EMPTY_PREFIX, DBValue::from_slice(key)); } Ok(out) } - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error> { - let hash = H::hash(key); + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + let hash = L::Hash::hash(key); let out = self.raw.remove(hash.as_ref())?; // remove if it already exists. if out.is_some() { - let aux_hash = H::hash(hash.as_ref()); - self.raw.db_mut().remove(&aux_hash, &[]); + let aux_hash = L::Hash::hash(hash.as_ref()); + self.raw.db_mut().remove(&aux_hash, EMPTY_PREFIX); } Ok(out) @@ -111,7 +111,7 @@ where mod test { use DBValue; use memory_db::{MemoryDB, HashKey}; - use hash_db::Hasher; + use hash_db::{Hasher, EMPTY_PREFIX}; use keccak_hasher::KeccakHasher; use reference_trie::{RefFatDBMut, RefTrieDB, Trie, TrieMut}; @@ -124,7 +124,10 @@ mod test { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); - assert_eq!(t.get(&KeccakHasher::hash(&[0x01u8, 0x23])), Ok(Some(DBValue::from_slice(&[0x01u8, 0x23])))); + assert_eq!( + t.get(&KeccakHasher::hash(&[0x01u8, 0x23])), + Ok(Some(DBValue::from_slice(&[0x01u8, 0x23]))), + ); } #[test] @@ -138,8 +141,8 @@ mod test { let mut t = RefFatDBMut::new(&mut memdb, &mut root); t.insert(&key, &val).unwrap(); assert_eq!(t.get(&key), Ok(Some(DBValue::from_slice(&val)))); - assert_eq!(t.db().get(&aux_hash, &[]), Some(DBValue::from_slice(&key))); + assert_eq!(t.db().get(&aux_hash, EMPTY_PREFIX), Some(DBValue::from_slice(&key))); t.remove(&key).unwrap(); - assert_eq!(t.db().get(&aux_hash, &[]), None); + assert_eq!(t.db().get(&aux_hash, EMPTY_PREFIX), None); } } diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs new file mode 100644 index 00000000..cf6f65ee --- /dev/null +++ b/trie-db/src/iter_build.rs @@ -0,0 +1,788 @@ +// Copyright 2017, 2019 Parity Technologies +// +// Licensed under the Apache License, Version .0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Alternative tools for working with key value ordered iterator without recursion. +//! This is iterative implementation of `trie_root` algorithm, using `NodeCodec` +//! implementation. +//! See `trie_visit` function. + +use hash_db::{Hasher, HashDB, Prefix}; +use core_::marker::PhantomData; +use core_::cmp::max; +use crate::triedbmut::{ChildReference}; +use crate::nibble::NibbleSlice; +use crate::nibble::nibble_ops; +use node_codec::NodeCodec; +use crate::{TrieLayout, TrieHash}; + +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; + +macro_rules! exponential_out { + (@3, [$($inpp:expr),*]) => { exponential_out!(@2, [$($inpp,)* $($inpp),*]) }; + (@2, [$($inpp:expr),*]) => { exponential_out!(@1, [$($inpp,)* $($inpp),*]) }; + (@1, [$($inpp:expr),*]) => { [$($inpp,)* $($inpp),*] }; +} + +type CacheNode = Option>; + +#[inline(always)] +fn new_vec_slice_buffer() -> [CacheNode; 16] { + exponential_out!(@3, [None, None]) +} + +type ArrayNode = [CacheNode>; 16]; + +/// Struct containing iteration cache, can be at most the length of the lowest nibble. +/// +/// Note that it is not memory optimal (all depth are allocated even if some are empty due +/// to node partial). +/// Three field are used, a cache over the children, an optional associated value and the depth. +struct CacheAccum (Vec<(ArrayNode, Option, usize)>, PhantomData); + +/// Initially allocated cache depth. +const INITIAL_DEPTH: usize = 10; + +impl CacheAccum + where + T: TrieLayout, + V: AsRef<[u8]>, +{ + + fn new() -> Self { + let v = Vec::with_capacity(INITIAL_DEPTH); + CacheAccum(v, PhantomData) + } + + #[inline(always)] + fn set_cache_value(&mut self, depth:usize, value: Option) { + if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { + self.0.push((new_vec_slice_buffer(), None, depth)); + } + let last = self.0.len() - 1; + debug_assert!(self.0[last].2 <= depth); + self.0[last].1 = value; + } + + #[inline(always)] + fn set_node(&mut self, depth: usize, nibble_index: usize, node: CacheNode>) { + if self.0.is_empty() || self.0[self.0.len() - 1].2 < depth { + self.0.push((new_vec_slice_buffer(), None, depth)); + } + + let last = self.0.len() - 1; + debug_assert!(self.0[last].2 == depth); + + self.0[last].0.as_mut()[nibble_index] = node; + } + + #[inline(always)] + fn last_depth(&self) -> usize { + let ix = self.0.len(); + if ix > 0 { + let last = ix - 1; + self.0[last].2 + } else { + 0 + } + } + + #[inline(always)] + fn last_last_depth(&self) -> usize { + let ix = self.0.len(); + if ix > 1 { + let last = ix - 2; + self.0[last].2 + } else { + 0 + } + } + + #[inline(always)] + fn is_empty(&self) -> bool { + self.0.is_empty() + } + #[inline(always)] + fn is_one(&self) -> bool { + self.0.len() == 1 + } + + #[inline(always)] + fn reset_depth(&mut self, depth: usize) { + debug_assert!(self.0[self.0.len() - 1].2 == depth); + self.0.pop(); + } + + fn flush_value ( + &mut self, + callback: &mut impl ProcessEncodedNode>, + target_depth: usize, + (k2, v2): &(impl AsRef<[u8]>, impl AsRef<[u8]>), + ) { + let nibble_value = nibble_ops::left_nibble_at(&k2.as_ref()[..], target_depth); + // is it a branch value (two candidate same ix) + let nkey = NibbleSlice::new_offset(&k2.as_ref()[..], target_depth + 1); + let encoded = T::Codec::leaf_node(nkey.right(), &v2.as_ref()[..]); + let pr = NibbleSlice::new_offset( + &k2.as_ref()[..], + k2.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - nkey.len(), + ); + let hash = callback.process(pr.left(), encoded, false); + + // insert hash in branch (first level branch only at this point) + self.set_node(target_depth, nibble_value as usize, Some(hash)); + } + + fn flush_branch( + &mut self, + no_extension: bool, + callback: &mut impl ProcessEncodedNode>, + ref_branch: impl AsRef<[u8]> + Ord, + new_depth: usize, + is_last: bool, + ) { + + while self.last_depth() > new_depth || is_last && !self.is_empty() { + + let lix = self.last_depth(); + let llix = max(self.last_last_depth(), new_depth); + + let (offset, slice_size, is_root) = + if llix == 0 && is_last && self.is_one() { + // branch root + (llix, lix - llix, true) + } else { + (llix + 1, lix - llix - 1, false) + }; + let nkey = if slice_size > 0 { + Some((offset, slice_size)) + } else { + None + }; + + let h = if no_extension { + // encode branch + self.no_extension(&ref_branch.as_ref()[..], callback, lix, is_root, nkey) + } else { + self.standard_extension(&ref_branch.as_ref()[..], callback, lix, is_root, nkey) + }; + if !is_root { + // put hash in parent + let nibble: u8 = nibble_ops::left_nibble_at(&ref_branch.as_ref()[..], llix); + self.set_node(llix, nibble as usize, Some(h)); + } + } + } + + #[inline(always)] + fn standard_extension( + &mut self, + key_branch: &[u8], + callback: &mut impl ProcessEncodedNode>, + branch_d: usize, + is_root: bool, + nkey: Option<(usize, usize)>, + ) -> ChildReference> { + let last = self.0.len() - 1; + assert_eq!(self.0[last].2, branch_d); + + // 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 pr = NibbleSlice::new_offset(&key_branch.as_ref()[..], branch_d); + let branch_hash = callback.process(pr.left(), encoded, is_root && nkey.is_none()); + + if let Some(nkeyix) = nkey { + let pr = NibbleSlice::new_offset(&key_branch.as_ref()[..], nkeyix.0); + let nib = pr.right_range_iter(nkeyix.1); + let encoded = T::Codec::extension_node(nib, nkeyix.1, branch_hash); + let h = callback.process(pr.left(), encoded, is_root); + h + } else { + branch_hash + } + } + + #[inline(always)] + fn no_extension( + &mut self, + key_branch: &[u8], + callback: &mut impl ProcessEncodedNode>, + branch_d: usize, + is_root: bool, + nkey: Option<(usize, usize)>, + ) -> ChildReference> { + let last = self.0.len() - 1; + debug_assert!(self.0[last].2 == branch_d); + // encode branch + let v = self.0[last].1.take(); + let nkeyix = nkey.unwrap_or((0, 0)); + let pr = NibbleSlice::new_offset(&key_branch.as_ref()[..], 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())); + 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.as_ref()[..], + branch_d - ext_length, + ); + callback.process(pr.left(), encoded, is_root) + } + +} + +/// Function visiting trie from key value inputs with a `ProccessEncodedNode` callback. +/// This is the main entry point of this module. +/// Calls to each node occurs ordered by byte key value but with longest keys first (from node to +/// branch to root), this differs from standard byte array ordering a bit. +pub fn trie_visit(input: I, callback: &mut F) + where + T: TrieLayout, + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + F: ProcessEncodedNode>, +{ + let no_extension = !T::USE_EXTENSION; + let mut depth_queue = CacheAccum::::new(); + // compare iter ordering + let mut iter_input = input.into_iter(); + if let Some(mut previous_value) = iter_input.next() { + // depth of last item + let mut last_depth = 0; + + let mut single = true; + for (k, v) in iter_input { + single = false; + let common_depth = nibble_ops::biggest_depth(&previous_value.0.as_ref()[..], &k.as_ref()[..]); + // 0 is a reserved value : could use option + let depth_item = common_depth; + if common_depth == previous_value.0.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE { + // the new key include the previous one : branch value case + // just stored value at branch depth + depth_queue.set_cache_value(common_depth, Some(previous_value.1)); + } else if depth_item >= last_depth { + // put previous with next (common branch previous value can be flush) + depth_queue.flush_value(callback, depth_item, &previous_value); + } else if depth_item < last_depth { + // do not put with next, previous is last of a branch + depth_queue.flush_value(callback, last_depth, &previous_value); + let ref_branches = previous_value.0; + depth_queue.flush_branch(no_extension, callback, ref_branches, depth_item, false); + } + + previous_value = (k, v); + last_depth = depth_item; + } + // last pendings + if single { + // one single element corner case + let (k2, v2) = previous_value; + let nkey = NibbleSlice::new_offset(&k2.as_ref()[..], last_depth); + let encoded = T::Codec::leaf_node(nkey.right(), &v2.as_ref()[..]); + let pr = NibbleSlice::new_offset( + &k2.as_ref()[..], + k2.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE - nkey.len(), + ); + callback.process(pr.left(), encoded, true); + } else { + depth_queue.flush_value(callback, last_depth, &previous_value); + let ref_branches = previous_value.0; + depth_queue.flush_branch(no_extension, callback, ref_branches, 0, true); + } + } else { + // nothing null root corner case + callback.process(hash_db::EMPTY_PREFIX, T::Codec::empty_node().to_vec(), true); + } +} + +/// Visitor trait to implement when using `trie_visit`. +pub trait ProcessEncodedNode { + /// Function call with prefix, encoded value and a boolean indicating if the + /// node is the root for each node of the trie. + /// + /// Note that the returned value can change depending on implementation, + /// 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; +} + +/// Get trie root and insert visited node in a hash_db. +/// As for all `ProcessEncodedNode` implementation, it +/// is only for full trie parsing (not existing trie). +pub struct TrieBuilder<'a, H, HO, V, DB> { + db: &'a mut DB, + pub root: Option, + _ph: PhantomData<(H, V)>, +} + +impl<'a, H, HO, V, DB> TrieBuilder<'a, H, HO, V, DB> { + pub fn new(db: &'a mut DB) -> Self { + TrieBuilder { db, root: None, _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, + is_root: bool, + ) -> 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 = 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. + pub root: Option, + _ph: PhantomData<(H)>, +} + +impl Default for TrieRoot { + fn default() -> Self { + TrieRoot { root: None, _ph: PhantomData } + } +} + +impl ProcessEncodedNode<::Out> for TrieRoot::Out> { + fn process( + &mut self, + _: Prefix, + encoded_node: Vec, + is_root: bool, + ) -> 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 = ::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. + pub root: Option>, + _ph: PhantomData<(H)>, +} + +impl Default for TrieRootUnhashed { + fn default() -> Self { + TrieRootUnhashed { root: None, _ph: PhantomData } + } +} + +#[cfg(feature = "std")] +/// Calculate the trie root of the trie. +/// Print a debug trace. +pub struct TrieRootPrint { + /// The resulting root. + pub root: Option, + _ph: PhantomData<(H)>, +} + +#[cfg(feature = "std")] +impl Default for TrieRootPrint { + fn default() -> Self { + TrieRootPrint { root: None, _ph: PhantomData } + } +} + +#[cfg(feature = "std")] +impl ProcessEncodedNode<::Out> for TrieRootPrint::Out> { + fn process( + &mut self, + p: Prefix, + encoded_node: Vec, + is_root: bool, + ) -> ChildReference<::Out> { + println!("Encoded node: {:x?}", &encoded_node); + println!(" with prefix: {:x?}", &p); + let len = encoded_node.len(); + if !is_root && len < ::LENGTH { + let mut h = <::Out as Default>::default(); + h.as_mut()[..len].copy_from_slice(&encoded_node[..len]); + + println!(" inline len {}", len); + return ChildReference::Inline(h, len); + } + let hash = ::hash(&encoded_node[..]); + if is_root { + self.root = Some(hash.clone()); + }; + println!(" hashed to {:x?}", hash.as_ref()); + ChildReference::Hash(hash) + } +} + +impl ProcessEncodedNode<::Out> for TrieRootUnhashed { + fn process( + &mut self, + _: Prefix, + encoded_node: Vec, + is_root: bool, + ) -> 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 = ::hash(&encoded_node[..]); + if is_root { + self.root = Some(encoded_node); + }; + ChildReference::Hash(hash) + } +} + +#[cfg(test)] +mod test { + use DBValue; + use memory_db::{MemoryDB, HashKey, PrefixedKey}; + use keccak_hasher::KeccakHasher; + + #[test] + fn trie_root_empty () { + compare_implementations(vec![]) + } + + #[test] + fn trie_one_node () { + compare_implementations(vec![ + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8]), + ]); + } + + #[test] + fn root_extension_one () { + compare_implementations(vec![ + (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), + ]); + } + + fn test_iter(data: Vec<(Vec, Vec)>) { + use reference_trie::{RefTrieDBMut, TrieMut, RefTrieDB, Trie}; + + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMut::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, TrieMut, RefTrieDBNoExt, 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(); + 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 compare_implementations(data: Vec<(Vec, Vec)>) { + test_iter(data.clone()); + test_iter_no_extension(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)>) { + let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + 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); + } + fn compare_implementations_no_extension(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); + } + 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); + } + fn compare_no_extension_insert_remove(data: Vec<(bool, Vec, Vec)>) { + let memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + reference_trie::compare_no_extension_insert_remove(data, memdb); + } + fn compare_root(data: Vec<(Vec, Vec)>) { + let memdb = MemoryDB::<_, HashKey<_>, _>::default(); + reference_trie::compare_root(data, memdb); + } + fn compare_unhashed(data: Vec<(Vec, Vec)>) { + reference_trie::compare_unhashed(data); + } + fn compare_unhashed_no_extension(data: Vec<(Vec, Vec)>) { + reference_trie::compare_unhashed_no_extension(data); + } + + // Following tests are a bunch of detected issue here for non regression. + + #[test] + fn trie_middle_node1 () { + compare_implementations(vec![ + (vec![1u8, 2u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), + ]); + } + #[test] + fn trie_middle_node2 () { + compare_implementations(vec![ + (vec![0u8, 2u8, 3u8, 5u8, 3u8], vec![1u8;32]), + (vec![1u8, 2u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), + (vec![1u8, 2u8, 3u8, 5u8], vec![7u8;32]), + (vec![1u8, 2u8, 3u8, 5u8, 3u8], vec![7u8;32]), + ]); + } + #[test] + fn root_extension_bis () { + compare_root(vec![ + (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), + ]); + } + #[test] + fn root_extension_tierce () { + let d = vec![ + (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;2]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;2]), + ]; + compare_unhashed(d.clone()); + compare_unhashed_no_extension(d); + } + #[test] + fn root_extension_tierce_big () { + // on more content unhashed would hash + compare_unhashed(vec![ + (vec![1u8, 2u8, 3u8, 3u8], vec![8u8;32]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;32]), + (vec![1u8, 6u8, 3u8, 3u8], vec![8u8;32]), + (vec![6u8, 2u8, 3u8, 3u8], vec![8u8;32]), + (vec![6u8, 2u8, 3u8, 13u8], vec![8u8;32]), + ]); + } + #[test] + fn trie_middle_node2x () { + compare_implementations(vec![ + (vec![0u8, 2u8, 3u8, 5u8, 3u8], vec![1u8;2]), + (vec![1u8, 2u8], vec![8u8;2]), + (vec![1u8, 2u8, 3u8, 4u8], vec![7u8;2]), + (vec![1u8, 2u8, 3u8, 5u8], vec![7u8;2]), + (vec![1u8, 2u8, 3u8, 5u8, 3u8], vec![7u8;2]), + ]); + } + #[test] + fn fuzz1 () { + compare_implementations(vec![ + (vec![01u8], vec![42u8, 9]), + (vec![01u8, 0u8], vec![0u8, 0]), + (vec![255u8, 2u8], vec![1u8, 0]), + ]); + } + #[test] + fn fuzz2 () { + compare_implementations(vec![ + (vec![0, 01u8], vec![42u8, 9]), + (vec![0, 01u8, 0u8], vec![0u8, 0]), + (vec![0, 255u8, 2u8], vec![1u8, 0]), + ]); + } + #[test] + fn fuzz3 () { + compare_implementations(vec![ + (vec![0], vec![196, 255]), + (vec![48], vec![138, 255]), + (vec![67], vec![0, 0]), + (vec![128], vec![255, 0]), + (vec![247], vec![0, 196]), + (vec![255], vec![0, 0]), + ]); + } + #[test] + fn fuzz_no_extension1 () { + compare_implementations(vec![ + (vec![0], vec![128, 0]), + (vec![128], vec![0, 0]), + ]); + } + #[test] + fn fuzz_no_extension2 () { + compare_implementations(vec![ + (vec![0], vec![6, 255]), + (vec![6], vec![255, 186]), + (vec![255], vec![186, 255]), + ]); + } + #[test] + fn fuzz_no_extension5 () { + compare_implementations(vec![ + (vec![0xaa], vec![0xa0]), + (vec![0xaa, 0xaa], vec![0xaa]), + (vec![0xaa, 0xbb], vec![0xab]), + (vec![0xbb], vec![0xb0]), + (vec![0xbb, 0xbb], vec![0xbb]), + (vec![0xbb, 0xcc], vec![0xbc]), + ]); + } + #[test] + fn fuzz_no_extension3 () { + compare_implementations(vec![ + (vec![0], vec![0, 0]), + (vec![11, 0], vec![0, 0]), + (vec![11, 252], vec![11, 0]), + ]); + + compare_implementations_no_extension_unordered(vec![ + (vec![11, 252], vec![11, 0]), + (vec![11, 0], vec![0, 0]), + (vec![0], vec![0, 0]), + ]); + } + #[test] + fn fuzz_no_extension4 () { + compare_implementations_no_extension(vec![ + (vec![0x01, 0x56], vec![0x1]), + (vec![0x02, 0x42], vec![0x2]), + (vec![0x02, 0x50], vec![0x3]), + ]); + } + #[test] + fn fuzz_no_extension_insert_remove_1 () { + 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); + } + #[test] + fn fuzz_no_extension_insert_remove_2 () { + 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); + } + #[test] + fn two_bytes_nibble_length () { + let data = vec![ + (vec![00u8], vec![0]), + (vec![01u8;64], vec![0;32]), + ]; + compare_implementations_no_extension(data.clone()); + compare_implementations_no_extension_prefixed(data.clone()); + } + #[test] + #[should_panic] + fn too_big_nibble_length_old () { + compare_implementations_h(vec![ + (vec![01u8;64], vec![0;32]), + ]); + } + #[test] + fn too_big_nibble_length_new () { + compare_implementations_no_extension(vec![ + (vec![01u8;((u16::max_value() as usize + 1) / 2) + 1], vec![0;32]), + ]); + } + #[test] + fn polka_re_test () { + compare_implementations(vec![ + (vec![77, 111, 111, 55, 111, 104, 121, 97], vec![68, 97, 105, 55, 105, 101, 116, 111]), + (vec![101, 105, 67, 104, 111, 111, 66, 56], vec![97, 56, 97, 113, 117, 53, 97]), + (vec![105, 97, 48, 77, 101, 105, 121, 101], vec![69, 109, 111, 111, 82, 49, 97, 105]), + ]); + } + +} diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 5ea099ab..3309c146 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2018 Parity Technologies +// Copyright 2017, 2019 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,9 +11,7 @@ // 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)] -#![cfg_attr(not(feature = "std"), feature(alloc))] //! Trie interface and implementation. @@ -56,8 +54,6 @@ use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -use core_::marker::PhantomData; - #[cfg(feature = "std")] use std::error::Error; @@ -85,9 +81,9 @@ pub mod recorder; mod fatdb; mod fatdbmut; mod lookup; -mod nibblevec; -mod nibbleslice; +mod nibble; mod node_codec; +mod iter_build; pub use hash_db::{HashDB, HashDBRef, Hasher}; pub use self::triedb::{TrieDB, TrieDBIterator}; @@ -98,8 +94,13 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibbleslice::NibbleSlice; -pub use node_codec::NodeCodec; +pub use self::nibble::{NibbleSlice, nibble_ops}; +pub use node_codec::{NodeCodec, Partial}; +pub use iter_build::{trie_visit, ProcessEncodedNode, + TrieBuilder, TrieRoot, TrieRootUnhashed}; + +#[cfg(feature = "std")] +pub use iter_build::TrieRootPrint; pub type DBValue = elastic_array::ElasticArray128; @@ -107,7 +108,8 @@ pub type DBValue = elastic_array::ElasticArray128; /// /// These borrow the data within them to avoid excessive copying on every /// trie operation. -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] pub enum TrieError { /// Attempted to create a trie with a state root not in the DB. InvalidStateRoot(T), @@ -121,8 +123,10 @@ pub enum TrieError { impl fmt::Display for TrieError where T: MaybeDebug, E: MaybeDebug { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - TrieError::InvalidStateRoot(ref root) => write!(f, "Invalid state root: {:?}", root), - TrieError::IncompleteDatabase(ref missing) => write!(f, "Database missing expected key: {:?}", missing), + TrieError::InvalidStateRoot(ref root) => + write!(f, "Invalid state root: {:?}", root), + TrieError::IncompleteDatabase(ref missing) => + write!(f, "Database missing expected key: {:?}", missing), TrieError::DecoderError(ref hash, ref decoder_err) => { write!(f, "Decoding failed for hash {:?}; err: {:?}", hash, decoder_err) } @@ -141,7 +145,8 @@ impl Error for TrieError where T: fmt::Debug, E: Error { } } -/// Trie result type. Boxed to avoid copying around extra space for the `Hasher`s `Out` on successful queries. +/// Trie result type. +/// Boxed to avoid copying around extra space for the `Hasher`s `Out` on successful queries. pub type Result = ::core_::result::Result>>; @@ -186,72 +191,83 @@ impl<'a, F, T, H: Hasher> Query for (&'a mut Recorder, F) where F: Fn } /// A key-value datastore implemented as a database-backed modified Merkle tree. -pub trait Trie> { +pub trait Trie { /// Return the root of the trie. - fn root(&self) -> &H::Out; + fn root(&self) -> &TrieHash; /// Is the trie empty? - fn is_empty(&self) -> bool { *self.root() == C::hashed_null_node() } + fn is_empty(&self) -> bool { *self.root() == L::Codec::hashed_null_node() } /// Does the trie contain a given key? - fn contains(&self, key: &[u8]) -> Result { - self.get(key).map(|x|x.is_some() ) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.get(key).map(|x| x.is_some() ) } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, H::Out, C::Error> where 'a: 'key { + fn get<'a, 'key>( + &'a self, + key: &'key [u8], + ) -> Result, TrieHash, CError> where 'a: 'key { self.get_with(key, DBValue::from_slice) } /// Search for the key with the given query parameter. See the docs of the `Query` /// trait for more details. - fn get_with<'a, 'key, Q: Query>( + fn get_with<'a, 'key, Q: Query>( &'a self, key: &'key [u8], query: Q - ) -> Result, H::Out, C::Error> where 'a: 'key; + ) -> Result, TrieHash, CError> where 'a: 'key; /// Returns a depth-first iterator over the elements of trie. fn iter<'a>(&'a self) -> Result< - Box> + 'a>, - H::Out, - C::Error, + Box, CError >> + 'a>, + TrieHash, + CError >; } /// A key-value datastore implemented as a database-backed modified Merkle tree. -pub trait TrieMut> { +pub trait TrieMut { /// Return the root of the trie. - fn root(&mut self) -> &H::Out; + fn root(&mut self) -> &TrieHash; /// Is the trie empty? fn is_empty(&self) -> bool; /// Does the trie contain a given key? - fn contains(&self, key: &[u8]) -> Result { + fn contains(&self, key: &[u8]) -> Result, CError> { self.get(key).map(|x| x.is_some()) } /// What is the value of the given key in this trie? - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, H::Out, C::Error> where 'a: 'key; + fn get<'a, 'key>( + &'a self, + key: &'key [u8], + ) -> Result, TrieHash, CError> where 'a: 'key; /// Insert a `key`/`value` pair into the trie. An empty value is equivalent to removing /// `key` from the trie. Returns the old value associated with this key, if it existed. - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error>; + fn insert( + &mut self, + key: &[u8], + value: &[u8], + ) -> Result, TrieHash, CError>; /// Remove a `key` from the trie. Equivalent to making it equal to the empty /// value. Returns the old value associated with this key, if it existed. - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error>; + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError>; } /// A trie iterator that also supports random access (`seek()`). -pub trait TrieIterator>: Iterator { +pub trait TrieIterator: Iterator { /// Position the iterator on the first element with key >= `key` - fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, >::Error>; + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError>; } /// Trie types -#[derive(Debug, PartialEq, Clone)] +#[derive(PartialEq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] pub enum TrieSpec { /// Generic trie. Generic, @@ -269,21 +285,20 @@ impl Default for TrieSpec { /// Trie factory. #[derive(Default, Clone)] -pub struct TrieFactory> { +pub struct TrieFactory { spec: TrieSpec, - mark_hash: PhantomData, - mark_codec: PhantomData, + layout: L, } /// All different kinds of tries. /// This is used to prevent a heap allocation for every created trie. -pub enum TrieKinds<'db, H: Hasher + 'db, C: NodeCodec> { +pub enum TrieKinds<'db, L: TrieLayout> { /// A generic trie db. - Generic(TrieDB<'db, H, C>), + Generic(TrieDB<'db, L>), /// A secure trie db. - Secure(SecTrieDB<'db, H, C>), + Secure(SecTrieDB<'db, L>), /// A fat trie db. - Fat(FatDB<'db, H, C>), + Fat(FatDB<'db, L>), } // wrapper macro for making the match easier to deal with. @@ -297,8 +312,8 @@ macro_rules! wrapper { } } -impl<'db, H: Hasher, C: NodeCodec> Trie for TrieKinds<'db, H, C> { - fn root(&self) -> &H::Out { +impl<'db, L: TrieLayout> Trie for TrieKinds<'db, L> { + fn root(&self) -> &TrieHash { wrapper!(self, root,) } @@ -306,41 +321,43 @@ impl<'db, H: Hasher, C: NodeCodec> Trie for TrieKinds<'db, H, C> { wrapper!(self, is_empty,) } - fn contains(&self, key: &[u8]) -> Result { + fn contains(&self, key: &[u8]) -> Result, CError> { wrapper!(self, contains, key) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, H::Out, C::Error> + fn get_with<'a, 'key, Q: Query>( + &'a self, key: &'key [u8], + query: Q, + ) -> Result, TrieHash, CError> where 'a: 'key { wrapper!(self, get_with, key, query) } fn iter<'a>(&'a self) -> Result< - Box> + 'a>, - H::Out, - C::Error, + Box, CError>> + 'a>, + TrieHash, + CError, > { wrapper!(self, iter,) } } -impl<'db, H, C> TrieFactory +impl<'db, L> TrieFactory where - H: Hasher, - C: NodeCodec + 'db + L: TrieLayout + 'db, { /// Creates new factory. - pub fn new(spec: TrieSpec) -> Self { - TrieFactory { spec, mark_hash: PhantomData, mark_codec: PhantomData } + pub fn new(spec: TrieSpec, layout: L) -> Self { + TrieFactory { spec, layout } } /// Create new immutable instance of Trie. pub fn readonly( &self, - db: &'db dyn HashDBRef, - root: &'db H::Out - ) -> Result, H::Out, >::Error> { + db: &'db dyn HashDBRef, + root: &'db TrieHash + ) -> Result, TrieHash, CError> { match self.spec { TrieSpec::Generic => Ok(TrieKinds::Generic(TrieDB::new(db, root)?)), TrieSpec::Secure => Ok(TrieKinds::Secure(SecTrieDB::new(db, root)?)), @@ -351,29 +368,106 @@ where /// Create new mutable instance of Trie. pub fn create( &self, - db: &'db mut dyn HashDB, - root: &'db mut H::Out, - ) -> Box + 'db> { + db: &'db mut dyn HashDB, + root: &'db mut TrieHash, + ) -> Box + 'db> { match self.spec { - TrieSpec::Generic => Box::new(TrieDBMut::<_, C>::new(db, root)), - TrieSpec::Secure => Box::new(SecTrieDBMut::<_, C>::new(db, root)), - TrieSpec::Fat => Box::new(FatDBMut::<_, C>::new(db, root)), + TrieSpec::Generic => Box::new(TrieDBMut::::new(db, root)), + TrieSpec::Secure => Box::new(SecTrieDBMut::::new(db, root)), + TrieSpec::Fat => Box::new(FatDBMut::::new(db, root)), } } /// Create new mutable instance of trie and check for errors. pub fn from_existing( &self, - db: &'db mut dyn HashDB, - root: &'db mut H::Out - ) -> Result + 'db>, H::Out, >::Error> { + db: &'db mut dyn HashDB, + root: &'db mut TrieHash, + ) -> Result + 'db>, TrieHash, CError> { match self.spec { - TrieSpec::Generic => Ok(Box::new(TrieDBMut::<_, C>::from_existing(db, root)?)), - TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::<_, C>::from_existing(db, root)?)), - TrieSpec::Fat => Ok(Box::new(FatDBMut::<_, C>::from_existing(db, root)?)), + TrieSpec::Generic => Ok(Box::new(TrieDBMut::::from_existing(db, root)?)), + TrieSpec::Secure => Ok(Box::new(SecTrieDBMut::::from_existing(db, root)?)), + TrieSpec::Fat => Ok(Box::new(FatDBMut::::from_existing(db, root)?)), } } /// Returns true iff the trie DB is a fat DB (allows enumeration of keys). pub fn is_fat(&self) -> bool { self.spec == TrieSpec::Fat } } + +/// Trait with definition of trie layout. +/// Contains all associated trait needed for +/// a trie definition or implementation. +pub trait TrieLayout { + /// If true, the trie will use extension nodes and + /// no partial in branch, if false the trie will only + /// use branch and node with partials in both. + const USE_EXTENSION: bool; + /// Hasher to use for this trie. + type Hash: Hasher; + /// Codec to use (needs to match hasher and nibble ops). + type Codec: NodeCodec; +} + +/// This traits associates a trie definition with prefered methods. +/// It also contains own default implementations and can be +/// use to allow switching implementation. +pub trait TrieConfiguration: Sized + TrieLayout { + /// Operation to build a trie db from its ordered iterator over its key/values. + fn trie_build(db: &mut DB, input: I) -> ::Out where + DB: HashDB, + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + let mut cb = TrieBuilder::new(db); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + } + /// Determines a trie root given its ordered contents, closed form. + fn trie_root(input: I) -> ::Out where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + let mut cb = TrieRoot::::default(); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + } + /// Determines a trie root node's data given its ordered contents, closed form. + fn trie_root_unhashed(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + { + let mut cb = TrieRootUnhashed::::default(); + trie_visit::(input.into_iter(), &mut cb); + cb.root.unwrap_or(Default::default()) + } + /// Encoding of index as a key (when reusing general trie for + /// indexed trie). + fn encode_index(input: u32) -> Vec { + // be for byte ordering + input.to_be_bytes().to_vec() + } + /// A trie root formed from the items, with keys attached according to their + /// compact-encoded index (using `parity-codec` crate). + fn ordered_trie_root(input: I) -> ::Out + where + I: IntoIterator, + A: AsRef<[u8]>, + { + Self::trie_root(input + .into_iter() + .enumerate() + .map(|(i, v)| (Self::encode_index(i as u32), v)) + ) + } +} + +/// Alias accessor to hasher hash output type from a `TrieLayout`. +pub type TrieHash = <::Hash as Hasher>::Out; +/// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. +pub type CError = < + ::Codec as NodeCodec<::Hash> +>::Error; diff --git a/trie-db/src/lookup.rs b/trie-db/src/lookup.rs index 7685d7ae..5eb0ec06 100644 --- a/trie-db/src/lookup.rs +++ b/trie-db/src/lookup.rs @@ -14,43 +14,43 @@ //! Trie lookup via HashDB. -use hash_db::{HashDBRef, Hasher}; -use nibbleslice::NibbleSlice; +use hash_db::HashDBRef; +use nibble::NibbleSlice; use node::Node; use node_codec::NodeCodec; -use super::{DBValue, Result, TrieError, Query}; -use ::core_::marker::PhantomData; +use super::{DBValue, Result, TrieError, Query, TrieLayout, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; /// Trie lookup helper object. -pub struct Lookup<'a, H: Hasher + 'a, C: NodeCodec, Q: Query> { +pub struct Lookup<'a, L: TrieLayout, Q: Query> { /// database to query from. - pub db: &'a dyn HashDBRef, + pub db: &'a dyn HashDBRef, /// Query object to record nodes and transform data. pub query: Q, /// Hash to start at - pub hash: H::Out, - pub marker: PhantomData, // TODO: probably not needed when all is said and done? When Query is made generic? + pub hash: TrieHash, } -impl<'a, H, C, Q> Lookup<'a, H, C, Q> +impl<'a, L, Q> Lookup<'a, L, Q> where - H: Hasher, - C: NodeCodec, - Q: Query, + L: TrieLayout, + Q: Query, { /// Look up the given key. If the value is found, it will be passed to the given /// function to decode or copy. - pub fn look_up(mut self, key: NibbleSlice) -> Result, H::Out, C::Error> { + pub fn look_up( + mut self, + key: NibbleSlice, + ) -> Result, TrieHash, CError> { let mut partial = key; let mut hash = self.hash; let mut key_nibbles = 0; // this loop iterates through non-inline nodes. for depth in 0.. { - let node_data = match self.db.get(&hash, &key.encoded_leftmost(key_nibbles, false)) { + let node_data = match self.db.get(&hash, key.mid(key_nibbles).left()) { Some(value) => value, None => return Err(Box::new(match depth { 0 => TrieError::InvalidStateRoot(hash), @@ -64,7 +64,7 @@ where // without incrementing the depth. let mut node_data = &node_data[..]; loop { - let decoded = match C::decode(node_data) { + let decoded = match L::Codec::decode(node_data) { Ok(node) => node, Err(e) => { return Err(Box::new(TrieError::DecoderError(hash, e))) @@ -97,11 +97,28 @@ where None => return Ok(None) } }, - _ => return Ok(None), + Node::NibbledBranch(slice, children, value) => { + if !partial.starts_with(&slice) { + return Ok(None) + } + + match partial.len() == slice.len() { + true => return Ok(value.map(move |val| self.query.decode(val))), + false => match children[partial.at(slice.len()) as usize] { + Some(x) => { + node_data = x; + partial = partial.mid(slice.len() + 1); + key_nibbles += slice.len() + 1; + } + None => return Ok(None) + } + } + }, + Node::Empty => return Ok(None), } // check if new node data is inline or hash. - if let Some(h) = C::try_decode_hash(&node_data) { + if let Some(h) = L::Codec::try_decode_hash(&node_data) { hash = h; break } diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs new file mode 100644 index 00000000..cf0e95ae --- /dev/null +++ b/trie-db/src/nibble/mod.rs @@ -0,0 +1,192 @@ +// Copyright 2019 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. + +//! Nibble oriented methods. + +mod nibblevec; +mod nibbleslice; +use elastic_array::ElasticArray36; +use crate::node::NodeKey; +use core_::cmp; + +/// Utility methods to work on radix 16 nibble. +pub mod nibble_ops { + + use super::*; + + /// Single nibble length in bit. + pub const BIT_PER_NIBBLE : usize = 4; + /// Number of nibble per byte. + pub const NIBBLE_PER_BYTE : usize = 2; + /// Number of child for a branch (trie radix). + pub const NIBBLE_LENGTH : usize = 16; + /// Nibble (half a byte). + pub const PADDING_BITMASK: u8 = 0x0F; + /// Size of header. + pub const CONTENT_HEADER_SIZE: u8 = 1; + + /// Mask a byte, keeping left nibble. + #[inline(always)] + pub fn pad_left(b: u8) -> u8 { + b & !PADDING_BITMASK + } + + /// Mask a byte, keeping right byte. + #[inline(always)] + pub fn pad_right(b: u8) -> u8 { + b & PADDING_BITMASK + } + + /// Get u8 nibble value at a given index of a byte. + #[inline(always)] + pub fn at_left(ix: u8, b: u8) -> u8 { + if ix == 1 { + b & PADDING_BITMASK + } else { + b >> BIT_PER_NIBBLE + } + } + + /// Get u8 nibble value at a given index in a left aligned array. + #[inline(always)] + pub fn left_nibble_at(v1: &[u8], ix: usize) -> u8 { + at_left( + (ix % NIBBLE_PER_BYTE) as u8, + v1[ix / NIBBLE_PER_BYTE] + ) + } + + /// Get u8 nibble value at a given index in a `NibbleSlice`. + #[inline(always)] + pub fn at(s: &NibbleSlice, i: usize) -> u8 { + let ix = (s.offset + i) / NIBBLE_PER_BYTE; + let pad = (s.offset + i) % NIBBLE_PER_BYTE; + at_left(pad as u8, s.data[ix]) + } + + /// Push u8 nibble value at a given index into an existing byte. + #[inline(always)] + pub fn push_at_left(ix: u8, v: u8, into: u8) -> u8 { + into | if ix == 1 { + v + } else { + v << BIT_PER_NIBBLE + } + } + + #[inline] + /// Calculate the number of needed padding a array of nibble length `i`. + pub fn number_padding(i: usize) -> usize { + i % NIBBLE_PER_BYTE + } + + /// The nibble shifts needed to align. + /// We use two value, one is a left shift and + /// the other is a right shift. + pub const SPLIT_SHIFTS: (usize, usize) = (4, 4); + + /// Count the biggest common depth between two left aligned packed nibble slice. + pub fn biggest_depth(v1: &[u8], v2: &[u8]) -> usize { + let upper_bound = cmp::min(v1.len(), v2.len()); + for a in 0 .. upper_bound { + if v1[a] != v2[a] { + return a * NIBBLE_PER_BYTE + left_common(v1[a], v2[a]); + } + } + upper_bound * NIBBLE_PER_BYTE + } + + /// Calculate the number of common nibble between two left aligned bytes. + #[inline(always)] + pub fn left_common(a: u8, b: u8) -> usize { + if a == b { + 2 + } else if pad_left(a) == pad_left(b) { + 1 + } else { + 0 + } + } + + /// Shifts right aligned key to add a given left offset. + /// Resulting in possibly padding at both left and right + /// (example usage when combining two keys). + pub fn shift_key(key: &mut NodeKey, offset: usize) -> bool { + let old_offset = key.0; + key.0 = offset; + if old_offset > offset { + // shift left + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; + let kl = key.1.len(); + (0..kl - 1).for_each(|i| key.1[i] = key.1[i] << s2 | key.1[i+1] >> s1); + key.1[kl - 1] = key.1[kl - 1] << s2; + true + } else if old_offset < offset { + // shift right + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; + key.1.push(0); + (1..key.1.len()).rev().for_each(|i| key.1[i] = key.1[i - 1] << s1 | key.1[i] >> s2); + key.1[0] = key.1[0] >> s2; + true + } else { + false + } + } + +} + +/// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. +/// Nibbles are always left aligned, so making a `NibbleVec` from +/// a `NibbleSlice` can get costy. +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, PartialEq, Eq)] +pub struct NibbleVec { + inner: ElasticArray36, + len: usize, +} + +/// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. +/// +/// This is an immutable struct. No operations actually change it. +/// +/// # Example +/// ```snippet +/// use patricia_trie::nibbleslice::NibbleSlice; +/// fn main() { +/// let d1 = &[0x01u8, 0x23, 0x45]; +/// let d2 = &[0x34u8, 0x50, 0x12]; +/// let d3 = &[0x00u8, 0x12]; +/// let n1 = NibbleSlice::new(d1); // 0,1,2,3,4,5 +/// let n2 = NibbleSlice::new(d2); // 3,4,5,0,1,2 +/// let n3 = NibbleSlice::new_offset(d3, 1); // 0,1,2 +/// assert!(n1 > n3); // 0,1,2,... > 0,1,2 +/// assert!(n1 < n2); // 0,... < 3,... +/// assert!(n2.mid(3) == n3); // 0,1,2 == 0,1,2 +/// assert!(n1.starts_with(&n3)); +/// assert_eq!(n1.common_prefix(&n3), 3); +/// assert_eq!(n2.mid(3).common_prefix(&n1), 3); +/// } +/// ``` +#[derive(Copy, Clone)] +pub struct NibbleSlice<'a> { + data: &'a [u8], + offset: usize, +} + +/// Iterator type for a nibble slice. +pub struct NibbleSliceIterator<'a> { + p: &'a NibbleSlice<'a>, + i: usize, +} + diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs new file mode 100644 index 00000000..e544cdf5 --- /dev/null +++ b/trie-db/src/nibble/nibbleslice.rs @@ -0,0 +1,426 @@ +// Copyright 2017, 2018 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. + +//! Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. + +use ::core_::cmp::*; +use ::core_::fmt; +use super::{nibble_ops, NibbleSlice, NibbleSliceIterator}; +use elastic_array::ElasticArray36; +use node::NodeKey; +use node_codec::Partial; +use hash_db::Prefix; + +impl<'a> Iterator for NibbleSliceIterator<'a> { + type Item = u8; + fn next(&mut self) -> Option { + self.i += 1; + match self.i <= self.p.len() { + true => Some(self.p.at(self.i - 1)), + false => None, + } + } +} + +impl<'a> NibbleSlice<'a> { + /// Create a new nibble slice with the given byte-slice. + pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_slice(data, 0) } + + /// Create a new nibble slice with the given byte-slice with a nibble offset. + pub fn new_offset(data: &'a [u8], offset: usize) -> Self { + Self::new_slice(data, offset) + } + + fn new_slice(data: &'a [u8], offset: usize) -> Self { + NibbleSlice { + data, + offset, + } + } + + /// Get an iterator for the series of nibbles. + pub fn iter(&'a self) -> NibbleSliceIterator<'a> { + NibbleSliceIterator { p: self, i: 0 } + } + + /// Get nibble slice from a `NodeKey`. + pub fn from_stored(i: &NodeKey) -> NibbleSlice { + NibbleSlice::new_offset(&i.1[..], i.0) + } + + /// Helper function to create a owned `NodeKey` from this `NibbleSlice`. + pub fn to_stored(&self) -> NodeKey { + let split = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let offset = self.offset % nibble_ops::NIBBLE_PER_BYTE; + (offset, self.data[split..].into()) + } + + /// Helper function to create a owned `NodeKey` from this `NibbleSlice`, + /// and for a given number of nibble. + /// Warning this method can be slow (number of nibble does not align the + /// original padding). + pub fn to_stored_range(&self, nb: usize) -> NodeKey { + if nb >= self.len() { return self.to_stored() } + if (self.offset + nb) % nibble_ops::NIBBLE_PER_BYTE == 0 { + // aligned + let start = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let end = (self.offset + nb) / nibble_ops::NIBBLE_PER_BYTE; + ( + self.offset % nibble_ops::NIBBLE_PER_BYTE, + ElasticArray36::from_slice(&self.data[start..end]), + ) + } else { + // unaligned + let start = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let end = (self.offset + nb) / nibble_ops::NIBBLE_PER_BYTE; + let ea = ElasticArray36::from_slice(&self.data[start..=end]); + let ea_offset = self.offset % nibble_ops::NIBBLE_PER_BYTE; + let n_offset = nibble_ops::number_padding(nb); + let mut result = (ea_offset, ea); + nibble_ops::shift_key(&mut result, n_offset); + result.1.pop(); + result + } + } + + /// Return true if the slice contains no nibbles. + pub fn is_empty(&self) -> bool { self.len() == 0 } + + /// Get the length (in nibbles, naturally) of this slice. + #[inline] + pub fn len(&self) -> usize { self.data.len() * nibble_ops::NIBBLE_PER_BYTE - self.offset } + + /// Get the nibble at position `i`. + #[inline(always)] + pub fn at(&self, i: usize) -> u8 { + nibble_ops::at(&self, i) + } + + /// Return object which represents a view on to this slice (further) offset by `i` nibbles. + pub fn mid(&self, i: usize) -> NibbleSlice<'a> { + NibbleSlice { + data: self.data, + offset: self.offset + i, + } + } + + /// Advance the view on the slice by `i` nibbles. + pub fn advance(&mut self, i: usize) { + debug_assert!(self.len() >= i); + self.offset += i; + } + + /// Move back to a previously valid fix offset position. + pub fn back(&self, i: usize) -> NibbleSlice<'a> { + NibbleSlice { + data: self.data, + offset: i, + } + } + + /// Do we start with the same nibbles as the whole of `them`? + pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() } + + /// How many of the same nibbles at the beginning do we match with `them`? + pub fn common_prefix(&self, them: &Self) -> usize { + let s = min(self.len(), them.len()); + let mut i = 0usize; + while i < s { + if self.at(i) != them.at(i) { break; } + i += 1; + } + i + } + + /// Return `Partial` representation of this slice: + /// first encoded byte and following slice. + pub fn right(&'a self) -> Partial { + let split = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let nb = (self.len() % nibble_ops::NIBBLE_PER_BYTE) as u8; + if nb > 0 { + ((nb, nibble_ops::pad_right(self.data[split])), &self.data[split + 1 ..]) + } else { + ((0, 0), &self.data[split..]) + } + } + + /// Return an iterator over `Partial` bytes representation. + pub fn right_iter(&'a self) -> impl Iterator + 'a { + let (mut first, sl) = self.right(); + let mut ix = 0; + ::core_::iter::from_fn(move || { + if first.0 > 0 { + first.0 = 0; + Some(nibble_ops::pad_right(first.1)) + } else { + if ix < sl.len() { + ix += 1; + Some(sl[ix - 1]) + } else { + None + } + } + }) + } + + /// Return `Partial` bytes iterator over a range of byte.. + /// Warning can be slow when unaligned (similar to `to_stored_range`). + pub fn right_range_iter(&'a self, to: usize) -> impl Iterator + 'a { + let mut nib_res = to % nibble_ops::NIBBLE_PER_BYTE; + let aligned_i = (self.offset + to) % nibble_ops::NIBBLE_PER_BYTE; + let aligned = aligned_i == 0; + let mut ix = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let ix_lim = (self.offset + to) / nibble_ops::NIBBLE_PER_BYTE; + ::core_::iter::from_fn( move || { + if aligned { + if nib_res > 0 { + let v = nibble_ops::pad_right(self.data[ix]); + nib_res = 0; + ix += 1; + Some(v) + } else if ix < ix_lim { + ix += 1; + Some(self.data[ix - 1]) + } else { + None + } + } else { + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; + // unaligned + if nib_res > 0 { + let v = self.data[ix] >> s1; + let v = nibble_ops::pad_right(v); + nib_res = 0; + Some(v) + } else if ix < ix_lim { + ix += 1; + let b1 = self.data[ix - 1] << s2; + let b2 = self.data[ix] >> s1; + Some(b1 | b2) + } else { + None + } + } + }) + } + + /// Return left portion of `NibbleSlice`, if the slice + /// originates from a full key it will be the `Prefix of + /// the node`. + pub fn left(&'a self) -> Prefix { + let split = self.offset / nibble_ops::NIBBLE_PER_BYTE; + let ix = (self.offset % nibble_ops::NIBBLE_PER_BYTE) as u8; + if ix == 0 { + (&self.data[..split], None) + } else { + (&self.data[..split], Some(nibble_ops::pad_left(self.data[split]))) + } + } + + /// Owned version of a `Prefix` from a `left` method call. + pub fn left_owned(&'a self) -> (ElasticArray36, Option) { + let (a, b) = self.left(); + (a.into(), b) + } +} + +impl<'a> Into for NibbleSlice<'a> { + fn into(self) -> NodeKey { + (self.offset, self.data.into()) + } +} + +impl<'a> PartialEq for NibbleSlice<'a> { + fn eq(&self, them: &Self) -> bool { + self.len() == them.len() && self.starts_with(them) + } +} + +impl<'a> Eq for NibbleSlice<'a> { } + +impl<'a> PartialOrd for NibbleSlice<'a> { + fn partial_cmp(&self, them: &Self) -> Option { + Some(self.cmp(them)) + } +} + +impl<'a> Ord for NibbleSlice<'a> { + fn cmp(&self, them: &Self) -> Ordering { + let s = min(self.len(), them.len()); + let mut i = 0usize; + while i < s { + match self.at(i).partial_cmp(&them.at(i)).unwrap() { + Ordering::Less => return Ordering::Less, + Ordering::Greater => return Ordering::Greater, + _ => i += 1, + } + } + self.len().cmp(&them.len()) + } +} + +#[cfg(feature = "std")] +impl<'a> fmt::Debug for NibbleSlice<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for i in 0..self.len() { + match i { + 0 => write!(f, "{:01x}", self.at(i))?, + _ => write!(f, "'{:01x}", self.at(i))?, + } + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::nibble::NibbleSlice; + use elastic_array::ElasticArray36; + static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; + + #[test] + fn basics() { + let n = NibbleSlice::new(D); + assert_eq!(n.len(), 6); + assert!(!n.is_empty()); + + let n = NibbleSlice::new_offset(D, 6); + assert!(n.is_empty()); + + let n = NibbleSlice::new_offset(D, 3); + assert_eq!(n.len(), 3); + for i in 0..3 { + assert_eq!(n.at(i), i as u8 + 3); + } + } + + #[test] + fn iterator() { + let n = NibbleSlice::new(D); + let mut nibbles: Vec = vec![]; + nibbles.extend(n.iter()); + assert_eq!(nibbles, (0u8..6).collect::>()) + } + + #[test] + fn mid() { + let n = NibbleSlice::new(D); + let m = n.mid(2); + for i in 0..4 { + assert_eq!(m.at(i), i as u8 + 2); + } + let m = n.mid(3); + for i in 0..3 { + assert_eq!(m.at(i), i as u8 + 3); + } + } + + #[test] + fn encoded_pre() { + let n = NibbleSlice::new(D); + assert_eq!(n.to_stored(), (0, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); + assert_eq!(n.mid(1).to_stored(), (1, ElasticArray36::from_slice(&[0x01, 0x23, 0x45]))); + assert_eq!(n.mid(2).to_stored(), (0, ElasticArray36::from_slice(&[0x23, 0x45]))); + assert_eq!(n.mid(3).to_stored(), (1, ElasticArray36::from_slice(&[0x23, 0x45]))); + } + + #[test] + fn from_encoded_pre() { + let n = NibbleSlice::new(D); + let stored: ElasticArray36 = [0x01, 0x23, 0x45][..].into(); + assert_eq!(n, NibbleSlice::from_stored(&(0, stored.clone()))); + assert_eq!(n.mid(1), NibbleSlice::from_stored(&(1, stored))); + } + + #[test] + fn range_iter() { + let n = NibbleSlice::new(D); + for i in [ + vec![], + vec![0x00], + vec![0x01], + vec![0x00, 0x12], + vec![0x01, 0x23], + vec![0x00, 0x12, 0x34], + vec![0x01, 0x23, 0x45], + ].iter().enumerate() { + range_iter_test(n, i.0, None, &i.1[..]); + } + for i in [ + vec![], + vec![0x01], + vec![0x12], + vec![0x01, 0x23], + vec![0x12, 0x34], + vec![0x01, 0x23, 0x45], + ].iter().enumerate() { + range_iter_test(n, i.0, Some(1), &i.1[..]); + } + for i in [ + vec![], + vec![0x02], + vec![0x23], + vec![0x02, 0x34], + vec![0x23, 0x45], + ].iter().enumerate() { + range_iter_test(n, i.0, Some(2), &i.1[..]); + } + for i in [ + vec![], + vec![0x03], + vec![0x34], + vec![0x03, 0x45], + ].iter().enumerate() { + range_iter_test(n, i.0, Some(3), &i.1[..]); + } + } + + fn range_iter_test(n: NibbleSlice, nb: usize, mid: Option, res: &[u8]) { + let n = if let Some(i) = mid { + n.mid(i) + } else { n }; + assert_eq!(&n.right_range_iter(nb).collect::>()[..], res); + } + + #[test] + fn shared() { + let n = NibbleSlice::new(D); + + let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67]; + let m = NibbleSlice::new(other); + + assert_eq!(n.common_prefix(&m), 4); + assert_eq!(m.common_prefix(&n), 4); + assert_eq!(n.mid(1).common_prefix(&m.mid(1)), 3); + assert_eq!(n.mid(1).common_prefix(&m.mid(2)), 0); + assert_eq!(n.common_prefix(&m.mid(4)), 6); + assert!(!n.starts_with(&m.mid(4))); + assert!(m.mid(4).starts_with(&n)); + } + + #[test] + fn compare() { + let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; + let n = NibbleSlice::new(D); + let m = NibbleSlice::new(other); + + assert!(n != m); + assert!(n > m); + assert!(m < n); + + assert!(n == m.mid(4)); + assert!(n >= m.mid(4)); + assert!(n <= m.mid(4)); + } +} diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs new file mode 100644 index 00000000..fc77ee22 --- /dev/null +++ b/trie-db/src/nibble/nibblevec.rs @@ -0,0 +1,282 @@ +// Copyright 2017, 2018 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. + +//! An owning, nibble-oriented byte vector. +use elastic_array::ElasticArray36; +use nibble::NibbleSlice; +use nibble::nibble_ops; +use hash_db::Prefix; +use node_codec::Partial; +use super::NibbleVec; + +impl Default for NibbleVec { + fn default() -> Self { + NibbleVec::new() + } +} + +impl NibbleVec { + /// Make a new `NibbleVec`. + pub fn new() -> Self { + NibbleVec { + inner: ElasticArray36::new(), + len: 0, + } + } + + /// Length of the `NibbleVec`. + #[inline(always)] + pub fn len(&self) -> usize { self.len } + + /// Retrurns true if `NibbleVec` has zero length. + pub fn is_empty(&self) -> bool { self.len == 0 } + + /// Try to get the nibble at the given offset. + #[inline] + pub fn at(&self, idx: usize) -> u8 { + let ix = idx / nibble_ops::NIBBLE_PER_BYTE; + let pad = idx % nibble_ops::NIBBLE_PER_BYTE; + nibble_ops::at_left(pad as u8, self.inner[ix]) + } + + /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. + pub fn push(&mut self, nibble: u8) { + let i = self.len % nibble_ops::NIBBLE_PER_BYTE; + + if i == 0 { + self.inner.push(nibble_ops::push_at_left(0, nibble, 0)); + } else { + let output = self.inner.last_mut() + .expect("len != 0 since len % 2 != 0; inner has a last element; qed"); + *output = nibble_ops::push_at_left(i as u8, nibble, *output); + } + self.len += 1; + } + + /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. + pub fn pop(&mut self) -> Option { + if self.is_empty() { + return None; + } + let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); + self.len -= 1; + let i_new = self.len % nibble_ops::NIBBLE_PER_BYTE; + if i_new != 0 { + self.inner.push(nibble_ops::pad_left(byte)); + } + Some(nibble_ops::at_left(i_new as u8, byte)) + } + + /// Remove then n last nibbles in a faster way than popping n times. + pub fn drop_lasts(&mut self, n: usize) { + if n == 0 { return; } + if n >= self.len { + self.clear(); + return; + } + let end = self.len - n; + let end_index = end / nibble_ops::NIBBLE_PER_BYTE + + if end % nibble_ops::NIBBLE_PER_BYTE == 0 { 0 } else { 1 }; + (end_index..self.inner.len()).for_each(|_| { self.inner.pop(); }); + self.len = end; + let pos = self.len % nibble_ops::NIBBLE_PER_BYTE; + if pos != 0 { + let kl = self.inner.len() - 1; + self.inner[kl] = nibble_ops::pad_left(self.inner[kl]); + } + } + + /// Get `Prefix` representation of this `NibbleVec`. + pub fn as_prefix(&self) -> Prefix { + let split = self.len / nibble_ops::NIBBLE_PER_BYTE; + let pos = (self.len % nibble_ops::NIBBLE_PER_BYTE) as u8; + if pos == 0 { + (&self.inner[..split], None) + } else { + (&self.inner[..split], Some(nibble_ops::pad_left(self.inner[split]))) + } + } + + /// Append another `NibbleVec`. Can be slow (alignement of second vec). + pub fn append(&mut self, v: &NibbleVec) { + + if v.len == 0 { return; } + let final_len = self.len + v.len; + let offset = self.len % nibble_ops::NIBBLE_PER_BYTE; + let final_offset = final_len % nibble_ops::NIBBLE_PER_BYTE; + let last_index = self.len / nibble_ops::NIBBLE_PER_BYTE; + if offset > 0 { + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; + self.inner[last_index] = nibble_ops::pad_left(self.inner[last_index]) + | (v.inner[0] >> s2); + (0..v.inner.len() - 1) + .for_each(|i| self.inner.push(v.inner[i] << s1 | v.inner[i+1] >> s2)); + if final_offset > 0 { + self.inner.push(v.inner[v.inner.len() - 1] << s1); + } + } else { + (0..v.inner.len()).for_each(|i| self.inner.push(v.inner[i])); + } + self.len += v.len; + } + + /// Append a `Partial`. Can be slow (alignement of partial). + pub fn append_partial(&mut self, (start_byte, sl): Partial) { + if start_byte.0 == 1 { + self.push(nibble_ops::at_left(1, start_byte.1)); + } + let pad = self.inner.len() * nibble_ops::NIBBLE_PER_BYTE - self.len; + if pad == 0 { + self.inner.append_slice(&sl[..]); + } else { + let kend = self.inner.len() - 1; + if sl.len() > 0 { + self.inner[kend] = nibble_ops::pad_left(self.inner[kend]); + let (s1, s2) = nibble_ops::SPLIT_SHIFTS; + self.inner[kend] |= sl[0] >> s1; + (0..sl.len() - 1).for_each(|i| self.inner.push(sl[i] << s2 | sl[i+1] >> s1)); + self.inner.push(sl[sl.len() - 1] << s2); + } + } + self.len += sl.len() * nibble_ops::NIBBLE_PER_BYTE; + } + + /// Utility function for chaining two optional appending + /// of `NibbleSlice` and/or a byte. + /// Can be slow. + pub(crate) fn append_optional_slice_and_nibble( + &mut self, + o_slice: Option<&NibbleSlice>, + o_index: Option, + ) -> usize { + let mut res = 0; + if let Some(slice) = o_slice { + self.append_partial(slice.right()); + res += slice.len(); + } + if let Some(ix) = o_index { + self.push(ix); + res += 1; + } + res + } + + /// Utility function for `append_optional_slice_and_nibble` after a clone. + /// Can be slow. + pub(crate) fn clone_append_optional_slice_and_nibble( + &self, + o_slice: Option<&NibbleSlice>, + o_index: Option, + ) -> Self { + let mut p = self.clone(); + p.append_optional_slice_and_nibble(o_slice, o_index); + p + } + + /// Get the underlying byte slice. + pub fn inner(&self) -> &[u8] { + &self.inner[..] + } + + /// clear + pub fn clear(&mut self) { + self.inner.clear(); + self.len = 0; + } + + /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if there is no padding. + pub fn as_nibbleslice(&self) -> Option { + if self.len % nibble_ops::NIBBLE_PER_BYTE == 0 { + Some(NibbleSlice::new(self.inner())) + } else { + None + } + } + +} + +impl<'a> From> for NibbleVec { + fn from(s: NibbleSlice<'a>) -> Self { + let mut v = NibbleVec::new(); + for i in 0..s.len() { + v.push(s.at(i)); + } + v + } +} + +#[cfg(test)] +mod tests { + use crate::nibble::NibbleVec; + use crate::nibble::nibble_ops; + + #[test] + fn push_pop() { + let mut v = NibbleVec::new(); + + for i in 0..(nibble_ops::NIBBLE_PER_BYTE * 3) { + let iu8 = (i % nibble_ops::NIBBLE_PER_BYTE) as u8; + v.push(iu8); + assert_eq!(v.len() - 1, i); + assert_eq!(v.at(i), iu8); + } + + for i in (0..(nibble_ops::NIBBLE_PER_BYTE * 3)).rev() { + let iu8 = (i % nibble_ops::NIBBLE_PER_BYTE) as u8; + let a = v.pop(); + assert_eq!(a, Some(iu8)); + assert_eq!(v.len(), i); + } + } + + #[test] + fn append_partial() { + append_partial_inner(&[1, 2, 3], &[], ((1, 1), &[0x23])); + append_partial_inner(&[1, 2, 3], &[1], ((0, 0), &[0x23])); + append_partial_inner(&[0, 1, 2, 3], &[0], ((1, 1), &[0x23])); + } + + fn append_partial_inner(res: &[u8], init: &[u8], partial: ((u8, u8), &[u8])) { + let mut resv = NibbleVec::new(); + res.iter().for_each(|r| resv.push(*r)); + let mut initv = NibbleVec::new(); + init.iter().for_each(|r| initv.push(*r)); + initv.append_partial(partial); + assert_eq!(resv, initv); + } + + #[test] + fn drop_lasts_test() { + let test_trun = |a: &[u8], b: usize, c: (&[u8], usize)| { + let mut k = NibbleVec::new(); + for v in a { + k.push(*v); + } + k.drop_lasts(b); + assert_eq!((&k.inner[..], k.len), c); + }; + test_trun(&[1, 2, 3, 4], 0, (&[0x12, 0x34], 4)); + test_trun(&[1, 2, 3, 4], 1, (&[0x12, 0x30], 3)); + test_trun(&[1, 2, 3, 4], 2, (&[0x12], 2)); + test_trun(&[1, 2, 3, 4], 3, (&[0x10], 1)); + test_trun(&[1, 2, 3, 4], 4, (&[], 0)); + test_trun(&[1, 2, 3, 4], 5, (&[], 0)); + test_trun(&[1, 2, 3], 0, (&[0x12, 0x30], 3)); + test_trun(&[1, 2, 3], 1, (&[0x12], 2)); + test_trun(&[1, 2, 3], 2, (&[0x10], 1)); + test_trun(&[1, 2, 3], 3, (&[], 0)); + test_trun(&[1, 2, 3], 4, (&[], 0)); + } + +} diff --git a/trie-db/src/nibbleslice.rs b/trie-db/src/nibbleslice.rs deleted file mode 100644 index 7bf81712..00000000 --- a/trie-db/src/nibbleslice.rs +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright 2017, 2018 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. - -//! Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. - -use ::core_::cmp::*; -use ::core_::fmt; -use elastic_array::ElasticArray36; - -/// Empty slice encoded as non-leaf partial key -pub const EMPTY_ENCODED: &[u8] = &[0]; - -/// Nibble-orientated view onto byte-slice, allowing nibble-precision offsets. -/// -/// This is an immutable struct. No operations actually change it. -/// -/// # Example -/// ```snippet -/// use patricia_trie::nibbleslice::NibbleSlice; -/// fn main() { -/// let d1 = &[0x01u8, 0x23, 0x45]; -/// let d2 = &[0x34u8, 0x50, 0x12]; -/// let d3 = &[0x00u8, 0x12]; -/// let n1 = NibbleSlice::new(d1); // 0,1,2,3,4,5 -/// let n2 = NibbleSlice::new(d2); // 3,4,5,0,1,2 -/// let n3 = NibbleSlice::new_offset(d3, 1); // 0,1,2 -/// assert!(n1 > n3); // 0,1,2,... > 0,1,2 -/// assert!(n1 < n2); // 0,... < 3,... -/// assert!(n2.mid(3) == n3); // 0,1,2 == 0,1,2 -/// assert!(n1.starts_with(&n3)); -/// assert_eq!(n1.common_prefix(&n3), 3); -/// assert_eq!(n2.mid(3).common_prefix(&n1), 3); -/// } -/// ``` -#[derive(Copy, Clone, Eq, Ord)] -pub struct NibbleSlice<'a> { - data: &'a [u8], - offset: usize, - data_encode_suffix: &'a [u8], - offset_encode_suffix: usize, -} - -/// Iterator type for a nibble slice. -pub struct NibbleSliceIterator<'a> { - p: &'a NibbleSlice<'a>, - i: usize, -} - -impl<'a> Iterator for NibbleSliceIterator<'a> { - type Item = u8; - fn next(&mut self) -> Option { - self.i += 1; - match self.i <= self.p.len() { - true => Some(self.p.at(self.i - 1)), - false => None, - } - } -} - -impl<'a> NibbleSlice<'a> { - /// Create a new nibble slice with the given byte-slice. - pub fn new(data: &'a [u8]) -> Self { NibbleSlice::new_offset(data, 0) } - - /// Create a new nibble slice with the given byte-slice with a nibble offset. - pub fn new_offset(data: &'a [u8], offset: usize) -> Self { - NibbleSlice { - data, - offset, - data_encode_suffix: &b""[..], - offset_encode_suffix: 0 - } - } - - /// Create a composed nibble slice; one followed by the other. - pub fn new_composed(a: &NibbleSlice<'a>, b: &NibbleSlice<'a>) -> Self { - NibbleSlice { - data: a.data, - offset: a.offset, - data_encode_suffix: b.data, - offset_encode_suffix: b.offset - } - } - - /// Get an iterator for the series of nibbles. - pub fn iter(&'a self) -> NibbleSliceIterator<'a> { - NibbleSliceIterator { p: self, i: 0 } - } - - /// Create a new nibble slice from the given HPE encoded data (e.g. output of `encoded()`). - pub fn from_encoded(data: &'a [u8]) -> (NibbleSlice, bool) { - if data.is_empty() { - (Self::new(&[]), false) - } else { - (Self::new_offset(data, if data[0] & 16 == 16 {1} else {2}), data[0] & 32 == 32) - } - } - - /// Is this an empty slice? - pub fn is_empty(&self) -> bool { self.len() == 0 } - - /// Get the length (in nibbles, naturally) of this slice. - #[inline] - pub fn len(&self) -> usize { (self.data.len() + self.data_encode_suffix.len()) * 2 - self.offset - self.offset_encode_suffix } - - /// Get the nibble at position `i`. - #[inline(always)] - pub fn at(&self, i: usize) -> u8 { - let l = self.data.len() * 2 - self.offset; - if i < l { - if (self.offset + i) & 1 == 1 { - self.data[(self.offset + i) / 2] & 15u8 - } - else { - self.data[(self.offset + i) / 2] >> 4 - } - } - else { - let i = i - l; - if (self.offset_encode_suffix + i) & 1 == 1 { - self.data_encode_suffix[(self.offset_encode_suffix + i) / 2] & 15u8 - } - else { - self.data_encode_suffix[(self.offset_encode_suffix + i) / 2] >> 4 - } - } - } - - /// Return object which represents a view on to this slice (further) offset by `i` nibbles. - pub fn mid(&self, i: usize) -> NibbleSlice<'a> { - NibbleSlice { - data: self.data, - offset: self.offset + i, - data_encode_suffix: &b""[..], - offset_encode_suffix: 0 - } - } - - /// Do we start with the same nibbles as the whole of `them`? - pub fn starts_with(&self, them: &Self) -> bool { self.common_prefix(them) == them.len() } - - /// How many of the same nibbles at the beginning do we match with `them`? - pub fn common_prefix(&self, them: &Self) -> usize { - let s = min(self.len(), them.len()); - let mut i = 0usize; - while i < s { - if self.at(i) != them.at(i) { break; } - i += 1; - } - i - } - - /// Encode while nibble slice in prefixed hex notation, noting whether it `is_leaf`. - #[inline] - pub fn encoded(&self, is_leaf: bool) -> ElasticArray36 { - let l = self.len(); - let mut r = ElasticArray36::new(); - let mut i = l % 2; - r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0}); - while i < l { - r.push(self.at(i) * 16 + self.at(i + 1)); - i += 2; - } - r - } - - /// Encode only the leftmost `n` bytes of the nibble slice in prefixed hex notation, - /// noting whether it `is_leaf`. - pub fn encoded_leftmost(&self, n: usize, is_leaf: bool) -> ElasticArray36 { - let l = min(self.len(), n); - let mut r = ElasticArray36::new(); - let mut i = l % 2; - r.push(if i == 1 {0x10 + self.at(0)} else {0} + if is_leaf {0x20} else {0}); - while i < l { - r.push(self.at(i) * 16 + self.at(i + 1)); - i += 2; - } - r - } -} - -impl<'a> PartialEq for NibbleSlice<'a> { - fn eq(&self, them: &Self) -> bool { - self.len() == them.len() && self.starts_with(them) - } -} - -impl<'a> PartialOrd for NibbleSlice<'a> { - fn partial_cmp(&self, them: &Self) -> Option { - let s = min(self.len(), them.len()); - let mut i = 0usize; - while i < s { - match self.at(i).partial_cmp(&them.at(i)).unwrap() { - Ordering::Less => return Some(Ordering::Less), - Ordering::Greater => return Some(Ordering::Greater), - _ => i += 1, - } - } - self.len().partial_cmp(&them.len()) - } -} - -impl<'a> fmt::Debug for NibbleSlice<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for i in 0..self.len() { - match i { - 0 => write!(f, "{:01x}", self.at(i))?, - _ => write!(f, "'{:01x}", self.at(i))?, - } - } - Ok(()) - } -} - -/// Join two encoded nibble slices. -pub fn combine_encoded(prefix: &[u8], extension: &[u8]) -> ElasticArray36 { - let slice = NibbleSlice::new_composed(&NibbleSlice::from_encoded(&prefix).0, &NibbleSlice::from_encoded(extension).0); - slice.encoded(false) -} - -#[cfg(test)] -mod tests { - use super::NibbleSlice; - use elastic_array::ElasticArray36; - static D: &'static [u8;3] = &[0x01u8, 0x23, 0x45]; - - #[test] - fn basics() { - let n = NibbleSlice::new(D); - assert_eq!(n.len(), 6); - assert!(!n.is_empty()); - - let n = NibbleSlice::new_offset(D, 6); - assert!(n.is_empty()); - - let n = NibbleSlice::new_offset(D, 3); - assert_eq!(n.len(), 3); - for i in 0..3 { - assert_eq!(n.at(i), i as u8 + 3); - } - } - - #[test] - fn iterator() { - let n = NibbleSlice::new(D); - let mut nibbles: Vec = vec![]; - nibbles.extend(n.iter()); - assert_eq!(nibbles, (0u8..6).collect::>()) - } - - #[test] - fn mid() { - let n = NibbleSlice::new(D); - let m = n.mid(2); - for i in 0..4 { - assert_eq!(m.at(i), i as u8 + 2); - } - let m = n.mid(3); - for i in 0..3 { - assert_eq!(m.at(i), i as u8 + 3); - } - } - - #[test] - fn encoded() { - let n = NibbleSlice::new(D); - assert_eq!(n.encoded(false), ElasticArray36::from_slice(&[0x00, 0x01, 0x23, 0x45])); - assert_eq!(n.encoded(true), ElasticArray36::from_slice(&[0x20, 0x01, 0x23, 0x45])); - assert_eq!(n.mid(1).encoded(false), ElasticArray36::from_slice(&[0x11, 0x23, 0x45])); - assert_eq!(n.mid(1).encoded(true), ElasticArray36::from_slice(&[0x31, 0x23, 0x45])); - } - - #[test] - fn from_encoded() { - let n = NibbleSlice::new(D); - assert_eq!((n, false), NibbleSlice::from_encoded(&[0x00, 0x01, 0x23, 0x45])); - assert_eq!((n, true), NibbleSlice::from_encoded(&[0x20, 0x01, 0x23, 0x45])); - assert_eq!((n.mid(1), false), NibbleSlice::from_encoded(&[0x11, 0x23, 0x45])); - assert_eq!((n.mid(1), true), NibbleSlice::from_encoded(&[0x31, 0x23, 0x45])); - } - - #[test] - fn shared() { - let n = NibbleSlice::new(D); - - let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45, 0x67]; - let m = NibbleSlice::new(other); - - assert_eq!(n.common_prefix(&m), 4); - assert_eq!(m.common_prefix(&n), 4); - assert_eq!(n.mid(1).common_prefix(&m.mid(1)), 3); - assert_eq!(n.mid(1).common_prefix(&m.mid(2)), 0); - assert_eq!(n.common_prefix(&m.mid(4)), 6); - assert!(!n.starts_with(&m.mid(4))); - assert!(m.mid(4).starts_with(&n)); - } - - #[test] - fn compare() { - let other = &[0x01u8, 0x23, 0x01, 0x23, 0x45]; - let n = NibbleSlice::new(D); - let m = NibbleSlice::new(other); - - assert!(n != m); - assert!(n > m); - assert!(m < n); - - assert!(n == m.mid(4)); - assert!(n >= m.mid(4)); - assert!(n <= m.mid(4)); - } -} diff --git a/trie-db/src/nibblevec.rs b/trie-db/src/nibblevec.rs deleted file mode 100644 index 242919d4..00000000 --- a/trie-db/src/nibblevec.rs +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2017, 2018 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. - -//! An owning, nibble-oriented byte vector. -use elastic_array::ElasticArray36; -use nibbleslice::NibbleSlice; - -/// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct NibbleVec { - inner: ElasticArray36, - len: usize, -} - -impl Default for NibbleVec { - fn default() -> Self { - NibbleVec::new() - } -} - -impl NibbleVec { - /// Make a new `NibbleVec` - pub fn new() -> Self { - NibbleVec { - inner: ElasticArray36::new(), - len: 0 - } - } - - /// Length of the `NibbleVec` - #[inline(always)] - pub fn len(&self) -> usize { self.len } - - /// Retrurns true if `NibbleVec` has zero length - pub fn is_empty(&self) -> bool { self.len == 0 } - - /// Try to get the nibble at the given offset. - #[inline] - pub fn at(&self, idx: usize) -> u8 { - if idx % 2 == 0 { - self.inner[idx / 2] >> 4 - } else { - self.inner[idx / 2] & 0x0F - } - } - - /// Push a nibble onto the `NibbleVec`. Ignores the high 4 bits. - pub fn push(&mut self, nibble: u8) { - let nibble = nibble & 0x0F; - - if self.len % 2 == 0 { - self.inner.push(nibble << 4); - } else { - *self.inner.last_mut().expect("len != 0 since len % 2 != 0; inner has a last element; qed") |= nibble; - } - - self.len += 1; - } - - /// Try to pop a nibble off the `NibbleVec`. Fails if len == 0. - pub fn pop(&mut self) -> Option { - if self.is_empty() { - return None; - } - - let byte = self.inner.pop().expect("len != 0; inner has last elem; qed"); - let nibble = if self.len % 2 == 0 { - self.inner.push(byte & 0xF0); - byte & 0x0F - } else { - byte >> 4 - }; - - self.len -= 1; - Some(nibble) - } - - /// Try to treat this `NibbleVec` as a `NibbleSlice`. Works only if len is even. - pub fn as_nibbleslice(&self) -> Option { - if self.len % 2 == 0 { - Some(NibbleSlice::new(self.inner())) - } else { - None - } - } - - /// Get the underlying byte slice. - pub fn inner(&self) -> &[u8] { - &self.inner[..] - } -} - -impl<'a> From> for NibbleVec { - fn from(s: NibbleSlice<'a>) -> Self { - let mut v = NibbleVec::new(); - for i in 0..s.len() { - v.push(s.at(i)); - } - v - } -} - -#[cfg(test)] -mod tests { - use super::NibbleVec; - - #[test] - fn push_pop() { - let mut v = NibbleVec::new(); - - for i in 0..16 { - v.push(i); - assert_eq!(v.len() - 1, i as usize); - assert_eq!(v.at(i as usize), i); - } - - for i in (0..16).rev() { - assert_eq!(v.pop(), Some(i)); - assert_eq!(v.len(), i as usize); - } - } - - #[test] - fn nibbleslice_conv() { - let mut v = NibbleVec::new(); - for i in 0..10 { - v.push(i); - } - - let v2: NibbleVec = v.as_nibbleslice().unwrap().into(); - assert_eq!(v, v2); - } -} diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index b3516198..416095e4 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -13,18 +13,21 @@ // limitations under the License. use elastic_array::ElasticArray36; -use nibbleslice::NibbleSlice; -use nibblevec::NibbleVec; +use nibble::NibbleSlice; +use nibble::nibble_ops; +use nibble::NibbleVec; use super::DBValue; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -/// Partial node key type. -pub type NodeKey = ElasticArray36; +/// Partial node key type: offset and owned value of a nibbleslice. +/// Offset is applied on first byte of array (bytes are right aligned). +pub type NodeKey = (usize, ElasticArray36); /// Type of node in the trie and essential information thereof. -#[derive(Eq, PartialEq, Debug, Clone)] +#[derive(Eq, PartialEq, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] pub enum Node<'a> { /// Null trie node; could be an empty root or an empty branch entry. Empty, @@ -32,12 +35,15 @@ pub enum Node<'a> { Leaf(NibbleSlice<'a>, &'a [u8]), /// Extension node; has key slice and node data. Data may not be null. Extension(NibbleSlice<'a>, &'a [u8]), - /// Branch node; has array of 16 child nodes (each possibly null) and an optional immediate node data. - Branch([Option<&'a [u8]>; 16], Option<&'a [u8]>), + /// Branch node; has slice of child nodes (each possibly null) + /// and an optional immediate node data. + Branch([Option<&'a [u8]>; nibble_ops::NIBBLE_LENGTH], Option<&'a [u8]>), + /// Branch node with support for a nibble (when extension nodes are not used). + NibbledBranch(NibbleSlice<'a>, [Option<&'a [u8]>; nibble_ops::NIBBLE_LENGTH], Option<&'a [u8]>), } - /// A Sparse (non mutable) owned vector struct to hold branch keys and value -#[derive(Eq, PartialEq, Debug, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Eq, PartialEq, Clone)] pub struct Branch { data: Vec, ubounds: [usize; 18], @@ -65,7 +71,7 @@ impl Branch { Branch { data, ubounds, has_value: maybe_value.is_some() } } - /// Get the node value, if any + /// Get the node value, if any. pub fn get_value(&self) -> Option<&[u8]> { if self.has_value { Some(&self.data[self.ubounds[16]..self.ubounds[17]]) @@ -74,7 +80,7 @@ impl Branch { } } - /// Test if the node has a value + /// Test if the node has a value. pub fn has_value(&self) -> bool { self.has_value } @@ -90,7 +96,8 @@ impl Branch { } /// An owning node type. Useful for trie iterators. -#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq)] pub enum OwnedNode { /// Empty trie node. Empty, @@ -98,17 +105,24 @@ pub enum OwnedNode { Leaf(NibbleVec, DBValue), /// Extension node: partial key and child node. Extension(NibbleVec, DBValue), - /// Branch node: 16 children and an optional value. + /// Branch node: children and an optional value. Branch(Branch), + /// Branch node: children and an optional value. + NibbledBranch(NibbleVec, Branch), } impl<'a> From> for OwnedNode { fn from(node: Node<'a>) -> Self { match node { Node::Empty => OwnedNode::Empty, - Node::Leaf(k, v) => OwnedNode::Leaf(k.into(), DBValue::from_slice(v)), - Node::Extension(k, child) => OwnedNode::Extension(k.into(), DBValue::from_slice(child)), - Node::Branch(c, val) => OwnedNode::Branch(Branch::new(c, val)), + Node::Leaf(k, v) => + OwnedNode::Leaf(k.into(), DBValue::from_slice(v)), + Node::Extension(k, child) => + OwnedNode::Extension(k.into(), DBValue::from_slice(child)), + Node::Branch(c, val) => + OwnedNode::Branch(Branch::new(c, val)), + Node::NibbledBranch(k, c, val) => + OwnedNode::NibbledBranch(k.into(), Branch::new(c, val)), } } } diff --git a/trie-db/src/node_codec.rs b/trie-db/src/node_codec.rs index b45b9718..afa8cb20 100644 --- a/trie-db/src/node_codec.rs +++ b/trie-db/src/node_codec.rs @@ -18,6 +18,11 @@ use hash_db::Hasher; use node::Node; use ChildReference; +#[cfg(feature = "std")] +use std::borrow::Borrow; + +#[cfg(not(feature = "std"))] +use core::borrow::Borrow; #[cfg(feature = "std")] use std::error::Error; @@ -26,17 +31,21 @@ use std::error::Error; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -use elastic_array::ElasticArray128; - #[cfg(not(feature = "std"))] pub trait Error {} #[cfg(not(feature = "std"))] impl Error for T {} -/// Trait for trie node encoding/decoding +/// Representation of a nible slice (right aligned). +/// It contains a right aligned padded first byte (first pair element is the number of nibbles +/// (0 to max nb nibble - 1), second pair element is the padded nibble), and a slice over +/// the remaining bytes. +pub type Partial<'a> = ((u8, u8), &'a[u8]); + +/// Trait for trie node encoding/decoding. pub trait NodeCodec: Sized { - /// Codec error type + /// Codec error type. type Error: Error; /// Get the hashed null node. @@ -45,22 +54,42 @@ pub trait NodeCodec: Sized { /// Decode bytes to a `Node`. Returns `Self::E` on failure. fn decode(data: &[u8]) -> Result; - /// Decode bytes to the `Hasher`s output type. Returns `None` on failure. + /// Decode bytes to the `Hasher`s output type. Returns `None` on failure. fn try_decode_hash(data: &[u8]) -> Option; /// Check if the provided bytes correspond to the codecs "empty" node. fn is_empty_node(data: &[u8]) -> bool; - /// Returns an empty node - fn empty_node() -> Vec; + /// Returns an encoded empty node. + fn empty_node() -> &'static [u8]; /// Returns an encoded leaf node - fn leaf_node(partial: &[u8], value: &[u8]) -> Vec; + fn leaf_node(partial: Partial, value: &[u8]) -> Vec; /// Returns an encoded extension node - fn ext_node(partial: &[u8], child_ref: ChildReference) -> Vec; - - /// Returns an encoded branch node. Takes an iterator yielding `ChildReference` and an optional value - fn branch_node(children: I, value: Option>) -> Vec - where I: IntoIterator>> + Iterator>>; + /// Note that number_nibble is the number of element of the iterator + /// it can possibly be obtain by `Iterator` `size_hint`, but + /// for simplicity it is used directly as a parameter. + fn extension_node( + partial: impl Iterator, + number_nibble: usize, + child_ref: ChildReference, + ) -> Vec; + + /// Returns an encoded branch node. + /// Takes an iterator yielding `ChildReference` and an optional value. + fn branch_node( + children: impl Iterator>>>, + value: Option<&[u8]>, + ) -> Vec; + + /// Returns an encoded branch node with a possible partial path. + /// `number_nibble` is the partial path length as in `extension_node`. + fn branch_node_nibbled( + partial: impl Iterator, + number_nibble: usize, + children: impl Iterator>>>, + value: Option<&[u8]> + ) -> Vec; } + diff --git a/trie-db/src/recorder.rs b/trie-db/src/recorder.rs index 0d6bd3ea..6c0bb4b6 100644 --- a/trie-db/src/recorder.rs +++ b/trie-db/src/recorder.rs @@ -18,7 +18,8 @@ use alloc::vec::Vec; /// A record of a visited node. -#[derive(PartialEq, Eq, Debug, Clone)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(PartialEq, Eq, Clone)] pub struct Record { /// The depth of this node. pub depth: u32, @@ -31,7 +32,7 @@ pub struct Record { } /// Records trie nodes as they pass it. -#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Debug))] pub struct Recorder { nodes: Vec>, min_depth: u32, @@ -183,8 +184,8 @@ mod tests { 254, 16, 83, 28, 5, 111, 103, 12, 99, 97, 116, 52, 11, 111, 116, 100, 111, 103, 24, 104, 111, 116, 99, 97, 116, 52, 11, 110, 115, 101, 114, 116, 24, 114, 101, 109, 111, 118, 101, 124, 254, 192, 0, 64, 10, 5, 116, 116, 101, 114, 36, 99, 111, 110, 102, - 117, 115, 105, 111, 110, 40, 8, 5, 110, 99, 104, 16, 116, 105, 109, 101, 52, 11, 111, - 116, 100, 111, 103, 24, 110, 111, 116, 99, 97, 116 + 117, 115, 105, 111, 110, 40, 8, 5, 110, 99, 104, 16, 116, 105, 109, 101, 52, 11, + 111, 116, 100, 111, 103, 24, 110, 111, 116, 99, 97, 116 ] ]); } diff --git a/trie-db/src/sectriedb.rs b/trie-db/src/sectriedb.rs index fef51a8f..e98880bd 100644 --- a/trie-db/src/sectriedb.rs +++ b/trie-db/src/sectriedb.rs @@ -14,8 +14,7 @@ use hash_db::{HashDBRef, Hasher}; use super::triedb::TrieDB; -use super::{Result, DBValue, Trie, TrieItem, TrieIterator, Query}; -use node_codec::NodeCodec; +use super::{Result, DBValue, Trie, TrieItem, TrieIterator, Query, TrieLayout, CError, TrieHash}; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -23,18 +22,16 @@ use alloc::boxed::Box; /// A `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// /// Use it as a `Trie` trait object. You can use `raw()` to get the backing `TrieDB` object. -pub struct SecTrieDB<'db, H, C> +pub struct SecTrieDB<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayout, { - raw: TrieDB<'db, H, C> + raw: TrieDB<'db, L> } -impl<'db, H, C> SecTrieDB<'db, H, C> +impl<'db, L> SecTrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { /// Create a new trie with the backing database `db` and empty `root` /// @@ -42,44 +39,47 @@ where /// This guarantees the trie is built correctly. /// Returns an error if root does not exist. pub fn new( - db: &'db dyn HashDBRef, - root: &'db H::Out, - ) -> Result { + db: &'db dyn HashDBRef, + root: &'db TrieHash, + ) -> Result, CError> { Ok(SecTrieDB { raw: TrieDB::new(db, root)? }) } /// Get a reference to the underlying raw `TrieDB` struct. - pub fn raw(&self) -> &TrieDB { + pub fn raw(&self) -> &TrieDB { &self.raw } /// Get a mutable reference to the underlying raw `TrieDB` struct. - pub fn raw_mut(&mut self) -> &mut TrieDB<'db, H, C> { + pub fn raw_mut(&mut self) -> &mut TrieDB<'db, L> { &mut self.raw } } -impl<'db, H, C> Trie for SecTrieDB<'db, H, C> +impl<'db, L> Trie for SecTrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { - fn root(&self) -> &H::Out { self.raw.root() } + fn root(&self) -> &TrieHash { self.raw.root() } - fn contains(&self, key: &[u8]) -> Result { - self.raw.contains(H::hash(key).as_ref()) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.raw.contains(L::Hash::hash(key).as_ref()) } - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, H::Out, C::Error> + fn get_with<'a, 'key, Q: Query>( + &'a self, + key: &'key [u8], + query: Q, + ) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get_with(H::hash(key).as_ref(), query) + self.raw.get_with(L::Hash::hash(key).as_ref(), query) } fn iter<'a>(&'a self) -> Result< - Box> + 'a>, - H::Out, - C::Error, + Box, CError>> + 'a>, + TrieHash, + CError > { TrieDB::iter(&self.raw) } diff --git a/trie-db/src/sectriedbmut.rs b/trie-db/src/sectriedbmut.rs index 45f5e80c..324002fb 100644 --- a/trie-db/src/sectriedbmut.rs +++ b/trie-db/src/sectriedbmut.rs @@ -13,29 +13,27 @@ // limitations under the License. use hash_db::{HashDB, Hasher}; -use super::{Result, DBValue, TrieMut, TrieDBMut}; -use node_codec::NodeCodec; +use super::{Result, DBValue, TrieMut, TrieDBMut, TrieLayout, TrieHash, CError}; /// A mutable `Trie` implementation which hashes keys and uses a generic `HashDB` backing database. /// -/// Use it as a `Trie` or `TrieMut` trait object. You can use `raw()` to get the backing `TrieDBMut` object. -pub struct SecTrieDBMut<'db, H, C> +/// Use it as a `Trie` or `TrieMut` trait object. You can use `raw()` to get the backing `TrieDBMut` +/// object. +pub struct SecTrieDBMut<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayout { - raw: TrieDBMut<'db, H, C> + raw: TrieDBMut<'db, L> } -impl<'db, H, C> SecTrieDBMut<'db, H, C> +impl<'db, L> SecTrieDBMut<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout { /// Create a new trie with the backing database `db` and empty `root` /// Initialise to the state entailed by the genesis block. /// This guarantees the trie is built correctly. - pub fn new(db: &'db mut dyn HashDB, root: &'db mut H::Out) -> Self { + pub fn new(db: &'db mut dyn HashDB, root: &'db mut TrieHash) -> Self { SecTrieDBMut { raw: TrieDBMut::new(db, root) } } @@ -43,25 +41,24 @@ where /// /// Returns an error if root does not exist. pub fn from_existing( - db: &'db mut dyn HashDB, - root: &'db mut H::Out, - ) -> Result { + db: &'db mut dyn HashDB, + root: &'db mut TrieHash, + ) -> Result, CError> { Ok(SecTrieDBMut { raw: TrieDBMut::from_existing(db, root)? }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDB { self.raw.db() } + pub fn db(&self) -> &dyn HashDB { self.raw.db() } /// Get the backing database. - pub fn db_mut(&mut self) -> &mut dyn HashDB { self.raw.db_mut() } + pub fn db_mut(&mut self) -> &mut dyn HashDB { self.raw.db_mut() } } -impl<'db, H, C> TrieMut for SecTrieDBMut<'db, H, C> +impl<'db, L> TrieMut for SecTrieDBMut<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { - fn root(&mut self) -> &H::Out { + fn root(&mut self) -> &TrieHash { self.raw.root() } @@ -69,22 +66,25 @@ where self.raw.is_empty() } - fn contains(&self, key: &[u8]) -> Result { - self.raw.contains(&H::hash(key).as_ref()) + fn contains(&self, key: &[u8]) -> Result, CError> { + self.raw.contains(&L::Hash::hash(key).as_ref()) } - fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, H::Out, C::Error> + fn get<'a, 'key>(&'a self, key: &'key [u8]) -> Result, TrieHash, CError> where 'a: 'key { - self.raw.get(&H::hash(key).as_ref()) + self.raw.get(&L::Hash::hash(key).as_ref()) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error> { - self.raw.insert(&H::hash(key).as_ref(), value) + fn insert( + &mut self, key: &[u8], + value: &[u8], + ) -> Result, TrieHash, CError> { + self.raw.insert(&L::Hash::hash(key).as_ref(), value) } - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error> { - self.raw.remove(&H::hash(key).as_ref()) + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + self.raw.remove(&L::Hash::hash(key).as_ref()) } } @@ -105,6 +105,9 @@ mod test { t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); } let t = RefTrieDB::new(&memdb, &root).unwrap(); - assert_eq!(t.get(&KeccakHasher::hash(&[0x01u8, 0x23])).unwrap().unwrap(), DBValue::from_slice(&[0x01u8, 0x23])); + assert_eq!( + t.get(&KeccakHasher::hash(&[0x01u8, 0x23])).unwrap().unwrap(), + DBValue::from_slice(&[0x01u8, 0x23]), + ); } } diff --git a/trie-db/src/triedb.rs b/trie-db/src/triedb.rs index 1b0aed0c..b2add430 100644 --- a/trie-db/src/triedb.rs +++ b/trie-db/src/triedb.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2018 Parity Technologies +// Copyright 2017, 2019 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,16 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use hash_db::{Hasher, HashDBRef}; -use nibbleslice::{self, NibbleSlice}; -#[cfg(feature = "std")] -use nibbleslice::combine_encoded; +use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; +use nibble::{NibbleSlice, nibble_ops}; use super::node::{Node, OwnedNode}; use node_codec::NodeCodec; use super::lookup::Lookup; -use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query}; -use ::core_::marker::PhantomData; - +use super::{Result, DBValue, Trie, TrieItem, TrieError, TrieIterator, Query, + TrieLayout, CError, TrieHash}; +use super::nibble::NibbleVec; #[cfg(feature = "std")] use ::std::fmt; #[cfg(feature = "std")] @@ -34,7 +32,6 @@ use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::vec::Vec; -use elastic_array::ElasticArray36; /// A `Trie` implementation using a generic `HashDB` backing database, a `Hasher` /// implementation to generate keys and a `NodeCodec` implementation to encode/decode @@ -66,53 +63,53 @@ use elastic_array::ElasticArray36; /// assert_eq!(t.get(b"foo").unwrap().unwrap(), DBValue::from_slice(b"bar")); /// } /// ``` -pub struct TrieDB<'db, H, C> +pub struct TrieDB<'db, L> where - H: Hasher + 'db, - C: NodeCodec + L: TrieLayout, { - db: &'db dyn HashDBRef, - root: &'db H::Out, + db: &'db dyn HashDBRef, + root: &'db TrieHash, /// The number of hashes performed so far in operations on this trie. hash_count: usize, - codec_marker: PhantomData, } -impl<'db, H, C> TrieDB<'db, H, C> +impl<'db, L> TrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { /// Create a new trie with the backing database `db` and `root` /// Returns an error if `root` does not exist pub fn new( - db: &'db dyn HashDBRef, - root: &'db H::Out, - ) -> Result { - if !db.contains(root, nibbleslice::EMPTY_ENCODED) { + db: &'db dyn HashDBRef, + root: &'db TrieHash + ) -> Result, CError> { + if !db.contains(root, EMPTY_PREFIX) { Err(Box::new(TrieError::InvalidStateRoot(*root))) } else { - Ok(TrieDB {db, root, hash_count: 0, codec_marker: PhantomData}) + Ok(TrieDB {db, root, hash_count: 0}) } } /// Get the backing database. - pub fn db(&'db self) -> &'db dyn HashDBRef { self.db } + pub fn db(&'db self) -> &'db dyn HashDBRef { self.db } /// Get the data of the root node. - pub fn root_data(&self) -> Result { + pub fn root_data(&self) -> Result, CError> { self.db - .get(self.root, nibbleslice::EMPTY_ENCODED) + .get(self.root, EMPTY_PREFIX) .ok_or_else(|| Box::new(TrieError::InvalidStateRoot(*self.root))) } /// Given some node-describing data `node`, and node key return the actual node RLP. - /// This could be a simple identity operation in the case that the node is sufficiently small, but - /// may require a database lookup. If `is_root_data` then this is root-data and + /// This could be a simple identity operation in the case that the node is sufficiently small, + /// but may require a database lookup. If `is_root_data` then this is root-data and /// is known to be literal. /// `partial_key` is encoded nibble slice that addresses the node. - fn get_raw_or_lookup(&'db self, node: &[u8], partial_key: &[u8]) -> Result, H::Out, C::Error> { - match (partial_key == nibbleslice::EMPTY_ENCODED, C::try_decode_hash(node)) { + fn get_raw_or_lookup( + &'db self, node: &[u8], + partial_key: Prefix, + ) -> Result, TrieHash, CError> { + match (partial_key.0.is_empty() && partial_key.1.is_none(), L::Codec::try_decode_hash(node)) { (false, Some(key)) => { self.db .get(&key, partial_key) @@ -124,28 +121,30 @@ where } } -impl<'db, H, C> Trie for TrieDB<'db, H, C> +impl<'db, L> Trie for TrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { - fn root(&self) -> &H::Out { self.root } - - fn get_with<'a, 'key, Q: Query>(&'a self, key: &'key [u8], query: Q) -> Result, H::Out, C::Error> - where 'a: 'key + fn root(&self) -> &TrieHash { self.root } + + fn get_with<'a, 'key, Q: Query>( + &'a self, + key: &'key [u8], + query: Q, + ) -> Result, TrieHash, CError> + where 'a: 'key, { - Lookup { + Lookup:: { db: self.db, query: query, hash: self.root.clone(), - marker: PhantomData::, }.look_up(NibbleSlice::new(key)) } - fn iter<'a>(&'a self) -> Result< - Box> + 'a>, - H::Out, - C::Error, + fn iter<'a>(&'a self)-> Result< + Box, CError>> + 'a>, + TrieHash, + CError, > { TrieDBIterator::new(self).map(|iter| Box::new(iter) as Box<_>) } @@ -154,56 +153,56 @@ where #[cfg(feature="std")] // This is for pretty debug output only -struct TrieAwareDebugNode<'db, 'a, H, C> +struct TrieAwareDebugNode<'db, 'a, L> where - H: Hasher + 'db, - C: NodeCodec + 'db + L: TrieLayout, { - trie: &'db TrieDB<'db, H, C>, + trie: &'db TrieDB<'db, L>, node_key: &'a[u8], - partial_key: ElasticArray36, + partial_key: NibbleVec, index: Option, } #[cfg(feature="std")] -impl<'db, 'a, H, C> fmt::Debug for TrieAwareDebugNode<'db, 'a, H, C> +impl<'db, 'a, L> fmt::Debug for TrieAwareDebugNode<'db, 'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, &self.partial_key) { - match C::decode(&node) { + if let Ok(node) = self.trie.get_raw_or_lookup(self.node_key, self.partial_key.as_prefix()) { + match L::Codec::decode(&node) { Ok(Node::Leaf(slice, value)) => match (f.debug_struct("Node::Leaf"), self.index) { - (ref mut d, Some(ref i)) => d.field("index", i), + (ref mut d, Some(i)) => d.field("index", &i), (ref mut d, _) => d, } .field("slice", &slice) .field("value", &value) .finish(), - Ok(Node::Extension(ref slice, ref item)) => + Ok(Node::Extension(slice, item)) => match (f.debug_struct("Node::Extension"), self.index) { - (ref mut d, Some(ref i)) => d.field("index", i), + (ref mut d, Some(i)) => d.field("index", &i), (ref mut d, _) => d, } .field("slice", &slice) .field("item", &TrieAwareDebugNode{ trie: self.trie, node_key: item, - partial_key: combine_encoded(&self.partial_key, item), + partial_key: self.partial_key + .clone_append_optional_slice_and_nibble(Some(&slice), None), index: None, }) .finish(), Ok(Node::Branch(ref nodes, ref value)) => { - let nodes: Vec> = nodes.into_iter() + let nodes: Vec> = nodes.into_iter() .enumerate() .filter_map(|(i, n)| n.map(|n| (i, n))) .map(|(i, n)| TrieAwareDebugNode { trie: self.trie, index: Some(i as u8), node_key: n, - partial_key: combine_encoded(&self.partial_key, n), + partial_key: self.partial_key + .clone_append_optional_slice_and_nibble(None, Some(i as u8)), }) .collect(); match (f.debug_struct("Node::Branch"), self.index) { @@ -214,6 +213,26 @@ where .field("value", &value) .finish() }, + Ok(Node::NibbledBranch(slice, nodes, value)) => { + let nodes: Vec> = nodes.into_iter() + .enumerate() + .filter_map(|(i, n)| n.map(|n| (i, n))) + .map(|(i, n)| TrieAwareDebugNode { + trie: self.trie, + index: Some(i as u8), + node_key: n, + partial_key: self.partial_key + .clone_append_optional_slice_and_nibble(Some(&slice), Some(i as u8)), + }).collect(); + match (f.debug_struct("Node::NibbledBranch"), self.index) { + (ref mut d, Some(ref i)) => d.field("index", i), + (ref mut d, _) => d, + } + .field("slice", &slice) + .field("nodes", &nodes) + .field("value", &value) + .finish() + }, Ok(Node::Empty) => f.debug_struct("Node::Empty").finish(), Err(e) => f.debug_struct("BROKEN_NODE") @@ -233,10 +252,9 @@ where } #[cfg(feature="std")] -impl<'db, H, C> fmt::Debug for TrieDB<'db, H, C> +impl<'db, L> fmt::Debug for TrieDB<'db, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let root_rlp = self.root_data().unwrap(); @@ -245,14 +263,16 @@ where .field("root", &TrieAwareDebugNode { trie: self, node_key: &root_rlp[..], - partial_key: Default::default(), + partial_key: NibbleVec::new(), index: None, }) .finish() } } -#[derive(Clone, Eq, PartialEq, Debug)] + +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Clone, Eq, PartialEq)] enum Status { Entering, At, @@ -260,7 +280,8 @@ enum Status { Exiting, } -#[derive(Eq, PartialEq, Debug)] +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Eq, PartialEq)] struct Crumb { node: OwnedNode, status: Status, @@ -272,36 +293,47 @@ impl Crumb { self.status = match (&self.status, &self.node) { (_, &OwnedNode::Empty) => Status::Exiting, (&Status::Entering, _) => Status::At, - (&Status::At, &OwnedNode::Branch(_)) => Status::AtChild(0), - (&Status::AtChild(x), &OwnedNode::Branch(_)) if x < 15 => Status::AtChild(x + 1), + (&Status::At, &OwnedNode::Branch(_)) + | (&Status::At, &OwnedNode::NibbledBranch(..)) => Status::AtChild(0), + (&Status::AtChild(x), &OwnedNode::Branch(_)) + | (&Status::AtChild(x), &OwnedNode::NibbledBranch(..)) + if x < (nibble_ops::NIBBLE_LENGTH - 1) => Status::AtChild(x + 1), _ => Status::Exiting, } } } /// Iterator for going through all values in the trie. -pub struct TrieDBIterator<'a, H: Hasher + 'a, C: NodeCodec + 'a> { - db: &'a TrieDB<'a, H, C>, +pub struct TrieDBIterator<'a, L: TrieLayout> { + db: &'a TrieDB<'a, L>, trail: Vec, - key_nibbles: Vec, + key_nibbles: NibbleVec, } -impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { +impl<'a, L: TrieLayout> TrieDBIterator<'a, L> { /// Create a new iterator. - pub fn new(db: &'a TrieDB) -> Result, H::Out, C::Error> { - let mut r = TrieDBIterator { db, trail: Vec::with_capacity(8), key_nibbles: Vec::with_capacity(64) }; + pub fn new(db: &'a TrieDB) -> Result, TrieHash, CError> { + let mut r = TrieDBIterator { + db, + trail: Vec::with_capacity(8), + key_nibbles: NibbleVec::new(), + }; db.root_data().and_then(|root_data| r.descend(&root_data))?; Ok(r) } - fn seek<'key>(&mut self, node_data: &DBValue, key: NibbleSlice<'key>) -> Result<(), H::Out, C::Error> { + fn seek<'key>( + &mut self, + node_data: &DBValue, + key: NibbleSlice<'key>, + ) -> Result<(), TrieHash, CError> { let mut node_data = Cow::Borrowed(node_data); let mut partial = key; let mut full_key_nibbles = 0; loop { let data = { - let node = C::decode(&node_data) - .map_err(|e|Box::new(TrieError::DecoderError(H::Out::default(), e)))?; + let node = L::Codec::decode(&node_data) + .map_err(|e| Box::new(TrieError::DecoderError(>::default(), e)))?; match node { Node::Leaf(slice, _) => { if slice >= partial { @@ -316,7 +348,7 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { }); } - self.key_nibbles.extend(slice.iter()); + self.key_nibbles.append_partial(slice.right()); return Ok(()) }, Node::Extension(ref slice, ref item) => { @@ -325,10 +357,12 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { status: Status::At, node: node.clone().into(), }); - self.key_nibbles.extend(slice.iter()); + self.key_nibbles.append_partial(slice.right()); full_key_nibbles += slice.len(); partial = partial.mid(slice.len()); - let data = self.db.get_raw_or_lookup(&*item, &key.encoded_leftmost(full_key_nibbles, false))?; + let data = self.db + .get_raw_or_lookup(&*item, key.back(full_key_nibbles) + .left())?; data } else { self.descend(&node_data)?; @@ -350,17 +384,51 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { node: node.clone().into(), }); self.key_nibbles.push(i); - full_key_nibbles += 1; - partial = partial.mid(1); if let Some(ref child) = nodes[i as usize] { - let child = self.db.get_raw_or_lookup(&*child, &key.encoded_leftmost(full_key_nibbles, false))?; + full_key_nibbles += 1; + partial = partial.mid(1); + let child = self.db + .get_raw_or_lookup(&*child, key.back(full_key_nibbles) + .left())?; child } else { return Ok(()) } } }, - _ => return Ok(()), + Node::NibbledBranch(ref slice, ref nodes, _) => { + if !partial.starts_with(slice) { + self.descend(&node_data)?; + return Ok(()) + } + if partial.len() == slice.len() { + self.trail.push(Crumb { + status: Status::Entering, + node: node.clone().into(), + }); + return Ok(()) + } else { + + let i = partial.at(slice.len()); + self.trail.push(Crumb { + status: Status::AtChild(i as usize), + node: node.clone().into(), + }); + self.key_nibbles.append_partial(slice.right()); + self.key_nibbles.push(i); + if let Some(ref child) = nodes[i as usize] { + full_key_nibbles += slice.len() + 1; + partial = partial.mid(slice.len() + 1); + let child = self.db + .get_raw_or_lookup(&*child, key.back(full_key_nibbles) + .left())?; + child + } else { + return Ok(()) + } + } + }, + Node::Empty => return Ok(()), } }; @@ -369,53 +437,38 @@ impl<'a, H: Hasher, C: NodeCodec> TrieDBIterator<'a, H, C> { } /// Descend into a payload. - fn descend(&mut self, d: &[u8]) -> Result<(), H::Out, C::Error> { - let node_data = &self.db.get_raw_or_lookup(d, &self.encoded_key())?; - let node = C::decode(&node_data) - .map_err(|e|Box::new(TrieError::DecoderError(H::Out::default(), e)))?; + fn descend(&mut self, d: &[u8]) -> Result<(), TrieHash, CError> { + let node_data = &self.db.get_raw_or_lookup(d, self.key_nibbles.as_prefix())?; + let node = L::Codec::decode(&node_data) + .map_err(|e| Box::new(TrieError::DecoderError(>::default(), e)))?; Ok(self.descend_into_node(node.into())) } /// Descend into a payload. fn descend_into_node(&mut self, node: OwnedNode) { - self.trail.push(Crumb { status: Status::Entering, node }); - match &self.trail.last().expect("just pushed item; qed").node { - &OwnedNode::Leaf(ref n, _) | &OwnedNode::Extension(ref n, _) => { - self.key_nibbles.extend((0..n.len()).map(|i| n.at(i))); - }, + let trail = &mut self.trail; + let key_nibbles = &mut self.key_nibbles; + trail.push(Crumb { status: Status::Entering, node }); + match &trail.last().expect("just pushed item; qed").node { + &OwnedNode::Leaf(ref n, _) + | &OwnedNode::Extension(ref n, _) + | &OwnedNode::NibbledBranch(ref n, _) + => key_nibbles.append(n), _ => {} } } - /// The present key. - fn key(&self) -> Vec { - // collapse the key_nibbles down to bytes. - let nibbles = &self.key_nibbles; - let mut i = 1; - let mut result = >::with_capacity(nibbles.len() / 2); - let len = nibbles.len(); - while i < len { - result.push(nibbles[i - 1] * 16 + nibbles[i]); - i += 2; - } - result + /// The present key. This can only be called on valued node (key is therefore + /// aligned to byte). + fn key(&self) -> NibbleSlice { + self.key_nibbles.as_nibbleslice().expect("a key is aligned to byte;qed") } - /// Encoded key for storage lookup - fn encoded_key(&self) -> ElasticArray36 { - let key = self.key(); - let slice = NibbleSlice::new(&key); - if self.key_nibbles.len() % 2 == 1 { - NibbleSlice::new_composed(&slice, &NibbleSlice::new_offset(&self.key_nibbles[(self.key_nibbles.len() - 1)..], 1)).encoded(false) - } else { - slice.encoded(false) - } - } } -impl<'a, H: Hasher, C: NodeCodec> TrieIterator for TrieDBIterator<'a, H, C> { +impl<'a, L: TrieLayout> TrieIterator for TrieDBIterator<'a, L> { /// Position the iterator on the first element with key >= `key` - fn seek(&mut self, key: &[u8]) -> Result<(), H::Out, C::Error> { + fn seek(&mut self, key: &[u8]) -> Result<(), TrieHash, CError> { self.trail.clear(); self.key_nibbles.clear(); let root_node = self.db.root_data()?; @@ -423,8 +476,8 @@ impl<'a, H: Hasher, C: NodeCodec> TrieIterator for TrieDBIterator<'a, H } } -impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { - type Item = TrieItem<'a, H::Out, C::Error>; +impl<'a, L: TrieLayout> Iterator for TrieDBIterator<'a, L> { + type Item = TrieItem<'a, TrieHash, CError>; fn next(&mut self) -> Option { enum IterStep<'b, O, E> { @@ -441,36 +494,47 @@ impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { (Status::Exiting, n) => { match *n { OwnedNode::Leaf(ref n, _) | OwnedNode::Extension(ref n, _) => { - let l = self.key_nibbles.len(); - self.key_nibbles.truncate(l - n.len()); + self.key_nibbles.drop_lasts(n.len()); }, OwnedNode::Branch(_) => { self.key_nibbles.pop(); }, - _ => {} + OwnedNode::NibbledBranch(ref n, _) => { + self.key_nibbles.drop_lasts(n.len() + 1); + }, + OwnedNode::Empty => {}, } IterStep::PopTrail }, - (Status::At, &OwnedNode::Branch(ref branch)) if branch.has_value() => { + (Status::At, &OwnedNode::NibbledBranch(_, ref branch)) + | (Status::At, &OwnedNode::Branch(ref branch)) if branch.has_value() => { let value = branch.get_value().expect("already checked `has_value`"); - return Some(Ok((self.key(), DBValue::from_slice(value)))); + return Some(Ok((self.key().right().1.into(), DBValue::from_slice(value)))); }, (Status::At, &OwnedNode::Leaf(_, ref v)) => { - return Some(Ok((self.key(), v.clone()))); + return Some(Ok((self.key().right().1.into(), v.clone()))); }, (Status::At, &OwnedNode::Extension(_, ref d)) => { - IterStep::Descend::(self.db.get_raw_or_lookup(&*d, &self.encoded_key())) + IterStep::Descend::, CError>( + self.db.get_raw_or_lookup(&*d, self.key_nibbles.as_prefix()) + ) }, - (Status::At, &OwnedNode::Branch(_)) => IterStep::Continue, - (Status::AtChild(i), &OwnedNode::Branch(ref branch)) if branch.index(i).is_some() => { + (Status::At, &OwnedNode::Branch(_)) + | (Status::At, &OwnedNode::NibbledBranch(_, _)) => IterStep::Continue, + (Status::AtChild(i), &OwnedNode::Branch(ref branch)) + | (Status::AtChild(i), &OwnedNode::NibbledBranch(_, ref branch)) + if branch.index(i).is_some() => { match i { 0 => self.key_nibbles.push(0), - i => *self.key_nibbles.last_mut() - .expect("pushed as 0; moves sequentially; removed afterwards; qed") = i as u8, + i => { + self.key_nibbles.pop(); + self.key_nibbles.push(i as u8); + }, } - IterStep::Descend::(self.db.get_raw_or_lookup( + IterStep::Descend::, CError>(self.db.get_raw_or_lookup( &branch.index(i).expect("this arm guarded by branch[i].is_some(); qed"), - &self.encoded_key())) + self.key_nibbles.as_prefix())) }, - (Status::AtChild(i), &OwnedNode::Branch(_)) => { + (Status::AtChild(i), &OwnedNode::Branch(_)) + | (Status::AtChild(i), &OwnedNode::NibbledBranch(_, _)) => { if i == 0 { self.key_nibbles.push(0); } @@ -484,11 +548,11 @@ impl<'a, H: Hasher, C: NodeCodec> Iterator for TrieDBIterator<'a, H, C> { IterStep::PopTrail => { self.trail.pop(); }, - IterStep::Descend::(Ok(d)) => { - let node = C::decode(&d).ok()?; + IterStep::Descend::, CError>(Ok(d)) => { + let node = L::Codec::decode(&d).ok()?; self.descend_into_node(node.into()) }, - IterStep::Descend::(Err(e)) => { + IterStep::Descend::, CError>(Err(e)) => { return Some(Err(e)) } IterStep::Continue => {}, @@ -503,6 +567,7 @@ mod tests { use keccak_hasher::KeccakHasher; use DBValue; use reference_trie::{RefTrieDB, RefTrieDBMut, RefLookup, Trie, TrieMut, NibbleSlice}; + use reference_trie::{RefTrieDBNoExt, RefTrieDBMutNoExt}; #[test] fn iterator_works() { @@ -532,6 +597,34 @@ mod tests { assert_eq!(pairs, iter_pairs); } + #[test] + fn iterator_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 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![ @@ -551,17 +644,74 @@ mod tests { let t = RefTrieDB::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); - assert_eq!(iter.next().unwrap().unwrap(), (hex!("0103000000000000000464").to_vec(), DBValue::from_slice(&hex!("fffffffffe")[..]))); + assert_eq!( + iter.next().unwrap().unwrap(), + ( + hex!("0103000000000000000464").to_vec(), + DBValue::from_slice(&hex!("fffffffffe")[..]), + ) + ); iter.seek(&hex!("00")[..]).unwrap(); - assert_eq!(pairs, iter.map(|x| x.unwrap()).map(|(k, v)| (k, v[..].to_vec())).collect::>()); + 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::>()[..]); + assert_eq!( + &pairs[1..], + &iter.map(|x| x.unwrap()) + .map(|(k, v)| (k, v[..].to_vec())) + .collect::>()[..] + ); + } + + #[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(), DBValue::from_slice(&hex!("fffffffffe")[..])) + ); + 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() { - let d = vec![DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B")]; + let d = vec![ + DBValue::from_slice(b"A"), + DBValue::from_slice(b"AA"), + DBValue::from_slice(b"AB"), + DBValue::from_slice(b"B"), + ]; let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); @@ -573,24 +723,63 @@ mod tests { } let t = RefTrieDB::new(&memdb, &root).unwrap(); - assert_eq!(d.iter().map(|i| i.clone().into_vec()).collect::>(), t.iter().unwrap().map(|x| x.unwrap().0).collect::>()); + assert_eq!( + d.iter() + .map(|i| i.clone().into_vec()) + .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_without_extension() { + let d = vec![ + DBValue::from_slice(b"A"), + DBValue::from_slice(b"AA"), + DBValue::from_slice(b"AB"), + DBValue::from_slice(b"B"), + ]; + + let mut memdb = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + for x in &d { + t.insert(x, x).unwrap(); + } + } + + let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); + assert_eq!( + d.iter().map(|i| i.clone().into_vec()).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![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; + let d = vec![ + DBValue::from_slice(b"A"), + DBValue::from_slice(b"AA"), + DBValue::from_slice(b"AB"), + DBValue::from_slice(b"B"), + ]; let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { - let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); for x in &d { t.insert(x, x).unwrap(); } } - let t = RefTrieDB::new(&memdb, &root).unwrap(); + let t = RefTrieDBNoExt::new(&memdb, &root).unwrap(); let mut iter = t.iter().unwrap(); assert_eq!(iter.next().unwrap().unwrap(), (b"A".to_vec(), DBValue::from_slice(b"A"))); iter.seek(b"!").unwrap(); @@ -619,24 +808,45 @@ mod tests { } #[test] - fn get_len() { + fn get_length_with_extension() { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(b"A", b"ABC").unwrap(); - t.insert(b"B", b"ABCBA").unwrap(); + t.insert(b"B", b"ABCBAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(); } let t = RefTrieDB::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() { - let d = vec![ DBValue::from_slice(b"A"), DBValue::from_slice(b"AA"), DBValue::from_slice(b"AB"), DBValue::from_slice(b"B") ]; + let d = vec![ + DBValue::from_slice(b"A"), + DBValue::from_slice(b"AA"), + DBValue::from_slice(b"AB"), + DBValue::from_slice(b"B"), + ]; let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); @@ -700,11 +910,11 @@ mod tests { }, }, }"); + } #[test] fn test_lookup_with_corrupt_data_returns_decoder_error() { - use std::marker::PhantomData; let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); @@ -718,7 +928,7 @@ mod tests { // 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, marker: PhantomData }; + let lookup = RefLookup { 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/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 5a5d90d0..a69c8a37 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -14,16 +14,15 @@ //! In-memory trie representation. -use super::{Result, TrieError, TrieMut}; +use super::{Result, TrieError, TrieMut, TrieLayout, TrieHash, CError}; use super::lookup::Lookup; use super::node::Node as EncodedNode; use node_codec::NodeCodec; use super::{DBValue, node::NodeKey}; -use hash_db::{HashDB, Hasher}; -use nibbleslice::{self, NibbleSlice, combine_encoded}; - -use ::core_::marker::PhantomData; +use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; +use nibble::{NibbleVec, NibbleSlice, nibble_ops}; +use elastic_array::ElasticArray36; use ::core_::mem; use ::core_::ops::Index; use ::core_::hash::Hash; @@ -45,11 +44,11 @@ use alloc::vec::Vec; // For lookups into the Node storage buffer. // This is deliberately non-copyable. -#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Debug))] struct StorageHandle(usize); // Handles to nodes in the trie. -#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Debug))] enum NodeHandle { /// Loaded into memory. InMemory(StorageHandle), @@ -70,34 +69,12 @@ fn empty_children() -> Box<[Option>; 16]> { ]) } -struct Partial<'key> { - key: NibbleSlice<'key>, - split: usize, -} - -impl<'key> Partial<'key> { - fn new(key: NibbleSlice) -> Partial { - Partial { - key, - split: 0, - } - } - - fn advance(&mut self, by: usize) { - self.split += by; - } - - fn mid(&self) -> NibbleSlice<'key> { - self.key.mid(self.split) - } - - fn encoded_prefix(&self) -> NodeKey { - self.key.encoded_leftmost(self.split, false) - } -} +/// Type alias to indicate the nible covers a full key, +/// therefore its left side is a full prefix. +type NibbleFullKey<'key> = NibbleSlice<'key>; /// Node types in the Trie. -#[derive(Debug)] +#[cfg_attr(feature = "std", derive(Debug))] enum Node { /// Empty node. Empty, @@ -111,12 +88,15 @@ enum Node { /// The child node is always a branch. Extension(NodeKey, NodeHandle), /// A branch has up to 16 children and an optional value. - Branch(Box<[Option>; 16]>, Option) + Branch(Box<[Option>; 16]>, Option), + /// Branch node with support for a nibble (to avoid extension node). + NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), } impl Node where - O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + PartialEq + Eq + Hash + Send + Sync + Clone + Copy + O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + + PartialEq + Eq + Hash + Send + Sync + Clone + Copy { // load an inline node into memory or get the hash to do the lookup later. fn inline_or_hash( @@ -136,23 +116,24 @@ where }) } - // decode a node from encoded bytes without getting its children. - fn from_encoded( - data: &[u8], + // Decode a node from encoded bytes. + fn from_encoded<'a, 'b, C, H>( + data: &'a[u8], db: &dyn HashDB, - storage: &mut NodeStorage, + storage: &'b mut NodeStorage, ) -> Self - where C: NodeCodec, H: Hasher, + where + C: NodeCodec, H: Hasher, { match C::decode(data).unwrap_or(EncodedNode::Empty) { EncodedNode::Empty => Node::Empty, - EncodedNode::Leaf(k, v) => Node::Leaf(k.encoded(true), DBValue::from_slice(&v)), + EncodedNode::Leaf(k, v) => Node::Leaf(k.into(), DBValue::from_slice(&v)), EncodedNode::Extension(key, cb) => { Node::Extension( - key.encoded(false), + key.into(), Self::inline_or_hash::(cb, db, storage)) - } - EncodedNode::Branch(ref encoded_children, val) => { + }, + EncodedNode::Branch(encoded_children, val) => { let mut child = |i:usize| { encoded_children[i].map(|data| Self::inline_or_hash::(data, db, storage) @@ -167,7 +148,23 @@ where ]); Node::Branch(children, val.map(DBValue::from_slice)) - } + }, + EncodedNode::NibbledBranch(k, encoded_children, val) => { + let mut child = |i:usize| { + encoded_children[i].map(|data| + Self::inline_or_hash::(data, db, storage) + ) + }; + + let children = Box::new([ + child(0), child(1), child(2), child(3), + child(4), child(5), child(6), child(7), + child(8), child(9), child(10), child(11), + child(12), child(13), child(14), child(15), + ]); + + Node::NibbledBranch(k.into(), children, val.map(DBValue::from_slice)) + }, } } @@ -175,25 +172,57 @@ where fn into_encoded(self, mut child_cb: F) -> Vec where C: NodeCodec, - F: FnMut(NodeHandle, &NodeKey) -> ChildReference, + F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { match self { - Node::Empty => C::empty_node(), - Node::Leaf(partial, value) => C::leaf_node(&partial, &value), - Node::Extension(partial, child) => C::ext_node(&partial, child_cb(child, &partial)), + Node::Empty => C::empty_node().to_vec(), + Node::Leaf(partial, value) => { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + C::leaf_node(pr.right(), &value) + }, + Node::Extension(partial, child) => { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + let it = pr.right_iter(); + let c = child_cb(child, Some(&pr), None); + C::extension_node( + it, + pr.len(), + c, + ) + }, Node::Branch(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, &NibbleSlice::new_offset(&[i as u8], 1).encoded(false))) - ), - value + .map(|(i, maybe_child)| { + maybe_child.map(|child| child_cb(child, None, Some(i as u8))) + }), + value.as_ref().map(|v| &v[..]) ) - } + }, + 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[..]) + ) + }, } } } @@ -296,8 +325,10 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { /// /// Use it as a `TrieMut` trait object. You can use `db()` to get the backing database object. /// Note that changes are not committed to the database until `commit` is called. +/// /// Querying the root or dropping the trie will commit automatically. /// +/// /// # Example /// ``` /// extern crate trie_db; @@ -325,78 +356,78 @@ impl<'a, H> Index<&'a StorageHandle> for NodeStorage { /// assert!(!t.contains(b"foo").unwrap()); /// } /// ``` -pub struct TrieDBMut<'a, H, C> +pub struct TrieDBMut<'a, L> where - H: Hasher + 'a, - C: NodeCodec + L: TrieLayout, { - storage: NodeStorage, - db: &'a mut dyn HashDB, - root: &'a mut H::Out, - root_handle: NodeHandle, - death_row: HashSet<(H::Out, NodeKey)>, + storage: NodeStorage>, + db: &'a mut dyn HashDB, + root: &'a mut TrieHash, + root_handle: NodeHandle>, + death_row: HashSet<(TrieHash, (ElasticArray36, Option))>, /// The number of hash operations this trie has performed. /// Note that none are performed until changes are committed. hash_count: usize, - marker: PhantomData, // TODO: rpheimer: "we could have the NodeCodec trait take &self to its methods and then we don't need PhantomData. we can just store an instance of C: NodeCodec in the trie struct. If it's a ZST it won't have any additional overhead anyway" } -impl<'a, H, C> TrieDBMut<'a, H, C> +impl<'a, L> TrieDBMut<'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { /// Create a new trie with backing database `db` and empty `root`. - pub fn new(db: &'a mut dyn HashDB, root: &'a mut H::Out) -> Self { - *root = C::hashed_null_node(); - let root_handle = NodeHandle::Hash(C::hashed_null_node()); + pub fn new(db: &'a mut dyn HashDB, root: &'a mut TrieHash) -> Self { + *root = L::Codec::hashed_null_node(); + let root_handle = NodeHandle::Hash(L::Codec::hashed_null_node()); TrieDBMut { storage: NodeStorage::empty(), - db: db, - root: root, - root_handle: root_handle, + db, + root, + root_handle, death_row: HashSet::new(), hash_count: 0, - marker: PhantomData, } } /// 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, - root: &'a mut H::Out, - ) -> Result { - if !db.contains(root, nibbleslice::EMPTY_ENCODED) { + db: &'a mut dyn HashDB, + root: &'a mut TrieHash, + ) -> Result, CError> { + if !db.contains(root, EMPTY_PREFIX) { return Err(Box::new(TrieError::InvalidStateRoot(*root))); } let root_handle = NodeHandle::Hash(*root); Ok(TrieDBMut { storage: NodeStorage::empty(), - db: db, - root: root, - root_handle: root_handle, + db, + root, + root_handle, death_row: HashSet::new(), hash_count: 0, - marker: PhantomData, }) } /// Get the backing database. - pub fn db(&self) -> &dyn HashDB { + pub fn db(&self) -> &dyn HashDB { self.db } /// Get the backing database mutably. - pub fn db_mut(&mut self) -> &mut dyn HashDB { + pub fn db_mut(&mut self) -> &mut dyn HashDB { self.db } - // cache a node by hash - fn cache(&mut self, hash: H::Out, key: &[u8]) -> Result { - let node_encoded = self.db.get(&hash, key).ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; - let node = Node::from_encoded::( + // Cache a node by hash. + fn cache( + &mut self, + hash: TrieHash, + key: Prefix, + ) -> Result, CError> { + let node_encoded = self.db.get(&hash, key) + .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(hash)))?; + let node = Node::from_encoded::( &node_encoded, &*self.db, &mut self.storage @@ -404,10 +435,21 @@ where Ok(self.storage.alloc(Stored::Cached(node, hash))) } - // inspect a node, choosing either to replace, restore, or delete it. - // if restored or replaced, returns the new node along with a flag of whether it was changed. - fn inspect(&mut self, stored: Stored, key: &mut Partial, inspector: F) -> Result, bool)>, H::Out, C::Error> - where F: FnOnce(&mut Self, Node, &mut Partial) -> Result, H::Out, C::Error> { + // Inspect a node, choosing either to replace, restore, or delete it. + // If restored or replaced, returns the new node along with a flag of whether it was changed. + fn inspect( + &mut self, + stored: Stored>, + key: &mut NibbleFullKey, + inspector: F, + ) -> Result>, bool)>, TrieHash, CError> + where + F: FnOnce( + &mut Self, + Node>, + &mut NibbleFullKey, + ) -> Result>, TrieHash, CError>, + { Ok(match stored { Stored::New(node) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::New(node), false)), @@ -417,47 +459,50 @@ where Stored::Cached(node, hash) => match inspector(self, node, key)? { Action::Restore(node) => Some((Stored::Cached(node, hash), false)), Action::Replace(node) => { - self.death_row.insert((hash, key.encoded_prefix())); + self.death_row.insert((hash, key.left_owned())); Some((Stored::New(node), true)) } Action::Delete => { - self.death_row.insert((hash, key.encoded_prefix())); + self.death_row.insert((hash, key.left_owned())); None } }, }) } - // walk the trie, attempting to find the key's node. - fn lookup<'x, 'key>(&'x self, mut partial: NibbleSlice<'key>, handle: &NodeHandle) -> Result, H::Out, C::Error> + // Walk the trie, attempting to find the key's node. + fn lookup<'x, 'key>( + &'x self, + mut partial: NibbleSlice<'key>, + handle: &NodeHandle>, + ) -> Result, TrieHash, CError> where 'x: 'key { let mut handle = handle; loop { let (mid, child) = match *handle { - NodeHandle::Hash(ref hash) => return Lookup { + NodeHandle::Hash(ref hash) => return Lookup:: { db: &self.db, query: DBValue::from_slice, hash: hash.clone(), - marker: PhantomData::, }.look_up(partial), NodeHandle::InMemory(ref handle) => match self.storage[handle] { Node::Empty => return Ok(None), Node::Leaf(ref key, ref value) => { - if NibbleSlice::from_encoded(key).0 == partial { + if NibbleSlice::from_stored(key) == partial { return Ok(Some(DBValue::from_slice(value))); } else { return Ok(None); } - } + }, Node::Extension(ref slice, ref child) => { - let slice = NibbleSlice::from_encoded(slice).0; + let slice = NibbleSlice::from_stored(slice); if partial.starts_with(&slice) { (slice.len(), child) } else { return Ok(None); } - } + }, Node::Branch(ref children, ref value) => { if partial.is_empty() { return Ok(value.as_ref().map(|v| DBValue::from_slice(v))); @@ -468,7 +513,21 @@ where None => return Ok(None), } } - } + }, + Node::NibbledBranch(ref slice, ref children, ref value) => { + let slice = NibbleSlice::from_stored(slice); + if partial.is_empty() { + return Ok(value.as_ref().map(|v| DBValue::from_slice(v))); + } else if partial.starts_with(&slice) { + let idx = partial.at(0); + match children[idx as usize].as_ref() { + Some(child) => (1 + slice.len(), child), + None => return Ok(None), + } + } else { + return Ok(None) + } + }, } }; @@ -477,12 +536,19 @@ where } } - /// insert a key-value pair into the trie, creating new nodes if necessary. - fn insert_at(&mut self, handle: NodeHandle, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result<(StorageHandle, bool), H::Out, C::Error> { + /// Insert a key-value pair into the trie, creating new nodes if necessary. + fn insert_at( + &mut self, + handle: NodeHandle>, + key: &mut NibbleFullKey, + value: DBValue, + old_val: &mut Option, + ) -> Result<(StorageHandle, bool), TrieHash, CError> { let h = match handle { NodeHandle::InMemory(h) => h, - NodeHandle::Hash(h) => self.cache(h, &key.encoded_prefix())?, + NodeHandle::Hash(h) => self.cache(h, key.left())?, }; + // cache then destroy for hash handle (handle being root in most case) let stored = self.storage.destroy(h); let (new_stored, changed) = self.inspect(stored, key, move |trie, stored, key| { trie.insert_inspector(stored, key, value, old_val).map(|a| a.into_action()) @@ -491,17 +557,28 @@ where Ok((self.storage.alloc(new_stored), changed)) } - /// the insertion inspector. - fn insert_inspector(&mut self, node: Node, key: &mut Partial, value: DBValue, old_val: &mut Option) -> Result, H::Out, C::Error> { - let partial = key.mid(); + /// The insertion inspector. + fn insert_inspector( + &mut self, + node: Node>, + key: &mut NibbleFullKey, + value: DBValue, + old_val: &mut Option, + ) -> Result>, TrieHash, CError> { + let partial = key.clone(); + + #[cfg(feature = "std")] trace!(target: "trie", "augmented (partial: {:?}, value: {:#x?})", partial, value); Ok(match node { Node::Empty => { + #[cfg(feature = "std")] trace!(target: "trie", "empty: COMPOSE"); - InsertAction::Replace(Node::Leaf(partial.encoded(true), value)) - } + InsertAction::Replace(Node::Leaf(partial.to_stored(), value)) + }, Node::Branch(mut children, stored_value) => { + debug_assert!(L::USE_EXTENSION); + #[cfg(feature = "std")] trace!(target: "trie", "branch: ROUTE,AUGMENT"); if partial.is_empty() { @@ -517,26 +594,126 @@ where let idx = partial.at(0) as usize; key.advance(1); if let Some(child) = children[idx].take() { - // original had something there. recurse down into it. + // Original had something there. recurse down into it. let (new_child, changed) = self.insert_at(child, key, value, old_val)?; children[idx] = Some(new_child.into()); if !changed { - // the new node we composed didn't change. that means our branch is untouched too. + // The new node we composed didn't change. + // It means our branch is untouched too. return Ok(InsertAction::Restore(Node::Branch(children, stored_value))); } } else { - // original had nothing there. compose a leaf. - let leaf = self.storage.alloc(Stored::New(Node::Leaf(key.mid().encoded(true), value))); + // Original had nothing there. compose a leaf. + let leaf = self.storage.alloc( + Stored::New(Node::Leaf(key.to_stored(), value)) + ); children[idx] = Some(leaf.into()); } InsertAction::Replace(Node::Branch(children, stored_value)) } - } + }, + Node::NibbledBranch(encoded, mut children, stored_value) => { + debug_assert!(!L::USE_EXTENSION); + #[cfg(feature = "std")] + trace!(target: "trie", "branch: ROUTE,AUGMENT"); + let existing_key = NibbleSlice::from_stored(&encoded); + + let common = partial.common_prefix(&existing_key); + if common == existing_key.len() && common == partial.len() { + let unchanged = stored_value.as_ref() == Some(&value); + let branch = Node::NibbledBranch( + existing_key.to_stored(), + children, + Some(value), + ); + *old_val = stored_value; + + match unchanged { + true => InsertAction::Restore(branch), + false => InsertAction::Replace(branch), + } + } else if common < existing_key.len() { + // insert a branch value in between + #[cfg(feature = "std")] + trace!( + target: "trie", + "partially-shared-prefix (exist={:?}; new={:?}; common={:?}):\ + AUGMENT-AT-END", + existing_key.len(), + partial.len(), + common, + ); + let nbranch_partial = existing_key.mid(common + 1).to_stored(); + let low = Node::NibbledBranch(nbranch_partial, children, stored_value); + let ix = existing_key.at(common); + let mut children = empty_children(); + let alloc_storage = self.storage.alloc(Stored::New(low)); + + + children[ix as usize] = Some(alloc_storage.into()); + + if partial.len() - common == 0 { + InsertAction::Replace(Node::NibbledBranch( + existing_key.to_stored_range(common), + children, + Some(value), + ) + ) + } else { + let ix = partial.at(common); + let stored_leaf = Node::Leaf(partial.mid(common + 1).to_stored(), value); + let leaf = self.storage.alloc(Stored::New(stored_leaf)); + + children[ix as usize] = Some(leaf.into()); + InsertAction::Replace(Node::NibbledBranch( + existing_key.to_stored_range(common), + children, + None, + ) + ) + + } + + } else { + // Append after common == existing_key and partial > common + #[cfg(feature = "std")] + trace!(target: "trie", "branch: ROUTE,AUGMENT"); + let idx = partial.at(common) as usize; + key.advance(common + 1); + if let Some(child) = children[idx].take() { + // Original had something there. recurse down into it. + let (new_child, changed) = self.insert_at(child, key, value, old_val)?; + children[idx] = Some(new_child.into()); + if !changed { + // The new node we composed didn't change. + // It means our branch is untouched too. + let n_branch = Node::NibbledBranch( + existing_key.to_stored(), + children, + stored_value, + ); + return Ok(InsertAction::Restore(n_branch)); + } + } else { + // Original had nothing there. compose a leaf. + let leaf = self.storage.alloc( + Stored::New(Node::Leaf(key.to_stored(), value)), + ); + children[idx] = Some(leaf.into()); + } + InsertAction::Replace(Node::NibbledBranch( + existing_key.to_stored(), + children, + stored_value, + )) + } + }, Node::Leaf(encoded, stored_value) => { - let existing_key = NibbleSlice::from_encoded(&encoded).0; - let cp = partial.common_prefix(&existing_key); - if cp == existing_key.len() && cp == partial.len() { + let existing_key = NibbleSlice::from_stored(&encoded); + let common = partial.common_prefix(&existing_key); + if common == existing_key.len() && common == partial.len() { + #[cfg(feature = "std")] trace!(target: "trie", "equivalent-leaf: REPLACE"); // equivalent leaf. let unchanged = stored_value == value; @@ -547,62 +724,115 @@ where true => InsertAction::Restore(Node::Leaf(encoded.clone(), value)), false => InsertAction::Replace(Node::Leaf(encoded.clone(), value)), } - } else if cp == 0 { - trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); + } else if (L::USE_EXTENSION && common == 0) + || (!L::USE_EXTENSION && common < existing_key.len()) { + #[cfg(feature = "std")] + trace!( + target: "trie", + "lesser-common-prefix, not-both-empty (exist={:?}; new={:?}):\ + TRANSMUTE,AUGMENT", + existing_key.len(), + partial.len(), + ); // one of us isn't empty: transmute to branch here let mut children = empty_children(); - let branch = if existing_key.is_empty() { + let branch = if L::USE_EXTENSION && existing_key.is_empty() { // always replace since branch isn't leaf. Node::Branch(children, Some(stored_value)) } else { - let idx = existing_key.at(0) as usize; - let new_leaf = Node::Leaf(existing_key.mid(1).encoded(true), stored_value); + let idx = existing_key.at(common) as usize; + let new_leaf = Node::Leaf( + existing_key.mid(common + 1).to_stored(), + stored_value, + ); children[idx] = Some(self.storage.alloc(Stored::New(new_leaf)).into()); - Node::Branch(children, None) + if L::USE_EXTENSION { + Node::Branch(children, None) + } else { + Node::NibbledBranch(partial.to_stored_range(common), children, None) + } }; - // always replace because whatever we get out here is not the branch we started with. - let branch_action = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); + // always replace because whatever we get out here + // is not the branch we started with. + let branch_action = self.insert_inspector(branch, key, value, old_val)? + .unwrap_node(); InsertAction::Replace(branch_action) - } else if cp == existing_key.len() { - trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + } else if !L::USE_EXTENSION { + #[cfg(feature = "std")] + trace!(target: "trie", "complete-prefix (common={:?}): AUGMENT-AT-END", common); + + // fully-shared prefix for an extension. + // make a stub branch + let branch = Node::NibbledBranch( + existing_key.to_stored(), + empty_children(), + Some(stored_value), + ); + // augment the new branch. + let branch = self.insert_inspector(branch, key, value, old_val)? + .unwrap_node(); + + InsertAction::Replace(branch) + + } else if common == existing_key.len() { + debug_assert!(L::USE_EXTENSION); + #[cfg(feature = "std")] + trace!(target: "trie", "complete-prefix (common={:?}): AUGMENT-AT-END", common); // fully-shared prefix for an extension. // make a stub branch and an extension. let branch = Node::Branch(empty_children(), Some(stored_value)); // augment the new branch. - key.advance(cp); + key.advance(common); let branch = self.insert_inspector(branch, key, value, old_val)?.unwrap_node(); // always replace since we took a leaf and made an extension. let branch_handle = self.storage.alloc(Stored::New(branch)).into(); - InsertAction::Replace(Node::Extension(existing_key.encoded(false), branch_handle)) + InsertAction::Replace(Node::Extension(existing_key.to_stored(), branch_handle)) } else { - trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); + debug_assert!(L::USE_EXTENSION); + #[cfg(feature = "std")] + trace!( + target: "trie", + "partially-shared-prefix (exist={:?}; new={:?}; common={:?}):\ + AUGMENT-AT-END", + existing_key.len(), + partial.len(), + common, + ); // partially-shared prefix for an extension. // start by making a leaf. - let low = Node::Leaf(existing_key.mid(cp).encoded(true), stored_value); + let low = Node::Leaf(existing_key.mid(common).to_stored(), stored_value); - // augment it. this will result in the Leaf -> cp == 0 routine, + // augment it. this will result in the Leaf -> common == 0 routine, // which creates a branch. - key.advance(cp); - let augmented_low = self.insert_inspector(low, key, value, old_val)?.unwrap_node(); - + key.advance(common); + let augmented_low = self.insert_inspector(low, key, value, old_val)? + .unwrap_node(); // make an extension using it. this is a replacement. InsertAction::Replace(Node::Extension( - existing_key.encoded_leftmost(cp, false), + existing_key.to_stored_range(common), self.storage.alloc(Stored::New(augmented_low)).into() )) } - } + }, Node::Extension(encoded, child_branch) => { - let existing_key = NibbleSlice::from_encoded(&encoded).0; - let cp = partial.common_prefix(&existing_key); - if cp == 0 { - trace!(target: "trie", "no-common-prefix, not-both-empty (exist={:?}; new={:?}): TRANSMUTE,AUGMENT", existing_key.len(), partial.len()); + debug_assert!(L::USE_EXTENSION); + let existing_key = NibbleSlice::from_stored(&encoded); + let common = partial.common_prefix(&existing_key); + if common == 0 { + #[cfg(feature = "std")] + trace!( + target: "trie", + "no-common-prefix, not-both-empty (exist={:?}; new={:?}):\ + TRANSMUTE,AUGMENT", + existing_key.len(), + partial.len(), + ); // partial isn't empty: make a branch here // extensions may not have empty partial keys. @@ -615,22 +845,28 @@ where Some(child_branch) } else { // more work required after branching. - let ext = Node::Extension(existing_key.mid(1).encoded(false), child_branch); + let ext = Node::Extension(existing_key.mid(1).to_stored(), child_branch); Some(self.storage.alloc(Stored::New(ext)).into()) }; // continue inserting. - let branch_action = self.insert_inspector(Node::Branch(children, None), key, value, old_val)?.unwrap_node(); + let branch_action = self.insert_inspector( + Node::Branch(children, None), + key, + value, + old_val, + )?.unwrap_node(); InsertAction::Replace(branch_action) - } else if cp == existing_key.len() { - trace!(target: "trie", "complete-prefix (cp={:?}): AUGMENT-AT-END", cp); + } else if common == existing_key.len() { + #[cfg(feature = "std")] + trace!(target: "trie", "complete-prefix (common={:?}): AUGMENT-AT-END", common); // fully-shared prefix. // insert into the child node. - key.advance(cp); + key.advance(common); let (new_child, changed) = self.insert_at(child_branch, key, value, old_val)?; - let new_ext = Node::Extension(existing_key.encoded(false), new_child.into()); + let new_ext = Node::Extension(existing_key.to_stored(), new_child.into()); // if the child branch wasn't changed, meaning this extension remains the same. match changed { @@ -638,56 +874,92 @@ where false => InsertAction::Restore(new_ext), } } else { - trace!(target: "trie", "partially-shared-prefix (exist={:?}; new={:?}; cp={:?}): AUGMENT-AT-END", existing_key.len(), partial.len(), cp); + #[cfg(feature = "std")] + trace!( + target: "trie", + "partially-shared-prefix (exist={:?}; new={:?}; common={:?}):\ + AUGMENT-AT-END", + existing_key.len(), + partial.len(), + common, + ); // partially-shared. - let low = Node::Extension(existing_key.mid(cp).encoded(false), child_branch); - // augment the extension. this will take the cp == 0 path, creating a branch. - key.advance(cp); - let augmented_low = self.insert_inspector(low, key, value, old_val)?.unwrap_node(); + let low = Node::Extension(existing_key.mid(common).to_stored(), child_branch); + // augment the extension. this will take the common == 0 path, + // creating a branch. + key.advance(common); + let augmented_low = self.insert_inspector(low, key, value, old_val)? + .unwrap_node(); // always replace, since this extension is not the one we started with. // this is known because the partial key is only the common prefix. InsertAction::Replace(Node::Extension( - existing_key.encoded_leftmost(cp, false), + existing_key.to_stored_range(common), self.storage.alloc(Stored::New(augmented_low)).into() )) } - } + }, }) } - /// Remove a node from the trie based on key. - fn remove_at(&mut self, handle: NodeHandle, key: &mut Partial, old_val: &mut Option) -> Result, H::Out, C::Error> { + /// Removes a node from the trie based on key. + fn remove_at( + &mut self, + handle: NodeHandle>, + key: &mut NibbleFullKey, + old_val: &mut Option, + ) -> Result, TrieHash, CError> { let stored = match handle { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, &key.encoded_prefix())?; + let handle = self.cache(h, key.left())?; self.storage.destroy(handle) } }; - let opt = self.inspect(stored, key, move |trie, node, key| trie.remove_inspector(node, key, old_val))?; + let opt = self.inspect( + stored, + key, + move |trie, node, key| trie.remove_inspector(node, key, old_val), + )?; Ok(opt.map(|(new, changed)| (self.storage.alloc(new), changed))) } - /// the removal inspector - fn remove_inspector(&mut self, node: Node, key: &mut Partial, old_val: &mut Option) -> Result, H::Out, C::Error> { - let partial = key.mid(); + /// The removal inspector. + fn remove_inspector( + &mut self, + node: Node>, + key: &mut NibbleFullKey, + old_val: &mut Option, + ) -> Result>, TrieHash, CError> { + let partial = key.clone(); Ok(match (node, partial.is_empty()) { (Node::Empty, _) => Action::Delete, (Node::Branch(c, None), true) => Action::Restore(Node::Branch(c, None)), + (Node::NibbledBranch(n, c, None), true) => + Action::Restore(Node::NibbledBranch(n, c, None)), (Node::Branch(children, Some(val)), true) => { *old_val = Some(val); // always replace since we took the value out. - Action::Replace(self.fix(Node::Branch(children, None), key.encoded_prefix())?) - } + Action::Replace(self.fix(Node::Branch(children, None), key.clone())?) + }, + (Node::NibbledBranch(n, children, Some(val)), true) => { + *old_val = Some(val); + // always replace since we took the value out. + Action::Replace(self.fix(Node::NibbledBranch(n, children, None), key.clone())?) + }, (Node::Branch(mut children, value), false) => { let idx = partial.at(0) as usize; if let Some(child) = children[idx].take() { - trace!(target: "trie", "removing value out of branch child, partial={:?}", partial); - let prefix = key.encoded_prefix(); + #[cfg(feature = "std")] + trace!( + target: "trie", + "removing value out of branch child, partial={:?}", + partial, + ); + let prefix = key.clone(); key.advance(1); match self.remove_at(child, key, old_val)? { Some((new, changed)) => { @@ -703,6 +975,7 @@ where None => { // the child we took was deleted. // the node may need fixing. + #[cfg(feature = "std")] trace!(target: "trie", "branch child deleted, partial={:?}", partial); Action::Replace(self.fix(Node::Branch(children, value), prefix)?) } @@ -711,28 +984,98 @@ where // no change needed. Action::Restore(Node::Branch(children, value)) } - } + }, + (Node::NibbledBranch(encoded, mut children, value), false) => { + let (common, existing_length) = { + let existing_key = NibbleSlice::from_stored(&encoded); + (existing_key.common_prefix(&partial), existing_key.len()) + }; + if common == existing_length && common == partial.len() { + + // replace val + if let Some(val) = value { + *old_val = Some(val); + + let f = self.fix(Node::NibbledBranch(encoded, children, None), key.clone()); + Action::Replace(f?) + } else { + Action::Restore(Node::NibbledBranch(encoded, children, None)) + } + } else if common < existing_length { + // partway through an extension -- nothing to do here. + Action::Restore(Node::NibbledBranch(encoded, children, value)) + } else { + // common == existing_length && common < partial.len() : check children + let idx = partial.at(common) as usize; + + if let Some(child) = children[idx].take() { + #[cfg(feature = "std")] + trace!( + target: "trie", + "removing value out of branch child, partial={:?}", + partial, + ); + let prefix = key.clone(); + key.advance(common + 1); + match self.remove_at(child, key, old_val)? { + Some((new, changed)) => { + children[idx] = Some(new.into()); + let branch = Node::NibbledBranch(encoded, children, value); + match changed { + // child was changed, so we were too. + true => Action::Replace(branch), + // unchanged, so we are too. + false => Action::Restore(branch), + } + }, + None => { + // the child we took was deleted. + // the node may need fixing. + #[cfg(feature = "std")] + trace!( + target: "trie", + "branch child deleted, partial={:?}", + partial, + ); + Action::Replace( + self.fix(Node::NibbledBranch(encoded, children, value), prefix)? + ) + }, + } + } else { + // no change needed. + Action::Restore(Node::NibbledBranch(encoded, children, value)) + } + } + }, (Node::Leaf(encoded, value), _) => { - if NibbleSlice::from_encoded(&encoded).0 == partial { + if NibbleSlice::from_stored(&encoded) == partial { // this is the node we were looking for. Let's delete it. *old_val = Some(value); Action::Delete } else { // leaf the node alone. - trace!(target: "trie", "restoring leaf wrong partial, partial={:?}, existing={:?}", partial, NibbleSlice::from_encoded(&encoded).0); + #[cfg(feature = "std")] + trace!( + target: "trie", + "restoring leaf wrong partial, partial={:?}, existing={:?}", + partial, + NibbleSlice::from_stored(&encoded), + ); Action::Restore(Node::Leaf(encoded, value)) } - } + }, (Node::Extension(encoded, child_branch), _) => { - let (cp, existing_len) = { - let existing_key = NibbleSlice::from_encoded(&encoded).0; + let (common, existing_length) = { + let existing_key = NibbleSlice::from_stored(&encoded); (existing_key.common_prefix(&partial), existing_key.len()) }; - if cp == existing_len { + if common == existing_length { // try to remove from the child branch. + #[cfg(feature = "std")] trace!(target: "trie", "removing from extension child, partial={:?}", partial); - let prefix = key.encoded_prefix(); - key.advance(cp); + let prefix = key.clone(); + key.advance(common); match self.remove_at(child_branch, key, old_val)? { Some((new_child, changed)) => { let new_child = new_child.into(); @@ -740,7 +1083,9 @@ where // if the child branch was unchanged, then the extension is too. // otherwise, this extension may need fixing. match changed { - true => Action::Replace(self.fix(Node::Extension(encoded, new_child), prefix)?), + true => Action::Replace( + self.fix(Node::Extension(encoded, new_child), prefix)? + ), false => Action::Restore(Node::Extension(encoded, new_child)), } } @@ -754,7 +1099,7 @@ where // partway through an extension -- nothing to do here. Action::Restore(Node::Extension(encoded, child_branch)) } - } + }, }) } @@ -764,11 +1109,15 @@ where /// _invalid state_ means: /// - Branch node where there is only a single entry; /// - Extension node followed by anything other than a Branch node. - fn fix(&mut self, node: Node, key: NodeKey) -> Result, H::Out, C::Error> { + fn fix( + &mut self, + node: Node>, + key: NibbleSlice, + ) -> Result>, TrieHash, CError> { match node { Node::Branch(mut children, value) => { // if only a single value, transmute to leaf/extension and feed through fixed. - #[derive(Debug)] + #[cfg_attr(feature = "std", derive(Debug))] enum UsedIndex { None, One(u8), @@ -787,31 +1136,149 @@ where } match (used_index, value) { - (UsedIndex::None, None) => panic!("Branch with no subvalues. Something went wrong."), + (UsedIndex::None, None) => + panic!("Branch with no subvalues. Something went wrong."), (UsedIndex::One(a), None) => { // only one onward node. make an extension. - let new_partial = NibbleSlice::new_offset(&[a], 1).encoded(false); - let child = children[a as usize].take().expect("used_index only set if occupied; qed"); + + let new_partial = NibbleSlice::new_offset(&[a], 1).to_stored(); + let child = children[a as usize].take() + .expect("used_index only set if occupied; qed"); let new_node = Node::Extension(new_partial, child); self.fix(new_node, key) } (UsedIndex::None, Some(value)) => { // make a leaf. + #[cfg(feature = "std")] trace!(target: "trie", "fixing: branch -> leaf"); - Ok(Node::Leaf(NibbleSlice::new(&[]).encoded(true), value)) + Ok(Node::Leaf(NibbleSlice::new(&[]).to_stored(), value)) } (_, value) => { // all is well. + #[cfg(feature = "std")] trace!(target: "trie", "fixing: restoring branch"); Ok(Node::Branch(children, value)) } } - } + }, + Node::NibbledBranch(enc_nibble, mut children, value) => { + // if only a single value, transmute to leaf/extension and feed through fixed. + #[cfg_attr(feature = "std", derive(Debug))] + enum UsedIndex { + None, + One(u8), + Many, + }; + let mut used_index = UsedIndex::None; + for i in 0..16 { + match (children[i].is_none(), &used_index) { + (false, &UsedIndex::None) => used_index = UsedIndex::One(i as u8), + (false, &UsedIndex::One(_)) => { + used_index = UsedIndex::Many; + break; + } + _ => continue, + } + } + + match (used_index, value) { + (UsedIndex::None, None) => + panic!("Branch with no subvalues. Something went wrong."), + (UsedIndex::One(a), None) => { + // only one onward node. use child instead + let child = children[a as usize].take() + .expect("used_index only set if occupied; qed"); + let mut key2 = key.clone(); + key2.advance((enc_nibble.1.len() * nibble_ops::NIBBLE_PER_BYTE) - enc_nibble.0); + let (start, alloc_start, prefix_end) = match key2.left() { + (start, None) => (start, None, Some(nibble_ops::push_at_left(0, a, 0))), + (start, Some(v)) => { + let mut so: ElasticArray36 = start.into(); + so.push(nibble_ops::pad_left(v) | a); + (start, Some(so), None) + }, + }; + let child_prefix = (alloc_start.as_ref().map(|start| &start[..]).unwrap_or(start), prefix_end); + let stored = match child { + NodeHandle::InMemory(h) => self.storage.destroy(h), + NodeHandle::Hash(h) => { + let handle = self.cache(h, child_prefix)?; + self.storage.destroy(handle) + } + }; + let child_node = match stored { + Stored::New(node) => node, + Stored::Cached(node, hash) => { + self.death_row.insert(( + hash, + (child_prefix.0[..].into(), child_prefix.1), + )); + node + }, + }; + match child_node { + Node::Leaf(sub_partial, value) => { + let mut enc_nibble = enc_nibble; + combine_key( + &mut enc_nibble, + (nibble_ops::NIBBLE_PER_BYTE - 1, &[a][..]), + ); + combine_key( + &mut enc_nibble, + (sub_partial.0, &sub_partial.1[..]), + ); + Ok(Node::Leaf(enc_nibble, value)) + }, + Node::NibbledBranch(sub_partial, ch_children, ch_value) => { + let mut enc_nibble = enc_nibble; + combine_key( + &mut enc_nibble, + (nibble_ops::NIBBLE_PER_BYTE - 1, &[a][..]), + ); + combine_key( + &mut enc_nibble, + (sub_partial.0, &sub_partial.1[..]), + ); + Ok(Node::NibbledBranch(enc_nibble, ch_children, ch_value)) + }, + _ => unreachable!(), + } + }, + (UsedIndex::None, Some(value)) => { + // make a leaf. + #[cfg(feature = "std")] + trace!(target: "trie", "fixing: branch -> leaf"); + Ok(Node::Leaf(enc_nibble, value)) + }, + (_, value) => { + // all is well. + #[cfg(feature = "std")] + trace!(target: "trie", "fixing: restoring branch"); + Ok(Node::NibbledBranch(enc_nibble, children, value)) + }, + } + }, Node::Extension(partial, child) => { + // We could advance key, but this code can also be called + // recursively, so there might be some prefix from branch. + let last = partial.1[partial.1.len() - 1] & (255 >> 4); + let mut key2 = key.clone(); + key2.advance((partial.1.len() * nibble_ops::NIBBLE_PER_BYTE) - partial.0 - 1); + let (start, alloc_start, prefix_end) = match key2.left() { + (start, None) => (start, None, Some(nibble_ops::push_at_left(0, last, 0))), + (start, Some(v)) => { + let mut so: ElasticArray36 = start.into(); + // Complete last byte with `last`. + so.push(nibble_ops::pad_left(v) | last); + (start, Some(so), None) + }, + }; + let child_prefix = (alloc_start.as_ref().map(|start| &start[..]).unwrap_or(start), prefix_end); + let stored = match child { NodeHandle::InMemory(h) => self.storage.destroy(h), NodeHandle::Hash(h) => { - let handle = self.cache(h, &combine_encoded(&key, &partial))?; + let handle = self.cache(h, child_prefix)?; self.storage.destroy(handle) } }; @@ -826,30 +1293,40 @@ where // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. - self.death_row.insert((hash, key.clone())); + self.death_row.insert( + (hash, (child_prefix.0[..].into(), child_prefix.1)), + ); } - let partial = NibbleSlice::from_encoded(&partial).0; - let sub_partial = NibbleSlice::from_encoded(&sub_partial).0; - - let new_partial = NibbleSlice::new_composed(&partial, &sub_partial); - trace!(target: "trie", "fixing: extension combination. new_partial={:?}", new_partial); - let new_partial = new_partial.encoded(false); - self.fix(Node::Extension(new_partial, sub_child), key) + // subpartial + let mut partial = partial; + combine_key(&mut partial, (sub_partial.0, &sub_partial.1[..])); + #[cfg(feature = "std")] + trace!( + target: "trie", + "fixing: extension combination. new_partial={:?}", + partial, + ); + self.fix(Node::Extension(partial, sub_child), key) } Node::Leaf(sub_partial, value) => { // combine with node below. if let Some(hash) = maybe_hash { // delete the cached child since we are going to replace it. - self.death_row.insert((hash, key)); + self.death_row.insert((hash, (child_prefix.0[..].into(), child_prefix.1))); } - let partial = NibbleSlice::from_encoded(&partial).0; - let sub_partial = NibbleSlice::from_encoded(&sub_partial).0; - - let new_partial = NibbleSlice::new_composed(&partial, &sub_partial); - trace!(target: "trie", "fixing: extension -> leaf. new_partial={:?}", new_partial); - Ok(Node::Leaf(new_partial.encoded(true), value)) + // subpartial oly + let mut partial = partial; + combine_key(&mut partial, (sub_partial.0, &sub_partial.1[..])); + #[cfg(feature = "std")] + trace!( + target: "trie", + "fixing: extension -> leaf. new_partial={:?}", + partial, + ); + Ok(Node::Leaf(partial, value)) } child_node => { + #[cfg(feature = "std")] trace!(target: "trie", "fixing: restoring extension"); // reallocate the child node. @@ -862,7 +1339,7 @@ where Ok(Node::Extension(partial, self.storage.alloc(stored).into())) } } - } + }, other => Ok(other), // only ext and branch need fixing. } } @@ -870,12 +1347,14 @@ where /// Commit the in-memory changes to disk, freeing their storage and /// updating the state root. pub fn commit(&mut self) { + #[cfg(feature = "std")] trace!(target: "trie", "Committing trie changes to db."); // always kill all the nodes on death row. + #[cfg(feature = "std")] trace!(target: "trie", "{:?} nodes to remove from db", self.death_row.len()); for (hash, prefix) in self.death_row.drain() { - self.db.remove(&hash, &prefix); + self.db.remove(&hash, (&prefix.0[..], prefix.1)); } let handle = match self.root_handle() { @@ -885,13 +1364,18 @@ where match self.storage.destroy(handle) { Stored::New(node) => { - let encoded_root = node.into_encoded::<_, C, H>(|child, k| { - let combined = combine_encoded(nibbleslice::EMPTY_ENCODED, k); - self.commit_child(child, &combined) - }); + let mut k = NibbleVec::new(); + let encoded_root = 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 + } + ); + #[cfg(feature = "std")] trace!(target: "trie", "encoded root node: {:#x?}", &encoded_root[..]); - - *self.root = self.db.insert(nibbleslice::EMPTY_ENCODED, &encoded_root[..]); + *self.root = self.db.insert(EMPTY_PREFIX, &encoded_root[..]); self.hash_count += 1; self.root_handle = NodeHandle::Hash(*self.root); @@ -899,7 +1383,9 @@ where Stored::Cached(node, hash) => { // probably won't happen, but update the root and move on. *self.root = hash; - self.root_handle = NodeHandle::InMemory(self.storage.alloc(Stored::Cached(node, hash))); + self.root_handle = NodeHandle::InMemory( + self.storage.alloc(Stored::Cached(node, hash)), + ); } } } @@ -909,7 +1395,11 @@ where /// case where we can fit the actual data in the `Hasher`s output type, we /// store the data inline. This function is used as the callback to the /// `into_encoded` method of `Node`. - fn commit_child(&mut self, handle: NodeHandle, prefix: &NodeKey) -> ChildReference { + fn commit_child( + &mut self, + handle: NodeHandle>, + prefix: &mut NibbleVec, + ) -> ChildReference> { match handle { NodeHandle::Hash(hash) => ChildReference::Hash(hash), NodeHandle::InMemory(storage_handle) => { @@ -917,19 +1407,26 @@ where Stored::Cached(_, hash) => ChildReference::Hash(hash), Stored::New(node) => { let encoded = { - let commit_child = |node_handle, partial: &NodeKey| { - let combined = combine_encoded(&prefix, partial); - self.commit_child(node_handle, &combined) + let commit_child = | + node_handle, + o_slice: Option<&NibbleSlice>, + o_index: Option + | { + let mov = prefix.append_optional_slice_and_nibble(o_slice, o_index); + let cr = self.commit_child(node_handle, prefix); + prefix.drop_lasts(mov); + cr }; - node.into_encoded::<_, C, H>(commit_child) + node.into_encoded::<_, L::Codec, L::Hash>(commit_child) }; - if encoded.len() >= H::LENGTH { - let hash = self.db.insert(&prefix, &encoded[..]); + if encoded.len() >= L::Hash::LENGTH { + let hash = self.db.insert(prefix.as_prefix(), &encoded[..]); self.hash_count +=1; ChildReference::Hash(hash) } else { - // it's a small value, so we cram it into a `H::Out` and tag with length - let mut h = H::Out::default(); + // it's a small value, so we cram it into a `TrieHash` + // and tag with length + let mut h = >::default(); let len = encoded.len(); h.as_mut()[..len].copy_from_slice(&encoded[..len]); ChildReference::Inline(h, len) @@ -941,7 +1438,7 @@ where } // a hack to get the root node's handle - fn root_handle(&self) -> NodeHandle { + fn root_handle(&self) -> NodeHandle> { match self.root_handle { NodeHandle::Hash(h) => NodeHandle::Hash(h), NodeHandle::InMemory(StorageHandle(x)) => NodeHandle::InMemory(StorageHandle(x)), @@ -949,19 +1446,19 @@ where } } -impl<'a, H, C> TrieMut for TrieDBMut<'a, H, C> + +impl<'a, L> TrieMut for TrieDBMut<'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { - fn root(&mut self) -> &H::Out { + fn root(&mut self) -> &TrieHash { self.commit(); self.root } fn is_empty(&self) -> bool { match self.root_handle { - NodeHandle::Hash(h) => h == C::hashed_null_node(), + NodeHandle::Hash(h) => h == L::Codec::hashed_null_node(), NodeHandle::InMemory(ref h) => match self.storage[h] { Node::Empty => true, _ => false, @@ -969,49 +1466,58 @@ where } } - fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, H::Out, C::Error> + fn get<'x, 'key>(&'x self, key: &'key [u8]) -> Result, TrieHash, CError> where 'x: 'key { self.lookup(NibbleSlice::new(key), &self.root_handle) } - fn insert(&mut self, key: &[u8], value: &[u8]) -> Result, H::Out, C::Error> { + fn insert( + &mut self, + key: &[u8], + value: &[u8], + ) -> Result, TrieHash, CError> { if value.is_empty() { return self.remove(key) } let mut old_val = None; + #[cfg(feature = "std")] trace!(target: "trie", "insert: key={:#x?}, value={:#x?}", key, value); let root_handle = self.root_handle(); let (new_handle, changed) = self.insert_at( root_handle, - &mut Partial::new(NibbleSlice::new(key)), + &mut NibbleSlice::new(key), DBValue::from_slice(value), &mut old_val, )?; + #[cfg(feature = "std")] trace!(target: "trie", "insert: altered trie={}", changed); self.root_handle = NodeHandle::InMemory(new_handle); Ok(old_val) } - fn remove(&mut self, key: &[u8]) -> Result, H::Out, C::Error> { + fn remove(&mut self, key: &[u8]) -> Result, TrieHash, CError> { + #[cfg(feature = "std")] trace!(target: "trie", "remove: key={:#x?}", key); let root_handle = self.root_handle(); - let mut key = Partial::new(NibbleSlice::new(key)); + let mut key = NibbleSlice::new(key); let mut old_val = None; match self.remove_at(root_handle, &mut key, &mut old_val)? { Some((handle, changed)) => { + #[cfg(feature = "std")] trace!(target: "trie", "remove: altered trie={}", changed); self.root_handle = NodeHandle::InMemory(handle); } None => { + #[cfg(feature = "std")] trace!(target: "trie", "remove: obliterated trie"); - self.root_handle = NodeHandle::Hash(C::hashed_null_node()); - *self.root = C::hashed_null_node(); + self.root_handle = NodeHandle::Hash(L::Codec::hashed_null_node()); + *self.root = L::Codec::hashed_null_node(); } } @@ -1019,16 +1525,31 @@ where } } -impl<'a, H, C> Drop for TrieDBMut<'a, H, C> +impl<'a, L> Drop for TrieDBMut<'a, L> where - H: Hasher, - C: NodeCodec + L: TrieLayout, { fn drop(&mut self) { self.commit(); } } +/// combine two NodeKeys +fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { + debug_assert!(start.0 < nibble_ops::NIBBLE_PER_BYTE); + debug_assert!(end.0 < nibble_ops::NIBBLE_PER_BYTE); + let final_offset = (start.0 + end.0) % nibble_ops::NIBBLE_PER_BYTE; + let _shifted = nibble_ops::shift_key(start, final_offset); + let st = if end.0 > 0 { + let sl = start.1.len(); + start.1[sl - 1] |= nibble_ops::pad_right(end.1[0]); + 1 + } else { + 0 + }; + (st..end.1.len()).for_each(|i| start.1.push(end.1[i])); +} + #[cfg(test)] mod tests { use env_logger; @@ -1037,11 +1558,12 @@ mod tests { use memory_db::{MemoryDB, PrefixedKey}; use hash_db::{Hasher, HashDB}; use keccak_hasher::KeccakHasher; - use reference_trie::{RefTrieDBMut, TrieMut, NodeCodec, - ReferenceNodeCodec, ref_trie_root}; + use elastic_array::ElasticArray36; + use reference_trie::{RefTrieDBMutNoExt, RefTrieDBMut, TrieMut, NodeCodec, + ReferenceNodeCodec, reference_trie_root, reference_trie_root_no_extension}; fn populate_trie<'db>( - db: &'db mut HashDB, + db: &'db mut dyn HashDB, root: &'db mut ::Out, v: &[(Vec, Vec)] ) -> RefTrieDBMut<'db> { @@ -1061,6 +1583,31 @@ mod tests { } } + 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 { + >::hashed_null_node() + } + #[test] fn playpen() { env_logger::init(); @@ -1077,7 +1624,7 @@ mod tests { count: 100, }.make_with(&mut seed); - let real = ref_trie_root(x.clone()); + 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); @@ -1094,15 +1641,59 @@ mod tests { assert_eq!(*memtrie.root(), real); unpopulate_trie(&mut memtrie, &x); memtrie.commit(); - if *memtrie.root() != ReferenceNodeCodec::hashed_null_node() { + 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(); + if *memtrie.root() != hashed_null_node { println!("- TRIE MISMATCH"); println!(""); - println!("{:#x?} vs {:#x?}", memtrie.root(), ReferenceNodeCodec::hashed_null_node()); + println!("{:#x?} vs {:#x?}", memtrie.root(), hashed_null_node); for i in &x { println!("{:#x?} -> {:#x?}", i.0, i.1); } } - assert_eq!(*memtrie.root(), ReferenceNodeCodec::hashed_null_node()); + assert_eq!(*memtrie.root(), hashed_null_node); } } @@ -1111,7 +1702,8 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); - assert_eq!(*t.root(), ReferenceNodeCodec::hashed_null_node()); + let hashed_null_node = reference_hashed_null_node(); + assert_eq!(*t.root(), hashed_null_node); } #[test] @@ -1120,7 +1712,10 @@ mod tests { let mut root = Default::default(); let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ])); + assert_eq!( + *t.root(), + reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]) ]), + ); } #[test] @@ -1129,17 +1724,35 @@ mod tests { let mut memdb = MemoryDB::, DBValue>::default(); let mut root = Default::default(); - let mut t1 = RefTrieDBMut::new(&mut memdb, &mut root); - t1.insert(&[0x01, 0x23], big_value).unwrap(); - t1.insert(&[0x01, 0x34], big_value).unwrap(); - let mut memdb2 = MemoryDB::, DBValue>::default(); - let mut root2 = Default::default(); - let mut t2 = RefTrieDBMut::new(&mut memdb2, &mut root2); - - t2.insert(&[0x01], big_value).unwrap(); - t2.insert(&[0x01, 0x23], big_value).unwrap(); - t2.insert(&[0x01, 0x34], big_value).unwrap(); - t2.remove(&[0x01]).unwrap(); + let mut t = RefTrieDBMut::new(&mut memdb, &mut root); + + t.insert(&[0x01], big_value).unwrap(); + t.insert(&[0x01, 0x23], big_value).unwrap(); + t.insert(&[0x01, 0x34], big_value).unwrap(); + t.remove(&[0x01]).unwrap(); + } + + #[test] + fn remove_to_empty_no_extension() { + let big_value = b"00000000000000000000000000000000"; + let big_value2 = b"00000000000000000000000000000002"; + let big_value3 = b"00000000000000000000000000000004"; + + let mut memdb = MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut root = Default::default(); + { + let mut t = RefTrieDBMutNoExt::new(&mut memdb, &mut root); + + t.insert(&[0x01, 0x23], big_value3).unwrap(); + t.insert(&[0x01], big_value2).unwrap(); + t.insert(&[0x01, 0x34], big_value).unwrap(); + t.remove(&[0x01]).unwrap(); + // commit on drop + } + assert_eq!(&root[..], &reference_trie::calc_root_no_extension(vec![ + (vec![0x01u8, 0x23], big_value3.to_vec()), + (vec![0x01u8, 0x34], big_value.to_vec()), + ])[..]); } #[test] @@ -1149,7 +1762,10 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x23], &[0x23u8, 0x45]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ])); + assert_eq!( + *t.root(), + reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x23u8, 0x45]) ]), + ); } #[test] @@ -1159,7 +1775,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x11u8, 0x23], &[0x11u8, 0x23]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), (vec![0x11u8, 0x23], vec![0x11u8, 0x23]) ])); @@ -1173,7 +1789,7 @@ mod tests { 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(), ref_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]), @@ -1187,7 +1803,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[], &[0x0]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![], vec![0x0]), (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), ])); @@ -1200,7 +1816,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], &[0x01u8, 0x23]).unwrap(); t.insert(&[0x01u8, 0x34], &[0x01u8, 0x34]).unwrap(); - assert_eq!(*t.root(), ref_trie_root(vec![ + assert_eq!(*t.root(), reference_trie_root(vec![ (vec![0x01u8, 0x23], vec![0x01u8, 0x23]), (vec![0x01u8, 0x34], vec![0x01u8, 0x34]), ])); @@ -1214,7 +1830,7 @@ mod tests { 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(), ref_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]), @@ -1231,7 +1847,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], big_value0).unwrap(); t.insert(&[0x11u8, 0x23], big_value1).unwrap(); - assert_eq!(*t.root(), ref_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()) ])); @@ -1246,7 +1862,7 @@ mod tests { let mut t = RefTrieDBMut::new(&mut memdb, &mut root); t.insert(&[0x01u8, 0x23], big_value).unwrap(); t.insert(&[0x11u8, 0x23], big_value).unwrap(); - assert_eq!(*t.root(), ref_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()) ])); @@ -1302,7 +1918,7 @@ mod tests { count: 4, }.make_with(&mut seed); - let real = ref_trie_root(x.clone()); + 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); @@ -1360,14 +1976,15 @@ mod tests { t.insert(key, value).unwrap(); } - assert_eq!(*t.root(), ref_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()); - assert_eq!(*t.root(), ReferenceNodeCodec::hashed_null_node()); + let hashed_null_node = reference_hashed_null_node(); + assert_eq!(*t.root(), hashed_null_node); } #[test] @@ -1378,7 +1995,7 @@ mod tests { min_key: 5, journal_key: 0, value_mode: ValueMode::Index, - count: 4, + count: 2, }.make_with(&mut seed); let mut db = MemoryDB::, DBValue>::default(); @@ -1388,10 +2005,25 @@ mod tests { assert!(t.insert(key, value).unwrap().is_none()); assert_eq!(t.insert(key, value).unwrap(), Some(DBValue::from_slice(value))); } - for (key, value) in x { assert_eq!(t.remove(&key).unwrap(), Some(DBValue::from_slice(&value))); assert!(t.remove(&key).unwrap().is_none()); } } + + #[test] + fn combine_test() { + let a: ElasticArray36 = [0x12, 0x34][..].into(); + let b: &[u8] = [0x56, 0x78][..].into(); + let test_comb = |a: (_, &ElasticArray36<_>), b, c| { + let mut a = (a.0, a.1.clone()); + super::combine_key(&mut a, b); + assert_eq!((a.0, &a.1[..]), c); + }; + test_comb((0, &a), (0, &b), (0, &[0x12, 0x34, 0x56, 0x78][..])); + test_comb((1, &a), (0, &b), (1, &[0x12, 0x34, 0x56, 0x78][..])); + test_comb((0, &a), (1, &b), (1, &[0x01, 0x23, 0x46, 0x78][..])); + test_comb((1, &a), (1, &b), (0, &[0x23, 0x46, 0x78][..])); + } + } diff --git a/trie-root/Cargo.toml b/trie-root/Cargo.toml index 069a2342..517d942e 100644 --- a/trie-root/Cargo.toml +++ b/trie-root/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "trie-root" -version = "0.14.0" +version = "0.15.0" authors = ["Parity Technologies "] description = "In-memory patricia trie operations" repository = "https://github.com/paritytech/trie" @@ -8,14 +8,14 @@ license = "Apache-2.0" categories = [ "no-std" ] [dependencies] -hash-db = { path = "../hash-db", default-features = false, version = "0.14.0"} +hash-db = { path = "../hash-db", default-features = false, version = "0.15.0"} [dev-dependencies] hex-literal = "0.1" -keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.14.0" } -trie-standardmap = { path = "../test-support/trie-standardmap", version = "0.14.0" } +keccak-hasher = { path = "../test-support/keccak-hasher", version = "0.15.0" } +trie-standardmap = { path = "../test-support/trie-standardmap", version = "0.15.0" } # DISABLE the following line when publishing until cyclic dependencies are resolved https://github.com/rust-lang/cargo/issues/4242 -reference-trie = { path = "../test-support/reference-trie", version = "0.14.0" } +reference-trie = { path = "../test-support/reference-trie", version = "0.15.0" } [features] default = ["std"] diff --git a/trie-root/src/lib.rs b/trie-root/src/lib.rs index 5810c280..91fecda9 100644 --- a/trie-root/src/lib.rs +++ b/trie-root/src/lib.rs @@ -17,8 +17,6 @@ //! This module should be used to generate trie root hash. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] -#![cfg_attr(not(feature = "std"), feature(alloc))] extern crate hash_db; @@ -47,8 +45,13 @@ pub trait TrieStream { fn append_empty_data(&mut self); /// Start a new Branch node, possibly with a value; takes a list indicating /// which slots in the Branch node has further child nodes. - fn begin_branch(&mut self, maybe_value: Option<&[u8]>, has_children: impl Iterator); - // Append an empty child node. Optional. + fn begin_branch( + &mut self, + maybe_key: Option<&[u8]>, + maybe_value: Option<&[u8]>, + has_children: impl Iterator, + ); + /// Append an empty child node. Optional. fn append_empty_child(&mut self) {} /// Wrap up a Branch node portion of a `TrieStream` and append the value /// stored on the Branch (if any). @@ -63,7 +66,7 @@ pub trait TrieStream { fn out(self) -> Vec; } -fn shared_prefix_len(first: &[T], second: &[T]) -> usize { +fn shared_prefix_length(first: &[T], second: &[T]) -> usize { first.iter() .zip(second.iter()) .position(|(f, s)| f != s) @@ -98,6 +101,16 @@ pub fn trie_root(input: I) -> H::Out where B: AsRef<[u8]>, H: Hasher, S: TrieStream, +{ + trie_root_inner::(input, false) +} + +fn trie_root_inner(input: I, no_extension: bool) -> H::Out where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + S: TrieStream, { // first put elements into btree to sort them and to remove duplicates let input = input @@ -122,17 +135,42 @@ pub fn trie_root(input: I) -> H::Out where .collect::>(); let mut stream = S::new(); - build_trie::(&input, 0, &mut stream); + build_trie::(&input, 0, &mut stream, no_extension); H::hash(&stream.out()) } +/// Variant of `trie_root` for patricia trie without extension node. +/// See [`trie_root`]. +pub fn trie_root_no_extension(input: I) -> H::Out where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + S: TrieStream, +{ + trie_root_inner::(input, true) +} + //#[cfg(test)] // consider feature="std" +/// Method similar to `trie_root` but returning the root encoded +/// node instead of its hash. +/// Mainly use for testing or debugging. pub fn unhashed_trie(input: I) -> Vec where I: IntoIterator, A: AsRef<[u8]> + Ord, B: AsRef<[u8]>, H: Hasher, S: TrieStream, +{ + unhashed_trie_inner::(input, false) +} + +fn unhashed_trie_inner(input: I, no_extension: bool) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + S: TrieStream, { // first put elements into btree to sort them and to remove duplicates let input = input @@ -156,10 +194,22 @@ pub fn unhashed_trie(input: I) -> Vec where .collect::>(); let mut stream = S::new(); - build_trie::(&input, 0, &mut stream); + build_trie::(&input, 0, &mut stream, no_extension); stream.out() } +/// Variant of `unhashed_trie` for patricia trie without extension node. +/// See [`unhashed_trie`]. +pub fn unhashed_trie_no_extension(input: I) -> Vec where + I: IntoIterator, + A: AsRef<[u8]> + Ord, + B: AsRef<[u8]>, + H: Hasher, + S: TrieStream, +{ + unhashed_trie_inner::(input, true) +} + /// Generates a key-hashed (secure) trie root hash for a vector of key-value tuples. /// /// ```rust @@ -195,8 +245,7 @@ pub fn sec_trie_root(input: I) -> H::Out where /// Takes a slice of key/value tuples where the key is a slice of nibbles /// and encodes it into the provided `Stream`. -// pub fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) -fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where +fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S, no_extension: bool) where A: AsRef<[u8]>, B: AsRef<[u8]>, H: Hasher, @@ -215,17 +264,28 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where // shared with the first key. // e.g. input = [ [1'7'3'10'12'13], [1'7'3'], [1'7'7'8'9'] ] => [1'7'] is common => 2 let shared_nibble_count = input.iter().skip(1).fold(key.len(), |acc, &(ref k, _)| { - cmp::min( shared_prefix_len(key, k.as_ref()), acc ) + cmp::min( shared_prefix_length(key, k.as_ref()), acc ) }); // Add an extension node if the number of shared nibbles is greater // than what we saw on the last call (`cursor`): append the new part // of the path then recursively append the remainder of all items // who had this partial key. - if shared_nibble_count > cursor { + let (cursor, o_branch_slice) = if no_extension { + if shared_nibble_count > cursor { + (shared_nibble_count, Some(&key[cursor..shared_nibble_count])) + } else { + (cursor, Some(&key[0..0])) + } + } else if shared_nibble_count > cursor { stream.append_extension(&key[cursor..shared_nibble_count]); - build_trie_trampoline::(input, shared_nibble_count, stream); + build_trie_trampoline::( + input, + shared_nibble_count, + stream, + no_extension, + ); return; - } + } else { (cursor, None) }; // We'll be adding a branch node because the path is as long as it gets. // First we need to figure out what entries this branch node will have... @@ -251,14 +311,19 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where } // Put out the node header: - stream.begin_branch(value, shared_nibble_counts.iter().map(|&n| n > 0)); + stream.begin_branch(o_branch_slice, value, shared_nibble_counts.iter().map(|&n| n > 0)); - // Fill in each slot in the branch node. We don't need to bother with empty slots since they - // were registered in the header. + // Fill in each slot in the branch node. We don't need to bother with empty slots + // since they were registered in the header. let mut begin = match value { None => 0, _ => 1 }; for &count in &shared_nibble_counts { if count > 0 { - build_trie_trampoline::(&input[begin..(begin + count)], cursor + 1, stream); + build_trie_trampoline::( + &input[begin..(begin + count)], + cursor + 1, + stream, + no_extension, + ); begin += count; } else { stream.append_empty_child(); @@ -270,13 +335,18 @@ fn build_trie(input: &[(A, B)], cursor: usize, stream: &mut S) where } } -fn build_trie_trampoline(input: &[(A, B)], cursor: usize, stream: &mut S) where +fn build_trie_trampoline( + input: &[(A, B)], + cursor: usize, + stream: &mut S, + no_extension: bool, +) where A: AsRef<[u8]>, B: AsRef<[u8]>, H: Hasher, S: TrieStream, { let mut substream = S::new(); - build_trie::(input, cursor, &mut substream); + build_trie::(input, cursor, &mut substream, no_extension); stream.append_substream::(substream); }