diff --git a/Cargo.toml b/Cargo.toml index d86f964..ece5d4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ keccak-hash = "0.10.0" parking_lot = { version = "0.12.1", features = ["serde"] } thiserror = "1.0.40" log = "0.4.17" +num = { version = "0.4.1", optional = true } num-traits = "0.2.15" uint = "0.9.5" rlp = "0.5.2" @@ -39,7 +40,7 @@ serde_json = "1.0.96" [features] default = ["trie_debug"] -trie_debug = [] +trie_debug = ["num"] [lib] doc-scrape-examples = true diff --git a/src/debug_tools/mod.rs b/src/debug_tools/mod.rs index c05eb26..8e25c6a 100644 --- a/src/debug_tools/mod.rs +++ b/src/debug_tools/mod.rs @@ -4,3 +4,4 @@ pub mod common; pub mod diff; pub mod query; +pub mod stats; diff --git a/src/debug_tools/stats.rs b/src/debug_tools/stats.rs new file mode 100644 index 0000000..38429e6 --- /dev/null +++ b/src/debug_tools/stats.rs @@ -0,0 +1,382 @@ +//! Simple tooling to extract stats from tries. +//! +//! This is particularly useful when comparing a "base" trie against a sub-trie +//! (hashed out trie) created from it. + +use std::fmt::{self, Display}; + +use num_traits::ToPrimitive; + +use crate::partial_trie::{Node, PartialTrie}; + +#[derive(Debug, Default)] +pub struct TrieStats { + name: Option, + counts: NodeCounts, + depth_stats: DepthStats, +} + +impl Display for TrieStats { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Trie Stats:")?; + + match self.name.as_ref() { + Some(name) => writeln!(f, " ({})", name)?, + None => writeln!(f)?, + } + + writeln!(f, "Counts:\n{}", self.counts)?; + writeln!(f, "Depth stats:\n{}", self.depth_stats) + } +} + +impl TrieStats { + pub fn compare(&self, other: &Self) -> TrieComparison { + TrieComparison { + node_comp: self.counts.compare(&other.counts), + depth_comp: self.depth_stats.compare(&other.depth_stats), + } + } +} + +/// Total node counts for a trie. +#[derive(Debug, Default)] +struct NodeCounts { + empty: usize, + hash: usize, + branch: usize, + extension: usize, + leaf: usize, +} + +impl Display for NodeCounts { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let tot_nodes = self.total_nodes(); + + Self::write_node_count_stats(f, "Empty", self.empty, tot_nodes)?; + Self::write_node_count_stats(f, "Hash", self.hash, tot_nodes)?; + Self::write_node_count_stats(f, "Branch", self.branch, tot_nodes)?; + Self::write_node_count_stats(f, "Extension", self.extension, tot_nodes)?; + Self::write_node_count_stats(f, "Leaf", self.leaf, tot_nodes) + } +} + +impl NodeCounts { + fn write_node_count_stats( + f: &mut fmt::Formatter<'_>, + node_t_name: &str, + count: usize, + tot_count: usize, + ) -> fmt::Result { + let perc = (count as f32 / tot_count as f32) * 100.0; + writeln!(f, "{}: {} ({:.2}%)", node_t_name, count, perc) + } +} + +impl NodeCounts { + fn total_nodes(&self) -> usize { + self.empty + self.total_node_non_empty() + } + + fn total_node_non_empty(&self) -> usize { + self.branch + self.extension + self.hash_and_leaf_node_count() + } + + fn hash_and_leaf_node_count(&self) -> usize { + self.hash + self.leaf + } + + fn compare(&self, other: &Self) -> NodeComparison { + NodeComparison { + tot_node_rat: RatioStat::new(self.total_nodes(), other.total_nodes()), + non_empty_rat: RatioStat::new( + self.total_node_non_empty(), + other.total_node_non_empty(), + ), + empty_rat: RatioStat::new(self.empty, other.empty), + hash_rat: RatioStat::new(self.hash, other.hash), + branch_rat: RatioStat::new(self.branch, other.branch), + extension_rat: RatioStat::new(self.extension, other.extension), + leaf_rat: RatioStat::new(self.leaf, other.leaf), + } + } +} + +/// Information on the comparison between two tries. +#[derive(Debug)] +pub struct TrieComparison { + node_comp: NodeComparison, + depth_comp: DepthComparison, +} + +impl Display for TrieComparison { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Node comparison: {}", self.node_comp)?; + writeln!(f, "Depth comparison: {}", self.depth_comp) + } +} + +// TODO: Consider computing these values lazily? +#[derive(Debug)] +struct NodeComparison { + tot_node_rat: RatioStat, + non_empty_rat: RatioStat, + + empty_rat: RatioStat, + hash_rat: RatioStat, + branch_rat: RatioStat, + extension_rat: RatioStat, + leaf_rat: RatioStat, +} + +impl Display for NodeComparison { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Total nodes: {}", self.tot_node_rat)?; + writeln!(f, "Non-empty: {}", self.non_empty_rat)?; + + writeln!(f, "Total empty: {}", self.empty_rat)?; + writeln!(f, "Total hash: {}", self.hash_rat)?; + writeln!(f, "Total branch: {}", self.branch_rat)?; + writeln!(f, "Total extension: {}", self.extension_rat)?; + writeln!(f, "Total leaf: {}", self.leaf_rat) + } +} + +#[derive(Debug)] +struct DepthComparison { + lowest_depth_rat: RatioStat, + avg_leaf_depth_rat: RatioStat, + avg_hash_depth_rat: RatioStat, +} + +impl Display for DepthComparison { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Lowest depth: {}", self.lowest_depth_rat)?; + writeln!(f, "Avg leaf depth: {}", self.avg_leaf_depth_rat)?; + writeln!(f, "Avg hash depth: {}", self.avg_hash_depth_rat) + } +} + +/// Type to hold (and compare) a given variable from two different tries.s +#[derive(Debug)] +struct RatioStat { + a: T, + b: T, +} + +impl Display for RatioStat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{:.3} / {:.3} ({:.3}%)", + self.a, + self.b, + // Note that the `Error` type for `fmt` does not hold any extra information and can + // only indicate that something went wrong. + self.get_a_over_b_perc().map_err(|_| fmt::Error)? + ) + } +} + +impl RatioStat { + /// `new` doesn't have any logic, but this will reduce a lot of line lengths + /// since this is called so many times. + fn new(a: T, b: T) -> Self { + Self { a, b } + } + + fn get_a_over_b_perc(&self) -> Result { + Ok((Self::try_to_f32(&self.a)? / Self::try_to_f32(&self.b)?) * 100.0) + } + + fn try_to_f32(v: &T) -> Result { + v.to_f32().ok_or(()) + } +} + +/// "Raw" state that is mutated as we traverse down the trie. Is processed into +/// a more useful format later on. +#[derive(Debug, Default)] +struct CurrTrackingState { + counts: NodeCounts, + + // The "*_sum" variables are just accumulators that we process later to get average depths. + leaf_depth_sum: u64, + hash_depth_sum: u64, + lowest_depth: usize, +} + +impl CurrTrackingState { + fn update_lowest_depth_if_larger(&mut self, curr_depth: usize) { + if self.lowest_depth < curr_depth { + self.lowest_depth = curr_depth; + } + } +} + +/// Depth in terms of node depth (not key length). +#[derive(Clone, Debug, Default)] +struct DepthStats { + lowest_depth: usize, + avg_leaf_depth: f32, + avg_hash_depth: f32, +} + +impl Display for DepthStats { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "Lowest depth: {}", self.lowest_depth)?; + writeln!(f, "Average leaf depth: {:.3}", self.avg_leaf_depth)?; + writeln!(f, "Average hash depth: {:.3}", self.avg_hash_depth) + } +} + +impl DepthStats { + fn compare(&self, other: &Self) -> DepthComparison { + DepthComparison { + lowest_depth_rat: RatioStat::new(self.lowest_depth, other.lowest_depth), + avg_leaf_depth_rat: RatioStat::new(self.avg_leaf_depth, other.avg_leaf_depth), + avg_hash_depth_rat: RatioStat::new(self.avg_hash_depth, other.avg_hash_depth), + } + } +} + +pub fn get_trie_stats(trie: &T) -> TrieStats { + get_trie_stats_common(trie, None) +} + +pub fn get_trie_stats_with_name(trie: &T, name: String) -> TrieStats { + get_trie_stats_common(trie, Some(name)) +} + +fn get_trie_stats_common(trie: &T, name: Option) -> TrieStats { + let mut state = CurrTrackingState::default(); + + get_trie_stats_rec(trie, &mut state, 0); + + let depth_stats = DepthStats { + lowest_depth: state.lowest_depth, + avg_leaf_depth: state.leaf_depth_sum as f32 / state.counts.leaf as f32, + avg_hash_depth: state.hash_depth_sum as f32 / state.counts.hash as f32, + }; + + TrieStats { + name, + counts: state.counts, + depth_stats, + } +} + +fn get_trie_stats_rec( + node: &Node, + state: &mut CurrTrackingState, + curr_depth: usize, +) { + match node { + Node::Empty => { + state.counts.empty += 1; + } + Node::Hash(_) => { + state.counts.hash += 1; + state.hash_depth_sum += curr_depth as u64; + state.update_lowest_depth_if_larger(curr_depth); + } + Node::Branch { children, value: _ } => { + state.counts.branch += 1; + + for c in children { + get_trie_stats_rec(c, state, curr_depth + 1); + } + } + Node::Extension { nibbles: _, child } => { + state.counts.extension += 1; + get_trie_stats_rec(child, state, curr_depth + 1); + } + Node::Leaf { + nibbles: _, + value: _, + } => { + state.counts.leaf += 1; + state.leaf_depth_sum += curr_depth as u64; + state.update_lowest_depth_if_larger(curr_depth); + } + } +} + +#[cfg(test)] +mod tests { + use super::get_trie_stats; + use crate::{ + partial_trie::{HashedPartialTrie, PartialTrie}, + testing_utils::{ + generate_n_random_fixed_trie_hash_entries, generate_n_random_fixed_trie_value_entries, + handmade_trie_1, + }, + }; + + const MASSIVE_TRIE_SIZE: usize = 100_000; + + #[test] + fn hand_made_trie_has_correct_node_stats() { + let (trie, _) = handmade_trie_1(); + let stats = get_trie_stats(&trie); + + assert_eq!(stats.counts.leaf, 4); + assert_eq!(stats.counts.hash, 0); + assert_eq!(stats.counts.branch, 4); + assert_eq!(stats.counts.extension, 2); + + // empty = (n_branch * 4) - n_leaf - (n_branch - 1) + assert_eq!(stats.counts.empty, 57); + } + + // TODO: Low-priority. Finish later. + #[test] + #[ignore] + fn perfectly_balanced_trie_has_correct_node_stats() { + todo!() + } + + #[test] + fn massive_leaf_trie_has_correct_leaf_node_stats() { + create_trie_and_stats_from_entries_and_assert(MASSIVE_TRIE_SIZE, 0, 9522); + } + + #[test] + fn massive_hash_trie_has_correct_hash_node_stats() { + create_trie_and_stats_from_entries_and_assert(0, MASSIVE_TRIE_SIZE, 9855); + } + + #[test] + fn massive_mixed_trie_has_correct_hash_node_stats() { + create_trie_and_stats_from_entries_and_assert( + MASSIVE_TRIE_SIZE / 2, + MASSIVE_TRIE_SIZE / 2, + 1992, + ); + } + + fn create_trie_and_stats_from_entries_and_assert( + n_leaf_nodes: usize, + n_hash_nodes: usize, + seed: u64, + ) { + let val_entries = generate_n_random_fixed_trie_value_entries(n_leaf_nodes, seed); + let hash_entries = generate_n_random_fixed_trie_hash_entries(n_hash_nodes, seed + 1); + + let mut trie = HashedPartialTrie::default(); + trie.extend(val_entries); + trie.extend(hash_entries); + + let stats = get_trie_stats(&trie); + + assert_eq!(stats.counts.leaf, n_leaf_nodes); + assert_eq!(stats.counts.hash, n_hash_nodes); + } + + // TODO: Low-priority. Finish later. + #[test] + #[ignore] + fn depth_stats_work() { + todo!() + } +} diff --git a/src/lib.rs b/src/lib.rs index 676e19b..aa2df51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,4 +23,4 @@ mod utils; pub mod debug_tools; #[cfg(test)] -mod testing_utils; +pub(crate) mod testing_utils; diff --git a/src/nibbles.rs b/src/nibbles.rs index b463feb..730d924 100644 --- a/src/nibbles.rs +++ b/src/nibbles.rs @@ -607,6 +607,8 @@ impl Nibbles { let hex_string_raw = hex_encode_f(&byte_buf[(64 - count_bytes)..64]); let hex_char_iter_raw = hex_string_raw.chars(); + // We need this skip to make both match arms have the same type. + #[allow(clippy::iter_skip_zero)] let mut hex_string = String::from("0x"); match is_even(self.count) { false => hex_string.extend(hex_char_iter_raw.skip(1)), diff --git a/src/testing_utils.rs b/src/testing_utils.rs index ef98045..51b40e5 100644 --- a/src/testing_utils.rs +++ b/src/testing_utils.rs @@ -5,11 +5,11 @@ use std::{ use ethereum_types::{H256, U256, U512}; use log::info; -use rand::{rngs::StdRng, seq::IteratorRandom, Rng, SeedableRng}; +use rand::{rngs::StdRng, seq::IteratorRandom, Rng, RngCore, SeedableRng}; use crate::{ nibbles::Nibbles, - partial_trie::{Node, PartialTrie}, + partial_trie::{HashedPartialTrie, Node, PartialTrie}, trie_ops::ValOrHash, utils::is_even, }; @@ -20,8 +20,11 @@ use crate::{ /// chances of these collisions occurring. const MIN_BYTES_FOR_VAR_KEY: usize = 5; +pub(crate) type TrieType = HashedPartialTrie; + pub(crate) type TestInsertValEntry = (Nibbles, Vec); pub(crate) type TestInsertHashEntry = (Nibbles, H256); +type TestInsertEntry = (Nibbles, T); // Don't want this exposed publicly, but it is useful for testing. impl From for Nibbles { @@ -70,34 +73,51 @@ where (k.into(), vec![v]) } -pub(crate) fn generate_n_random_fixed_trie_entries( +pub(crate) fn generate_n_random_fixed_trie_value_entries( n: usize, seed: u64, ) -> impl Iterator { - gen_n_random_trie_entries_common(n, seed, gen_fixed_nibbles) + gen_n_random_trie_value_entries_common(n, seed, gen_fixed_nibbles, gen_rand_u256_bytes) } -pub(crate) fn generate_n_random_variable_keys( +pub(crate) fn generate_n_random_fixed_trie_hash_entries( n: usize, seed: u64, -) -> impl Iterator { - gen_n_random_trie_entries_common(n, seed, gen_variable_nibbles) +) -> impl Iterator { + gen_n_random_trie_value_entries_common(n, seed, gen_fixed_nibbles, |_| H256::random()) } -pub(crate) fn generate_n_random_fixed_even_nibble_padded_trie_entries( +pub(crate) fn generate_n_random_variable_trie_value_entries( n: usize, seed: u64, ) -> impl Iterator { - gen_n_random_trie_entries_common(n, seed, gen_variable_nibbles_even_padded_nibbles) + gen_n_random_trie_value_entries_common(n, seed, gen_variable_nibbles, gen_rand_u256_bytes) } -fn gen_n_random_trie_entries_common Nibbles>( +pub(crate) fn generate_n_random_fixed_even_nibble_padded_trie_value_entries( n: usize, seed: u64, - u256_gen_f: F, ) -> impl Iterator { + gen_n_random_trie_value_entries_common( + n, + seed, + gen_variable_nibbles_even_padded_nibbles, + gen_rand_u256_bytes, + ) +} + +fn gen_n_random_trie_value_entries_common< + T, + K: Fn(&mut StdRng) -> Nibbles, + V: Fn(&mut StdRng) -> T, +>( + n: usize, + seed: u64, + key_gen_f: K, + val_gen_f: V, +) -> impl Iterator> { let mut rng = StdRng::seed_from_u64(seed); - (0..n).map(move |i| (u256_gen_f(&mut rng), i.to_be_bytes().to_vec())) + (0..n).map(move |_| (key_gen_f(&mut rng), val_gen_f(&mut rng))) } pub(crate) fn generate_n_hash_nodes_entries_for_empty_slots_in_trie( @@ -144,7 +164,6 @@ fn gen_variable_nibbles(rng: &mut StdRng) -> Nibbles { U256::from_little_endian(&bytes).into() } - // TODO: Replace with `PartialTrie` `iter` methods once done... pub(crate) fn get_non_hash_values_in_trie( trie: &Node, @@ -161,3 +180,44 @@ pub(crate) fn unwrap_iter_item_to_val(item: ValOrHash) -> Vec { ValOrHash::Hash(_) => unreachable!(), } } + +fn gen_rand_u256_bytes(rng: &mut StdRng) -> Vec { + let num_bytes = 256 / 8; + + let mut buf = vec![0; num_bytes]; + rng.fill_bytes(&mut buf); + + buf +} + +/// Initializes a trie with keys large enough to force hashing (nodes less than +/// 32 bytes are not hashed). +pub(crate) fn create_trie_with_large_entry_nodes + Copy>(keys: &[T]) -> TrieType { + let mut trie = TrieType::default(); + for (k, v) in keys.iter().map(|k| (*k).into()).map(large_entry) { + trie.insert(k, v.clone()); + } + + trie +} + +pub(crate) fn handmade_trie_1() -> (TrieType, Vec) { + let ks = vec![0x1234, 0x1324, 0x132400005_u64, 0x2001, 0x2002]; + let ks_nibbles: Vec = ks.into_iter().map(|k| k.into()).collect(); + let trie = create_trie_with_large_entry_nodes(&ks_nibbles); + + // Branch (0x) --> 1, 2 + // Branch (0x1) --> 2, 3 + // Leaf (0x1234) --> (n: 0x34, v: [0]) + + // Extension (0x13) --> n: 0x24 + // Branch (0x1324, v: [1]) --> 0 + // Leaf (0x132400005) --> (0x0005, v: [2]) + + // Extension (0x2) --> n: 0x00 + // Branch (0x200) --> 1, 2 + // Leaf (0x2001) --> (n: 0x1, v: [3]) + // Leaf (0x2002) --> (n: 0x2, v: [4]) + + (trie, ks_nibbles) +} diff --git a/src/trie_hashing.rs b/src/trie_hashing.rs index 55acab1..b6dc864 100644 --- a/src/trie_hashing.rs +++ b/src/trie_hashing.rs @@ -108,9 +108,9 @@ mod tests { nibbles::{Nibble, Nibbles}, partial_trie::{HashedPartialTrie, Node, PartialTrie, WrappedNode}, testing_utils::{ - common_setup, entry, generate_n_random_fixed_even_nibble_padded_trie_entries, - generate_n_random_fixed_trie_entries, generate_n_random_variable_keys, large_entry, - TestInsertValEntry, + common_setup, entry, generate_n_random_fixed_even_nibble_padded_trie_value_entries, + generate_n_random_fixed_trie_value_entries, + generate_n_random_variable_trie_value_entries, large_entry, TestInsertValEntry, }, trie_hashing::hash_bytes, }; @@ -306,8 +306,11 @@ mod tests { fn massive_random_data_insert_fixed_keys_hashes_agree_with_eth_trie() { common_setup(); insert_entries_into_our_and_lib_tries_and_assert_equal_hashes( - &generate_n_random_fixed_trie_entries(NUM_INSERTS_FOR_ETH_TRIE_CRATE_MASSIVE_TEST, 0) - .collect::>(), + &generate_n_random_fixed_trie_value_entries( + NUM_INSERTS_FOR_ETH_TRIE_CRATE_MASSIVE_TEST, + 0, + ) + .collect::>(), ); } @@ -315,7 +318,7 @@ mod tests { fn massive_random_data_insert_variable_keys_hashes_agree_with_eth_trie() { common_setup(); insert_entries_into_our_and_lib_tries_and_assert_equal_hashes( - &generate_n_random_fixed_even_nibble_padded_trie_entries( + &generate_n_random_fixed_even_nibble_padded_trie_value_entries( NUM_INSERTS_FOR_ETH_TRIE_CRATE_MASSIVE_TEST, 0, ) @@ -357,7 +360,7 @@ mod tests { fn massive_trie_data_deletion_agrees_with_eth_trie() { common_setup(); - let entries: Vec<_> = generate_n_random_fixed_even_nibble_padded_trie_entries( + let entries: Vec<_> = generate_n_random_fixed_even_nibble_padded_trie_value_entries( NUM_INSERTS_FOR_ETH_TRIE_CRATE_MASSIVE_TEST, 8, ) @@ -412,15 +415,17 @@ mod tests { #[test] fn replacing_part_of_a_trie_with_a_hash_node_produces_same_hash() { let entries = (0..16).flat_map(|i| { - generate_n_random_variable_keys(NODES_PER_BRANCH_FOR_HASH_REPLACEMENT_TEST, i).map( - move |(mut k, v)| { - // Force all keys to be under a given branch at root. - k.truncate_n_nibbles_front_mut(1); - k.push_nibble_front(i as Nibble); - - (k, v) - }, + generate_n_random_variable_trie_value_entries( + NODES_PER_BRANCH_FOR_HASH_REPLACEMENT_TEST, + i, ) + .map(move |(mut k, v)| { + // Force all keys to be under a given branch at root. + k.truncate_n_nibbles_front_mut(1); + k.push_nibble_front(i as Nibble); + + (k, v) + }) }); let mut trie = HashedPartialTrie::from_iter(entries); diff --git a/src/trie_ops.rs b/src/trie_ops.rs index 24f78a0..7883e47 100644 --- a/src/trie_ops.rs +++ b/src/trie_ops.rs @@ -778,8 +778,9 @@ mod tests { testing_utils::{ common_setup, entry, entry_with_value, generate_n_hash_nodes_entries_for_empty_slots_in_trie, - generate_n_random_fixed_trie_entries, generate_n_random_variable_keys, - get_non_hash_values_in_trie, unwrap_iter_item_to_val, TestInsertValEntry, + generate_n_random_fixed_trie_value_entries, + generate_n_random_variable_trie_value_entries, get_non_hash_values_in_trie, + unwrap_iter_item_to_val, TestInsertValEntry, }, utils::create_mask_of_1s, }; @@ -877,7 +878,8 @@ mod tests { #[test] fn mass_inserts_fixed_sized_keys_all_entries_are_retrievable() { common_setup(); - let entries: Vec<_> = generate_n_random_fixed_trie_entries(MASSIVE_TRIE_SIZE, 0).collect(); + let entries: Vec<_> = + generate_n_random_fixed_trie_value_entries(MASSIVE_TRIE_SIZE, 0).collect(); insert_entries_and_assert_all_exist_in_trie_with_no_extra(&entries); } @@ -885,7 +887,8 @@ mod tests { #[test] fn mass_inserts_variable_sized_keys_all_entries_are_retrievable() { common_setup(); - let entries: Vec<_> = generate_n_random_variable_keys(MASSIVE_TRIE_SIZE, 0).collect(); + let entries: Vec<_> = + generate_n_random_variable_trie_value_entries(MASSIVE_TRIE_SIZE, 0).collect(); insert_entries_and_assert_all_exist_in_trie_with_no_extra(&entries); } @@ -894,7 +897,7 @@ mod tests { fn mass_inserts_variable_sized_keys_with_hash_nodes_all_entries_are_retrievable() { common_setup(); let non_hash_entries: Vec<_> = - generate_n_random_variable_keys(MASSIVE_TRIE_SIZE, 0).collect(); + generate_n_random_variable_trie_value_entries(MASSIVE_TRIE_SIZE, 0).collect(); let mut trie = StandardTrie::from_iter(non_hash_entries.iter().cloned()); let extra_hash_entries = generate_n_hash_nodes_entries_for_empty_slots_in_trie( @@ -925,11 +928,11 @@ mod tests { StandardTrie::new(Node::Empty) ); - let entries = generate_n_random_fixed_trie_entries(MASSIVE_TRIE_SIZE, 0); + let entries = generate_n_random_fixed_trie_value_entries(MASSIVE_TRIE_SIZE, 0); let big_trie_1 = StandardTrie::from_iter(entries); assert_eq!(big_trie_1, big_trie_1); - let entries = generate_n_random_fixed_trie_entries(MASSIVE_TRIE_SIZE, 1); + let entries = generate_n_random_fixed_trie_value_entries(MASSIVE_TRIE_SIZE, 1); let big_trie_2 = StandardTrie::from_iter(entries); assert_ne!(big_trie_1, big_trie_2) @@ -951,7 +954,7 @@ mod tests { common_setup(); let random_entries: Vec<_> = - generate_n_random_fixed_trie_entries(MASSIVE_TRIE_SIZE, 9001).collect(); + generate_n_random_fixed_trie_value_entries(MASSIVE_TRIE_SIZE, 9001).collect(); let trie = StandardTrie::from_iter(random_entries.iter().cloned()); for (k, v) in random_entries.into_iter() { @@ -966,7 +969,7 @@ mod tests { fn held_trie_cow_references_do_not_change_as_trie_changes() { common_setup(); - let entries = generate_n_random_variable_keys(COW_TEST_TRIE_SIZE, 9002); + let entries = generate_n_random_variable_trie_value_entries(COW_TEST_TRIE_SIZE, 9002); let mut all_nodes_in_trie_after_each_insert = Vec::new(); let mut root_node_after_each_insert = Vec::new(); @@ -993,7 +996,7 @@ mod tests { common_setup(); let entries: HashSet<_> = - generate_n_random_variable_keys(MASSIVE_TRIE_SIZE, 9003).collect(); + generate_n_random_variable_trie_value_entries(MASSIVE_TRIE_SIZE, 9003).collect(); let trie = StandardTrie::from_iter(entries.iter().cloned()); let trie_items: HashSet<_> = trie @@ -1027,7 +1030,8 @@ mod tests { fn deletion_massive_trie() { common_setup(); - let entries: Vec<_> = generate_n_random_variable_keys(MASSIVE_TRIE_SIZE, 7).collect(); + let entries: Vec<_> = + generate_n_random_variable_trie_value_entries(MASSIVE_TRIE_SIZE, 7).collect(); let mut trie = StandardTrie::from_iter(entries.iter().cloned()); // Delete half of the elements diff --git a/src/trie_subsets.rs b/src/trie_subsets.rs index be9aae4..aa5f805 100644 --- a/src/trie_subsets.rs +++ b/src/trie_subsets.rs @@ -351,13 +351,14 @@ mod tests { use crate::{ nibbles::Nibbles, partial_trie::{HashedPartialTrie, Node, PartialTrie}, - testing_utils::{generate_n_random_fixed_trie_entries, large_entry}, + testing_utils::{ + create_trie_with_large_entry_nodes, generate_n_random_fixed_trie_value_entries, + handmade_trie_1, TrieType, + }, trie_ops::ValOrHash, utils::TrieNodeType, }; - type TrieType = HashedPartialTrie; - const MASSIVE_TEST_NUM_SUB_TRIES: usize = 10; const MASSIVE_TEST_NUM_SUB_TRIE_SIZE: usize = 5000; @@ -472,35 +473,9 @@ mod tests { assert!(!leaf_keys.contains(&Nibbles::from(0x12345))); } - // Initializes a trie with keys large enough to force hashing (nodes less than - // 32 bytes are not hashed). - fn create_trie_with_large_entry_nodes + Copy>(keys: &[T]) -> TrieType { - let mut trie = TrieType::default(); - for (k, v) in keys.iter().map(|k| (*k).into()).map(large_entry) { - trie.insert(k, v.clone()); - } - - trie - } - #[test] fn intermediate_nodes_are_included_in_subset() { - let ks = vec![0x1234, 0x1324, 0x132400005_u64, 0x2001, 0x2002]; - let trie = create_trie_with_large_entry_nodes(&ks); - - // Branch (0x) --> 1, 2 - // Branch (0x1) --> 2, 3 - // Leaf (0x1234) --> (n: 0x34, v: [0]) - - // Branch (0x1324, v: [1]) --> 0 - // Leaf (0x132400005) --> (0x0005, v: [2]) - - // Extension (0x2) --> n: 0x00 - // Branch (0x200) --> 1, 2 - // Leaf (0x2001) --> (n: 0x1, v: [3]) - // Leaf (0x2002) --> (n: 0x2, v: [4]) - - let ks_nibbles: Vec = ks.into_iter().map(|k| k.into()).collect(); + let (trie, ks_nibbles) = handmade_trie_1(); let trie_subset_all = create_trie_subset(&trie, ks_nibbles.iter().cloned()).unwrap(); let subset_keys = get_all_nibbles_of_leaf_nodes_in_trie(&trie_subset_all); @@ -648,7 +623,7 @@ mod tests { let trie_size = MASSIVE_TEST_NUM_SUB_TRIES * MASSIVE_TEST_NUM_SUB_TRIE_SIZE; let random_entries: Vec<_> = - generate_n_random_fixed_trie_entries(trie_size, seed).collect(); + generate_n_random_fixed_trie_value_entries(trie_size, seed).collect(); let entry_keys: Vec<_> = random_entries.iter().map(|(k, _)| k).cloned().collect(); let trie = TrieType::from_iter(random_entries);