From 58d173ef7b57de15685df896a531e6934e5ed3f8 Mon Sep 17 00:00:00 2001 From: Bobbin Threadbare Date: Thu, 6 Feb 2025 17:52:32 -0800 Subject: [PATCH] chore: minor code cleanup --- Cargo.lock | 89 +++++--- src/merkle/smt/full/concurrent/mod.rs | 265 +++++++++++++----------- src/merkle/smt/full/concurrent/tests.rs | 13 ++ 3 files changed, 212 insertions(+), 155 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1fa306d7..99b7e0b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -142,9 +142,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -160,9 +160,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.10" +version = "1.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2" dependencies = [ "jobserver", "libc", @@ -204,9 +204,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.27" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" +checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" dependencies = [ "clap_builder", "clap_derive", @@ -226,9 +226,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.24" +version = "4.5.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" dependencies = [ "heck", "proc-macro2", @@ -256,9 +256,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -409,10 +409,22 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets", +] + [[package]] name = "glob" version = "0.3.2" @@ -438,6 +450,7 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash", + "rayon", "serde", ] @@ -564,7 +577,7 @@ dependencies = [ "cc", "clap", "criterion", - "getrandom", + "getrandom 0.2.15", "glob", "hashbrown", "hex", @@ -661,9 +674,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "oorandom" @@ -779,7 +792,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -873,9 +886,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -914,9 +927,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -948,9 +961,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", @@ -959,13 +972,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.15.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" dependencies = [ "cfg-if", "fastrand", - "getrandom", + "getrandom 0.3.1", "once_cell", "rustix", "windows-sys", @@ -1015,9 +1028,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "utf8parse" @@ -1033,9 +1046,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wait-timeout" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] @@ -1056,6 +1069,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -1244,6 +1266,15 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1507ef312ea5569d54c2c7446a18b82143eb2a2e21f5c3ec7cfbe8200c03bd7c" +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/src/merkle/smt/full/concurrent/mod.rs b/src/merkle/smt/full/concurrent/mod.rs index ea890d48..fc5d71e1 100644 --- a/src/merkle/smt/full/concurrent/mod.rs +++ b/src/merkle/smt/full/concurrent/mod.rs @@ -14,6 +14,9 @@ mod tests; type MutatedSubtreeLeaves = Vec>; +// CONCURRENT IMPLEMENTATIONS +// ================================================================================================ + impl Smt { /// Parallel implementation of [`Smt::with_entries()`]. /// @@ -115,51 +118,8 @@ impl Smt { } } - /// Performs the initial transforms for constructing a [`SparseMerkleTree`] by composing - /// subtrees. In other words, this function takes the key-value inputs to the tree, and produces - /// the inputs to feed into [`build_subtree()`]. - /// - /// `pairs` *must* already be sorted **by leaf index column**, not simply sorted by key. If - /// `pairs` is not correctly sorted, the returned computations will be incorrect. - /// - /// # Panics - /// With debug assertions on, this function panics if it detects that `pairs` is not correctly - /// sorted. Without debug assertions, the returned computations will be incorrect. - fn sorted_pairs_to_leaves(pairs: Vec<(RpoDigest, Word)>) -> PairComputations { - Self::process_sorted_pairs_to_leaves(pairs, Self::pairs_to_leaf) - } - - /// Computes leaves from a set of key-value pairs and current leaf values. - /// Derived from `sorted_pairs_to_leaves` - fn sorted_pairs_to_mutated_subtree_leaves( - &self, - pairs: Vec<(RpoDigest, Word)>, - ) -> (MutatedSubtreeLeaves, UnorderedMap) { - // Map to track new key-value pairs for mutated leaves - let mut new_pairs = UnorderedMap::new(); - - let accumulator = Self::process_sorted_pairs_to_leaves(pairs, |leaf_pairs| { - let mut leaf = self.get_leaf(&leaf_pairs[0].0); - - for (key, value) in leaf_pairs { - // Check if the value has changed - let old_value = - new_pairs.get(&key).cloned().unwrap_or_else(|| self.get_value(&key)); - - // Skip if the value hasn't changed - if value == old_value { - continue; - } - - // Otherwise, update the leaf and track the new key-value pair - leaf = self.construct_prospective_leaf(leaf, &key, &value); - new_pairs.insert(key, value); - } - - leaf - }); - (accumulator.leaves, new_pairs) - } + // SUBTREE MUTATION + // -------------------------------------------------------------------------------------------- /// Computes the node mutations and the root of a subtree fn build_subtree_mutations( @@ -190,7 +150,7 @@ impl Smt { // the tree. let parent_index = NodeIndex::new_unchecked(next_depth, first_leaf.col).parent(); let parent_node = self.get_inner_node(parent_index); - let combined_node = Self::fetch_sibling_pair(&mut iter, first_leaf, parent_node); + let combined_node = fetch_sibling_pair(&mut iter, first_leaf, parent_node); let combined_hash = combined_node.hash(); let &empty_hash = EmptySubtreeRoots::entry(tree_depth, current_depth); @@ -219,39 +179,102 @@ impl Smt { (node_mutations, root_leaf) } - /// Constructs an `InnerNode` representing the sibling pair of which `first_leaf` is a part: - /// - If `first_leaf` is a right child, the left child is copied from the `parent_node`. - /// - If `first_leaf` is a left child, the right child is taken from `iter` if it was also - /// mutated or copied from the `parent_node`. + // SUBTREE CONSTRUCTION + // -------------------------------------------------------------------------------------------- + + /// Computes the raw parts for a new sparse Merkle tree from a set of key-value pairs. + /// + /// `entries` need not be sorted. This function will sort them. + fn build_subtrees(mut entries: Vec<(RpoDigest, Word)>) -> (InnerNodes, Leaves) { + entries.sort_by_key(|item| { + let index = Self::key_to_leaf_index(&item.0); + index.value() + }); + Self::build_subtrees_from_sorted_entries(entries) + } + + /// Computes the raw parts for a new sparse Merkle tree from a set of key-value pairs. /// - /// Returns the `InnerNode` containing the hashes of the sibling pair. - fn fetch_sibling_pair( - iter: &mut core::iter::Peekable>, - first_leaf: SubtreeLeaf, - parent_node: InnerNode, - ) -> InnerNode { - let is_right_node = first_leaf.col.is_odd(); - - if is_right_node { - let left_leaf = SubtreeLeaf { - col: first_leaf.col - 1, - hash: parent_node.left, - }; - InnerNode { - left: left_leaf.hash, - right: first_leaf.hash, - } - } else { - let right_col = first_leaf.col + 1; - let right_leaf = match iter.peek().copied() { - Some(SubtreeLeaf { col, .. }) if col == right_col => iter.next().unwrap(), - _ => SubtreeLeaf { col: right_col, hash: parent_node.right }, - }; - InnerNode { - left: first_leaf.hash, - right: right_leaf.hash, - } + /// This function is mostly an implementation detail of + /// [`Smt::with_entries_concurrent()`]. + fn build_subtrees_from_sorted_entries(entries: Vec<(RpoDigest, Word)>) -> (InnerNodes, Leaves) { + use rayon::prelude::*; + + let mut accumulated_nodes: InnerNodes = Default::default(); + + let PairComputations { + leaves: mut leaf_subtrees, + nodes: initial_leaves, + } = Self::sorted_pairs_to_leaves(entries); + + for current_depth in (SUBTREE_DEPTH..=SMT_DEPTH).step_by(SUBTREE_DEPTH as usize).rev() { + let (nodes, mut subtree_roots): (Vec>, Vec) = + leaf_subtrees + .into_par_iter() + .map(|subtree| { + debug_assert!(subtree.is_sorted()); + debug_assert!(!subtree.is_empty()); + let (nodes, subtree_root) = + build_subtree(subtree, SMT_DEPTH, current_depth); + (nodes, subtree_root) + }) + .unzip(); + + leaf_subtrees = SubtreeLeavesIter::from_leaves(&mut subtree_roots).collect(); + accumulated_nodes.extend(nodes.into_iter().flatten()); + + debug_assert!(!leaf_subtrees.is_empty()); } + (accumulated_nodes, initial_leaves) + } + + // LEAF NODE CONSTRUCTION + // -------------------------------------------------------------------------------------------- + + /// Performs the initial transforms for constructing a [`SparseMerkleTree`] by composing + /// subtrees. In other words, this function takes the key-value inputs to the tree, and produces + /// the inputs to feed into [`build_subtree()`]. + /// + /// `pairs` *must* already be sorted **by leaf index column**, not simply sorted by key. If + /// `pairs` is not correctly sorted, the returned computations will be incorrect. + /// + /// # Panics + /// With debug assertions on, this function panics if it detects that `pairs` is not correctly + /// sorted. Without debug assertions, the returned computations will be incorrect. + fn sorted_pairs_to_leaves(pairs: Vec<(RpoDigest, Word)>) -> PairComputations { + Self::process_sorted_pairs_to_leaves(pairs, Self::pairs_to_leaf) + } + + /// Computes leaves from a set of key-value pairs and current leaf values. + /// Derived from `sorted_pairs_to_leaves` + fn sorted_pairs_to_mutated_subtree_leaves( + &self, + pairs: Vec<(RpoDigest, Word)>, + ) -> (MutatedSubtreeLeaves, UnorderedMap) { + // Map to track new key-value pairs for mutated leaves + let mut new_pairs = UnorderedMap::new(); + + let accumulator = Self::process_sorted_pairs_to_leaves(pairs, |leaf_pairs| { + let mut leaf = self.get_leaf(&leaf_pairs[0].0); + + for (key, value) in leaf_pairs { + // Check if the value has changed + let old_value = + new_pairs.get(&key).cloned().unwrap_or_else(|| self.get_value(&key)); + + // Skip if the value hasn't changed + if value == old_value { + continue; + } + + // Otherwise, update the leaf and track the new key-value pair + leaf = self.construct_prospective_leaf(leaf, &key, &value); + new_pairs.insert(key, value); + } + + leaf + }); + (accumulator.leaves, new_pairs) } /// Processes sorted key-value pairs to compute leaves for a subtree. @@ -340,52 +363,6 @@ impl Smt { accumulator.leaves = SubtreeLeavesIter::from_leaves(&mut accumulated_leaves).collect(); accumulator } - - /// Computes the raw parts for a new sparse Merkle tree from a set of key-value pairs. - /// - /// `entries` need not be sorted. This function will sort them. - fn build_subtrees(mut entries: Vec<(RpoDigest, Word)>) -> (InnerNodes, Leaves) { - entries.sort_by_key(|item| { - let index = Self::key_to_leaf_index(&item.0); - index.value() - }); - Self::build_subtrees_from_sorted_entries(entries) - } - - /// Computes the raw parts for a new sparse Merkle tree from a set of key-value pairs. - /// - /// This function is mostly an implementation detail of - /// [`Smt::with_entries_concurrent()`]. - fn build_subtrees_from_sorted_entries(entries: Vec<(RpoDigest, Word)>) -> (InnerNodes, Leaves) { - use rayon::prelude::*; - - let mut accumulated_nodes: InnerNodes = Default::default(); - - let PairComputations { - leaves: mut leaf_subtrees, - nodes: initial_leaves, - } = Self::sorted_pairs_to_leaves(entries); - - for current_depth in (SUBTREE_DEPTH..=SMT_DEPTH).step_by(SUBTREE_DEPTH as usize).rev() { - let (nodes, mut subtree_roots): (Vec>, Vec) = - leaf_subtrees - .into_par_iter() - .map(|subtree| { - debug_assert!(subtree.is_sorted()); - debug_assert!(!subtree.is_empty()); - let (nodes, subtree_root) = - build_subtree(subtree, SMT_DEPTH, current_depth); - (nodes, subtree_root) - }) - .unzip(); - - leaf_subtrees = SubtreeLeavesIter::from_leaves(&mut subtree_roots).collect(); - accumulated_nodes.extend(nodes.into_iter().flatten()); - - debug_assert!(!leaf_subtrees.is_empty()); - } - (accumulated_nodes, initial_leaves) - } } // SUBTREES @@ -399,7 +376,7 @@ const COLS_PER_SUBTREE: u64 = u64::pow(2, SUBTREE_DEPTH as u32); /// Helper struct for organizing the data we care about when computing Merkle subtrees. /// -/// Note that these represet "conceptual" leaves of some subtree, not necessarily +/// Note that these represent "conceptual" leaves of some subtree, not necessarily /// the leaf type for the sparse Merkle tree. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct SubtreeLeaf { @@ -432,6 +409,7 @@ impl Default for PairComputations { pub(crate) struct SubtreeLeavesIter<'s> { leaves: core::iter::Peekable>, } + impl<'s> SubtreeLeavesIter<'s> { fn from_leaves(leaves: &'s mut Vec) -> Self { // TODO: determine if there is any notable performance difference between taking a Vec, @@ -441,6 +419,7 @@ impl<'s> SubtreeLeavesIter<'s> { Self { leaves: leaves.drain(..).peekable() } } } + impl Iterator for SubtreeLeavesIter<'_> { type Item = Vec; @@ -494,8 +473,7 @@ impl Iterator for SubtreeLeavesIter<'_> { /// more entries than can fit in a depth-8 subtree, if `leaves` contains leaves belonging to /// different depth-8 subtrees, if `bottom_depth` is lower in the tree than the specified /// maximum depth (`DEPTH`), or if `leaves` is not sorted. -#[cfg(feature = "concurrent")] -pub(crate) fn build_subtree( +fn build_subtree( mut leaves: Vec, tree_depth: u8, bottom_depth: u8, @@ -570,6 +548,41 @@ pub(crate) fn build_subtree( (inner_nodes, root) } +/// Constructs an `InnerNode` representing the sibling pair of which `first_leaf` is a part: +/// - If `first_leaf` is a right child, the left child is copied from the `parent_node`. +/// - If `first_leaf` is a left child, the right child is taken from `iter` if it was also mutated +/// or copied from the `parent_node`. +/// +/// Returns the `InnerNode` containing the hashes of the sibling pair. +fn fetch_sibling_pair( + iter: &mut core::iter::Peekable>, + first_leaf: SubtreeLeaf, + parent_node: InnerNode, +) -> InnerNode { + let is_right_node = first_leaf.col.is_odd(); + + if is_right_node { + let left_leaf = SubtreeLeaf { + col: first_leaf.col - 1, + hash: parent_node.left, + }; + InnerNode { + left: left_leaf.hash, + right: first_leaf.hash, + } + } else { + let right_col = first_leaf.col + 1; + let right_leaf = match iter.peek().copied() { + Some(SubtreeLeaf { col, .. }) if col == right_col => iter.next().unwrap(), + _ => SubtreeLeaf { col: right_col, hash: parent_node.right }, + }; + InnerNode { + left: first_leaf.hash, + right: right_leaf.hash, + } + } +} + #[cfg(feature = "internal")] pub fn build_subtree_for_bench( leaves: Vec, diff --git a/src/merkle/smt/full/concurrent/tests.rs b/src/merkle/smt/full/concurrent/tests.rs index c000a245..6d857ae4 100644 --- a/src/merkle/smt/full/concurrent/tests.rs +++ b/src/merkle/smt/full/concurrent/tests.rs @@ -34,6 +34,7 @@ fn test_sorted_pairs_to_leaves() { // Subtree 2. Another normal leaf. (RpoDigest::new([ONE, ONE, ONE, Felt::new(1024)]), [ONE; 4]), ]; + let control = Smt::with_entries_sequential(entries.clone()).unwrap(); let control_leaves: Vec = { let mut entries_iter = entries.iter().cloned(); @@ -52,6 +53,7 @@ fn test_sorted_pairs_to_leaves() { assert_eq!(entries_iter.next(), None); control_leaves }; + let control_subtree_leaves: Vec> = { let mut control_leaves_iter = control_leaves.iter(); let mut next_leaf = || control_leaves_iter.next().unwrap(); @@ -68,6 +70,7 @@ fn test_sorted_pairs_to_leaves() { assert_eq!(control_leaves_iter.next(), None); control_subtree_leaves }; + let subtrees: PairComputations = Smt::sorted_pairs_to_leaves(entries); // This will check that the hashes, columns, and subtree assignments all match. assert_eq!(subtrees.leaves, control_subtree_leaves); @@ -80,6 +83,7 @@ fn test_sorted_pairs_to_leaves() { .leaves() .map(|(index, value)| (index.index.value(), value.clone())) .collect(); + for (column, test_leaf) in subtrees.nodes { if test_leaf.is_empty() { continue; @@ -90,6 +94,7 @@ fn test_sorted_pairs_to_leaves() { assert_eq!(control_leaf, &test_leaf); } } + // Helper for the below tests. fn generate_entries(pair_count: u64) -> Vec<(RpoDigest, Word)> { (0..pair_count) @@ -101,6 +106,7 @@ fn generate_entries(pair_count: u64) -> Vec<(RpoDigest, Word)> { }) .collect() } + fn generate_updates(entries: Vec<(RpoDigest, Word)>, updates: usize) -> Vec<(RpoDigest, Word)> { const REMOVAL_PROBABILITY: f64 = 0.2; let mut rng = thread_rng(); @@ -125,6 +131,7 @@ fn generate_updates(entries: Vec<(RpoDigest, Word)>, updates: usize) -> Vec<(Rpo sorted_entries.sort_by_key(|(key, _)| Smt::key_to_leaf_index(key).value()); sorted_entries } + #[test] fn test_single_subtree() { // A single subtree's worth of leaves. @@ -154,6 +161,7 @@ fn test_single_subtree() { "Subtree-computed root at index {control_root_index:?} does not match control" ); } + // Test that not just can we compute a subtree correctly, but we can feed the results of one // subtree into computing another. In other words, test that `build_subtree()` is correctly // composable. @@ -201,6 +209,7 @@ fn test_two_subtrees() { let control_root = control.get_inner_node(index).hash(); assert_eq!(control_root, root_leaf.hash, "Root mismatch"); } + #[test] fn test_singlethreaded_subtrees() { const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 64; @@ -282,6 +291,7 @@ fn test_singlethreaded_subtrees() { // And of course the root we got from each place should match. assert_eq!(control.root(), root_leaf.hash); } + /// The parallel version of `test_singlethreaded_subtree()`. #[test] fn test_multithreaded_subtrees() { @@ -361,6 +371,7 @@ fn test_multithreaded_subtrees() { // And of course the root we got from each place should match. assert_eq!(control.root(), root_leaf.hash); } + #[test] fn test_with_entries_concurrent() { const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 64; @@ -370,6 +381,7 @@ fn test_with_entries_concurrent() { assert_eq!(smt.root(), control.root()); assert_eq!(smt, control); } + /// Concurrent mutations #[test] fn test_singlethreaded_subtree_mutations() { @@ -431,6 +443,7 @@ fn test_singlethreaded_subtree_mutations() { assert_eq!(test_value, &value); } } + #[test] fn test_compute_mutations_parallel() { const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 64;