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.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
|
@ -142,9 +142,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.16.0"
|
version = "3.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
|
@ -160,9 +160,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.10"
|
version = "1.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
|
checksum = "755717a7de9ec452bf7f3f1a3099085deabd7f2962b861dae91ecd7a365903d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -204,9 +204,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.27"
|
version = "4.5.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
|
checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -226,9 +226,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.24"
|
version = "4.5.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
|
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -256,9 +256,9 @@ checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.16"
|
version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -409,10 +409,22 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||||
"wasm-bindgen",
|
"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]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -438,6 +450,7 @@ dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"foldhash",
|
"foldhash",
|
||||||
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -564,7 +577,7 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"clap",
|
"clap",
|
||||||
"criterion",
|
"criterion",
|
||||||
"getrandom",
|
"getrandom 0.2.15",
|
||||||
"glob",
|
"glob",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"hex",
|
"hex",
|
||||||
|
@ -661,9 +674,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.20.2"
|
version = "1.20.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oorandom"
|
name = "oorandom"
|
||||||
|
@ -779,7 +792,7 @@ version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -873,9 +886,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.18"
|
version = "1.0.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
|
@ -914,9 +927,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.137"
|
version = "1.0.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -948,9 +961,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.96"
|
version = "2.0.98"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
|
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -959,13 +972,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.15.0"
|
version = "3.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom",
|
"getrandom 0.3.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
|
@ -1015,9 +1028,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.14"
|
version = "1.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
|
@ -1033,9 +1046,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wait-timeout"
|
name = "wait-timeout"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -1056,6 +1069,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
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]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.100"
|
version = "0.2.100"
|
||||||
|
@ -1244,6 +1266,15 @@ version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1507ef312ea5569d54c2c7446a18b82143eb2a2e21f5c3ec7cfbe8200c03bd7c"
|
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]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.35"
|
version = "0.7.35"
|
||||||
|
|
|
@ -14,6 +14,9 @@ mod tests;
|
||||||
|
|
||||||
type MutatedSubtreeLeaves = Vec<Vec<SubtreeLeaf>>;
|
type MutatedSubtreeLeaves = Vec<Vec<SubtreeLeaf>>;
|
||||||
|
|
||||||
|
// CONCURRENT IMPLEMENTATIONS
|
||||||
|
// ================================================================================================
|
||||||
|
|
||||||
impl Smt {
|
impl Smt {
|
||||||
/// Parallel implementation of [`Smt::with_entries()`].
|
/// 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
|
/// 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
|
/// subtrees. In other words, this function takes the key-value inputs to the tree, and produces
|
||||||
/// the inputs to feed into [`build_subtree()`].
|
/// the inputs to feed into [`build_subtree()`].
|
||||||
|
@ -161,99 +277,6 @@ impl Smt {
|
||||||
(accumulator.leaves, new_pairs)
|
(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.
|
/// 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
|
/// 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.leaves = SubtreeLeavesIter::from_leaves(&mut accumulated_leaves).collect();
|
||||||
accumulator
|
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
|
// 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.
|
/// 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.
|
/// the leaf type for the sparse Merkle tree.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
pub struct SubtreeLeaf {
|
pub struct SubtreeLeaf {
|
||||||
|
@ -432,6 +409,7 @@ impl<K, L> Default for PairComputations<K, L> {
|
||||||
pub(crate) struct SubtreeLeavesIter<'s> {
|
pub(crate) struct SubtreeLeavesIter<'s> {
|
||||||
leaves: core::iter::Peekable<alloc::vec::Drain<'s, SubtreeLeaf>>,
|
leaves: core::iter::Peekable<alloc::vec::Drain<'s, SubtreeLeaf>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'s> SubtreeLeavesIter<'s> {
|
impl<'s> SubtreeLeavesIter<'s> {
|
||||||
fn from_leaves(leaves: &'s mut Vec<SubtreeLeaf>) -> Self {
|
fn from_leaves(leaves: &'s mut Vec<SubtreeLeaf>) -> Self {
|
||||||
// TODO: determine if there is any notable performance difference between taking a Vec,
|
// 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() }
|
Self { leaves: leaves.drain(..).peekable() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for SubtreeLeavesIter<'_> {
|
impl Iterator for SubtreeLeavesIter<'_> {
|
||||||
type Item = Vec<SubtreeLeaf>;
|
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
|
/// 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
|
/// different depth-8 subtrees, if `bottom_depth` is lower in the tree than the specified
|
||||||
/// maximum depth (`DEPTH`), or if `leaves` is not sorted.
|
/// maximum depth (`DEPTH`), or if `leaves` is not sorted.
|
||||||
#[cfg(feature = "concurrent")]
|
fn build_subtree(
|
||||||
pub(crate) fn build_subtree(
|
|
||||||
mut leaves: Vec<SubtreeLeaf>,
|
mut leaves: Vec<SubtreeLeaf>,
|
||||||
tree_depth: u8,
|
tree_depth: u8,
|
||||||
bottom_depth: u8,
|
bottom_depth: u8,
|
||||||
|
@ -570,6 +548,41 @@ pub(crate) fn build_subtree(
|
||||||
(inner_nodes, root)
|
(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")]
|
#[cfg(feature = "internal")]
|
||||||
pub fn build_subtree_for_bench(
|
pub fn build_subtree_for_bench(
|
||||||
leaves: Vec<SubtreeLeaf>,
|
leaves: Vec<SubtreeLeaf>,
|
||||||
|
|
|
@ -34,6 +34,7 @@ fn test_sorted_pairs_to_leaves() {
|
||||||
// Subtree 2. Another normal leaf.
|
// Subtree 2. Another normal leaf.
|
||||||
(RpoDigest::new([ONE, ONE, ONE, Felt::new(1024)]), [ONE; 4]),
|
(RpoDigest::new([ONE, ONE, ONE, Felt::new(1024)]), [ONE; 4]),
|
||||||
];
|
];
|
||||||
|
|
||||||
let control = Smt::with_entries_sequential(entries.clone()).unwrap();
|
let control = Smt::with_entries_sequential(entries.clone()).unwrap();
|
||||||
let control_leaves: Vec<SmtLeaf> = {
|
let control_leaves: Vec<SmtLeaf> = {
|
||||||
let mut entries_iter = entries.iter().cloned();
|
let mut entries_iter = entries.iter().cloned();
|
||||||
|
@ -52,6 +53,7 @@ fn test_sorted_pairs_to_leaves() {
|
||||||
assert_eq!(entries_iter.next(), None);
|
assert_eq!(entries_iter.next(), None);
|
||||||
control_leaves
|
control_leaves
|
||||||
};
|
};
|
||||||
|
|
||||||
let control_subtree_leaves: Vec<Vec<SubtreeLeaf>> = {
|
let control_subtree_leaves: Vec<Vec<SubtreeLeaf>> = {
|
||||||
let mut control_leaves_iter = control_leaves.iter();
|
let mut control_leaves_iter = control_leaves.iter();
|
||||||
let mut next_leaf = || control_leaves_iter.next().unwrap();
|
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);
|
assert_eq!(control_leaves_iter.next(), None);
|
||||||
control_subtree_leaves
|
control_subtree_leaves
|
||||||
};
|
};
|
||||||
|
|
||||||
let subtrees: PairComputations<u64, SmtLeaf> = Smt::sorted_pairs_to_leaves(entries);
|
let subtrees: PairComputations<u64, SmtLeaf> = Smt::sorted_pairs_to_leaves(entries);
|
||||||
// This will check that the hashes, columns, and subtree assignments all match.
|
// This will check that the hashes, columns, and subtree assignments all match.
|
||||||
assert_eq!(subtrees.leaves, control_subtree_leaves);
|
assert_eq!(subtrees.leaves, control_subtree_leaves);
|
||||||
|
@ -80,6 +83,7 @@ fn test_sorted_pairs_to_leaves() {
|
||||||
.leaves()
|
.leaves()
|
||||||
.map(|(index, value)| (index.index.value(), value.clone()))
|
.map(|(index, value)| (index.index.value(), value.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
for (column, test_leaf) in subtrees.nodes {
|
for (column, test_leaf) in subtrees.nodes {
|
||||||
if test_leaf.is_empty() {
|
if test_leaf.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
|
@ -90,6 +94,7 @@ fn test_sorted_pairs_to_leaves() {
|
||||||
assert_eq!(control_leaf, &test_leaf);
|
assert_eq!(control_leaf, &test_leaf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper for the below tests.
|
// Helper for the below tests.
|
||||||
fn generate_entries(pair_count: u64) -> Vec<(RpoDigest, Word)> {
|
fn generate_entries(pair_count: u64) -> Vec<(RpoDigest, Word)> {
|
||||||
(0..pair_count)
|
(0..pair_count)
|
||||||
|
@ -101,6 +106,7 @@ fn generate_entries(pair_count: u64) -> Vec<(RpoDigest, Word)> {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_updates(entries: Vec<(RpoDigest, Word)>, updates: usize) -> Vec<(RpoDigest, Word)> {
|
fn generate_updates(entries: Vec<(RpoDigest, Word)>, updates: usize) -> Vec<(RpoDigest, Word)> {
|
||||||
const REMOVAL_PROBABILITY: f64 = 0.2;
|
const REMOVAL_PROBABILITY: f64 = 0.2;
|
||||||
let mut rng = thread_rng();
|
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.sort_by_key(|(key, _)| Smt::key_to_leaf_index(key).value());
|
||||||
sorted_entries
|
sorted_entries
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_single_subtree() {
|
fn test_single_subtree() {
|
||||||
// A single subtree's worth of leaves.
|
// 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"
|
"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
|
// 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
|
// subtree into computing another. In other words, test that `build_subtree()` is correctly
|
||||||
// composable.
|
// composable.
|
||||||
|
@ -201,6 +209,7 @@ fn test_two_subtrees() {
|
||||||
let control_root = control.get_inner_node(index).hash();
|
let control_root = control.get_inner_node(index).hash();
|
||||||
assert_eq!(control_root, root_leaf.hash, "Root mismatch");
|
assert_eq!(control_root, root_leaf.hash, "Root mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_singlethreaded_subtrees() {
|
fn test_singlethreaded_subtrees() {
|
||||||
const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 64;
|
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.
|
// And of course the root we got from each place should match.
|
||||||
assert_eq!(control.root(), root_leaf.hash);
|
assert_eq!(control.root(), root_leaf.hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The parallel version of `test_singlethreaded_subtree()`.
|
/// The parallel version of `test_singlethreaded_subtree()`.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_multithreaded_subtrees() {
|
fn test_multithreaded_subtrees() {
|
||||||
|
@ -361,6 +371,7 @@ fn test_multithreaded_subtrees() {
|
||||||
// And of course the root we got from each place should match.
|
// And of course the root we got from each place should match.
|
||||||
assert_eq!(control.root(), root_leaf.hash);
|
assert_eq!(control.root(), root_leaf.hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_with_entries_concurrent() {
|
fn test_with_entries_concurrent() {
|
||||||
const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 64;
|
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.root(), control.root());
|
||||||
assert_eq!(smt, control);
|
assert_eq!(smt, control);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Concurrent mutations
|
/// Concurrent mutations
|
||||||
#[test]
|
#[test]
|
||||||
fn test_singlethreaded_subtree_mutations() {
|
fn test_singlethreaded_subtree_mutations() {
|
||||||
|
@ -431,6 +443,7 @@ fn test_singlethreaded_subtree_mutations() {
|
||||||
assert_eq!(test_value, &value);
|
assert_eq!(test_value, &value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compute_mutations_parallel() {
|
fn test_compute_mutations_parallel() {
|
||||||
const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 64;
|
const PAIR_COUNT: u64 = COLS_PER_SUBTREE * 64;
|
||||||
|
|
Loading…
Add table
Reference in a new issue