diff --git a/memory-db/src/lib.rs b/memory-db/src/lib.rs index 31b97f51..4f2dffc5 100644 --- a/memory-db/src/lib.rs +++ b/memory-db/src/lib.rs @@ -48,6 +48,7 @@ use core::{ mem, marker::PhantomData, cmp::Eq, + cmp, borrow::Borrow, }; @@ -157,8 +158,13 @@ impl PartialEq> for MemoryDB M: MemTracker + PartialEq, { fn eq(&self, other: &MemoryDB) -> bool { - for a in self.data.iter() { - match other.data.get(&a.0) { + let (s, other) = if self.data.len() > other.data.len() { + (&self.data, &other.data) + } else { + (&other.data, &self.data) + }; + for a in s.iter() { + match other.get(&a.0) { Some(v) if v != a.1 => return false, None => return false, _ => (), @@ -178,7 +184,7 @@ impl Eq for MemoryDB {} pub trait KeyFunction { - type Key: Send + Sync + Clone + hash::Hash + Eq; + type Key: Send + Sync + Clone + hash::Hash + Eq + AsRef<[u8]>; fn key(hash: &H::Out, prefix: Prefix) -> Self::Key; } diff --git a/test-support/reference-trie/src/lib.rs b/test-support/reference-trie/src/lib.rs index 37a5f914..41ab25f6 100644 --- a/test-support/reference-trie/src/lib.rs +++ b/test-support/reference-trie/src/lib.rs @@ -29,9 +29,11 @@ use trie_db::{ TrieBuilder, TrieRoot, Partial, + OwnedPrefix, }; use std::borrow::Borrow; use keccak_hasher::KeccakHasher; +pub use trie_db::traverse::{batch_update, InputAction, unprefixed_detached_trie, prefixed_detached_trie}; use trie_db::{ nibble_ops, NodeCodec, @@ -1200,6 +1202,31 @@ pub fn compare_no_extension_insert_remove( assert_eq!(*t.root(), calc_root_no_extension(data2)); } +pub fn trie_traverse_key_no_extension_build<'a, I, K, V, B>( + // TODO db to non mutable + db: &'a mut dyn hash_db::HashDBRef, + root: &'a ::Out, + elements: I, +) -> ( + ::Out, + impl Iterator::Out, Option>)>, + impl Iterator, OwnedPrefix, ::Out)>, +) + where + I: IntoIterator)>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, +{ + let elements = elements.into_iter().map(|(k, v)| (k, if let Some(v) = v { + InputAction::Insert(v) + } else { + InputAction::Delete + })); + let (root, values, values_detached, detached_root) = batch_update::(db, root, elements).unwrap(); + (root, values.into_iter().chain(values_detached.into_iter()), detached_root.into_iter()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/trie-db/fuzz/Cargo.toml b/trie-db/fuzz/Cargo.toml index 9a831179..d208c9be 100644 --- a/trie-db/fuzz/Cargo.toml +++ b/trie-db/fuzz/Cargo.toml @@ -64,3 +64,11 @@ path = "fuzz_targets/trie_proof_valid.rs" [[bin]] name = "trie_proof_invalid" path = "fuzz_targets/trie_proof_invalid.rs" + +[[bin]] +name = "batch_update" +path = "fuzz_targets/batch_update.rs" + +[[bin]] +name = "detach_attach" +path = "fuzz_targets/detach_attach.rs" diff --git a/trie-db/fuzz/fuzz_targets/batch_update.rs b/trie-db/fuzz/fuzz_targets/batch_update.rs new file mode 100644 index 00000000..0f5ef521 --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/batch_update.rs @@ -0,0 +1,10 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // use next line to favor complex structure and inline data + trie_db_fuzz::fuzz_batch_update(data, |_v| (), false); + // use next line to favor db prefix verification + //trie_db_fuzz::fuzz_batch_update(data, |v| v.extend(&[4u8; 32]), true); +}); diff --git a/trie-db/fuzz/fuzz_targets/detach_attach.rs b/trie-db/fuzz/fuzz_targets/detach_attach.rs new file mode 100644 index 00000000..6affac7f --- /dev/null +++ b/trie-db/fuzz/fuzz_targets/detach_attach.rs @@ -0,0 +1,10 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + // use next line to favor complex structure and inline data + //trie_db_fuzz::fuzz_detach_attach(data, |_v| (), false); + // use next line to favor db prefix verification + trie_db_fuzz::fuzz_detach_attach(data, |v| v.extend(&[4u8; 32]), true); +}); diff --git a/trie-db/fuzz/src/lib.rs b/trie-db/fuzz/src/lib.rs index 4b9d0efc..e1d97852 100644 --- a/trie-db/fuzz/src/lib.rs +++ b/trie-db/fuzz/src/lib.rs @@ -20,7 +20,7 @@ use reference_trie::{ calc_root_no_extension, compare_no_extension_insert_remove, ExtensionLayout, - NoExtensionLayout, + NoExtensionLayout, batch_update, InputAction, proof::{generate_proof, verify_proof}, reference_trie_root, RefTrieDBMut, @@ -72,7 +72,7 @@ fn fuzz_to_data(input: &[u8]) -> Vec<(Vec,Vec)> { result } -fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec,Vec)> { +fn fuzz_removal(data: Vec<(Vec,Vec)>) -> Vec<(bool, Vec, Vec)> { let mut res = Vec::new(); let mut torem = None; for (a, d) in data.into_iter().enumerate() { @@ -363,3 +363,183 @@ fn test_generate_proof( (root, proof, items) } + +pub fn fuzz_batch_update(input: &[u8], build_val: fn(&mut Vec), compare_db: bool) { + let data = fuzz_to_data(input); + let mut data = fuzz_removal(data); + for i in data.iter_mut() { + build_val(&mut i.2); + } + let data = data; +//println!("{}: {:?}", data.len(), data); + let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut root = Default::default(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 0..data.len() / 2 { + let key: &[u8]= &data[i].1; + let val: &[u8] = &data[i].2; + t.insert(key, val).unwrap(); + } + } + + let initial_root = root.clone(); + let mut initial_db = db.clone(); + + let mut sorted_data = std::collections::BTreeMap::new(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::from_existing(&mut db, &mut root) + .unwrap(); + for i in data.len() / 2..data.len() { + let key: &[u8]= &data[i].1; + let val: &[u8] = &data[i].2; + if !data[i].0 { + sorted_data.insert(key, Some(val)); + t.insert(key, val).unwrap(); + } else { + sorted_data.insert(key, None); + // remove overwrite insert from fuzz_removal ordering, + // that is important + t.remove(key).unwrap(); + } + } + } +//println!("{:?}", sorted_data); + let (calc_root, payload, _detached) = reference_trie::trie_traverse_key_no_extension_build( + &mut initial_db, + &initial_root, + sorted_data.into_iter(), + ); + assert!(calc_root == root); + + if compare_db { + for (p, h, v) in payload { + use hash_db::HashDB; + if let Some(v) = v { + let prefix = (p.0.as_ref(), p.1); + initial_db.emplace(h, prefix, v[..].into()); + } else { + let prefix = (p.0.as_ref(), p.1); + initial_db.remove(&h, prefix); + } + } + + assert!(initial_db == db); + } +} + +pub fn fuzz_detach_attach(input: &[u8], build_val: fn(&mut Vec), compare_db: bool) { + //x: &[(Vec, Vec)], + //d: &Vec, + let mut data = fuzz_to_data(input); + if data.len() == 0 { + return; + } + for i in data.iter_mut() { + build_val(&mut i.1); + } + let x = &data[1..]; + let d = &data[0].0; + //println!("{:?}\n d {:?}", x, d); + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 1..x.len() { + let key: &[u8]= &x[i].0; + let val: &[u8] = &x[i].1; + t.insert(key, val).unwrap(); + } + } + let initial_root = root.clone(); + let initial_db = db.clone(); + // reference + { + let mut t = RefTrieDBMutNoExt::from_existing(&mut db, &mut root).unwrap(); + for i in 1..x.len() { + if x[i].0.starts_with(d) { + let key: &[u8]= &x[i].0; + t.remove(key).unwrap(); + } + } + } + let elements = Some(d.clone()).into_iter().map(|k| (k, InputAction::, _>::Detach)); + let (calc_root, payload, payload_det, detached_root) = batch_update::( + &initial_db, + &initial_root, + elements, + ).unwrap(); + + assert_eq!(calc_root, root); + + let mut batch_delta = initial_db.clone(); + for (p, h, v) in payload.into_iter().chain(payload_det) { + use hash_db::HashDB; + if let Some(v) = v { + let prefix = (p.0.as_ref(), p.1); + batch_delta.emplace(h, prefix, v[..].into()); + } else { + let prefix = (p.0.as_ref(), p.1); + batch_delta.remove(&h, prefix); + } + } + + // attach back + let elements = detached_root.into_iter().map(|(k, _prefix, root)| (k, InputAction::, _>::Attach(root))); + let (calc_root, payload, payload_det, detached_root) = batch_update::( + &batch_delta, + &calc_root, + elements, + ).unwrap(); + if detached_root.is_empty() { + if compare_db { + for (p, h, v) in payload.into_iter().chain(payload_det) { + use hash_db::HashDB; + if let Some(v) = v { + let prefix = (p.0.as_ref(), p.1); + batch_delta.emplace(h, prefix, v[..].into()); + } else { + let prefix = (p.0.as_ref(), p.1); + batch_delta.remove(&h, prefix); + } + } + batch_delta.purge(); + assert!(initial_db == batch_delta); + } + assert!(calc_root == initial_root); + } else { + // case where there was a node fuse due to dettach + // we could inject manually detached node and compare + } +} + + +#[test] +fn test() { + let tests = [ + vec![0x1,0x0,0x0,0x0,0x8,0xc,0x8,0x8,0x8,0x0,0x3d,0x0,0x9d,0x4,0x4e], + vec![0x1,0x0,0x0,0x0,0x8,0x8,0x8,0x8,0x8,0x0,0x80,0x0,0x9d,0x4,0x4e], + vec![0x0,0x80,0xd4,0xd4,0xd4,0xd4,0x3a,0x3a,0x3f,0x0,0x3f,0x0], + vec![0x0,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x1,0x8d,0x2c,0xd4,0x0,0x0,0x33,0x8a,0x20,0x80,0x9a,0x2c,0xd4,0x0,0x0,0x33,0x8a,0xff,0x8], + vec![0xff,0xd1,0x0,0x90,0x40,0xd4,0x8d,0x1,0x0,0x0,0xff,0x90,0x40,0xd4,0x8d,0x1,0x0,0x8d,0xce,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0xd0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x6,0x8,0x15,0x1,0x4,0x0,0x8d,0x87,0xcf,0x0,0x3f,0xcb,0xd8,0xb9,0xa2,0x4d,0x9a,0xd6,0xd2,0x0,0x0,0x0,0x0,0x80,0x0,0x6,0x8,0x15,0x1,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23,0xc7,0xd7,0xd7,0xfb,0x7f,0x83,0x3,0x37,0x37,0x37,0xb2,0xa8,0xb,0xf5,0x5a,0x50,0xb6,0x0,0xff,0x17,0x21,0x0], + vec![0x43,0x19,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x0,0x6,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0xc7,0x8d,0x0,0x5b,0x2d,0xbd,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xaa,0x0,0x0,0x49,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc,0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x40,0x2,0x4,0x0,0x0,0xc7,0x8d,0x0,0x0,0xe0], + vec![0x7f,0x2b,0x4,0x3a,0x89,0xfb,0x0,0x2e,0x70,0x0,0x0,0x2e,0x2,0x0,0x0,0x0,0x41,0xd1,0x2e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0xff,0xcd,0x72,0xfb,0x7f,0xc3,0x0,0x9a,0xff,0xff,0xff,0x72,0xfb,0x7f,0xff,0x0,0x0,0x0,0x0,0x0,0x82,0x82,0x81,0x81,0x29,0x8f,0x7d,0x42,0x12,0xf7,0xb4,0x77,0xd6,0x65,0x91,0xff,0x96,0xa9,0xe0,0x64,0xbc,0xc9,0x8a,0x70,0xc,0x4,0x0,0x0,0xc3,0x0,0x0,0x0,0x0,0x0,0x0], + vec![0x0,0x84,0xff,0xfb,0x7f,0xff,0xff,0xff,0xff,0x7f,0x70,0xff,0xff,0x7f,0x72,0xfd,0xc3,0x0,0x4,0xfb,0xff,0x10,0x10,0x10,0x10,0x10,0x9a,0x4], + vec![0x0,0x80,0xd4,0x0,0x1,0xc,0x0,0xc,0xd,0x2,0x9,0xd4,0xd4,0x0,0x8,0x8,0x8,0x1,0x0,0x0,0x0,0x0], + vec![0x0,0x80,0x0,0xd1,0x0,0x0,0x9a,0x4,0x4,0x0,0x0,0xc], + vec![0x0,0x0,0xff,0xff,0x0,0x0,0x4,0x8d,0x87,0xd1], + vec![0x0,0x0,0x0,0x0,0x4,0x0,0xff,0xd1,0x0,0x0,0xfe,0x0], + vec![0x0,0x0,0xfe,0x0,0xff,0xff,0xd1,0xd1,0x27,0x0], + vec![0x0,0xfe,0x41,0x0,0x0,0x80,0x0,0x0,0xff], + vec![0x0,0x0,0x0,0x4,0x20,0x1a,0x0,0x0], + vec![0x0,0x0,0x0,0x0,0xfe,0xff,0xff,0xd1,0x27], + vec![0x0,0x0,0x0,0xb7,0x4,0xf8,0x0,0x0,0x0], + vec![0xa,0x0,0x0,0x0,0x0,0x4,0x0,0x0], + vec![0x0,0x0,0x0,0x0,0x8d,0x4], + vec![0x0,0x0,0x4,0x8d,0x8d,0x4], + ]; + for v in tests.iter() { + fuzz_batch_update(&v[..], |_v| (), false); + fuzz_batch_update(&v[..], |v| v.extend(&[4u8; 32]), true); + } +} diff --git a/trie-db/src/iter_build.rs b/trie-db/src/iter_build.rs index 1f5f4747..4afbceac 100644 --- a/trie-db/src/iter_build.rs +++ b/trie-db/src/iter_build.rs @@ -200,7 +200,7 @@ impl CacheAccum v.as_ref().map(|v| v.as_ref()), ); self.reset_depth(branch_d); - let pr = NibbleSlice::new_offset(&key_branch, branch_d); + let pr = NibbleSlice::new_offset(key_branch, branch_d); let branch_hash = callback.process(pr.left(), encoded, is_root && nkey.is_none()); if let Some(nkeyix) = nkey { diff --git a/trie-db/src/lib.rs b/trie-db/src/lib.rs index 37bfe64e..44ce9726 100644 --- a/trie-db/src/lib.rs +++ b/trie-db/src/lib.rs @@ -51,6 +51,7 @@ pub mod recorder; mod fatdb; mod fatdbmut; mod iter_build; +pub mod traverse; mod iterator; mod lookup; mod nibble; @@ -66,7 +67,7 @@ pub use self::fatdb::{FatDB, FatDBIterator}; pub use self::fatdbmut::FatDBMut; pub use self::recorder::{Recorder, Record}; pub use self::lookup::Lookup; -pub use self::nibble::{NibbleSlice, NibbleVec, nibble_ops}; +pub use self::nibble::{NibbleSlice, NibbleVec, nibble_ops, OwnedPrefix}; pub use crate::node_codec::{NodeCodec, Partial}; pub use crate::iter_build::{trie_visit, ProcessEncodedNode, TrieBuilder, TrieRoot, TrieRootUnhashed}; @@ -128,7 +129,6 @@ impl Error for TrieError where T: fmt::Debug, E: Error {} /// Boxed to avoid copying around extra space for the `Hasher`s `Out` on successful queries. pub type Result = crate::rstd::result::Result>>; - /// Trie-Item type used for iterators over trie data. pub type TrieItem<'a, U, E> = Result<(Vec, DBValue), U, E>; @@ -450,3 +450,8 @@ pub trait TrieConfiguration: Sized + TrieLayout { pub type TrieHash = <::Hash as Hasher>::Out; /// Alias accessor to `NodeCodec` associated `Error` type from a `TrieLayout`. pub type CError = <::Codec as NodeCodec>::Error; + +/// This was only implemented for trie without extension, it could +/// be implemented for trie and extension in the future but is not +/// at this point. +const NO_EXTENSION_ONLY: &str = "trie without extension implemented only"; diff --git a/trie-db/src/nibble/leftnibbleslice.rs b/trie-db/src/nibble/leftnibbleslice.rs index c31301be..e7243efc 100644 --- a/trie-db/src/nibble/leftnibbleslice.rs +++ b/trie-db/src/nibble/leftnibbleslice.rs @@ -226,4 +226,4 @@ mod tests { Ordering::Equal ); } -} \ No newline at end of file +} diff --git a/trie-db/src/nibble/mod.rs b/trie-db/src/nibble/mod.rs index 2d1b3df5..9022a710 100644 --- a/trie-db/src/nibble/mod.rs +++ b/trie-db/src/nibble/mod.rs @@ -151,6 +151,9 @@ pub mod nibble_ops { /// Backing storage for `NibbleVec`s. pub(crate) type BackingByteVec = smallvec::SmallVec<[u8; 36]>; +/// Backing storage for `Prefix`. +pub type OwnedPrefix = (BackingByteVec, Option); + /// Owning, nibble-oriented byte vector. Counterpart to `NibbleSlice`. /// Nibbles are always left aligned, so making a `NibbleVec` from /// a `NibbleSlice` can get costy. diff --git a/trie-db/src/nibble/nibbleslice.rs b/trie-db/src/nibble/nibbleslice.rs index 3a9a3910..4f90471b 100644 --- a/trie-db/src/nibble/nibbleslice.rs +++ b/trie-db/src/nibble/nibbleslice.rs @@ -211,6 +211,11 @@ impl<'a> NibbleSlice<'a> { }) } + /// Return nibble as a tuple representation. + pub fn right_ref(self) -> (usize, &'a [u8]) { + (self.offset, self.data) + } + /// Return left portion of `NibbleSlice`, if the slice /// originates from a full key it will be the `Prefix of /// the node`. @@ -224,6 +229,13 @@ impl<'a> NibbleSlice<'a> { } } + /// Return length of left portion of `NibbleSlice`, if the slice + /// originates from a full key it will be the length of th `Prefix of + /// the node`. + pub fn left_len(&'a self) -> usize { + self.offset + } + /// Owned version of a `Prefix` from a `left` method call. pub fn left_owned(&'a self) -> (BackingByteVec, Option) { let (a, b) = self.left(); @@ -233,7 +245,9 @@ impl<'a> NibbleSlice<'a> { impl<'a> Into for NibbleSlice<'a> { fn into(self) -> NodeKey { - (self.offset, self.data.into()) + let new_offset = self.offset % nibble_ops::NIBBLE_PER_BYTE; + let start = self.offset / nibble_ops::NIBBLE_PER_BYTE; + (new_offset, self.data[start..].into()) } } diff --git a/trie-db/src/nibble/nibblevec.rs b/trie-db/src/nibble/nibblevec.rs index fcbc1b46..ac58a2e2 100644 --- a/trie-db/src/nibble/nibblevec.rs +++ b/trie-db/src/nibble/nibblevec.rs @@ -35,6 +35,20 @@ impl NibbleVec { } } + /// Make a new `NibbleVec`. + /// TODO replace by From LeftNibbleSlice?? + pub fn from(d: &[u8], l: usize) -> Self { + let mut v = Self::default(); + let ix = l / nibble_ops::NIBBLE_PER_BYTE; + let pad = l % nibble_ops::NIBBLE_PER_BYTE; + (0..ix).for_each(|i| v.inner.push(d[i])); + if pad == 1 { + v.inner.push(nibble_ops::pad_left(d[ix])); + } + v.len = l; + v + } + /// Length of the `NibbleVec`. #[inline(always)] pub fn len(&self) -> usize { self.len } diff --git a/trie-db/src/node.rs b/trie-db/src/node.rs index ef50e8d6..7a112b51 100644 --- a/trie-db/src/node.rs +++ b/trie-db/src/node.rs @@ -13,11 +13,19 @@ // limitations under the License. use hash_db::Hasher; +use crate::NO_EXTENSION_ONLY; use crate::nibble::{self, NibbleSlice}; use crate::nibble::nibble_ops; use crate::node_codec::NodeCodec; +use crate::rstd::{borrow::Borrow, ops::Range, boxed::Box, vec::Vec}; -use crate::rstd::{borrow::Borrow, ops::Range}; + +/// Owned handle to a node, to use when there is no caching. +pub type StorageHandle = Vec; + +type TNode = crate::triedbmut::NodeMut; + +type TNodeHandle = crate::triedbmut::NodeHandleMut; /// Partial node key type: offset and owned value of a nibbleslice. /// Offset is applied on first byte of array (bytes are right aligned). @@ -75,6 +83,17 @@ impl NodeHandlePlan { NodeHandlePlan::Inline(range) => NodeHandle::Inline(&data[range.clone()]), } } + + fn build_owned_handle + Default>(&self, data: &[u8]) -> TNodeHandle { + match self { + NodeHandlePlan::Hash(range) => { + let mut hash = H::default(); + hash.as_mut().copy_from_slice(&data[range.clone()]); + TNodeHandle::Hash(hash) + }, + NodeHandlePlan::Inline(range) => TNodeHandle::InMemory((&data[range.clone()]).into()), + } + } } /// A `NibbleSlicePlan` is a blueprint for decoding a nibble slice from a byte slice. The @@ -177,7 +196,7 @@ impl NodePlan { /// An `OwnedNode` is an owned type from which a `Node` can be constructed which borrows data from /// the `OwnedNode`. This is useful for trie iterators. #[cfg_attr(feature = "std", derive(Debug))] -#[derive(PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct OwnedNode> { data: D, plan: NodePlan, @@ -204,4 +223,279 @@ impl> OwnedNode { pub fn node(&self) -> Node { self.plan.build(self.data.borrow()) } + + /// Get extension part of the node (partial) if any. + pub fn partial(&self) -> Option { + match &self.plan { + NodePlan::Branch { .. } + | NodePlan::Empty => None, + NodePlan::Leaf { partial, .. } + | NodePlan::NibbledBranch { partial, .. } + | NodePlan::Extension { partial, .. } => + Some(partial.build(self.data.borrow())), + } + } + + /// Tell if there is a value defined at this node position. + pub fn has_value(&self) -> bool { + match &self.plan { + NodePlan::Extension { .. } + | NodePlan::Empty => false, + NodePlan::Leaf { .. } => true, + NodePlan::Branch { value, .. } + | NodePlan::NibbledBranch { value, .. } => value.is_some(), + } + } + + + /// Get value part of the node (partial) if any. + pub fn value(&self) -> Option { + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Branch { .. } + | NodePlan::Extension { .. } + | NodePlan::Empty => None, + NodePlan::Leaf { value, .. } + => Some(data[value.clone()].into()), + | NodePlan::NibbledBranch { value, .. } + => value.as_ref().map(|v| data[v.clone()].into()), + } + } + + /// Try to access child. + pub fn child(&self, ix: u8) -> Option { + match &self.plan { + NodePlan::Leaf { .. } + | NodePlan::Extension { .. } + | NodePlan::Empty => None, + NodePlan::NibbledBranch { children, .. } + | NodePlan::Branch { children, .. } => + children[ix as usize].as_ref().map(|child| child.build(self.data.borrow())), + } + } + + /// Tell if it is the empty node. + pub fn is_empty(&self) -> bool { + if let NodePlan::Empty = &self.plan { + true + } else { + false + } + } + + /// Return number of children for this node. + pub fn number_child(&self) -> usize { + match &self.plan { + NodePlan::Leaf { .. } + | NodePlan::Empty => 0, + NodePlan::Extension { .. } => 1, + NodePlan::NibbledBranch { children, .. } + | NodePlan::Branch { children, .. } => children.len(), + } + } +} + +impl> OwnedNode { + + + fn init_child_slice + Default>( + data: &[u8], + children: &[Option; nibble_ops::NIBBLE_LENGTH], + skip_index: Option, + ) -> Box<[Option>; 16]> { + let mut child_slices = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + if let Some(skip) = skip_index { + for i in 0..nibble_ops::NIBBLE_LENGTH { + if i != skip { + child_slices[i] = children[i].as_ref().map(|child| child.build_owned_handle(data)); + } + } + } else { + for i in 0..nibble_ops::NIBBLE_LENGTH { + child_slices[i] = children[i].as_ref().map(|child| child.build_owned_handle(data)); + } + } + child_slices + } + + /// Remove n first byte from the existing partial, return updated node if updated. + pub(crate) fn advance_partial + Default>(&mut self, nb: usize) -> Option> { + if nb == 0 { + return None; + } + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Leaf { partial, value } => { + let mut partial = partial.build(data); + partial.advance(nb); + Some(TNode::Leaf( + partial.into(), + data[value.clone()].into(), + )) + }, + NodePlan::Extension { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), + NodePlan::Branch { .. } + | NodePlan::Empty => None, + NodePlan::NibbledBranch { partial, value, children } => { + let mut partial = partial.build(data); + partial.advance(nb); + + Some(TNode::NibbledBranch( + partial.into(), + Self::init_child_slice(data, children, None), + value.as_ref().map(|value| data[value.clone()].into()), + )) + }, + } + } + + /// Set a partial and return new node if changed. + pub(crate) fn set_partial + Default>(&mut self, new_partial: NodeKey) -> Option> { + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Leaf { value, partial } => { + let partial = partial.build(data); + if partial == NibbleSlice::from_stored(&new_partial) { + return None; + } + Some(TNode::Leaf( + new_partial, + data[value.clone()].into(), + )) + }, + NodePlan::Extension { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), + NodePlan::Branch { .. } + | NodePlan::Empty => None, + NodePlan::NibbledBranch { value, children, partial } => { + let partial = partial.build(data); + if partial == NibbleSlice::from_stored(&new_partial) { + return None; + } + Some(TNode::NibbledBranch( + new_partial, + Self::init_child_slice(data, children, None), + value.as_ref().map(|value| data[value.clone()].into()), + )) + }, + } + } + + /// Set a value and return new node if changed. + pub(crate) fn set_value + Default>(&mut self, new_value: &[u8]) -> Option> { + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Empty => { + Some(TNode::Leaf( + Default::default(), + new_value.into(), + )) + }, + NodePlan::Leaf { partial, value } => { + if &data[value.clone()] == new_value { + return None; + } + Some(TNode::Leaf( + partial.build(data).into(), + new_value.into(), + )) + }, + NodePlan::Extension { .. } => None, + NodePlan::Branch { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), + NodePlan::NibbledBranch { partial, value, children } => { + if let Some(value) = value { + if &data[value.clone()] == new_value { + return None; + } + } + + Some(TNode::NibbledBranch( + partial.build(data).into(), + Self::init_child_slice(data, children, None), + Some(new_value.into()), + )) + }, + } + } + + /// Remove a value, return the change if something did change either node deleted or new value + /// for node. + /// Note that we are allowed to return a branch with no value and a single child (would need to + /// be fix depending on calling context (there could be some appending afterward)). + pub(crate) fn remove_value + Default>(&mut self) -> Option>> { + let data = &self.data.borrow(); + match &self.plan { + NodePlan::Leaf { .. } => Some(None), + NodePlan::Branch { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), + NodePlan::Extension { .. } + | NodePlan::Empty => None, + NodePlan::NibbledBranch { partial, value, children } => { + if value.is_none() { + return None; + } + + Some(Some(TNode::NibbledBranch( + partial.build(data).into(), + Self::init_child_slice(data, children, None), + None, + ))) + }, + } + } + + /// Set a handle to a child node or remove it if handle is none. + /// Return possibly updated node. + pub(crate) fn set_handle + Default>(&mut self, handle: Option>, index: u8) + -> Option> { + + let index = index as usize; + let data = &self.data.borrow(); + match &mut self.plan { + NodePlan::Empty => unreachable!("Do not add handle to empty but replace the node instead"), + NodePlan::Extension { .. } + | NodePlan::Branch { .. } => unimplemented!("{}", NO_EXTENSION_ONLY), + NodePlan::Leaf { partial, value } => { + if handle.is_some() { + let mut child_slices = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + child_slices[index] = handle; + + Some(TNode::NibbledBranch( + partial.build(data).into(), + child_slices, + Some(data[value.clone()].into())), + ) + } else { + None + } + }, + NodePlan::NibbledBranch { partial, value, children } => { + if handle.is_none() && children[index].is_none() { + None + } else { + let value = if let Some(value) = value.clone() { + Some(data[value.clone()].into()) + } else { + None + }; + let mut child_slices = Self::init_child_slice(data, children, Some(index)); + child_slices[index] = handle; + + Some(TNode::NibbledBranch( + partial.build(data).into(), + child_slices, + value, + )) + } + }, + } + } } diff --git a/trie-db/src/traverse.rs b/trie-db/src/traverse.rs new file mode 100644 index 00000000..4260757a --- /dev/null +++ b/trie-db/src/traverse.rs @@ -0,0 +1,1413 @@ +// 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. + +//! Traverse a trie following a given set of keys. +//! +//! The traversal stack is updatable, and is therefore usable for +//! batch update of ordered key values. +//! +//! Note that this part of trie crate currently do not support extension +//! node. Regarding how the code is designed, implementing extension should +//! be done by using a tuple of extension and branch node as a branch (storing +//! an additional hash in branch and only adapting fetch and write methods). + +use crate::triedbmut::{NibbleFullKey}; +use crate::node::{OwnedNode, NodeHandle, NodeKey, StorageHandle}; +use crate::nibble::{NibbleVec, nibble_ops, NibbleSlice}; +#[cfg(feature = "std")] +use std::borrow::Borrow; +#[cfg(not(feature = "std"))] +use core::borrow::Borrow; +#[cfg(not(feature = "std"))] +use alloc::vec::Vec; +use crate::{TrieLayout, TrieHash, CError, Result, TrieError}; +use crate::nibble::{BackingByteVec, OwnedPrefix}; +use hash_db::{HashDBRef, Prefix, EMPTY_PREFIX}; +use crate::NodeCodec; +use crate::rstd::mem; +use crate::rstd::boxed::Box; + + +type Node = crate::triedbmut::NodeMut; + +type OwnedNodeHandle = crate::triedbmut::NodeHandleMut; + +/// StackedNodeState can be updated. +/// A state can be use. +enum StackedNodeState + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + /// Read node. + Unchanged(OwnedNode), + /// Read node, attached, we need to update + /// parent hash or root. + UnchangedAttached(OwnedNode), + /// Modified node. + Changed(Node>), + /// Deleted node. + Deleted, +} + +/// Item on stack it contains updatable traverse +/// specific field to manage update that split +/// partial from a node, and for buffering the first +/// child of a node to avoid storing node that will +/// be change later by a fuse operation (removing +/// a branch without value and a single child). +struct StackedItem + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + /// the given node item. + item: StackedNode, + /// Store split child data created when adding a new node in an existing + /// partial. + /// That is the only situation where we got a modified item that may need + /// a to be iterated on at a next iteration. + /// Note that this cannot be fuse with `first_modified_child` because of + /// the case where a split child is at a bigger child index than the first + /// modified and will later be deleted, leading to a single child without + /// value fuse branch trigger. + split_child: Option>, + /// Store first child, until `exit` get call, this is needed + /// to be able to fuse branch containing a single child (delay + /// `exit` call of first element after process of the second one). + /// Store child and the key use when storing (to calculate final + /// nibble, this is more memory costy than strictly necessary). + /// Note that split_child is always a first_modified_child. + first_modified_child: Option>, + /// true as long as the value can be deleted and no more + /// than one branch cannot be deleted. + can_fuse: bool, +} + +/// Variant of stacked item to store first changed node. +struct StackedNode + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + /// Internal node representation. + node: StackedNodeState, + /// Hash and prefix used to access this node, for inline node (except root) and + /// new nodes this is None. + previous_db_handle: Option<(TrieHash, OwnedPrefix)>, + /// Index of prefix. + depth_prefix: usize, + /// Depth of node, it is prefix depth and partial depth. + depth: usize, + /// parent index (only relevant when parent is a branch). + parent_index: u8, +} + +impl From> for StackedNode + where + B: Borrow<[u8]>, + T: TrieLayout, +{ + fn from(item: StackedItem) -> Self { + item.item + } +} + +impl StackedItem + where + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, + T: TrieLayout, +{ + fn split_child_index(&self) -> Option { + match self.split_child.as_ref() { + Some(child) => Some(child.parent_index), + _ => None, + } + } + + fn first_modified_child_index(&self) -> Option { + self.first_modified_child.as_ref().map(|c| c.parent_index) + } + + fn is_split_child(&self, index: u8) -> bool { + self.split_child_index().map(|child_index| child_index == index).unwrap_or(false) + } + + // take first child (used for fusing, otherwhise process_first_modified_child is probably what you want) + fn take_first_modified_child(&mut self) -> Option> { + // descending in first child is only for fusizg node + // so we do not update self first child status (will be deleted). + if let Some(item) = self.first_modified_child.take() { + Some(StackedItem { + item, + first_modified_child: None, + split_child: None, + can_fuse: false, + }) + } else { + None + } + } + + fn descend_child(&mut self, index: u8, db: &dyn HashDBRef, prefix: Prefix) -> Result< + Option>, + TrieHash, + CError + > { + Ok(if self.is_split_child(index) { + if let Some(item) = self.split_child.take() { + // from a split child key is none (partial changed on split) + Some(StackedItem { + item, + first_modified_child: None, + split_child: None, + can_fuse: true, + }) + } else { + unreachable!("Broken split expectation, visited twice"); + } + } else { + if let Some(node_handle) = self.item.node.child(index) { + + let (node, previous_db_handle) = match node_handle { + NodeHandle::Hash(handle_hash) => { + let mut hash = as Default>::default(); + hash.as_mut()[..].copy_from_slice(handle_hash.as_ref()); + (StackedNodeState::fetch( + db, &hash, prefix, + )?, Some((hash, owned_prefix(&prefix)))) + }, + NodeHandle::Inline(node_encoded) => { + // Instantiating B is only for inline node, still costy. + (StackedNodeState::Unchanged( + OwnedNode::new::(B::from(node_encoded)) + .map_err(|e| Box::new(TrieError::DecoderError( + self.item.previous_db_handle.clone().map(|i| i.0).unwrap_or_else(Default::default), + e, + )))? + ), None) + }, + }; + let depth_prefix = self.item.depth + 1; + let depth = depth_prefix + node.partial().map(|p| p.len()).unwrap_or(0); + Some(StackedItem { + item: StackedNode { + node, + previous_db_handle, + parent_index: index, + depth_prefix, + depth, + }, + first_modified_child: None, + split_child: None, + can_fuse: true, + }) + } else { + None + } + }) + } + + // replace self by new branch at split and adding to self as a stacked item child. + fn do_split_child< + F: ProcessStack, + >(&mut self, mid_index: usize, key: &[u8], callback: &mut F) { +// // if self got split child, it can be processed +// // (we went up and this is a next key) (ordering) + self.process_first_modified_child(key, callback); + self.process_split_child(key, callback); + // or it means we need to store key to +// debug_assert!(self.split_child_index().is_none()); + let dest_branch = if mid_index % nibble_ops::NIBBLE_PER_BYTE == 0 { + let new_slice = NibbleSlice::new_offset( + &key[..mid_index / nibble_ops::NIBBLE_PER_BYTE], + self.item.depth_prefix, + ); + Node::empty_branch(new_slice) + } else { + let new_slice = NibbleSlice::new_offset( + &key[..], + self.item.depth_prefix, + ); + let owned = new_slice.to_stored_range(mid_index - self.item.depth_prefix); + Node::empty_branch(NibbleSlice::from_stored(&owned)) + }; + + let old_depth = self.item.depth; + self.item.depth = mid_index; + let mut child = mem::replace( + &mut self.item.node, + StackedNodeState::Changed(dest_branch), + ); + + let parent_index = child.partial() + .map(|p| p.at(mid_index - self.item.depth_prefix)).unwrap_or(0); + + child.advance_partial(1 + mid_index - self.item.depth_prefix); +// // split occurs before visiting a single child +// debug_assert!(self.first_modified_child.is_none()); + // debug_assert!(!self.cannot_fuse); + // ordering key also ensure + // debug_assert!(self.split_child_index().is_none()); + + // not setting child relation (will be set on exit) + let child = StackedNode { + node: child, + previous_db_handle: None, + depth_prefix: 1 + mid_index, + depth: old_depth, + parent_index, + }; + debug_assert!(self.first_modified_child.is_none()); + self.split_child = Some(child); + } + + fn append_child< + F: ProcessStack, + >(&mut self, child: StackedNode, prefix: Prefix, callback: &mut F) { + if let Some(handle) = callback.exit( + prefix, + child.node, + child.previous_db_handle, + ) { + self.item.node.set_handle(handle, child.parent_index); + } + } + + fn process_split_child< + F: ProcessStack, + >(&mut self, key: &[u8], callback: &mut F) { + if let Some(child) = self.split_child.take() { + // prefix is slice + let mut build_prefix = NibbleVec::from(key, self.item.depth_prefix); + self.item.node.partial().map(|p| build_prefix.append_partial(p.right())); + build_prefix.push(child.parent_index); + self.append_child(child, build_prefix.as_prefix(), callback); + } + } + + fn process_first_modified_child< + F: ProcessStack, + >(&mut self, key: &[u8], callback: &mut F) { + if let Some(child) = self.first_modified_child.take() { + if let Some(split_child_index) = self.split_child_index() { + if split_child_index < child.parent_index { + self.process_split_child(key.as_ref(), callback); + } + } + + let mut build_prefix = NibbleVec::from(key, self.item.depth_prefix); + self.item.node.partial().map(|p| build_prefix.append_partial(p.right())); + build_prefix.push(child.parent_index); + self.append_child(child, build_prefix.as_prefix(), callback); + } + } + + fn process_child< + F: ProcessStack, + >(&mut self, mut child: StackedItem, key: &[u8], callback: &mut F) { + + if let Some(first_modified_child_index) = self.first_modified_child_index() { + if first_modified_child_index < child.item.parent_index { + self.process_first_modified_child(key, callback); + } + } + if let Some(split_child_index) = self.split_child_index() { + if split_child_index < child.item.parent_index { + self.process_split_child(key.as_ref(), callback); + } + } + // split child can be unprocessed (when going up it is kept after second node + // in expectation of other children process. + child.process_first_modified_child(key, callback); + child.process_split_child(key, callback); + let nibble_slice = NibbleSlice::new_offset(key.as_ref(), child.item.depth_prefix); + self.append_child(child.into(), nibble_slice.left(), callback); + self.can_fuse = false; + } + + fn process_root< + F: ProcessStack, + >(mut self, key: &[u8], callback: &mut F) { + self.process_first_modified_child(key, callback); + self.process_split_child(key, callback); + callback.exit_root( + self.item.node, + self.item.previous_db_handle, + ) + } + + // consume branch and return item to attach to parent + fn fuse_branch< + F: ProcessStack, + >(&mut self, mut child: StackedItem, key: &[u8], callback: &mut F) { + let child_depth = self.item.depth + 1 + child.item.node.partial().map(|p| p.len()).unwrap_or(0); + let _ = mem::replace(&mut self.item.node, child.item.node); + // delete child + callback.exit( + EMPTY_PREFIX, + StackedNodeState::Deleted, child.item.previous_db_handle.take(), + ).expect("no new node on empty"); + + let partial = NibbleSlice::new_offset(key, self.item.depth_prefix) + .to_stored_range(child_depth - self.item.depth_prefix); + self.item.node.set_partial(partial); + self.item.depth = child_depth; + self.can_fuse = true; + self.first_modified_child = None; + self.split_child = None; + } + + fn fuse_node>( + &mut self, + key: &[u8], + db: &dyn HashDBRef, + callback: &mut F, + ) -> Result, CError> { + let first_modified_child_index = self.first_modified_child_index(); + let (deleted, fuse_index) = self.item.node.fuse_node((first_modified_child_index, self.split_child_index())); + // needed also to resolve + if let Some(fuse_index) = fuse_index { + // try first child + if let Some(child) = self.take_first_modified_child() { + debug_assert!(child.item.parent_index == fuse_index); + let mut prefix = NibbleVec::from(key.as_ref(), self.item.depth); + prefix.push(fuse_index); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); + self.fuse_branch(child, prefix.inner(), callback); + } else { + let mut prefix = NibbleVec::from(key.as_ref(), self.item.depth); + prefix.push(fuse_index); + let child = self.descend_child(fuse_index, db, prefix.as_prefix())? + .expect("result of first child is define if consistent db"); + child.item.node.partial().map(|p| prefix.append_partial(p.right())); + self.fuse_branch(child, prefix.inner(), callback); + } + Ok(true) + } else { + Ok(deleted) + } + } +} + +impl StackedNodeState + where + B: Borrow<[u8]> + AsRef<[u8]>, + T: TrieLayout, +{ + /// Get extension part of the node (partial) if any. + fn is_empty(&self) -> bool { + match self { + StackedNodeState::Unchanged(node) => node.is_empty(), + StackedNodeState::UnchangedAttached(node) => node.is_empty(), + StackedNodeState::Changed(node) => node.is_empty(), + StackedNodeState::Deleted => true, + } + } + + /// Get extension part of the node (partial) if any. + fn partial(&self) -> Option { + match self { + StackedNodeState::Unchanged(node) => node.partial(), + StackedNodeState::UnchangedAttached(node) => node.partial(), + StackedNodeState::Changed(node) => node.partial(), + StackedNodeState::Deleted => None, + } + } + + /// Try to access child. + fn child(&self, ix: u8) -> Option { + match self { + StackedNodeState::Unchanged(node) => node.child(ix), + StackedNodeState::UnchangedAttached(node) => node.child(ix), + StackedNodeState::Changed(node) => node.child(ix), + StackedNodeState::Deleted => None, + } + } + + /// Tell if there is a value defined at this node position. + fn has_value(&self) -> bool { + match self { + StackedNodeState::Unchanged(node) => node.has_value(), + StackedNodeState::UnchangedAttached(node) => node.has_value(), + StackedNodeState::Changed(node) => node.has_value(), + StackedNodeState::Deleted => false, + } + } + + /// Set a value if the node can contain one. + fn set_value(&mut self, value: &[u8]) { + match self { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { + if let Some(new) = node.set_value(value) { + *self = StackedNodeState::Changed(new); + } + }, + StackedNodeState::Changed(node) => node.set_value(value), + StackedNodeState::Deleted => (), + } + } + + /// Change a partial if the node contains one. + fn advance_partial(&mut self, nb: usize) { + match self { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { + if let Some(new) = node.advance_partial(nb) { + *self = StackedNodeState::Changed(new); + } + }, + StackedNodeState::Changed(node) => node.advance_partial(nb), + StackedNodeState::Deleted => (), + } + } + + /// Set a new partial. + fn set_partial(&mut self, partial: NodeKey) { + match self { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { + if let Some(new) = node.set_partial(partial) { + *self = StackedNodeState::Changed(new); + } + }, + StackedNodeState::Changed(node) => node.set_partial(partial), + StackedNodeState::Deleted => (), + } + } + + /// Remove a value if the node contains one. + fn remove_value(&mut self) { + match self { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { + match node.remove_value() { + Some(Some(new)) => + *self = StackedNodeState::Changed(new), + Some(None) => + *self = StackedNodeState::Deleted, + None => (), + } + }, + StackedNodeState::Changed(node) => { + if node.remove_value() { + *self = StackedNodeState::Deleted; + } + }, + StackedNodeState::Deleted => (), + } + } + + /// Set a handle to a child node or remove it if handle is none. + fn set_handle(&mut self, handle: Option>>, index: u8) { + match self { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => { + let change = node.set_handle(handle, index); + match change { + Some(new) => *self = StackedNodeState::Changed(new), + None => (), + } + }, + StackedNodeState::Changed(node) => { + node.set_handle(handle, index); + }, + StackedNodeState::Deleted => unreachable!(), + } + } + + /// Returns index of node to fuse if node was fused. + fn fuse_node(&mut self, pending: (Option, Option)) -> (bool, Option) { + match self { + StackedNodeState::Deleted + | StackedNodeState::UnchangedAttached(..) + | StackedNodeState::Unchanged(..) => (false, None), + StackedNodeState::Changed(node) => { + let (deleted, fuse) = node.try_fuse_node(pending); + if deleted { + *self = StackedNodeState::Deleted; + } + (deleted, fuse) + }, + } + } + + /// Encode node + fn into_encoded(self) -> Vec { + match self { + StackedNodeState::UnchangedAttached(node) + | StackedNodeState::Unchanged(node) => node.data().to_vec(), + StackedNodeState::Changed(node) => node.into_encoded::<_, T::Codec, T::Hash>( + |child, _o_slice, _o_index| { + child.into_child_ref::() + }), + StackedNodeState::Deleted => T::Codec::empty_node().to_vec(), + } + } + + /// Fetch by hash, no caching. + fn fetch( + db: &dyn HashDBRef, + hash: &TrieHash, + key: Prefix, + ) -> Result, CError> { + Ok(StackedNodeState::Unchanged( + Self::fetch_node(db, hash, key)? + )) + } + + /// Fetch a node by hash. + fn fetch_node( + db: &dyn HashDBRef, + hash: &TrieHash, + key: Prefix, + ) -> Result, TrieHash, CError> { + let node_encoded = db.get(hash, key) + .ok_or_else(|| Box::new(TrieError::IncompleteDatabase(*hash)))?; + + Ok( + OwnedNode::new::(node_encoded) + .map_err(|e| Box::new(TrieError::DecoderError(*hash, e)))? + ) + } +} + +/// Visitor trait to implement when using `trie_traverse_key`. +trait ProcessStack + where + T: TrieLayout, + B: Borrow<[u8]> + AsRef<[u8]>, +{ + /// Descend node, it should (if we want update): + /// - return a new child for the new value. + /// - replace `self` by a new branch with `self` as its split child + /// and a new child for the new value. + /// - change value of `self` only. + fn enter_terminal( + &mut self, + stacked: &mut StackedItem, + key_element: &[u8], + action: InputAction<&[u8], &TrieHash>, + to_attach_node: Option>, + state: TraverseState, + ) -> Option>; + + /// Callback on exit a node, commit action on change node should be applied here. + fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>) + -> Option>>>; + /// Callback on a detached node. + fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>); + /// Same as `exit` but for root (very last exit call). + fn exit_root(&mut self, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>); +} + +/// State when descending +enum TraverseState { + /// This is the right node for value. + ValueMatch, + /// after node + AfterNode, + /// Mid partial and index + MidPartial(usize), +} + +/// Action for a key to traverse. +pub enum InputAction { + /// Delete a value if it exists. + Delete, + /// Insert a value. If value is already define, + /// it will be overwrite. + Insert(V), + /// Detach trie content at a given. + /// Handle detached nodes is managed by process stack logic. + Detach, + /// Attach a trie with given hash. + /// Handle of conflict is managed by process stack logic. + /// More common strategie would be to replace content and handle + /// the replaced content as a detached content is handled. + Attach(H), +} + +impl, H> InputAction { + + /// Alternative to `std::convert::AsRef`. + /// Retun optionally a reference to a hash to an node to fetch for this action. + pub fn as_ref(&self) -> (InputAction<&[u8], &H>, Option<&H>) { + match self { + InputAction::Insert(v) => (InputAction::Insert(v.as_ref()), None), + InputAction::Delete => (InputAction::Delete, None), + InputAction::Attach(attach_root) => (InputAction::Attach(&attach_root), Some(&attach_root)), + InputAction::Detach => (InputAction::Detach, None), + } + } +} + +/// The main entry point for traversing a trie by a set of keys. +fn trie_traverse_key<'a, T, I, K, V, B, F>( + db: &'a dyn HashDBRef, + root_hash: &'a TrieHash, + elements: I, + callback: &mut F, +) -> Result<(), TrieHash, CError> + where + T: TrieLayout, + I: Iterator>)>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, + F: ProcessStack, +{ + + // Stack of traversed nodes + let mut stack: smallvec::SmallVec<[StackedItem; 16]> = Default::default(); + + let current = if let Ok(root) = StackedNodeState::fetch(db, root_hash, EMPTY_PREFIX) { + root + } else { + return Err(Box::new(TrieError::InvalidStateRoot(*root_hash))); + }; + + let depth = current.partial().map(|p| p.len()).unwrap_or(0); + let mut current = StackedItem { + item: StackedNode { + node: current, + previous_db_handle: Some((*root_hash, owned_prefix(&EMPTY_PREFIX))), + depth_prefix: 0, + depth, + parent_index: 0, + }, + first_modified_child: None, + split_child: None, + can_fuse: true, + }; + + let mut previous_key: Option = None; + + for next_query in elements.into_iter().map(|e| Some(e)).chain(Some(None)) { + + let last = next_query.is_none(); + + // PATH UP over the previous key and value + if let Some(key) = previous_key.as_ref() { + + // Pop deleted (empty) nodes. + if current.item.node.is_empty() { + assert!(current.first_modified_child.is_none()); + assert!(current.split_child.is_none()); + if let Some(mut parent) = stack.pop() { + let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); + parent.append_child(current.into(), prefix.left(), callback); + current = parent; + } else { + // Empty trie, next additional value is therefore a leaf. + // Also delete this entry (could have been something else than empty before). + debug_assert!(current.item.depth_prefix == 0); + match next_query.as_ref() { + Some((key, InputAction::Insert(value))) => { + callback.exit( + EMPTY_PREFIX, + current.item.node, + current.item.previous_db_handle, + ); + let leaf = Node::new_leaf( + NibbleSlice::new(key.as_ref()), + value.as_ref(), + ); + current = StackedItem { + item: StackedNode { + node: StackedNodeState::Changed(leaf), + previous_db_handle: None, + depth_prefix: 0, + depth: key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index: 0, + }, + first_modified_child: None, + split_child: None, + can_fuse: true, + }; + continue; + }, + Some((key, InputAction::Attach(attach_root))) => { + let root_prefix = (key.as_ref(), None); + let node = StackedNodeState::::fetch(db, &attach_root, root_prefix)?; + if !node.is_empty() { + let depth = node.partial().map(|p| p.len()).unwrap_or(0); + current = StackedItem { + item: StackedNode { + node, + previous_db_handle: None, + depth_prefix: 0, + depth, + parent_index: 0, + }, + first_modified_child: None, + split_child: None, + can_fuse: true, + }; + } + continue; + }, + Some((_key, InputAction::Detach)) + | Some((_key, InputAction::Delete)) => { + continue; + }, + None => { + callback.exit_root( + current.item.node, + current.item.previous_db_handle, + ); + return Ok(()); + }, + } + } + } + + let target_common_depth = next_query.as_ref().map(|(next, _)| nibble_ops::biggest_depth( + key.as_ref(), + next.as_ref(), + )).unwrap_or(0); // last element goes up to root + + current.fuse_node(key.as_ref(), db, callback)?; + + // unstack nodes if needed + while last || target_common_depth < current.item.depth_prefix { + // child change or addition + if let Some(mut parent) = stack.pop() { + if !parent.can_fuse { + // process exit, as we already assert two child, no need to store in case of parent + // fusing. + // Deletion case is guaranted by ordering of input (fix delete only if no first + // and no split). + current.process_first_modified_child(key.as_ref(), callback); + current.process_split_child(key.as_ref(), callback); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); + parent.append_child(current.into(), prefix.left(), callback); + } else if let Some(first_modified_child_index) = parent.first_modified_child_index() { + debug_assert!(first_modified_child_index < current.item.parent_index); + parent.process_child(current, key.as_ref(), callback); + } else { + if let Some(split_child_index) = parent.split_child_index() { + if split_child_index < current.item.parent_index { + parent.can_fuse = false; + } + } + if parent.can_fuse && parent.item.node.has_value() { + // this could not be fuse (delete value occurs before), + // no stacking of first child + parent.can_fuse = false; + } + if !parent.can_fuse { + parent.process_child(current, key.as_ref(), callback); + } else { + current.process_first_modified_child(key.as_ref(), callback); + // split child is after first child (would be processed otherwhise), so no need to + // order the two instructions. + current.process_split_child(key.as_ref(), callback); + // first node visited on a fusable element, store in parent first child and process later. + // Process an eventual split child (index after current). + parent.first_modified_child = Some(current.into()); + } + } + current = parent; + } else { + if last { + current.process_first_modified_child(key.as_ref(), callback); + current.process_split_child(key.as_ref(), callback); + current.process_root(key.as_ref(), callback); + return Ok(()); + } else { + // move to next key + break; + } + } + + if current.fuse_node(key.as_ref(), db, callback)? { + // no need to try to go down when fuse succeed + continue; + } + } + } + + // PATH DOWN descending in next_query. + if let Some((key, value)) = next_query { + let dest_slice = NibbleFullKey::new(key.as_ref()); + let dest_depth = key.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE; + let mut descend_mid_index = None; + loop { + let common_index = current.item.node.partial() + .map(|current_partial| { + let target_partial = NibbleSlice::new_offset(key.as_ref(), current.item.depth_prefix); + current.item.depth_prefix + current_partial.common_prefix(&target_partial) + }).unwrap_or(current.item.depth_prefix); + if common_index == current.item.depth && dest_depth > current.item.depth { + let next_index = dest_slice.at(current.item.depth); + let prefix = NibbleSlice::new_offset(key.as_ref(), current.item.depth + 1); + if let Some(child) = current.descend_child(next_index, db, prefix.left())? { + stack.push(current); + current = child; + } else { + break; + } + } else { + if common_index < current.item.depth { + descend_mid_index = Some(common_index); + } + break; + } + } + + let traverse_state = if let Some(mid_index) = descend_mid_index { + TraverseState::MidPartial(mid_index) + } else if dest_depth > current.item.depth { + // over callback + TraverseState::AfterNode + } else { + debug_assert!(dest_depth == current.item.depth); + // value replace callback + TraverseState::ValueMatch + }; + + let (value, do_fetch) = value.as_ref(); + let fetch = if let Some(hash) = do_fetch { + // This is fetching node to attach + let root_prefix = (key.as_ref(), None); + let hash = StackedNodeState::<_, T>::fetch_node(db, &hash, root_prefix)?; + Some(hash) + } else { + None + }; + if let Some(new_child) = callback.enter_terminal( + &mut current, + key.as_ref(), + value, + fetch, + traverse_state, + ) { + stack.push(current); + current = new_child; + } + previous_key = Some(key); + } + } + + Ok(()) +} + +/// Contains ordered node change for this iteration. +/// The resulting root hash. +/// The latest changed node. +struct BatchUpdate { + register_update: C, + register_update_attach_detach: CD, + register_detached: D, + root: H, +} + +impl ProcessStack for BatchUpdate, C, CD, D> + where + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, + T: TrieLayout, + C: FnMut((OwnedPrefix, TrieHash, Option>)), + CD: FnMut((OwnedPrefix, TrieHash, Option>)), + D: FnMut((Vec, OwnedPrefix, TrieHash)), +{ + fn enter_terminal( + &mut self, + stacked: &mut StackedItem, + key_element: &[u8], + action: InputAction<&[u8], &TrieHash>, + to_attach_node: Option>, + state: TraverseState, + ) -> Option> { + match state { + TraverseState::ValueMatch => { + match action { + InputAction::Insert(value) => { + stacked.item.node.set_value(value); + }, + InputAction::Delete => { + stacked.item.node.remove_value(); + }, + InputAction::Attach(attach_root) => { + let prefix_nibble = NibbleSlice::new_offset(&key_element[..], stacked.item.depth_prefix); + let prefix = prefix_nibble.left(); + let to_attach = to_attach_node + .expect("Attachment resolution must be done before calling this function"); + let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); + if stacked.item.depth_prefix != stacked.item.depth { + match to_attach.partial() { + Some(partial) if partial.len() > 0 => { + let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix).into(); + crate::triedbmut::combine_key(&mut build_partial, partial.right_ref()); + to_attach.set_partial(build_partial); + }, + _ => { + if let Some(partial) = stacked.item.node.partial() { + to_attach.set_partial(partial.into()); + } + }, + } + stacked.item.node.advance_partial(stacked.item.depth - stacked.item.depth_prefix); + } + let detached = mem::replace(&mut stacked.item.node, to_attach); + let detached_db = mem::replace( + &mut stacked.item.previous_db_handle, + Some((attach_root.clone(), owned_prefix(&(key_element, None)))), + ); + stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); + self.exit_detached(key_element, prefix, detached, detached_db); + }, + InputAction::Detach => { + let prefix_nibble = NibbleSlice::new_offset(&key_element[..], stacked.item.depth_prefix); + let prefix = prefix_nibble.left(); + let to_attach = StackedNodeState::Deleted; + if stacked.item.depth_prefix != stacked.item.depth { + stacked.item.node.advance_partial(stacked.item.depth - stacked.item.depth_prefix); + } + let detached = mem::replace(&mut stacked.item.node, to_attach); + let detached_db = mem::replace(&mut stacked.item.previous_db_handle, None); + self.exit_detached(key_element, prefix, detached, detached_db); + }, + } + None + }, + TraverseState::AfterNode => { + match action { + InputAction::Insert(val) => { + // corner case of empty trie. + let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; + // dest is a leaf appended to terminal + let dest_leaf = Node::new_leaf( + NibbleSlice::new_offset(key_element, stacked.item.depth + offset), + val, + ); + let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); + let mut new_child = StackedItem { + item: StackedNode { + node: StackedNodeState::Changed(dest_leaf), + previous_db_handle: None, + depth_prefix: stacked.item.depth + offset, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + }, + split_child: None, + first_modified_child: None, + can_fuse: false, + }; + return if stacked.item.node.is_empty() { + // replace empty. + new_child.item.previous_db_handle = stacked.item.previous_db_handle.take(); + *stacked = new_child; + None + } else { + // append to parent is done on exit through changed nature of the new leaf. + Some(new_child) + } + }, + InputAction::Delete => { + // nothing to delete. + None + }, + InputAction::Attach(attach_root) => { + let offset = if stacked.item.node.is_empty() { 0 } else { 1 }; + let parent_index = NibbleSlice::new(key_element).at(stacked.item.depth); + let to_attach = to_attach_node + .expect("Attachment resolution must be done before calling this function"); + let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); + let depth_prefix = stacked.item.depth + offset; + match to_attach.partial() { + Some(partial) if partial.len() > 0 => { + let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, depth_prefix).into(); + crate::triedbmut::combine_key(&mut build_partial, partial.right_ref()); + to_attach.set_partial(build_partial); + }, + _ => { + let partial: NodeKey = NibbleSlice::new_offset(key_element, depth_prefix).into(); + to_attach.set_partial(partial.into()); + }, + }; + + let depth = depth_prefix + to_attach.partial().map(|p| p.len()).unwrap_or(0); + let mut new_child = StackedItem { + item: StackedNode { + node: to_attach, + // Attach root is prefixed at attach key + previous_db_handle: Some((attach_root.clone(), owned_prefix(&(key_element, None)))), + depth_prefix, + depth, + parent_index, + }, + split_child: None, + first_modified_child: None, + can_fuse: false, + }; + return if stacked.item.node.is_empty() { + // replace empty. + let detached_db = mem::replace(&mut new_child.item.previous_db_handle, stacked.item.previous_db_handle.take()); + self.exit_detached(key_element, EMPTY_PREFIX, StackedNodeState::::Deleted, detached_db); + *stacked = new_child; + None + } else { + // append to parent is done on exit through changed nature of the new leaf. + Some(new_child) + } + }, + InputAction::Detach => { + // nothing to detach + None + }, + } + }, + TraverseState::MidPartial(mid_index) => { + match action { + InputAction::Insert(value) => { + assert!(!stacked.item.node.is_empty()); + stacked.do_split_child(mid_index, key_element, self); + let (offset, parent_index) = if key_element.len() == 0 { + // corner case of adding at top of trie + (0, 0) + } else { + (1, NibbleSlice::new(key_element).at(mid_index)) + }; + let child = Node::new_leaf( + NibbleSlice::new_offset(key_element, offset + mid_index), + value.as_ref(), + ); + return if mid_index == key_element.len() * nibble_ops::NIBBLE_PER_BYTE { + + // set value in new branch + stacked.item.node.set_value(value); + stacked.can_fuse = false; + None + } else { + let child = StackedItem { + item: StackedNode { + node: StackedNodeState::Changed(child), + previous_db_handle: None, + depth_prefix: offset + mid_index, + depth: key_element.as_ref().len() * nibble_ops::NIBBLE_PER_BYTE, + parent_index, + }, + split_child: None, + first_modified_child: None, + can_fuse: false, + }; + Some(child) + } + }, + InputAction::Delete => { + // nothing to delete. + None + }, + InputAction::Attach(attach_root) => { + let prefix_nibble = NibbleSlice::new(&key_element[..]); + let prefix = prefix_nibble.left(); + let to_attach = to_attach_node + .expect("Attachment resolution must be done before calling this function"); + let mut to_attach = StackedNodeState::UnchangedAttached(to_attach); + match to_attach.partial() { + Some(partial) if partial.len() > 0 => { + let mut build_partial: NodeKey = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix).into(); + crate::triedbmut::combine_key(&mut build_partial, partial.right_ref()); + to_attach.set_partial(build_partial); + }, + _ => { + let build_partial = NibbleSlice::new_offset(key_element, stacked.item.depth_prefix); + if build_partial.len() > 0 { + to_attach.set_partial(build_partial.into()); + } + }, + } + if mid_index > 0 { + stacked.item.node.advance_partial(mid_index - stacked.item.depth_prefix); + } + let detached = mem::replace(&mut stacked.item.node, to_attach); + //detached.advance_partial(mid_index); + let detached_db = mem::replace( + &mut stacked.item.previous_db_handle, + Some((attach_root.clone(), owned_prefix(&(key_element, None)))), + ); + stacked.item.depth = stacked.item.depth_prefix + stacked.item.node.partial().map(|p| p.len()).unwrap_or(0); + self.exit_detached(key_element, prefix, detached, detached_db); + None + }, + InputAction::Detach => { + if mid_index == NibbleSlice::new(key_element).len() { + // on a path do a switch + if stacked.item.node.is_empty() { + unreachable!("we should not iterate in middle of an empty; this is a bug"); + } + let prefix_nibble = NibbleSlice::new(&key_element[..]); + let prefix = prefix_nibble.left(); + let to_attach = StackedNodeState::Deleted; + let mut detached = mem::replace(&mut stacked.item.node, to_attach); + detached.advance_partial(mid_index - stacked.item.depth_prefix); + let detached_db = mem::replace(&mut stacked.item.previous_db_handle, None); + self.exit_detached(key_element, prefix, detached, detached_db); + } + None + }, + } + }, + } + } + + fn exit(&mut self, prefix: Prefix, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>) + -> Option>>> { + let register = &mut self.register_update; + match stacked { + StackedNodeState::Changed(node) => Some(Some({ + if let Some((h, p)) = prev_db { + register((p, h, None)); + } + let encoded = node.into_encoded::<_, T::Codec, T::Hash>( + |child, _o_slice, _o_index| { + child.into_child_ref::() + } + ); + if encoded.len() < ::LENGTH { + OwnedNodeHandle::InMemory(encoded) + } else { + let hash = ::hash(&encoded[..]); + // costy clone (could get read from here) + register((owned_prefix(&prefix), hash.clone(), Some(encoded))); + OwnedNodeHandle::Hash(hash) + } + })), + StackedNodeState::Deleted => { + if let Some((h, p)) = prev_db { + register((p, h.clone(), None)); + } + Some(None) + }, + StackedNodeState::UnchangedAttached(node) => Some(Some({ + let encoded = node.data().to_vec(); + if encoded.len() < ::LENGTH { + if let Some((h, p)) = prev_db { + register((p, h, None)); + } + OwnedNodeHandle::InMemory(encoded) + } else { + OwnedNodeHandle::Hash(::hash(&encoded[..])) + } + })), + _ => None, + } + } + + fn exit_root(&mut self, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>) { + let prefix = EMPTY_PREFIX; + let register = &mut self.register_update; + match stacked { + s@StackedNodeState::Deleted + | s@StackedNodeState::Changed(..) => { + let encoded = s.into_encoded(); + let hash = ::hash(&encoded[..]); + self.root = hash.clone(); + if let Some((h, p)) = prev_db { + register((p, h.clone(), None)); + } + register((owned_prefix(&prefix), hash, Some(encoded))); + }, + StackedNodeState::UnchangedAttached(node) => { + let encoded = node.data().to_vec(); + let hash = ::hash(&encoded[..]); + self.root = hash.clone(); + }, + _ => (), + } + } + + fn exit_detached(&mut self, key_element: &[u8], prefix: Prefix, stacked: StackedNodeState, prev_db: Option<(TrieHash, OwnedPrefix)>) { + let detached_prefix = (key_element, None); + + let is_empty_node = stacked.is_empty(); + let register_up = &mut self.register_update_attach_detach; + let register = &mut self.register_detached; + match stacked { + s@StackedNodeState::Deleted + | s@StackedNodeState::Changed(..) => { + // same as root: also hash inline nodes. + if let Some((h, p)) = prev_db { + register_up((p, h.clone(), None)); + } + if !is_empty_node { + let encoded = s.into_encoded(); + let hash = ::hash(&encoded[..]); + register_up((owned_prefix(&detached_prefix), hash.clone(), Some(encoded))); + register((key_element.to_vec(), owned_prefix(&prefix), hash)); + } + }, + s@StackedNodeState::UnchangedAttached(..) + | s@StackedNodeState::Unchanged(..) => { + if !is_empty_node { + let hash = if let Some((not_inline, _previous_prefix)) = prev_db { + not_inline + } else { + let encoded = s.into_encoded(); + let hash = ::hash(&encoded[..]); + register_up((owned_prefix(&detached_prefix), hash.clone(), Some(encoded))); + hash + }; + register((key_element.to_vec(), owned_prefix(&prefix), hash)); + } + }, + } + } +} + + +fn owned_prefix(prefix: &Prefix) -> (BackingByteVec, Option) { + (prefix.0.into(), prefix.1) +} + +/// Extract prefix from a owned prefix. +pub fn from_owned_prefix(prefix: &OwnedPrefix) -> Prefix { + (&prefix.0[..], prefix.1) +} + +/// Update trie, returning deltas and root. +pub fn batch_update<'a, T, I, K, V, B>( + db: &'a dyn HashDBRef, + root_hash: &'a TrieHash, + elements: I, +) -> Result<( + TrieHash, + Vec<(OwnedPrefix, TrieHash, Option>)>, + Vec<(OwnedPrefix, TrieHash, Option>)>, + Vec<(Vec, OwnedPrefix, TrieHash)>, +),TrieHash, CError> + where + T: TrieLayout, + I: Iterator>)>, + K: AsRef<[u8]> + Ord, + V: AsRef<[u8]>, + B: Borrow<[u8]> + AsRef<[u8]> + for<'b> From<&'b [u8]>, +{ + let mut dest = Vec::new(); + let mut dest2 = Vec::new(); + let mut dest_detached = Vec::new(); + let mut batch_update = BatchUpdate { + register_update: |update| { + dest.push(update) + }, + register_update_attach_detach: |update| { + dest2.push(update) + }, + register_detached: |update| { + dest_detached.push(update) + }, + root: root_hash.clone(), + }; + trie_traverse_key::(db, root_hash, elements, &mut batch_update)?; + Ok((batch_update.root, dest, dest2, dest_detached)) +} + + + +/// For a dettached trie root, remove prefix. +/// Usually the prefix is the key path used to detach the node +/// for a key function that attach prefix. +/// This transformation is not needed if there is no prefix +/// added by the key function of the backend db. +/// This method maitain all trie key in memory, a different implementation +/// should be use if inner mutability is possible (trie iterator touch each +/// key only once so it should be safe to delete immediatly). +pub fn unprefixed_detached_trie( + source_db: &mut dyn hash_db::HashDB>, + mut target_db: Option<&mut dyn hash_db::HashDB>>, + root: TrieHash, + prefix: &[u8], +) -> Result<(),TrieHash, CError> + where + T: TrieLayout, +{ + let mapped_source = PrefixedDBRef::(source_db, prefix); + let input = crate::TrieDB::::new(&mapped_source, &root)?; + let mut keys: Vec<(NibbleVec, TrieHash)> = Vec::new(); + for elt in crate::TrieDBNodeIterator::new(&input)? { + if let Ok((prefix, Some(hash), _)) = elt { + keys.push((prefix, hash)); + } + } + let mut prefix_slice = prefix.to_vec(); + let prefix_slice_len = prefix_slice.len(); + for (prefix, hash) in keys.into_iter() { + let prefix = prefix.as_prefix(); + prefix_slice.truncate(prefix_slice_len); + prefix_slice.extend_from_slice(prefix.0); + let prefix_source = (prefix_slice.as_ref(), prefix.1); + if let Some(value) = source_db.get(&hash, prefix_source) { + source_db.remove(&hash, prefix_source); + if let Some(target) = target_db.as_mut() { + target.emplace(hash, prefix, value); + } else { + source_db.emplace(hash, prefix, value); + } + } + } + Ok(()) +} + +struct PrefixedDBRef<'a, T: TrieLayout>(&'a mut dyn hash_db::HashDB>, &'a [u8]); + +impl<'a, T: TrieLayout> hash_db::HashDBRef> for PrefixedDBRef<'a, T> { + fn get(&self, key: &TrieHash, prefix: Prefix) -> Option> { + let mut prefix_slice = self.1.to_vec(); + prefix_slice.extend_from_slice(prefix.0); + self.0.get(key, (prefix_slice.as_ref(), prefix.1)) + } + + fn contains(&self, key: &TrieHash, prefix: Prefix) -> bool { + let mut prefix_slice = self.1.to_vec(); + prefix_slice.extend_from_slice(prefix.0); + self.0.contains(key, (prefix_slice.as_ref(), prefix.1)) + } +} + +pub fn prefixed_detached_trie( + source_db: &mut dyn hash_db::HashDB>, + mut target_db: Option<&mut dyn hash_db::HashDB>>, + root: TrieHash, + prefix: &[u8], +) -> Result<(),TrieHash, CError> + where + T: TrieLayout, +{ + let input = crate::TrieDB::::new(&source_db, &root)?; + let mut keys: Vec<(NibbleVec, TrieHash)> = Vec::new(); + for elt in crate::TrieDBNodeIterator::new(&input)? { + if let Ok((prefix, Some(hash), _)) = elt { + keys.push((prefix, hash)); + } + } + let mut prefix_slice = prefix.to_vec(); + let prefix_slice_len = prefix_slice.len(); + for (prefix, hash) in keys.into_iter() { + let prefix = prefix.as_prefix(); + if let Some(value) = source_db.get(&hash, prefix) { + prefix_slice.truncate(prefix_slice_len); + prefix_slice.extend_from_slice(prefix.0); + let prefix_dest = (prefix_slice.as_ref(), prefix.1); + source_db.remove(&hash, prefix); + if let Some(target) = target_db.as_mut() { + target.emplace(hash, prefix_dest, value); + } else { + source_db.emplace(hash, prefix_dest, value); + } + } + } + Ok(()) +} diff --git a/trie-db/src/triedbmut.rs b/trie-db/src/triedbmut.rs index 6338ba0f..6a6cf715 100644 --- a/trie-db/src/triedbmut.rs +++ b/trie-db/src/triedbmut.rs @@ -1,4 +1,4 @@ -// Copyright 2017, 2020 Parity Technologies +// Copyright 2017, 2021 Parity Technologies // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,10 +14,10 @@ //! In-memory trie representation. -use super::{DBValue, node::NodeKey}; -use super::{Result, TrieError, TrieMut, TrieLayout, TrieHash, CError}; -use super::lookup::Lookup; -use super::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, decode_hash}; +use crate::{DBValue, node::NodeKey, NO_EXTENSION_ONLY}; +use crate::{Result, TrieError, TrieMut, TrieLayout, TrieHash, CError}; +use crate::lookup::Lookup; +use crate::node::{NodeHandle as EncodedNodeHandle, Node as EncodedNode, decode_hash}; use hash_db::{HashDB, Hasher, Prefix, EMPTY_PREFIX}; use hashbrown::HashSet; @@ -38,17 +38,52 @@ use crate::rstd::fmt::{self, Debug}; // For lookups into the Node storage buffer. // This is deliberately non-copyable. #[cfg_attr(feature = "std", derive(Debug))] -struct StorageHandle(usize); +pub(crate) struct StorageHandle(usize); // Handles to nodes in the trie. #[cfg_attr(feature = "std", derive(Debug))] -enum NodeHandle { +pub enum NodeHandleMut { /// Loaded into memory. - InMemory(StorageHandle), + InMemory(SH), /// Either a hash or an inline node Hash(H), } +impl, SH: AsRef<[u8]>> NodeHandleMut { + /// Get a node handle ref to this handle. + pub(crate) fn as_ref(&self) -> crate::node::NodeHandle { + match self { + NodeHandleMut::InMemory(sh) => crate::node::NodeHandle::Inline(sh.as_ref()), + NodeHandleMut::Hash(h) => crate::node::NodeHandle::Hash(h.as_ref()), + } + } +} + +impl + Send + Copy, SH: AsRef<[u8]>> NodeHandleMut +where + H: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + + PartialEq + Eq + Hash + Send + Sync + Clone + Copy, + SH: AsRef<[u8]>, +{ + /// Get a child reference (this has a cost but doing + /// otherwhise requires changing codec trait). + pub(crate) fn into_child_ref

(self) -> ChildReference + where + H2: Hasher + { + match self { + NodeHandleMut::InMemory(sh) => { + let sh_ref = sh.as_ref(); + let mut h = H2::Out::default(); + let len = sh_ref.len(); + h.as_mut()[..len].copy_from_slice(&sh_ref[..len]); + ChildReference::Inline(h, len) + }, + NodeHandleMut::Hash(h) => ChildReference::Hash(h), + } + } +} + impl From for NodeHandle { fn from(handle: StorageHandle) -> Self { NodeHandle::InMemory(handle) @@ -64,10 +99,10 @@ fn empty_children() -> Box<[Option>; 16]> { /// Type alias to indicate the nible covers a full key, /// therefore its left side is a full prefix. -type NibbleFullKey<'key> = NibbleSlice<'key>; +pub(crate) type NibbleFullKey<'key> = NibbleSlice<'key>; /// Node types in the Trie. -enum Node { +pub(crate) enum NodeMut { /// Empty node. Empty, /// A leaf node contains the end of a key and a value. @@ -78,11 +113,225 @@ enum Node { /// The shared portion is encoded from a `NibbleSlice` meaning it contains /// a flag indicating it is an extension. /// The child node is always a branch. - Extension(NodeKey, NodeHandle), + Extension(NodeKey, NodeHandleMut), /// A branch has up to 16 children and an optional value. - Branch(Box<[Option>; 16]>, Option), + Branch(Box<[Option>; 16]>, Option), /// Branch node with support for a nibble (to avoid extension node). - NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), + NibbledBranch(NodeKey, Box<[Option>; 16]>, Option), +} + +impl NodeMut { + /// Tell if it is the empty node. + pub(crate) fn is_empty(&self) -> bool { + if let NodeMut::Empty = &self { + true + } else { + false + } + } + + /// Get extension part of the node (partial) if any. + pub(crate) fn partial(&self) -> Option { + match self { + NodeMut::Branch { .. } + | NodeMut::Empty => None, + NodeMut::NibbledBranch(partial, ..) + | NodeMut::Extension(partial, ..) + | NodeMut::Leaf(partial, ..) + => Some(NibbleSlice::new_offset(&partial.1[..], partial.0)), + } + } + + /// Advance partial offset if there is a partial. + pub(crate) fn advance_partial(&mut self, nb: usize) { + match self { + NodeMut::Extension(..) + | NodeMut::Branch ( .. ) + | NodeMut::Empty => (), + NodeMut::NibbledBranch(partial, ..) + | NodeMut::Leaf(partial, ..) => { + let mut new_partial: NibbleSlice = NibbleSlice::new_offset(partial.1.as_ref(), partial.0); + new_partial.advance(nb); + *partial = new_partial.into(); + }, + } + } + + /// Set partial if possible. + pub(crate) fn set_partial(&mut self, new_partial: NodeKey) { + match self { + NodeMut::Branch ( .. ) + | NodeMut::Empty => (), + NodeMut::Extension(partial, ..) + | NodeMut::NibbledBranch(partial, ..) + | NodeMut::Leaf(partial, ..) => { + *partial = new_partial; + }, + } + } + + /// Set value to node if possible. + pub(crate) fn has_value(&self) -> bool { + match self { + NodeMut::Extension(..) + | NodeMut::Empty => false, + NodeMut::Branch (_, val ) + | NodeMut::NibbledBranch(_, _, val) => val.is_some(), + NodeMut::Leaf(_, _) => true, + } + } + + /// Set value to node if possible. + pub(crate) fn set_value(&mut self, value: &[u8]) { + match self { + NodeMut::Extension(..) + | NodeMut::Empty => (), + NodeMut::Branch ( _, val ) + | NodeMut::NibbledBranch(_, _, val) + => *val = Some(value.into()), + NodeMut::Leaf(_, val) + => *val = value.into(), + } + } + + /// Return true if the node can be removed to. + pub(crate) fn remove_value(&mut self) -> bool { + match self { + NodeMut::Extension(..) + | NodeMut::Empty => false, + NodeMut::Branch(encoded_children, val) + | NodeMut::NibbledBranch(_, encoded_children, val) + => { + *val = None; + !encoded_children.iter().any(Option::is_some) + }, + NodeMut::Leaf(..) + => true, + } + } + + /// This is only for no extension trie (a variant would be + /// needed for trie with extension). + pub(crate) fn set_handle( + &mut self, + handle: Option>, + index: u8, + ) { + let index = index as usize; + let node = mem::replace(self, NodeMut::Empty); + *self = match node { + NodeMut::Extension(..) + | NodeMut::Branch(..) => unimplemented!("{}", NO_EXTENSION_ONLY), + NodeMut::Empty => unreachable!("Do not add handle to empty but replace the node instead"), + NodeMut::NibbledBranch(partial, encoded_children, val) + if handle.is_none() && encoded_children[index].is_none() => { + NodeMut::NibbledBranch(partial, encoded_children, val) + }, + NodeMut::NibbledBranch(partial, mut encoded_children, val) => { + encoded_children[index] = handle; + NodeMut::NibbledBranch(partial, encoded_children, val) + }, + NodeMut::Leaf(partial, val) if handle.is_some() => { + let mut children = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + children[index] = handle; + + NodeMut::NibbledBranch(partial, children, Some(val)) + }, + n@NodeMut::Leaf(..) => n, + }; + } + + /// Try to fuse a node, possibly changing a branch into a leaf. + /// If node was deleted, true is returned. + /// If node can be fused with a child, it is unchanged and the + /// child index is returned. + pub(crate) fn try_fuse_node( + &mut self, + pending: (Option, Option), + ) -> (bool, Option) { + let node = mem::replace(self, NodeMut::Empty); + let (node, fuse) = match node { + NodeMut::Extension(..) + | NodeMut::Branch(..) => unreachable!("Only for no extension trie"), + n@NodeMut::Empty + | n@NodeMut::Leaf(..) => (n, None), + NodeMut::NibbledBranch(partial, encoded_children, val) => { + let mut count = 0; + let mut other_index = None; + if let Some(pending) = pending.0 { + if encoded_children[pending as usize].is_none() { + count += 1; + other_index = Some(pending); + } + } + if let Some(pending) = pending.1 { + if encoded_children[pending as usize].is_none() { + count += 1; + other_index = Some(pending); + } + } + for c in encoded_children.iter() { + if c.is_some() { + count += 1; + } + if count > 1 { + break; + } + } + if val.is_some() && count == 0 { + (NodeMut::Leaf(partial, val.expect("Tested above")), None) + } else if val.is_none() && count == 0 { + (NodeMut::Empty, None) + } else if val.is_none() && count == 1 { + let child_ix = encoded_children.iter().position(Option::is_some) + .unwrap_or_else(|| other_index.expect("counted above") as usize); + (NodeMut::NibbledBranch(partial, encoded_children, val), Some(child_ix as u8)) + } else { + (NodeMut::NibbledBranch(partial, encoded_children, val), None) + } + }, + }; + *self = node; + (if let NodeMut::Empty = self { + true + } else { + false + }, fuse) + } + + pub(crate) fn new_leaf(prefix: NibbleSlice, value: &[u8]) -> Self { + NodeMut::Leaf(prefix.to_stored(), value.into()) + } + + pub(crate) fn empty_branch(prefix: NibbleSlice) -> Self { + let children = Box::new([ + None, None, None, None, + None, None, None, None, + None, None, None, None, + None, None, None, None, + ]); + + NodeMut::NibbledBranch(prefix.to_stored(), children, None) + } +} + +impl, SH: AsRef<[u8]>> NodeMut { + /// Try to access child. + pub fn child(&self, ix: u8) -> Option { + match self { + NodeMut::Leaf { .. } + | NodeMut::Extension { .. } + | NodeMut::Empty => None, + NodeMut::NibbledBranch ( _, children, _ ) + | NodeMut::Branch ( children, .. ) => + children[ix as usize].as_ref().map(|child| child.as_ref()), + } + } } #[cfg(feature = "std")] @@ -99,7 +348,7 @@ impl<'a> Debug for ToHex<'a> { } #[cfg(feature = "std")] -impl Debug for Node { +impl Debug for NodeMut { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match *self { Self::Empty => write!(fmt, "Empty"), @@ -115,10 +364,15 @@ impl Debug for Node { } } + +type NodeHandle = NodeHandleMut; + +type Node = NodeMut; + impl Node -where - O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug - + PartialEq + Eq + Hash + Send + Sync + Clone + Copy + where + 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( @@ -201,18 +455,24 @@ where }; Ok(node) } +} - // TODO: parallelize - fn into_encoded(self, mut child_cb: F) -> Vec +impl NodeMut +where + O: AsRef<[u8]> + AsMut<[u8]> + Default + crate::MaybeDebug + + PartialEq + Eq + Hash + Send + Sync + Clone + Copy +{ +/* pub(crate) fn into_encoded_ref(&self, mut child_cb: F) -> Vec where - C: NodeCodec, - F: FnMut(NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, + C: NodeCodec, + F: FnMut(&NodeHandle, Option<&NibbleSlice>, Option) -> ChildReference, H: Hasher, { match self { Node::Empty => C::empty_node().to_vec(), Node::Leaf(partial, value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); +// println!("d{:?} {:?}", pr.right(), value); C::leaf_node(pr.right(), &value) }, Node::Extension(partial, child) => { @@ -225,7 +485,64 @@ where c, ) }, - Node::Branch(mut children, value) => { + Node::Branch(children, value) => { + C::branch_node( + // map the `NodeHandle`s from the Branch to `ChildReferences` + children.iter() + .enumerate() + .map(|(i, maybe_child)| { + maybe_child.as_ref().map(|child| child_cb(&child, None, Some(i as u8))) + }), + value.as_ref().map(|v| &v[..]) + ) + }, + Node::NibbledBranch(partial, children, value) => { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + let it = pr.right_iter(); + C::branch_node_nibbled( + it, + pr.len(), + // map the `NodeHandle`s from the Branch to `ChildReferences` + children.iter() + .enumerate() + .map(|(i, maybe_child)| { + //let branch_index = [i as u8]; + maybe_child.as_ref().map(|child| { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + child_cb(&child, Some(&pr), Some(i as u8)) + }) + }), + value.as_ref().map(|v| &v[..]) + ) + }, + } + }*/ + + // TODO: parallelize + pub(crate) fn into_encoded(self, mut child_cb: F) -> Vec + where + C: NodeCodec, + F: FnMut(NodeHandleMut, Option<&NibbleSlice>, Option) -> ChildReference, + H: Hasher, + { + match self { + NodeMut::Empty => C::empty_node().to_vec(), + NodeMut::Leaf(partial, value) => { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); +// println!("d{:?} {:?}", pr.right(), value); + C::leaf_node(pr.right(), &value) + }, + NodeMut::Extension(partial, child) => { + let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); + let it = pr.right_iter(); + let c = child_cb(child, Some(&pr), None); + C::extension_node( + it, + pr.len(), + c, + ) + }, + NodeMut::Branch(mut children, value) => { C::branch_node( // map the `NodeHandle`s from the Branch to `ChildReferences` children.iter_mut() @@ -237,7 +554,7 @@ where value.as_ref().map(|v| &v[..]) ) }, - Node::NibbledBranch(partial, mut children, value) => { + NodeMut::NibbledBranch(partial, mut children, value) => { let pr = NibbleSlice::new_offset(&partial.1[..], partial.0); let it = pr.right_iter(); C::branch_node_nibbled( @@ -1179,7 +1496,7 @@ where None, One(u8), Many, - }; + } let mut used_index = UsedIndex::None; for i in 0..16 { match (children[i].is_none(), &used_index) { @@ -1225,7 +1542,7 @@ where None, One(u8), Many, - }; + } let mut used_index = UsedIndex::None; for i in 0..16 { match (children[i].is_none(), &used_index) { @@ -1592,7 +1909,7 @@ where } /// combine two NodeKeys -fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { +pub(crate) fn combine_key(start: &mut NodeKey, end: (usize, &[u8])) { debug_assert!(start.0 < nibble_ops::NIBBLE_PER_BYTE); debug_assert!(end.0 < nibble_ops::NIBBLE_PER_BYTE); let final_offset = (start.0 + end.0) % nibble_ops::NIBBLE_PER_BYTE; diff --git a/trie-db/test/benches/bench.rs b/trie-db/test/benches/bench.rs index e2a63523..7b1b5e59 100644 --- a/trie-db/test/benches/bench.rs +++ b/trie-db/test/benches/bench.rs @@ -35,6 +35,8 @@ criterion_group!(benches, trie_iteration, nibble_common_prefix, trie_proof_verification, + trie_mut_same_key_single, + trie_mut_same_key_batch, ); criterion_main!(benches); @@ -102,7 +104,6 @@ fn root_b_big_v(c: &mut Criterion) { ); } - fn root_a_small_v(c: &mut Criterion) { let data : Vec, Vec)>> = vec![ input2(29, 204800, 32), @@ -166,7 +167,6 @@ fn root_old(c: &mut Criterion) { ); } - fn root_new(c: &mut Criterion) { let data : Vec, Vec)>> = vec![ input(1, 5120), @@ -351,8 +351,6 @@ fn trie_mut_ref_root_b(c: &mut Criterion) { data); } - - fn trie_mut_a(c: &mut Criterion) { use trie_db::TrieMut; use memory_db::HashKey; @@ -496,3 +494,64 @@ fn trie_proof_verification(c: &mut Criterion) { }) ); } + +fn trie_mut_same_key_single(c: &mut Criterion) { + use memory_db::PrefixedKey; + use trie_db::TrieMut; + let data : Vec<(Vec, Vec)> = input_unsorted(29, 204800, 32); + + let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut root = Default::default(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 0..data.len() { + let key: &[u8]= &data[i].0; + let val: &[u8] = &data[i].1; + t.insert(key, val).unwrap(); + } + } + + c.bench_function("trie_mut_same_key_single", move |b: &mut Bencher| + b.iter(|| { + let mut mdb = db.clone(); + let mut n_root = root.clone(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::from_existing(&mut mdb, &mut n_root).unwrap(); + for i in 0..data.len() { + let key: &[u8]= &data[i].0; + // change val to key + t.insert(key, key).unwrap(); + } + } + assert!(n_root != root); + })); +} + +fn trie_mut_same_key_batch(c: &mut Criterion) { + //use memory_db::HashKey; + use memory_db::PrefixedKey; + use trie_db::TrieMut; + let data : Vec<(Vec, Vec)> = input_unsorted(29, 204800, 32); + + let mut db = memory_db::MemoryDB::<_, PrefixedKey<_>, _>::default(); + let mut root = Default::default(); + { + let mut t = reference_trie::RefTrieDBMutNoExt::new(&mut db, &mut root); + for i in 0..data.len() { + let key: &[u8]= &data[i].0; + let val: &[u8] = &data[i].1; + t.insert(key, val).unwrap(); + } + } + + c.bench_function("trie_mut_same_key_batch", move |b: &mut Bencher| + b.iter(|| { + let mut mdb = db.clone(); + // sort + let data: std::collections::BTreeSet> = data.iter().map(|(a, _b)| a.clone()).collect(); + let (calc_root, _payload, _detached) = reference_trie::trie_traverse_key_no_extension_build( + &mut mdb, &root, data.iter().map(|a| (a, Some(&a[..]))) + ); + assert!(calc_root != root); + })); +} diff --git a/trie-db/test/src/lib.rs b/trie-db/test/src/lib.rs index 592451e8..46412ad2 100644 --- a/trie-db/test/src/lib.rs +++ b/trie-db/test/src/lib.rs @@ -37,3 +37,5 @@ mod recorder; mod iter_build; #[cfg(test)] mod trie_codec; +#[cfg(test)] +mod traverse; diff --git a/trie-db/test/src/traverse.rs b/trie-db/test/src/traverse.rs new file mode 100644 index 00000000..3c3d6ea1 --- /dev/null +++ b/trie-db/test/src/traverse.rs @@ -0,0 +1,618 @@ +// Copyright 2017, 2021 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traverse tests. + +use reference_trie::{RefTrieDBMutNoExt, RefTrieDBNoExt, InputAction, +trie_traverse_key_no_extension_build, NoExtensionLayout, batch_update, +unprefixed_detached_trie, prefixed_detached_trie, +}; + +use memory_db::{MemoryDB, PrefixedKey}; +// use memory_db::{MemoryDB, HashKey as PrefixedKey}; +use keccak_hasher::KeccakHasher; +use trie_db::{DBValue, OwnedPrefix, TrieMut, NibbleSlice}; +use hash_db::HashDB; +use crate::triedbmut::populate_trie_no_extension; + +type H256 = ::Out; + +fn memory_db_from_delta( + delta: impl Iterator>)>, + mdb: &mut MemoryDB, DBValue>, + check_order: bool, +) { + // Ordering logic is almost always correct between delet and create (delete first in case of + // same location), there is one exception: deleting a node resulting to fusing a parent, then + // the parent fusing can write at a prior index. + // Therefore a `ProcessStack` that need ordering for delete will need a buffer. + // Such buffer will be at maximum the size of the stack depth minus one or the number of child + // in a branch (but since it is triggered by consecutive node fuse it should really be small). + // Then we limit this test to insert here. + let mut previous_prefix_insert = None; + //let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix, prev_delete: bool, is_delet: bool| { + let cp_prefix = |previous: &OwnedPrefix, next: &OwnedPrefix| { + if check_order { + println!("{:?} -> {:?}", previous, next); + /* if previous == next { + // we can have two same value if it is deletion then creation + assert!(prev_delete && !is_delet); + return; + }*/ + let prev_slice = NibbleSlice::new(previous.0.as_slice()); + let p_at = |i| { + if i < prev_slice.len() { + Some(prev_slice.at(i)) + } else if i == prev_slice.len() { + previous.1 + } else { + None + } + }; + + let next_slice = NibbleSlice::new(next.0.as_slice()); + let n_at = |i| { + if i < next_slice.len() { + Some(next_slice.at(i)) + } else if i == next_slice.len() { + next.1 + } else { + None + } + }; + let mut i = 0; + loop { + match (p_at(i), n_at(i)) { + (Some(p), Some(n)) => { + if p < n { + break; + } else if p == n { + i += 1; + } else { + panic!("Misordered results"); + } + }, + (Some(_p), None) => { + // moving upward is fine + break; + }, + (None, Some(_p)) => { + // next is bellow first, that is not correct + panic!("Misordered results"); + }, + (None, None) => { + panic!("Two consecutive action at same node") + //unreachable!("equality tested firsthand") + }, + } + } + } + }; + for (p, h, v) in delta { + let is_delete = v.is_none(); + if !is_delete { + previous_prefix_insert.as_ref().map(|prev| cp_prefix(prev, &p)); + } + + //println!("p{:?}, {:?}, {:?}", p, h, v); + if let Some(v) = v { + let prefix = (p.0.as_ref(), p.1); + // damn elastic array in value looks costy + mdb.emplace(h, prefix, v[..].into()); + } else { + let prefix = (p.0.as_ref(), p.1); + mdb.remove(&h, prefix); + } + if !is_delete { + previous_prefix_insert = Some(p); + } + } +} + +fn compare_with_triedbmut( + x: &[(Vec, Vec)], + v: &[(Vec, Option>)], +) { + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + populate_trie_no_extension(&mut db, &mut root, x).commit(); + { + let t = RefTrieDBNoExt::new(&db, &root); + println!("bef {:?}", t); + } + + println!("AB {:?}", db.clone().drain()); + let initial_root = root.clone(); + let mut initial_db = db.clone(); + // reference + { + let mut t = RefTrieDBMutNoExt::from_existing(&mut db, &mut root).unwrap(); + for i in 0..v.len() { + let key: &[u8]= &v[i].0; + if let Some(val) = v[i].1.as_ref() { + t.insert(key, val.as_ref()).unwrap(); + } else { + t.remove(key).unwrap(); + } + } + } + println!("AA {:?}", db.clone().drain()); + { + let t = RefTrieDBNoExt::new(&db, &root); + println!("aft {:?}", t); + } + + + let (calc_root, payload, _detached) = trie_traverse_key_no_extension_build( + &mut initial_db, + &initial_root, + v.iter().map(|(a, b)| (a, b.as_ref())), + ); + + assert_eq!(calc_root, root); + + let mut batch_delta = initial_db; + memory_db_from_delta(payload, &mut batch_delta, true); + // test by checking both triedb only + let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t2); + let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); + println!("{:?}", t2b); + + println!("{:?}", db.clone().drain()); + println!("{:?}", batch_delta.clone().drain()); + assert!(db == batch_delta); +} + +fn compare_with_triedbmut_detach( + x: &[(Vec, Vec)], + d: &Vec, +) { + let mut db = MemoryDB::, DBValue>::default(); + let mut root = Default::default(); + populate_trie_no_extension(&mut db, &mut root, x).commit(); + { + let t = RefTrieDBNoExt::new(&db, &root); + println!("bef {:?}", t); + } + let initial_root = root.clone(); + let initial_db = db.clone(); + // reference + { + let mut t = RefTrieDBMutNoExt::from_existing(&mut db, &mut root).unwrap(); + for i in 0..x.len() { + if x[i].0.starts_with(d) { + let key: &[u8]= &x[i].0; + t.remove(key).unwrap(); + } + } + } + { + let t = RefTrieDBNoExt::new(&db, &root); + println!("aft {:?}", t); + } + let elements = Some(d.clone()).into_iter().map(|k| (k, InputAction::, _>::Detach)); + let (calc_root, payload, payload_detached, detached_root) = batch_update::( + &initial_db, + &initial_root, + elements, + ).unwrap(); + + assert_eq!(calc_root, root); + + let mut batch_delta = initial_db.clone(); + memory_db_from_delta(payload.into_iter(), &mut batch_delta, true); + memory_db_from_delta(payload_detached.into_iter(), &mut batch_delta, false); + // test by checking both triedb only + let t2 = RefTrieDBNoExt::new(&db, &root).unwrap(); + println!("{:?}", t2); + let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); + println!("{:?}", t2b); + + if let Some((_k, _, d_root)) = detached_root.iter().next() { + unprefixed_detached_trie::( + &mut batch_delta, + None, + d_root.clone(), + d.as_ref(), + ).unwrap(); + let t2b = RefTrieDBNoExt::new(&batch_delta, d_root).unwrap(); + println!("{:?}", t2b); + prefixed_detached_trie::( + &mut batch_delta, + None, + d_root.clone(), + d.as_ref(), + ).unwrap(); + } + + println!("{:?}", db.clone().drain()); + println!("{:?}", batch_delta.clone().drain()); + + // attach back + let elements = detached_root.into_iter().map(|(k, _prefix, root)| (k, InputAction::, _>::Attach(root))); + let (calc_root, payload, payload_detached, _detached_root) = batch_update::( + &batch_delta, + &calc_root, + elements, + ).unwrap(); + // TODO this did not work previously for a valid reason: find it again and explain. + // assert!(detached_root.is_empty()); + memory_db_from_delta(payload.into_iter(), &mut batch_delta, true); + memory_db_from_delta(payload_detached.into_iter(), &mut batch_delta, false); + // test by checking both triedb only + let t2 = RefTrieDBNoExt::new(&initial_db, &initial_root).unwrap(); + println!("{:?}", t2); + let t2b = RefTrieDBNoExt::new(&batch_delta, &calc_root).unwrap(); + println!("{:?}", t2b); + assert!(calc_root == initial_root); + + println!("{:?}", initial_db.clone().drain()); + println!("{:?}", batch_delta.clone().drain()); + batch_delta.purge(); + assert!(initial_db == batch_delta); +} + +#[test] +fn empty_node_null_key() { + compare_with_triedbmut( + &[], + &[ + (vec![], Some(vec![0xffu8, 0x33])), + ], + ); + compare_with_triedbmut_detach(&[], &vec![]); +} + +#[test] +fn non_empty_node_null_key() { + let db = &[ + (vec![0x0u8], vec![4, 32]), + ]; + compare_with_triedbmut( + db, + &[ + (vec![], Some(vec![0xffu8, 0x33])), + ], + ); + compare_with_triedbmut_detach(db, &vec![]); + compare_with_triedbmut_detach(&[ + (vec![0x01u8, 0x23], vec![4, 32]), + ], &vec![0x01u8]); +} + +#[test] +fn empty_node_with_key() { + compare_with_triedbmut( + &[], + &[ + (vec![0x04u8], Some(vec![0xffu8, 0x33])), + ], + ); +} + +#[test] +fn simple_fuse() { + compare_with_triedbmut( + &[ + (vec![0x04u8], vec![4, 32]), + (vec![0x04, 0x04], vec![4, 33]), + (vec![0x04, 0x04, 0x04], vec![4, 35]), + ], + &[ + (vec![0x04u8, 0x04], None), + ], + ); +} + +#[test] +fn dummy1() { + let db = &[ + (vec![0x04u8], vec![4, 32]), + ]; + compare_with_triedbmut( + db, + &[ + (vec![0x06u8], Some(vec![0xffu8, 0x33])), + (vec![0x08u8], Some(vec![0xffu8, 0x33])), + ], + ); + compare_with_triedbmut_detach(db, &vec![0x04u8]); + compare_with_triedbmut_detach(db, &vec![0x04u8, 0x01]); + compare_with_triedbmut_detach(db, &vec![]); +} + +#[test] +fn two_recursive_mid_insert() { + compare_with_triedbmut( + &[ + (vec![0x0u8], vec![4, 32]), + ], + &[ + (vec![0x04u8], Some(vec![0xffu8, 0x33])), + (vec![0x20u8], Some(vec![0xffu8, 0x33])), + ], + ); +} + +#[test] +fn dummy2() { + let db = &[ + (vec![0x01u8, 0x01u8, 0x23], vec![0x01u8; 32]), + (vec![0x01u8, 0x81u8, 0x23], vec![0x02u8; 32]), + (vec![0x01u8, 0xf1u8, 0x23], vec![0x01u8, 0x24]), + ]; + compare_with_triedbmut( + db, + &[ + (vec![0x01u8, 0x01u8, 0x23], Some(vec![0xffu8; 32])), + (vec![0x01u8, 0x81u8, 0x23], Some(vec![0xfeu8; 32])), + (vec![0x01u8, 0x81u8, 0x23], None), + ], + ); + compare_with_triedbmut_detach(db, &vec![]); + compare_with_triedbmut_detach(db, &vec![0x02]); + compare_with_triedbmut_detach(db, &vec![0x01u8]); + compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81]); + compare_with_triedbmut_detach(db, &vec![0x01u8, 0x81, 0x23]); +} + +#[test] +fn dummy2_20() { + compare_with_triedbmut_detach(&[ + (vec![0], vec![0, 0]), + (vec![1], vec![0, 0]), + (vec![8], vec![1, 0]), + ], &vec![0]); + /* Detach does fuse a branch, and + * then when attaching it will swap. + * compare_with_triedbmut_detach(&[ + (vec![0], vec![50, 0]), + (vec![8], vec![0, 50]), + (vec![50], vec![0, 42]), + ], &vec![0]);*/ +} + +#[test] +fn dummy2_23() { + compare_with_triedbmut_detach(&[ + (vec![0], vec![3; 40]), + (vec![1], vec![2; 40]), + (vec![8], vec![1; 40]), + ], &vec![0]); +} + +#[test] +fn dettach_middle() { + let db = &[ + (vec![0x00u8, 0x01, 0x23], vec![0x01u8; 32]), + (vec![0x00, 0x01, 0x81u8, 0x23], vec![0x02u8; 32]), + (vec![0x00, 0x01, 0xf1u8, 0x23], vec![0x01u8, 0x24]), + ]; + compare_with_triedbmut_detach(db, &vec![0x00u8]); + compare_with_triedbmut_detach(db, &vec![0x00u8, 0x01, 0x81]); +} + +#[test] +fn delete_to_empty() { + compare_with_triedbmut( + &[ + (vec![1, 254u8], vec![4u8; 33]), + ], + &[ + (vec![1, 254u8], None), + ], + ); +} + +#[test] +fn fuse_root_node() { + compare_with_triedbmut( + &[ + (vec![2, 254u8], vec![4u8; 33]), + (vec![1, 254u8], vec![4u8; 33]), + ], + &[ + (vec![1, 254u8], None), + ], + ); +} + +#[test] +fn dummy4() { + compare_with_triedbmut( + &[ + (vec![255u8, 251, 127, 255, 255], vec![255, 255]), + (vec![255, 255, 127, 112, 255], vec![0, 4]), + (vec![255, 127, 114, 253, 195], vec![1, 2]), + ], + &[ + (vec![0u8], Some(vec![4; 251])), + (vec![255, 251, 127, 255, 255], Some(vec![1, 2])), + ], + ); +} + +#[test] +fn dummy6() { + compare_with_triedbmut( + &[ + (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], vec![255, 255]), + (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], vec![0, 4]), + (vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 208, 208, 208, 208, 208, 208], vec![1, 2]), + ], + &[ + (vec![0, 6, 8, 21, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 199, 215], Some(vec![4, 251])), + (vec![0, 144, 64, 212, 141, 1, 0, 0, 255, 144, 64, 212, 141, 1, 0, 141, 206, 0], None), + (vec![141, 135, 207, 0, 63, 203, 216, 185, 162, 77, 154, 214, 210, 0, 0, 0, 0, 128], Some(vec![49, 251])), + (vec![208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 6, 8, 21, 1, 4, 0], Some(vec![4, 21])), + ], + ); +} + +#[test] +fn fuse_with_child_partial() { + compare_with_triedbmut( + &[ + (vec![212], vec![212, 212]), + ], + &[ + (vec![58], Some(vec![63, 0])), + (vec![63], None), + (vec![212], None), + ], + ); +} + +#[test] +fn dummy7() { + compare_with_triedbmut( + &[ + (vec![0], vec![0, 212]), + (vec![8, 8], vec![0, 212]), + ], + &[ + (vec![0], None), + (vec![8, 0], Some(vec![63, 0])), + (vec![128], None), + ], + ); +} + +#[test] +fn dummy8() { + compare_with_triedbmut( + &[ + (vec![0], vec![0, 212]), + (vec![8, 8], vec![0, 212]), + ], + &[ + (vec![0], None), + (vec![8, 0], Some(vec![63, 0])), + (vec![128], Some(vec![63, 0])), + ], + ); +} + +#[test] +fn dummy9() { + compare_with_triedbmut( + &[ + (vec![0], vec![0, 212]), + (vec![1], vec![111, 22]), + ], + &[ + (vec![0], None), + (vec![5], Some(vec![63, 0])), + (vec![14], None), + (vec![64], Some(vec![63, 0])), + ], + ); +} + +#[test] +fn dummy_51() { + compare_with_triedbmut( + &[ + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + ], + &[ + (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), + (vec![128], Some(vec![49, 251])), + ], + ); +} + +#[test] +fn emptied_then_insert() { + compare_with_triedbmut( + &[ + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + ], + &[ + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), + (vec![128], Some(vec![49, 251])), + ], + ); +} + +#[test] +fn dummy5() { + compare_with_triedbmut( + &[ + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], vec![1, 2]), + ], + &[ + (vec![9, 1, 141, 44, 212, 0, 0, 51, 138, 32], Some(vec![4, 251])), + (vec![9, 9, 9, 9, 9, 9, 9, 9, 9, 9], None), + (vec![128], Some(vec![49, 251])), + ], + ); +} + +#[test] +fn dummy_big() { + compare_with_triedbmut( + &[ + (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], vec![1, 2]), + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], vec![1, 2]), + + ], + &[ + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144], None), + (vec![144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, 208], Some(vec![4; 32])), + (vec![255, 255, 255, 255, 255, 255, 15, 0, 98, 34, 255, 0, 197, 193, 31, 5, 64, 0, 248, 197, 247, 231, 58, 0, 3, 214, 1, 192, 122, 39, 226, 0], None), + ], + ); +} + +#[test] +fn single_latest_change_value_does_not_work() { + compare_with_triedbmut( + &[ + (vec![0, 0, 0, 0], vec![255;32]), + (vec![0, 0, 0, 3], vec![5; 32]), + (vec![0, 0, 6, 0], vec![6; 32]), + (vec![0, 0, 0, 170], vec![1; 32]), + (vec![0, 0, 73, 0], vec![2; 32]), + (vec![0, 0, 0, 0], vec![3; 32]), + (vec![0, 199, 141, 0], vec![4; 32]), + ], + &[ + (vec![0, 0, 0, 0], Some(vec![0; 32])), + (vec![0, 0, 199, 141], Some(vec![0; 32])), + (vec![0, 199, 141, 0], None), + (vec![12, 0, 128, 0, 0, 0, 0, 0, 0, 4, 64, 2, 4], Some(vec![0; 32])), + (vec![91], None), + ], + ); +} + +#[test] +fn chained_fuse() { + compare_with_triedbmut( + &[ + (vec![0u8], vec![1; 32]), + (vec![0, 212], vec![2; 32]), + (vec![0, 212, 96], vec![3; 32]), + (vec![0, 212, 96, 88], vec![3; 32]), + ], + &[ + (vec![0u8], None), + (vec![0, 212], None), + (vec![0, 212, 96], None), + (vec![0, 212, 96, 88], Some(vec![3; 32])), + ], + ); +} diff --git a/trie-db/test/src/triedbmut.rs b/trie-db/test/src/triedbmut.rs index 7103af76..d434f6dc 100644 --- a/trie-db/test/src/triedbmut.rs +++ b/trie-db/test/src/triedbmut.rs @@ -43,7 +43,7 @@ fn unpopulate_trie<'db>(t: &mut RefTrieDBMut<'db>, v: &[(Vec, Vec)]) { } } -fn populate_trie_no_extension<'db>( +pub(crate) fn populate_trie_no_extension<'db>( db: &'db mut dyn HashDB, root: &'db mut ::Out, v: &[(Vec, Vec)]