simple_smt: reduce serialized size, use static hashes of the empty word
This commit is contained in:
parent
9b0ce0810b
commit
1f92d5417a
2 changed files with 32 additions and 13 deletions
|
@ -10,12 +10,19 @@ pub struct EmptySubtreeRoots;
|
||||||
impl EmptySubtreeRoots {
|
impl EmptySubtreeRoots {
|
||||||
/// Returns a static slice with roots of empty subtrees of a Merkle tree starting at the
|
/// Returns a static slice with roots of empty subtrees of a Merkle tree starting at the
|
||||||
/// specified depth.
|
/// specified depth.
|
||||||
pub const fn empty_hashes(depth: u8) -> &'static [RpoDigest] {
|
pub const fn empty_hashes(tree_depth: u8) -> &'static [RpoDigest] {
|
||||||
let ptr = &EMPTY_SUBTREES[255 - depth as usize] as *const RpoDigest;
|
let ptr = &EMPTY_SUBTREES[255 - tree_depth as usize] as *const RpoDigest;
|
||||||
// Safety: this is a static/constant array, so it will never be outlived. If we attempt to
|
// Safety: this is a static/constant array, so it will never be outlived. If we attempt to
|
||||||
// use regular slices, this wouldn't be a `const` function, meaning we won't be able to use
|
// use regular slices, this wouldn't be a `const` function, meaning we won't be able to use
|
||||||
// the returned value for static/constant definitions.
|
// the returned value for static/constant definitions.
|
||||||
unsafe { slice::from_raw_parts(ptr, depth as usize + 1) }
|
unsafe { slice::from_raw_parts(ptr, tree_depth as usize + 1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the node's digest for a sub-tree with all its leaves set to the empty word.
|
||||||
|
pub const fn entry(tree_depth: u8, node_depth: u8) -> &'static RpoDigest {
|
||||||
|
assert!(node_depth <= tree_depth);
|
||||||
|
let pos = 255 - tree_depth + node_depth;
|
||||||
|
&EMPTY_SUBTREES[pos as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1583,3 +1590,16 @@ fn all_depths_opens_to_zero() {
|
||||||
.for_each(|(x, computed)| assert_eq!(x, computed));
|
.for_each(|(x, computed)| assert_eq!(x, computed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_entry() {
|
||||||
|
// check the leaf is always the empty work
|
||||||
|
for depth in 0..255 {
|
||||||
|
assert_eq!(EmptySubtreeRoots::entry(depth, depth), &RpoDigest::new(EMPTY_WORD));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the root matches the first element of empty_hashes
|
||||||
|
for depth in 0..255 {
|
||||||
|
assert_eq!(EmptySubtreeRoots::entry(depth, 0), &EmptySubtreeRoots::empty_hashes(depth)[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ pub struct SimpleSmt {
|
||||||
root: RpoDigest,
|
root: RpoDigest,
|
||||||
leaves: BTreeMap<u64, Word>,
|
leaves: BTreeMap<u64, Word>,
|
||||||
branches: BTreeMap<NodeIndex, BranchNode>,
|
branches: BTreeMap<NodeIndex, BranchNode>,
|
||||||
empty_hashes: Vec<RpoDigest>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleSmt {
|
impl SimpleSmt {
|
||||||
|
@ -52,13 +51,11 @@ impl SimpleSmt {
|
||||||
return Err(MerkleError::DepthTooBig(depth as u64));
|
return Err(MerkleError::DepthTooBig(depth as u64));
|
||||||
}
|
}
|
||||||
|
|
||||||
let empty_hashes = EmptySubtreeRoots::empty_hashes(depth).to_vec();
|
let root = *EmptySubtreeRoots::entry(depth, 0);
|
||||||
let root = empty_hashes[0];
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
root,
|
root,
|
||||||
depth,
|
depth,
|
||||||
empty_hashes,
|
|
||||||
leaves: BTreeMap::new(),
|
leaves: BTreeMap::new(),
|
||||||
branches: BTreeMap::new(),
|
branches: BTreeMap::new(),
|
||||||
})
|
})
|
||||||
|
@ -133,10 +130,12 @@ impl SimpleSmt {
|
||||||
} else if index.depth() == self.depth() {
|
} else if index.depth() == self.depth() {
|
||||||
// the lookup in empty_hashes could fail only if empty_hashes were not built correctly
|
// the lookup in empty_hashes could fail only if empty_hashes were not built correctly
|
||||||
// by the constructor as we check the depth of the lookup above.
|
// by the constructor as we check the depth of the lookup above.
|
||||||
Ok(RpoDigest::from(
|
let leaf_pos = index.value();
|
||||||
self.get_leaf_node(index.value())
|
let leaf = match self.get_leaf_node(leaf_pos) {
|
||||||
.unwrap_or_else(|| *self.empty_hashes[index.depth() as usize]),
|
Some(word) => word.into(),
|
||||||
))
|
None => *EmptySubtreeRoots::entry(self.depth, index.depth()),
|
||||||
|
};
|
||||||
|
Ok(leaf)
|
||||||
} else {
|
} else {
|
||||||
Ok(self.get_branch_node(&index).parent())
|
Ok(self.get_branch_node(&index).parent())
|
||||||
}
|
}
|
||||||
|
@ -248,8 +247,8 @@ impl SimpleSmt {
|
||||||
|
|
||||||
fn get_branch_node(&self, index: &NodeIndex) -> BranchNode {
|
fn get_branch_node(&self, index: &NodeIndex) -> BranchNode {
|
||||||
self.branches.get(index).cloned().unwrap_or_else(|| {
|
self.branches.get(index).cloned().unwrap_or_else(|| {
|
||||||
let node = self.empty_hashes[index.depth() as usize + 1];
|
let node = EmptySubtreeRoots::entry(self.depth, index.depth() + 1);
|
||||||
BranchNode { left: node, right: node }
|
BranchNode { left: *node, right: *node }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue