Compare commits
8 commits
next
...
sparse-pat
Author | SHA1 | Date | |
---|---|---|---|
5ab7980fbc | |||
5685f6d4b6 | |||
80bd9af671 | |||
22c19983ac | |||
d524543899 | |||
aa386c67a4 | |||
07bda60233 | |||
5eb9a14c40 |
10 changed files with 339 additions and 181 deletions
|
@ -1,6 +1,5 @@
|
||||||
## 0.15.0 (TBD)
|
## 0.15.0 (TBD)
|
||||||
|
|
||||||
- Added default constructors to `MmrPeaks` and `PartialMmr` (#409).
|
|
||||||
|
|
||||||
## 0.14.0 (2025-03-15)
|
## 0.14.0 (2025-03-15)
|
||||||
|
|
||||||
|
|
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -254,7 +254,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"criterion-plot",
|
"criterion-plot",
|
||||||
"is-terminal",
|
"is-terminal",
|
||||||
"itertools 0.10.5",
|
"itertools",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"oorandom",
|
"oorandom",
|
||||||
|
@ -275,7 +275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cast",
|
"cast",
|
||||||
"itertools 0.10.5",
|
"itertools",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -453,15 +453,6 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
|
@ -533,7 +524,6 @@ dependencies = [
|
||||||
"glob",
|
"glob",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"hex",
|
"hex",
|
||||||
"itertools 0.14.0",
|
|
||||||
"num",
|
"num",
|
||||||
"num-complex",
|
"num-complex",
|
||||||
"proptest",
|
"proptest",
|
||||||
|
|
|
@ -83,7 +83,6 @@ assert_matches = { version = "1.5", default-features = false }
|
||||||
criterion = { version = "0.5", features = ["html_reports"] }
|
criterion = { version = "0.5", features = ["html_reports"] }
|
||||||
getrandom = { version = "0.3", default-features = false }
|
getrandom = { version = "0.3", default-features = false }
|
||||||
hex = { version = "0.4", default-features = false, features = ["alloc"] }
|
hex = { version = "0.4", default-features = false, features = ["alloc"] }
|
||||||
itertools = { version = "0.14" }
|
|
||||||
proptest = { version = "1.6", default-features = false, features = ["alloc"]}
|
proptest = { version = "1.6", default-features = false, features = ["alloc"]}
|
||||||
rand_chacha = { version = "0.9", default-features = false }
|
rand_chacha = { version = "0.9", default-features = false }
|
||||||
rand-utils = { version = "0.12", package = "winter-rand-utils" }
|
rand-utils = { version = "0.12", package = "winter-rand-utils" }
|
||||||
|
|
|
@ -70,18 +70,6 @@ pub struct PartialMmr {
|
||||||
pub(crate) track_latest: bool,
|
pub(crate) track_latest: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PartialMmr {
|
|
||||||
/// Creates a new [PartialMmr] with default values.
|
|
||||||
fn default() -> Self {
|
|
||||||
let forest = 0;
|
|
||||||
let peaks = Vec::new();
|
|
||||||
let nodes = BTreeMap::new();
|
|
||||||
let track_latest = false;
|
|
||||||
|
|
||||||
Self { forest, peaks, nodes, track_latest }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialMmr {
|
impl PartialMmr {
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
// --------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -34,13 +34,6 @@ pub struct MmrPeaks {
|
||||||
peaks: Vec<RpoDigest>,
|
peaks: Vec<RpoDigest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MmrPeaks {
|
|
||||||
/// Returns new [`MmrPeaks`] instantiated from an empty vector of peaks and 0 leaves.
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { num_leaves: 0, peaks: Vec::new() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MmrPeaks {
|
impl MmrPeaks {
|
||||||
// CONSTRUCTOR
|
// CONSTRUCTOR
|
||||||
// --------------------------------------------------------------------------------------------
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -12,23 +12,6 @@ use crate::{
|
||||||
merkle::{InOrderIndex, MerklePath, MerkleTree, MmrProof, NodeIndex, int_to_node},
|
merkle::{InOrderIndex, MerklePath, MerkleTree, MmrProof, NodeIndex, int_to_node},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn tests_empty_mmr_peaks() {
|
|
||||||
let peaks = MmrPeaks::default();
|
|
||||||
assert_eq!(peaks.num_peaks(), 0);
|
|
||||||
assert_eq!(peaks.num_leaves(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_empty_partial_mmr() {
|
|
||||||
let mmr = PartialMmr::default();
|
|
||||||
assert_eq!(mmr.num_leaves(), 0);
|
|
||||||
assert_eq!(mmr.forest(), 0);
|
|
||||||
assert_eq!(mmr.peaks(), MmrPeaks::default());
|
|
||||||
assert!(mmr.nodes.is_empty());
|
|
||||||
assert!(!mmr.track_latest);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_position_equal_or_higher_than_leafs_is_never_contained() {
|
fn test_position_equal_or_higher_than_leafs_is_never_contained() {
|
||||||
let empty_forest = 0;
|
let empty_forest = 0;
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl MerklePath {
|
||||||
|
|
||||||
/// Creates a new Merkle path from a list of nodes.
|
/// Creates a new Merkle path from a list of nodes.
|
||||||
///
|
///
|
||||||
/// The list must be in order of deepest to shallowest.
|
/// The list is assumed to be in order of deepest to shallowest.
|
||||||
pub fn new(nodes: Vec<RpoDigest>) -> Self {
|
pub fn new(nodes: Vec<RpoDigest>) -> Self {
|
||||||
assert!(nodes.len() <= u8::MAX.into(), "MerklePath may have at most 256 items");
|
assert!(nodes.len() <= u8::MAX.into(), "MerklePath may have at most 256 items");
|
||||||
Self { nodes }
|
Self { nodes }
|
||||||
|
@ -43,9 +43,19 @@ impl MerklePath {
|
||||||
/// The `depth` parameter is defined in terms of `self.depth()`. Merkle paths conventionally do
|
/// The `depth` parameter is defined in terms of `self.depth()`. Merkle paths conventionally do
|
||||||
/// not include the root, so the shallowest depth is `1`, and the deepest depth is
|
/// not include the root, so the shallowest depth is `1`, and the deepest depth is
|
||||||
/// `self.depth()`.
|
/// `self.depth()`.
|
||||||
pub fn at_depth(&self, depth: NonZero<u8>) -> Option<RpoDigest> {
|
pub fn at_depth(&self, depth: NonZero<u8>) -> Option<&RpoDigest> {
|
||||||
let index = u8::checked_sub(self.depth(), depth.get())?;
|
let index = u8::checked_sub(self.depth(), depth.get())?;
|
||||||
self.nodes.get(index as usize).copied()
|
self.nodes.get(index as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the path node at the specified index, or [None] if the index is out
|
||||||
|
/// of bounds.
|
||||||
|
///
|
||||||
|
/// The node at index 0 is the deepest part of the path.
|
||||||
|
///
|
||||||
|
/// This is a checked version of using the indexing operator `[]`.
|
||||||
|
pub fn at_idx(&self, index: usize) -> Option<&RpoDigest> {
|
||||||
|
self.nodes.get(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the depth in which this Merkle path proof is valid.
|
/// Returns the depth in which this Merkle path proof is valid.
|
||||||
|
|
|
@ -84,14 +84,14 @@ pub(crate) trait SparseMerkleTree<const DEPTH: u8> {
|
||||||
/// Mostly this is an implementation detail of [`Self::open()`].
|
/// Mostly this is an implementation detail of [`Self::open()`].
|
||||||
fn get_path(&self, key: &Self::Key) -> MerklePath {
|
fn get_path(&self, key: &Self::Key) -> MerklePath {
|
||||||
let index = NodeIndex::from(Self::key_to_leaf_index(key));
|
let index = NodeIndex::from(Self::key_to_leaf_index(key));
|
||||||
index.proof_indices().map(|index| self.get_node_hash(index)).collect()
|
index.proof_indices().map(|index| self.get_hash(index)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the hash of a node at an arbitrary index, including the root or leaf hashes.
|
/// Get the hash of a node at an arbitrary index, including the root or leaf hashes.
|
||||||
///
|
///
|
||||||
/// The root index simply returns [`Self::root()`]. Other hashes are retrieved by calling
|
/// The root index simply returns [`Self::root()`]. Other hashes are retrieved by calling
|
||||||
/// [`Self::get_inner_node()`] on the parent, and returning the respective child hash.
|
/// [`Self::get_inner_node()`] on the parent, and returning the respective child hash.
|
||||||
fn get_node_hash(&self, index: NodeIndex) -> RpoDigest {
|
fn get_hash(&self, index: NodeIndex) -> RpoDigest {
|
||||||
if index.is_root() {
|
if index.is_root() {
|
||||||
return self.root();
|
return self.root();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
use alloc::collections::BTreeSet;
|
use alloc::collections::BTreeSet;
|
||||||
|
|
||||||
|
use crate::merkle::{SparseMerklePath, SparseValuePath};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
super::ValuePath, EMPTY_WORD, EmptySubtreeRoots, InnerNode, InnerNodeInfo, InnerNodes,
|
super::ValuePath, EMPTY_WORD, EmptySubtreeRoots, InnerNode, InnerNodeInfo, InnerNodes,
|
||||||
LeafIndex, MerkleError, MerklePath, MutationSet, NodeIndex, RpoDigest, SMT_MAX_DEPTH,
|
LeafIndex, MerkleError, MerklePath, MutationSet, NodeIndex, RpoDigest, SMT_MAX_DEPTH,
|
||||||
SMT_MIN_DEPTH, SparseMerkleTree, Word,
|
SMT_MIN_DEPTH, SparseMerkleTree, Word,
|
||||||
};
|
};
|
||||||
use crate::merkle::{SparseMerklePath, SparseValuePath};
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -172,7 +173,7 @@ impl<const DEPTH: u8> SimpleSmt<DEPTH> {
|
||||||
/// path to the leaf, as well as the leaf itself.
|
/// path to the leaf, as well as the leaf itself.
|
||||||
pub fn open(&self, key: &LeafIndex<DEPTH>) -> SparseValuePath {
|
pub fn open(&self, key: &LeafIndex<DEPTH>) -> SparseValuePath {
|
||||||
let value = RpoDigest::new(self.get_value(key));
|
let value = RpoDigest::new(self.get_value(key));
|
||||||
let nodes = key.index.proof_indices().map(|index| self.get_node_hash(index));
|
let nodes = key.index.proof_indices().map(|index| self.get_hash(index));
|
||||||
// `from_sized_iter()` returns an error if there are more nodes than `SMT_MAX_DEPTH`, but
|
// `from_sized_iter()` returns an error if there are more nodes than `SMT_MAX_DEPTH`, but
|
||||||
// this could only happen if we have more levels than `SMT_MAX_DEPTH` ourselves, which is
|
// this could only happen if we have more levels than `SMT_MAX_DEPTH` ourselves, which is
|
||||||
// guarded against in `SimpleSmt::new()`.
|
// guarded against in `SimpleSmt::new()`.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use alloc::{borrow::Cow, vec::Vec};
|
use alloc::vec::Vec;
|
||||||
use core::{
|
use core::{
|
||||||
iter::{self, FusedIterator},
|
iter::{self, FusedIterator},
|
||||||
num::NonZero,
|
num::NonZero,
|
||||||
|
@ -14,10 +14,10 @@ use super::{
|
||||||
/// with empty nodes.
|
/// with empty nodes.
|
||||||
///
|
///
|
||||||
/// Empty nodes in the path are stored only as their position, represented with a bitmask. A
|
/// Empty nodes in the path are stored only as their position, represented with a bitmask. A
|
||||||
/// maximum of 64 nodes (`SMT_MAX_DEPTH`) can be stored (empty and non-empty). The more nodes in a
|
/// maximum of 64 nodes in the path can be empty. The more nodes in a path are empty, the less
|
||||||
/// path are empty, the less memory this struct will use. This type calculates empty nodes on-demand
|
/// memory this struct will use. This type calculates empty nodes on-demand when iterated through,
|
||||||
/// when iterated through, converted to a [MerklePath], or an empty node is retrieved with
|
/// converted to a [MerklePath], or an empty node is retrieved with [`SparseMerklePath::at_idx()`]
|
||||||
/// [`SparseMerklePath::at_depth()`], which will incur overhead.
|
/// or [`SparseMerklePath::at_depth()`], which will incur overhead.
|
||||||
///
|
///
|
||||||
/// NOTE: This type assumes that Merkle paths always span from the root of the tree to a leaf.
|
/// NOTE: This type assumes that Merkle paths always span from the root of the tree to a leaf.
|
||||||
/// Partial paths are not supported.
|
/// Partial paths are not supported.
|
||||||
|
@ -25,8 +25,6 @@ use super::{
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
pub struct SparseMerklePath {
|
pub struct SparseMerklePath {
|
||||||
/// A bitmask representing empty nodes. The set bit corresponds to the depth of an empty node.
|
/// A bitmask representing empty nodes. The set bit corresponds to the depth of an empty node.
|
||||||
/// The least significant bit (bit 0) describes depth 1 node (root's children).
|
|
||||||
/// The `bit index + 1` is equal to node's depth.
|
|
||||||
empty_nodes_mask: u64,
|
empty_nodes_mask: u64,
|
||||||
/// The non-empty nodes, stored in depth-order, but not contiguous across depth.
|
/// The non-empty nodes, stored in depth-order, but not contiguous across depth.
|
||||||
nodes: Vec<RpoDigest>,
|
nodes: Vec<RpoDigest>,
|
||||||
|
@ -38,7 +36,8 @@ impl SparseMerklePath {
|
||||||
/// of deepest to shallowest.
|
/// of deepest to shallowest.
|
||||||
///
|
///
|
||||||
/// Knowing the size is necessary to calculate the depth of the tree, which is needed to detect
|
/// Knowing the size is necessary to calculate the depth of the tree, which is needed to detect
|
||||||
/// which nodes are empty nodes.
|
/// which nodes are empty nodes. If you know the size but your iterator type is not
|
||||||
|
/// [ExactSizeIterator], use [`SparseMerklePath::from_iter_with_depth()`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns [MerkleError::DepthTooBig] if `tree_depth` is greater than [SMT_MAX_DEPTH].
|
/// Returns [MerkleError::DepthTooBig] if `tree_depth` is greater than [SMT_MAX_DEPTH].
|
||||||
|
@ -47,27 +46,38 @@ impl SparseMerklePath {
|
||||||
I: IntoIterator<IntoIter: ExactSizeIterator, Item = RpoDigest>,
|
I: IntoIterator<IntoIter: ExactSizeIterator, Item = RpoDigest>,
|
||||||
{
|
{
|
||||||
let iterator = iterator.into_iter();
|
let iterator = iterator.into_iter();
|
||||||
let tree_depth = iterator.len() as u8;
|
// `iterator.len() as u8` will truncate, but not below `SMT_MAX_DEPTH`, which
|
||||||
|
// `from_iter_with_depth` checks for.
|
||||||
|
Self::from_iter_with_depth(iterator.len() as u8, iterator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs a sparse Merkle path from a manually specified tree depth, and an iterator over
|
||||||
|
/// Merkle nodes from deepest to shallowest.
|
||||||
|
///
|
||||||
|
/// Knowing the size is necessary to calculate the depth of the tree, which is needed to detect
|
||||||
|
/// which nodes are empty nodes.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns [MerkleError::DepthTooBig] if `tree_depth` is greater than [SMT_MAX_DEPTH].
|
||||||
|
pub fn from_iter_with_depth(
|
||||||
|
tree_depth: u8,
|
||||||
|
iter: impl IntoIterator<Item = RpoDigest>,
|
||||||
|
) -> Result<Self, MerkleError> {
|
||||||
if tree_depth > SMT_MAX_DEPTH {
|
if tree_depth > SMT_MAX_DEPTH {
|
||||||
return Err(MerkleError::DepthTooBig(tree_depth as u64));
|
return Err(MerkleError::DepthTooBig(tree_depth as u64));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut empty_nodes_mask: u64 = 0;
|
let path: Self = iter::zip(path_depth_iter(tree_depth), iter)
|
||||||
let mut nodes: Vec<RpoDigest> = Default::default();
|
.map(|(depth, node)| {
|
||||||
|
|
||||||
for (depth, node) in iter::zip(path_depth_iter(tree_depth), iterator) {
|
|
||||||
let &equivalent_empty_node = EmptySubtreeRoots::entry(tree_depth, depth.get());
|
let &equivalent_empty_node = EmptySubtreeRoots::entry(tree_depth, depth.get());
|
||||||
let is_empty = node == equivalent_empty_node;
|
let is_empty = node == equivalent_empty_node;
|
||||||
let node = if is_empty { None } else { Some(node) };
|
let node = if is_empty { None } else { Some(node) };
|
||||||
|
|
||||||
match node {
|
(depth, node)
|
||||||
Some(node) => nodes.push(node),
|
})
|
||||||
None => empty_nodes_mask |= Self::bitmask_for_depth(depth),
|
.collect();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(SparseMerklePath { nodes, empty_nodes_mask })
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the total depth of this path, i.e., the number of nodes this path represents.
|
/// Returns the total depth of this path, i.e., the number of nodes this path represents.
|
||||||
|
@ -85,24 +95,63 @@ impl SparseMerklePath {
|
||||||
/// Returns [MerkleError::DepthTooBig] if `node_depth` is greater than the total depth of this
|
/// Returns [MerkleError::DepthTooBig] if `node_depth` is greater than the total depth of this
|
||||||
/// path.
|
/// path.
|
||||||
pub fn at_depth(&self, node_depth: NonZero<u8>) -> Result<RpoDigest, MerkleError> {
|
pub fn at_depth(&self, node_depth: NonZero<u8>) -> Result<RpoDigest, MerkleError> {
|
||||||
|
let node = self
|
||||||
|
.at_depth_nonempty(node_depth)?
|
||||||
|
.unwrap_or_else(|| *EmptySubtreeRoots::entry(self.depth(), node_depth.get()));
|
||||||
|
|
||||||
|
Ok(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a specific non-empty node in this path at a given depth, or `None` if the specified
|
||||||
|
/// node is an empty node.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns [MerkleError::DepthTooBig] if `node_depth` is greater than the total depth of this
|
||||||
|
/// path.
|
||||||
|
pub fn at_depth_nonempty(
|
||||||
|
&self,
|
||||||
|
node_depth: NonZero<u8>,
|
||||||
|
) -> Result<Option<RpoDigest>, MerkleError> {
|
||||||
if node_depth.get() > self.depth() {
|
if node_depth.get() > self.depth() {
|
||||||
return Err(MerkleError::DepthTooBig(node_depth.get().into()));
|
return Err(MerkleError::DepthTooBig(node_depth.get().into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = if let Some(nonempty_index) = self.get_nonempty_index(node_depth) {
|
if self.is_depth_empty(node_depth) {
|
||||||
self.nodes[nonempty_index]
|
return Ok(None);
|
||||||
} else {
|
}
|
||||||
*EmptySubtreeRoots::entry(self.depth(), node_depth.get())
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(node)
|
// Our index needs to account for all the empty nodes that aren't in `self.nodes`.
|
||||||
|
let nonempty_index = self.get_nonempty_index(node_depth);
|
||||||
|
|
||||||
|
Ok(Some(self.nodes[nonempty_index]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path node at the specified index, or [None] if the index is out of bounds.
|
||||||
|
///
|
||||||
|
/// The node at index 0 is the deepest part of the path.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use core::num::NonZero;
|
||||||
|
/// # use miden_crypto::{ZERO, ONE, hash::rpo::RpoDigest, merkle::SparseMerklePath};
|
||||||
|
/// # let zero = RpoDigest::new([ZERO; 4]);
|
||||||
|
/// # let one = RpoDigest::new([ONE; 4]);
|
||||||
|
/// # let sparse_path = SparseMerklePath::from_sized_iter(vec![zero, one, one, zero]).unwrap();
|
||||||
|
/// let depth = NonZero::new(sparse_path.depth()).unwrap();
|
||||||
|
/// assert_eq!(
|
||||||
|
/// sparse_path.at_idx(0).unwrap(),
|
||||||
|
/// sparse_path.at_depth(depth).unwrap(),
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn at_idx(&self, index: usize) -> Option<RpoDigest> {
|
||||||
|
// If this overflows *or* if the depth is zero then the index was out of bounds.
|
||||||
|
let depth = NonZero::new(u8::checked_sub(self.depth(), index as u8)?)?;
|
||||||
|
self.at_depth(depth).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROVIDERS
|
// PROVIDERS
|
||||||
// ============================================================================================
|
// ============================================================================================
|
||||||
|
|
||||||
/// Constructs a borrowing iterator over the nodes in this path.
|
/// Constructs a borrowing iterator over the nodes in this path.
|
||||||
/// Starts from the leaf and iterates toward the root (excluding the root).
|
|
||||||
pub fn iter(&self) -> impl ExactSizeIterator<Item = RpoDigest> {
|
pub fn iter(&self) -> impl ExactSizeIterator<Item = RpoDigest> {
|
||||||
self.into_iter()
|
self.into_iter()
|
||||||
}
|
}
|
||||||
|
@ -119,20 +168,14 @@ impl SparseMerklePath {
|
||||||
(self.empty_nodes_mask & Self::bitmask_for_depth(node_depth)) != 0
|
(self.empty_nodes_mask & Self::bitmask_for_depth(node_depth)) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Index of the non-empty node in the `self.nodes` vector. If the specified depth is
|
fn get_nonempty_index(&self, node_depth: NonZero<u8>) -> usize {
|
||||||
/// empty, None is returned.
|
|
||||||
fn get_nonempty_index(&self, node_depth: NonZero<u8>) -> Option<usize> {
|
|
||||||
if self.is_depth_empty(node_depth) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let bit_index = node_depth.get() - 1;
|
let bit_index = node_depth.get() - 1;
|
||||||
let without_shallower = self.empty_nodes_mask >> bit_index;
|
let without_shallower = self.empty_nodes_mask >> bit_index;
|
||||||
let empty_deeper = without_shallower.count_ones() as usize;
|
let empty_deeper = without_shallower.count_ones() as usize;
|
||||||
// The vec index we would use if we didn't have any empty nodes to account for...
|
// The vec index we would use if we didn't have any empty nodes to account for...
|
||||||
let normal_index = (self.depth() - node_depth.get()) as usize;
|
let normal_index = (self.depth() - node_depth.get()) as usize;
|
||||||
// subtracted by the number of empty nodes that are deeper than us.
|
// subtracted by the number of empty nodes that are deeper than us.
|
||||||
Some(normal_index - empty_deeper)
|
normal_index - empty_deeper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,21 +195,8 @@ impl Deserializable for SparseMerklePath {
|
||||||
source: &mut R,
|
source: &mut R,
|
||||||
) -> Result<Self, DeserializationError> {
|
) -> Result<Self, DeserializationError> {
|
||||||
let depth = source.read_u8()?;
|
let depth = source.read_u8()?;
|
||||||
if depth > SMT_MAX_DEPTH {
|
|
||||||
return Err(DeserializationError::InvalidValue(format!(
|
|
||||||
"SparseMerklePath max depth exceeded ({} > {})",
|
|
||||||
depth, SMT_MAX_DEPTH
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
let empty_nodes_mask = source.read_u64()?;
|
let empty_nodes_mask = source.read_u64()?;
|
||||||
let empty_nodes_count = empty_nodes_mask.count_ones();
|
let count = depth as u32 - empty_nodes_mask.count_ones();
|
||||||
if empty_nodes_count > depth as u32 {
|
|
||||||
return Err(DeserializationError::InvalidValue(format!(
|
|
||||||
"SparseMerklePath has more empty nodes ({}) than its full length ({})",
|
|
||||||
empty_nodes_count, depth
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
let count = depth as u32 - empty_nodes_count;
|
|
||||||
let nodes = source.read_many::<RpoDigest>(count as usize)?;
|
let nodes = source.read_many::<RpoDigest>(count as usize)?;
|
||||||
Ok(Self { empty_nodes_mask, nodes })
|
Ok(Self { empty_nodes_mask, nodes })
|
||||||
}
|
}
|
||||||
|
@ -181,13 +211,13 @@ impl From<SparseMerklePath> for MerklePath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This conversion returns [MerkleError::DepthTooBig] if the path length is greater than
|
||||||
|
/// [`SMT_MAX_DEPTH`].
|
||||||
impl TryFrom<MerklePath> for SparseMerklePath {
|
impl TryFrom<MerklePath> for SparseMerklePath {
|
||||||
type Error = MerkleError;
|
type Error = MerkleError;
|
||||||
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// This conversion returns [MerkleError::DepthTooBig] if the path length is greater than
|
|
||||||
/// [`SMT_MAX_DEPTH`].
|
|
||||||
fn try_from(path: MerklePath) -> Result<Self, MerkleError> {
|
fn try_from(path: MerklePath) -> Result<Self, MerkleError> {
|
||||||
SparseMerklePath::from_sized_iter(path)
|
SparseMerklePath::from_sized_iter(path)
|
||||||
}
|
}
|
||||||
|
@ -202,11 +232,41 @@ impl From<SparseMerklePath> for Vec<RpoDigest> {
|
||||||
// ITERATORS
|
// ITERATORS
|
||||||
// ================================================================================================
|
// ================================================================================================
|
||||||
|
|
||||||
/// Iterator for [`SparseMerklePath`]. Starts from the leaf and iterates toward the root (excluding
|
/// Contructs a [SparseMerklePath] out of an iterator of optional nodes, where `None` indicates an
|
||||||
/// the root).
|
/// empty node.
|
||||||
|
impl FromIterator<(NonZero<u8>, Option<RpoDigest>)> for SparseMerklePath {
|
||||||
|
fn from_iter<I>(iter: I) -> SparseMerklePath
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = (NonZero<u8>, Option<RpoDigest>)>,
|
||||||
|
{
|
||||||
|
let mut empty_nodes_mask: u64 = 0;
|
||||||
|
let mut nodes: Vec<RpoDigest> = Default::default();
|
||||||
|
|
||||||
|
for (depth, node) in iter {
|
||||||
|
match node {
|
||||||
|
Some(node) => nodes.push(node),
|
||||||
|
None => empty_nodes_mask |= Self::bitmask_for_depth(depth),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SparseMerklePath { nodes, empty_nodes_mask }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'p> IntoIterator for &'p SparseMerklePath {
|
||||||
|
type Item = <SparseMerklePathIter<'p> as Iterator>::Item;
|
||||||
|
type IntoIter = SparseMerklePathIter<'p>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> SparseMerklePathIter<'p> {
|
||||||
|
let tree_depth = self.depth();
|
||||||
|
SparseMerklePathIter { path: self, next_depth: tree_depth }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Borrowing iterator for [`SparseMerklePath`].
|
||||||
pub struct SparseMerklePathIter<'p> {
|
pub struct SparseMerklePathIter<'p> {
|
||||||
/// The "inner" value we're iterating over.
|
/// The "inner" value we're iterating over.
|
||||||
path: Cow<'p, SparseMerklePath>,
|
path: &'p SparseMerklePath,
|
||||||
|
|
||||||
/// The depth a `next()` call will get. `next_depth == 0` indicates that the iterator has been
|
/// The depth a `next()` call will get. `next_depth == 0` indicates that the iterator has been
|
||||||
/// exhausted.
|
/// exhausted.
|
||||||
|
@ -223,10 +283,7 @@ impl Iterator for SparseMerklePathIter<'_> {
|
||||||
self.next_depth = this_depth.get() - 1;
|
self.next_depth = this_depth.get() - 1;
|
||||||
|
|
||||||
// `this_depth` is only ever decreasing, so it can't ever exceed `self.path.depth()`.
|
// `this_depth` is only ever decreasing, so it can't ever exceed `self.path.depth()`.
|
||||||
let node = self
|
let node = self.path.at_depth(this_depth).unwrap();
|
||||||
.path
|
|
||||||
.at_depth(this_depth)
|
|
||||||
.expect("current depth should never exceed the path depth");
|
|
||||||
Some(node)
|
Some(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,32 +304,57 @@ impl FusedIterator for SparseMerklePathIter<'_> {}
|
||||||
|
|
||||||
// TODO: impl DoubleEndedIterator.
|
// TODO: impl DoubleEndedIterator.
|
||||||
|
|
||||||
|
/// Owning iterator for [SparseMerklePath].
|
||||||
|
pub struct IntoIter {
|
||||||
|
/// The "inner" value we're iterating over.
|
||||||
|
path: SparseMerklePath,
|
||||||
|
|
||||||
|
/// The depth a `next()` call will get. `next_depth == 0` indicates that the iterator has been
|
||||||
|
/// exhausted.
|
||||||
|
next_depth: u8,
|
||||||
|
}
|
||||||
|
|
||||||
impl IntoIterator for SparseMerklePath {
|
impl IntoIterator for SparseMerklePath {
|
||||||
type IntoIter = SparseMerklePathIter<'static>;
|
type IntoIter = IntoIter;
|
||||||
type Item = <Self::IntoIter as Iterator>::Item;
|
type Item = <Self::IntoIter as Iterator>::Item;
|
||||||
|
|
||||||
fn into_iter(self) -> SparseMerklePathIter<'static> {
|
fn into_iter(self) -> IntoIter {
|
||||||
let tree_depth = self.depth();
|
let tree_depth = self.depth();
|
||||||
SparseMerklePathIter {
|
IntoIter { path: self, next_depth: tree_depth }
|
||||||
path: Cow::Owned(self),
|
|
||||||
next_depth: tree_depth,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p> IntoIterator for &'p SparseMerklePath {
|
impl Iterator for IntoIter {
|
||||||
type Item = <SparseMerklePathIter<'p> as Iterator>::Item;
|
type Item = RpoDigest;
|
||||||
type IntoIter = SparseMerklePathIter<'p>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> SparseMerklePathIter<'p> {
|
fn next(&mut self) -> Option<RpoDigest> {
|
||||||
let tree_depth = self.depth();
|
let this_depth = self.next_depth;
|
||||||
SparseMerklePathIter {
|
// Paths don't include the root, so if `this_depth` is 0 then we keep returning `None`.
|
||||||
path: Cow::Borrowed(self),
|
let this_depth = NonZero::new(this_depth)?;
|
||||||
next_depth: tree_depth,
|
self.next_depth = this_depth.get() - 1;
|
||||||
|
|
||||||
|
// `this_depth` is only ever decreasing, so it can't ever exceed `self.path.depth()`.
|
||||||
|
let node = self.path.at_depth(this_depth).unwrap();
|
||||||
|
Some(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IntoIter always knows its exact size.
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
|
let remaining = ExactSizeIterator::len(self);
|
||||||
|
(remaining, Some(remaining))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ExactSizeIterator for IntoIter {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.next_depth as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FusedIterator for IntoIter {}
|
||||||
|
|
||||||
|
// TODO: impl DoubleEndedIterator.
|
||||||
|
|
||||||
// COMPARISONS
|
// COMPARISONS
|
||||||
// ================================================================================================
|
// ================================================================================================
|
||||||
impl PartialEq<MerklePath> for SparseMerklePath {
|
impl PartialEq<MerklePath> for SparseMerklePath {
|
||||||
|
@ -324,13 +406,13 @@ impl From<(SparseMerklePath, Word)> for SparseValuePath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This conversion returns [MerkleError::DepthTooBig] if the path length is greater than
|
||||||
|
/// [`SMT_MAX_DEPTH`].
|
||||||
impl TryFrom<ValuePath> for SparseValuePath {
|
impl TryFrom<ValuePath> for SparseValuePath {
|
||||||
type Error = MerkleError;
|
type Error = MerkleError;
|
||||||
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// This conversion returns [MerkleError::DepthTooBig] if the path length is greater than
|
|
||||||
/// [`SMT_MAX_DEPTH`].
|
|
||||||
fn try_from(other: ValuePath) -> Result<Self, MerkleError> {
|
fn try_from(other: ValuePath) -> Result<Self, MerkleError> {
|
||||||
let ValuePath { value, path } = other;
|
let ValuePath { value, path } = other;
|
||||||
let path = SparseMerklePath::try_from(path)?;
|
let path = SparseMerklePath::try_from(path)?;
|
||||||
|
@ -374,12 +456,10 @@ fn path_depth_iter(tree_depth: u8) -> impl ExactSizeIterator<Item = NonZero<u8>>
|
||||||
top_down_iter.rev()
|
top_down_iter.rev()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TESTS
|
|
||||||
// ================================================================================================
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::num::NonZero;
|
use core::{iter, num::NonZero};
|
||||||
|
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
|
|
||||||
|
@ -477,34 +557,145 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(sparse_path.empty_nodes_mask, EMPTY_BITS);
|
assert_eq!(sparse_path.empty_nodes_mask, EMPTY_BITS);
|
||||||
|
|
||||||
// Keep track of how many non-empty nodes we have seen
|
// Depth 8.
|
||||||
let mut nonempty_idx = 0;
|
{
|
||||||
|
let depth: u8 = 8;
|
||||||
|
|
||||||
// Test starting from the deepest nodes (depth 8)
|
// Check that the way we calculate these indices is correct.
|
||||||
for depth in (1..=8).rev() {
|
|
||||||
let idx = (sparse_path.depth() - depth) as usize;
|
let idx = (sparse_path.depth() - depth) as usize;
|
||||||
let bit = 1 << (depth - 1);
|
assert_eq!(idx, 0);
|
||||||
|
|
||||||
// Check that the depth bit is set correctly...
|
// Check that the way we calculate these bitmasks is correct.
|
||||||
|
let bit = 0b1000_0000;
|
||||||
|
assert_eq!(bit, 1 << (depth - 1));
|
||||||
|
|
||||||
|
// Check that the depth-8 bit is not set...
|
||||||
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
||||||
|
assert!(!is_set);
|
||||||
|
// ...which should match the status of the `sparse_nodes` element being `None`.
|
||||||
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
||||||
|
|
||||||
if is_set {
|
// And finally, check that we can calculate non-empty indices correctly.
|
||||||
// Check that we don't return digests for empty nodes
|
|
||||||
let &test_node = sparse_nodes.get(idx).unwrap();
|
|
||||||
assert_eq!(test_node, None);
|
|
||||||
} else {
|
|
||||||
// Check that we can calculate non-empty indices correctly.
|
|
||||||
let control_node = raw_nodes.get(idx).unwrap();
|
let control_node = raw_nodes.get(idx).unwrap();
|
||||||
assert_eq!(
|
let nonempty_idx: usize = 0;
|
||||||
sparse_path.get_nonempty_index(NonZero::new(depth).unwrap()).unwrap(),
|
assert_eq!(sparse_path.get_nonempty_index(NonZero::new(depth).unwrap()), nonempty_idx);
|
||||||
nonempty_idx
|
|
||||||
);
|
|
||||||
let test_node = sparse_path.nodes.get(nonempty_idx).unwrap();
|
let test_node = sparse_path.nodes.get(nonempty_idx).unwrap();
|
||||||
assert_eq!(test_node, control_node);
|
assert_eq!(test_node, control_node);
|
||||||
|
|
||||||
nonempty_idx += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rinse and repeat for each remaining depth.
|
||||||
|
|
||||||
|
// Depth 7.
|
||||||
|
{
|
||||||
|
let depth: u8 = 7;
|
||||||
|
let idx = (sparse_path.depth() - depth) as usize;
|
||||||
|
assert_eq!(idx, 1);
|
||||||
|
let bit = 0b0100_0000;
|
||||||
|
assert_eq!(bit, 1 << (depth - 1));
|
||||||
|
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
||||||
|
assert!(is_set);
|
||||||
|
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
||||||
|
|
||||||
|
let &test_node = sparse_nodes.get(idx).unwrap();
|
||||||
|
assert_eq!(test_node, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth 6.
|
||||||
|
{
|
||||||
|
let depth: u8 = 6;
|
||||||
|
let idx = (sparse_path.depth() - depth) as usize;
|
||||||
|
assert_eq!(idx, 2);
|
||||||
|
let bit = 0b0010_0000;
|
||||||
|
assert_eq!(bit, 1 << (depth - 1));
|
||||||
|
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
||||||
|
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
||||||
|
assert!(is_set);
|
||||||
|
|
||||||
|
let &test_node = sparse_nodes.get(idx).unwrap();
|
||||||
|
assert_eq!(test_node, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth 5.
|
||||||
|
{
|
||||||
|
let depth: u8 = 5;
|
||||||
|
let idx = (sparse_path.depth() - depth) as usize;
|
||||||
|
assert_eq!(idx, 3);
|
||||||
|
let bit = 0b0001_0000;
|
||||||
|
assert_eq!(bit, 1 << (depth - 1));
|
||||||
|
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
||||||
|
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
||||||
|
assert!(!is_set);
|
||||||
|
|
||||||
|
let control_node = raw_nodes.get(idx).unwrap();
|
||||||
|
let nonempty_idx: usize = 1;
|
||||||
|
assert_eq!(sparse_path.nodes.get(nonempty_idx).unwrap(), control_node);
|
||||||
|
assert_eq!(sparse_path.get_nonempty_index(NonZero::new(depth).unwrap()), nonempty_idx,);
|
||||||
|
let test_node = sparse_path.nodes.get(nonempty_idx).unwrap();
|
||||||
|
assert_eq!(test_node, control_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth 4.
|
||||||
|
{
|
||||||
|
let depth: u8 = 4;
|
||||||
|
let idx = (sparse_path.depth() - depth) as usize;
|
||||||
|
assert_eq!(idx, 4);
|
||||||
|
let bit = 0b0000_1000;
|
||||||
|
assert_eq!(bit, 1 << (depth - 1));
|
||||||
|
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
||||||
|
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
||||||
|
assert!(!is_set);
|
||||||
|
|
||||||
|
let control_node = raw_nodes.get(idx).unwrap();
|
||||||
|
let nonempty_idx: usize = 2;
|
||||||
|
assert_eq!(sparse_path.nodes.get(nonempty_idx).unwrap(), control_node);
|
||||||
|
assert_eq!(sparse_path.get_nonempty_index(NonZero::new(depth).unwrap()), nonempty_idx,);
|
||||||
|
let test_node = sparse_path.nodes.get(nonempty_idx).unwrap();
|
||||||
|
assert_eq!(test_node, control_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth 3.
|
||||||
|
{
|
||||||
|
let depth: u8 = 3;
|
||||||
|
let idx = (sparse_path.depth() - depth) as usize;
|
||||||
|
assert_eq!(idx, 5);
|
||||||
|
let bit = 0b0000_0100;
|
||||||
|
assert_eq!(bit, 1 << (depth - 1));
|
||||||
|
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
||||||
|
assert!(is_set);
|
||||||
|
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
||||||
|
|
||||||
|
let &test_node = sparse_nodes.get(idx).unwrap();
|
||||||
|
assert_eq!(test_node, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth 2.
|
||||||
|
{
|
||||||
|
let depth: u8 = 2;
|
||||||
|
let idx = (sparse_path.depth() - depth) as usize;
|
||||||
|
assert_eq!(idx, 6);
|
||||||
|
let bit = 0b0000_0010;
|
||||||
|
assert_eq!(bit, 1 << (depth - 1));
|
||||||
|
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
||||||
|
assert!(is_set);
|
||||||
|
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
||||||
|
|
||||||
|
let &test_node = sparse_nodes.get(idx).unwrap();
|
||||||
|
assert_eq!(test_node, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth 1.
|
||||||
|
{
|
||||||
|
let depth: u8 = 1;
|
||||||
|
let idx = (sparse_path.depth() - depth) as usize;
|
||||||
|
assert_eq!(idx, 7);
|
||||||
|
let bit = 0b0000_0001;
|
||||||
|
assert_eq!(bit, 1 << (depth - 1));
|
||||||
|
let is_set = (sparse_path.empty_nodes_mask & bit) != 0;
|
||||||
|
assert!(is_set);
|
||||||
|
assert_eq!(is_set, sparse_nodes.get(idx).unwrap().is_none());
|
||||||
|
|
||||||
|
let &test_node = sparse_nodes.get(idx).unwrap();
|
||||||
|
assert_eq!(test_node, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -516,25 +707,21 @@ mod tests {
|
||||||
let index = NodeIndex::from(Smt::key_to_leaf_index(key));
|
let index = NodeIndex::from(Smt::key_to_leaf_index(key));
|
||||||
|
|
||||||
let control_path = tree.get_path(key);
|
let control_path = tree.get_path(key);
|
||||||
for (&control_node, proof_index) in
|
for (&control_node, proof_index) in iter::zip(&*control_path, index.proof_indices()) {
|
||||||
itertools::zip_eq(&*control_path, index.proof_indices())
|
let proof_node = tree.get_hash(proof_index);
|
||||||
{
|
assert_eq!(control_node, proof_node, "WHat");
|
||||||
let proof_node = tree.get_node_hash(proof_index);
|
|
||||||
assert_eq!(control_node, proof_node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let sparse_path =
|
let sparse_path =
|
||||||
SparseMerklePath::from_sized_iter(control_path.clone().into_iter()).unwrap();
|
SparseMerklePath::from_sized_iter(control_path.clone().into_iter()).unwrap();
|
||||||
for (sparse_node, proof_idx) in
|
for (sparse_node, proof_idx) in iter::zip(sparse_path.clone(), index.proof_indices()) {
|
||||||
itertools::zip_eq(sparse_path.clone(), index.proof_indices())
|
let proof_node = tree.get_hash(proof_idx);
|
||||||
{
|
assert_eq!(sparse_node, proof_node, "WHat");
|
||||||
let proof_node = tree.get_node_hash(proof_idx);
|
|
||||||
assert_eq!(sparse_node, proof_node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(control_path.depth(), sparse_path.depth());
|
assert_eq!(control_path.depth(), sparse_path.depth());
|
||||||
for (control, sparse) in itertools::zip_eq(control_path, sparse_path) {
|
for (i, (control, sparse)) in iter::zip(control_path, sparse_path).enumerate() {
|
||||||
assert_eq!(control, sparse);
|
assert_eq!(control, sparse, "on iteration {i}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,15 +738,24 @@ mod tests {
|
||||||
|
|
||||||
// Test random access by depth.
|
// Test random access by depth.
|
||||||
for depth in path_depth_iter(control_path.depth()) {
|
for depth in path_depth_iter(control_path.depth()) {
|
||||||
let control_node = control_path.at_depth(depth).unwrap();
|
let &control_node = control_path.at_depth(depth).unwrap();
|
||||||
let sparse_node = sparse_path.at_depth(depth).unwrap();
|
let sparse_node = sparse_path.at_depth(depth).unwrap();
|
||||||
assert_eq!(control_node, sparse_node, "at depth {depth} for entry {i}");
|
assert_eq!(control_node, sparse_node, "at depth {depth} for entry {i}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test random access by index.
|
||||||
|
// Letting index get to `control_path.len()` will test that both sides correctly return
|
||||||
|
// `None` for out of bounds access.
|
||||||
|
for index in 0..=(control_path.len()) {
|
||||||
|
let control_node = control_path.at_idx(index).copied();
|
||||||
|
let sparse_node = sparse_path.at_idx(index);
|
||||||
|
assert_eq!(control_node, sparse_node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_borrowing_iterator() {
|
fn test_owning_iterator() {
|
||||||
let tree = make_smt(8192);
|
let tree = make_smt(8192);
|
||||||
|
|
||||||
for (key, _value) in tree.entries() {
|
for (key, _value) in tree.entries() {
|
||||||
|
@ -570,9 +766,7 @@ mod tests {
|
||||||
|
|
||||||
// Test that both iterators yield the same amount of the same values.
|
// Test that both iterators yield the same amount of the same values.
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
for (&control_node, sparse_node) in
|
for (&control_node, sparse_node) in iter::zip(control_path.iter(), sparse_path.iter()) {
|
||||||
itertools::zip_eq(control_path.iter(), sparse_path.iter())
|
|
||||||
{
|
|
||||||
count += 1;
|
count += 1;
|
||||||
assert_eq!(control_node, sparse_node);
|
assert_eq!(control_node, sparse_node);
|
||||||
}
|
}
|
||||||
|
@ -581,7 +775,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_owning_iterator() {
|
fn test_borrowing_iterator() {
|
||||||
let tree = make_smt(8192);
|
let tree = make_smt(8192);
|
||||||
|
|
||||||
for (key, _value) in tree.entries() {
|
for (key, _value) in tree.entries() {
|
||||||
|
@ -593,7 +787,7 @@ mod tests {
|
||||||
|
|
||||||
// Test that both iterators yield the same amount of the same values.
|
// Test that both iterators yield the same amount of the same values.
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
for (control_node, sparse_node) in itertools::zip_eq(control_path, sparse_path) {
|
for (control_node, sparse_node) in iter::zip(control_path, sparse_path) {
|
||||||
count += 1;
|
count += 1;
|
||||||
assert_eq!(control_node, sparse_node);
|
assert_eq!(control_node, sparse_node);
|
||||||
}
|
}
|
||||||
|
@ -612,6 +806,7 @@ mod tests {
|
||||||
sparse_path.at_depth(NonZero::new(1).unwrap()),
|
sparse_path.at_depth(NonZero::new(1).unwrap()),
|
||||||
Err(MerkleError::DepthTooBig(1))
|
Err(MerkleError::DepthTooBig(1))
|
||||||
);
|
);
|
||||||
|
assert_eq!(sparse_path.at_idx(0), None);
|
||||||
assert_eq!(sparse_path.iter().next(), None);
|
assert_eq!(sparse_path.iter().next(), None);
|
||||||
assert_eq!(sparse_path.into_iter().next(), None);
|
assert_eq!(sparse_path.into_iter().next(), None);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue