chore: minor code cleanup
This commit is contained in:
parent
1b77fa8039
commit
58d173ef7b
3 changed files with 228 additions and 171 deletions
89
Cargo.lock
generated
89
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -14,6 +14,9 @@ mod tests;
|
|||
|
||||
type MutatedSubtreeLeaves = Vec<Vec<SubtreeLeaf>>;
|
||||
|
||||
// CONCURRENT IMPLEMENTATIONS
|
||||
// ================================================================================================
|
||||
|
||||
impl Smt {
|
||||
/// Parallel implementation of [`Smt::with_entries()`].
|
||||
///
|
||||
|
@ -115,6 +118,119 @@ impl Smt {
|
|||
}
|
||||
}
|
||||
|
||||
// SUBTREE MUTATION
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/// Computes the node mutations and the root of a subtree
|
||||
fn build_subtree_mutations(
|
||||
&self,
|
||||
mut leaves: Vec<SubtreeLeaf>,
|
||||
tree_depth: u8,
|
||||
bottom_depth: u8,
|
||||
) -> (NodeMutations, SubtreeLeaf)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
debug_assert!(bottom_depth <= tree_depth);
|
||||
debug_assert!(Integer::is_multiple_of(&bottom_depth, &SUBTREE_DEPTH));
|
||||
debug_assert!(leaves.len() <= usize::pow(2, SUBTREE_DEPTH as u32));
|
||||
|
||||
let subtree_root_depth = bottom_depth - SUBTREE_DEPTH;
|
||||
let mut node_mutations: NodeMutations = Default::default();
|
||||
let mut next_leaves: Vec<SubtreeLeaf> = Vec::with_capacity(leaves.len() / 2);
|
||||
|
||||
for current_depth in (subtree_root_depth..bottom_depth).rev() {
|
||||
debug_assert!(current_depth <= bottom_depth);
|
||||
|
||||
let next_depth = current_depth + 1;
|
||||
let mut iter = leaves.drain(..).peekable();
|
||||
|
||||
while let Some(first_leaf) = iter.next() {
|
||||
// This constructs a valid index because next_depth will never exceed the depth of
|
||||
// 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 = fetch_sibling_pair(&mut iter, first_leaf, parent_node);
|
||||
let combined_hash = combined_node.hash();
|
||||
|
||||
let &empty_hash = EmptySubtreeRoots::entry(tree_depth, current_depth);
|
||||
|
||||
// Add the parent node even if it is empty for proper upward updates
|
||||
next_leaves.push(SubtreeLeaf {
|
||||
col: parent_index.value(),
|
||||
hash: combined_hash,
|
||||
});
|
||||
|
||||
node_mutations.insert(
|
||||
parent_index,
|
||||
if combined_hash != empty_hash {
|
||||
NodeMutation::Addition(combined_node)
|
||||
} else {
|
||||
NodeMutation::Removal
|
||||
},
|
||||
);
|
||||
}
|
||||
drop(iter);
|
||||
leaves = mem::take(&mut next_leaves);
|
||||
}
|
||||
|
||||
debug_assert_eq!(leaves.len(), 1);
|
||||
let root_leaf = leaves.pop().unwrap();
|
||||
(node_mutations, root_leaf)
|
||||
}
|
||||
|
||||
// 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.
|
||||
///
|
||||
/// 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<UnorderedMap<_, _>>, Vec<SubtreeLeaf>) =
|
||||
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()`].
|
||||
|
@ -161,99 +277,6 @@ impl Smt {
|
|||
(accumulator.leaves, new_pairs)
|
||||
}
|
||||
|
||||
/// Computes the node mutations and the root of a subtree
|
||||
fn build_subtree_mutations(
|
||||
&self,
|
||||
mut leaves: Vec<SubtreeLeaf>,
|
||||
tree_depth: u8,
|
||||
bottom_depth: u8,
|
||||
) -> (NodeMutations, SubtreeLeaf)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
debug_assert!(bottom_depth <= tree_depth);
|
||||
debug_assert!(Integer::is_multiple_of(&bottom_depth, &SUBTREE_DEPTH));
|
||||
debug_assert!(leaves.len() <= usize::pow(2, SUBTREE_DEPTH as u32));
|
||||
|
||||
let subtree_root_depth = bottom_depth - SUBTREE_DEPTH;
|
||||
let mut node_mutations: NodeMutations = Default::default();
|
||||
let mut next_leaves: Vec<SubtreeLeaf> = Vec::with_capacity(leaves.len() / 2);
|
||||
|
||||
for current_depth in (subtree_root_depth..bottom_depth).rev() {
|
||||
debug_assert!(current_depth <= bottom_depth);
|
||||
|
||||
let next_depth = current_depth + 1;
|
||||
let mut iter = leaves.drain(..).peekable();
|
||||
|
||||
while let Some(first_leaf) = iter.next() {
|
||||
// This constructs a valid index because next_depth will never exceed the depth of
|
||||
// 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_hash = combined_node.hash();
|
||||
|
||||
let &empty_hash = EmptySubtreeRoots::entry(tree_depth, current_depth);
|
||||
|
||||
// Add the parent node even if it is empty for proper upward updates
|
||||
next_leaves.push(SubtreeLeaf {
|
||||
col: parent_index.value(),
|
||||
hash: combined_hash,
|
||||
});
|
||||
|
||||
node_mutations.insert(
|
||||
parent_index,
|
||||
if combined_hash != empty_hash {
|
||||
NodeMutation::Addition(combined_node)
|
||||
} else {
|
||||
NodeMutation::Removal
|
||||
},
|
||||
);
|
||||
}
|
||||
drop(iter);
|
||||
leaves = mem::take(&mut next_leaves);
|
||||
}
|
||||
|
||||
debug_assert_eq!(leaves.len(), 1);
|
||||
let root_leaf = leaves.pop().unwrap();
|
||||
(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`.
|
||||
///
|
||||
/// Returns the `InnerNode` containing the hashes of the sibling pair.
|
||||
fn fetch_sibling_pair(
|
||||
iter: &mut core::iter::Peekable<alloc::vec::Drain<SubtreeLeaf>>,
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes sorted key-value pairs to compute leaves for a subtree.
|
||||
///
|
||||
/// This function groups key-value pairs by their corresponding column index and processes each
|
||||
|
@ -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<UnorderedMap<_, _>>, Vec<SubtreeLeaf>) =
|
||||
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<K, L> Default for PairComputations<K, L> {
|
|||
pub(crate) struct SubtreeLeavesIter<'s> {
|
||||
leaves: core::iter::Peekable<alloc::vec::Drain<'s, SubtreeLeaf>>,
|
||||
}
|
||||
|
||||
impl<'s> SubtreeLeavesIter<'s> {
|
||||
fn from_leaves(leaves: &'s mut Vec<SubtreeLeaf>) -> 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<SubtreeLeaf>;
|
||||
|
||||
|
@ -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<SubtreeLeaf>,
|
||||
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<alloc::vec::Drain<SubtreeLeaf>>,
|
||||
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<SubtreeLeaf>,
|
||||
|
|
|
@ -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<SmtLeaf> = {
|
||||
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<Vec<SubtreeLeaf>> = {
|
||||
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<u64, SmtLeaf> = 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;
|
||||
|
|
Loading…
Add table
Reference in a new issue