bugfix: fix internal nodes of for empty leafs of a SMT
The path returned by `EmptySubtreeRoots` starts at the root, and goes to the leaf. The MerkleStore constructor assumed the other direction, so the parent/child hashes were reversed. This fixes the bug and adds a test.
This commit is contained in:
parent
854892ba9d
commit
9e6c8ff700
2 changed files with 80 additions and 10 deletions
|
@ -43,7 +43,7 @@ pub struct Node {
|
||||||
/// let mut store = MerkleStore::new();
|
/// let mut store = MerkleStore::new();
|
||||||
///
|
///
|
||||||
/// // the store is initialized with the SMT empty nodes
|
/// // the store is initialized with the SMT empty nodes
|
||||||
/// assert_eq!(store.num_internal_nodes(), 64);
|
/// assert_eq!(store.num_internal_nodes(), 255);
|
||||||
///
|
///
|
||||||
/// // populates the store with two merkle trees, common nodes are shared
|
/// // populates the store with two merkle trees, common nodes are shared
|
||||||
/// store.add_merkle_tree([A, B, C, D, E, F, G, H0]);
|
/// store.add_merkle_tree([A, B, C, D, E, F, G, H0]);
|
||||||
|
@ -65,7 +65,7 @@ pub struct Node {
|
||||||
///
|
///
|
||||||
/// // Common internal nodes are shared, the two added trees have a total of 30, but the store has
|
/// // Common internal nodes are shared, the two added trees have a total of 30, but the store has
|
||||||
/// // only 10 new entries, corresponding to the 10 unique internal nodes of these trees.
|
/// // only 10 new entries, corresponding to the 10 unique internal nodes of these trees.
|
||||||
/// assert_eq!(store.num_internal_nodes() - 64, 10);
|
/// assert_eq!(store.num_internal_nodes() - 255, 10);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct MerkleStore {
|
pub struct MerkleStore {
|
||||||
|
@ -85,11 +85,12 @@ impl MerkleStore {
|
||||||
/// Creates an empty `MerkleStore` instance.
|
/// Creates an empty `MerkleStore` instance.
|
||||||
pub fn new() -> MerkleStore {
|
pub fn new() -> MerkleStore {
|
||||||
// pre-populate the store with the empty hashes
|
// pre-populate the store with the empty hashes
|
||||||
let subtrees = EmptySubtreeRoots::empty_hashes(64);
|
let subtrees = EmptySubtreeRoots::empty_hashes(255);
|
||||||
let nodes = subtrees
|
let nodes = subtrees
|
||||||
.iter()
|
.iter()
|
||||||
|
.rev()
|
||||||
.copied()
|
.copied()
|
||||||
.zip(subtrees.iter().skip(1).copied())
|
.zip(subtrees.iter().rev().skip(1).copied())
|
||||||
.map(|(child, parent)| {
|
.map(|(child, parent)| {
|
||||||
(
|
(
|
||||||
parent,
|
parent,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
hash::rpo::Rpo256,
|
hash::rpo::Rpo256,
|
||||||
merkle::{int_to_node, MerklePathSet},
|
merkle::{int_to_node, MerklePathSet},
|
||||||
Felt, Word,
|
Felt, Word, WORD_SIZE, ZERO,
|
||||||
};
|
};
|
||||||
|
|
||||||
const KEYS4: [u64; 4] = [0, 1, 2, 3];
|
const KEYS4: [u64; 4] = [0, 1, 2, 3];
|
||||||
|
@ -12,6 +12,7 @@ const LEAVES4: [Word; 4] = [
|
||||||
int_to_node(3),
|
int_to_node(3),
|
||||||
int_to_node(4),
|
int_to_node(4),
|
||||||
];
|
];
|
||||||
|
const EMPTY: Word = [ZERO; WORD_SIZE];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_root_not_in_store() -> Result<(), MerkleError> {
|
fn test_root_not_in_store() -> Result<(), MerkleError> {
|
||||||
|
@ -141,6 +142,51 @@ fn test_merkle_tree() -> Result<(), MerkleError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_roots() {
|
||||||
|
let store = MerkleStore::default();
|
||||||
|
let mut root = RpoDigest::new(EMPTY);
|
||||||
|
|
||||||
|
for depth in 0..255 {
|
||||||
|
root = Rpo256::merge(&[root; 2]);
|
||||||
|
assert!(
|
||||||
|
store.get_node(root.into(), NodeIndex::new(0, 0)).is_ok(),
|
||||||
|
"The root of the empty tree of depth {depth} must be registered"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_leaf_paths_for_empty_trees() -> Result<(), MerkleError> {
|
||||||
|
let store = MerkleStore::default();
|
||||||
|
|
||||||
|
// Starts at 1 because leafs are not included in the store.
|
||||||
|
// Ends at 64 because it is not possible to represent an index of a depth greater than 64,
|
||||||
|
// because a u64 is used to index the leaf.
|
||||||
|
for depth in 1..64 {
|
||||||
|
let smt = SimpleSmt::new(depth)?;
|
||||||
|
|
||||||
|
let index = NodeIndex::new(depth, 0);
|
||||||
|
let store_path = store.get_path(smt.root(), index)?;
|
||||||
|
let smt_path = smt.get_path(index)?;
|
||||||
|
assert_eq!(
|
||||||
|
store_path.value, EMPTY,
|
||||||
|
"the leaf of an empty tree is always ZERO"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
store_path.path, smt_path,
|
||||||
|
"the returned merkle path does not match the computed values"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
store_path.path.compute_root(depth.into(), EMPTY),
|
||||||
|
smt.root(),
|
||||||
|
"computed root from the path must match the empty tree root"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_invalid_node() {
|
fn test_get_invalid_node() {
|
||||||
let mut store = MerkleStore::default();
|
let mut store = MerkleStore::default();
|
||||||
|
@ -211,6 +257,11 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
||||||
Ok(LEAVES4[3]),
|
Ok(LEAVES4[3]),
|
||||||
"node 3 must be in the tree"
|
"node 3 must be in the tree"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
store.get_node(smt.root(), NodeIndex::new(smt.depth(), 4)),
|
||||||
|
Ok(EMPTY),
|
||||||
|
"unmodified node 4 must be ZERO"
|
||||||
|
);
|
||||||
|
|
||||||
// STORE LEAVES MATCH TREE ===============================================================
|
// STORE LEAVES MATCH TREE ===============================================================
|
||||||
// sanity check the values returned by the store and the tree
|
// sanity check the values returned by the store and the tree
|
||||||
|
@ -234,6 +285,11 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
||||||
store.get_node(smt.root(), NodeIndex::new(smt.depth(), 3)),
|
store.get_node(smt.root(), NodeIndex::new(smt.depth(), 3)),
|
||||||
"node 3 must be the same for both SparseMerkleTree and MerkleStore"
|
"node 3 must be the same for both SparseMerkleTree and MerkleStore"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
smt.get_node(&NodeIndex::new(smt.depth(), 4)),
|
||||||
|
store.get_node(smt.root(), NodeIndex::new(smt.depth(), 4)),
|
||||||
|
"node 4 must be the same for both SparseMerkleTree and MerkleStore"
|
||||||
|
);
|
||||||
|
|
||||||
// STORE MERKLE PATH MATCHS ==============================================================
|
// STORE MERKLE PATH MATCHS ==============================================================
|
||||||
// assert the merkle path returned by the store is the same as the one in the tree
|
// assert the merkle path returned by the store is the same as the one in the tree
|
||||||
|
@ -255,7 +311,7 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LEAVES4[1], result.value,
|
LEAVES4[1], result.value,
|
||||||
"Value for merkle path at index 0 must match leaf value"
|
"Value for merkle path at index 1 must match leaf value"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
smt.get_path(NodeIndex::new(smt.depth(), 1)),
|
smt.get_path(NodeIndex::new(smt.depth(), 1)),
|
||||||
|
@ -268,12 +324,12 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LEAVES4[2], result.value,
|
LEAVES4[2], result.value,
|
||||||
"Value for merkle path at index 0 must match leaf value"
|
"Value for merkle path at index 2 must match leaf value"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
smt.get_path(NodeIndex::new(smt.depth(), 2)),
|
smt.get_path(NodeIndex::new(smt.depth(), 2)),
|
||||||
Ok(result.path),
|
Ok(result.path),
|
||||||
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
"merkle path for index 2 must be the same for the MerkleTree and MerkleStore"
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = store
|
let result = store
|
||||||
|
@ -281,12 +337,25 @@ fn test_sparse_merkle_tree() -> Result<(), MerkleError> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
LEAVES4[3], result.value,
|
LEAVES4[3], result.value,
|
||||||
"Value for merkle path at index 0 must match leaf value"
|
"Value for merkle path at index 3 must match leaf value"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
smt.get_path(NodeIndex::new(smt.depth(), 3)),
|
smt.get_path(NodeIndex::new(smt.depth(), 3)),
|
||||||
Ok(result.path),
|
Ok(result.path),
|
||||||
"merkle path for index 0 must be the same for the MerkleTree and MerkleStore"
|
"merkle path for index 3 must be the same for the MerkleTree and MerkleStore"
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = store
|
||||||
|
.get_path(smt.root(), NodeIndex::new(smt.depth(), 4))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
EMPTY, result.value,
|
||||||
|
"Value for merkle path at index 4 must match leaf value"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
smt.get_path(NodeIndex::new(smt.depth(), 4)),
|
||||||
|
Ok(result.path),
|
||||||
|
"merkle path for index 4 must be the same for the MerkleTree and MerkleStore"
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Loading…
Add table
Reference in a new issue